質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

Q&A

0回答

1918閲覧

AudioUnitでOpenALライクな実装をしたい

退会済みユーザー

退会済みユーザー

総合スコア0

Objective-C

Objective-Cはオブジェクト指向型のプログラミング言語のひとつです。C言語をベースにSmalltalkが取り入れられています。

0グッド

0クリップ

投稿2016/08/08 11:30

編集2016/08/08 16:09

OSX向けのゲームを作っています。
ゲーム音の再生にOpenALを利用していたのですが、同時再生できるSourceや一度に生成できるBufferの数に制限があるため、AudioUnitを使うことにしました。
以下のサイトなどを参考にしてとりあえず再生まではできたのですが、簡単に同時再生できるようにラッピングしようとしたところで分からない部分が出てきました。
http://d.hatena.ne.jp/shu223/20140506/1399406906
http://qiita.com/MJeeeey/items/b09e65cbded0cc3eb193

以下のコードは音声ファイルを読み込み、メモリ上のデータから再生するだけのものです。(エラー処理が不完全ですが...)

lang

1@import AudioToolbox; 2 3SInt64 fileLengthFrames; 4AudioStreamBasicDescription setFormat; 5AudioBufferList list; 6UInt32 totalFrame = 0; 7 8OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { 9 OSStatus err = noErr; 10 11 if (totalFrame > fileLengthFrames) return err; 12 13 void *data = list.mBuffers[0].mData; 14 ioData->mBuffers[0].mData = data + totalFrame * setFormat.mBytesPerFrame; 15 16 totalFrame += inNumberFrames; 17 18 return err; 19} 20 21int main(int argc, const char * argv[]) { 22 23 NSURL *url = [NSURL fileURLWithPath:@"test.wav"]; 24 OSStatus err = noErr; 25 26 // 27 // Extended Audio File Services 28 // 29 30 // ファイルを開く 31 ExtAudioFileRef audioFile; 32 err = ExtAudioFileOpenURL((__bridge CFURLRef _Nonnull)url, &audioFile); 33 if (err != noErr) goto ERROR; 34 35 // ファイルのフレーム数を取得 36 UInt32 propertyDataSize = sizeof(fileLengthFrames); 37 err = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileLengthFrames, &propertyDataSize, &fileLengthFrames); 38 if (err != noErr) goto ERROR; 39 40 // ファイルの情報を取得 41 AudioStreamBasicDescription getFormat; 42 propertyDataSize = sizeof(getFormat); 43 err = ExtAudioFileGetProperty(audioFile, kExtAudioFileProperty_FileDataFormat, &propertyDataSize, &getFormat); 44 if (err != noErr) goto ERROR; 45 46 // 変換フォーマットを設定 47 setFormat.mSampleRate = getFormat.mSampleRate; 48 setFormat.mFormatID = kAudioFormatLinearPCM; 49 setFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; 50 setFormat.mBitsPerChannel = 32; 51 setFormat.mChannelsPerFrame = getFormat.mChannelsPerFrame; 52 setFormat.mFramesPerPacket = 1; 53 setFormat.mBytesPerFrame = setFormat.mBitsPerChannel / 8 * setFormat.mChannelsPerFrame; 54 setFormat.mBytesPerPacket = setFormat.mBytesPerFrame * setFormat.mFramesPerPacket; 55 propertyDataSize = sizeof(setFormat); 56 err = ExtAudioFileSetProperty(audioFile, kExtAudioFileProperty_ClientDataFormat, propertyDataSize, &setFormat); 57 if (err != noErr) goto ERROR; 58 59 // ファイルを変換&読み込み 60 UInt32 dataSize = (UInt32)(fileLengthFrames * setFormat.mBytesPerFrame); 61 void *data = malloc(dataSize); 62 list.mNumberBuffers = 1; 63 list.mBuffers[0].mNumberChannels = setFormat.mChannelsPerFrame; 64 list.mBuffers[0].mDataByteSize = dataSize; 65 list.mBuffers[0].mData = data; 66 err = ExtAudioFileRead(audioFile, (UInt32 *)&fileLengthFrames, &list); 67 if (err != noErr) goto ERROR; 68 69 // 解放 70 ExtAudioFileDispose(audioFile); 71 72 // 73 // Audio Unit 74 // 75 76 // オーディオ処理グラフ作成 77 AUGraph graph; 78 err = NewAUGraph(&graph); 79 if (err != noErr) goto ERROR; 80 81 // ノードを作成してグラフに追加 82 AUNode ioNode; 83 AudioComponentDescription cd; 84 cd.componentType = kAudioUnitType_Output; 85 cd.componentSubType = kAudioUnitSubType_DefaultOutput; 86 cd.componentManufacturer = kAudioUnitManufacturer_Apple; 87 cd.componentFlags = 0; 88 cd.componentFlagsMask = 0; 89 err = AUGraphAddNode(graph, &cd, &ioNode); 90 if (err != noErr) goto ERROR; 91 92 // グラフを開く 93 err = AUGraphOpen(graph); 94 if (err != noErr) goto ERROR; 95 96 // フォーマットをユニットの入力に設定 97 AudioUnit ioUnit; 98 propertyDataSize = sizeof(setFormat); 99 err = AUGraphNodeInfo(graph, ioNode, NULL, &ioUnit); 100 if (err != noErr) goto ERROR; 101 err = AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &setFormat, propertyDataSize); 102 if (err != noErr) goto ERROR; 103 104 // グラフを初期化 105 err = AUGraphInitialize(graph); 106 if (err != noErr) goto ERROR; 107 108 // コールバックを作成してノードの入力に登録 109 AURenderCallbackStruct callbackStruct; 110 callbackStruct.inputProc = renderCallback; 111 callbackStruct.inputProcRefCon = NULL; 112 err = AUGraphSetNodeInputCallback(graph, ioNode, 0, &callbackStruct); 113 if (err != noErr) goto ERROR; 114 115 // 再生 116 err = AUGraphStart(graph); 117 if (err != noErr) goto ERROR; 118 119 sleep(3); 120 121 // 停止 122 err = AUGraphStop(graph); 123 if (err != noErr) goto ERROR; 124 125 // グラフを閉じる 126 AUGraphClose(graph); 127 if (err != noErr) goto ERROR; 128 129 return 0; 130 131ERROR: 132 printf("Error %d\n", err); 133 return 1; 134}

これを単純化して、たとえば、

lang

1// 音データを表す構造体 2Sound s; 3const char *path = "test.wav"; 4// ファイルパスから音データを読み込む 5loadSound(path, &s); 6// 再生 7play(&s); 8// 停止 9stop(&s); 10// 破棄 11disposeSound(&s);

のようにしたいです。(あくまでも例です。)

同時再生したい場合、ミキサーユニットを使うとよさそうなことは分かったのですが、以下のサイトによればミキサーの入力1つ1つに対してコールバック関数やフォーマットを設定する必要があります。
http://objective-audio.jp/2009/03/audiounit-multichannelmixer.html
これは「同時に再生させたいデータの数と同じ数だけコールバック関数を定義する必要がある」という理解でいいのでしょうか?
だとすると、例えば100個の音声データを同時に再生させたい場合、あらかじめ100個のコールバック関数を書かなければいけないような気がするのですが・・・これはあまりにも非現実的な方法に思えます。
任意の数の音声データを同時再生させるためには、動的に音声データを追加入力できるような仕組みが必要だと思うのですが、何か良いやりかたがあるのでしょうか?
AudioUnitやCoreAudioに詳しい方にお伺いしたいです。

追記: 質問してからすぐ気がついたのですが、コールバック関数にUInt32 inBusNumberという引数があることに気づきました。これで入力ごとに処理を分ければ解決するかもしれません。

追記2: よく見たらグローバル変数を使わなくてもcallbackStruct.inputProcRefConとコールバック関数のvoid *inRefConで音データを送れそうですね・・・コールバック関数に関する理解が足りないみたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問