Initial release
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : AUv3AudioEngine.h
|
||||
// Created by : Steinberg, 07/2017.
|
||||
// Description : VST 3 AUv3Wrapper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface AUv3AudioEngine : NSObject
|
||||
|
||||
@property (assign) AUAudioUnit* currentAudioUnit;
|
||||
|
||||
- (NSError*)loadAudioFile:(NSURL*)url;
|
||||
- (instancetype)initWithComponentType:(uint32_t)unitComponentType;
|
||||
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
|
||||
completion:(void (^) (void))completionBlock;
|
||||
- (BOOL)startStop;
|
||||
- (void)shutdown;
|
||||
@end
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : AUv3AudioEngine.mm
|
||||
// Created by : Steinberg, 07/2017.
|
||||
// Description : VST 3 AUv3Wrapper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#import "AUv3AudioEngine.h"
|
||||
#import <CoreMIDI/CoreMIDI.h>
|
||||
#import <functional>
|
||||
#import <vector>
|
||||
#import <utility>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg {
|
||||
namespace Vst {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
class MidiIO
|
||||
{
|
||||
public:
|
||||
using ReadCallback = std::function<void (const MIDIPacketList* pktlist)>;
|
||||
|
||||
MidiIO (ReadCallback&& callback) : readCallback (std::move (callback)) { activate (); }
|
||||
~MidiIO () = default;
|
||||
|
||||
private:
|
||||
bool activate ();
|
||||
bool deactivate ();
|
||||
|
||||
void onSourceAdded (MIDIObjectRef source);
|
||||
void onSetupChanged ();
|
||||
void disconnectSources ();
|
||||
|
||||
void onInput (const MIDIPacketList* pktlist);
|
||||
|
||||
static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon);
|
||||
static void notifyProc (const MIDINotification* message, void* refCon);
|
||||
|
||||
MIDIClientRef client {0};
|
||||
MIDIPortRef inputPort {0};
|
||||
MIDIEndpointRef destPort {0};
|
||||
|
||||
ReadCallback readCallback;
|
||||
|
||||
using ConnectionList = std::vector<MIDIEndpointRef>;
|
||||
ConnectionList connectedSources;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool MidiIO::activate ()
|
||||
{
|
||||
if (client)
|
||||
return true;
|
||||
|
||||
OSStatus err;
|
||||
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if ((err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr))
|
||||
return false;
|
||||
|
||||
if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) != noErr))
|
||||
{
|
||||
MIDIClientDispose (client);
|
||||
client = 0;
|
||||
return false;
|
||||
}
|
||||
name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleName"];
|
||||
if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this,
|
||||
&destPort) != noErr))
|
||||
{
|
||||
MIDIPortDispose (inputPort);
|
||||
inputPort = 0;
|
||||
MIDIClientDispose (client);
|
||||
client = 0;
|
||||
return false;
|
||||
}
|
||||
onSetupChanged ();
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
bool MidiIO::deactivate ()
|
||||
{
|
||||
if (client == 0)
|
||||
return true;
|
||||
disconnectSources ();
|
||||
auto status = MIDIEndpointDispose (destPort);
|
||||
destPort = 0;
|
||||
status |= MIDIPortDispose (inputPort);
|
||||
inputPort = 0;
|
||||
status |= MIDIClientDispose (client);
|
||||
client = 0;
|
||||
return status == noErr;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::onSourceAdded (MIDIObjectRef source)
|
||||
{
|
||||
connectedSources.push_back ((MIDIEndpointRef)source);
|
||||
MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::onSetupChanged ()
|
||||
{
|
||||
disconnectSources ();
|
||||
ItemCount numSources = MIDIGetNumberOfSources ();
|
||||
for (ItemCount i = 0; i < numSources; i++)
|
||||
{
|
||||
onSourceAdded (MIDIGetSource (i));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::disconnectSources ()
|
||||
{
|
||||
for (auto source : connectedSources)
|
||||
MIDIPortDisconnectSource (inputPort, source);
|
||||
connectedSources.clear ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::onInput (const MIDIPacketList* pktlist)
|
||||
{
|
||||
if (readCallback)
|
||||
readCallback (pktlist);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon)
|
||||
{
|
||||
MidiIO* io = static_cast<MidiIO*> (readProcRefCon);
|
||||
io->onInput (pktlist);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
void MidiIO::notifyProc (const MIDINotification* message, void* refCon)
|
||||
{
|
||||
if (message->messageID == kMIDIMsgSetupChanged)
|
||||
{
|
||||
MidiIO* mio = (MidiIO*)refCon;
|
||||
mio->onSetupChanged ();
|
||||
}
|
||||
}
|
||||
|
||||
using MidiIOPtr = std::unique_ptr<MidiIO>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Vst
|
||||
} // Steinberg
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@implementation AUv3AudioEngine
|
||||
{
|
||||
AVAudioEngine* audioEngine;
|
||||
AVAudioFile* audioFile;
|
||||
AVAudioPlayerNode* playerNode;
|
||||
AVAudioUnit* avAudioUnit;
|
||||
Steinberg::Vst::MidiIOPtr midi;
|
||||
UInt32 componentType;
|
||||
BOOL playing;
|
||||
BOOL isDone;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (instancetype)initWithComponentType:(uint32_t)unitComponentType
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
isDone = false;
|
||||
|
||||
if (self)
|
||||
{
|
||||
audioEngine = [[AVAudioEngine alloc] init];
|
||||
componentType = unitComponentType;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
NSError* error = nil;
|
||||
BOOL success =
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
||||
if (NO == success)
|
||||
{
|
||||
NSLog (@"Error setting category: %@", [error localizedDescription]);
|
||||
}
|
||||
#endif
|
||||
|
||||
playing = false;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)shutdown
|
||||
{
|
||||
if (playing)
|
||||
[self stopPlaying];
|
||||
audioEngine = nil;
|
||||
midi.reset ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)onAudioUnitInstantiated:(AVAudioUnit* __nullable)audioUnit
|
||||
error:(NSError* __nullable)error
|
||||
completion:(void (^) (void))completionBlock
|
||||
{
|
||||
if (audioUnit == nil)
|
||||
return;
|
||||
|
||||
avAudioUnit = audioUnit;
|
||||
|
||||
_currentAudioUnit = avAudioUnit.AUAudioUnit;
|
||||
[audioEngine attachNode:avAudioUnit];
|
||||
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
|
||||
|
||||
completionBlock ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
|
||||
completion:(void (^) (void))completionBlock
|
||||
{
|
||||
[AVAudioUnit instantiateWithComponentDescription:desc
|
||||
options:0
|
||||
completionHandler:^(AVAudioUnit* __nullable audioUnit,
|
||||
NSError* __nullable error) {
|
||||
[self onAudioUnitInstantiated:audioUnit
|
||||
error:error
|
||||
completion:completionBlock];
|
||||
}];
|
||||
|
||||
if (componentType == kAudioUnitType_MusicDevice)
|
||||
{
|
||||
midi = Steinberg::Vst::MidiIOPtr (
|
||||
new Steinberg::Vst::MidiIO ([=] (const MIDIPacketList* pktlist) {
|
||||
[self scheduleMIDIPackets:pktlist];
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (NSError*)loadAudioFile:(NSURL*)url
|
||||
{
|
||||
BOOL isPlaying = playing;
|
||||
if (isPlaying)
|
||||
[self startStop];
|
||||
if (playerNode)
|
||||
{
|
||||
[playerNode stop];
|
||||
[audioEngine detachNode:playerNode];
|
||||
}
|
||||
|
||||
NSError* error = nil;
|
||||
audioFile = [[AVAudioFile alloc] initForReading:url error:&error];
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
[audioEngine detachNode:avAudioUnit];
|
||||
[audioEngine attachNode:avAudioUnit];
|
||||
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
|
||||
|
||||
playerNode = [[AVAudioPlayerNode alloc] init];
|
||||
[audioEngine attachNode:playerNode];
|
||||
[audioEngine connect:playerNode to:avAudioUnit format:audioFile.processingFormat];
|
||||
|
||||
if (isPlaying)
|
||||
[self startStop];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (BOOL)startStop
|
||||
{
|
||||
playing = !playing;
|
||||
|
||||
playing ? ([self startPlaying]) : ([self stopPlaying]);
|
||||
|
||||
return playing;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)startPlaying
|
||||
{
|
||||
[self activateSession:true];
|
||||
NSError* error = nil;
|
||||
|
||||
if (![audioEngine startAndReturnError:&error])
|
||||
{
|
||||
NSLog (@"engine failed to start: %@", error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerNode)
|
||||
{
|
||||
[self loopAudioFile];
|
||||
[playerNode play];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)stopPlaying
|
||||
{
|
||||
if (playerNode)
|
||||
[playerNode stop];
|
||||
[audioEngine stop];
|
||||
[self activateSession:false];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)loopAudioFile
|
||||
{
|
||||
if (playerNode)
|
||||
{
|
||||
[playerNode scheduleFile:audioFile
|
||||
atTime:nil
|
||||
completionHandler:^{
|
||||
if (playerNode.playing)
|
||||
[self loopAudioFile];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)scheduleMIDIPackets:(const MIDIPacketList*) pktlist
|
||||
{
|
||||
if (!_currentAudioUnit || _currentAudioUnit.scheduleMIDIEventBlock == nil)
|
||||
return;
|
||||
|
||||
auto packet = &pktlist->packet[0];
|
||||
for (auto i = 0u; i < pktlist->numPackets; i++)
|
||||
{
|
||||
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, packet->length,
|
||||
packet->data);
|
||||
packet = MIDIPacketNext (packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)loopMIDIsequence
|
||||
{
|
||||
UInt8 cbytes[3], *cbytesPtr;
|
||||
|
||||
cbytesPtr = cbytes;
|
||||
|
||||
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
cbytesPtr[0] = 0xB0;
|
||||
cbytesPtr[1] = 123;
|
||||
cbytesPtr[2] = 0;
|
||||
|
||||
if (_currentAudioUnit.scheduleMIDIEventBlock == nil)
|
||||
return;
|
||||
|
||||
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
|
||||
usleep (useconds_t (0.1 * 1e6));
|
||||
|
||||
float releaseTime = 0.05;
|
||||
|
||||
usleep (useconds_t (0.1 * 1e6));
|
||||
|
||||
int i = 0;
|
||||
|
||||
@synchronized (self)
|
||||
{
|
||||
while (playing)
|
||||
{
|
||||
if (releaseTime < 10.0)
|
||||
releaseTime = (releaseTime * 1.05) > 10.0 ? (releaseTime * 1.05) : 10.0;
|
||||
|
||||
cbytesPtr[0] = 0x90;
|
||||
cbytesPtr[1] = UInt8 (60 + i);
|
||||
cbytesPtr[2] = UInt8 (64); // note on
|
||||
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
|
||||
cbytesPtr);
|
||||
|
||||
usleep (useconds_t (0.2 * 1e6));
|
||||
|
||||
cbytesPtr[0] = 0x80;
|
||||
cbytesPtr[1] = UInt8 (60 + i);
|
||||
cbytesPtr[2] = UInt8 (0); // note off
|
||||
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
|
||||
cbytesPtr);
|
||||
|
||||
i += 2;
|
||||
if (i >= 24)
|
||||
{
|
||||
i = -12;
|
||||
}
|
||||
}
|
||||
|
||||
cbytesPtr[0] = 0xB0;
|
||||
cbytesPtr[1] = 123;
|
||||
cbytesPtr[2] = 0;
|
||||
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
|
||||
|
||||
isDone = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
- (void)activateSession:(BOOL)active
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
NSError* error = nil;
|
||||
BOOL success = [[AVAudioSession sharedInstance] setActive:active error:nil];
|
||||
if (NO == success)
|
||||
{
|
||||
NSLog (@"Error setting category: %@", [error localizedDescription]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename : AUv3Wrapper.h
|
||||
// Created by : Steinberg, 07/2017.
|
||||
// Description : VST 3 AUv3Wrapper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <CoreAudioKit/AUViewController.h>
|
||||
|
||||
@class AUv3Wrapper;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@interface AUv3WrapperViewController : AUViewController
|
||||
|
||||
@property (nonatomic, strong) AUv3Wrapper* audioUnit;
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@interface AUv3Wrapper : AUAudioUnit
|
||||
- (void)beginEdit:(int32_t)tag;
|
||||
- (void)endEdit:(int32_t)tag;
|
||||
- (void)performEdit:(int32_t)tag value:(double)value;
|
||||
- (void)syncParameterValues;
|
||||
- (void)updateParameters;
|
||||
- (void)onTimer;
|
||||
- (void)onParamTitlesChanged;
|
||||
- (void)onNoteExpressionChanged;
|
||||
- (void)onLatencyChanged;
|
||||
- (BOOL)enableMPESupport:(BOOL)state;
|
||||
- (BOOL)setMPEInputDeviceMasterChannel:(NSInteger)masterChannel
|
||||
memberBeginChannel:(NSInteger)memberBeginChannel
|
||||
memberEndChannel:(NSInteger)memberEndChannel;
|
||||
@end
|
||||
+2647
File diff suppressed because it is too large
Load Diff
+23
@@ -0,0 +1,23 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename :
|
||||
// Created by : Steinberg, 07/2017.
|
||||
// Description : VST 3 AUv3Wrapper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#import "AUv3Wrapper.h"
|
||||
|
||||
@interface AUv3WrapperViewController (AUAudioUnitFactory) <AUAudioUnitFactory>
|
||||
|
||||
@end
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Project : VST SDK
|
||||
//
|
||||
// Category : Helpers
|
||||
// Filename :
|
||||
// Created by : Steinberg, 07/2017.
|
||||
// Description : VST 3 AUv3Wrapper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#import "AUv3WrapperFactory.h"
|
||||
|
||||
@implementation AUv3WrapperViewController (AUAudioUnitFactory)
|
||||
|
||||
- (AUv3Wrapper *) createAudioUnitWithComponentDescription:(AudioComponentDescription) desc error:(NSError **)error {
|
||||
@synchronized (self)
|
||||
{
|
||||
if (!self.audioUnit)
|
||||
{
|
||||
if (![NSThread isMainThread])
|
||||
{
|
||||
dispatch_sync(dispatch_get_main_queue(), [&]{
|
||||
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
|
||||
}
|
||||
}
|
||||
}
|
||||
return self.audioUnit;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user