1 
2 #include "aulayer.h"
3 #include <gui/SurgeGUIEditor.h>
4 #include <AudioToolbox/AudioUnitUtilities.h>
5 #include <AudioUnit/AudioUnitCarbonView.h>
6 #include "aulayer_cocoaui.h"
7 
8 typedef SurgeSynthesizer sub3_synth;
9 
10 //----------------------------------------------------------------------------------------------------
11 
aulayer(AudioUnit au)12 aulayer::aulayer(AudioUnit au) : AUInstrumentBase(au, 1, 1)
13 {
14     plugin_instance = 0;
15     editor_instance = 0;
16     checkNamesEvery = 0;
17 }
18 
19 //----------------------------------------------------------------------------------------------------
20 
~aulayer()21 aulayer::~aulayer()
22 {
23     // Editor refers to synth so delete it first
24     if (editor_instance)
25     {
26         delete editor_instance;
27     }
28 
29     if (plugin_instance)
30     {
31         plugin_instance->~plugin();
32         _aligned_free(plugin_instance);
33     }
34 }
35 
36 //----------------------------------------------------------------------------------------------------
37 
GetNumCustomUIComponents()38 int aulayer::GetNumCustomUIComponents() { return 1; }
39 
40 //----------------------------------------------------------------------------------------------------
41 
SupportedNumChannels(const AUChannelInfo ** outInfo)42 UInt32 aulayer::SupportedNumChannels(const AUChannelInfo **outInfo)
43 {
44     if (!outInfo)
45         return 2;
46     cinfo[0].inChannels = 0;
47     cinfo[0].outChannels = 2;
48     cinfo[1].inChannels = 2;
49     cinfo[1].outChannels = 2;
50     *outInfo = cinfo;
51     return 2;
52 }
53 
54 //----------------------------------------------------------------------------------------------------
55 
56 #define kComponentSubType 'Srge'
57 
58 //----------------------------------------------------------------------------------------------------
59 
Version()60 ComponentResult aulayer::Version() { return kVersionNumber; }
61 
62 //----------------------------------------------------------------------------------------------------
63 
GetUIComponentDescs(ComponentDescription * inDescArray)64 void aulayer::GetUIComponentDescs(ComponentDescription *inDescArray)
65 {
66     inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
67     inDescArray[0].componentSubType = kComponentSubType;
68     inDescArray[0].componentManufacturer = kComponentManuf;
69     inDescArray[0].componentFlags = 0;
70     inDescArray[0].componentFlagsMask = 0;
71 }
72 
73 //----------------------------------------------------------------------------------------------------
74 
HasIcon()75 bool aulayer::HasIcon() { return true; }
76 
77 //----------------------------------------------------------------------------------------------------
78 
GetIconLocation()79 CFURLRef aulayer::GetIconLocation()
80 {
81     CFURLRef icon = 0;
82     CFBundleRef bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.vemberaudio.audiounit.surge"));
83 
84     if (bundle)
85     {
86         CFURLRef resources = CFBundleCopyResourcesDirectoryURL(bundle);
87         if (resources)
88         {
89             icon = CFURLCreateCopyAppendingPathComponent(NULL, resources, CFSTR("surgeicon.icns"),
90                                                          false);
91             CFRelease(resources);
92         }
93     }
94     return icon;
95 }
96 
97 //----------------------------------------------------------------------------------------------------
98 
InitializePlugin()99 void aulayer::InitializePlugin()
100 {
101     if (!plugin_instance)
102     {
103         // sub3_synth* synth = (sub3_synth*)_aligned_malloc(sizeof(sub3_synth),16);
104         // new(synth) sub3_synth(this);
105         // FIXME: The VST uses a std::unique_ptr<> and we probably should here also
106         plugin_instance = new SurgeSynthesizer(this);
107 
108         // This allows us standalone performance mode. See issue #146 and comment below tagged with
109         // issue number
110         plugin_instance->time_data.ppqPos = 0;
111 
112         sub3_synth *s = (sub3_synth *)plugin_instance;
113 
114         // Generate preset list in an ordered fashion
115         presetOrderToPatchList.clear();
116         for (int i = 0; i < s->storage.firstThirdPartyCategory; i++)
117         {
118             // Remap index to the corresponding category in alphabetical order.
119             int c = s->storage.patchCategoryOrdering[i];
120             for (auto p : s->storage.patchOrdering)
121             {
122                 if (s->storage.patch_list[p].category == c)
123                 {
124                     presetOrderToPatchList.push_back(p);
125                     surgePatchToAUPatch[p] = presetOrderToPatchList.size() - 1;
126                 }
127             }
128         }
129     }
130     assert(plugin_instance);
131 }
132 
133 //----------------------------------------------------------------------------------------------------
134 
IsPluginInitialized()135 bool aulayer::IsPluginInitialized() { return plugin_instance != 0; }
136 
137 //----------------------------------------------------------------------------------------------------
138 
Initialize()139 ComponentResult aulayer::Initialize()
140 {
141     if (!IsPluginInitialized())
142     {
143         InitializePlugin();
144     }
145 
146     double samplerate = GetOutput(0)->GetStreamFormat().mSampleRate;
147     plugin_instance->setSamplerate(samplerate);
148     plugin_instance->audio_processing_active = true;
149     plugin_instance->allNotesOff();
150     sampleRateCache = samplerate;
151 
152     blockpos = 0;
153     events_this_block = 0;
154     // init parameters
155     for (UInt32 i = 0; i < n_total_params + num_metaparameters; i++)
156     {
157         parameterIDlist[i] = i;
158         parameterIDlist_CFString[i] = 0;
159     }
160 
161     AUInstrumentBase::Initialize();
162     return noErr;
163 }
164 
165 //----------------------------------------------------------------------------------------------------
166 
Cleanup()167 void aulayer::Cleanup() { AUInstrumentBase::Cleanup(); }
168 
169 //----------------------------------------------------------------------------------------------------
170 
StreamFormatWritable(AudioUnitScope scope,AudioUnitElement element)171 bool aulayer::StreamFormatWritable(AudioUnitScope scope, AudioUnitElement element)
172 {
173 
174     return true; // IsInitialized() ? false : true;
175 }
176 
177 //----------------------------------------------------------------------------------------------------
178 
ChangeStreamFormat(AudioUnitScope inScope,AudioUnitElement inElement,const CAStreamBasicDescription & inPrevFormat,const CAStreamBasicDescription & inNewFormat)179 ComponentResult aulayer::ChangeStreamFormat(AudioUnitScope inScope, AudioUnitElement inElement,
180                                             const CAStreamBasicDescription &inPrevFormat,
181                                             const CAStreamBasicDescription &inNewFormat)
182 {
183     double samplerate = inNewFormat.mSampleRate;
184     ComponentResult result =
185         AUBase::ChangeStreamFormat(inScope, inElement, inPrevFormat, inNewFormat);
186 
187     if (plugin_instance)
188         plugin_instance->setSamplerate(samplerate);
189     sampleRateCache = samplerate;
190 
191     return result;
192 }
193 
194 //----------------------------------------------------------------------------------------------------
195 
Reset(AudioUnitScope inScope,AudioUnitElement inElement)196 ComponentResult aulayer::Reset(AudioUnitScope inScope, AudioUnitElement inElement)
197 {
198     if ((inScope == kAudioUnitScope_Global) && (inElement == 0) && plugin_instance)
199     {
200         double samplerate = GetOutput(0)->GetStreamFormat().mSampleRate;
201         if (samplerate != sampleRateCache)
202         {
203             plugin_instance->setSamplerate(samplerate);
204             sampleRateCache = samplerate;
205         }
206         plugin_instance->allNotesOff();
207     }
208     return noErr;
209 }
210 
211 //----------------------------------------------------------------------------------------------------
212 
ValidFormat(AudioUnitScope inScope,AudioUnitElement inElement,const CAStreamBasicDescription & inNewFormat)213 bool aulayer::ValidFormat(AudioUnitScope inScope, AudioUnitElement inElement,
214                           const CAStreamBasicDescription &inNewFormat)
215 {
216 
217     if (!FormatIsCanonical(inNewFormat))
218         return false;
219     if (inElement > 0)
220         return false; // only 1 input/output element supported
221     if (inScope == kAudioUnitScope_Input)
222     {
223         if (inNewFormat.NumberChannels() == 0)
224             return true;
225         else if (inNewFormat.NumberChannels() == 2)
226         {
227             return true;
228         }
229     }
230     else if (inScope == kAudioUnitScope_Output)
231     {
232         if (inNewFormat.NumberChannels() == 2)
233             return true;
234     }
235     else if (inScope == kAudioUnitScope_Global)
236     {
237         return true;
238     }
239     return false;
240 }
241 
242 //----------------------------------------------------------------------------------------------------
243 
StartNote(MusicDeviceInstrumentID inInstrument,MusicDeviceGroupID inGroupID,NoteInstanceID * outNoteInstanceID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams & inParams)244 ComponentResult aulayer::StartNote(MusicDeviceInstrumentID inInstrument,
245                                    MusicDeviceGroupID inGroupID, NoteInstanceID *outNoteInstanceID,
246                                    UInt32 inOffsetSampleFrame,
247                                    const MusicDeviceNoteParams &inParams)
248 {
249     return 0;
250 }
251 
252 //----------------------------------------------------------------------------------------------------
253 
StopNote(MusicDeviceGroupID inGroupID,NoteInstanceID inNoteInstanceID,UInt32 inOffsetSampleFrame)254 ComponentResult aulayer::StopNote(MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID,
255                                   UInt32 inOffsetSampleFrame)
256 {
257     return 0;
258 }
259 
260 //----------------------------------------------------------------------------------------------------
261 
HandleNoteOn(UInt8 inChannel,UInt8 inNoteNumber,UInt8 inVelocity,UInt32 inStartFrame)262 OSStatus aulayer::HandleNoteOn(UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity,
263                                UInt32 inStartFrame)
264 {
265     plugin_instance->playNote(inChannel, inNoteNumber, inVelocity, 0);
266     return noErr;
267 }
268 
269 //----------------------------------------------------------------------------------------------------
270 
HandleNoteOff(UInt8 inChannel,UInt8 inNoteNumber,UInt8 inVelocity,UInt32 inStartFrame)271 OSStatus aulayer::HandleNoteOff(UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity,
272                                 UInt32 inStartFrame)
273 {
274     plugin_instance->releaseNote(inChannel, inNoteNumber, inVelocity);
275     return noErr;
276 }
277 
278 //----------------------------------------------------------------------------------------------------
279 
HandleControlChange(UInt8 inChannel,UInt8 inController,UInt8 inValue,UInt32 inStartFrame)280 OSStatus aulayer::HandleControlChange(UInt8 inChannel, UInt8 inController, UInt8 inValue,
281                                       UInt32 inStartFrame)
282 {
283     plugin_instance->channelController(inChannel, inController, inValue);
284     return noErr;
285 }
286 
287 //----------------------------------------------------------------------------------------------------
288 
HandleChannelPressure(UInt8 inChannel,UInt8 inValue,UInt32 inStartFrame)289 OSStatus aulayer::HandleChannelPressure(UInt8 inChannel, UInt8 inValue, UInt32 inStartFrame)
290 {
291     plugin_instance->channelAftertouch(inChannel, inValue);
292     return noErr;
293 }
294 
295 //----------------------------------------------------------------------------------------------------
296 
HandlePitchWheel(UInt8 inChannel,UInt8 inPitch1,UInt8 inPitch2,UInt32 inStartFrame)297 OSStatus aulayer::HandlePitchWheel(UInt8 inChannel, UInt8 inPitch1, UInt8 inPitch2,
298                                    UInt32 inStartFrame)
299 {
300     long value = (inPitch1 & 0x7f) + ((inPitch2 & 0x7f) << 7);
301     plugin_instance->pitchBend(inChannel, value - 8192);
302     return noErr;
303 }
304 
305 //----------------------------------------------------------------------------------------------------
306 
HandlePolyPressure(UInt8 inChannel,UInt8 inKey,UInt8 inValue,UInt32 inStartFrame)307 OSStatus aulayer::HandlePolyPressure(UInt8 inChannel, UInt8 inKey, UInt8 inValue,
308                                      UInt32 inStartFrame)
309 {
310     plugin_instance->polyAftertouch(inChannel, inKey, inValue);
311     return noErr;
312 }
313 
314 //----------------------------------------------------------------------------------------------------
315 
HandleProgramChange(UInt8 inChannel,UInt8 inValue)316 OSStatus aulayer::HandleProgramChange(UInt8 inChannel, UInt8 inValue)
317 {
318     plugin_instance->programChange(inChannel, inValue);
319     return noErr;
320 }
321 
322 //----------------------------------------------------------------------------------------------------
323 
Render(AudioUnitRenderActionFlags & ioActionFlags,const AudioTimeStamp & inTimeStamp,UInt32 inNumberFrames)324 ComponentResult aulayer::Render(AudioUnitRenderActionFlags &ioActionFlags,
325                                 const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames)
326 {
327     assert(IsPluginInitialized());
328     assert(IsInitialized());
329     SurgeSynthesizer *s = (SurgeSynthesizer *)plugin_instance;
330     s->process_input = 0;
331 
332     if (checkNamesEvery++ == 20)
333     {
334         checkNamesEvery = 0;
335         if (std::atomic_exchange(&parameterNameUpdated, false))
336         {
337             /*
338               What's the right magic for here I wonder?
339               https://www.kvraudio.com/forum/viewtopic.php?t=198125&start=30
340               AudioUnitParameter param;
341               param.mAudioUnit = GetComponentInstance();
342               param.mParameterID = kAUParameterListener_AnyParameter;
343 
344               AUParameterListenerNotify (nullptr, nullptr, &param);
345             */
346         }
347     }
348 
349     float sampleRate = sampleRateCache;
350     float *outputs[N_OUTPUTS];
351     float *inputs[N_INPUTS];
352 
353     AudioUnitRenderActionFlags xflags = 0;
354     if (HasInput(0))
355     {
356         ComponentResult result = PullInput(0, xflags, inTimeStamp, inNumberFrames);
357 
358         if (result == noErr)
359         {
360             for (long i = 0; i < N_INPUTS; i++)
361             {
362                 inputs[i] = GetInput(0)->GetChannelData(i);
363             }
364             s->process_input = inputs[0] != 0;
365         }
366     }
367     // Get output buffer list and extract the i/o buffer pointers.
368     // if (N_OUTPUTS>0)
369     {
370         AudioBufferList &asOutBufs = GetOutput(0)->GetBufferList();
371         for (long o = 0; o < N_OUTPUTS; ++o)
372         {
373             outputs[o] = (float *)(asOutBufs.mBuffers[o].mData);
374             if (asOutBufs.mBuffers[o].mDataByteSize <= 0 || o >= asOutBufs.mNumberBuffers)
375                 outputs[o] = nil;
376         }
377     }
378 
379     // Get the transport status
380     Boolean isPlaying;
381 
382     if (CallHostTransportState(&isPlaying,
383                                NULL, // &isTransportStateChanged,
384                                NULL, // &currentSampleInTimeline,
385                                NULL, // &isCycling,
386                                NULL, // &cycleStartBeat,
387                                NULL  // &cycleEndBeat
388                                ) < 0)
389     {
390         isPlaying = false;
391     }
392 
393     // do each buffer
394     Float64 CurrentBeat, CurrentTempo;
395     if (CallHostBeatAndTempo(&CurrentBeat, &CurrentTempo) >= 0)
396     {
397         plugin_instance->time_data.tempo = CurrentTempo;
398 
399         // If the engine isn't playing, so CurrentBeat is a constant, then result here is
400         // resetting time_data.ppqPos to the same thing over and over. That means the lfo_freerun
401         // mode basically acts like keypress mode since time_data.ppqPos - which maps to songpos
402         // is always reset to zero or a frame or so beyond. If instead we only reset it when
403         // playing then exactly what we want occurs. In playback mode we get perfectly predictable
404         // oscillator start and stop; but in performance mode we get freerun working off the
405         // internal clock which we synthesize by running the "move clock" block below.
406         //
407         // See github issue #146
408         if (isPlaying)
409             plugin_instance->time_data.ppqPos = CurrentBeat;
410 
411         plugin_instance->resetStateFromTimeData();
412     }
413     else
414     {
415         plugin_instance->time_data.tempo = 120;
416     }
417 
418     UInt32 oDS;
419     Float32 otsNum;
420     UInt32 otsDen;
421     Float64 ocmD;
422     if (CallHostMusicalTimeLocation(&oDS, &otsNum, &otsDen, &ocmD) >= 0)
423     {
424         plugin_instance->time_data.timeSigNumerator = (int)otsNum;
425         plugin_instance->time_data.timeSigDenominator = (int)otsDen;
426     }
427 
428     unsigned int events_processed = 0;
429 
430     unsigned int i;
431     for (i = 0; i < inNumberFrames; i++)
432     {
433         if (blockpos == 0)
434         {
435             // move clock
436             plugin_instance->time_data.ppqPos +=
437                 (double)BLOCK_SIZE * plugin_instance->time_data.tempo / (60. * sampleRate);
438 
439             // process events for the current block
440             while (events_processed < events_this_block)
441             {
442                 if ((i + blockpos) > eventbuffer[events_processed].inStartFrame)
443                 {
444                     AuMIDIEvent *e = &eventbuffer[events_processed];
445                     AUMIDIBase::HandleMidiEvent(e->status, e->channel, e->data1, e->data2,
446                                                 e->inStartFrame);
447                     events_processed++;
448                 }
449                 else
450                     break;
451             }
452 
453             // run synth engine for the current block
454             plugin_instance->process();
455         }
456 
457         if (s->process_input)
458         {
459             int inp;
460             for (inp = 0; inp < N_INPUTS; inp++)
461             {
462                 plugin_instance->input[inp][blockpos] = inputs[inp][i];
463             }
464         }
465 
466         int outp;
467         for (outp = 0; outp < N_OUTPUTS; outp++)
468         {
469             outputs[outp][i] = (float)plugin_instance->output[outp][blockpos]; // replacing
470         }
471 
472         blockpos++;
473         if (blockpos >= BLOCK_SIZE)
474             blockpos = 0;
475     }
476 
477     // process remaining events (there shouldn't be any)
478     while (events_processed < events_this_block)
479     {
480         AuMIDIEvent *e = &eventbuffer[events_processed];
481         AUMIDIBase::HandleMidiEvent(e->status, e->channel, e->data1, e->data2, e->inStartFrame);
482         events_processed++;
483     }
484     events_this_block = 0; // reset eventbuffer
485 
486     return noErr;
487 }
488 
489 //----------------------------------------------------------------------------------------------------
490 
491 //----------------------------------------------------------------------------------------------------
492 
GetPropertyInfo(AudioUnitPropertyID iID,AudioUnitScope iScope,AudioUnitElement iElem,UInt32 & iSize,Boolean & fWritable)493 ComponentResult aulayer::GetPropertyInfo(AudioUnitPropertyID iID, AudioUnitScope iScope,
494                                          AudioUnitElement iElem, UInt32 &iSize, Boolean &fWritable)
495 {
496     // OK so big todo here and in GetProperty
497     // 1: Switch these all to have an inScope--kAudioUnitScope_Global guard
498     // 2: Switch these to be a switch
499     // 3: Make Cocoa UI make the datasize the size of the view info. Take a look in
500     // juce_audio_plugin_client/AU/juce_AU_Wrapper.mm 4: That will probably core out since the calss
501     // isn't defined. That's OK! MOve on from there.
502     if (iScope == kAudioUnitScope_Global)
503     {
504         switch (iID)
505         {
506         case kAudioUnitProperty_CocoaUI:
507             iSize = sizeof(AudioUnitCocoaViewInfo);
508             fWritable = true;
509             return noErr;
510             break;
511         case kVmbAAudioUnitProperty_GetEditPointer:
512             iSize = sizeof(SurgeGUIEditor *);
513             fWritable = true;
514             return noErr;
515             break;
516         case kAudioUnitProperty_ParameterValueName:
517             iSize = sizeof(AudioUnitParameterValueName);
518             return noErr;
519             break;
520         case kAudioUnitProperty_ParameterClumpName:
521             iSize = sizeof(AudioUnitParameterNameInfo);
522             return noErr;
523             break;
524         case kVmbAAudioUnitProperty_GetPluginCPPInstance:
525             iSize = sizeof(void *);
526             return noErr;
527             break;
528         case kAudioUnitProperty_SupportsMPE:
529             iSize = sizeof(uint32_t);
530             fWritable = false;
531             return noErr;
532             break;
533         }
534     }
535 
536     return AUInstrumentBase::GetPropertyInfo(iID, iScope, iElem, iSize, fWritable);
537 }
538 
539 //----------------------------------------------------------------------------------------------------
540 
RestoreState(CFPropertyListRef plist)541 ComponentResult aulayer::RestoreState(CFPropertyListRef plist)
542 {
543     ComponentResult result = AUBase::RestoreState(plist);
544     if (result != noErr)
545         return result;
546 
547     CFDictionaryRef dict = static_cast<CFDictionaryRef>(plist);
548 
549     CFDataRef data = reinterpret_cast<CFDataRef>(CFDictionaryGetValue(dict, rawchunkname));
550     if (data != NULL)
551     {
552         if (!IsPluginInitialized())
553         {
554             InitializePlugin();
555         }
556         const UInt8 *p;
557         p = CFDataGetBytePtr(data);
558         size_t psize = CFDataGetLength(data);
559         plugin_instance->loadRaw(p, psize, false);
560 
561         plugin_instance->loadFromDawExtraState();
562         if (editor_instance)
563             editor_instance->loadFromDAWExtraState(plugin_instance);
564 
565         // This stops any prior factory loads from clobbering me. #2102
566         plugin_instance->patchid_queue = -1;
567     }
568     return noErr;
569 }
570 
571 //----------------------------------------------------------------------------------------------------
572 
SaveState(CFPropertyListRef * plist)573 ComponentResult aulayer::SaveState(CFPropertyListRef *plist)
574 {
575     // store AUBase class data
576     ComponentResult result = AUBase::SaveState(plist);
577     if (result != noErr)
578         return result;
579     if (!IsInitialized())
580         return kAudioUnitErr_Uninitialized;
581 
582     // append raw chunk data
583     // TODO It is here that we can find memory leaks!!!
584 
585     CFMutableDictionaryRef dict = (CFMutableDictionaryRef)*plist;
586     void *data;
587 
588     plugin_instance->populateDawExtraState();
589     if (editor_instance)
590         editor_instance->populateDawExtraState(plugin_instance);
591 
592     CFIndex size = plugin_instance->saveRaw(&data);
593     CFDataRef dataref =
594         CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)data, size, kCFAllocatorNull);
595     CFDictionarySetValue(dict, rawchunkname, dataref);
596     CFRelease(dataref);
597 
598     return noErr;
599 }
600 
601 //----------------------------------------------------------------------------------------------------
602 
GetPresets(CFArrayRef * outData) const603 ComponentResult aulayer::GetPresets(CFArrayRef *outData) const
604 {
605     // kAudioUnitProperty_FactoryPresets
606 
607     // Type: CFArrayRef containing AUPreset's
608     // Returns an array of AUPreset that contain a number and name for each of the presets.
609     // The number of each preset must be greater (or equal to) zero, and the numbers need not be
610     // ordered or contiguous. The name of each preset can be presented to the user as a means of
611     // identifying each preset.
612     // The CFArrayRef should be released by the caller.
613 
614     if (!IsInitialized())
615         return kAudioUnitErr_Uninitialized;
616 
617     if (outData == NULL)
618         return noErr;
619 
620     sub3_synth *s = (sub3_synth *)plugin_instance;
621 
622     UInt32 n_presets = presetOrderToPatchList.size();
623 
624     CFMutableArrayRef newArray =
625         CFArrayCreateMutable(kCFAllocatorDefault, n_presets, &kCFAUPresetArrayCallBacks);
626     if (newArray == NULL)
627         return coreFoundationUnknownErr;
628 
629     for (long i = 0; i < n_presets; i++)
630     {
631         auto patch = s->storage.patch_list[presetOrderToPatchList[i]];
632         std::string nm = s->storage.patch_category[patch.category].name + " / " + patch.name;
633         CFAUPresetRef newPreset =
634             CFAUPresetCreate(kCFAllocatorDefault, i,
635                              CFStringCreateWithCString(NULL, nm.c_str(), kCFStringEncodingUTF8));
636         if (newPreset != NULL)
637         {
638             CFArrayAppendValue(newArray, newPreset);
639             CFAUPresetRelease(newPreset);
640         }
641     }
642 
643     *outData = (CFArrayRef)newArray;
644     return noErr;
645     // return coreFoundationUnknownErr; // this isn't OK so tell the host that
646 }
647 
648 //----------------------------------------------------------------------------------------------------
649 
NewFactoryPresetSet(const AUPreset & inNewFactoryPreset)650 OSStatus aulayer::NewFactoryPresetSet(const AUPreset &inNewFactoryPreset)
651 {
652     if (!IsInitialized())
653         return false;
654     if (inNewFactoryPreset.presetNumber < 0)
655         return false;
656     SetAFactoryPresetAsCurrent(inNewFactoryPreset);
657     sub3_synth *s = (sub3_synth *)plugin_instance;
658     s->patchid_queue = presetOrderToPatchList[inNewFactoryPreset.presetNumber];
659     s->processThreadunsafeOperations();
660     return true;
661 }
662 
663 //----------------------------------------------------------------------------------------------------
664 
GetParameterList(AudioUnitScope inScope,AudioUnitParameterID * outParameterList,UInt32 & outNumParameters)665 ComponentResult aulayer::GetParameterList(AudioUnitScope inScope,
666                                           AudioUnitParameterID *outParameterList,
667                                           UInt32 &outNumParameters)
668 {
669     if ((inScope != kAudioUnitScope_Global) || !IsInitialized())
670     {
671         outNumParameters = 0;
672         return noErr;
673     }
674 
675     outNumParameters = n_total_params + num_metaparameters;
676     if (outParameterList)
677         memcpy(outParameterList, parameterIDlist,
678                sizeof(AudioUnitParameterID) * (n_total_params + num_metaparameters));
679     return noErr;
680 }
681 
682 //----------------------------------------------------------------------------------------------------
683 
GetParameterInfo(AudioUnitScope inScope,AudioUnitParameterID inParameterID,AudioUnitParameterInfo & outParameterInfo)684 ComponentResult aulayer::GetParameterInfo(AudioUnitScope inScope,
685                                           AudioUnitParameterID inParameterID,
686                                           AudioUnitParameterInfo &outParameterInfo)
687 {
688     // FIXME: I think this code can be a bit tighter and cleaner, but it works OK for now
689     outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
690     outParameterInfo.minValue = 0.f;
691     outParameterInfo.maxValue = 1.f;
692     outParameterInfo.defaultValue = 0.f;
693 
694     outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable |
695                              kAudioUnitParameterFlag_IsReadable | kAudioUnitParameterFlag_CanRamp |
696                              kAudioUnitParameterFlag_IsHighResolution;
697 
698     // kAudioUnitParameterFlag_IsGlobalMeta
699 
700     sprintf(outParameterInfo.name, "-");
701     // outParameterInfo.unitName
702 
703     if (inScope != kAudioUnitScope_Global)
704         return kAudioUnitErr_InvalidParameter;
705     if (!IsInitialized())
706         return kAudioUnitErr_Uninitialized; // return defaults
707 
708     SurgeSynthesizer::ID pid;
709     plugin_instance->fromDAWSideIndex(inParameterID, pid);
710 
711     plugin_instance->getParameterName(pid, outParameterInfo.name);
712     outParameterInfo.flags |= kAudioUnitParameterFlag_HasCFNameString |
713                               kAudioUnitParameterFlag_CFNameRelease |
714                               kAudioUnitParameterFlag_HasClump | kAudioUnitParameterFlag_HasName;
715     outParameterInfo.cfNameString =
716         CFStringCreateWithCString(NULL, outParameterInfo.name, kCFStringEncodingUTF8);
717 
718     parametermeta pm;
719     plugin_instance->getParameterMeta(pid, pm);
720     outParameterInfo.minValue = pm.fmin;
721     outParameterInfo.maxValue = pm.fmax;
722     outParameterInfo.defaultValue = pm.fdefault;
723     if (pm.expert)
724         outParameterInfo.flags |= kAudioUnitParameterFlag_ExpertMode;
725     if (pm.meta)
726         outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
727 
728     outParameterInfo.clumpID = pm.clump;
729 
730     return noErr;
731 }
732 
733 //----------------------------------------------------------------------------------------------------
734 
GetParameter(AudioUnitParameterID inID,AudioUnitScope inScope,AudioUnitElement inElement,Float32 & outValue)735 ComponentResult aulayer::GetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
736                                       AudioUnitElement inElement, Float32 &outValue)
737 {
738     if (inScope != kAudioUnitScope_Global)
739         return kAudioUnitErr_InvalidParameter;
740     if (inID >= n_total_params + num_metaparameters)
741         return kAudioUnitErr_InvalidParameter;
742     if (!IsInitialized())
743         return kAudioUnitErr_Uninitialized;
744     SurgeSynthesizer::ID iid;
745     if (plugin_instance->fromDAWSideIndex(inID, iid))
746         outValue = plugin_instance->getParameter01(iid);
747     return noErr;
748 }
749 
750 //----------------------------------------------------------------------------------------------------
751 
SetParameter(AudioUnitParameterID inID,AudioUnitScope inScope,AudioUnitElement inElement,Float32 inValue,UInt32 inBufferOffsetInFrames)752 ComponentResult aulayer::SetParameter(AudioUnitParameterID inID, AudioUnitScope inScope,
753                                       AudioUnitElement inElement, Float32 inValue,
754                                       UInt32 inBufferOffsetInFrames)
755 {
756     if (inScope != kAudioUnitScope_Global)
757         return kAudioUnitErr_InvalidParameter;
758     if (inID >= n_total_params + num_metaparameters)
759         return kAudioUnitErr_InvalidParameter;
760     if (!IsInitialized())
761         return kAudioUnitErr_Uninitialized;
762     SurgeSynthesizer::ID iid;
763     if (plugin_instance->fromDAWSideIndex(inID, iid))
764         plugin_instance->setParameter01(iid, inValue, true);
765     return noErr;
766 }
767 
768 //----------------------------------------------------------------------------------------------------
769 
CanScheduleParameters() const770 bool aulayer::CanScheduleParameters() const
771 {
772     return false; // TODO add support
773 }
774 
775 //----------------------------------------------------------------------------------------------------
776 
ParameterBeginEdit(int p)777 bool aulayer::ParameterBeginEdit(int p)
778 {
779     AudioUnitEvent event;
780     event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
781     event.mArgument.mParameter.mAudioUnit = GetComponentInstance();
782     event.mArgument.mParameter.mParameterID =
783         p; // plugin_instance->remapInternalToExternalApiId(p);
784     event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
785     event.mArgument.mParameter.mElement = 0;
786     AUEventListenerNotify(NULL, NULL, &event);
787     return true;
788 }
789 
790 //----------------------------------------------------------------------------------------------------
791 
ParameterEndEdit(int p)792 bool aulayer::ParameterEndEdit(int p)
793 {
794     AudioUnitEvent event;
795     event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
796     event.mArgument.mParameter.mAudioUnit = GetComponentInstance();
797     event.mArgument.mParameter.mParameterID =
798         p; // plugin_instance->remapInternalToExternalApiId(p);
799     event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
800     event.mArgument.mParameter.mElement = 0;
801     AUEventListenerNotify(NULL, NULL, &event);
802     return true;
803 }
804 
805 //----------------------------------------------------------------------------------------------------
806 
ParameterUpdate(int p)807 bool aulayer::ParameterUpdate(int p)
808 {
809     AudioUnitEvent event;
810     event.mEventType = kAudioUnitEvent_ParameterValueChange;
811     event.mArgument.mParameter.mAudioUnit = GetComponentInstance();
812     event.mArgument.mParameter.mParameterID =
813         p; // plugin_instance->remapInternalToExternalApiId(p);
814     event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
815     event.mArgument.mParameter.mElement = 0;
816     AUEventListenerNotify(NULL, NULL, &event);
817     return true;
818 }
819 
820 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
821 //	AUMIDIBase::HandleMidiEvent
822 //
823 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HandleMidiEvent(UInt8 status,UInt8 channel,UInt8 data1,UInt8 data2,UInt32 inStartFrame)824 OSStatus aulayer::HandleMidiEvent(UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2,
825                                   UInt32 inStartFrame)
826 {
827     if (!IsInitialized())
828         return kAudioUnitErr_Uninitialized;
829 
830     if (events_this_block >= 1024)
831         return -1; // buffer full
832 
833     eventbuffer[events_this_block].status = status;
834     eventbuffer[events_this_block].channel = channel;
835     eventbuffer[events_this_block].data1 = data1;
836     eventbuffer[events_this_block].data2 = data2;
837     eventbuffer[events_this_block].inStartFrame = inStartFrame;
838 
839     events_this_block++;
840 
841     return noErr;
842 }
843 
844 AUDIOCOMPONENT_ENTRY(AUMusicDeviceFactory, aulayer);
845