From bd93d120a0444f145d45b8704dd8c95232f5abb1 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Sun, 13 Mar 2016 18:48:17 -0700 Subject: [PATCH] Added (disabled) AUPlayer configuration view, which is obviously not in real time due to the buffering, hence it is disabled. --- Plugins/MIDI/MIDI.xcodeproj/project.pbxproj | 12 ++ Plugins/MIDI/MIDI/AUPlayer.h | 13 +- Plugins/MIDI/MIDI/AUPlayer.mm | 80 +++++++-- Plugins/MIDI/MIDI/AUPlayerView.h | 52 ++++++ Plugins/MIDI/MIDI/AUPlayerView.mm | 169 ++++++++++++++++++++ Plugins/MIDI/MIDI/MIDIDecoder.h | 2 + Plugins/MIDI/MIDI/MIDIDecoder.mm | 17 +- 7 files changed, 325 insertions(+), 20 deletions(-) create mode 100644 Plugins/MIDI/MIDI/AUPlayerView.h create mode 100644 Plugins/MIDI/MIDI/AUPlayerView.mm diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 9ddf527da..14ec07366 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 83686AAC1C5C69D400671C7A /* AUPlayerView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83686AAB1C5C69D400671C7A /* AUPlayerView.mm */; }; + 83686AB11C5C783000671C7A /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83686AB01C5C783000671C7A /* CoreAudioKit.framework */; }; 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */; }; 839CA224180D902100553DBA /* midi_processing.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066E0180D56BA008E3612 /* midi_processing.framework */; }; 83B0668B180D5668008E3612 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83B0668A180D5668008E3612 /* Cocoa.framework */; }; @@ -74,6 +76,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 83686AAB1C5C69D400671C7A /* AUPlayerView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayerView.mm; sourceTree = ""; }; + 83686AAD1C5C6A2700671C7A /* AUPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUPlayerView.h; sourceTree = ""; }; + 83686AAE1C5C780500671C7A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 83686AB01C5C783000671C7A /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; }; 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; 83B06687180D5668008E3612 /* MIDI.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MIDI.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 83B0668A180D5668008E3612 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -112,6 +118,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 83686AB11C5C783000671C7A /* CoreAudioKit.framework in Frameworks */, 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */, 83B0670F180D6F7F008E3612 /* libbass.dylib in Frameworks */, 83B06701180D5747008E3612 /* midi_processing.framework in Frameworks */, @@ -143,6 +150,8 @@ 83B06689180D5668008E3612 /* Frameworks */ = { isa = PBXGroup; children = ( + 83686AB01C5C783000671C7A /* CoreAudioKit.framework */, + 83686AAE1C5C780500671C7A /* AudioToolbox.framework */, 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */, 83B06714180D6FC8008E3612 /* libbass_mpc.dylib */, 83B06715180D6FC8008E3612 /* libbassflac.dylib */, @@ -170,6 +179,8 @@ 83B06690180D5668008E3612 /* MIDI */ = { isa = PBXGroup; children = ( + 83686AAD1C5C6A2700671C7A /* AUPlayerView.h */, + 83686AAB1C5C69D400671C7A /* AUPlayerView.mm */, 83E973451C4378880007F413 /* AUPlayer.mm */, 83E973461C4378880007F413 /* AUPlayer.h */, 83FAF8A618ADD60100057CAF /* PlaylistController.h */, @@ -290,6 +301,7 @@ buildActionMask = 2147483647; files = ( 83E973471C4378880007F413 /* AUPlayer.mm in Sources */, + 83686AAC1C5C69D400671C7A /* AUPlayerView.mm in Sources */, 83B06709180D64DA008E3612 /* MIDIPlayer.cpp in Sources */, 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */, 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */, diff --git a/Plugins/MIDI/MIDI/AUPlayer.h b/Plugins/MIDI/MIDI/AUPlayer.h index 5eefe25bb..942e88c59 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.h +++ b/Plugins/MIDI/MIDI/AUPlayer.h @@ -9,6 +9,8 @@ #import #import +class AUPluginUI; + class AUPlayer : public MIDIPlayer { public: @@ -19,8 +21,8 @@ public: virtual ~AUPlayer(); // configuration - /*void setSoundFont( const char * in ); - void setFileSoundFont( const char * in );*/ + void setSoundFont( const char * in ); + /*void setFileSoundFont( const char * in );*/ //void showDialog(); typedef void (*callback)(OSType uSubType, OSType uManufacturer, const char * name); @@ -36,15 +38,18 @@ protected: virtual bool startup(); private: - /*void loadSoundFont(const char * name); + void loadSoundFont(const char * name); std::string sSoundFontName; - std::string sFileSoundFontName;*/ + /*std::string sFileSoundFontName;*/ AudioTimeStamp mTimeStamp; AudioUnit samplerUnit[3]; + bool samplerUIinitialized[3]; + AUPluginUI * samplerUI[3]; + AudioBufferList *bufferList; float *audioBuffer; diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm index 41ded9582..83f3baaea 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.mm +++ b/Plugins/MIDI/MIDI/AUPlayer.mm @@ -4,6 +4,12 @@ #define SF2PACK +// #define AUPLAYERVIEW + +#ifdef AUPLAYERVIEW +#import "AUPlayerView.h" +#endif + #define _countof(arr) (sizeof(arr) / sizeof((arr)[0])) AUPlayer::AUPlayer() : MIDIPlayer() @@ -11,6 +17,14 @@ AUPlayer::AUPlayer() : MIDIPlayer() samplerUnit[0] = NULL; samplerUnit[1] = NULL; samplerUnit[2] = NULL; +#ifdef AUPLAYERVIEW + samplerUI[0] = NULL; + samplerUI[1] = NULL; + samplerUI[2] = NULL; + samplerUIinitialized[0] = false; + samplerUIinitialized[1] = false; + samplerUIinitialized[2] = false; +#endif bufferList = NULL; audioBuffer = NULL; @@ -25,7 +39,10 @@ AUPlayer::~AUPlayer() void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) { - if (!(b & 0x80000000)) +#ifdef AUPLAYERVIEW + int _port = -1; +#endif + if (!(b & 0x80000000)) { unsigned char event[ 3 ]; event[ 0 ] = (unsigned char)b; @@ -33,6 +50,9 @@ void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) event[ 2 ] = (unsigned char)( b >> 16 ); unsigned port = (b >> 24) & 0x7F; if ( port > 2 ) port = 2; +#ifdef AUPLAYERVIEW + _port = (int)port; +#endif MusicDeviceMIDIEvent(samplerUnit[port], event[0], event[1], event[2], sample_offset); } else @@ -42,6 +62,9 @@ void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) std::size_t size, port; mSysexMap.get_entry( n, data, size, port ); if ( port > 2 ) port = 2; +#ifdef AUPLAYERVIEW + _port = (int)port; +#endif MusicDeviceSysEx(samplerUnit[port], data, (UInt32) size); if ( port == 0 ) { @@ -49,6 +72,15 @@ void AUPlayer::send_event(uint32_t b, uint32_t sample_offset) MusicDeviceSysEx(samplerUnit[2], data, (UInt32) size); } } +#ifdef AUPLAYERVIEW + if (_port >= 0 && !samplerUIinitialized[_port]) + { + samplerUIinitialized[_port] = true; + dispatch_async(dispatch_get_main_queue(), ^{ + samplerUI[_port] = new AUPluginUI(samplerUnit[_port]); + }); + } +#endif } void AUPlayer::render_512(float * out) @@ -86,18 +118,42 @@ void AUPlayer::shutdown() { if ( samplerUnit[2] ) { +#ifdef AUPLAYERVIEW + if ( samplerUI[2] ) + { + delete samplerUI[2]; + samplerUI[2] = 0; + samplerUIinitialized[2] = false; + } +#endif AudioUnitUninitialize( samplerUnit[2] ); AudioComponentInstanceDispose( samplerUnit[2] ); samplerUnit[2] = NULL; } if ( samplerUnit[1] ) { +#ifdef AUPLAYERVIEW + if ( samplerUI[1] ) + { + delete samplerUI[1]; + samplerUI[1] = 0; + samplerUIinitialized[1] = false; + } +#endif AudioUnitUninitialize( samplerUnit[1] ); AudioComponentInstanceDispose( samplerUnit[1] ); samplerUnit[1] = NULL; } if ( samplerUnit[0] ) { +#ifdef AUPLAYERVIEW + if ( samplerUI[0] ) + { + delete samplerUI[0]; + samplerUI[0] = 0; + samplerUIinitialized[0] = false; + } +#endif AudioUnitUninitialize( samplerUnit[0] ); AudioComponentInstanceDispose( samplerUnit[0] ); samplerUnit[0] = NULL; @@ -150,13 +206,17 @@ void AUPlayer::setComponent(OSType uSubType, OSType uManufacturer) shutdown(); } -/*void AUPlayer::setSoundFont( const char * in ) +void AUPlayer::setSoundFont( const char * in ) { - sSoundFontName = in; - shutdown(); + const char * ext = strrchr(in, '.'); + if (*ext && ((strncasecmp(ext + 1, "sf2", 3) == 0) || (strncasecmp(ext + 1, "dls", 3) == 0))) + { + sSoundFontName = in; + shutdown(); + } } -void AUPlayer::setFileSoundFont( const char * in ) +/*void AUPlayer::setFileSoundFont( const char * in ) { sFileSoundFontName = in; shutdown(); @@ -257,10 +317,8 @@ bool AUPlayer::startup() AudioUnitReset (samplerUnit[i], kAudioUnitScope_Global, 0); - /* value = 1; AudioUnitSetProperty(samplerUnit[i], kMusicDeviceProperty_StreamFromDisk, kAudioUnitScope_Global, 0, &value, size); - */ error = AudioUnitInitialize(samplerUnit[i]); @@ -269,12 +327,12 @@ bool AUPlayer::startup() } // Now load instruments - /*if (sSoundFontName.length()) + if (sSoundFontName.length()) { loadSoundFont( sSoundFontName.c_str() ); } - if ( sFileSoundFontName.length() ) + /*if ( sFileSoundFontName.length() ) { loadSoundFont( sFileSoundFontName.c_str() ); }*/ @@ -295,7 +353,7 @@ bool AUPlayer::startup() return true; } -/*void AUPlayer::loadSoundFont(const char *name) +void AUPlayer::loadSoundFont(const char *name) { // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement // In addition, the File Manager API became deprecated starting in 10.8 @@ -311,4 +369,4 @@ bool AUPlayer::startup() CFRelease(url); } -}*/ +} diff --git a/Plugins/MIDI/MIDI/AUPlayerView.h b/Plugins/MIDI/MIDI/AUPlayerView.h new file mode 100644 index 000000000..695a43764 --- /dev/null +++ b/Plugins/MIDI/MIDI/AUPlayerView.h @@ -0,0 +1,52 @@ +// +// AUPlayerView.h +// MIDI +// +// Created by Christopher Snowhill on 1/29/16. +// Copyright © 2016 Christopher Snowhill. All rights reserved. +// + +#ifndef __AUPlayerView_h__ +#define __AUPlayerView_h__ + +#include +#include + +#include +#include +#include +#include + +class AUPluginUI +{ +public: + AUPluginUI (AudioUnit & sampler); + ~AUPluginUI (); + +private: + AudioUnit & au; + int prefheight; + int prefwidth; + + bool mapped; + bool resizable; + int min_width; + int min_height; + int req_width; + int req_height; + int alo_width; + int alo_height; + + /* Cocoa */ + + NSWindow* cocoa_window; + NSView* au_view; + NSRect last_au_frame; + + bool test_cocoa_view_support (); + int create_cocoa_view (); + + bool plugin_class_valid (Class pluginClass); +}; + +#endif diff --git a/Plugins/MIDI/MIDI/AUPlayerView.mm b/Plugins/MIDI/MIDI/AUPlayerView.mm new file mode 100644 index 000000000..f0de19816 --- /dev/null +++ b/Plugins/MIDI/MIDI/AUPlayerView.mm @@ -0,0 +1,169 @@ +// +// AUPlayerView.mm +// MIDI +// +// Created by Christopher Snowhill on 1/29/16. +// Copyright © 2016 Christopher Snowhill. All rights reserved. +// + +#import +#import + +#import "AUPlayerView.h" + +AUPluginUI::AUPluginUI (AudioUnit & _au) +: au (_au) +, mapped (false) +, resizable (false) +, min_width (0) +, min_height (0) +, req_width (0) +, req_height (0) +, alo_width (0) +, alo_height (0) +{ + cocoa_window = nil; + au_view = nil; + + if (test_cocoa_view_support()) { + create_cocoa_view (); + } + + if (au_view) { + cocoa_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, req_width, req_height) styleMask:(NSTitledWindowMask | NSClosableWindowMask) backing:NSBackingStoreBuffered defer:NO]; + + [cocoa_window setAutodisplay:YES]; + [cocoa_window setTitle:@"AU Plug-in"]; + [cocoa_window setOneShot:YES]; + + [cocoa_window setContentView:au_view]; + + [cocoa_window orderFront:cocoa_window]; + } +} + +AUPluginUI::~AUPluginUI() +{ +} + +bool +AUPluginUI::test_cocoa_view_support () +{ + UInt32 dataSize = 0; + Boolean isWritable = 0; + OSStatus err = AudioUnitGetPropertyInfo(au, + kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, &dataSize, &isWritable); + + return dataSize > 0 && err == noErr; +} + +bool +AUPluginUI::plugin_class_valid (Class pluginClass) +{ + if([pluginClass conformsToProtocol: @protocol(AUCocoaUIBase)]) { + if([pluginClass instancesRespondToSelector: @selector(interfaceVersion)] && + [pluginClass instancesRespondToSelector: @selector(uiViewForAudioUnit:withSize:)]) { + return true; + } + } + return false; +} + +int +AUPluginUI::create_cocoa_view () +{ + bool wasAbleToLoadCustomView = false; + AudioUnitCocoaViewInfo* cocoaViewInfo = NULL; + UInt32 numberOfClasses = 0; + UInt32 dataSize; + Boolean isWritable; + NSString* factoryClassName = 0; + NSURL* CocoaViewBundlePath = NULL; + + OSStatus result = AudioUnitGetPropertyInfo (au, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable ); + + numberOfClasses = (dataSize - sizeof(CFURLRef)) / sizeof(CFStringRef); + + // Does view have custom Cocoa UI? + + if ((result == noErr) && (numberOfClasses > 0) ) { + + cocoaViewInfo = (AudioUnitCocoaViewInfo *)malloc(dataSize); + + if(AudioUnitGetProperty(au, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + cocoaViewInfo, + &dataSize) == noErr) { + + CocoaViewBundlePath = (__bridge NSURL *)cocoaViewInfo->mCocoaAUViewBundleLocation; + + // we only take the first view in this example. + factoryClassName = (__bridge NSString *)cocoaViewInfo->mCocoaAUViewClass[0]; + } else { + if (cocoaViewInfo != NULL) { + free (cocoaViewInfo); + cocoaViewInfo = NULL; + } + } + } + + // [A] Show custom UI if view has it + + if (CocoaViewBundlePath && factoryClassName) { + NSBundle *viewBundle = [NSBundle bundleWithPath:[CocoaViewBundlePath path]]; + + if (viewBundle == NULL) { + return -1; + } else { + Class factoryClass = [viewBundle classNamed:factoryClassName]; + if (!factoryClass) { + return -1; + } + + // make sure 'factoryClass' implements the AUCocoaUIBase protocol + if (!plugin_class_valid (factoryClass)) { + return -1; + } + // make a factory + id factory = [[factoryClass alloc] init]; + if (factory == NULL) { + return -1; + } + + // make a view + au_view = [factory uiViewForAudioUnit:au withSize:NSZeroSize]; + + // cleanup + if (cocoaViewInfo) { + UInt32 i; + for (i = 0; i < numberOfClasses; i++) + CFRelease(cocoaViewInfo->mCocoaAUViewClass[i]); + + free (cocoaViewInfo); + } + wasAbleToLoadCustomView = true; + } + } + + if (!wasAbleToLoadCustomView) { + // load generic Cocoa view + au_view = [[AUGenericView alloc] initWithAudioUnit:au]; + [(AUGenericView *)au_view setShowsExpertParameters:1]; + } + + // Get the initial size of the new AU View's frame + NSRect frame = [au_view frame]; + min_width = req_width = CGRectGetWidth(NSRectToCGRect(frame)); + min_height = req_height = CGRectGetHeight(NSRectToCGRect(frame)); + resizable = [au_view autoresizingMask]; + + return 0; +} diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.h b/Plugins/MIDI/MIDI/MIDIDecoder.h index 4cc06d9fa..8e91c4d06 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.h +++ b/Plugins/MIDI/MIDI/MIDIDecoder.h @@ -12,10 +12,12 @@ #import "Plugin.h" +class AUPlayer; class BMPlayer; @interface MIDIDecoder : NSObject { BMPlayer* bmplayer; + AUPlayer* auplayer; MIDIPlayer* player; midi_container midi_file; diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index c71519e48..056b71032 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -121,8 +121,6 @@ static OSType getOSType(const char * in_) DLog(@"Track num: %i", track_num); - AUPlayer * auplayer = NULL; - NSString * plugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"]; if (!plugin || [plugin isEqualToString:@"BASSMIDI"]) { @@ -149,12 +147,18 @@ static OSType getOSType(const char * in_) componentSubType = getOSType(cplugin); componentManufacturer = getOSType(cplugin + 4); - + auplayer = new AUPlayer; auplayer->setComponent(componentSubType, componentManufacturer); auplayer->setSampleRate( 44100 ); + if ( [soundFontPath length] ) + { + auplayer->setSoundFont( [soundFontPath UTF8String] ); + soundFontsAssigned = YES; + } + player = auplayer; } @@ -197,12 +201,15 @@ static OSType getOSType(const char * in_) if ( !repeatone && framesRead >= localTotalFrames ) return 0; - if ( bmplayer && !soundFontsAssigned ) { + if ( (bmplayer||auplayer) && !soundFontsAssigned ) { NSString * soundFontPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; if (soundFontPath == nil) return 0; - bmplayer->setSoundFont( [soundFontPath UTF8String] ); + if (bmplayer) + bmplayer->setSoundFont( [soundFontPath UTF8String] ); + else if (auplayer) + auplayer->setSoundFont( [soundFontPath UTF8String] ); soundFontsAssigned = YES; }