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