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(¶meterNameUpdated, 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, ¶m);
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, // ¤tSampleInTimeline,
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