Compare commits

...

7 Commits

Author SHA1 Message Date
tildearrow d1b78f787b update credits 2023-08-30 17:38:45 -05:00
tildearrow 68787a4d8b add PortAudio backend - PLEASE READ
PLEASE DO:

```
git submodule update --init --recursive
```

AFTER PULLING THIS COMMIT.
2023-08-30 17:32:51 -05:00
tildearrow 7d605c9d76 GUI: why do I see a 0.5 reference 2023-08-30 14:27:29 -05:00
tildearrow 80013089a2 GUI: colorize macro speed/delay button if used 2023-08-30 14:06:04 -05:00
tildearrow 5a688c58cb OPM/OPZ: invert noise frequencies
since ymfm is default
2023-08-30 13:46:47 -05:00
tildearrow 63dcacf33d dev170 - YM2151: fix noise frequency on ymfm
issue #1441
raising ver num to allow possible compat change
2023-08-30 13:35:22 -05:00
tildearrow 426d4b44f0 dang it let me freeze 2023-08-30 13:12:25 -05:00
18 changed files with 365 additions and 19 deletions

3
.gitmodules vendored
View File

@ -12,3 +12,6 @@
[submodule "extern/adpcm"]
path = extern/adpcm
url = https://github.com/superctr/adpcm
[submodule "extern/portaudio"]
path = extern/portaudio
url = https://github.com/PortAudio/portaudio.git

View File

@ -27,6 +27,7 @@ include(TestBigEndian)
if (ANDROID)
set(USE_RTMIDI_DEFAULT OFF)
set(WITH_PORTAUDIO_DEFAULT OFF)
set(USE_BACKWARD_DEFAULT OFF)
find_library(TERMUX rt)
if (TERMUX)
@ -34,6 +35,7 @@ if (ANDROID)
endif()
else()
set(USE_RTMIDI_DEFAULT ON)
set(WITH_PORTAUDIO_DEFAULT ON)
if (WIN32 OR APPLE)
set(USE_BACKWARD_DEFAULT ON)
else()
@ -78,6 +80,7 @@ option(USE_SDL2 "Build with SDL2. Required to build with GUI." ${USE_SDL2_DEFAUL
option(USE_SNDFILE "Build with libsndfile. Required in order to work with audio files." ${USE_SNDFILE_DEFAULT})
option(USE_BACKWARD "Use backward-cpp to print a backtrace on crash/abort." ${USE_BACKWARD_DEFAULT})
option(WITH_JACK "Whether to build with JACK support. Auto-detects if JACK is available" ${WITH_JACK_DEFAULT})
option(WITH_PORTAUDIO "Whether to build with PortAudio for audio output." ${WITH_PORTAUDIO_DEFAULT})
option(WITH_RENDER_SDL "Whether to build with the SDL_Renderer render backend." ${WITH_RENDER_SDL_DEFAULT})
option(WITH_RENDER_OPENGL "Whether to build with the OpenGL render backend." ${WITH_RENDER_OPENGL_DEFAULT})
option(WITH_RENDER_DX11 "Whether to build with the DirectX 11 render backend." ${WITH_RENDER_DX11_DEFAULT})
@ -85,6 +88,7 @@ option(USE_GLES "Use OpenGL ES for the OpenGL render backend." ${USE_GLES_DEFAUL
option(SYSTEM_FFTW "Use a system-installed version of FFTW instead of the vendored one" OFF)
option(SYSTEM_FMT "Use a system-installed version of fmt instead of the vendored one" OFF)
option(SYSTEM_LIBSNDFILE "Use a system-installed version of libsndfile instead of the vendored one" OFF)
option(SYSTEM_PORTAUDIO "Use a system-installed version of PortAudio instead of the vendored one" OFF)
option(SYSTEM_RTMIDI "Use a system-installed version of RtMidi instead of the vendored one" OFF)
option(SYSTEM_ZLIB "Use a system-installed version of zlib instead of the vendored one" OFF)
option(SYSTEM_SDL2 "Use a system-installed version of SDL2 instead of the vendored one" ${SYSTEM_SDL2_DEFAULT})
@ -204,6 +208,25 @@ else()
message(STATUS "Not using libsndfile")
endif()
if (WITH_PORTAUDIO)
if (SYSTEM_PORTAUDIO)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PORTAUDIO REQUIRED portaudio)
list(APPEND DEPENDENCIES_INCLUDE_DIRS ${PORTAUDIO_INCLUDE_DIRS})
list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${PORTAUDIO_CFLAGS_OTHER})
list(APPEND DEPENDENCIES_LIBRARIES ${PORTAUDIO_LIBRARIES})
list(APPEND DEPENDENCIES_LIBRARY_DIRS ${PORTAUDIO_LIBRARY_DIRS})
list(APPEND DEPENDENCIES_LINK_OPTIONS ${PORTAUDIO_LDFLAGS_OTHER})
list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${PORTAUDIO_LDFLAGS})
message(STATUS "Using system-installed PortAudio")
else()
set(PA_BUILD_SHARED_LIBS OFF CACHE BOOL "Build dynamic library" FORCE)
add_subdirectory(extern/portaudio EXCLUDE_FROM_ALL)
list(APPEND DEPENDENCIES_LIBRARIES PortAudio)
message(STATUS "Using vendored PortAudio")
endif()
endif()
if (USE_RTMIDI)
if (SYSTEM_RTMIDI)
find_package(PkgConfig REQUIRED)
@ -345,6 +368,14 @@ else()
message(STATUS "Building without JACK support")
endif()
if (WITH_PORTAUDIO)
list(APPEND AUDIO_SOURCES src/audio/pa.cpp)
message(STATUS "Building with PortAudio")
list(APPEND DEPENDENCIES_DEFINES HAVE_PA)
else()
message(STATUS "Building without PortAudio")
endif()
if (USE_RTMIDI)
list(APPEND AUDIO_SOURCES src/audio/rtmidi.cpp)
message(STATUS "Building with RtMidi")

View File

@ -252,6 +252,7 @@ Available options:
| `USE_SNDFILE` | `ON` | Build with libsndfile (required in order to work with audio files) |
| `USE_BACKWARD` | `ON` | Use backward-cpp to print a backtrace on crash/abort |
| `WITH_JACK` | `ON` if system-installed JACK detected, otherwise `OFF` | Whether to build with JACK support. Auto-detects if JACK is available |
| `WITH_PORTAUDIO` | `ON` | Whether to build with PortAudio. |
| `SYSTEM_FFTW` | `OFF` | Use a system-installed version of FFTW instead of the vendored one |
| `SYSTEM_FMT` | `OFF` | Use a system-installed version of fmt instead of the vendored one |
| `SYSTEM_LIBSNDFILE` | `OFF` | Use a system-installed version of libsndfile instead of the vendored one |

1
extern/portaudio vendored Submodule

@ -0,0 +1 @@
Subproject commit 6ee9836a08d201c118b4715d4d70242816584000

222
src/audio/pa.cpp Normal file
View File

@ -0,0 +1,222 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
#include <vector>
#include "../ta-log.h"
#include "pa.h"
int taPAProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags, void* inst) {
TAAudioPA* instance=(TAAudioPA*)inst;
return instance->onProcess(in,out,nframes,timeInfo,flags);
}
int TAAudioPA::onProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags) {
for (int i=0; i<desc.inChans; i++) {
if (nframes>desc.bufsize) {
delete[] inBufs[i];
inBufs[i]=new float[nframes];
}
}
for (int i=0; i<desc.outChans; i++) {
if (nframes>desc.bufsize) {
delete[] outBufs[i];
outBufs[i]=new float[nframes];
}
}
if (nframes!=desc.bufsize) {
desc.bufsize=nframes;
}
if (audioProcCallback!=NULL) {
if (midiIn!=NULL) midiIn->gather();
audioProcCallback(audioProcCallbackUser,inBufs,outBufs,desc.inChans,desc.outChans,desc.bufsize);
}
float* fbuf=(float*)out;
for (size_t j=0; j<desc.bufsize; j++) {
for (size_t i=0; i<desc.outChans; i++) {
fbuf[j*desc.outChans+i]=outBufs[i][j];
}
}
return 0;
}
void* TAAudioPA::getContext() {
return (void*)&ac;
}
bool TAAudioPA::quit() {
if (!initialized) return false;
Pa_CloseStream(ac);
if (running) {
running=false;
}
for (int i=0; i<desc.outChans; i++) {
delete[] outBufs[i];
}
delete[] outBufs;
initialized=false;
return true;
}
bool TAAudioPA::setRun(bool run) {
if (!initialized) return false;
if (running==run) return running;
PaError status;
if (run) {
status=Pa_StartStream(ac);
} else {
status=Pa_StopStream(ac);
}
if (status!=paNoError) {
logW("error while setting run status: %s",Pa_GetErrorText(status));
return running;
}
running=run;
return running;
}
std::vector<String> TAAudioPA::listAudioDevices() {
std::vector<String> ret;
if (!audioSysStarted) {
PaError status=Pa_Initialize();
if (status!=paNoError) {
logE("could not initialize PortAudio to list audio devices");
return ret;
} else {
audioSysStarted=true;
}
}
int count=Pa_GetDeviceCount();
if (count<0) return ret;
for (int i=0; i<count; i++) {
const PaDeviceInfo* devInfo=Pa_GetDeviceInfo(i);
if (devInfo==NULL) continue;
if (devInfo->maxOutputChannels<1) continue;
if (devInfo->name!=NULL) {
ret.push_back(String(devInfo->name));
}
}
return ret;
}
bool TAAudioPA::init(TAAudioDesc& request, TAAudioDesc& response) {
if (initialized) {
logE("audio already initialized");
return false;
}
PaError status;
if (!audioSysStarted) {
status=Pa_Initialize();
if (status!=paNoError) {
logE("could not initialize PortAudio");
return false;
} else {
audioSysStarted=true;
}
}
desc=request;
desc.outFormat=TA_AUDIO_FORMAT_F32;
const PaDeviceInfo* devInfo=NULL;
int outDeviceID=0;
if (desc.deviceName.empty()) {
outDeviceID=Pa_GetDefaultOutputDevice();
devInfo=Pa_GetDeviceInfo(outDeviceID);
} else {
int count=Pa_GetDeviceCount();
bool found=false;
if (count<0) {
logE("audio device not found");
return false;
}
for (int i=0; i<count; i++) {
devInfo=Pa_GetDeviceInfo(i);
if (devInfo==NULL) continue;
if (devInfo->maxOutputChannels<1) continue;
if (devInfo->name!=NULL) {
if (strcmp(devInfo->name,desc.deviceName.c_str())==0) {
outDeviceID=i;
found=true;
break;
}
}
}
if (!found) {
logE("audio device not found");
return false;
}
}
// check output channels and sample rate
if (devInfo!=NULL) {
if (desc.outChans>devInfo->maxOutputChannels) desc.outChans=devInfo->maxOutputChannels;
}
PaStreamParameters outParams;
outParams.device=outDeviceID;
outParams.channelCount=desc.outChans;
outParams.sampleFormat=paFloat32;
outParams.suggestedLatency=(double)(desc.bufsize*desc.fragments)/desc.rate;
outParams.hostApiSpecificStreamInfo=NULL;
logV("opening audio device...");
status=Pa_OpenStream(
&ac,
NULL,
&outParams,
desc.rate,
0,
paClipOff|paDitherOff,
taPAProcess,
this
);
if (status!=paNoError) {
logE("could not open audio device: %s",Pa_GetErrorText(status));
return false;
}
desc.deviceName=devInfo->name;
desc.inChans=0;
if (desc.outChans>0) {
outBufs=new float*[desc.outChans];
for (int i=0; i<desc.outChans; i++) {
outBufs[i]=new float[desc.bufsize];
}
}
response=desc;
initialized=true;
return true;
}

38
src/audio/pa.h Normal file
View File

@ -0,0 +1,38 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "taAudio.h"
#include <portaudio.h>
class TAAudioPA: public TAAudio {
PaStream* ac;
bool audioSysStarted;
public:
int onProcess(const void* in, void* out, unsigned long nframes, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags flags);
void* getContext();
bool quit();
bool setRun(bool run);
std::vector<String> listAudioDevices();
bool init(TAAudioDesc& request, TAAudioDesc& response);
TAAudioPA():
ac(NULL),
audioSysStarted(false) {}
};

View File

@ -32,6 +32,9 @@
#ifdef HAVE_JACK
#include "../audio/jack.h"
#endif
#ifdef HAVE_PA
#include "../audio/pa.h"
#endif
#include <math.h>
#include <float.h>
#include <fmt/printf.h>
@ -3279,6 +3282,8 @@ bool DivEngine::initAudioBackend() {
if (audioEngine==DIV_AUDIO_NULL) {
if (getConfString("audioEngine","SDL")=="JACK") {
audioEngine=DIV_AUDIO_JACK;
} else if (getConfString("audioEngine","SDL")=="PortAudio") {
audioEngine=DIV_AUDIO_PORTAUDIO;
} else {
audioEngine=DIV_AUDIO_SDL;
}
@ -3322,6 +3327,21 @@ bool DivEngine::initAudioBackend() {
#endif
#else
output=new TAAudioJACK;
#endif
break;
case DIV_AUDIO_PORTAUDIO:
#ifndef HAVE_PA
logE("Furnace was not compiled with PortAudio!");
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 TAAudioPA;
#endif
break;
case DIV_AUDIO_SDL:

View File

@ -56,8 +56,8 @@
#define DIV_UNSTABLE
#define DIV_VERSION "dev169"
#define DIV_ENGINE_VERSION 169
#define DIV_VERSION "dev170"
#define DIV_ENGINE_VERSION 170
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -73,6 +73,7 @@ enum DivStatusView {
enum DivAudioEngines {
DIV_AUDIO_JACK=0,
DIV_AUDIO_SDL=1,
DIV_AUDIO_PORTAUDIO=2,
DIV_AUDIO_NULL=126,
DIV_AUDIO_DUMMY=127

View File

@ -175,7 +175,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (chan[i].std.duty.had) {
if (chan[i].std.duty.val>0) {
rWrite(0x0f,0x80|(0x20-chan[i].std.duty.val));
rWrite(0x0f,0x80|(chan[i].std.duty.val-1));
} else {
rWrite(0x0f,0);
}
@ -768,9 +768,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
if (c.chan!=7) break;
if (c.value) {
if (c.value>0x1f) {
rWrite(0x0f,0x80);
rWrite(0x0f,0x80|0x1f);
} else {
rWrite(0x0f,0x80|(0x1f-c.value));
rWrite(0x0f,0x80|(c.value-1));
}
} else {
rWrite(0x0f,0);

View File

@ -176,7 +176,7 @@ int32_t opm_registers::clock_noise_and_lfo()
{
// base noise frequency is measured at 2x 1/2 FM frequency; this
// means each tick counts as two steps against the noise counter
uint32_t freq = noise_frequency();
uint32_t freq = noise_frequency() ^ 0x1f;
for (int rep = 0; rep < 2; rep++)
{
// evidence seems to suggest the LFSR is clocked continually and just

View File

@ -327,7 +327,7 @@ int32_t opz_registers::clock_noise_and_lfo()
{
// base noise frequency is measured at 2x 1/2 FM frequency; this
// means each tick counts as two steps against the noise counter
uint32_t freq = noise_frequency();
uint32_t freq = noise_frequency() ^ 0x1f;
for (int rep = 0; rep < 2; rep++)
{
// evidence seems to suggest the LFSR is clocked continually and just

View File

@ -134,7 +134,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
if (chan[i].std.duty.had) {
if (chan[i].std.duty.val>0) {
rWrite(0x0f,0x80|(0x20-chan[i].std.duty.val));
rWrite(0x0f,0x80|(chan[i].std.duty.val-1));
} else {
rWrite(0x0f,0);
}
@ -865,9 +865,9 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
if (c.chan!=7) break;
if (c.value) {
if (c.value>0x1f) {
rWrite(0x0f,0x80);
rWrite(0x0f,0x80|0x1f);
} else {
rWrite(0x0f,0x80|(0x1f-c.value));
rWrite(0x0f,0x80|(c.value-1));
}
} else {
rWrite(0x0f,0);

View File

@ -160,6 +160,7 @@ const char* aboutLine[]={
"libsndfile by Erik de Castro Lopo",
"Portable File Dialogs by Sam Hocevar",
"Native File Dialog by Frogtoss Games",
"PortAudio",
"RtMidi by Gary P. Scavone",
"FFTW by Matteo Frigo and Steven G. Johnson",
"backward-cpp by Google",

View File

@ -509,9 +509,15 @@ void FurnaceGUI::drawMobileControls() {
doAction(GUI_ACTION_SAVE_AS);
}
ImGui::Button("1.1+ .dmf");
if (ImGui::Button("1.1+ .dmf")) {
mobileMenuOpen=false;
openFileDialog(GUI_FILE_SAVE_DMF);
}
ImGui::SameLine();
ImGui::Button("Legacy .dmf");
if (ImGui::Button("Legacy .dmf")) {
mobileMenuOpen=false;
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
}
ImGui::SameLine();
if (ImGui::Button("Export Audio")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);

View File

@ -4365,7 +4365,7 @@ bool FurnaceGUI::loop() {
if (ImGui::BeginMenu(settings.capitalMenuBar?"Help":"help")) {
if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen;
if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen;
if (ImGui::MenuItem("inspector",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) inspectorOpen=!inspectorOpen;
if (ImGui::MenuItem("inspector")) inspectorOpen=!inspectorOpen;
if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset();
if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) {
aboutOpen=true;

View File

@ -1802,7 +1802,9 @@ void FurnaceGUI::drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float avail
}
#define BUTTON_TO_SET_PROPS(_x) \
pushToggleColors(_x.macro->speed!=1 || _x.macro->delay); \
ImGui::Button(ICON_FA_ELLIPSIS_H "##IMacroSet"); \
popToggleColors(); \
if (ImGui::IsItemHovered()) { \
ImGui::SetTooltip("Delay/Step Length"); \
} \

View File

@ -82,11 +82,13 @@ const char* patFonts[]={
const char* audioBackends[]={
"JACK",
"SDL"
"SDL",
"PortAudio"
};
const bool isProAudio[]={
true,
false,
false
};
@ -724,17 +726,27 @@ void FurnaceGUI::drawSettings() {
if (ImGui::BeginTable("##Output",2)) {
ImGui::TableSetupColumn("##Label",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Combo",ImGuiTableColumnFlags_WidthStretch);
#ifdef HAVE_JACK
#if defined(HAVE_JACK) || defined(HAVE_PA)
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Backend");
ImGui::TableNextColumn();
int prevAudioEngine=settings.audioEngine;
if (ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2)) {
if (ImGui::BeginCombo("##Backend",audioBackends[settings.audioEngine])) {
if (ImGui::Selectable("JACK",settings.audioEngine==DIV_AUDIO_JACK)) {
settings.audioEngine=DIV_AUDIO_JACK;
}
if (ImGui::Selectable("SDL",settings.audioEngine==DIV_AUDIO_SDL)) {
settings.audioEngine=DIV_AUDIO_SDL;
}
if (ImGui::Selectable("PortAudio",settings.audioEngine==DIV_AUDIO_PORTAUDIO)) {
settings.audioEngine=DIV_AUDIO_PORTAUDIO;
}
if (settings.audioEngine!=prevAudioEngine) {
if (!isProAudio[settings.audioEngine]) settings.audioChans=2;
}
ImGui::EndCombo();
}
#endif
@ -1231,7 +1243,7 @@ void FurnaceGUI::drawSettings() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Arcade/YM2151");
ImGui::Text("YM2151");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2);
@ -1242,7 +1254,7 @@ void FurnaceGUI::drawSettings() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Genesis/YM2612");
ImGui::Text("YM2612");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##YM2612Core",&settings.ym2612Core,ym2612Cores,2);
@ -3045,6 +3057,13 @@ void FurnaceGUI::syncSettings() {
settings.patFontSize=e->getConfInt("patFontSize",18);
settings.iconSize=e->getConfInt("iconSize",16);
settings.audioEngine=(e->getConfString("audioEngine","SDL")=="SDL")?1:0;
if (e->getConfString("audioEngine","SDL")=="PortAudio") {
settings.audioEngine=DIV_AUDIO_JACK;
} else if (e->getConfString("audioEngine","SDL")=="PortAudio") {
settings.audioEngine=DIV_AUDIO_PORTAUDIO;
} else {
settings.audioEngine=DIV_AUDIO_SDL;
}
settings.audioDevice=e->getConfString("audioDevice","");
settings.audioChans=e->getConfInt("audioChans",2);
settings.midiInDevice=e->getConfString("midiInDevice","");
@ -3218,7 +3237,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.headFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
clampSetting(settings.iconSize,2,48);
clampSetting(settings.audioEngine,0,1);
clampSetting(settings.audioEngine,0,2);
clampSetting(settings.audioQuality,0,1);
clampSetting(settings.audioBufSize,32,4096);
clampSetting(settings.audioRate,8000,384000);

View File

@ -166,6 +166,7 @@ TAParamResult pVersion(String) {
printf("- libsndfile by Erik de Castro Lopo and rest of libsndfile team (LGPLv2.1)\n");
printf("- SDL2 by Sam Lantinga (zlib license)\n");
printf("- zlib by Jean-loup Gailly and Mark Adler (zlib license)\n");
printf("- PortAudio (PortAudio license)\n");
printf("- RtMidi by Gary P. Scavone (RtMidi license)\n");
printf("- backward-cpp by Google (MIT)\n");
printf("- Dear ImGui by Omar Cornut (MIT)\n");