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

Contents of /linuxsampler/trunk/src/engines/AbstractEngine.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2012 - (show annotations) (download)
Fri Oct 23 17:53:17 2009 UTC (14 years, 6 months ago) by iliev
File size: 27634 byte(s)
* Refactoring: moved the independent code from
  the Gigasampler format engine to base classes
* SFZ format engine: experimental code (not usable yet)
* SoundFont format engine: experimental code (not usable yet)
* Fixed crash which may occur when MIDI key + transpose is out of range

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005-2009 Christian Schoenebeck *
7 * Copyright (C) 2009 Grigor Iliev *
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 * This program is distributed in the hope that it will be useful, *
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17 * GNU General Public License for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License *
20 * along with this program; if not, write to the Free Software *
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
22 * MA 02111-1307 USA *
23 ***************************************************************************/
24
25 #include "AbstractEngine.h"
26 #include "AbstractEngineChannel.h"
27 #include "EngineFactory.h"
28 #include "../common/global_private.h"
29
30 namespace LinuxSampler {
31
32 //InstrumentResourceManager Engine::instruments;
33
34 std::map<AbstractEngine::Format, std::map<AudioOutputDevice*,AbstractEngine*> > AbstractEngine::engines;
35
36 /**
37 * Get an AbstractEngine object for the given AbstractEngineChannel and the
38 * given AudioOutputDevice. All engine channels which are connected to
39 * the same audio output device will use the same engine instance. This
40 * method will be called by an EngineChannel whenever it's
41 * connecting to an audio output device.
42 *
43 * @param pChannel - engine channel which acquires an engine object
44 * @param pDevice - the audio output device \a pChannel is connected to
45 */
46 AbstractEngine* AbstractEngine::AcquireEngine(AbstractEngineChannel* pChannel, AudioOutputDevice* pDevice) {
47 AbstractEngine* pEngine = NULL;
48 // check if there's already an engine for the given audio output device
49 std::map<AbstractEngine::Format, std::map<AudioOutputDevice*,AbstractEngine*> >::iterator it;
50 it = engines.find(pChannel->GetEngineFormat());
51 if (it != engines.end() && (*it).second.count(pDevice)) {
52 dmsg(4,("Using existing Engine.\n"));
53 pEngine = (*it).second[pDevice];
54
55 // Disable the engine while the new engine channel is
56 // added and initialized. The engine will be enabled again
57 // in EngineChannel::Connect.
58 pEngine->DisableAndLock();
59 } else { // create a new engine (and disk thread) instance for the given audio output device
60 dmsg(4,("Creating new Engine.\n"));
61 pEngine = (AbstractEngine*) EngineFactory::Create(pChannel->EngineName());
62 pEngine->Connect(pDevice);
63 engines[pChannel->GetEngineFormat()][pDevice] = pEngine;
64 }
65 // register engine channel to the engine instance
66 pEngine->engineChannels.add(pChannel);
67 // remember index in the ArrayList
68 pChannel->iEngineIndexSelf = pEngine->engineChannels.size() - 1;
69 dmsg(4,("This Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));
70 return pEngine;
71 }
72
73 AbstractEngine::AbstractEngine() {
74 pAudioOutputDevice = NULL;
75 pEventGenerator = NULL;
76 pSysexBuffer = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0);
77 pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0);
78 pEventPool = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT);
79 pGlobalEvents = new RTList<Event>(pEventPool);
80 FrameTime = 0;
81 }
82
83 AbstractEngine::~AbstractEngine() {
84 if (pEventQueue) delete pEventQueue;
85 if (pEventPool) delete pEventPool;
86 if (pEventGenerator) delete pEventGenerator;
87 if (pGlobalEvents) delete pGlobalEvents;
88 if (pSysexBuffer) delete pSysexBuffer;
89 Unregister();
90 }
91
92 /**
93 * Once an engine channel is disconnected from an audio output device,
94 * it will immediately call this method to unregister itself from the
95 * engine instance and if that engine instance is not used by any other
96 * engine channel anymore, then that engine instance will be destroyed.
97 *
98 * @param pChannel - engine channel which wants to disconnect from it's
99 * engine instance
100 * @param pDevice - audio output device \a pChannel was connected to
101 */
102 void AbstractEngine::FreeEngine(AbstractEngineChannel* pChannel, AudioOutputDevice* pDevice) {
103 dmsg(4,("Disconnecting EngineChannel from Engine.\n"));
104 AbstractEngine* pEngine = engines[pChannel->GetEngineFormat()][pDevice];
105 // unregister EngineChannel from the Engine instance
106 pEngine->engineChannels.remove(pChannel);
107 // if the used Engine instance is not used anymore, then destroy it
108 if (pEngine->engineChannels.empty()) {
109 pDevice->Disconnect(pEngine);
110 engines[pChannel->GetEngineFormat()].erase(pDevice);
111 delete pEngine;
112 dmsg(4,("Destroying Engine.\n"));
113 }
114 else dmsg(4,("This Engine has now %d EngineChannels.\n",pEngine->engineChannels.size()));
115 }
116
117 void AbstractEngine::Enable() {
118 dmsg(3,("AbstractEngine: enabling\n"));
119 EngineDisabled.PushAndUnlock(false, 2, 0, true); // set condition object 'EngineDisabled' to false (wait max. 2s)
120 dmsg(3,("AbstractEngine: enabled (val=%d)\n", EngineDisabled.GetUnsafe()));
121 }
122
123 /**
124 * Temporarily stop the engine to not do anything. The engine will just be
125 * frozen during that time, that means after enabling it again it will
126 * continue where it was, with all its voices and playback state it had at
127 * the point of disabling. Notice that the engine's (audio) thread will
128 * continue to run, it just remains in an inactive loop during that time.
129 *
130 * If you need to be sure that all voices and disk streams are killed as
131 * well, use @c SuspendAll() instead.
132 *
133 * @see Enable(), SuspendAll()
134 */
135 void AbstractEngine::Disable() {
136 dmsg(3,("AbstractEngine: disabling\n"));
137 bool* pWasDisabled = EngineDisabled.PushAndUnlock(true, 2); // wait max. 2s
138 if (!pWasDisabled) dmsg(3,("AbstractEngine warning: Timeout waiting to disable engine.\n"));
139 }
140
141 void AbstractEngine::DisableAndLock() {
142 dmsg(3,("AbstractEngine: disabling\n"));
143 bool* pWasDisabled = EngineDisabled.Push(true, 2); // wait max. 2s
144 if (!pWasDisabled) dmsg(3,("AbstractEngine warning: Timeout waiting to disable engine.\n"));
145 }
146
147 /**
148 * Reset all voices and disk thread and clear input event queue and all
149 * control and status variables.
150 */
151 void AbstractEngine::Reset() {
152 DisableAndLock();
153 ResetInternal();
154 ResetScaleTuning();
155 Enable();
156 }
157
158 /**
159 * Reset to normal, chromatic scale (means equal tempered).
160 */
161 void AbstractEngine::ResetScaleTuning() {
162 memset(&ScaleTuning[0], 0x00, 12);
163 }
164
165 /**
166 * Copy all events from the engine's global input queue buffer to the
167 * engine's internal event list. This will be done at the beginning of
168 * each audio cycle (that is each RenderAudio() call) to distinguish
169 * all global events which have to be processed in the current audio
170 * cycle. These events are usually just SysEx messages. Every
171 * EngineChannel has it's own input event queue buffer and event list
172 * to handle common events like NoteOn, NoteOff and ControlChange
173 * events.
174 *
175 * @param Samples - number of sample points to be processed in the
176 * current audio cycle
177 */
178 void AbstractEngine::ImportEvents(uint Samples) {
179 RingBuffer<Event,false>::NonVolatileReader eventQueueReader = pEventQueue->get_non_volatile_reader();
180 Event* pEvent;
181 while (true) {
182 // get next event from input event queue
183 if (!(pEvent = eventQueueReader.pop())) break;
184 // if younger event reached, ignore that and all subsequent ones for now
185 if (pEvent->FragmentPos() >= Samples) {
186 eventQueueReader--;
187 dmsg(2,("Younger Event, pos=%d ,Samples=%d!\n",pEvent->FragmentPos(),Samples));
188 pEvent->ResetFragmentPos();
189 break;
190 }
191 // copy event to internal event list
192 if (pGlobalEvents->poolIsEmpty()) {
193 dmsg(1,("Event pool emtpy!\n"));
194 break;
195 }
196 *pGlobalEvents->allocAppend() = *pEvent;
197 }
198 eventQueueReader.free(); // free all copied events from input queue
199 }
200
201 /**
202 * Clear all engine global event lists.
203 */
204 void AbstractEngine::ClearEventLists() {
205 pGlobalEvents->clear();
206 }
207
208 /**
209 * Will be called in case the respective engine channel sports FX send
210 * channels. In this particular case, engine channel local buffers are
211 * used to render and mix all voices to. This method is responsible for
212 * copying the audio data from those local buffers to the master audio
213 * output channels as well as to the FX send audio output channels with
214 * their respective FX send levels.
215 *
216 * @param pEngineChannel - engine channel from which audio should be
217 * routed
218 * @param Samples - amount of sample points to be routed in
219 * this audio fragment cycle
220 */
221 void AbstractEngine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) {
222 AbstractEngineChannel* pChannel = static_cast<AbstractEngineChannel*>(pEngineChannel);
223 // route dry signal
224 {
225 AudioChannel* pDstL = pAudioOutputDevice->Channel(pChannel->AudioDeviceChannelLeft);
226 AudioChannel* pDstR = pAudioOutputDevice->Channel(pChannel->AudioDeviceChannelRight);
227 pChannel->pChannelLeft->MixTo(pDstL, Samples);
228 pChannel->pChannelRight->MixTo(pDstR, Samples);
229 }
230 // route FX send signal
231 {
232 for (int iFxSend = 0; iFxSend < pChannel->GetFxSendCount(); iFxSend++) {
233 FxSend* pFxSend = pChannel->GetFxSend(iFxSend);
234 for (int iChan = 0; iChan < 2; ++iChan) {
235 AudioChannel* pSource =
236 (iChan)
237 ? pChannel->pChannelRight
238 : pChannel->pChannelLeft;
239 const int iDstChan = pFxSend->DestinationChannel(iChan);
240 if (iDstChan < 0) {
241 dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
242 goto channel_cleanup;
243 }
244 AudioChannel* pDstChan = NULL;
245 if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect
246 EffectChain* pEffectChain =
247 pAudioOutputDevice->MasterEffectChain(
248 pFxSend->DestinationMasterEffectChain()
249 );
250 if (!pEffectChain) {
251 dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain()));
252 goto channel_cleanup;
253 }
254 Effect* pEffect =
255 pEffectChain->GetEffect(
256 pFxSend->DestinationMasterEffect()
257 );
258 if (!pEffect) {
259 dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain()));
260 goto channel_cleanup;
261 }
262 pDstChan = pEffect->InputChannel(iDstChan);
263 } else { // FX send routed directly to an audio output channel
264 pDstChan = pAudioOutputDevice->Channel(iDstChan);
265 }
266 if (!pDstChan) {
267 dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan));
268 goto channel_cleanup;
269 }
270 pSource->MixTo(pDstChan, Samples, pFxSend->Level());
271 }
272 }
273 }
274 channel_cleanup:
275 // reset buffers with silence (zero out) for the next audio cycle
276 pChannel->pChannelLeft->Clear();
277 pChannel->pChannelRight->Clear();
278 }
279
280 /**
281 * Calculates the Roland GS sysex check sum.
282 *
283 * @param AddrReader - reader which currently points to the first GS
284 * command address byte of the GS sysex message in
285 * question
286 * @param DataSize - size of the GS message data (in bytes)
287 */
288 uint8_t AbstractEngine::GSCheckSum(const RingBuffer<uint8_t,false>::NonVolatileReader AddrReader, uint DataSize) {
289 RingBuffer<uint8_t,false>::NonVolatileReader reader = AddrReader;
290 uint bytes = 3 /*addr*/ + DataSize;
291 uint8_t addr_and_data[bytes];
292 reader.read(&addr_and_data[0], bytes);
293 uint8_t sum = 0;
294 for (uint i = 0; i < bytes; i++) sum += addr_and_data[i];
295 return 128 - sum % 128;
296 }
297
298 /**
299 * Allows to tune each of the twelve semitones of an octave.
300 *
301 * @param ScaleTunes - detuning of all twelve semitones (in cents)
302 */
303 void AbstractEngine::AdjustScale(int8_t ScaleTunes[12]) {
304 memcpy(&this->ScaleTuning[0], &ScaleTunes[0], 12); //TODO: currently not sample accurate
305 }
306
307 uint AbstractEngine::VoiceCount() {
308 return atomic_read(&ActiveVoiceCount);
309 }
310
311 void AbstractEngine::SetVoiceCount(uint Count) {
312 atomic_set(&ActiveVoiceCount, Count);
313 }
314
315 uint AbstractEngine::VoiceCountMax() {
316 return ActiveVoiceCountMax;
317 }
318
319 /**
320 * Moves pitchbend event from the general (input) event list to the engine
321 * channel's event list. It will actually processed later by the
322 * respective voice.
323 *
324 * @param pEngineChannel - engine channel on which this event occured on
325 * @param itPitchbendEvent - absolute pitch value and time stamp of the event
326 */
327 void AbstractEngine::ProcessPitchbend(AbstractEngineChannel* pEngineChannel, Pool<Event>::Iterator& itPitchbendEvent) {
328 pEngineChannel->Pitch = itPitchbendEvent->Param.Pitch.Pitch; // store current pitch value
329 }
330
331 void AbstractEngine::ProcessFxSendControllers (
332 AbstractEngineChannel* pEngineChannel,
333 Pool<Event>::Iterator& itControlChangeEvent
334 ) {
335 if (!pEngineChannel->fxSends.empty()) {
336 for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) {
337 FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend);
338 if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) {
339 pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value);
340 pFxSend->SetInfoChanged(true);
341 }
342 }
343 }
344 }
345
346 /**
347 * Will be called by the MIDI input device whenever a MIDI system
348 * exclusive message has arrived.
349 *
350 * @param pData - pointer to sysex data
351 * @param Size - lenght of sysex data (in bytes)
352 * @param pSender - the MIDI input port on which the SysEx message was
353 * received
354 */
355 void AbstractEngine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) {
356 Event event = pEventGenerator->CreateEvent();
357 event.Type = Event::type_sysex;
358 event.Param.Sysex.Size = Size;
359 event.pEngineChannel = NULL; // as Engine global event
360 event.pMidiInputPort = pSender;
361 if (pEventQueue->write_space() > 0) {
362 if (pSysexBuffer->write_space() >= Size) {
363 // copy sysex data to input buffer
364 uint toWrite = Size;
365 uint8_t* pPos = (uint8_t*) pData;
366 while (toWrite) {
367 const uint writeNow = RTMath::Min(toWrite, pSysexBuffer->write_space_to_end());
368 pSysexBuffer->write(pPos, writeNow);
369 toWrite -= writeNow;
370 pPos += writeNow;
371
372 }
373 // finally place sysex event into input event queue
374 pEventQueue->push(&event);
375 }
376 else dmsg(1,("Engine: Sysex message too large (%d byte) for input buffer (%d byte)!",Size,CONFIG_SYSEX_BUFFER_SIZE));
377 }
378 else dmsg(1,("Engine: Input event queue full!"));
379 }
380
381 /**
382 * Reacts on MIDI system exclusive messages.
383 *
384 * @param itSysexEvent - sysex data size and time stamp of the sysex event
385 */
386 void AbstractEngine::ProcessSysex(Pool<Event>::Iterator& itSysexEvent) {
387 RingBuffer<uint8_t,false>::NonVolatileReader reader = pSysexBuffer->get_non_volatile_reader();
388
389 uint8_t exclusive_status, id;
390 if (!reader.pop(&exclusive_status)) goto free_sysex_data;
391 if (!reader.pop(&id)) goto free_sysex_data;
392 if (exclusive_status != 0xF0) goto free_sysex_data;
393
394 switch (id) {
395 case 0x7f: { // (Realtime) Universal Sysex (GM Standard)
396 uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;;
397 if (!reader.pop(&sysex_channel)) goto free_sysex_data;
398 if (!reader.pop(&sub_id1)) goto free_sysex_data;
399 if (!reader.pop(&sub_id2)) goto free_sysex_data;
400 if (!reader.pop(&val_lsb)) goto free_sysex_data;
401 if (!reader.pop(&val_msb)) goto free_sysex_data;
402 //TODO: for now we simply ignore the sysex channel, seldom used anyway
403 switch (sub_id1) {
404 case 0x04: // Device Control
405 switch (sub_id2) {
406 case 0x01: { // Master Volume
407 const double volume =
408 double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0;
409 #if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
410 // apply volume to all sampler channels that
411 // are connected to the same MIDI input port
412 // this sysex message arrived on
413 for (int i = 0; i < engineChannels.size(); ++i) {
414 EngineChannel* pEngineChannel = engineChannels[i];
415 if (pEngineChannel->GetMidiInputPort() ==
416 itSysexEvent->pMidiInputPort)
417 {
418 pEngineChannel->Volume(volume);
419 }
420 }
421 #else
422 // apply volume globally to the whole sampler
423 GLOBAL_VOLUME = volume;
424 #endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT
425 break;
426 }
427 }
428 break;
429 }
430 break;
431 }
432 case 0x41: { // Roland
433 dmsg(3,("Roland Sysex\n"));
434 uint8_t device_id, model_id, cmd_id;
435 if (!reader.pop(&device_id)) goto free_sysex_data;
436 if (!reader.pop(&model_id)) goto free_sysex_data;
437 if (!reader.pop(&cmd_id)) goto free_sysex_data;
438 if (model_id != 0x42 /*GS*/) goto free_sysex_data;
439 if (cmd_id != 0x12 /*DT1*/) goto free_sysex_data;
440
441 // command address
442 uint8_t addr[3]; // 2 byte addr MSB, followed by 1 byte addr LSB)
443 const RingBuffer<uint8_t,false>::NonVolatileReader checksum_reader = reader; // so we can calculate the check sum later
444 if (reader.read(&addr[0], 3) != 3) goto free_sysex_data;
445 if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters
446 dmsg(3,("\tSystem Parameter\n"));
447 if (addr[2] == 0x7f) { // GS reset
448 for (int i = 0; i < engineChannels.size(); ++i) {
449 AbstractEngineChannel* pEngineChannel
450 = static_cast<AbstractEngineChannel*>(engineChannels[i]);
451 if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) {
452 KillAllVoices(pEngineChannel, itSysexEvent);
453 pEngineChannel->ResetControllers();
454 }
455 }
456 }
457 }
458 else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters
459 dmsg(3,("\tCommon Parameter\n"));
460 }
461 else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x10) { // Part Parameters (1)
462 dmsg(3,("\tPart Parameter\n"));
463 switch (addr[2]) {
464 case 0x40: { // scale tuning
465 dmsg(3,("\t\tScale Tuning\n"));
466 uint8_t scale_tunes[12]; // detuning of all 12 semitones of an octave
467 if (reader.read(&scale_tunes[0], 12) != 12) goto free_sysex_data;
468 uint8_t checksum;
469 if (!reader.pop(&checksum)) goto free_sysex_data;
470 #if CONFIG_ASSERT_GS_SYSEX_CHECKSUM
471 if (GSCheckSum(checksum_reader, 12)) goto free_sysex_data;
472 #endif // CONFIG_ASSERT_GS_SYSEX_CHECKSUM
473 for (int i = 0; i < 12; i++) scale_tunes[i] -= 64;
474 AdjustScale((int8_t*) scale_tunes);
475 dmsg(3,("\t\t\tNew scale applied.\n"));
476 break;
477 }
478 case 0x15: { // chromatic / drumkit mode
479 dmsg(3,("\t\tMIDI Instrument Map Switch\n"));
480 uint8_t part = addr[1] & 0x0f;
481 uint8_t map;
482 if (!reader.pop(&map)) goto free_sysex_data;
483 for (int i = 0; i < engineChannels.size(); ++i) {
484 AbstractEngineChannel* pEngineChannel
485 = static_cast<AbstractEngineChannel*>(engineChannels[i]);
486 if (
487 (pEngineChannel->midiChannel == part ||
488 pEngineChannel->midiChannel == midi_chan_all) &&
489 pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort
490 ) {
491 try {
492 pEngineChannel->SetMidiInstrumentMap(map);
493 } catch (Exception e) {
494 dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str()));
495 goto free_sysex_data;
496 } catch (...) {
497 dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part));
498 goto free_sysex_data;
499 }
500 }
501 }
502 dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part));
503 break;
504 }
505 }
506 }
507 else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2)
508 }
509 else if (addr[0] == 0x41) { // Drum Setup Parameters
510 }
511 break;
512 }
513 }
514
515 free_sysex_data: // finally free sysex data
516 pSysexBuffer->increment_read_ptr(itSysexEvent->Param.Sysex.Size);
517 }
518
519 String AbstractEngine::GetFormatString(Format f) {
520 switch(f) {
521 case GIG: return "GIG";
522 case SF2: return "SF2";
523 case SFZ: return "SFZ";
524 default: return "UNKNOWN";
525 }
526 }
527
528 String AbstractEngine::EngineName() {
529 return GetFormatString(GetEngineFormat());
530 }
531
532 // static constant initializers
533 const AbstractEngine::FloatTable AbstractEngine::VolumeCurve(InitVolumeCurve());
534 const AbstractEngine::FloatTable AbstractEngine::PanCurve(InitPanCurve());
535 const AbstractEngine::FloatTable AbstractEngine::CrossfadeCurve(InitCrossfadeCurve());
536
537 float* AbstractEngine::InitVolumeCurve() {
538 // line-segment approximation
539 const float segments[] = {
540 0, 0, 2, 0.0046, 16, 0.016, 31, 0.051, 45, 0.115, 54.5, 0.2,
541 64.5, 0.39, 74, 0.74, 92, 1.03, 114, 1.94, 119.2, 2.2, 127, 2.2
542 };
543 return InitCurve(segments);
544 }
545
546 float* AbstractEngine::InitPanCurve() {
547 // line-segment approximation
548 const float segments[] = {
549 0, 0, 1, 0,
550 2, 0.05, 31.5, 0.7, 51, 0.851, 74.5, 1.12,
551 127, 1.41, 128, 1.41
552 };
553 return InitCurve(segments, 129);
554 }
555
556 float* AbstractEngine::InitCrossfadeCurve() {
557 // line-segment approximation
558 const float segments[] = {
559 0, 0, 1, 0.03, 10, 0.1, 51, 0.58, 127, 1
560 };
561 return InitCurve(segments);
562 }
563
564 float* AbstractEngine::InitCurve(const float* segments, int size) {
565 float* y = new float[size];
566 for (int x = 0 ; x < size ; x++) {
567 if (x > segments[2]) segments += 2;
568 y[x] = segments[1] + (x - segments[0]) *
569 (segments[3] - segments[1]) / (segments[2] - segments[0]);
570 }
571 return y;
572 }
573
574 } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC