プログラム例

WWWWARD > ソフトウェア > SounDecoder > プログラム例

[備考開始]

この文書は古くなっています。その内SounDecoder 0.2.0.0用に書き直すので、暫くお待ち下さい。

[備考終了]

[注釈開始]

次のSounDecoderのバージョンでは、 SounDecoder.SoundDecoder.Open() は明示的に呼び出されるべきものとする予定です。これは、プロパティの値の取得など、一見軽そうな処理の中にも、実際には重い(かも知れない)デコーダの初期化が行われうる事が不適切だと(後になって)思われたからです。

SounDecoder.SoundDecoder.Open() の二回目以降の呼び出しは今までと同様に無視されるようにする為、「このコードが実行される段階では確実にデコーダは開かれている」という確証が無い限り、 SounDecoder.SoundDecoder.Open() を予め呼び出すように書いて下さい。

なお、このドキュメントのこの備考以降及びリファレンスは未だSounDecoder 0.1.0.0用のものであり、 SounDecoder.SoundDecoder.Open() は明示的に呼ばれていません。

[注釈終了]

1. SounDecoder

1-1. サウンド全体のデコード

SounDecoderで中心となるクラスは、抽象クラス SounDecoder.SoundDecoder です。フォーマットに拘わらず、デコーダはすべてこのクラスを通して扱えるので、デコーダに特有の利用方法などを覚える必要はありません。

SounDecoder.SoundDecoder の最も簡単な利用例は、ファイルストリームを使って初期化して、ファイルストリームに全サンプル列を書き出すというものでしょう。初期化に必要なストリームは、 System.IO.Stream の継承クラスであり、(かつ、特に断りが無い限り)読み取りとシークをサポートするストリームです。勿論、最も一般的な選択肢である System.IO.FileStream はこの要件を満たしています。 System.IO.PipeStream などではシークがサポートされていない為、利用できません。これは、SounDecoderがループ再生などを行う再生ライブラリから利用される事を念頭に置いて設計された為です。

入力ストリームに含まれるサウンドデータのフォーマットが予め判っている場合、 SounDecoder.SoundDecoder の継承クラスのコンストラクタを直接呼び出して初期化します。また、このクラスは System.IDisposable を実装する為、破棄する場合は System.IDisposable.Dispose() を呼び出します。

[プログラムコード開始]

using System.IO;
using SounDecoder;
 
namespace SounDecoderExample {
public class Program {
public static void Main( string[] args ) {
// ファイルから入力ストリームを作る
Stream source = new FileStream( @".\sample.wav", FileMode.Open, FileAccess.Read, FileShare.Read );
 
// ここではusing文を使ってWaveLpcmDecoderを作る
using ( SounDecoder decoder = new WaveLpcmDecoder( source ) ) {
// バイト配列 samples にサンプル列を取得する
byte[] samples = (byte[])decoder.ReadEntireData( typeof( byte ) );
 
// samples.dat にサンプル列を書き込む事にする
using ( Stream output = new FileStream( @".\samples.dat", FileMode.Create, FileAccess.Write, FileShare.Read ) ) {
output.Write( samples, 0, samples.Length );
}
}
 
return;
}
}
}

[プログラムコード終了]

SounDecoder.ReadEntireData(Type) は、引数typeで与えられた型を要素型とする一次元配列を新たに作成し、デコードされたサンプル列を格納して返します。

一つ、初期化に関して注意する事があります。それは、初期化時にコンストラクタに与えたストリームの管理は、すべて SounDecoder.SoundDecoder に任せる必要がある、という事です。SounDecoderを利用するプログラムでは、一旦コンストラクタに与えたストリームの状態を外部から変更しないように注意して下さい。 SounDecoder.SoundDecoder は、 SounDecoder.SoundDecoder.Dispose() が呼ばれた時に使用しているストリームをきちんと破棄します。

1-2. フォーマットの取得

ここでいうフォーマットとは、RIFF WAVEだとか、Ogg Vorbisなどの事ではなく、サンプリングレート(標本化周波数)や量子化ビット数などの事です。このフォーマットは、 SounDecoder.SoundFormat 構造体で記述され、 SounDecoder.SoundDecoder.Format プロパティで取得できます。これを使えば、例えばRIFF WAVEに落とす簡易デコーダも作れます。

[プログラムコード開始]

using System.IO;
using System.Text;
using SounDecoder;
 
namespace SounDecoderExample {
public class Program {
public static void Main( string[] args ) {
Stream source = new FileStream( @".\sample2.ogg", FileMode.Open, FileAccess.Read, FileShare.Read );
 
using ( SounDecoder decoder = new OggVorbisDecoder( source ) ) {
byte[] samples = (byte[])decoder.ReadEntireData( typeof( byte ) );
// サウンドフォーマットを取得してくる
SoundFormat format = decoder.Format;
 
using ( BinaryWriter writer = new BinaryWriter( new FileStream( @".\sample2.wav", FileMode.Create, FileAccess.Write, FileShare.Read ) ) ) {
uint length = (uint)samples.Length;
 
if ( ( length & 0x1 ) == 0x1 )
length++;
 
writer.Write( Encoding.ASCII.GetBytes( "RIFF" ) ); // FourCC "RIFF"
writer.Write( (uint)( 4 + 4 + 4 + 16 + 4 + 4 + length ) ); // これ以降の長さ
writer.Write( Encoding.ASCII.GetBytes( "WAVE" ) ); // FourCC "WAVE"
writer.Write( Encoding.ASCII.GetBytes( "fmt " ) ); // FourCC "fmt "
writer.Write( (uint)16 ); // "fmt "チャンクの長さ
writer.Write( (short)1 ); // WAVEFORMATタグ: PCM
writer.Write( format.Channels ); // チャネル数
writer.Write( format.SamplesPerSecond ); // サンプリングレート
writer.Write( format.BytesPerSecond ); // 必要帯域幅
writer.Write( format.BlockAlign ); // ブロックアラインメント
writer.Write( format.BitsPerSample ); // 量子化ビット数
writer.Write( Encoding.ASCII.GetBytes( "data" ) ); // FourCC "data"
writer.Write( length ); // "data"チャンクの長さ
writer.Write( samples ); // サンプル列
if ( ( samples.Length & 0x1 ) == 0x1 )
writer.Write( (byte)0 ); // パディング
}
}
 
return;
}
}
}

[プログラムコード終了]

1-3. 自動識別

ストリームに含まれるデータのフォーマット(ここではWAVEだとか、Ogg Vorbisだとか)が判らない事もあるかも知れません。例えばプレイヤーを作る場合、プレイヤーのユーザはわざわざ「これはMP3ファイルですよ」とか「FLACファイルなんだけど、再生できるかな?」なんて言ってくれません。入力をファイルストリームに限定するなら拡張子を見るというのも一つの手ですが、多くのフォーマットでは、ファイルの先頭にフォーマットを識別する為の情報が入っています。 SounDecoder.SoundDecoder.Identify(Stream) は、これを利用してフォーマットを識別し、適切なクラスのコンストラクタを呼んで SounDecoder.SoundDecoder のインスタンスを返します(但し、対象となるフォーマットはSounDecoderが標準でサポートするフォーマットに限られます)。

[プログラムコード開始]

using System.IO;
using SounDecoder;
 
namespace SounDecoderExample {
public class Program {
public static void Main( string[] args ) {
Stream source;
 
if ( args.Length < 2 ) {
Console.WriteLine( "usage: identify filename" );
return;
}
 
source = new FileStream( args[ 1 ], FileMode.Open, FileAccess.Read, FileShare.Read );
 
try {
using ( SounDecoder decoder = SoundDecoder.Identify( source ) ) {
Type type = decoder.GetType();
 
if ( type == typeof( WaveLpcmDecoder ) )
Console.WriteLine( "RIFF WAVE LPCM" );
else if ( type == typeof( OggVorbisDecoder ) )
Console.WriteLine( "Ogg Vorbis" );
else if ( type == typeof( NativeFlacDecoder ) )
Console.WriteLine( "Native FLAC" );
else if ( type == typeof( OggFlacDecoder ) )
Console.WriteLine( "Ogg FLAC" );
else if ( type == typeof( MpegAudioDecoder ) )
Console.WriteLine( "MPEG Audio (maybe)" );
}
}
catch ( InvalidDataException ) {
Console.WriteLine( "Unknown" );
}
 
return;
}
}
}

[プログラムコード終了]

フォーマットが正しいRIFF WAVE、Ogg Vorbis、Native/Ogg FLACの何れかであれば、このメソッドによって必ず適切なクラスのインスタンスが得られます。一方、MP3などであった場合、正しいMP3ストリームであったとしても System.IO.InvalidDataException が投げられるかも知れません。このメソッドはRIFF WAVE、Ogg Vorbis、Native/Ogg FLACの何れでもない場合は、ID3タグが先頭にあるかどうかでしか分岐しません。先頭の3バイトがASCII文字列"ID3"であれば SounDecoder.MpegAudioDecoder のインスタンスが返されますが、そうでなければ System.IO.InvalidDataException が投げられます。

WWWWARD > ソフトウェア > SounDecoder > プログラム例
[プログラム例]
Published : 2009-02-19T01:22:01+09:00
Last Modified : 2009-09-02T03:13:16+09:00
Table of Contents : SounDecoder
Verified with : Valid XHTML 1.1
Copyright © 2009  E+X.