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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 738 - (hide 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 schoenebeck 411 /***************************************************************************
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 persson 438 namespace LinuxSampler { namespace gig {
27 schoenebeck 411
28     EngineChannel::EngineChannel() {
29     pMIDIKeyInfo = new midi_key_info_t[128];
30     pEngine = NULL;
31     pInstrument = NULL;
32 schoenebeck 460 pEvents = NULL; // we allocate when we retrieve the right Engine object
33 schoenebeck 554 pEventQueue = new RingBuffer<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
34 schoenebeck 411 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 schoenebeck 473 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
42 persson 438 pMIDIKeyInfo[i].RoundRobinIndex = 0;
43 schoenebeck 411 }
44     InstrumentIdx = -1;
45     InstrumentStat = -1;
46     AudioDeviceChannelLeft = -1;
47     AudioDeviceChannelRight = -1;
48 schoenebeck 675 pMidiInputPort = NULL;
49     midiChannel = midi_chan_all;
50 schoenebeck 670 ResetControllers();
51 schoenebeck 411 }
52    
53     EngineChannel::~EngineChannel() {
54 schoenebeck 460 DisconnectAudioOutputDevice();
55 schoenebeck 411 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 schoenebeck 660 * 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 schoenebeck 670 * 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 schoenebeck 660 * TODO: This "poll method" is just a lazy solution and might be
72     * replaced in future.
73 schoenebeck 670 * @param bNewStatus - (optional, default: false) sets the new status flag
74 schoenebeck 660 * @returns true if engine channel status has changed since last
75     * StatusChanged() call
76     */
77 schoenebeck 670 bool EngineChannel::StatusChanged(bool bNewStatus) {
78 schoenebeck 660 bool b = bStatusChanged;
79 schoenebeck 670 bStatusChanged = bNewStatus;
80 schoenebeck 660 return b;
81     }
82    
83 schoenebeck 670 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 schoenebeck 660 /**
94 schoenebeck 411 * 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 schoenebeck 473 pMIDIKeyInfo[i].VoiceTheftsQueued = 0;
110 schoenebeck 411 }
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 schoenebeck 660
124     // status of engine channel has changed, so set notify flag
125     bStatusChanged = true;
126 schoenebeck 411 }
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 persson 438
161 schoenebeck 411 ResetInternal();
162 persson 438
163 schoenebeck 411 // 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 schoenebeck 517 * we are currently using on this EngineChannel is going to be updated,
222     * so we can stop playback before that happens.
223 schoenebeck 411 */
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 schoenebeck 660 bStatusChanged = true; // status of engine has changed, so set notify flag
239 schoenebeck 411 }
240    
241 schoenebeck 517 /**
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 schoenebeck 660 bStatusChanged = true; // status of engine has changed, so set notify flag
251 schoenebeck 517 }
252    
253 schoenebeck 412 void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {
254 schoenebeck 460 if (pEngine) {
255     if (pEngine->pAudioOutputDevice == pAudioOut) return;
256 schoenebeck 412 DisconnectAudioOutputDevice();
257     }
258 schoenebeck 411 pEngine = Engine::AcquireEngine(this, pAudioOut);
259 persson 438 ResetInternal();
260 schoenebeck 738 pEvents = new RTList<Event>(pEngine->pEventPool);
261 schoenebeck 411 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 schoenebeck 460 if (pEvents) {
275     delete pEvents;
276     pEvents = NULL;
277     }
278 schoenebeck 411 for (uint i = 0; i < 128; i++) {
279 schoenebeck 420 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 schoenebeck 411 }
288     Engine* oldEngine = pEngine;
289     AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
290     pEngine = NULL;
291     Engine::FreeEngine(this, oldAudioDevice);
292     AudioDeviceChannelLeft = -1;
293 persson 438 AudioDeviceChannelRight = -1;
294 schoenebeck 411 }
295     }
296    
297     void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
298     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
299 persson 438
300 schoenebeck 411 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 schoenebeck 675 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 schoenebeck 411 /**
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 persson 438 event.pEngineChannel = this;
363 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
364 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
365 schoenebeck 411 }
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 schoenebeck 412 event.pEngineChannel = this;
382 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
383 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
384 schoenebeck 411 }
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 persson 438 if (pEngine) {
395 schoenebeck 411 Event event = pEngine->pEventGenerator->CreateEvent();
396     event.Type = Event::type_pitchbend;
397     event.Param.Pitch.Pitch = Pitch;
398 schoenebeck 412 event.pEngineChannel = this;
399 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
400 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
401 schoenebeck 411 }
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 schoenebeck 412 event.pEngineChannel = this;
418 schoenebeck 411 if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
419 schoenebeck 412 else dmsg(1,("EngineChannel: Input event queue full!"));
420 schoenebeck 411 }
421     }
422    
423 schoenebeck 460 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 schoenebeck 473 void EngineChannel::ResetControllers() {
436 schoenebeck 670 Pitch = 0;
437     SustainPedal = false;
438     GlobalVolume = 1.0;
439     GlobalPanLeft = 1.0f;
440     GlobalPanRight = 1.0f;
441 schoenebeck 473 // set all MIDI controller values to zero
442     memset(ControllerTable, 0x00, 128);
443     }
444    
445 schoenebeck 460 /**
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 schoenebeck 411 float EngineChannel::Volume() {
482     return GlobalVolume;
483     }
484    
485     void EngineChannel::Volume(float f) {
486     GlobalVolume = f;
487 schoenebeck 660 bStatusChanged = true; // status of engine channel has changed, so set notify flag
488 schoenebeck 411 }
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 persson 438 }
509 schoenebeck 411
510 schoenebeck 475 String EngineChannel::EngineName() {
511     return LS_GIG_ENGINE_NAME;
512     }
513 schoenebeck 660
514 schoenebeck 411 }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC