/[svn]/linuxsampler/trunk/src/engines/gig/EngineChannel.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/engines/gig/EngineChannel.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 675 - (show annotations) (download)
Wed Jun 22 22:09:28 2005 UTC (18 years, 10 months ago) by schoenebeck
File size: 21672 byte(s)
* update MIDI channel info on program change

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #include "EngineChannel.h"
25
26 namespace LinuxSampler { namespace gig {
27
28 EngineChannel::EngineChannel() {
29 pMIDIKeyInfo = new midi_key_info_t[128];
30 pEngine = NULL;
31 pInstrument = NULL;
32 pEvents = NULL; // we allocate when we retrieve the right Engine object
33 pCCEvents = NULL; // we allocate when we retrieve the right Engine object
34 pEventQueue = new RingBuffer<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
35 pActiveKeys = new Pool<uint>(128);
36 for (uint i = 0; i < 128; i++) {
37 pMIDIKeyInfo[i].pActiveVoices = NULL; // we allocate when we retrieve the right Engine object
38 pMIDIKeyInfo[i].KeyPressed = false;
39 pMIDIKeyInfo[i].Active = false;
40 pMIDIKeyInfo[i].ReleaseTrigger = false;
41 pMIDIKeyInfo[i].pEvents = NULL; // we allocate when we retrieve the right Engine object
42 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
43 pMIDIKeyInfo[i].RoundRobinIndex = 0;
44 }
45 for (uint i = 0; i < Event::destination_count; i++) {
46 pSynthesisEvents[i] = NULL; // we allocate when we retrieve the right Engine object
47 }
48 InstrumentIdx = -1;
49 InstrumentStat = -1;
50 AudioDeviceChannelLeft = -1;
51 AudioDeviceChannelRight = -1;
52 pMidiInputPort = NULL;
53 midiChannel = midi_chan_all;
54 ResetControllers();
55 }
56
57 EngineChannel::~EngineChannel() {
58 DisconnectAudioOutputDevice();
59 if (pInstrument) Engine::instruments.HandBack(pInstrument, this);
60 if (pEventQueue) delete pEventQueue;
61 if (pActiveKeys) delete pActiveKeys;
62 if (pMIDIKeyInfo) delete[] pMIDIKeyInfo;
63 }
64
65 /**
66 * Implementation of virtual method from abstract EngineChannel interface.
67 * This method will periodically be polled (e.g. by the LSCP server) to
68 * check if some engine channel parameter has changed since the last
69 * StatusChanged() call.
70 *
71 * This method can also be used to mark the engine channel as changed
72 * from outside, e.g. by a MIDI input device. The optional argument
73 * \a nNewStatus can be used for this.
74 *
75 * TODO: This "poll method" is just a lazy solution and might be
76 * replaced in future.
77 * @param bNewStatus - (optional, default: false) sets the new status flag
78 * @returns true if engine channel status has changed since last
79 * StatusChanged() call
80 */
81 bool EngineChannel::StatusChanged(bool bNewStatus) {
82 bool b = bStatusChanged;
83 bStatusChanged = bNewStatus;
84 return b;
85 }
86
87 void EngineChannel::Reset() {
88 if (pEngine) pEngine->DisableAndLock();
89 ResetInternal();
90 ResetControllers();
91 if (pEngine) {
92 pEngine->Enable();
93 pEngine->Reset();
94 }
95 }
96
97 /**
98 * This method is not thread safe!
99 */
100 void EngineChannel::ResetInternal() {
101 CurrentKeyDimension = 0;
102
103 // reset key info
104 for (uint i = 0; i < 128; i++) {
105 if (pMIDIKeyInfo[i].pActiveVoices)
106 pMIDIKeyInfo[i].pActiveVoices->clear();
107 if (pMIDIKeyInfo[i].pEvents)
108 pMIDIKeyInfo[i].pEvents->clear();
109 pMIDIKeyInfo[i].KeyPressed = false;
110 pMIDIKeyInfo[i].Active = false;
111 pMIDIKeyInfo[i].ReleaseTrigger = false;
112 pMIDIKeyInfo[i].itSelf = Pool<uint>::Iterator();
113 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
114 }
115
116 // reset all key groups
117 std::map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();
118 for (; iter != ActiveKeyGroups.end(); iter++) iter->second = NULL;
119
120 // free all active keys
121 pActiveKeys->clear();
122
123 // delete all input events
124 pEventQueue->init();
125
126 if (pEngine) pEngine->ResetInternal();
127
128 // status of engine channel has changed, so set notify flag
129 bStatusChanged = true;
130 }
131
132 LinuxSampler::Engine* EngineChannel::GetEngine() {
133 return pEngine;
134 }
135
136 /**
137 * More or less a workaround to set the instrument name, index and load
138 * status variable to zero percent immediately, that is without blocking
139 * the calling thread. It might be used in future for other preparations
140 * as well though.
141 *
142 * @param FileName - file name of the Gigasampler instrument file
143 * @param Instrument - index of the instrument in the .gig file
144 * @see LoadInstrument()
145 */
146 void EngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
147 InstrumentFile = FileName;
148 InstrumentIdx = Instrument;
149 InstrumentStat = 0;
150 }
151
152 /**
153 * Load an instrument from a .gig file. PrepareLoadInstrument() has to
154 * be called first to provide the information which instrument to load.
155 * This method will then actually start to load the instrument and block
156 * the calling thread until loading was completed.
157 *
158 * @returns detailed description of the method call result
159 * @see PrepareLoadInstrument()
160 */
161 void EngineChannel::LoadInstrument() {
162
163 if (pEngine) pEngine->DisableAndLock();
164
165 ResetInternal();
166
167 // free old instrument
168 if (pInstrument) {
169 // give old instrument back to instrument manager
170 Engine::instruments.HandBack(pInstrument, this);
171 }
172
173 // delete all key groups
174 ActiveKeyGroups.clear();
175
176 // request gig instrument from instrument manager
177 try {
178 instrument_id_t instrid;
179 instrid.FileName = InstrumentFile;
180 instrid.iInstrument = InstrumentIdx;
181 pInstrument = Engine::instruments.Borrow(instrid, this);
182 if (!pInstrument) {
183 InstrumentStat = -1;
184 dmsg(1,("no instrument loaded!!!\n"));
185 exit(EXIT_FAILURE);
186 }
187 }
188 catch (RIFF::Exception e) {
189 InstrumentStat = -2;
190 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;
191 throw LinuxSamplerException(msg);
192 }
193 catch (InstrumentResourceManagerException e) {
194 InstrumentStat = -3;
195 String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();
196 throw LinuxSamplerException(msg);
197 }
198 catch (...) {
199 InstrumentStat = -4;
200 throw LinuxSamplerException("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");
201 }
202
203 // rebuild ActiveKeyGroups map with key groups of current instrument
204 for (::gig::Region* pRegion = pInstrument->GetFirstRegion(); pRegion; pRegion = pInstrument->GetNextRegion())
205 if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;
206
207 InstrumentIdxName = pInstrument->pInfo->Name;
208 InstrumentStat = 100;
209
210 // inform audio driver for the need of two channels
211 try {
212 if (pEngine && pEngine->pAudioOutputDevice)
213 pEngine->pAudioOutputDevice->AcquireChannels(2); // gig Engine only stereo
214 }
215 catch (AudioOutputException e) {
216 String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
217 throw LinuxSamplerException(msg);
218 }
219
220 if (pEngine) pEngine->Enable();
221 }
222
223 /**
224 * Will be called by the InstrumentResourceManager when the instrument
225 * we are currently using on this EngineChannel is going to be updated,
226 * so we can stop playback before that happens.
227 */
228 void EngineChannel::ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
229 dmsg(3,("gig::Engine: Received instrument update message.\n"));
230 if (pEngine) pEngine->DisableAndLock();
231 ResetInternal();
232 this->pInstrument = NULL;
233 }
234
235 /**
236 * Will be called by the InstrumentResourceManager when the instrument
237 * update process was completed, so we can continue with playback.
238 */
239 void EngineChannel::ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
240 this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
241 if (pEngine) pEngine->Enable();
242 bStatusChanged = true; // status of engine has changed, so set notify flag
243 }
244
245 /**
246 * Will be called by the InstrumentResourceManager on progress changes
247 * while loading or realoading an instrument for this EngineChannel.
248 *
249 * @param fProgress - current progress as value between 0.0 and 1.0
250 */
251 void EngineChannel::OnResourceProgress(float fProgress) {
252 this->InstrumentStat = int(fProgress * 100.0f);
253 dmsg(7,("gig::EngineChannel: progress %d%", InstrumentStat));
254 bStatusChanged = true; // status of engine has changed, so set notify flag
255 }
256
257 void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {
258 if (pEngine) {
259 if (pEngine->pAudioOutputDevice == pAudioOut) return;
260 DisconnectAudioOutputDevice();
261 }
262 pEngine = Engine::AcquireEngine(this, pAudioOut);
263 ResetInternal();
264 pEvents = new RTList<Event>(pEngine->pEventPool);
265 pCCEvents = new RTList<Event>(pEngine->pEventPool);
266 for (uint i = 0; i < Event::destination_count; i++) {
267 pSynthesisEvents[i] = new RTList<Event>(pEngine->pEventPool);
268 }
269 for (uint i = 0; i < 128; i++) {
270 pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pEngine->pVoicePool);
271 pMIDIKeyInfo[i].pEvents = new RTList<Event>(pEngine->pEventPool);
272 }
273 AudioDeviceChannelLeft = 0;
274 AudioDeviceChannelRight = 1;
275 pOutputLeft = pAudioOut->Channel(0)->Buffer();
276 pOutputRight = pAudioOut->Channel(1)->Buffer();
277 }
278
279 void EngineChannel::DisconnectAudioOutputDevice() {
280 if (pEngine) { // if clause to prevent disconnect loops
281 ResetInternal();
282 if (pEvents) {
283 delete pEvents;
284 pEvents = NULL;
285 }
286 if (pCCEvents) {
287 delete pCCEvents;
288 pCCEvents = NULL;
289 }
290 for (uint i = 0; i < 128; i++) {
291 if (pMIDIKeyInfo[i].pActiveVoices) {
292 delete pMIDIKeyInfo[i].pActiveVoices;
293 pMIDIKeyInfo[i].pActiveVoices = NULL;
294 }
295 if (pMIDIKeyInfo[i].pEvents) {
296 delete pMIDIKeyInfo[i].pEvents;
297 pMIDIKeyInfo[i].pEvents = NULL;
298 }
299 }
300 for (uint i = 0; i < Event::destination_count; i++) {
301 if (pSynthesisEvents[i]) {
302 delete pSynthesisEvents[i];
303 pSynthesisEvents[i] = NULL;
304 }
305 }
306 Engine* oldEngine = pEngine;
307 AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
308 pEngine = NULL;
309 Engine::FreeEngine(this, oldAudioDevice);
310 AudioDeviceChannelLeft = -1;
311 AudioDeviceChannelRight = -1;
312 }
313 }
314
315 void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
316 if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
317
318 AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
319 if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
320 switch (EngineAudioChannel) {
321 case 0: // left output channel
322 pOutputLeft = pChannel->Buffer();
323 AudioDeviceChannelLeft = AudioDeviceChannel;
324 break;
325 case 1: // right output channel
326 pOutputRight = pChannel->Buffer();
327 AudioDeviceChannelRight = AudioDeviceChannel;
328 break;
329 default:
330 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
331 }
332 }
333
334 int EngineChannel::OutputChannel(uint EngineAudioChannel) {
335 switch (EngineAudioChannel) {
336 case 0: // left channel
337 return AudioDeviceChannelLeft;
338 case 1: // right channel
339 return AudioDeviceChannelRight;
340 default:
341 throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
342 }
343 }
344
345 void EngineChannel::Connect(MidiInputPort* pMidiPort, midi_chan_t MidiChannel) {
346 if (!pMidiPort || pMidiPort == this->pMidiInputPort) return;
347 DisconnectMidiInputPort();
348 this->pMidiInputPort = pMidiPort;
349 this->midiChannel = MidiChannel;
350 pMidiPort->Connect(this, MidiChannel);
351 }
352
353 void EngineChannel::DisconnectMidiInputPort() {
354 MidiInputPort* pOldPort = this->pMidiInputPort;
355 this->pMidiInputPort = NULL;
356 if (pOldPort) pOldPort->Disconnect(this);
357 }
358
359 MidiInputPort* EngineChannel::GetMidiInputPort() {
360 return pMidiInputPort;
361 }
362
363 midi_chan_t EngineChannel::MidiChannel() {
364 return midiChannel;
365 }
366
367 /**
368 * Will be called by the MIDIIn Thread to let the audio thread trigger a new
369 * voice for the given key.
370 *
371 * @param Key - MIDI key number of the triggered key
372 * @param Velocity - MIDI velocity value of the triggered key
373 */
374 void EngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity) {
375 if (pEngine) {
376 Event event = pEngine->pEventGenerator->CreateEvent();
377 event.Type = Event::type_note_on;
378 event.Param.Note.Key = Key;
379 event.Param.Note.Velocity = Velocity;
380 event.pEngineChannel = this;
381 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
382 else dmsg(1,("EngineChannel: Input event queue full!"));
383 }
384 }
385
386 /**
387 * Will be called by the MIDIIn Thread to signal the audio thread to release
388 * voice(s) on the given key.
389 *
390 * @param Key - MIDI key number of the released key
391 * @param Velocity - MIDI release velocity value of the released key
392 */
393 void EngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity) {
394 if (pEngine) {
395 Event event = pEngine->pEventGenerator->CreateEvent();
396 event.Type = Event::type_note_off;
397 event.Param.Note.Key = Key;
398 event.Param.Note.Velocity = Velocity;
399 event.pEngineChannel = this;
400 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
401 else dmsg(1,("EngineChannel: Input event queue full!"));
402 }
403 }
404
405 /**
406 * Will be called by the MIDIIn Thread to signal the audio thread to change
407 * the pitch value for all voices.
408 *
409 * @param Pitch - MIDI pitch value (-8192 ... +8191)
410 */
411 void EngineChannel::SendPitchbend(int Pitch) {
412 if (pEngine) {
413 Event event = pEngine->pEventGenerator->CreateEvent();
414 event.Type = Event::type_pitchbend;
415 event.Param.Pitch.Pitch = Pitch;
416 event.pEngineChannel = this;
417 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
418 else dmsg(1,("EngineChannel: Input event queue full!"));
419 }
420 }
421
422 /**
423 * Will be called by the MIDIIn Thread to signal the audio thread that a
424 * continuous controller value has changed.
425 *
426 * @param Controller - MIDI controller number of the occured control change
427 * @param Value - value of the control change
428 */
429 void EngineChannel::SendControlChange(uint8_t Controller, uint8_t Value) {
430 if (pEngine) {
431 Event event = pEngine->pEventGenerator->CreateEvent();
432 event.Type = Event::type_control_change;
433 event.Param.CC.Controller = Controller;
434 event.Param.CC.Value = Value;
435 event.pEngineChannel = this;
436 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
437 else dmsg(1,("EngineChannel: Input event queue full!"));
438 }
439 }
440
441 void EngineChannel::ClearEventLists() {
442 pEvents->clear();
443 pCCEvents->clear();
444 for (uint i = 0; i < Event::destination_count; i++) {
445 pSynthesisEvents[i]->clear();
446 }
447 // empty MIDI key specific event lists
448 {
449 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
450 RTList<uint>::Iterator end = pActiveKeys->end();
451 for(; iuiKey != end; ++iuiKey) {
452 pMIDIKeyInfo[*iuiKey].pEvents->clear(); // free all events on the key
453 }
454 }
455 }
456
457 void EngineChannel::ResetControllers() {
458 Pitch = 0;
459 SustainPedal = false;
460 GlobalVolume = 1.0;
461 GlobalPanLeft = 1.0f;
462 GlobalPanRight = 1.0f;
463 // set all MIDI controller values to zero
464 memset(ControllerTable, 0x00, 128);
465 }
466
467 /**
468 * Copy all events from the engine channel's input event queue buffer to
469 * the internal event list. This will be done at the beginning of each
470 * audio cycle (that is each RenderAudio() call) to distinguish all
471 * events which have to be processed in the current audio cycle. Each
472 * EngineChannel has it's own input event queue for the common channel
473 * specific events (like NoteOn, NoteOff and ControlChange events).
474 * Beside that, the engine also has a input event queue for global
475 * events (usually SysEx messages).
476 *
477 * @param Samples - number of sample points to be processed in the
478 * current audio cycle
479 */
480 void EngineChannel::ImportEvents(uint Samples) {
481 RingBuffer<Event>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
482 Event* pEvent;
483 while (true) {
484 // get next event from input event queue
485 if (!(pEvent = eventQueueReader.pop())) break;
486 // if younger event reached, ignore that and all subsequent ones for now
487 if (pEvent->FragmentPos() >= Samples) {
488 eventQueueReader--;
489 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
490 pEvent->ResetFragmentPos();
491 break;
492 }
493 // copy event to internal event list
494 if (pEvents->poolIsEmpty()) {
495 dmsg(1,("Event pool emtpy!\n"));
496 break;
497 }
498 *pEvents->allocAppend() = *pEvent;
499 }
500 eventQueueReader.free(); // free all copied events from input queue
501 }
502
503 float EngineChannel::Volume() {
504 return GlobalVolume;
505 }
506
507 void EngineChannel::Volume(float f) {
508 GlobalVolume = f;
509 bStatusChanged = true; // status of engine channel has changed, so set notify flag
510 }
511
512 uint EngineChannel::Channels() {
513 return 2;
514 }
515
516 String EngineChannel::InstrumentFileName() {
517 return InstrumentFile;
518 }
519
520 String EngineChannel::InstrumentName() {
521 return InstrumentIdxName;
522 }
523
524 int EngineChannel::InstrumentIndex() {
525 return InstrumentIdx;
526 }
527
528 int EngineChannel::InstrumentStatus() {
529 return InstrumentStat;
530 }
531
532 String EngineChannel::EngineName() {
533 return LS_GIG_ENGINE_NAME;
534 }
535
536 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC