1 /*
2 Copyright (C) 2016 Apple Inc. All Rights Reserved.
3 See LICENSE.txt for this sample’s licensing information
4
5 Abstract:
6 Part of Core Audio AUInstrument Base Classes
7 */
8
9 #include "SynthElement.h"
10 #include "AUInstrumentBase.h"
11 #include "AUMIDIDefs.h"
12
13 #undef DEBUG_PRINT
14 #define DEBUG_PRINT 0
15 #define DEBUG_PRINT_NOTE 0
16 #define DEBUG_PRINT_RENDER 0
17
18 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
MidiControls()19 MidiControls::MidiControls()
20 {
21 Reset();
22 }
23
Reset()24 void MidiControls::Reset()
25 {
26 memset(mControls, 0, sizeof(mControls));
27 memset(mPolyPressure, 0, sizeof(mPolyPressure));
28 mMonoPressure = 0;
29 mProgramChange = 0;
30 mPitchBend = 0;
31 mActiveRPN = 0;
32 mActiveNRPN = 0;
33 mActiveRPValue = 0;
34 mActiveNRPValue = 0;
35 mControls[kMidiController_Pan] = 64;
36 mControls[kMidiController_Expression] = 127;
37 mPitchBendDepth = 24 << 7;
38 mFPitchBendDepth = 24.0f;
39 mFPitchBend = 0.0f;
40 }
41
42
SynthElement(AUInstrumentBase * audioUnit,UInt32 inElement)43 SynthElement::SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement)
44 : AUElement(audioUnit), mIndex(inElement)
45 {
46 }
47
~SynthElement()48 SynthElement::~SynthElement()
49 {
50 }
51
SynthGroupElement(AUInstrumentBase * audioUnit,UInt32 inElement,MIDIControlHandler * inHandler)52 SynthGroupElement::SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler)
53 : SynthElement(audioUnit, inElement),
54 mCurrentAbsoluteFrame(-1),
55 mMidiControlHandler(inHandler),
56 mSustainIsOn(false), mSostenutoIsOn(false), mOutputBus(0), mGroupID(kUnassignedGroup)
57 {
58 for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i)
59 mNoteList[i].mState = (SynthNoteState) i;
60 }
61
~SynthGroupElement()62 SynthGroupElement::~SynthGroupElement()
63 {
64 delete mMidiControlHandler;
65 }
66
SetGroupID(MusicDeviceGroupID inGroup)67 void SynthGroupElement::SetGroupID (MusicDeviceGroupID inGroup)
68 {
69 // can't re-assign a group once its been assigned
70 if (mGroupID != kUnassignedGroup) throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement);
71 mGroupID = inGroup;
72 }
73
Reset()74 void SynthGroupElement::Reset()
75 {
76 #if DEBUG_PRINT
77 printf("SynthGroupElement::Reset\n");
78 #endif
79 mMidiControlHandler->Reset();
80 for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i)
81 mNoteList[i].Empty();
82 }
83
SynthPartElement(AUInstrumentBase * audioUnit,UInt32 inElement)84 SynthPartElement::SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement)
85 : SynthElement(audioUnit, inElement)
86 {
87 }
88
89 // Return the SynthNote with the given inNoteID, if found. If unreleasedOnly is true, only look for
90 // attacked and sostenutoed notes, otherwise search all states. Return state of found note via outNoteState.
91
GetNote(NoteInstanceID inNoteID,bool unreleasedOnly,UInt32 * outNoteState)92 SynthNote *SynthGroupElement::GetNote(NoteInstanceID inNoteID, bool unreleasedOnly, UInt32 *outNoteState)
93 {
94 #if DEBUG_PRINT_RENDER
95 printf("SynthGroupElement::GetNote %d, unreleased = %d\n", inNoteID, unreleasedOnly);
96 #endif
97 const UInt32 lastNoteState = unreleasedOnly ?
98 (mSostenutoIsOn ? kNoteState_Sostenutoed : kNoteState_Attacked)
99 : kNoteState_Released;
100 SynthNote *note = NULL;
101 // Search for notes in each successive state
102 for (UInt32 noteState = kNoteState_Attacked; noteState <= lastNoteState; ++noteState)
103 {
104 if (outNoteState) *outNoteState = noteState; // even if we find nothing
105 note = mNoteList[noteState].mHead;
106 while (note && note->mNoteID != inNoteID)
107 {
108 #if DEBUG_PRINT_RENDER
109 printf(" checking %p id: %d\n", note, note->mNoteID);
110 #endif
111 note = note->mNext;
112 }
113 if (note)
114 {
115 #if DEBUG_PRINT_RENDER
116 printf(" found %p\n", note);
117 #endif
118 break;
119 }
120 }
121 return note;
122 }
123
NoteOn(SynthNote * note,SynthPartElement * part,NoteInstanceID inNoteID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams & inParams)124 void SynthGroupElement::NoteOn(SynthNote *note,
125 SynthPartElement *part,
126 NoteInstanceID inNoteID,
127 UInt32 inOffsetSampleFrame,
128 const MusicDeviceNoteParams &inParams)
129 {
130 #if DEBUG_PRINT_NOTE
131 printf("SynthGroupElement::NoteOn %d\n", inNoteID);
132 #endif
133 // TODO: CONSIDER FIXING this to not need to initialize mCurrentAbsoluteFrame to -1.
134 UInt64 absoluteFrame = (mCurrentAbsoluteFrame == -1) ? inOffsetSampleFrame : mCurrentAbsoluteFrame + inOffsetSampleFrame;
135 if (note->AttackNote(part, this, inNoteID, absoluteFrame, inOffsetSampleFrame, inParams)) {
136 mNoteList[kNoteState_Attacked].AddNote(note);
137 }
138 }
139
NoteOff(NoteInstanceID inNoteID,UInt32 inFrame)140 void SynthGroupElement::NoteOff(NoteInstanceID inNoteID, UInt32 inFrame)
141 {
142 #if DEBUG_PRINT_NOTE
143 printf("SynthGroupElement::NoteOff %d\n", inNoteID);
144 #endif
145 UInt32 noteState = kNoteState_Attacked;
146 SynthNote *note = GetNote(inNoteID, true, ¬eState); // asking for unreleased only
147 if (note)
148 {
149 #if DEBUG_PRINT_NOTE
150 printf(" old note state: %d\n", note->mState);
151 #endif
152 if (noteState == kNoteState_Attacked)
153 {
154 mNoteList[noteState].RemoveNote(note);
155 if (mSustainIsOn) {
156 mNoteList[kNoteState_ReleasedButSustained].AddNote(note);
157 } else {
158 note->Release(inFrame);
159 mNoteList[kNoteState_Released].AddNote(note);
160 }
161 #if DEBUG_PRINT_NOTE
162 printf(" new note state: %d\n", note->mState);
163 #endif
164 }
165 else /* if (noteState == kNoteState_Sostenutoed) */
166 {
167 mNoteList[kNoteState_Sostenutoed].RemoveNote(note);
168 mNoteList[kNoteState_ReleasedButSostenutoed].AddNote(note);
169 }
170 }
171 }
172
NoteEnded(SynthNote * inNote,UInt32 inFrame)173 void SynthGroupElement::NoteEnded(SynthNote *inNote, UInt32 inFrame)
174 {
175 #if DEBUG_PRINT_NOTE
176 printf("SynthGroupElement::NoteEnded: id %d state %d\n", inNote->mNoteID, inNote->mState);
177 #endif
178 if (inNote->IsSounding()) {
179 SynthNoteList *list = &mNoteList[inNote->GetState()];
180 list->RemoveNote(inNote);
181 }
182
183 GetAUInstrument()->AddFreeNote(inNote);
184 }
185
NoteFastReleased(SynthNote * inNote)186 void SynthGroupElement::NoteFastReleased(SynthNote *inNote)
187 {
188 #if DEBUG_PRINT_NOTE
189 printf("SynthGroupElement::NoteFastReleased id %d state %d\n", inNote->mNoteID, inNote->mState);
190 #endif
191 if (inNote->IsActive()) {
192 mNoteList[inNote->GetState()].RemoveNote(inNote);
193 GetAUInstrument()->DecNumActiveNotes();
194 mNoteList[kNoteState_FastReleased].AddNote(inNote);
195 }
196 else {
197 Assert(true, "ASSERT FAILED: Attempting to fast-release non-active note");
198 }
199 }
200
ChannelMessage(UInt16 controllerID,UInt16 inValue)201 bool SynthGroupElement::ChannelMessage(UInt16 controllerID, UInt16 inValue)
202 {
203 bool handled = true;
204 #if DEBUG_PRINT
205 printf("SynthGroupElement::ChannelMessage(0x%x, %u)\n", controllerID, inValue);
206 #endif
207 // Sustain and sostenuto are "pedal events", and are handled during render cycle
208 if (controllerID <= kMidiController_RPN_MSB && controllerID != kMidiController_Sustain && controllerID != kMidiController_Sostenuto)
209 handled = mMidiControlHandler->SetController(controllerID, UInt8(inValue));
210 else
211 {
212 switch (controllerID)
213 {
214 case kMidiMessage_ProgramChange:
215 handled = mMidiControlHandler->SetProgramChange(inValue);
216 break;
217 case kMidiMessage_PitchWheel:
218 handled = mMidiControlHandler->SetPitchWheel(inValue);
219 break;
220 case kMidiMessage_ChannelPressure:
221 #if DEBUG_PRINT
222 printf("SynthGroupElement::ChannelMessage: Channel Pressure %u\n", inValue);
223 #endif
224 handled = mMidiControlHandler->SetChannelPressure(UInt8(inValue));
225 break;
226 case kMidiMessage_PolyPressure:
227 { UInt8 inKey = inValue >> 7;
228 UInt8 val = inValue & 0x7f;
229 handled = mMidiControlHandler->SetPolyPressure(inKey, val);
230 break;
231 }
232 default:
233 handled = false;
234 break;
235 }
236 }
237 return handled;
238 }
239
SostenutoOn(UInt32 inFrame)240 void SynthGroupElement::SostenutoOn(UInt32 inFrame)
241 {
242 #if DEBUG_PRINT
243 printf("SynthGroupElement::SostenutoOn\n");
244 #endif
245 if (!mSostenutoIsOn) {
246 mMidiControlHandler->SetController(kMidiController_Sostenuto, 127);
247 mSostenutoIsOn = true;
248 mNoteList[kNoteState_Sostenutoed].TransferAllFrom(&mNoteList[kNoteState_Attacked], inFrame);
249 }
250 }
251
SostenutoOff(UInt32 inFrame)252 void SynthGroupElement::SostenutoOff(UInt32 inFrame)
253 {
254 #if DEBUG_PRINT
255 printf("SynthGroupElement::SostenutoOff\n");
256 #endif
257 if (mSostenutoIsOn) {
258 mMidiControlHandler->SetController(kMidiController_Sostenuto, 0);
259 mSostenutoIsOn = false;
260 mNoteList[kNoteState_Attacked].TransferAllFrom(&mNoteList[kNoteState_Sostenutoed], inFrame);
261 if (mSustainIsOn)
262 mNoteList[kNoteState_ReleasedButSustained].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame);
263 else
264 mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame);
265 }
266 }
267
268
SustainOn(UInt32 inFrame)269 void SynthGroupElement::SustainOn(UInt32 inFrame)
270 {
271 #if DEBUG_PRINT
272 // printf("SynthGroupElement::SustainOn\n");
273 #endif
274 if (!mSustainIsOn) {
275 mMidiControlHandler->SetController(kMidiController_Sustain, 127);
276 mSustainIsOn = true;
277 }
278 }
279
SustainOff(UInt32 inFrame)280 void SynthGroupElement::SustainOff(UInt32 inFrame)
281 {
282 #if DEBUG_PRINT
283 // printf("SynthGroupElement::SustainOff\n");
284 #endif
285 if (mSustainIsOn) {
286 mMidiControlHandler->SetController(kMidiController_Sustain, 0);
287 mSustainIsOn = false;
288
289 mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSustained], inFrame);
290 }
291 }
292
AllNotesOff(UInt32 inFrame)293 void SynthGroupElement::AllNotesOff(UInt32 inFrame)
294 {
295 #if DEBUG_PRINT
296 printf("SynthGroupElement::AllNotesOff\n");
297 #endif
298 SynthNote *note;
299 for (UInt32 i=0 ; i<=kNoteState_Sostenutoed; ++i)
300 {
301 UInt32 newState = (i == kNoteState_Attacked) ?
302 kNoteState_Released : kNoteState_ReleasedButSostenutoed;
303 note = mNoteList[i].mHead;
304 while (note)
305 {
306 SynthNote *nextNote = note->mNext;
307
308 mNoteList[i].RemoveNote(note);
309 note->Release(inFrame);
310 mNoteList[newState].AddNote(note);
311
312 note = nextNote;
313 }
314 }
315 }
316
AllSoundOff(UInt32 inFrame)317 void SynthGroupElement::AllSoundOff(UInt32 inFrame)
318 {
319 #if DEBUG_PRINT
320 printf("SynthGroupElement::AllSoundOff\n");
321 #endif
322 SynthNote *note;
323
324 for (UInt32 i=0 ; i<kNumberOfActiveNoteStates; ++i)
325 {
326 note = mNoteList[i].mHead;
327 while (note)
328 {
329 SynthNote *nextNote = note->mNext;
330
331 mNoteList[i].RemoveNote(note);
332 note->FastRelease(inFrame);
333 mNoteList[kNoteState_FastReleased].AddNote(note);
334 GetAUInstrument()->DecNumActiveNotes();
335 note = nextNote;
336 }
337 }
338 }
339
ResetAllControllers(UInt32 inFrame)340 void SynthGroupElement::ResetAllControllers(UInt32 inFrame)
341 {
342 #if DEBUG_PRINT
343 printf("SynthGroupElement::ResetAllControllers\n");
344 #endif
345 mMidiControlHandler->Reset();
346 }
347
Render(SInt64 inAbsoluteSampleFrame,UInt32 inNumberFrames,AUScope & outputs)348 OSStatus SynthGroupElement::Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs)
349 {
350 // Avoid duplicate calls at same sample offset
351 if (inAbsoluteSampleFrame != mCurrentAbsoluteFrame)
352 {
353 mCurrentAbsoluteFrame = inAbsoluteSampleFrame;
354 AudioBufferList* buffArray[16];
355 UInt32 numOutputs = outputs.GetNumberOfElements();
356 for (UInt32 outBus = 0; outBus < numOutputs && outBus < 16; ++outBus)
357 {
358 buffArray[outBus] = &GetAudioUnit()->GetOutput(outBus)->GetBufferList();
359 }
360
361 for (UInt32 i=0 ; i<kNumberOfSoundingNoteStates; ++i)
362 {
363 SynthNote *note = mNoteList[i].mHead;
364 while (note)
365 {
366 #if DEBUG_PRINT_RENDER
367 printf("SynthGroupElement::Render: state %d, note %p\n", i, note);
368 #endif
369 SynthNote *nextNote = note->mNext;
370
371 OSStatus err = note->Render(inAbsoluteSampleFrame, inNumberFrames, buffArray, numOutputs);
372 if (err) return err;
373
374 note = nextNote;
375 }
376 }
377 }
378 return noErr;
379 }
380
381
382