857 |
* this audio fragment cycle |
* this audio fragment cycle |
858 |
*/ |
*/ |
859 |
void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) { |
void Engine::RouteAudio(EngineChannel* pEngineChannel, uint Samples) { |
860 |
// route master signal |
// route dry signal |
861 |
{ |
{ |
862 |
AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft); |
AudioChannel* pDstL = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelLeft); |
863 |
AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight); |
AudioChannel* pDstR = pAudioOutputDevice->Channel(pEngineChannel->AudioDeviceChannelRight); |
868 |
{ |
{ |
869 |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
870 |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
871 |
// left channel |
for (int iChan = 0; iChan < 2; ++iChan) { |
872 |
const int iDstL = pFxSend->DestinationChannel(0); |
AudioChannel* pSource = |
873 |
if (iDstL < 0) { |
(iChan) |
874 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (L) destination channel")); |
? pEngineChannel->pChannelRight |
875 |
} else { |
: pEngineChannel->pChannelLeft; |
876 |
AudioChannel* pDstL = pAudioOutputDevice->Channel(iDstL); |
const int iDstChan = pFxSend->DestinationChannel(iChan); |
877 |
if (!pDstL) { |
if (iDstChan < 0) { |
878 |
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)); |
879 |
} else pEngineChannel->pChannelLeft->MixTo(pDstL, Samples, pFxSend->Level()); |
goto channel_cleanup; |
880 |
} |
} |
881 |
// right channel |
AudioChannel* pDstChan = NULL; |
882 |
const int iDstR = pFxSend->DestinationChannel(1); |
if (pFxSend->DestinationMasterEffectChain() >= 0) { // fx send routed to an internal master effect |
883 |
if (iDstR < 0) { |
EffectChain* pEffectChain = |
884 |
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (R) destination channel")); |
pAudioOutputDevice->MasterEffectChain( |
885 |
} else { |
pFxSend->DestinationMasterEffectChain() |
886 |
AudioChannel* pDstR = pAudioOutputDevice->Channel(iDstR); |
); |
887 |
if (!pDstR) { |
if (!pEffectChain) { |
888 |
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())); |
889 |
} else pEngineChannel->pChannelRight->MixTo(pDstR, Samples, pFxSend->Level()); |
goto channel_cleanup; |
890 |
|
} |
891 |
|
Effect* pEffect = |
892 |
|
pEffectChain->GetEffect( |
893 |
|
pFxSend->DestinationMasterEffect() |
894 |
|
); |
895 |
|
if (!pEffect) { |
896 |
|
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination effect %d of effect chain %d", ((iChan) ? "R" : "L"), pFxSend->DestinationMasterEffect(), pFxSend->DestinationMasterEffectChain())); |
897 |
|
goto channel_cleanup; |
898 |
|
} |
899 |
|
pDstChan = pEffect->InputChannel(iDstChan); |
900 |
|
} else { // FX send routed directly to an audio output channel |
901 |
|
pDstChan = pAudioOutputDevice->Channel(iDstChan); |
902 |
|
} |
903 |
|
if (!pDstChan) { |
904 |
|
dmsg(1,("Engine::RouteAudio() Error: invalid FX send (%s) destination channel (%d->%d)", ((iChan) ? "R" : "L"), iChan, iDstChan)); |
905 |
|
goto channel_cleanup; |
906 |
|
} |
907 |
|
pSource->MixTo(pDstChan, Samples, pFxSend->Level()); |
908 |
} |
} |
909 |
} |
} |
910 |
} |
} |
911 |
|
channel_cleanup: |
912 |
// reset buffers with silence (zero out) for the next audio cycle |
// reset buffers with silence (zero out) for the next audio cycle |
913 |
pEngineChannel->pChannelLeft->Clear(); |
pEngineChannel->pChannelLeft->Clear(); |
914 |
pEngineChannel->pChannelRight->Clear(); |
pEngineChannel->pChannelRight->Clear(); |
1637 |
void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) { |
void Engine::ProcessControlChange(EngineChannel* pEngineChannel, Pool<Event>::Iterator& itControlChangeEvent) { |
1638 |
dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value)); |
dmsg(4,("Engine::ContinuousController cc=%d v=%d\n", itControlChangeEvent->Param.CC.Controller, itControlChangeEvent->Param.CC.Value)); |
1639 |
|
|
1640 |
|
// handle the "control triggered" MIDI rule: a control change |
1641 |
|
// event can trigger a new note on or note off event |
1642 |
|
if (pEngineChannel->pInstrument) { |
1643 |
|
|
1644 |
|
::gig::MidiRule* rule; |
1645 |
|
for (int i = 0 ; (rule = pEngineChannel->pInstrument->GetMidiRule(i)) ; i++) { |
1646 |
|
|
1647 |
|
if (::gig::MidiRuleCtrlTrigger* ctrlTrigger = |
1648 |
|
dynamic_cast< ::gig::MidiRuleCtrlTrigger*>(rule)) { |
1649 |
|
if (itControlChangeEvent->Param.CC.Controller == |
1650 |
|
ctrlTrigger->ControllerNumber) { |
1651 |
|
|
1652 |
|
uint8_t oldCCValue = pEngineChannel->ControllerTable[ |
1653 |
|
itControlChangeEvent->Param.CC.Controller]; |
1654 |
|
uint8_t newCCValue = itControlChangeEvent->Param.CC.Value; |
1655 |
|
|
1656 |
|
for (int i = 0 ; i < ctrlTrigger->Triggers ; i++) { |
1657 |
|
::gig::MidiRuleCtrlTrigger::trigger_t* pTrigger = |
1658 |
|
&ctrlTrigger->pTriggers[i]; |
1659 |
|
|
1660 |
|
// check if the controller has passed the |
1661 |
|
// trigger point in the right direction |
1662 |
|
if ((pTrigger->Descending && |
1663 |
|
oldCCValue > pTrigger->TriggerPoint && |
1664 |
|
newCCValue <= pTrigger->TriggerPoint) || |
1665 |
|
(!pTrigger->Descending && |
1666 |
|
oldCCValue < pTrigger->TriggerPoint && |
1667 |
|
newCCValue >= pTrigger->TriggerPoint)) { |
1668 |
|
|
1669 |
|
RTList<Event>::Iterator itNewEvent = pGlobalEvents->allocAppend(); |
1670 |
|
if (itNewEvent) { |
1671 |
|
*itNewEvent = *itControlChangeEvent; |
1672 |
|
itNewEvent->Param.Note.Key = pTrigger->Key; |
1673 |
|
|
1674 |
|
if (pTrigger->NoteOff || pTrigger->Velocity == 0) { |
1675 |
|
itNewEvent->Type = Event::type_note_off; |
1676 |
|
itNewEvent->Param.Note.Velocity = 100; |
1677 |
|
|
1678 |
|
ProcessNoteOff(pEngineChannel, itNewEvent); |
1679 |
|
} else { |
1680 |
|
itNewEvent->Type = Event::type_note_on; |
1681 |
|
//TODO: if Velocity is 255, the triggered velocity should |
1682 |
|
// depend on how fast the controller is moving |
1683 |
|
itNewEvent->Param.Note.Velocity = |
1684 |
|
pTrigger->Velocity == 255 ? 100 : |
1685 |
|
pTrigger->Velocity; |
1686 |
|
|
1687 |
|
ProcessNoteOn(pEngineChannel, itNewEvent); |
1688 |
|
} |
1689 |
|
} |
1690 |
|
else dmsg(1,("Event pool emtpy!\n")); |
1691 |
|
} |
1692 |
|
} |
1693 |
|
} |
1694 |
|
} |
1695 |
|
} |
1696 |
|
} |
1697 |
|
|
1698 |
// update controller value in the engine channel's controller table |
// update controller value in the engine channel's controller table |
1699 |
pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value; |
pEngineChannel->ControllerTable[itControlChangeEvent->Param.CC.Controller] = itControlChangeEvent->Param.CC.Value; |
1700 |
|
|
1728 |
//TODO: not sample accurate yet |
//TODO: not sample accurate yet |
1729 |
pEngineChannel->GlobalPanLeft = PanCurve[128 - itControlChangeEvent->Param.CC.Value]; |
pEngineChannel->GlobalPanLeft = PanCurve[128 - itControlChangeEvent->Param.CC.Value]; |
1730 |
pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value]; |
pEngineChannel->GlobalPanRight = PanCurve[itControlChangeEvent->Param.CC.Value]; |
1731 |
|
pEngineChannel->iLastPanRequest = itControlChangeEvent->Param.CC.Value; |
1732 |
break; |
break; |
1733 |
} |
} |
1734 |
case 64: { // sustain |
case 64: { // sustain |
1869 |
if (!pEngineChannel->fxSends.empty()) { |
if (!pEngineChannel->fxSends.empty()) { |
1870 |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
for (int iFxSend = 0; iFxSend < pEngineChannel->GetFxSendCount(); iFxSend++) { |
1871 |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
FxSend* pFxSend = pEngineChannel->GetFxSend(iFxSend); |
1872 |
if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) |
if (pFxSend->MidiController() == itControlChangeEvent->Param.CC.Controller) { |
1873 |
pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value); |
pFxSend->SetLevel(itControlChangeEvent->Param.CC.Value); |
1874 |
pFxSend->SetInfoChanged(true); |
pFxSend->SetInfoChanged(true); |
1875 |
|
} |
1876 |
} |
} |
1877 |
} |
} |
1878 |
} |
} |
1891 |
if (exclusive_status != 0xF0) goto free_sysex_data; |
if (exclusive_status != 0xF0) goto free_sysex_data; |
1892 |
|
|
1893 |
switch (id) { |
switch (id) { |
1894 |
|
case 0x7f: { // (Realtime) Universal Sysex (GM Standard) |
1895 |
|
uint8_t sysex_channel, sub_id1, sub_id2, val_msb, val_lsb;; |
1896 |
|
if (!reader.pop(&sysex_channel)) goto free_sysex_data; |
1897 |
|
if (!reader.pop(&sub_id1)) goto free_sysex_data; |
1898 |
|
if (!reader.pop(&sub_id2)) goto free_sysex_data; |
1899 |
|
if (!reader.pop(&val_lsb)) goto free_sysex_data; |
1900 |
|
if (!reader.pop(&val_msb)) goto free_sysex_data; |
1901 |
|
//TODO: for now we simply ignore the sysex channel, seldom used anyway |
1902 |
|
switch (sub_id1) { |
1903 |
|
case 0x04: // Device Control |
1904 |
|
switch (sub_id2) { |
1905 |
|
case 0x01: // Master Volume |
1906 |
|
GLOBAL_VOLUME = |
1907 |
|
double((uint(val_msb)<<7) | uint(val_lsb)) / 16383.0; |
1908 |
|
break; |
1909 |
|
} |
1910 |
|
break; |
1911 |
|
} |
1912 |
|
break; |
1913 |
|
} |
1914 |
case 0x41: { // Roland |
case 0x41: { // Roland |
1915 |
dmsg(3,("Roland Sysex\n")); |
dmsg(3,("Roland Sysex\n")); |
1916 |
uint8_t device_id, model_id, cmd_id; |
uint8_t device_id, model_id, cmd_id; |
2089 |
} |
} |
2090 |
|
|
2091 |
String Engine::Version() { |
String Engine::Version() { |
2092 |
String s = "$Revision: 1.87 $"; |
String s = "$Revision: 1.92 $"; |
2093 |
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 |
2094 |
} |
} |
2095 |
|
|