support more than 2 output channels

up to 16 on JACK
to-do: add more mixer settings
This commit is contained in:
tildearrow 2023-01-05 02:40:17 -05:00
parent 77f7fcd555
commit 71e40dc015
11 changed files with 127 additions and 42 deletions

View File

@ -131,6 +131,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
bool mustClear=false;
for (int i=0; i<outs; i++) {
if (bb[i]==NULL) {
logV("creating buf %d because it doesn't exist",i);
bb[i]=blip_new(bbInLen);
if (bb[i]==NULL) {
logE("not enough memory!");
@ -142,6 +143,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
if (bbOut[i]==NULL) bbOut[i]=new short[bbInLen];
memset(bbIn[i],0,bbInLen*sizeof(short));
memset(bbOut[i],0,bbInLen*sizeof(short));
mustClear=true;
}
}
if (mustClear) clear();

View File

@ -4150,10 +4150,13 @@ bool DivEngine::initAudioBackend() {
want.rate=getConfInt("audioRate",44100);
want.fragments=2;
want.inChans=0;
want.outChans=2;
want.outChans=getConfInt("audioChans",2);
want.outFormat=TA_AUDIO_FORMAT_F32;
want.name="Furnace";
if (want.outChans<1) want.outChans=1;
if (want.outChans>16) 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; i<got.outChans; i++) {
if (oscBuf[i]==NULL) {
oscBuf[i]=new float[32768];
}
memset(oscBuf[i],0,32768*sizeof(float));
}
if (output->initMidi(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; i<DIV_MAX_OUTPUTS; i++) {
if (oscBuf[i]!=NULL) delete[] oscBuf[i];
}
if (yrw801ROM!=NULL) delete[] yrw801ROM;
if (tg100ROM!=NULL) delete[] tg100ROM;
if (mu5ROM!=NULL) delete[] mu5ROM;

View File

@ -493,7 +493,7 @@ class DivEngine {
int dispatchOfChan[DIV_MAX_CHANS];
int dispatchChanOfChan[DIV_MAX_CHANS];
bool keyHit[DIV_MAX_CHANS];
float* oscBuf[2];
float* oscBuf[DIV_MAX_OUTPUTS];
float oscSize;
int oscReadPos, oscWritePos;
int tickMult;
@ -1123,7 +1123,6 @@ class DivEngine {
curOrders(NULL),
curPat(NULL),
tempIns(NULL),
oscBuf{NULL,NULL},
oscSize(1),
oscReadPos(0),
oscWritePos(0),
@ -1142,6 +1141,7 @@ class DivEngine {
memset(pitchTable,0,4096*sizeof(int));
memset(sysDefs,0,256*sizeof(void*));
memset(walked,0,8192);
memset(oscBuf,0,DIV_MAX_OUTPUTS*(sizeof(float*)));
for (int i=0; i<256; i++) {
sysFileMapFur[i]=DIV_SYSTEM_NULL;

View File

@ -67,7 +67,6 @@ void DivPlatformVIC20::acquire(short** buf, size_t len) {
short samp;
vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER);
buf[0][h]=samp;
buf[1][h]=samp;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0;
}

View File

@ -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<size; i++) {
oscBuf[0][oscWritePos]=out[0][i];
oscBuf[1][oscWritePos]=out[1][i];
for (int j=0; j<outChans; j++) {
if (oscBuf[j]==NULL) continue;
oscBuf[j][oscWritePos]=out[j][i];
}
if (++oscWritePos>=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; j<size; j++) {
out[0][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volL;
out[1][j]+=((float)disCont[i].bbOut[1][j]/32768.0)*volR;
int howManyToFill=MIN(outChans,disCont[i].dispatch->getOutputCount());
for (int k=0; k<howManyToFill; k++) {
// volL if even, volR if odd. if howManyToFill is odd and it is the last channel then ignore
const float whichVol=((howManyToFill&1) && (k==howManyToFill-1))?1.0:((k&1)?volR:volL);
out[k][j]+=((float)disCont[i].bbOut[k][j]/32768.0)*whichVol;
}
}
} else {
for (size_t j=0; j<size; j++) {
out[0][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volL;
out[1][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*volR;
for (int k=0; k<outChans; k++) {
// volL if even, volR if odd. if outChans is odd and it is the last channel then ignore
const float whichVol=((outChans&1) && (k==outChans-1))?1.0:((k&1)?volR:volL);
out[k][j]+=((float)disCont[i].bbOut[0][j]/32768.0)*whichVol;
}
}
}
}
@ -1739,19 +1747,25 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
while (metroPos>=1) metroPos--;
}
// TODO: handle more than 2 outputs
for (unsigned int i=0; i<size; i++) {
oscBuf[0][oscWritePos]=out[0][i];
oscBuf[1][oscWritePos]=out[1][i];
for (int j=0; j<outChans; j++) {
if (oscBuf[j]==NULL) continue;
oscBuf[j][oscWritePos]=out[j][i];
}
if (++oscWritePos>=32768) oscWritePos=0;
}
oscSize=size;
// TODO: handle more than 2 outputs
if (forceMono) {
if (forceMono && outChans>1) {
for (size_t i=0; i<size; i++) {
out[0][i]=(out[0][i]+out[1][i])*0.5;
out[1][i]=out[0][i];
float chanSum=out[0][i];
for (int j=1; j<outChans; j++) {
chanSum=out[j][i];
}
out[0][i]=chanSum/outChans;
for (int j=1; j<outChans; j++) {
out[j][i]=out[0][i];
}
}
}
if (clampSamples) {

View File

@ -205,7 +205,13 @@ void FurnaceGUI::drawAbout() {
float r=0;
float g=0;
float b=0;
float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3;
float peakMix=settings.partyTime?0:0.3;
if (settings.partyTime) {
for (int j=0; j<e->getAudioDescGot().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;

View File

@ -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;

View File

@ -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<FurnaceGUIWindows> curWindowThreadSafe;
float peak[2];
float peak[DIV_MAX_OUTPUTS];
float patChanX[DIV_MAX_CHANS+1];
float patChanSlideY[DIV_MAX_CHANS+1];
float lastPatternWidth, longThreshold;

View File

@ -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; j<e->getAudioDescGot().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; i<e->getAudioDescGot().outChans; i++) {
peak[i]*=1.0-peakDecay;
if (peak[i]<0.0001) {
peak[i]=0.0;

View File

@ -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);

View File

@ -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; i<outChans; i++) {
float logPeak=(20*log10(peak[i])/36.0);
if (logPeak==NAN) logPeak=0.0;
if (logPeak<-1.0) logPeak=-1.0;
@ -66,10 +67,10 @@ void FurnaceGUI::drawVolMeter() {
ImRect s;
if (aspectRatio) {
s=ImRect(
ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)),
ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5))
ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)/outChans)),
ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)/outChans))
);
if (i==0) s.Max.y-=dpiScale;
if (i!=(outChans-1)) s.Max.y-=dpiScale;
if (isClipping) {
dl->AddRectFilled(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 {