rework android-rmt display, add a few buttons
[goodguy/history.git] / cinelerra-5.0 / cinelerra / filescene.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  * 
19  */
20
21
22
23 #include "asset.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "file.h"
27 #include "filescene.h"
28 #include "filesystem.h"
29 #include "format.inc"
30 #include "libmjpeg.h"
31 #include "scenegraph.h"
32
33
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39
40
41 extern "C"
42 {
43 #include <uuid.h>
44 }
45
46
47 // Paths relative to the exe path
48 #define FESTIVAL_PATH "/festival"
49 #define FESTIVAL_LIB_PATH "/lib/"
50 #define ASSET_PATH "/models/"
51 #define FREAD_SIZE 0x10000
52 #define WAVHEADER 44
53 #define FESTIVAL_SAMPLERATE 16000
54 #define PAUSE_SAMPLES FESTIVAL_SAMPLERATE
55 // Amount to truncate if ...
56 #define ADVANCE_SAMPLES FESTIVAL_SAMPLERATE
57
58 // Maximum characters in a line of dialog is limited by festival
59 #define MAX_CHARS 65536
60
61 #define STRIP_LINE(string) \
62 /* Strip linefeeds */ \
63         while(len > 0 && (string[len - 1] == '\n' || \
64                 string[len - 1] == ' ')) \
65         { \
66                 string[len - 1] = 0; \
67                 len--; \
68         } \
69  \
70 /* Strip comments */ \
71         for(i = 0; i < len; i++) \
72         { \
73                 if(string[i] == '#') \
74                 { \
75                         string[i] = 0; \
76                         i = len; \
77                 } \
78         }
79
80
81
82
83 #define STRING_PARAMETER(title, need_char, dst) \
84 if(!strncmp(command, title, strlen(title))) \
85 { \
86 /* advance to argument */ \
87         i += strlen(title); \
88         while(string[i] != 0 && string[i] == ' ') \
89                 i++; \
90  \
91         if(current_char || !need_char) \
92         { \
93 /* printf("STRING_PARAMETER %s %s %p\n", title, string + i, dst); */ \
94                 strcpy(dst, string + i); \
95         } \
96         else \
97         { \
98                 printf("FileScene::read_script %d Line %d: %s but no current character\n",  \
99                         __LINE__, \
100                         current_line, \
101                         title); \
102         } \
103  \
104         i = len; \
105 }
106
107 static int read_parameter(char *string,
108         int *i,
109         const char *title, 
110         char *dst_string, 
111         float *dst_float0, 
112         float *dst_float1, 
113         float *dst_float2)
114 {
115         char *command = string + *i;
116         char *arg_text[3];
117
118         if(!strncmp(command, title, strlen(title)))
119         {
120                 *i += strlen(title);
121
122                 for(int j = 0; j < 4; j++)
123                 {
124 /* skip to start of argument */
125                         while(string[*i] != 0 && string[*i] == ' ')
126                                 (*i)++;
127
128                         if(string[*i] != 0)
129                         {
130                                 arg_text[j] = string + *i;
131                                 while(string[*i] != 0 && string[*i] != ' ')
132                                         (*i)++;
133                         }
134                         else
135                         {
136                                 arg_text[j] = 0;
137                         }
138                 }
139
140 // printf("read_parameter %d %s %s %s %s\n", 
141 // __LINE__, 
142 // title, 
143 // arg_text[0], 
144 // arg_text[1], 
145 // arg_text[2]);
146
147                 if(arg_text[0])
148                 {
149                         if(dst_string)
150                         {
151                                 char *ptr1 = dst_string;
152                                 char *ptr2 = arg_text[0];
153                                 while(*ptr2 != 0 && *ptr2 != ' ')
154                                         *ptr1++ = *ptr2++;
155                                 *ptr1 = 0;
156                         }
157                         else
158                         if(dst_float0)
159                         {
160                                 *dst_float0 = atof(arg_text[0]);
161                         }
162                 }
163
164                 if(arg_text[1])
165                 {
166                         if(dst_float1)
167                         {
168                                 *dst_float1 = atof(arg_text[1]);
169                         }
170                 }
171
172                 if(arg_text[2])
173                 {
174                         if(dst_float2)
175                         {
176                                 *dst_float2 = atof(arg_text[2]);
177                         }
178                 }
179
180                 return 1;
181         }
182
183         return 0;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198 FileScene::FileScene(Asset *asset, File *file)
199  : FileBase(asset, file)
200 {
201         reset_parameters();
202         get_exe_path(exe_path);
203 }
204
205
206 FileScene::~FileScene()
207 {
208         close_file();
209 }
210
211
212
213 int FileScene::open_file(int rd, int wr)
214 {
215 // Load the script to get character count
216         read_script();
217         
218
219
220 // Set asset format
221         asset->format = FILE_SCENE;
222         asset->video_data = 1;
223 // Should be set in the scene file
224         asset->layers = 1;
225         asset->width = 1280;
226         asset->height = 720;
227 // Dictated by character animations
228         asset->frame_rate = (double)30000 / 1001;
229 // Some arbitrary default length.
230 // Hard to calculate without rendering it.
231         asset->video_length = -1;
232
233
234
235         asset->audio_data = 1;
236 // The speech synthesizer outputs different samplerates depending on the speaker.
237 // The heroine voices are all 16khz
238         asset->sample_rate = FESTIVAL_SAMPLERATE;
239 // Mono voice placement for now
240 // Maybe a different track for each voice in the future or 3D positioning
241         asset->channels = script->total_characters();
242         asset->audio_length = -1;
243
244
245         return 0;
246 }
247
248 int FileScene::close_file()
249 {
250         delete script;
251         delete [] audio_temp;
252 //      delete overlayer;
253 //      delete affine;
254         FileBase::close_file();
255         return 0;
256 }
257
258
259 int FileScene::check_sig(Asset *asset, char *test)
260 {
261         if(!strncmp(test, "TEXT2MOVIE", 10)) return 1;
262
263         return 0;
264 }
265
266
267
268 int FileScene::set_video_position(int64_t x)
269 {
270         return 0;
271 }
272
273
274 int FileScene::set_audio_position(int64_t x)
275 {
276         return 0;
277 }
278
279
280 int FileScene::read_frame(VFrame *frame)
281 {
282 // Everything is timed based on speech, so render the audio for this frame.
283         const int debug = 0;
284         
285         
286         frame->clear_frame();
287         int64_t audio_position1 = (int64_t)(file->current_frame * 
288                 asset->sample_rate /
289                 asset->frame_rate);
290         int64_t audio_position2 = (int64_t)(audio_position1 + 
291                 asset->sample_rate /
292                 asset->frame_rate);
293
294         if(debug) printf("FileScene::read_frame %d frame=" _LD
295                 " frame_rate=%f sample_rate=%d audio_position1=" _LD
296                 " audio_position2=" _LD "\n", __LINE__,
297                 file->current_frame, asset->frame_rate,
298                 asset->sample_rate, audio_position1,
299                 audio_position2);
300
301         render_chunks(audio_position1, 
302                 audio_position2 - audio_position1,
303                 1);
304         if(!script) return 1;
305         if(debug) printf("FileScene::read_frame %d\n", __LINE__);
306
307 //      script->dump();
308
309
310 // Determine lip position from amplitude
311         double accum = 0;
312         for(int i = 0; i < audio_position2 - audio_position1; i++)
313         {
314                 double sample_float = fabs((double)audio_temp[i] / 32768);
315                 if(sample_float > accum) accum = sample_float;
316         }
317         if(debug) printf("FileScene::read_frame %d accum=%f\n", __LINE__, accum);
318
319 // Reset cameras
320         for(int i = 0; i < script->total_characters(); i++)
321         {
322                 SceneChar *character = script->get_character(i);
323                 character->current_camera = SceneChar::CAMERA_WIDE;
324                 character->is_speeking = 0;
325                 character->max = 0;
326         }
327
328 // Now determine most recent character which is speaking from the sample times.
329         int64_t current_sample = 0;
330         SceneChar *speeking_character = 0;
331 // Sample relative to start of chunk
332         int64_t chunk_sample = 0;
333         for(int i = 0; 
334                 i < script->total_chunks() && current_sample < audio_position2; 
335                 i++)
336         {
337                 SceneChunk *chunk = script->get_chunk(i);
338                 int samples = chunk->audio_size / 2;
339
340
341                 if(audio_position1 >= current_sample && 
342                         audio_position1 < current_sample + samples)
343                 {
344                         speeking_character = chunk->character;
345                         speeking_character->max = chunk->max;
346                         chunk->character->is_speeking = 1;
347                         chunk_sample = audio_position1 - current_sample;
348 //                      break;
349                 }
350                 else
351                 if(!chunk->character->is_speeking)
352                 {
353                         chunk->character->increment_camera();
354                 }
355
356                 current_sample += chunk->advance_samples;
357         }
358
359
360
361         if(debug) printf("FileScene::read_frame %d\n", __LINE__);
362         
363 // Render the scene.
364 // Store component placement in a scene graph.
365         SceneGraph scene;
366         //SceneNode *speeking_node = 0;
367
368 // Scale for the entire scene
369         SceneCamera *camera = new SceneCamera;
370         scene.append_camera(camera);
371         camera->at_x = 0;
372         camera->at_y = 0;
373
374 // Render background
375         if(script->background[0])
376         {
377                 script->render_background(&scene);
378         }
379
380         if(debug) printf("FileScene::read_frame %d\n", __LINE__);
381
382 // Render characters
383         for(int i = 0; i < script->total_characters(); i++)
384         {
385                 SceneChar *character = script->get_character(i);
386
387                 character->read_model();
388
389 // Nodes for the character
390                 SceneNode *character_node = new SceneNode(character->name);
391                 scene.append(character_node);
392                 character_node->sx = character_node->sy = character->scale;
393
394                 SceneNode *head_node = new SceneNode("head");
395
396                 SceneNode *body_node = new SceneNode("body");
397
398 // Render in the order listed in the file
399                 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
400                 for(int j = 0; j < 2; j++)
401                 {
402                         if(debug) printf("FileScene::read_frame %d j=%d head_order=%d body_order=%d\n", 
403                                 __LINE__, 
404                                 j,
405                                 character->head_order,
406                                 character->body_order);
407                         if(j == character->head_order) character_node->append(head_node);
408                         else
409                         if(j == character->body_order) character_node->append(body_node);
410                 }
411
412                 SceneNode *eye_node = 0;
413                 if(character->eyes.size())
414                 {
415                         eye_node = new SceneNode("eyes");
416                         head_node->append(eye_node);
417                 }
418
419                 SceneNode *mouth_node = new SceneNode("mouth");
420                 head_node->append(mouth_node);
421
422 // Logical character placement
423                 switch(script->total_characters())
424                 {
425                         case 1:
426                                 if(speeking_character == character &&
427                                         speeking_character->current_camera == SceneChar::CAMERA_CU)
428                                 {
429                                         camera->at_x = asset->width / 4;
430                                 }
431
432                                 character_node->x = (int)(asset->width / 2 - 
433                                         character->w * character->scale / 2);
434                                 character_node->y = (int)(asset->height - 
435                                         character->h * character->scale);
436                                 break;
437
438                         case 2:
439                                 if(i == 0)
440                                 {
441                                         character_node->x = 0;
442                                         character_node->y = (int)(asset->height - 
443                                                 character->h * character->scale);
444
445                                         if(speeking_character == character &&
446                                                 speeking_character->current_camera == SceneChar::CAMERA_CU)
447                                         {
448                                                 camera->at_y = character_node->y;
449                                                 camera->at_x = character_node->x + 
450                                                         (character->head->x + character->head->image->get_w() / 2) * character->scale - 
451                                                         asset->width / 4;
452                                                 CLAMP(camera->at_x, 0, asset->width / 2);
453                                                 CLAMP(camera->at_y, 0, asset->height);
454 //printf("FileScene::read_frame %d camera->at_x=%f camera->at_y=%f\n", __LINE__, camera->at_x, camera->at_y);
455                                         }
456
457                                         if(character->faces_left)
458                                         {
459                                                 character_node->flip = 1;
460                                                 character_node->x = asset->width - 
461                                                         character->w * character->scale;
462                                         }
463                                 }
464                                 else
465                                 {
466
467                                         character_node->x = (int)(asset->width - 
468                                                 character->w * character->scale);
469                                         character_node->y = (int)(asset->height - 
470                                                 character->h * character->scale);
471
472                                         if(speeking_character == character &&
473                                                 speeking_character->current_camera == SceneChar::CAMERA_CU)
474                                         {
475                                                 camera->at_x = character_node->x + 
476                                                         character->head->x * character->scale - 
477                                                         asset->width / 4;
478                                                 CLAMP(camera->at_x, 0, asset->width / 2);
479                                         }
480
481                                         if(!character->faces_left) 
482                                         {
483                                                 character_node->flip = 1;
484                                                 character_node->x = 0;
485                                         }
486                                 }
487                                 break;
488
489                         case 3:
490                                 if(i == 0)
491                                 {
492                                         character_node->x = 0;
493                                         character_node->y = (int)(asset->height - 
494                                                 character->h * character->scale);
495                                         if(character->faces_left)
496                                         {
497                                                 character_node->flip = 1;
498                                         }
499                                 }
500                                 else
501                                 if(i == 1)
502                                 {
503                                         if(speeking_character == character &&
504                                                 speeking_character->current_camera == SceneChar::CAMERA_CU)
505                                         {
506                                                 camera->at_x = asset->width / 4;
507                                         }
508                                         character_node->x = (int)(asset->width / 2 - 
509                                                 character->w * character->scale / 2);
510                                         character_node->y = (int)(asset->height - 
511                                                 character->h * character->scale);
512                                         if(character->faces_left)
513                                         {
514                                                 character_node->flip = 1;
515                                         }
516                                 }
517                                 else
518                                 {
519                                         if(speeking_character == character &&
520                                                 speeking_character->current_camera == SceneChar::CAMERA_CU)
521                                         {
522                                                 camera->at_x = asset->width / 2;
523                                         }
524                                         character_node->x = (int)(asset->width - 
525                                                 character->w * character->scale);
526                                         character_node->y = (int)(asset->height - 
527                                                 character->h * character->scale);
528                                         if(!character->faces_left)
529                                         {
530                                                 character_node->flip = 1;
531                                                 character_node->x = 0;
532                                         }
533                                 }
534                                 break;
535                 }
536
537 // Clamp the head
538                 if(character_node->y < 0) character_node->y = 0;
539
540 // Add remaining parts
541                 body_node->copy_ref(character->body);
542                 head_node->copy_ref(character->head);
543
544 // Speeker head rotation
545                 if(speeking_character == character)
546                 {
547                         //speeking_node = character_node;
548                 
549                         int head_time = (chunk_sample / asset->sample_rate / 2) % 2;
550
551                         if(head_time > 0)
552                         {
553                                 double temp;
554                                 double anim_position = modf((double)chunk_sample / asset->sample_rate / 2, &temp);
555                                 double anim_length = 0.1;
556 // printf("FileScene::read_frame %d %d %f\n", 
557 // __LINE__, 
558 // head_time,
559 // anim_position);
560
561                                 if(anim_position < anim_length)
562                                         head_node->ry = -5 * anim_position / anim_length;
563                                 else
564                                 if(anim_position > 1.0 - anim_length)
565                                         head_node->ry = -5 * (1.0 - anim_position) / anim_length;
566                                 else
567                                         head_node->ry = -5;
568                         }
569                 }
570
571 //head_node->ry = -5;
572
573 // Eyes
574                 double intpart;
575                 if(character->eyes.size())
576                 {
577                         if(modf((file->current_frame / asset->frame_rate + script->get_char_number(character)) / 5, &intpart) <= 
578                                 0.1 / 5 &&
579                                 file->current_frame / asset->frame_rate > 1)
580                         {
581                                 eye_node->copy_ref(character->eyes.get(0));
582                         }
583                         else
584                                 eye_node->copy_ref(character->eyes.get(1));
585                 }
586
587 // Compute the mouth image
588                 int fraction = 0;
589                 if(character->is_speeking && character->max > 0)
590                 {
591                         fraction = (int)(accum * character->mouths.size() / character->max);
592                         if(fraction >= character->mouths.size())
593                                 fraction = character->mouths.size() - 1;
594                 }
595
596                 mouth_node->copy_ref(character->mouths.get(fraction));
597
598
599 // camera->scale = 2;
600 // camera->at_x = asset->width / 2;
601 // camera->at_y = 0;
602
603 // Compute camera
604                 if(speeking_character == character &&
605                         speeking_character->current_camera == SceneChar::CAMERA_CU)
606                 {
607 // If closeup, increase scene scale
608                         camera->scale = 2;
609                 }
610         }
611
612
613
614
615         if(debug) printf("FileScene::read_frame %d\n", __LINE__);
616
617
618
619 // Render scene graph
620         scene.render(frame, file->cpus);
621                 
622                 
623                 
624         if(debug) printf("FileScene::read_frame %d\n", __LINE__);
625
626
627         return 0;
628 }
629
630
631 int FileScene::read_samples(double *buffer, int64_t len)
632 {
633 // Speech rendering
634 // Everything is timed based on speech, so we have to do this for video, too.
635         render_chunks(file->current_sample, len, 0);
636
637 //      script->dump();
638
639
640 // Convert temp to output
641         for(int i = 0; i < len; i++)
642         {
643                 buffer[i] = (double)audio_temp[i] / 32768;
644         }
645
646         return 0;
647 }
648
649
650 int64_t FileScene::get_memory_usage()
651 {
652 //PRINT_TRACE
653         int total = 0x100000;
654         if(script)
655         {
656                 total += script->get_memory_usage();
657                 
658         }
659 //PRINT_TRACE
660         return total;
661 }
662
663
664
665 int FileScene::get_best_colormodel(Asset *asset, int driver)
666 {
667         return 0;
668 }
669
670
671 int FileScene::colormodel_supported(int colormodel)
672 {
673         return BC_RGBA8888;
674 }
675
676
677 int FileScene::can_copy_from(Asset *asset, int64_t position)
678 {
679         return 0;
680 }
681
682
683 int FileScene::reset_parameters_derived()
684 {
685         script = 0;
686         audio_temp = 0;
687         temp_allocated = 0;
688 //      overlayer = 0;
689 //      affine = 0;
690         return 0;
691 }
692
693
694
695 void FileScene::render_chunks(int64_t start_position, 
696         int64_t len, 
697         int all_channels)
698 {
699         int64_t end_position = start_position + len;
700         const int debug = 0;
701
702
703         if(debug) printf("FileScene::render_chunks %d start_position="
704                 _LD " len=" _LD "\n", __LINE__, start_position, len);
705
706 // Update script
707         read_script();
708
709         if(!script) return;
710
711         if(debug) PRINT_TRACE
712
713 // Reallocate temp buffer
714         if(len > temp_allocated)
715         {
716                 delete [] audio_temp;
717                 audio_temp = new int16_t[len];
718                 temp_allocated = len;
719         }
720         bzero(audio_temp, sizeof(int16_t) * len);
721
722         if(debug) PRINT_TRACE
723
724
725
726
727 // Find start_position in script output.
728 // Must know length of every chunk before end of buffer
729         int64_t current_sample = 0;
730         for(int i = 0; i < script->total_chunks() &&
731                 current_sample < end_position; i++)
732         {
733                 SceneChunk *chunk = script->get_chunk(i);
734                 chunk->used = 0;
735                 if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
736
737 // If extent of audio output hasn't been calculated, render it
738                 if(!chunk->audio_size)
739                 {
740                         if(debug) printf("FileScene::render_chunks %d i=%d\n", __LINE__, i);
741                         chunk->render();
742                 }
743
744 // Dialog chunk is inside output buffer
745                 if(current_sample + chunk->audio_size / 2 > start_position &&
746                         current_sample < end_position)
747                 {
748                         if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
749
750 // If no audio output exists, render it
751                         if(!chunk->audio)
752                         {
753                                 if(debug) printf("FileScene::render_chunks %d rerendering audio\n", __LINE__);
754                                 chunk->render();
755                         }
756
757                         if(debug) printf("FileScene::render_chunks %d: Using \"%s\" samples=%d\n",
758                                 __LINE__,
759                                 chunk->text,
760                                 chunk->audio_size / 2);
761                         if(debug) printf("FileScene::render_chunks %d: start_position="
762                                         _LD " current_sample=" _LD "\n", __LINE__,
763                                         start_position, current_sample);
764
765 // Memcpy it.
766 // TODO: allow characters to talk simultaneously
767                         int64_t src_offset = start_position - current_sample;
768                         int64_t dst_offset = 0;
769                         int64_t src_len = chunk->audio_size / 2 - src_offset;
770
771                         if(debug) printf("FileScene::render_chunks %d: src_offset="
772                                 _LD " dst_offset=" _LD " src_len=" _LD "\n", __LINE__,
773                                 src_offset, dst_offset, src_len);
774
775                         if(src_offset < 0)
776                         {
777                                 dst_offset -= src_offset;
778                                 src_len += src_offset;
779                                 src_offset = 0;
780                         }
781
782                         if(dst_offset + src_len > len)
783                         {
784                                 src_len = len - dst_offset;
785                         }
786
787                         if(debug) printf("FileScene::render_chunks %d: src_offset="
788                                 _LD " dst_offset=" _LD " src_len=" _LD "\n", __LINE__,
789                                 src_offset, dst_offset, src_len);
790
791 // Transfer if right channel
792                         if(all_channels || 
793                                 file->current_channel == script->get_char_number(chunk->character))
794                         {
795                                 for(int j = 0; j < src_len; j++)
796                                 {
797                                         audio_temp[dst_offset + j] = 
798                                                 chunk->audio[(src_offset + j) * 2] | 
799                                                 (chunk->audio[(src_offset + j) * 2 + 1] << 8);
800                                 }
801                         }
802
803                         if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
804                         chunk->used = 1;
805                 }
806
807                 current_sample += chunk->advance_samples;
808         }
809
810 // Erase unused dialog chunks
811         if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
812         for(int i = 0; i < script->total_chunks(); i++)
813         {
814                 SceneChunk *chunk = script->get_chunk(i);
815                 if(!chunk->used && chunk->audio)
816                 {
817                         if(debug) printf("FileScene::render_chunks %d erasing unused audio\n", __LINE__);
818                         delete [] chunk->audio;
819                         chunk->audio = 0;
820                         chunk->audio_allocated = 0;
821                 }
822         }
823         if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
824         
825         
826 }
827
828
829
830 int FileScene::read_script()
831 {
832         const int debug = 0;
833         struct stat ostat;
834         if(stat(asset->path, &ostat))
835         {
836                 printf("FileScene::read_script %d: %s\n", __LINE__, strerror(errno));
837                 return 1;
838         }
839
840 // Test timestamp
841         if(script)
842         {
843                 if(ostat.st_mtime == script->timestamp)
844                 {
845                         if(debug) printf("FileScene::read_script %d: script unchanged\n", __LINE__);
846                         return 0;
847                 }
848         }
849
850
851 // Read new script
852         delete script;
853         script = 0;
854         if(!script) script = new SceneTokens(this, file->cpus);
855         script->timestamp = ostat.st_mtime;
856
857         script->read_script(asset->path);
858
859         return 1;
860 }
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876 SceneChar::SceneChar(SceneTokens *script)
877 {
878         this->script = script;
879         name[0] = 0;
880         voice[0] = 0;
881         body = 0;
882         head = 0;
883         body_order = 0;
884         head_order = 1;
885         sprintf(model, "heroine01");
886         current_camera = CAMERA_WIDE;
887         faces_left = 0;
888         scale = 1;
889         max = 0;
890         is_speeking = 0;
891 }
892
893 SceneChar::~SceneChar()
894 {
895         mouths.remove_all_objects();
896         eyes.remove_all_objects();
897         delete body;
898         delete head;
899 }
900
901
902 void SceneChar::increment_camera()
903 {
904         current_camera++;
905         if(current_camera >= CAMERA_TOTAL) 
906                 current_camera = 0;
907 }
908
909
910 int SceneChar::read_model()
911 {
912 // Read descriptor file
913         const int debug = 0;
914         int current_line = 0;
915         float x, y;
916         int i;
917         int current_order = 0;
918         char path[BCTEXTLEN];
919
920 // Already read it
921         if(body) return 0;
922
923         script->convert_path(path, model);
924         FILE *fd = fopen(path, "r");
925         
926
927 // Read assets
928         if(fd)
929         {
930 // Read 1 line
931                 char string[BCTEXTLEN];
932                 char string2[BCTEXTLEN];
933
934                 while(!feof(fd))
935                 {
936                         char *result = fgets(string, BCTEXTLEN, fd);
937                         
938                         if(result)
939                         {
940                                 int len = strlen(string);
941
942                                 STRIP_LINE(string);
943
944                                 if(debug) printf("SceneChar::read_model %d: %s\n", 
945                                         __LINE__, 
946                                         string);
947                         
948                                 for(i = 0; i < len; i++)
949                                 {
950                                         if(isalnum(string[i]))
951                                         {
952                                                 string2[0] = 0;
953                                                 
954                                                 if(read_parameter(string,
955                                                         &i,
956                                                         "width:", 
957                                                         0, 
958                                                         &w, 
959                                                         0, 
960                                                         0))
961                                                 {
962                                                 }
963                                                 else
964                                                 if(read_parameter(string,
965                                                         &i,
966                                                         "height:", 
967                                                         0, 
968                                                         &h, 
969                                                         0, 
970                                                         0))
971                                                 {
972                                                 }
973                                                 else
974                                                 if(read_parameter(string,
975                                                         &i,
976                                                         "body:", 
977                                                         string2, 
978                                                         0, 
979                                                         &x, 
980                                                         &y))
981                                                 {
982 // Load image
983                                                         body = new SceneNode(script->load_image(string2), 1, x, y);
984                                                         body_order = current_order++;
985                                                 }
986                                                 else
987                                                 if(read_parameter(string,
988                                                         &i,
989                                                         "head:", 
990                                                         string2, 
991                                                         0, 
992                                                         &x, 
993                                                         &y))
994                                                 {
995 // Load image
996                                                         head = new SceneNode(script->load_image(string2), 1, x, y);
997                                                         head_order = current_order++;
998                                                 }
999                                                 else
1000                                                 if(read_parameter(string,
1001                                                         &i,
1002                                                         "mouth:", 
1003                                                         string2, 
1004                                                         0, 
1005                                                         &x, 
1006                                                         &y))
1007                                                 {
1008 // Load image
1009                                                         SceneNode *mouth;
1010                                                         mouths.append(mouth = new SceneNode(script->load_image(string2), 1, x, y));
1011 // Make coordinates relative to head
1012                                                         if(head)
1013                                                         {
1014                                                                 mouth->x -= head->x;
1015                                                                 mouth->y -= head->y;
1016                                                         }
1017                                                 }
1018                                                 else
1019                                                 if(read_parameter(string,
1020                                                         &i,
1021                                                         "eyes:", 
1022                                                         string2, 
1023                                                         0, 
1024                                                         &x, 
1025                                                         &y))
1026                                                 {
1027 // Load image
1028                                                         SceneNode *eye;
1029                                                         eyes.append(eye = new SceneNode(script->load_image(string2), 1, x, y));
1030 // Make coordinates relative to head
1031                                                         if(head)
1032                                                         {
1033                                                                 eye->x -= head->x;
1034                                                                 eye->y -= head->y;
1035                                                         }
1036                                                 }
1037                                                 else
1038                                                 if(read_parameter(string,
1039                                                         &i,
1040                                                         "faces_left", 
1041                                                         0, 
1042                                                         0, 
1043                                                         0, 
1044                                                         0))
1045                                                 {
1046                                                         faces_left = 1;
1047                                                 }
1048                                                 else
1049                                                 if(read_parameter(string,
1050                                                         &i,
1051                                                         "scale:", 
1052                                                         0, 
1053                                                         &scale, 
1054                                                         0, 
1055                                                         0))
1056                                                 {
1057                                                 }
1058                                                 
1059                                                 i = len;
1060                                         }
1061                                 }
1062                         }
1063                         
1064                         current_line++;
1065                 }
1066                 
1067                 fclose(fd);
1068                 if(debug) dump();
1069                 return 0;
1070         }
1071         
1072         printf("SceneChar::read_model %d: %s %s\n", __LINE__, path, strerror(errno));
1073         return 1;
1074 }
1075
1076 int SceneChar::get_memory_usage()
1077 {
1078         int total = 0;
1079         if(body) total += body->get_memory_usage();
1080         if(head) total += head->get_memory_usage();
1081         for(int i = 0; i < mouths.size(); i++)
1082                 total += mouths.get(i)->get_memory_usage();
1083         return total;
1084 }
1085
1086 void SceneChar::dump()
1087 {
1088         printf("SceneChar::dump %d: %p name=%s voice=%s model=%s body=%p eyes=%d mouths=%d\n", 
1089                 __LINE__, 
1090                 this,
1091                 name, 
1092                 voice, 
1093                 model,
1094                 body,
1095                 eyes.size(),
1096                 mouths.size());
1097         printf("SceneChar::dump %d: w=%f h=%f\n", __LINE__, w, h);
1098
1099
1100         for(int i = 0; i < mouths.size(); i++)
1101         {
1102                 SceneNode *node = mouths.get(i);
1103                 printf("    mouth=%p x=%f y=%f\n", node, node->x, node->y);
1104         }
1105
1106         for(int i = 0; i < eyes.size(); i++)
1107         {
1108                 SceneNode *node = eyes.get(i);
1109                 printf("    eyes=%p x=%f y=%f\n", node, node->x, node->y);
1110         }
1111 }
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121 // Dialog from a single character
1122 SceneChunk::SceneChunk(SceneTokens *script)
1123 {
1124         text = 0;
1125         character = 0;
1126         audio = 0;
1127         audio_size = 0;
1128         audio_allocated = 0;
1129         advance_samples = 0;
1130         used = 0;
1131         max = 0;
1132         command = NO_COMMAND;
1133         this->script = script;
1134 }
1135
1136 SceneChunk::~SceneChunk()
1137 {
1138         delete [] text;
1139         delete [] audio;
1140 }
1141
1142 void SceneChunk::dump()
1143 {
1144         printf("SceneChunk::dump %d: character=%s command=%d text=%s\n", 
1145                 __LINE__, 
1146                 character->name,
1147                 command,
1148                 text);
1149         printf("SceneChunk::dump %d: audio=%p audio_size=%d advance_samples=%d\n",
1150                 __LINE__,
1151                 audio,
1152                 audio_size,
1153                 advance_samples);
1154 }
1155
1156 int SceneChunk::get_memory_usage()
1157 {
1158         return audio_allocated;
1159 }
1160
1161
1162 void SceneChunk::append_text(char *new_text)
1163 {
1164         char string[BCTEXTLEN];
1165
1166 // Replace "
1167 // Convert ' to \'
1168         char *ptr = string;
1169         int len = strlen(new_text);
1170         for(int i = 0; i < len; i++)
1171         {
1172 //              if(new_text[i] == '"')
1173 //                      *ptr++ = ' ';
1174 //              else
1175 //              if(new_text[i] == '\'')
1176 //              {
1177 //                      *ptr++ = '\\';
1178 //                      *ptr++ = '\'';
1179 //                      *ptr++ = '\'';
1180 //              }
1181 //              else
1182                         *ptr++ = new_text[i];
1183         }
1184         *ptr++ = 0;
1185         
1186         int len2 = strlen(string);
1187         if(text)
1188         {
1189                 int len1 = strlen(text);
1190                 int len3 = 1;
1191                 int need_space = 0;
1192
1193 //              if(len1 > 0 && isalnum(text[len1 - 1]))
1194                 if(len1 > 0 && text[len1 - 1] != ' ')
1195                 {
1196                         need_space = 1;
1197                         len3++;
1198                 }
1199
1200                 text = (char*)realloc(text, len1 + len2 + len3);
1201
1202 // Append space
1203                 if(need_space)
1204                 {
1205                         text[len1] = ' ';
1206                         text[len1 + 1] = 0;
1207                 }
1208         }
1209         else
1210         {
1211                 text = new char[len2 + 1];
1212                 text[0] = 0;
1213         }
1214
1215         strcat(text, string);
1216 }
1217
1218
1219
1220
1221 void SceneChunk::render()
1222 {
1223         const int debug = 0;
1224         int len = 0;
1225         char command_line[BCTEXTLEN];
1226         char string2[MAX_CHARS];
1227         char script_path[BCTEXTLEN];
1228         
1229         //int total_args = 0;
1230         if(text) len = strlen(text);
1231         char *text_end = text + len;
1232         char *text_ptr = text;
1233
1234         audio_size = 0;
1235
1236         if(!character)
1237         {
1238                 printf("SceneChunk::render %d: no character defined.\n", __LINE__);
1239         }
1240         
1241         if(len > MAX_CHARS)
1242         {
1243                 printf("SceneChunk::render %d: text '%s' exceeds festival's maximum line length of %d chars.\n",
1244                         __LINE__,
1245                         text,
1246                         MAX_CHARS);
1247         }
1248
1249 // Process command
1250         switch(command)
1251         {
1252                 case SceneChunk::PAUSE_COMMAND:
1253                         audio_allocated = PAUSE_SAMPLES * 2;
1254                         audio_size = PAUSE_SAMPLES * 2;
1255                         advance_samples = PAUSE_SAMPLES;
1256                         audio = (unsigned char*)realloc(audio, audio_allocated);
1257                         bzero(audio, audio_size);
1258                         max = 0;
1259                         break;
1260         }
1261
1262         while(text && text_ptr < text_end)
1263         {
1264 // Copy at most MAX_CHARS of data into string2
1265                 char *ptr = string2;
1266                 for(int i = 0; i < MAX_CHARS && text_ptr < text_end; i++)
1267                 {
1268                         *ptr++ = *text_ptr++;
1269                 }
1270                 *ptr++ = 0;
1271
1272
1273 // Rewind to white space if still more text
1274                 if(text_ptr < text_end)
1275                 {
1276                         ptr--;
1277                         text_ptr--;
1278                         while(*text_ptr != ' ' && 
1279                                 *text_ptr != '\n' && 
1280                                 text_ptr > text)
1281                         {
1282                                 text_ptr--;
1283                                 ptr--;
1284                         }
1285
1286
1287 // Truncate string2 at white space
1288                         *ptr = 0;
1289 // If no white space, abort.
1290                         if(text_ptr <= text)
1291                         {
1292                                 break;
1293                         }
1294
1295                 }
1296
1297                 uuid_t temp_id;
1298                 sprintf(script_path, "/tmp/cinelerra.");
1299                 uuid_generate(temp_id);
1300                 uuid_unparse(temp_id, script_path + strlen(script_path));
1301                 FILE *script_fd = fopen(script_path, "w");
1302
1303                 sprintf(command_line, "%s%s --libdir %s%s -b %s", 
1304                         script->file->exe_path,
1305                         FESTIVAL_PATH,
1306                         script->file->exe_path,
1307                         FESTIVAL_LIB_PATH,
1308                         script_path);
1309
1310 // Create script.
1311 // The maximum text length is limited with the command line
1312
1313                 fprintf(script_fd, 
1314                         "(voice_%s)\n"
1315                         "(set! text (Utterance Text \"%s\"))\n"
1316                         "(utt.synth text)"
1317                         "(utt.save.wave text \"-\")",
1318                         character->voice,
1319                         string2);
1320                 fclose(script_fd);
1321
1322
1323                 if(debug)
1324                 {
1325                         printf("SceneChunk::render %d %s\n", 
1326                                 __LINE__,
1327                                 command_line);
1328                         
1329                         FILE *script_fd = fopen(script_path, "r");
1330                         while(!feof(script_fd))
1331                                 fputc(fgetc(script_fd), stdout);
1332                         printf("\n");
1333                         fclose(script_fd);
1334                 }
1335
1336 // popen only does half duplex
1337                 FILE *fd = popen(command_line, "r");
1338
1339 // Capture output
1340                 if(fd)
1341                 {
1342                         int audio_start = audio_size;
1343
1344                 
1345                         if(debug) printf("SceneChunk::render %d\n", 
1346                                 __LINE__);
1347                         while(!feof(fd))
1348                         {
1349                                 if(debug) printf("SceneChunk::render %d\n", 
1350                                         __LINE__);
1351                                 if(audio_size + FREAD_SIZE > audio_allocated)
1352                                 {
1353                                         audio_allocated += FREAD_SIZE;
1354                                         audio = (unsigned char*)realloc(audio, audio_allocated);
1355                                 }
1356
1357
1358                                 if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d\n", 
1359                                         __LINE__,
1360                                         audio,
1361                                         audio_size);
1362
1363                                 int bytes_read = fread(audio + audio_size, 1, FREAD_SIZE, fd);
1364                                 if(debug) printf("SceneChunk::render %d bytes_read=%d\n", 
1365                                         __LINE__,
1366                                         bytes_read);
1367                                 audio_size += bytes_read;
1368                                 if(bytes_read < FREAD_SIZE)
1369                                 {
1370                                         break;
1371                                 }
1372                         }
1373
1374
1375                         pclose(fd);
1376
1377                         if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d audio_allocated=%d\n", 
1378                                 __LINE__,
1379                                 audio,
1380                                 audio_size,
1381                                 audio_allocated);
1382
1383 // Strip WAV header
1384
1385                         if(audio_size - audio_start > WAVHEADER)
1386                         {
1387 // for(int i = 0; i < 128; i++)
1388 // {
1389 // printf("%c ", audio[audio_start + i]);
1390 // if(!((i + 1) % 16)) printf("\n");
1391 // }
1392 // printf("\n");
1393 // Find header after error messages
1394                                 int header_size = WAVHEADER;
1395                                 for(int i = audio_start; i < audio_start + audio_size - 4; i++)
1396                                 {
1397                                         if(audio[i] == 'R' &&
1398                                                 audio[i + 1] == 'I' &&
1399                                                 audio[i + 2] == 'F' &&
1400                                                 audio[i + 3] == 'F')
1401                                         {
1402                                                 header_size += i - audio_start;
1403                                                 break;
1404                                         }
1405                                 }
1406
1407                                 memcpy(audio + audio_start, 
1408                                         audio + audio_start + header_size, 
1409                                         audio_size - audio_start - header_size);
1410                                 audio_size -= header_size;
1411                                 if(debug) printf("SceneChunk::render %d: audio_size=%d\n",
1412                                         __LINE__,
1413                                         audio_size);
1414                         }
1415                         
1416                         advance_samples = audio_size / 2;
1417                 }
1418                 else
1419                 {
1420                         printf("SceneChunk::render %d: Couldn't run %s: %s\n",
1421                                 __LINE__,
1422                                 command_line,
1423                                 strerror(errno));
1424                 }
1425
1426                 remove(script_path);
1427                 if(debug) printf("SceneChunk::render %d max=%f\n", 
1428                         __LINE__,
1429                         max);
1430         }
1431
1432
1433         if(text)
1434         {
1435
1436 // Truncate if ...
1437                 text_ptr = text + len - 1;
1438                 while(text_ptr > text && 
1439                         (*text_ptr == ' ' ||
1440                         *text_ptr == '\n'))
1441                         text_ptr--;
1442                 if(text_ptr > text + 3 &&
1443                         *(text_ptr - 0) == '.' &&
1444                         *(text_ptr - 1) == '.' &&
1445                         *(text_ptr - 2) == '.')
1446                 {
1447                         advance_samples -= ADVANCE_SAMPLES;
1448                         if(advance_samples < 0) advance_samples = 0;
1449                 }
1450
1451 // Calculate loudest part
1452                 max = 0;
1453                 for(int i = 0; i < audio_size; i += 2)
1454                 {
1455                         int16_t sample = audio[i] |
1456                                 (audio[i + 1] << 8);
1457                         double sample_float = fabs((double)sample / 32768);
1458                         if(sample_float > max) max = sample_float;
1459                 }
1460         }
1461 }
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475 SceneTokens::SceneTokens(FileScene *file, int cpus)
1476 {
1477         background[0] = 0;
1478         background_image = 0;
1479         timestamp = 0;
1480         this->cpus = cpus;
1481         this->file = file;
1482 //      overlayer = 0;
1483 }
1484
1485 SceneTokens::~SceneTokens()
1486 {
1487         chunks.remove_all_objects();
1488         characters.remove_all_objects();
1489         delete background_image;
1490 //      delete overlayer;
1491 }
1492
1493 SceneChar* SceneTokens::get_character(char *name)
1494 {
1495         const int debug = 0;
1496         if(debug) printf("SceneTokens::get_character %d %d\n",
1497                 __LINE__,
1498                 characters.size());
1499
1500         for(int i = 0; i < characters.size(); i++)
1501         {
1502                 if(!strcmp(characters.get(i)->name, name)) return characters.get(i);
1503         }
1504         if(debug) printf("SceneTokens::get_character %d %d\n",
1505                 __LINE__,
1506                 characters.size());
1507         
1508         SceneChar *result = new SceneChar(this);
1509         if(debug) printf("SceneTokens::get_character %d %d this=%p\n",
1510                 __LINE__,
1511                 characters.size(),
1512                 &characters);
1513
1514         characters.append(result);
1515         if(debug) printf("SceneTokens::get_character %d %d\n",
1516                 __LINE__,
1517                 characters.size());
1518
1519         strcpy(result->name, name);
1520         if(debug) printf("SceneTokens::get_character %d %d\n",
1521                 __LINE__,
1522                 characters.size());
1523
1524         if(debug) printf("SceneTokens::get_character %d %d\n",
1525                 __LINE__,
1526                 characters.size());
1527
1528         return result;
1529 }
1530
1531 SceneChar* SceneTokens::get_character(int number)
1532 {
1533         return characters.get(number);
1534 }
1535
1536 int SceneTokens::get_char_number(SceneChar *ptr)
1537 {
1538         for(int i = 0; i < characters.size(); i++)
1539                 if(ptr == characters.get(i)) return i;
1540         return 0;
1541 }
1542
1543 SceneChunk* SceneTokens::new_chunk()
1544 {
1545         SceneChunk *result = new SceneChunk(this);
1546         chunks.append(result);
1547         return result;
1548 }
1549
1550 int SceneTokens::total_chunks()
1551 {
1552         return chunks.size();
1553 }
1554
1555 int SceneTokens::total_characters()
1556 {
1557         return characters.size();
1558 }
1559
1560 SceneChunk* SceneTokens::get_chunk(int number)
1561 {
1562         return chunks.get(number);
1563 }
1564
1565 int SceneTokens::read_script(char *path)
1566 {
1567         const int debug = 0;
1568
1569         strcpy(this->path, path);
1570
1571         FILE *fd = fopen(path, "r");
1572         if(fd)
1573         {
1574 // Read 1 line
1575                 char string[BCTEXTLEN];
1576 // Current character name
1577                 char char_name[BCTEXTLEN];
1578                 SceneChar *current_char = 0;
1579                 char_name[0] = 0;
1580                 SceneChunk *current_chunk = 0;
1581                 int current_line = 0;
1582                 int i;
1583
1584                 while(!feof(fd))
1585                 {
1586                         char *result = fgets(string, BCTEXTLEN, fd);
1587                         current_line++;
1588
1589                         if(result)
1590                         {
1591                                 int len = strlen(string);
1592                                 STRIP_LINE(string)
1593
1594                                 if(debug) printf("SceneTokens::read_script %d: %s\n", 
1595                                         __LINE__, 
1596                                         string);
1597
1598 // Skip the file ID & empty lines
1599                                 if(string[0] == 0 ||
1600                                         !strncmp(string, "TEXT2MOVIE", 10))
1601                                         continue;
1602
1603                                 int got_it = 0;
1604                                 for(i = 0; i < len; i++)
1605                                 {
1606                                         if(isalnum(string[i]))
1607                                         {
1608                                                 got_it = 1;
1609                                                 i = len;
1610                                         }
1611                                 }
1612
1613                                 if(!got_it) continue;
1614
1615 // A line all in caps is a character name
1616                                 got_it = 1;
1617                                 for(i = 0; i < len; i++)
1618                                 {
1619                                         if(islower(string[i]))
1620                                         {
1621                                                 got_it = 0;
1622                                                 i = len;
1623                                         }
1624                                 }
1625
1626                                 if(got_it)
1627                                 {
1628                                         strcpy(char_name, string);
1629
1630                                         if(debug) printf("SceneTokens::read_script %d: char_name=%s\n", 
1631                                                 __LINE__, 
1632                                                 char_name);
1633
1634                                         current_char = get_character(char_name);
1635
1636                                         if(debug) printf("SceneTokens::read_script %d current_char=%p\n", 
1637                                                 __LINE__, 
1638                                                 current_char);
1639
1640 // Reset the current chunk pointer
1641                                         current_chunk = 0;
1642                                         i = len;
1643                                 }
1644                                 else
1645                                         i = 0;
1646
1647 // Certain words are commands
1648                                 for(; i < len; i++)
1649                                 {
1650                                         if(string[i] == '[' || isalnum(string[i]))
1651                                         {
1652                                                 char *command = string + i;
1653
1654                                                 STRING_PARAMETER("voice:", 1, current_char->voice)
1655                                                 else
1656                                                 STRING_PARAMETER("model:", 1, current_char->model)
1657                                                 else
1658                                                 STRING_PARAMETER("background:", 0, background)
1659                                                 else
1660 // Default is dialogue
1661                                                 {
1662                                                         if(!current_char)
1663                                                         {
1664                                                                 printf("SceneTokens::read_script %d Line %d: dialogue text but no current character\n", 
1665                                                                         __LINE__,
1666                                                                         current_line);
1667                                                         }
1668                                                         else
1669                                                         {
1670                                                                 if(!current_chunk)
1671                                                                 {
1672                                                                         current_chunk = new_chunk();
1673                                                                         current_chunk->character = current_char;
1674                                                                 }
1675
1676 // Append dialogue to current chunk
1677                                                                 current_chunk->append_text(string + i);
1678                                                         }
1679                                                         
1680                                                         i = len;
1681                                                 }
1682                                         }
1683                                 }
1684                         }
1685                 }
1686                 
1687                 
1688                 
1689                 fclose(fd);
1690
1691                 if(debug) printf("SceneTokens::read_script %d total_chunks=%d\n", 
1692                         __LINE__,
1693                         total_chunks());
1694 // Parse commands in dialogue
1695                 for(i = 0; i < total_chunks(); i++)
1696                 {
1697                         SceneChunk *chunk = get_chunk(i);
1698                         if(chunk->text)
1699                         {
1700                                 char *ptr = chunk->text;
1701                                 char *end = chunk->text + strlen(chunk->text);
1702                                 int got_text = 0;
1703                                 if(debug) printf("SceneTokens::read_script %d %s\n", __LINE__, chunk->text);
1704                                 while(ptr < end)
1705                                 {
1706 // Start of command
1707                                         if(*ptr == '[')
1708                                         {
1709                                                 if(debug) printf("SceneTokens::read_script %d [\n", __LINE__);
1710 // Split chunk
1711                                                 if(got_text)
1712                                                 {
1713                                                         SceneChunk *new_chunk = new SceneChunk(this);
1714                                                         new_chunk->character = chunk->character;
1715                                                         chunks.insert(new_chunk, i + 1);
1716                                                         
1717 // Move text from start of command to new chunk.
1718                                                         new_chunk->append_text(ptr);
1719 // Truncate current chunk
1720                                                         *ptr = 0;
1721 // Advance to next chunk
1722                                                         ptr = new_chunk->text;
1723                                                         end = new_chunk->text + strlen(new_chunk->text);
1724                                                         chunk = new_chunk;
1725                                                         i++;
1726                                                 }
1727                                                 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1728
1729 // Read command
1730                                                 while(ptr < end && 
1731                                                         (*ptr == '[' || *ptr == ' ' || *ptr == '\n')) 
1732                                                         ptr++;
1733
1734                                                 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1735
1736                                                 char *ptr2 = string;
1737                                                 char *string_end = string + BCTEXTLEN;
1738                                                 while(*ptr != ']' && 
1739                                                         *ptr != ' ' &&
1740                                                         *ptr != '\n' &&
1741                                                         ptr < end && 
1742                                                         ptr2 < string_end - 1)
1743                                                 {
1744                                                         *ptr2++ = *ptr++;
1745                                                 }
1746                                                 *ptr2 = 0;
1747                                                 if(debug) printf("SceneTokens::read_script %d command=%s\n", __LINE__, string);
1748
1749 // Convert command to code
1750                                                 if(!strcasecmp(string, "pause"))
1751                                                 {
1752                                                         chunk->command = SceneChunk::PAUSE_COMMAND;
1753                                                 }
1754                                                 else
1755                                                 {
1756 // TODO: line numbers
1757                                                         printf("SceneTokens::read_script %d: Unknown command '%s'\n", 
1758                                                                 __LINE__, 
1759                                                                 string);
1760                                                 }
1761                                                 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1762
1763 // Search for more text
1764                                                 while(ptr < end && 
1765                                                         (*ptr == ']' || *ptr == ' ' || *ptr == '\n')) 
1766                                                         ptr++;
1767
1768 // Create new chunk for rest of text
1769                                                 if(ptr < end)
1770                                                 {
1771                                                         SceneChunk *new_chunk = new SceneChunk(this);
1772                                                         new_chunk->character = chunk->character;
1773                                                         chunks.insert(new_chunk, i + 1);
1774 // Move text from end of command to new chunk.
1775                                                         new_chunk->append_text(ptr);
1776 // Parse next chunk in next iteration
1777                                                         ptr = end;
1778 //                                                      i++;
1779                                                 }
1780                                                 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1781
1782 // Truncate current chunk
1783                                                 chunk->text[0] = 0;
1784                                         }
1785                                         else
1786 // Got non whitespace
1787                                         if(*ptr != ' ' &&
1788                                                 *ptr != '\n')
1789                                         {
1790                                                 got_text = 1;
1791                                         }
1792
1793                                         ptr++;
1794                                 }
1795                         }
1796                 }
1797                 
1798                 
1799                 if(debug) dump();
1800                 return 0;
1801         }
1802
1803         printf("SceneTokens::read_script %d: %s %s\n", __LINE__, path, strerror(errno));
1804         return 1;
1805 }
1806
1807 void SceneTokens::convert_path(char *dst, char *src)
1808 {
1809 // Absolute path in src
1810         if(src[0] == '/')
1811         {
1812                 strcpy(dst, src);
1813         }
1814         else
1815 // Relative path
1816         {
1817 // Try directory of script
1818                 FileSystem fs;
1819                 fs.extract_dir(dst, path);
1820                 strcat(dst, src);
1821                 
1822                 struct stat ostat;
1823                 if(stat(dst, &ostat))
1824                 {
1825 // Try cinelerra directory
1826                         get_exe_path(dst);
1827                         strcat(dst, ASSET_PATH);
1828                         strcat(dst, src);
1829 //printf("SceneTokens::convert_path %d %s\n", __LINE__, dst);
1830                 }
1831         }
1832 }
1833
1834 VFrame* SceneTokens::load_image(char *path)
1835 {
1836         VFrame *result = 0;
1837         char complete_path[BCTEXTLEN];
1838         convert_path(complete_path, path);
1839
1840         int64_t size = FileSystem::get_size(complete_path);
1841         if(!size)
1842         {
1843                 printf("SceneTokens::load_image %d: Couldn't open %s\n",
1844                         __LINE__,
1845                         complete_path);
1846                 return 0;
1847         }
1848
1849         unsigned char *data = new unsigned char[size + 4];
1850         FILE *fd = fopen(complete_path, "r");
1851         (void)fread(data + 4, 1, size, fd);
1852         data[0] = (size >> 24) & 0xff;
1853         data[1] = (size >> 16) & 0xff;
1854         data[2] = (size >> 8) & 0xff;
1855         data[3] = size & 0xff;
1856         result = new VFramePng(data, 1.);
1857         delete [] data;
1858
1859
1860         if(!BC_CModels::has_alpha(result->get_color_model()) )
1861                 printf("SceneTokens::load_image %d: image %s has no alpha channel\n", 
1862                         __LINE__,
1863                         path);
1864         return result;
1865 }
1866
1867 void SceneTokens::render_background(SceneGraph *scene)
1868 {
1869 // Decompress background image
1870         if(!background_image)
1871         {
1872                 background_image = load_image(background);
1873         }
1874         
1875         if(background_image)
1876         {
1877                 SceneNode *node = new SceneNode("background");
1878                 scene->append(node);
1879                 node->image = background_image;
1880                 node->x = 0;
1881                 node->y = 0;
1882                 node->sx = 1;
1883                 node->sy = 1;
1884         }
1885 }
1886
1887 int SceneTokens::get_memory_usage()
1888 {
1889         int total = 0;
1890         if(background_image) total += background_image->get_memory_usage();
1891         for(int i = 0; i < total_chunks(); i++)
1892         {
1893                 total += get_chunk(i)->get_memory_usage();
1894         }
1895         for(int i = 0; i < total_characters(); i++)
1896         {
1897                 total += get_character(i)->get_memory_usage();
1898         }
1899         return total;
1900 }
1901
1902
1903 void SceneTokens::dump()
1904 {
1905         printf("SceneTokens::dump %d background=%s\n", __LINE__, background);
1906         for(int i = 0; i < characters.size(); i++)
1907         {
1908                 characters.get(i)->dump();
1909         }
1910
1911         
1912         for(int i = 0; i < chunks.size(); i++)
1913         {
1914                 chunks.get(i)->dump();
1915         }
1916 }
1917
1918
1919
1920