//-------------------------------------------------------------------------------------- // File: SoundEffectInstance.cpp // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "pch.h" #include "SoundCommon.h" using namespace DirectX; //====================================================================================== // SoundEffectInstance //====================================================================================== // Internal object implementation class. class SoundEffectInstance::Impl : public IVoiceNotify { public: Impl( _In_ AudioEngine* engine, _In_ SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags ) : mBase(), mEffect( effect ), mWaveBank( nullptr ), mIndex( 0 ), mLooped( false ) { assert( engine != 0 ); engine->RegisterNotify( this, false ); assert( mEffect != 0 ); mBase.Initialize( engine, effect->GetFormat(), flags ); } Impl( _In_ AudioEngine* engine, _In_ WaveBank* waveBank, uint32_t index, SOUND_EFFECT_INSTANCE_FLAGS flags ) : mBase(), mEffect( nullptr ), mWaveBank( waveBank ), mIndex( index ), mLooped( false ) { assert( engine != 0 ); engine->RegisterNotify( this, false ); char buff[64]; auto wfx = reinterpret_cast( buff ); assert( mWaveBank != 0 ); mBase.Initialize( engine, mWaveBank->GetFormat( index, wfx, 64 ), flags ); } virtual ~Impl() { mBase.DestroyVoice(); if ( mBase.engine ) { mBase.engine->UnregisterNotify( this, false, false ); mBase.engine = nullptr; } } void Play( bool loop ); // IVoiceNotify virtual void __cdecl OnBufferEnd() override { // We don't register for this notification for SoundEffectInstances, so this should not be invoked assert( false ); } virtual void __cdecl OnCriticalError() override { mBase.OnCriticalError(); } virtual void __cdecl OnReset() override { mBase.OnReset(); } virtual void __cdecl OnUpdate() override { // We do not register for update notification assert(false); } virtual void __cdecl OnDestroyEngine() override { mBase.OnDestroy(); } virtual void __cdecl OnTrim() override { mBase.OnTrim(); } virtual void __cdecl GatherStatistics( AudioStatistics& stats ) const override { mBase.GatherStatistics(stats); } SoundEffectInstanceBase mBase; SoundEffect* mEffect; WaveBank* mWaveBank; uint32_t mIndex; bool mLooped; }; void SoundEffectInstance::Impl::Play( bool loop ) { if ( !mBase.voice ) { if ( mWaveBank ) { char buff[64]; auto wfx = reinterpret_cast( buff ); mBase.AllocateVoice( mWaveBank->GetFormat( mIndex, wfx, 64) ); } else { assert( mEffect != 0 ); mBase.AllocateVoice( mEffect->GetFormat() ); } } if ( !mBase.Play() ) return; // Submit audio data for STOPPED -> PLAYING state transition XAUDIO2_BUFFER buffer; #if defined(_XBOX_ONE) || (_WIN32_WINNT < _WIN32_WINNT_WIN8) || (_WIN32_WINNT >= _WIN32_WINNT_WIN10) bool iswma = false; XAUDIO2_BUFFER_WMA wmaBuffer; if ( mWaveBank ) { iswma = mWaveBank->FillSubmitBuffer( mIndex, buffer, wmaBuffer ); } else { assert( mEffect != 0 ); iswma = mEffect->FillSubmitBuffer( buffer, wmaBuffer ); } #else if ( mWaveBank ) { mWaveBank->FillSubmitBuffer( mIndex, buffer ); } else { assert( mEffect != 0 ); mEffect->FillSubmitBuffer( buffer ); } #endif buffer.Flags = XAUDIO2_END_OF_STREAM; if ( loop ) { mLooped = true; buffer.LoopCount = XAUDIO2_LOOP_INFINITE; } else { mLooped = false; buffer.LoopCount = buffer.LoopBegin = buffer.LoopLength = 0; } buffer.pContext = nullptr; HRESULT hr; #if defined(_XBOX_ONE) || (_WIN32_WINNT < _WIN32_WINNT_WIN8) || (_WIN32_WINNT >= _WIN32_WINNT_WIN10) if ( iswma ) { hr = mBase.voice->SubmitSourceBuffer( &buffer, &wmaBuffer ); } else #endif { hr = mBase.voice->SubmitSourceBuffer( &buffer, nullptr ); } if ( FAILED(hr) ) { #ifdef _DEBUG DebugTrace( "ERROR: SoundEffectInstance failed (%08X) when submitting buffer:\n", hr ); char buff[64]; auto wfx = ( mWaveBank ) ? mWaveBank->GetFormat( mIndex, reinterpret_cast( buff ), 64 ) : mEffect->GetFormat(); size_t length = ( mWaveBank ) ? mWaveBank->GetSampleSizeInBytes( mIndex ) : mEffect->GetSampleSizeInBytes(); DebugTrace( "\tFormat Tag %u, %u channels, %u-bit, %u Hz, %Iu bytes\n", wfx->wFormatTag, wfx->nChannels, wfx->wBitsPerSample, wfx->nSamplesPerSec, length ); #endif mBase.Stop( true, mLooped ); throw std::exception( "SubmitSourceBuffer" ); } } //-------------------------------------------------------------------------------------- // SoundEffectInstance //-------------------------------------------------------------------------------------- // Private constructors _Use_decl_annotations_ SoundEffectInstance::SoundEffectInstance( AudioEngine* engine, SoundEffect* effect, SOUND_EFFECT_INSTANCE_FLAGS flags ) : pImpl( new Impl( engine, effect, flags ) ) { } _Use_decl_annotations_ SoundEffectInstance::SoundEffectInstance( AudioEngine* engine, WaveBank* waveBank, int index, SOUND_EFFECT_INSTANCE_FLAGS flags ) : pImpl( new Impl( engine, waveBank, index, flags ) ) { } // Move constructor. SoundEffectInstance::SoundEffectInstance(SoundEffectInstance&& moveFrom) : pImpl(std::move(moveFrom.pImpl)) { } // Move assignment. SoundEffectInstance& SoundEffectInstance::operator= (SoundEffectInstance&& moveFrom) { pImpl = std::move(moveFrom.pImpl); return *this; } // Public destructor. SoundEffectInstance::~SoundEffectInstance() { if( pImpl ) { if ( pImpl->mWaveBank ) { pImpl->mWaveBank->UnregisterInstance( this ); pImpl->mWaveBank = nullptr; } if ( pImpl->mEffect ) { pImpl->mEffect->UnregisterInstance( this ); pImpl->mEffect = nullptr; } } } // Public methods. void SoundEffectInstance::Play( bool loop ) { pImpl->Play( loop ); } void SoundEffectInstance::Stop( bool immediate ) { pImpl->mBase.Stop( immediate, pImpl->mLooped ); } void SoundEffectInstance::Pause() { pImpl->mBase.Pause(); } void SoundEffectInstance::Resume() { pImpl->mBase.Resume(); } void SoundEffectInstance::SetVolume( float volume ) { pImpl->mBase.SetVolume( volume ); } void SoundEffectInstance::SetPitch( float pitch ) { pImpl->mBase.SetPitch( pitch ); } void SoundEffectInstance::SetPan( float pan ) { pImpl->mBase.SetPan( pan ); } void SoundEffectInstance::Apply3D( const AudioListener& listener, const AudioEmitter& emitter, bool rhcoords ) { pImpl->mBase.Apply3D( listener, emitter, rhcoords ); } // Public accessors. bool SoundEffectInstance::IsLooped() const { return pImpl->mLooped; } SoundState SoundEffectInstance::GetState() { return pImpl->mBase.GetState( true ); } // Notifications. void SoundEffectInstance::OnDestroyParent() { pImpl->mBase.OnDestroy(); pImpl->mWaveBank = nullptr; pImpl->mEffect = nullptr; }