diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 46320691..f72d665b 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -131,6 +131,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size) bool mustClear=false; for (int i=0; i16) want.outChans=16; + output->setCallback(process,this); if (!output->init(want,got)) { @@ -4164,6 +4167,13 @@ bool DivEngine::initAudioBackend() { return false; } + for (int i=0; iinitMidi(false)) { midiIns=output->midiIn->listDevices(); midiOuts=output->midiOut->listDevices(); @@ -4296,12 +4306,6 @@ bool DivEngine::init() { keyHit[i]=false; } - oscBuf[0]=new float[32768]; - oscBuf[1]=new float[32768]; - - memset(oscBuf[0],0,32768*sizeof(float)); - memset(oscBuf[1],0,32768*sizeof(float)); - initDispatch(); renderSamples(); reset(); @@ -4328,8 +4332,9 @@ bool DivEngine::quit() { logI("saving config."); saveConf(); active=false; - delete[] oscBuf[0]; - delete[] oscBuf[1]; + for (int i=0; idata[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0; } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 8696864c..54aef382 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1579,10 +1579,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi if (!playing) { if (out!=NULL) { - // TODO: handle more than 2 outputs for (unsigned int i=0; i=32768) oscWritePos=0; } oscSize=size; @@ -1707,13 +1708,20 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi volR*=disCont[i].dispatch->getPostAmp(); if (disCont[i].dispatch->getOutputCount()>1) { for (size_t j=0; jgetOutputCount()); + for (int k=0; k=1) metroPos--; } - // TODO: handle more than 2 outputs for (unsigned int i=0; i=32768) oscWritePos=0; } oscSize=size; - // TODO: handle more than 2 outputs - if (forceMono) { + if (forceMono && outChans>1) { for (size_t i=0; igetAudioDescGot().outChans; j++) { + peakMix+=peak[j]; + } + peakMix/=e->getAudioDescGot().outChans; + } ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b); dl->AddRectFilled(ImVec2(0,0),ImVec2(canvasW,canvasH),0xff000000); bool skip=false; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 59d23f2e..72195bb4 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6095,8 +6095,7 @@ FurnaceGUI::FurnaceGUI(): memset(willExport,1,DIV_MAX_CHIPS*sizeof(bool)); - peak[0]=0; - peak[1]=0; + memset(peak,0,DIV_MAX_OUTPUTS*sizeof(float)); opMaskTransposeNote.note=true; opMaskTransposeNote.ins=false; diff --git a/src/gui/gui.h b/src/gui/gui.h index 88c30651..6ed7f2d3 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1172,6 +1172,7 @@ class FurnaceGUI { int mainFontSize, patFontSize, iconSize; int audioEngine; int audioQuality; + int audioChans; int arcadeCore; int ym2612Core; int snCore; @@ -1306,6 +1307,7 @@ class FurnaceGUI { iconSize(16), audioEngine(DIV_AUDIO_SDL), audioQuality(0), + audioChans(2), arcadeCore(0), ym2612Core(0), snCore(0), @@ -1458,7 +1460,7 @@ class FurnaceGUI { bool keepLoopAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton; FurnaceGUIWindows curWindow, nextWindow, curWindowLast; std::atomic curWindowThreadSafe; - float peak[2]; + float peak[DIV_MAX_OUTPUTS]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; float lastPatternWidth, longThreshold; diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 7791e8e5..7f414221 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -49,14 +49,19 @@ void FurnaceGUI::readOsc() { int oscReadPos=(writePos-winSize)&0x7fff; for (int i=0; i<512; i++) { int pos=(oscReadPos+(i*winSize/512))&0x7fff; - oscValues[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; + oscValues[i]=0; + for (int j=0; jgetAudioDescGot().outChans; j++) { + oscValues[i]+=e->oscBuf[j][pos]; + } + oscValues[i]/=e->getAudioDescGot().outChans; + if (oscValues[i]>0.001f || oscValues[i]<-0.001f) { WAKE_UP; } } float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; - for (int i=0; i<2; i++) { + for (int i=0; igetAudioDescGot().outChans; i++) { peak[i]*=1.0-peakDecay; if (peak[i]<0.0001) { peak[i]=0.0; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2b670184..2b12c430 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -73,6 +73,22 @@ const char* audioBackends[]={ "SDL" }; +const bool isProAudio[]={ + true, + false +}; + +const char* nonProAudioOuts[]={ + "Mono", + "Stereo", + "What?", + "Quadraphonic", + "What?", + "5.1 Surround", + "What?", + "7.1 Surround" +}; + const char* audioQualities[]={ "High", "Low" @@ -192,6 +208,11 @@ const char* specificControls[18]={ settings.audioBufSize=x; \ } +#define CHANS_SELECTABLE(x) \ + if (ImGui::Selectable(nonProAudioOuts[x-1],settings.audioChans==x)) { \ + settings.audioChans=x; \ + } + #define UI_COLOR_CONFIG(what,label) \ if (ImGui::ColorEdit4(label "##CC_" #what,(float*)&uiColors[what])) { \ applyUISettings(false); \ @@ -676,9 +697,16 @@ void FurnaceGUI::drawSettings() { ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; if (ImGui::BeginChild("SettingsView",settingsViewSize)) { +#ifdef HAVE_JACK ImGui::Text("Backend"); ImGui::SameLine(); - ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2); + int prevAudioEngine=settings.audioEngine; + if (ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2)) { + if (settings.audioEngine!=prevAudioEngine) { + if (!isProAudio[settings.audioEngine]) settings.audioChans=2; + } + } +#endif ImGui::Text("Device"); ImGui::SameLine(); @@ -711,6 +739,27 @@ void FurnaceGUI::drawSettings() { ImGui::EndCombo(); } + if (isProAudio[settings.audioEngine]) { + ImGui::Text("Outputs"); + ImGui::SameLine(); + if (ImGui::InputInt("##AudioChansI",&settings.audioChans,1,1)) { + if (settings.audioChans<1) settings.audioChans=1; + if (settings.audioChans>16) settings.audioChans=16; + } + } else { + ImGui::Text("Channels"); + ImGui::SameLine(); + String chStr=(settings.audioChans<1 || settings.audioChans>8)?"What?":nonProAudioOuts[settings.audioChans-1]; + if (ImGui::BeginCombo("##AudioChans",chStr.c_str())) { + CHANS_SELECTABLE(1); + CHANS_SELECTABLE(2); + CHANS_SELECTABLE(4); + CHANS_SELECTABLE(6); + CHANS_SELECTABLE(8); + ImGui::EndCombo(); + } + } + ImGui::Text("Buffer size"); ImGui::SameLine(); String bs=fmt::sprintf("%d (latency: ~%.1fms)",settings.audioBufSize,2000.0*(double)settings.audioBufSize/(double)MAX(1,settings.audioRate)); @@ -757,8 +806,8 @@ void FurnaceGUI::drawSettings() { TAAudioDesc& audioWant=e->getAudioDescWant(); TAAudioDesc& audioGot=e->getAudioDescGot(); - ImGui::Text("want: %d samples @ %.0fHz",audioWant.bufsize,audioWant.rate); - ImGui::Text("got: %d samples @ %.0fHz",audioGot.bufsize,audioGot.rate); + ImGui::Text("want: %d samples @ %.0fHz (%d channels)",audioWant.bufsize,audioWant.rate,audioWant.outChans); + ImGui::Text("got: %d samples @ %.0fHz (%d channels)",audioGot.bufsize,audioGot.rate,audioWant.outChans); ImGui::Separator(); @@ -2343,6 +2392,7 @@ void FurnaceGUI::syncSettings() { settings.iconSize=e->getConfInt("iconSize",16); settings.audioEngine=(e->getConfString("audioEngine","SDL")=="SDL")?1:0; settings.audioDevice=e->getConfString("audioDevice",""); + settings.audioChans=e->getConfInt("audioChans",2); settings.midiInDevice=e->getConfString("midiInDevice",""); settings.midiOutDevice=e->getConfString("midiOutDevice",""); settings.c163Name=e->getConfString("c163Name",DIV_C163_DEFAULT_NAME); @@ -2476,6 +2526,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.audioQuality,0,1); clampSetting(settings.audioBufSize,32,4096); clampSetting(settings.audioRate,8000,384000); + clampSetting(settings.audioChans,1,16); clampSetting(settings.arcadeCore,0,1); clampSetting(settings.ym2612Core,0,1); clampSetting(settings.snCore,0,1); @@ -2643,6 +2694,7 @@ void FurnaceGUI::commitSettings() { e->setConf("audioQuality",settings.audioQuality); e->setConf("audioBufSize",settings.audioBufSize); e->setConf("audioRate",settings.audioRate); + e->setConf("audioChans",settings.audioChans); e->setConf("arcadeCore",settings.arcadeCore); e->setConf("ym2612Core",settings.ym2612Core); e->setConf("snCore",settings.snCore); diff --git a/src/gui/volMeter.cpp b/src/gui/volMeter.cpp index d058e31f..5233a69e 100644 --- a/src/gui/volMeter.cpp +++ b/src/gui/volMeter.cpp @@ -51,7 +51,8 @@ void FurnaceGUI::drawVolMeter() { ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); - for (int i=0; i<2; i++) { + int outChans=e->getAudioDescGot().outChans; + for (int i=0; iAddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); } else { @@ -77,10 +78,10 @@ void FurnaceGUI::drawVolMeter() { } } else { s=ImRect( - ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)), - ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0)) + ImLerp(rect.Min,rect.Max,ImVec2(float(i)/outChans,1.0-logPeak)), + ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)/outChans,1.0)) ); - if (i==0) s.Max.x-=dpiScale; + if (i==(outChans-1)) s.Max.x-=dpiScale; if (isClipping) { dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); } else {