4 |
* * |
* * |
5 |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
7 |
* Copyright (C) 2009-2010 Christian Schoenebeck and Grigor Iliev * |
* Copyright (C) 2009-2012 Christian Schoenebeck and Grigor Iliev * |
8 |
|
* Copyright (C) 2012-2016 Christian Schoenebeck and Andreas Persson * |
9 |
* * |
* * |
10 |
* This program is free software; you can redistribute it and/or modify * |
* This program is free software; you can redistribute it and/or modify * |
11 |
* it under the terms of the GNU General Public License as published by * |
* it under the terms of the GNU General Public License as published by * |
35 |
{ |
{ |
36 |
pEngine = NULL; |
pEngine = NULL; |
37 |
pEvents = NULL; // we allocate when we retrieve the right Engine object |
pEvents = NULL; // we allocate when we retrieve the right Engine object |
38 |
|
delayedEvents.pList = NULL; |
39 |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
40 |
InstrumentIdx = -1; |
InstrumentIdx = -1; |
41 |
InstrumentStat = -1; |
InstrumentStat = -1; |
43 |
pChannelRight = NULL; |
pChannelRight = NULL; |
44 |
AudioDeviceChannelLeft = -1; |
AudioDeviceChannelLeft = -1; |
45 |
AudioDeviceChannelRight = -1; |
AudioDeviceChannelRight = -1; |
|
pMidiInputPort = NULL; |
|
46 |
midiChannel = midi_chan_all; |
midiChannel = midi_chan_all; |
47 |
ResetControllers(); |
ResetControllers(); |
48 |
PortamentoMode = false; |
PortamentoMode = false; |
49 |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
PortamentoTime = CONFIG_PORTAMENTO_TIME_DEFAULT; |
50 |
|
pScript = NULL; |
51 |
} |
} |
52 |
|
|
53 |
AbstractEngineChannel::~AbstractEngineChannel() { |
AbstractEngineChannel::~AbstractEngineChannel() { |
102 |
|
|
103 |
void AbstractEngineChannel::Reset() { |
void AbstractEngineChannel::Reset() { |
104 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
105 |
ResetInternal(); |
ResetInternal(false/*don't reset engine*/); |
106 |
ResetControllers(); |
ResetControllers(); |
107 |
if (pEngine) { |
if (pEngine) { |
108 |
pEngine->Enable(); |
pEngine->Enable(); |
114 |
Pitch = 0; |
Pitch = 0; |
115 |
GlobalVolume = 1.0f; |
GlobalVolume = 1.0f; |
116 |
MidiVolume = 1.0; |
MidiVolume = 1.0; |
|
GlobalPanLeft = 1.0f; |
|
|
GlobalPanRight = 1.0f; |
|
117 |
iLastPanRequest = 64; |
iLastPanRequest = 64; |
118 |
GlobalTranspose = 0; |
GlobalTranspose = 0; |
119 |
// set all MIDI controller values to zero |
// set all MIDI controller values to zero |
130 |
/** |
/** |
131 |
* This method is not thread safe! |
* This method is not thread safe! |
132 |
*/ |
*/ |
133 |
void AbstractEngineChannel::ResetInternal() { |
void AbstractEngineChannel::ResetInternal(bool bResetEngine) { |
134 |
CurrentKeyDimension = 0; |
CurrentKeyDimension = 0; |
135 |
PortamentoPos = -1.0f; // no portamento active yet |
PortamentoPos = -1.0f; // no portamento active yet |
136 |
|
|
137 |
|
// delete all active instrument script events |
138 |
|
if (pScript) pScript->resetEvents(); |
139 |
|
|
140 |
|
// free all delayed MIDI events |
141 |
|
delayedEvents.clear(); |
142 |
|
|
143 |
// delete all input events |
// delete all input events |
144 |
pEventQueue->init(); |
pEventQueue->init(); |
145 |
|
|
146 |
if (pEngine) pEngine->ResetInternal(); |
if (bResetEngine && pEngine) pEngine->ResetInternal(); |
147 |
|
|
148 |
// status of engine channel has changed, so set notify flag |
// status of engine channel has changed, so set notify flag |
149 |
bStatusChanged = true; |
bStatusChanged = true; |
188 |
int iMidiPan = int(f * 64.0f) + 64; |
int iMidiPan = int(f * 64.0f) + 64; |
189 |
if (iMidiPan > 127) iMidiPan = 127; |
if (iMidiPan > 127) iMidiPan = 127; |
190 |
else if (iMidiPan < 0) iMidiPan = 0; |
else if (iMidiPan < 0) iMidiPan = 0; |
|
GlobalPanLeft = AbstractEngine::PanCurve[128 - iMidiPan]; |
|
|
GlobalPanRight = AbstractEngine::PanCurve[iMidiPan]; |
|
191 |
iLastPanRequest = iMidiPan; |
iLastPanRequest = iMidiPan; |
192 |
} |
} |
193 |
|
|
195 |
return (pEngine) ? pEngine->pAudioOutputDevice : NULL; |
return (pEngine) ? pEngine->pAudioOutputDevice : NULL; |
196 |
} |
} |
197 |
|
|
198 |
|
/** |
199 |
|
* Gets thread safe access to the currently connected audio output |
200 |
|
* device from other threads than the lscp thread. |
201 |
|
*/ |
202 |
|
AudioOutputDevice* AbstractEngineChannel::GetAudioOutputDeviceSafe() { |
203 |
|
LockGuard lock(EngineMutex); |
204 |
|
return GetAudioOutputDevice(); |
205 |
|
} |
206 |
|
|
207 |
void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) { |
void AbstractEngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) { |
208 |
if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet."); |
if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet."); |
209 |
|
|
236 |
} |
} |
237 |
} |
} |
238 |
|
|
239 |
|
void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort) { |
240 |
|
if (!pMidiPort) return; |
241 |
|
|
242 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
243 |
|
|
244 |
|
// check if connection already exists |
245 |
|
for (int i = 0; i < connections->size(); ++i) |
246 |
|
if ((*connections)[i] == pMidiPort) |
247 |
|
return; // to avoid endless recursion |
248 |
|
|
249 |
|
connections->add(pMidiPort); |
250 |
|
|
251 |
|
// inform MIDI port about this new connection |
252 |
|
pMidiPort->Connect(this, MidiChannel()); |
253 |
|
} |
254 |
|
|
255 |
|
void AbstractEngineChannel::Disconnect(MidiInputPort* pMidiPort) { |
256 |
|
if (!pMidiPort) return; |
257 |
|
|
258 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
259 |
|
|
260 |
|
for (int i = 0; i < connections->size(); ++i) { |
261 |
|
if ((*connections)[i] == pMidiPort) { |
262 |
|
connections->remove(i); |
263 |
|
// inform MIDI port about this disconnection |
264 |
|
pMidiPort->Disconnect(this); |
265 |
|
return; |
266 |
|
} |
267 |
|
} |
268 |
|
} |
269 |
|
|
270 |
|
void AbstractEngineChannel::DisconnectAllMidiInputPorts() { |
271 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
272 |
|
ArrayList<MidiInputPort*> clonedList = *connections; |
273 |
|
connections->clear(); |
274 |
|
for (int i = 0; i < clonedList.size(); ++i) clonedList[i]->Disconnect(this); |
275 |
|
} |
276 |
|
|
277 |
|
uint AbstractEngineChannel::GetMidiInputPortCount() { |
278 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
279 |
|
return connections->size(); |
280 |
|
} |
281 |
|
|
282 |
|
MidiInputPort* AbstractEngineChannel::GetMidiInputPort(uint index) { |
283 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
284 |
|
return (index < connections->size()) ? (*connections)[index] : NULL; |
285 |
|
} |
286 |
|
|
287 |
|
// deprecated (just for API backward compatibility) - may be removed in future |
288 |
void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) { |
void AbstractEngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) { |
289 |
if (!pMidiPort || pMidiPort == this->pMidiInputPort) return; |
if (!pMidiPort) return; |
290 |
DisconnectMidiInputPort(); |
|
291 |
this->pMidiInputPort = pMidiPort; |
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
292 |
this->midiChannel = MidiChannel; |
|
293 |
|
// check against endless recursion |
294 |
|
if (connections->size() == 1 && (*connections)[0] == pMidiPort && this->midiChannel == MidiChannel) |
295 |
|
return; |
296 |
|
|
297 |
|
if (!isValidMidiChan(MidiChannel)) |
298 |
|
throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); |
299 |
|
|
300 |
|
this->midiChannel = MidiChannel; |
301 |
|
|
302 |
|
// disconnect all currently connected MIDI ports |
303 |
|
ArrayList<MidiInputPort*> clonedList = *connections; |
304 |
|
connections->clear(); |
305 |
|
for (int i = 0; i < clonedList.size(); ++i) |
306 |
|
clonedList[i]->Disconnect(this); |
307 |
|
|
308 |
|
// connect the new port |
309 |
|
connections->add(pMidiPort); |
310 |
pMidiPort->Connect(this, MidiChannel); |
pMidiPort->Connect(this, MidiChannel); |
311 |
} |
} |
312 |
|
|
313 |
|
// deprecated (just for API backward compatibility) - may be removed in future |
314 |
void AbstractEngineChannel::DisconnectMidiInputPort() { |
void AbstractEngineChannel::DisconnectMidiInputPort() { |
315 |
MidiInputPort* pOldPort = this->pMidiInputPort; |
DisconnectAllMidiInputPorts(); |
|
this->pMidiInputPort = NULL; |
|
|
if (pOldPort) pOldPort->Disconnect(this); |
|
316 |
} |
} |
317 |
|
|
318 |
|
// deprecated (just for API backward compatibility) - may be removed in future |
319 |
MidiInputPort* AbstractEngineChannel::GetMidiInputPort() { |
MidiInputPort* AbstractEngineChannel::GetMidiInputPort() { |
320 |
return pMidiInputPort; |
return GetMidiInputPort(0); |
321 |
} |
} |
322 |
|
|
323 |
midi_chan_t AbstractEngineChannel::MidiChannel() { |
midi_chan_t AbstractEngineChannel::MidiChannel() { |
324 |
return midiChannel; |
return midiChannel; |
325 |
} |
} |
326 |
|
|
327 |
|
void AbstractEngineChannel::SetMidiChannel(midi_chan_t MidiChannel) { |
328 |
|
if (this->midiChannel == MidiChannel) return; |
329 |
|
if (!isValidMidiChan(MidiChannel)) |
330 |
|
throw MidiInputException("Invalid MIDI channel (" + ToString(int(MidiChannel)) + ")"); |
331 |
|
|
332 |
|
this->midiChannel = MidiChannel; |
333 |
|
|
334 |
|
Sync< ArrayList<MidiInputPort*> > connections = midiInputs.back(); |
335 |
|
ArrayList<MidiInputPort*> clonedList = *connections; |
336 |
|
|
337 |
|
DisconnectAllMidiInputPorts(); |
338 |
|
|
339 |
|
for (int i = 0; i < clonedList.size(); ++i) Connect(clonedList[i]); |
340 |
|
} |
341 |
|
|
342 |
void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) { |
void AbstractEngineChannel::Connect(VirtualMidiDevice* pDevice) { |
343 |
// double buffer ... double work ... |
// double buffer ... double work ... |
344 |
{ |
{ |
372 |
* @param Key - MIDI key number of the triggered key |
* @param Key - MIDI key number of the triggered key |
373 |
* @param Velocity - MIDI velocity value of the triggered key |
* @param Velocity - MIDI velocity value of the triggered key |
374 |
*/ |
*/ |
375 |
void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity) { |
void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { |
376 |
if (pEngine) { |
if (pEngine) { |
377 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
378 |
|
LockGuard g; |
379 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
380 |
|
|
381 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
382 |
event.Type = Event::type_note_on; |
event.Type = Event::type_note_on; |
383 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
384 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
385 |
|
event.Param.Note.Channel = MidiChannel; |
386 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
387 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
388 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
410 |
* @param FragmentPos - sample point position in the current audio |
* @param FragmentPos - sample point position in the current audio |
411 |
* fragment to which this event belongs to |
* fragment to which this event belongs to |
412 |
*/ |
*/ |
413 |
void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) { |
void AbstractEngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) { |
414 |
if (FragmentPos < 0) { |
if (FragmentPos < 0) { |
415 |
dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!")); |
dmsg(1,("EngineChannel::SendNoteOn(): negative FragmentPos! Seems MIDI driver is buggy!")); |
416 |
} |
} |
417 |
else if (pEngine) { |
else if (pEngine) { |
418 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
419 |
|
LockGuard g; |
420 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
421 |
|
|
422 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
423 |
event.Type = Event::type_note_on; |
event.Type = Event::type_note_on; |
424 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
425 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
426 |
|
event.Param.Note.Channel = MidiChannel; |
427 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
428 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
429 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
449 |
* @param Key - MIDI key number of the released key |
* @param Key - MIDI key number of the released key |
450 |
* @param Velocity - MIDI release velocity value of the released key |
* @param Velocity - MIDI release velocity value of the released key |
451 |
*/ |
*/ |
452 |
void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity) { |
void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel) { |
453 |
if (pEngine) { |
if (pEngine) { |
454 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
455 |
|
LockGuard g; |
456 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
457 |
|
|
458 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
459 |
event.Type = Event::type_note_off; |
event.Type = Event::type_note_off; |
460 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
461 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
462 |
|
event.Param.Note.Channel = MidiChannel; |
463 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
464 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
465 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
487 |
* @param FragmentPos - sample point position in the current audio |
* @param FragmentPos - sample point position in the current audio |
488 |
* fragment to which this event belongs to |
* fragment to which this event belongs to |
489 |
*/ |
*/ |
490 |
void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, int32_t FragmentPos) { |
void AbstractEngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity, uint8_t MidiChannel, int32_t FragmentPos) { |
491 |
if (FragmentPos < 0) { |
if (FragmentPos < 0) { |
492 |
dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!")); |
dmsg(1,("EngineChannel::SendNoteOff(): negative FragmentPos! Seems MIDI driver is buggy!")); |
493 |
} |
} |
494 |
else if (pEngine) { |
else if (pEngine) { |
495 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
496 |
|
LockGuard g; |
497 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
498 |
|
|
499 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
500 |
event.Type = Event::type_note_off; |
event.Type = Event::type_note_off; |
501 |
event.Param.Note.Key = Key; |
event.Param.Note.Key = Key; |
502 |
event.Param.Note.Velocity = Velocity; |
event.Param.Note.Velocity = Velocity; |
503 |
|
event.Param.Note.Channel = MidiChannel; |
504 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
505 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
506 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
525 |
* |
* |
526 |
* @param Pitch - MIDI pitch value (-8192 ... +8191) |
* @param Pitch - MIDI pitch value (-8192 ... +8191) |
527 |
*/ |
*/ |
528 |
void AbstractEngineChannel::SendPitchbend(int Pitch) { |
void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel) { |
529 |
if (pEngine) { |
if (pEngine) { |
530 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
531 |
|
LockGuard g; |
532 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
533 |
|
|
534 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
535 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
536 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
537 |
|
event.Param.Pitch.Channel = MidiChannel; |
538 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
539 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
540 |
else dmsg(1,("EngineChannel: Input event queue full!")); |
else dmsg(1,("EngineChannel: Input event queue full!")); |
551 |
* @param FragmentPos - sample point position in the current audio |
* @param FragmentPos - sample point position in the current audio |
552 |
* fragment to which this event belongs to |
* fragment to which this event belongs to |
553 |
*/ |
*/ |
554 |
void AbstractEngineChannel::SendPitchbend(int Pitch, int32_t FragmentPos) { |
void AbstractEngineChannel::SendPitchbend(int Pitch, uint8_t MidiChannel, int32_t FragmentPos) { |
555 |
if (FragmentPos < 0) { |
if (FragmentPos < 0) { |
556 |
dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!")); |
dmsg(1,("AbstractEngineChannel::SendPitchBend(): negative FragmentPos! Seems MIDI driver is buggy!")); |
557 |
} |
} |
558 |
else if (pEngine) { |
else if (pEngine) { |
559 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
560 |
|
LockGuard g; |
561 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
562 |
|
|
563 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
564 |
event.Type = Event::type_pitchbend; |
event.Type = Event::type_pitchbend; |
565 |
event.Param.Pitch.Pitch = Pitch; |
event.Param.Pitch.Pitch = Pitch; |
566 |
|
event.Param.Pitch.Channel = MidiChannel; |
567 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
568 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
569 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
579 |
* @param Controller - MIDI controller number of the occured control change |
* @param Controller - MIDI controller number of the occured control change |
580 |
* @param Value - value of the control change |
* @param Value - value of the control change |
581 |
*/ |
*/ |
582 |
void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value) { |
void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel) { |
583 |
if (pEngine) { |
if (pEngine) { |
584 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
585 |
|
LockGuard g; |
586 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
587 |
|
|
588 |
Event event = pEngine->pEventGenerator->CreateEvent(); |
Event event = pEngine->pEventGenerator->CreateEvent(); |
589 |
event.Type = Event::type_control_change; |
event.Type = Event::type_control_change; |
590 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
591 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
592 |
|
event.Param.CC.Channel = MidiChannel; |
593 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
594 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
595 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
607 |
* @param FragmentPos - sample point position in the current audio |
* @param FragmentPos - sample point position in the current audio |
608 |
* fragment to which this event belongs to |
* fragment to which this event belongs to |
609 |
*/ |
*/ |
610 |
void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, int32_t FragmentPos) { |
void AbstractEngineChannel::SendControlChange(uint8_t Controller, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { |
611 |
if (FragmentPos < 0) { |
if (FragmentPos < 0) { |
612 |
dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!")); |
dmsg(1,("AbstractEngineChannel::SendControlChange(): negative FragmentPos! Seems MIDI driver is buggy!")); |
613 |
} |
} |
614 |
else if (pEngine) { |
else if (pEngine) { |
615 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
616 |
|
LockGuard g; |
617 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
618 |
|
|
619 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
620 |
event.Type = Event::type_control_change; |
event.Type = Event::type_control_change; |
621 |
event.Param.CC.Controller = Controller; |
event.Param.CC.Controller = Controller; |
622 |
event.Param.CC.Value = Value; |
event.Param.CC.Value = Value; |
623 |
|
event.Param.CC.Channel = MidiChannel; |
624 |
event.pEngineChannel = this; |
event.pEngineChannel = this; |
625 |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
626 |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
627 |
} |
} |
628 |
} |
} |
629 |
|
|
630 |
|
void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel) { |
631 |
|
if (pEngine) { |
632 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
633 |
|
LockGuard g; |
634 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
635 |
|
|
636 |
|
Event event = pEngine->pEventGenerator->CreateEvent(); |
637 |
|
event.Type = Event::type_channel_pressure; |
638 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
639 |
|
event.Param.ChannelPressure.Value = Value; |
640 |
|
event.Param.ChannelPressure.Channel = MidiChannel; |
641 |
|
event.pEngineChannel = this; |
642 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
643 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
644 |
|
} |
645 |
|
} |
646 |
|
|
647 |
|
void AbstractEngineChannel::SendChannelPressure(uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { |
648 |
|
if (pEngine) { |
649 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
650 |
|
LockGuard g; |
651 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
652 |
|
|
653 |
|
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
654 |
|
event.Type = Event::type_channel_pressure; |
655 |
|
event.Param.ChannelPressure.Controller = CTRL_TABLE_IDX_AFTERTOUCH; // required for instrument scripts |
656 |
|
event.Param.ChannelPressure.Value = Value; |
657 |
|
event.Param.ChannelPressure.Channel = MidiChannel; |
658 |
|
event.pEngineChannel = this; |
659 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
660 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
661 |
|
} |
662 |
|
} |
663 |
|
|
664 |
|
void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel) { |
665 |
|
if (pEngine) { |
666 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
667 |
|
LockGuard g; |
668 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
669 |
|
|
670 |
|
Event event = pEngine->pEventGenerator->CreateEvent(); |
671 |
|
event.Type = Event::type_note_pressure; |
672 |
|
event.Param.NotePressure.Key = Key; |
673 |
|
event.Param.NotePressure.Value = Value; |
674 |
|
event.Param.NotePressure.Channel = MidiChannel; |
675 |
|
event.pEngineChannel = this; |
676 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
677 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
678 |
|
} |
679 |
|
} |
680 |
|
|
681 |
|
void AbstractEngineChannel::SendPolyphonicKeyPressure(uint8_t Key, uint8_t Value, uint8_t MidiChannel, int32_t FragmentPos) { |
682 |
|
if (pEngine) { |
683 |
|
// protection in case there are more than 1 MIDI input threads sending MIDI events to this EngineChannel |
684 |
|
LockGuard g; |
685 |
|
if (hasMultipleMIDIInputs()) g = LockGuard(MidiInputMutex); |
686 |
|
|
687 |
|
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
688 |
|
event.Type = Event::type_note_pressure; |
689 |
|
event.Param.NotePressure.Key = Key; |
690 |
|
event.Param.NotePressure.Value = Value; |
691 |
|
event.Param.NotePressure.Channel = MidiChannel; |
692 |
|
event.pEngineChannel = this; |
693 |
|
if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event); |
694 |
|
else dmsg(1,("AbstractEngineChannel: Input event queue full!")); |
695 |
|
} |
696 |
|
} |
697 |
|
|
698 |
|
bool AbstractEngineChannel::applyTranspose(Event* event) { |
699 |
|
if (event->Type != Event::type_note_on && event->Type != Event::type_note_off) |
700 |
|
return true; // event OK (not a note event, nothing to do with it here) |
701 |
|
|
702 |
|
//HACK: we should better add the transpose value only to the most mandatory places (like for retrieving the region and calculating the tuning), because otherwise voices will unintendedly survive when changing transpose while playing |
703 |
|
const int k = event->Param.Note.Key + GlobalTranspose; |
704 |
|
if (k < 0 || k > 127) |
705 |
|
return false; // bad event, drop it |
706 |
|
|
707 |
|
event->Param.Note.Key = k; |
708 |
|
|
709 |
|
return true; // event OK |
710 |
|
} |
711 |
|
|
712 |
/** |
/** |
713 |
* Copy all events from the engine channel's input event queue buffer to |
* Copy all events from the engine channel's input event queue buffer to |
714 |
* the internal event list. This will be done at the beginning of each |
* the internal event list. This will be done at the beginning of each |
726 |
// import events from pure software MIDI "devices" |
// import events from pure software MIDI "devices" |
727 |
// (e.g. virtual keyboard in instrument editor) |
// (e.g. virtual keyboard in instrument editor) |
728 |
{ |
{ |
729 |
|
const uint8_t channel = MidiChannel() == midi_chan_all ? 0 : MidiChannel(); |
730 |
const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices |
const int FragmentPos = 0; // randomly chosen, we don't care about jitter for virtual MIDI devices |
731 |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
Event event = pEngine->pEventGenerator->CreateEvent(FragmentPos); |
732 |
VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device |
VirtualMidiDevice::event_t devEvent; // the event format we get from the virtual MIDI device |
744 |
event.Type = Event::type_note_on; |
event.Type = Event::type_note_on; |
745 |
event.Param.Note.Key = devEvent.Arg1; |
event.Param.Note.Key = devEvent.Arg1; |
746 |
event.Param.Note.Velocity = devEvent.Arg2; |
event.Param.Note.Velocity = devEvent.Arg2; |
747 |
|
event.Param.Note.Channel = channel; |
748 |
|
// apply transpose setting to (note on/off) event |
749 |
|
if (!applyTranspose(&event)) |
750 |
|
continue; // note value is out of range, so drop this event |
751 |
|
// assign a new note to this note-on event |
752 |
|
if (!pEngine->LaunchNewNote(this, &event)) |
753 |
|
continue; // failed launching new note, so drop this event |
754 |
break; |
break; |
755 |
case VirtualMidiDevice::EVENT_TYPE_NOTEOFF: |
case VirtualMidiDevice::EVENT_TYPE_NOTEOFF: |
756 |
event.Type = Event::type_note_off; |
event.Type = Event::type_note_off; |
757 |
event.Param.Note.Key = devEvent.Arg1; |
event.Param.Note.Key = devEvent.Arg1; |
758 |
event.Param.Note.Velocity = devEvent.Arg2; |
event.Param.Note.Velocity = devEvent.Arg2; |
759 |
|
event.Param.Note.Channel = channel; |
760 |
|
if (!applyTranspose(&event)) |
761 |
|
continue; // note value is out of range, so drop this event |
762 |
break; |
break; |
763 |
case VirtualMidiDevice::EVENT_TYPE_CC: |
case VirtualMidiDevice::EVENT_TYPE_CC: |
764 |
event.Type = Event::type_control_change; |
switch (devEvent.Arg1) { |
765 |
event.Param.CC.Controller = devEvent.Arg1; |
case 0: // bank select MSB ... |
766 |
event.Param.CC.Value = devEvent.Arg2; |
SetMidiBankMsb(devEvent.Arg2); |
767 |
|
continue; // don't push this event into FIFO |
768 |
|
case 32: // bank select LSB ... |
769 |
|
SetMidiBankLsb(devEvent.Arg2); |
770 |
|
continue; // don't push this event into FIFO |
771 |
|
default: // regular MIDI CC ... |
772 |
|
event.Type = Event::type_control_change; |
773 |
|
event.Param.CC.Controller = devEvent.Arg1; |
774 |
|
event.Param.CC.Value = devEvent.Arg2; |
775 |
|
event.Param.CC.Channel = channel; |
776 |
|
} |
777 |
|
break; |
778 |
|
case VirtualMidiDevice::EVENT_TYPE_PITCHBEND: |
779 |
|
event.Type = Event::type_pitchbend; |
780 |
|
event.Param.Pitch.Pitch = int(devEvent.Arg2 << 7 | devEvent.Arg1) - 8192; |
781 |
|
event.Param.Pitch.Channel = channel; |
782 |
break; |
break; |
783 |
|
case VirtualMidiDevice::EVENT_TYPE_PROGRAM: |
784 |
|
SendProgramChange(devEvent.Arg1); |
785 |
|
continue; // don't push this event into FIFO |
786 |
default: |
default: |
787 |
std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" |
std::cerr << "AbstractEngineChannel::ImportEvents() ERROR: unknown event type (" |
788 |
<< devEvent.Type << "). This is a bug!"; |
<< devEvent.Type << "). This is a bug!"; |
814 |
pEvent->ResetFragmentPos(); |
pEvent->ResetFragmentPos(); |
815 |
break; |
break; |
816 |
} |
} |
|
// copy event to internal event list |
|
817 |
if (pEvents->poolIsEmpty()) { |
if (pEvents->poolIsEmpty()) { |
818 |
dmsg(1,("Event pool emtpy!\n")); |
dmsg(1,("Event pool emtpy!\n")); |
819 |
break; |
break; |
820 |
} |
} |
821 |
|
// apply transpose setting to (note on/off) event |
822 |
|
if (!applyTranspose(pEvent)) |
823 |
|
continue; // it's a note event which has a note value out of range, so drop this event |
824 |
|
// assign a new note to this event (if its a note-on event) |
825 |
|
if (pEvent->Type == Event::type_note_on) |
826 |
|
if (!pEngine->LaunchNewNote(this, pEvent)) |
827 |
|
continue; // failed launching new note, so drop this event |
828 |
|
// copy event to internal event list |
829 |
*pEvents->allocAppend() = *pEvent; |
*pEvents->allocAppend() = *pEvent; |
830 |
} |
} |
831 |
eventQueueReader.free(); // free all copied events from input queue |
eventQueueReader.free(); // free all copied events from input queue |
832 |
} |
} |
833 |
|
|
834 |
|
/** |
835 |
|
* Called by real-time instrument script functions to schedule a new event |
836 |
|
* @a delay microseconds in future. |
837 |
|
* |
838 |
|
* @b IMPORTANT: for the supplied @a delay to be scheduled correctly, the |
839 |
|
* passed @a pEvent must be assigned a valid fragment time within the |
840 |
|
* current audio fragment boundaries. That fragment time will be used by |
841 |
|
* this method as basis for interpreting what "now" acutally is, and thus |
842 |
|
* it will be used as basis for calculating the precise scheduling time |
843 |
|
* for @a delay. The easiest way to achieve this is by copying a recent |
844 |
|
* event which happened within the current audio fragment cycle: i.e. the |
845 |
|
* original event which caused calling this method here. |
846 |
|
* |
847 |
|
* @param pEvent - event to be scheduled in future (event data will be copied) |
848 |
|
* @param delay - amount of microseconds in future (from now) when event shall be processed |
849 |
|
* @returns unique event ID of scheduled new event, or NULL on error |
850 |
|
*/ |
851 |
|
event_id_t AbstractEngineChannel::ScheduleEventMicroSec(const Event* pEvent, int delay) { |
852 |
|
dmsg(3,("AbstractEngineChannel::ScheduleEventMicroSec(Event.Type=%d,delay=%d)\n", pEvent->Type, delay)); |
853 |
|
RTList<Event>::Iterator itEvent = pEvents->allocAppend(); |
854 |
|
if (!itEvent) { |
855 |
|
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): Event pool emtpy!\n")); |
856 |
|
return 0; |
857 |
|
} |
858 |
|
RTList<ScheduledEvent>::Iterator itNode = delayedEvents.schedulerNodes.allocAppend(); |
859 |
|
if (!itNode) { // scheduler node pool empty ... |
860 |
|
dmsg(1,("AbstractEngineChannel::ScheduleEventMicroSec(): ScheduledEvent pool empty!\n")); |
861 |
|
pEvents->free(itEvent); |
862 |
|
return 0; |
863 |
|
} |
864 |
|
// copy passed event |
865 |
|
*itEvent = *pEvent; |
866 |
|
// move copied event to list of delayed events |
867 |
|
itEvent = itEvent.moveToEndOf(delayedEvents.pList); |
868 |
|
// connect scheduler node with the copied event |
869 |
|
itNode->itEvent = itEvent; |
870 |
|
// add entry to time sorted scheduler queue for copied event |
871 |
|
pEngine->pEventGenerator->scheduleAheadMicroSec( |
872 |
|
delayedEvents.queue, *itNode, itEvent->FragmentPos(), delay |
873 |
|
); |
874 |
|
//dmsg(5,("ScheduledEvent queue size: %d\n", delayedEvents.queue.size())); |
875 |
|
return pEvents->getID(itEvent); |
876 |
|
} |
877 |
|
|
878 |
|
/** |
879 |
|
* Called by real-time instrument script functions to ignore the event |
880 |
|
* reflected by given event ID. The event will be freed immediately to its |
881 |
|
* pool and cannot be dereferenced by its old ID anymore. Even if its |
882 |
|
* allocated back from the Pool later on, it will have a different ID. |
883 |
|
* |
884 |
|
* @param id - unique ID of event to be dropped |
885 |
|
*/ |
886 |
|
void AbstractEngineChannel::IgnoreEvent(event_id_t id) { |
887 |
|
RTList<Event>::Iterator it = pEvents->fromID(id); |
888 |
|
if (it) pEvents->free(it); |
889 |
|
} |
890 |
|
|
891 |
|
/** @brief Drop the requested event. |
892 |
|
* |
893 |
|
* Called by real-time instrument script functions to ignore the event |
894 |
|
* reflected by the given event @a id. This method detects whether the |
895 |
|
* passed ID is actually a @c Note ID or a regular @c Event ID and act |
896 |
|
* accordingly. |
897 |
|
* |
898 |
|
* @param id - event id (from script scope) |
899 |
|
* @see ScriptID |
900 |
|
*/ |
901 |
|
void AbstractEngineChannel::IgnoreEventByScriptID(const ScriptID& id) { |
902 |
|
switch (id.type()) { |
903 |
|
case ScriptID::EVENT: |
904 |
|
IgnoreEvent( id.eventID() ); |
905 |
|
break; |
906 |
|
case ScriptID::NOTE: |
907 |
|
IgnoreNote( id.noteID() ); |
908 |
|
break; |
909 |
|
} |
910 |
|
} |
911 |
|
|
912 |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
FxSend* AbstractEngineChannel::AddFxSend(uint8_t MidiCtrl, String Name) throw (Exception) { |
913 |
if (pEngine) pEngine->DisableAndLock(); |
if (pEngine) pEngine->DisableAndLock(); |
914 |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
FxSend* pFxSend = new FxSend(this, MidiCtrl, Name); |
998 |
*/ |
*/ |
999 |
void AbstractEngineChannel::AddGroup(uint group) { |
void AbstractEngineChannel::AddGroup(uint group) { |
1000 |
if (group) { |
if (group) { |
1001 |
typedef std::map<uint, RTList<Event>*> map_t; |
std::pair<ActiveKeyGroupMap::iterator, bool> p = |
1002 |
|
ActiveKeyGroups.insert(ActiveKeyGroupMap::value_type(group, 0)); |
|
std::pair<map_t::iterator, bool> p = |
|
|
ActiveKeyGroups.insert(map_t::value_type(group, 0)); |
|
1003 |
if (p.second) { |
if (p.second) { |
1004 |
(*p.first).second = new RTList<Event>(pEngine->pEventPool); |
// If the engine channel is pending deletion (see bug |
1005 |
|
// #113), pEngine will be null, so we can't use |
1006 |
|
// pEngine->pEventPool here. Instead we're using a |
1007 |
|
// specialized RTList that allows specifying the pool |
1008 |
|
// later. |
1009 |
|
(*p.first).second = new LazyList<Event>; |
1010 |
} |
} |
1011 |
} |
} |
1012 |
} |
} |
1018 |
dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup)); |
dmsg(4,("HandelKeyGroupConflicts KeyGroup=%d\n", KeyGroup)); |
1019 |
if (KeyGroup) { |
if (KeyGroup) { |
1020 |
// send a release event to all active voices in the group |
// send a release event to all active voices in the group |
1021 |
RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(); |
RTList<Event>::Iterator itEvent = ActiveKeyGroups[KeyGroup]->allocAppend(pEngine->pEventPool); |
1022 |
*itEvent = *itNoteOnEvent; |
*itEvent = *itNoteOnEvent; |
1023 |
} |
} |
1024 |
} |
} |
1028 |
* audio thread, after all voices have been rendered. |
* audio thread, after all voices have been rendered. |
1029 |
*/ |
*/ |
1030 |
void AbstractEngineChannel::ClearGroupEventLists() { |
void AbstractEngineChannel::ClearGroupEventLists() { |
1031 |
for (std::map<uint,RTList<Event>*>::iterator iter = ActiveKeyGroups.begin(); |
for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin(); |
1032 |
iter != ActiveKeyGroups.end(); iter++) { |
iter != ActiveKeyGroups.end(); iter++) { |
1033 |
if (iter->second) { |
if (iter->second) { |
1034 |
iter->second->clear(); |
iter->second->clear(); |
1042 |
* Remove all lists with group events. |
* Remove all lists with group events. |
1043 |
*/ |
*/ |
1044 |
void AbstractEngineChannel::DeleteGroupEventLists() { |
void AbstractEngineChannel::DeleteGroupEventLists() { |
1045 |
for (std::map<uint,RTList<Event>*>::iterator iter = ActiveKeyGroups.begin(); |
for (ActiveKeyGroupMap::iterator iter = ActiveKeyGroups.begin(); |
1046 |
iter != ActiveKeyGroups.end(); iter++) { |
iter != ActiveKeyGroups.end(); iter++) { |
1047 |
delete iter->second; |
delete iter->second; |
1048 |
} |
} |