/[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 411 - (show annotations) (download)
Sat Feb 26 02:01:14 2005 UTC (19 years, 1 month 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 /***************************************************************************
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