From cc80bfbd817ad49713fe083643503429807e8638 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 3 Jun 2022 01:18:32 -0500 Subject: [PATCH] PC speaker: add alternative output methods --- src/engine/platform/pcspkr.cpp | 127 ++++++++++++++++++++++++++++----- src/engine/platform/pcspkr.h | 4 +- src/gui/gui.h | 4 +- src/gui/settings.cpp | 17 +++++ 4 files changed, 129 insertions(+), 23 deletions(-) diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 648fad1ff..a8b144623 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #endif #define PCSPKR_DIVIDER 4 @@ -59,7 +60,6 @@ void DivPlatformPCSpeaker::pcSpeakerThread() { } realQueueLock.unlock(); #ifdef __linux__ - static struct input_event ie; static struct timespec ts, tSleep, rSleep; if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) { printf("could not get time!\n"); @@ -78,19 +78,76 @@ void DivPlatformPCSpeaker::pcSpeakerThread() { nanosleep(&tSleep,&rSleep); } if (beepFD>=0) { - ie.time.tv_sec=r.tv_sec; - ie.time.tv_usec=r.tv_nsec/1000; - ie.type=EV_SND; - ie.code=SND_TONE; - if (r.val>0) { - ie.value=chipClock/r.val; - } else { - ie.value=0; - } - if (write(beepFD,&ie,sizeof(struct input_event))<0) { - perror("error while writing frequency!"); - } else { - //printf("writing freq: %d\n",r.val); + switch (realOutMethod) { + case 0: { // evdev + static struct input_event ie; + ie.time.tv_sec=r.tv_sec; + ie.time.tv_usec=r.tv_nsec/1000; + ie.type=EV_SND; + ie.code=SND_TONE; + if (r.val>0) { + ie.value=chipClock/r.val; + } else { + ie.value=0; + } + if (write(beepFD,&ie,sizeof(struct input_event))<0) { + perror("error while writing frequency!"); + } else { + //printf("writing freq: %d\n",r.val); + } + break; + } + case 1: // KIOCSOUND (on tty) + if (ioctl(beepFD,KIOCSOUND,r.val)<0) { + perror("ioctl error"); + } + break; + case 2: { // /dev/port + unsigned char bOut; + bOut=0; + if (r.val==0) { + lseek(beepFD,0x61,SEEK_SET); + read(beepFD,&bOut,1); + bOut&=(~3); + lseek(beepFD,0x61,SEEK_SET); + write(beepFD,&bOut,1); + } else { + lseek(beepFD,0x43,SEEK_SET); + bOut=0xb6; + write(beepFD,&bOut,1); + lseek(beepFD,0x42,SEEK_SET); + bOut=r.val&0xff; + write(beepFD,&bOut,1); + lseek(beepFD,0x42,SEEK_SET); + bOut=r.val>>8; + write(beepFD,&bOut,1); + lseek(beepFD,0x61,SEEK_SET); + read(beepFD,&bOut,1); + bOut|=3; + lseek(beepFD,0x61,SEEK_SET); + write(beepFD,&bOut,1); + } + break; + } + case 3: // KIOCSOUND (on stdout) + if (ioctl(beepFD,KIOCSOUND,r.val)<0) { + perror("ioctl error"); + } + break; + case 4: // outb() + if (r.val==0) { + outb(inb(0x61)&(~3),0x61); + realOutEnabled=false; + } else { + outb(0xb6,0x43); + outb(r.val&0xff,0x42); + outb(r.val>>8,0x42); + if (!realOutEnabled) { + outb(inb(0x61)|3,0x61); + realOutEnabled=true; + } + } + break; } } else { printf("not writing because fd is less than 0\n"); @@ -193,6 +250,7 @@ void DivPlatformPCSpeaker::beepFreq(int freq, int delay) { #ifdef __linux__ struct timespec ts; double addition=1000000000.0*(double)delay/(double)rate; + addition+=1500000000.0*((double)parent->getAudioDescGot().bufsize/parent->getAudioDescGot().rate); if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) { ts.tv_sec=0; ts.tv_nsec=0; @@ -449,19 +507,48 @@ void DivPlatformPCSpeaker::reset() { low=0; band=0; - if (speakerType==3) { + //if (speakerType==3) { #ifdef __linux__ if (beepFD==-1) { - beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY); + switch (realOutMethod) { + case 0: // evdev + beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY); + break; + case 1: // KIOCSOUND (on tty) + beepFD=open("/dev/tty1",O_WRONLY); + break; + case 2: // /dev/port + beepFD=open("/dev/port",O_WRONLY); + break; + case 3: // KIOCSOUND (on stdout) + beepFD=STDOUT_FILENO; + break; + case 4: // outb() + beepFD=-1; + if (ioperm(0x61,8,1)<0) { + perror("ioperm 0x61"); + break; + } + if (ioperm(0x43,8,1)<0) { + perror("ioperm 0x43"); + break; + } + if (ioperm(0x42,8,1)<0) { + perror("ioperm 0x42"); + break; + } + beepFD=STDOUT_FILENO; + break; + } if (beepFD<0) { perror("error while opening PC speaker"); } } #endif beepFreq(0); - } else { + /*} else { beepFreq(0); - } + }*/ if (realOutThread==NULL) { realOutThread=new std::thread(_pcSpeakerThread,this); @@ -506,6 +593,8 @@ int DivPlatformPCSpeaker::init(DivEngine* p, int channels, int sugRate, unsigned beepFD=-1; realOutQuit=false; realOutThread=NULL; + realOutMethod=parent->getConfInt("pcSpeakerOutMethod",0); + realOutEnabled=false; for (int i=0; i<1; i++) { isMuted[i]=false; } @@ -527,7 +616,7 @@ void DivPlatformPCSpeaker::quit() { delete realOutThread; } #ifdef __linux__ - if (beepFD>=0) close(beepFD); + if (beepFD>=0 && realOutMethod<3) close(beepFD); #endif delete oscBuf; } diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 117e2b6bf..8b0371a89 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -76,8 +76,8 @@ class DivPlatformPCSpeaker: public DivDispatch { std::queue realQueue; std::mutex realQueueLock; bool isMuted[1]; - bool on, flip, lastOn; - int pos, speakerType, beepFD; + bool on, flip, lastOn, realOutEnabled; + int pos, speakerType, beepFD, realOutMethod; float low, band; float low2, high2, band2; float low3, band3; diff --git a/src/gui/gui.h b/src/gui/gui.h index e4a98337b..da2ebc55f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -871,6 +871,7 @@ class FurnaceGUI { int saaCore; int nesCore; int fdsCore; + int pcSpeakerOutMethod; String yrw801Path; String tg100Path; String mu5Path; @@ -905,10 +906,8 @@ class FurnaceGUI { int avoidRaisingPattern; int insFocusesPattern; int stepOnInsert; - // TODO flags int unifiedDataView; int sysFileDialog; - // end int roundedWindows; int roundedButtons; int roundedMenus; @@ -972,6 +971,7 @@ class FurnaceGUI { saaCore(1), nesCore(0), fdsCore(0), + pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), mu5Path(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 8176c6be6..10e93743f 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -94,6 +94,14 @@ const char* nesCores[]={ "NSFplay" }; +const char* pcspkrOutMethods[]={ + "evdev SND_TONE", + "KIOCSOUND on /dev/tty1", + "/dev/port", + "KIOCSOUND on standard output", + "outb()" +}; + const char* valueInputStyles[]={ "Disabled/custom", "Two octaves (0 is C-4, F is D#5)", @@ -898,6 +906,12 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + ImGui::Separator(); + + ImGui::Text("PC Speaker strategy"); + ImGui::SameLine(); + ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); + ImGui::Separator(); ImGui::Text("Sample ROMs:"); @@ -1937,6 +1951,7 @@ void FurnaceGUI::syncSettings() { settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); settings.fdsCore=e->getConfInt("fdsCore",0); + settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); settings.yrw801Path=e->getConfString("yrw801Path",""); settings.tg100Path=e->getConfString("tg100Path",""); settings.mu5Path=e->getConfString("mu5Path",""); @@ -2029,6 +2044,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.saaCore,0,1); clampSetting(settings.nesCore,0,1); clampSetting(settings.fdsCore,0,1); + clampSetting(settings.pcSpeakerOutMethod,0,4); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); clampSetting(settings.patRowsBase,0,1); @@ -2150,6 +2166,7 @@ void FurnaceGUI::commitSettings() { e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); e->setConf("fdsCore",settings.fdsCore); + e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); e->setConf("yrw801Path",settings.yrw801Path); e->setConf("tg100Path",settings.tg100Path); e->setConf("mu5Path",settings.mu5Path);