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, &noteState);	// 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