/[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 411 - (hide annotations) (download)
Sat Feb 26 02:01:14 2005 UTC (19 years, 2 months ago) by schoenebeck
File size: 15324 byte(s)
* design change: using now one sampler engine instance and one disk thread
  instance for all sampler channels that are connected to the same audio
  output device (and are using the same engine type of course)
* added EngineFactory / EngineChannelFactory to remove the annoying build
  dependencies e.g. of the lscpserver to the actual sampler engine
  implementations
* bumped version to 0.3.0 (current CVS state is still quite broken,
  previous, stable CVS version was tagged as "v0_2_0" and is also available
  as source tarball)

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     namespace LinuxSampler { namespace gig {
27    
28     EngineChannel::EngineChannel() {
29     pMIDIKeyInfo = new midi_key_info_t[128];
30     pEngine = NULL;
31     pInstrument = NULL;
32     pEventQueue = new RingBuffer<Event>(MAX_EVENTS_PER_FRAGMENT, 0);
33     pActiveKeys = new Pool<uint>(128);
34     for (uint i = 0; i < 128; i++) {
35     pMIDIKeyInfo[i].pActiveVoices = NULL; // we allocate when we retrieve the right Engine object
36     pMIDIKeyInfo[i].KeyPressed = false;
37     pMIDIKeyInfo[i].Active = false;
38     pMIDIKeyInfo[i].ReleaseTrigger = false;
39     pMIDIKeyInfo[i].pEvents = NULL; // we allocate when we retrieve the right Engine object
40     }
41     InstrumentIdx = -1;
42     InstrumentStat = -1;
43     AudioDeviceChannelLeft = -1;
44     AudioDeviceChannelRight = -1;
45     }
46    
47     EngineChannel::~EngineChannel() {
48     if (pInstrument) Engine::instruments.HandBack(pInstrument, this);
49     for (uint i = 0; i < 128; i++) {
50     if (pMIDIKeyInfo[i].pActiveVoices) {
51     pMIDIKeyInfo[i].pActiveVoices->clear();
52     delete pMIDIKeyInfo[i].pActiveVoices;
53     }
54     if (pMIDIKeyInfo[i].pEvents) {
55     pMIDIKeyInfo[i].pEvents->clear();
56     delete pMIDIKeyInfo[i].pEvents;
57     }
58     }
59     if (pEventQueue) delete pEventQueue;
60     if (pActiveKeys) delete pActiveKeys;
61     if (pMIDIKeyInfo) delete[] pMIDIKeyInfo;
62     }
63    
64     /**
65     * This method is not thread safe!
66     */
67     void EngineChannel::ResetInternal() {
68     Pitch = 0;
69     SustainPedal = false;
70     GlobalVolume = 1.0;
71     CurrentKeyDimension = 0;
72    
73     // set all MIDI controller values to zero
74     memset(ControllerTable, 0x00, 128);
75    
76     // reset key info
77     for (uint i = 0; i < 128; i++) {
78     if (pMIDIKeyInfo[i].pActiveVoices)
79     pMIDIKeyInfo[i].pActiveVoices->clear();
80     if (pMIDIKeyInfo[i].pEvents)
81     pMIDIKeyInfo[i].pEvents->clear();
82     pMIDIKeyInfo[i].KeyPressed = false;
83     pMIDIKeyInfo[i].Active = false;
84     pMIDIKeyInfo[i].ReleaseTrigger = false;
85     pMIDIKeyInfo[i].itSelf = Pool<uint>::Iterator();
86     }
87    
88     // reset all key groups
89     std::map<uint,uint*>::iterator iter = ActiveKeyGroups.begin();
90     for (; iter != ActiveKeyGroups.end(); iter++) iter->second = NULL;
91    
92     // free all active keys
93     pActiveKeys->clear();
94    
95     // delete all input events
96     pEventQueue->init();
97    
98     if (pEngine) pEngine->ResetInternal();
99     }
100    
101     int EngineChannel::RenderAudio(uint Samples) {
102     return (pEngine) ? pEngine->RenderAudio(this, Samples) : 0;
103     }
104    
105     LinuxSampler::Engine* EngineChannel::GetEngine() {
106     return pEngine;
107     }
108    
109     /**
110     * More or less a workaround to set the instrument name, index and load
111     * status variable to zero percent immediately, that is without blocking
112     * the calling thread. It might be used in future for other preparations
113     * as well though.
114     *
115     * @param FileName - file name of the Gigasampler instrument file
116     * @param Instrument - index of the instrument in the .gig file
117     * @see LoadInstrument()
118     */
119     void EngineChannel::PrepareLoadInstrument(const char* FileName, uint Instrument) {
120     InstrumentFile = FileName;
121     InstrumentIdx = Instrument;
122     InstrumentStat = 0;
123     }
124    
125     /**
126     * Load an instrument from a .gig file. PrepareLoadInstrument() has to
127     * be called first to provide the information which instrument to load.
128     * This method will then actually start to load the instrument and block
129     * the calling thread until loading was completed.
130     *
131     * @returns detailed description of the method call result
132     * @see PrepareLoadInstrument()
133     */
134     void EngineChannel::LoadInstrument() {
135    
136     if (pEngine) pEngine->DisableAndLock();
137    
138     ResetInternal();
139    
140     // free old instrument
141     if (pInstrument) {
142     // give old instrument back to instrument manager
143     Engine::instruments.HandBack(pInstrument, this);
144     }
145    
146     // delete all key groups
147     ActiveKeyGroups.clear();
148    
149     // request gig instrument from instrument manager
150     try {
151     instrument_id_t instrid;
152     instrid.FileName = InstrumentFile;
153     instrid.iInstrument = InstrumentIdx;
154     pInstrument = Engine::instruments.Borrow(instrid, this);
155     if (!pInstrument) {
156     InstrumentStat = -1;
157     dmsg(1,("no instrument loaded!!!\n"));
158     exit(EXIT_FAILURE);
159     }
160     }
161     catch (RIFF::Exception e) {
162     InstrumentStat = -2;
163     String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message;
164     throw LinuxSamplerException(msg);
165     }
166     catch (InstrumentResourceManagerException e) {
167     InstrumentStat = -3;
168     String msg = "gig::Engine error: Failed to load instrument, cause: " + e.Message();
169     throw LinuxSamplerException(msg);
170     }
171     catch (...) {
172     InstrumentStat = -4;
173     throw LinuxSamplerException("gig::Engine error: Failed to load instrument, cause: Unknown exception while trying to parse gig file.");
174     }
175    
176     // rebuild ActiveKeyGroups map with key groups of current instrument
177     for (::gig::Region* pRegion = pInstrument->GetFirstRegion(); pRegion; pRegion = pInstrument->GetNextRegion())
178     if (pRegion->KeyGroup) ActiveKeyGroups[pRegion->KeyGroup] = NULL;
179    
180     InstrumentIdxName = pInstrument->pInfo->Name;
181     InstrumentStat = 100;
182    
183     // inform audio driver for the need of two channels
184     try {
185     if (pEngine && pEngine->pAudioOutputDevice)
186     pEngine->pAudioOutputDevice->AcquireChannels(2); // gig Engine only stereo
187     }
188     catch (AudioOutputException e) {
189     String msg = "Audio output device unable to provide 2 audio channels, cause: " + e.Message();
190     throw LinuxSamplerException(msg);
191     }
192    
193     if (pEngine) pEngine->Enable();
194     }
195    
196     /**
197     * Will be called by the InstrumentResourceManager when the instrument
198     * we are currently using in this engine is going to be updated, so we
199     * can stop playback before that happens.
200     */
201     void EngineChannel::ResourceToBeUpdated(::gig::Instrument* pResource, void*& pUpdateArg) {
202     dmsg(3,("gig::Engine: Received instrument update message.\n"));
203     if (pEngine) pEngine->DisableAndLock();
204     ResetInternal();
205     this->pInstrument = NULL;
206     }
207    
208     /**
209     * Will be called by the InstrumentResourceManager when the instrument
210     * update process was completed, so we can continue with playback.
211     */
212     void EngineChannel::ResourceUpdated(::gig::Instrument* pOldResource, ::gig::Instrument* pNewResource, void* pUpdateArg) {
213     this->pInstrument = pNewResource; //TODO: there are couple of engine parameters we should update here as well if the instrument was updated (see LoadInstrument())
214     if (pEngine) pEngine->Enable();
215     }
216    
217     void EngineChannel::Connect(AudioOutputDevice* pAudioOut) {
218     pEngine = Engine::AcquireEngine(this, pAudioOut);
219     ResetInternal();
220     for (uint i = 0; i < 128; i++) {
221     pMIDIKeyInfo[i].pActiveVoices = new RTList<Voice>(pEngine->pVoicePool);
222     pMIDIKeyInfo[i].pEvents = new RTList<Event>(pEngine->pEventPool);
223     }
224     AudioDeviceChannelLeft = 0;
225     AudioDeviceChannelRight = 1;
226     pOutputLeft = pAudioOut->Channel(0)->Buffer();
227     pOutputRight = pAudioOut->Channel(1)->Buffer();
228     }
229    
230     void EngineChannel::DisconnectAudioOutputDevice() {
231     if (pEngine) { // if clause to prevent disconnect loops
232     ResetInternal();
233     for (uint i = 0; i < 128; i++) {
234     if (pMIDIKeyInfo[i].pActiveVoices) delete pMIDIKeyInfo[i].pActiveVoices;
235     if (pMIDIKeyInfo[i].pEvents) delete pMIDIKeyInfo[i].pEvents;
236     }
237     Engine* oldEngine = pEngine;
238     AudioOutputDevice* oldAudioDevice = pEngine->pAudioOutputDevice;
239     pEngine = NULL;
240     Engine::FreeEngine(this, oldAudioDevice);
241     AudioDeviceChannelLeft = -1;
242     AudioDeviceChannelRight = -1;
243     oldAudioDevice->Disconnect(this);
244     }
245     }
246    
247     void EngineChannel::SetOutputChannel(uint EngineAudioChannel, uint AudioDeviceChannel) {
248     if (!pEngine || !pEngine->pAudioOutputDevice) throw AudioOutputException("No audio output device connected yet.");
249    
250     AudioChannel* pChannel = pEngine->pAudioOutputDevice->Channel(AudioDeviceChannel);
251     if (!pChannel) throw AudioOutputException("Invalid audio output device channel " + ToString(AudioDeviceChannel));
252     switch (EngineAudioChannel) {
253     case 0: // left output channel
254     pOutputLeft = pChannel->Buffer();
255     AudioDeviceChannelLeft = AudioDeviceChannel;
256     break;
257     case 1: // right output channel
258     pOutputRight = pChannel->Buffer();
259     AudioDeviceChannelRight = AudioDeviceChannel;
260     break;
261     default:
262     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
263     }
264     }
265    
266     int EngineChannel::OutputChannel(uint EngineAudioChannel) {
267     switch (EngineAudioChannel) {
268     case 0: // left channel
269     return AudioDeviceChannelLeft;
270     case 1: // right channel
271     return AudioDeviceChannelRight;
272     default:
273     throw AudioOutputException("Invalid engine audio channel " + ToString(EngineAudioChannel));
274     }
275     }
276    
277     /**
278     * Will be called by the MIDIIn Thread to let the audio thread trigger a new
279     * voice for the given key.
280     *
281     * @param Key - MIDI key number of the triggered key
282     * @param Velocity - MIDI velocity value of the triggered key
283     */
284     void EngineChannel::SendNoteOn(uint8_t Key, uint8_t Velocity) {
285     if (pEngine) {
286     Event event = pEngine->pEventGenerator->CreateEvent();
287     event.Type = Event::type_note_on;
288     event.Param.Note.Key = Key;
289     event.Param.Note.Velocity = Velocity;
290     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
291     else dmsg(1,("Engine: Input event queue full!"));
292     }
293     }
294    
295     /**
296     * Will be called by the MIDIIn Thread to signal the audio thread to release
297     * voice(s) on the given key.
298     *
299     * @param Key - MIDI key number of the released key
300     * @param Velocity - MIDI release velocity value of the released key
301     */
302     void EngineChannel::SendNoteOff(uint8_t Key, uint8_t Velocity) {
303     if (pEngine) {
304     Event event = pEngine->pEventGenerator->CreateEvent();
305     event.Type = Event::type_note_off;
306     event.Param.Note.Key = Key;
307     event.Param.Note.Velocity = Velocity;
308     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
309     else dmsg(1,("Engine: Input event queue full!"));
310     }
311     }
312    
313     /**
314     * Will be called by the MIDIIn Thread to signal the audio thread to change
315     * the pitch value for all voices.
316     *
317     * @param Pitch - MIDI pitch value (-8192 ... +8191)
318     */
319     void EngineChannel::SendPitchbend(int Pitch) {
320     if (pEngine) {
321     Event event = pEngine->pEventGenerator->CreateEvent();
322     event.Type = Event::type_pitchbend;
323     event.Param.Pitch.Pitch = Pitch;
324     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
325     else dmsg(1,("Engine: Input event queue full!"));
326     }
327     }
328    
329     /**
330     * Will be called by the MIDIIn Thread to signal the audio thread that a
331     * continuous controller value has changed.
332     *
333     * @param Controller - MIDI controller number of the occured control change
334     * @param Value - value of the control change
335     */
336     void EngineChannel::SendControlChange(uint8_t Controller, uint8_t Value) {
337     if (pEngine) {
338     Event event = pEngine->pEventGenerator->CreateEvent();
339     event.Type = Event::type_control_change;
340     event.Param.CC.Controller = Controller;
341     event.Param.CC.Value = Value;
342     if (this->pEventQueue->write_space() > 0) this->pEventQueue->push(&event);
343     else dmsg(1,("Engine: Input event queue full!"));
344     }
345     }
346    
347     float EngineChannel::Volume() {
348     return GlobalVolume;
349     }
350    
351     void EngineChannel::Volume(float f) {
352     GlobalVolume = f;
353     }
354    
355     uint EngineChannel::Channels() {
356     return 2;
357     }
358    
359     String EngineChannel::InstrumentFileName() {
360     return InstrumentFile;
361     }
362    
363     String EngineChannel::InstrumentName() {
364     return InstrumentIdxName;
365     }
366    
367     int EngineChannel::InstrumentIndex() {
368     return InstrumentIdx;
369     }
370    
371     int EngineChannel::InstrumentStatus() {
372     return InstrumentStat;
373     }
374    
375     }} // namespace LinuxSampler::gig

  ViewVC Help
Powered by ViewVC