/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_HardwareChannelAX.cppi 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:$ *---------------------------------------------------------------------------*/ #include namespace nw { namespace snd { namespace internal { namespace driver { namespace { u16 GetSDKFormatFromSampleFormat( SampleFormat sampleFormat ) { switch( sampleFormat ) { case SAMPLE_FORMAT_DSP_ADPCM: return AX_PB_FORMAT_ADPCM; case SAMPLE_FORMAT_PCM_S8: return AX_PB_FORMAT_PCM8; /* case SAMPLE_FORMAT_PCM_S16_BE: case SAMPLE_FORMAT_PCM_S16_LE: */ case SAMPLE_FORMAT_PCM_S16: return AX_PB_FORMAT_PCM16; case SAMPLE_FORMAT_IMA_ADPCM: default: NW_ASSERTMSG( false, "Invalid format\n" ); return AX_PB_FORMAT_ADPCM; } } } // anonymous namespace HardwareChannel::DspAddress HardwareChannel::GetDspAddressBySample( const void* baseAddress, u32 samples, SampleFormat format ) { if ( baseAddress != NULL ) { baseAddress = reinterpret_cast( OS_CachedToPhysical( baseAddress ) ); } HardwareChannel::DspAddress addr = 0; switch ( format ) { case SAMPLE_FORMAT_DSP_ADPCM: addr = ( reinterpret_cast( baseAddress ) << 1 ) + ( samples / 14 ) * 16 + ( samples % 14 ) + 2 ; break; case SAMPLE_FORMAT_PCM_S8: addr = reinterpret_cast( baseAddress ) + samples ; break; case SAMPLE_FORMAT_PCM_S16: addr = ( reinterpret_cast( baseAddress ) >> 1 ) + samples ; break; case SAMPLE_FORMAT_IMA_ADPCM: default: NW_ASSERTMSG( false, "Invalid format\n" ); break; } return addr; } u32 HardwareChannel::GetSampleByDspAddress( const void* baseAddress, DspAddress addr, SampleFormat format ) { if ( baseAddress != NULL ) { baseAddress = reinterpret_cast( OS_CachedToPhysical( baseAddress ) ); } u32 samples = 0; switch ( format ) { case SAMPLE_FORMAT_DSP_ADPCM: samples = addr - ( reinterpret_cast( baseAddress ) << 1 ); samples = ( samples / 16 ) * 14 + ( samples % 16 ) - 2; break; case SAMPLE_FORMAT_PCM_S8: samples = addr - reinterpret_cast( baseAddress ); break; case SAMPLE_FORMAT_PCM_S16: samples = addr - ( reinterpret_cast( baseAddress ) >> 1 ); break; default: NW_ASSERTMSG( false, "Invalid format\n" ); break; } return samples; } void HardwareChannel::Initialize() { } void HardwareChannel::OnInitialize() { } void HardwareChannel::SetParamBlock( ParameterBlock* vpb ) { NW_NULL_ASSERT( vpb ); m_pVpb = vpb; m_SyncFlag = 0; m_IsFirstVeUpdate = true; } void HardwareChannel::Sync() { if ( ! IsAvailable() ) return; m_pVpb->sync |= m_SyncFlag; m_SyncFlag = 0; } bool HardwareChannel::IsPlayFinished() const { if ( m_pWaveData == NULL ) return false; DspAddress dspAddr = GetCurrentPlayingDspAddress(); const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress(); DspAddress beginPos = GetDspAddressBySample( zeroBuffer, 0, m_Format ); DspAddress endPos = beginPos; switch ( m_Format ) { case SAMPLE_FORMAT_DSP_ADPCM: endPos += ( HardwareManager::ZERO_BUFFER_SIZE << 1 ); break; case SAMPLE_FORMAT_PCM_S8: endPos += ( HardwareManager::ZERO_BUFFER_SIZE ); break; /* case SAMPLE_FORMAT_PCM_S16_BE: case SAMPLE_FORMAT_PCM_S16_LE: */ case SAMPLE_FORMAT_PCM_S16: endPos += ( HardwareManager::ZERO_BUFFER_SIZE >> 1 ); break; default: NW_ASSERTMSG( false, "Invalid format\n" ); return false; } if ( beginPos <= dspAddr && dspAddr < endPos ) { return true; } return false; } void HardwareChannel::Run() { if ( IsAvailable() ) { AX_SetVoiceState( m_pVpb, AX_PB_STATE_RUN ); } } void HardwareChannel::Stop() { if ( IsAvailable() ) { AX_SetVoiceState( m_pVpb, AX_PB_STATE_STOP ); } } void HardwareChannel::StopAtPoint( const void* baseAddress, u32 samples ) { if ( ! IsAvailable() ) return; const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress(); DspAddress beginPos = GetDspAddressBySample( zeroBuffer, 0, m_Format ); DspAddress endPos = GetDspAddressBySample( baseAddress, samples-1, m_Format ); SetVoiceLoopAddr( beginPos ); SetVoiceEndAddr( endPos ); SetVoiceLoop( AXPBADDR_LOOP_OFF ); } void HardwareChannel::SetLoopFlag( bool loopFlag ) { if ( ! IsAvailable() ) return; if ( loopFlag ) { SetVoiceLoop( AXPBADDR_LOOP_ON ); } else { SetVoiceLoop( AXPBADDR_LOOP_OFF ); } } void HardwareChannel::SetVoiceLoopAddr( u32 addr ) { if ( ! IsAvailable() ) return; m_pVpb->pb.addr.loopAddressHi = static_cast( addr >> 16 ); m_pVpb->pb.addr.loopAddressLo = static_cast( addr & 0xFFFF ); if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) ) { m_pVpb->sync |= AX_SYNC_USER_LOOPADDR; } } void HardwareChannel::SetVoiceEndAddr( u32 addr ) { if ( ! IsAvailable() ) return; m_pVpb->pb.addr.endAddressHi = static_cast( addr >> 16 ); m_pVpb->pb.addr.endAddressLo = static_cast( addr & 0xFFFF ); if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) ) { m_pVpb->sync |= AX_SYNC_USER_ENDADDR; } } void HardwareChannel::SetVoiceLoop( u32 loop ) { if ( ! IsAvailable() ) return; m_pVpb->pb.addr.loopFlag = static_cast( loop ); if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) ) { m_pVpb->sync |= AX_SYNC_USER_LOOP; } } // パラメータ設定関数 void HardwareChannel::SetAddr( bool loopFlag, const void* waveAddr, u32 startOffset, u32 loopStart, u32 loopEnd ) { if ( ! IsAvailable() ) return; DspAddress startPos; DspAddress loopPos; DspAddress endPos; if ( startOffset > loopEnd ) { // 開始位置が範囲外なのでノイズにならないようにゼロバッファを再生して終了 const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress(); loopFlag = false; startPos = GetDspAddressBySample( zeroBuffer, 0, m_Format ); loopPos = GetDspAddressBySample( zeroBuffer, 0, m_Format ); endPos = GetDspAddressBySample( zeroBuffer, 1, m_Format ); } else { if ( loopFlag ) { loopPos = GetDspAddressBySample( waveAddr, loopStart, m_Format ); } else { const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress(); loopPos = GetDspAddressBySample( zeroBuffer, 0, m_Format ); } startPos = GetDspAddressBySample( waveAddr, startOffset, m_Format ); endPos = GetDspAddressBySample( waveAddr, loopEnd-1, m_Format ); } AXPBADDR addr; addr.loopFlag = static_cast( loopFlag ? AXPBADDR_LOOP_ON: AXPBADDR_LOOP_OFF ); addr.format = GetSDKFormatFromSampleFormat( m_Format ); addr.loopAddressHi = static_cast( loopPos >> 16 ); addr.loopAddressLo = static_cast( loopPos & 0xFFFF ); addr.endAddressHi = static_cast( endPos >> 16 ); addr.endAddressLo = static_cast( endPos & 0xFFFF ); addr.currentAddressHi = static_cast( startPos >> 16 ); addr.currentAddressLo = static_cast( startPos & 0xFFFF ); AX_SetVoiceAddr( m_pVpb, const_cast( &addr ) ); } void HardwareChannel::SetDspAdpcm( const DspAdpcmParam* param ) { if ( ! IsAvailable() ) return; DspAdpcmParameterBlock adpcm; switch ( m_Format ) { case SAMPLE_FORMAT_DSP_ADPCM: NW_NULL_ASSERT( param ); (void)std::memcpy( adpcm.a, param->coef, sizeof(u16)*16 ); adpcm.gain = 0; // param->gain; adpcm.predScale = param->predScale; adpcm.yn1 = static_cast( param->yn1 ); adpcm.yn2 = static_cast( param->yn2 ); break; #if 0 // ほかのフォーマットで SetAdpcm() 関数に入ることは無い /* case SAMPLE_FORMAT_PCM_S16_BE: case SAMPLE_FORMAT_PCM_S16_LE: */ case SAMPLE_FORMAT_PCM_S16: (void)std::memset( adpcm.a, 0, sizeof(u16)*16 ); adpcm.gain = 0x0800; adpcm.predScale = 0; adpcm.yn1 = 0; adpcm.yn2 = 0; break; case SAMPLE_FORMAT_PCM_S8: (void)std::memset( adpcm.a, 0, sizeof(u16)*16 ); adpcm.gain = 0x0100; adpcm.predScale = 0; adpcm.yn1 = 0; adpcm.yn2 = 0; break; case SAMPLE_FORMAT_IMA_ADPCM: #endif default: NW_ASSERTMSG( false, "Invalid format\n" ); break; } std::memcpy( &m_pVpb->pb.adpcm, &adpcm, sizeof( adpcm ) ); m_SyncFlag |= AX_SYNC_USER_ADPCM; } void HardwareChannel::SetDspAdpcmLoop( const DspAdpcmLoopParam* param ) { if ( ! IsAvailable() ) return; DspAdpcmLoopParameterBlock adpcmloop; if ( m_Format == SAMPLE_FORMAT_DSP_ADPCM ) { NW_NULL_ASSERT( param ); adpcmloop.loopPredScale = param->loopPredScale; adpcmloop.loopYn1 = static_cast( param->loopYn1 ); adpcmloop.loopYn2 = static_cast( param->loopYn2 ); } else { adpcmloop.loopPredScale = 0; adpcmloop.loopYn1 = 0; adpcmloop.loopYn2 = 0; } std::memcpy( &m_pVpb->pb.adpcmLoop, &adpcmloop, sizeof( adpcmloop ) ); m_SyncFlag |= AX_SYNC_USER_ADPCMLOOP; } void HardwareChannel::SetSrcType( SrcType type, f32 pitch ) { if ( ! IsAvailable() ) return; u16 src = AX_PB_SRCSEL_POLYPHASE; // ひとまず初期化 u16 coef = AX_PB_COEFSEL_16KHZ; if ( type == SRC_TYPE_4TAP ) { f32 ratio = GetDspRatio( pitch ); if ( ratio > 4.0f/3.0f ) coef = AX_PB_COEFSEL_8KHZ; else if ( ratio > 1.0f ) coef = AX_PB_COEFSEL_12KHZ; // else coef = AX_PB_COEFSEL_16KHZ; // デフォルトで 16KHZ に設定されている。 } else if ( type == SRC_TYPE_LINEAR ) { src = AX_PB_SRCSEL_LINEAR; } else { src = AX_PB_SRCSEL_NONE; } m_pVpb->pb.srcSelect = src; m_pVpb->pb.coefSelect = coef; m_SyncFlag |= AX_SYNC_USER_SRCSELECT; } void HardwareChannel::SetSrc( f32 ratio, bool initialUpdate ) { if ( ! IsAvailable() ) return; if ( initialUpdate ) { ratio = GetDspRatio( ratio ); ratio = nw::ut::Clamp( ratio, 0.0f, 65535.0f ); u32 srcBits = static_cast( 0x00010000 * ratio ); AXPBSRC src; src.ratioHi = static_cast( srcBits >> 16 ); src.ratioLo = static_cast( srcBits & 0xFFFF ); src.currentAddressFrac = 0; src.last_samples[0] = 0; src.last_samples[1] = 0; src.last_samples[2] = 0; src.last_samples[3] = 0; std::memcpy( &m_pVpb->pb.src, &src, sizeof( src ) ); m_SyncFlag &= ~AX_SYNC_USER_SRCRATIO; m_SyncFlag |= AX_SYNC_USER_SRC; } else { static const u32 SRC_RATIO_BASE = 0x10000; u32 r = static_cast( GetDspRatio(ratio) * SRC_RATIO_BASE ); m_pVpb->pb.src.ratioHi = static_cast( r >> 16 ); m_pVpb->pb.src.ratioLo = static_cast( r & 0xFFFF ); if ( ! ( m_SyncFlag & AX_SYNC_USER_SRC ) ) { m_SyncFlag |= AX_SYNC_USER_SRCRATIO; } } } f32 HardwareChannel::GetDspRatio( f32 ratio ) const { return ratio * m_SampleRate / AX_IN_SAMPLES_PER_SEC; } void HardwareChannel::SetMixParam( const MixParam& param ) { if ( ! IsAvailable() ) return; AXPBMIX mix; // mix構造体にvolumeを設定 mix.main.l = static_cast(param.mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT]); mix.main.r = static_cast(param.mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]); mix.aux[ 0 ].l = static_cast(param.auxBusA[nn::snd::CHANNEL_INDEX_FRONT_LEFT]); mix.aux[ 0 ].r = static_cast(param.auxBusA[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]); mix.aux[ 1 ].l = static_cast(param.auxBusB[nn::snd::CHANNEL_INDEX_FRONT_LEFT]); mix.aux[ 1 ].r = static_cast(param.auxBusB[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]); SetVoiceMix( mix ); } void HardwareChannel::SetVolume( f32 volume ) { if ( ! IsAvailable() ) return; int vol = static_cast(volume * static_cast( VOICE_GAIN_MAX ) ); vol = nw::ut::Clamp( vol, 0, 0xffff ); if ( IsPlaying() ) { m_pVpb->pb.ve.originVolume = m_pVpb->pb.ve.targetVolume; m_pVpb->pb.ve.targetVolume = vol; } else { m_pVpb->pb.ve.originVolume = m_pVpb->pb.ve.targetVolume = vol; } } void HardwareChannel::SetVoiceMix( const AXPBMIX& mix, bool immediatelySync ) { if ( ! IsAvailable() ) return; if ( IsPlaying() ) { m_pVpb->pb.mix.main.originL = m_pVpb->pb.mix.main.l; m_pVpb->pb.mix.main.originR = m_pVpb->pb.mix.main.r; m_pVpb->pb.mix.main.l = mix.main.l; m_pVpb->pb.mix.main.r = mix.main.r; for ( int i=0; ipb.mix.aux[i].originL = m_pVpb->pb.mix.aux[i].l; m_pVpb->pb.mix.aux[i].originR = m_pVpb->pb.mix.aux[i].r; m_pVpb->pb.mix.aux[i].l = mix.aux[i].l; m_pVpb->pb.mix.aux[i].r = mix.aux[i].r; } } else { m_pVpb->pb.mix.main.originL = m_pVpb->pb.mix.main.l = mix.main.l; m_pVpb->pb.mix.main.originR = m_pVpb->pb.mix.main.r = mix.main.r; for ( int i=0; ipb.mix.aux[i].originL = m_pVpb->pb.mix.aux[i].l = mix.aux[i].l; m_pVpb->pb.mix.aux[i].originR = m_pVpb->pb.mix.aux[i].r = mix.aux[i].r; } } if ( immediatelySync ) m_pVpb->sync |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL; else m_SyncFlag |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL; } HardwareChannel::DspAddress HardwareChannel::GetCurrentPlayingDspAddress() const { if ( ! IsAvailable() ) return 0; DspAddress dspAddr = static_cast( ( m_pVpb->pb.addr.currentAddressHi << 16 ) + m_pVpb->pb.addr.currentAddressLo ); return dspAddr; } HardwareChannel::DspAddress HardwareChannel::GetLoopStartDspAddress() const { if ( ! IsAvailable() ) return 0; DspAddress dspAddr = static_cast( ( m_pVpb->pb.addr.loopAddressHi << 16 ) + m_pVpb->pb.addr.loopAddressLo ); return dspAddr; } HardwareChannel::DspAddress HardwareChannel::GetLoopEndDspAddress() const { if ( ! IsAvailable() ) return 0; DspAddress dspAddr = static_cast( ( m_pVpb->pb.addr.endAddressHi << 16 ) + m_pVpb->pb.addr.endAddressLo ); return dspAddr; } bool HardwareChannel::IsPlaying() const { return IsAvailable() && ( m_pVpb->pb.state == AX_PB_STATE_RUN ); } bool HardwareChannel::IsCurrentAddressCovered( const void* begin, const void* end ) const { if ( ! IsAvailable() ) return false; DspAddress dspAddr = GetCurrentPlayingDspAddress(); u32 samples = Util::GetSampleByByte( static_cast( reinterpret_cast( end ) - reinterpret_cast( begin ) ), m_Format ); DspAddress dspAddressBegin = GetDspAddressBySample( begin, 0, m_Format ); DspAddress dspAddressEnd = GetDspAddressBySample( begin, samples, m_Format ); if ( ( dspAddressBegin <= dspAddr ) && ( dspAddr < dspAddressEnd ) ) { return true; } return false; } u32 HardwareChannel::GetPlayPosition() const { if ( ! IsAvailable() ) { return 0; } if ( m_pWaveData == NULL ) { return 0; } u32 end_samples = GetSampleByDspAddress( m_pWaveData, GetLoopEndDspAddress(), m_Format ) + 1; if ( IsPlayFinished() ) { return end_samples; } u32 samples = GetSampleByDspAddress( m_pWaveData, GetCurrentPlayingDspAddress(), m_Format ); if ( samples > end_samples ) samples = end_samples; return samples; } } // namespace nw::snd::internal::driver } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw