/* ** 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) 2001 - All Rights Reserved // //---------------------------------------------------------------------------- // // Project: WPAudio // // Module: AUD // // File name: AUD_Windows.cpp // // Created: 5/09/01 // //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // Includes //---------------------------------------------------------------------------- #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include // 'assignment within condition expression'. #pragma warning(disable : 4706) //---------------------------------------------------------------------------- // Externals //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // Defines //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // Private Types //---------------------------------------------------------------------------- struct _aud_thread { char name[200]; volatile int quit; /* thread must quit */ volatile int count; volatile int leaving; /* thread is quiting */ volatile TimeStamp interval; /* itask interval */ volatile int running; /* thread is running */ HANDLE handle; /* threads handle (windows) */ void *data; AUD_ThreadCB *code; DWORD id; /* thread id (windows) */ CRITICAL_SECTION access; AudioServiceInfo update; ProfileCPU cpu; DBG_TYPE() }; DBG_DECLARE_TYPE ( AUD_Thread ) //---------------------------------------------------------------------------- // Private Data //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // Public Data //---------------------------------------------------------------------------- static HWND audioMainWindowHandle = NULL; //---------------------------------------------------------------------------- // Private Prototypes //---------------------------------------------------------------------------- static DWORD WINAPI AUD_service_thread ( VOID *data ); //---------------------------------------------------------------------------- // Private Functions //---------------------------------------------------------------------------- static DWORD WINAPI AUD_service_thread ( VOID *data ) { AUD_Thread *thread = (AUD_Thread *) data; if ( !thread ) { return 0; } AUD_ThreadBeginCriticalSection ( thread ); thread->running = TRUE; thread->leaving = FALSE; while ( !thread->quit ) { if ( thread->code ( thread, thread->data )) { AUD_ThreadEndCriticalSection ( thread ); Sleep ( (unsigned long ) thread->interval ); AUD_ThreadBeginCriticalSection ( thread ); } else { AUD_ThreadEndCriticalSection ( thread ); Sleep ( MSECONDS(5)); AUD_ThreadBeginCriticalSection ( thread ); } thread->count++; } AUD_ThreadEndCriticalSection ( thread ); thread->leaving = TRUE; return 0; } //---------------------------------------------------------------------------- // Public Functions //---------------------------------------------------------------------------- AUD_Thread* AUD_ThreadCreate ( const char *name, AUD_ThreadPriority pri, AUD_ThreadCB *code ) { AUD_Thread *thread; ALLOC_STRUCT ( thread, AUD_Thread ); DBG_SET_TYPE ( thread, AUD_Thread ); if ( !name ) { name = "no name given"; } strncpy ( thread->name, name, ArrayLen(thread->name)); ArrayEnd(thread->name); thread->quit = FALSE; thread->leaving = FALSE; thread->running = FALSE; thread->count = 0; thread->code = code; AudioServiceInfoInit ( &thread->update ); ProfileCPUInit ( thread->cpu ); InitializeCriticalSection ( &thread->access ); AUD_ThreadSetInterval ( thread, SECONDS(1)/30 ); if ( !(thread->handle = CreateThread ( NULL, 4*1024, AUD_service_thread, thread, 0, &thread->id ))) { DBGPRINTF (( "ERROR: Failed to create audio thread: '%s'\n", thread->name )); return NULL; } int set; switch (pri) { case AUD_THREAD_PRI_NORMAL: set = TRUE; break; case AUD_THREAD_PRI_HIGH: set = SetThreadPriority ( thread->handle, THREAD_PRIORITY_HIGHEST ); break; case AUD_THREAD_PRI_REALTIME: set = SetThreadPriority ( thread->handle, THREAD_PRIORITY_TIME_CRITICAL ); break; default: DBG_MSGASSERT ( FALSE, ("Illegal thread priority")); set = TRUE; } if ( !set ) { char buffer[1024]; FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, buffer, sizeof(buffer), NULL); DBGPRINTF (( "Unable to change the priority of the thread - %s\n", buffer )); msg_assert ( FALSE, ( "Unable to change the priority of the thread - %s\n", buffer )); } DBGPRINTF (( "Created audio thread: '%s'\n", thread->name )); return thread; } //============================================================================ // AUD_ThreadDestroy //============================================================================ void AUD_ThreadDestroy ( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); thread->quit = TRUE; while ( !thread->leaving ); WaitForSingleObject ( thread->handle, SECONDS(5)); CloseHandle ( thread->handle ); DeleteCriticalSection ( &thread->access ); DBGPRINTF (( "Removed audio thread: '%s'\n", thread->name )); DBG_INVALIDATE_TYPE ( thread ); AudioMemFree ( thread ); } //============================================================================ // AUD_ThreadBeginCriticalSection //============================================================================ void AUD_ThreadBeginCriticalSection ( AUD_Thread *thread) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); EnterCriticalSection ( &thread->access ); } //============================================================================ // AUD_ThreadEndCriticalSection //============================================================================ void AUD_ThreadEndCriticalSection ( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); LeaveCriticalSection ( &thread->access ); } //============================================================================ // AUD_ThreadSetData //============================================================================ void AUD_ThreadSetData ( AUD_Thread *thread, void *data ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); AUD_ThreadBeginCriticalSection ( thread ); thread->data = data; AUD_ThreadEndCriticalSection ( thread ); } //============================================================================ // AUD_ThreadSetInterval //============================================================================ void AUD_ThreadSetInterval ( AUD_Thread *thread, TimeStamp interval ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); AUD_ThreadBeginCriticalSection ( thread ); thread->interval = interval; AudioServiceSetInterval ( &thread->update, thread->interval ); AudioServiceSetMustServiceInterval ( &thread->update, thread->interval*4 ); AudioServiceSetResetInterval ( &thread->update, thread->interval*4 ); AUD_ThreadEndCriticalSection ( thread ); } //============================================================================ // AUD_ThreadGetInterval //============================================================================ TimeStamp AUD_ThreadGetInterval ( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); return thread->interval; } //============================================================================ // AUD_ThreadName //============================================================================ char* AUD_ThreadName( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); return thread->name; } //============================================================================ // AUD_ThreadCPUProfile //============================================================================ ProfileCPU* AUD_ThreadCPUProfile( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); return &thread->cpu; } //============================================================================ // AUD_ThreadAudioServiceInfo //============================================================================ AudioServiceInfo* AUD_ThreadServiceInfo( AUD_Thread *thread ) { DBG_ASSERT_TYPE ( thread, AUD_Thread ); return &thread->update; } //============================================================================ // AudioFormatReadWaveFile //============================================================================ int AudioFormatReadWaveFile ( File *file, AudioFormat *format, int *bytes ) { RIFF_HEADER rh; RIFF_CHUNK chunk; WAVEFORMATEX *wformat = NULL; int got_format = FALSE; int result = FALSE; //int mp3 = FALSE; DBG_ASSERT_TYPE ( format, AudioFormat ); if ( bytes ) { *bytes = 0; } if ( !file ) { goto done; } file->seek ( 0, File::START ); /* read wav info */ if ( file->read ( &rh, sizeof (rh)) != sizeof(rh) ) { DBGPRINTF (( "error: cannot read file\n" )); goto done; } if ( rh.form != vRIFF || rh.type != vWAVE ) { file->seek ( 0, File::START ); result = AudioFormatReadMP3File ( file, format, bytes ); goto done; } while ( file->read ( &chunk, sizeof (chunk) ) == sizeof(chunk) ) { switch ( chunk.type ) { case vFMT : if ( chunk.length < sizeof ( WAVEFORMATEX ) ) { wformat = (WAVEFORMATEX *) malloc ( sizeof(WAVEFORMATEX)); memset ( wformat, 0, sizeof ( WAVEFORMATEX) ); } else { wformat = (WAVEFORMATEX *) malloc ( chunk.length ); memset ( wformat, 0, chunk.length ); } file->read ( wformat, chunk.length ); wformat->cbSize = (ushort) chunk.length; got_format = TRUE; break; case vDATA: *bytes = chunk.length; goto got_data; default: file->seek ( chunk.length, File::CURRENT ); break; } } DBGPRINTF (( "no data chunk found\n" )); goto done; got_data: if ( !wformat ) { DBGPRINTF (( "no format chunk found\n" )); goto done; } format->SampleWidth = wformat->wBitsPerSample / 8; if ( wformat->wFormatTag == WAVE_FORMAT_IMA_ADPCM ) { format->cdata.adpcm.BlockSize = wformat->nBlockAlign; format->Compression = AUDIO_COMPRESS_IMA_ADPCM; format->SampleWidth = 2; } else if ( wformat->wFormatTag == WAVE_FORMAT_ADPCM ) { ADPCMWAVEFORMAT *aformat = (ADPCMWAVEFORMAT *)wformat; if ( aformat->wNumCoef != 7 && memcmp ( aformat->aCoef, MSADPCM_StdCoef, sizeof ( MSADPCM_StdCoef )) ) { //currently we only support MS ADPCM using the standard coef table goto done; } format->cdata.adpcm.BlockSize = wformat->nBlockAlign; format->Compression = AUDIO_COMPRESS_MS_ADPCM; format->SampleWidth = 2; } else if ( wformat->wFormatTag == WAVE_FORMAT_PCM ) { format->Compression = AUDIO_COMPRESS_NONE; } else if ( wformat->wFormatTag == WAVE_FORMAT_MPEGLAYER3 ) { result = AudioFormatReadMP3File ( file, format, NULL ); goto done; } else { goto done; } format->Channels = wformat->nChannels; format->BytesPerSecond = wformat->nAvgBytesPerSec; format->Rate = wformat->nSamplesPerSec; format->Flags = mAUDIO_FORMAT_PCM; AudioFormatUpdate ( format ); result = TRUE; done: if ( wformat ) { free ( wformat ); } return result; } //============================================================================ // WindowsDebugPrint //============================================================================ void WindowsDebugPrint( const char * lpOutputString ) { OutputDebugStringA ( lpOutputString ); } //============================================================================ // AudioSetWindowsHandle //============================================================================ void AudioSetWindowsHandle ( HWND hwnd ) { audioMainWindowHandle = hwnd; } //============================================================================ // AudioGetWindowsHandle //============================================================================ HWND AudioGetWindowsHandle ( void ) { return audioMainWindowHandle; }