diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-03-01 00:22:33 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-03-01 00:22:33 +0900 | 
| commit | 23dc19065c182e61bce9c997f31e23b001a85f2c (patch) | |
| tree | ec4dc1d49362b1fc95c2608bb40651cc6f8f2917 | |
| parent | 2432f71b1e1b1e474a2ff5b833107246a3e3762a (diff) | |
gtk: add tone viewer
| -rw-r--r-- | gtk/Makefile.am | 2 | ||||
| -rw-r--r-- | gtk/configure.ac | 1 | ||||
| -rw-r--r-- | gtk/main.c | 25 | ||||
| -rw-r--r-- | gtk/toneview.c | 107 | ||||
| -rw-r--r-- | gtk/toneview.h | 14 | ||||
| -rw-r--r-- | tonedata/tonedata.c | 107 | ||||
| -rw-r--r-- | tonedata/tonedata.h | 48 | 
7 files changed, 302 insertions, 2 deletions
| diff --git a/gtk/Makefile.am b/gtk/Makefile.am index e765244..665f3ca 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -15,6 +15,8 @@ FMDSP_SRC=../fmdsp/fmdsp.c \            ../fmdsp/font_fmdsp_small.c  fmplayer_SOURCES=main.c \ +                 toneview.c \ +                 ../tonedata/tonedata.c \                   $(LIBOPNA_SRC) \                   $(FMDRIVER_SRC) \                   $(FMDSP_SRC) diff --git a/gtk/configure.ac b/gtk/configure.ac index bde5f6e..8e13a34 100644 --- a/gtk/configure.ac +++ b/gtk/configure.ac @@ -1,5 +1,6 @@  AC_INIT([fmplayer], [0.1.0])  AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) +AM_SILENT_RULES([yes])  AC_PROG_CC_C99  dnl AM_PATH_SDL2([2.0.5]) @@ -5,12 +5,14 @@  #include <stdint.h>  #include <stdbool.h>  #include <cairo.h> +#include <stdatomic.h>  #include "fmdriver/fmdriver_fmp.h"  #include "fmdriver/ppz8.h"  #include "libopna/opna.h"  #include "libopna/opnatimer.h"  #include "fmdsp/fmdsp.h" +#include "toneview.h"  #define DATADIR "/.local/share/fmplayer/"  //#define FMDSP_2X @@ -64,6 +66,10 @@ static void on_menu_quit(GtkMenuItem *menuitem, gpointer ptr) {    quit();  } +static void on_tone_view(GtkMenuItem *menuitem, gpointer ptr) { +  show_toneview(); +} +  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, @@ -81,6 +87,13 @@ static int pastream_cb(const void *inptr, void *outptr, unsigned long frames,    int16_t *buf = (int16_t *)outptr;    memset(outptr, 0, sizeof(int16_t)*frames*2);    opna_timer_mix(timer, buf, frames); + +  bool xchg = false; +  if (atomic_compare_exchange_weak_explicit(&toneview_g.flag, &xchg, true, +      memory_order_acquire, memory_order_relaxed)) { +    tonedata_from_opna(&toneview_g.tonedata, &g.opna); +    atomic_store_explicit(&toneview_g.flag, false, memory_order_release); +  }      return paContinue;  } @@ -401,6 +414,14 @@ static GtkWidget *create_menubar() {    GtkWidget *quit = gtk_menu_item_new_with_label("Quit");    g_signal_connect(quit, "activate", G_CALLBACK(on_menu_quit), 0);    gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit); + +  GtkWidget *window = gtk_menu_item_new_with_label("Window"); +  GtkWidget *filemenu = gtk_menu_new(); +  gtk_menu_item_set_submenu(GTK_MENU_ITEM(window), filemenu); +  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), window); +  GtkWidget *toneview = gtk_menu_item_new_with_label("Tone view"); +  g_signal_connect(toneview, "activate", G_CALLBACK(on_tone_view), 0); +  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), toneview);    return menubar;  } @@ -452,7 +473,7 @@ static void mask_update(void) {  static void mask_set(unsigned mask, bool shift) {    if (shift) { -    opna_set_mask(&g.opna, mask); +    opna_set_mask(&g.opna, ~mask);    } else {      opna_set_mask(&g.opna, opna_get_mask(&g.opna) ^ mask);    } @@ -470,7 +491,7 @@ static gboolean key_press_cb(GtkWidget *w,        return TRUE;      }    } -  bool shift = e->key.state & GDK_SHIFT_MASK; +  bool shift = e->key.state & GDK_CONTROL_MASK;    switch (e->key.keyval) {    case GDK_KEY_F6:      if (g.current_uri) { diff --git a/gtk/toneview.c b/gtk/toneview.c new file mode 100644 index 0000000..79750fe --- /dev/null +++ b/gtk/toneview.c @@ -0,0 +1,107 @@ +#include <gtk/gtk.h> +#include "toneview.h" +#include <stdatomic.h> +#include <stdbool.h> + +struct toneview_g toneview_g; + +static struct { +  GtkWidget *tonewin; +  GtkWidget *label[6]; +  struct fmplayer_tonedata tonedata; +  struct fmplayer_tonedata tonedata_n; +  char strbuf[FMPLAYER_TONEDATA_STR_SIZE]; +  enum fmplayer_tonedata_format format; +  bool normalize; +  GtkClipboard *clipboard; +} g; + +static void on_destroy(GtkWidget *w, gpointer ptr) { +  (void)w; +  (void)ptr; +  g.tonewin = 0; +} + +gboolean tick_cb(GtkWidget *widget, GdkFrameClock *clock, gpointer ptr) { +  (void)widget; +  (void)clock; +  (void)ptr; +  bool xchg = false; +  if (atomic_compare_exchange_weak_explicit(&toneview_g.flag, &xchg, true, +      memory_order_acquire, memory_order_relaxed)) { +    g.tonedata = toneview_g.tonedata; +    atomic_store_explicit(&toneview_g.flag, false, memory_order_release); +  } +  g.tonedata_n = g.tonedata; +  for (int c = 0; c < 6; c++) { +    if (g.normalize) { +      tonedata_ch_normalize_tl(&g.tonedata_n.ch[c]); +    } +    tonedata_ch_string(g.format, g.strbuf, &g.tonedata_n.ch[c], 0); +    gtk_label_set_text(GTK_LABEL(g.label[c]), g.strbuf); +  } +  return G_SOURCE_CONTINUE; +} + +static void on_format_changed(GtkComboBox *widget, gpointer ptr) { +  (void)ptr; +  g.format = gtk_combo_box_get_active(widget); +} + +static void on_normalize_toggled(GtkToggleButton *widget, gpointer ptr) { +  (void)ptr; +  g.normalize = gtk_toggle_button_get_active(widget); +} + +static void on_copy_clicked(GtkButton *button, gpointer ptr) { +  (void)button; +  int c = (intptr_t)ptr; +  if (!g.clipboard) { +    GdkDisplay *disp = gdk_display_get_default(); +    if (disp) { +      g.clipboard = gtk_clipboard_get_default(disp); +    } +  } +  if (g.clipboard) { +    tonedata_ch_string(g.format, g.strbuf, &g.tonedata_n.ch[c], 0); +    gtk_clipboard_set_text(g.clipboard, g.strbuf, -1); +  } +} + +void show_toneview(void) { +  if (!g.tonewin) { +    g.tonewin = gtk_window_new(GTK_WINDOW_TOPLEVEL); +    gtk_window_set_title(GTK_WINDOW(g.tonewin), "FM Tone Viewer"); +    g_signal_connect(g.tonewin, "destroy", G_CALLBACK(on_destroy), 0); +    GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); +    gtk_container_add(GTK_CONTAINER(g.tonewin), box); +    GtkWidget *ctrlbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +    gtk_container_add(GTK_CONTAINER(box), ctrlbox); +    GtkWidget *format = gtk_combo_box_text_new(); +    gtk_box_pack_start(GTK_BOX(ctrlbox), format, FALSE, TRUE, 0); +    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(format), "PMD"); +    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(format), "FMP"); +    gtk_combo_box_set_active(GTK_COMBO_BOX(format), g.format); +    g_signal_connect(format, "changed", G_CALLBACK(on_format_changed), 0); +    GtkWidget *normalizecheck = gtk_check_button_new_with_label("Normalize"); +    gtk_box_pack_start(GTK_BOX(ctrlbox), normalizecheck, FALSE, TRUE, 0); +    g_signal_connect(normalizecheck, "toggled", G_CALLBACK(on_normalize_toggled), 0); +    for (int c = 0; c < 6; c++) { +      GtkWidget *cbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); +      gtk_box_pack_start(GTK_BOX(box), cbox, TRUE, TRUE, 0); +      GtkWidget *tonetext = gtk_label_new(0); +      PangoAttrList *pattrl = pango_attr_list_new(); +      PangoAttribute *pattr = pango_attr_family_new("monospace"); +      pango_attr_list_insert(pattrl, pattr); +      gtk_label_set_attributes(GTK_LABEL(tonetext), pattrl); +      pango_attr_list_unref(pattrl); +      gtk_box_pack_start(GTK_BOX(cbox), tonetext, TRUE, TRUE, 0); +      GtkWidget *copybutton = gtk_button_new_with_label("Copy"); +      g_signal_connect(copybutton, "clicked", G_CALLBACK(on_copy_clicked), (gpointer)((intptr_t)c)); +      gtk_box_pack_start(GTK_BOX(cbox), copybutton, FALSE, TRUE, 0); +      g.label[c] = tonetext; +    } +    gtk_widget_add_tick_callback(g.tonewin, tick_cb, 0, 0); +    gtk_widget_show_all(g.tonewin); +  } +} diff --git a/gtk/toneview.h b/gtk/toneview.h new file mode 100644 index 0000000..51c1769 --- /dev/null +++ b/gtk/toneview.h @@ -0,0 +1,14 @@ +#ifndef MYON_FMPLAYER_GTK_TONEVIEW_H_INCLUDED +#define MYON_FMPLAYER_GTK_TONEVIEW_H_INCLUDED + +#include "tonedata/tonedata.h" +#include <stdatomic.h> + +extern struct toneview_g { +  struct fmplayer_tonedata tonedata; +  atomic_bool flag; +} toneview_g; + +void show_toneview(); + +#endif // MYON_FMPLAYER_GTK_TONEVIEW_H_INCLUDED diff --git a/tonedata/tonedata.c b/tonedata/tonedata.c new file mode 100644 index 0000000..0d5daa6 --- /dev/null +++ b/tonedata/tonedata.c @@ -0,0 +1,107 @@ +#include "tonedata.h" +#include "libopna/opna.h" +#include <stdio.h> +void tonedata_from_opna( +  struct fmplayer_tonedata *tonedata, +  const struct opna *opna +) { +  const struct opna_fm *fm = &opna->fm; +  for (int c = 0; c < 6; c++) { +    const struct opna_fm_channel *op_ch = &fm->channel[c]; +    struct fmplayer_tonedata_channel *td_ch = &tonedata->ch[c]; +    for (int s = 0; s < 4; s++) { +      const struct opna_fm_slot *op_sl = &op_ch->slot[s]; +      struct fmplayer_tonedata_slot *td_sl = &td_ch->slot[s]; +      td_sl->ar = op_sl->ar; +      td_sl->dr = op_sl->dr; +      td_sl->sr = op_sl->sr; +      td_sl->rr = op_sl->rr; +      td_sl->sl = op_sl->sl; +      td_sl->tl = op_sl->tl; +      td_sl->ks = op_sl->ks; +      td_sl->ml = op_sl->mul; +      td_sl->dt = op_sl->det; +      // TODO: hardware LFO not implemented +      td_sl->ams = 0; +    } +    td_ch->alg = op_ch->alg; +    td_ch->fb = op_ch->fb; +  } +} + +void tonedata_ch_normalize_tl( +  struct fmplayer_tonedata_channel *ch +) { +  static const uint8_t alg_out[8] = { +    0x8, 0x8, 0x8, 0x8, 0xa, 0xe, 0xe, 0xf +  }; +  uint8_t outbit = alg_out[ch->alg & 7]; +  uint8_t max_tl = 127; +  for (int s = 0; s < 4; s++) { +    if (!(outbit & (1<<s))) continue; +    if (max_tl > ch->slot[s].tl) max_tl = ch->slot[s].tl; +  } +  for (int s = 0; s < 4; s++) { +    if (!(outbit & (1<<s))) continue; +    ch->slot[s].tl -= max_tl; +  } +} + +void tonedata_ch_string( +  enum fmplayer_tonedata_format format, +  char *buf, +  const struct fmplayer_tonedata_channel *ch, +  uint8_t tonenum +) { +  switch (format) { +  case FMPLAYER_TONEDATA_FMT_PMD: +    snprintf(buf, FMPLAYER_TONEDATA_STR_SIZE, +            "@%3d %1d %1d\n" +            " %2d %2d %2d %2d %2d %3d %1d %2d %1d %1d\n" +            " %2d %2d %2d %2d %2d %3d %1d %2d %1d %1d\n" +            " %2d %2d %2d %2d %2d %3d %1d %2d %1d %1d\n" +            " %2d %2d %2d %2d %2d %3d %1d %2d %1d %1d\n", +            tonenum, ch->alg, ch->fb, +            ch->slot[0].ar, ch->slot[0].dr, ch->slot[0].sr, ch->slot[0].rr, +            ch->slot[0].sl, ch->slot[0].tl, ch->slot[0].ks, ch->slot[0].ml, +            ch->slot[0].dt, ch->slot[0].ams, +            ch->slot[1].ar, ch->slot[1].dr, ch->slot[1].sr, ch->slot[1].rr, +            ch->slot[1].sl, ch->slot[1].tl, ch->slot[1].ks, ch->slot[1].ml, +            ch->slot[1].dt, ch->slot[1].ams, +            ch->slot[2].ar, ch->slot[2].dr, ch->slot[2].sr, ch->slot[2].rr, +            ch->slot[2].sl, ch->slot[2].tl, ch->slot[2].ks, ch->slot[2].ml, +            ch->slot[2].dt, ch->slot[2].ams, +            ch->slot[3].ar, ch->slot[3].dr, ch->slot[3].sr, ch->slot[3].rr, +            ch->slot[3].sl, ch->slot[3].tl, ch->slot[3].ks, ch->slot[3].ml, +            ch->slot[3].dt, ch->slot[3].ams +    ); +    break; +  case FMPLAYER_TONEDATA_FMT_FMP: +    snprintf(buf, FMPLAYER_TONEDATA_STR_SIZE, +            "'@%3d\n" +            "'@ %2d, %2d, %2d, %2d, %2d, %3d, %1d, %2d, %1d\n" +            "'@ %2d, %2d, %2d, %2d, %2d, %3d, %1d, %2d, %1d\n" +            "'@ %2d, %2d, %2d, %2d, %2d, %3d, %1d, %2d, %1d\n" +            "'@ %2d, %2d, %2d, %2d, %2d, %3d, %1d, %2d, %1d\n" +            "'@ %1d, %1d", +            tonenum, +            ch->slot[0].ar, ch->slot[0].dr, ch->slot[0].sr, ch->slot[0].rr, +            ch->slot[0].sl, ch->slot[0].tl, ch->slot[0].ks, ch->slot[0].ml, +            ch->slot[0].dt, +            ch->slot[1].ar, ch->slot[1].dr, ch->slot[1].sr, ch->slot[1].rr, +            ch->slot[1].sl, ch->slot[1].tl, ch->slot[1].ks, ch->slot[1].ml, +            ch->slot[1].dt, +            ch->slot[2].ar, ch->slot[2].dr, ch->slot[2].sr, ch->slot[2].rr, +            ch->slot[2].sl, ch->slot[2].tl, ch->slot[2].ks, ch->slot[2].ml, +            ch->slot[2].dt, +            ch->slot[3].ar, ch->slot[3].dr, ch->slot[3].sr, ch->slot[3].rr, +            ch->slot[3].sl, ch->slot[3].tl, ch->slot[3].ks, ch->slot[3].ml, +            ch->slot[3].dt, +            ch->alg, ch->fb +    ); +    break; +  default: +    buf[0] = 0; +    break; +  } +} diff --git a/tonedata/tonedata.h b/tonedata/tonedata.h new file mode 100644 index 0000000..c8dd03c --- /dev/null +++ b/tonedata/tonedata.h @@ -0,0 +1,48 @@ +#ifndef MYON_FMPLAYER_TONEDATA_H_INCLUDED +#define MYON_FMPLAYER_TONEDATA_H_INCLUDED + +#include <stdint.h> + +struct fmplayer_tonedata { +  struct fmplayer_tonedata_channel { +    struct fmplayer_tonedata_slot { +      uint8_t ar; +      uint8_t dr; +      uint8_t sr; +      uint8_t rr; +      uint8_t sl; +      uint8_t tl; +      uint8_t ks; +      uint8_t ml; +      uint8_t dt; +      uint8_t ams; +    } slot[4]; +    uint8_t fb; +    uint8_t alg; +  } ch[6]; +}; + +struct opna; +void tonedata_from_opna( +  struct fmplayer_tonedata *tonedata, +  const struct opna *opna +); + +enum fmplayer_tonedata_format { +  FMPLAYER_TONEDATA_FMT_PMD, +  FMPLAYER_TONEDATA_FMT_FMP +}; + +enum { +  FMPLAYER_TONEDATA_STR_SIZE = 0x100 +}; +void tonedata_ch_normalize_tl(struct fmplayer_tonedata_channel *ch); +void tonedata_ch_string( +  enum fmplayer_tonedata_format format, +  char *buf, +  const struct fmplayer_tonedata_channel *ch, +  uint8_t tonenum +); + +#endif // MYON_FMPLAYER_TONEDATA_H_INCLUDED + | 
