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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2012 - (hide 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 iliev 2012 /***************************************************************************
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