/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_Voice.cpp Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 27399 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include // std::memcpy #include #include #include #include #ifdef NW_PLATFORM_CTRWIN #include #include #endif // NW_PLATFORM_CTRWIN namespace nw { namespace snd { namespace internal { namespace driver { namespace { #ifdef NW_DEBUG f32 s_Pan; f32 s_SurroundPan; MixParam s_Mix; #endif #ifdef NW_PLATFORM_CTR nn::snd::InterpolationType GetSdkInterpolationType( u8 interpolationType ) { nn::snd::InterpolationType result = nn::snd::INTERPOLATION_TYPE_POLYPHASE; switch ( interpolationType ) { case 0: // そのまま break; case 1: result = nn::snd::INTERPOLATION_TYPE_LINEAR; break; case 2: result = nn::snd::INTERPOLATION_TYPE_NONE; break; default: NN_ASSERTMSG( 0, "invalid InterpolationType(%d)\n", interpolationType ); break; } return result; } nn::snd::SampleFormat GetSdkSampleFormat( nw::snd::SampleFormat format ) { nn::snd::SampleFormat result = nn::snd::SAMPLE_FORMAT_PCM16; switch ( format ) { case nw::snd::SAMPLE_FORMAT_PCM_S8: result = nn::snd::SAMPLE_FORMAT_PCM8; break; case nw::snd::SAMPLE_FORMAT_PCM_S16: // そのまま break; case nw::snd::SAMPLE_FORMAT_DSP_ADPCM: result = nn::snd::SAMPLE_FORMAT_ADPCM; break; case nw::snd::SAMPLE_FORMAT_PCM_S32: NW_ASSERTMSG( 0, "invalid SampleFormat: nn::snd::SAMPLE_FORMAT_PCM_S32\n"); break; case nw::snd::SAMPLE_FORMAT_IMA_ADPCM: result = nn::snd::SAMPLE_FORMAT_PCM16; NW_ASSERTMSG( 0, "invalid SampleFormat: nn::snd::SAMPLE_FORMAT_IMA_ADPCM\n"); break; } return result; } nw::snd::SampleFormat GetNwSampleFormat( nn::snd::SampleFormat format ) { nw::snd::SampleFormat result = nw::snd::SAMPLE_FORMAT_PCM_S16; switch ( format ) { case nn::snd::SAMPLE_FORMAT_PCM16: // そのまま break; case nn::snd::SAMPLE_FORMAT_PCM8: result = nw::snd::SAMPLE_FORMAT_PCM_S8; break; case nn::snd::SAMPLE_FORMAT_ADPCM: result = nw::snd::SAMPLE_FORMAT_DSP_ADPCM; break; } return result; } u32 FrameToNibbleAddress( u32 frame ) { u32 mod = frame % 14; u32 nibbleAddress = ( frame / 14 ) * 16; if ( mod != 0 ) { nibbleAddress += ( mod + 2 ); } return nibbleAddress; } #endif // NW_PLATFORM_CTR inline void PanCurveToPanInfo( Util::PanInfo& panInfo, PanCurve curve ) { switch ( curve ) { case PAN_CURVE_SQRT: panInfo.curve = Util::PAN_CURVE_SQRT; break; case PAN_CURVE_SQRT_0DB: panInfo.curve = Util::PAN_CURVE_SQRT; panInfo.centerZeroFlag = true; break; case PAN_CURVE_SQRT_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_SQRT; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; case PAN_CURVE_SINCOS: panInfo.curve = Util::PAN_CURVE_SINCOS; break; case PAN_CURVE_SINCOS_0DB: panInfo.curve = Util::PAN_CURVE_SINCOS; panInfo.centerZeroFlag = true; break; case PAN_CURVE_SINCOS_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_SINCOS; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; case PAN_CURVE_LINEAR: panInfo.curve = Util::PAN_CURVE_LINEAR; break; case PAN_CURVE_LINEAR_0DB: panInfo.curve = Util::PAN_CURVE_LINEAR; panInfo.centerZeroFlag = true; break; case PAN_CURVE_LINEAR_0DB_CLAMP: panInfo.curve = Util::PAN_CURVE_LINEAR; panInfo.centerZeroFlag = true; panInfo.zeroClampFlag = true; break; default: panInfo.curve = Util::PAN_CURVE_SQRT; break; } } // モノラル・バランス or デュアル用パン計算 inline void CalcPanForMono( f32& left, f32& right, const Util::PanInfo& panInfo ) { left = right = Util::CalcPanRatio( Voice::PAN_CENTER, panInfo ); } // ステレオ or サラウンド・バランス用パン計算 inline void CalcBarancePanForStereo( f32& left, f32& right, const f32& pan, int channelIndex, const Util::PanInfo& panInfo ) { if ( channelIndex == 0 ) { left = Util::CalcPanRatio( pan, panInfo ); right = 0.0f; } else if ( channelIndex == 1 ) { left = 0.0f; right = Util::CalcPanRatio( Voice::PAN_CENTER - pan, panInfo ); } } // ステレオ or サラウンド用デュアルパン計算 inline void CalcDualPanForStereo( f32& left, f32& right, const f32& pan, const Util::PanInfo& panInfo ) { left = Util::CalcPanRatio( pan, panInfo ); right = Util::CalcPanRatio( Voice::PAN_CENTER - pan, panInfo ); } // モノラル or ステレオ用サラウンドパン計算 inline void CalcSurroundPanForMono( f32& front, f32& rear, const Util::PanInfo& panInfo ) { front = Util::CalcSurroundPanRatio( Voice::SPAN_FRONT, panInfo ); rear = Util::CalcSurroundPanRatio( Voice::SPAN_REAR, panInfo ); } // サラウンド用サラウンドパン計算 inline void CalcSurroundPanForSurround( f32& front, f32& rear, const f32& span, const Util::PanInfo& panInfo ) { front = Util::CalcSurroundPanRatio( span, panInfo ); rear = Util::CalcSurroundPanRatio( Voice::SPAN_REAR - span, panInfo ); } } // anonymous namespace // ------------------------------------------------------------------------ // 定数 const f32 Voice::VOLUME_MIN = 0.0f; const f32 Voice::VOLUME_DEFAULT = 1.0f; const f32 Voice::VOLUME_MAX = 2.0f; const f32 Voice::PAN_LEFT = -1.0f; const f32 Voice::PAN_CENTER = 0.0f; const f32 Voice::PAN_RIGHT = 1.0f; const f32 Voice::SPAN_FRONT = 0.0f; const f32 Voice::SPAN_CENTER = 1.0f; const f32 Voice::SPAN_REAR = 2.0f; const f32 Voice::CUTOFF_FREQ_MIN = 0.0f; const f32 Voice::CUTOFF_FREQ_MAX = 1.0f; const f32 Voice::BIQUAD_VALUE_MIN = 0.0f; const f32 Voice::BIQUAD_VALUE_MAX = 1.0f; const f32 Voice::SEND_MIN = 0.0f; const f32 Voice::SEND_MAX = 1.0f; /* ======================================================================== inline function ======================================================================== */ inline u16 CalcMixVolume( f32 volume ) { if ( volume <= 0.f ) return 0UL; return static_cast( ut::Min( 0x0000ffffUL, static_cast( volume * 0x8000 ) ) ); } Voice::Voice() : m_Callback( NULL ), m_IsActive( false ), m_IsStart( false ), m_IsStarted( false ), m_IsPause( false ), m_SyncFlag( 0 ) { for ( int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++ ) { m_pHardwareChannel[ channelIndex ] = NULL; } } Voice::~Voice() { for ( int channelIndex = 0; channelIndex < CHANNEL_MAX; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN HardwareChannelManager::GetInstance().FreeHardwareChannel( pChannel ); #else nn::snd::FreeVoice( pChannel ); #endif } } } #ifdef NW_PLATFORM_CTRWIN bool Voice::Alloc( int channelCount, int priority, Voice::VoiceCallback callback, void* callbackData ) { NW_MINMAX_ASSERT( channelCount, 1, CHANNEL_MAX ); channelCount = ut::Clamp( channelCount, 1, CHANNEL_MAX ); NW_ASSERT( ! m_IsActive ); // ボイス取得 { HardwareChannel* channelTable[ CHANNEL_MAX ]; // RVL で、NW 層と SDK 層でのプライオリティ変換のために必要 u32 sdkVoicePriority = ( priority == PRIORITY_NODROP ) ? VOICE_PRIORITY_NODROP : VOICE_PRIORITY_USE; // 必要なボイスをまとめて確保する for ( int i = 0 ; i < channelCount; i++ ) { HardwareChannel* pSdkVoice = NULL; while( pSdkVoice == NULL ) { pSdkVoice = HardwareChannelManager::GetInstance().AllocHardwareChannel( sdkVoicePriority, HardwareChannelCallbackFunc, this ); // チャンネルの空きが無い if ( pSdkVoice == NULL ) { int dropVoiceCount = VoiceManager::GetInstance().DropLowestPriorityVoice( sdkVoicePriority ); if ( dropVoiceCount == 0 ) { for ( int j = 0; j < i; j++ ) { HardwareChannelManager::GetInstance().FreeHardwareChannel( channelTable[ j ] ); } return false; } continue; } } pSdkVoice->SetPriority( sdkVoicePriority ); channelTable[ i ] = pSdkVoice; } // 確保完了 → メンバに設定 for ( int channelIndex = 0; channelIndex < channelCount; channelIndex++ ) { m_pHardwareChannel[ channelIndex ] = channelTable[ channelIndex ]; } } // パラメータ初期化 InitParam( channelCount, callback, callbackData ); m_IsActive = true; return true; } #else // ifdef NW_PLATFORM_CTRWIN bool Voice::Alloc( int channelCount, int priority, Voice::VoiceCallback callback, void* callbackData ) { NW_MINMAX_ASSERT( channelCount, 1, CHANNEL_MAX ); channelCount = ut::Clamp( channelCount, 1, CHANNEL_MAX ); m_ChannelCount = 0; NW_ASSERT( ! m_IsActive ); u32 sdkVoicePriority = ut::Clamp( priority, 0, nn::snd::VOICE_PRIORITY_NODROP ); // ボイス取得 m_IsAllocating = true; m_AllocateErrorFlag = false; if ( channelCount == 1 ) { HardwareChannel* pSdkVoice = NULL; #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pSdkVoice = nn::snd::AllocVoice( sdkVoicePriority, SdkVoiceDropCallbackFunc, reinterpret_cast( this ) ); #else pSdkVoice = NULL; #endif if ( pSdkVoice == NULL ) { return false; } m_pHardwareChannel[ 0 ] = pSdkVoice; m_ChannelCount = 1; } else { // 必要なボイスをまとめて確保する for ( int i = 0 ; i < channelCount; i++ ) { HardwareChannel* pSdkVoice = NULL; while ( pSdkVoice == NULL ) { #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pSdkVoice = nn::snd::AllocVoice( sdkVoicePriority, SdkVoiceDropCallbackFuncMulti, reinterpret_cast( this ) ); #else pSdkVoice = NULL; #endif // チャンネルの空きが無い if ( pSdkVoice == NULL ) { int dropVoiceCount = VoiceManager::GetInstance().DropLowestPriorityVoice( sdkVoicePriority ); if ( dropVoiceCount == 0 ) { m_AllocateErrorFlag = true; break; } continue; } } m_pHardwareChannel[ i ] = pSdkVoice; m_ChannelCount += 1; } if ( m_AllocateErrorFlag ) { for ( int i = 0; i < m_ChannelCount; i++ ) { if ( m_pHardwareChannel[i] ) { #ifndef NW_SND_DEBUG_NOUSE_CTRSDK nn::snd::FreeVoice( m_pHardwareChannel[i] ); #endif } } return false; } } m_IsAllocating = false; // パラメータ初期化 InitParam( channelCount, callback, callbackData ); m_IsActive = true; return true; } #endif // endif NW_PLATFORM_CTRWIN void Voice::Free() { if ( ! m_IsActive ) return; for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN HardwareChannelManager::GetInstance().FreeHardwareChannel( pChannel ); #else #ifndef NW_SND_DEBUG_NOUSE_CTRSDK nn::snd::FreeVoice( pChannel ); #endif #endif m_pHardwareChannel[ channelIndex ] = NULL; } } m_ChannelCount = 0; VoiceManager::GetInstance().FreeVoice( this ); m_IsActive = false; } void Voice::Initialize( const WaveInfo& waveInfo, u32 startOffset ) { #ifdef NW_PLATFORM_CTR // TODO: 途中再生には未対応 NW_UNUSED_VARIABLE( startOffset ); #endif m_Format = waveInfo.sampleFormat; // チャンネルパラメータ設定 for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel == NULL ) continue; #ifdef NW_PLATFORM_CTRWIN NW_NULL_ASSERT( waveInfo.channelParam[channelIndex].dataAddress ); const void* dataAddr = waveInfo.channelParam[channelIndex].dataAddress; // adpcmInfoをオフセット再生のためにCPUでデコードして書き換える DspAdpcmParam adpcmParam; if ( m_Format == SAMPLE_FORMAT_DSP_ADPCM ) { adpcmParam = waveInfo.channelParam[channelIndex].adpcmParam; HardwareChannel::CalcOffsetDspAdpcmParam( &adpcmParam.predScale, &adpcmParam.yn1, &adpcmParam.yn2, startOffset, dataAddr, adpcmParam ); } pChannel->Initialize( dataAddr, m_Format, waveInfo.sampleRate ); pChannel->SetAddr( waveInfo.loopFlag, dataAddr, startOffset, static_cast(waveInfo.loopStartFrame), static_cast(waveInfo.loopEndFrame) ); if ( m_Format == SAMPLE_FORMAT_DSP_ADPCM ) { pChannel->SetDspAdpcm( &adpcmParam ); pChannel->SetDspAdpcmLoop( &waveInfo.channelParam[channelIndex].adpcmLoopParam ); } pChannel->SetSrcType( SRC_TYPE_4TAP, m_Pitch ); #else // NW_PLATFORM_CTRWIN #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetChannelCount( 1 ); pChannel->SetSampleFormat( GetSdkSampleFormat( m_Format ) ); pChannel->SetSampleRate( waveInfo.sampleRate ); // Voice::Initialize を呼んだ後に、Voice ユーザー (Channel や StreamTrack) が // WaveBuffer の操作をする必要があります。 // NN_LOG("[forSDK] SDKFormat(%d)\n", GetSdkSampleFormat( m_Format )); pChannel->SetInterpolationType( Util::GetInterpolationType( HardwareManager::GetInstance().GetSrcType() ) ); #endif #endif // NW_PLATFORM_CTRWIN } m_IsPause = false; m_IsPausing = false; m_IsStart = false; m_IsStarted = false; } void Voice::Start() { m_IsStart = true; m_IsPause = false; m_SyncFlag |= UPDATE_START; } void Voice::Stop() { if ( m_IsStarted ) { StopAllHardwareChannel(); m_IsStarted = false; } m_IsPausing = false; m_IsPause = false; m_IsStart = false; } void Voice::StopFinished() { if ( ! m_IsActive ) { return; } // 再生停止チェック if ( m_IsStarted ) { if ( IsPlayFinished() ) { // 波形停止 if ( m_Callback != NULL ) { m_Callback( this, CALLBACK_STATUS_FINISH_WAVE, m_pCallbackData ); } m_IsStarted = false; m_IsStart = false; } } } void Voice::Pause( bool flag ) { if ( m_IsPause == flag ) return; m_IsPause = flag; m_SyncFlag |= UPDATE_PAUSE; } void Voice::Calc() { if ( m_IsStart ) { // 再生レート if ( m_SyncFlag & UPDATE_SRC ) { CalcSrc( false ); m_SyncFlag &= ~UPDATE_SRC; } // ボリューム if ( m_SyncFlag & UPDATE_VE ) { CalcVe(); m_SyncFlag &= ~UPDATE_VE; } // ミックス if ( m_SyncFlag & UPDATE_MIX ) { CalcMix(); m_SyncFlag &= ~UPDATE_MIX; } // ローパスフィルタ if ( m_SyncFlag & UPDATE_LPF ) { CalcLpf(); m_SyncFlag &= ~UPDATE_LPF; } // Biquadフィルタ if ( m_SyncFlag & UPDATE_BIQUAD ) { CalcBiquadFilter(); m_SyncFlag &= ~UPDATE_BIQUAD; } } } void Voice::Update() { if ( ! m_IsActive ) return; // ボイスの再生・停止処理をあとでまとめて行うためのフラグ enum { NONE, RUN, STOP, PAUSE } runFlag = NONE; // 再生開始 if ( m_SyncFlag & UPDATE_START ) { if ( m_IsStart && !m_IsStarted ) { CalcSrc( true ); CalcMix(); CalcVe(); // マスターボリューム反映 runFlag = RUN; m_IsStarted = true; m_SyncFlag &= ~UPDATE_START; m_SyncFlag &= ~UPDATE_SRC; m_SyncFlag &= ~UPDATE_MIX; } } if ( m_IsStarted ) { // 一時停止 if ( m_SyncFlag & UPDATE_PAUSE ) { if ( m_IsStart ) { if ( m_IsPause ) { runFlag = PAUSE; m_IsPausing = true; } else { runFlag = RUN; m_IsPausing = false; } m_SyncFlag &= ~UPDATE_PAUSE; } } #ifdef NW_PLATFORM_CTRWIN // パラメータ設定をsyncさせる SyncHardwareChannel(); #endif } // ボイスの再生・停止処理をまとめて行う // 同一フレームで、まだ再生されていないボイスに対して // 再生→停止の処理を行うとデポップが発生するため switch ( runFlag ) { case RUN: RunAllHardwareChannel(); break; case STOP: StopAllHardwareChannel(); break; case PAUSE: PauseAllHardwareChannel(); case NONE: default: break; } } void Voice::SetVolume( f32 volume ) { if ( volume < VOLUME_MIN ) volume = VOLUME_MIN; if ( volume != m_Volume ) { m_Volume = volume; m_SyncFlag |= UPDATE_VE; } } void Voice::SetPitch( f32 pitch ) { if ( pitch != m_Pitch ) { m_Pitch = pitch; m_SyncFlag |= UPDATE_SRC; } } void Voice::SetPanMode( PanMode panMode ) { if ( panMode != m_PanMode ) { m_PanMode = panMode; m_SyncFlag |= UPDATE_MIX; } } void Voice::SetPanCurve( PanCurve panCurve ) { if ( panCurve != m_PanCurve ) { m_PanCurve = panCurve; m_SyncFlag |= UPDATE_MIX; } } void Voice::SetPan( f32 pan ) { if ( pan != m_Pan ) { m_Pan = pan; m_SyncFlag |= UPDATE_MIX; } } void Voice::SetSurroundPan( f32 span ) { if ( span != m_SurroundPan ) { m_SurroundPan = span; m_SyncFlag |= UPDATE_MIX; } } void Voice::SetLpfFreq( f32 lpfFreq ) { if ( lpfFreq != m_LpfFreq ) { m_LpfFreq = lpfFreq; m_SyncFlag |= UPDATE_LPF; } } void Voice::SetBiquadFilter( int type, f32 value ) { NW_MINMAX_ASSERT( type, 0, BIQUAD_FILTER_TYPE_USER_MAX ); value = ut::Clamp( value, BIQUAD_VALUE_MIN, BIQUAD_VALUE_MAX ); bool isUpdate = false; if ( type != m_BiquadType ) { m_BiquadType = static_cast( type ); isUpdate = true; } if ( value != m_BiquadValue ) { m_BiquadValue = value; isUpdate = true; } if ( isUpdate ) { m_SyncFlag |= UPDATE_BIQUAD; } } void Voice::SetPriority( int priority ) { NW_MINMAX_ASSERT( priority, PRIORITY_MIN, PRIORITY_MAX ); m_Priority = priority; VoiceManager::GetInstance().ChangeVoicePriority( this ); // リリースの場合はUpdateVoicesPriorityではなくここですぐに設定 if ( m_Priority == PRIORITY_RELEASE ) { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetPriority( VOICE_PRIORITY_RELEASE ); #endif } } } } void Voice::SetMainSend( f32 send ) { send += 1.0f; if ( send < SEND_MIN ) send = SEND_MIN; if ( send != m_MainSend ) { m_MainSend = send; m_SyncFlag |= UPDATE_MIX; } } void Voice::SetFxSend( AuxBus bus, f32 send ) { NW_MINMAXLT_ASSERT( bus, AUX_BUS_A, AUX_BUS_A + AUX_BUS_NUM ); if ( send < SEND_MIN ) send = SEND_MIN; if ( send != m_FxSend[ bus ] ) { m_FxSend[ bus ] = send; m_SyncFlag |= UPDATE_MIX; } } #ifdef NW_PLATFORM_CTRWIN void Voice::HardwareChannelCallbackFunc( HardwareChannel* dropVoice, HardwareChannel::HardwareChannelCallbackStatus status, void* callbackData ) { Voice* voice = static_cast( callbackData ); NW_NULL_ASSERT( voice ); VoiceCallbackStatus voiceStatus = CALLBACK_STATUS_FINISH_WAVE; bool freeDropVoice = false; switch ( status ) { case HardwareChannel::CALLBACK_STATUS_CANCEL: voiceStatus = CALLBACK_STATUS_CANCEL; break; case HardwareChannel::CALLBACK_STATUS_DROP_DSP: voiceStatus = CALLBACK_STATUS_DROP_DSP; freeDropVoice = true; break; } // 同じボイスのAXVPBを全て止めて解放 for ( int channelIndex = 0; channelIndex < voice->m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = voice->m_pHardwareChannel[ channelIndex ]; if ( pChannel == NULL ) continue; if ( pChannel == dropVoice ) { if ( ! freeDropVoice ) { // DROP_DSPの時は解放してはいけない HardwareChannelManager::GetInstance().FreeHardwareChannel( pChannel ); } } else { pChannel->Stop(); HardwareChannelManager::GetInstance().FreeHardwareChannel( pChannel ); } voice->m_pHardwareChannel[ channelIndex ] = NULL; } voice->m_IsPause = false; voice->m_IsStart = false; voice->m_ChannelCount = 0; if ( freeDropVoice ) { voice->Free(); } if ( voice->m_Callback != NULL ) { voice->m_Callback( voice, voiceStatus, voice->m_pCallbackData ); } } #else void Voice::SdkVoiceDropCallbackFunc( nn::snd::Voice* pDropSdkVoice, uptr userArg ) { NW_UNUSED_VARIABLE( pDropSdkVoice ); Voice* pVoice = reinterpret_cast( userArg ); NW_NULL_ASSERT( pVoice ); // NOTE: NW4R ではステータス CANCEL / DROP_DSP があったが、NW4C では DROP のみ // 同じ NW4C Voice が持っている SDK Voice を止めて解放 NW_ASSERT( pVoice->m_ChannelCount == 1 ); NW_ASSERT( pVoice->m_pHardwareChannel[0] == pDropSdkVoice ); pVoice->m_pHardwareChannel[0] = NULL; pVoice->m_IsPause = false; pVoice->m_IsStart = false; pVoice->m_ChannelCount = 0; VoiceManager::GetInstance().FreeVoice( pVoice ); pVoice->m_IsActive = false; // NW4C Voice のコールバックを呼ぶ (Channel などが設定) if ( pVoice->m_Callback != NULL ) { pVoice->m_Callback( pVoice, CALLBACK_STATUS_DROP_DSP, pVoice->m_pCallbackData ); } } void Voice::SdkVoiceDropCallbackFuncMulti( nn::snd::Voice* pDropSdkVoice, uptr userArg ) { Voice* pVoice = reinterpret_cast( userArg ); NW_NULL_ASSERT( pVoice ); // NOTE: NW4R ではステータス CANCEL / DROP_DSP があったが、NW4C では DROP のみ if ( pVoice->m_IsAllocating ) { pVoice->m_AllocateErrorFlag = true; for ( int ch = 0; ch < pVoice->m_ChannelCount; ch++ ) { nn::snd::Voice* pSdkVoice = pVoice->m_pHardwareChannel[ch]; if ( pSdkVoice == pDropSdkVoice ) { pVoice->m_pHardwareChannel[ch] = NULL; return; } } NW_ASSERTMSG( false, "invalid allocating voice" ); } else { // 同じ NW4C Voice が持っている SDK Voice を止めて解放 for ( int ch = 0; ch < pVoice->m_ChannelCount; ch++ ) { nn::snd::Voice* pSdkVoice = pVoice->m_pHardwareChannel[ch]; if ( pSdkVoice == NULL ) { continue; } if ( pSdkVoice != pDropSdkVoice ) { #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pSdkVoice->SetState( nn::snd::Voice::STATE_STOP ); nn::snd::FreeVoice( pSdkVoice ); #endif } pVoice->m_pHardwareChannel[ch] = NULL; } } pVoice->m_IsPause = false; pVoice->m_IsStart = false; pVoice->m_ChannelCount = 0; VoiceManager::GetInstance().FreeVoice( pVoice ); pVoice->m_IsActive = false; // NW4C Voice のコールバックを呼ぶ (Channel などが設定) if ( pVoice->m_Callback != NULL ) { pVoice->m_Callback( pVoice, CALLBACK_STATUS_DROP_DSP, pVoice->m_pCallbackData ); } } #endif void Voice::InitParam( int channelCount, Voice::VoiceCallback callback, void* callbackData ) { NW_MINMAX_ASSERT( channelCount, 1, CHANNEL_MAX ); #ifdef NW_PLATFORM_CTRWIN m_ChannelCount = channelCount; #else // m_ChannelCount は AllocVoice 内で設定される NW_UNUSED_VARIABLE( channelCount ); #endif m_Callback = callback; m_pCallbackData = callbackData; m_SyncFlag = 0; m_IsPause = false; m_IsPausing = false; m_IsStarted = false; m_pLastWaveBuffer = NULL; // Init Param m_Volume = VOLUME_DEFAULT; m_LpfFreq = 1.0f; m_BiquadType = BIQUAD_FILTER_TYPE_NONE; m_BiquadValue = 0.0f; m_Pan = PAN_CENTER; m_SurroundPan = SPAN_FRONT; m_MainSend = SEND_MAX; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { m_FxSend[ i ] = SEND_MIN; } m_Pitch = 1.0f; m_PanMode = PAN_MODE_DUAL; m_PanCurve = PAN_CURVE_SQRT; // 子クラスのメソッドを呼ぶ // OnInitParam(); } #ifdef NW_PLATFORM_CTRWIN void Voice::SyncHardwareChannel() { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { if ( m_pHardwareChannel[ channelIndex ] != NULL ) { m_pHardwareChannel[ channelIndex ]->Sync(); } } } #endif void Voice::CalcSrc( bool initialUpdate ) { f32 ratio = m_Pitch; for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->SetSrc( ratio, initialUpdate ); #else NW_UNUSED_VARIABLE( initialUpdate ); #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetPitch( ratio ); #endif #endif // NN_LOG("[forSDK] pitch(%4d)\n", (int)( ratio*10000 )); } } } void Voice::CalcVe() { f32 volume = m_Volume; volume *= HardwareManager::GetInstance().GetOutputVolume(); for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; if ( pChannel != NULL ) { #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetVolume( volume ); // NN_LOG("[forSDK] Vol(%4d)\n", (int)(volume*10000)); #endif } } } void Voice::CalcMix() { MixParam mix; std::memset( &mix, 0, sizeof(mix) ); for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; if ( pChannel == NULL ) continue; CalcMixParam( channelIndex, &mix ); #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetMixParam( mix ); #endif // NN_LOG("[forSDK] MIX L(%4d) R(%4d)\n", // (int)( mix.mainBus[0] * 10000 ), // (int)( mix.mainBus[1] * 10000 ) ); } } void Voice::CalcLpf() { u16 freq = Util::CalcLpfFreq( m_LpfFreq ); for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->SetLpf( freq ); #else #ifdef NW_SND_AVAILABLE_NN_BOTH_FILTER if ( freq >= 16000 ) { pChannel->EnableMonoFilter( false ); } else { pChannel->EnableMonoFilter( true ); pChannel->SetMonoFilterCoefficients( freq ); } #endif #endif } } } void Voice::CalcBiquadFilter() { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->SetBiquad( m_BiquadType, m_BiquadValue ); #else { const BiquadFilterCallback* cb = HardwareManager::GetInstance().GetBiquadFilterCallback( m_BiquadType ); if ( cb == NULL ) { #ifdef NW_SND_AVAILABLE_NN_BOTH_FILTER pChannel->EnableBiquadFilter( false ); #else pChannel->SetFilterType( nn::snd::FILTER_TYPE_NONE ); #endif } else { #ifdef NW_SND_AVAILABLE_NN_BOTH_FILTER if ( m_BiquadValue <= 0.0f ) { pChannel->EnableBiquadFilter( false ); } else { pChannel->EnableBiquadFilter( true ); BiquadFilterCallback::Coefficients coef; cb->GetCoefficients( m_BiquadType, m_BiquadValue, &coef ); pChannel->SetBiquadFilterCoefficients( coef ); } #else BiquadFilterCallback::Coefficients coef; cb->GetCoefficients( m_BiquadType, m_BiquadValue, &coef ); pChannel->SetFilterType( nn::snd::FILTER_TYPE_BIQUAD ); pChannel->SetBiquadFilterCoefficients( &coef ); #endif } } #endif } } } #ifdef NW_PLATFORM_CTRWIN void Voice::CalcMixParam( int channelIndex, nw::snd::internal::MixParam* mix ) { NW_NULL_ASSERT( mix ); // NW_NULL_ASSERT( rmtMix ); f32 mainVolume = 1.0f; f32 mainSend = m_MainSend; f32 fxSend[ AUX_BUS_NUM ]; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { fxSend[ i ] = m_FxSend[ i ]; } f32 main = mainVolume * ut::Clamp( mainSend, 0.0f, 1.0f ); f32 fx[ AUX_BUS_NUM ]; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { fx[ i ] = mainVolume * ut::Clamp( fxSend[ i ], 0.0f, 1.0f ); } // ------------------------------------------------------------------------ // パン設定 f32 left = 0.0f; f32 right = 0.0f; f32 lrMixed = 0.0f; f32 surround = 0.0f; f32 front = 0.0f; f32 rear = 0.0f; Util::PanInfo panInfo; PanCurveToPanInfo( panInfo, m_PanCurve ); if ( ( m_ChannelCount > 1 ) && ( m_PanMode == PAN_MODE_BALANCE ) ) { // バランスとしてパンを処理する f32 pan = m_Pan; f32 span = m_SurroundPan; if ( channelIndex == 0 ) { left = Util::CalcPanRatio( pan, panInfo ); right = 0.0f; } else if ( channelIndex == 1 ) { left = 0.0f; right = Util::CalcPanRatio( PAN_CENTER - pan, panInfo ); } front = Util::CalcSurroundPanRatio( span, panInfo ); rear = Util::CalcSurroundPanRatio( SPAN_REAR - span, panInfo ); } else { // 通常の定位処理 f32 voicePan = 0.0f; // チャンネルごとにオフセットを与える if ( m_ChannelCount == 2 ) { if ( channelIndex == 0 ) voicePan = -1.0f; if ( channelIndex == 1 ) voicePan = 1.0f; } f32 pan; // f32 span; // TODO: PC 版のサラウンドはひとまず後回し switch ( HardwareManager::GetInstance().GetOutputMode() ) { // TODO:[SURR] case OUTPUT_MODE_DPL2: // TODO:[SURR] TransformDpl2Pan( // TODO:[SURR] &pan, // TODO:[SURR] &span, // TODO:[SURR] m_Pan + voicePan, // TODO:[SURR] m_SurroundPan // TODO:[SURR] ); // TODO:[SURR] break; case OUTPUT_MODE_MONO: case OUTPUT_MODE_STEREO: // TODO:[SURR] case OUTPUT_MODE_SURROUND: default: pan = m_Pan + voicePan; // TODO:[SURR] span = m_SurroundPan; break; } // pan left = Util::CalcPanRatio( pan, panInfo ); right = Util::CalcPanRatio( PAN_CENTER - pan, panInfo ); // TODO:[SURR] front = Util::CalcSurroundPanRatio( span, panInfo ); // TODO:[SURR] rear = Util::CalcSurroundPanRatio( SPAN_REAR - span, panInfo ); } // TODO:[SURR] surround = Util::CalcVolumeRatio( SURROUND_ATTENUATED_DB ); lrMixed = ( left + right ) * 0.5f; // ----------------------------------------------------------------- // 構造体にセットする // 各チャンネルのボリューム f32 m_l = 0.0f; // 左のボリューム f32 m_r = 0.0f; // 右のボリューム f32 auxL[ AUX_BUS_NUM ]; // Aux? の 左ボリューム f32 auxR[ AUX_BUS_NUM ]; // Aux? の 右ボリューム switch ( HardwareManager::GetInstance().GetOutputMode() ) { case OUTPUT_MODE_STEREO: m_l = main * left ; m_r = main * right; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { auxL[ i ] = fx[ i ] * left; auxR[ i ] = fx[ i ] * right; } // a_l = fx_a * left ; // a_r = fx_a * right; // a_s = fx_a * 0.0f; // b_l = fx_b * left ; // b_r = fx_b * right; // b_s = fx_b * 0.0f; // c_l = fx_c * left ; // c_r = fx_c * right; // c_s = fx_c * 0.0f; break; case OUTPUT_MODE_MONO: m_l = main * lrMixed; m_r = main * lrMixed; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { auxL[ i ] = fx[ i ] * lrMixed; auxR[ i ] = fx[ i ] * lrMixed; } // a_l = fx_a * lrMixed; // a_r = fx_a * lrMixed; // a_s = fx_a * 0.0f; // b_l = fx_b * lrMixed; // b_r = fx_b * lrMixed; // b_s = fx_b * 0.0f; // c_l = fx_c * lrMixed; // c_r = fx_c * lrMixed; // c_s = fx_c * 0.0f; break; } // 計算された値を、mix構造体に代入する値に変換して代入 mix->mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = CalcMixVolume( m_l ); mix->mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = CalcMixVolume( m_r ); mix->auxBusA[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = CalcMixVolume( auxL[ 0 ] ); mix->auxBusA[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = CalcMixVolume( auxR[ 0 ] ); mix->auxBusB[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = CalcMixVolume( auxL[ 1 ] ); mix->auxBusB[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = CalcMixVolume( auxR[ 1 ] ); } #else // for NW_PLATFORM_CTR void Voice::CalcMixParam( int channelIndex, nw::snd::internal::MixParam* mix ) { NW_NULL_ASSERT( mix ); f32 main = ut::Clamp( m_MainSend, 0.0f, 1.0f ); f32 fx[ AUX_BUS_NUM ]; for ( int i = 0; i < AUX_BUS_NUM; i++ ) { fx[ i ] = ut::Clamp( m_FxSend[i], 0.0f, 1.0f ); } register f32 left, right, front, rear; left = right = front = rear = 0.0f; Util::PanInfo panInfo; PanCurveToPanInfo( panInfo, m_PanCurve ); const OutputMode mode = HardwareManager::GetInstance().GetOutputMode(); // left, right の計算 switch ( mode ) { case OUTPUT_MODE_MONO: CalcPanForMono( left, right, panInfo ); break; case OUTPUT_MODE_STEREO: case OUTPUT_MODE_SURROUND: if ( ( m_ChannelCount > 1 ) && ( m_PanMode == PAN_MODE_BALANCE ) ) { CalcBarancePanForStereo( left, right, m_Pan, channelIndex, panInfo ); } else { register f32 voicePan = m_Pan; if ( m_ChannelCount == 2 ) { if ( channelIndex == 0 ) { voicePan -= 1.0f; } if ( channelIndex == 1 ) { voicePan += 1.0f; } } CalcDualPanForStereo( left, right, voicePan, panInfo ); } break; } // front, rear の計算 switch ( mode ) { case OUTPUT_MODE_MONO: case OUTPUT_MODE_STEREO: CalcSurroundPanForMono( front, rear, panInfo ); break; case OUTPUT_MODE_SURROUND: CalcSurroundPanForSurround( front, rear, m_SurroundPan, panInfo ); break; } // MixParam の計算 register f32 tmpVol[nn::snd::CHANNEL_INDEX_NUM]; tmpVol[nn::snd::CHANNEL_INDEX_FRONT_LEFT] = front * left; tmpVol[nn::snd::CHANNEL_INDEX_FRONT_RIGHT] = front * right; tmpVol[nn::snd::CHANNEL_INDEX_REAR_LEFT] = rear * left; tmpVol[nn::snd::CHANNEL_INDEX_REAR_RIGHT] = rear * right; for ( int i = 0; i < nn::snd::CHANNEL_INDEX_NUM; i++ ) { mix->mainBus[i] = main * tmpVol[i]; mix->auxBusA[i] = fx[0] * tmpVol[i]; mix->auxBusB[i] = fx[1] * tmpVol[i]; } #ifdef NW_DEBUG s_Pan = m_Pan; s_SurroundPan = m_SurroundPan; s_Mix = *mix; #endif // NN_LOG("Front (%.2f, %.2f) Rear(%.2f, %.2f)\n", // mix->mainBus[0], mix->mainBus[1], // mix->mainBus[2], mix->mainBus[3] ); } #endif // NW_PLATFORM_CTRWIN void Voice::RunAllHardwareChannel() { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->Run(); #else #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetState( nn::snd::Voice::STATE_PLAY ); #endif #endif } } } void Voice::StopAllHardwareChannel() { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->Stop(); #else #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetState( nn::snd::Voice::STATE_STOP ); #endif #endif } } } void Voice::PauseAllHardwareChannel() { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->Stop(); #else #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetState( nn::snd::Voice::STATE_PAUSE ); #endif #endif } } } const HardwareChannel* Voice::GetHardwareChannel( int channelIndex ) const { NW_ASSERT( channelIndex < CHANNEL_MAX ); return m_pHardwareChannel[ channelIndex ]; } void Voice::UpdateVoicesPriority() { if ( m_Priority == PRIORITY_RELEASE ) return; for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { #ifdef NW_PLATFORM_CTRWIN pChannel->SetPriority( VOICE_PRIORITY_USE ); #else pChannel->SetPriority( m_Priority ); #endif } } } u32 Voice::GetCurrentPlayingSample() const { if ( ! IsActive() ) return 0; #ifndef NW_SND_DEBUG_NOUSE_CTRSDK return m_pHardwareChannel[0]->GetPlayPosition(); #else return 0; #endif } #ifdef NW_PLATFORM_CTRWIN void Voice::StopAtPoint( int channelIndex, const void* baseAddress, u32 samples ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->StopAtPoint( baseAddress, samples ); } } void Voice::SetLoopFlag( bool loopFlag ) { for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetLoopFlag( loopFlag ); } } } void Voice::SetLoopStart( s32 channelIndex, const void* baseAddress, u32 samples ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetLoopStart( baseAddress, samples ); } } void Voice::SetLoopEnd( s32 channelIndex, const void* baseAddress, u32 samples ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetLoopEnd( baseAddress, samples ); } } void Voice::SetDspAdpcmLoop( s32 channelIndex, const DspAdpcmLoopParam* param ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetDspAdpcmLoop( param ); } } #endif // NW_PLATFORM_CTRWIN nw::snd::SampleFormat Voice::GetFormat() const { NW_ASSERT( IsActive() ); if ( IsActive() ) { return m_Format; } return SAMPLE_FORMAT_PCM_S16; } bool Voice::IsRun() const { if ( IsActive() ) { #ifdef NW_PLATFORM_CTRWIN return m_pHardwareChannel[0]->IsPlaying(); #else #ifndef NW_SND_DEBUG_NOUSE_CTRSDK return ( m_pHardwareChannel[0]->GetState() == nn::snd::Voice::STATE_PLAY ); #endif #endif } return false; } bool Voice::IsPlayFinished() const { if ( IsActive() ) { #ifdef NW_PLATFORM_CTRWIN return m_pHardwareChannel[0]->IsPlayFinished(); #else if ( m_pLastWaveBuffer == NULL ) return false; if ( m_pLastWaveBuffer->status == nn::snd::WaveBuffer::STATUS_DONE ) { return true; } #endif } return false; } void Voice::SetFrontBypass( bool isFrontBypass ) { #ifdef NW_PLATFORM_CTR for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetFrontBypassFlag( isFrontBypass ); } } #endif } void Voice::SetInterpolationType( u8 interpolationType ) { #ifdef NW_PLATFORM_CTR for ( int channelIndex = 0; channelIndex < m_ChannelCount; channelIndex++ ) { HardwareChannel* pChannel = m_pHardwareChannel[channelIndex]; if ( pChannel != NULL ) { pChannel->SetInterpolationType( GetSdkInterpolationType( interpolationType ) ); } } #endif } #ifdef NW_PLATFORM_CTR void Voice::AppendWaveBuffer( int channelIndex, nn::snd::WaveBuffer* pBuffer, bool lastFlag ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; NW_NULL_ASSERT( pBuffer ); NW_NULL_ASSERT( pChannel ); #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->AppendWaveBuffer( pBuffer ); // NN_LOG("[forSDK] WaveBuffer: address(%p) size(%d)\n", // pBuffer->bufferAddress, pBuffer->sampleLength ); #endif if ( lastFlag ) { m_pLastWaveBuffer = pBuffer; } } void Voice::SetAdpcmParam( int channelIndex, const nn::snd::AdpcmParam& param ) { HardwareChannel* pChannel = m_pHardwareChannel[ channelIndex ]; NW_NULL_ASSERT( pChannel ); #ifndef NW_SND_DEBUG_NOUSE_CTRSDK pChannel->SetAdpcmParam( param ); #else NW_UNUSED_VARIABLE( param ); #endif } u32 Voice::FrameToByte( u32 sample, nw::snd::SampleFormat format ) { switch ( format ) { case nw::snd::SAMPLE_FORMAT_DSP_ADPCM: { return ( FrameToNibbleAddress( sample ) >> 1 ); } case nw::snd::SAMPLE_FORMAT_PCM_S8: { return sample; } case nw::snd::SAMPLE_FORMAT_PCM_S16: { return ( sample << 1 ); } default: NW_ASSERTMSG( false, "Invalid format\n" ); return 0; } } #endif #ifdef NW_DEBUG void Voice::GetDebugMixParam( f32& pan, f32& span, nw::snd::internal::MixParam& mix ) { pan = s_Pan; span = s_SurroundPan; mix = s_Mix; } #endif } // namespace nw::snd::internal::driver } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw