10. サウンド

CTR-SDK のサウンドライブラリは、DSP にロードされたコンポーネントと通信することでサウンドの再生を制御します。サウンドライブラリには 24 個のボイスオブジェクトが用意されており、アプリケーションはそこから必要な数のボイスオブジェクトを確保し、ウェーブバッファ(音源データ情報)を登録することでサウンドの再生を行うことができます。

図 10-1. サウンド再生に関係するライブラリとクラスの関連図

DSP Library SND Library Voice WaveBuffer ChannelCount SampleFormat SampleRate Pitch Priority State MixParam Volume MasterVolume Status Data DefaultComponent DspCycles SyncObject status loopFlag userParam bufferAddress sampleLength Component Send Allocate Append Signal pAdpcmContext Member [R/W] Member [Read] next Id Class/Structure Member [Write] Member [Hidden] AdpcmParam InterpolationType AuxReturnVolume AuxCallback

10.1. 初期化

サウンドを再生するには DSP を使用しなければなりません。そのため、サウンド再生に使用する SND ライブラリを初期化する前に、DSP ライブラリの初期化とサウンド再生のためのコンポーネントのロードが必要となります。

DSP ライブラリの初期化は nn::dsp::Initialize() で行い、nn::dsp::LoadDefaultComponent() でサウンドの再生を行うコンポーネントをロードします。

SND ライブラリの初期化は nn::snd::Initialize() で行います。

コード 10-1. DSP、SND ライブラリの初期化
nn::Result result;
result = nn::dsp::Initialize();
result = nn::dsp::LoadDefaultComponent();
result = nn::snd::Initialize();

10.1.1. 出力バッファ数の設定

DSP から出力される最終的なサウンドデータの出力バッファの数を nn::snd::SetOutputBufferCount() で指定することができます。

コード 10-2. 出力バッファ数の設定
void nn::snd::SetOutputBufferCount(s32 count);
s32 nn::snd::GetOutputBufferCount();

count には 2 または 3 を指定することができます。デフォルトの設定は 2 です。

出力バッファの数を 2 に設定した場合は、音源データが実際に発音されるまでの遅延時間は最小となりますが、サウンドスレッドの処理に遅延が発生したときに発音が途切れることがあります。

出力バッファの数を 3 に設定した場合は、2 に設定した場合に比べて発音が約 5 ms 遅延しますが、サウンドスレッドの処理が多少遅延したときでも発音が途切れることがありません。

この関数は出力バッファの内容をクリアしますので、ライブラリの初期化直後などの無音状態で呼び出してください。

現在の設定は nn::snd::GetOutputBufferCount() で取得することができます。

10.2. ボイスオブジェクトの確保

SND ライブラリは、自身の持つ 24 個のボイスオブジェクトに結び付けられた音源データ情報をもとにサウンドを合成し、再生します。つまり、最大で 24 の音源データを同時に再生することができます。

ボイスオブジェクトは有限のリソースですので、アプリケーションは再生したい音源データに結びつけるボイスオブジェクトを、nn::snd::AllocVoice() でライブラリから確保しなければなりません。

コード 10-3. ボイスオブジェクトの確保
nn::snd::Voice* nn::snd::AllocVoice(
        s32 priority, nn::snd::VoiceDropCallbackFunc callback, uptr userArg);

priority には、確保するボイスオブジェクトの優先順位を 0 ~ nn::snd::VOICE_PRIORITY_NODROP(0x7FFF = 32767)の範囲で指定します。値が大きいほど優先度は高くなります。範囲外の値を指定した場合は、アサートで停止します。

priority の指定によって、すでにボイスオブジェクトが最大数まで確保されているときの動作が異なります。

priority nn::snd::VOICE_PRIORITY_NODROP の場合、確保済みのボイスオブジェクトのうちで最低の優先順位が同じ nn::snd::VOICE_PRIORITY_NODROP ならばボイスオブジェクトの確保に失敗して NULL が返されます。それ以外の場合、確保済みのボイスオブジェクトのうちで最低の優先順位であるボイスオブジェクトが解放(ドロップ)されます。確保しようとしているボイスオブジェクトの優先順位が一番低い場合は確保に失敗します。

callback には、ライブラリが強制的にボイスオブジェクトをドロップしたときに呼び出されるコールバック関数を指定します。NULL を指定した場合はドロップ時にコールバックが発生しません。userArg にはコールバック時に渡される引数を指定します。不要な場合は NULL を渡してください。

コールバック関数の型は以下のように定義されています。

コード 10-4. ボイスオブジェクトのドロップコールバック関数
typedef void (* VoiceDropCallbackFunc)(nn::snd::Voice *pVoice, uptr userArg);

pVoice はドロップされたボイスオブジェクトへのポインタ、userArg は確保時に指定した引数です。

10.2.1. ボイスドロップ処理のモード

SND ライブラリは、ボイスオブジェクトが上限を超えて確保されようとしたときや、DSP 内部での処理負荷が増大する恐れがあるときに優先度の低いボイスオブジェクトを解放(ドロップ)します。

このうち、処理負荷による制御には 2 つのモードが用意されており、nn::snd::SetVoiceDropMode() で設定することができます。

コード 10-5. ボイスドロップ処理のモード設定
void nn::snd::SetVoiceDropMode(nn::snd::VoiceDropMode mode);

mode VOICE_DROP_MODE_DEFAULT を指定した場合は、予測値のみを参考にしてドロップ処理を行います。

VOICE_DROP_MODE_REAL_TIME を指定した場合は、予測値に加えて実際の処理負荷を参考にしてドロップ処理を行います。ただし、このモードに設定する場合は出力バッファ数が 3 に設定されていなければなりません。

10.3. 音源データ情報の設定

音源データを格納するための領域(バッファ領域)は、デバイスメモリ上の領域から 32 Byte アライメントの連続した領域で確保しなければなりません。バッファ領域は 32 Byte アライメント、サイズが 32 Byte の倍数でなければなりません。その中から音源データのバッファを確保する際は、先頭アドレスが 32 Byte アライメントの連続した領域でなければならない制限はそのままですが、サイズは 32 Byte の倍数でなくてもかまいません。

バッファに書き込んだ音源データは、そのままではキャッシュにしか書き込まれていない場合があります。必ず nn::snd::FlushDataCache() を呼び出して、メモリに書き込まれている状態にしてください。

音源データは音源データ情報の構造体 nn::snd::WaveBuffer を介して DSP に渡します。音源データ情報は、音源データに関する情報を設定する前に、nn::snd::InitializeWaveBuffer() で初期化しなければなりません。再生を完了した音源データ情報を再利用する場合も同様です。

初期化した音源データ情報に設定する情報は、バッファの先頭アドレス(bufferAddress)、サンプル長(sampleLength)、ループ指定フラグ(loopFlag)の各メンバ変数に設定します。サンプルフォーマットが DSP ADPCM の場合は、ADPCM コンテキスト(pAdpcmContext)への設定も行ってください。ユーザーパラメータ(userParam)には任意のデータを設定することができます。そのほかのメンバは変更しないでください。

音源データに関する、そのほかの情報(チャンネル数、サンプルフォーマット、サンプリングレート、ADPCM パラメータの基本的な情報)はボイスオブジェクトに設定します。

チャンネル数は nn::snd::Voice::SetChannelCount() で設定することができます。モノラルのデータならば 1 を、ステレオのデータならば 2 を設定してください。それ以外の値は無効です。

サンプルフォーマットは nn::snd::Voice::SetSampleFormat() で設定することができます。SND ライブラリが対応している音源データのフォーマットは 8 bit / 16 bit PCM と DSP ADPCM の 3 フォーマットです。

表 10-1. サンプルフォーマット一覧

種別

ステレオ再生

SAMPLE_FORMAT_PCM8

8 bit PCM

interleaved

SAMPLE_FORMAT_PCM16

16 bit PCM

interleaved

SAMPLE_FORMAT_ADPCM

DSP ADPCM

不可

サンプルフォーマットが DSP ADPCM の場合、ADPCM パラメータを nn::snd::Voice::SetAdpcmParam() で設定してください。また、音源データのヘッダに格納されている ADPCM コンテキストデータの構造体へのアドレスを nn::snd::WaveBufferpAdpcmContext メンバ変数に設定し、設定したコンテキストデータはバッファの再生が終了するまで変更しないでください。

サンプリングレートは nn::snd::Voice::SetSampleRate() で設定することができます。音源データの周波数そのままの値を設定してください。

音声データの補間方法は nn::snd::Voice::SetInterpolationType() で設定することができ、現在の設定は nn::snd::Voice::GetInterpolationType() で取得することができます。対応している補間方法は以下の 3 種類です。デフォルトでは INTERPOLATION_TYPE_POLYPHASE が設定されています。

表 10-2. 補間方法一覧

補間方法

INTERPOLATION_TYPE_POLYPHASE

4 点による補間を行います。設定されたサンプルレートとピッチから、最適な係数が選択されます。

INTERPOLATION_TYPE_LINEAR

線形補間を行います。

INTERPOLATION_TYPE_NONE

補間を行いません。音源データの周波数が 32728 Hz ではない場合、再生されるサウンドにノイズが発生します。

音源データに関する情報をすべて設定したあと、nn::snd::Voice::AppendWaveBuffer() でボイスオブジェクトに音源データ情報を登録します。複数の音源データ情報を続けて登録することができますが、1 サウンドフレーム(約 4.889 ミリ秒)のうちに 1 つのボイスオブジェクトで再生される音源データ情報は最大で 4 つです。

登録後の音源データの状態は nn::snd::WaveBufferStatus メンバ変数で確認することができます。ボイスオブジェクトの登録前ならば STATUS_FREE、再生待ちならば STATUS_WAIT、再生中ならば STATUS_PLAY、再生完了ならば STATUS_DONE です。このうち、STATUS_WAITSTATUS_PLAY のときは、音源データがボイスオブジェクトの管理下におかれていますので、設定などを変更しないでください。

nn::snd::Voice::UpdateWaveBuffer() を呼び出して、登録されている音源データ情報を変更することができます。ただし、変更可能な情報はサンプル長(sampleLength)とループ指定フラグ(loopFlag)のみで、SendParameterToDsp() による更新のタイミングによっては指定されたサンプル長よりもあとのデータが再生されたり、すでに再生が完了しているために変更が無効になったりすることがあります。

登録された音源データ情報は nn::snd::Voice::DeleteWaveBuffer() で削除することができます。SendParameterToDsp() による更新で、状態が再生完了になるまで情報を書き換えないようにしてください。

DSP ADPCM の音源データをストリーム再生する場合、pAdpcmContext メンバ変数への設定はボイスオブジェクトに最初に登録される音源データのみでかまいません。そのあとに登録される音源データの pAdpcmContext メンバ変数が NULL の場合、コンテキストの更新が行われません。

10.4. サウンドスレッドの作成

アプリケーションでスレッド(サウンドスレッド)を作成し、そのスレッド内で DSP ライブラリから SND ライブラリへの通知を待つように設計してください。これは約 4.889 ミリ秒ごとに通知が行われるためです。また、作成するスレッドの優先度はなるべく高く設定し、スレッド内では重い処理を行わないようにしなければ、サウンドの再生が途切れる可能性があります。

サウンドスレッド内では、基本的に以下の処理をループさせることになります。

  1. DSP ライブラリからの通知を nn::snd::WaitForDspSync() で待ちます。
  2. アプリケーションは確保しているボイスオブジェクトに登録された音源データ情報の状態を確認し、再生が完了しているならば次に再生する音源データ情報を nn::snd::Voice::AppendWaveBuffer() で登録します。その際、サウンドスレッド内では音源データ情報の登録だけを行い、サウンドの再生に使用する音源データはあらかじめ別のスレッドでバッファにロードするようにしてください。
  3. nn::snd::SendParameterToDsp() を呼び出し、前回の通知からこれまでに変更されたパラメータや新たに登録された音源データ情報などを DSP に対して送信し、サウンドの再生に反映させます。

 

注意:

nn::snd::AllocVoice()nn::snd::FreeVoice() を除いて、ボイスオブジェクトを操作する関数はスレッドセーフではありません。サウンドスレッド以外のスレッドで呼び出す場合は、クリティカルセクションなどで排他制御を行う必要があります。

10.5. サウンドの再生

サウンドの再生・停止・一時停止はボイスオブジェクトの状態を変更することで行います。状態の設定と取得はそれぞれ nn::snd::Voice::SetState()nn::snd::Voice::GetState() で行うことができます。サウンドの再生を開始または一時停止状態を解除するときは STATE_PLAY を、停止するときは STATE_STOP を、一時停止するときは STATE_PAUSE を、それぞれ設定してください。

表 10-3. ボイスオブジェクトの状態

状態

説明

STATE_PLAY

サウンドの再生を指令する、または再生中です。

STATE_STOP

サウンドの停止を指令する、または停止しています。

STATE_PAUSE

サウンドの一時停止を指令する、または一時停止中です。STATE_PLAY に変更することで続きから再生されます。

状態の変更はすぐに反映されるわけではありません。nn::snd::SendParameterToDsp() によってパラメータが送信されるまで、つまりサウンドフレームの更新間隔(約 4.889 ミリ秒)の遅延が発生する可能性があります。

再生に関する設定は、状態以外にもいくつかあります。

優先順位

ボイスオブジェクトの優先順位を設定するには、nn::snd::Voice::SetPriority() を呼び出します。現在の設定を取得するには、nn::snd::Voice::GetPriority() を呼び出してください。再生は優先順位の高いボイスオブジェクト(同じ優先順位ならば後に確保したもの)から行われるため、処理負荷が要因となって優先順位の低いボイスが再生されない可能性があります。

マスターボリューム

マスターボリュームは SND ライブラリ全体のボリュームです。nn::snd::SetMasterVolume() で設定することができ、1.0 で等倍に設定されます。現在の設定を取得するには、nn::snd::GetMasterVolume() を呼び出してください。

ボリューム

ボイスオブジェクトそれぞれのボリュームです。nn::snd::Voice::SetVolume() で設定することができます。現在の設定を取得するには、nn::snd::Voice::GetVolume() を呼び出してください。ボリュームは 1.0 で等倍に設定されます。

ミックスパラメータ

ミックスパラメータは 4 チャンネル(FrontLeft / FrontRight / RearLeft / RearRight)それぞれのゲインのことです。

nn::snd::Voice::SetMixParam() で設定することができます。現在の設定を取得するには、nn::snd::Voice::GetMixParam() を呼び出してください。nn::snd::MixParam 構造体のそれぞれのメンバにゲインの値が格納されます。ゲインは 1.0 で等倍に設定されます。

サウンドの再生ボリュームは、マスターボリューム、ボリューム、ミックスパラメータの設定値が乗算されたものとなります。

ピッチ

ピッチは元の音源データのサンプリングレートに対する速度比のことです。nn::snd::Voice::SetPitch() で設定することができます。現在の設定を取得するには、nn::snd::Voice::GetPitch() を呼び出してください。ピッチは 1.0 で等倍に設定されます。サンプリングレートが 32 kHz の音源データに対して 0.5 を設定した場合、音源データは 16 kHz で再生されます。

問い合わせ

サウンドが再生中であるかどうかは nn::snd::Voice::IsPlaying() の呼び出しで判別することができます。

現在の再生位置は、nn::snd::Voice::GetPlayPosition() で取得することができます。返される値は、DSP によって次に処理されるメインメモリ上のデータの先頭位置(サンプル数)です。

AUX バス

A と B の 2 つの AUX バスを利用して、再生されるサウンドにディレイなどのエフェクトをかけることができます。AUX バスのボリュームは nn::snd::SetAuxReturnVolume()nn::snd::GetAuxReturnVolume() で設定および取得を行うことができます。ボリュームは 1.0 で等倍に設定されます。ミックスパラメータには AUX バスのゲインを設定するメンバがありますので、AUX バスへ送られる音声データをチャンネルごとに調整することができます。

AUX バスでのサウンドの加工はそれぞれのバスに設定されたコールバック関数で行います。コールバック関数の設定は nn::snd::RegisterAuxCallback() で行うことができ、解除は nn::snd::ClearAuxCallback() で行うことができます。現在設定されているコールバック関数の取得は nn::snd::GetAuxCallback() で行うことができます。コールバック関数の型は以下のように定義されています。

コード 10-6. AUX バスのコールバック関数
typedef void(*AuxCallback)(nn::snd::AuxBusData* data, s32 sampleLength,
                           uptr userData);

data にはサンプル長(バイトサイズではありません)が sampleLength の音声データが渡されます。チャンネルごとにバッファのアドレスが設定されており、それぞれのバッファを書き換えることで加工した音声データを AUX バスの出力として再生することができます。

ボイスの補間方法

音源データのサンプリングレートから再生されるサウンドの周波数へと変換される際の補間方法を設定することができます。nn::snd::Voice::SetInterpolationType()nn::snd::Voice::GetInterpolationType() で補間方法の設定と取得を行うことができます。

補間方法は、デフォルトの 4 点補間(INTERPOLATION_TYPE_POLYPHASE)、線形補間(INTERPOLATION_TYPE_LINEAR)、補間なし(INTERPOLATION_TYPE_NONE)から選択します。4 点補間では、ライブラリがサンプリングレートとピッチから最適な係数を選択します。

フィルタ

ボイスオブジェクトごとにフィルタを適用することができます。フィルタは nn::snd::Voice::SetFilterType()nn::snd::Voice::GetFilterType() で設定および取得を行うことができます。設定可能なフィルタには、単極フィルタ(FILTER_TYPE_MONOPOLE)と双極フィルタ(FILTER_TYPE_BIQUAD)があり、デフォルトはフィルタなし(FILTER_TYPE_NONE)に設定されています。また、それぞれのフィルタの係数を設定する関数が用意されています。

単極フィルタの係数は nn::snd::Voice::SetMonoFilterCoefficients()nn::snd::Voice::GetMonoFilterCoefficients() で設定および取得を行うことができます。係数そのままを設定することも、カットオフ周波数を指定して単極ローパスフィルタとして設定することもできます。

双極フィルタの係数は nn::snd::Voice::SetBiquadFilterCoefficients()nn::snd::Voice::GetBiquadFilterCoefficients() で設定および取得を行うことができます。こちらは係数による設定のみに対応しています。

係数による設定では、ベースとなる周波数を 32728 Hz としてそれぞれのフィルタの係数を計算してください。

クリッピングモード

最終的なサウンド出力は 16 bit をオーバーフローした部分がクリッピングされたものになります。クリッピングの方法として、通常のクリッピングと高周波ノイズが軽減されるソフトクリッピングを利用することができます。クリッピング方法の指定は、nn::snd::SetClippingMode() でクリッピングモードの設定を行います。通常のクリッピングを指定する場合は CLIPPING_MODE_NORMAL を、ソフトクリッピングを指定する場合は CLIPPING_MODE_SOFT を指定します。現在の設定は nn::snd::GetClippingMode() で取得することができ、デフォルトはソフトクリッピングが設定されています。

ソフトクリッピングを用いた場合は非線形のクリップ処理が行われるため、通常のクリップ処理で発生する音割れを緩和することができますが、最大振幅に達していないサンプル値も修正されてしまいます。通常の音楽など、多くの場合はほとんど影響がありませんが、単一周波数のサイン波を入力した場合などに倍音成分が付加される可能性があります。

下図は、振幅の大きなサイン波がそれぞれのクリッピングモードでクリップ処理された結果の波形です。緑の線(Soft Clip)がソフトクリッピングモードによるクリップ処理の結果を示しています。

図 10-2. クリッピングモードによるクリップ処理結果の違い

amplitude Normal Clip Soft Clip tim [sample]

自動フェードイン

再生開始時のボリュームを 0 から設定されたボリュームまで 1 サウンドフレームかけて変化させ、自動的にフェードインさせることができます。フェードイン処理は nn::snd::Voice::SetStartFrameFadeInFlag() でボイスオブジェクトごとに設定することができ、true に設定することで処理が行われるようになります。デフォルトは false(フェードイン処理を行わない)に設定されています。

bcwav ファイルの利用

nn::snd::Voice クラスの SetupBcwav() を使用することで、ctr_WaveConverter でコンバートされた波形ファイル(bcwav ファイル)を簡単にサウンドの再生に利用することができます。

コード 10-7. bcwav ファイルの利用
bool nn::snd::Voice::SetupBcwav(uptr addrBcwav,
  nn::snd::WaveBuffer* pWaveBuffer0, nn::snd::WaveBuffer* pWaveBuffer1,
  nn::snd::Bcwav::ChannelIndex channelIndex = nn::snd::Bcwav::CHANNEL_INDEX_L);

addrBcwav には、bcwav ファイルを読み込んだバッファの先頭アドレスを指定します。バッファに読み込まれた内容はそのまま音源データとして利用しますので、バッファは音源データと同様にデバイスメモリから 32 Byte アライメントの先頭アドレス、32 Byte の倍数で確保し、必ず読み込んだ内容を nn::snd::FlushDataCache() でメモリに書き込まれている状態にしてください。

pWaveBuffer0 pWaveBuffer1 には、初回再生時とループ再生時に使用する nn::snd::WaveBuffer 構造体へのポインタを指定します。構造体の実体はアプリケーションで用意しなければなりません。ループフラグの設定されていない音源であることが分かっている場合は、pWaveBuffer1NULL を渡すことができます。

channelIndex には、音源データがステレオフォーマットである場合に、Voice クラスにどちらのチャンネルの音源データを割り当てるのかを指定します。音源データがモノラルである場合、この引数には CHANNEL_INDEX_L を指定しなければなりません。現在、ctr_WaveConverter でコンバートされたステレオの音源データは interleaved ではありません。そのため、ステレオの bcwav ファイルを再生するには、Voice クラスが 2 つ必要になります。

処理に成功し、再生準備が整った場合は true が返されます。bcwav ファイルのヘッダにないパラメータのうち、ボリュームとピッチには 1.0 が自動的に設定されます。ミックスパラメータの設定は行われません。

bcwav ファイルのヘッダ情報などにアクセスする場合は、nn::snd::Bcwav クラスを利用してください。

エフェクト(ディレイ、リバーブ)

サウンドの再生にエフェクトをかけるには、AUX バスのコールバック関数を利用してアプリケーションで実装する以外にも、ライブラリで用意されているエフェクトを適用することができます。

SND ライブラリでは、ディレイ(nn::snd::FxDelay クラス)とリバーブ(nn::snd::FxReverb クラス)を用意しています。これらのエフェクトを利用する場合は、それぞれのクラスのインスタンスを生成し、パラメータの設定とワークメモリの確保を行ってから、そのインスタンスとエフェクトを適用する AUX バスを指定して nn::snd::SetEffect() を呼び出します。1 つのバスには 1 種類のエフェクトのみ適用することができ、複数回呼び出した場合は最後に呼び出したエフェクトが適用されます。エフェクトの処理は DSP ではなく CPU で行われます。

エフェクトを解除する場合は、AUX バスを指定して nn::snd::ClearEffect() を呼び出してください。この関数はエフェクトのみを解除しますので、AUX バスのコールバック関数は解除されません。

補足:

エフェクトのパラメータについては、関数リファレンスを参照してください。

10.5.1. サウンド出力モード

SND ライブラリのサウンド出力のモードにはモノラル、ステレオ、サラウンドの 3 種類のモードが用意されており、nn::snd::SetSoundOutputMode()nn::snd::GetSoundOutputMode() で設定と現設定の取得を行うことができます。

コード 10-8. サウンド出力モードの定義と設定および取得の関数
typedef enum
{
    OUTPUT_MODE_MONO       = 0,
    OUTPUT_MODE_STEREO     = 1,
    OUTPUT_MODE_3DSURROUND = 2
} nn::snd::OutputMode;

bool nn::snd::SetSoundOutputMode(nn::snd::OutputMode mode);
nn::snd::OutputMode nn::snd::GetSoundOutputMode(void);

nn::snd::SetSoundOutputMode() の返り値が true ならば処理に成功し、サウンド出力モードが mode で指定されたモードに設定されます。デフォルトのサウンド出力モードは OUTPUT_MODE_STEREO(ステレオ)に設定されています。

10.5.1.1. モノラル

全ボイスと AUX バスの出力がミックスされた 4 チャンネル(FrontLeft / FrontRight / RearLeft / RearRight)に対して、以下のように処理を行った結果を左右のスピーカーから出力します。

Output = ( FrontLeft + FrontRight + RearLeft + RearRight ) * 0.5

10.5.1.2. ステレオ

全ボイスと AUX バスの出力がミックスされた 4 チャンネル(FrontLeft / FrontRight / RearLeft / RearRight)に対して、以下のように処理を行った結果を左右のスピーカーからそれぞれ出力します。

OutputLeft = ( FrontLeft + RearLeft )

OutputRight = ( FrontRight + RearRight )

10.5.1.3. サラウンド

全ボイスと AUX バスの出力がミックスされた 4 チャンネル(FrontLeft / FrontRight / RearLeft / RearRight)に対して 3D サラウンド処理を行い、出力に空間的な広がりを与えます。3D サラウンド処理は仮想スピーカーの位置とかかり具合のパラメータからスピーカー出力を計算します。ボイス単位ではなく、ミックス後の出力に対して 3D サラウンド処理が行われますが、フロントチャンネル(FrontLeft と FrontRight)の出力に関しては、3D サラウンド処理の影響を受けないようにボイス単位でバイパスすることができます。

3D サラウンド処理が行われると、パンの位置を中心に設定したときよりも、左右いずれかの端に設定したときに音量が大きく感じられる傾向があります。この音量の違いが気になる場合は、左右の端におけるミックスパラメータの上限を 0.8 程度に制限するなどの補正を行うことで解消される可能性があります。

仮想スピーカーの位置

仮想スピーカー位置のモードを nn::snd::SetSurroundSpeakerPosition() で指定します。関数の呼び出しによるモードの指定に成功した場合は true が返されます。

コード 10-9. 仮想スピーカー位置の設定
bool nn::snd::SetSurroundSpeakerPosition(nn::snd::SurroundSpeakerPosition pos);

pos に指定する値は以下のモードから選択します。

表 10-4. 仮想スピーカー位置のモード

設定値

説明

SURROUND_SPEAKER_POSITION_SQUARE

Square モード。正方形を形作るように配置されます。

SURROUND_SPEAKER_POSITION_WIDE

Wide モード。やや左右に広がる形に配置されます。

仮想スピーカーのモードによって、4 つのチャンネルがどのような角度で左右対称に配置するのかが決定します。下図は、それぞれのモードで仮想スピーカーがどのような角度で配置されるのかを示したものです。

図 10-3. 仮想スピーカー位置のモードと仮想スピーカーの配置

FrontLeft FrontRight RearLeft RearRight Center 45° 135° 60° 110° SURROUND_SPEAKER_POSITION_SQUARE SURROUND_SPEAKER_POSITION_WIDE Listening Position

かかり具合(サラウンドのデプス値)

nn::snd::SetSurroundDepth() で指定するデプス値によって、サラウンド効果の大小を変化させることができます。関数の呼び出しによるデプス値の指定に成功した場合は true が返されます。

コード 10-10. サラウンドのデプス値の設定
bool nn::snd::SetSurroundDepth(f32 depth);

depth には 0.0 ~ 1.0 の範囲でデプス値を指定します。0.0 で効果が最小、1.0 で効果が最大になります。デフォルトは 1.0 に設定されています。

3D サラウンド処理は、出力がスピーカーかヘッドホンかに応じて自動的に異なる処理を行います。このデプス値はスピーカーから出力されるときには有効ですが、ヘッドホンから出力されるときには無効となり、サラウンド効果が変化しません。

フロントバイパス設定

リアチャンネルの出力は必ず 3D サラウンド処理の影響を受けますが、フロントチャンネルの出力が 3D サラウンドの影響を受けないように、バイパスするかどうかをボイスオブジェクト単位で設定することができます。バイパスの設定は nn::snd::Voice クラスのメンバ関数 SetFrontBypassFlag() で行うことができます。

コード 10-11. ボイスオブジェクトのフロントバイパス設定
void nn::snd::Voice::SetFrontBypassFlag(bool flag);

フロントチャンネルの出力をバイパスさせる場合は、flagtrue を渡してください。デフォルトは false(バイパスしない)に設定されています。

図 10-4. フロントバイパス設定と 3D サラウンド処理の影響

Voice Bypass 3D Surround Front Rear true false Output

フロントバイパス設定は AUX バスのフロントチャンネルに対しても行うことができます。

コード 10-12. AUX バスのフロントバイパス設定
bool nn::snd::SetAuxFrontBypass(nn::snd::AuxBusId busId, bool flag);

busId には AUX バスの ID を、flag にはバイパスさせる場合に true を渡します。デフォルトは、全バスが false(バイパスしない)に設定されています。

10.5.2. ヘッドホンの接続状態

ヘッドホン端子にヘッドホンが接続されているかどうかを nn::snd::GetHeadphoneStatus() または nn::snd::UpdateHeadphoneStatus() で判断することができます。ヘッドホンが接続されているときは、返り値に true が返されます。

GetHeadphoneStatus() はサウンドスレッドで呼び出される SendParameterToDsp() 内で定期的に更新される状態からヘッドホンの接続状況を返すため、最大で 32 サウンドフレーム(約 160 ms)古い情報である場合があります。リアルタイム性が要求される状況では UpdateHeadphoneStatus() を呼び出してください。ただし、この関数は DSP の状態を更新するため、処理負荷が小さいものではないことに注意が必要です。

コード 10-13. ヘッドホンの接続状態
bool nn::snd::GetHeadphoneStatus();
bool nn::snd::UpdateHeadphoneStatus();

10.5.3. 出力される音声データの取得

最終的にスピーカーから出力される、ミックス済みの音声データをバッファに取り込むことができます。

コード 10-14. 出力される音声データの取得
bool nn::snd::GetMixedBusData(s16* pData, 
                              s32 nSamplesPerFrame = NN_SND_SAMPLES_PER_FRAME);

pData には音声データを格納するバッファを指定します。バッファの先頭アドレスは nn::snd::MIXED_BUS_DATA_ALIGNMENT ( 4 Byte ) アライメントでなければなりません。必要となるバッファのサイズは sizeof(s16) * nSamplesPerFrame * 2 で計算することができます。

nSamplesPerFrame には、1 チャンネルあたりのサンプル数(通常は NN_SND_SAMPLES_PER_FRAME)を指定します。

音声データの取り込みに成功した場合は true が返されます。音声データはステレオの 16 bit PCM データで、左右のチャンネルがインターリーブされて格納されます。ヘッダ情報は含まれていません。サンプリングレートはサウンド DSP のサンプリングレート(約 32728 Hz)です。

10.5.4. DSP ADPCM フォーマットへのエンコード

nn::snd::EncodeAdpcmData() を呼び出して、モノラルの 16 bit PCM データを DSP ADPCM フォーマットにエンコードすることができます。

コード 10-15. DSP ADPCM フォーマットへのエンコード
s32 nn::snd::GetAdpcmOutputBufferSize(s32 nSamples);
void nn::snd::EncodeAdpcmData(s16* pInput, u8* pOutput, s32 nSamples, 
                              s32 sampleRate, s32 loopStart, s32 loopEnd, 
                              nn::snd::DspsndAdpcmHeader* pInfo);

pInput には、エンコードに使用する 16 bit PCM データ(モノラル)が格納されているバッファの先頭アドレスを指定します。モノラルの音声データでなければエンコードすることができません。nSamplessampleRate には、音声データのサンプル数とサンプリングレートを指定してください。

pOutput には、エンコード結果の DSP ADPCM データを格納するバッファを指定します。バッファの先頭アドレスは 4 Byte アライメントでなければなりません。必要となるバッファのサイズは、音声データのサンプル数を引数に呼び出した nn::snd::GetAdpcmOutputBufferSize() の返り値で求めることができます。

loopStart loopEnd には、ループの開始と終了のサンプル位置を指定します。サンプル位置は、16 bit PCM データの先頭を 0 とする値で指定してください。

pInfo には、DSP ADPCM データのヘッダ情報を格納する構造体へのポインタを指定してください。

10.5.5. DSP ADPCM フォーマットのデコード

nn::snd::DecodeAdpcmData() を呼び出して、DSP ADPCM フォーマットの音声データをモノラルの 16 bit PCM データにデコードすることができます。

コード 10-16. DSP ADPCM フォーマットのデコード
void nn::snd::DecodeAdpcmData(const u8* pInput, s16* pOutput, 
                              const nn::snd::AdpcmParam& param, 
                              nn::snd::AdpcmContext& context, s32 nSamples);

pInput には、デコードする DSP ADPCM データが格納されているバッファの先頭アドレスを指定します。paramcontext には、DSP ADPCM のパラメータとコンテキストを、nSamples には、音声データのサンプル数を指定してください。

pOutput には、デコード結果の 16 bit PCM データ(モノラル)を格納するバッファを指定します。バッファの先頭アドレスは 4 Byte アライメントでなければなりません。

実行が完了したあとの context には、デコード終了時のコンテキスト情報が格納されます。

10.5.6. サンプル位置とニブル数の相互変換

PCM データのサンプル位置を DSP ADPCM のニブル数へ、または、その逆の変換を行う関数を用意しています。

コード 10-17. サンプル位置とニブル数の相互変換
u32 nn::snd::ConvertAdpcmPos2Nib(u32 nPos);
u32 nn::snd::ConvertAdpcmNib2Pos(u32 nNib);

10.5.7. 蓋閉じによるスリープを拒否した際のサウンド出力

蓋が閉じられたことによるスリープを拒否した場合、サウンドはヘッドホンに強制的に出力されますが、以下の関数でその挙動を制御することができます。

コード 10-18. 蓋閉じによるスリープを拒否した際のサウンド出力の制御
nn::Result nn::snd::SetHeadphoneOutOnShellClose(bool forceout);

引数 forceoutfalse を指定することで、蓋閉じ状態でもヘッドホンが接続されていなければスピーカーからサウンドが出力されるようになります。true を指定した場合は、ヘッドホンの接続状態に関係なく、ヘッドホンから出力されます。

注意:

引数 forceoutfalse を指定する場合は、必ずスリープ要求を拒否してください。拒否しなかった場合、蓋が閉じられてからスリープ状態に遷移するまでの間、スピーカーからサウンドが出力されることになります。

なお、スリープ要求を受け付けるコールバック関数内でこの関数を呼び出した場合、そのスリープ要求では設定が変更されず、次のスリープ要求から有効になります。

10.5.8. ノイズが発生する原因と対策

本体の仕様により、以下のようなノイズが発生することがあります。

10.5.8.1. スピーカーから 「ザー」 というノイズが発生する

原因は、スピーカーおよび筐体の機構上、500Hz 付近でビビリノイズが発生しやすい特性があり、500Hz 付近の帯域を含む音をパンを振って鳴らすとサラウンド処理がこの帯域の音を増幅してしまうためです。

以下の条件で発生します。

  • スピーカーで鳴らしている。
  • 400~600Hz のサイン波や、サイン波に近い音色を鳴らしている(フルートやホルンなど)。
  • 音量を大きくしている。
  • パンをめいっぱい振っている。
  • サラウンドを ON にしている。

以下のような対応策が考えられます。

  • パンを左右に極端に振らない。
  • もう少しセンターに寄せ、2 つのスピーカーが「程良い」強さで鳴るようにすることで音量感を維持する。
  • 400~600Hz の帯域をイコライザーでおさえる。
  • 音色を変える。

10.5.8.2. 「ジジジ」 という機械ノイズが発生する

原因は、スピーカーから大きな音量で音を出したときに発生する筐体の振動が 3Dボリュームに伝播し、3Dボリュームが筐体とぶつかるためです。

400Hz ~ 1kHz 程度のサイン波のような単音でこのノイズは顕著になり、音を出しているときに 3Dボリュームを指等で押さえてノイズが消えればこの症状です。

このノイズは、音を小さくするか、複合音にすることで目立たなくすることができます。

10.5.8.3. 「サー」 というノイズが重畳する

原因は、スピーカーの構造上の問題です。

音量が大きく、400Hz ~ 1kHz 程度のサイン波のような単音でやや顕著になります。

このノイズは、音を小さくするか、複合音にすることで目立たなくすることができます。

10.5.8.4. 特定の状況でヘッドホンから 「プツッ」 というノイズが聞こえる

以下の状況で、ヘッドホンから「プツッ」というノイズが聞こえる本体があります。

  • 電源ON/OFF時
  • スリープ入/出時
  • 互換モード遷移時

本体の仕様によるもので対策はありません。スライドボリュームを 0 にした状態で電源を ON にした際にノイズが発生する本体では上記のタイミングで必ずノイズが発生します。

なお、このノイズが発生する本体は量産実機と開発実機の中に混在していますが、PARTNER-CTR で発生することはありません。

10.6. 音源データ情報の再利用

音源データの再生が完了した音源データ情報は、nn::snd::InitializeWaveBuffer() で初期化して再利用することができます。音源データの再生が完了しているかどうかは、音源データ情報の status メンバで判断することができます。

表 10-5. 音源データ情報の状態

状態

説明

STATUS_FREE

音源データ情報を初期化した直後の状態です。

STATUS_WAIT

音源データ情報をボイスオブジェクトに登録した直後の状態です。

STATUS_PLAY

音源データが再生されている状態です。

STATUS_DONE

音源データの再生が完了している状態です。停止させた場合も含みます。

STATUS_TO_BE_DELETED

nn::snd::Voice::DeleteWaveBuffer() により、登録されていた音源データの削除が予定されている状態です。

次の nn::snd::SendParameterToDsp() の実行時に STATUS_DONE に遷移します。

状態の遷移を図にすると、以下のようになります。

図 10-5. 音源データ情報の状態遷移

STATUS_FREE STATUS_WAIT STATUS_PLAY STATUS_DONE nn::snd::InitializeWaveBuffer() nn::snd::Voice::AppendWaveBuffer() nn::snd::Voice::SetState(STATE_PLAY) nn::snd::Voice::SetState(STATE_STOP) or Playing wave data is finished.

10.7. ボイスオブジェクトの解放

不要になったボイスオブジェクトは nn::snd::FreeVoice() で解放してください。ただし、ボイスオブジェクトの確保でライブラリからドロップ対象とされたボイスオブジェクトは、この関数で解放してはいけません。

10.8. 終了処理

サウンドの再生に利用していた DSP および SND のライブラリの終了処理は、以下の手順で行ってください。

  1. nn::snd::Finalize() を呼び出して、SND ライブラリの使用を終了する。
  2. nn::dsp::UnloadComponent() を呼び出して DSP にロードしていたコンポーネントをアンロードし、DSP を停止する。
  3. nn::dsp::Finalize() を呼び出して、DSP ライブラリの使用を終了する。
コード 10-19. サウンドの終了処理
nn::snd::Finalize();
nn::dsp::UnloadComponent();
nn::dsp::Finalize();