/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** 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 3 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, see . */ /***************************************************************************** ** ** ** Westwood Studios Pacific. ** ** ** ** Confidential Information ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Dune Emperor ** ** ** ** Module: (_) ** ** ** ** Version: $ID$ ** ** ** ** File name: audplay.cpp ** ** ** ** Created by: 04/??/00 TR ** ** ** ** Description: ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include // 'assignment within condition expression'. #pragma warning(disable : 4706) DBG_DECLARE_TYPE ( AudioStreamer ); /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ #define AS_PLAYING 0x00000001 #define AS_PAUSED 0x00000002 #define AS_FILL 0x00000004 // service thread should fill this stream #define AS_NO_FILE_CLOSE 0x00000080 // file was not opened by us #define AS_OPEN 0x00000010 // we have a file to play #define AS_LOOPING 0x00000020 // seamlessly loop stream #define MIN_FRAME_SIZE (4*1024) /***************************************************************************** ** Private Types ** *****************************************************************************/ typedef struct AudioStreamerTag { ListNode nd; volatile int flags; AudioDevice *device; AudioChannel *channel; TimeStamp buffering; TimeStamp end_time_stamp; TimeStamp start_time_stamp; STM_STREAM *stream; STM_ACCESS *in; STM_ACCESS *out; AudioSample sample; AudioFormat format; File *file; Lock pause; Lock lock; int pending_bytes; // number of bytes submitted to audio playback int total_bytes; int data_start; int bytes_left; int frame_size; int stream_pos; int max_volume; char stream_name[80]; #ifndef IG_FINAL_RELEASE char name[200]; #endif DBG_TYPE() } AudioStreamer; /***************************************************************************** ** Private Data ** *****************************************************************************/ static AUD_Thread *thread = NULL; static ListHead streams; static int initialized = FALSE; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ static int streamNextFrame ( AudioChannel *channel ); static int streamSampleDone ( AudioChannel *channel ); static int streamStop ( AudioChannel *channel ); static void service_stream ( AudioStreamer *as ); static AUD_ThreadCB service_streams; static int recalcBuffering ( AudioStreamer *as, TimeStamp buffering, AudioFormat *format ); /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ static int streamNextFrame ( AudioChannel *channel ) { AudioStreamer *as = (AudioStreamer*) channel->Data; DBG_ASSERT_TYPE ( as, AudioStreamer ); STM_AccessAdvance ( as->out, as->pending_bytes ); // mark these bytes as having been used as->stream_pos += as->pending_bytes; if ( as->stream_pos > as->total_bytes ) { as->stream_pos %= as->total_bytes; } STM_AccessGetBlock ( as->out ); if ( as->frame_size > as->out->Block.Bytes ) { as->pending_bytes = as->out->Block.Bytes; } else { as->pending_bytes = as->frame_size; } channel->bytesInFrame = as->pending_bytes; channel->bytesRemaining = channel->bytesInFrame; channel->frameData = (char *) as->out->Block.Data; return vNO_ERROR; } /******************************************************************/ /* */ /* */ /******************************************************************/ static int streamSampleDone ( AudioChannel *channel ) { return streamStop ( channel ); } /******************************************************************/ /* */ /* */ /******************************************************************/ static int streamStop ( AudioChannel *channel ) { AudioStreamer *as = (AudioStreamer*) channel->Data; DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( as->flags & (AS_PLAYING|AS_PAUSED)) { as->end_time_stamp = AudioGetTime(); } FLAGS_CLEAR ( as->flags, AS_PLAYING | AS_PAUSED | AS_FILL ); LockInit ( &as->pause ); return vNO_ERROR; } /******************************************************************/ /* */ /* */ /******************************************************************/ static int streamFill ( AudioStreamer *as, TimeStamp amount ) { int bytes_to_fill; int transfered = 0; // initialize to prevent compiler warning int bytes; int result; int looping; DBG_ASSERT_TYPE ( as, AudioStreamer ); AUD_ThreadBeginCriticalSection ( thread ); looping = ( as->flags & AS_LOOPING ) ; if ( amount ) { bytes_to_fill = AudioFormatBytes ( &as->format, amount ); } else { bytes_to_fill = (int) STM_AccessTotalBytes ( as->in ); } while ( bytes_to_fill ) { if ( (bytes = bytes_to_fill) > as->bytes_left ) { bytes = as->bytes_left; } result = STM_AccessFileTransfer ( as->in, as->file, bytes, &transfered ); as->bytes_left -= transfered; bytes_to_fill -= transfered; if ( as->bytes_left <= 0 || result == STM_EOF || result == STM_FAIL ) { if ( looping ) { // seek to start of stream and continue filling as->file->seek ( as->data_start, File::START ); as->bytes_left = as->total_bytes; } else { FLAGS_CLEAR ( as->flags, AS_FILL ); as->bytes_left = 0; bytes_to_fill = 0; } } } AUD_ThreadEndCriticalSection ( thread ); return transfered; } /******************************************************************/ /* */ /* */ /******************************************************************/ static void service_stream ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( ! (as->flags & AS_FILL ) ) { return; } streamFill ( as, 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ static int service_streams ( AUD_Thread *thread, void *data ) { AudioStreamer *as; ListHead *list = (ListHead *) data; if ( list ) { as = (AudioStreamer*) ListFirstItem ( list ); while ( as ) { service_stream ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } return TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ static int recalcBuffering ( AudioStreamer *as, TimeStamp buffering, AudioFormat *format ) { uint size; STM_STREAM *stm; DBG_ASSERT_TYPE ( as, AudioStreamer ); stm = as->stream; DBG_ASSERT ( stm ); DBG_ASSERT ( format ); size = (uint) AudioFormatBytes ( format, buffering ); as->frame_size = AudioFormatBytes ( format, AudioChannelFrameTime ( as->channel )*2 ); if ( as->frame_size < MIN_FRAME_SIZE ) { as->frame_size = MIN_FRAME_SIZE; } if ( STM_StreamTotalBytes ( stm ) != size ) { STM_SBUFFER *buffer; STM_StreamDestroyBuffers ( stm ); buffer = STM_SBufferCreate ( size, 0 ); if ( buffer ) { STM_StreamAddSBuffer ( stm, buffer ); } } return STM_StreamTotalBytes ( stm ) == size; } /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerInit ( void ) { ListInit ( &streams ); thread = AUD_ThreadCreate ( "Streamer Thread", AUD_THREAD_PRI_HIGH, service_streams ); if ( thread ) { AUD_ThreadSetInterval ( thread, SECONDS(1) ); AUD_ThreadSetData ( thread, &streams ); } initialized = TRUE; return TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerDeinit ( void ) { AudioStreamer *as; if ( !initialized ) { return ; } msg_assert ( ListIsEmpty ( &streams), ("Not all streams were destroyed")); while ( as = (AudioStreamer*) ListFirstItem ( &streams )) { AudioStreamerStop ( as ); AudioStreamerDestroy ( as ); } if ( thread ) { AUD_ThreadDestroy ( thread ); thread = NULL; } initialized = FALSE; } /******************************************************************/ /* */ /* */ /******************************************************************/ AudioStreamer* AudioStreamerCreate ( AudioDevice *dev, TimeStamp buffering ) { AudioStreamer *as; if ( !dev || !initialized ) { return NULL; } if ( buffering <=0 || buffering < SECONDS(7)) { buffering = SECONDS(7); } ALLOC_STRUCT ( as, AudioStreamer ); DBG_SET_TYPE ( as, AudioStreamer ); ListNodeInit ( &as->nd ); as->device = dev; as->channel = AudioDeviceReserveChannel ( dev, AUDIO_CHANNEL_TYPE_USER ); as->buffering = buffering; AudioSampleInit ( &as->sample ); AudioFormatInit ( &as->format ); LockInit ( &as->pause ); LockInit ( &as->lock ); strcpy ( as->stream_name, "Audio Stream" ); if ( !as->channel ) { msg_assert ( FALSE, ("Unable to reserve channel for streamer")); goto error; } as->max_volume = AUDIO_VOLUME_MAX; as->channel->GroupAttribs = NULL; as->channel->SfxAttribs = NULL; as->channel->CompAttribs = NULL; as->channel->FadeAttribs = NULL; as->channel->Data = (void *) as; as->channel->CB_NextFrame = streamNextFrame; as->channel->CB_SampleDone = streamSampleDone; as->channel->CB_Stop = streamStop; as->stream = STM_StreamCreate ( ); if ( !as->stream ) { goto error; } AUD_ThreadBeginCriticalSection ( thread ); ListNodeAppend ( &streams, &as->nd ); AUD_ThreadEndCriticalSection ( thread ); return as; error: AudioStreamerDestroy ( as ); return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerDestroy ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); msg_assert ( !(as->flags & AS_PLAYING), ("Trying to destroy as stream while is is still playing")); if ( ListNodeInList ( &as->nd )) { AUD_ThreadBeginCriticalSection ( thread ); ListNodeRemove ( &as->nd ); AUD_ThreadEndCriticalSection ( thread ); } AudioStreamerClose ( as ); if ( as->in ) { STM_AccessRelease ( as->in ); as->in = NULL; } if ( as->out ) { STM_AccessRelease ( as->out ); as->out = NULL; } if ( as->stream ) { STM_StreamDestroy ( as->stream ); } if ( as->channel ) { AudioChannelRelease ( as->channel ); } if ( as->file ) { if ( ! (as->flags & AS_NO_FILE_CLOSE )) { as->file->close() ; } as->file = NULL; } DBG_INVALIDATE_TYPE ( as ); MEM_Free ( as ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetAttribs ( AudioStreamer *as, AudioAttribs *attribs ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); as->channel->GroupAttribs = attribs; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetFadeAttribs ( AudioStreamer *as, AudioAttribs *attribs ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); as->channel->FadeAttribs = attribs; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerOpen ( AudioStreamer *as, File *file) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( Locked ( &as->lock ) || !file ) { return FALSE; } AUD_ThreadBeginCriticalSection ( thread ); AudioStreamerClose ( as ); FLAGS_SET ( as->flags, AS_NO_FILE_CLOSE ); // we did not open this file so do not close it as->file = file; if ( !AudioFormatReadWaveFile ( as->file, &as->format, &as->total_bytes )) { goto error; } strncpy ( as->name, file->getName(), ArrayLen ( as->name ) ); ArrayEnd ( as->name ); AudioSampleSetName ( &as->sample, as->name ); as->data_start = as->file->position(); as->stream_pos = 0; FLAGS_SET ( as->flags, AS_OPEN ); AUD_ThreadEndCriticalSection ( thread ); return TRUE; error: if ( as->file ) { as->file = NULL; } FLAGS_CLEAR ( as->flags, AS_OPEN ); AUD_ThreadEndCriticalSection ( thread ); return FALSE; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerOpen ( AudioStreamer *as, const char *filename ) { File *file; int opened; file = TheFileSystem->open ( (char *)filename, File::READ|File::BINARY ); if ( !file ) { return FALSE; } opened = AudioStreamerOpen ( as, file ); if ( !opened ) { file->close(); return FALSE; } // we did open this file so automatically close when stream is closed FLAGS_CLEAR ( as->flags, AS_NO_FILE_CLOSE ); return TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerStart ( AudioStreamer *as ) { TimeStamp time; DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( Locked ( &as->lock ) || !(as->flags & AS_OPEN )) { return FALSE; } AUD_ThreadBeginCriticalSection ( thread ); as->stream_pos = AudioFormatSeekToPos ( as->file, &as->format, as->stream_pos, as->data_start ); as->bytes_left = as->total_bytes - as->stream_pos; if ( as->in ) { STM_AccessRelease ( as->in ); as->in = NULL; } if ( as->out ) { STM_AccessRelease ( as->out ); as->out = NULL; } if ( !recalcBuffering ( as, as->buffering, &as->format ) ) { goto error; } STM_StreamReset ( as->stream); as->in = STM_StreamAcquireAccess ( as->stream, vSTM_ACCESS_ID_IN ); as->out = STM_StreamAcquireAccess ( as->stream, vSTM_ACCESS_ID_OUT ); if ( !as->in || !as->out ) { goto error; } time = max ( AudioChannelBufferTime ( as->channel )*2, AUD_ThreadGetInterval(thread)*4); time = max ( time, (TimeStamp)SECONDS(4)); if ( !streamFill ( as, time )) { goto error; } STM_AccessGetBlock ( as->out ); as->sample.Data = (char *) as->out->Block.Data; as->sample.Bytes = AudioFormatBytes ( &as->format, AudioChannelFrameTime ( as->channel )); if ( as->sample.Bytes > as->out->Block.Bytes ) { as->sample.Bytes = as->out->Block.Bytes ; } as->sample.Format = &as->format; as->pending_bytes = as->sample.Bytes; //AudioAttribsSetVolume ( &as->channel->ChannelAttribs, AUDIO_VOLUME_MAX ); AudioChannelSetSample ( as->channel, &as->sample ); FLAGS_SET ( as->flags, AS_PLAYING ); if ( AudioChannelStart ( as->channel ) != vNO_ERROR ) { FLAGS_CLEAR ( as->flags, AS_PLAYING ); STM_AccessRelease ( as->in ); as->in = NULL; STM_AccessRelease ( as->out ); as->out = NULL; goto error; } as->start_time_stamp = AudioGetTime(); as->end_time_stamp = as->start_time_stamp; FLAGS_SET ( as->flags, AS_FILL ); AUD_ThreadEndCriticalSection ( thread ); return TRUE; error: AUD_ThreadEndCriticalSection ( thread ); return FALSE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerStop ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( NotLocked ( &as->lock ) ) { if ( as->channel ) { AUD_ThreadBeginCriticalSection ( thread ); AudioChannelStop ( as->channel ); AUD_ThreadEndCriticalSection ( thread ); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerClose ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( NotLocked ( &as->lock ) ) { AUD_ThreadBeginCriticalSection ( thread ); AudioStreamerStop ( as ); if ( as->file ) { if ( ! (as->flags & AS_NO_FILE_CLOSE )) { as->file->close(); } as->file = NULL; } FLAGS_CLEAR ( as->flags, AS_OPEN | AS_NO_FILE_CLOSE | AS_LOOPING ) ; AUD_ThreadEndCriticalSection ( thread ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerLock ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); LockAcquire ( &as->lock ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerUnlock ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); LockRelease ( &as->lock ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerPause ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( NotLocked ( &as->pause )) { if ( as->channel ) { AUD_ThreadBeginCriticalSection ( thread ); AudioChannelPause ( as->channel ); if ( as->flags & AS_PLAYING ) { FLAGS_SET ( as->flags, AS_PAUSED ); FLAGS_CLEAR ( as->flags, AS_PLAYING ); } AUD_ThreadEndCriticalSection ( thread ); } } LockAcquire ( &as->pause ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerResume ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( Locked ( &as->pause ) ) { LockRelease ( &as->pause ); if ( NotLocked ( &as->pause )) { if ( as->channel ) { AUD_ThreadBeginCriticalSection ( thread ); if ( as->flags & AS_PAUSED ) { FLAGS_CLEAR ( as->flags, AS_PAUSED ); FLAGS_SET ( as->flags, AS_PLAYING ); } AudioChannelResume ( as->channel ); AUD_ThreadEndCriticalSection ( thread ); } } } } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerIsPlaying ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as , AudioStreamer ); return as->flags & AS_PLAYING ; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerActive ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as , AudioStreamer ); return as->flags & (AS_PLAYING|AS_PAUSED) ; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetName ( AudioStreamer *as, const char *name ) { DBG_ASSERT_TYPE ( as , AudioStreamer ); strncpy ( as->stream_name, name, sizeof(as->stream_name)); as->stream_name[ sizeof(as->stream_name) -1 ] = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ const char *AudioStreamerName ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as , AudioStreamer ); return as->stream_name; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerFadeIn ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); AudioAttribsAdjustVolume ( &as->channel->ChannelAttribs, as->max_volume ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerFadeOut ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); AudioAttribsAdjustVolume ( &as->channel->ChannelAttribs, AUDIO_VOLUME_MIN ); } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerIsFading ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return AudioStreamerIsPlaying ( as ) && !AudioAttribsVolumeAdjusted ( &as->channel->ChannelAttribs ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerWaitForFade ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); TimeStamp time_out = AudioGetTime() + as->channel->ChannelAttribs.VolumeLevel.duration; while ( AudioStreamerIsFading ( as ) && (AudioGetTime() < time_out) ) { //ServiceAllAudio (); TODO: trolfs break; } } /******************************************************************/ /* */ /* */ /******************************************************************/ TimeStamp AudioStreamerEndTimeStamp ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return as->end_time_stamp; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerStarve ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); AUD_ThreadBeginCriticalSection ( thread ); while ( as ) { FLAGS_CLEAR ( as->flags, AS_FILL ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } AUD_ThreadEndCriticalSection ( thread ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerStopAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } AUD_ThreadBeginCriticalSection ( thread ); as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerStop ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } AUD_ThreadEndCriticalSection ( thread ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerCloseAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerClose ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerCloseAllStreamsUsingFile ( File *file ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { if ( as->file == file ) { AudioStreamerClose ( as ); } as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerLockAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerLock ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerUnlockAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerUnlock ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerPauseAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerPause ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerResumeAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerResume ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerFadeOutAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerFadeOut ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerFadeInAllStreams ( void ) { AudioStreamer *as; if ( !initialized ) { return; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { AudioStreamerFadeIn ( as ); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerAllFaded ( void ) { AudioStreamer *as; if ( !initialized ) { return true; } as = (AudioStreamer*) ListFirstItem ( &streams ); while ( as ) { if ( AudioStreamerIsFading ( as ) ) { return FALSE; } as = (AudioStreamer*) ListNextItem ( &as->nd ); } return TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerGetPosition ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return as->stream_pos; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetPosition ( AudioStreamer *as, int pos ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); int playing = AudioStreamerIsPlaying ( as ); AudioStreamerStop ( as ); as->stream_pos = pos; if ( playing ) { AudioStreamerStart ( as ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ TimeStamp AudioStreamerGetTimePosition ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( as->flags & AS_OPEN ) { return AudioFormatTime ( &as->format, as->stream_pos ); } return 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetTimePosition ( AudioStreamer *as, TimeStamp time ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( as->flags & AS_OPEN ) { AudioStreamerSetPosition ( as, AudioFormatBytes ( &as->format, time )); } else { AudioStreamerSetPosition ( as, 0 ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetVolume ( AudioStreamer *as, int volume ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); AudioChannelSetVolume ( as->channel, volume ); } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerGetVolume ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return AudioChannelGetVolume ( as->channel ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetMaxVolume ( AudioStreamer *as, int volume ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); if ( volume > AUDIO_VOLUME_MAX ) { volume = AUDIO_VOLUME_MAX; } else if ( volume < AUDIO_VOLUME_MIN ) { volume = AUDIO_VOLUME_MIN; } as->max_volume = volume; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerGetMaxVolume ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return as->max_volume; } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetLooping ( AudioStreamer *as, int loop ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); AUD_ThreadBeginCriticalSection ( thread ); if ( loop ) { FLAGS_SET ( as->flags, AS_LOOPING ); } else { FLAGS_CLEAR ( as->flags, AS_LOOPING ); } AUD_ThreadEndCriticalSection ( thread ); } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerIsLooping ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return as->flags & AS_LOOPING; } /******************************************************************/ /* */ /* */ /******************************************************************/ int AudioStreamerIsAudible ( AudioStreamer *as ) { DBG_ASSERT_TYPE ( as, AudioStreamer ); return AudioChannelIsAudible ( as->channel ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerSetPriority ( AudioStreamer *as, Int priority ) { } #define vRANGE 15 /******************************************************************/ /* */ /* */ /******************************************************************/ static void streamDump ( AudioStreamer *as, int index, void (*print) ( char *text) ) { int range = vRANGE; int in; int total; int percent; char buffer[200]; uint out_pos; uint in_pos; if ( !as->stream ) { sprintf ( buffer, "%02d: Invalid Stream\n", index); print ( buffer ); return; } total = STM_StreamTotalBytes ( as->stream ); in = 0; out_pos = 0; in_pos = 0; percent = 0; if ( total ) { in = (STM_StreamTotalBytesIn ( as->stream )*range)/total; if ( as->out ) { out_pos = (STM_AccessPosition ( as->out ) * range)/total; } if ( as->in ) { in_pos = (STM_AccessPosition ( as->in ) * range)/total; } percent = (STM_StreamTotalBytesIn ( as->stream )*100)/total; } if ( out_pos == in_pos ) { if ( in == 0) { if ( out_pos < vRANGE-1 ) { in_pos = (out_pos + 1); } else { in_pos = 0; } } else { if ( out_pos > 0 ) { in_pos = (out_pos - 1); } else { in_pos = vRANGE-1 ; } } } char gauge[vRANGE+1]; uint i; for ( i =0; i < vRANGE; i++) { gauge[i] = ' '; } gauge[vRANGE] = 0; if ( out_pos > in_pos ) { for ( i =out_pos+1; i < vRANGE; i++) gauge[i] = '*'; for ( i =0; i < in_pos; i++) gauge[i] = '*'; } else { for ( i =out_pos+1; i < in_pos; i++) gauge[i] = '*'; } gauge[in_pos] = '>'; gauge[out_pos] = '<'; char name[sizeof(as->name)+1]; char *ptr ; int seconds, minutes; strcpy ( name, as->name ); ptr = strrchr ( name, '.' ); if ( ptr ) { *ptr = 0; } ptr = strrchr ( name, '\\' ); if ( ptr ) { strcpy ( name, ptr ); } TimeStamp time = AudioFormatTime ( &as->format, as->total_bytes - as->stream_pos ); minutes = (int) IN_MINUTES(time); time -= MINUTES(minutes); seconds = (int) IN_SECONDS(time); time -= SECONDS(seconds); time = (int) IN_MSECONDS(time); sprintf ( buffer, "%.12s %4d Kb %.31s \n", AudioStreamerName( as ), total/1024, name); print ( buffer ); sprintf ( buffer, " Status: %s %s %s %d:%02d:%03d \n", as->flags & AS_PLAYING ? "Play " : (as->flags & AS_PAUSED ? "Paused" : "Stop ") , as->flags & AS_FILL ? "Fill" : " ", Locked( &as->lock ) ? "Locked" : " ", minutes, seconds, time ); print ( buffer ); sprintf ( buffer, " Buffer: |%s| %3d %%\n", gauge, percent ); print ( buffer ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void AudioStreamerDump ( void (*print) ( char *text )) { AudioStreamer *as; char buffer[200]; char temp[100]; int index = 0; if ( !initialized ) { print ("\nAudio Streamer not initialized\n" ); return ; } print ("\nAudio Streamer\n" ); AudioServiceInfoDump ( AUD_ThreadServiceInfo (thread), temp ); sprintf ( buffer, "service:%s\n", temp ); print (buffer ); as = (AudioStreamer*) ListFirstItem ( &streams ); if ( as ) { while ( as ) { streamDump ( as , index++, print); as = (AudioStreamer*) ListNextItem ( &as->nd ); } } else { print ("No streams created\n" ); } }