S/PDIFって何?
S/PDIF ... Sony Philips Digital InterFace
言葉の通り、Sony社とPhilips社で仕様を定めた音声をデジタルで送受信するI/Fです。
詳細はWikipediaに譲るとして、結構昔、自分が高校生くらいの時に何でもかんでもデジタル化だよ!!
ってそんな時代に登場した端子とケーブルだと勝手に記憶してます
結構古い技術なので、最近はHDMIケーブルに置き換わってしまっているかと思いますが、偶にデスクトップPCにS/PDIF端子が付いてる場合があります。
そこで、S/PDIF端子を使用して、Amazonで買ったS/PDIF->アナログコンバーターを経由して音を再生しようと言うのが今回のテーマです。
使用する材料
・S/PDIF端子が付いているPC。
・S/PDIFデジタル音声コンバーター
例えば、こんなの
この2つをS/PDIFのケーブルで接続。
ヘッドフォンはデジタル音声コンバーターに接続して音声を視聴します。
とりあえず音を鳴らしてみる
Windowsのタスクバーの右下にあるスピーカーをクリック。
音声出力先で"Realtek Digital Output (Realtek(R) Audio)"を選択します。
そしたら、普通にWindows上の音が出力されていると思います。
えっ、WASAPIどうした?
やっぱり本当の所、S/PDIFは少し古い技術
S/PDIFは、デジタル化の波と共に普及したと最初に書きましたが、その時代に流行ったもう1つは、ホームシアターです。
最近じゃ7.1chとかDollby Atomのような一体何個スピーカーつなぐのよって、映画館行った方が早いわって感じですけど、あの時代は5.1chで、スピーカー5個とサブウーファーとアンプがセットになった、ホームシアターセットみたいな製品がたくさん販売されていました。
そこで登場するのはもちろんS/PDIFです。DVDプレーヤーとアンプを接続するために、S/PDIFケーブルで接続してそれぞれのスピーカーの音を鳴らしていました。
しかし、ここで問題があります。S/PDIFは帯域がそれほど大きくなく、PCM音源5.1ch分を、そのまま送信する事ができません。そのため、音声データは主にAC3(DollbyDigital)形式で圧縮されており、アンプでデコードされたのち、それぞれのスピーカーへと音が送られていました。
ちなみにHDMIは帯域が大きいのでPCM音源5.1chを圧縮せずそのまま送信する事ができます。流石です。
WASAPIでAC3圧縮されているデータをそのまま再生
先程出てきた、S/PDIFデジタル音声コンバーターは、実はAC3のデコードに対応しています。
そのため、PCでAC3圧縮をデコードせずに、そのまま送信することで、音を再生できます。
最初に出てきたPCの設定を変えるだけで音が鳴ったのは何故か?と言う事ですが、
あれは2chステレオなので、S/PDIFでも帯域が足りていて、PCMデータを送信して音を鳴らしていたのです。
ここからが本題。
WASAPIでどうやって、AC3データのパススルー再生を実現するかのポイントについて記述していきます。
WASAPIの初期化
WASAPIの設定を行う時に、圧縮されているデータをどうやった表現するかという点がポイントなのですが、それは以下のように表現します。
・S/PDIFのステレオ2chを使用する!
・でもそこで送信するデータはDollbyDigital(AC3)で圧縮されてるよ。
・実は5.1chです。(でもこの設定は要らないのかも)
Cのコードにすると以下のような感じです。
WAVEFORMATEXTENSIBLE_IEC61937 format;
memset(&format, 0x00, sizeof(format));
format.FormatExt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format.FormatExt.Format.nSamplesPerSec = 48000;
format.FormatExt.Format.wBitsPerSample = 16;
format.FormatExt.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE_IEC61937) - sizeof(WAVEFORMATEX);
format.FormatExt.Format.nChannels = 2;
format.FormatExt.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
format.FormatExt.Format.nBlockAlign = format.FormatExt.Format.nChannels * format.FormatExt.Format.wBitsPerSample / 8;
format.FormatExt.Format.nAvgBytesPerSec = format.FormatExt.Format.nSamplesPerSec * format.FormatExt.Format.nBlockAlign;
format.FormatExt.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL;
format.FormatExt.Samples.wValidBitsPerSample = 16;
format.dwEncodedSamplesPerSec = 48000;
format.dwEncodedChannelCount = 6;
format.dwAverageBytesPerSec = 0;
int nFrames = 960; // この値が小さすぎると処理が追いつかずノイズに繋がりやすい
REFERENCE_TIME hnsRequestedDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC / format.FormatExt.Format.nSamplesPerSec * nFrames + 0.5);
hr = m_audio_client->Initialize(
AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
hnsRequestedDuration,
hnsRequestedDuration,
&format.FormatExt.Format,
NULL
);
WASAPIでAC3データ再生
基本的には、PCMデータを再生する方法と同一です。
ポイントとしては以下の通りです。
・送信可能なデータサイズは、フレーム数 x バイトアライン
・AC3のRAWデータではなく、S/PDIFのヘッダでラッピングされたAC3データを送信する。
※細かなエラー処理は省略してます。
// 最初のデータを設定しておく
pRenderClient->GetBuffer(bufferFrameCount, &pData);
int ret = fread(pData, 1, bufferFrameCount * format.FormatExt.Format.nBlockAlign, fp);
pRenderClient->ReleaseBuffer(ret / format.FormatExt.Format.nBlockAlign, 0);
// 再生開始
m_audio_client->Start();
unsigned char* data = new unsigned char[bufferFrameCount * format.FormatExt.Format.nBlockAlign];
while (true)
{
// 次に送るデータを準備しておく
memset(data, 0x00, bufferFrameCount * format.FormatExt.Format.nBlockAlign);
int ret = fread(data, 1, bufferFrameCount * format.FormatExt.Format.nBlockAlign, fp);
// WASAPIからの次データ送信許可を待つ
DWORD retval = WaitForSingleObject(hEvent, INFINITE);
if (retval != WAIT_OBJECT_0)
{
break;
}
// データを送信する
pRenderClient->GetBuffer(bufferFrameCount, &pData);
memcpy(pData, data, bufferFrameCount * format.FormatExt.Format.nBlockAlign);
hr = pRenderClient->ReleaseBuffer(ret / format.FormatExt.Format.nBlockAlign, 0);
if (ret < bufferFrameCount * format.FormatExt.Format.nBlockAlign)
{
// 終了
break;
}
}
// 再生停止
m_audio_client->Stop();
S/PDIFでラップされたAC3データ作成方法
今回は、ffmpegを使用して事前にS/PDIF(AC3)データを準備しておきました。
% ffmpeg -i 変換元のAC3のRAWデータファイル名.ac3 変換後のS/PDFI(AC3)データファイル名.spdif
実際は、このような処理をリアルタイムで実行する事になると思いますが、それもffmpeg等を利用することで実現できると思います。
結局、音質は?
デジタルです。悪い筈がありません。(いやPCMだってデジタルだけど?)
ノイズが乗るとすれば、それはアナログに変換した後、ヘッドフォンのコネクタの接続のあたりに、ノイズが待ち構えていただけのことです。(ノイズは、この世界の何処にでも潜んでいます。)
PCが最大のノイズ発生源であり、PCから物理的に距離をとって、遠くからアナログの音を取り出すことで、ノイズは抑制できるかもです。