[iOS]Looping Recorderの基礎(Audio Unit (6))
録音したデータを再生しようとしたが、上手くいかなかったので、アプリケーション内部で生成したサイン波の再生から、一歩ずつ進める事にした。
再生部を検討していて、録音部で使用したAudio Unit Processing Graph Servicesの使用方法は見直した方が良さそうだったので、再生部はAudio Unitを直接生成した。ヘッダーの内容は以下のとおり。
再生はAudioUnitを直接生成した。
@interface AudioUnitViewController : UIViewController
@property (nonatomic, assign) AudioUnit audioUnit;
@property (nonatomic, assign) double phase;
@property (nonatomic, assign) Float64 sampleRate;
@property (nonatomic, assign) BOOL isPlaying;
@end
Audio Unitの生成メソッドだ。
- (void)prepareAudioUnit
{
AudioComponentDescription cd;
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_RemoteIO;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
AudioComponent component = AudioComponentFindNext(NULL, &cd);
AudioComponentInstanceNew(component, &__audioUnit);
AudioUnitInitialize(self.audioUnit);
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = MyPlayAURenderCallack;
callbackStruct.inputProcRefCon = self;
AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callbackStruct, sizeof(AURenderCallbackStruct));
self.phase = 0.0;
self.sampleRate = 44100.0;
AudioStreamBasicDescription audioFormat = [self auCanonicalASBDSampleRate:self.sampleRate channel:2];
AudioUnitSetProperty(self.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioFormat, sizeof(AudioStreamBasicDescription));
}
再生のコールバック関数。サイン波を設定している。
static OSStatus MyPlayAURenderCallack (
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData
)
{
AudioUnitViewController *viewController = (AudioUnitViewController *)inRefCon;
float freq = 440 * 2.0 * M_PI / viewController.sampleRate;
double phase = viewController.phase;
AudioUnitSampleType *outL = ioData->mBuffers[0].mData;
AudioUnitSampleType *outR = ioData->mBuffers[1].mData;
for (int i = 0; i < inNumberFrames; i++) {
float wave = sin(phase);
AudioUnitSampleType sample = wave * (1 << kAudioUnitSampleFractionBits);
*outL++ = sample;
*outR++ = sample;
phase = phase + freq;
}
viewController.phase = phase;
return noErr;
}
色々、デバッグ出力を入れている為か、スムーズでないか、鳴るようになった。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。
[iOS]Looping Recorderの基礎(Audio Unit (5))
Remote IO UnitのRemote Inputについて疑問が多いので、色々と調べてみたいが、実際に動作させてみて分かるという所もあるので前に進める。
音楽CDは、44.1kHz / 2ch 16bit。
Audio Unitの標準サンプル単位AudioUnitSampleTypeは、iOSでは8.24固定小数点。OS Xでは、Float32らしい。つまり、32bit。
オーディオ正準形のサンプル単位AudioSampleTypeは、16bit符号付き整数。
Remote Outputの入出力の形式は、『iPhone Core Audioプログラミング』によると、iPhone OS 2.2.1までは32bit。iPhone OS 3.0以降では16bitらしい。
以上の情報を参考にして、扱うサンプルは、Audio Unit標準サンプル単位のモノラルにする事にした。
@interface AudioUnitViewController : UIViewController
@property (nonatomic, assign) AudioUnitSampleType *buffer;
@property (nonatomic, assign) uint32_t startingSampleCount;
@property (nonatomic, assign) uint32_t maxSampleCount;
@end
バッファに書き込むメソッドを用意する。
- (void)write:(UInt32)inNumberFrames data:(AudioBufferList *)ioData
{
uint32_t available = self.maxSampleCount - self.startingSampleCount;
if (available < inNumberFrames) {
inNumberFrames = available;
}
memcpy(self.buffer + self.startingSampleCount, ioData->mBuffers[0].mData, sizeof(AudioUnitSampleType) * inNumberFrames);
self.startingSampleCount = self.startingSampleCount + inNumberFrames;
if (self.maxSampleCount <= self.startingSampleCount) {
[self stop:nil];
}
}
録音のコールバック関数に渡されたデータをこのバッファに書き込む。
static OSStatus MyAURenderCallack(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
AudioUnitViewController *viewController = (AudioUnitViewController *)inRefCon;
[viewController write:inNumberFrames data:ioData];
return noErr;
}
ちょっと、乱暴なコードとなったが、次回は、このバッファのデータを再生してみる。さて、どうなるか。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。
[iOS]Looping Recorderの基礎(Audio Unit (4))
Remote IO UnitのRemote Inputで得られるデータについて、調べてみた。
マイク等をつないでいない、素の状態の入力と出力のチャンネル数は、Audio Session Serveiceで得る事ができる。
AudioSessionInitialize(NULL, NULL, NULL, NULL);
UInt32 nChannels = 0;
UInt32 size = sizeof(nChannels);
AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels,
&size,
&nChannels);
NSLog(@"Input nChannels:%u", (unsigned int)nChannels);
AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels,
&size,
&nChannels);
NSLog(@"Output nChannels:%u", (unsigned int)nChannels);
シミュレータの結果は、以下のとおり。
2012-04-03 06:22:05.331 DemoAudio[8706:10703] Input nChannels:2
2012-04-03 06:22:05.332 DemoAudio[8706:10703] Output nChannels:2
入力も出力も2チャンネル。ステレオのようだ。実機での結果は、以下のとおり。
2012-04-03 06:31:17.136 DemoAudio[7496:707] Input nChannels:17072444
2012-04-03 06:31:17.142 DemoAudio[7496:707] Output nChannels:2
あれ、入力のチャンネル数がおかしい。
シミュレータと実機では得られる入力データが異なった。前回説明したが、シミュレータでは以下のとおり。
2012-04-01 01:45:33.482 DemoAudio[7045:10703] -[AudioUnitViewController record:]
2012-04-01 01:45:34.925 DemoAudio[7045:17503] MyAURenderCallack, inNumberFrames:512
2012-04-01 01:45:34.926 DemoAudio[7045:17503] ioData: mNumberBuffers(1)
2012-04-01 01:45:34.928 DemoAudio[7045:17503] ioData->mBuffers[0]: mNumberChannels(2), mDataByteSize(2048)
実機では、こうなった。
2012-04-03 06:43:20.907 DemoAudio[7496:707] -[AudioUnitViewController record:]
2012-04-03 06:43:21.316 DemoAudio[7496:4203] MyAURenderCallack, inNumberFrames:1024
2012-04-03 06:43:21.319 DemoAudio[7496:4203] ioData: mNumberBuffers(2)
2012-04-03 06:43:21.329 DemoAudio[7496:4203] ioData->mBuffers[0]: mNumberChannels(1), mDataByteSize(4096)
2012-04-03 06:43:21.333 DemoAudio[7496:4203] ioData->mBuffers[1]: mNumberChannels(1), mDataByteSize(4096)
どちらも2チャンネルで、シミュレータはインターリーブ、実機は非インターリーブのようにみえる。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。
設立記念日
本日は、Bitz Co., Ltd.の設立記念日。前身の個人事業所から数えると14周年。法人成りしてからは、9周年となります。
これも皆様のご支援のおかげだと思います。ありがとうございます。
起業10年後の生存確率は5%という記事を読んだ事があります。真偽のほどは分かりませんが、何れにしても、事業を継続してゆくのには困難を伴いますので、10周年を迎えられる様に頑張りたいと思っています。
宜しくお願いします。
[iOS]Looping Recorderの基礎(Audio Unit (3))
進みが遅くて申し訳ないが、自分自身も確認しながらなので、一歩ずつ進めさせて欲しい。
サンプルでは、オーディオデータフォーマットを設定する関数として、『iPhone Core Audioプログラミング』の説明を参考にして、この本で説明されたAudio Unit正準形とオーディオ正準形の2つに対応したものを用意している。Audio Unit正準形は非インターリーブ、オーディオ正準形はインターリーブ。
録音のコールバック関数に渡されるデータについて確認して行こう。前回から少し出力内容を変更したが、以下がシミュレータで録音時に印字されるデバッグ出力だ。
2012-04-01 01:45:33.482 DemoAudio[7045:10703] -[AudioUnitViewController record:]
2012-04-01 01:45:34.925 DemoAudio[7045:17503] MyAURenderCallack, inNumberFrames:512
2012-04-01 01:45:34.926 DemoAudio[7045:17503] ioData: mNumberBuffers(1)
2012-04-01 01:45:34.928 DemoAudio[7045:17503] ioData->mBuffers[0]: mNumberChannels(2), mDataByteSize(2048)
inNumberFramesが記録されたサンプル数で、44.1kHzでは1秒間で44100個×チャンネル数となる。上記では512となっているので、1秒間に44100÷512≒約86回、コールバック関数が呼ばれるということになる。
オーディオデータフォーマットは、Audio Unit正準形に設定してる。なので、mNumberBuffersが1なのでモノラルということになるのか?
サンプルは、32ビットなので、4byte×512サンプル=2048。mDataByteSizeの値と一致している。
ただし、mNumberChannelsが2だ。文書によると非インターリーブの場合1なので、インターリーブなのか?
申し訳ない。続きは次回まで。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。
[iOS]ページ・ビュー・コントローラについて
iOS 5から、UIPageViewControllerが追加された。
書籍を模した、ページめくりを行うアプリケーションのUIの実装をサポートするクラスだ。
これを使ったサンプル・コードの作成に挑戦したいと思っているが、今回は紹介のみ。これの使い方を学ぶには、XcodeでPage-Based Applicationの雛形が用意されているので、雛形として生成されるプロジェクトを調べる事が、今のところ近道のようだ。
関連情報
View Controller Catalog for iOSk
iOSのCocoa touchフレームワークで用意されていますView Controllerについて説明されている。どんなView Controllerがあるか確認しやすいと思う。
[iOS]Looping Recorderの基礎(Audio Unit (2))
そもそも、録音のコールバック関数に渡されるデータは何なのだろうか?
型は以下のとおり。
static OSStatus MyAURenderCallack(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
typedef struct AudioBufferList {
UInt32 mNumberBuffers;
AudioBuffer mBuffers[1];
} AudioBufferList;
typedef struct AudioBuffer {
UInt32 mNumberChannels;
UInt32 mDataByteSize;
void* mData;
} AudioBuffer;
AudioBuffer構造体のmDataメンバー変数は、AudioUnitSampleType型だ。
typedef SInt32 AudioUnitSampleType;
OS XのAudio UnitではFloat32型という情報があったが、Xcodeの文書では、SInt32となっていた。サイズは同じで、それをどう使うのかアプリ次第なので、SInt32に統一されたのだろうか?ちなみに、iOSでは8.24固定小数点だそうだ。
先日の録音時に呼ばれるコールバック関数のデバッグ出力をもう少し詳細にしてみた。
static OSStatus MyAURenderCallack(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
DBGMSG(@"%s, inNumberFrames:%u", __func__, (unsigned int)inNumberFrames);
DBGMSG(@"ioData: mNumberBuffers(%u)", (unsigned int)ioData->mNumberBuffers);
DBGMSG(@"ioData->mBuffers: mNumberChannels(%u), mDataByteSize(%u)",
(unsigned int)ioData->mBuffers->mNumberChannels,
(unsigned int)ioData->mBuffers->mDataByteSize);
return noErr;
}
エミュレータでの出力結果は以下のとおり。
2012-03-30 00:56:22.237 DemoAudio[1566:12307] MyAURenderCallack, inNumberFrames:512
2012-03-30 00:56:22.239 DemoAudio[1566:12307] ioData: mNumberBuffers(1)
2012-03-30 00:56:22.240 DemoAudio[1566:12307] ioData->mBuffers: mNumberChannels(2), mDataByteSize(2048)
次回は、これをアプリケーションが独自に持っているリングバッファにどう書き込むかだ。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。
[iOS]Bitz NowPlaingのご紹介
本日、新しい有料アプリケーションBitz NowPlayingをiTunes Storeからリリースしました。
iOS機器で再生している曲をtweetするアプリケーションです。
もし、望まれるのでしたら、ミシュランのように三ツ星印を追加する事で出来ます。
缶コーヒー一杯弱のお値段で貴方の音楽生活が充実するかも!?
そして、早速、機能改善したVer.1.0.1をiTCにアップロードしました。
次のバージョンもお楽しみに。
[iOS]Looping Recorderの基礎(Audio Unit)
Audio UnitはCore Audioを拡張するPlug-insの事で、Audio Unitを使用すれば、音の生成や変換等の機能が利用できる。
追加するフレームワークは、『AudioUnit.framework』。インポートするヘッダ・ファイルは『AudioUnit/AudioUnit.h』だ。
今回は、録音部分についてチャレンジだ!
ループペダルの実装には、録音と再生のためのAudio Unitを別々用意することになると思うが、扱いが楽になるという事と、将来の拡張を考え、Audio Unit Processing Graph Servicesを利用する事にする。
以下が初期化のコードだ。
- (AudioStreamBasicDescription)canonicalASBDSampleRate:(Float64)sampleRate channel:(UInt32)channel
{
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = sampleRate;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagsCanonical;
audioFormat.mChannelsPerFrame = channel;
audioFormat.mBytesPerPacket = sizeof(AudioSampleType) * channel;
audioFormat.mBytesPerFrame = sizeof(AudioSampleType) * channel;
audioFormat.mFramesPerPacket = 1;
audioFormat.mBitsPerChannel = 8 * sizeof(AudioSampleType);
audioFormat.mReserved = 0;
return audioFormat;
}
- (void)prepareAUGraph
{
AUNode remoteIONode;
AudioUnit remoteIOUnit;
NewAUGraph(&__auGraph);
AUGraphOpen(self.auGraph);
AudioComponentDescription cd;
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_RemoteIO;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
AUGraphAddNode(self.auGraph, &cd, &remoteIONode);
AUGraphNodeInfo(self.auGraph, remoteIONode, NULL, &remoteIOUnit);
UInt32 flag = 1;
AudioUnitSetProperty(remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));
AudioStreamBasicDescription audioFormat = [self canonicalASBDSampleRate:44100.0 channel:1];
AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &audioFormat, sizeof(AudioStreamBasicDescription));
AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioFormat, sizeof(AudioStreamBasicDescription));
AUGraphInitialize(self.auGraph);
}
RemoteIOユニットでマイクを開く。その際、入力がモノラルで、出力がステレオだと、片方から再生となってしまう為、出力をモノラルに合わせている。
recordボタンを押下されるとレンダリングを開始し、レンダリング通知関数を登録して、録音されたデータを受け取れるようにする。
- (IBAction)record:(id)sender
{
if (self.isRecording) return;
AUGraphStart(self.auGraph);
AUGraphAddRenderNotify(self.auGraph, MyAURenderCallack, NULL);
self.isRecording = YES;
}
レンダリング通知関数では、今な何もしていない。
static OSStatus MyAURenderCallack(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
DBGMSG(@"%s, inNumberFrames:%u", __func__, (unsigned int)inNumberFrames);
return noErr;
}
次回は、レンダリング通知関数に渡されたデータをリングバッファに書き込み、これをループ再生させる予定だ。
ソースコード
GitHubからどうぞ。
https://github.com/murakami/DemoAudio - GitHub
関連情報
Cocoa Life KOF2011特別編 - Facebook
Cocoa勉強会 関西の会誌。
iPhone Core Audioプログラミング(永野 哲久 著)
とれも参考にさせていただきました。