aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/Makefile.am3
-rw-r--r--gtk/configdialog.c157
-rw-r--r--gtk/configdialog.h20
-rw-r--r--gtk/main.c27
-rw-r--r--gtk/wavesave.c6
5 files changed, 211 insertions, 2 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 281fa65..833e5ad 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -65,7 +65,8 @@ fmplayer_SOURCES=main.c \
$(LIBOPNA_SRC) \
$(FMDRIVER_SRC) \
$(FMDSP_SRC) \
- $(SOUNDOUT_SRC)
+ $(SOUNDOUT_SRC) \
+ configdialog.c
if ENABLE_OPENGL
fmplayer_SOURCES+=oscilloview-gl.c
diff --git a/gtk/configdialog.c b/gtk/configdialog.c
new file mode 100644
index 0000000..350633f
--- /dev/null
+++ b/gtk/configdialog.c
@@ -0,0 +1,157 @@
+#include "configdialog.h"
+#include <math.h>
+
+static double mix_to_db(uint32_t mix) {
+ return 20.0 * log10((double)mix / 0x10000);
+}
+
+static uint32_t db_to_mix(double db) {
+ return round(pow(10.0, db / 20.0) * 0x10000);
+}
+
+static struct {
+ GtkWidget *configwin;
+ config_update_func *func;
+ void *ptr;
+ GtkWidget *radio_ppz8_none;
+ GtkWidget *radio_ppz8_linear;
+ GtkWidget *radio_ppz8_sinc;
+} g;
+
+struct fmplayer_config fmplayer_config = {
+ .ssg_mix = 0x10000,
+ .ppz8_interp = PPZ8_INTERP_SINC,
+};
+
+static void on_destroy(GtkWidget *w, gpointer ptr) {
+ (void)w;
+ (void)ptr;
+ g.configwin = 0;
+}
+
+static void on_toggled_ssg(GtkToggleButton *radio_ssg_opna, gpointer ptr) {
+ GtkWidget *spin_ssg_mix = ptr;
+ fmplayer_config.ssg_ymf288 = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio_ssg_opna));
+ gtk_widget_set_sensitive(spin_ssg_mix, !fmplayer_config.ssg_ymf288);
+ g.func(g.ptr);
+}
+
+static void on_changed_ssg_mix(GtkSpinButton *spin_ssg_mix, gpointer ptr) {
+ (void)ptr;
+ fmplayer_config.ssg_mix = db_to_mix(gtk_spin_button_get_value(spin_ssg_mix));
+ g.func(g.ptr);
+}
+
+static void on_changed_ppz8_interp(GtkToggleButton *b, gpointer ptr) {
+ (void)b;
+ (void)ptr;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g.radio_ppz8_none))) {
+ fmplayer_config.ppz8_interp = PPZ8_INTERP_NONE;
+ } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g.radio_ppz8_linear))) {
+ fmplayer_config.ppz8_interp = PPZ8_INTERP_LINEAR;
+ } else {
+ fmplayer_config.ppz8_interp = PPZ8_INTERP_SINC;
+ }
+ g.func(g.ptr);
+}
+
+static void on_toggled_fm_hires_sin(GtkToggleButton *b, gpointer ptr) {
+ (void)ptr;
+ fmplayer_config.fm_hires_sin = gtk_toggle_button_get_active(b);
+ g.func(g.ptr);
+}
+
+static void on_toggled_fm_hires_env(GtkToggleButton *b, gpointer ptr) {
+ (void)ptr;
+ fmplayer_config.fm_hires_env = gtk_toggle_button_get_active(b);
+ g.func(g.ptr);
+}
+
+void show_configdialog(config_update_func *func, void *ptr) {
+ g.func = func;
+ g.ptr = ptr;
+ if (!g.configwin) {
+ g.configwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(g.configwin), "FMPlayer config");
+ g_signal_connect(g.configwin, "destroy", G_CALLBACK(on_destroy), 0);
+ GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ gtk_container_add(GTK_CONTAINER(g.configwin), box);
+ GtkWidget *frame_fm = gtk_frame_new("FM");
+ gtk_container_add(GTK_CONTAINER(box), frame_fm);
+ GtkWidget *fmbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ gtk_container_add(GTK_CONTAINER(frame_fm), fmbox);
+ GtkWidget *check_fm_hires_sin = gtk_check_button_new_with_label("Enable higher resolution sine table");
+ g_signal_connect(check_fm_hires_sin, "toggled",
+ G_CALLBACK(on_toggled_fm_hires_sin), 0);
+ gtk_container_add(GTK_CONTAINER(fmbox), check_fm_hires_sin);
+ GtkWidget *check_fm_hires_env = gtk_check_button_new_with_label("Enable higher resolution envelope");
+ g_signal_connect(check_fm_hires_env, "toggled",
+ G_CALLBACK(on_toggled_fm_hires_env), 0);
+ gtk_container_add(GTK_CONTAINER(fmbox), check_fm_hires_env);
+
+ GtkWidget *frame_ssg = gtk_frame_new("SSG (249600 Hz to 55467 Hz resampling + DC output)");
+ gtk_container_add(GTK_CONTAINER(box), frame_ssg);
+ GtkWidget *ssgbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ gtk_container_add(GTK_CONTAINER(frame_ssg), ssgbox);
+ GtkWidget *radio_ssg_opna = gtk_radio_button_new_with_label(0,
+ "OPNA analog circuit simulation (sinc + HPF)");
+ GSList *radio_ssg_list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_ssg_opna));
+ gtk_container_add(GTK_CONTAINER(ssgbox), radio_ssg_opna);
+ GtkWidget *ssgspinbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+ gtk_container_add(GTK_CONTAINER(ssgbox), ssgspinbox);
+ gtk_container_add(GTK_CONTAINER(ssgspinbox), gtk_label_new("Volume offset (dB):"));
+ GtkWidget *spin_ssg_mix = gtk_spin_button_new_with_range(-18.0, 18.0, 0.01);
+ g_signal_connect(radio_ssg_opna, "toggled", G_CALLBACK(on_toggled_ssg), spin_ssg_mix);
+ g_signal_connect(spin_ssg_mix, "value-changed", G_CALLBACK(on_changed_ssg_mix), 0);
+ gtk_container_add(GTK_CONTAINER(ssgspinbox), spin_ssg_mix);
+ gtk_container_add(GTK_CONTAINER(ssgbox), gtk_label_new("PC-9801-86 / YMF288: 0.0dB (reference)"));
+ gtk_container_add(GTK_CONTAINER(ssgbox), gtk_label_new("PC-9801-26 / Speakboard: 1.6dB (not verified)"));
+ GtkWidget *radio_ssg_ymf288 = gtk_radio_button_new_with_label(
+ radio_ssg_list,
+ "Bit perfect with OPN3-L aka YMF288 (average of nearest 4.5 samples)");
+ gtk_container_add(GTK_CONTAINER(ssgbox), radio_ssg_ymf288);
+
+ GtkWidget *frame_ppz8 = gtk_frame_new("PPZ8 interpolation");
+ gtk_container_add(GTK_CONTAINER(box), frame_ppz8);
+ GtkWidget *ppz8box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ gtk_container_add(GTK_CONTAINER(frame_ppz8), ppz8box);
+ g.radio_ppz8_none = gtk_radio_button_new_with_label(
+ 0, "Nearest neighbor (ppz8.com equivalent)");
+ GSList *radio_ppz8_list =
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(g.radio_ppz8_none));
+ gtk_container_add(GTK_CONTAINER(ppz8box), g.radio_ppz8_none);
+ g.radio_ppz8_linear = gtk_radio_button_new_with_label(
+ radio_ppz8_list, "Linear");
+ gtk_container_add(GTK_CONTAINER(ppz8box), g.radio_ppz8_linear);
+ radio_ppz8_list =
+ gtk_radio_button_get_group(GTK_RADIO_BUTTON(g.radio_ppz8_linear));
+ g.radio_ppz8_sinc = gtk_radio_button_new_with_label(
+ radio_ppz8_list, "Sinc (best quality)");
+ gtk_container_add(GTK_CONTAINER(ppz8box), g.radio_ppz8_sinc);
+ g_signal_connect(g.radio_ppz8_none, "toggled",
+ G_CALLBACK(on_changed_ppz8_interp), 0);
+ g_signal_connect(g.radio_ppz8_linear, "toggled",
+ G_CALLBACK(on_changed_ppz8_interp), 0);
+ g_signal_connect(g.radio_ppz8_sinc, "toggled",
+ G_CALLBACK(on_changed_ppz8_interp), 0);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_fm_hires_sin), fmplayer_config.fm_hires_sin);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_fm_hires_env), fmplayer_config.fm_hires_env);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_ssg_mix), mix_to_db(fmplayer_config.ssg_mix));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_ssg_ymf288), fmplayer_config.ssg_ymf288);
+ switch (fmplayer_config.ppz8_interp) {
+ case PPZ8_INTERP_NONE:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g.radio_ppz8_none), true);
+ break;
+ case PPZ8_INTERP_LINEAR:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g.radio_ppz8_linear), true);
+ break;
+ default:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g.radio_ppz8_sinc), true);
+ break;
+ }
+ gtk_widget_show_all(g.configwin);
+ } else {
+
+ }
+}
diff --git a/gtk/configdialog.h b/gtk/configdialog.h
new file mode 100644
index 0000000..bd0743f
--- /dev/null
+++ b/gtk/configdialog.h
@@ -0,0 +1,20 @@
+#ifndef MYON_FMPLAYER_GTK_CONFIGDIALOG_H
+#define MYON_FMPLAYER_GTK_CONFIGDIALOG_H
+
+#include <gtk/gtk.h>
+#include "libopna/opna.h"
+#include "fmdriver/ppz8.h"
+
+extern struct fmplayer_config {
+ bool fm_hires_env;
+ bool fm_hires_sin;
+ bool ssg_ymf288;
+ uint32_t ssg_mix;
+ enum ppz8_interp ppz8_interp;
+} fmplayer_config;
+
+typedef void config_update_func(void *ptr);
+
+void show_configdialog(config_update_func *func, void *ptr);
+
+#endif // MYON_FMPLAYER_GTK_CONFIGDIALOG_H
diff --git a/gtk/main.c b/gtk/main.c
index ac3ed71..f9c5da1 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -17,6 +17,7 @@
#include "oscillo/oscillo.h"
#include "oscilloview.h"
#include "wavesave.h"
+#include "configdialog.h"
#include "common/fmplayer_common.h"
#include "fft/fft.h"
@@ -45,6 +46,7 @@ static struct {
GtkWidget *filechooser_widget;
bool sound_paused;
struct sound_state *ss;
+ atomic_flag opna_flag;
struct opna opna;
struct opna_timer opna_timer;
struct ppz8 ppz8;
@@ -66,6 +68,7 @@ static struct {
struct fmplayer_fft_input_data fftdata;
} g = {
.oscillo_should_update = true,
+ .opna_flag = ATOMIC_FLAG_INIT,
.at_fftdata_flag = ATOMIC_FLAG_INIT,
};
@@ -111,6 +114,23 @@ static void on_oscillo_view(GtkMenuItem *menuitem, gpointer ptr) {
show_oscilloview();
}
+static void config_update(void *ptr) {
+ (void)ptr;
+ while (atomic_flag_test_and_set_explicit(&g.opna_flag, memory_order_acquire));
+ opna_ssg_set_mix(&g.opna.ssg, fmplayer_config.ssg_mix);
+ opna_ssg_set_ymf288(&g.opna.ssg, &g.opna.resampler, fmplayer_config.ssg_ymf288);
+ ppz8_set_interpolation(&g.ppz8, fmplayer_config.ppz8_interp);
+ opna_fm_set_hires_sin(&g.opna.fm, fmplayer_config.fm_hires_sin);
+ opna_fm_set_hires_env(&g.opna.fm, fmplayer_config.fm_hires_env);
+ atomic_flag_clear_explicit(&g.opna_flag, memory_order_release);
+}
+
+static void on_config(GtkMenuItem *menuitem, gpointer ptr) {
+ (void)menuitem;
+ (void)ptr;
+ show_configdialog(config_update, 0);
+}
+
static void msgbox_err(const char *msg) {
GtkWidget *d = gtk_message_dialog_new(GTK_WINDOW(g.mainwin), GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
@@ -122,10 +142,11 @@ static void msgbox_err(const char *msg) {
static void soundout_cb(void *userptr, int16_t *buf, unsigned frames) {
struct opna_timer *timer = (struct opna_timer *)userptr;
memset(buf, 0, sizeof(int16_t)*frames*2);
+ while (atomic_flag_test_and_set_explicit(&g.opna_flag, memory_order_acquire));
opna_timer_mix_oscillo(timer, buf, frames,
g.oscillo_should_update ?
g.oscillodata_audiothread : 0);
-
+ atomic_flag_clear_explicit(&g.opna_flag, memory_order_release);
if (!atomic_flag_test_and_set_explicit(
&toneview_g.flag, memory_order_acquire)) {
tonedata_from_opna(&toneview_g.tonedata, &g.opna);
@@ -211,6 +232,7 @@ static bool openfile(const char *uri) {
memset(g.adpcm_ram, 0, sizeof(g.adpcm_ram));
fmplayer_init_work_opna(&g.work, &g.ppz8, &g.opna, &g.opna_timer, g.adpcm_ram);
opna_set_mask(&g.opna, mask);
+ config_update(0);
char *disppath = g_filename_from_uri(uri, 0, 0);
if (disppath) {
strncpy(g.work.filename, disppath, sizeof(g.work.filename)-1);
@@ -269,6 +291,9 @@ static GtkWidget *create_menubar() {
GtkWidget *oscilloview = gtk_menu_item_new_with_label("Oscillo view");
g_signal_connect(oscilloview, "activate", G_CALLBACK(on_oscillo_view), 0);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), oscilloview);
+ GtkWidget *config = gtk_menu_item_new_with_label("Config");
+ g_signal_connect(config, "activate", G_CALLBACK(on_config), 0);
+ gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), config);
return menubar;
}
diff --git a/gtk/wavesave.c b/gtk/wavesave.c
index 0af656f..115f3fb 100644
--- a/gtk/wavesave.c
+++ b/gtk/wavesave.c
@@ -5,6 +5,7 @@
#include "libopna/opna.h"
#include "libopna/opnatimer.h"
#include "common/fmplayer_common.h"
+#include "configdialog.h"
enum {
SRATE = 55467,
@@ -168,6 +169,11 @@ static void wavesave(GtkWindow *parent,
}
struct thread_write_data *tdata = g_new0(struct thread_write_data, 1);
fmplayer_init_work_opna(&tdata->work, &tdata->ppz8, &tdata->opna, &tdata->timer, tdata->adpcm_ram);
+ opna_ssg_set_mix(&tdata->opna.ssg, fmplayer_config.ssg_mix);
+ opna_ssg_set_ymf288(&tdata->opna.ssg, &tdata->opna.resampler, fmplayer_config.ssg_ymf288);
+ ppz8_set_interpolation(&tdata->ppz8, fmplayer_config.ppz8_interp);
+ opna_fm_set_hires_sin(&tdata->opna.fm, fmplayer_config.fm_hires_sin);
+ opna_fm_set_hires_env(&tdata->opna.fm, fmplayer_config.fm_hires_env);
fmplayer_file_load(&tdata->work, fmfile, loopcnt);
tdata->fadeout.timer = &tdata->timer;
tdata->fadeout.work = &tdata->work;