3 * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
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.
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.
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
24 #include "bcsignals.h"
27 #include "filescene.h"
28 #include "filesystem.h"
30 #include "scenegraph.h"
46 // Paths relative to the exe path
47 #ifdef HAVE_FESTIVAL_BUILTIN
48 #define FESTIVAL_PATH "/festival"
49 #define FESTIVAL_LIB_PATH "/lib/"
51 #define ASSET_PATH "/models/"
52 #define FREAD_SIZE 0x10000
54 #define FESTIVAL_SAMPLERATE 16000
55 #define PAUSE_SAMPLES FESTIVAL_SAMPLERATE
56 // Amount to truncate if ...
57 #define ADVANCE_SAMPLES FESTIVAL_SAMPLERATE
59 // Maximum characters in a line of dialog is limited by festival
60 #define MAX_CHARS 65536
62 #define STRIP_LINE(string) \
63 /* Strip linefeeds */ \
64 while(len > 0 && (string[len - 1] == '\n' || \
65 string[len - 1] == ' ')) \
67 string[len - 1] = 0; \
71 /* Strip comments */ \
72 for(i = 0; i < len; i++) \
74 if(string[i] == '#') \
84 #define STRING_PARAMETER(title, need_char, dst) \
85 if(!strncmp(command, title, strlen(title))) \
87 /* advance to argument */ \
89 while(string[i] != 0 && string[i] == ' ') \
92 if(current_char || !need_char) \
94 /* printf("STRING_PARAMETER %s %s %p\n", title, string + i, dst); */ \
95 strcpy(dst, string + i); \
99 printf("FileScene::read_script %d Line %d: %s but no current character\n", \
108 static int read_parameter(char *string,
116 char *command = string + *i;
119 if(!strncmp(command, title, strlen(title)))
123 for(int j = 0; j < 4; j++)
125 /* skip to start of argument */
126 while(string[*i] != 0 && string[*i] == ' ')
131 arg_text[j] = string + *i;
132 while(string[*i] != 0 && string[*i] != ' ')
141 // printf("read_parameter %d %s %s %s %s\n",
152 char *ptr1 = dst_string;
153 char *ptr2 = arg_text[0];
154 while(*ptr2 != 0 && *ptr2 != ' ')
161 *dst_float0 = atof(arg_text[0]);
169 *dst_float1 = atof(arg_text[1]);
177 *dst_float2 = atof(arg_text[2]);
199 FileScene::FileScene(Asset *asset, File *file)
200 : FileBase(asset, file)
203 strncpy(exec_path, File::get_cinlib_path(), sizeof(exec_path));
207 FileScene::~FileScene()
214 int FileScene::open_file(int rd, int wr)
216 // Load the script to get character count
222 asset->format = FILE_SCENE;
223 asset->video_data = 1;
224 // Should be set in the scene file
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;
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;
249 int FileScene::close_file()
252 delete [] audio_temp;
255 FileBase::close_file();
260 int FileScene::check_sig(Asset *asset, char *test)
262 if(!strncmp(test, "TEXT2MOVIE", 10)) return 1;
269 int FileScene::set_video_position(int64_t x)
275 int FileScene::set_audio_position(int64_t x)
281 int FileScene::read_frame(VFrame *frame)
283 // Everything is timed based on speech, so render the audio for this frame.
287 frame->clear_frame();
288 int64_t audio_position1 = (int64_t)(file->current_frame *
291 int64_t audio_position2 = (int64_t)(audio_position1 +
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,
302 render_chunks(audio_position1,
303 audio_position2 - audio_position1,
305 if(!script) return 1;
306 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
311 // Determine lip position from amplitude
313 for(int i = 0; i < audio_position2 - audio_position1; i++)
315 double sample_float = fabs((double)audio_temp[i] / 32768);
316 if(sample_float > accum) accum = sample_float;
318 if(debug) printf("FileScene::read_frame %d accum=%f\n", __LINE__, accum);
321 for(int i = 0; i < script->total_characters(); i++)
323 SceneChar *character = script->get_character(i);
324 character->current_camera = SceneChar::CAMERA_WIDE;
325 character->is_speeking = 0;
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;
335 i < script->total_chunks() && current_sample < audio_position2;
338 SceneChunk *chunk = script->get_chunk(i);
339 int samples = chunk->audio_size / 2;
342 if(audio_position1 >= current_sample &&
343 audio_position1 < current_sample + samples)
345 speeking_character = chunk->character;
346 speeking_character->max = chunk->max;
347 chunk->character->is_speeking = 1;
348 chunk_sample = audio_position1 - current_sample;
352 if(!chunk->character->is_speeking)
354 chunk->character->increment_camera();
357 current_sample += chunk->advance_samples;
362 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
365 // Store component placement in a scene graph.
367 //SceneNode *speeking_node = 0;
369 // Scale for the entire scene
370 SceneCamera *camera = new SceneCamera;
371 scene.append_camera(camera);
376 if(script->background[0])
378 script->render_background(&scene);
381 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
384 for(int i = 0; i < script->total_characters(); i++)
386 SceneChar *character = script->get_character(i);
388 character->read_model();
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;
395 SceneNode *head_node = new SceneNode("head");
397 SceneNode *body_node = new SceneNode("body");
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++)
403 if(debug) printf("FileScene::read_frame %d j=%d head_order=%d body_order=%d\n",
406 character->head_order,
407 character->body_order);
408 if(j == character->head_order) character_node->append(head_node);
410 if(j == character->body_order) character_node->append(body_node);
413 SceneNode *eye_node = 0;
414 if(character->eyes.size())
416 eye_node = new SceneNode("eyes");
417 head_node->append(eye_node);
420 SceneNode *mouth_node = new SceneNode("mouth");
421 head_node->append(mouth_node);
423 // Logical character placement
424 switch(script->total_characters())
427 if(speeking_character == character &&
428 speeking_character->current_camera == SceneChar::CAMERA_CU)
430 camera->at_x = asset->width / 4;
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);
442 character_node->x = 0;
443 character_node->y = (int)(asset->height -
444 character->h * character->scale);
446 if(speeking_character == character &&
447 speeking_character->current_camera == SceneChar::CAMERA_CU)
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 -
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);
458 if(character->faces_left)
460 character_node->flip = 1;
461 character_node->x = asset->width -
462 character->w * character->scale;
468 character_node->x = (int)(asset->width -
469 character->w * character->scale);
470 character_node->y = (int)(asset->height -
471 character->h * character->scale);
473 if(speeking_character == character &&
474 speeking_character->current_camera == SceneChar::CAMERA_CU)
476 camera->at_x = character_node->x +
477 character->head->x * character->scale -
479 CLAMP(camera->at_x, 0, asset->width / 2);
482 if(!character->faces_left)
484 character_node->flip = 1;
485 character_node->x = 0;
493 character_node->x = 0;
494 character_node->y = (int)(asset->height -
495 character->h * character->scale);
496 if(character->faces_left)
498 character_node->flip = 1;
504 if(speeking_character == character &&
505 speeking_character->current_camera == SceneChar::CAMERA_CU)
507 camera->at_x = asset->width / 4;
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)
515 character_node->flip = 1;
520 if(speeking_character == character &&
521 speeking_character->current_camera == SceneChar::CAMERA_CU)
523 camera->at_x = asset->width / 2;
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)
531 character_node->flip = 1;
532 character_node->x = 0;
539 if(character_node->y < 0) character_node->y = 0;
541 // Add remaining parts
542 body_node->copy_ref(character->body);
543 head_node->copy_ref(character->head);
545 // Speeker head rotation
546 if(speeking_character == character)
548 //speeking_node = character_node;
550 int head_time = (chunk_sample / asset->sample_rate / 2) % 2;
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",
562 if(anim_position < anim_length)
563 head_node->ry = -5 * anim_position / anim_length;
565 if(anim_position > 1.0 - anim_length)
566 head_node->ry = -5 * (1.0 - anim_position) / anim_length;
572 //head_node->ry = -5;
576 if(character->eyes.size())
578 if(modf((file->current_frame / asset->frame_rate + script->get_char_number(character)) / 5, &intpart) <=
580 file->current_frame / asset->frame_rate > 1)
582 eye_node->copy_ref(character->eyes.get(0));
585 eye_node->copy_ref(character->eyes.get(1));
588 // Compute the mouth image
590 if(character->is_speeking && character->max > 0)
592 fraction = (int)(accum * character->mouths.size() / character->max);
593 if(fraction >= character->mouths.size())
594 fraction = character->mouths.size() - 1;
597 mouth_node->copy_ref(character->mouths.get(fraction));
600 // camera->scale = 2;
601 // camera->at_x = asset->width / 2;
605 if(speeking_character == character &&
606 speeking_character->current_camera == SceneChar::CAMERA_CU)
608 // If closeup, increase scene scale
616 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
620 // Render scene graph
621 scene.render(frame, file->cpus);
625 if(debug) printf("FileScene::read_frame %d\n", __LINE__);
632 int FileScene::read_samples(double *buffer, int64_t len)
635 // Everything is timed based on speech, so we have to do this for video, too.
636 render_chunks(file->current_sample, len, 0);
641 // Convert temp to output
642 for(int i = 0; i < len; i++)
644 buffer[i] = (double)audio_temp[i] / 32768;
651 int64_t FileScene::get_memory_usage()
654 int total = 0x100000;
657 total += script->get_memory_usage();
666 int FileScene::get_best_colormodel(Asset *asset, int driver)
672 int FileScene::colormodel_supported(int colormodel)
678 int FileScene::can_copy_from(Asset *asset, int64_t position)
684 int FileScene::reset_parameters_derived()
696 void FileScene::render_chunks(int64_t start_position,
700 int64_t end_position = start_position + len;
704 if(debug) printf("FileScene::render_chunks %d start_position=%jd"
705 " len=%jd\n", __LINE__, start_position, len);
712 if(debug) PRINT_TRACE
714 // Reallocate temp buffer
715 if(len > temp_allocated)
717 delete [] audio_temp;
718 audio_temp = new int16_t[len];
719 temp_allocated = len;
721 bzero(audio_temp, sizeof(int16_t) * len);
723 if(debug) PRINT_TRACE
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++)
734 SceneChunk *chunk = script->get_chunk(i);
736 if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
738 // If extent of audio output hasn't been calculated, render it
739 if(!chunk->audio_size)
741 if(debug) printf("FileScene::render_chunks %d i=%d\n", __LINE__, i);
745 // Dialog chunk is inside output buffer
746 if(current_sample + chunk->audio_size / 2 > start_position &&
747 current_sample < end_position)
749 if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
751 // If no audio output exists, render it
754 if(debug) printf("FileScene::render_chunks %d rerendering audio\n", __LINE__);
758 if(debug) printf("FileScene::render_chunks %d: Using \"%s\" samples=%d\n",
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);
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;
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);
778 dst_offset -= src_offset;
779 src_len += src_offset;
783 if(dst_offset + src_len > len)
785 src_len = len - dst_offset;
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);
792 // Transfer if right channel
794 file->current_channel == script->get_char_number(chunk->character))
796 for(int j = 0; j < src_len; j++)
798 audio_temp[dst_offset + j] =
799 chunk->audio[(src_offset + j) * 2] |
800 (chunk->audio[(src_offset + j) * 2 + 1] << 8);
804 if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
808 current_sample += chunk->advance_samples;
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++)
815 SceneChunk *chunk = script->get_chunk(i);
816 if(!chunk->used && chunk->audio)
818 if(debug) printf("FileScene::render_chunks %d erasing unused audio\n", __LINE__);
819 delete [] chunk->audio;
821 chunk->audio_allocated = 0;
824 if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
831 int FileScene::read_script()
835 if(stat(asset->path, &ostat))
837 printf("FileScene::read_script %d: %s\n", __LINE__, strerror(errno));
844 if(ostat.st_mtime == script->timestamp)
846 if(debug) printf("FileScene::read_script %d: script unchanged\n", __LINE__);
855 if(!script) script = new SceneTokens(this, file->cpus);
856 script->timestamp = ostat.st_mtime;
858 script->read_script(asset->path);
877 SceneChar::SceneChar(SceneTokens *script)
879 this->script = script;
886 sprintf(model, "heroine01");
887 current_camera = CAMERA_WIDE;
894 SceneChar::~SceneChar()
896 mouths.remove_all_objects();
897 eyes.remove_all_objects();
903 void SceneChar::increment_camera()
906 if(current_camera >= CAMERA_TOTAL)
911 int SceneChar::read_model()
913 // Read descriptor file
915 int current_line = 0;
918 int current_order = 0;
919 char path[BCTEXTLEN];
924 script->convert_path(path, model);
925 FILE *fd = fopen(path, "r");
932 char string[BCTEXTLEN];
933 char string2[BCTEXTLEN];
937 char *result = fgets(string, BCTEXTLEN, fd);
941 int len = strlen(string);
945 if(debug) printf("SceneChar::read_model %d: %s\n",
949 for(i = 0; i < len; i++)
951 if(isalnum(string[i]))
955 if(read_parameter(string,
965 if(read_parameter(string,
975 if(read_parameter(string,
984 body = new SceneNode(script->load_image(string2), 1, x, y);
985 body_order = current_order++;
988 if(read_parameter(string,
997 head = new SceneNode(script->load_image(string2), 1, x, y);
998 head_order = current_order++;
1001 if(read_parameter(string,
1011 mouths.append(mouth = new SceneNode(script->load_image(string2), 1, x, y));
1012 // Make coordinates relative to head
1015 mouth->x -= head->x;
1016 mouth->y -= head->y;
1020 if(read_parameter(string,
1030 eyes.append(eye = new SceneNode(script->load_image(string2), 1, x, y));
1031 // Make coordinates relative to head
1039 if(read_parameter(string,
1050 if(read_parameter(string,
1073 printf("SceneChar::read_model %d: %s %s\n", __LINE__, path, strerror(errno));
1077 int SceneChar::get_memory_usage()
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();
1087 void SceneChar::dump()
1089 printf("SceneChar::dump %d: %p name=%s voice=%s model=%s body=%p eyes=%d mouths=%d\n",
1098 printf("SceneChar::dump %d: w=%f h=%f\n", __LINE__, w, h);
1101 for(int i = 0; i < mouths.size(); i++)
1103 SceneNode *node = mouths.get(i);
1104 printf(" mouth=%p x=%f y=%f\n", node, node->x, node->y);
1107 for(int i = 0; i < eyes.size(); i++)
1109 SceneNode *node = eyes.get(i);
1110 printf(" eyes=%p x=%f y=%f\n", node, node->x, node->y);
1122 // Dialog from a single character
1123 SceneChunk::SceneChunk(SceneTokens *script)
1129 audio_allocated = 0;
1130 advance_samples = 0;
1133 command = NO_COMMAND;
1134 this->script = script;
1137 SceneChunk::~SceneChunk()
1143 void SceneChunk::dump()
1145 printf("SceneChunk::dump %d: character=%s command=%d text=%s\n",
1150 printf("SceneChunk::dump %d: audio=%p audio_size=%d advance_samples=%d\n",
1157 int SceneChunk::get_memory_usage()
1159 return audio_allocated;
1163 void SceneChunk::append_text(char *new_text)
1165 char string[BCTEXTLEN];
1170 int len = strlen(new_text);
1171 for(int i = 0; i < len; i++)
1173 // if(new_text[i] == '"')
1176 // if(new_text[i] == '\'')
1183 *ptr++ = new_text[i];
1187 int len2 = strlen(string);
1190 int len1 = strlen(text);
1194 // if(len1 > 0 && isalnum(text[len1 - 1]))
1195 if(len1 > 0 && text[len1 - 1] != ' ')
1201 text = (char*)realloc(text, len1 + len2 + len3);
1212 text = new char[len2 + 1];
1216 strcat(text, string);
1222 void SceneChunk::render()
1224 const int debug = 0;
1226 char command_line[BCTEXTLEN];
1227 char string2[MAX_CHARS];
1228 char script_path[BCTEXTLEN];
1230 //int total_args = 0;
1231 if(text) len = strlen(text);
1232 char *text_end = text + len;
1233 char *text_ptr = text;
1239 printf("SceneChunk::render %d: no character defined.\n", __LINE__);
1244 printf("SceneChunk::render %d: text '%s' exceeds festival's maximum line length of %d chars.\n",
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);
1263 while(text && text_ptr < text_end)
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++)
1269 *ptr++ = *text_ptr++;
1274 // Rewind to white space if still more text
1275 if(text_ptr < text_end)
1279 while(*text_ptr != ' ' &&
1280 *text_ptr != '\n' &&
1288 // Truncate string2 at white space
1290 // If no white space, abort.
1291 if(text_ptr <= text)
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");
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,
1310 sprintf(command_line, "festival -b %s", script_path);
1314 // The maximum text length is limited with the command line
1318 "(set! text (Utterance Text \"%s\"))\n"
1320 "(utt.save.wave text \"-\")",
1328 printf("SceneChunk::render %d %s\n",
1332 FILE *script_fd = fopen(script_path, "r");
1333 while(!feof(script_fd))
1334 fputc(fgetc(script_fd), stdout);
1339 // popen only does half duplex
1340 FILE *fd = popen(command_line, "r");
1345 int audio_start = audio_size;
1348 if(debug) printf("SceneChunk::render %d\n",
1352 if(debug) printf("SceneChunk::render %d\n",
1354 if(audio_size + FREAD_SIZE > audio_allocated)
1356 audio_allocated += FREAD_SIZE;
1357 audio = (unsigned char*)realloc(audio, audio_allocated);
1361 if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d\n",
1366 int bytes_read = fread(audio + audio_size, 1, FREAD_SIZE, fd);
1367 if(debug) printf("SceneChunk::render %d bytes_read=%d\n",
1370 audio_size += bytes_read;
1371 if(bytes_read < FREAD_SIZE)
1380 if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d audio_allocated=%d\n",
1388 if(audio_size - audio_start > WAVHEADER)
1390 // for(int i = 0; i < 128; i++)
1392 // printf("%c ", audio[audio_start + i]);
1393 // if(!((i + 1) % 16)) 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++)
1400 if(audio[i] == 'R' &&
1401 audio[i + 1] == 'I' &&
1402 audio[i + 2] == 'F' &&
1403 audio[i + 3] == 'F')
1405 header_size += i - audio_start;
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",
1419 advance_samples = audio_size / 2;
1423 printf("SceneChunk::render %d: Couldn't run %s: %s\n",
1429 remove(script_path);
1430 if(debug) printf("SceneChunk::render %d max=%f\n",
1440 text_ptr = text + len - 1;
1441 while(text_ptr > text &&
1442 (*text_ptr == ' ' ||
1445 if(text_ptr > text + 3 &&
1446 *(text_ptr - 0) == '.' &&
1447 *(text_ptr - 1) == '.' &&
1448 *(text_ptr - 2) == '.')
1450 advance_samples -= ADVANCE_SAMPLES;
1451 if(advance_samples < 0) advance_samples = 0;
1454 // Calculate loudest part
1456 for(int i = 0; i < audio_size; i += 2)
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;
1478 SceneTokens::SceneTokens(FileScene *file, int cpus)
1481 background_image = 0;
1488 SceneTokens::~SceneTokens()
1490 chunks.remove_all_objects();
1491 characters.remove_all_objects();
1492 delete background_image;
1493 // delete overlayer;
1496 SceneChar* SceneTokens::get_character(char *name)
1498 const int debug = 0;
1499 if(debug) printf("SceneTokens::get_character %d %d\n",
1503 for(int i = 0; i < characters.size(); i++)
1505 if(!strcmp(characters.get(i)->name, name)) return characters.get(i);
1507 if(debug) printf("SceneTokens::get_character %d %d\n",
1511 SceneChar *result = new SceneChar(this);
1512 if(debug) printf("SceneTokens::get_character %d %d this=%p\n",
1517 characters.append(result);
1518 if(debug) printf("SceneTokens::get_character %d %d\n",
1522 strcpy(result->name, name);
1523 if(debug) printf("SceneTokens::get_character %d %d\n",
1527 if(debug) printf("SceneTokens::get_character %d %d\n",
1534 SceneChar* SceneTokens::get_character(int number)
1536 return characters.get(number);
1539 int SceneTokens::get_char_number(SceneChar *ptr)
1541 for(int i = 0; i < characters.size(); i++)
1542 if(ptr == characters.get(i)) return i;
1546 SceneChunk* SceneTokens::new_chunk()
1548 SceneChunk *result = new SceneChunk(this);
1549 chunks.append(result);
1553 int SceneTokens::total_chunks()
1555 return chunks.size();
1558 int SceneTokens::total_characters()
1560 return characters.size();
1563 SceneChunk* SceneTokens::get_chunk(int number)
1565 return chunks.get(number);
1568 int SceneTokens::read_script(char *path)
1570 const int debug = 0;
1572 strcpy(this->path, path);
1574 FILE *fd = fopen(path, "r");
1578 char string[BCTEXTLEN];
1579 // Current character name
1580 char char_name[BCTEXTLEN];
1581 SceneChar *current_char = 0;
1583 SceneChunk *current_chunk = 0;
1584 int current_line = 0;
1589 char *result = fgets(string, BCTEXTLEN, fd);
1594 int len = strlen(string);
1597 if(debug) printf("SceneTokens::read_script %d: %s\n",
1601 // Skip the file ID & empty lines
1602 if(string[0] == 0 ||
1603 !strncmp(string, "TEXT2MOVIE", 10))
1607 for(i = 0; i < len; i++)
1609 if(isalnum(string[i]))
1616 if(!got_it) continue;
1618 // A line all in caps is a character name
1620 for(i = 0; i < len; i++)
1622 if(islower(string[i]))
1631 strcpy(char_name, string);
1633 if(debug) printf("SceneTokens::read_script %d: char_name=%s\n",
1637 current_char = get_character(char_name);
1639 if(debug) printf("SceneTokens::read_script %d current_char=%p\n",
1643 // Reset the current chunk pointer
1650 // Certain words are commands
1653 if(string[i] == '[' || isalnum(string[i]))
1655 char *command = string + i;
1657 STRING_PARAMETER("voice:", 1, current_char->voice)
1659 STRING_PARAMETER("model:", 1, current_char->model)
1661 STRING_PARAMETER("background:", 0, background)
1663 // Default is dialogue
1667 printf("SceneTokens::read_script %d Line %d: dialogue text but no current character\n",
1675 current_chunk = new_chunk();
1676 current_chunk->character = current_char;
1679 // Append dialogue to current chunk
1680 current_chunk->append_text(string + i);
1694 if(debug) printf("SceneTokens::read_script %d total_chunks=%d\n",
1697 // Parse commands in dialogue
1698 for(i = 0; i < total_chunks(); i++)
1700 SceneChunk *chunk = get_chunk(i);
1703 char *ptr = chunk->text;
1704 char *end = chunk->text + strlen(chunk->text);
1706 if(debug) printf("SceneTokens::read_script %d %s\n", __LINE__, chunk->text);
1712 if(debug) printf("SceneTokens::read_script %d [\n", __LINE__);
1716 SceneChunk *new_chunk = new SceneChunk(this);
1717 new_chunk->character = chunk->character;
1718 chunks.insert(new_chunk, i + 1);
1720 // Move text from start of command to new chunk.
1721 new_chunk->append_text(ptr);
1722 // Truncate current chunk
1724 // Advance to next chunk
1725 ptr = new_chunk->text;
1726 end = new_chunk->text + strlen(new_chunk->text);
1730 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1734 (*ptr == '[' || *ptr == ' ' || *ptr == '\n'))
1737 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1739 char *ptr2 = string;
1740 char *string_end = string + BCTEXTLEN;
1741 while(*ptr != ']' &&
1745 ptr2 < string_end - 1)
1750 if(debug) printf("SceneTokens::read_script %d command=%s\n", __LINE__, string);
1752 // Convert command to code
1753 if(!strcasecmp(string, "pause"))
1755 chunk->command = SceneChunk::PAUSE_COMMAND;
1759 // TODO: line numbers
1760 printf("SceneTokens::read_script %d: Unknown command '%s'\n",
1764 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1766 // Search for more text
1768 (*ptr == ']' || *ptr == ' ' || *ptr == '\n'))
1771 // Create new chunk for rest of text
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
1783 if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
1785 // Truncate current chunk
1789 // Got non whitespace
1806 printf("SceneTokens::read_script %d: %s %s\n", __LINE__, path, strerror(errno));
1810 void SceneTokens::convert_path(char *dst, char *src)
1812 // Absolute path in src
1820 // Try directory of script
1822 fs.extract_dir(dst, path);
1826 if(stat(dst, &ostat))
1828 // Try cinelerra directory
1829 strcpy(dst, File::get_cindat_path());
1830 strcat(dst, ASSET_PATH);
1832 //printf("SceneTokens::convert_path %d %s\n", __LINE__, dst);
1837 VFrame* SceneTokens::load_image(char *path)
1840 char complete_path[BCTEXTLEN];
1841 convert_path(complete_path, path);
1843 int64_t size = FileSystem::get_size(complete_path);
1846 printf("SceneTokens::load_image %d: Couldn't open %s\n",
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.);
1863 if(!BC_CModels::has_alpha(result->get_color_model()) )
1864 printf("SceneTokens::load_image %d: image %s has no alpha channel\n",
1870 void SceneTokens::render_background(SceneGraph *scene)
1872 // Decompress background image
1873 if(!background_image)
1875 background_image = load_image(background);
1878 if(background_image)
1880 SceneNode *node = new SceneNode("background");
1881 scene->append(node);
1882 node->image = background_image;
1890 int SceneTokens::get_memory_usage()
1893 if(background_image) total += background_image->get_memory_usage();
1894 for(int i = 0; i < total_chunks(); i++)
1896 total += get_chunk(i)->get_memory_usage();
1898 for(int i = 0; i < total_characters(); i++)
1900 total += get_character(i)->get_memory_usage();
1906 void SceneTokens::dump()
1908 printf("SceneTokens::dump %d background=%s\n", __LINE__, background);
1909 for(int i = 0; i < characters.size(); i++)
1911 characters.get(i)->dump();
1915 for(int i = 0; i < chunks.size(); i++)
1917 chunks.get(i)->dump();