diff options
Diffstat (limited to 'gtk')
| -rw-r--r-- | gtk/Makefile.am | 3 | ||||
| -rw-r--r-- | gtk/configdialog.c | 157 | ||||
| -rw-r--r-- | gtk/configdialog.h | 20 | ||||
| -rw-r--r-- | gtk/main.c | 27 | ||||
| -rw-r--r-- | gtk/wavesave.c | 6 | 
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 @@ -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; | 
