1 /*
2      File: SynthElement.cpp
3  Abstract: SynthElement.h
4   Version: 1.1
5 
6  Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
7  Inc. ("Apple") in consideration of your agreement to the following
8  terms, and your use, installation, modification or redistribution of
9  this Apple software constitutes acceptance of these terms.  If you do
10  not agree with these terms, please do not use, install, modify or
11  redistribute this Apple software.
12 
13  In consideration of your agreement to abide by the following terms, and
14  subject to these terms, Apple grants you a personal, non-exclusive
15  license, under Apple's copyrights in this original Apple software (the
16  "Apple Software"), to use, reproduce, modify and redistribute the Apple
17  Software, with or without modifications, in source and/or binary forms;
18  provided that if you redistribute the Apple Software in its entirety and
19  without modifications, you must retain this notice and the following
20  text and disclaimers in all such redistributions of the Apple Software.
21  Neither the name, trademarks, service marks or logos of Apple Inc. may
22  be used to endorse or promote products derived from the Apple Software
23  without specific prior written permission from Apple.  Except as
24  expressly stated in this notice, no other rights or licenses, express or
25  implied, are granted by Apple herein, including but not limited to any
26  patent rights that may be infringed by your derivative works or by other
27  works in which the Apple Software may be incorporated.
28 
29  The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
30  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 
35  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42  POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2014 Apple Inc. All Rights Reserved.
45 
46 */
47 #include "SynthElement.h"
48 #include "AUInstrumentBase.h"
49 #include "AUMIDIDefs.h"
50 
51 #undef DEBUG_PRINT
52 #define DEBUG_PRINT 0
53 #define DEBUG_PRINT_NOTE 0
54 #define DEBUG_PRINT_RENDER 0
55 
56 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
MidiControls()57 MidiControls::MidiControls()
58 {
59 	Reset();
60 }
61 
Reset()62 void MidiControls::Reset()
63 {
64 	memset(mControls, 0, sizeof(mControls));
65 	memset(mPolyPressure, 0, sizeof(mPolyPressure));
66 	mMonoPressure = 0;
67 	mProgramChange = 0;
68 	mPitchBend = 0;
69 	mActiveRPN = 0;
70 	mActiveNRPN = 0;
71 	mActiveRPValue = 0;
72 	mActiveNRPValue = 0;
73 	mControls[kMidiController_Pan] = 64;
74 	mControls[kMidiController_Expression] = 127;
75 	mPitchBendDepth = 24 << 7;
76 	mFPitchBendDepth = 24.0f;
77 	mFPitchBend = 0.0f;
78 }
79 
80 
SynthElement(AUInstrumentBase * audioUnit,UInt32 inElement)81 SynthElement::SynthElement(AUInstrumentBase *audioUnit, UInt32 inElement)
82 	: AUElement(audioUnit), mIndex(inElement)
83 {
84 }
85 
~SynthElement()86 SynthElement::~SynthElement()
87 {
88 }
89 
SynthGroupElement(AUInstrumentBase * audioUnit,UInt32 inElement,MIDIControlHandler * inHandler)90 SynthGroupElement::SynthGroupElement(AUInstrumentBase *audioUnit, UInt32 inElement, MIDIControlHandler *inHandler)
91 	: SynthElement(audioUnit, inElement),
92 	mCurrentAbsoluteFrame(-1),
93 	mMidiControlHandler(inHandler),
94 	mSustainIsOn(false), mSostenutoIsOn(false), mOutputBus(0), mGroupID(kUnassignedGroup)
95 {
96 	for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i)
97 		mNoteList[i].mState = (SynthNoteState) i;
98 }
99 
~SynthGroupElement()100 SynthGroupElement::~SynthGroupElement()
101 {
102 	delete mMidiControlHandler;
103 }
104 
SetGroupID(MusicDeviceGroupID inGroup)105 void	SynthGroupElement::SetGroupID (MusicDeviceGroupID inGroup)
106 {
107 		// can't re-assign a group once its been assigned
108 	if (mGroupID != kUnassignedGroup) throw static_cast<OSStatus>(kAudioUnitErr_InvalidElement);
109 	mGroupID = inGroup;
110 }
111 
Reset()112 void SynthGroupElement::Reset()
113 {
114 #if DEBUG_PRINT
115 	printf("SynthGroupElement::Reset\n");
116 #endif
117 	mMidiControlHandler->Reset();
118 	for (UInt32 i=0; i<kNumberOfSoundingNoteStates; ++i)
119 		mNoteList[i].Empty();
120 }
121 
SynthPartElement(AUInstrumentBase * audioUnit,UInt32 inElement)122 SynthPartElement::SynthPartElement(AUInstrumentBase *audioUnit, UInt32 inElement)
123 	: SynthElement(audioUnit, inElement)
124 {
125 }
126 
127 // Return the SynthNote with the given inNoteID, if found.  If unreleasedOnly is true, only look for
128 // attacked and sostenutoed notes, otherwise search all states.  Return state of found note via outNoteState.
129 
GetNote(NoteInstanceID inNoteID,bool unreleasedOnly,UInt32 * outNoteState)130 SynthNote *SynthGroupElement::GetNote(NoteInstanceID inNoteID, bool unreleasedOnly, UInt32 *outNoteState)
131 {
132 #if DEBUG_PRINT_RENDER
133 	printf("SynthGroupElement::GetNote %d, unreleased = %d\n", inNoteID, unreleasedOnly);
134 #endif
135 	const UInt32 lastNoteState = unreleasedOnly ?
136 									(mSostenutoIsOn ? kNoteState_Sostenutoed : kNoteState_Attacked)
137 										: kNoteState_Released;
138 	SynthNote *note = NULL;
139 	// Search for notes in each successive state
140 	for (UInt32 noteState = kNoteState_Attacked; noteState <= lastNoteState; ++noteState)
141 	{
142 		if (outNoteState) *outNoteState = noteState;	// even if we find nothing
143 		note = mNoteList[noteState].mHead;
144 		while (note && note->mNoteID != inNoteID)
145 		{
146 #if DEBUG_PRINT_RENDER
147 			printf("   checking %p id: %d\n", note, note->mNoteID);
148 #endif
149 			note = note->mNext;
150 		}
151 		if (note)
152 		{
153 #if DEBUG_PRINT_RENDER
154 			printf("  found %p\n", note);
155 #endif
156 			break;
157 		}
158 	}
159 	return note;
160 }
161 
NoteOn(SynthNote * note,SynthPartElement * part,NoteInstanceID inNoteID,UInt32 inOffsetSampleFrame,const MusicDeviceNoteParams & inParams)162 void SynthGroupElement::NoteOn(SynthNote *note,
163 							   SynthPartElement *part,
164 							   NoteInstanceID inNoteID,
165 							   UInt32 inOffsetSampleFrame,
166 							   const MusicDeviceNoteParams &inParams)
167 {
168 #if DEBUG_PRINT_NOTE
169 	printf("SynthGroupElement::NoteOn %d\n", inNoteID);
170 #endif
171 	// TODO: CONSIDER FIXING this to not need to initialize mCurrentAbsoluteFrame to -1.
172 	UInt64 absoluteFrame = (mCurrentAbsoluteFrame == -1) ? inOffsetSampleFrame : mCurrentAbsoluteFrame + inOffsetSampleFrame;
173 	if (note->AttackNote(part, this, inNoteID, absoluteFrame, inOffsetSampleFrame, inParams)) {
174 		mNoteList[kNoteState_Attacked].AddNote(note);
175 	}
176 }
177 
NoteOff(NoteInstanceID inNoteID,UInt32 inFrame)178 void SynthGroupElement::NoteOff(NoteInstanceID inNoteID, UInt32 inFrame)
179 {
180 #if DEBUG_PRINT_NOTE
181 	printf("SynthGroupElement::NoteOff %d\n", inNoteID);
182 #endif
183 	UInt32 noteState = kNoteState_Attacked;
184 	SynthNote *note = GetNote(inNoteID, true, &noteState);	// asking for unreleased only
185 	if (note)
186 	{
187 #if DEBUG_PRINT_NOTE
188 		printf("  old note state: %d\n", note->mState);
189 #endif
190 		if (noteState == kNoteState_Attacked)
191 		{
192 			mNoteList[noteState].RemoveNote(note);
193 			if (mSustainIsOn) {
194 				mNoteList[kNoteState_ReleasedButSustained].AddNote(note);
195 			} else {
196 				note->Release(inFrame);
197 				mNoteList[kNoteState_Released].AddNote(note);
198 			}
199 #if DEBUG_PRINT_NOTE
200 			printf("  new note state: %d\n", note->mState);
201 #endif
202 		}
203 		else /* if (noteState == kNoteState_Sostenutoed) */
204 		{
205 			mNoteList[kNoteState_Sostenutoed].RemoveNote(note);
206 			mNoteList[kNoteState_ReleasedButSostenutoed].AddNote(note);
207 		}
208 	}
209 }
210 
NoteEnded(SynthNote * inNote,UInt32 inFrame)211 void SynthGroupElement::NoteEnded(SynthNote *inNote, UInt32 inFrame)
212 {
213 #if DEBUG_PRINT_NOTE
214 	printf("SynthGroupElement::NoteEnded: id %d state %d\n", inNote->mNoteID, inNote->mState);
215 #endif
216 	if (inNote->IsSounding()) {
217 		SynthNoteList *list = &mNoteList[inNote->GetState()];
218 		list->RemoveNote(inNote);
219 	}
220 
221 	GetAUInstrument()->AddFreeNote(inNote);
222 }
223 
NoteFastReleased(SynthNote * inNote)224 void SynthGroupElement::NoteFastReleased(SynthNote *inNote)
225 {
226 #if DEBUG_PRINT_NOTE
227 	printf("SynthGroupElement::NoteFastReleased id %d state %d\n", inNote->mNoteID, inNote->mState);
228 #endif
229 	if (inNote->IsActive()) {
230 		mNoteList[inNote->GetState()].RemoveNote(inNote);
231 		GetAUInstrument()->DecNumActiveNotes();
232 		mNoteList[kNoteState_FastReleased].AddNote(inNote);
233 	}
234 	else {
235 		Assert(true, "ASSERT FAILED:  Attempting to fast-release non-active note");
236 	}
237 }
238 
ChannelMessage(UInt16 controllerID,UInt16 inValue)239 bool SynthGroupElement::ChannelMessage(UInt16 controllerID, UInt16 inValue)
240 {
241 	bool handled = true;
242 #if DEBUG_PRINT
243 	printf("SynthGroupElement::ChannelMessage(0x%x, %u)\n", controllerID, inValue);
244 #endif
245 	// Sustain and sostenuto are "pedal events", and are handled during render cycle
246 	if (controllerID <= kMidiController_RPN_MSB && controllerID != kMidiController_Sustain && controllerID != kMidiController_Sostenuto)
247 		handled = mMidiControlHandler->SetController(controllerID, UInt8(inValue));
248 	else
249 	{
250 		switch (controllerID)
251 		{
252 			case kMidiMessage_ProgramChange:
253 				handled = mMidiControlHandler->SetProgramChange(inValue);
254 				break;
255 			case kMidiMessage_PitchWheel:
256 				handled = mMidiControlHandler->SetPitchWheel(inValue);
257 				break;
258 			case kMidiMessage_ChannelPressure:
259 #if DEBUG_PRINT
260 				printf("SynthGroupElement::ChannelMessage: Channel Pressure %u\n", inValue);
261 #endif
262 				handled = mMidiControlHandler->SetChannelPressure(UInt8(inValue));
263 				break;
264 			case kMidiMessage_PolyPressure:
265 			{	UInt8 inKey = inValue >> 7;
266 				UInt8 val = inValue & 0x7f;
267 				handled = mMidiControlHandler->SetPolyPressure(inKey, val);
268 				break;
269 			}
270 			default:
271 				handled = false;
272 				break;
273 		}
274 	}
275 	return handled;
276 }
277 
SostenutoOn(UInt32 inFrame)278 void SynthGroupElement::SostenutoOn(UInt32 inFrame)
279 {
280 #if DEBUG_PRINT
281 	printf("SynthGroupElement::SostenutoOn\n");
282 #endif
283 	if (!mSostenutoIsOn) {
284 		mMidiControlHandler->SetController(kMidiController_Sostenuto, 127);
285 		mSostenutoIsOn = true;
286 		mNoteList[kNoteState_Sostenutoed].TransferAllFrom(&mNoteList[kNoteState_Attacked], inFrame);
287 	}
288 }
289 
SostenutoOff(UInt32 inFrame)290 void SynthGroupElement::SostenutoOff(UInt32 inFrame)
291 {
292 #if DEBUG_PRINT
293 	printf("SynthGroupElement::SostenutoOff\n");
294 #endif
295 	if (mSostenutoIsOn) {
296 		mMidiControlHandler->SetController(kMidiController_Sostenuto, 0);
297 		mSostenutoIsOn = false;
298 		mNoteList[kNoteState_Attacked].TransferAllFrom(&mNoteList[kNoteState_Sostenutoed], inFrame);
299 		if (mSustainIsOn)
300 			mNoteList[kNoteState_ReleasedButSustained].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame);
301 		else
302 			mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSostenutoed], inFrame);
303 	}
304 }
305 
306 
SustainOn(UInt32 inFrame)307 void SynthGroupElement::SustainOn(UInt32 inFrame)
308 {
309 #if DEBUG_PRINT
310 //	printf("SynthGroupElement::SustainOn\n");
311 #endif
312 	if (!mSustainIsOn) {
313 		mMidiControlHandler->SetController(kMidiController_Sustain, 127);
314 		mSustainIsOn = true;
315 	}
316 }
317 
SustainOff(UInt32 inFrame)318 void SynthGroupElement::SustainOff(UInt32 inFrame)
319 {
320 #if DEBUG_PRINT
321 //	printf("SynthGroupElement::SustainOff\n");
322 #endif
323 	if (mSustainIsOn) {
324 		mMidiControlHandler->SetController(kMidiController_Sustain, 0);
325 		mSustainIsOn = false;
326 
327 		mNoteList[kNoteState_Released].TransferAllFrom(&mNoteList[kNoteState_ReleasedButSustained], inFrame);
328 	}
329 }
330 
AllNotesOff(UInt32 inFrame)331 void SynthGroupElement::AllNotesOff(UInt32 inFrame)
332 {
333 #if DEBUG_PRINT
334 	printf("SynthGroupElement::AllNotesOff\n");
335 #endif
336 	SynthNote *note;
337 	for (UInt32 i=0 ; i<=kNoteState_Sostenutoed; ++i)
338 	{
339 		UInt32 newState = (i == kNoteState_Attacked) ?
340 			kNoteState_Released : kNoteState_ReleasedButSostenutoed;
341 		note = mNoteList[i].mHead;
342 		while (note)
343 		{
344 			SynthNote *nextNote = note->mNext;
345 
346 			mNoteList[i].RemoveNote(note);
347 			note->Release(inFrame);
348 			mNoteList[newState].AddNote(note);
349 
350 			note = nextNote;
351 		}
352 	}
353 }
354 
AllSoundOff(UInt32 inFrame)355 void SynthGroupElement::AllSoundOff(UInt32 inFrame)
356 {
357 #if DEBUG_PRINT
358 	printf("SynthGroupElement::AllSoundOff\n");
359 #endif
360 	SynthNote *note;
361 
362 	for (UInt32 i=0 ; i<kNumberOfActiveNoteStates; ++i)
363 	{
364 		note = mNoteList[i].mHead;
365 		while (note)
366 		{
367 			SynthNote *nextNote = note->mNext;
368 
369 			mNoteList[i].RemoveNote(note);
370 			note->FastRelease(inFrame);
371 			mNoteList[kNoteState_FastReleased].AddNote(note);
372 			GetAUInstrument()->DecNumActiveNotes();
373 			note = nextNote;
374 		}
375 	}
376 }
377 
ResetAllControllers(UInt32 inFrame)378 void SynthGroupElement::ResetAllControllers(UInt32 inFrame)
379 {
380 #if DEBUG_PRINT
381 	printf("SynthGroupElement::ResetAllControllers\n");
382 #endif
383 	mMidiControlHandler->Reset();
384 }
385 
Render(SInt64 inAbsoluteSampleFrame,UInt32 inNumberFrames,AUScope & outputs)386 OSStatus SynthGroupElement::Render(SInt64 inAbsoluteSampleFrame, UInt32 inNumberFrames, AUScope &outputs)
387 {
388 	// Avoid duplicate calls at same sample offset
389 	if (inAbsoluteSampleFrame != mCurrentAbsoluteFrame)
390 	{
391 		mCurrentAbsoluteFrame = inAbsoluteSampleFrame;
392 		AudioBufferList* buffArray[16];
393 		UInt32 numOutputs = outputs.GetNumberOfElements();
394 		for (UInt32 outBus = 0; outBus < numOutputs && outBus < 16; ++outBus)
395 		{
396 			buffArray[outBus] = &GetAudioUnit()->GetOutput(outBus)->GetBufferList();
397 		}
398 
399 		for (UInt32 i=0 ; i<kNumberOfSoundingNoteStates; ++i)
400 		{
401 			SynthNote *note = mNoteList[i].mHead;
402 			while (note)
403 			{
404 #if DEBUG_PRINT_RENDER
405 				printf("SynthGroupElement::Render: state %d, note %p\n", i, note);
406 #endif
407 				SynthNote *nextNote = note->mNext;
408 
409 				OSStatus err = note->Render(inAbsoluteSampleFrame, inNumberFrames, buffArray, numOutputs);
410 				if (err) return err;
411 
412 				note = nextNote;
413 			}
414 		}
415 	}
416 	return noErr;
417 }
418 
419 
420