diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-08-24 02:51:44 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-08-24 02:51:44 +0900 | 
| commit | ad839eb8338e8b84342bf36480fbf0a7a04fa2bd (patch) | |
| tree | d700dd218c17050816fd5ae0e9a23671145e375f | |
| parent | c3347df0fbc1562d2a80f7ec9d99acbc0c6f2cbd (diff) | |
WIN32: add config dialog
| -rw-r--r-- | win32/configdialog.c | 408 | ||||
| -rw-r--r-- | win32/configdialog.h | 20 | ||||
| -rw-r--r-- | win32/fmplayer.mak | 3 | ||||
| -rw-r--r-- | win32/main.c | 54 | 
4 files changed, 482 insertions, 3 deletions
| diff --git a/win32/configdialog.c b/win32/configdialog.c new file mode 100644 index 0000000..82ca5b5 --- /dev/null +++ b/win32/configdialog.c @@ -0,0 +1,408 @@ +#include "configdialog.h" +#include <windowsx.h> +#include <commctrl.h> +#include <math.h> +#include <wchar.h> +#include <stdlib.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 { +  HINSTANCE hinst; +  HWND configwnd; +  ATOM config_class; +  HFONT font; +  void (*closecb)(void *); +  void *closecbptr; +  void (*changecb)(void *); +  void *changecbptr; +  HWND group_fm, group_ssg, group_ppz8; +  HWND check_fm_hires_sin; +  HWND check_fm_hires_env; +  HWND radio_ssg_opna; +  HWND radio_ssg_ymf288; +  HWND static_volume_offset, static_volume_info; +  HWND edit_ssg_mix; +  HWND radio_ppz8_none, radio_ppz8_linear, radio_ppz8_sinc; +  HWND updown_ssg_mix; +  WNDPROC groupbox_defproc; +  bool edit_ssg_mix_set; +  double ssg_mix_db; +} g; + +enum { +  WIN_W = 500, +  WIN_H = 370, +  GROUP_X = 5, +  GROUP_FM_Y = 5, +  GROUP_W = 490, +  GROUP_FM_H = 75, +  BOX_X = 15, +  CHECK_FM_HIRES_SIN_Y = GROUP_FM_Y + 20, +  BOX_W = 380, +  CHECK_H = 25, +  CHECK_FM_HIRES_ENV_Y = CHECK_FM_HIRES_SIN_Y + CHECK_H, +  GROUP_SSG_Y = GROUP_FM_Y+GROUP_FM_H+5, +  GROUP_SSG_H = 170, +  RADIO_SSG_OPNA_Y = GROUP_SSG_Y + 20, +  STATIC_VOLUME_OFFSET_X = 25, +  STATIC_VOLUME_OFFSET_Y = RADIO_SSG_OPNA_Y + CHECK_H + 5, +  STATIC_VOLUME_OFFSET_W = 110, +  STATIC_VOLUME_INFO_X = 35, +  STATIC_VOLUME_INFO_Y = STATIC_VOLUME_OFFSET_Y + CHECK_H + 5, +  EDIT_SSG_MIX_X = 150, +  EDIT_SSG_MIX_Y = STATIC_VOLUME_OFFSET_Y - 5, +  EDIT_SSG_MIX_W = 100, +  EDIT_SSG_MIX_H = 25, +  RADIO_SSG_YMF288_Y = STATIC_VOLUME_INFO_Y + (EDIT_SSG_MIX_H+5)*2, +  GROUP_PPZ8_Y = GROUP_SSG_Y+GROUP_SSG_H+5, +  GROUP_PPZ8_H = 100, +  RADIO_PPZ8_NONE_Y = GROUP_PPZ8_Y + 20, +  RADIO_PPZ8_LINEAR_Y = RADIO_PPZ8_NONE_Y + CHECK_H, +  RADIO_PPZ8_SINC_Y = RADIO_PPZ8_LINEAR_Y + CHECK_H, +}; + +enum { +  ID_CHECK_FM_HIRES_SIN = 0x10, +  ID_CHECK_FM_HIRES_ENV, +  ID_RADIO_SSG_OPNA, +  ID_RADIO_SSG_YMF288, +  ID_EDIT_SSG_MIX, +  ID_RADIO_PPZ8_NONE, +  ID_RADIO_PPZ8_LINEAR, +  ID_RADIO_PPZ8_SINC, +  ID_UPDOWN_SSG_MIX, +}; + +struct fmplayer_config fmplayer_config = { +  .ssg_mix = 0x10000, +  .ppz8_interp = PPZ8_INTERP_SINC, +}; + +extern HWND g_currentdlg; + +static bool groupbox_on_erasebkgnd(HWND hwnd, HDC hdc) { +  RECT cr; +  GetClientRect(hwnd, &cr); +  FillRect(hdc, &cr, (HBRUSH)(COLOR_BTNFACE+1)); +  return true; +} + +static LRESULT groupbox_wndproc( +  HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam +) { +  switch (msg) { +  HANDLE_MSG(hwnd, WM_ERASEBKGND, groupbox_on_erasebkgnd); +  } +  return CallWindowProc(g.groupbox_defproc, hwnd, msg, wParam, lParam); +} + +static void on_destroy(HWND hwnd) { +  DestroyWindow(g.radio_ppz8_none); +  DestroyWindow(g.radio_ppz8_linear); +  DestroyWindow(g.radio_ppz8_sinc); +  DestroyWindow(g.radio_ssg_ymf288); +  DestroyWindow(g.updown_ssg_mix); +  DestroyWindow(g.edit_ssg_mix); +  DestroyWindow(g.static_volume_info); +  DestroyWindow(g.static_volume_offset); +  DestroyWindow(g.radio_ssg_opna); +  DestroyWindow(g.check_fm_hires_sin); +  DestroyWindow(g.check_fm_hires_env); +  DestroyWindow(g.group_fm); +  DestroyWindow(g.group_ssg); +  DestroyWindow(g.group_ppz8); +  g.configwnd = 0; +  if (g.closecb) g.closecb(g.closecbptr); +} + +static void update_ssg_mix(void) { +  wchar_t buf[10]; +  swprintf(buf, sizeof(buf)/sizeof(buf[0]), +      L"%2.2f", g.ssg_mix_db); +  Edit_SetText(g.edit_ssg_mix, buf); +} + +static void on_command(HWND hwnd, int id, HWND hwnd_c, UINT code) { +  switch (id) { +  case ID_CHECK_FM_HIRES_SIN: +    fmplayer_config.fm_hires_sin = Button_GetCheck(g.check_fm_hires_sin); +    break; +  case ID_CHECK_FM_HIRES_ENV: +    fmplayer_config.fm_hires_env = Button_GetCheck(g.check_fm_hires_env); +    break; +  case ID_RADIO_SSG_OPNA: +    Button_SetCheck(g.radio_ssg_opna, true); +    Button_SetCheck(g.radio_ssg_ymf288, false); +    fmplayer_config.ssg_ymf288 = false; +    Edit_Enable(g.edit_ssg_mix, true); +    EnableWindow(g.updown_ssg_mix, true); +    break; +  case ID_RADIO_SSG_YMF288: +    Button_SetCheck(g.radio_ssg_opna, false); +    Button_SetCheck(g.radio_ssg_ymf288, true); +    fmplayer_config.ssg_ymf288 = true; +    Edit_Enable(g.edit_ssg_mix, false); +    EnableWindow(g.updown_ssg_mix, false); +    break; +  case ID_RADIO_PPZ8_NONE: +    Button_SetCheck(g.radio_ppz8_none, true); +    Button_SetCheck(g.radio_ppz8_linear, false); +    Button_SetCheck(g.radio_ppz8_sinc, false); +    fmplayer_config.ppz8_interp = PPZ8_INTERP_NONE; +    break; +  case ID_RADIO_PPZ8_LINEAR: +    Button_SetCheck(g.radio_ppz8_none, false); +    Button_SetCheck(g.radio_ppz8_linear, true); +    Button_SetCheck(g.radio_ppz8_sinc, false); +    fmplayer_config.ppz8_interp = PPZ8_INTERP_LINEAR; +    break; +  case ID_RADIO_PPZ8_SINC: +    Button_SetCheck(g.radio_ppz8_none, false); +    Button_SetCheck(g.radio_ppz8_linear, false); +    Button_SetCheck(g.radio_ppz8_sinc, true); +    fmplayer_config.ppz8_interp = PPZ8_INTERP_SINC; +    break; +  case ID_EDIT_SSG_MIX: +    if (code == EN_KILLFOCUS) { +      int len = Edit_GetTextLength(g.edit_ssg_mix) + 1; +      if (len) { +        wchar_t *buf = malloc(len * sizeof(buf[0])); +        if (buf) { +          Edit_GetText(g.edit_ssg_mix, buf, len); +          wchar_t *endp; +          g.ssg_mix_db = wcstod(buf, &endp); +          if (buf == endp || *endp) { +            g.ssg_mix_db = wcstol(buf, &endp, 0); +          } +          if (g.ssg_mix_db < -18.0) g.ssg_mix_db = -18.0; +          if (g.ssg_mix_db > 18.0) g.ssg_mix_db = 18.0; +          update_ssg_mix(); +          fmplayer_config.ssg_mix = db_to_mix(g.ssg_mix_db); +          free(buf); +        } +      } +    } +    break; +  } +  g.changecb(g.changecbptr); +} + +static bool on_create(HWND hwnd, const CREATESTRUCT *cs) { +  RECT wr; +  wr.left = 0; +  wr.right = WIN_W; +  wr.top = 0; +  wr.bottom = WIN_H; +  DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); +  DWORD exstyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); +  AdjustWindowRectEx(&wr, style, 0, exstyle); +  SetWindowPos(hwnd, HWND_TOP, 0, 0, wr.right-wr.left, wr.bottom-wr.top, +                SWP_NOZORDER | SWP_NOMOVE); +  if (!g.font) { +    NONCLIENTMETRICS ncm; +    ncm.cbSize = sizeof(ncm); +    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); +    g.font = CreateFontIndirect(&ncm.lfMessageFont); +  } +   +  g.group_fm = CreateWindowEx( +    0, L"button", L"FM", +    WS_CHILD | WS_VISIBLE | BS_GROUPBOX, +    GROUP_X, GROUP_FM_Y, +    GROUP_W, GROUP_FM_H, +    hwnd, 0, g.hinst, 0); +  g.group_ssg = CreateWindowEx( +    0, L"button", L"SSG (249600 Hz to 55467 Hz resampling + DC output)", +    WS_CHILD | WS_VISIBLE | BS_GROUPBOX, +    GROUP_X, GROUP_SSG_Y, +    GROUP_W, GROUP_SSG_H, +    hwnd, 0, g.hinst, 0); +  g.group_ppz8 = CreateWindowEx( +    0, L"button", L"PPZ8 interpolation", +    WS_CHILD | WS_VISIBLE | BS_GROUPBOX, +    GROUP_X, GROUP_PPZ8_Y, +    GROUP_W, GROUP_PPZ8_H, +    hwnd, 0, g.hinst, 0); +  g.check_fm_hires_sin = CreateWindowEx( +    0, L"button", L"Enable higher resolution sine table", +    WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, +    BOX_X, CHECK_FM_HIRES_SIN_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_CHECK_FM_HIRES_SIN, g.hinst, 0); +  g.check_fm_hires_env = CreateWindowEx( +    0, L"button", L"Enable higher resolution envelope", +    WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP, +    BOX_X, CHECK_FM_HIRES_ENV_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_CHECK_FM_HIRES_ENV, g.hinst, 0); +  g.radio_ssg_opna = CreateWindowEx( +    0, L"button", L"OPNA analog circuit simulation (sinc + HPF)", +    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | WS_TABSTOP | WS_GROUP, +    BOX_X, RADIO_SSG_OPNA_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_RADIO_SSG_OPNA, g.hinst, 0); +  g.radio_ssg_ymf288 = CreateWindowEx( +    0, L"button", L"Bit perfect with OPN3-L aka YMF288 (average of nearest 4.5 samples)", +    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, +    BOX_X, RADIO_SSG_YMF288_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_RADIO_SSG_YMF288, g.hinst, 0); +  g.static_volume_offset = CreateWindowEx( +    0, L"static", L"Volume offset (dB):", +    WS_CHILD | WS_VISIBLE, +    STATIC_VOLUME_OFFSET_X, STATIC_VOLUME_OFFSET_Y, +    STATIC_VOLUME_OFFSET_W, CHECK_H, +    hwnd, 0, g.hinst, 0); +  g.static_volume_info = CreateWindowEx( +    0, L"static", L"PC-9801-86 / YMF288: 0.0dB (reference)\nPC-9801-26 / Speakboard: 1.6dB (not verified)", +    WS_CHILD | WS_VISIBLE, +    STATIC_VOLUME_INFO_X, STATIC_VOLUME_INFO_Y, +    BOX_W, CHECK_H*2, +    hwnd, 0, g.hinst, 0); +  g.edit_ssg_mix = CreateWindowEx( +    WS_EX_CLIENTEDGE, L"edit", L"", +    WS_CHILD | WS_VISIBLE | WS_TABSTOP, +    EDIT_SSG_MIX_X, EDIT_SSG_MIX_Y, +    EDIT_SSG_MIX_W, EDIT_SSG_MIX_H, +    hwnd, (HMENU)ID_EDIT_SSG_MIX, g.hinst, 0); +  g.updown_ssg_mix = CreateWindowEx( +    0, UPDOWN_CLASS, L"", +    WS_CHILD | WS_VISIBLE | UDS_AUTOBUDDY | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_HOTTRACK, +    0, 0, 0, 0, +    hwnd, (HMENU)ID_UPDOWN_SSG_MIX, g.hinst, 0); +  g.radio_ppz8_none = CreateWindowEx( +    0, L"button", L"Nearest neighbor (ppz8.com equivalent)", +    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | WS_TABSTOP | WS_GROUP, +    BOX_X, RADIO_PPZ8_NONE_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_RADIO_PPZ8_NONE, g.hinst, 0); +  g.radio_ppz8_linear = CreateWindowEx( +    0, L"button", L"Linear", +    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, +    BOX_X, RADIO_PPZ8_LINEAR_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_RADIO_PPZ8_LINEAR, g.hinst, 0); +  g.radio_ppz8_sinc = CreateWindowEx( +    0, L"button", L"Sinc (best quality)", +    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, +    BOX_X, RADIO_PPZ8_SINC_Y, +    BOX_W, CHECK_H, +    hwnd, (HMENU)ID_RADIO_PPZ8_SINC, g.hinst, 0); +  g.groupbox_defproc = (WNDPROC)GetWindowLongPtr(g.group_fm, GWLP_WNDPROC); +  SetWindowLongPtr(g.group_fm, GWLP_WNDPROC, (intptr_t)groupbox_wndproc); +  SetWindowLongPtr(g.group_ssg, GWLP_WNDPROC, (intptr_t)groupbox_wndproc); +  SetWindowLongPtr(g.group_ppz8, GWLP_WNDPROC, (intptr_t)groupbox_wndproc); +  SetWindowFont(g.group_fm, g.font, TRUE); +  SetWindowFont(g.group_ssg, g.font, TRUE); +  SetWindowFont(g.group_ppz8, g.font, TRUE); +  SetWindowFont(g.check_fm_hires_sin, g.font, TRUE); +  SetWindowFont(g.check_fm_hires_env, g.font, TRUE); +  SetWindowFont(g.radio_ssg_opna, g.font, TRUE); +  SetWindowFont(g.radio_ssg_ymf288, g.font, TRUE); +  SetWindowFont(g.static_volume_offset, g.font, TRUE); +  SetWindowFont(g.static_volume_info, g.font, TRUE); +  SetWindowFont(g.edit_ssg_mix, g.font, TRUE); +  SetWindowFont(g.radio_ppz8_none, g.font, TRUE); +  SetWindowFont(g.radio_ppz8_linear, g.font, TRUE); +  SetWindowFont(g.radio_ppz8_sinc, g.font, TRUE); + +  if (fmplayer_config.fm_hires_sin) Button_SetCheck(g.check_fm_hires_sin, true); +  if (fmplayer_config.fm_hires_env) Button_SetCheck(g.check_fm_hires_env, true); + +  Button_SetCheck(fmplayer_config.ssg_ymf288 ? g.radio_ssg_ymf288 : g.radio_ssg_opna, true); +  switch (fmplayer_config.ppz8_interp) { +  case PPZ8_INTERP_LINEAR: +    Button_SetCheck(g.radio_ppz8_linear, true); +    break; +  case PPZ8_INTERP_SINC: +    Button_SetCheck(g.radio_ppz8_sinc, true); +    break; +  default: +    Button_SetCheck(g.radio_ppz8_none, true); +    break; +  } +  g.ssg_mix_db = mix_to_db(fmplayer_config.ssg_mix); +  update_ssg_mix(); +  Edit_Enable(g.edit_ssg_mix, !fmplayer_config.ssg_ymf288); +  EnableWindow(g.updown_ssg_mix, !fmplayer_config.ssg_ymf288); +  ShowWindow(hwnd, SW_SHOW); +  return true; +} + +static void on_activate(HWND hwnd, bool activate, HWND targetwnd, WINBOOL state) { +  if (activate) g_currentdlg = hwnd; +  else g_currentdlg = 0; +} + +static LRESULT CALLBACK wndproc( +  HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam +) { +  switch (msg) { +  HANDLE_MSG(hwnd, WM_DESTROY, on_destroy); +  HANDLE_MSG(hwnd, WM_CREATE, on_create); +  HANDLE_MSG(hwnd, WM_COMMAND, on_command); +  HANDLE_MSG(hwnd, WM_ACTIVATE, on_activate); +  case WM_NOTIFY: +    { +      const NMHDR *hdr = (NMHDR *)lParam; +      if (hdr->idFrom == ID_UPDOWN_SSG_MIX && hdr->code == UDN_DELTAPOS) { +        const NMUPDOWN *udnhdr = (NMUPDOWN *)hdr; +        g.ssg_mix_db -= udnhdr->iDelta * 0.1; +        g.ssg_mix_db = round(g.ssg_mix_db * 10.0) / 10.0; +        if (g.ssg_mix_db < -18.0) g.ssg_mix_db = -18.0; +        if (g.ssg_mix_db > 18.0) g.ssg_mix_db = 18.0; +        update_ssg_mix(); +        fmplayer_config.ssg_mix = db_to_mix(g.ssg_mix_db); +      } +      break; +    } +  } +  return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void configdialog_open(HINSTANCE hinst, HWND parent, void (*closecb)(void *), void *closecbptr, void (*changecb)(void *), void *changecbptr) { +  g.closecb = closecb; +  g.closecbptr = closecbptr; +  g.changecb = changecb; +  g.changecbptr = changecbptr; +  g.hinst = hinst; +  if (!g.configwnd) { +    if (!g.config_class) { +      WNDCLASS wc = {0}; +      wc.style = CS_HREDRAW | CS_VREDRAW; +      wc.lpfnWndProc = wndproc; +      wc.hInstance = hinst; +      wc.hIcon = LoadIcon(g.hinst, MAKEINTRESOURCE(1)); +      wc.hCursor = LoadCursor(NULL, IDC_ARROW); +      wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); +      wc.lpszClassName = L"myon_fmplayer_ym2608_config"; +      g.config_class = RegisterClass(&wc); +    } +    if (!g.config_class) { +      MessageBox(parent, L"Cannot register class", L"Error", MB_ICONSTOP); +      return; +    } +    g.configwnd = CreateWindowEx(0, +                                 MAKEINTATOM(g.config_class), +                                 L"FMPlayer config", +                                 WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN, +                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, +                                 parent, 0, g.hinst, 0); +  } else { +    SetForegroundWindow(g.configwnd); +  } +} + +void configdialog_close(void) { +  if (g.configwnd) { +    g.closecb = 0; +    DestroyWindow(g.configwnd); +  } +} diff --git a/win32/configdialog.h b/win32/configdialog.h new file mode 100644 index 0000000..fa9b60f --- /dev/null +++ b/win32/configdialog.h @@ -0,0 +1,20 @@ +#ifndef MYON_FMPLAYER_WIN32_CONFIGDIALOG_H_INCLUDED +#define MYON_FMPLAYER_WIN32_CONFIGDIALOG_H_INCLUDED + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdbool.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; + +void configdialog_open(HINSTANCE hinst, HWND parent, void (*closecb)(void *), void *closecbptr, void (*changecb)(void *), void *changecbptr); +void configdialog_close(void); + +#endif // MYON_FMPLAYER_WIN32_CONFIGDIALOG_H_INCLUDED diff --git a/win32/fmplayer.mak b/win32/fmplayer.mak index d62861c..122ec56 100644 --- a/win32/fmplayer.mak +++ b/win32/fmplayer.mak @@ -45,7 +45,8 @@ OBJBASE=main \          $(FMDRIVER_OBJS) \          $(LIBOPNA_OBJS) \          $(TONEDATA_OBJS) \ -        $(FMDSP_OBJS) +        $(FMDSP_OBJS) \ +				configdialog  RESBASE=lnf  LIBBASE=user32 \          kernel32 \ diff --git a/win32/main.c b/win32/main.c index c1df7a7..3519d1b 100644 --- a/win32/main.c +++ b/win32/main.c @@ -23,6 +23,7 @@  #include "common/fmplayer_common.h"  #include "wavesave.h"  #include "fft/fft.h" +#include "configdialog.h"  enum {    ID_OPENFILE = 0x10, @@ -32,6 +33,7 @@ enum {    ID_OSCILLOVIEW,    ID_ABOUT,    ID_WAVESAVE, +  ID_CONFIG,  };  #define FMPLAYER_CLASSNAME L"myon_fmplayer_ym2608_win32" @@ -66,8 +68,8 @@ static struct {    bool paused;    HWND mainwnd;    WNDPROC btn_defproc; -  HWND button_2x, button_toneview, button_oscilloview, button_about; -  bool toneview_on, oscilloview_on, about_on; +  HWND button_2x, button_toneview, button_oscilloview, button_about, button_config; +  bool toneview_on, oscilloview_on, about_on, config_on;    const wchar_t *lastopenpath;    bool fmdsp_2x;    struct oscillodata oscillodata_audiothread[LIBOPNA_OSCILLO_TRACK_COUNT]; @@ -78,8 +80,10 @@ static struct {    atomic_flag at_fftdata_flag;    struct fmplayer_fft_data at_fftdata;    struct fmplayer_fft_input_data fftdata; +  atomic_flag opna_flag;  } g = {    .at_fftdata_flag = ATOMIC_FLAG_INIT, +  .opna_flag = ATOMIC_FLAG_INIT,  };  HWND g_currentdlg; @@ -87,7 +91,9 @@ HWND g_currentdlg;  static void sound_cb(void *p, int16_t *buf, unsigned frames) {    struct opna_timer *timer = (struct opna_timer *)p;    ZeroMemory(buf, 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.oscillodata_audiothread); +  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); @@ -170,6 +176,11 @@ static void openfile(HWND hwnd, const wchar_t *path) {      about_set_adpcmrom_loaded(true);    }    opna_set_mask(&g.opna, mask); +  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);    WideCharToMultiByte(932, WC_NO_BEST_FIT_CHARS, path, -1, g.work.filename, sizeof(g.work.filename), 0, 0);    fmplayer_file_load(&g.work, g.fmfile, 1);    if (!g.sound) { @@ -504,6 +515,15 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {      50, 25,      hwnd, (HMENU)ID_ABOUT, g.hinst, 0    ); +  g.button_config = CreateWindowEx( +    0, +    L"BUTTON", +    L"&Config...", +    WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_CHECKBOX | BS_PUSHLIKE, +    460, 10, +    100, 25, +    hwnd, (HMENU)ID_CONFIG, g.hinst, 0 +  );    g.btn_defproc = (WNDPROC)GetWindowLongPtr(button, GWLP_WNDPROC);    SetWindowLongPtr(button, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(pbutton, GWLP_WNDPROC, (intptr_t)btn_wndproc); @@ -512,6 +532,7 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {    SetWindowLongPtr(g.button_toneview, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(g.button_oscilloview, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(g.button_about, GWLP_WNDPROC, (intptr_t)btn_wndproc); +  SetWindowLongPtr(g.button_config, GWLP_WNDPROC, (intptr_t)btn_wndproc);    NONCLIENTMETRICS ncm;    ncm.cbSize = sizeof(ncm);    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); @@ -523,6 +544,7 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {    SetWindowFont(g.button_toneview, font, TRUE);    SetWindowFont(g.button_oscilloview, font, TRUE);    SetWindowFont(g.button_about, font, TRUE); +  SetWindowFont(g.button_config, font, TRUE);    loadfont();    fmdsp_init(&g.fmdsp, g.font_loaded ? &g.font : 0);    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram); @@ -552,6 +574,23 @@ static void about_close_cb(void *ptr) {    Button_SetCheck(g.button_about, false);  } +static void configdialog_close_cb(void *ptr) { +  (void)ptr; +  g.config_on = false; +  Button_SetCheck(g.button_config, false); +} + +static void configdialog_change_cb(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_command(HWND hwnd, int id, HWND hwnd_c, UINT code) {    (void)code;    (void)hwnd_c; @@ -613,6 +652,17 @@ static void on_command(HWND hwnd, int id, HWND hwnd_c, UINT code) {        }      }      break; +  case ID_CONFIG: +    if (!g.config_on) { +      g.config_on = true; +      configdialog_open(g.hinst, hwnd, configdialog_close_cb, 0, configdialog_change_cb, 0); +      Button_SetCheck(g.button_config, true); +    } else { +      g.config_on = false; +      configdialog_close(); +      Button_SetCheck(g.button_config, false); +    } +    break;    }  } | 
