Cocoa練習帳 -30ページ目

[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 Units

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プログラミング(永野 哲久 著)

とれも参考にさせていただきました。

[OSX][iOS]描画コードの生成(PaintCode)

PaintCodeは、App Storeで、2012年3月26日時点で、6,900円で販売しているアプリケーションだ。


スクリーンショットと、アプリケーションの説明文のみからの情報だが、このアプリケーションを使った描いた画像は、OS XとiOSの描画コードに変換され、自分のアプリケーションで利用できるというもののようだ。


以前から欲しかった機能なので、機械があれば使用してみたい。


なお、アプリケーションのサイトからは評価版をダウンロードできるので、購入前に機能を確認する事が出来るようだ。




関連情報

PaintCode