Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AUComponent.h>
#include <vector>
#ifndef __OBJC__
struct UIImage;
struct NSString;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class AudioIO;
//------------------------------------------------------------------------
class IMidiProcessor
{
public:
virtual void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread) = 0;
};
//------------------------------------------------------------------------
class IAudioIOProcessor : public IMidiProcessor
{
public:
virtual void willStartAudio (AudioIO* audioIO) = 0;
virtual void didStopAudio (AudioIO* audioIO) = 0;
virtual void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) = 0;
};
//------------------------------------------------------------------------
class AudioIO
{
public:
static AudioIO* instance ();
tresult init (OSType type, OSType subType, OSType manufacturer, CFStringRef name);
bool switchToHost ();
bool sendRemoteControlEvent (AudioUnitRemoteControlEvent event);
UIImage* getHostIcon ();
tresult start ();
tresult stop ();
tresult addProcessor (IAudioIOProcessor* processor);
tresult removeProcessor (IAudioIOProcessor* processor);
// accessors
AudioUnit getRemoteIO () const { return remoteIO; }
SampleRate getSampleRate () const { return sampleRate; }
bool getInterAppAudioConnected () const { return interAppAudioConnected; }
// host context information
bool getBeatAndTempo (Float64& beat, Float64& tempo);
bool getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat);
bool getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat);
void setStaticFallbackTempo (Float64 tempo) { staticTempo = tempo; }
Float64 getStaticFallbackTempo () const { return staticTempo; }
static NSString* kConnectionStateChange;
//------------------------------------------------------------------------
protected:
AudioIO ();
~AudioIO ();
static OSStatus inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static OSStatus renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static void propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement);
static void midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame);
OSStatus inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
OSStatus renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
void midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame);
void remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement);
void setAudioSessionActive (bool state);
tresult setupRemoteIO (OSType type);
tresult setupAUGraph (OSType type);
void updateInterAppAudioConnectionState ();
AudioUnit remoteIO {nullptr};
AUGraph graph {nullptr};
AudioBufferList* ioBufferList {nullptr};
HostCallbackInfo hostCallback {};
UInt32 maxFrames {4096};
Float64 staticTempo {120.};
SampleRate sampleRate;
bool interAppAudioConnected {false};
std::vector<IAudioIOProcessor*> audioProcessors;
enum InternalState
{
kUninitialized,
kInitialized,
kStarted,
};
InternalState internalState {kUninitialized};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,554 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "AudioIO.h"
#import "MidiIO.h"
#import "pluginterfaces/base/fstrdefs.h"
#import <AVFoundation/AVAudioSession.h>
#import <AudioUnit/AudioUnit.h>
#import <UIKit/UIKit.h>
#define FORCE_INLINE __attribute__ ((always_inline))
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
static AudioBufferList* createBuffers (uint32 numChannels, uint32 maxFrames, uint32 frameSize)
{
AudioBufferList* result =
(AudioBufferList*)malloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * numChannels);
result->mNumberBuffers = numChannels;
for (int32 i = 0; i < numChannels; i++)
{
result->mBuffers[i].mDataByteSize = maxFrames * sizeof (float);
result->mBuffers[i].mData = calloc (1, result->mBuffers[i].mDataByteSize);
result->mBuffers[i].mNumberChannels = 1;
}
return result;
}
//------------------------------------------------------------------------
static void freeAudioBufferList (AudioBufferList* audioBufferList)
{
for (uint32 i = 0; i < audioBufferList->mNumberBuffers; i++)
{
free (audioBufferList->mBuffers[i].mData);
}
free (audioBufferList);
}
//------------------------------------------------------------------------
NSString* AudioIO::kConnectionStateChange = @"AudioIO::kConnectionStateChange";
//------------------------------------------------------------------------
AudioIO::AudioIO ()
{
sampleRate = [[AVAudioSession sharedInstance] sampleRate];
MidiIO::instance ();
}
//------------------------------------------------------------------------
AudioIO::~AudioIO ()
{
if (ioBufferList)
freeAudioBufferList (ioBufferList);
}
//------------------------------------------------------------------------
AudioIO* AudioIO::instance ()
{
static AudioIO gInstance;
return &gInstance;
}
//------------------------------------------------------------------------
tresult AudioIO::setupRemoteIO (OSType type)
{
if (remoteIO != nullptr)
{
AudioStreamBasicDescription streamFormat = {};
streamFormat.mChannelsPerFrame = 2;
streamFormat.mSampleRate = sampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerFrame = streamFormat.mBytesPerPacket = sizeof (Float32);
streamFormat.mBitsPerChannel = 32;
streamFormat.mFramesPerPacket = 1;
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 1, &maxFrames, sizeof (maxFrames));
if (status != noErr)
return kInternalError;
bool needInput = (type == kAudioUnitType_RemoteGenerator ||
type == kAudioUnitType_RemoteInstrument) == false;
UInt32 flag = 1;
if (needInput)
{
// enable IO Input
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
}
// enable IO Output
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, 0, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
AURenderCallbackStruct renderCallback = {};
if (needInput)
{
renderCallback.inputProc = inputCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, 1, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
}
renderCallback.inputProc = renderCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, 0, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
if (type == kAudioUnitType_RemoteInstrument || type == kAudioUnitType_RemoteMusicEffect)
{
AudioOutputUnitMIDICallbacks callBackStruct = {};
callBackStruct.userData = this;
callBackStruct.MIDIEventProc = midiEventCallbackStatic;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_MIDICallbacks,
kAudioUnitScope_Global, 0, &callBackStruct,
sizeof (callBackStruct));
if (status != noErr)
{
NSLog (@"Setting MIDICallback on OutputUnit failed");
}
}
if (ioBufferList)
freeAudioBufferList (ioBufferList);
ioBufferList =
createBuffers (streamFormat.mChannelsPerFrame, maxFrames, streamFormat.mBytesPerFrame);
if (ioBufferList == nullptr)
return kOutOfMemory;
status = AudioUnitAddPropertyListener (remoteIO, kAudioUnitProperty_IsInterAppConnected,
propertyChangeStatic, this);
status = AudioUnitAddPropertyListener (
remoteIO, kAudioOutputUnitProperty_HostTransportState, propertyChangeStatic, this);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::setupAUGraph (OSType type)
{
if (graph == nullptr)
{
OSStatus status = NewAUGraph (&graph);
if (status != noErr)
return kInternalError;
AudioComponentDescription iOUnitDescription;
iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
iOUnitDescription.componentFlags = 0;
iOUnitDescription.componentFlagsMask = 0;
iOUnitDescription.componentType = kAudioUnitType_Output;
iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
AUNode remoteIONode;
status = AUGraphAddNode (graph, &iOUnitDescription, &remoteIONode);
if (status != noErr)
return kInternalError;
status = AUGraphOpen (graph);
if (status != noErr)
return kInternalError;
status = AUGraphNodeInfo (graph, remoteIONode, nullptr, &remoteIO);
if (status != noErr)
return kInternalError;
return setupRemoteIO (type);
}
return kResultFalse;
}
//------------------------------------------------------------------------
void AudioIO::updateInterAppAudioConnectionState ()
{
if (remoteIO)
{
UInt32 connected;
UInt32 dataSize = sizeof (connected);
OSStatus status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_IsInterAppConnected,
kAudioUnitScope_Global, 0, &connected, &dataSize);
if (status == noErr)
{
if (interAppAudioConnected != connected)
{
if (connected)
{
UInt32 size = sizeof (HostCallbackInfo);
status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_HostCallbacks,
kAudioUnitScope_Global, 0, &hostCallback, &size);
}
else
{
memset (&hostCallback, 0, sizeof (HostCallbackInfo));
}
interAppAudioConnected = connected > 0 ? true : false;
[[NSNotificationCenter defaultCenter] postNotificationName:kConnectionStateChange
object:nil];
}
}
}
}
//------------------------------------------------------------------------
tresult AudioIO::init (OSType type, OSType subType, OSType manufacturer, CFStringRef name)
{
tresult result = setupAUGraph (type);
if (result != kResultTrue)
return result;
AudioComponentDescription desc = {type, subType, manufacturer, 0, 0};
OSStatus status = AudioOutputUnitPublish (&desc, name, 0, remoteIO);
if (status != noErr)
{
NSLog (@"AudioOutputUnitPublish failed with status:%d", (int)status);
}
internalState = kInitialized;
return result;
}
//------------------------------------------------------------------------
void AudioIO::setAudioSessionActive (bool state)
{
NSError* error;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:sampleRate error:&error];
[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&error];
[session setActive:(state ? YES : NO)error:&error];
}
//------------------------------------------------------------------------
tresult AudioIO::start ()
{
if (internalState == kInitialized)
{
bool appIsActive =
[UIApplication sharedApplication].applicationState == UIApplicationStateActive;
if (!(appIsActive || interAppAudioConnected))
{
return kResultFalse;
}
setAudioSessionActive (true);
Boolean graphInitialized = true;
OSStatus status = AUGraphIsInitialized (graph, &graphInitialized);
if (status != noErr)
return kInternalError;
if (graphInitialized == false)
{
status = AUGraphInitialize (graph);
if (status != noErr)
return kInternalError;
updateInterAppAudioConnectionState ();
}
for (auto processor : audioProcessors)
{
processor->willStartAudio (this);
}
status = AUGraphStart (graph);
if (status == noErr)
{
internalState = kStarted;
updateInterAppAudioConnectionState ();
return kResultTrue;
}
return kInternalError;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::stop ()
{
if (internalState == kStarted)
{
if (AUGraphStop (graph) == noErr)
{
for (auto processor : audioProcessors)
{
processor->didStopAudio (this);
}
internalState = kInitialized;
if (interAppAudioConnected == false)
setAudioSessionActive (false);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::addProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
audioProcessors.push_back (processor);
MidiIO::instance ().addProcessor (processor);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::removeProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
auto it = std::find (audioProcessors.begin (), audioProcessors.end (), processor);
if (it != audioProcessors.end ())
{
audioProcessors.erase (it);
MidiIO::instance ().removeProcessor (processor);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
bool AudioIO::switchToHost ()
{
if (remoteIO && interAppAudioConnected)
{
CFURLRef instrumentUrl;
UInt32 dataSize = sizeof (instrumentUrl);
OSStatus result =
AudioUnitGetProperty (remoteIO, kAudioUnitProperty_PeerURL, kAudioUnitScope_Global, 0,
&instrumentUrl, &dataSize);
if (result == noErr)
{
[[UIApplication sharedApplication] openURL:(__bridge NSURL*)instrumentUrl];
return true;
}
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::sendRemoteControlEvent (AudioUnitRemoteControlEvent event)
{
if (remoteIO && interAppAudioConnected)
{
UInt32 controlEvent = event;
UInt32 dataSize = sizeof (controlEvent);
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_RemoteControlToHost,
kAudioUnitScope_Global, 0, &controlEvent, dataSize);
return status == noErr;
}
return false;
}
//------------------------------------------------------------------------
UIImage* AudioIO::getHostIcon ()
{
if (remoteIO && interAppAudioConnected)
{
return AudioOutputUnitGetHostIcon (remoteIO, 128);
}
return nil;
}
//------------------------------------------------------------------------
bool AudioIO::getBeatAndTempo (Float64& beat, Float64& tempo)
{
if (hostCallback.beatAndTempoProc)
{
if (hostCallback.beatAndTempoProc (hostCallback.hostUserData, &beat, &tempo) == noErr)
return true;
}
tempo = staticTempo;
beat = 0;
return true;
}
//------------------------------------------------------------------------
bool AudioIO::getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat)
{
if (hostCallback.musicalTimeLocationProc)
{
if (hostCallback.musicalTimeLocationProc (hostCallback.hostUserData, &deltaSampleOffset,
&timeSigNumerator, &timeSigDenominator,
&downBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat)
{
if (hostCallback.transportStateProc2)
{
if (hostCallback.transportStateProc2 (hostCallback.hostUserData, &isPlaying, &isRecording,
&transportStateChanged, &sampleInTimeLine, &isCycling,
&cycleStartBeat, &cycleEndBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::remoteIOPropertyChanged (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement)
{
if (inID == kAudioUnitProperty_IsInterAppConnected)
{
bool wasConnected = interAppAudioConnected;
updateInterAppAudioConnectionState ();
if (wasConnected != interAppAudioConnected)
{
stop ();
start ();
}
}
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList* ioData)
{
if (ioData->mNumberBuffers == ioBufferList->mNumberBuffers)
{
for (uint32 i = 0; i < ioData->mNumberBuffers; i++)
{
memcpy (ioData->mBuffers[i].mData, ioBufferList->mBuffers[i].mData,
ioData->mBuffers[i].mDataByteSize);
}
bool outputIsSilence =
ioActionFlags ? *ioActionFlags & kAudioUnitRenderAction_OutputIsSilence : false;
for (auto processor : audioProcessors)
{
outputIsSilence = false;
processor->process (inTimeStamp, inBusNumber, inNumberFrames, ioData, outputIsSilence,
this);
}
if (ioActionFlags)
{
*ioActionFlags = outputIsSilence ? kAudioUnitRenderAction_OutputIsSilence : 0;
}
}
return noErr;
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
OSStatus status = AudioUnitRender (remoteIO, ioActionFlags, inTimeStamp, inBusNumber,
inNumberFrames, ioBufferList);
return status;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame)
{
for (auto processor : audioProcessors)
{
processor->onMIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame, true);
}
}
//------------------------------------------------------------------------
OSStatus AudioIO::inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->inputCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
OSStatus AudioIO::renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->renderCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
void AudioIO::propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->remoteIOPropertyChanged (inID, inScope, inElement);
}
//------------------------------------------------------------------------
void AudioIO::midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->midiEventCallback (inStatus, inData1, inData2, inOffsetSampleFrame);
}
}
}
}
@@ -0,0 +1,51 @@
if(SMTG_MAC)
option(SMTG_BUILD_INTERAPPAUDIO "Enable building the iOS InterAppAudio examples (deprecated)" OFF)
if(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
message("[SMTG] ********************************************************************************************************************************")
message("[SMTG] * The iOS InterAppAudio wrapper is deprecated and may be removed in the next SDK update. Please switch to AudioUnit V3 on iOS. *")
message("[SMTG] ********************************************************************************************************************************")
set(target interappaudio)
set(${target}_sources
AudioIO.mm
AudioIO.h
HostApp.mm
HostApp.h
MidiIO.mm
MidiIO.h
PresetBrowserViewController.mm
PresetBrowserViewController.h
PresetManager.mm
PresetManager.h
PresetSaveViewController.mm
PresetSaveViewController.h
SettingsViewController.mm
SettingsViewController.h
VST3Editor.mm
VST3Editor.h
VST3Plugin.mm
VST3Plugin.h
VSTInterAppAudioAppDelegateBase.mm
VSTInterAppAudioAppDelegateBase.h
)
add_library(${target} STATIC ${${target}_sources})
smtg_set_platform_ios(${target})
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER}
)
target_link_libraries(${target}
PRIVATE
sdk_ios
"-framework CoreGraphics"
"-framework UIKit"
"-framework CoreMIDI"
"-framework AudioToolbox"
"-framework AVFoundation"
)
endif(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
endif(SMTG_MAC)
@@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/HostApp.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#import "public.sdk/source/vst/hosting/hostclasses.h"
#import "base/source/fobject.h"
#import "pluginterfaces/vst/ivstinterappaudio.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class VST3Plugin;
//-----------------------------------------------------------------------------
class InterAppAudioHostApp : public FObject, public HostApplication, public IInterAppAudioHost
{
public:
//-----------------------------------------------------------------------------
static InterAppAudioHostApp* instance ();
void setPlugin (VST3Plugin* plugin);
VST3Plugin* getPlugin () const { return plugin; }
//-----------------------------------------------------------------------------
// IInterAppAudioHost
tresult PLUGIN_API getScreenSize (ViewRect* size, float* scale) override;
tresult PLUGIN_API connectedToHost () override;
tresult PLUGIN_API switchToHost () override;
tresult PLUGIN_API sendRemoteControlEvent (uint32 event) override;
tresult PLUGIN_API getHostIcon (void** icon) override;
tresult PLUGIN_API scheduleEventFromUI (Event& event) override;
IInterAppAudioPresetManager* PLUGIN_API createPresetManager (const TUID& cid) override;
tresult PLUGIN_API showSettingsView () override;
//-----------------------------------------------------------------------------
// HostApplication
tresult PLUGIN_API getName (String128 name) override;
OBJ_METHODS (InterAppAudioHostApp, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IHostApplication)
DEF_INTERFACE (IInterAppAudioHost)
END_DEFINE_INTERFACES (FObject)
protected:
InterAppAudioHostApp ();
VST3Plugin* plugin {nullptr};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,149 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/HostApp.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "HostApp.h"
#import "AudioIO.h"
#import "PresetManager.h"
#import "SettingsViewController.h"
#import "VST3Plugin.h"
#import "base/source/updatehandler.h"
#import "pluginterfaces/gui/iplugview.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
InterAppAudioHostApp* InterAppAudioHostApp::instance ()
{
static InterAppAudioHostApp gInstance;
return &gInstance;
}
//-----------------------------------------------------------------------------
InterAppAudioHostApp::InterAppAudioHostApp () = default;
//-----------------------------------------------------------------------------
void InterAppAudioHostApp::setPlugin (VST3Plugin* plugin)
{
this->plugin = plugin;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getName (String128 name)
{
String str ("InterAppAudioHost");
str.copyTo (name, 0, 127);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getScreenSize (ViewRect* size, float* scale)
{
if (size)
{
UIScreen* screen = [UIScreen mainScreen];
CGSize s = [screen currentMode].size;
UIWindow* window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
if (window)
{
NSArray* subViews = [window subviews];
if ([subViews count] == 1)
{
s = [[subViews objectAtIndex:0] bounds].size;
}
}
size->left = 0;
size->top = 0;
size->right = s.width;
size->bottom = s.height;
if (scale)
{
*scale = screen.scale;
}
return kResultTrue;
}
return kInvalidArgument;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::connectedToHost ()
{
return AudioIO::instance ()->getInterAppAudioConnected () ? kResultTrue : kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::switchToHost ()
{
return AudioIO::instance ()->switchToHost () ? kResultTrue : kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::sendRemoteControlEvent (uint32 event)
{
return AudioIO::instance ()->sendRemoteControlEvent (
static_cast<AudioUnitRemoteControlEvent> (event)) ?
kResultTrue :
kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getHostIcon (void** icon)
{
if (icon)
{
UIImage* hostIcon = AudioIO::instance ()->getHostIcon ();
if (hostIcon)
{
CGImageRef cgImage = [hostIcon CGImage];
if (cgImage)
{
*icon = cgImage;
return kResultTrue;
}
}
return kNotImplemented;
}
return kInvalidArgument;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::scheduleEventFromUI (Event& event)
{
if (plugin)
{
return plugin->scheduleEventFromUI (event);
}
return kNotInitialized;
}
//-----------------------------------------------------------------------------
IInterAppAudioPresetManager* PLUGIN_API InterAppAudioHostApp::createPresetManager (const TUID& cid)
{
return plugin ? new PresetManager (plugin, cid) : nullptr;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::showSettingsView ()
{
showIOSettings ();
return kResultTrue;
}
}
}
}
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/MidiIO.h
// Created by : Steinberg, 09/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
#include "AudioIO.h"
#include <CoreMIDI/CoreMIDI.h>
#include <vector>
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
class MidiIO
{
public:
static MidiIO& instance ();
bool setEnabled (bool state);
bool isEnabled () const;
// MIDI Network is experimental, do not use yet
void setMidiNetworkEnabled (bool state);
bool isMidiNetworkEnabled () const;
void setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy);
MIDINetworkConnectionPolicy getMidiNetworkPolicy () const;
void addProcessor (IMidiProcessor* processor);
void removeProcessor (IMidiProcessor* processor);
//-----------------------------------------------------------------------------
private:
MidiIO ();
~MidiIO ();
void onInput (const MIDIPacketList* pktlist);
void onSourceAdded (MIDIObjectRef source);
void onSetupChanged ();
void disconnectSources ();
MIDIClientRef client {0};
MIDIPortRef inputPort {0};
MIDIEndpointRef destPort {0};
using MidiProcessors = std::vector<IMidiProcessor*>;
MidiProcessors midiProcessors;
using ConnectionList = std::vector<MIDIEndpointRef>;
ConnectionList connectedSources;
static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon);
static void notifyProc (const MIDINotification* message, void* refCon);
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,206 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/MidiIO.mm
// Created by : Steinberg, 09/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "MidiIO.h"
#import <CoreMIDI/MIDINetworkSession.h>
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
MidiIO& MidiIO::instance ()
{
static MidiIO gInstance;
return gInstance;
}
//-----------------------------------------------------------------------------
MidiIO::MidiIO () = default;
//-----------------------------------------------------------------------------
MidiIO::~MidiIO ()
{
setEnabled (false);
}
//-----------------------------------------------------------------------------
void MidiIO::addProcessor (IMidiProcessor* processor)
{
midiProcessors.push_back (processor);
}
//-----------------------------------------------------------------------------
void MidiIO::removeProcessor (IMidiProcessor* processor)
{
auto it = std::find (midiProcessors.begin (), midiProcessors.end (), processor);
if (it != midiProcessors.end ())
{
midiProcessors.erase (it);
}
}
//-----------------------------------------------------------------------------
bool MidiIO::isEnabled () const
{
return client != 0;
}
//-----------------------------------------------------------------------------
bool MidiIO::setEnabled (bool state)
{
if (state)
{
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:@"CFBundleDisplayName"];
if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this,
&destPort) != noErr))
{
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
return false;
}
}
else
{
if (client == 0)
return true;
disconnectSources ();
MIDIEndpointDispose (destPort);
destPort = 0;
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
}
return true;
}
//-----------------------------------------------------------------------------
void MidiIO::setMidiNetworkEnabled (bool state)
{
if (inputPort && isMidiNetworkEnabled () != state)
{
if (!state)
{
MIDIPortDisconnectSource (inputPort,
[MIDINetworkSession defaultSession].sourceEndpoint);
}
[MIDINetworkSession defaultSession].enabled = state;
if (state)
{
MIDIPortConnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint,
0);
}
}
}
//-----------------------------------------------------------------------------
bool MidiIO::isMidiNetworkEnabled () const
{
return [MIDINetworkSession defaultSession].isEnabled;
}
//-----------------------------------------------------------------------------
void MidiIO::setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy)
{
[MIDINetworkSession defaultSession].connectionPolicy = policy;
}
//-----------------------------------------------------------------------------
MIDINetworkConnectionPolicy MidiIO::getMidiNetworkPolicy () const
{
return [MIDINetworkSession defaultSession].connectionPolicy;
}
//-----------------------------------------------------------------------------
void MidiIO::onInput (const MIDIPacketList* pktlist)
{
const MIDIPacket* packet = &pktlist->packet[0];
for (UInt32 i = 0; i < pktlist->numPackets; i++)
{
for (auto processor : midiProcessors)
{
processor->onMIDIEvent (packet->data[0], packet->data[1], packet->data[2], 0, false);
}
packet = MIDIPacketNext (packet);
}
}
//-----------------------------------------------------------------------------
void MidiIO::onSourceAdded (MIDIObjectRef source)
{
connectedSources.push_back ((MIDIEndpointRef)source);
MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL);
}
//-----------------------------------------------------------------------------
void MidiIO::disconnectSources ()
{
for (auto source : connectedSources)
MIDIPortDisconnectSource (inputPort, source);
connectedSources.clear ();
}
//-----------------------------------------------------------------------------
void MidiIO::onSetupChanged ()
{
disconnectSources ();
ItemCount numSources = MIDIGetNumberOfSources ();
for (ItemCount i = 0; i < numSources; i++)
{
onSourceAdded (MIDIGetSource (i));
}
}
//-----------------------------------------------------------------------------
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 ();
}
}
}
}
} // namespaces
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad9_7" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PresetBrowserViewController">
<connections>
<outlet property="containerView" destination="klJ-ou-M84" id="oc3-yW-Wj1"/>
<outlet property="presetTableView" destination="7ve-UC-DYv" id="DY3-Yy-Nem"/>
<outlet property="view" destination="2" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="klJ-ou-M84">
<rect key="frame" x="163" y="30" width="698" height="708"/>
<subviews>
<tableView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="7ve-UC-DYv">
<rect key="frame" x="20" y="20" width="658" height="630"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="-1" id="FIu-tQ-zaK"/>
<outlet property="delegate" destination="-1" id="Wmb-vA-YdQ"/>
</connections>
</tableView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IWt-40-Jgp">
<rect key="frame" x="20" y="668" width="30" height="30"/>
<state key="normal" title="Edit">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="toggleEditMode:" destination="-1" eventType="touchUpInside" id="uii-wI-Qvl"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="z3K-EZ-Ed5">
<rect key="frame" x="639" y="668" width="39" height="30"/>
<state key="normal" title="Close">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="cancel:" destination="-1" eventType="touchUpInside" id="BvF-YD-Tk0"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="7ve-UC-DYv" firstAttribute="top" secondItem="klJ-ou-M84" secondAttribute="top" constant="20" id="3Pq-2h-vKi"/>
<constraint firstAttribute="bottom" secondItem="z3K-EZ-Ed5" secondAttribute="bottom" constant="10" id="9ML-qG-4d0"/>
<constraint firstAttribute="trailing" secondItem="7ve-UC-DYv" secondAttribute="trailing" constant="20" id="Ioq-nG-kzq"/>
<constraint firstItem="IWt-40-Jgp" firstAttribute="leading" secondItem="klJ-ou-M84" secondAttribute="leading" constant="20" id="ZJq-DX-wOM"/>
<constraint firstAttribute="trailing" secondItem="z3K-EZ-Ed5" secondAttribute="trailing" constant="20" id="ag2-M7-lCo"/>
<constraint firstAttribute="bottom" secondItem="7ve-UC-DYv" secondAttribute="bottom" constant="58" id="cj6-1q-Q3I"/>
<constraint firstAttribute="bottom" secondItem="IWt-40-Jgp" secondAttribute="bottom" constant="10" id="kmf-ho-kIC"/>
<constraint firstItem="7ve-UC-DYv" firstAttribute="leading" secondItem="klJ-ou-M84" secondAttribute="leading" constant="20" id="xiJ-zB-UUa"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="klJ-ou-M84" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="163" id="5LY-UL-APH"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="top" secondItem="2" secondAttribute="top" constant="30" id="G9o-k3-fOs"/>
<constraint firstAttribute="bottom" secondItem="klJ-ou-M84" secondAttribute="bottom" constant="30" id="YXO-2R-3RQ"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="centerX" secondItem="2" secondAttribute="centerX" id="ZKb-qU-rmk"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="centerY" secondItem="2" secondAttribute="centerY" id="fR8-kU-uaI"/>
<constraint firstAttribute="trailing" secondItem="klJ-ou-M84" secondAttribute="trailing" constant="163" id="uzi-AN-g6z"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
</view>
</objects>
</document>
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <functional>
//-----------------------------------------------------------------------------
@interface PresetBrowserViewController
: UIViewController <UITableViewDataSource, UITableViewDelegate>
//-----------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)callback;
- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets;
@end
#endif // __OBJC__
/// \endcond
@@ -0,0 +1,251 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "PresetBrowserViewController.h"
#import "pluginterfaces/base/funknown.h"
//------------------------------------------------------------------------
@interface PresetBrowserViewController ()
//------------------------------------------------------------------------
{
IBOutlet UITableView* presetTableView;
IBOutlet UIView* containerView;
std::function<void (const char* presetPath)> callback;
Steinberg::FUID uid;
}
@property (strong) NSArray* factoryPresets;
@property (strong) NSArray* userPresets;
@property (strong) NSArray* displayPresets;
@property (assign) BOOL editMode;
@end
//------------------------------------------------------------------------
@implementation PresetBrowserViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)_callback
{
self = [super initWithNibName:@"PresetBrowserView" bundle:nil];
if (self)
{
callback = _callback;
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:self animated:YES completion:^{}];
}
return self;
}
//------------------------------------------------------------------------
- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets
{
self.factoryPresets = factoryPresets;
self.userPresets = userPresets;
[self updatePresetArray];
dispatch_async (dispatch_get_main_queue (), ^{ [presetTableView reloadData]; });
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
}
//------------------------------------------------------------------------
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//------------------------------------------------------------------------
- (void)updatePresetArray
{
if (self.userPresets)
{
self.displayPresets = [[self.factoryPresets arrayByAddingObjectsFromArray:self.userPresets]
sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
}
else
{
self.displayPresets = self.factoryPresets;
}
}
//------------------------------------------------------------------------
- (void)removeSelf
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
NSURL* url = [self.displayPresets objectAtIndex:indexPath.row];
if (url)
{
callback ([[url path] UTF8String]);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (IBAction)toggleEditMode:(id)sender
{
self.editMode = !self.editMode;
if (self.editMode)
{
NSMutableArray* indexPaths = [NSMutableArray new];
for (NSURL* url in self.factoryPresets)
{
NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url];
[indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[presetTableView deleteRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationFade];
}
else
{
[self updatePresetArray];
NSMutableArray* indexPaths = [NSMutableArray new];
for (NSURL* url in self.factoryPresets)
{
NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url];
[indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[presetTableView insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationFade];
}
[presetTableView setEditing:self.editMode animated:YES];
}
//------------------------------------------------------------------------
- (IBAction)cancel:(id)sender
{
if (callback)
{
callback (nullptr);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.editMode)
{
return [self.userPresets count];
}
return [self.displayPresets count];
}
//------------------------------------------------------------------------
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"PresetBrowserCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:@"PresetBrowserCell"];
}
cell.backgroundColor = [UIColor clearColor];
NSURL* presetUrl = nil;
if (self.editMode)
{
presetUrl = [self.userPresets objectAtIndex:indexPath.row];
cell.detailTextLabel.text = @"User";
}
else
{
presetUrl = [self.displayPresets objectAtIndex:indexPath.row];
if ([self.factoryPresets indexOfObject:presetUrl] == NSNotFound)
{
cell.detailTextLabel.text = @"User";
}
else
{
cell.detailTextLabel.text = @"Factory";
}
}
cell.textLabel.text = [[presetUrl lastPathComponent] stringByDeletingPathExtension];
return cell;
}
//------------------------------------------------------------------------
- (BOOL)tableView:(UITableView*)tableView canEditRowAtIndexPath:(NSIndexPath*)indexPath
{
if (self.editMode)
{
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (void)tableView:(UITableView*)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath*)indexPath
{
NSURL* presetUrl = [self.userPresets objectAtIndex:indexPath.row];
if (presetUrl)
{
NSFileManager* fs = [NSFileManager defaultManager];
NSError* error = nil;
if ([fs removeItemAtURL:presetUrl error:&error] == NO)
{
auto alertController =
[UIAlertController alertControllerWithTitle:[error localizedDescription]
message:[error localizedRecoverySuggestion]
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
}
else
{
NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.userPresets];
[newArray removeObject:presetUrl];
self.userPresets = newArray;
[presetTableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
@@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetManager.h
// Created by : Steinberg, 10/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
#include "VST3Plugin.h"
#include "base/source/fstring.h"
#include "pluginterfaces/vst/ivstinterappaudio.h"
#if __OBJC__
@class NSArray, PresetBrowserViewController, PresetSaveViewController;
#else
struct NSArray;
struct PresetBrowserViewController;
struct PresetSaveViewController;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class PresetManager : public FObject, public IInterAppAudioPresetManager
{
public:
PresetManager (VST3Plugin* plugin, const TUID& cid);
tresult PLUGIN_API runLoadPresetBrowser () override;
tresult PLUGIN_API runSavePresetBrowser () override;
tresult PLUGIN_API loadNextPreset () override;
tresult PLUGIN_API loadPreviousPreset () override;
DEFINE_INTERFACES
DEF_INTERFACE (IInterAppAudioPresetManager)
END_DEFINE_INTERFACES (FObject)
REFCOUNT_METHODS (FObject)
private:
enum PresetPathType
{
kFactory,
kUser
};
NSArray* getPresetPaths (PresetPathType type);
tresult loadPreset (bool next);
tresult loadPreset (const char* path);
void savePreset (const char* path);
VST3Plugin* plugin;
PresetBrowserViewController* visiblePresetBrowserViewController;
PresetSaveViewController* visibleSavePresetViewController;
FUID cid;
String lastPreset;
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,281 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetManager.mm
// Created by : Steinberg, 10/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "PresetManager.h"
#import "PresetBrowserViewController.h"
#import "PresetSaveViewController.h"
#import "public.sdk/source/vst/vstpresetfile.h"
#import "pluginterfaces/vst/ivstattributes.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
class PresetStream : public ReadOnlyBStream, public IStreamAttributes
{
public:
PresetStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize,
const char* utf8Path)
: ReadOnlyBStream (sourceStream, sourceOffset, sectionSize), fileName (utf8Path)
{
fileName.toWideString (kCP_Utf8);
}
virtual tresult PLUGIN_API getFileName (String128 name) override
{
if (fileName.length () > 0)
{
fileName.copyTo (name, 0, 128);
return kResultTrue;
}
return kResultFalse;
}
virtual IAttributeList* PLUGIN_API getAttributes () override { return nullptr; }
DEF_INTERFACES_1 (IStreamAttributes, ReadOnlyBStream)
REFCOUNT_METHODS (ReadOnlyBStream)
protected:
String fileName;
};
//-----------------------------------------------------------------------------
PresetManager::PresetManager (VST3Plugin* plugin, const TUID& cid)
: plugin (plugin)
, visiblePresetBrowserViewController (nil)
, visibleSavePresetViewController (nil)
, cid (cid)
{
id obj = [[NSUserDefaults standardUserDefaults] objectForKey:@"PresetManager|lastPreset"];
if (obj && [obj isKindOfClass:[NSString class]])
{
lastPreset = [obj UTF8String];
}
}
//-----------------------------------------------------------------------------
NSArray* PresetManager::getPresetPaths (PresetPathType type)
{
if (type == kFactory)
{
return [[NSBundle mainBundle] URLsForResourcesWithExtension:@"vstpreset"
subdirectory:@"Presets"];
}
NSFileManager* fs = [NSFileManager defaultManager];
NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:Nil
create:YES
error:NULL];
if (documentsUrl)
{
NSMutableArray* userUrls = [NSMutableArray new];
NSDirectoryEnumerator* enumerator =
[fs enumeratorAtURL:documentsUrl
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
errorHandler:nil];
for (NSURL* url in enumerator.allObjects)
{
if ([[url pathExtension] isEqualToString:@"vstpreset"])
{
[userUrls addObject:url];
}
}
return [userUrls sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
}
return nil;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::runLoadPresetBrowser ()
{
if (visiblePresetBrowserViewController != nil)
return kResultFalse;
addRef ();
visiblePresetBrowserViewController =
[[PresetBrowserViewController alloc] initWithCallback:[this] (const char* path) {
loadPreset (path);
visiblePresetBrowserViewController = nil;
release ();
}];
addRef ();
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (visiblePresetBrowserViewController)
{
[visiblePresetBrowserViewController setFactoryPresets:getPresetPaths (kFactory)
userPresets:getPresetPaths (kUser)];
}
release ();
});
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::runSavePresetBrowser ()
{
if (visibleSavePresetViewController != nil)
return kResultFalse;
addRef ();
visibleSavePresetViewController =
[[PresetSaveViewController alloc] initWithCallback:[this] (const char* path) {
savePreset (path);
visibleSavePresetViewController = nil;
release ();
}];
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::loadNextPreset ()
{
return loadPreset (true);
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::loadPreviousPreset ()
{
return loadPreset (false);
}
//-----------------------------------------------------------------------------
tresult PresetManager::loadPreset (bool next)
{
NSArray* presets =
[[getPresetPaths (kFactory) arrayByAddingObjectsFromArray:getPresetPaths (kUser)]
sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
__block NSUInteger index = NSNotFound;
if (lastPreset.isEmpty () == false)
{
NSURL* lastUrl =
[[NSURL fileURLWithPath:[NSString stringWithUTF8String:lastPreset]] fileReferenceURL];
if (lastUrl)
{
[presets enumerateObjectsUsingBlock:^(NSURL* obj, NSUInteger idx, BOOL* stop) {
if ([[obj fileReferenceURL] isEqual:lastUrl])
{
index = idx;
*stop = YES;
}
}];
}
}
if (index == NSNotFound)
{
if (next)
index = [presets count] - 1;
else
index = 1;
}
if (index != NSNotFound)
{
if (next)
{
if (index >= [presets count] - 1)
index = 0;
else
index++;
}
else
{
if (index == 0)
index = [presets count] - 1;
else
index--;
}
return loadPreset ([[[presets objectAtIndex:index] path] UTF8String]);
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PresetManager::loadPreset (const char* path)
{
if (path)
{
IPtr<IBStream> stream = owned (FileStream::open (path, "r"));
if (stream)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:path]
forKey:@"PresetManager|lastPreset"];
lastPreset = path;
auto component = U::cast<IComponent> (plugin->getAudioProcessor ());
IEditController* controller = plugin->getEditController ();
if (component)
{
PresetFile pf (stream);
if (!pf.readChunkList ())
return kResultFalse;
if (pf.getClassID () != cid)
return kResultFalse;
const PresetFile::Entry* e = pf.getEntry (kComponentState);
if (e == nullptr)
return kResultFalse;
auto filename = strrchr (path, '/');
if (filename)
filename++;
IPtr<PresetStream> readOnlyBStream =
owned (new PresetStream (stream, e->offset, e->size, filename));
tresult result = component->setState (readOnlyBStream);
if ((result == kResultTrue || result == kNotImplemented) && controller)
{
readOnlyBStream->seek (0, IBStream::kIBSeekSet);
controller->setComponentState (readOnlyBStream);
if (pf.contains (kControllerState))
{
e = pf.getEntry (kControllerState);
if (e)
{
readOnlyBStream =
owned (new PresetStream (stream, e->offset, e->size, filename));
controller->setState (readOnlyBStream);
}
}
}
return result;
}
}
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
void PresetManager::savePreset (const char* path)
{
IBStream* stream = FileStream::open (path, "w");
if (stream)
{
auto component = U::cast<IComponent> (plugin->getAudioProcessor ());
IEditController* controller = plugin->getEditController ();
if (component)
{
PresetFile::savePreset (stream, cid, component, controller);
}
stream->release ();
loadPreset (path);
}
}
}
}
} // namespaces
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad10_5" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PresetSaveViewController">
<connections>
<outlet property="containerView" destination="SIT-sP-q5k" id="Pam-4d-cjt"/>
<outlet property="presetName" destination="4mQ-Y5-WZh" id="MJT-bn-9QR"/>
<outlet property="view" destination="2" id="0Me-yB-Sts"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SIT-sP-q5k">
<rect key="frame" x="256" y="192" width="512" height="104"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Preset Name :" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EGr-Dl-Wf3">
<rect key="frame" x="20" y="20" width="108" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="4mQ-Y5-WZh">
<rect key="frame" x="147" y="16" width="345" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="-1" id="LIF-nc-eQa"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="v51-mb-0Tu">
<rect key="frame" x="455" y="68" width="37" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Save"/>
<connections>
<action selector="save:" destination="-1" eventType="touchUpInside" id="J1n-Ix-DQA"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SBt-7v-tI9">
<rect key="frame" x="20" y="68" width="53" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Cancel">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="cancel:" destination="-1" eventType="touchUpInside" id="ibs-yW-oi5"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="4mQ-Y5-WZh" secondAttribute="trailing" constant="20" id="02g-de-fEz"/>
<constraint firstItem="SBt-7v-tI9" firstAttribute="leading" secondItem="SIT-sP-q5k" secondAttribute="leading" constant="20" id="9Qx-G1-uxi"/>
<constraint firstAttribute="bottom" secondItem="v51-mb-0Tu" secondAttribute="bottom" constant="3" id="FL7-Pl-p0S"/>
<constraint firstItem="4mQ-Y5-WZh" firstAttribute="top" secondItem="SIT-sP-q5k" secondAttribute="top" constant="16" id="M1k-Wm-yVW"/>
<constraint firstItem="4mQ-Y5-WZh" firstAttribute="leading" secondItem="EGr-Dl-Wf3" secondAttribute="trailing" constant="19" id="Mrh-oC-pT4"/>
<constraint firstItem="EGr-Dl-Wf3" firstAttribute="leading" secondItem="SIT-sP-q5k" secondAttribute="leading" constant="20" id="dMD-jj-Yya"/>
<constraint firstItem="EGr-Dl-Wf3" firstAttribute="top" secondItem="SIT-sP-q5k" secondAttribute="top" constant="20" id="dbO-Rh-IOx"/>
<constraint firstAttribute="bottom" secondItem="SBt-7v-tI9" secondAttribute="bottom" constant="3" id="lZC-gE-21j"/>
<constraint firstAttribute="height" constant="104" id="wzX-0f-wG5"/>
<constraint firstAttribute="trailing" secondItem="v51-mb-0Tu" secondAttribute="trailing" constant="20" id="x9j-uT-byM"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="SIT-sP-q5k" secondAttribute="trailing" constant="256" id="QRq-bk-Kbd"/>
<constraint firstItem="SIT-sP-q5k" firstAttribute="top" secondItem="2" secondAttribute="top" constant="192" id="uF1-Qs-yD6"/>
<constraint firstItem="SIT-sP-q5k" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="256" id="yDr-cE-KWA"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
</objects>
</document>
@@ -0,0 +1,35 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <functional>
@interface PresetSaveViewController : UIViewController <UIAlertViewDelegate, UITextFieldDelegate>
- (id)initWithCallback:(std::function<void (const char* presetPath)>)callback;
@end
#endif //__OBJC__
/// \endcond
@@ -0,0 +1,161 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "PresetSaveViewController.h"
#import "pluginterfaces/base/funknown.h"
//------------------------------------------------------------------------
@interface PresetSaveViewController ()
//------------------------------------------------------------------------
{
IBOutlet UIView* containerView;
IBOutlet UITextField* presetName;
std::function<void (const char* presetPath)> callback;
Steinberg::FUID uid;
}
@end
//------------------------------------------------------------------------
@implementation PresetSaveViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)_callback
{
self = [super initWithNibName:@"PresetSaveView" bundle:nil];
if (self)
{
callback = _callback;
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:self
animated:YES
completion:^{ [self showKeyboard]; }];
}
return self;
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
}
//------------------------------------------------------------------------
- (void)showKeyboard
{
[presetName becomeFirstResponder];
}
//------------------------------------------------------------------------
- (void)removeSelf
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (NSURL*)presetURL
{
NSFileManager* fs = [NSFileManager defaultManager];
NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:Nil
create:YES
error:NULL];
if (documentsUrl)
{
NSURL* presetPath = [[documentsUrl URLByAppendingPathComponent:presetName.text]
URLByAppendingPathExtension:@"vstpreset"];
return presetPath;
}
return nil;
}
//------------------------------------------------------------------------
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
if ([textField.text length] > 0)
{
[self save:textField];
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (IBAction)save:(id)sender
{
if (callback)
{
NSURL* presetPath = [self presetURL];
NSFileManager* fs = [NSFileManager defaultManager];
if ([fs fileExistsAtPath:[presetPath path]])
{
// alert for overwrite
auto alertController = [UIAlertController
alertControllerWithTitle:NSLocalizedString (
@"A Preset with this name already exists",
"Alert title")
message:NSLocalizedString (@"Save it anyway ?", "Alert message")
preferredStyle:UIAlertControllerStyleAlert];
[alertController
addAction:[UIAlertAction
actionWithTitle:NSLocalizedString (@"Save", "Alert Save Button")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* _Nonnull action) {
callback ([[[self presetURL] path] UTF8String]);
[self removeSelf];
}]];
[alertController
addAction:[UIAlertAction
actionWithTitle:NSLocalizedString (@"Cancel", "Alert Cancel Button")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction* _Nonnull action) {}]];
[self presentViewController:alertController animated:YES completion:nil];
return;
}
callback ([[presetPath path] UTF8String]);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (IBAction)cancel:(id)sender
{
if (callback)
{
callback (nullptr);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad9_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SettingsViewController">
<connections>
<outlet property="containerView" destination="RNQ-ag-wXs" id="Snq-jH-mGX"/>
<outlet property="midiOnSwitch" destination="RUK-3J-c68" id="fsn-qn-B1b"/>
<outlet property="tempoView" destination="7Wa-1f-ZEh" id="aAo-kk-UXa"/>
<outlet property="view" destination="2" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RNQ-ag-wXs">
<rect key="frame" x="272" y="266" width="481" height="236"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RUK-3J-c68">
<rect key="frame" x="165" y="92" width="51" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="enableMidi:" destination="-1" eventType="valueChanged" id="W1y-Cl-XZT"/>
</connections>
</switch>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Enable MIDI Input" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uh8-Ad-fHW">
<rect key="frame" x="20" y="97" width="137" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gsH-Se-9I1">
<rect key="frame" x="220" y="186" width="40" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Close">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="close:" destination="-1" eventType="touchUpInside" id="8fR-1E-VU7"/>
</connections>
</button>
<pickerView contentMode="scaleAspectFit" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Wa-1f-ZEh">
<rect key="frame" x="329" y="0.0" width="86" height="216"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<outlet property="dataSource" destination="-1" id="kTa-mw-jgX"/>
<outlet property="delegate" destination="-1" id="afl-U5-l43"/>
</connections>
</pickerView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="BPM" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ccr-8n-eGh">
<rect key="frame" x="423" y="97" width="38" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tempo :" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jiY-aT-q6h">
<rect key="frame" x="222" y="97" width="97" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="481" id="CNb-19-uMh"/>
<constraint firstAttribute="height" constant="236" id="Gag-Ic-lk4"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="RNQ-ag-wXs" firstAttribute="centerX" secondItem="2" secondAttribute="centerX" id="d7Y-kL-wHV"/>
<constraint firstItem="RNQ-ag-wXs" firstAttribute="centerY" secondItem="2" secondAttribute="centerY" id="rA4-Fa-esq"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="297" y="-102"/>
</view>
</objects>
</document>
@@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
#ifdef __OBJC__
#import <UIKit/UIKit.h>
@interface SettingsViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
@end
#endif // __OBJC__
extern void showIOSettings ();
@@ -0,0 +1,126 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "SettingsViewController.h"
#import "AudioIO.h"
#import "MidiIO.h"
#import <CoreMIDI/MIDINetworkSession.h>
using namespace Steinberg::Vst::InterAppAudio;
static const NSUInteger kMinTempo = 30;
//------------------------------------------------------------------------
@interface SettingsViewController ()
//------------------------------------------------------------------------
{
IBOutlet UIView* containerView;
IBOutlet UISwitch* midiOnSwitch;
IBOutlet UIPickerView* tempoView;
}
@end
//------------------------------------------------------------------------
@implementation SettingsViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)init
{
self = [super initWithNibName:@"SettingsView" bundle:nil];
if (self)
{
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
}
return self;
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
midiOnSwitch.on = MidiIO::instance ().isEnabled ();
Float64 tempo = AudioIO::instance ()->getStaticFallbackTempo ();
[tempoView selectRow:tempo - kMinTempo inComponent:0 animated:YES];
}
//------------------------------------------------------------------------
- (IBAction)enableMidi:(id)sender
{
BOOL state = midiOnSwitch.on;
MidiIO::instance ().setEnabled (state);
}
//------------------------------------------------------------------------
- (IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (void)pickerView:(UIPickerView*)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
AudioIO::instance ()->setStaticFallbackTempo (row + kMinTempo);
}
//------------------------------------------------------------------------
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
return 1;
}
//------------------------------------------------------------------------
- (NSInteger)pickerView:(UIPickerView*)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 301 - kMinTempo;
}
//------------------------------------------------------------------------
- (NSString*)pickerView:(UIPickerView*)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
return [@(row + kMinTempo) stringValue];
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
//------------------------------------------------------------------------
void showIOSettings ()
{
SettingsViewController* controller = [[SettingsViewController alloc] init];
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:controller animated:YES completion:^{}];
}
@@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Editor.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#import "base/source/fobject.h"
#import "pluginterfaces/gui/iplugview.h"
#import <UIKit/UIKit.h>
namespace Steinberg {
namespace Vst {
class IEditController;
namespace InterAppAudio {
//------------------------------------------------------------------------
class VST3Editor : public FObject, public IPlugFrame
{
public:
//------------------------------------------------------------------------
VST3Editor ();
virtual ~VST3Editor ();
bool init (const CGRect& frame);
bool attach (IEditController* editController);
UIViewController* getViewController () const { return viewController; }
OBJ_METHODS (VST3Editor, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IPlugFrame)
END_DEFINE_INTERFACES (FObject)
protected:
// IPlugFrame
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) override;
IPlugView* plugView {nullptr};
UIViewController* viewController {nullptr};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Editor.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "VST3Editor.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
//------------------------------------------------------------------------
@interface VST3EditorViewController : UIViewController
//------------------------------------------------------------------------
@end
//------------------------------------------------------------------------
@implementation VST3EditorViewController
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
@end
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
VST3Editor::VST3Editor () = default;
//------------------------------------------------------------------------
VST3Editor::~VST3Editor ()
{
if (plugView)
{
plugView->release ();
}
}
//------------------------------------------------------------------------
bool VST3Editor::init (const CGRect& frame)
{
viewController = [VST3EditorViewController new];
viewController.view = [[UIView alloc] initWithFrame:frame];
return true;
}
//------------------------------------------------------------------------
bool VST3Editor::attach (IEditController* editController)
{
auto ec2 = U::cast<IEditController2> (editController);
if (ec2)
{
ec2->setKnobMode (kLinearMode);
}
plugView = editController->createView (ViewType::kEditor);
if (plugView)
{
if (plugView->isPlatformTypeSupported (kPlatformTypeUIView) == kResultTrue)
{
plugView->setFrame (this);
if (plugView->attached ((__bridge void*)viewController.view, kPlatformTypeUIView) ==
kResultTrue)
{
return true;
}
}
plugView->release ();
plugView = nullptr;
}
return false;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Editor::resizeView (IPlugView* view, ViewRect* newSize)
{
if (newSize && plugView && plugView == view)
{
if (view->onSize (newSize) == kResultTrue)
return kResultTrue;
return kResultFalse;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
} // InterAppAudio
} // Vst
} // Steinberg
@@ -0,0 +1,133 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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
/// \cond ignore
#import "AudioIO.h"
#import "public.sdk/source/vst/hosting/eventlist.h"
#import "public.sdk/source/vst/hosting/parameterchanges.h"
#import "public.sdk/source/vst/hosting/processdata.h"
#import "public.sdk/source/vst/utility/ringbuffer.h"
#import "base/source/fobject.h"
#import "base/source/timer.h"
#import "pluginterfaces/vst/ivstaudioprocessor.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
#import "pluginterfaces/vst/ivstprocesscontext.h"
#import <atomic>
#import <map>
#ifndef __OBJC__
struct NSData;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
static const int32 kMaxUIEvents = 100;
//------------------------------------------------------------------------
class VST3Plugin : public FObject,
public IComponentHandler,
public IAudioIOProcessor,
public ITimerCallback
{
public:
//------------------------------------------------------------------------
VST3Plugin ();
virtual ~VST3Plugin ();
bool init ();
IEditController* getEditController () const { return editController; }
IAudioProcessor* getAudioProcessor () const { return processor; }
tresult scheduleEventFromUI (Event& event);
NSData* getProcessorState ();
bool setProcessorState (NSData* data);
NSData* getControllerState ();
bool setControllerState (NSData* data);
OBJ_METHODS (VST3Plugin, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IComponentHandler)
END_DEFINE_INTERFACES (FObject)
protected:
typedef std::map<uint32, uint32> NoteIDPitchMap;
typedef uint32 ChannelAndCtrlNumber;
typedef std::map<ChannelAndCtrlNumber, ParamID> MIDIControllerToParamIDMap;
void createProcessorAndController ();
void updateProcessContext (AudioIO* audioIO);
MIDIControllerToParamIDMap createMIDIControllerToParamIDMap ();
// IComponentHandler
tresult PLUGIN_API beginEdit (ParamID id) override;
tresult PLUGIN_API performEdit (ParamID id, ParamValue valueNormalized) override;
tresult PLUGIN_API endEdit (ParamID id) override;
tresult PLUGIN_API restartComponent (int32 flags) override;
// IAudioIOProcessor
void willStartAudio (AudioIO* audioIO) override;
void didStopAudio (AudioIO* audioIO) override;
void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread) override;
void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) override;
// ITimerCallback
void onTimer (Timer* timer) override;
IAudioProcessor* processor {nullptr};
IEditController* editController {nullptr};
Timer* timer {nullptr};
HostProcessData processData;
ProcessContext processContext;
ParameterChangeTransfer inputParamChangeTransfer;
ParameterChangeTransfer outputParamChangeTransfer;
ParameterChanges inputParamChanges;
ParameterChanges outputParamChanges;
EventList inputEvents;
NoteIDPitchMap noteIDPitchMap;
std::atomic<int32> lastNodeID {0};
bool processing {false};
MIDIControllerToParamIDMap midiControllerToParamIDMap;
OneReaderOneWriter::RingBuffer<Event> uiScheduledEvents;
static ChannelAndCtrlNumber channelAndCtrlNumber (uint16 channel, CtrlNumber ctrler)
{
return (channel << 16) + ctrler;
}
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,598 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "VST3Plugin.h"
#import "HostApp.h"
#import "public.sdk/source/vst/auwrapper/NSDataIBStream.h"
#import "public.sdk/source/vst/hosting/hostclasses.h"
#import "base/source/updatehandler.h"
#import "pluginterfaces/base/ipluginbase.h"
#import "pluginterfaces/vst/ivstinterappaudio.h"
#import "pluginterfaces/vst/ivstmessage.h"
#import "pluginterfaces/vst/ivstmidicontrollers.h"
#import <libkern/OSAtomic.h>
//------------------------------------------------------------------------
extern "C" {
bool bundleEntry (CFBundleRef);
bool bundleExit (void);
}
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
__attribute__ ((constructor)) static void InitUpdateHandler ()
{
UpdateHandler::instance ();
}
//------------------------------------------------------------------------
VST3Plugin::VST3Plugin ()
{
processData.processContext = &processContext;
processData.inputParameterChanges = &inputParamChanges;
processData.outputParameterChanges = &outputParamChanges;
processData.inputEvents = &inputEvents;
}
//------------------------------------------------------------------------
VST3Plugin::~VST3Plugin ()
{
}
//------------------------------------------------------------------------
bool VST3Plugin::init ()
{
if (processor == nullptr && editController == nullptr)
{
::bundleEntry (CFBundleGetMainBundle ());
createProcessorAndController ();
}
return processor && editController;
}
//------------------------------------------------------------------------
void VST3Plugin::createProcessorAndController ()
{
Steinberg::IPluginFactory* factory = GetPluginFactory ();
if (factory == nullptr)
return;
IComponent* component = nullptr;
PClassInfo classInfo;
int32 classCount = factory->countClasses ();
for (int32 i = 0; i < classCount; i++)
{
if (factory->getClassInfo (i, &classInfo) != kResultTrue)
return;
if (strcmp (classInfo.category, kVstAudioEffectClass) == 0)
{
if (factory->createInstance (classInfo.cid, IComponent::iid, (void**)&component) !=
kResultTrue)
{
return;
}
break;
}
}
if (component)
{
if (component->initialize (InterAppAudioHostApp::instance ()->unknownCast ()) !=
kResultTrue)
{
component->release ();
return;
}
if (component->queryInterface (IEditController::iid, (void**)&editController) !=
kResultTrue)
{
TUID controllerCID {};
if (component->getControllerClassId (controllerCID) == kResultTrue)
{
if (factory->createInstance (controllerCID, IEditController::iid,
(void**)&editController) != kResultTrue)
return;
editController->setComponentHandler (this);
if (editController->initialize (
InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue)
{
component->release ();
editController->release ();
editController = nullptr;
return;
}
auto compConnection = U::cast<IConnectionPoint> (component);
auto ctrlerConnection = U::cast<IConnectionPoint> (editController);
if (compConnection && ctrlerConnection)
{
compConnection->connect (ctrlerConnection);
ctrlerConnection->connect (compConnection);
}
}
else
{
component->release ();
return;
}
}
component->queryInterface (IAudioProcessor::iid, (void**)&processor);
if (processor == nullptr)
{
if (editController)
{
editController->release ();
editController = nullptr;
}
}
else
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
if (component->getState (&state) == kResultTrue)
{
state.seek (0, IBStream::kIBSeekSet);
editController->setComponentState (&state);
}
int32 paramCount = editController->getParameterCount ();
inputParamChanges.setMaxParameters (paramCount);
inputParamChangeTransfer.setMaxParameters (paramCount);
outputParamChanges.setMaxParameters (paramCount);
outputParamChangeTransfer.setMaxParameters (paramCount);
midiControllerToParamIDMap = createMIDIControllerToParamIDMap ();
uiScheduledEvents.resize (kMaxUIEvents);
}
component->release ();
}
}
//------------------------------------------------------------------------
VST3Plugin::MIDIControllerToParamIDMap VST3Plugin::createMIDIControllerToParamIDMap ()
{
MIDIControllerToParamIDMap newMap;
auto midiMapping = U::cast<IMidiMapping> (editController);
if (midiMapping)
{
uint16 channelCount = 0;
auto component = U::cast<IComponent> (processor);
if (component)
{
int32 busCount = component->getBusCount (kEvent, kInput);
if (busCount > 0)
{
BusInfo busInfo;
if (component->getBusInfo (kEvent, kInput, 0, busInfo) == kResultTrue)
{
channelCount = busInfo.channelCount;
}
}
}
ParamID paramID;
for (int32 channel = 0; channel < channelCount; channel++)
{
for (CtrlNumber ctrler = 0; ctrler < kCountCtrlNumber; ctrler++)
{
if (midiMapping->getMidiControllerAssignment (0, channel, ctrler, paramID) ==
kResultTrue)
{
newMap.insert (
std::make_pair (channelAndCtrlNumber (channel, ctrler), paramID));
}
}
}
}
return newMap;
}
//------------------------------------------------------------------------
tresult VST3Plugin::scheduleEventFromUI (Event& event)
{
if (event.type == Event::kNoteOnEvent)
event.noteOn.noteId = lastNodeID++;
return uiScheduledEvents.push (event) ? kResultTrue : kResultFalse;
}
//------------------------------------------------------------------------
NSData* VST3Plugin::getProcessorState ()
{
if (processor)
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
auto comp = U::cast<IComponent> (processor);
if (comp->getState (&state) == kResultTrue)
{
return data;
}
}
return nil;
}
//------------------------------------------------------------------------
bool VST3Plugin::setProcessorState (NSData* data)
{
if (editController && processor)
{
NSDataIBStream stream (data);
auto comp = U::cast<IComponent> (processor);
if (comp->setState (&stream) == kResultTrue)
{
stream.seek (0, IBStream::kIBSeekSet);
editController->setComponentState (&stream);
return true;
}
}
return false;
}
//------------------------------------------------------------------------
NSData* VST3Plugin::getControllerState ()
{
if (editController)
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
if (editController->getState (&state) == kResultTrue)
{
return data;
}
}
return nil;
}
//------------------------------------------------------------------------
bool VST3Plugin::setControllerState (NSData* data)
{
if (editController)
{
NSDataIBStream stream (data);
if (editController->setState (&stream) == kResultTrue)
{
return true;
}
}
return false;
}
//------------------------------------------------------------------------
void VST3Plugin::willStartAudio (AudioIO* audioIO)
{
noteIDPitchMap.clear ();
lastNodeID.store (0);
ProcessSetup setup;
setup.processMode = kRealtime;
setup.symbolicSampleSize = kSample32;
setup.maxSamplesPerBlock = 4096; // TODO:
setup.sampleRate = audioIO->getSampleRate ();
processor->setupProcessing (setup);
SpeakerArrangement inputs[1];
SpeakerArrangement outputs[1];
inputs[0] = SpeakerArr::kStereo;
outputs[0] = SpeakerArr::kStereo;
processor->setBusArrangements (inputs, 1, outputs, 1);
auto comp = U::cast<IComponent> (processor);
comp->setActive (true);
processData.prepare (*comp, setup.maxSamplesPerBlock, setup.symbolicSampleSize);
auto iaaConnectionNotification = U::cast<IInterAppAudioConnectionNotification> (editController);
if (iaaConnectionNotification)
{
iaaConnectionNotification->onInterAppAudioConnectionStateChange (
audioIO->getInterAppAudioConnected () ? true : false);
}
timer = Timer::create (this, 16);
}
//------------------------------------------------------------------------
void VST3Plugin::didStopAudio (AudioIO* audioIO)
{
processor->setProcessing (false);
processing = false;
auto comp = U::cast<IComponent> (processor);
comp->setActive (false);
timer->release ();
timer = nullptr;
}
//------------------------------------------------------------------------
void VST3Plugin::onMIDIEvent (UInt32 inStatus, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread)
{
Event e = {};
e.flags = Event::kIsLive;
uint16 status = inStatus & 0xF0;
uint16 channel = inStatus & 0x0F;
if (status == 0x90 && data2 != 0) // note on
{
auto noteID = noteIDPitchMap.find ((channel << 8) + data1);
if (noteID != noteIDPitchMap.end ())
{
// for now, we just turn off the old note on
Event e2 = {};
e2.type = Event::kNoteOffEvent;
e2.noteOff.noteId = noteID->second;
e2.noteOff.channel = channel;
e2.noteOff.pitch = data1;
e2.noteOff.velocity = (float)data2 / 128.f;
e2.sampleOffset = 0;
inputEvents.addEvent (e2);
noteIDPitchMap.erase (noteID);
}
e.type = Event::kNoteOnEvent;
e.noteOn.channel = channel;
e.noteOn.pitch = data1;
e.noteOn.velocity = (float)data2 / 128.f;
e.noteOn.length = -1;
e.sampleOffset = sampleOffset;
if (withinRealtimeThread)
{
e.noteOn.noteId = lastNodeID++;
inputEvents.addEvent (e);
noteIDPitchMap.insert (std::make_pair ((channel << 8) + data1, e.noteOn.noteId));
}
else
{
scheduleEventFromUI (e);
}
}
else if (status == 0x80 || (status == 0x90 && data2 == 0)) // note off
{
auto noteID = noteIDPitchMap.find ((channel << 8) + data1);
if (noteID != noteIDPitchMap.end ())
{
e.type = Event::kNoteOffEvent;
e.noteOff.noteId = noteID->second;
e.noteOff.channel = channel;
e.noteOff.pitch = data1;
e.noteOff.velocity = (float)data2 / 128.f;
e.sampleOffset = sampleOffset;
if (withinRealtimeThread)
{
inputEvents.addEvent (e);
noteIDPitchMap.erase (noteID);
}
else
{
scheduleEventFromUI (e);
}
}
else
{
NSLog (@"NoteID not found:%d", (unsigned int)data1);
}
}
else if (status == 0xb0 && data1 < kAfterTouch) // controller
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, data1));
if (it != midiControllerToParamIDMap.end ())
{
ParamValue value = (ParamValue)data2 / 128.;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
else if (status == 0xe0) // pitch bend
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kPitchBend));
if (it != midiControllerToParamIDMap.end ())
{
uint16 _14bit;
_14bit = (uint16)data2;
_14bit <<= 7;
_14bit |= (uint16)data1;
ParamValue value = (double)_14bit / (double)0x3fff;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
else if (status == 0xd0) // aftertouch
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kAfterTouch));
if (it != midiControllerToParamIDMap.end ())
{
ParamValue value = (ParamValue)data1 / 128.;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
}
//------------------------------------------------------------------------
void VST3Plugin::process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO)
{
if (processing == false)
{
processor->setProcessing (true);
processing = true;
}
updateProcessContext (audioIO);
if (timeStamp)
processContext.systemTime = timeStamp->mHostTime;
// TODO: silence state update
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
{
processData.setChannelBuffer (kInput, 0, i, (float*)ioData->mBuffers[i].mData);
processData.setChannelBuffer (kOutput, 0, i, (float*)ioData->mBuffers[i].mData);
}
Event e;
while (uiScheduledEvents.pop (e))
{
inputEvents.addEvent (e);
if (e.type == Event::kNoteOnEvent)
{
auto noteID = noteIDPitchMap.find ((e.noteOn.channel << 8) + e.noteOn.pitch);
if (noteID == noteIDPitchMap.end ())
noteIDPitchMap.insert (
std::make_pair ((e.noteOn.channel << 8) + e.noteOn.pitch, e.noteOn.noteId));
}
}
processData.numSamples = numFrames;
inputParamChangeTransfer.transferChangesTo (inputParamChanges);
if (processor->process (processData) == kResultTrue)
{
inputParamChanges.clearQueue ();
outputParamChangeTransfer.transferChangesFrom (outputParamChanges);
outputParamChanges.clearQueue ();
inputEvents.clear ();
}
}
//------------------------------------------------------------------------
void VST3Plugin::updateProcessContext (AudioIO* audioIO)
{
memset (&processContext, 0, sizeof (ProcessContext));
processContext.sampleRate = audioIO->getSampleRate ();
Float64 beat = 0., tempo = 0.;
if (audioIO->getBeatAndTempo (beat, tempo))
{
processContext.state |=
ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid;
processContext.tempo = tempo;
processContext.projectTimeMusic = beat;
}
else
{
processContext.state |= ProcessContext::kTempoValid;
processContext.tempo = 120.;
}
UInt32 deltaSampleOffsetToNextBeat = 0;
Float32 timeSigNumerator = 0;
UInt32 timeSigDenominator = 0;
Float64 currentMeasureDownBeat = 0;
if (audioIO->getMusicalTimeLocation (deltaSampleOffsetToNextBeat, timeSigNumerator,
timeSigDenominator, currentMeasureDownBeat))
{
processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid |
ProcessContext::kClockValid;
processContext.timeSigNumerator = timeSigNumerator;
processContext.timeSigDenominator = timeSigDenominator;
processContext.samplesToNextClock = deltaSampleOffsetToNextBeat;
processContext.barPositionMusic = currentMeasureDownBeat;
}
Boolean isPlaying;
Boolean isRecording;
Boolean transportStateChanged;
Float64 currentSampleInTimeLine;
Boolean isCycling;
Float64 cycleStartBeat;
Float64 cycleEndBeat;
if (audioIO->getTransportState (isPlaying, isRecording, transportStateChanged,
currentSampleInTimeLine, isCycling, cycleStartBeat,
cycleEndBeat))
{
processContext.state |= ProcessContext::kCycleValid;
processContext.cycleStartMusic = cycleStartBeat;
processContext.cycleEndMusic = cycleEndBeat;
processContext.projectTimeSamples = currentSampleInTimeLine;
if (isPlaying)
processContext.state |= ProcessContext::kPlaying;
if (isCycling)
processContext.state |= ProcessContext::kCycleActive;
if (isRecording)
processContext.state |= ProcessContext::kRecording;
}
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::beginEdit (ParamID id)
{
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::performEdit (ParamID id, ParamValue valueNormalized)
{
inputParamChangeTransfer.addChange (id, valueNormalized, 0);
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::endEdit (ParamID id)
{
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::restartComponent (int32 flags)
{
tresult result = kNotImplemented;
return result;
}
//------------------------------------------------------------------------
void VST3Plugin::onTimer (Timer* timer)
{
ParamID paramID;
ParamValue paramValue;
int32 sampleOffset;
while (outputParamChangeTransfer.getNextChange (paramID, paramValue, sampleOffset))
{
editController->setParamNormalized (paramID, paramValue);
}
UpdateHandler::instance ()->triggerDeferedUpdates ();
}
}
}
}
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 <UIKit/UIKit.h>
//------------------------------------------------------------------------
/** Base UIApplicationDelegate class.
* This class provides the base handling of the audio engine, plug-in and plug-in editor\n
* You should subclass it for customization\n
* Make sure to call the methods of this class if you override one in your subclass !
*/
//------------------------------------------------------------------------
@interface VSTInterAppAudioAppDelegateBase : UIResponder <UIApplicationDelegate>
//------------------------------------------------------------------------
@property (strong, nonatomic) UIWindow* window;
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
- (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder;
- (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder;
- (void)applicationDidBecomeActive:(UIApplication*)application;
- (void)applicationWillResignActive:(UIApplication*)application;
@end
@@ -0,0 +1,193 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// 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 "VSTInterAppAudioAppDelegateBase.h"
#import "public.sdk/source/vst/interappaudio/AudioIO.h"
#import "public.sdk/source/vst/interappaudio/HostApp.h"
#import "public.sdk/source/vst/interappaudio/MidiIO.h"
#import "public.sdk/source/vst/interappaudio/VST3Editor.h"
#import "public.sdk/source/vst/interappaudio/VST3Plugin.h"
using namespace Steinberg::Vst::InterAppAudio;
//------------------------------------------------------------------------
static OSType fourCharCodeToOSType (NSString* inCode)
{
OSType rval = 0;
NSData* data = [inCode dataUsingEncoding:NSMacOSRomanStringEncoding];
[data getBytes:&rval length:sizeof (rval)];
HTONL (rval);
return rval;
}
//------------------------------------------------------------------------
@interface VSTInterAppAudioAppDelegateBase ()
//------------------------------------------------------------------------
{
VST3Plugin plugin;
VST3Editor editor;
BOOL audioIOInitialized;
}
@end
//------------------------------------------------------------------------
@implementation VSTInterAppAudioAppDelegateBase
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (BOOL)initAudioIO
{
id auArray = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AudioComponents"];
if (auArray)
{
id desc = [auArray objectAtIndex:0];
if (desc)
{
NSString* typeStr = [desc objectForKey:@"type"];
NSString* subtypeStr = [desc objectForKey:@"subtype"];
NSString* manufacturerStr = [desc objectForKey:@"manufacturer"];
NSString* nameStr = [desc objectForKey:@"name"];
if (typeStr && subtypeStr && manufacturerStr && nameStr)
{
OSType type = fourCharCodeToOSType (typeStr);
OSType subtype = fourCharCodeToOSType (subtypeStr);
OSType manufacturer = fourCharCodeToOSType (manufacturerStr);
AudioIO* audioIO = AudioIO::instance ();
if (audioIO->init (type, subtype, manufacturer, (__bridge CFStringRef)nameStr) ==
Steinberg::kResultTrue)
{
if (plugin.init ())
{
InterAppAudioHostApp::instance ()->setPlugin (&plugin);
audioIO->addProcessor (&plugin);
audioIOInitialized = YES;
return YES;
}
}
}
}
}
return NO;
}
//------------------------------------------------------------------------
- (BOOL)createUI
{
if (audioIOInitialized)
{
[UIApplication sharedApplication].statusBarHidden = YES;
self.window = [UIWindow new];
self.window.backgroundColor = [UIColor redColor];
CGRect screenSize = self.window.bounds;
if (editor.init (screenSize))
{
self.window.rootViewController = editor.getViewController ();
[self.window makeKeyAndVisible];
if (editor.attach (plugin.getEditController ()) == false)
{
return NO;
}
}
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (void)savePluginState:(NSCoder*)coder
{
NSData* processorState = plugin.getProcessorState ();
NSData* controllerState = plugin.getControllerState ();
if (processorState)
[coder encodeObject:processorState forKey:@"VST3ProcessorState"];
if (controllerState)
[coder encodeObject:controllerState forKey:@"VST3ControllerState"];
}
//------------------------------------------------------------------------
- (void)restorePluginState:(NSCoder*)coder
{
NSData* processorState = [coder decodeObjectForKey:@"VST3ProcessorState"];
if (processorState)
{
plugin.setProcessorState (processorState);
}
NSData* controllerState = [coder decodeObjectForKey:@"VST3ControllerState"];
if (controllerState)
{
plugin.setControllerState (controllerState);
}
}
//------------------------------------------------------------------------
// UIApplicationDelegate methods
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
return [self initAudioIO];
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
BOOL result = [self createUI];
if (result)
{
AudioIO::instance ()->start ();
}
return result;
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder
{
[self savePluginState:coder];
[coder encodeBool:MidiIO::instance ().isEnabled () forKey:@"MIDI Enabled"];
return YES;
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder
{
[self restorePluginState:coder];
BOOL midiEnabled = [coder decodeBoolForKey:@"MIDI Enabled"];
MidiIO::instance ().setEnabled (midiEnabled);
return YES;
}
//------------------------------------------------------------------------
- (void)applicationDidBecomeActive:(UIApplication*)application
{
AudioIO* audioIO = AudioIO::instance ();
audioIO->start ();
}
//------------------------------------------------------------------------
- (void)applicationWillResignActive:(UIApplication*)application
{
AudioIO* audioIO = AudioIO::instance ();
if (audioIO->getInterAppAudioConnected () == false && MidiIO::instance ().isEnabled () == false)
{
audioIO->stop ();
}
}
@end