Palm Programmer's Laboratory

トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

Palm OS Programmer's Companion Volume I/11-3

← 2 節に戻る ↑11 章トップへ 4 節に進む →


11-3 サウンド

 
Palm OS Garnet のサウンドマネージャは、2つの独立したサウンド機能を制御します。

  • シンプルサウンド:単一のモノラル矩形波音で、システムのヒープ音に適しています。これは伝統的な( Palm OS Garnet より前の )Palm OS サウンドです。
  • サンプリングサウンド:ステレオ・マルチフォーマットのサンプリングデータを録音・再生します( Palm OS Garnet の新機能 )。サンプリングサウンドはプログラム的に生成することもできますし、サウンドファイルから読み込むこともできます。

これらの機能は互いに独立しています。シンプルサウンドとサンプリングサウンドを同時に再生することは可能ですが、それぞれの API は互いに影響しません。例えば、サンプリングサウンド用のボリューム設定関数(SndStreamSetVolume)を使ってシンプルサウンドのボリュームを変更することはできません。

以下のセクションではサウンドマネージャのコンセプトについて概観します。API の詳細な解説や、このセクションで示すサンプリングデータのコンセプトに関する詳細なガイダンスについては、50 章の“サウンドマネージャ”を参照して下さい。

 

シンプルサウンド

シンプルサウンドを再生する方法には3種類あります。

  • SndDoCmd をコールすることで、指定した ピッチ、振幅、持続時間の単一音を再生できます。
  • SndPlaySystemSound を使えば定義済みのシステムサウンド(“情報”、“警告”、“エラー”など)を再生できます。
  • SndPlaySmf 関数にレベル0の標準 MIDI ファイル(SMF)を渡すことで曲を再生できます。例えば、組込みのDatebook アプリケーションが使用するアラーム音は MIDI レコードとしてシステムの MIDI データベースに保存されています。MIDI のサポートは Palm OS 3.0 以降に含まれています。MIDI と SMF フォーマットについては、MIDI の公式ウェブサイトである http://www.midi.org を参照して下さい。

 

サンプリングサウンド

サンプリングサウンドの機能には、基本的な関数が2つあります。

  • SndStreamCreate は、“生の”データの記録(または再生)バッファを使って新しいサンプリングサウンドの“ストリーム”をオープンします。ストリームを設定するためにしなければならない最初のトリックは、データの解釈方法を指定することです。(別の関数である SndStreamCreateExtended では、エンコーディングを指定できます。)
  • SndPlayResource は、(フォーマット済みの)サウンドファイルから読み取ったサウンドデータを再生するのに使用します。この関数はサウンドファイルのヘッダをもとに再生ストリームを設定してくれます。現在、非圧縮 WAV および IMA ADPCM WAV フォーマットのみを認識します(IMA ADPCM は DVI ADPCM として知られています)。SndPlayResource はサウンドの再生にのみ使用できます。録音には使用できません。

サウンドマネージャは、個々の録音(または再生)ストリームのボリュームやステレオパンニングを設定する関数も提供します。SndStreamSetVolume および SndStreamSetPan を参照して下さい。

 

シンプル vs サンプリング

2つの機能を比較すると、シンプルサウンドは理解しやすく、少量のプログラミングしか必要としません。ほとんどの場合、構造体を用意し、関数をコールすればビープ音が鳴ります。残念ながら、サウンド自体はプリミティブです。(シンプルサウンドのプログラミング例は下の“サウンドのプリファレンス”にあります。)

一方、サンプリングサウンドはもっと満足のいく(場合がある)ものですが、シンプルサウンドよりも多くの準備が必要になります。その量はやろうとしていることに依存します。サウンドファイルからサンプリングサウンドを再生するのは、シンプルサウンドと比べてもそれほど難しくありませんが、サウンドファイルを用意する必要はあります。プログラム的にサンプリング音を生成したり録音をするのはもっと多くの作業が必要です。サウンドデータを操作できるコールバック関数を実装しなければなりません。

IMPORTANT
シンプルサウンドとサンプリングサウンドの重要な違いのひとつに、それらが別々のボリュームスケールを使用するということがあります。シンプルサウンドのボリューム範囲は [0, 64] で、サンプリングサウンドのボリューム範囲は [0, 1024] です。

 

サウンドのプリファレンス

システムビープ音やアラーム音のような、短い“通知”サウンドをアプリケーションに追加する場合、Palm OS が提供している(シンプル)システムサウンドの使用をまず検討するべきです。これは SndPlaySystemSound 関数のリファレンスに一覧されています。

独自のシステム(的な)サウンドを作成したいのであれば、少なくともサウンドボリュームに関してはユーザープリファレンスの設定を順守するべきです。Palm OS 3.0 以降では、3種類のサウンドプリファレンス定数があります。

  • prefSysSoundVolume はデフォルトのシステムボリュームです。
  • prefGameSoundVolume はゲーム音に使用されます。
  • prefAlarmSoundVolume はアラームに使用されます。

サウンドプリファレンスの設定をシンプルサウンドのボリュームに適用するには、設定値の取得と設定を自分でする必要があります。例えば、以下の例ではアラーム音のボリュームを取得し、それをシンプルサウンドのボリュームに適用しています。

リスト 11.6 アラーム音のボリュームプリファレンスの取得

/* Create a ‘sound command’ structure. This will encode the parameters of the
tone we want to generate.
*/
SndCommandType sndCommand;

/* Ask for the ‘play a tone’ command. */
sndCommand.cmd = sndCmdFreqDurationAmp;

/* Set the frequency and duration. */
sndCommand.param1 = 1760;
sndCommand.param2 = 500;

/* Now get the alarm volume and set it in the struct. */
sndCommand.param3 = PrefGetPreference (prefAlarmSoundVolume);

/* Play the tone. */
SndDoCmd( 0, &sndCommand, true);

一方、サンプリングサウンドの API にはプリファレンスの設定を取得するためのボリューム定数(sndSystemVolume、sndGameVolume、および sndSysVolume)が提供されています。

リスト 11.7 サンプリングサウンドAPIでのボリューム定数の使用

/* Point our sound data pointer to a record that contains WAV data (record
retrieval isn’t shown).
*/
SndPtr soundData = MemHandleLock(...);

/* Play the data using the default alarm volume setting. */
SndPlayResource(soundData, sndAlarmVolume, sndFlagNormal);

/* Unlock the data. */
MemPtrUnlock(soundData);

できるだけ多くのバージョンのサウンドプリファレンスメカニズムと互換性を維持するために、アプリケーションは動作している Palm OS のバージョンをチェックするべきです。詳細は“システムバージョンフィーチャ”を参照して下さい。

 

スタンダード MIDI ファイル

シンプルサウンドの生成を制御するためにレベル0のスタンダードMIDIファイルを使用することができますが、これは広範囲なMIDIメッセージのサポートを意味しません。キーダウン、キーアップ、およびテンポチェンジのメッセージだけが認識されます。

MIDI データは MIDI データベースに保存することができます。

  • データベースタイプ sysFileTMidi が MIDI レコードデータベースを意味します。
  • さらに、システムの MIDI データベースはクリエータ sysFileCSystem によって識別されます。このデータベースはいくつかのシステムアラームサウンドを保持しています。

MIDI レコードはシステム MIDI データベースに追加することもできますし、独自のデータベースに保存することもできます。

MIDI データベースの各レコードは PalmSource 定義の MIDI レコードヘッダ、可読性のある MIDI データ名、および MIDI データ本体を連結したものです。図 11.1 に Palm OS の MIDI レコードを図解します。

図 11.1 Palm OS の MIDI レコード

トラックの名を取得する場合は、SndMidiRecType 構造体を使用します。これはレコードヘッダとトラック名をカプセル化しています。MIDI トラック名は NULL終端文字列です(たとえ空であっても)。そのため、長さは少なくとも1バイトであり、最大で sndMidiNameLength になります。

以下のコードでは新しい MIDI レコードを作成し、それをシステム MIDI データベースに追加しています。

リスト 11.8 システム MIDI データベースへの新しい MIDI レコードの追加

/* We need three things: A header, a name, and some data. We’ll get the name
   and data from somewhere, and create the header ourselves.
*/
char *midiName = ...;
MemHandle midiData = ...;
SndMidiRecHdrType midiHeader;

/* Database and record gadgetry. */
DmOpenRef database;
MemHandler record;
UInt16 *recordIndex = dmMaxRecordIndex;
UInt8* recordPtr;
UInt8* midiPtr;

/* MIDI header values: Always set the signature to sndMidiRecSignature, and
   reserved to 0. bDataOffset is an offset from the beginning of the header to the
   first byte of actual MIDI data. The name includes a null-terminator, hence the
   ‘+ 1’.
*/
midiHeader.signature = sndMidiRecSignature;
midiHeader.reserved = 0;
midiHeader.bDataOffset = sizeof(SndMidiRecHdrType) + StrLen(midiName) + 1;

/* Open the database and allocate a record. */
database = DmOpenDatabaseByTypeCreator( sysFileTMidi, sysFileCSystem,
                                        dmModeReadWrite | dmModeExclusive);
record = DmNewRecord(database, &recordIndex,
                     midiHeader.bDataOffset + MemHandleSize(midiData));

/* Lock the data and the record. */
midiDataPtr = MemHandleLock(midiData);
recordPtr = MemHandleLock(record);

/* Write the MIDI header. */
DmWrite( recordPtr, 0, &midiHeader, sizeof(midiHeader));

/* Write the track name. */
DmStrCopy( recordPtr, ((Uint32)(&((SndMidiRecType *)0)->name)), midiName);

/* Write the MIDI data. */
DmWrite( recordPtr, midiHeader.bDataOffset, midiDataPtr,
         MemHandleSize(midiData));

/* Unlock the handles, release the record, close the database. */
MemHandleUnlock( midiData);
MemHandleUnlock( record);
DmReleaseRecord( database, recordIndex, 1);
DmCloseDatabase( database);

MIDI レコードを取得する場合、レコードのクリエータがわかっていれば SndCreateMidiList 関数を使用することができます。それ以外の場合はデータマネージャの関数を使用してすべての MIDI レコードを反復します。

 

サウンドストリームの作成

サンプリングサウンド機能の一部であるサウンドストリーム API は、サウンドマネージャでもっとも柔軟な部分です。サウンドストリームはサンプリングデータをサウンドハードウェアへ送ったり、サウンドハードウェアから読み取ったりします。15個の出力ストリームと1つの入力ストリームがあり並列して動作します(あるいは動作できます)。

サウンドストリームを使用するには、どのようなデータをやりとりするのかを指定する必要があります。ストリームをセットアップするために指定しなければならない全ての情報 ―― 量子化ビット数、サンプリングレート、チャネル数など ―― は、SndStreamCreate あるいは SndStreamCreateExtended に渡します。

また、コールバック関数のポインタも指定する必要があります( SndStreamBufferCallback および SndStreamVariableBufferCallback を参照して下さい )。この関数の実装が作業のほとんどを占めることになります。ストリームを開始( SndStreamStart )すると、コールバック関数がデータバッファあたり1回、自動的に呼ばれます。入力ストリームを操作している(言い換えると録音している)場合、コールバック関数はバッファを空にしてそのデータで何か処理を行ない、次のバッファが現れる前に復帰することが期待されます。出力ストリームのコールバックはその逆で、バッファをデータで満たします。

データの総量に関係するため、コールバック関数はできる限り素早く処理を行なう必要があります。これは出力ストリームのコールバックでは特に重要です。1つ以上のストリームがアテンションを競い合うだけでなく、全ての出力コールバックは( サウンドマネージャが作成・管理している )同一タスク内で動作しています。もし(出力)ストリーム A のコールバックがストリームをデータで満たすのに時間がかかり過ぎれば、ストリーム B, C などのコールバックは枯渇してしまうでしょう。そのようなスレッドは途切れ途切れ( glitchy )なサウンドを作成することになってしまいます。

※訳注:上の段落、訳に自信がありません...

サンプリングサウンド関数がサポートするフォーマットは、その関数のリファレンスに記載されています。

 


← 2 節に戻る ↑11 章トップへ 4 節に進む →