From 540f5c78c9c0c945cf8eee8f4991310c492b679e Mon Sep 17 00:00:00 2001 From: "lllucius@gmail.com" Date: Tue, 25 Nov 2014 08:08:15 +0000 Subject: [PATCH] AudioUnits rework... They now work on Yosemite. AudioUnits with a custom Cocoa UI now display graphically instead of reverting to the generic view The Cocoa version of the generic view is now used when needed...instead of the Carbon version. The order of UI preference is Cocoa, Carbon, Generic, unless force to Generic view user setting. They now support realtime preview. They also support dialog resizing as I found many that scaled nicely (mostly Apple's). Uses the new Effect format so now supports user and factory presets. NOTE: Be VERY critical when testing this as I've never written Objective-C or Cocoa code before! --- mac/Audacity.xcodeproj/project.pbxproj | 24 +- src/PluginManager.cpp | 4 + src/effects/Effect.cpp | 28 +- src/effects/LoadEffects.cpp | 15 - src/effects/VST/VSTEffect.cpp | 2 +- src/effects/audiounits/AudioUnitCocoaHelper.h | 28 + src/effects/audiounits/AudioUnitCocoaHelper.m | 301 ++ src/effects/audiounits/AudioUnitEffect.cpp | 2767 ++++++++++++----- src/effects/audiounits/AudioUnitEffect.h | 259 +- src/effects/audiounits/LoadAudioUnits.cpp | 49 - src/effects/audiounits/LoadAudioUnits.h | 11 - src/prefs/EffectsPrefs.cpp | 2 +- 12 files changed, 2606 insertions(+), 884 deletions(-) create mode 100644 src/effects/audiounits/AudioUnitCocoaHelper.h create mode 100644 src/effects/audiounits/AudioUnitCocoaHelper.m delete mode 100644 src/effects/audiounits/LoadAudioUnits.cpp delete mode 100644 src/effects/audiounits/LoadAudioUnits.h 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