diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index ac8866f26..d036f00d7 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -324,7 +324,6 @@ 1790B12C09883BFD008A330A /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFF909883BFD008A330A /* Dither.cpp */; }; 1790B12E09883BFD008A330A /* Amplify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFFE09883BFD008A330A /* Amplify.cpp */; }; 1790B12F09883BFD008A330A /* AudioUnitEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00109883BFD008A330A /* AudioUnitEffect.cpp */; }; - 1790B13009883BFD008A330A /* LoadAudioUnits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00409883BFD008A330A /* LoadAudioUnits.cpp */; }; 1790B13409883BFD008A330A /* ChangePitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00C09883BFD008A330A /* ChangePitch.cpp */; }; 1790B13509883BFD008A330A /* ChangeSpeed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00E09883BFD008A330A /* ChangeSpeed.cpp */; }; 1790B13609883BFD008A330A /* ChangeTempo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B01009883BFD008A330A /* ChangeTempo.cpp */; }; @@ -484,6 +483,7 @@ 28105DAC0AD09FC500BB4269 /* px_mixer.h in Headers */ = {isa = PBXBuildFile; fileRef = 28105DA20AD09FC500BB4269 /* px_mixer.h */; }; 2810644B1818EEB5004F678B /* cpu_detect_x86.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2810644A1818EEB5004F678B /* cpu_detect_x86.cpp */; }; 2810644D1818EED3004F678B /* cpu_detect.h in Headers */ = {isa = PBXBuildFile; fileRef = 2810644C1818EED3004F678B /* cpu_detect.h */; }; + 2812E9D91A1F773A001C24D3 /* AudioUnitCocoaHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 2812E9D81A1F773A001C24D3 /* AudioUnitCocoaHelper.m */; }; 2816372E0BAE3B6C0079C746 /* LinkingHtmlWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2816372C0BAE3B6C0079C746 /* LinkingHtmlWindow.cpp */; }; 282D474C0B9E8D900034BC49 /* Snap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 282D474A0B9E8D900034BC49 /* Snap.cpp */; }; 283135EC0DFB9D110076D551 /* ImportFFmpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 283135EA0DFB9D110076D551 /* ImportFFmpeg.cpp */; }; @@ -1312,7 +1312,6 @@ ED663B9A16543647007F53A5 /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFF909883BFD008A330A /* Dither.cpp */; }; ED663B9B16543647007F53A5 /* Amplify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFFE09883BFD008A330A /* Amplify.cpp */; }; ED663B9C16543647007F53A5 /* AudioUnitEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00109883BFD008A330A /* AudioUnitEffect.cpp */; }; - ED663B9D16543647007F53A5 /* LoadAudioUnits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00409883BFD008A330A /* LoadAudioUnits.cpp */; }; ED663BA016543647007F53A5 /* ChangePitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00C09883BFD008A330A /* ChangePitch.cpp */; }; ED663BA116543647007F53A5 /* ChangeSpeed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00E09883BFD008A330A /* ChangeSpeed.cpp */; }; ED663BA216543647007F53A5 /* ChangeTempo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B01009883BFD008A330A /* ChangeTempo.cpp */; }; @@ -1637,7 +1636,6 @@ ED85B47316A47353006DA21D /* Dither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFF909883BFD008A330A /* Dither.cpp */; }; ED85B47416A47353006DA21D /* Amplify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790AFFE09883BFD008A330A /* Amplify.cpp */; }; ED85B47516A47353006DA21D /* AudioUnitEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00109883BFD008A330A /* AudioUnitEffect.cpp */; }; - ED85B47616A47353006DA21D /* LoadAudioUnits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00409883BFD008A330A /* LoadAudioUnits.cpp */; }; ED85B47816A47353006DA21D /* ChangePitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00C09883BFD008A330A /* ChangePitch.cpp */; }; ED85B47916A47353006DA21D /* ChangeSpeed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B00E09883BFD008A330A /* ChangeSpeed.cpp */; }; ED85B47A16A47353006DA21D /* ChangeTempo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1790B01009883BFD008A330A /* ChangeTempo.cpp */; }; @@ -2846,8 +2844,6 @@ 1790AFFF09883BFD008A330A /* Amplify.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = Amplify.h; sourceTree = ""; tabWidth = 3; }; 1790B00109883BFD008A330A /* AudioUnitEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = AudioUnitEffect.cpp; sourceTree = ""; tabWidth = 3; }; 1790B00209883BFD008A330A /* AudioUnitEffect.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = AudioUnitEffect.h; sourceTree = ""; tabWidth = 3; }; - 1790B00409883BFD008A330A /* LoadAudioUnits.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = LoadAudioUnits.cpp; sourceTree = ""; tabWidth = 3; }; - 1790B00509883BFD008A330A /* LoadAudioUnits.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = LoadAudioUnits.h; sourceTree = ""; tabWidth = 3; }; 1790B00C09883BFD008A330A /* ChangePitch.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = ChangePitch.cpp; sourceTree = ""; tabWidth = 3; }; 1790B00D09883BFD008A330A /* ChangePitch.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = ChangePitch.h; sourceTree = ""; tabWidth = 3; }; 1790B00E09883BFD008A330A /* ChangeSpeed.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = ChangeSpeed.cpp; sourceTree = ""; tabWidth = 3; }; @@ -3177,6 +3173,8 @@ 2812A5B90DF63FF000576305 /* Debug_Static.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug_Static.xcconfig; sourceTree = ""; }; 2812A5BB0DF63FFD00576305 /* Release_Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release_Shared.xcconfig; sourceTree = ""; }; 2812A5BD0DF6400E00576305 /* Release_Static.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release_Static.xcconfig; sourceTree = ""; }; + 2812E9D71A1F773A001C24D3 /* AudioUnitCocoaHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioUnitCocoaHelper.h; sourceTree = ""; }; + 2812E9D81A1F773A001C24D3 /* AudioUnitCocoaHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioUnitCocoaHelper.m; sourceTree = ""; }; 2813897919E6163C004111ED /* SelectedRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectedRegion.h; sourceTree = ""; }; 2816372C0BAE3B6C0079C746 /* LinkingHtmlWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = LinkingHtmlWindow.cpp; sourceTree = ""; tabWidth = 3; }; 2816372D0BAE3B6C0079C746 /* LinkingHtmlWindow.h */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.c.h; path = LinkingHtmlWindow.h; sourceTree = ""; tabWidth = 3; }; @@ -5276,10 +5274,10 @@ 1790B00009883BFD008A330A /* audiounits */ = { isa = PBXGroup; children = ( + 2812E9D71A1F773A001C24D3 /* AudioUnitCocoaHelper.h */, + 2812E9D81A1F773A001C24D3 /* AudioUnitCocoaHelper.m */, 1790B00109883BFD008A330A /* AudioUnitEffect.cpp */, 1790B00209883BFD008A330A /* AudioUnitEffect.h */, - 1790B00409883BFD008A330A /* LoadAudioUnits.cpp */, - 1790B00509883BFD008A330A /* LoadAudioUnits.h */, ); path = audiounits; sourceTree = ""; @@ -8860,7 +8858,6 @@ 1790B12C09883BFD008A330A /* Dither.cpp in Sources */, 1790B12E09883BFD008A330A /* Amplify.cpp in Sources */, 1790B12F09883BFD008A330A /* AudioUnitEffect.cpp in Sources */, - 1790B13009883BFD008A330A /* LoadAudioUnits.cpp in Sources */, 1790B13409883BFD008A330A /* ChangePitch.cpp in Sources */, 1790B13509883BFD008A330A /* ChangeSpeed.cpp in Sources */, 1790B13609883BFD008A330A /* ChangeTempo.cpp in Sources */, @@ -9159,6 +9156,7 @@ 28001B3E1A0F0E5D007DD161 /* NumericTextCtrl.cpp in Sources */, 28001B4B1A0F0EB6007DD161 /* SpectralSelectionBar.cpp in Sources */, 28BB98051A15BE6800D1CC80 /* NoiseReduction.cpp in Sources */, + 2812E9D91A1F773A001C24D3 /* AudioUnitCocoaHelper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9373,7 +9371,6 @@ ED663B9A16543647007F53A5 /* Dither.cpp in Sources */, ED663B9B16543647007F53A5 /* Amplify.cpp in Sources */, ED663B9C16543647007F53A5 /* AudioUnitEffect.cpp in Sources */, - ED663B9D16543647007F53A5 /* LoadAudioUnits.cpp in Sources */, ED663BA016543647007F53A5 /* ChangePitch.cpp in Sources */, ED663BA116543647007F53A5 /* ChangeSpeed.cpp in Sources */, ED663BA216543647007F53A5 /* ChangeTempo.cpp in Sources */, @@ -9681,7 +9678,6 @@ ED85B47316A47353006DA21D /* Dither.cpp in Sources */, ED85B47416A47353006DA21D /* Amplify.cpp in Sources */, ED85B47516A47353006DA21D /* AudioUnitEffect.cpp in Sources */, - ED85B47616A47353006DA21D /* LoadAudioUnits.cpp in Sources */, ED85B47816A47353006DA21D /* ChangePitch.cpp in Sources */, ED85B47916A47353006DA21D /* ChangeSpeed.cpp in Sources */, ED85B47A16A47353006DA21D /* ChangeTempo.cpp in Sources */, @@ -10474,6 +10470,8 @@ "-framework", CoreAudio, "-framework", + CoreAudioKit, + "-framework", CoreMIDI, "-framework", AudioUnit, @@ -10825,6 +10823,8 @@ "-framework", CoreAudio, "-framework", + CoreAudioKit, + "-framework", CoreMIDI, "-framework", AudioUnit, @@ -11142,6 +11142,8 @@ "-framework", CoreAudio, "-framework", + CoreAudioKit, + "-framework", CoreMIDI, "-framework", AudioUnit, @@ -11650,6 +11652,8 @@ "-framework", CoreAudio, "-framework", + CoreAudioKit, + "-framework", CoreMIDI, "-framework", AudioUnit, diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 20433f04a..b96daa8dc 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -1869,6 +1869,10 @@ const PluginDescriptor *PluginManager::GetFirstPluginForEffectType(EffectType ty { PluginDescriptor & plug = mPluginsIter->second; bool familyEnabled; + if (type == PluginTypeEffect) + { + gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); + } gPrefs->Read(plug.GetEffectFamily() + wxT("/Enable"), &familyEnabled, true); if (plug.IsEnabled() && plug.GetEffectType() == type && familyEnabled) { diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 143c48d31..fcfd69df6 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -1669,7 +1669,24 @@ sampleCount Effect::RealtimeProcess(int group, } // Finally call the plugin to process the block - len = mClient->RealtimeProcess(mCurrentGroup++, clientIn, clientOut, numSamples); + len = 0; + sampleCount maxBlock = mClient->GetBlockSize(numSamples); + for (sampleCount block = 0; block < numSamples; block += maxBlock) + { + sampleCount cnt = (block + maxBlock > numSamples ? numSamples - block : maxBlock); + len += mClient->RealtimeProcess(mCurrentGroup, clientIn, clientOut, cnt); + + for (int i = 0 ; i < mNumAudioIn; i++) + { + clientIn[i] += cnt; + } + + for (int i = 0 ; i < mNumAudioOut; i++) + { + clientOut[i] += cnt; + } + } + mCurrentGroup++; } return len; @@ -1993,7 +2010,8 @@ END_EVENT_TABLE() EffectUIHost::EffectUIHost(wxWindow *parent, EffectHostInterface *host, EffectUIClientInterface *client) -: wxDialog(parent, wxID_ANY, host->GetName()) +: wxDialog(parent, wxID_ANY, host->GetName(), + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); @@ -2019,6 +2037,7 @@ EffectUIHost::~EffectUIHost() bool EffectUIHost::Initialize() { wxBoxSizer *vs = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *hs = new wxBoxSizer(wxHORIZONTAL); EffectPanel *w = new EffectPanel(this); @@ -2030,7 +2049,8 @@ bool EffectUIHost::Initialize() wxSizer *s = CreateStdButtonSizer(this, eSettingsButton | eOkButton | eCancelButton); - vs->Add(w, 1, wxEXPAND); + hs->Add(w, 1, wxEXPAND); + vs->Add(hs, 1, wxEXPAND); vs->Add(s, 0, wxEXPAND | wxALIGN_CENTER_VERTICAL); SetSizer(vs); @@ -2048,7 +2068,7 @@ bool EffectUIHost::Initialize() #else (w->GetChildren().GetCount() > 1 ? w : FindWindowById(wxID_OK))->SetFocus(); #endif - + LoadUserPresets(); mClient->LoadUserPreset(mHost->GetCurrentSettingsGroup()); diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index ca747b730..b46758146 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -57,15 +57,6 @@ #include "ChangeTempo.h" #endif -#ifdef USE_AUDIO_UNITS -#include "audiounits/LoadAudioUnits.h" -#endif - -#ifdef USE_VAMP -#include "vamp/LoadVamp.h" -#endif - - void LoadEffects() { @@ -270,12 +261,6 @@ void LoadEffects() // Analyze menu em.RegisterEffect(new EffectFindClipping()); - -#ifdef USE_AUDIO_UNITS - if (gPrefs->Read(wxT("/AudioUnits/Enable"), true)) { - LoadAudioUnits(); - } -#endif } void UnloadEffects() diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 58652662e..f40636506 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -554,7 +554,7 @@ bool VSTEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxStrin bool VSTEffectsModule::IsPluginValid(const PluginID & ID, const wxString & path) { - return wxFileName::FileExists(path); + return wxFileName::FileExists(path) || wxFileName::DirExists(path); } IdentInterface *VSTEffectsModule::CreateInstance(const PluginID & WXUNUSED(ID), diff --git a/src/effects/audiounits/AudioUnitCocoaHelper.h b/src/effects/audiounits/AudioUnitCocoaHelper.h new file mode 100644 index 000000000..acea662f4 --- /dev/null +++ b/src/effects/audiounits/AudioUnitCocoaHelper.h @@ -0,0 +1,28 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + AudioUnitCocoaHelper.h + + Leland Lucius + +**********************************************************************/ + +#ifndef AUDACITY_AUDIOUNIT_COCOA_HELPER_H +#define AUDACITY_AUDIOUNIT_COCOA_HELPER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +HIViewRef createGeneric(AudioUnit unit); +HIViewRef createCocoa(AudioUnit unit); +HIViewRef createCarbon(AudioUnit unit, WindowRef window, AudioUnitCarbonView *carbonView); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/effects/audiounits/AudioUnitCocoaHelper.m b/src/effects/audiounits/AudioUnitCocoaHelper.m new file mode 100644 index 000000000..18c17002e --- /dev/null +++ b/src/effects/audiounits/AudioUnitCocoaHelper.m @@ -0,0 +1,301 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + AudioUnitCocoaHelper.m + + Leland Lucius + +*******************************************************************//** + +\class AUScrollView +\brief An NSScrollView subclass that hosts AUs + +NOTE: I do NOT to Objective-C(++), so if you see ANYTHING wrong do + not hesitate to let me know. -Leland + +*//*******************************************************************/ + +#import +#import +#import +#import +#import + +#import "AudioUnitCocoaHelper.h" + +@interface AUScrollView: NSScrollView +{ + NSView *auView; + HIViewRef hiViewRef; +} + +- (id)initWithFrame:(NSRect)frameRect; + +/* Might be useful for improved resizing +- (void)auResized:(NSNotification *)notification; +- (void)myResized:(NSNotification *)notification; +*/ + +@end + +@implementation AUScrollView + +- (id)initWithFrame:(NSRect)frameRect +{ + self = [super initWithFrame:frameRect]; + + return self; +} + +- (void)reflectScrolledClipView:(NSClipView *)aClipView +{ + [super reflectScrolledClipView:aClipView]; + + // Force a full refresh as some effects seem to need it + [self setNeedsDisplay:YES]; +} + +- (BOOL)autoresizesSubviews +{ + // Let NSView automatically resize our children + return YES; +} +@end + +/* Might be useful for improved resizing. I'd worked up this + really elaborate resizing stuff that "almost" worked. Rather + than spend even more time, I decided to get rid of it all and + let resizing happen as it will. This code should be short + lived anyway as it will probably not be needed once we convert + to wxWidgets 3+. + +-(void) setViews:(NSView *)aView hiViewRef:(HIViewRef)hView +{ + auView = aView; + hiViewRef = hView; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(myResized:) name:NSViewFrameDidChangeNotification object:self]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(auResized:) name:NSViewFrameDidChangeNotification object:auView]; + +} + +- (void)myResized:(NSNotification *)notification +{ +} + +- (void)auResized:(NSNotification *)notification +{ +} + +*/ + +/////////////////////////////////////////////////////////////////////////////// +// Create a Cocoa based generic AU view wrapped in a Carbon view +/////////////////////////////////////////////////////////////////////////////// +HIViewRef createGeneric(AudioUnit unit) +{ + HIViewRef hiView = NULL; + OSStatus result; + + // Create a generic AU view + NSView *auView = [[AUGenericView alloc] initWithAudioUnit: unit]; + if (auView != nil) + { + // Allow expert parameters to be used + [(AUGenericView *) auView setShowsExpertParameters:YES]; + + // Get the AU view's frame for later + NSRect viewFrame = [auView frame]; + + // Create the view that will host the AU view + AUScrollView *scrollView = + [[[AUScrollView alloc] initWithFrame:viewFrame] autorelease]; + + // Not sure if this is necessary, but crashes seemed to occur + // without it. + [scrollView retain]; + + // Set the scroller options + [scrollView setDrawsBackground:YES]; + [scrollView setAutohidesScrollers:YES]; + [scrollView setHasHorizontalScroller:YES]; + [scrollView setHasVerticalScroller:YES]; + [scrollView setBorderType:NSNoBorder]; + [scrollView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + + // Let the scrollview know about the AU view + // + // Should the AU view be released after this??? + [scrollView setDocumentView:auView]; + // [auView release]; + + // Carbonize it + result = HICocoaViewCreate(scrollView, 0, &hiView); + if (result == noErr) + { + // Resize the HIView to match the AU view + SizeControl(hiView, viewFrame.size.width, viewFrame.size.height); + } + } + + return hiView; +} + +/////////////////////////////////////////////////////////////////////////////// +// Create a Cocoa based custom AU view wrapped in a Carbon view +/////////////////////////////////////////////////////////////////////////////// +HIViewRef createCocoa(AudioUnit unit) +{ + HIViewRef hiView = NULL; + OSStatus result; + + AudioUnitCocoaViewInfo cocoaViewInfo; + UInt32 dataSize = sizeof(AudioUnitCocoaViewInfo); + + // Get info about first Cocoa view + result = AudioUnitGetProperty(unit, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + &cocoaViewInfo, + &dataSize); + if (result != noErr) + { + return NULL; + } + + // Looks like the AU has a Cocoa UI, so load the factory class + NSURL *bundleLoc = (NSURL *) cocoaViewInfo.mCocoaAUViewBundleLocation; + NSString *className = (NSString *) cocoaViewInfo.mCocoaAUViewClass[0]; + if (!bundleLoc || !className) + { + return NULL; + } + + // Load the bundle + NSBundle *bundle = [NSBundle bundleWithPath: [bundleLoc path]]; + if (bundle != nil) + { + // Load the class from the bundle + Class factoryClass = [bundle classNamed: className]; + if (factoryClass != nil) + { + // Create an instance of the class + id factoryInst = [[[factoryClass alloc] init] autorelease]; + if (factoryInst != nil) + { + // Suggest a resonable size + NSSize size = {800, 600}; + + // Create the view + NSView *auView = [factoryInst uiViewForAudioUnit: unit withSize: size]; + if (auView != nil) + { + // Get the AU views frame for later + NSRect viewFrame = [auView frame]; + + // Create the view that will host the AU view + AUScrollView *scrollView = + [[[AUScrollView alloc] initWithFrame:viewFrame] autorelease]; + + // Not sure if this is necessary, but crashes seemed to occur + // without it. + [scrollView retain]; + + // Set the scroller options + [scrollView setDrawsBackground:YES]; + [scrollView setAutohidesScrollers:YES]; + [scrollView setHasHorizontalScroller:YES]; + [scrollView setHasVerticalScroller:YES]; + [scrollView setBorderType:NSNoBorder]; + + // Let the scrollview know about the AU view + // + // Should the AU view be released after this??? + [scrollView setDocumentView:auView]; + + // Carbonize it + result = HICocoaViewCreate(scrollView, 0, &hiView); + if (result == noErr) + { + // Resize the HIView to match the AU view + SizeControl(hiView, viewFrame.size.width, viewFrame.size.height); + } + } + } + } + + // Release the bundle??? + // [bundle release]; + } + + // Release the bundle path + [bundleLoc release]; + + return hiView; +} + +/////////////////////////////////////////////////////////////////////////////// +// Create a Carbon based AU view +/////////////////////////////////////////////////////////////////////////////// +HIViewRef createCarbon(AudioUnit unit, WindowRef window, AudioUnitCarbonView *carbonView) +{ + HIViewRef hiView = NULL; + OSStatus result; + + // Retrieve the view component description + ComponentDescription compDesc; + UInt32 dataSize = sizeof(compDesc); + result = AudioUnitGetProperty(unit, + kAudioUnitProperty_GetUIComponentList, + kAudioUnitScope_Global, + 0, + &compDesc, + &dataSize); + if (result != noErr) + { + return NULL; + } + + // Try to open it + Component comp = FindNextComponent(NULL, &compDesc); + result = OpenAComponent(comp, carbonView); + if (result != noErr) + { + return NULL; + } + + // Get the root control + ControlRef root = HIViewGetRoot(window); + GetRootControl(window, &root); + + // Find the content view within our window + HIViewRef content; + result = HIViewFindByID(root, kHIViewWindowContentID, &content); + if (result != noErr) + { + CloseComponent(*carbonView); + return NULL; + } + + // Suggest a reasonable size + Float32Point loc = {0.0, 0.0}; + Float32Point size = {800.0, 600.0}; + + // And create it + result = AudioUnitCarbonViewCreate(*carbonView, + unit, + window, + root, + &loc, + &size, + &hiView); + + return hiView; +} diff --git a/src/effects/audiounits/AudioUnitEffect.cpp b/src/effects/audiounits/AudioUnitEffect.cpp index f707bd784..f1dc647ee 100644 --- a/src/effects/audiounits/AudioUnitEffect.cpp +++ b/src/effects/audiounits/AudioUnitEffect.cpp @@ -5,26 +5,15 @@ AudioUnitEffect.cpp Dominic Mazzoni + Leland Lucius *******************************************************************//** \class AudioUnitEffect \brief An Effect class that handles a wide range of effects. ??Mac only?? -*//****************************************************************//** - -\class AudioUnitGUIControl -\brief Used with AudioUnitEffect - -*//****************************************************************//** - -\class AudioUnitDialog -\brief A Dialog used with AudioUnitEffect - *//*******************************************************************/ -#include "AudioUnitEffect.h" - #include #include #include @@ -32,136 +21,892 @@ #include #include #include +#include #include - -#if ((wxMAJOR_VERSION == 2) && (wxMINOR_VERSION < 6)) -#error Audio Units support requires wxMac 2.6 -#endif - #include -// -// Forward declarations of GUI classes -// +#include "../../widgets/valnum.h" -class AudioUnitGUIControl : public wxControl + +#include "AudioUnitEffect.h" +#include "AudioUnitCocoaHelper.h" + +// ============================================================================ +// Module registration entry point +// +// This is the symbol that Audacity looks for when the module is built as a +// dynamic library. +// +// When the module is builtin to Audacity, we use the same function, but it is +// declared static so as not to clash with other builtin modules. +// ============================================================================ +DECLARE_MODULE_ENTRY(AudacityModule) { - public: - inline AudioUnitGUIControl(wxWindow *parent, wxWindowID id, - wxPoint pos, wxSize size, - ControlRef controlRef) { - Create(parent, id, pos, size, controlRef); + // Create and register the importer + return new AudioUnitEffectsModule(moduleManager, path); +} + +// ============================================================================ +// Register this as a builtin module +// ============================================================================ +DECLARE_BUILTIN_MODULE(AudioUnitEffectsBuiltin); + +/////////////////////////////////////////////////////////////////////////////// +// +// AudioUnitEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +AudioUnitEffectsModule::AudioUnitEffectsModule(ModuleManagerInterface *moduleManager, + const wxString *path) +{ + mModMan = moduleManager; + if (path) + { + mPath = *path; + } +} + +AudioUnitEffectsModule::~AudioUnitEffectsModule() +{ + mPath.Clear(); +} + +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString AudioUnitEffectsModule::GetID() +{ + // Can be anything, but this is a v4 UUID + return wxT("1e767ec4-6f78-4c94-b6b8-c50b5780093b"); +} + +wxString AudioUnitEffectsModule::GetPath() +{ + return mPath; +} + +wxString AudioUnitEffectsModule::GetName() +{ + return _("AudioUnit Effects Module"); +} + +wxString AudioUnitEffectsModule::GetVendor() +{ + return _("The Audacity Team"); +} + +wxString AudioUnitEffectsModule::GetVersion() +{ + // This "may" be different if this were to be maintained as a separate DLL + return AUDIOUNITEFFECTS_VERSION; +} + +wxString AudioUnitEffectsModule::GetDescription() +{ + return _("Provides AudioUnit Effects support to Audacity"); +} + +// ============================================================================ +// ModuleInterface implementation +// ============================================================================ + +bool AudioUnitEffectsModule::Initialize() +{ + // Nothing to do here + return true; +} + +void AudioUnitEffectsModule::Terminate() +{ + // Nothing to do here + return; +} + +bool AudioUnitEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) +{ + // Nothing to be done here + return true; +} + +wxArrayString AudioUnitEffectsModule::FindPlugins(PluginManagerInterface & pm) +{ + wxArrayString effects; + + LoadAudioUnitsOfType(kAudioUnitType_Effect, effects); + LoadAudioUnitsOfType(kAudioUnitType_Generator, effects); + LoadAudioUnitsOfType(kAudioUnitType_MusicEffect, effects); + + return effects; +} + +bool AudioUnitEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) +{ + wxString name; + Component component = FindAudioUnit(path, name); + if (component == NULL) + { + return false; } - virtual ~AudioUnitGUIControl(); - bool Create(wxWindow *parent, wxWindowID id, - wxPoint pos, wxSize size, - ControlRef controlRef); - void OnMouse(wxMouseEvent &event); + AudioUnitEffect effect(path, name, component); + if (!effect.SetHost(NULL)) + { + return false; + } - private: - short GetModifiers(wxMouseEvent &event); - DECLARE_EVENT_TABLE() -}; + pm.RegisterEffectPlugin(this, &effect); -class AudioUnitDialog : public wxDialog + return true; +} + +bool AudioUnitEffectsModule::IsPluginValid(const PluginID & ID, + const wxString & path) { - public: - AudioUnitDialog(wxWindow *parent, wxWindowID id, wxString title, - AudioUnit unit, - AudioUnitCarbonView carbonView, - Effect *effect); - virtual ~AudioUnitDialog(); + wxString name; + return FindAudioUnit(path, name) != NULL; +} - void RemoveHandler(); +IdentInterface *AudioUnitEffectsModule::CreateInstance(const PluginID & ID, + const wxString & path) +{ + wxString name; + Component component = FindAudioUnit(path, name); + if (component == NULL) + { + return NULL; + } - void OnOK(wxCommandEvent &event); - void OnCancel(wxCommandEvent &event); - void OnPreview(wxCommandEvent &event); + return new AudioUnitEffect(path, name, component); +} - private: - AudioUnit mUnit; - AudioUnitGUIControl *mGUIControl; - wxBoxSizer *mMainSizer; - Effect *mEffect; +void AudioUnitEffectsModule::DeleteInstance(IdentInterface *instance) +{ + AudioUnitEffect *effect = dynamic_cast(instance); + if (effect) + { + delete effect; + } +} - EventHandlerRef mHandlerRef; - EventHandlerUPP mHandlerUPP; +// ============================================================================ +// AudioUnitEffectsModule implementation +// ============================================================================ + +void AudioUnitEffectsModule::LoadAudioUnitsOfType(OSType inAUType, + wxArrayString & effects) +{ + ComponentDescription desc; + Component component; + + desc.componentType = inAUType; + desc.componentSubType = 0; + desc.componentManufacturer = 0; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + component = FindNextComponent(NULL, &desc); + while (component != NULL) + { + ComponentDescription found; + Handle nameHandle = NewHandle(0); + GetComponentInfo(component, &found, nameHandle, 0, 0); + HLock(nameHandle); + int len = ((const char *)(*nameHandle))[0]; + wxString name(((const char *)(*nameHandle)+1), wxConvISO8859_1, len); + HUnlock(nameHandle); + DisposeHandle(nameHandle); + + effects.Add(wxString::Format(wxT("%-4.4s/%-4.4s/%-4.4s/%s"), + FromOSType(found.componentManufacturer).c_str(), + FromOSType(found.componentType).c_str(), + FromOSType(found.componentSubType).c_str(), + name.c_str())); + + component = FindNextComponent (component, &desc); + } +} + +Component AudioUnitEffectsModule::FindAudioUnit(const wxString & path, + wxString & name) +{ + wxStringTokenizer tokens(path, wxT("/")); + + ComponentDescription desc; + + desc.componentManufacturer = ToOSType(tokens.GetNextToken()); + desc.componentType = ToOSType(tokens.GetNextToken()); + desc.componentSubType = ToOSType(tokens.GetNextToken()); + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + name = tokens.GetNextToken(); + + return FindNextComponent(NULL, &desc); +} + +wxString AudioUnitEffectsModule::FromOSType(OSType type) +{ + OSType rev = (type & 0xff000000) >> 24 | + (type & 0x00ff0000) >> 8 | + (type & 0x0000ff00) << 8 | + (type & 0x000000ff) << 24; + + return wxString::FromUTF8((char *)&rev, 4).c_str(); +} + +OSType AudioUnitEffectsModule::ToOSType(const wxString & type) +{ + wxCharBuffer buf = type.ToUTF8(); + + OSType rev = ((unsigned char)buf.data()[0]) << 24 | + ((unsigned char)buf.data()[1]) << 16 | + ((unsigned char)buf.data()[2]) << 8 | + ((unsigned char)buf.data()[3]); + + return rev; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// AudioUnitEffectSettingsDialog +// +/////////////////////////////////////////////////////////////////////////////// + +class AudioUnitEffectSettingsDialog:public wxDialog +{ +public: + AudioUnitEffectSettingsDialog(wxWindow * parent, EffectHostInterface *host); + virtual ~AudioUnitEffectSettingsDialog(); + + void PopulateOrExchange(ShuttleGui & S); + + void OnOk(wxCommandEvent & evt); + +private: + EffectHostInterface *mHost; + int mBufferSize; + bool mUseBufferDelay; + bool mUseGUI; + bool mRescan; DECLARE_EVENT_TABLE() }; +BEGIN_EVENT_TABLE(AudioUnitEffectSettingsDialog, wxDialog) + EVT_BUTTON(wxID_OK, AudioUnitEffectSettingsDialog::OnOk) +END_EVENT_TABLE() + +AudioUnitEffectSettingsDialog::AudioUnitEffectSettingsDialog(wxWindow * parent, EffectHostInterface *host) +: wxDialog(parent, wxID_ANY, wxString(_("AudioUnit Effect Settings"))) +{ +#if defined(EXPERIMENTAL_REALTIME_EFFECTS) && defined(__WXMAC__) + HIWindowChangeClass((WindowRef) MacGetWindowRef(), kMovableModalWindowClass); +#endif + + mHost = host; + + mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize, 8192); + mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true); + mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true); + + ShuttleGui S(this, eIsCreating); + PopulateOrExchange(S); +} + +AudioUnitEffectSettingsDialog::~AudioUnitEffectSettingsDialog() +{ +} + +void AudioUnitEffectSettingsDialog::PopulateOrExchange(ShuttleGui & S) +{ + S.SetBorder(5); + S.StartHorizontalLay(wxEXPAND, 1); + { + S.StartVerticalLay(false); + { + S.StartStatic(_("Buffer Size")); + { + wxIntegerValidator vld(&mBufferSize); + vld.SetRange(8, 1048576 * 1); + + S.AddVariableText(wxString() + + _("The buffer size controls the number of samples sent to the effect ") + + _("on each iteration. Smaller values will cause slower processing and ") + + _("some effects require 8192 samples or less to work properly. However ") + + _("most effects can accept large buffers and using them will greatly ") + + _("reduce processing time."))->Wrap(650); + + S.StartHorizontalLay(wxALIGN_LEFT); + { + wxTextCtrl *t; + t = S.TieNumericTextBox(_("&Buffer Size (8 to 1048576 samples):"), + mBufferSize, + 12); + t->SetMinSize(wxSize(100, -1)); + t->SetValidator(vld); + } + S.EndHorizontalLay(); + } + S.EndStatic(); + + S.StartStatic(_("Buffer Delay Compensation")); + { + S.AddVariableText(wxString() + + _("As part of their processing, some AudioUnit effects must delay returning ") + + _("audio to Audacity. When not compensating for this delay, you will ") + + _("notice that small silences have been inserted into the audio. ") + + _("Enabling this setting will provide that compensation, but it may ") + + _("not work for all AudioUnit effects."))->Wrap(650); + + S.StartHorizontalLay(wxALIGN_LEFT); + { + S.TieCheckBox(_("Enable &compensation"), + mUseBufferDelay); + } + S.EndHorizontalLay(); + } + S.EndStatic(); + + S.StartStatic(_("Graphical Mode")); + { + S.AddVariableText(wxString() + + _("Most AudioUnit effects have a graphical interface for setting parameter values.") + + _(" A basic text-only method is also available. ") + + _(" Reopen the effect for this to take effect."))->Wrap(650); + S.TieCheckBox(_("Enable &graphical interface"), + mUseGUI); + } + S.EndStatic(); + } + S.EndVerticalLay(); + } + S.EndHorizontalLay(); + + S.AddStandardButtons(); + + Layout(); + Fit(); + Center(); +} + +void AudioUnitEffectSettingsDialog::OnOk(wxCommandEvent & WXUNUSED(evt)) +{ + if (!Validate()) + { + return; + } + + ShuttleGui S(this, eIsGettingFromDialog); + PopulateOrExchange(S); + + mHost->SetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize); + mHost->SetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay); + mHost->SetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI); + + EndModal(wxID_OK); +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// AudioUnitEffectEventHelper +// +/////////////////////////////////////////////////////////////////////////////// + +class AudioUnitEffectEventHelper : public wxEvtHandler +{ +public: + AudioUnitEffectEventHelper(AudioUnitEffect *effect); + virtual ~AudioUnitEffectEventHelper(); + + // AudioUnitEffectEventHelper implementatino + + void OnSize(wxSizeEvent & evt); + +private: + AudioUnitEffect *mEffect; + + DECLARE_EVENT_TABLE(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// AudioUnitEffectEventHelper +// +/////////////////////////////////////////////////////////////////////////////// + +BEGIN_EVENT_TABLE(AudioUnitEffectEventHelper, wxEvtHandler) + EVT_SIZE(AudioUnitEffectEventHelper::OnSize) +END_EVENT_TABLE() + +AudioUnitEffectEventHelper::AudioUnitEffectEventHelper(AudioUnitEffect *effect) +{ + mEffect = effect; +} + +AudioUnitEffectEventHelper::~AudioUnitEffectEventHelper() +{ +} + +// ============================================================================ +// AudioUnitEffectEventHelper implementation +// ============================================================================ + +void AudioUnitEffectEventHelper::OnSize(wxSizeEvent & evt) +{ + HIViewRef viewRef = mEffect->mAUView; + wxWindow *parent = mEffect->mParent; + + // The parent panel has been resized, so make the AU the + // same size. + HIRect rect; + HIViewGetFrame((HIViewRef) parent->GetHandle(), &rect); + HIViewSetFrame(viewRef, &rect); +} + +/////////////////////////////////////////////////////////////////////////////// // // AudioUnitEffect // +/////////////////////////////////////////////////////////////////////////////// -AudioUnitEffect::AudioUnitEffect(wxString name, Component component): - mName(name), - mComponent(component) +/* Not using this yet...was intended to improve resizing + +// Event handler to capture the window close event +static const EventTypeSpec controlEventList[] = { - SetEffectFlags(PLUGIN_EFFECT | PROCESS_EFFECT); + {kEventClassControl,kEventControlBoundsChanged}, +}; + +pascal OSStatus +AudioUnitEffect::ControlEventHandlerCallback(EventHandlerCallRef handler, EventRef event, void *data) +{ + return ((AudioUnitEffect *)data)->ControlEventHandler(event); +} +OSStatus AudioUnitEffect::ControlEventHandler(EventRef event) +{ + OSStatus result = eventNotHandledErr; + +printf("CONTROL class %d kind %d\n", GetEventClass(event), GetEventKind(event)); + if (GetEventClass(event) == kEventClassControl && GetEventKind(event) == kEventControlBoundsChanged) + { + + HIRect rect; + HIViewGetFrame(mAUView, &rect); + + ... + + return noErr; + } + + return result; +} +*/ + +// Event handler to capture the window close event +static const EventTypeSpec windowEventList[] = +{ + {kEventClassMouse, kEventMouseDown}, + {kEventClassMouse, kEventMouseUp}, + {kEventClassMouse, kEventMouseMoved}, + {kEventClassMouse, kEventMouseDragged}, + {kEventClassMouse, kEventMouseEntered}, + {kEventClassMouse, kEventMouseExited}, + {kEventClassMouse, kEventMouseWheelMoved}, + {kEventClassMouse, kEventMouseScroll}, + +// {kEventClassWindow, kEventWindowClose}, +}; + +pascal OSStatus +AudioUnitEffect::WindowEventHandlerCallback(EventHandlerCallRef handler, EventRef event, void *data) +{ + return ((AudioUnitEffect *)data)->WindowEventHandler(event); +} + +OSStatus AudioUnitEffect::WindowEventHandler(EventRef eventRef) +{ + wxMacCarbonEvent event(eventRef); + OSStatus result = eventNotHandledErr; + + // Give Cocoa (in HIView) controls first dibs at mouse event + if (GetEventClass(event) == kEventClassMouse) + { + OSStatus result; + HIPoint pt; + + result = event.GetParameter(kEventParamMouseLocation, + typeHIPoint, + sizeof(pt), + &pt); + + WindowRef rootWindow = (WindowRef)mDialog->MacGetTopLevelWindowRef(); + ControlRef rootControl = HIViewGetRoot(rootWindow); + + HIViewRef hitRef = 0; + result = HIViewGetViewForMouseEvent(rootControl, + eventRef, + &hitRef); + if (hitRef == mAUView && !mIsCarbon) + { + return SendEventToEventTarget(event, mEventRef); + } + } + +/* Not used, but leaving just in case + if (GetEventClass(event) == kEventClassWindow && GetEventKind(event) == kEventWindowClose) + { + result = noErr; + } +*/ + + return result; +} + +AudioUnitEffect::AudioUnitEffect(const wxString & path, + const wxString & name, + Component component, + AudioUnitEffect *master) +{ + mPath = path; + mName = name.AfterFirst(wxT(':')).Trim(true).Trim(false); + mVendor = name.BeforeFirst(wxT(':')).Trim(true).Trim(false); + mComponent = component; + mMaster = master; mUnit = NULL; + + mLatency = 0.0; + mTailTime = 0.0; + mBlockSize = 0.0; + + mUIHost = NULL; + mDialog = NULL; + mParent = NULL; + mCarbonView = NULL; + mHandlerRef = NULL; + mHandlerUPP = NULL; + + mEventHelper = NULL; + mEventListenerRef = NULL; } AudioUnitEffect::~AudioUnitEffect() { - if (mUnit) { + if (mEventListenerRef) + { + AUListenerDispose(mEventListenerRef); + } + + if (mUnit) + { CloseComponent(mUnit); } } -wxString AudioUnitEffect::GetEffectName() +// ============================================================================ +// IdentInterface implementation +// ============================================================================ + +wxString AudioUnitEffect::GetID() { - return mName + wxT("..."); + return mPath; } -std::set AudioUnitEffect::GetEffectCategories() +wxString AudioUnitEffect::GetPath() { - return std::set(); + return mPath; } -wxString AudioUnitEffect::GetEffectIdentifier() +wxString AudioUnitEffect::GetName() { - wxStringTokenizer st(mName, wxT(" ")); - wxString id; + return mName; +} - // CamelCase the name - while (st.HasMoreTokens()) { - wxString tok = st.GetNextToken(); +wxString AudioUnitEffect::GetVendor() +{ + return mVendor; +} - id += tok.Left(1).MakeUpper() + tok.Mid(1); +wxString AudioUnitEffect::GetVersion() +{ + ComponentResult version = GetComponentVersion((ComponentInstance) mComponent); + + return wxString::Format(wxT("%d.%d.%d"), + (version >> 16) & 0xffff, + (version >> 8) & 0xff, + version & 0xff); +} + +wxString AudioUnitEffect::GetDescription() +{ + return wxT("N/A"); +} + +// ============================================================================ +// EffectIdentInterface implementation +// ============================================================================ + +EffectType AudioUnitEffect::GetType() +{ + if (mAudioIns == 0 && mAudioOuts == 0) + { + return EffectTypeNone; } - return id; -} - -wxString AudioUnitEffect::GetEffectAction() -{ - return wxString::Format(_("Performing Effect: %s"), - mName.c_str()); -} - -bool AudioUnitEffect::Init() -{ - if (!mUnit) { - OpenAComponent(mComponent, &mUnit); + if (mAudioIns == 0) + { + return EffectTypeGenerate; } - if (!mUnit) { + if (mAudioOuts == 0) + { + return EffectTypeAnalyze; + } + + return EffectTypeProcess; +} + +wxString AudioUnitEffect::GetFamily() +{ + return AUDIOUNITEFFECTS_FAMILY; +} + +bool AudioUnitEffect::IsInteractive() +{ + return mInteractive; +} + +bool AudioUnitEffect::IsDefault() +{ + return false; +} + +bool AudioUnitEffect::IsLegacy() +{ + return false; +} + +bool AudioUnitEffect::SupportsRealtime() +{ + return GetType() == EffectTypeProcess; +} + +bool AudioUnitEffect::SupportsAutomation() +{ + OSStatus result; + UInt32 dataSize; + Boolean isWritable; + + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { return false; } - mSupportsMono = SetRateAndChannels(mUnit, 1, mProjectRate); - mSupportsStereo = SetRateAndChannels(mUnit, 2, mProjectRate); + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; - if (!mSupportsMono && !mSupportsStereo) { + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } - mSupportsMono = SetRateAndChannels(mUnit, 1, 44100.0); - mSupportsStereo = SetRateAndChannels(mUnit, 2, 44100.0); + for (int i = 0; i < cnt; i++) + { + AudioUnitParameterInfo info; + dataSize = sizeof(info); + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + array[i], + &info, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } - if (!mSupportsMono && !mSupportsStereo) { - printf("Audio Unit doesn't support mono or stereo.\n"); + if (info.flags & kAudioUnitParameterFlag_IsWritable) + { + // All we need is one + delete [] array; + return true; + } + } + + delete [] array; + + return false; +} + +// ============================================================================ +// EffectClientInterface Implementation +// ============================================================================ + +bool AudioUnitEffect::SetHost(EffectHostInterface *host) +{ + OSStatus result; + + mHost = host; + + mSampleRate = 44100; + ComponentResult auResult; + auResult = OpenAComponent(mComponent, &mUnit); + if (!mUnit) + { + return false; + } + + GetChannelCounts(); + + SetRateAndChannels(); + + UInt32 dataSize; + + // Retrieve the latency (can be updated via an event) + dataSize = sizeof(mLatency); + mLatency = 0.0; + AudioUnitGetProperty(mUnit, + kAudioUnitProperty_Latency, + kAudioUnitScope_Global, + 0, + &mLatency, + &dataSize); + + // Retrieve the tail time + dataSize = sizeof(mTailTime); + mTailTime = 0.0; + AudioUnitGetProperty(mUnit, + kAudioUnitProperty_TailTime, + kAudioUnitScope_Global, + 0, + &mTailTime, + &dataSize); + + // Retrieve the desired number of frames per slice + dataSize = sizeof(mBlockSize); + mBlockSize = 512; + AudioUnitGetProperty(mUnit, + kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, + 0, + &mBlockSize, + &dataSize); + + // mHost will be null during registration + if (mHost) + { + mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mBufferSize, 8192); + mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true); + mHost->GetSharedConfig(wxT("Settings"), wxT("UseGUI"), mUseGUI, true); + + bool haveDefaults; + mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false); + if (!haveDefaults) + { + SaveParameters(wxT("Default")); + mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true); + } + + LoadParameters(wxT("Current")); + } + + if (!mMaster) + { + result = AUEventListenerCreate(AudioUnitEffect::EventListenerCallback, + this, + (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetCurrentEventLoop()), + kCFRunLoopDefaultMode, + 0.0, + 0.0, + &mEventListenerRef); + if (result != noErr) + { + return false; + } + + AudioUnitEvent event; + + event.mEventType = kAudioUnitEvent_ParameterValueChange; + event.mArgument.mParameter.mAudioUnit = mUnit; + event.mArgument.mParameter.mParameterID = 1; //kAUParameterListener_AnyParameter; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + event.mArgument.mParameter.mElement = 0; + + UInt32 dataSize; + Boolean isWritable; + + // Retrieve the list of properties + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { + return false; + } + + // And get them + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; + + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } + + // Register them as something we're interested in + for (int i = 0; i < cnt; i++) + { + event.mArgument.mParameter.mParameterID = array[i]; + result = AUEventListenerAddEventType(mEventListenerRef, + this, + &event); + if (result != noErr) + { + delete [] array; + return false; + } + } + + delete [] array; + + event.mEventType = kAudioUnitEvent_PropertyChange; + event.mArgument.mProperty.mAudioUnit = mUnit; + event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency; + event.mArgument.mProperty.mScope = kAudioUnitScope_Global; + event.mArgument.mProperty.mElement = 0; + + result = AUEventListenerAddEventType(mEventListenerRef, + this, + &event); + if (result != noErr) + { return false; } } @@ -169,133 +914,980 @@ bool AudioUnitEffect::Init() return true; } -bool AudioUnitEffect::PromptUser() +int AudioUnitEffect::GetAudioInCount() { - OSErr result; - ComponentDescription desc; - Component carbonViewComponent = NULL; - AudioUnitCarbonView carbonView = NULL; + return mAudioIns; +} - GetComponentInfo(mComponent, &desc, 0, 0, 0); - carbonViewComponent = GetCarbonViewComponent(desc.componentSubType); - result = OpenAComponent(carbonViewComponent, &carbonView); - if (result != 0) { - printf("Couldn't open carbon view component\n"); +int AudioUnitEffect::GetAudioOutCount() +{ + return mAudioOuts; +} + +int AudioUnitEffect::GetMidiInCount() +{ + return 0; +} + +int AudioUnitEffect::GetMidiOutCount() +{ + return 0; +} + +void AudioUnitEffect::SetSampleRate(sampleCount rate) +{ + mSampleRate = rate; +} + +sampleCount AudioUnitEffect::GetBlockSize(sampleCount maxBlockSize) +{ + return mBlockSize; +} + +sampleCount AudioUnitEffect::GetLatency() +{ + if (!mLatencyDone) + { + mLatencyDone = true; + return mLatency * mSampleRate; + } + + return 0; +} + +sampleCount AudioUnitEffect::GetTailSize() +{ + return mTailTime * mSampleRate; +} + +bool AudioUnitEffect::IsReady() +{ + return mReady; +} + +bool AudioUnitEffect::ProcessInitialize() +{ + ComponentResult auResult; + + mInputList = new AudioBufferList[mAudioIns]; + mInputList->mNumberBuffers = mAudioIns; + + mOutputList = new AudioBufferList[mAudioOuts]; + mOutputList->mNumberBuffers = mAudioOuts; + + memset(&mTimeStamp, 0, sizeof(AudioTimeStamp)); + mTimeStamp.mSampleTime = 0; // This is a double-precision number that should + // accumulate the number of frames processed so far + mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; + + if (!SetRateAndChannels()) + { return false; } - AudioUnitDialog dlog(mParent, -1, mName, - mUnit, carbonView, this); - dlog.CentreOnParent(); - dlog.ShowModal(); - - CloseComponent(carbonView); - - return (dlog.GetReturnCode() == wxID_OK); -} - -bool AudioUnitEffect::Process() -{ - bool bGoodResult = true; - - CopyInputTracks(); - - TrackListIterator iter(mOutputTracks); - - int count = 0; - Track *left = iter.First(); - Track *right; - while(left) { - sampleCount lstart, rstart; - sampleCount len; - GetSamples((WaveTrack *)left, &lstart, &len); - - right = NULL; - if (left->GetLinked() && mSupportsStereo) { - right = iter.Next(); - GetSamples((WaveTrack *)right, &rstart, &len); - } - - bool success = false; - - if (!mSupportsStereo && right) { - // If the effect is mono, apply to each channel separately - - success = ProcessStereo(count, (WaveTrack *)left, NULL, - lstart, 0, len); - if (success) - success = ProcessStereo(count, (WaveTrack *)right, NULL, - rstart, 0, len); - } - else success = ProcessStereo(count, - (WaveTrack *)left, (WaveTrack *)right, - lstart, rstart, len); - if (!success) { - bGoodResult = false; - break; - } - - left = iter.Next(); - count++; + AURenderCallbackStruct callbackStruct; + callbackStruct.inputProc = RenderCallback; + callbackStruct.inputProcRefCon = this; + auResult = AudioUnitSetProperty(mUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callbackStruct, + sizeof(AURenderCallbackStruct)); + if (auResult != 0) + { + printf("Setting input render callback failed.\n"); + return false; } - ReplaceProcessedTracks(bGoodResult); + auResult = AudioUnitInitialize(mUnit); + if (auResult != 0) + { + printf("Couldn't initialize audio unit\n"); + return false; + } - return bGoodResult; + auResult = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0); + if (auResult != 0) + { + return false; + } + + mReady = true; + + return true; } -void AudioUnitEffect::End() +bool AudioUnitEffect::ProcessFinalize() +{ + if (mReady) + { + AudioUnitUninitialize(mUnit); + + mReady = false; + } + + if (mOutputList) + { + delete [] mOutputList; + mInputList = NULL; + } + + if (mInputList) + { + delete [] mInputList; + mInputList = NULL; + } + + return true; +} + +sampleCount AudioUnitEffect::ProcessBlock(float **inbuf, float **outbuf, sampleCount size) +{ + for (int i = 0; i < mAudioIns; i++) + { + mInputList->mBuffers[i].mNumberChannels = 1; + mInputList->mBuffers[i].mData = inbuf[i]; + mInputList->mBuffers[i].mDataByteSize = sizeof(float) * size; + } + + for (int i = 0; i < mAudioOuts; i++) + { + mOutputList->mBuffers[i].mNumberChannels = 1; + mOutputList->mBuffers[i].mData = outbuf[i]; + mOutputList->mBuffers[i].mDataByteSize = sizeof(float) * size; + } + + AudioUnitRenderActionFlags flags = 0; + ComponentResult auResult; + + auResult = AudioUnitRender(mUnit, + &flags, + &mTimeStamp, + 0, + size, + mOutputList); + if (auResult != 0) + { + printf("Render failed: %d %4.4s\n", (int)auResult, (char *)&auResult); + return 0; + } + + mTimeStamp.mSampleTime += size; + + return size; +} + +bool AudioUnitEffect::RealtimeInitialize() +{ + // This is really just a dummy value and one to make the dialog happy since + // all processing is handled by slaves. + SetSampleRate(44100); + mMasterIn = NULL; + mMasterInLen = 0; + mMasterOut = NULL; + mMasterOutLen = 0; + + return ProcessInitialize(); +} + +bool AudioUnitEffect::RealtimeFinalize() +{ + for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++) + { + mSlaves[i]->RealtimeFinalize(); + delete mSlaves[i]; + } + mSlaves.Clear(); + + if (mMasterIn) + { + for (int i = 0; i < mAudioIns; i++) + { + delete [] mMasterIn[i]; + } + delete [] mMasterIn; + mMasterIn = NULL; + } + + if (mMasterOut) + { + for (int i = 0; i < mAudioOuts; i++) + { + delete [] mMasterOut[i]; + } + delete [] mMasterOut; + mMasterOut = NULL; + } + + return ProcessFinalize(); +} + +bool AudioUnitEffect::RealtimeSuspend() +{ + return true; +} + +bool AudioUnitEffect::RealtimeResume() +{ + ComponentResult auResult; + + auResult = AudioUnitReset(mUnit, kAudioUnitScope_Global, 0); + if (auResult != 0) + { + return false; + } + + return true; +} + +sampleCount AudioUnitEffect::RealtimeProcess(int group, + float **inbuf, + float **outbuf, + sampleCount numSamples) +{ + if (group < 0 || group >= (int) mSlaves.GetCount()) + { + return 0; + } + + if (group == 0) + { + if (mMasterIn == NULL || mMasterInLen < numSamples) + { + if (mMasterIn) + { + for (int i = 0; i < mAudioIns; i++) + { + delete [] mMasterIn[i]; + } + delete [] mMasterIn; + } + + mMasterIn = new float *[mAudioIns]; + for (int i = 0; i < mAudioIns; i++) + { + mMasterIn[i] = new float[numSamples]; + } + mMasterInLen = numSamples; + } + + for (int i = 0; i < mAudioIns; i++) + { + memset(mMasterIn[i], 0, numSamples * sizeof(float)); + } + } + + int chanCnt = wxMin(mSlaves[group]->GetChannelCount(), mAudioIns); + for (int c = 0; c < chanCnt; c++) + { + for (int i = 0; i < numSamples; i++) + { + mMasterIn[c][i] += inbuf[c][i]; + } + } + + if (group == (int) mSlaves.GetCount() - 1) + { + if (mMasterOut == NULL || mMasterOutLen < numSamples) + { + if (mMasterOut) + { + for (int i = 0; i < mAudioOuts; i++) + { + delete [] mMasterOut[i]; + } + delete [] mMasterOut; + mMasterOut = NULL; + } + + mMasterOut = new float *[mAudioOuts]; + for (int i = 0; i < mAudioOuts; i++) + { + mMasterOut[i] = new float[numSamples]; + } + mMasterOutLen = numSamples; + } + + ProcessBlock(mMasterIn, mMasterOut,numSamples); + } + + return mSlaves[group]->ProcessBlock(inbuf, outbuf, numSamples); +} + +bool AudioUnitEffect::RealtimeAddProcessor(int numChannels, float sampleRate) +{ + AudioUnitEffect *slave = new AudioUnitEffect(mPath, mName, mComponent, this); + if (!slave->SetHost(NULL)) + { + delete slave; + return false; + } + + slave->SetChannelCount(numChannels); + slave->SetSampleRate(sampleRate); + + if (!CopyParameters(mUnit, slave->mUnit)) + { + delete slave; + return false; + } + + mSlaves.Add(slave); + + return slave->RealtimeInitialize(); +} + +bool AudioUnitEffect::ShowInterface(wxWindow *parent, bool forceModal) +{ + if (mDialog) + { + mDialog->Close(true); + return false; + } + + mDialog = mHost->CreateUI(parent, this); + if (!mDialog) + { + return false; + } + + if ((SupportsRealtime() || GetType() == EffectTypeAnalyze) && !forceModal) + { + mDialog->Show(); + + return false; + } + + bool res = mDialog->ShowModal() != 0; + mDialog = NULL; + + return res; +} + +bool AudioUnitEffect::GetAutomationParameters(EffectAutomationParameters & parms) +{ + OSStatus result; + UInt32 dataSize; + Boolean isWritable; + + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { + return false; + } + + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; + + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } + + for (int i = 0; i < cnt; i++) + { + AudioUnitParameterInfo info; + dataSize = sizeof(info); + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + array[i], + &info, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } + + wxString name; + if (info.flags & kAudioUnitParameterFlag_HasCFNameString) + { + wxMacCFStringHolder nameHolder(info.cfNameString, false); + name = nameHolder.AsString(); + if (info.flags & kAudioUnitParameterFlag_CFNameRelease) + { + CFRelease(info.cfNameString); + } + } + + if (name.IsEmpty()) + { + continue; + } + + AudioUnitParameterValue value; + result = AudioUnitGetParameter(mUnit, + array[i], + kAudioUnitScope_Global, + 0, + &value); + if (result != noErr) + { + delete [] array; + return false; + } + parms.Write(name, value); + } + + delete [] array; + + return true; +} + +bool AudioUnitEffect::SetAutomationParameters(EffectAutomationParameters & parms) +{ + OSStatus result; + UInt32 dataSize; + Boolean isWritable; + + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { + return false; + } + + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; + + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } + + for (int i = 0; i < cnt; i++) + { + AudioUnitParameterInfo info; + dataSize = sizeof(info); + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + array[i], + &info, + &dataSize); + if (result != noErr) + { + delete [] array; + return false; + } + + wxString name; + if (info.flags & kAudioUnitParameterFlag_HasCFNameString) + { + wxMacCFStringHolder nameHolder(info.cfNameString, false); + name = nameHolder.AsString(); + if (info.flags & kAudioUnitParameterFlag_CFNameRelease) + { + CFRelease(info.cfNameString); + } + } + + if (name.IsEmpty()) + { + continue; + } + + + double d = 0.0; + if (!parms.Read(name, &d)) + { + delete [] array; + return false; + } + + AudioUnitParameterValue value = d; + result = AudioUnitSetParameter(mUnit, + array[i], + kAudioUnitScope_Global, + 0, + value, + 0); + if (result != noErr) + { + delete [] array; + return false; + } + } + + delete [] array; + + return true; +} + +// ============================================================================ +// EffectUIClientInterface Implementation +// ============================================================================ + +void AudioUnitEffect::SetUIHost(EffectUIHostInterface *host) +{ + mUIHost = host; +} + +bool AudioUnitEffect::PopulateUI(wxWindow *parent) +{ + OSStatus result; + mCarbonView = NULL; + + mDialog = (wxDialog *) wxGetTopLevelParent(parent); + mParent = parent; + + WindowRef windowRef = (WindowRef) mDialog->MacGetWindowRef(); + ControlRef rootControl = HIViewGetRoot(windowRef); + + // Find the content view within our window + HIViewRef contentView; + HIViewFindByID(HIViewGetRoot(windowRef), kHIViewWindowContentID, &contentView); + + mIsCocoa = false; + mIsCarbon = false; + mIsGeneric = false; + + // Create the AU editor + HIViewRef auView = NULL; + if (mUseGUI) + { + auView = createCocoa(mUnit); + if (auView != NULL) + { + mIsCocoa = true; + } + else + { + auView = createCarbon(mUnit, windowRef, &mCarbonView); + if (auView != NULL) + { + mIsCarbon = true; + // Some effects do not work unless the default handler is removed since + // it captures many of the events that the plugins need. But, it must be + // done last since proper window sizing will not occur otherwise. + ::RemoveEventHandler((EventHandlerRef) mDialog->MacGetEventHandler()); + } + } + } + + // Either GUI creation failed or the user wants the generic view + if (auView == NULL) + { + auView = createGeneric(mUnit); + if (auView != NULL) + { + mIsGeneric = true; + } + } + + // Total failure...bail + if (auView == NULL) + { + return false; + } + mAUView = auView; + + HIViewAddSubview((HIViewRef) mParent->GetHandle(), auView); + HIViewPlaceInSuperviewAt(auView, 0, 0); + HIViewSetVisible(auView, true); + + HIRect rect; + HIViewGetFrame(auView, &rect); + + mParent->SetMinSize(wxSize(rect.size.width, rect.size.height)); + mParent->SetSize(wxSize(rect.size.width, rect.size.height)); + mDialog->Layout(); + mDialog->Fit(); + mDialog->SetMinSize(mDialog->GetSize()); + + mEventHelper = new AudioUnitEffectEventHelper(this); + mParent->PushEventHandler(mEventHelper); + + mEventRef = GetControlEventTarget(auView); + + // Install a bare minimum handler so we can capture the window close event. If + // it's not captured, we will crash at Audacity termination since the window + // is still on the wxWidgets toplevel window lists, but it's already gone. + mHandlerUPP = NewEventHandlerUPP(AudioUnitEffect::WindowEventHandlerCallback); + InstallWindowEventHandler(windowRef, + mHandlerUPP, + GetEventTypeCount(windowEventList), + windowEventList, + this, + &mHandlerRef); + +/* Was intended for improved resizing...not being used + + // Install a bare minimum handler so we can capture the window close event. If + // it's not captured, we will crash at Audacity termination since the window + // is still on the wxWidgets toplevel window lists, but it's already gone. + mControlHandlerUPP = NewEventHandlerUPP(AudioUnitEffect::ControlEventHandlerCallback); + InstallControlEventHandler(auView, + mControlHandlerUPP, + GetEventTypeCount(controlEventList), + controlEventList, + this, + &mControlHandlerRef); +*/ + + return true; +} + +bool AudioUnitEffect::ValidateUI() +{ +#if 0 + if (!mParent->Validate()) + { + return false; + } + + if (GetType() == EffectTypeGenerate) + { + mHost->SetDuration(mDuration->GetValue()); + } +#endif + return true; +} + +bool AudioUnitEffect::HideUI() +{ +#if 0 + if (GetType() == EffectTypeAnalyze || mNumOutputControls > 0) + { + return false; + } +#endif + return true; +} + +bool AudioUnitEffect::CloseUI() +{ + if (mEventHelper) + { + mParent->RemoveEventHandler(mEventHelper); + delete mEventHelper; + } + + if (mCarbonView) + { + CloseComponent(mCarbonView); + } + + if (mIsCarbon) + { + // Reinstall the wxWidgets toplevel event handler + mDialog->MacInstallTopLevelWindowEventHandler(); + } + + mUIHost = NULL; + mParent = NULL; + mDialog = NULL; + + return true; +} + +void AudioUnitEffect::LoadUserPreset(const wxString & name) +{ + LoadParameters(name); +} + +void AudioUnitEffect::SaveUserPreset(const wxString & name) +{ + SaveParameters(name); +} + +void AudioUnitEffect::LoadFactoryPreset(int id) +{ + OSStatus result; + + // Retrieve the list of factory presets + CFArrayRef array; + UInt32 dataSize = sizeof(CFArrayRef); + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, + &array, + &dataSize); + if (result == noErr) + { + if (id < CFArrayGetCount(array)) + { + AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, id); + + result = AudioUnitSetProperty(mUnit, + kAudioUnitProperty_PresentPreset, + kAudioUnitScope_Global, + 0, + preset, + sizeof(AUPreset)); + if (result == noErr) + { + AudioUnitParameter aup; + aup.mAudioUnit = mUnit; + aup.mParameterID = kAUParameterListener_AnyParameter; + aup.mScope = kAudioUnitScope_Global; + aup.mElement = 0; + AUParameterListenerNotify(NULL, NULL, &aup); + } + } + + CFRelease(array); + } + + return; +} + +void AudioUnitEffect::LoadFactoryDefaults() +{ + LoadParameters(mHost->GetFactoryDefaultsGroup()); +} + +wxArrayString AudioUnitEffect::GetFactoryPresets() +{ + OSStatus result; + wxArrayString presets; + + // Retrieve the list of factory presets + CFArrayRef array; + UInt32 dataSize = sizeof(CFArrayRef); + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, + &array, + &dataSize); + if (result == noErr) + { + for (CFIndex i = 0, cnt = CFArrayGetCount(array); i < cnt; i++) + { + AUPreset *preset = (AUPreset *) CFArrayGetValueAtIndex(array, i); + wxMacCFStringHolder holder(preset->presetName, false); + presets.Add(holder.AsString()); + } + CFRelease(array); + } + + return presets; +} + +void AudioUnitEffect::ExportPresets() { } -bool AudioUnitEffect::SetRateAndChannels(AudioUnit unit, - int numChannels, - Float64 sampleRate) +void AudioUnitEffect::ImportPresets() { - AudioStreamBasicDescription streamFormat; - ComponentResult auResult; +} - auResult = AudioUnitSetProperty(unit, kAudioUnitProperty_SampleRate, - kAudioUnitScope_Global, 0, - &sampleRate, sizeof(Float64)); - if (auResult != 0) { +void AudioUnitEffect::ShowOptions() +{ + AudioUnitEffectSettingsDialog dlg(mParent, mHost); + dlg.ShowModal(); +} + +// ============================================================================ +// AudioUnitEffect Implementation +// ============================================================================ + +void AudioUnitEffect::LoadParameters(const wxString & group) +{ + OSStatus result; + UInt32 dataSize; + Boolean isWritable; + + wxString value; + if (!mHost->GetPrivateConfig(group, wxT("Value"), value, wxEmptyString)) + { + return; + } + wxStringTokenizer tokens(value, wxT(',')); + + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { + return; + } + + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + + if (cnt != tokens.CountTokens()) + { + return; + } + + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; + + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return; + } + + for (int i = 0; i < cnt; i++) + { + double d = 0.0; + tokens.GetNextToken().ToDouble(&d); + + AudioUnitParameterValue value = d; + result = AudioUnitSetParameter(mUnit, + array[i], + kAudioUnitScope_Global, + 0, + value, + 0); + if (result != noErr) + { + delete [] array; + return; + } + } + + AudioUnitParameter aup; + aup.mAudioUnit = mUnit; + aup.mParameterID = kAUParameterListener_AnyParameter; + aup.mScope = kAudioUnitScope_Global; + aup.mElement = 0; + AUParameterListenerNotify(NULL, NULL, &aup); + + delete [] array; +} + +void AudioUnitEffect::SaveParameters(const wxString & group) +{ + OSStatus result; + UInt32 dataSize; + Boolean isWritable; + wxString parms; + + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result != noErr) + { + return; + } + + UInt32 cnt = dataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID *array = new AudioUnitParameterID[cnt]; + + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + array, + &dataSize); + if (result != noErr) + { + delete [] array; + return; + } + + for (int i = 0; i < cnt; i++) + { + AudioUnitParameterValue value; + result = AudioUnitGetParameter(mUnit, + array[i], + kAudioUnitScope_Global, + 0, + &value); + if (result != noErr) + { + delete [] array; + return; + } + + parms += wxString::Format(wxT(",%f"), value); + } + + mHost->SetPrivateConfig(group, wxT("Value"), parms.Mid(1)); + + delete [] array; +} + +bool AudioUnitEffect::SetRateAndChannels() +{ + ComponentResult auResult; + + auResult = AudioUnitSetProperty(mUnit, + kAudioUnitProperty_SampleRate, + kAudioUnitScope_Global, + 0, + &mSampleRate, + sizeof(Float64)); + if (auResult != 0) + { printf("Didn't accept sample rate\n"); return false; } - streamFormat.mSampleRate = sampleRate; + AudioStreamBasicDescription streamFormat; + + streamFormat.mSampleRate = mSampleRate; streamFormat.mFormatID = kAudioFormatLinearPCM; streamFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; streamFormat.mBitsPerChannel = 32; - streamFormat.mChannelsPerFrame = numChannels; + streamFormat.mChannelsPerFrame = mAudioIns; streamFormat.mFramesPerPacket = 1; streamFormat.mBytesPerFrame = 4; streamFormat.mBytesPerPacket = 4; - auResult = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, + auResult = AudioUnitSetProperty(mUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, &streamFormat, sizeof(AudioStreamBasicDescription)); if (auResult != 0) + { return false; + } - auResult = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, 0, + streamFormat.mChannelsPerFrame = mAudioOuts; + auResult = AudioUnitSetProperty(mUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &streamFormat, sizeof(AudioStreamBasicDescription)); if (auResult != 0) - return false; - - auResult = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Global, 0, - &streamFormat, - sizeof(AudioStreamBasicDescription)); - - if (auResult != 0) { - printf("Didn't accept global stream format\n"); + { return false; } @@ -314,10 +1906,14 @@ bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit) // getting back the size of the parameter list size = 0; - auResult = AudioUnitGetProperty(srcUnit, kAudioUnitProperty_ParameterList, - kAudioUnitScope_Global, 0, - NULL, &size); - if (auResult != 0) { + auResult = AudioUnitGetProperty(srcUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + NULL, + &size); + if (auResult != 0) + { printf("Couldn't get number of parameters\n"); return false; } @@ -326,10 +1922,14 @@ bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit) numParameters = size / sizeof(AudioUnitParameterID); parameters = new AudioUnitParameterID[numParameters]; - auResult = AudioUnitGetProperty(srcUnit, kAudioUnitProperty_ParameterList, - kAudioUnitScope_Global, 0, - parameters, &size); - if (auResult != 0) { + auResult = AudioUnitGetProperty(srcUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + parameters, + &size); + if (auResult != 0) + { printf("Couldn't get parameter list\n"); delete[] parameters; return false; @@ -338,20 +1938,29 @@ bool AudioUnitEffect::CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit) // Copy the parameters from the main unit to the unit specific to // this track - for(i=0; iGetRate(); - AudioUnit trackUnit = mUnit; - AURenderCallbackStruct callbackStruct; - AudioTimeStamp timeStamp; - AudioBufferList *bufferList = NULL; - ComponentResult auResult; - int waveTrackBlockSize; - UInt32 size; - UInt32 unitBlockSize; - float *leftBuffer; - float *rightBuffer; - bool success = true; + return mNumChannels; +} - if (!SetRateAndChannels(trackUnit, numChannels, sampleRate)) { - printf("Unable to setup audio unit for channels=%d rate=%.1f\n", - numChannels, sampleRate); - return false; - } +void AudioUnitEffect::SetChannelCount(int numChannels) +{ + mNumChannels = numChannels; +} - unitBlockSize = 0; - size = sizeof(UInt32); - auResult = AudioUnitGetProperty(trackUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - 0, - &unitBlockSize, - &size); - - if (unitBlockSize == 0 || auResult != 0) { - printf("Warning: didn't get audio unit's MaximumFramesPerSlice\n"); - printf("Trying to set MaximumFramesPerSlice to 512\n"); - unitBlockSize = 512; - auResult = AudioUnitSetProperty(trackUnit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - 0, - &unitBlockSize, - sizeof(UInt32)); - if (auResult != 0) - printf("Unable to set MaximumFramesPerSlice, rendering may fail...\n"); - } - - auResult = AudioUnitInitialize(trackUnit); - if (auResult != 0) { - printf("Couldn't initialize audio unit\n"); - return false; - } - - auResult = AudioUnitReset(trackUnit, kAudioUnitScope_Global, 0); - if (auResult != 0) { - printf("Reset failed.\n"); - AudioUnitUninitialize(trackUnit); - return false; - } - - callbackStruct.inputProc = SimpleAudioRenderCallback; - callbackStruct.inputProcRefCon = this; - auResult = AudioUnitSetProperty(trackUnit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, - 0, - &callbackStruct, - sizeof(AURenderCallbackStruct)); - if (auResult != 0) { - printf("Setting input render callback failed.\n"); - AudioUnitUninitialize(trackUnit); - return false; - } - - memset(&timeStamp, 0, sizeof(AudioTimeStamp)); - timeStamp.mSampleTime = 0; // This is a double-precision number that should - // accumulate the number of frames processed so far - timeStamp.mFlags = kAudioTimeStampSampleTimeValid; - - waveTrackBlockSize = left->GetMaxBlockSize() * 2; - - leftBuffer = NULL; - rightBuffer = NULL; - mLeftBufferForCallback = NULL; - mRightBufferForCallback = NULL; - - if (left) { - leftBuffer = new float[waveTrackBlockSize]; - } - - if (right) { - rightBuffer = new float[waveTrackBlockSize]; - } - - bufferList = (AudioBufferList *)malloc(sizeof(UInt32) + - numChannels * sizeof(AudioBuffer)); - if (!bufferList) +OSStatus AudioUnitEffect::Render(AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumFrames, + AudioBufferList *ioData) +{ + for (int i = 0; i < ioData->mNumberBuffers; i++) { - printf("Setting input render callback failed.\n"); - AudioUnitUninitialize(trackUnit); - // free allocated memory - if (leftBuffer) { - delete[] leftBuffer; - } - if (rightBuffer) { - delete[] rightBuffer; - } - return false; - } - bufferList->mNumberBuffers = numChannels; - - sampleCount originalLen = len; - sampleCount ls = lstart; - sampleCount rs = rstart; - while (len) { - int block = waveTrackBlockSize; - if (block > len) { - block = len; - } - - if (left) { - left->Get((samplePtr)leftBuffer, floatSample, ls, block); - } - - if (right) { - right->Get((samplePtr)rightBuffer, floatSample, rs, block); - } - - success = DoRender(trackUnit, numChannels, leftBuffer, rightBuffer, - block, unitBlockSize, &timeStamp, bufferList); - if (!success) { - break; - } - - if (left) { - left->Set((samplePtr)leftBuffer, floatSample, ls, block); - } - - if (right) { - right->Set((samplePtr)rightBuffer, floatSample, rs, block); - } - - len -= block; - ls += block; - rs += block; - - if (left && right) { - if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen)) { - success = false; - break; - } - } - else { - if (TrackProgress(count, (ls-lstart)/(double)originalLen)) { - success = false; - break; - } - } - } - - if (bufferList) { - free(bufferList); - } - - if (leftBuffer) { - delete[] leftBuffer; - } - - if (rightBuffer) { - delete[] rightBuffer; - } - - AudioUnitUninitialize(trackUnit); - - return success; -} - -bool AudioUnitEffect::DoRender(AudioUnit unit, - int numChannels, - float *leftBuffer, - float *rightBuffer, - int len, - int unitBlockSize, - AudioTimeStamp *timeStamp, - AudioBufferList *bufferList) -{ - AudioUnitRenderActionFlags flags; - ComponentResult auResult; - int block; - - for (int i = 0; i < len; i += block) { - block = unitBlockSize; - if (i + block > len) { - block = len - i; - } - - for (int j = 0; jmBuffers[j].mNumberChannels = 1; - bufferList->mBuffers[j].mData = NULL; - bufferList->mBuffers[j].mDataByteSize = sizeof(float) * block; - } - - if (leftBuffer) { - mLeftBufferForCallback = &leftBuffer[i]; - bufferList->mBuffers[0].mData = mLeftBufferForCallback; - } - - if (rightBuffer) { - mRightBufferForCallback = &rightBuffer[i]; - bufferList->mBuffers[1].mData = mRightBufferForCallback; - } - - flags = 0; - - auResult = AudioUnitRender(unit, &flags, timeStamp, - 0, block, bufferList); - if (auResult != 0) { - printf("Render failed: %d %4.4s\n", (int)auResult, (char *)&auResult); - return false; - } - - timeStamp->mSampleTime += block; - } - - return true; -} - -// static -OSStatus AudioUnitEffect::SimpleAudioRenderCallback - (void *inRefCon, - AudioUnitRenderActionFlags *inActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumFrames, - AudioBufferList *ioData) -{ - AudioUnitEffect *This = (AudioUnitEffect *)inRefCon; - - if (This->mLeftBufferForCallback) { - ioData->mBuffers[0].mData = This->mLeftBufferForCallback; - } - - if (This->mRightBufferForCallback) { - ioData->mBuffers[1].mData = This->mRightBufferForCallback; + ioData->mBuffers[i].mData = mInputList->mBuffers[i].mData; } return 0; } -Component AudioUnitEffect::GetCarbonViewComponent(OSType subtype) +// static +OSStatus AudioUnitEffect::RenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumFrames, + AudioBufferList *ioData) { - ComponentDescription desc; - Component component; - - desc.componentType = kAudioUnitCarbonViewComponentType; // 'auvw' - desc.componentSubType = subtype; - desc.componentManufacturer = 0; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - // First see if we can find a carbon view designed specifically for this - // plug-in: - - component = FindNextComponent(NULL, &desc); - if (component) - return component; - - // If not, grab the generic carbon view, which will create a GUI for - // any Audio Unit. - - desc.componentSubType = kAUCarbonViewSubType_Generic; - component = FindNextComponent(NULL, &desc); - - return component; + return ((AudioUnitEffect *) inRefCon)->Render(inActionFlags, + inTimeStamp, + inBusNumber, + inNumFrames, + ioData); } -// -// AudioUnitGUIControl methods -// - -BEGIN_EVENT_TABLE(AudioUnitGUIControl, wxControl) - EVT_MOUSE_EVENTS(AudioUnitGUIControl::OnMouse) -END_EVENT_TABLE() - -AudioUnitGUIControl::~AudioUnitGUIControl() +void AudioUnitEffect::EventListener(const AudioUnitEvent *inEvent, + AudioUnitParameterValue inParameterValue) { - // Don't want to dispose of it... - m_peer = NULL; -} - -bool AudioUnitGUIControl::Create(wxWindow *parent, wxWindowID id, - wxPoint pos, wxSize size, - ControlRef controlRef) -{ - m_macIsUserPane = FALSE ; - - if ( !wxControl::Create(parent, id, pos, size, - 0, wxDefaultValidator, - wxString(wxT("AudioUnitControl"))) ) - return false; - - m_peer = new wxMacControl(this, controlRef); - - Rect outBounds; - GetControlBounds(controlRef, &outBounds); - - pos.x = outBounds.left; - pos.y = outBounds.top; - size.x = outBounds.right - outBounds.left; - size.y = outBounds.bottom - outBounds.top; - - MacPostControlCreate(pos, size); - - return true; -} - -short AudioUnitGUIControl::GetModifiers(wxMouseEvent &event) -{ - short modifiers = 0; - - if ( !event.m_leftDown && !event.m_rightDown ) - modifiers |= btnState ; - - if ( event.m_shiftDown ) - modifiers |= shiftKey ; - - if ( event.m_controlDown ) - modifiers |= controlKey ; - - if ( event.m_altDown ) - modifiers |= optionKey ; - - if ( event.m_metaDown ) - modifiers |= cmdKey ; - - return modifiers; -} - -void AudioUnitGUIControl::OnMouse(wxMouseEvent &event) -{ - int x = event.m_x ; - int y = event.m_y ; - - MacClientToRootWindow( &x , &y ) ; - - Point localwhere ; - ControlHandle control; - - localwhere.h = x ; - localwhere.v = y ; - - short modifiers = GetModifiers(event); - - if (event.GetEventType() == wxEVT_LEFT_DOWN || - event.GetEventType() == wxEVT_LEFT_DCLICK ) { - #if ((wxMAJOR_VERSION == 2) && (wxMINOR_VERSION <= 4)) - WindowRef rootWindow = (WindowRef)GetParent()->MacGetRootWindow(); - #else - WindowRef rootWindow = (WindowRef)GetParent()->MacGetTopLevelWindowRef(); - #endif - - ControlPartCode code; - - code = FindControl(localwhere, - rootWindow, - &control); - - if (code) { - Rect outBounds; - GetControlBounds((ControlRef)control, &outBounds); - - code = ::HandleControlClick(control, - localwhere, modifiers, - (ControlActionUPP)-1) ; + if (mMaster) + { + // We're a slave, so just set the parameter + AudioUnitSetParameter(mUnit, + inEvent->mArgument.mParameter.mParameterID, + kAudioUnitScope_Global, + 0, + inParameterValue, + 0); + } + else + { + // We're the master, so propogate + for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++) + { + mSlaves[i]->EventListener(inEvent, inParameterValue); } } } - -// -// AudioUnitDialog methods -// - -// Event handler to capture the window close event -static const EventTypeSpec eventList[] = + +// static +void AudioUnitEffect::EventListenerCallback(void *inCallbackRefCon, + void *inObject, + const AudioUnitEvent *inEvent, + UInt64 inEventHostTime, + AudioUnitParameterValue inParameterValue) { - {kEventClassWindow, kEventWindowClose}, -}; + ((AudioUnitEffect *) inCallbackRefCon)->EventListener(inEvent, + inParameterValue); +} -static pascal OSStatus EventHandler(EventHandlerCallRef handler, EventRef event, void *data) +void AudioUnitEffect::GetChannelCounts() { - OSStatus result = eventNotHandledErr; + Boolean isWritable = 0; + UInt32 dataSize = 0; + OSStatus result; - AudioUnitDialog *dlg = (AudioUnitDialog *)data; - - if (GetEventClass(event) == kEventClassWindow && GetEventKind(event) == kEventWindowClose) { - dlg->RemoveHandler(); - dlg->Close(); - result = noErr; + // Does AU have channel info + result = AudioUnitGetPropertyInfo(mUnit, + kAudioUnitProperty_SupportedNumChannels, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + if (result) + { + // None supplied. Apparently all FX type units can do any number of INs + // and OUTs as long as they are the same number. In this case, we'll + // just say stereo. + // + // We should probably check to make sure we're dealing with an FX type. + mAudioIns = 2; + mAudioOuts = 2; + return; } - return result; -} + AUChannelInfo *info = (AUChannelInfo *) malloc(dataSize); + // Retrieve the channel info + result = AudioUnitGetProperty(mUnit, + kAudioUnitProperty_SupportedNumChannels, + kAudioUnitScope_Global, + 0, + info, + &dataSize); + if (result) + { + // Oh well, not much we can do out this case + mAudioIns = 2; + mAudioOuts = 2; -void EventListener(void *inUserData, AudioUnitCarbonView inView, - const AudioUnitParameter *inParameter, - AudioUnitCarbonViewEventID inEvent, - const void *inEventParam) -{ - // We're not actually using this yet... -} - -enum { - PreviewID = 1 -}; - -BEGIN_EVENT_TABLE(AudioUnitDialog, wxDialog) - EVT_BUTTON(wxID_OK, AudioUnitDialog::OnOK) - EVT_BUTTON(wxID_CANCEL, AudioUnitDialog::OnCancel) - EVT_BUTTON(PreviewID, AudioUnitDialog::OnPreview) -END_EVENT_TABLE() - -AudioUnitDialog::AudioUnitDialog(wxWindow *parent, wxWindowID id, - wxString title, - AudioUnit unit, - AudioUnitCarbonView carbonView, - Effect *effect): - mUnit(unit), - mEffect(effect), - mHandlerUPP(NULL), - mHandlerRef(NULL) -{ - long style = wxDEFAULT_DIALOG_STYLE; - - #if ((wxMAJOR_VERSION == 2) && (wxMINOR_VERSION <= 4)) - - wxDialog::Create(parent, id, title, - wxDefaultPosition, wxSize(500, 400), - style, title); - - #else - - // wxMac 2.5 version, all of this is just to attempt to turn off - // compositing... - - // From wxDialog::Create - SetExtraStyle(GetExtraStyle() | wxTOPLEVEL_EX_DIALOG); - style |= wxTAB_TRAVERSAL; - - // From wxTopLevelWindow::Create - wxTopLevelWindow::Init(); - m_windowStyle = style; - - // We added this line because many plug-ins are not happy with - // compositing...Requires a patch to wx such that the line - // "attr |= kWindowCompositingAttribute" only happens - // if m_macUsesCompositing is true... - // - // LL: Unfortunately, wxWidgets 2.7+ doesn't have a direct - // way to disable compositing, so bypass it until a way - // is found. -#if !wxCHECK_VERSION(2, 7, 0) - m_macUsesCompositing = false; -#endif - - // Rest of wxTopLevelWindow::Create - SetName(title); - m_windowId = id == -1 ? NewControlId() : id; - MacCreateRealWindow(title, wxDefaultPosition, wxSize(500, 400), - MacRemoveBordersFromStyle(style) , title) ; - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); - wxTopLevelWindows.Append(this); - if ( parent ) - parent->AddChild(this); - - #endif - - // - // On to our own code - // - - WindowRef windowRef = (WindowRef)MacGetWindowRef(); - ComponentResult auResult; - ControlRef rootControl = NULL; - GetRootControl(windowRef, &rootControl); - - int width = 500; - int height = 400; - Float32Point location = {0, 0}; - Float32Point size = {width, height}; - ControlRef audioUnitControl = NULL; - - auResult = AudioUnitCarbonViewCreate(carbonView, - unit, - windowRef, - rootControl, - &location, - &size, - &audioUnitControl); - - AudioUnitCarbonViewSetEventListener(carbonView, EventListener, this); - - mMainSizer = new wxBoxSizer(wxVERTICAL); - - if (auResult == 0) { - mGUIControl = new AudioUnitGUIControl(this, -1, - wxPoint(0, 0), - wxSize(width, height), - audioUnitControl); - - // Eventually, to handle resizing controls, call: - // AudioUnitCarbonViewSetEventListener with the event: - // kEventControlBoundsChanged - - mMainSizer->Add(mGUIControl, 1, wxEXPAND); - - mGUIControl->SetFocus(); - } - else - mGUIControl = NULL; - - wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL); - - wxButton *preview = new wxButton(this, PreviewID, _("Pre&view")); - hSizer->Add(preview, 0, wxALL, 10); - - hSizer->Add(10, 10); - - wxButton *ok = new wxButton(this, wxID_OK, _("OK")); - ok->SetDefault(); - ok->SetFocus(); - hSizer->Add(ok, 0, wxALL, 10); - - wxButton *cancel = new wxButton(this, wxID_CANCEL, _("&Cancel")); - hSizer->Add(cancel, 0, wxALL, 10); - - mMainSizer->Add(hSizer, 0, wxALIGN_CENTER); - - this->SetAutoLayout(true); - this->SetSizer(mMainSizer); - mMainSizer->Fit(this); - mMainSizer->SetSizeHints(this); - - // Some VST effects do not work unless the default handler is removed since - // it captures many of the events that the plugins need. But, it must be - // done last since proper window sizing will not occur otherwise. - ::RemoveEventHandler((EventHandlerRef)MacGetEventHandler()); - - // Install a bare minimum handler so we can capture the window close event. If - // it's not captured, we will crash at Audacity termination since the window - // is still on the wxWidgets toplevel window lists, but it's already gone. - mHandlerUPP = NewEventHandlerUPP(EventHandler); - InstallWindowEventHandler(windowRef, - mHandlerUPP, - GetEventTypeCount(eventList), - eventList, - this, - &mHandlerRef); - -} - -AudioUnitDialog::~AudioUnitDialog() -{ - RemoveHandler(); -} - -void AudioUnitDialog::RemoveHandler() -{ - if (mHandlerRef) { - ::RemoveEventHandler(mHandlerRef); - mHandlerRef = NULL; - MacInstallTopLevelWindowEventHandler(); + free(info); + return; } - if (mHandlerUPP) { - DisposeEventHandlerUPP(mHandlerUPP); - mHandlerUPP = NULL; + // This is where it gets weird...not sure what is the best + // way to do this really. If we knew how many ins/outs we + // really needed, we could make a better choice. + + bool haven2m = false; // nothing -> mono + bool haven2s = false; // nothing -> stereo + bool havem2n = false; // mono -> nothing + bool haves2n = false; // stereo -> nothing + bool havem2m = false; // mono -> mono + bool haves2s = false; // stereo -> stereo + bool havem2s = false; // mono -> stereo + bool haves2m = false; // stereo -> mono + + mAudioIns = 2; + mAudioOuts = 2; + + // Look only for exact channel constraints + for (int i = 0; i < dataSize / sizeof(AUChannelInfo); i++) + { + AUChannelInfo *ci = &info[i]; + + int ic = ci->inChannels; + int oc = ci->outChannels; + + if (ic < 0 && oc >= 0) + { + ic = 2; + } + else if (ic >= 0 && oc < 0) + { + oc = 2; + } + else if (ic < 0 && oc < 0) + { + ic = 2; + oc = 2; + } + + if (ic == 2 && oc == 2) + { + haves2s = true; + } + else if (ic == 1 && oc == 1) + { + havem2m = true; + } + else if (ic == 1 && oc == 2) + { + havem2s = true; + } + else if (ic == 2 && oc == 1) + { + haves2m = true; + } + else if (ic == 0 && oc == 2) + { + haven2s = true; + } + else if (ic == 0 && oc == 1) + { + haven2m = true; + } + else if (ic == 1 && oc == 0) + { + havem2n = true; + } + else if (ic == 2 && oc == 0) + { + haves2n = true; + } } -} -void AudioUnitDialog::OnOK(wxCommandEvent &event) -{ - EndModal(wxID_OK); -} + if (haves2s) + { + mAudioIns = 2; + mAudioOuts = 2; + } + else if (havem2m) + { + mAudioIns = 1; + mAudioOuts = 1; + } + else if (havem2s) + { + mAudioIns = 1; + mAudioOuts = 2; + } + else if (haves2m) + { + mAudioIns = 2; + mAudioOuts = 1; + } + else if (haven2m) + { + mAudioIns = 0; + mAudioOuts = 1; + } + else if (haven2s) + { + mAudioIns = 0; + mAudioOuts = 2; + } + else if (haves2n) + { + mAudioIns = 2; + mAudioOuts = 0; + } + else if (havem2n) + { + mAudioIns = 1; + mAudioOuts = 0; + } -void AudioUnitDialog::OnCancel(wxCommandEvent &event) -{ - EndModal(wxID_CANCEL); -} - -void AudioUnitDialog::OnPreview(wxCommandEvent &event) -{ - mEffect->Preview(); + return; } diff --git a/src/effects/audiounits/AudioUnitEffect.h b/src/effects/audiounits/AudioUnitEffect.h index 0cc9ce34f..ef15aaf72 100644 --- a/src/effects/audiounits/AudioUnitEffect.h +++ b/src/effects/audiounits/AudioUnitEffect.h @@ -5,6 +5,7 @@ AudioUnitEffect.h Dominic Mazzoni + Leland Lucius **********************************************************************/ @@ -18,61 +19,255 @@ #include #include #include +#include -class AudioUnitEffect:public Effect { +#include "audacity/EffectInterface.h" +#include "audacity/ModuleInterface.h" +#include "audacity/PluginInterface.h" - public: +#define AUDIOUNITEFFECTS_VERSION wxT("1.0.0.0"); +#define AUDIOUNITEFFECTS_FAMILY L"AudioUnit" - AudioUnitEffect(wxString name, Component component); +class AudioUnitEffect; + +WX_DEFINE_ARRAY_PTR(AudioUnitEffect *, AudioUnitEffectArray); + +class AudioUnitEffectEventHelper; + +class AudioUnitEffect : public EffectClientInterface, + public EffectUIClientInterface +{ +public: + AudioUnitEffect(const wxString & path, + const wxString & name, + Component component, + AudioUnitEffect *master = NULL); virtual ~AudioUnitEffect(); - virtual wxString GetEffectName(); + // IdentInterface implementation - virtual std::set GetEffectCategories(); + virtual PluginID GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); - virtual wxString GetEffectIdentifier(); + // EffectIdentInterface implementation - virtual wxString GetEffectAction(); + virtual EffectType GetType(); + virtual wxString GetFamily(); + virtual bool IsInteractive(); + virtual bool IsDefault(); + virtual bool IsLegacy(); + virtual bool SupportsRealtime(); + virtual bool SupportsAutomation(); - virtual bool Init(); + // EffectClientInterface implementation - virtual bool PromptUser(); + virtual bool SetHost(EffectHostInterface *host); - virtual bool Process(); + virtual int GetAudioInCount(); + virtual int GetAudioOutCount(); - virtual void End(); + virtual int GetMidiInCount(); + virtual int GetMidiOutCount(); - private: - bool SetRateAndChannels(AudioUnit unit, - int numChannels, Float64 sampleRate); + virtual void SetSampleRate(sampleCount rate); + virtual sampleCount GetBlockSize(sampleCount maxBlockSize); - bool ProcessStereo(int count, WaveTrack * left, WaveTrack *right, - sampleCount lstart, sampleCount rstart, - sampleCount len); + virtual sampleCount GetLatency(); + virtual sampleCount GetTailSize(); - bool DoRender(AudioUnit unit, int numChannels, - float *leftBuffer, float *rightBuffer, - int len, int unitBlockSize, - AudioTimeStamp *timeStamp, - AudioBufferList *bufferList); + virtual bool IsReady(); + virtual bool ProcessInitialize(); + virtual bool ProcessFinalize(); + virtual sampleCount ProcessBlock(float **inbuf, float **outbuf, sampleCount size); + + virtual bool RealtimeInitialize(); + virtual bool RealtimeAddProcessor(int numChannels, float sampleRate); + virtual bool RealtimeFinalize(); + virtual bool RealtimeSuspend(); + virtual bool RealtimeResume(); + virtual sampleCount RealtimeProcess(int group, + float **inbuf, + float **outbuf, + sampleCount numSamples); + + virtual bool ShowInterface(wxWindow *parent, bool forceModal = false); + + virtual bool GetAutomationParameters(EffectAutomationParameters & parms); + virtual bool SetAutomationParameters(EffectAutomationParameters & parms); + + // EffectUIClientInterface implementation + + virtual void SetUIHost(EffectUIHostInterface *host); + virtual bool PopulateUI(wxWindow *parent); + virtual bool ValidateUI(); + virtual bool HideUI(); + virtual bool CloseUI(); + + virtual void LoadUserPreset(const wxString & name); + virtual void SaveUserPreset(const wxString & name); + + virtual void LoadFactoryPreset(int id); + virtual void LoadFactoryDefaults(); + + virtual wxArrayString GetFactoryPresets(); + + virtual void ExportPresets(); + virtual void ImportPresets(); + virtual void ShowOptions(); + + // AudioUnitEffect implementation + +private: + bool SetRateAndChannels(); bool CopyParameters(AudioUnit srcUnit, AudioUnit dstUnit); - static OSStatus - SimpleAudioRenderCallback(void *inRefCon, - AudioUnitRenderActionFlags *inActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumFrames, - AudioBufferList *ioData); + // Realtime + int GetChannelCount(); + void SetChannelCount(int numChannels); + + static OSStatus RenderCallback(void *inRefCon, + AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumFrames, + AudioBufferList *ioData); + OSStatus Render(AudioUnitRenderActionFlags *inActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumFrames, + AudioBufferList *ioData); - Component GetCarbonViewComponent(OSType subtype); + static void EventListenerCallback(void *inCallbackRefCon, + void *inObject, + const AudioUnitEvent *inEvent, + UInt64 inEventHostTime, + AudioUnitParameterValue inParameterValue); + void EventListener(const AudioUnitEvent *inEvent, + AudioUnitParameterValue inParameterValue); + static pascal OSStatus WindowEventHandlerCallback(EventHandlerCallRef handler, + EventRef event, + void *data); + OSStatus WindowEventHandler(EventRef event); + + static pascal OSStatus ControlEventHandlerCallback(EventHandlerCallRef handler, + EventRef event, + void *data); + OSStatus ControlEventHandler(EventRef event); + + void GetChannelCounts(); + + void LoadParameters(const wxString & group); + void SaveParameters(const wxString & group); + + wxString mPath; wxString mName; + wxString mVendor; Component mComponent; AudioUnit mUnit; bool mSupportsMono; bool mSupportsStereo; - float *mLeftBufferForCallback; - float *mRightBufferForCallback; + + EffectHostInterface *mHost; + int mAudioIns; + int mAudioOuts; + bool mInteractive; + bool mLatencyDone; + Float64 mLatency; // in seconds...multiply by samplerate + Float64 mTailTime; // in seconds...multiply by samplerate + UInt32 mBlockSize; + double mSampleRate; + + int mBufferSize; + bool mUseBufferDelay; + + AudioTimeStamp mTimeStamp; + bool mReady; + + AudioBufferList *mInputList; + AudioBufferList *mOutputList; + + EffectUIHostInterface *mUIHost; + wxWindow *mParent; + wxDialog *mDialog; + wxSizerItem *mContainer; + AudioUnitCarbonView mCarbonView; + bool mUseGUI; + HIViewRef mAUView; + EventTargetRef mEventRef; + bool mIsCocoa; + bool mIsCarbon; + bool mIsGeneric; + + AudioUnitEffect *mMaster; // non-NULL if a slave + AudioUnitEffectArray mSlaves; + int mNumChannels; + float **mMasterIn; + int mMasterInLen; + float **mMasterOut; + int mMasterOutLen; + + AUEventListenerRef mEventListenerRef; + + EventHandlerRef mHandlerRef; + EventHandlerUPP mHandlerUPP; + EventHandlerRef mControlHandlerRef; + EventHandlerUPP mControlHandlerUPP; + + AudioUnitEffectEventHelper *mEventHelper; + friend class AudioUnitEffectEventHelper; }; + +/////////////////////////////////////////////////////////////////////////////// +// +// AudioUnitEffectsModule +// +/////////////////////////////////////////////////////////////////////////////// + +class AudioUnitEffectsModule : public ModuleInterface +{ +public: + AudioUnitEffectsModule(ModuleManagerInterface *moduleManager, const wxString *path); + virtual ~AudioUnitEffectsModule(); + + // IdentInterface implementatino + + virtual wxString GetID(); + virtual wxString GetPath(); + virtual wxString GetName(); + virtual wxString GetVendor(); + virtual wxString GetVersion(); + virtual wxString GetDescription(); + + // ModuleInterface implementation + + virtual bool Initialize(); + virtual void Terminate(); + + virtual bool AutoRegisterPlugins(PluginManagerInterface & pm); + virtual wxArrayString FindPlugins(PluginManagerInterface & pm); + virtual bool RegisterPlugin(PluginManagerInterface & pm, const wxString & path); + + virtual bool IsPluginValid(const PluginID & ID, const wxString & path); + + virtual IdentInterface *CreateInstance(const PluginID & ID, const wxString & path); + virtual void DeleteInstance(IdentInterface *instance); + + // AudioUnitEffectModule implementation + + void LoadAudioUnitsOfType(OSType inAUType, wxArrayString & effects); + Component FindAudioUnit(const wxString & path, wxString & name); + + wxString FromOSType(OSType type); + OSType ToOSType(const wxString & type); + +private: + ModuleManagerInterface *mModMan; + wxString mPath; +}; + diff --git a/src/effects/audiounits/LoadAudioUnits.cpp b/src/effects/audiounits/LoadAudioUnits.cpp deleted file mode 100644 index 3a71ccf50..000000000 --- a/src/effects/audiounits/LoadAudioUnits.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - LoadAudioUnits.cpp - - Dominic Mazzoni - -**********************************************************************/ - -#include -#include - -#include "../EffectManager.h" -#include "AudioUnitEffect.h" - -void LoadAudioUnitsOfType(OSType inAUType) -{ - ComponentDescription desc; - Component component; - - desc.componentType = inAUType; - desc.componentSubType = 0; - desc.componentManufacturer = 0; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - component = FindNextComponent(NULL, &desc); - while (component != NULL) { - ComponentDescription found; - Handle nameHandle = NewHandle(0); - GetComponentInfo(component, &found, nameHandle, 0, 0); - HLock(nameHandle); - int len = ((const char *)(*nameHandle))[0]; - wxString name(((const char *)(*nameHandle)+1), wxConvISO8859_1, len); - HUnlock(nameHandle); - DisposeHandle(nameHandle); - - EffectManager::Get().RegisterEffect(new AudioUnitEffect(name, component)); - - component = FindNextComponent (component, &desc); - } -} - -void LoadAudioUnits() -{ - LoadAudioUnitsOfType(kAudioUnitType_Effect); //'aufx' - LoadAudioUnitsOfType(kAudioUnitType_MusicEffect); //'aumf' -} diff --git a/src/effects/audiounits/LoadAudioUnits.h b/src/effects/audiounits/LoadAudioUnits.h deleted file mode 100644 index bc41af37e..000000000 --- a/src/effects/audiounits/LoadAudioUnits.h +++ /dev/null @@ -1,11 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - LoadAudioUnits.h - - Dominic Mazzoni - -**********************************************************************/ - -void LoadAudioUnits(); diff --git a/src/prefs/EffectsPrefs.cpp b/src/prefs/EffectsPrefs.cpp index 1ade02700..6a34c1fe0 100644 --- a/src/prefs/EffectsPrefs.cpp +++ b/src/prefs/EffectsPrefs.cpp @@ -59,7 +59,7 @@ void EffectsPrefs::PopulateOrExchange(ShuttleGui & S) #if USE_AUDIO_UNITS S.TieCheckBox(_("Audio Unit"), - wxT("/AudioUnits/Enable"), + wxT("/AudioUnit/Enable"), true); #endif