Audio UnitでCocoaのCustomViewを使う

前回のサンプルコードをただビルドしただけのSinSynthは、AUホストでプラグインを表示すると「GenericView」で表示されます。パラメータ定義に応じて自動生成される、所謂こういうやつ↓

Custom View (Cocoa View) を使いたい

エフェクターならまだしも、普通ソフトシンセはGenericViewではどうにもできないくらい膨大なパラメータ数になるので、Custom Viewを作る必要がありますね。前回使ったサンプルコードの中に「AudioUnitEffectExample」というものがあって、これはLPFのプラグインCocoaのCustom Viewが用意されています。AU Instrumentの場合であっても、これを真似すればOKです。

作成手順

SinSynthにCocoaVIewを追加する手順。詳細は「AudioUnitEffectExample」をXcodeで開いて見てもらうとして、必要最低限の要点だけ。

1) SinSynthプロジェクトにTARGETを追加。「OS X -> Framework & Library -> Bundle」
Product Nameは「CocoaUI」とします。

2) TARGET CocoaUIにAUCocoaUIBaseプロトコルのViewFactoryと、NSViewのサブクラスを追加

OuiSinSynth_ViewFactory.h

#import <Cocoa/Cocoa.h>
#import <AudioUnit/AUCocoaUIView.h>

@class OuiSinSynth_UIView;

@interface OuiSinSynth_ViewFactory : NSObject <AUCocoaUIBase>
{
    IBOutlet OuiSinSynth_UIView *uiFreshlyLoadedView;
}
- (NSString *) description;  
@end

OuiSinSynth_ViewFactory.m

#import "OuiSinSynth_ViewFactory.h"
#import "OuiSinSynth_UIView.h"

@implementation OuiSinSynth_ViewFactory

- (unsigned) interfaceVersion {
    return 0;
}

- (NSString *) description {
    return @"Demo: Sin Synth";
}

- (NSView *)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize {
        if (![[NSBundle bundleForClass:[self class]] loadNibNamed:@"CocoaView" owner:self topLevelObjects:nil]) {
        NSLog (@"Unable to load nib for view.");
        return nil;
    }
    
    [uiFreshlyLoadedView setAU:inAU];
    
    NSView *returnView = uiFreshlyLoadedView;
    uiFreshlyLoadedView = nil;
    
    return returnView;
}
@end

OuiSinSynth_UIView.h

#import <Cocoa/Cocoa.h>
#import <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>

@interface OuiSinSynth_UIView : NSView
{
    AudioUnit mAU;
}
- (void)setAU:(AudioUnit)inAU;
@end

OuiSinSynth_UIView.m

#import "OuiSinSynth_UIView.h"

@implementation OuiSinSynth_UIView
- (void)setAU:(AudioUnit)inAU {
    mAU = inAU;
}
@end

3) CocoaUIにxibファイルを追加。 「OS X -> User Interface View」 ファイル名は「CocoaView.xib」とします。(とりあえずラベルをひとつ載せておく)

4) CocoaView.xibの File's Owner を OuiSinSynth_ViewFactory に変更
Viewの CustomClass を OuiSinSynth_UIView に変更
VIewを OuiSinSynth_ViewFactoryクラスの uiFreshlyLoadedView の IBOutlet に設定

5) SinSynthクラスにUI定義用のメソッド追加

SinSynth.h

class SinSynth : public AUMonotimbralInstrumentBase
{
public:
    ***省略***

    virtual OSStatus GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void * outData );
    virtual OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 & outDataSize, Boolean & outWritable);

    ***省略***

SinSynth.cpp

    ***省略***

OSStatus SinSynth::GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 & outDataSize, Boolean & outWritable)
{
    if (inScope == kAudioUnitScope_Global)
    {
        switch (inID)
        {
            case kAudioUnitProperty_CocoaUI:
                outWritable = false;
                outDataSize = sizeof (AudioUnitCocoaViewInfo);
                return noErr;
        }
    }
    
    return AUInstrumentBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}

OSStatus SinSynth::GetProperty(AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void * outData )
{
    if (inScope == kAudioUnitScope_Global)
    {
        switch (inID)
        {
            case kAudioUnitProperty_CocoaUI:
            {
                CFBundleRef bundle = CFBundleGetBundleWithIdentifier( CFSTR("com.apple.audiounit.sinsynth") );
                
                if (bundle == NULL) {
                    printf("error bundle\n");
                    return -43; //fnfErr;
                }
                
                CFURLRef bundleURL = CFBundleCopyResourceURL( bundle,
                                                             CFSTR("CocoaUI"),
                                                             CFSTR("bundle"),
                                                             NULL);
                if (bundleURL == NULL) {
                    printf("error bundle url\n");
                    return -43; //fnfErr;
                }
                
                CFStringRef className = CFSTR("OuiSinSynth_ViewFactory");
                AudioUnitCocoaViewInfo cocoaInfo = { bundleURL, { className } };
                *((AudioUnitCocoaViewInfo *)outData) = cocoaInfo;
                            
                return noErr;
            }
        }
    }
    
    return AUInstrumentBase::GetProperty (inID, inScope, inElement, outData);
}

    ***省略***

6) TARGET「SinSynth」のTarget Dependenciesに「CocoaUI」を追加
7) TARGET「SinSynth」にCopy Filesをもうひとつ追加して「CocoaUI.bundle」を選択
8) 以上。ビルドしてAU Labで実行。
プラグイン右上のドロップダウンが「GenericView」になっている場合は、「Demo: Sin Synth」に変更するとCustom Viewが表示されます。

Hello Worldだけでなかなか手間のかかること!
ここまでできればあとは頑張ってUIを作り込むだけ。そっちの方が大変……

Xcode5徹底解説 for iOS/OSX

Xcode5徹底解説 for iOS/OSX