LoginSignup
0
0

WASAPIでS/PDIFパススルー再生

Posted at

S/PDIFって何?

S/PDIF ... Sony Philips Digital InterFace
言葉の通り、Sony社とPhilips社で仕様を定めた音声をデジタルで送受信するI/Fです。
詳細はWikipediaに譲るとして、結構昔、自分が高校生くらいの時に何でもかんでもデジタル化だよ!!
ってそんな時代に登場した端子とケーブルだと勝手に記憶してます:sweat_smile:

結構古い技術なので、最近は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どうした?:sweat_smile:

やっぱり本当の所、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から物理的に距離をとって、遠くからアナログの音を取り出すことで、ノイズは抑制できるかもです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0