/[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 738 - (show annotations) (download)
Tue Aug 16 17:14:25 2005 UTC (18 years, 8 months ago) by schoenebeck
File size: 20721 byte(s)
* extensive synthesis optimization: reimplementation of EGs and LFO(s),
  removed synthesis parameter prerendering and the synthesis parameter
  matrix in general, splitting each audio fragment into subfragments now
  where each subfragment uses constant synthesis parameters
  (everything's still very buggy ATM)

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

  ViewVC Help
Powered by ViewVC