1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17 #include <assert.h>
18 #include <string.h>
19 #include <stdlib.h>
20 // for abs()
21 #include "sound.h"
22 #include "sndintrn.h"
23 #include "libs/tasklib.h"
24 #include "libs/timelib.h"
25 #include "libs/threadlib.h"
26 #include "libs/log.h"
27 #include "libs/memlib.h"
28
29
30 static Task decoderTask;
31
32 static TimeCount musicFadeStartTime;
33 static sint32 musicFadeInterval;
34 static int musicFadeStartVolume;
35 static int musicFadeDelta;
36 // Mutex protects fade structures
37 static Mutex fade_mutex;
38
39 static void add_scope_data (TFB_SoundSource *source, uint32 bytes);
40
41
42 void
PlayStream(TFB_SoundSample * sample,uint32 source,bool looping,bool scope,bool rewind)43 PlayStream (TFB_SoundSample *sample, uint32 source, bool looping, bool scope,
44 bool rewind)
45 {
46 uint32 i;
47 sint32 offset;
48 TFB_SoundDecoder *decoder;
49
50 if (!sample)
51 return;
52
53 StopStream (source);
54 if (sample->callbacks.OnStartStream &&
55 !sample->callbacks.OnStartStream (sample))
56 return; // callback failed
57
58 if (sample->buffer_tag)
59 memset (sample->buffer_tag, 0,
60 sample->num_buffers * sizeof (sample->buffer_tag[0]));
61
62 decoder = sample->decoder;
63 offset = sample->offset;
64 if (rewind)
65 SoundDecoder_Rewind (decoder);
66 else
67 offset += (sint32)(SoundDecoder_GetTime (decoder) * ONE_SECOND);
68
69 soundSource[source].sample = sample;
70 decoder->looping = looping;
71 audio_Sourcei (soundSource[source].handle, audio_LOOPING, false);
72
73 if (scope)
74 { // Prealloc the scope buffer in advance so that we do not
75 // realloc it a zillion times
76 soundSource[source].sbuf_size = sample->num_buffers *
77 decoder->buffer_size + PAD_SCOPE_BYTES;
78 soundSource[source].sbuffer = HCalloc (soundSource[source].sbuf_size);
79 }
80
81 for (i = 0; i < sample->num_buffers; ++i)
82 {
83 uint32 decoded_bytes;
84
85 decoded_bytes = SoundDecoder_Decode (decoder);
86 #if 0
87 log_add (log_Debug, "PlayStream(): source:%d filename:%s start:%d "
88 "position:%d bytes:%d\n",
89 source, decoder->filename, decoder->start_sample,
90 decoder->pos, decoded_bytes);
91 #endif
92 if (decoded_bytes == 0)
93 break;
94
95 audio_BufferData (sample->buffer[i], decoder->format,
96 decoder->buffer, decoded_bytes, decoder->frequency);
97 audio_SourceQueueBuffers (soundSource[source].handle, 1,
98 &sample->buffer[i]);
99 if (sample->callbacks.OnQueueBuffer)
100 sample->callbacks.OnQueueBuffer (sample, sample->buffer[i]);
101
102 if (scope)
103 add_scope_data (&soundSource[source], decoded_bytes);
104
105 if (decoder->error != SOUNDDECODER_OK)
106 {
107 if (decoder->error != SOUNDDECODER_EOF ||
108 !sample->callbacks.OnEndChunk ||
109 !sample->callbacks.OnEndChunk (sample, sample->buffer[i]))
110 { // Decoder probably run out of data before we could fill
111 // all buffers, and OnEndChunk() did not set a new one
112 break;
113 }
114 else
115 { // OnEndChunk() probably set a new decoder, get it
116 decoder = sample->decoder;
117 }
118 }
119 }
120
121 soundSource[source].sbuf_lasttime = GetTimeCounter ();
122 // Adjust the start time so it looks like the stream has been playing
123 // from the very beginning
124 soundSource[source].start_time = GetTimeCounter () - offset;
125 soundSource[source].pause_time = 0;
126 soundSource[source].stream_should_be_playing = TRUE;
127 audio_SourcePlay (soundSource[source].handle);
128 }
129
130 void
StopStream(uint32 source)131 StopStream (uint32 source)
132 {
133 StopSource (source);
134
135 soundSource[source].stream_should_be_playing = FALSE;
136 soundSource[source].sample = NULL;
137
138 if (soundSource[source].sbuffer)
139 {
140 void *sbuffer = soundSource[source].sbuffer;
141 soundSource[source].sbuffer = NULL;
142 HFree (sbuffer);
143 }
144 soundSource[source].sbuf_size = 0;
145 soundSource[source].sbuf_head = 0;
146 soundSource[source].sbuf_tail = 0;
147 soundSource[source].pause_time = 0;
148 }
149
150 void
PauseStream(uint32 source)151 PauseStream (uint32 source)
152 {
153 soundSource[source].stream_should_be_playing = FALSE;
154 if (!soundSource[source].pause_time)
155 soundSource[source].pause_time = GetTimeCounter ();
156 audio_SourcePause (soundSource[source].handle);
157 }
158
159 void
ResumeStream(uint32 source)160 ResumeStream (uint32 source)
161 {
162 if (soundSource[source].pause_time)
163 { // Adjust the start time so it looks like the stream has
164 // been playing all this time non-stop
165 soundSource[source].start_time += GetTimeCounter ()
166 - soundSource[source].pause_time;
167 }
168 soundSource[source].pause_time = 0;
169 soundSource[source].stream_should_be_playing = TRUE;
170 audio_SourcePlay (soundSource[source].handle);
171 }
172
173 void
SeekStream(uint32 source,uint32 pos)174 SeekStream (uint32 source, uint32 pos)
175 {
176 TFB_SoundSample* sample = soundSource[source].sample;
177 bool looping;
178 bool scope;
179
180 if (!sample)
181 return;
182 looping = sample->decoder->looping;
183 scope = soundSource[source].sbuffer != NULL;
184
185 StopSource (source);
186 SoundDecoder_Seek (sample->decoder, pos);
187 PlayStream (sample, source, looping, scope, false);
188 }
189
190 BOOLEAN
PlayingStream(uint32 source)191 PlayingStream (uint32 source)
192 {
193 return soundSource[source].stream_should_be_playing;
194 }
195
196
197 TFB_SoundSample *
TFB_CreateSoundSample(TFB_SoundDecoder * decoder,uint32 num_buffers,const TFB_SoundCallbacks * pcbs)198 TFB_CreateSoundSample (TFB_SoundDecoder *decoder, uint32 num_buffers,
199 const TFB_SoundCallbacks *pcbs /* can be NULL */)
200 {
201 TFB_SoundSample *sample;
202
203 sample = HCalloc (sizeof (*sample));
204 sample->decoder = decoder;
205 sample->num_buffers = num_buffers;
206 sample->buffer = HCalloc (sizeof (audio_Object) * num_buffers);
207 audio_GenBuffers (num_buffers, sample->buffer);
208 if (pcbs)
209 sample->callbacks = *pcbs;
210
211 return sample;
212 }
213
214 // Deletes all TFB_SoundSample data structures, except decoder
215 void
TFB_DestroySoundSample(TFB_SoundSample * sample)216 TFB_DestroySoundSample (TFB_SoundSample *sample)
217 {
218 if (sample->buffer)
219 {
220 audio_DeleteBuffers (sample->num_buffers, sample->buffer);
221 HFree (sample->buffer);
222 }
223 HFree (sample->buffer_tag);
224 HFree (sample);
225 }
226
227 void
TFB_SetSoundSampleData(TFB_SoundSample * sample,void * data)228 TFB_SetSoundSampleData (TFB_SoundSample *sample, void* data)
229 {
230 sample->data = data;
231 }
232
233 void*
TFB_GetSoundSampleData(TFB_SoundSample * sample)234 TFB_GetSoundSampleData (TFB_SoundSample *sample)
235 {
236 return sample->data;
237 }
238
239 void
TFB_SetSoundSampleCallbacks(TFB_SoundSample * sample,const TFB_SoundCallbacks * pcbs)240 TFB_SetSoundSampleCallbacks (TFB_SoundSample *sample,
241 const TFB_SoundCallbacks *pcbs /* can be NULL */)
242 {
243 if (pcbs)
244 sample->callbacks = *pcbs;
245 else
246 memset (&sample->callbacks, 0, sizeof (sample->callbacks));
247 }
248
249 TFB_SoundDecoder*
TFB_GetSoundSampleDecoder(TFB_SoundSample * sample)250 TFB_GetSoundSampleDecoder (TFB_SoundSample *sample)
251 {
252 return sample->decoder;
253 }
254
255 TFB_SoundTag*
TFB_FindTaggedBuffer(TFB_SoundSample * sample,audio_Object buffer)256 TFB_FindTaggedBuffer (TFB_SoundSample *sample, audio_Object buffer)
257 {
258 uint32 buf_num;
259
260 if (!sample->buffer_tag)
261 return NULL; // do not have any tags
262
263 for (buf_num = 0;
264 buf_num < sample->num_buffers &&
265 (!sample->buffer_tag[buf_num].in_use ||
266 sample->buffer_tag[buf_num].buf_name != buffer
267 );
268 buf_num++)
269 ;
270
271 return buf_num < sample->num_buffers ?
272 &sample->buffer_tag[buf_num] : NULL;
273 }
274
275 bool
TFB_TagBuffer(TFB_SoundSample * sample,audio_Object buffer,intptr_t data)276 TFB_TagBuffer (TFB_SoundSample *sample, audio_Object buffer, intptr_t data)
277 {
278 uint32 buf_num;
279
280 if (!sample->buffer_tag)
281 sample->buffer_tag = HCalloc (sizeof (TFB_SoundTag) *
282 sample->num_buffers);
283
284 for (buf_num = 0;
285 buf_num < sample->num_buffers &&
286 sample->buffer_tag[buf_num].in_use &&
287 sample->buffer_tag[buf_num].buf_name != buffer;
288 buf_num++)
289 ;
290
291 if (buf_num >= sample->num_buffers)
292 return false; // no empty slot
293
294 sample->buffer_tag[buf_num].in_use = 1;
295 sample->buffer_tag[buf_num].buf_name = buffer;
296 sample->buffer_tag[buf_num].data = data;
297
298 return true;
299 }
300
301 void
TFB_ClearBufferTag(TFB_SoundTag * ptag)302 TFB_ClearBufferTag (TFB_SoundTag *ptag)
303 {
304 ptag->in_use = 0;
305 ptag->buf_name = 0;
306 }
307
308 static void
remove_scope_data(TFB_SoundSource * source,audio_Object buffer)309 remove_scope_data (TFB_SoundSource *source, audio_Object buffer)
310 {
311 audio_IntVal buf_size;
312
313 audio_GetBufferi (buffer, audio_SIZE, &buf_size);
314 source->sbuf_head += buf_size;
315 // the buffer is cyclic
316 source->sbuf_head %= source->sbuf_size;
317
318 source->sbuf_lasttime = GetTimeCounter ();
319 }
320
321 static void
add_scope_data(TFB_SoundSource * source,uint32 bytes)322 add_scope_data (TFB_SoundSource *source, uint32 bytes)
323 {
324 uint8 *sbuffer = source->sbuffer;
325 uint8 *dec_buf = source->sample->decoder->buffer;
326 uint32 tail_bytes;
327 uint32 wrap_bytes;
328
329 if (source->sbuf_tail + bytes > source->sbuf_size)
330 { // does not fit at the tail, have to split it up
331 tail_bytes = source->sbuf_size - source->sbuf_tail;
332 wrap_bytes = bytes - tail_bytes;
333 }
334 else
335 { // all fits at the tail
336 tail_bytes = bytes;
337 wrap_bytes = 0;
338 }
339
340 if (tail_bytes)
341 {
342 memcpy (sbuffer + source->sbuf_tail, dec_buf, tail_bytes);
343 source->sbuf_tail += tail_bytes;
344 }
345
346 if (wrap_bytes)
347 {
348 memcpy (sbuffer, dec_buf + tail_bytes, wrap_bytes);
349 source->sbuf_tail = wrap_bytes;
350 }
351 }
352
353 static void
process_stream(TFB_SoundSource * source)354 process_stream (TFB_SoundSource *source)
355 {
356 TFB_SoundSample *sample = source->sample;
357 TFB_SoundDecoder *decoder = sample->decoder;
358 bool end_chunk_failed = false;
359 audio_IntVal processed;
360 audio_IntVal queued;
361
362 audio_GetSourcei (source->handle, audio_BUFFERS_PROCESSED, &processed);
363 audio_GetSourcei (source->handle, audio_BUFFERS_QUEUED, &queued);
364
365 if (processed == 0)
366 { // Nothing was played
367 audio_IntVal state;
368
369 audio_GetSourcei (source->handle, audio_SOURCE_STATE, &state);
370 if (state != audio_PLAYING)
371 {
372 if (queued == 0 && decoder->error == SOUNDDECODER_EOF)
373 { // The stream has reached the end
374 log_add (log_Info, "StreamDecoderTaskFunc(): "
375 "finished playing %s", decoder->filename);
376 source->stream_should_be_playing = FALSE;
377
378 if (sample->callbacks.OnEndStream)
379 sample->callbacks.OnEndStream (sample);
380 }
381 else
382 {
383 log_add (log_Warning, "StreamDecoderTaskFunc(): "
384 "buffer underrun playing %s", decoder->filename);
385 audio_SourcePlay (source->handle);
386 }
387 }
388 }
389
390 // Unqueue processed buffers and replace them with new ones
391 for (; processed > 0; --processed)
392 {
393 uint32 error;
394 audio_Object buffer;
395 uint32 decoded_bytes;
396
397 audio_GetError (); // clear error state
398
399 // Get the buffer that finished playing
400 audio_SourceUnqueueBuffers (source->handle, 1, &buffer);
401 error = audio_GetError();
402 if (error != audio_NO_ERROR)
403 {
404 log_add (log_Warning, "StreamDecoderTaskFunc(): "
405 "error after audio_SourceUnqueueBuffers: %x, file %s",
406 error, decoder->filename);
407 break;
408 }
409
410 // Process a callback on a tagged buffer, if any
411 if (sample->callbacks.OnTaggedBuffer)
412 {
413 TFB_SoundTag* tag = TFB_FindTaggedBuffer (sample, buffer);
414 if (tag)
415 sample->callbacks.OnTaggedBuffer (sample, tag);
416 }
417
418 if (source->sbuffer)
419 remove_scope_data (source, buffer);
420
421 // See what state the decoder was left in last time around
422 if (decoder->error != SOUNDDECODER_OK)
423 {
424 if (decoder->error == SOUNDDECODER_EOF)
425 {
426 if (end_chunk_failed)
427 continue; // should not do it again
428
429 if (!sample->callbacks.OnEndChunk ||
430 !sample->callbacks.OnEndChunk (sample, source->last_q_buf))
431 { // Reached the end of the current stream and we did not
432 // get another sample to play (relevant for Trackplayer)
433 end_chunk_failed = true;
434 continue;
435 }
436 else
437 { // OnEndChunk succeeded, so someone (read: Trackplayer)
438 // wants to keep going, probably with a new decoder.
439 // Get the new decoder
440 decoder = sample->decoder;
441 }
442 }
443 else
444 { // Decoder returned a real error, keep going
445 #if 0
446 log_add (log_Debug, "StreamDecoderTaskFunc(): "
447 "decoder->error is %d for %s", decoder->error,
448 decoder->filename);
449 #endif
450 continue;
451 }
452 }
453
454 // Now replace the unqueued buffer with a new one
455 decoded_bytes = SoundDecoder_Decode (decoder);
456 if (decoder->error == SOUNDDECODER_ERROR)
457 {
458 log_add (log_Warning, "StreamDecoderTaskFunc(): "
459 "SoundDecoder_Decode error %d, file %s",
460 decoder->error, decoder->filename);
461 source->stream_should_be_playing = FALSE;
462 continue;
463 }
464
465 if (decoded_bytes == 0)
466 { // Nothing was decoded, keep going
467 continue;
468 // This loses a stream buffer, which we cannot get back
469 // w/o restarting the stream, but we should never get here.
470 }
471
472 // And a new buffer is born
473 audio_BufferData (buffer, decoder->format, decoder->buffer,
474 decoded_bytes, decoder->frequency);
475 error = audio_GetError();
476 if (error != audio_NO_ERROR)
477 {
478 log_add (log_Warning, "StreamDecoderTaskFunc(): "
479 "error after audio_BufferData: %x, file %s, decoded %d",
480 error, decoder->filename, decoded_bytes);
481 continue;
482 }
483
484 // Now queue the buffer
485 audio_SourceQueueBuffers (source->handle, 1, &buffer);
486 error = audio_GetError();
487 if (error != audio_NO_ERROR)
488 {
489 log_add (log_Warning, "StreamDecoderTaskFunc(): "
490 "error after audio_SourceQueueBuffers: %x, file %s, "
491 "decoded %d", error, decoder->filename, decoded_bytes);
492 continue;
493 }
494
495 // Remember the last queued buffer so we can pass it to callbacks
496 source->last_q_buf = buffer;
497 if (sample->callbacks.OnQueueBuffer)
498 sample->callbacks.OnQueueBuffer (sample, buffer);
499
500 if (source->sbuffer)
501 add_scope_data (source, decoded_bytes);
502 }
503 }
504
505 static void
processMusicFade(void)506 processMusicFade (void)
507 {
508 TimeCount Now;
509 sint32 elapsed;
510 int newVolume;
511
512 LockMutex (fade_mutex);
513
514 if (!musicFadeInterval)
515 { // there is no fade set
516 UnlockMutex (fade_mutex);
517 return;
518 }
519
520 Now = GetTimeCounter ();
521 elapsed = Now - musicFadeStartTime;
522 if (elapsed > musicFadeInterval)
523 elapsed = musicFadeInterval;
524
525 newVolume = musicFadeStartVolume + (long)musicFadeDelta * elapsed
526 / musicFadeInterval;
527 SetMusicVolume (newVolume);
528
529 if (elapsed >= musicFadeInterval)
530 musicFadeInterval = 0; // fade is over
531
532 UnlockMutex (fade_mutex);
533 }
534
535 static int
StreamDecoderTaskFunc(void * data)536 StreamDecoderTaskFunc (void *data)
537 {
538 Task task = (Task)data;
539 int active_streams;
540 int i;
541
542 while (!Task_ReadState (task, TASK_EXIT))
543 {
544 active_streams = 0;
545
546 processMusicFade ();
547
548 for (i = MUSIC_SOURCE; i < NUM_SOUNDSOURCES; ++i)
549 {
550 TFB_SoundSource *source = &soundSource[i];
551
552 LockMutex (source->stream_mutex);
553
554 if (!source->sample ||
555 !source->sample->decoder ||
556 !source->stream_should_be_playing ||
557 source->sample->decoder->error == SOUNDDECODER_ERROR)
558 {
559 UnlockMutex (source->stream_mutex);
560 continue;
561 }
562
563 process_stream (source);
564 active_streams++;
565
566 UnlockMutex (source->stream_mutex);
567 }
568
569 if (active_streams == 0)
570 { // Throttle down the thread when there are no active streams
571 HibernateThread (ONE_SECOND / 10);
572 }
573 else
574 TaskSwitch ();
575 }
576
577 FinishTask (task);
578 return 0;
579 }
580
581 static inline sint32
readSoundSample(void * ptr,int sample_size)582 readSoundSample (void *ptr, int sample_size)
583 {
584 if (sample_size == sizeof (uint8))
585 return (*(uint8*)ptr - 128) << 8;
586 else
587 return *(sint16*)ptr;
588 }
589
590 // Graphs the current sound data for the oscilloscope.
591 // Includes a rudimentary automatic gain control (AGC) to properly graph
592 // the streams at different gain levels (based on running average).
593 // We use AGC because different pieces of music and speech can easily be
594 // at very different gain levels, because the game is moddable.
595 int
GraphForegroundStream(uint8 * data,sint32 width,sint32 height,bool wantSpeech)596 GraphForegroundStream (uint8 *data, sint32 width, sint32 height,
597 bool wantSpeech)
598 {
599 int source_num;
600 TFB_SoundSource *source;
601 TFB_SoundDecoder *decoder;
602 int channels;
603 int sample_size;
604 int full_sample;
605 int step;
606 long played_time;
607 long delta;
608 uint8 *sbuffer;
609 unsigned long pos;
610 int scale;
611 sint32 i;
612 // AGC variables
613 #define DEF_PAGE_MAX 28000
614 #define AGC_PAGE_COUNT 16
615 static int page_sum = DEF_PAGE_MAX * AGC_PAGE_COUNT;
616 static int pages[AGC_PAGE_COUNT] =
617 {
618 DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
619 DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
620 DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
621 DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
622 };
623 static int page_head;
624 #define AGC_FRAME_COUNT 8
625 static int frame_sum;
626 static int frames;
627 static int avg_amp = DEF_PAGE_MAX; // running amplitude (sort of) average
628 int target_amp;
629 int max_a;
630 #define VAD_MIN_ENERGY 100
631 long energy;
632
633
634 // Prefer speech to music
635 source_num = SPEECH_SOURCE;
636 source = &soundSource[source_num];
637 LockMutex (source->stream_mutex);
638 if (wantSpeech && (!source->sample ||
639 !source->sample->decoder || !source->sample->decoder->is_null))
640 { // Use speech waveform, since it's available
641 // Step is picked experimentally. Using step of 1 sample at 11025Hz,
642 // because human speech is mostly in the low frequencies, and it looks
643 // better this way.
644 step = 1;
645 }
646 else
647 { // We do not have speech -- use music waveform
648 UnlockMutex (source->stream_mutex);
649
650 source_num = MUSIC_SOURCE;
651 source = &soundSource[source_num];
652 LockMutex (source->stream_mutex);
653
654 // Step is picked experimentally. Using step of 4 samples at 11025Hz.
655 // It looks better this way.
656 step = 4;
657 }
658
659 if (!PlayingStream (source_num) || !source->sample
660 || !source->sample->decoder || !source->sbuffer
661 || source->sbuf_size == 0)
662 { // We don't have data to return, oh well.
663 UnlockMutex (source->stream_mutex);
664 return 0;
665 }
666 decoder = source->sample->decoder;
667
668 if (!audio_GetFormatInfo (decoder->format, &channels, &sample_size))
669 {
670 UnlockMutex (source->stream_mutex);
671 log_add (log_Debug, "GraphForegroundStream(): uknown format %u",
672 (unsigned)decoder->format);
673 return 0;
674 }
675 full_sample = channels * sample_size;
676
677 // See how far into the buffer we should be now
678 played_time = GetTimeCounter () - source->sbuf_lasttime;
679 delta = played_time * decoder->frequency * full_sample / ONE_SECOND;
680 // align delta to sample start
681 delta = delta & ~(full_sample - 1);
682
683 if (delta < 0)
684 {
685 log_add (log_Debug, "GraphForegroundStream(): something is messed"
686 " with timing, delta %ld", delta);
687 delta = 0;
688 }
689 else if (delta > (long)source->sbuf_size)
690 { // Stream decoder task has just had a heart attack, not much we can do
691 delta = 0;
692 }
693
694 // Step is in 11025 Hz units, so we need to adjust to source frequency
695 step = decoder->frequency * step / 11025;
696 if (step == 0)
697 step = 1;
698 step *= full_sample;
699
700 sbuffer = source->sbuffer;
701 pos = source->sbuf_head + delta;
702
703 // We are not basing the scaling factor on signal energy, because we
704 // want it to *look* pretty instead of sounding nice and even
705 target_amp = (height >> 1) >> 1;
706 scale = avg_amp / target_amp;
707
708 max_a = 0;
709 energy = 0;
710 for (i = 0; i < width; ++i, pos += step)
711 {
712 sint32 s;
713 int t;
714
715 pos %= source->sbuf_size;
716
717 s = readSoundSample (sbuffer + pos, sample_size);
718 if (channels > 1)
719 s += readSoundSample (sbuffer + pos + sample_size, sample_size);
720
721 energy += (s * s) / 0x10000;
722 t = abs(s);
723 if (t > max_a)
724 max_a = t;
725
726 s = (s / scale) + (height >> 1);
727 if (s < 0)
728 s = 0;
729 else if (s > height - 1)
730 s = height - 1;
731
732 data[i] = s;
733 }
734 energy /= width;
735
736 // Very basic VAD. We don't want to count speech pauses in the average
737 if (energy > VAD_MIN_ENERGY)
738 {
739 // Record the maximum amplitude (sort of)
740 frame_sum += max_a;
741 ++frames;
742 if (frames == AGC_FRAME_COUNT)
743 { // Got a full page
744 frame_sum /= AGC_FRAME_COUNT;
745 // Record the page
746 page_sum -= pages[page_head];
747 page_sum += frame_sum;
748 pages[page_head] = frame_sum;
749 page_head = (page_head + 1) % AGC_PAGE_COUNT;
750
751 frame_sum = 0;
752 frames = 0;
753
754 avg_amp = page_sum / AGC_PAGE_COUNT;
755 }
756 }
757
758 UnlockMutex (source->stream_mutex);
759 return 1;
760 }
761
762 // This function is normally called on the Starcon2Main thread
763 bool
SetMusicStreamFade(sint32 howLong,int endVolume)764 SetMusicStreamFade (sint32 howLong, int endVolume)
765 {
766 bool ret = true;
767
768 LockMutex (fade_mutex);
769
770 if (howLong < 0)
771 howLong = 0;
772
773 musicFadeStartTime = GetTimeCounter ();
774 musicFadeInterval = howLong;
775 musicFadeStartVolume = musicVolume;
776 musicFadeDelta = endVolume - musicFadeStartVolume;
777 if (!musicFadeInterval)
778 ret = false; // reject
779
780 UnlockMutex (fade_mutex);
781
782 return ret;
783 }
784
785 int
InitStreamDecoder(void)786 InitStreamDecoder (void)
787 {
788 fade_mutex = CreateMutex ("Stream fade mutex", SYNC_CLASS_AUDIO);
789 if (!fade_mutex)
790 return -1;
791
792 decoderTask = AssignTask (StreamDecoderTaskFunc, 1024,
793 "audio stream decoder");
794 if (!decoderTask)
795 return -1;
796
797 return 0;
798 }
799
800 void
UninitStreamDecoder(void)801 UninitStreamDecoder (void)
802 {
803 if (decoderTask)
804 {
805 ConcludeTask (decoderTask);
806 decoderTask = NULL;
807 }
808
809 if (fade_mutex)
810 {
811 DestroyMutex (fade_mutex);
812 fade_mutex = NULL;
813 }
814 }
815