+++ /dev/null
-/*
- * CINELERRA
- * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-
-
-#include "asset.h"
-#include "bcsignals.h"
-#include "clip.h"
-#include "file.h"
-#include "filescene.h"
-#include "filesystem.h"
-#include "format.inc"
-#include "libmjpeg.h"
-#include "scenegraph.h"
-
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-
-extern "C"
-{
-#include <uuid.h>
-}
-
-
-// Paths relative to the exe path
-#define FESTIVAL_PATH "/festival"
-#define FESTIVAL_LIB_PATH "/lib/"
-#define ASSET_PATH "/models/"
-#define FREAD_SIZE 0x10000
-#define WAVHEADER 44
-#define FESTIVAL_SAMPLERATE 16000
-#define PAUSE_SAMPLES FESTIVAL_SAMPLERATE
-// Amount to truncate if ...
-#define ADVANCE_SAMPLES FESTIVAL_SAMPLERATE
-
-// Maximum characters in a line of dialog is limited by festival
-#define MAX_CHARS 65536
-
-#define STRIP_LINE(string) \
-/* Strip linefeeds */ \
- while(len > 0 && (string[len - 1] == '\n' || \
- string[len - 1] == ' ')) \
- { \
- string[len - 1] = 0; \
- len--; \
- } \
- \
-/* Strip comments */ \
- for(i = 0; i < len; i++) \
- { \
- if(string[i] == '#') \
- { \
- string[i] = 0; \
- i = len; \
- } \
- }
-
-
-
-
-#define STRING_PARAMETER(title, need_char, dst) \
-if(!strncmp(command, title, strlen(title))) \
-{ \
-/* advance to argument */ \
- i += strlen(title); \
- while(string[i] != 0 && string[i] == ' ') \
- i++; \
- \
- if(current_char || !need_char) \
- { \
-/* printf("STRING_PARAMETER %s %s %p\n", title, string + i, dst); */ \
- strcpy(dst, string + i); \
- } \
- else \
- { \
- printf("FileScene::read_script %d Line %d: %s but no current character\n", \
- __LINE__, \
- current_line, \
- title); \
- } \
- \
- i = len; \
-}
-
-static int read_parameter(char *string,
- int *i,
- const char *title,
- char *dst_string,
- float *dst_float0,
- float *dst_float1,
- float *dst_float2)
-{
- char *command = string + *i;
- char *arg_text[3];
-
- if(!strncmp(command, title, strlen(title)))
- {
- *i += strlen(title);
-
- for(int j = 0; j < 4; j++)
- {
-/* skip to start of argument */
- while(string[*i] != 0 && string[*i] == ' ')
- (*i)++;
-
- if(string[*i] != 0)
- {
- arg_text[j] = string + *i;
- while(string[*i] != 0 && string[*i] != ' ')
- (*i)++;
- }
- else
- {
- arg_text[j] = 0;
- }
- }
-
-// printf("read_parameter %d %s %s %s %s\n",
-// __LINE__,
-// title,
-// arg_text[0],
-// arg_text[1],
-// arg_text[2]);
-
- if(arg_text[0])
- {
- if(dst_string)
- {
- char *ptr1 = dst_string;
- char *ptr2 = arg_text[0];
- while(*ptr2 != 0 && *ptr2 != ' ')
- *ptr1++ = *ptr2++;
- *ptr1 = 0;
- }
- else
- if(dst_float0)
- {
- *dst_float0 = atof(arg_text[0]);
- }
- }
-
- if(arg_text[1])
- {
- if(dst_float1)
- {
- *dst_float1 = atof(arg_text[1]);
- }
- }
-
- if(arg_text[2])
- {
- if(dst_float2)
- {
- *dst_float2 = atof(arg_text[2]);
- }
- }
-
- return 1;
- }
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-FileScene::FileScene(Asset *asset, File *file)
- : FileBase(asset, file)
-{
- reset_parameters();
- get_exe_path(exe_path);
-}
-
-
-FileScene::~FileScene()
-{
- close_file();
-}
-
-
-
-int FileScene::open_file(int rd, int wr)
-{
-// Load the script to get character count
- read_script();
-
-
-
-// Set asset format
- asset->format = FILE_SCENE;
- asset->video_data = 1;
-// Should be set in the scene file
- asset->layers = 1;
- asset->width = 1280;
- asset->height = 720;
-// Dictated by character animations
- asset->frame_rate = (double)30000 / 1001;
-// Some arbitrary default length.
-// Hard to calculate without rendering it.
- asset->video_length = -1;
-
-
-
- asset->audio_data = 1;
-// The speech synthesizer outputs different samplerates depending on the speaker.
-// The heroine voices are all 16khz
- asset->sample_rate = FESTIVAL_SAMPLERATE;
-// Mono voice placement for now
-// Maybe a different track for each voice in the future or 3D positioning
- asset->channels = script->total_characters();
- asset->audio_length = -1;
-
-
- return 0;
-}
-
-int FileScene::close_file()
-{
- delete script;
- delete [] audio_temp;
-// delete overlayer;
-// delete affine;
- FileBase::close_file();
- return 0;
-}
-
-
-int FileScene::check_sig(Asset *asset, char *test)
-{
- if(!strncmp(test, "TEXT2MOVIE", 10)) return 1;
-
- return 0;
-}
-
-
-
-int FileScene::set_video_position(int64_t x)
-{
- return 0;
-}
-
-
-int FileScene::set_audio_position(int64_t x)
-{
- return 0;
-}
-
-
-int FileScene::read_frame(VFrame *frame)
-{
-// Everything is timed based on speech, so render the audio for this frame.
- const int debug = 0;
-
-
- frame->clear_frame();
- int64_t audio_position1 = (int64_t)(file->current_frame *
- asset->sample_rate /
- asset->frame_rate);
- int64_t audio_position2 = (int64_t)(audio_position1 +
- asset->sample_rate /
- asset->frame_rate);
-
- if(debug) printf("FileScene::read_frame %d frame=" _LD
- " frame_rate=%f sample_rate=%d audio_position1=" _LD
- " audio_position2=" _LD "\n", __LINE__,
- file->current_frame, asset->frame_rate,
- asset->sample_rate, audio_position1,
- audio_position2);
-
- render_chunks(audio_position1,
- audio_position2 - audio_position1,
- 1);
- if(!script) return 1;
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
-
-// script->dump();
-
-
-// Determine lip position from amplitude
- double accum = 0;
- for(int i = 0; i < audio_position2 - audio_position1; i++)
- {
- double sample_float = fabs((double)audio_temp[i] / 32768);
- if(sample_float > accum) accum = sample_float;
- }
- if(debug) printf("FileScene::read_frame %d accum=%f\n", __LINE__, accum);
-
-// Reset cameras
- for(int i = 0; i < script->total_characters(); i++)
- {
- SceneChar *character = script->get_character(i);
- character->current_camera = SceneChar::CAMERA_WIDE;
- character->is_speeking = 0;
- character->max = 0;
- }
-
-// Now determine most recent character which is speaking from the sample times.
- int64_t current_sample = 0;
- SceneChar *speeking_character = 0;
-// Sample relative to start of chunk
- int64_t chunk_sample = 0;
- for(int i = 0;
- i < script->total_chunks() && current_sample < audio_position2;
- i++)
- {
- SceneChunk *chunk = script->get_chunk(i);
- int samples = chunk->audio_size / 2;
-
-
- if(audio_position1 >= current_sample &&
- audio_position1 < current_sample + samples)
- {
- speeking_character = chunk->character;
- speeking_character->max = chunk->max;
- chunk->character->is_speeking = 1;
- chunk_sample = audio_position1 - current_sample;
-// break;
- }
- else
- if(!chunk->character->is_speeking)
- {
- chunk->character->increment_camera();
- }
-
- current_sample += chunk->advance_samples;
- }
-
-
-
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
-
-// Render the scene.
-// Store component placement in a scene graph.
- SceneGraph scene;
- //SceneNode *speeking_node = 0;
-
-// Scale for the entire scene
- SceneCamera *camera = new SceneCamera;
- scene.append_camera(camera);
- camera->at_x = 0;
- camera->at_y = 0;
-
-// Render background
- if(script->background[0])
- {
- script->render_background(&scene);
- }
-
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
-
-// Render characters
- for(int i = 0; i < script->total_characters(); i++)
- {
- SceneChar *character = script->get_character(i);
-
- character->read_model();
-
-// Nodes for the character
- SceneNode *character_node = new SceneNode(character->name);
- scene.append(character_node);
- character_node->sx = character_node->sy = character->scale;
-
- SceneNode *head_node = new SceneNode("head");
-
- SceneNode *body_node = new SceneNode("body");
-
-// Render in the order listed in the file
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
- for(int j = 0; j < 2; j++)
- {
- if(debug) printf("FileScene::read_frame %d j=%d head_order=%d body_order=%d\n",
- __LINE__,
- j,
- character->head_order,
- character->body_order);
- if(j == character->head_order) character_node->append(head_node);
- else
- if(j == character->body_order) character_node->append(body_node);
- }
-
- SceneNode *eye_node = 0;
- if(character->eyes.size())
- {
- eye_node = new SceneNode("eyes");
- head_node->append(eye_node);
- }
-
- SceneNode *mouth_node = new SceneNode("mouth");
- head_node->append(mouth_node);
-
-// Logical character placement
- switch(script->total_characters())
- {
- case 1:
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
- camera->at_x = asset->width / 4;
- }
-
- character_node->x = (int)(asset->width / 2 -
- character->w * character->scale / 2);
- character_node->y = (int)(asset->height -
- character->h * character->scale);
- break;
-
- case 2:
- if(i == 0)
- {
- character_node->x = 0;
- character_node->y = (int)(asset->height -
- character->h * character->scale);
-
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
- camera->at_y = character_node->y;
- camera->at_x = character_node->x +
- (character->head->x + character->head->image->get_w() / 2) * character->scale -
- asset->width / 4;
- CLAMP(camera->at_x, 0, asset->width / 2);
- CLAMP(camera->at_y, 0, asset->height);
-//printf("FileScene::read_frame %d camera->at_x=%f camera->at_y=%f\n", __LINE__, camera->at_x, camera->at_y);
- }
-
- if(character->faces_left)
- {
- character_node->flip = 1;
- character_node->x = asset->width -
- character->w * character->scale;
- }
- }
- else
- {
-
- character_node->x = (int)(asset->width -
- character->w * character->scale);
- character_node->y = (int)(asset->height -
- character->h * character->scale);
-
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
- camera->at_x = character_node->x +
- character->head->x * character->scale -
- asset->width / 4;
- CLAMP(camera->at_x, 0, asset->width / 2);
- }
-
- if(!character->faces_left)
- {
- character_node->flip = 1;
- character_node->x = 0;
- }
- }
- break;
-
- case 3:
- if(i == 0)
- {
- character_node->x = 0;
- character_node->y = (int)(asset->height -
- character->h * character->scale);
- if(character->faces_left)
- {
- character_node->flip = 1;
- }
- }
- else
- if(i == 1)
- {
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
- camera->at_x = asset->width / 4;
- }
- character_node->x = (int)(asset->width / 2 -
- character->w * character->scale / 2);
- character_node->y = (int)(asset->height -
- character->h * character->scale);
- if(character->faces_left)
- {
- character_node->flip = 1;
- }
- }
- else
- {
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
- camera->at_x = asset->width / 2;
- }
- character_node->x = (int)(asset->width -
- character->w * character->scale);
- character_node->y = (int)(asset->height -
- character->h * character->scale);
- if(!character->faces_left)
- {
- character_node->flip = 1;
- character_node->x = 0;
- }
- }
- break;
- }
-
-// Clamp the head
- if(character_node->y < 0) character_node->y = 0;
-
-// Add remaining parts
- body_node->copy_ref(character->body);
- head_node->copy_ref(character->head);
-
-// Speeker head rotation
- if(speeking_character == character)
- {
- //speeking_node = character_node;
-
- int head_time = (chunk_sample / asset->sample_rate / 2) % 2;
-
- if(head_time > 0)
- {
- double temp;
- double anim_position = modf((double)chunk_sample / asset->sample_rate / 2, &temp);
- double anim_length = 0.1;
-// printf("FileScene::read_frame %d %d %f\n",
-// __LINE__,
-// head_time,
-// anim_position);
-
- if(anim_position < anim_length)
- head_node->ry = -5 * anim_position / anim_length;
- else
- if(anim_position > 1.0 - anim_length)
- head_node->ry = -5 * (1.0 - anim_position) / anim_length;
- else
- head_node->ry = -5;
- }
- }
-
-//head_node->ry = -5;
-
-// Eyes
- double intpart;
- if(character->eyes.size())
- {
- if(modf((file->current_frame / asset->frame_rate + script->get_char_number(character)) / 5, &intpart) <=
- 0.1 / 5 &&
- file->current_frame / asset->frame_rate > 1)
- {
- eye_node->copy_ref(character->eyes.get(0));
- }
- else
- eye_node->copy_ref(character->eyes.get(1));
- }
-
-// Compute the mouth image
- int fraction = 0;
- if(character->is_speeking && character->max > 0)
- {
- fraction = (int)(accum * character->mouths.size() / character->max);
- if(fraction >= character->mouths.size())
- fraction = character->mouths.size() - 1;
- }
-
- mouth_node->copy_ref(character->mouths.get(fraction));
-
-
-// camera->scale = 2;
-// camera->at_x = asset->width / 2;
-// camera->at_y = 0;
-
-// Compute camera
- if(speeking_character == character &&
- speeking_character->current_camera == SceneChar::CAMERA_CU)
- {
-// If closeup, increase scene scale
- camera->scale = 2;
- }
- }
-
-
-
-
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
-
-
-
-// Render scene graph
- scene.render(frame, file->cpus);
-
-
-
- if(debug) printf("FileScene::read_frame %d\n", __LINE__);
-
-
- return 0;
-}
-
-
-int FileScene::read_samples(double *buffer, int64_t len)
-{
-// Speech rendering
-// Everything is timed based on speech, so we have to do this for video, too.
- render_chunks(file->current_sample, len, 0);
-
-// script->dump();
-
-
-// Convert temp to output
- for(int i = 0; i < len; i++)
- {
- buffer[i] = (double)audio_temp[i] / 32768;
- }
-
- return 0;
-}
-
-
-int64_t FileScene::get_memory_usage()
-{
-//PRINT_TRACE
- int total = 0x100000;
- if(script)
- {
- total += script->get_memory_usage();
-
- }
-//PRINT_TRACE
- return total;
-}
-
-
-
-int FileScene::get_best_colormodel(Asset *asset, int driver)
-{
- return 0;
-}
-
-
-int FileScene::colormodel_supported(int colormodel)
-{
- return BC_RGBA8888;
-}
-
-
-int FileScene::can_copy_from(Asset *asset, int64_t position)
-{
- return 0;
-}
-
-
-int FileScene::reset_parameters_derived()
-{
- script = 0;
- audio_temp = 0;
- temp_allocated = 0;
-// overlayer = 0;
-// affine = 0;
- return 0;
-}
-
-
-
-void FileScene::render_chunks(int64_t start_position,
- int64_t len,
- int all_channels)
-{
- int64_t end_position = start_position + len;
- const int debug = 0;
-
-
- if(debug) printf("FileScene::render_chunks %d start_position="
- _LD " len=" _LD "\n", __LINE__, start_position, len);
-
-// Update script
- read_script();
-
- if(!script) return;
-
- if(debug) PRINT_TRACE
-
-// Reallocate temp buffer
- if(len > temp_allocated)
- {
- delete [] audio_temp;
- audio_temp = new int16_t[len];
- temp_allocated = len;
- }
- bzero(audio_temp, sizeof(int16_t) * len);
-
- if(debug) PRINT_TRACE
-
-
-
-
-// Find start_position in script output.
-// Must know length of every chunk before end of buffer
- int64_t current_sample = 0;
- for(int i = 0; i < script->total_chunks() &&
- current_sample < end_position; i++)
- {
- SceneChunk *chunk = script->get_chunk(i);
- chunk->used = 0;
- if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
-
-// If extent of audio output hasn't been calculated, render it
- if(!chunk->audio_size)
- {
- if(debug) printf("FileScene::render_chunks %d i=%d\n", __LINE__, i);
- chunk->render();
- }
-
-// Dialog chunk is inside output buffer
- if(current_sample + chunk->audio_size / 2 > start_position &&
- current_sample < end_position)
- {
- if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
-
-// If no audio output exists, render it
- if(!chunk->audio)
- {
- if(debug) printf("FileScene::render_chunks %d rerendering audio\n", __LINE__);
- chunk->render();
- }
-
- if(debug) printf("FileScene::render_chunks %d: Using \"%s\" samples=%d\n",
- __LINE__,
- chunk->text,
- chunk->audio_size / 2);
- if(debug) printf("FileScene::render_chunks %d: start_position="
- _LD " current_sample=" _LD "\n", __LINE__,
- start_position, current_sample);
-
-// Memcpy it.
-// TODO: allow characters to talk simultaneously
- int64_t src_offset = start_position - current_sample;
- int64_t dst_offset = 0;
- int64_t src_len = chunk->audio_size / 2 - src_offset;
-
- if(debug) printf("FileScene::render_chunks %d: src_offset="
- _LD " dst_offset=" _LD " src_len=" _LD "\n", __LINE__,
- src_offset, dst_offset, src_len);
-
- if(src_offset < 0)
- {
- dst_offset -= src_offset;
- src_len += src_offset;
- src_offset = 0;
- }
-
- if(dst_offset + src_len > len)
- {
- src_len = len - dst_offset;
- }
-
- if(debug) printf("FileScene::render_chunks %d: src_offset="
- _LD " dst_offset=" _LD " src_len=" _LD "\n", __LINE__,
- src_offset, dst_offset, src_len);
-
-// Transfer if right channel
- if(all_channels ||
- file->current_channel == script->get_char_number(chunk->character))
- {
- for(int j = 0; j < src_len; j++)
- {
- audio_temp[dst_offset + j] =
- chunk->audio[(src_offset + j) * 2] |
- (chunk->audio[(src_offset + j) * 2 + 1] << 8);
- }
- }
-
- if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
- chunk->used = 1;
- }
-
- current_sample += chunk->advance_samples;
- }
-
-// Erase unused dialog chunks
- if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
- for(int i = 0; i < script->total_chunks(); i++)
- {
- SceneChunk *chunk = script->get_chunk(i);
- if(!chunk->used && chunk->audio)
- {
- if(debug) printf("FileScene::render_chunks %d erasing unused audio\n", __LINE__);
- delete [] chunk->audio;
- chunk->audio = 0;
- chunk->audio_allocated = 0;
- }
- }
- if(debug) printf("FileScene::render_chunks %d\n", __LINE__);
-
-
-}
-
-
-
-int FileScene::read_script()
-{
- const int debug = 0;
- struct stat ostat;
- if(stat(asset->path, &ostat))
- {
- printf("FileScene::read_script %d: %s\n", __LINE__, strerror(errno));
- return 1;
- }
-
-// Test timestamp
- if(script)
- {
- if(ostat.st_mtime == script->timestamp)
- {
- if(debug) printf("FileScene::read_script %d: script unchanged\n", __LINE__);
- return 0;
- }
- }
-
-
-// Read new script
- delete script;
- script = 0;
- if(!script) script = new SceneTokens(this, file->cpus);
- script->timestamp = ostat.st_mtime;
-
- script->read_script(asset->path);
-
- return 1;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-SceneChar::SceneChar(SceneTokens *script)
-{
- this->script = script;
- name[0] = 0;
- voice[0] = 0;
- body = 0;
- head = 0;
- body_order = 0;
- head_order = 1;
- sprintf(model, "heroine01");
- current_camera = CAMERA_WIDE;
- faces_left = 0;
- scale = 1;
- max = 0;
- is_speeking = 0;
-}
-
-SceneChar::~SceneChar()
-{
- mouths.remove_all_objects();
- eyes.remove_all_objects();
- delete body;
- delete head;
-}
-
-
-void SceneChar::increment_camera()
-{
- current_camera++;
- if(current_camera >= CAMERA_TOTAL)
- current_camera = 0;
-}
-
-
-int SceneChar::read_model()
-{
-// Read descriptor file
- const int debug = 0;
- int current_line = 0;
- float x, y;
- int i;
- int current_order = 0;
- char path[BCTEXTLEN];
-
-// Already read it
- if(body) return 0;
-
- script->convert_path(path, model);
- FILE *fd = fopen(path, "r");
-
-
-// Read assets
- if(fd)
- {
-// Read 1 line
- char string[BCTEXTLEN];
- char string2[BCTEXTLEN];
-
- while(!feof(fd))
- {
- char *result = fgets(string, BCTEXTLEN, fd);
-
- if(result)
- {
- int len = strlen(string);
-
- STRIP_LINE(string);
-
- if(debug) printf("SceneChar::read_model %d: %s\n",
- __LINE__,
- string);
-
- for(i = 0; i < len; i++)
- {
- if(isalnum(string[i]))
- {
- string2[0] = 0;
-
- if(read_parameter(string,
- &i,
- "width:",
- 0,
- &w,
- 0,
- 0))
- {
- }
- else
- if(read_parameter(string,
- &i,
- "height:",
- 0,
- &h,
- 0,
- 0))
- {
- }
- else
- if(read_parameter(string,
- &i,
- "body:",
- string2,
- 0,
- &x,
- &y))
- {
-// Load image
- body = new SceneNode(script->load_image(string2), 1, x, y);
- body_order = current_order++;
- }
- else
- if(read_parameter(string,
- &i,
- "head:",
- string2,
- 0,
- &x,
- &y))
- {
-// Load image
- head = new SceneNode(script->load_image(string2), 1, x, y);
- head_order = current_order++;
- }
- else
- if(read_parameter(string,
- &i,
- "mouth:",
- string2,
- 0,
- &x,
- &y))
- {
-// Load image
- SceneNode *mouth;
- mouths.append(mouth = new SceneNode(script->load_image(string2), 1, x, y));
-// Make coordinates relative to head
- if(head)
- {
- mouth->x -= head->x;
- mouth->y -= head->y;
- }
- }
- else
- if(read_parameter(string,
- &i,
- "eyes:",
- string2,
- 0,
- &x,
- &y))
- {
-// Load image
- SceneNode *eye;
- eyes.append(eye = new SceneNode(script->load_image(string2), 1, x, y));
-// Make coordinates relative to head
- if(head)
- {
- eye->x -= head->x;
- eye->y -= head->y;
- }
- }
- else
- if(read_parameter(string,
- &i,
- "faces_left",
- 0,
- 0,
- 0,
- 0))
- {
- faces_left = 1;
- }
- else
- if(read_parameter(string,
- &i,
- "scale:",
- 0,
- &scale,
- 0,
- 0))
- {
- }
-
- i = len;
- }
- }
- }
-
- current_line++;
- }
-
- fclose(fd);
- if(debug) dump();
- return 0;
- }
-
- printf("SceneChar::read_model %d: %s %s\n", __LINE__, path, strerror(errno));
- return 1;
-}
-
-int SceneChar::get_memory_usage()
-{
- int total = 0;
- if(body) total += body->get_memory_usage();
- if(head) total += head->get_memory_usage();
- for(int i = 0; i < mouths.size(); i++)
- total += mouths.get(i)->get_memory_usage();
- return total;
-}
-
-void SceneChar::dump()
-{
- printf("SceneChar::dump %d: %p name=%s voice=%s model=%s body=%p eyes=%d mouths=%d\n",
- __LINE__,
- this,
- name,
- voice,
- model,
- body,
- eyes.size(),
- mouths.size());
- printf("SceneChar::dump %d: w=%f h=%f\n", __LINE__, w, h);
-
-
- for(int i = 0; i < mouths.size(); i++)
- {
- SceneNode *node = mouths.get(i);
- printf(" mouth=%p x=%f y=%f\n", node, node->x, node->y);
- }
-
- for(int i = 0; i < eyes.size(); i++)
- {
- SceneNode *node = eyes.get(i);
- printf(" eyes=%p x=%f y=%f\n", node, node->x, node->y);
- }
-}
-
-
-
-
-
-
-
-
-
-// Dialog from a single character
-SceneChunk::SceneChunk(SceneTokens *script)
-{
- text = 0;
- character = 0;
- audio = 0;
- audio_size = 0;
- audio_allocated = 0;
- advance_samples = 0;
- used = 0;
- max = 0;
- command = NO_COMMAND;
- this->script = script;
-}
-
-SceneChunk::~SceneChunk()
-{
- delete [] text;
- delete [] audio;
-}
-
-void SceneChunk::dump()
-{
- printf("SceneChunk::dump %d: character=%s command=%d text=%s\n",
- __LINE__,
- character->name,
- command,
- text);
- printf("SceneChunk::dump %d: audio=%p audio_size=%d advance_samples=%d\n",
- __LINE__,
- audio,
- audio_size,
- advance_samples);
-}
-
-int SceneChunk::get_memory_usage()
-{
- return audio_allocated;
-}
-
-
-void SceneChunk::append_text(char *new_text)
-{
- char string[BCTEXTLEN];
-
-// Replace "
-// Convert ' to \'
- char *ptr = string;
- int len = strlen(new_text);
- for(int i = 0; i < len; i++)
- {
-// if(new_text[i] == '"')
-// *ptr++ = ' ';
-// else
-// if(new_text[i] == '\'')
-// {
-// *ptr++ = '\\';
-// *ptr++ = '\'';
-// *ptr++ = '\'';
-// }
-// else
- *ptr++ = new_text[i];
- }
- *ptr++ = 0;
-
- int len2 = strlen(string);
- if(text)
- {
- int len1 = strlen(text);
- int len3 = 1;
- int need_space = 0;
-
-// if(len1 > 0 && isalnum(text[len1 - 1]))
- if(len1 > 0 && text[len1 - 1] != ' ')
- {
- need_space = 1;
- len3++;
- }
-
- text = (char*)realloc(text, len1 + len2 + len3);
-
-// Append space
- if(need_space)
- {
- text[len1] = ' ';
- text[len1 + 1] = 0;
- }
- }
- else
- {
- text = new char[len2 + 1];
- text[0] = 0;
- }
-
- strcat(text, string);
-}
-
-
-
-
-void SceneChunk::render()
-{
- const int debug = 0;
- int len = 0;
- char command_line[BCTEXTLEN];
- char string2[MAX_CHARS];
- char script_path[BCTEXTLEN];
-
- //int total_args = 0;
- if(text) len = strlen(text);
- char *text_end = text + len;
- char *text_ptr = text;
-
- audio_size = 0;
-
- if(!character)
- {
- printf("SceneChunk::render %d: no character defined.\n", __LINE__);
- }
-
- if(len > MAX_CHARS)
- {
- printf("SceneChunk::render %d: text '%s' exceeds festival's maximum line length of %d chars.\n",
- __LINE__,
- text,
- MAX_CHARS);
- }
-
-// Process command
- switch(command)
- {
- case SceneChunk::PAUSE_COMMAND:
- audio_allocated = PAUSE_SAMPLES * 2;
- audio_size = PAUSE_SAMPLES * 2;
- advance_samples = PAUSE_SAMPLES;
- audio = (unsigned char*)realloc(audio, audio_allocated);
- bzero(audio, audio_size);
- max = 0;
- break;
- }
-
- while(text && text_ptr < text_end)
- {
-// Copy at most MAX_CHARS of data into string2
- char *ptr = string2;
- for(int i = 0; i < MAX_CHARS && text_ptr < text_end; i++)
- {
- *ptr++ = *text_ptr++;
- }
- *ptr++ = 0;
-
-
-// Rewind to white space if still more text
- if(text_ptr < text_end)
- {
- ptr--;
- text_ptr--;
- while(*text_ptr != ' ' &&
- *text_ptr != '\n' &&
- text_ptr > text)
- {
- text_ptr--;
- ptr--;
- }
-
-
-// Truncate string2 at white space
- *ptr = 0;
-// If no white space, abort.
- if(text_ptr <= text)
- {
- break;
- }
-
- }
-
- uuid_t temp_id;
- sprintf(script_path, "/tmp/cinelerra.");
- uuid_generate(temp_id);
- uuid_unparse(temp_id, script_path + strlen(script_path));
- FILE *script_fd = fopen(script_path, "w");
-
- sprintf(command_line, "%s%s --libdir %s%s -b %s",
- script->file->exe_path,
- FESTIVAL_PATH,
- script->file->exe_path,
- FESTIVAL_LIB_PATH,
- script_path);
-
-// Create script.
-// The maximum text length is limited with the command line
-
- fprintf(script_fd,
- "(voice_%s)\n"
- "(set! text (Utterance Text \"%s\"))\n"
- "(utt.synth text)"
- "(utt.save.wave text \"-\")",
- character->voice,
- string2);
- fclose(script_fd);
-
-
- if(debug)
- {
- printf("SceneChunk::render %d %s\n",
- __LINE__,
- command_line);
-
- FILE *script_fd = fopen(script_path, "r");
- while(!feof(script_fd))
- fputc(fgetc(script_fd), stdout);
- printf("\n");
- fclose(script_fd);
- }
-
-// popen only does half duplex
- FILE *fd = popen(command_line, "r");
-
-// Capture output
- if(fd)
- {
- int audio_start = audio_size;
-
-
- if(debug) printf("SceneChunk::render %d\n",
- __LINE__);
- while(!feof(fd))
- {
- if(debug) printf("SceneChunk::render %d\n",
- __LINE__);
- if(audio_size + FREAD_SIZE > audio_allocated)
- {
- audio_allocated += FREAD_SIZE;
- audio = (unsigned char*)realloc(audio, audio_allocated);
- }
-
-
- if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d\n",
- __LINE__,
- audio,
- audio_size);
-
- int bytes_read = fread(audio + audio_size, 1, FREAD_SIZE, fd);
- if(debug) printf("SceneChunk::render %d bytes_read=%d\n",
- __LINE__,
- bytes_read);
- audio_size += bytes_read;
- if(bytes_read < FREAD_SIZE)
- {
- break;
- }
- }
-
-
- pclose(fd);
-
- if(debug) printf("SceneChunk::render %d audio=%p audio_size=%d audio_allocated=%d\n",
- __LINE__,
- audio,
- audio_size,
- audio_allocated);
-
-// Strip WAV header
-
- if(audio_size - audio_start > WAVHEADER)
- {
-// for(int i = 0; i < 128; i++)
-// {
-// printf("%c ", audio[audio_start + i]);
-// if(!((i + 1) % 16)) printf("\n");
-// }
-// printf("\n");
-// Find header after error messages
- int header_size = WAVHEADER;
- for(int i = audio_start; i < audio_start + audio_size - 4; i++)
- {
- if(audio[i] == 'R' &&
- audio[i + 1] == 'I' &&
- audio[i + 2] == 'F' &&
- audio[i + 3] == 'F')
- {
- header_size += i - audio_start;
- break;
- }
- }
-
- memcpy(audio + audio_start,
- audio + audio_start + header_size,
- audio_size - audio_start - header_size);
- audio_size -= header_size;
- if(debug) printf("SceneChunk::render %d: audio_size=%d\n",
- __LINE__,
- audio_size);
- }
-
- advance_samples = audio_size / 2;
- }
- else
- {
- printf("SceneChunk::render %d: Couldn't run %s: %s\n",
- __LINE__,
- command_line,
- strerror(errno));
- }
-
- remove(script_path);
- if(debug) printf("SceneChunk::render %d max=%f\n",
- __LINE__,
- max);
- }
-
-
- if(text)
- {
-
-// Truncate if ...
- text_ptr = text + len - 1;
- while(text_ptr > text &&
- (*text_ptr == ' ' ||
- *text_ptr == '\n'))
- text_ptr--;
- if(text_ptr > text + 3 &&
- *(text_ptr - 0) == '.' &&
- *(text_ptr - 1) == '.' &&
- *(text_ptr - 2) == '.')
- {
- advance_samples -= ADVANCE_SAMPLES;
- if(advance_samples < 0) advance_samples = 0;
- }
-
-// Calculate loudest part
- max = 0;
- for(int i = 0; i < audio_size; i += 2)
- {
- int16_t sample = audio[i] |
- (audio[i + 1] << 8);
- double sample_float = fabs((double)sample / 32768);
- if(sample_float > max) max = sample_float;
- }
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-SceneTokens::SceneTokens(FileScene *file, int cpus)
-{
- background[0] = 0;
- background_image = 0;
- timestamp = 0;
- this->cpus = cpus;
- this->file = file;
-// overlayer = 0;
-}
-
-SceneTokens::~SceneTokens()
-{
- chunks.remove_all_objects();
- characters.remove_all_objects();
- delete background_image;
-// delete overlayer;
-}
-
-SceneChar* SceneTokens::get_character(char *name)
-{
- const int debug = 0;
- if(debug) printf("SceneTokens::get_character %d %d\n",
- __LINE__,
- characters.size());
-
- for(int i = 0; i < characters.size(); i++)
- {
- if(!strcmp(characters.get(i)->name, name)) return characters.get(i);
- }
- if(debug) printf("SceneTokens::get_character %d %d\n",
- __LINE__,
- characters.size());
-
- SceneChar *result = new SceneChar(this);
- if(debug) printf("SceneTokens::get_character %d %d this=%p\n",
- __LINE__,
- characters.size(),
- &characters);
-
- characters.append(result);
- if(debug) printf("SceneTokens::get_character %d %d\n",
- __LINE__,
- characters.size());
-
- strcpy(result->name, name);
- if(debug) printf("SceneTokens::get_character %d %d\n",
- __LINE__,
- characters.size());
-
- if(debug) printf("SceneTokens::get_character %d %d\n",
- __LINE__,
- characters.size());
-
- return result;
-}
-
-SceneChar* SceneTokens::get_character(int number)
-{
- return characters.get(number);
-}
-
-int SceneTokens::get_char_number(SceneChar *ptr)
-{
- for(int i = 0; i < characters.size(); i++)
- if(ptr == characters.get(i)) return i;
- return 0;
-}
-
-SceneChunk* SceneTokens::new_chunk()
-{
- SceneChunk *result = new SceneChunk(this);
- chunks.append(result);
- return result;
-}
-
-int SceneTokens::total_chunks()
-{
- return chunks.size();
-}
-
-int SceneTokens::total_characters()
-{
- return characters.size();
-}
-
-SceneChunk* SceneTokens::get_chunk(int number)
-{
- return chunks.get(number);
-}
-
-int SceneTokens::read_script(char *path)
-{
- const int debug = 0;
-
- strcpy(this->path, path);
-
- FILE *fd = fopen(path, "r");
- if(fd)
- {
-// Read 1 line
- char string[BCTEXTLEN];
-// Current character name
- char char_name[BCTEXTLEN];
- SceneChar *current_char = 0;
- char_name[0] = 0;
- SceneChunk *current_chunk = 0;
- int current_line = 0;
- int i;
-
- while(!feof(fd))
- {
- char *result = fgets(string, BCTEXTLEN, fd);
- current_line++;
-
- if(result)
- {
- int len = strlen(string);
- STRIP_LINE(string)
-
- if(debug) printf("SceneTokens::read_script %d: %s\n",
- __LINE__,
- string);
-
-// Skip the file ID & empty lines
- if(string[0] == 0 ||
- !strncmp(string, "TEXT2MOVIE", 10))
- continue;
-
- int got_it = 0;
- for(i = 0; i < len; i++)
- {
- if(isalnum(string[i]))
- {
- got_it = 1;
- i = len;
- }
- }
-
- if(!got_it) continue;
-
-// A line all in caps is a character name
- got_it = 1;
- for(i = 0; i < len; i++)
- {
- if(islower(string[i]))
- {
- got_it = 0;
- i = len;
- }
- }
-
- if(got_it)
- {
- strcpy(char_name, string);
-
- if(debug) printf("SceneTokens::read_script %d: char_name=%s\n",
- __LINE__,
- char_name);
-
- current_char = get_character(char_name);
-
- if(debug) printf("SceneTokens::read_script %d current_char=%p\n",
- __LINE__,
- current_char);
-
-// Reset the current chunk pointer
- current_chunk = 0;
- i = len;
- }
- else
- i = 0;
-
-// Certain words are commands
- for(; i < len; i++)
- {
- if(string[i] == '[' || isalnum(string[i]))
- {
- char *command = string + i;
-
- STRING_PARAMETER("voice:", 1, current_char->voice)
- else
- STRING_PARAMETER("model:", 1, current_char->model)
- else
- STRING_PARAMETER("background:", 0, background)
- else
-// Default is dialogue
- {
- if(!current_char)
- {
- printf("SceneTokens::read_script %d Line %d: dialogue text but no current character\n",
- __LINE__,
- current_line);
- }
- else
- {
- if(!current_chunk)
- {
- current_chunk = new_chunk();
- current_chunk->character = current_char;
- }
-
-// Append dialogue to current chunk
- current_chunk->append_text(string + i);
- }
-
- i = len;
- }
- }
- }
- }
- }
-
-
-
- fclose(fd);
-
- if(debug) printf("SceneTokens::read_script %d total_chunks=%d\n",
- __LINE__,
- total_chunks());
-// Parse commands in dialogue
- for(i = 0; i < total_chunks(); i++)
- {
- SceneChunk *chunk = get_chunk(i);
- if(chunk->text)
- {
- char *ptr = chunk->text;
- char *end = chunk->text + strlen(chunk->text);
- int got_text = 0;
- if(debug) printf("SceneTokens::read_script %d %s\n", __LINE__, chunk->text);
- while(ptr < end)
- {
-// Start of command
- if(*ptr == '[')
- {
- if(debug) printf("SceneTokens::read_script %d [\n", __LINE__);
-// Split chunk
- if(got_text)
- {
- SceneChunk *new_chunk = new SceneChunk(this);
- new_chunk->character = chunk->character;
- chunks.insert(new_chunk, i + 1);
-
-// Move text from start of command to new chunk.
- new_chunk->append_text(ptr);
-// Truncate current chunk
- *ptr = 0;
-// Advance to next chunk
- ptr = new_chunk->text;
- end = new_chunk->text + strlen(new_chunk->text);
- chunk = new_chunk;
- i++;
- }
- if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
-
-// Read command
- while(ptr < end &&
- (*ptr == '[' || *ptr == ' ' || *ptr == '\n'))
- ptr++;
-
- if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
-
- char *ptr2 = string;
- char *string_end = string + BCTEXTLEN;
- while(*ptr != ']' &&
- *ptr != ' ' &&
- *ptr != '\n' &&
- ptr < end &&
- ptr2 < string_end - 1)
- {
- *ptr2++ = *ptr++;
- }
- *ptr2 = 0;
- if(debug) printf("SceneTokens::read_script %d command=%s\n", __LINE__, string);
-
-// Convert command to code
- if(!strcasecmp(string, "pause"))
- {
- chunk->command = SceneChunk::PAUSE_COMMAND;
- }
- else
- {
-// TODO: line numbers
- printf("SceneTokens::read_script %d: Unknown command '%s'\n",
- __LINE__,
- string);
- }
- if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
-
-// Search for more text
- while(ptr < end &&
- (*ptr == ']' || *ptr == ' ' || *ptr == '\n'))
- ptr++;
-
-// Create new chunk for rest of text
- if(ptr < end)
- {
- SceneChunk *new_chunk = new SceneChunk(this);
- new_chunk->character = chunk->character;
- chunks.insert(new_chunk, i + 1);
-// Move text from end of command to new chunk.
- new_chunk->append_text(ptr);
-// Parse next chunk in next iteration
- ptr = end;
-// i++;
- }
- if(debug) printf("SceneTokens::read_script %d\n", __LINE__);
-
-// Truncate current chunk
- chunk->text[0] = 0;
- }
- else
-// Got non whitespace
- if(*ptr != ' ' &&
- *ptr != '\n')
- {
- got_text = 1;
- }
-
- ptr++;
- }
- }
- }
-
-
- if(debug) dump();
- return 0;
- }
-
- printf("SceneTokens::read_script %d: %s %s\n", __LINE__, path, strerror(errno));
- return 1;
-}
-
-void SceneTokens::convert_path(char *dst, char *src)
-{
-// Absolute path in src
- if(src[0] == '/')
- {
- strcpy(dst, src);
- }
- else
-// Relative path
- {
-// Try directory of script
- FileSystem fs;
- fs.extract_dir(dst, path);
- strcat(dst, src);
-
- struct stat ostat;
- if(stat(dst, &ostat))
- {
-// Try cinelerra directory
- get_exe_path(dst);
- strcat(dst, ASSET_PATH);
- strcat(dst, src);
-//printf("SceneTokens::convert_path %d %s\n", __LINE__, dst);
- }
- }
-}
-
-VFrame* SceneTokens::load_image(char *path)
-{
- VFrame *result = 0;
- char complete_path[BCTEXTLEN];
- convert_path(complete_path, path);
-
- int64_t size = FileSystem::get_size(complete_path);
- if(!size)
- {
- printf("SceneTokens::load_image %d: Couldn't open %s\n",
- __LINE__,
- complete_path);
- return 0;
- }
-
- unsigned char *data = new unsigned char[size + 4];
- FILE *fd = fopen(complete_path, "r");
- (void)fread(data + 4, 1, size, fd);
- data[0] = (size >> 24) & 0xff;
- data[1] = (size >> 16) & 0xff;
- data[2] = (size >> 8) & 0xff;
- data[3] = size & 0xff;
- result = new VFramePng(data, 1.);
- delete [] data;
-
-
- if(!BC_CModels::has_alpha(result->get_color_model()) )
- printf("SceneTokens::load_image %d: image %s has no alpha channel\n",
- __LINE__,
- path);
- return result;
-}
-
-void SceneTokens::render_background(SceneGraph *scene)
-{
-// Decompress background image
- if(!background_image)
- {
- background_image = load_image(background);
- }
-
- if(background_image)
- {
- SceneNode *node = new SceneNode("background");
- scene->append(node);
- node->image = background_image;
- node->x = 0;
- node->y = 0;
- node->sx = 1;
- node->sy = 1;
- }
-}
-
-int SceneTokens::get_memory_usage()
-{
- int total = 0;
- if(background_image) total += background_image->get_memory_usage();
- for(int i = 0; i < total_chunks(); i++)
- {
- total += get_chunk(i)->get_memory_usage();
- }
- for(int i = 0; i < total_characters(); i++)
- {
- total += get_character(i)->get_memory_usage();
- }
- return total;
-}
-
-
-void SceneTokens::dump()
-{
- printf("SceneTokens::dump %d background=%s\n", __LINE__, background);
- for(int i = 0; i < characters.size(); i++)
- {
- characters.get(i)->dump();
- }
-
-
- for(int i = 0; i < chunks.size(); i++)
- {
- chunks.get(i)->dump();
- }
-}
-
-
-
-