1/*---------------------------------------------------------------------------*
2  Project:  NintendoWare
3  File:     snd_HardwareChannelAX.cppi
4
5  Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6
7  These coded instructions, statements, and computer programs contain
8  proprietary information of Nintendo of America Inc. and/or Nintendo
9  Company Ltd., and are protected by Federal copyright law.  They may
10  not be disclosed to third parties or copied or duplicated in any form,
11  in whole or in part, without the prior written consent of Nintendo.
12
13  $Revision:$
14 *---------------------------------------------------------------------------*/
15
16#include <nn/os/cache.h>
17
18namespace nw {
19namespace snd {
20namespace internal {
21namespace driver {
22
23namespace
24{
25
26u16 GetSDKFormatFromSampleFormat( SampleFormat sampleFormat )
27{
28    switch( sampleFormat )
29    {
30    case SAMPLE_FORMAT_DSP_ADPCM:
31        return AX_PB_FORMAT_ADPCM;
32
33    case SAMPLE_FORMAT_PCM_S8:
34        return AX_PB_FORMAT_PCM8;
35
36    /*
37    case SAMPLE_FORMAT_PCM_S16_BE:
38    case SAMPLE_FORMAT_PCM_S16_LE:
39    */
40    case SAMPLE_FORMAT_PCM_S16:
41        return AX_PB_FORMAT_PCM16;
42
43    case SAMPLE_FORMAT_IMA_ADPCM:
44    default:
45        NW_ASSERTMSG( false, "Invalid format\n" );
46        return AX_PB_FORMAT_ADPCM;
47    }
48}
49
50} // anonymous namespace
51
52
53HardwareChannel::DspAddress HardwareChannel::GetDspAddressBySample(
54        const void* baseAddress,
55        u32 samples,
56        SampleFormat format )
57{
58    if ( baseAddress != NULL )
59    {
60        baseAddress = reinterpret_cast<const void*>( OS_CachedToPhysical( baseAddress ) );
61    }
62
63    HardwareChannel::DspAddress addr = 0;
64
65    switch ( format )
66    {
67    case SAMPLE_FORMAT_DSP_ADPCM:
68        addr =
69            ( reinterpret_cast<HardwareChannel::DspAddress>( baseAddress ) << 1 ) +
70            ( samples / 14 ) * 16 + ( samples % 14 ) + 2
71            ;
72        break;
73
74    case SAMPLE_FORMAT_PCM_S8:
75        addr =
76            reinterpret_cast<HardwareChannel::DspAddress>( baseAddress ) +
77            samples
78            ;
79        break;
80
81    case SAMPLE_FORMAT_PCM_S16:
82        addr =
83            ( reinterpret_cast<HardwareChannel::DspAddress>( baseAddress ) >> 1 ) +
84            samples
85            ;
86        break;
87
88    case SAMPLE_FORMAT_IMA_ADPCM:
89    default:
90        NW_ASSERTMSG( false, "Invalid format\n" );
91        break;
92    }
93
94    return addr;
95}
96
97u32 HardwareChannel::GetSampleByDspAddress(
98        const void* baseAddress,
99        DspAddress addr,
100        SampleFormat format )
101{
102    if ( baseAddress != NULL )
103    {
104        baseAddress = reinterpret_cast<const void*>( OS_CachedToPhysical( baseAddress ) );
105    }
106
107    u32 samples = 0;
108
109    switch ( format )
110    {
111    case SAMPLE_FORMAT_DSP_ADPCM:
112        samples = addr - ( reinterpret_cast<DspAddress>( baseAddress ) << 1 );
113        samples = ( samples / 16 ) * 14 + ( samples % 16 ) - 2;
114        break;
115
116    case SAMPLE_FORMAT_PCM_S8:
117        samples = addr - reinterpret_cast<DspAddress>( baseAddress );
118        break;
119
120    case SAMPLE_FORMAT_PCM_S16:
121        samples = addr - ( reinterpret_cast<DspAddress>( baseAddress ) >> 1 );
122        break;
123
124    default:
125        NW_ASSERTMSG( false, "Invalid format\n" );
126        break;
127    }
128
129    return samples;
130}
131
132void HardwareChannel::Initialize()
133{
134}
135
136void HardwareChannel::OnInitialize()
137{
138}
139
140void HardwareChannel::SetParamBlock( ParameterBlock* vpb )
141{
142    NW_NULL_ASSERT( vpb );
143    m_pVpb = vpb;
144    m_SyncFlag = 0;
145    m_IsFirstVeUpdate = true;
146}
147
148void HardwareChannel::Sync()
149{
150    if ( ! IsAvailable() ) return;
151
152    m_pVpb->sync |= m_SyncFlag;
153    m_SyncFlag = 0;
154}
155
156bool HardwareChannel::IsPlayFinished() const
157{
158    if ( m_pWaveData == NULL ) return false;
159
160    DspAddress dspAddr = GetCurrentPlayingDspAddress();
161
162    const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress();
163
164    DspAddress beginPos = GetDspAddressBySample( zeroBuffer, 0, m_Format );
165    DspAddress endPos = beginPos;
166    switch ( m_Format )
167    {
168    case SAMPLE_FORMAT_DSP_ADPCM:
169        endPos += ( HardwareManager::ZERO_BUFFER_SIZE << 1 );
170        break;
171    case SAMPLE_FORMAT_PCM_S8:
172        endPos += ( HardwareManager::ZERO_BUFFER_SIZE );
173        break;
174    /*
175    case SAMPLE_FORMAT_PCM_S16_BE:
176    case SAMPLE_FORMAT_PCM_S16_LE:
177    */
178    case SAMPLE_FORMAT_PCM_S16:
179        endPos += ( HardwareManager::ZERO_BUFFER_SIZE >> 1 );
180        break;
181    default:
182        NW_ASSERTMSG( false, "Invalid format\n" );
183        return false;
184    }
185
186    if ( beginPos <= dspAddr && dspAddr < endPos ) {
187        return true;
188    }
189
190    return false;
191}
192
193void HardwareChannel::Run()
194{
195    if ( IsAvailable() )
196    {
197        AX_SetVoiceState( m_pVpb, AX_PB_STATE_RUN );
198    }
199}
200
201void HardwareChannel::Stop()
202{
203    if ( IsAvailable() )
204    {
205        AX_SetVoiceState( m_pVpb, AX_PB_STATE_STOP );
206    }
207}
208
209void HardwareChannel::StopAtPoint( const void* baseAddress, u32 samples )
210{
211    if ( ! IsAvailable() ) return;
212
213    const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress();
214
215    DspAddress beginPos = GetDspAddressBySample( zeroBuffer, 0, m_Format );
216    DspAddress endPos = GetDspAddressBySample( baseAddress, samples-1, m_Format );
217
218    SetVoiceLoopAddr( beginPos );
219    SetVoiceEndAddr( endPos );
220    SetVoiceLoop( AXPBADDR_LOOP_OFF );
221}
222
223void HardwareChannel::SetLoopFlag( bool loopFlag )
224{
225    if ( ! IsAvailable() ) return;
226
227    if ( loopFlag )
228    {
229        SetVoiceLoop( AXPBADDR_LOOP_ON );
230    }
231    else
232    {
233        SetVoiceLoop( AXPBADDR_LOOP_OFF );
234    }
235}
236
237void HardwareChannel::SetVoiceLoopAddr( u32 addr )
238{
239    if ( ! IsAvailable() ) return;
240
241    m_pVpb->pb.addr.loopAddressHi = static_cast<u16>( addr >> 16 );
242    m_pVpb->pb.addr.loopAddressLo = static_cast<u16>( addr & 0xFFFF );
243    if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) )
244    {
245        m_pVpb->sync |= AX_SYNC_USER_LOOPADDR;
246    }
247}
248
249void HardwareChannel::SetVoiceEndAddr( u32 addr )
250{
251    if ( ! IsAvailable() ) return;
252
253    m_pVpb->pb.addr.endAddressHi = static_cast<u16>( addr >> 16 );
254    m_pVpb->pb.addr.endAddressLo = static_cast<u16>( addr & 0xFFFF );
255    if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) )
256    {
257        m_pVpb->sync |= AX_SYNC_USER_ENDADDR;
258    }
259}
260
261void HardwareChannel::SetVoiceLoop( u32 loop )
262{
263    if ( ! IsAvailable() ) return;
264
265    m_pVpb->pb.addr.loopFlag = static_cast<u16>( loop );
266    if ( ! ( m_pVpb->sync & AX_SYNC_USER_ADDR ) )
267    {
268        m_pVpb->sync |= AX_SYNC_USER_LOOP;
269    }
270}
271
272// パラメータ設定関数
273void HardwareChannel::SetAddr(
274    bool loopFlag,
275    const void* waveAddr,
276    u32 startOffset,
277    u32 loopStart,
278    u32 loopEnd
279)
280{
281    if ( ! IsAvailable() ) return;
282
283    DspAddress startPos;
284    DspAddress loopPos;
285    DspAddress endPos;
286    if ( startOffset > loopEnd )
287    {
288        // 開始位置が範囲外なのでノイズにならないようにゼロバッファを再生して終了
289        const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress();
290        loopFlag = false;
291        startPos = GetDspAddressBySample( zeroBuffer, 0, m_Format );
292        loopPos = GetDspAddressBySample( zeroBuffer, 0, m_Format );
293        endPos = GetDspAddressBySample( zeroBuffer, 1, m_Format );
294    }
295    else
296    {
297        if ( loopFlag )
298        {
299            loopPos = GetDspAddressBySample( waveAddr, loopStart, m_Format );
300        }
301        else
302        {
303            const void* zeroBuffer = HardwareManager::GetInstance().GetZeroBufferAddress();
304            loopPos = GetDspAddressBySample( zeroBuffer, 0, m_Format );
305        }
306        startPos = GetDspAddressBySample( waveAddr, startOffset, m_Format );
307        endPos = GetDspAddressBySample( waveAddr, loopEnd-1, m_Format );
308    }
309
310    AXPBADDR addr;
311    addr.loopFlag           = static_cast<u16>( loopFlag ? AXPBADDR_LOOP_ON: AXPBADDR_LOOP_OFF );
312    addr.format             = GetSDKFormatFromSampleFormat( m_Format );
313    addr.loopAddressHi      = static_cast<u16>( loopPos  >> 16 );
314    addr.loopAddressLo      = static_cast<u16>( loopPos  &  0xFFFF );
315    addr.endAddressHi       = static_cast<u16>( endPos   >> 16 );
316    addr.endAddressLo       = static_cast<u16>( endPos   &  0xFFFF );
317    addr.currentAddressHi   = static_cast<u16>( startPos >> 16 );
318    addr.currentAddressLo   = static_cast<u16>( startPos &  0xFFFF );
319
320    AX_SetVoiceAddr( m_pVpb, const_cast<AXPBADDR*>( &addr ) );
321}
322
323void HardwareChannel::SetDspAdpcm( const DspAdpcmParam* param )
324{
325    if ( ! IsAvailable() ) return;
326
327    DspAdpcmParameterBlock adpcm;
328    switch ( m_Format )
329    {
330    case SAMPLE_FORMAT_DSP_ADPCM:
331        NW_NULL_ASSERT( param );
332        (void)std::memcpy( adpcm.a, param->coef, sizeof(u16)*16 );
333        adpcm.gain      = 0; // param->gain;
334        adpcm.predScale = param->predScale;
335        adpcm.yn1       = static_cast<u16>( param->yn1 );
336        adpcm.yn2       = static_cast<u16>( param->yn2 );
337        break;
338
339#if 0   // ほかのフォーマットで SetAdpcm() 関数に入ることは無い
340    /*
341    case SAMPLE_FORMAT_PCM_S16_BE:
342    case SAMPLE_FORMAT_PCM_S16_LE:
343    */
344    case SAMPLE_FORMAT_PCM_S16:
345        (void)std::memset( adpcm.a, 0, sizeof(u16)*16 );
346        adpcm.gain      = 0x0800;
347        adpcm.predScale = 0;
348        adpcm.yn1       = 0;
349        adpcm.yn2       = 0;
350        break;
351    case SAMPLE_FORMAT_PCM_S8:
352        (void)std::memset( adpcm.a, 0, sizeof(u16)*16 );
353        adpcm.gain      = 0x0100;
354        adpcm.predScale = 0;
355        adpcm.yn1       = 0;
356        adpcm.yn2       = 0;
357        break;
358    case SAMPLE_FORMAT_IMA_ADPCM:
359#endif
360    default:
361        NW_ASSERTMSG( false, "Invalid format\n" );
362        break;
363    }
364
365    std::memcpy( &m_pVpb->pb.adpcm, &adpcm, sizeof( adpcm ) );
366    m_SyncFlag |= AX_SYNC_USER_ADPCM;
367}
368
369void HardwareChannel::SetDspAdpcmLoop( const DspAdpcmLoopParam* param )
370{
371    if ( ! IsAvailable() ) return;
372
373    DspAdpcmLoopParameterBlock adpcmloop;
374    if ( m_Format == SAMPLE_FORMAT_DSP_ADPCM )
375    {
376        NW_NULL_ASSERT( param );
377        adpcmloop.loopPredScale = param->loopPredScale;
378        adpcmloop.loopYn1        = static_cast<u16>( param->loopYn1 );
379        adpcmloop.loopYn2        = static_cast<u16>( param->loopYn2 );
380    }
381    else
382    {
383        adpcmloop.loopPredScale = 0;
384        adpcmloop.loopYn1        = 0;
385        adpcmloop.loopYn2        = 0;
386    }
387
388    std::memcpy( &m_pVpb->pb.adpcmLoop, &adpcmloop, sizeof( adpcmloop ) );
389    m_SyncFlag |= AX_SYNC_USER_ADPCMLOOP;
390}
391
392void HardwareChannel::SetSrcType( SrcType type, f32 pitch )
393{
394    if ( ! IsAvailable() ) return;
395
396    u16 src = AX_PB_SRCSEL_POLYPHASE;   // ひとまず初期化
397    u16 coef = AX_PB_COEFSEL_16KHZ;
398
399    if ( type == SRC_TYPE_4TAP )
400    {
401        f32 ratio = GetDspRatio( pitch );
402
403        if ( ratio > 4.0f/3.0f ) coef = AX_PB_COEFSEL_8KHZ;
404        else if ( ratio > 1.0f ) coef = AX_PB_COEFSEL_12KHZ;
405        // else coef = AX_PB_COEFSEL_16KHZ;
406                // デフォルトで 16KHZ に設定されている。
407    }
408    else if ( type == SRC_TYPE_LINEAR )
409    {
410        src = AX_PB_SRCSEL_LINEAR;
411    }
412    else
413    {
414        src = AX_PB_SRCSEL_NONE;
415    }
416
417    m_pVpb->pb.srcSelect  = src;
418    m_pVpb->pb.coefSelect = coef;
419
420    m_SyncFlag |= AX_SYNC_USER_SRCSELECT;
421}
422
423void HardwareChannel::SetSrc( f32 ratio, bool initialUpdate )
424{
425    if ( ! IsAvailable() ) return;
426
427    if ( initialUpdate )
428    {
429        ratio = GetDspRatio( ratio );
430        ratio = nw::ut::Clamp( ratio, 0.0f, 65535.0f );
431
432        u32 srcBits = static_cast<u32>( 0x00010000 * ratio );
433
434        AXPBSRC src;
435        src.ratioHi = static_cast<u16>( srcBits >> 16 );
436        src.ratioLo = static_cast<u16>( srcBits & 0xFFFF );
437
438        src.currentAddressFrac = 0;
439        src.last_samples[0] = 0;
440        src.last_samples[1] = 0;
441        src.last_samples[2] = 0;
442        src.last_samples[3] = 0;
443
444        std::memcpy( &m_pVpb->pb.src, &src, sizeof( src ) );
445        m_SyncFlag &= ~AX_SYNC_USER_SRCRATIO;
446        m_SyncFlag |= AX_SYNC_USER_SRC;
447    }
448    else
449    {
450        static const u32 SRC_RATIO_BASE = 0x10000;
451        u32 r = static_cast<u32>( GetDspRatio(ratio) * SRC_RATIO_BASE );
452        m_pVpb->pb.src.ratioHi = static_cast<u16>( r >> 16 );
453        m_pVpb->pb.src.ratioLo = static_cast<u16>( r & 0xFFFF );
454        if ( ! ( m_SyncFlag & AX_SYNC_USER_SRC ) )
455        {
456            m_SyncFlag |= AX_SYNC_USER_SRCRATIO;
457        }
458    }
459}
460
461f32 HardwareChannel::GetDspRatio( f32 ratio ) const
462{
463    return ratio * m_SampleRate / AX_IN_SAMPLES_PER_SEC;
464}
465
466void HardwareChannel::SetMixParam( const MixParam& param )
467{
468    if ( ! IsAvailable() ) return;
469
470    AXPBMIX mix;
471
472    // mix構造体にvolumeを設定
473    mix.main.l = static_cast<u16>(param.mainBus[nn::snd::CHANNEL_INDEX_FRONT_LEFT]);
474    mix.main.r = static_cast<u16>(param.mainBus[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]);
475
476    mix.aux[ 0 ].l = static_cast<u16>(param.auxBusA[nn::snd::CHANNEL_INDEX_FRONT_LEFT]);
477    mix.aux[ 0 ].r = static_cast<u16>(param.auxBusA[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]);
478
479    mix.aux[ 1 ].l = static_cast<u16>(param.auxBusB[nn::snd::CHANNEL_INDEX_FRONT_LEFT]);
480    mix.aux[ 1 ].r = static_cast<u16>(param.auxBusB[nn::snd::CHANNEL_INDEX_FRONT_RIGHT]);
481
482    SetVoiceMix( mix );
483}
484
485void HardwareChannel::SetVolume( f32 volume )
486{
487    if ( ! IsAvailable() ) return;
488
489    int vol = static_cast<int>(volume * static_cast<f32>( VOICE_GAIN_MAX ) );
490    vol = nw::ut::Clamp( vol, 0, 0xffff );
491
492    if ( IsPlaying() ) {
493        m_pVpb->pb.ve.originVolume = m_pVpb->pb.ve.targetVolume;
494        m_pVpb->pb.ve.targetVolume = vol;
495    }
496    else {
497        m_pVpb->pb.ve.originVolume = m_pVpb->pb.ve.targetVolume = vol;
498    }
499}
500
501void HardwareChannel::SetVoiceMix( const AXPBMIX& mix, bool immediatelySync )
502{
503    if ( ! IsAvailable() ) return;
504
505    if ( IsPlaying() ) {
506        m_pVpb->pb.mix.main.originL = m_pVpb->pb.mix.main.l;
507        m_pVpb->pb.mix.main.originR = m_pVpb->pb.mix.main.r;
508        m_pVpb->pb.mix.main.l = mix.main.l;
509        m_pVpb->pb.mix.main.r = mix.main.r;
510
511        for ( int i=0; i<AX_AUX_BUS_NUM ;i++){
512            m_pVpb->pb.mix.aux[i].originL = m_pVpb->pb.mix.aux[i].l;
513            m_pVpb->pb.mix.aux[i].originR = m_pVpb->pb.mix.aux[i].r;
514            m_pVpb->pb.mix.aux[i].l = mix.aux[i].l;
515            m_pVpb->pb.mix.aux[i].r = mix.aux[i].r;
516        }
517    }
518    else
519    {
520        m_pVpb->pb.mix.main.originL = m_pVpb->pb.mix.main.l = mix.main.l;
521        m_pVpb->pb.mix.main.originR = m_pVpb->pb.mix.main.r = mix.main.r;
522
523        for ( int i=0; i<AX_AUX_BUS_NUM ;i++){
524            m_pVpb->pb.mix.aux[i].originL = m_pVpb->pb.mix.aux[i].l = mix.aux[i].l;
525            m_pVpb->pb.mix.aux[i].originR = m_pVpb->pb.mix.aux[i].r = mix.aux[i].r;
526        }
527    }
528
529    if ( immediatelySync ) m_pVpb->sync |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
530    else m_SyncFlag |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
531}
532
533HardwareChannel::DspAddress HardwareChannel::GetCurrentPlayingDspAddress() const
534{
535    if ( ! IsAvailable() ) return 0;
536
537    DspAddress dspAddr = static_cast<DspAddress>(
538            ( m_pVpb->pb.addr.currentAddressHi << 16 )
539            + m_pVpb->pb.addr.currentAddressLo );
540    return dspAddr;
541}
542
543HardwareChannel::DspAddress HardwareChannel::GetLoopStartDspAddress() const
544{
545    if ( ! IsAvailable() ) return 0;
546
547    DspAddress dspAddr = static_cast<DspAddress>(
548            ( m_pVpb->pb.addr.loopAddressHi << 16 )
549            + m_pVpb->pb.addr.loopAddressLo );
550    return dspAddr;
551}
552
553HardwareChannel::DspAddress HardwareChannel::GetLoopEndDspAddress() const
554{
555    if ( ! IsAvailable() ) return 0;
556
557    DspAddress dspAddr = static_cast<DspAddress>(
558            ( m_pVpb->pb.addr.endAddressHi << 16 )
559            + m_pVpb->pb.addr.endAddressLo );
560    return dspAddr;
561}
562
563bool HardwareChannel::IsPlaying() const
564{
565    return IsAvailable() && ( m_pVpb->pb.state == AX_PB_STATE_RUN );
566}
567
568bool HardwareChannel::IsCurrentAddressCovered(
569        const void* begin, const void* end ) const
570{
571    if ( ! IsAvailable() ) return false;
572
573    DspAddress dspAddr = GetCurrentPlayingDspAddress();
574
575    u32 samples = Util::GetSampleByByte( static_cast<u32>(
576            reinterpret_cast<const u8*>( end ) - reinterpret_cast<const u8*>( begin ) ),
577            m_Format );
578    DspAddress dspAddressBegin = GetDspAddressBySample(
579        begin,
580        0,
581        m_Format
582    );
583    DspAddress dspAddressEnd = GetDspAddressBySample(
584        begin,
585        samples,
586        m_Format
587    );
588
589    if ( ( dspAddressBegin <= dspAddr ) && ( dspAddr < dspAddressEnd ) )
590    {
591        return true;
592    }
593
594    return false;
595}
596
597u32 HardwareChannel::GetPlayPosition() const
598{
599    if ( ! IsAvailable() ) { return 0; }
600
601    if ( m_pWaveData == NULL ) { return 0; }
602
603    u32 end_samples = GetSampleByDspAddress(
604        m_pWaveData,
605        GetLoopEndDspAddress(),
606        m_Format ) + 1;
607
608    if ( IsPlayFinished() )
609    {
610        return end_samples;
611    }
612
613    u32 samples = GetSampleByDspAddress(
614            m_pWaveData,
615            GetCurrentPlayingDspAddress(),
616            m_Format );
617
618    if ( samples > end_samples ) samples = end_samples;
619
620    return samples;
621}
622
623} // namespace nw::snd::internal::driver
624} // namespace nw::snd::internal
625} // namespace nw::snd
626} // namespace nw
627
628
629