ASIO backend, part 2

half-working:
- crashes on exit
- doesn't work on launch (must go to settings > audio and click OK)
This commit is contained in:
tildearrow 2025-10-25 04:30:23 -05:00
parent 0cec98199e
commit f5fd8102c7
7 changed files with 170 additions and 12 deletions

View file

@ -52,6 +52,98 @@ void TAAudioASIO::onProcess(int index) {
}
// upload here...
for (int i=0; i<totalChans; i++) {
if (chanInfo[i].isInput==ASIOTrue) continue;
int ch=chanInfo[i].channel;
if (ch>=desc.outChans) continue;
float* srcBuf=outBufs[ch];
switch (chanInfo[i].type) {
// little-endian
case ASIOSTInt16LSB: {
short* buf=(short*)bufInfo[i].buffers[index];
for (unsigned int j=0; j<desc.bufsize; j++) {
buf[j]=CLAMP(srcBuf[j],-1.0,1.0)*32767.0f;
}
break;
}
// TODO: how does this work? it's vaguely described in the docs
case ASIOSTInt24LSB: {
break;
}
case ASIOSTInt32LSB: {
int* buf=(int*)bufInfo[i].buffers[index];
for (unsigned int j=0; j<desc.bufsize; j++) {
int val=CLAMP(srcBuf[j],-1.0,1.0)*8388608.0f;
if (val<-8388608) val=-8388608;
if (val>8388607) val=-8388607;
val<<=8;
buf[j]=val;
}
break;
}
case ASIOSTFloat32LSB: {
float* buf=(float*)bufInfo[i].buffers[index];
for (unsigned int j=0; j<desc.bufsize; j++) {
buf[j]=srcBuf[j];
}
break;
}
case ASIOSTFloat64LSB: {
double* buf=(double*)bufInfo[i].buffers[index];
for (unsigned int j=0; j<desc.bufsize; j++) {
buf[j]=srcBuf[j];
}
break;
}
// TODO: implement these formats D:
// big-endian
case ASIOSTInt16MSB: {
break;
}
case ASIOSTInt24MSB: {
break;
}
case ASIOSTInt32MSB: {
break;
}
case ASIOSTFloat32MSB: {
break;
}
case ASIOSTFloat64MSB: {
break;
}
// what the hell..............
case ASIOSTInt32LSB16: {
break;
}
case ASIOSTInt32LSB18: {
break;
}
case ASIOSTInt32LSB20: {
break;
}
case ASIOSTInt32LSB24: {
break;
}
case ASIOSTInt32MSB16: {
break;
}
case ASIOSTInt32MSB18: {
break;
}
case ASIOSTInt32MSB20: {
break;
}
case ASIOSTInt32MSB24: {
break;
}
default: // unsupported
break;
}
}
/*if (nframes!=desc.bufsize) {
desc.bufsize=nframes;
@ -66,29 +158,41 @@ bool TAAudioASIO::quit() {
if (!initialized) return false;
if (running) {
logV("CRASH: STOPPING NOW (QUIT)......");
ASIOStop();
running=false;
}
logV("CRASH: ASIODisposeBuffers()");
ASIODisposeBuffers();
logV("CRASH: erase inBufs");
for (int i=0; i<desc.inChans; i++) {
delete[] inBufs[i];
inBufs[i]=NULL;
}
logV("CRASH: erase outBufs");
for (int i=0; i<desc.outChans; i++) {
delete[] outBufs[i];
outBufs[i]=NULL;
}
delete[] inBufs;
delete[] outBufs;
inBufs=NULL;
outBufs=NULL;
logV("CRASH: erase arrays");
if (inBufs!=NULL) {
delete[] inBufs;
inBufs=NULL;
}
if (outBufs!=NULL) {
delete[] outBufs;
outBufs=NULL;
}
logV("CRASH: ASIOExit()");
ASIOExit();
logV("CRASH: removeCurrentDriver()");
drivers.removeCurrentDriver();
logV("CRASH: reset callback instance");
callbackInstance=NULL;
initialized=false;
@ -105,6 +209,7 @@ bool TAAudioASIO::setRun(bool run) {
running=true;
} else {
// does it matter whether stop was successful?
logV("CRASH: STOPPING NOW......");
ASIOStop();
running=false;
}
@ -124,6 +229,13 @@ bool TAAudioASIO::init(TAAudioDesc& request, TAAudioDesc& response) {
if (desc.deviceName.empty()) {
// load first driver if not specified
logV("getting driver names...");
if (!driverNamesInit) {
for (int i=0; i<ASIO_DRIVER_MAX; i++) {
// 64 just in case
driverNames[i]=new char[64];
}
driverNamesInit=true;
}
driverCount=drivers.getDriverNames(driverNames,ASIO_DRIVER_MAX);
// quit if we couldn't find any drivers
@ -136,7 +248,7 @@ bool TAAudioASIO::init(TAAudioDesc& request, TAAudioDesc& response) {
}
// load driver
logV("loading ASIO driver...");
logV("loading ASIO driver... (%s)",desc.deviceName);
strncpy(deviceNameCopy,desc.deviceName.c_str(),63);
if (!drivers.loadDriver(deviceNameCopy)) {
logE("failed to load ASIO driver!");
@ -225,7 +337,7 @@ bool TAAudioASIO::init(TAAudioDesc& request, TAAudioDesc& response) {
chanInfo[totalChans].isInput=ASIOFalse;
ASIOGetChannelInfo(&chanInfo[totalChans]);
bufInfo[totalChans].channelNum=i;
bufInfo[totalChans++].isInput=ASIOTrue;
bufInfo[totalChans++].isInput=ASIOFalse;
outBufs[i]=new float[actualBufSize];
}
}
@ -268,7 +380,14 @@ bool TAAudioASIO::init(TAAudioDesc& request, TAAudioDesc& response) {
std::vector<String> TAAudioASIO::listAudioDevices() {
std::vector<String> ret;
memset(driverNames,0,sizeof(void*)*ASIO_DRIVER_MAX);
if (!driverNamesInit) {
for (int i=0; i<ASIO_DRIVER_MAX; i++) {
// 64 just in case
driverNames[i]=new char[64];
}
driverNamesInit=true;
}
driverCount=drivers.getDriverNames(driverNames,ASIO_DRIVER_MAX);
for (int i=0; i<driverCount; i++) {
ret.push_back(driverNames[i]);

View file

@ -34,6 +34,7 @@ class TAAudioASIO: public TAAudio {
char* driverNames[ASIO_DRIVER_MAX];
int driverCount;
bool driverNamesInit;
char deviceNameCopy[64];
@ -51,5 +52,6 @@ class TAAudioASIO: public TAAudio {
TAAudioASIO():
totalChans(0),
driverCount(0) {}
driverCount(0),
driverNamesInit(false) {}
};

View file

@ -36,6 +36,9 @@
#ifdef HAVE_PA
#include "../audio/pa.h"
#endif
#ifdef HAVE_ASIO
#include "../audio/asio.h"
#endif
#include "../audio/pipe.h"
#include <math.h>
#include <float.h>
@ -3909,6 +3912,8 @@ bool DivEngine::initAudioBackend() {
audioEngine=DIV_AUDIO_JACK;
} else if (getConfString("audioEngine","SDL")=="PortAudio") {
audioEngine=DIV_AUDIO_PORTAUDIO;
} else if (getConfString("audioEngine","SDL")=="ASIO") {
audioEngine=DIV_AUDIO_ASIO;
} else {
audioEngine=DIV_AUDIO_SDL;
}
@ -3970,6 +3975,21 @@ bool DivEngine::initAudioBackend() {
#endif
#else
output=new TAAudioPA;
#endif
break;
case DIV_AUDIO_ASIO:
#ifndef HAVE_ASIO
logE("Furnace was not compiled with ASIO support!");
setConf("audioEngine","SDL");
saveConf();
#ifdef HAVE_SDL2
output=new TAAudioSDL;
#else
logE("Furnace was not compiled with SDL support either!");
output=new TAAudio;
#endif
#else
output=new TAAudioASIO;
#endif
break;
case DIV_AUDIO_SDL:

View file

@ -76,6 +76,7 @@ enum DivAudioEngines {
DIV_AUDIO_SDL=1,
DIV_AUDIO_PORTAUDIO=2,
DIV_AUDIO_PIPE=3,
DIV_AUDIO_ASIO=4,
DIV_AUDIO_NULL=126,
DIV_AUDIO_DUMMY=127

View file

@ -131,6 +131,9 @@ const char* aboutLine[]={
_N("Portable File Dialogs by Sam Hocevar"),
_N("Native File Dialog by Frogtoss Games"),
"PortAudio",
#ifdef HAVE_ASIO
_N("ASIO® by Steinberg Media Technologies"),
#endif
_N("Weak-JACK by x42"),
_N("RtMidi by Gary P. Scavone"),
_N("FFTW by Matteo Frigo and Steven G. Johnson"),

View file

@ -130,7 +130,10 @@ const char* patFonts[]={
const char* audioBackends[]={
"JACK",
"SDL",
"PortAudio"
"PortAudio",
// pipe (invalid choice in GUI)
"Uhh, can you explain to me what exactly you were trying to do?",
"ASIO"
};
const char* audioQualities[]={
@ -1248,7 +1251,7 @@ void FurnaceGUI::drawSettings() {
if (ImGui::BeginTable("##Output",2)) {
ImGui::TableSetupColumn("##Label",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Combo",ImGuiTableColumnFlags_WidthStretch);
#if defined(HAVE_JACK) || defined(HAVE_PA)
#if defined(HAVE_JACK) || defined(HAVE_PA) || defined(HAVE_ASIO)
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
@ -1271,6 +1274,12 @@ void FurnaceGUI::drawSettings() {
settings.audioEngine=DIV_AUDIO_PORTAUDIO;
settingsChanged=true;
}
#endif
#ifdef HAVE_ASIO
if (ImGui::Selectable("ASIO",settings.audioEngine==DIV_AUDIO_ASIO)) {
settings.audioEngine=DIV_AUDIO_ASIO;
settingsChanged=true;
}
#endif
if (settings.audioEngine!=prevAudioEngine) {
audioEngineChanged=true;
@ -4917,6 +4926,8 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
settings.audioEngine=DIV_AUDIO_JACK;
} else if (conf.getString("audioEngine","SDL")=="PortAudio") {
settings.audioEngine=DIV_AUDIO_PORTAUDIO;
} else if (conf.getString("audioEngine","SDL")=="ASIO") {
settings.audioEngine=DIV_AUDIO_ASIO;
} else {
settings.audioEngine=DIV_AUDIO_SDL;
}
@ -5201,7 +5212,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
clampSetting(settings.headFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
clampSetting(settings.iconSize,2,48);
clampSetting(settings.audioEngine,0,2);
clampSetting(settings.audioEngine,0,4);
clampSetting(settings.audioQuality,0,1);
clampSetting(settings.audioHiPass,0,1);
clampSetting(settings.audioBufSize,32,4096);

View file

@ -178,11 +178,13 @@ TAParamResult pAudio(String val) {
e.setAudio(DIV_AUDIO_SDL);
} else if (val=="portaudio") {
e.setAudio(DIV_AUDIO_PORTAUDIO);
} else if (val=="asio") {
e.setAudio(DIV_AUDIO_ASIO);
} else if (val=="pipe") {
e.setAudio(DIV_AUDIO_PIPE);
changeLogOutput(stderr);
} else {
logE("invalid value for audio engine! valid values are: jack, sdl, portaudio, pipe.");
logE("invalid value for audio engine! valid values are: jack, sdl, portaudio, asio, pipe.");
return TA_PARAM_ERROR;
}
return TA_PARAM_SUCCESS;