Process subtitles correctly with no timestamp file; bug #1060; some trackplayer cleanups

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3193 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2009-10-08 01:26:26 +00:00
parent f619b0b8e1
commit 39021b42ec
5 changed files with 342 additions and 357 deletions
+1
View File
@@ -1,4 +1,5 @@
Changes towards version 0.7: Changes towards version 0.7:
- Process subtitles correctly with no timestamp file (bug #1060) - Alex
- Lander will no longer hang when killed on planets with a lot of - Lander will no longer hang when killed on planets with a lot of
natural disasters (bug #584) - Alex natural disasters (bug #584) - Alex
- Canceling load from the main menu returns to main menu (bug #679) - Alex - Canceling load from the main menu returns to main menu (bug #679) - Alex
+1 -1
View File
@@ -907,7 +907,7 @@ DoConvSummary (SUMMARY_STATE *pSS)
if (!pSS->Initialized) if (!pSS->Initialized)
{ {
pSS->PrintNext = TRUE; pSS->PrintNext = TRUE;
pSS->NextSub = first_chain; pSS->NextSub = chain_head;
pSS->LeftOver = NULL; pSS->LeftOver = NULL;
pSS->MenuRepeatDelay = 0; pSS->MenuRepeatDelay = 0;
pSS->InputFunc = DoConvSummary; pSS->InputFunc = DoConvSummary;
@@ -321,6 +321,7 @@ SoundDecoder_Lookup (const char* fileext)
TFB_SoundDecoder* TFB_SoundDecoder*
SoundDecoder_Load (uio_DirHandle *dir, char *filename, SoundDecoder_Load (uio_DirHandle *dir, char *filename,
uint32 buffer_size, uint32 startTime, sint32 runTime) uint32 buffer_size, uint32 startTime, sint32 runTime)
// runTime < 0 specifies a default length for a nul decoder
{ {
const char* pext; const char* pext;
TFB_RegSoundDecoder* info; TFB_RegSoundDecoder* info;
@@ -417,7 +418,9 @@ SoundDecoder_Load (uio_DirHandle *dir, char *filename,
} }
decoder->length -= startTime / 1000.0f; decoder->length -= startTime / 1000.0f;
if (runTime > 0 && runTime / 1000.0 < decoder->length) if (decoder->length < 0)
decoder->length = 0;
else if (runTime > 0 && runTime / 1000.0 < decoder->length)
decoder->length = (float)(runTime / 1000.0); decoder->length = (float)(runTime / 1000.0);
decoder->start_sample = (uint32)(startTime / 1000.0f * decoder->frequency); decoder->start_sample = (uint32)(startTime / 1000.0f * decoder->frequency);
+3 -3
View File
@@ -23,7 +23,7 @@ typedef struct tfb_soundchain
float start_time; float start_time;
int tag_me; int tag_me;
uint32 track_num; uint32 track_num;
void *text; UNICODE *text;
TFB_TrackCB callback; TFB_TrackCB callback;
struct tfb_soundchain *next; struct tfb_soundchain *next;
} TFB_SoundChain; } TFB_SoundChain;
@@ -35,10 +35,10 @@ typedef struct tfb_soundchaindata
} TFB_SoundChainData; } TFB_SoundChainData;
extern TFB_SoundChain *first_chain; extern TFB_SoundChain *chain_head;
TFB_SoundChain *create_soundchain (TFB_SoundDecoder *decoder, float startTime); TFB_SoundChain *create_soundchain (TFB_SoundDecoder *decoder, float startTime);
void destroy_soundchain (TFB_SoundChain *chain); void destroy_soundchain (TFB_SoundChain *chain);
TFB_SoundChain *get_previous_chain (TFB_SoundChain *first_chain, TFB_SoundChain *current_chain); TFB_SoundChain *get_chain_previous (TFB_SoundChain *head, TFB_SoundChain *current);
#endif // TRACKINT_H #endif // TRACKINT_H
+271 -290
View File
@@ -24,24 +24,24 @@
#include <stdlib.h> #include <stdlib.h>
static int tct; //total number of subtitle tracks static int track_count; //total number of subtitle tracks
static int tcur; //currently playing subtitle track static int cur_track; //currently playing subtitle track
static UNICODE *cur_page = 0; //current page of subtitle track static UNICODE *cur_page = 0; //current page of subtitle track
static int no_page_break = 0; static int no_page_break = 0;
static int track_pos_changed = 0; // set whenever ff, frev is enabled static int track_pos_changed = 0; // set whenever ff, frev is enabled
static TFB_SoundChain *cur_text_chain = NULL; //current link w/ subbies
#define MAX_CLIPS 50
static TFB_SoundSample *sound_sample = NULL; static TFB_SoundSample *sound_sample = NULL;
TFB_SoundChain *first_chain = NULL; //first decoder in linked list TFB_SoundChain *chain_head = NULL; //first decoder in linked list
static TFB_SoundChain *last_chain = NULL; //last decoder in linked list static TFB_SoundChain *chain_tail = NULL; //last decoder in linked list
static TFB_SoundChain *last_ts_chain = NULL; //last element in the chain with a subtitle static TFB_SoundChain *last_sub = NULL; //last element in the chain with a subtitle
static Mutex track_mutex; //protects tcur and tct static Mutex track_mutex; //protects cur_track and track_count
void recompute_track_pos (TFB_SoundSample *sample, void recompute_track_pos (TFB_SoundSample *sample, TFB_SoundChain *head,
TFB_SoundChain *first_chain, sint32 offset); sint32 offset);
bool is_sample_playing(TFB_SoundSample* samp); bool is_sample_playing(TFB_SoundSample* samp);
void destroy_sound_sample (TFB_SoundSample *sample);
// stream callbacks // stream callbacks
static bool OnTrackStart (TFB_SoundSample* sample); static bool OnTrackStart (TFB_SoundSample* sample);
static bool OnChunkEnd (TFB_SoundSample* sample, audio_Object buffer); static bool OnChunkEnd (TFB_SoundSample* sample, audio_Object buffer);
@@ -66,8 +66,8 @@ JumpTrack (void)
{ {
TFB_SoundChainData* scd; TFB_SoundChainData* scd;
uint32 cur_time; uint32 cur_time;
sint32 total_length = (sint32)((last_chain->start_time + sint32 total_length = (sint32)((chain_tail->start_time +
last_chain->decoder->length) * (float)ONE_SECOND); chain_tail->decoder->length) * (float)ONE_SECOND);
if (!sound_sample) if (!sound_sample)
return; return;
@@ -80,8 +80,8 @@ JumpTrack (void)
soundSource[SPEECH_SOURCE].start_time = soundSource[SPEECH_SOURCE].start_time =
(sint32)cur_time - total_length; (sint32)cur_time - total_length;
track_pos_changed = 1; track_pos_changed = 1;
scd->play_chain_ptr = last_chain; scd->play_chain_ptr = chain_tail;
recompute_track_pos(sound_sample, first_chain, total_length + 1); recompute_track_pos (sound_sample, chain_head, total_length + 1);
UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex); UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
PlayingTrack(); PlayingTrack();
} }
@@ -113,7 +113,7 @@ PlayTrack (void)
// ResumeTrack should resume a paused track, or start a stopped track, and do nothing // ResumeTrack should resume a paused track, or start a stopped track, and do nothing
// for a playing track // for a playing track
void void
ResumeTrack () ResumeTrack (void)
{ {
TFB_SoundChainData* scd; TFB_SoundChainData* scd;
@@ -151,23 +151,23 @@ ResumeTrack ()
} }
COUNT COUNT
PlayingTrack () PlayingTrack (void)
{ {
// this is not a great way to detect whether the track is playing, // this is not a great way to detect whether the track is playing,
// but as it should work during fast-forward/rewind, 'PlayingStream' can't be used // but as it should work during fast-forward/rewind, 'PlayingStream' can't be used
// if (tct == 0) // if (track_count == 0)
// return ((COUNT)~0); // return ((COUNT)~0);
if (sound_sample && is_sample_playing (sound_sample)) if (sound_sample && is_sample_playing (sound_sample))
{ {
int last_track; int last_track;
UNICODE *last_page; UNICODE *last_page;
LockMutex (track_mutex); LockMutex (track_mutex);
last_track = tcur; last_track = cur_track;
last_page = cur_page; last_page = cur_page;
UnlockMutex (track_mutex); UnlockMutex (track_mutex);
if (do_subtitles (last_page)) if (do_subtitles (last_page))
{ {
return (tcur + 1); return cur_track + 1;
} }
else else
{ {
@@ -184,35 +184,33 @@ PlayingTrack ()
} }
void void
StopTrack () StopTrack (void)
{ {
LockMutex (soundSource[SPEECH_SOURCE].stream_mutex); LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
StopStream (SPEECH_SOURCE); StopStream (SPEECH_SOURCE);
UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex); UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
if (first_chain) if (chain_head)
{ {
destroy_soundchain (first_chain); destroy_soundchain (chain_head);
first_chain = NULL; chain_head = NULL;
last_chain = NULL; chain_tail = NULL;
last_ts_chain = NULL; last_sub = NULL;
} }
if (sound_sample) if (sound_sample)
{ {
DestroyMutex (track_mutex); DestroyMutex (track_mutex);
audio_DeleteBuffers (sound_sample->num_buffers, sound_sample->buffer); destroy_sound_sample (sound_sample);
HFree (sound_sample->buffer);
HFree (sound_sample->buffer_tag);
HFree (sound_sample->data);
HFree (sound_sample);
sound_sample = NULL; sound_sample = NULL;
} }
tct = tcur = 0; track_count = 0;
cur_track = 0;
cur_page = 0; cur_page = 0;
do_subtitles ((void *)~0); do_subtitles ((void *)~0);
} }
bool is_sample_playing(TFB_SoundSample* sample) static bool
is_sample_playing (TFB_SoundSample* sample)
{ {
TFB_SoundChainData* scd = (TFB_SoundChainData*) sample->data; TFB_SoundChainData* scd = (TFB_SoundChainData*) sample->data;
@@ -226,8 +224,8 @@ DoTrackTag (TFB_SoundChain *chain)
LockMutex (track_mutex); LockMutex (track_mutex);
if (chain->callback) if (chain->callback)
chain->callback (); chain->callback ();
tcur = chain->track_num; cur_track = chain->track_num;
cur_page = (UNICODE *) chain->text; cur_page = chain->text;
UnlockMutex (track_mutex); UnlockMutex (track_mutex);
} }
@@ -299,21 +297,22 @@ OnTrackTag (TFB_SoundSample* sample, TFB_SoundTag* tag)
} }
int int
GetTimeStamps(UNICODE *TimeStamps, sint32 *time_stamps) GetTimeStamps (UNICODE *TimeStamps, sint32 *time_stamps)
{ {
int pos; int pos;
int num = 0; int num = 0;
while (*TimeStamps && (pos = strcspn (TimeStamps, ",\r\n"))) while (*TimeStamps && (pos = strcspn (TimeStamps, ",\r\n")))
{ {
UNICODE valStr[32]; UNICODE valStr[32];
uint32 val; uint32 val;
strncpy (valStr, TimeStamps, pos); memcpy (valStr, TimeStamps, pos);
valStr[pos] = '\0'; valStr[pos] = '\0';
val = strtoul (valStr, NULL, 10); val = strtoul (valStr, NULL, 10);
if (val) if (val)
{ {
*time_stamps = (sint32)val; *time_stamps = val;
num++; num++;
time_stamps++; time_stamps++;
} }
@@ -324,92 +323,56 @@ GetTimeStamps(UNICODE *TimeStamps, sint32 *time_stamps)
} }
#define TEXT_SPEED 80 #define TEXT_SPEED 80
UNICODE ** // Returns number of parsed pages
SplitSubPages (UNICODE *text, sint32 *timestamp, int *num_pages) int
SplitSubPages (UNICODE *text, UNICODE *pages[], sint32 timestamp[], int size)
{ {
UNICODE **split_text = NULL; int lead_ellips = 0;
int pos = 0, ellips = 0; COUNT page;
COUNT page = 0;
while (text[pos]) for (page = 0; page < size && *text != '\0'; ++page)
{ {
if (text[pos] == '\n' || text[pos] == '\r') int aft_ellips;
{ int pos;
if (! split_text)
split_text = (UNICODE **) HMalloc (sizeof (UNICODE *) * (page + 1)); // seek to EOL or end of the string
else pos = strcspn (text, "\r\n");
split_text = (UNICODE **) HRealloc (split_text,
sizeof (UNICODE *) * (page + 1));
// XXX: this will only work when ASCII punctuation and spaces // XXX: this will only work when ASCII punctuation and spaces
// are used exclusively // are used exclusively
if (!ispunct (text[pos - 1]) && !isspace (text[pos - 1])) aft_ellips = 3 * (text[pos] != '\0' && pos > 0 &&
{ !ispunct (text[pos - 1]) && !isspace (text[pos - 1]));
split_text[page] = HMalloc (sizeof (UNICODE) * (pos + ellips + 4)); pages[page] = HMalloc (sizeof (UNICODE) *
if (ellips) (lead_ellips + pos + aft_ellips + 1));
strcpy (split_text[page], ".."); if (lead_ellips)
strncpy (split_text[page] + ellips, text, pos); strcpy (pages[page], "..");
strcpy (split_text[page] + ellips + pos, "..."); memcpy (pages[page] + lead_ellips, text, pos);
timestamp[page] = - pos * TEXT_SPEED; pages[page][lead_ellips + pos] = '\0'; // string term
if (timestamp[page] > -1000) if (aft_ellips)
timestamp[page] = -1000; strcpy (pages[page] + lead_ellips + pos, "...");
ellips = 2; timestamp[page] = pos * TEXT_SPEED;
text = text + pos; if (timestamp[page] < 1000)
pos = 0; timestamp[page] = 1000;
page ++; lead_ellips = aft_ellips ? 2 : 0;
text += pos;
// Skip any EOL
text += strspn (text, "\r\n");
} }
else
{ return page;
split_text[page] = HMalloc (sizeof (UNICODE) * (pos + ellips + 1));
if (ellips)
strcpy (split_text[page], "..");
strncpy (split_text[page] + ellips, text, pos);
*(split_text[page] + ellips + pos) = 0;
timestamp[page] = - pos * TEXT_SPEED;
if (timestamp[page] > -1000)
timestamp[page] = -1000;
ellips = 0;
text = text + pos;
pos = 0;
page ++;
}
while (text[pos] == '\n' || text[pos] == '\r')
text++;
}
else
pos++;
}
if (pos)
{
if (! split_text)
split_text = (UNICODE **) HMalloc (sizeof (UNICODE *) * (page + 1));
else
split_text = (UNICODE **) HRealloc (split_text,
sizeof (UNICODE *) * (page + 1));
split_text[page] = HMalloc (sizeof (UNICODE) * (pos + ellips + 1));
if (ellips)
strcpy (split_text[page], "..");
strncpy (split_text[page] + ellips, text, pos);
*(split_text[page] + ellips + pos) = 0;
timestamp[page] = - pos * TEXT_SPEED;
if (timestamp[page] > -1000)
timestamp[page] = -1000;
timestamp[page] += -1000;
page ++;
}
*num_pages = page;
return (split_text);
} }
// decodes several tracks into one and adds it to queue // decodes several tracks into one and adds it to queue
// track list is NULL-terminated // track list is NULL-terminated
// May only be called after at least one SpliceTrack(). This is a limitation
// for the sake of timestamps, but it does not have to be so.
void void
SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText) SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText)
{ {
#define MAX_MULTI_TRACKS 20 #define MAX_MULTI_TRACKS 20
#define MAX_MULTI_BUFFERS 100 #define MAX_MULTI_BUFFERS 100
TFB_SoundDecoder* track_decs[MAX_MULTI_TRACKS + 1]; TFB_SoundDecoder* track_decs[MAX_MULTI_TRACKS + 1];
TFB_SoundChain *begin_chain;
int tracks; int tracks;
int slen; int slen1, slen2;
if (!TrackText) if (!TrackText)
{ {
@@ -417,14 +380,7 @@ SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText)
return; return;
} }
if (tct >= MAX_CLIPS) if (!sound_sample || !chain_tail)
{
log_add (log_Warning, "SpliceMultiTrack(): no more clip slots (%d)",
MAX_CLIPS);
return;
}
if (! sound_sample)
{ {
log_add (log_Warning, "SpliceMultiTrack(): Cannot be called before SpliceTrack()"); log_add (log_Warning, "SpliceMultiTrack(): Cannot be called before SpliceTrack()");
return; return;
@@ -444,10 +400,8 @@ SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText)
track_decs[tracks]->format); track_decs[tracks]->format);
SoundDecoder_DecodeAll (track_decs[tracks]); SoundDecoder_DecodeAll (track_decs[tracks]);
last_chain->next = create_soundchain (track_decs[tracks], sound_sample->length); chain_tail->next = create_soundchain (track_decs[tracks], sound_sample->length);
last_chain = last_chain->next; chain_tail = chain_tail->next;
if (tracks == 0)
begin_chain = last_chain;
sound_sample->length += track_decs[tracks]->length; sound_sample->length += track_decs[tracks]->length;
} }
else else
@@ -465,102 +419,123 @@ SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText)
return; return;
} }
slen = strlen (TrackText); slen1 = strlen (last_sub->text);
if (tct) slen2 = strlen (TrackText);
{ last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
int slen1 = strlen ((UNICODE *)cur_text_chain->text); strcpy (last_sub->text + slen1, TrackText);
cur_text_chain->text = HRealloc (
(UNICODE *)cur_text_chain->text, slen1 + slen + 1);
strcpy (&((UNICODE *)cur_text_chain->text)[slen1], TrackText);
}
else
{
begin_chain->text = HMalloc (slen + 1);
strcpy ((UNICODE *)begin_chain->text, TrackText);
cur_text_chain = begin_chain;
begin_chain->tag_me = 1;
begin_chain->track_num = tct;
tct++;
}
no_page_break = 1;
no_page_break = 1;
} }
// XXX: This code and the entire trackplayer are begging to be overhauled
void void
SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, TFB_TrackCB cb) SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, TFB_TrackCB cb)
{ {
unsigned long startTime; static UNICODE last_track_name[128] = "";
UNICODE **split_text = NULL; static unsigned long startTime = 0;
if (TrackText) #define MAX_PAGES 50
{ UNICODE *pages[MAX_PAGES];
if (TrackName == 0) sint32 time_stamps[MAX_PAGES];
{ int num_pages;
if (tct) int page;
{
int slen1, slen2;
UNICODE *oTT;
int num_pages = 0, page_counter;
sint32 time_stamps[50];
if (!last_ts_chain || !last_ts_chain->text) if (!TrackText)
return;
if (!TrackName)
{ // Appending a piece of subtitles to the last track
int slen1, slen2;
if (track_count == 0)
{ {
log_add (log_Warning, "SpliceTrack(): Tried to append" log_add (log_Warning, "SpliceTrack(): Tried to append a subtitle,"
" a subtitle to a NULL string"); " but no current track");
return; return;
} }
split_text = SplitSubPages (TrackText, time_stamps, &num_pages);
if (! split_text) if (!last_sub || !last_sub->text)
{ {
log_add (log_Warning, "SpliceTrack(): Failed to parse sutitles"); log_add (log_Warning, "SpliceTrack(): Tried to append a subtitle"
" to a NULL string");
return; return;
} }
oTT = (UNICODE *)last_ts_chain->text;
slen1 = strlen (oTT); num_pages = SplitSubPages (TrackText, pages, time_stamps, MAX_PAGES);
slen2 = strlen (split_text[0]); if (num_pages == 0)
last_ts_chain->text = HRealloc (oTT, slen1 + slen2 + 1);
strcpy (&((UNICODE *)last_ts_chain->text)[slen1], split_text[0]);
HFree (split_text[0]);
for (page_counter = 1; page_counter < num_pages; page_counter++)
{ {
if (! last_ts_chain->next) log_add (log_Warning, "SpliceTrack(): Failed to parse subtitles");
return;
}
// The last page's stamp is a suggested value. The track should
// actually play to the end.
time_stamps[num_pages - 1] = -time_stamps[num_pages - 1];
// Add the first piece to the last subtitle page
slen1 = strlen (last_sub->text);
slen2 = strlen (pages[0]);
last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
strcpy (last_sub->text + slen1, pages[0]);
HFree (pages[0]);
// Add the rest of the pages
for (page = 1; page < num_pages; ++page)
{ {
log_add (log_Warning, "SpliceTrack(): More text pages than timestamps!"); if (last_sub->next)
{ // nodes prepared by previous call, just fill in the subs
last_sub = last_sub->next;
last_sub->text = pages[page];
}
else
{ // probably no timestamps were provided, so need more work
TFB_SoundDecoder *decoder = SoundDecoder_Load (contentDir,
last_track_name, 4096, startTime, time_stamps[page]);
if (!decoder)
{
log_add (log_Warning, "SpliceTrack(): couldn't load %s", TrackName);
break; break;
} }
last_ts_chain = last_ts_chain->next; startTime += (unsigned long)(decoder->length * 1000);
last_ts_chain->text = split_text[page_counter]; chain_tail->next = create_soundchain (decoder, sound_sample->length);
chain_tail = chain_tail->next;
chain_tail->tag_me = 1;
chain_tail->track_num = track_count - 1;
chain_tail->text = pages[page];
chain_tail->callback = cb;
// We have to tag only one page with a callback
cb = NULL;
last_sub = chain_tail;
sound_sample->length += decoder->length;
} }
} }
} }
else else
{ { // Adding a new track
int num_pages = 0, num_timestamps = 0, page_counter; int num_timestamps = 0;
sint32 time_stamps[50];
int i;
for (i = 0; i < 50; i++) utf8StringCopy (last_track_name, sizeof (last_track_name), TrackName);
time_stamps[i] = -1000;
split_text = SplitSubPages (TrackText, time_stamps, &num_pages); num_pages = SplitSubPages (TrackText, pages, time_stamps, MAX_PAGES);
if (! split_text) if (num_pages == 0)
{ {
log_add (log_Warning, "SpliceTrack(): Failed to parse sutitles"); log_add (log_Warning, "SpliceTrack(): Failed to parse sutitles");
return; return;
} }
if (no_page_break && tct) // The last page's stamp is a suggested value. The track should
// actually play to the end.
time_stamps[num_pages - 1] = -time_stamps[num_pages - 1];
if (no_page_break && track_count)
{ {
int slen1, slen2; int slen1, slen2;
UNICODE *oTT;
oTT = (UNICODE *)cur_text_chain->text; slen1 = strlen (last_sub->text);
slen1 = strlen (oTT); slen2 = strlen (pages[0]);
slen2 = strlen (split_text[0]); last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
cur_text_chain->text = HRealloc (oTT, slen1 + slen2 + 1); strcpy (last_sub->text + slen1, pages[0]);
strcpy (&((UNICODE *)cur_text_chain->text)[slen1], split_text[0]); HFree (pages[0]);
HFree (split_text[0]);
} }
else else
tct++; track_count++;
log_add (log_Info, "SpliceTrack(): loading %s", TrackName); log_add (log_Info, "SpliceTrack(): loading %s", TrackName);
@@ -568,55 +543,63 @@ SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, TFB_Tra
{ {
num_timestamps = GetTimeStamps (TimeStamp, time_stamps) + 1; num_timestamps = GetTimeStamps (TimeStamp, time_stamps) + 1;
if (num_timestamps < num_pages) if (num_timestamps < num_pages)
{
log_add (log_Warning, "SpliceTrack(): number of timestamps" log_add (log_Warning, "SpliceTrack(): number of timestamps"
" doesn't match number of pages!"); " doesn't match number of pages!");
} }
else if (num_timestamps > num_pages)
{ // We most likely will get more subtitles appended later
// Set the last page to the rest of the track
time_stamps[num_timestamps - 1] = -100000;
}
}
else else
{
num_timestamps = num_pages; num_timestamps = num_pages;
startTime = 0; }
for (page_counter = 0; page_counter < num_timestamps; page_counter++)
{
if (! sound_sample)
{
TFB_SoundDecoder *decoder;
TFB_SoundChainData* scd;
track_mutex = CreateMutex("trackplayer mutex", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_AUDIO); startTime = 0;
for (page = 0; page < num_timestamps; ++page)
{
static float old_volume = 0.0f;
TFB_SoundDecoder *decoder = SoundDecoder_Load (contentDir,
TrackName, 4096, startTime, time_stamps[page]);
if (!decoder)
{
log_add (log_Warning, "SpliceTrack(): couldn't load %s", TrackName);
break;
}
if (!sound_sample)
{
TFB_SoundChainData* scd = HCalloc (sizeof (TFB_SoundChainData));
track_mutex = CreateMutex ("trackplayer mutex", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_AUDIO);
sound_sample = (TFB_SoundSample *) HMalloc (sizeof (TFB_SoundSample)); sound_sample = (TFB_SoundSample *) HMalloc (sizeof (TFB_SoundSample));
scd = (TFB_SoundChainData *) HCalloc (sizeof (TFB_SoundChainData));
sound_sample->data = scd; sound_sample->data = scd;
sound_sample->callbacks = trackCBs; sound_sample->callbacks = trackCBs;
sound_sample->num_buffers = 8; sound_sample->num_buffers = 8;
sound_sample->buffer_tag = HCalloc (sizeof (TFB_SoundTag) * sound_sample->num_buffers); sound_sample->buffer_tag = HCalloc (sizeof (TFB_SoundTag) * sound_sample->num_buffers);
sound_sample->buffer = HMalloc (sizeof (audio_Object) * sound_sample->num_buffers); sound_sample->buffer = HMalloc (sizeof (audio_Object) * sound_sample->num_buffers);
audio_GenBuffers (sound_sample->num_buffers, sound_sample->buffer);
decoder = SoundDecoder_Load (contentDir, TrackName, 4096,
startTime, time_stamps[page_counter]);
scd->read_chain_ptr = create_soundchain (decoder, 0.0);
sound_sample->decoder = decoder; sound_sample->decoder = decoder;
first_chain = last_chain = scd->read_chain_ptr;
sound_sample->length = 0; sound_sample->length = 0;
scd->play_chain_ptr = 0; audio_GenBuffers (sound_sample->num_buffers, sound_sample->buffer);
chain_head = create_soundchain (decoder, 0.0);
chain_tail = chain_head;
scd->read_chain_ptr = chain_head;
scd->play_chain_ptr = NULL;
} }
else else
{ {
TFB_SoundDecoder *decoder; chain_tail->next = create_soundchain (decoder, sound_sample->length);
decoder = SoundDecoder_Load (contentDir, TrackName, 4096, chain_tail = chain_tail->next;
startTime, time_stamps[page_counter]);
last_chain->next = create_soundchain (decoder, sound_sample->length);
last_chain = last_chain->next;
} }
startTime += abs (time_stamps[page_counter]); startTime += (unsigned long)(decoder->length * 1000);
#if 0 #if 0
log_add (log_Debug, "page (%d of %d): %d ts: %d", log_add (log_Debug, "page (%d of %d): %d ts: %d",
page_counter, num_pages, page, num_pages,
startTime, time_stamps[page_counter]); startTime, time_stamps[page]);
#endif #endif
if (last_chain->decoder) if (decoder->is_null)
{
static float old_volume = 0.0f;
if (last_chain->decoder->is_null)
{ {
if (speechVolumeScale != 0.0f) if (speechVolumeScale != 0.0f)
{ {
@@ -636,39 +619,28 @@ SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, TFB_Tra
old_volume = 0.0f; old_volume = 0.0f;
} }
sound_sample->length += last_chain->decoder->length; sound_sample->length += decoder->length;
if (! no_page_break) if (!no_page_break)
{ {
last_chain->tag_me = 1; chain_tail->tag_me = 1;
// last_chain->tag.value = (void *)(((tct - 1) << 8) | page_counter); // chain_tail->tag.value = (void *)(((track_count - 1) << 8) | page);
last_chain->track_num = tct - 1; chain_tail->track_num = track_count - 1;
if (page_counter < num_pages) if (page < num_pages)
{ {
last_chain->text = split_text[page_counter]; chain_tail->text = pages[page];
last_ts_chain = last_chain; last_sub = chain_tail;
} }
last_chain->callback = cb; chain_tail->callback = cb;
cur_text_chain = last_chain; // We have to tag only one page with a callback
cb = NULL;
} }
no_page_break = 0; no_page_break = 0;
} }
else
{
log_add (log_Warning, "SpliceTrack(): couldn't load %s", TrackName);
audio_DeleteBuffers (sound_sample->num_buffers, sound_sample->buffer);
destroy_soundchain (first_chain);
first_chain = NULL;
HFree (sound_sample->buffer);
HFree (sound_sample);
sound_sample = NULL;
}
}
}
} }
} }
void void
PauseTrack () PauseTrack (void)
{ {
if (sound_sample && sound_sample->decoder) if (sound_sample && sound_sample->decoder)
{ {
@@ -680,47 +652,47 @@ PauseTrack ()
} }
void void
recompute_track_pos (TFB_SoundSample *sample, TFB_SoundChain *first_chain, sint32 offset) recompute_track_pos (TFB_SoundSample *sample, TFB_SoundChain *head, sint32 offset)
{ {
TFB_SoundChainData* scd; TFB_SoundChainData* scd;
TFB_SoundChain *cur_chain = first_chain; TFB_SoundChain *cur = head;
if (! sample) if (! sample)
return; return;
scd = (TFB_SoundChainData*) sample->data; scd = (TFB_SoundChainData*) sample->data;
while (cur_chain->next && while (cur->next &&
(sint32)(cur_chain->next->start_time * (float)ONE_SECOND) < offset) (sint32)(cur->next->start_time * (float)ONE_SECOND) < offset)
{ {
if (cur_chain->tag_me) if (cur->tag_me)
DoTrackTag (cur_chain); DoTrackTag (cur);
cur_chain = cur_chain->next; cur = cur->next;
} }
if (cur_chain->tag_me) if (cur->tag_me)
DoTrackTag (cur_chain); DoTrackTag (cur);
if ((sint32)((cur_chain->start_time + cur_chain->decoder->length) * (float)ONE_SECOND) < offset) if ((sint32)((cur->start_time + cur->decoder->length) * (float)ONE_SECOND) < offset)
{ {
scd->read_chain_ptr = NULL; scd->read_chain_ptr = NULL;
sample->decoder = NULL; sample->decoder = NULL;
} }
else else
{ {
scd->read_chain_ptr = cur_chain; scd->read_chain_ptr = cur;
SoundDecoder_Seek(scd->read_chain_ptr->decoder, SoundDecoder_Seek(scd->read_chain_ptr->decoder,
(uint32)(1000 * (offset / (float)ONE_SECOND - cur_chain->start_time))); (uint32)(1000 * (offset / (float)ONE_SECOND - cur->start_time)));
} }
} }
void void
FastReverse_Smooth () FastReverse_Smooth (void)
{ {
if (sound_sample) if (sound_sample)
{ {
sint32 offset; sint32 offset;
uint32 cur_time; uint32 cur_time;
sint32 total_length = (sint32)((last_chain->start_time + sint32 total_length = (sint32)((chain_tail->start_time +
last_chain->decoder->length) * (float)ONE_SECOND); chain_tail->decoder->length) * (float)ONE_SECOND);
LockMutex (soundSource[SPEECH_SOURCE].stream_mutex); LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
PauseStream (SPEECH_SOURCE); PauseStream (SPEECH_SOURCE);
cur_time = GetTimeCounter(); cur_time = GetTimeCounter();
@@ -737,7 +709,7 @@ FastReverse_Smooth ()
} }
else else
offset = cur_time - soundSource[SPEECH_SOURCE].start_time; offset = cur_time - soundSource[SPEECH_SOURCE].start_time;
recompute_track_pos(sound_sample, first_chain, offset); recompute_track_pos (sound_sample, chain_head, offset);
UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex); UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
PlayingTrack(); PlayingTrack();
@@ -745,18 +717,18 @@ FastReverse_Smooth ()
} }
void void
FastReverse_Page () FastReverse_Page (void)
{ {
if (sound_sample) if (sound_sample)
{ {
TFB_SoundChainData* scd = (TFB_SoundChainData*) sound_sample->data; TFB_SoundChainData* scd = (TFB_SoundChainData*) sound_sample->data;
TFB_SoundChain *prev_chain; TFB_SoundChain *prev;
LockMutex (soundSource[SPEECH_SOURCE].stream_mutex); LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
prev_chain = get_previous_chain (first_chain, scd->play_chain_ptr); prev = get_chain_previous (chain_head, scd->play_chain_ptr);
if (prev_chain) if (prev)
{ {
scd->read_chain_ptr = prev_chain; scd->read_chain_ptr = prev;
PlayStream (sound_sample, PlayStream (sound_sample,
SPEECH_SOURCE, false, SPEECH_SOURCE, false,
speechVolumeScale != 0.0f, true); speechVolumeScale != 0.0f, true);
@@ -766,14 +738,14 @@ FastReverse_Page ()
} }
void void
FastForward_Smooth () FastForward_Smooth (void)
{ {
if (sound_sample) if (sound_sample)
{ {
sint32 offset; sint32 offset;
uint32 cur_time; uint32 cur_time;
sint32 total_length = (sint32)((last_chain->start_time + sint32 total_length = (sint32)((chain_tail->start_time +
last_chain->decoder->length) * (float)ONE_SECOND); chain_tail->decoder->length) * (float)ONE_SECOND);
LockMutex (soundSource[SPEECH_SOURCE].stream_mutex); LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
PauseStream (SPEECH_SOURCE); PauseStream (SPEECH_SOURCE);
cur_time = GetTimeCounter(); cur_time = GetTimeCounter();
@@ -783,27 +755,26 @@ FastForward_Smooth ()
(sint32)cur_time - total_length - 1; (sint32)cur_time - total_length - 1;
offset = cur_time - soundSource[SPEECH_SOURCE].start_time; offset = cur_time - soundSource[SPEECH_SOURCE].start_time;
track_pos_changed = 1; track_pos_changed = 1;
recompute_track_pos(sound_sample, first_chain, offset); recompute_track_pos (sound_sample, chain_head, offset);
UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex); UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
PlayingTrack(); PlayingTrack ();
} }
} }
int int
FastForward_Page () FastForward_Page (void)
{ {
if (sound_sample) if (sound_sample)
{ {
TFB_SoundChainData* scd = (TFB_SoundChainData*) sound_sample->data; TFB_SoundChainData* scd = (TFB_SoundChainData*) sound_sample->data;
TFB_SoundChain *cur_ptr = scd->play_chain_ptr; TFB_SoundChain *cur = scd->play_chain_ptr;
LockMutex (soundSource[SPEECH_SOURCE].stream_mutex); LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
while (cur_ptr->next && !cur_ptr->next->tag_me) while (cur->next && !cur->next->tag_me)
cur_ptr = cur_ptr->next; cur = cur->next;
if (cur_ptr->next) if (cur->next)
{ {
scd->read_chain_ptr = cur_ptr->next; scd->read_chain_ptr = cur->next;
PlayStream (sound_sample, PlayStream (sound_sample,
SPEECH_SOURCE, false, SPEECH_SOURCE, false,
speechVolumeScale != 0.0f, true); speechVolumeScale != 0.0f, true);
@@ -845,46 +816,56 @@ TFB_SoundChain *
create_soundchain (TFB_SoundDecoder *decoder, float startTime) create_soundchain (TFB_SoundDecoder *decoder, float startTime)
{ {
TFB_SoundChain *chain; TFB_SoundChain *chain;
chain = (TFB_SoundChain *)HMalloc (sizeof (TFB_SoundChain)); chain = HMalloc (sizeof (TFB_SoundChain));
chain->decoder = decoder; chain->decoder = decoder;
chain->next = NULL; chain->next = NULL;
chain->start_time = startTime; chain->start_time = startTime;
chain->tag_me = 0; chain->tag_me = 0;
chain->text = 0; chain->text = 0;
return (chain); return chain;
} }
void void
destroy_soundchain (TFB_SoundChain *chain) destroy_soundchain (TFB_SoundChain *chain)
{ {
while (chain->next) TFB_SoundChain *next = NULL;
for ( ; chain; chain = next)
{ {
TFB_SoundChain *tmp_chain = chain->next; next = chain->next;
chain->next = chain->next->next; if (chain->decoder)
if (tmp_chain->decoder)
SoundDecoder_Free (tmp_chain->decoder);
if (tmp_chain->text)
HFree (tmp_chain->text);
HFree (tmp_chain);
}
SoundDecoder_Free (chain->decoder); SoundDecoder_Free (chain->decoder);
HFree (chain->text);
HFree (chain); HFree (chain);
}
}
void
destroy_sound_sample (TFB_SoundSample *sample)
{
if (sample->buffer)
{
audio_DeleteBuffers (sample->num_buffers, sample->buffer);
HFree (sample->buffer);
}
HFree (sample->buffer_tag);
HFree (sample->data);
HFree (sample);
} }
TFB_SoundChain * TFB_SoundChain *
get_previous_chain (TFB_SoundChain *first_chain, TFB_SoundChain *current_chain) get_chain_previous (TFB_SoundChain *head, TFB_SoundChain *current)
{ {
TFB_SoundChain *prev_chain, *last_valid = NULL; TFB_SoundChain *prev, *last_valid = NULL;
prev_chain = first_chain; prev = head;
if (prev_chain == current_chain) if (prev == current)
return (prev_chain); return prev;
while (prev_chain->next) while (prev->next)
{ {
if (prev_chain->tag_me) if (prev->tag_me)
last_valid = prev_chain; last_valid = prev;
if (prev_chain->next == current_chain) if (prev->next == current)
return (last_valid); return last_valid;
prev_chain = prev_chain->next; prev = prev->next;
} }
return (first_chain); return head;
} }