support more than 2 output channels
up to 16 on JACK to-do: add more mixer settings
This commit is contained in:
parent
77f7fcd555
commit
71e40dc015
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue