3 |
* LinuxSampler - modular, streaming capable sampler * |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
* * |
5 |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
* Copyright (C) 2003,2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005-2008 Christian Schoenebeck * |
* Copyright (C) 2005-2009 Christian Schoenebeck * |
7 |
* * |
* * |
8 |
* This program is free software; you can redistribute it and/or modify * |
* 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 * |
* it under the terms of the GNU General Public License as published by * |
107 |
pSysexBuffer = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0); |
pSysexBuffer = new RingBuffer<uint8_t,false>(CONFIG_SYSEX_BUFFER_SIZE, 0); |
108 |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
pEventQueue = new RingBuffer<Event,false>(CONFIG_MAX_EVENTS_PER_FRAGMENT, 0); |
109 |
pEventPool = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT); |
pEventPool = new Pool<Event>(CONFIG_MAX_EVENTS_PER_FRAGMENT); |
110 |
pVoicePool = new Pool<Voice>(CONFIG_MAX_VOICES); |
pVoicePool = new Pool<Voice>(GLOBAL_MAX_VOICES); |
111 |
pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES); |
pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(GLOBAL_MAX_VOICES); |
112 |
pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(CONFIG_MAX_VOICES); |
pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(GLOBAL_MAX_VOICES); |
113 |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
pVoiceStealingQueue = new RTList<Event>(pEventPool); |
114 |
pGlobalEvents = new RTList<Event>(pEventPool); |
pGlobalEvents = new RTList<Event>(pEventPool); |
115 |
|
iMaxDiskStreams = GLOBAL_MAX_STREAMS; |
116 |
|
FrameTime = 0; |
117 |
|
|
118 |
for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
119 |
iterVoice->SetEngine(this); |
iterVoice->SetEngine(this); |
213 |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
214 |
iPendingStreamDeletions++; |
iPendingStreamDeletions++; |
215 |
} |
} |
216 |
|
// free the voice to the voice pool and update key info |
217 |
|
FreeVoice(pEngineChannel, itVoice); |
218 |
} |
} |
219 |
} |
} |
220 |
} |
} |
295 |
// make sure that the engine does not get any sysex messages |
// make sure that the engine does not get any sysex messages |
296 |
// while it's reseting |
// while it's reseting |
297 |
bool sysexDisabled = MidiInputPort::RemoveSysexListener(this); |
bool sysexDisabled = MidiInputPort::RemoveSysexListener(this); |
298 |
ActiveVoiceCount = 0; |
SetVoiceCount(0); |
299 |
ActiveVoiceCountMax = 0; |
ActiveVoiceCountMax = 0; |
300 |
|
|
301 |
// reset voice stealing parameters |
// reset voice stealing parameters |
346 |
* @param pAudioOut - audio output device to connect to |
* @param pAudioOut - audio output device to connect to |
347 |
*/ |
*/ |
348 |
void Engine::Connect(AudioOutputDevice* pAudioOut) { |
void Engine::Connect(AudioOutputDevice* pAudioOut) { |
349 |
|
// caution: don't ignore if connecting to the same device here, |
350 |
|
// because otherwise SetMaxDiskStreams() implementation won't work anymore! |
351 |
|
|
352 |
pAudioOutputDevice = pAudioOut; |
pAudioOutputDevice = pAudioOut; |
353 |
|
|
354 |
ResetInternal(); |
ResetInternal(); |
365 |
this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle(); |
this->MaxSamplesPerCycle = pAudioOutputDevice->MaxSamplesPerCycle(); |
366 |
this->SampleRate = pAudioOutputDevice->SampleRate(); |
this->SampleRate = pAudioOutputDevice->SampleRate(); |
367 |
|
|
368 |
// FIXME: audio drivers with varying fragment sizes might be a problem here |
MinFadeOutSamples = int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1; |
369 |
MaxFadeOutPos = MaxSamplesPerCycle - int(double(SampleRate) * CONFIG_EG_MIN_RELEASE_TIME) - 1; |
if (MaxSamplesPerCycle < MinFadeOutSamples) { |
|
if (MaxFadeOutPos < 0) { |
|
370 |
std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME " |
std::cerr << "gig::Engine: WARNING, CONFIG_EG_MIN_RELEASE_TIME " |
371 |
<< "too big for current audio fragment size & sampling rate! " |
<< "too big for current audio fragment size & sampling rate! " |
372 |
<< "May lead to click sounds if voice stealing chimes in!\n" << std::flush; |
<< "May lead to click sounds if voice stealing chimes in!\n" << std::flush; |
373 |
// force volume ramp downs at the beginning of each fragment |
// force volume ramp downs at the beginning of each fragment |
374 |
MaxFadeOutPos = 0; |
MinFadeOutSamples = MaxSamplesPerCycle; |
375 |
// lower minimum release time |
// lower minimum release time |
376 |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
const float minReleaseTime = (float) MaxSamplesPerCycle / (float) SampleRate; |
377 |
for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
387 |
delete this->pDiskThread; |
delete this->pDiskThread; |
388 |
dmsg(1,("OK\n")); |
dmsg(1,("OK\n")); |
389 |
} |
} |
390 |
this->pDiskThread = new DiskThread(((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo |
this->pDiskThread = |
391 |
&instruments); |
new DiskThread( |
392 |
|
iMaxDiskStreams, |
393 |
|
((pAudioOut->MaxSamplesPerCycle() << CONFIG_MAX_PITCH) << 1) + 6, //FIXME: assuming stereo |
394 |
|
&instruments |
395 |
|
); |
396 |
|
|
397 |
if (!pDiskThread) { |
if (!pDiskThread) { |
398 |
dmsg(0,("gig::Engine new diskthread = NULL\n")); |
dmsg(0,("gig::Engine new diskthread = NULL\n")); |
399 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
419 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
420 |
} |
} |
421 |
} |
} |
422 |
|
pVoicePool->clear(); |
423 |
} |
} |
424 |
|
|
425 |
/** |
/** |
446 |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
if (hStream != Stream::INVALID_HANDLE) { // voice actually used a stream |
447 |
iPendingStreamDeletions++; |
iPendingStreamDeletions++; |
448 |
} |
} |
449 |
|
//NOTE: maybe we should call FreeVoice() here, shouldn't cause a harm though I think, since the voices should be freed by RenderActiveVoices() in the render loop, they are probably just freed a bit later than they could/should be |
450 |
} |
} |
451 |
} |
} |
452 |
} |
} |
587 |
// return if engine disabled |
// return if engine disabled |
588 |
if (EngineDisabled.Pop()) { |
if (EngineDisabled.Pop()) { |
589 |
dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe())); |
dmsg(5,("gig::Engine: engine disabled (val=%d)\n",EngineDisabled.GetUnsafe())); |
590 |
|
EngineDisabled.RttDone(); |
591 |
return 0; |
return 0; |
592 |
} |
} |
593 |
|
|
598 |
// update time of start and end of this audio fragment (as events' time stamps relate to this) |
// update time of start and end of this audio fragment (as events' time stamps relate to this) |
599 |
pEventGenerator->UpdateFragmentTime(Samples); |
pEventGenerator->UpdateFragmentTime(Samples); |
600 |
|
|
601 |
// We only allow a maximum of CONFIG_MAX_VOICES voices to be spawned |
// We only allow the given maximum number of voices to be spawned |
602 |
// in each audio fragment. All subsequent request for spawning new |
// in each audio fragment. All subsequent request for spawning new |
603 |
// voices in the same audio fragment will be ignored. |
// voices in the same audio fragment will be ignored. |
604 |
VoiceSpawnsLeft = CONFIG_MAX_VOICES; |
VoiceSpawnsLeft = MaxVoices(); |
605 |
|
|
606 |
// get all events from the engine's global input event queue which belong to the current fragment |
// get all events from the engine's global input event queue which belong to the current fragment |
607 |
// (these are usually just SysEx messages) |
// (these are usually just SysEx messages) |
700 |
pVoiceStealingQueue->clear(); |
pVoiceStealingQueue->clear(); |
701 |
|
|
702 |
// just some statistics about this engine instance |
// just some statistics about this engine instance |
703 |
ActiveVoiceCount = ActiveVoiceCountTemp; |
SetVoiceCount(ActiveVoiceCountTemp); |
704 |
if (ActiveVoiceCount > ActiveVoiceCountMax) ActiveVoiceCountMax = ActiveVoiceCount; |
if (VoiceCount() > ActiveVoiceCountMax) ActiveVoiceCountMax = VoiceCount(); |
705 |
|
|
706 |
// in case regions were previously suspended and we killed voices |
// in case regions were previously suspended and we killed voices |
707 |
// with disk streams due to that, check if those streams have finally |
// with disk streams due to that, check if those streams have finally |
713 |
} |
} |
714 |
FrameTime += Samples; |
FrameTime += Samples; |
715 |
|
|
716 |
|
EngineDisabled.RttDone(); |
717 |
return 0; |
return 0; |
718 |
} |
} |
719 |
|
|
800 |
voiceCount++; |
voiceCount++; |
801 |
|
|
802 |
if (itVoice->PlaybackState == Voice::playback_state_disk) { |
if (itVoice->PlaybackState == Voice::playback_state_disk) { |
803 |
if ((itVoice->DiskStreamRef).State == Stream::state_active) streamCount++; |
if ((itVoice->DiskStreamRef).State != Stream::state_unused) streamCount++; |
804 |
} |
} |
805 |
} else { // voice reached end, is now inactive |
} else { // voice reached end, is now inactive |
806 |
FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices |
FreeVoice(pEngineChannel, itVoice); // remove voice from the list of active voices |
841 |
pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1); |
pEngineChannel->SetVoiceCount(pEngineChannel->GetVoiceCount() + 1); |
842 |
|
|
843 |
if (itNewVoice->PlaybackState == Voice::playback_state_disk) { |
if (itNewVoice->PlaybackState == Voice::playback_state_disk) { |
844 |
if (itNewVoice->DiskStreamRef.State == Stream::state_active) { |
if (itNewVoice->DiskStreamRef.State != Stream::state_unused) { |
845 |
pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1); |
pEngineChannel->SetDiskStreamCount(pEngineChannel->GetDiskStreamCount() + 1); |
846 |
} |
} |
847 |
} |
} |
872 |
* this audio fragment cycle |
* this audio fragment cycle |
873 |
*/ |
*/ |
874 |
void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) { |
void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) { |
875 |
// route master signal |
// route dry signal |
876 |
{ |
{ |
877 |
AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft); |
AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft); |
878 |
AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight); |
AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight); |
883 |
{ |
{ |
884 |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
885 |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
886 |
// left channel |
for (int iChan = 0; iChan < 2; ++iChan) { |
887 |
const int iDstL = pFxSend->DestinationChannel(0); |
AudioChannel* pSource = |
888 |
if (iDstL < 0) { |
(iChan) |
889 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel")); |
? pEngineChannel->pChannelRight |
890 |
} else { |
: pEngineChannel->pChannelLeft; |
891 |
AudioChannel* pDstL = pAudioOutputDevice->Channel(iDstL); |
const int iDstChan = pFxSend->DestinationChannel(iChan); |
892 |
if (!pDstL) { |
if (iDstChan < 0) { |
893 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel")); |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan)); |
894 |
} else pEngineChannel->pChannelLeft->MixTo(pDstL, Samples, pFxSend->Level()); |
goto channel_cleanup; |
895 |
} |
} |
896 |
// right channel |
AudioChannel* pDstChan = NULL; |
897 |
const int iDstR = pFxSend->DestinationChannel(1); |
if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect |
898 |
if (iDstR < 0) { |
EffectChain* pEffectChain = |
899 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel")); |
pAudioOutputDevice->MasterEffectChain( |
900 |
} else { |
pFxSend->DestinationMasterEffectChain() |
901 |
AudioChannel* pDstR = pAudioOutputDevice->Channel(iDstR); |
); |
902 |
if (!pDstR) { |
if (!pEffectChain) { |
903 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel")); |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffectChain())); |
904 |
} else pEngineChannel->pChannelRight->MixTo(pDstR, Samples, pFxSend->Level()); |
goto channel_cleanup; |
905 |
|
} |
906 |
|
Effect* pEffect = |
907 |
|
pEffectChain->GetEffect( |
908 |
|
pFxSend->DestinationMasterEffect() |
909 |
|
); |
910 |
|
if (!pEffect) { |
911 |
|
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain())); |
912 |
|
goto channel_cleanup; |
913 |
|
} |
914 |
|
pDstChan = pEffect->InputChannel(iDstChan); |
915 |
|
} else { // FX send routed directly to an audio output channel |
916 |
|
pDstChan = pAudioOutputDevice->Channel(iDstChan); |
917 |
|
} |
918 |
|
if (!pDstChan) { |
919 |
|
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan)); |
920 |
|
goto channel_cleanup; |
921 |
|
} |
922 |
|
pSource->MixTo(pDstChan, Samples, pFxSend->Level()); |
923 |
} |
} |
924 |
} |
} |
925 |
} |
} |
926 |
|
channel_cleanup: |
927 |
// reset buffers with silence (zero out) for the next audio cycle |
// reset buffers with silence (zero out) for the next audio cycle |
928 |
pEngineChannel->pChannelLeft->Clear(); |
pEngineChannel->pChannelLeft->Clear(); |
929 |
pEngineChannel->pChannelRight->Clear(); |
pEngineChannel->pChannelRight->Clear(); |
969 |
* |
* |
970 |
* @param pData - pointer to sysex data |
* @param pData - pointer to sysex data |
971 |
* @param Size - lenght of sysex data (in bytes) |
* @param Size - lenght of sysex data (in bytes) |
972 |
|
* @param pSender - the MIDI input port on which the SysEx message was |
973 |
|
* received |
974 |
*/ |
*/ |
975 |
void Engine::SendSysex(void* pData, uint Size) { |
void Engine::SendSysex(void* pData, uint Size, MidiInputPort* pSender) { |
976 |
Event event = pEventGenerator->CreateEvent(); |
Event event = pEventGenerator->CreateEvent(); |
977 |
event.Type = Event::type_sysex; |
event.Type = Event::type_sysex; |
978 |
event.Param.Sysex.Size = Size; |
event.Param.Sysex.Size = Size; |
979 |
event.pEngineChannel = NULL; // as Engine global event |
event.pEngineChannel = NULL; // as Engine global event |
980 |
|
event.pMidiInputPort = pSender; |
981 |
if (pEventQueue->write_space() > 0) { |
if (pEventQueue->write_space() > 0) { |
982 |
if (pSysexBuffer->write_space() >= Size) { |
if (pSysexBuffer->write_space() >= Size) { |
983 |
// copy sysex data to input buffer |
// copy sysex data to input buffer |
1746 |
//TODO: not sample accurate yet |
//TODO: not sample accurate yet |
1747 |
pEngineChannel->GlobalPanLeft = PanCurve[128 - itControlChangeEvent->Param.CC.Value]; |
pEngineChannel->GlobalPanLeft = PanCurve[128 - itControlChangeEvent->Param.CC.Value]; |
1748 |
pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value]; |
pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value]; |
1749 |
|
pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
1750 |
break; |
break; |
1751 |
} |
} |
1752 |
case 64: { // sustain |
case 64: { // sustain |
1887 |
if (!pEngineChannel->fxSends.empty()) { |
if (!pEngineChannel->fxSends.empty()) { |
1888 |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
1889 |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
1890 |
if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) |
if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) { |
1891 |
pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value); |
pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value); |
1892 |
pFxSend->SetInfoChanged(true); |
pFxSend->SetInfoChanged(true); |
1893 |
|
} |
1894 |
} |
} |
1895 |
} |
} |
1896 |
} |
} |
1909 |
if (exclusive_status != 0xF0) goto free_sysex_data; |
if (exclusive_status != 0xF0) goto free_sysex_data; |
1910 |
|
|
1911 |
switch (id) { |
switch (id) { |
1912 |
|
case 0x7f: { // (Realtime) Universal Sysex (GM Standard) |
1913 |
|
uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;; |
1914 |
|
if (!reader.pop(&sysex_channel)) goto free_sysex_data; |
1915 |
|
if (!reader.pop(&sub_id1)) goto free_sysex_data; |
1916 |
|
if (!reader.pop(&sub_id2)) goto free_sysex_data; |
1917 |
|
if (!reader.pop(&val_lsb)) goto free_sysex_data; |
1918 |
|
if (!reader.pop(&val_msb)) goto free_sysex_data; |
1919 |
|
//TODO: for now we simply ignore the sysex channel, seldom used anyway |
1920 |
|
switch (sub_id1) { |
1921 |
|
case 0x04: // Device Control |
1922 |
|
switch (sub_id2) { |
1923 |
|
case 0x01: { // Master Volume |
1924 |
|
const double volume = |
1925 |
|
double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0; |
1926 |
|
#if CONFIG_MASTER_VOLUME_SYSEX_BY_PORT |
1927 |
|
// apply volume to all sampler channels that |
1928 |
|
// are connected to the same MIDI input port |
1929 |
|
// this sysex message arrived on |
1930 |
|
for (int i = 0; i < engineChannels.size(); ++i) { |
1931 |
|
EngineChannel* pEngineChannel = engineChannels[i]; |
1932 |
|
if (pEngineChannel->GetMidiInputPort() == |
1933 |
|
itSysexEvent->pMidiInputPort) |
1934 |
|
{ |
1935 |
|
pEngineChannel->Volume(volume); |
1936 |
|
} |
1937 |
|
} |
1938 |
|
#else |
1939 |
|
// apply volume globally to the whole sampler |
1940 |
|
GLOBAL_VOLUME = volume; |
1941 |
|
#endif // CONFIG_MASTER_VOLUME_SYSEX_BY_PORT |
1942 |
|
break; |
1943 |
|
} |
1944 |
|
} |
1945 |
|
break; |
1946 |
|
} |
1947 |
|
break; |
1948 |
|
} |
1949 |
case 0x41: { // Roland |
case 0x41: { // Roland |
1950 |
dmsg(3,("Roland Sysex\n")); |
dmsg(3,("Roland Sysex\n")); |
1951 |
uint8_t device_id, model_id, cmd_id; |
uint8_t device_id, model_id, cmd_id; |
1961 |
if (reader.read(&addr[0], 3) != 3) goto free_sysex_data; |
if (reader.read(&addr[0], 3) != 3) goto free_sysex_data; |
1962 |
if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters |
if (addr[0] == 0x40 && addr[1] == 0x00) { // System Parameters |
1963 |
dmsg(3,("\tSystem Parameter\n")); |
dmsg(3,("\tSystem Parameter\n")); |
1964 |
|
if (addr[2] == 0x7f) { // GS reset |
1965 |
|
for (int i = 0; i < engineChannels.size(); ++i) { |
1966 |
|
EngineChannel* pEngineChannel = engineChannels[i]; |
1967 |
|
if (pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort) { |
1968 |
|
KillAllVoices(pEngineChannel, itSysexEvent); |
1969 |
|
pEngineChannel->ResetControllers(); |
1970 |
|
} |
1971 |
|
} |
1972 |
|
} |
1973 |
} |
} |
1974 |
else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters |
else if (addr[0] == 0x40 && addr[1] == 0x01) { // Common Parameters |
1975 |
dmsg(3,("\tCommon Parameter\n")); |
dmsg(3,("\tCommon Parameter\n")); |
1991 |
dmsg(3,("\t\t\tNew scale applied.\n")); |
dmsg(3,("\t\t\tNew scale applied.\n")); |
1992 |
break; |
break; |
1993 |
} |
} |
1994 |
|
case 0x15: { // chromatic / drumkit mode |
1995 |
|
dmsg(3,("\t\tMIDI Instrument Map Switch\n")); |
1996 |
|
uint8_t part = addr[1] & 0x0f; |
1997 |
|
uint8_t map; |
1998 |
|
if (!reader.pop(&map)) goto free_sysex_data; |
1999 |
|
for (int i = 0; i < engineChannels.size(); ++i) { |
2000 |
|
EngineChannel* pEngineChannel = engineChannels[i]; |
2001 |
|
if ( |
2002 |
|
(pEngineChannel->midiChannel == part || |
2003 |
|
pEngineChannel->midiChannel == midi_chan_all) && |
2004 |
|
pEngineChannel->GetMidiInputPort() == itSysexEvent->pMidiInputPort |
2005 |
|
) { |
2006 |
|
try { |
2007 |
|
pEngineChannel->SetMidiInstrumentMap(map); |
2008 |
|
} catch (Exception e) { |
2009 |
|
dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d: %s\n", map, part, e.Message().c_str())); |
2010 |
|
goto free_sysex_data; |
2011 |
|
} catch (...) { |
2012 |
|
dmsg(2,("\t\t\tCould not apply MIDI instrument map %d to part %d (unknown exception)\n", map, part)); |
2013 |
|
goto free_sysex_data; |
2014 |
|
} |
2015 |
|
} |
2016 |
|
} |
2017 |
|
dmsg(3,("\t\t\tApplied MIDI instrument map %d to part %d.\n", map, part)); |
2018 |
|
break; |
2019 |
|
} |
2020 |
} |
} |
2021 |
} |
} |
2022 |
else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2) |
else if (addr[0] == 0x40 && (addr[1] & 0xf0) == 0x20) { // Part Parameters (2) |
2123 |
} |
} |
2124 |
|
|
2125 |
uint Engine::VoiceCount() { |
uint Engine::VoiceCount() { |
2126 |
return ActiveVoiceCount; |
return atomic_read(&ActiveVoiceCount); |
2127 |
|
} |
2128 |
|
|
2129 |
|
void Engine::SetVoiceCount(uint Count) { |
2130 |
|
atomic_set(&ActiveVoiceCount, Count); |
2131 |
} |
} |
2132 |
|
|
2133 |
uint Engine::VoiceCountMax() { |
uint Engine::VoiceCountMax() { |
2134 |
return ActiveVoiceCountMax; |
return ActiveVoiceCountMax; |
2135 |
} |
} |
2136 |
|
|
2137 |
|
int Engine::MaxVoices() { |
2138 |
|
return pVoicePool->poolSize(); |
2139 |
|
} |
2140 |
|
|
2141 |
|
void Engine::SetMaxVoices(int iVoices) throw (Exception) { |
2142 |
|
if (iVoices < 1) |
2143 |
|
throw Exception("Maximum voices for an engine cannot be set lower than 1"); |
2144 |
|
|
2145 |
|
SuspendAll(); |
2146 |
|
|
2147 |
|
// NOTE: we need to clear pDimRegionsInUse before deleting pDimRegionPool, |
2148 |
|
// otherwise memory corruption will occur if there are active voices (see bug #118) |
2149 |
|
for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) { |
2150 |
|
engineChannels[iChannel]->ClearDimRegionsInUse(); |
2151 |
|
} |
2152 |
|
|
2153 |
|
if (pDimRegionPool[0]) delete pDimRegionPool[0]; |
2154 |
|
if (pDimRegionPool[1]) delete pDimRegionPool[1]; |
2155 |
|
|
2156 |
|
pDimRegionPool[0] = new Pool< ::gig::DimensionRegion*>(iVoices); |
2157 |
|
pDimRegionPool[1] = new Pool< ::gig::DimensionRegion*>(iVoices); |
2158 |
|
|
2159 |
|
for (int iChannel = 0; iChannel < engineChannels.size(); iChannel++) { |
2160 |
|
engineChannels[iChannel]->ResetDimRegionsInUse(); |
2161 |
|
} |
2162 |
|
|
2163 |
|
try { |
2164 |
|
pVoicePool->resizePool(iVoices); |
2165 |
|
} catch (...) { |
2166 |
|
throw Exception("FATAL: Could not resize voice pool!"); |
2167 |
|
} |
2168 |
|
|
2169 |
|
for (RTList<Voice>::Iterator iterVoice = pVoicePool->allocAppend(); iterVoice == pVoicePool->last(); iterVoice = pVoicePool->allocAppend()) { |
2170 |
|
iterVoice->SetEngine(this); |
2171 |
|
iterVoice->pDiskThread = this->pDiskThread; |
2172 |
|
} |
2173 |
|
pVoicePool->clear(); |
2174 |
|
|
2175 |
|
ResumeAll(); |
2176 |
|
} |
2177 |
|
|
2178 |
bool Engine::DiskStreamSupported() { |
bool Engine::DiskStreamSupported() { |
2179 |
return true; |
return true; |
2180 |
} |
} |
2181 |
|
|
2182 |
uint Engine::DiskStreamCount() { |
uint Engine::DiskStreamCount() { |
2183 |
return (pDiskThread) ? pDiskThread->ActiveStreamCount : 0; |
return (pDiskThread) ? pDiskThread->GetActiveStreamCount() : 0; |
2184 |
} |
} |
2185 |
|
|
2186 |
uint Engine::DiskStreamCountMax() { |
uint Engine::DiskStreamCountMax() { |
2187 |
return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; |
return (pDiskThread) ? pDiskThread->ActiveStreamCountMax : 0; |
2188 |
} |
} |
2189 |
|
|
2190 |
|
int Engine::MaxDiskStreams() { |
2191 |
|
return iMaxDiskStreams; |
2192 |
|
} |
2193 |
|
|
2194 |
|
void Engine::SetMaxDiskStreams(int iStreams) throw (Exception) { |
2195 |
|
if (iStreams < 0) |
2196 |
|
throw Exception("Maximum disk streams for an engine cannot be set lower than 0"); |
2197 |
|
|
2198 |
|
SuspendAll(); |
2199 |
|
|
2200 |
|
iMaxDiskStreams = iStreams; |
2201 |
|
|
2202 |
|
// reconnect to audio output device, because that will automatically |
2203 |
|
// recreate the disk thread with the required amount of streams |
2204 |
|
if (pAudioOutputDevice) Connect(pAudioOutputDevice); |
2205 |
|
|
2206 |
|
ResumeAll(); |
2207 |
|
} |
2208 |
|
|
2209 |
String Engine::DiskStreamBufferFillBytes() { |
String Engine::DiskStreamBufferFillBytes() { |
2210 |
return pDiskThread->GetBufferFillBytes(); |
return pDiskThread->GetBufferFillBytes(); |
2211 |
} |
} |
2223 |
} |
} |
2224 |
|
|
2225 |
String Engine::Version() { |
String Engine::Version() { |
2226 |
String s = "$Revision: 1.88 $"; |
String s = "$Revision: 1.103 $"; |
2227 |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
2228 |
} |
} |
2229 |
|
|