5 #define Function _Function
7 #define WIN32_LEAN_AND_MEAN
8 #define MessageBox _MessageBox
24 public void AudioSetBalance(double percent)
27 streamingSound.balance = percent;
30 static HWAVEOUT hWaveOut;
33 public void OpenMixer()
36 WAVEFORMATEX waveFormat = { 0 };
37 waveFormat.nSamplesPerSec = 44100; //(cur_stream && cur_stream->audio_st && cur_stream->audio_st->codec) ? cur_stream->audio_st->codec->sample_rate : 44100;
38 waveFormat.wBitsPerSample = 16;
39 waveFormat.nChannels = 2;
40 waveFormat.cbSize = 0;
41 waveFormat.wFormatTag = WAVE_FORMAT_PCM;
42 waveFormat.nBlockAlign = (waveFormat.wBitsPerSample >> 3) * waveFormat.nChannels;
43 waveFormat.nAvgBytesPerSec = waveFormat.nBlockAlign * waveFormat.nSamplesPerSec;
45 waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, 0, 0, CALLBACK_NULL);
46 mixerOpen((HMIXER *)&hmx, (uint)hWaveOut, 0, 0, MIXER_OBJECTF_HWAVEOUT);
49 public void CloseMixer()
51 mixerClose((HMIXER)hmx);
52 waveOutClose(hWaveOut);
55 public bool AudioSetVolume(VolumeControl type, double percent)
58 if(type == application)
62 streamingSound.volume = percent;
70 // waveOutSetVolume((HWAVEOUT)0, (uint)(percent * 0xFFFF) | ((uint)(percent * 0xFFFF) << 16));
72 uMixerLine.cbStruct = sizeof(MIXERLINE);
73 uMixerLine.dwComponentType = (type == pcm) ? MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT : MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
74 if(mixerGetLineInfo(hmx, &uMixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
76 MIXERLINECONTROLS uMixerLineControls;
77 MIXERCONTROL mixerControl;
79 uMixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
80 uMixerLineControls.dwLineID = uMixerLine.dwLineID;
81 uMixerLineControls.dwControlID = MIXERCONTROL_CONTROLTYPE_VOLUME;
82 uMixerLineControls.cControls = 1;
83 uMixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
84 uMixerLineControls.pamxctrl = &mixerControl;
85 if(mixerGetLineControls(hmx, &uMixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
87 MIXERCONTROLDETAILS uDetails;
90 uDetails.cMultipleItems = 0;
91 uDetails.dwControlID = mixerControl.dwControlID;
92 uDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
93 uDetails.cbDetails = sizeof(uint);
94 uDetails.paDetails = &value;
95 uDetails.cChannels = 1;
96 value = (uint)(percent * mixerControl.Bounds.lMaximum);
97 mixerSetControlDetails(hmx, &uDetails, MIXER_SETCONTROLDETAILSF_VALUE);
105 public bool AudioGetVolume(VolumeControl type, double * percent)
108 if(type == application)
112 *percent = streamingSound.volume;
118 MIXERLINE uMixerLine;
120 uMixerLine.cbStruct = sizeof(MIXERLINE);
121 uMixerLine.dwComponentType = (type == pcm) ? MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT : MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
122 if(mixerGetLineInfo(hmx, &uMixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
124 MIXERLINECONTROLS uMixerLineControls;
125 MIXERCONTROL mixerControl;
127 uMixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
128 uMixerLineControls.dwLineID = uMixerLine.dwLineID;
129 uMixerLineControls.dwControlID = MIXERCONTROL_CONTROLTYPE_VOLUME;
130 uMixerLineControls.cControls = 1;
131 uMixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
133 uMixerLineControls.pamxctrl = &mixerControl;
134 if(mixerGetLineControls(hmx, &uMixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
136 MIXERCONTROLDETAILS uDetails;
139 uDetails.cMultipleItems = 0;
140 uDetails.dwControlID = mixerControl.dwControlID;
141 uDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
142 uDetails.cbDetails = sizeof(uint);
143 uDetails.paDetails = &value;
144 uDetails.cChannels = 1;
145 mixerGetControlDetails(hmx, &uDetails, MIXER_SETCONTROLDETAILSF_VALUE);
146 *percent = (double)value / mixerControl.Bounds.lMaximum;
154 public int OpenAudio(AudioSpec wanted, AudioSpec result)
156 #define NUM_PLAY_NOTIFICATIONS 16
157 uint nBlockAlign, dwNotifySize;
159 delete streamingSound;
161 g_pSoundManager.Initialize(wanted.windowHandle);
162 g_pSoundManager.SetPrimaryBufferFormat(wanted.channels, wanted.freq, wanted.bits);
166 nBlockAlign = wanted.bits / 8 * wanted.channels;
167 // dwNotifySize = 16384 / NUM_PLAY_NOTIFICATIONS; //wanted.freq * 3 * nBlockAlign / NUM_PLAY_NOTIFICATIONS;
168 dwNotifySize = wanted.freq / 2 / NUM_PLAY_NOTIFICATIONS; //wanted.freq * 3 * nBlockAlign / NUM_PLAY_NOTIFICATIONS;
169 dwNotifySize -= dwNotifySize % nBlockAlign;
171 result.size = dwNotifySize * NUM_PLAY_NOTIFICATIONS;
173 g_pSoundManager.InitStreaming(&streamingSound, NUM_PLAY_NOTIFICATIONS, dwNotifySize, dSoundThread.g_hNotificationEvent,
174 wanted.callback, wanted.userdata, wanted.channels, wanted.freq, wanted.bits);
177 streamingSound.volume = wanted.volume;
178 dSoundThread.bDone = false;
180 //dSoundThread.Create();
186 public void PauseAudio(int value)
191 dSoundThread.Create();
195 public void CloseAudio()
199 dSoundThread.bDone = true;
200 SetEvent(dSoundThread.g_hNotificationEvent);
203 delete streamingSound;
206 static bool PlayBuffer( bool bLooped )
212 if(streamingSound.Reset())
215 pDSB = streamingSound.GetBuffer( 0 );
217 SetEvent(dSoundThread.g_hNotificationEvent);
221 static class StreamingSound
223 IDirectSoundBuffer ** apDSBuffer;
224 IDirectSoundBuffer * pDSB;
228 uint dwCreationFlags;
232 uint dwNextWriteOffset;
233 bool bFillNextNotificationWithSilence;
234 void (*callback)(void * data, void * buffer, int len);
238 public property double volume
242 int vol = (int)(20 * log10(value) * 100);
243 vol = Max(-10000, vol);
244 IDirectSoundBuffer_SetVolume(apDSBuffer[0], vol);
253 public property double balance
257 int pan = -2173 + (int)(value * 2173 * 2);
258 IDirectSoundBuffer_SetPan(apDSBuffer[0], pan);
262 property uint dwNumBuffers
266 dwNumBuffers = value;
267 apDSBuffer = new0 IDirectSoundBuffer *[dwNumBuffers];
271 property IDirectSoundBuffer ** apDSBuffer
278 for( i=0; i<dwNumBuffers; i++ )
279 apDSBuffer[i] = value[i];
287 for( i=0; i<dwNumBuffers; i++ )
291 IDirectSoundBuffer_Release( apDSBuffer[i] );
296 delete( apDSBuffer );
299 int RestoreBuffer( IDirectSoundBuffer * pDSB, bool* pbWasRestored )
305 return CO_E_NOTINITIALIZED;
307 *pbWasRestored = false;
309 if( FAILED( hr = IDirectSoundBuffer_GetStatus(pDSB, &dwStatus) ) )
310 return 1; //DXUT_ERR( L"GetStatus", hr );
312 if( dwStatus & DSBSTATUS_BUFFERLOST )
314 // Since the app could have just been activated, then
315 // DirectSound may not be giving us control yet, so
316 // the restoring the buffer may fail.
317 // If it does, sleep until DirectSound gives us control.
320 hr = IDirectSoundBuffer_Restore(pDSB);
321 if( hr == DSERR_BUFFERLOST )
324 while( ( hr = IDirectSoundBuffer_Restore(pDSB) ) == DSERR_BUFFERLOST );
326 if( pbWasRestored != null )
327 *pbWasRestored = true;
333 return S_OK; //FALSE;
337 IDirectSoundBuffer * GetFreeBuffer()
343 for( i=0; i<dwNumBuffers; i++ )
348 IDirectSoundBuffer_GetStatus(apDSBuffer[i], &dwStatus );
349 if ( ( dwStatus & DSBSTATUS_PLAYING ) == 0 )
354 if( i != dwNumBuffers )
355 return apDSBuffer[ i ];
357 return apDSBuffer[ rand() % dwNumBuffers ];
360 IDirectSoundBuffer * GetBuffer( uint dwIndex )
362 if( apDSBuffer == null )
364 if( dwIndex >= dwNumBuffers )
367 return apDSBuffer[dwIndex];
370 int Play( uint dwPriority, uint dwFlags, uint lVolume, uint lFrequency, uint lPan )
375 //IDirectSoundBuffer * pDSB;
377 // For Streaming Sound
378 dwFlags |= DSBPLAY_LOOPING;
380 if( apDSBuffer == null )
381 return CO_E_NOTINITIALIZED;
383 pDSB = GetFreeBuffer();
386 return 1; //DXUT_ERR( L"GetFreeBuffer", E_FAIL );
388 // Restore the buffer if it was lost
389 if( FAILED( hr = RestoreBuffer( pDSB, &bRestored ) ) )
390 return 1; //DXUT_ERR( L"RestoreBuffer", hr );
394 // The buffer was restored, so we need to fill it with new data
395 if( FAILED( hr = FillBufferWithSound( pDSB, false ) ) )
396 return 1; //DXUT_ERR( L"FillBufferWithSound", hr );
399 if( dwCreationFlags & DSBCAPS_CTRLVOLUME )
401 //IDirectSoundBuffer_SetVolume(pDSB, lVolume);
404 if( lFrequency != -1 &&
405 (dwCreationFlags & DSBCAPS_CTRLFREQUENCY) )
407 IDirectSoundBuffer_SetFrequency(pDSB, lFrequency);
410 if( dwCreationFlags & DSBCAPS_CTRLPAN )
412 //IDirectSoundBuffer_SetPan(pDSB, lPan);
414 result = IDirectSoundBuffer_Play(pDSB, 0, dwPriority, dwFlags);
422 if( apDSBuffer == null )
423 return CO_E_NOTINITIALIZED;
425 for( i=0; i<dwNumBuffers; i++ )
426 hr |= IDirectSoundBuffer_Stop(apDSBuffer[i]);
431 bool HandleWaveStreamNotification( )
433 uint dwCurrentPlayPos;
435 uint dwBytesWrittenToBuffer;
436 void * pDSLockedBuffer = null;
437 void * pDSLockedBuffer2 = null;
438 uint dwDSLockedBufferSize;
439 uint dwDSLockedBufferSize2;
440 uint playCursor, writeCursor;
451 // Figure out how much data has been played so far. When we have played
452 // past the end of the file, we will either need to start filling the
453 // buffer with silence or starting reading from the beginning of the file,
454 // depending if the user wants to loop the sound
455 if(IDirectSoundBuffer_GetCurrentPosition(apDSBuffer[0], &dwCurrentPlayPos, null))
459 playCursor = (dwCurrentPlayPos / dwNotifySize);
460 writeCursor = dwNextWriteOffset;
462 //printf("Play Cursor : %d, Write Cursor %d\n", playCursor, writeCursor);
463 if(writeCursor > playCursor)
466 size = (dwDSBufferSize / dwNotifySize) - start;
467 for(c = start; c<start + size; c++)
471 //printf("Writing %d - %d\n", c, c);
472 IDirectSoundBuffer_Lock(apDSBuffer[0], c * dwNotifySize, 1 * dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L);
473 //printf("In callback...");
474 callback(data, pDSLockedBuffer, dwDSLockedBufferSize);
476 IDirectSoundBuffer_Unlock(apDSBuffer[0], pDSLockedBuffer, dwDSLockedBufferSize, null, 0 );
477 dwNextWriteOffset = 0;
484 for(c = start; c<start + size; c++)
488 //printf("Writing %d - %d\n", c, c);
489 IDirectSoundBuffer_Lock(apDSBuffer[0], c * dwNotifySize, 1 * dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L);
490 //printf("In callback...");
491 callback(data, pDSLockedBuffer, dwDSLockedBufferSize);
493 IDirectSoundBuffer_Unlock(apDSBuffer[0], pDSLockedBuffer, dwDSLockedBufferSize, null, 0 );
494 dwNextWriteOffset = (playCursor) % (dwDSBufferSize / dwNotifySize);
501 size = playCursor - writeCursor;
502 for(c = start; c<start + size; c++)
506 //printf("Writing %d - %d\n", c, c);
507 IDirectSoundBuffer_Lock(apDSBuffer[0], c * dwNotifySize, 1 * dwNotifySize, &pDSLockedBuffer, &dwDSLockedBufferSize, &pDSLockedBuffer2, &dwDSLockedBufferSize2, 0L);
508 //printf("In callback...");
509 callback(data, pDSLockedBuffer, dwDSLockedBufferSize);
511 IDirectSoundBuffer_Unlock(apDSBuffer[0], pDSLockedBuffer, dwDSLockedBufferSize, null, 0 );
512 dwNextWriteOffset = (playCursor) % (dwDSBufferSize / dwNotifySize);
515 //printf("Next Offset: %d\n", dwNextWriteOffset);
531 dwNextWriteOffset = 0;
532 bFillNextNotificationWithSilence = false;
534 // Restore the buffer if it was lost
535 if(RestoreBuffer( apDSBuffer[0], &bRestored ))
540 // The buffer was restored, so we need to fill it with new data
541 if(FillBufferWithSound( apDSBuffer[0], false))
545 result = IDirectSoundBuffer_SetCurrentPosition(apDSBuffer[0], 0L );
549 int FillBufferWithSound( IDirectSoundBuffer * pDSB, bool bRepeatWavIfBufferLarger )
552 void* pDSLockedBuffer = null; // Pointer to locked buffer memory
553 uint dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
554 uint dwWavDataRead = 0; // Amount of data read from the wav file
557 return CO_E_NOTINITIALIZED;
559 // Make sure we have focus, and we didn't just switch in from
560 // an app which had a DirectSound device
561 if(RestoreBuffer( pDSB, null ))
564 // Lock the buffer down
565 if(IDirectSoundBuffer_Lock(pDSB, 0, dwDSBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, null, null, 0L ))
568 callback(data, pDSLockedBuffer, dwDSLockedBufferSize);
570 dwWavDataRead = dwDSLockedBufferSize;
572 printf("Filling 16 spots\n");
573 IDirectSoundBuffer_Unlock(pDSB, pDSLockedBuffer, dwDSLockedBufferSize, null, 0 );
575 //dwNextWriteOffset = dwNotifySize*2;
580 static class CSoundManager
587 IDirectSound_Release( pDS );
590 int Initialize( void * hWnd )
596 IDirectSound_Release( pDS );
600 // Create IDirectSound using the primary sound device
601 if( FAILED( hr = DirectSoundCreate8( null, &pDS, null ) ) )
602 return 1; //DXUT_ERR( L"DirectSoundCreate8", hr );
604 // Set DirectSound coop level
605 if( FAILED( hr = IDirectSound_SetCooperativeLevel(pDS, hWnd, DSSCL_PRIORITY ) ) )
606 return 1; //DXUT_ERR( L"SetCooperativeLevel", hr );
612 int SetPrimaryBufferFormat( uint dwPrimaryChannels, uint dwPrimaryFreq, uint dwPrimaryBitRate )
615 IDirectSoundBuffer * pDSBPrimary = null;
620 return CO_E_NOTINITIALIZED;
622 // Get the primary buffer
623 ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
624 dsbd.dwSize = sizeof(DSBUFFERDESC);
625 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
626 dsbd.dwBufferBytes = 0;
627 dsbd.lpwfxFormat = null;
629 if( FAILED( hr = IDirectSound_CreateSoundBuffer(pDS, &dsbd, &pDSBPrimary, null ) ) )
630 return 1; // DXUT_ERR( L"CreateSoundBuffer", hr );
632 ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
633 wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM;
634 wfx.nChannels = (WORD) dwPrimaryChannels;
635 wfx.nSamplesPerSec = (uint) dwPrimaryFreq;
636 wfx.wBitsPerSample = (WORD) dwPrimaryBitRate;
637 wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels);
638 wfx.nAvgBytesPerSec = (uint) (wfx.nSamplesPerSec * wfx.nBlockAlign);
640 if( FAILED( hr = IDirectSoundBuffer_SetFormat(pDSBPrimary, &wfx) ) )
641 return 1; //DXUT_ERR( L"SetFormat", hr );
645 IDirectSoundBuffer_Release( pDSBPrimary );
652 bool InitStreaming(StreamingSound * ppStreamingSound, uint dwNotifyCount, uint dwNotifySize, void * hNotifyEvent,
653 void (*callback)(void * data, void * buffer, int len), void * data, int nChannels, int freq,int bits)
656 IDirectSoundBuffer * pDSBuffer = null;
657 uint dwDSBufferSize = 0;
658 DSBPOSITIONNOTIFY * aPosNotify = null;
659 IDirectSoundNotify * pDSNotify = null;
665 if(!ppStreamingSound || !hNotifyEvent)
668 dwDSBufferSize = dwNotifySize * dwNotifyCount;
670 ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
671 dsbd.dwSize= sizeof(DSBUFFERDESC);
672 dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
673 dsbd.dwBufferBytes = dwDSBufferSize;
674 dsbd.guid3DAlgorithm = DS3DALG_DEFAULT;
675 dsbd.lpwfxFormat = new0 WAVEFORMATEX[1];
676 dsbd.lpwfxFormat->wFormatTag = WAVE_FORMAT_PCM;
677 dsbd.lpwfxFormat->nChannels = (uint16)nChannels;
678 dsbd.lpwfxFormat->nSamplesPerSec = freq;
679 dsbd.lpwfxFormat->wBitsPerSample = (uint16)bits;
680 dsbd.lpwfxFormat->nBlockAlign = dsbd.lpwfxFormat->nChannels * dsbd.lpwfxFormat->wBitsPerSample / 8;
681 dsbd.lpwfxFormat->nAvgBytesPerSec = dsbd.lpwfxFormat->nSamplesPerSec * dsbd.lpwfxFormat->nBlockAlign;
683 if(IDirectSound_CreateSoundBuffer(pDS, &dsbd, &pDSBuffer, null))
685 delete dsbd.lpwfxFormat;
688 delete dsbd.lpwfxFormat;
690 if(IDirectSoundBuffer_QueryInterface(pDSBuffer, &IID_IDirectSoundNotify, (void**)&pDSNotify))
696 aPosNotify = new DSBPOSITIONNOTIFY[ dwNotifyCount ];
697 if( aPosNotify == null )
700 for( i = 0; i < dwNotifyCount; i++ )
702 aPosNotify[i].dwOffset = (dwNotifySize * i) + dwNotifySize - 1;
703 aPosNotify[i].hEventNotify = hNotifyEvent;
706 if(IDirectSoundNotify_SetNotificationPositions(pDSNotify, dwNotifyCount, aPosNotify ))
709 IDirectSoundNotify_Release(pDSNotify);
710 delete( aPosNotify );
715 IDirectSoundNotify_Release(pDSNotify);
718 *ppStreamingSound = StreamingSound
720 dwCreationFlags = DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME, dwNumBuffers = 1, dwNotifySize = dwNotifySize, dwDSBufferSize = dwDSBufferSize, apDSBuffer = &pDSBuffer,
722 callback = callback, data = data
724 // ppStreamingSound->FillBufferWithSound( pDSBuffer, false );
729 static DSoundThread dSoundThread { };
731 static CSoundManager g_pSoundManager { };
732 static StreamingSound streamingSound;
734 static class DSoundThread : Thread
736 void * g_hNotificationEvent;
740 g_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
744 CloseHandle( g_hNotificationEvent );
749 SetPriority(timeCritical);
752 WaitForSingleObject(g_hNotificationEvent, INFINITE);
757 streamingSound.FillBufferWithSound( streamingSound.GetBuffer( 0 ), false );
758 streamingSound.Play( 0, 0, 0, -1, 0);
761 else if(!streamingSound.HandleWaveStreamNotification())