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, ¬eState); // 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