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