diff options
Diffstat (limited to 'win32')
| -rw-r--r-- | win32/fmplayer.mak | 4 | ||||
| -rw-r--r-- | win32/main.c | 118 | ||||
| -rw-r--r-- | win32/wavesave.c | 210 | ||||
| -rw-r--r-- | win32/wavesave.h | 9 | ||||
| -rw-r--r-- | win32/wavewrite.c | 90 | ||||
| -rw-r--r-- | win32/wavewrite.h | 15 | 
6 files changed, 357 insertions, 89 deletions
| diff --git a/win32/fmplayer.mak b/win32/fmplayer.mak index 14e3246..9a514b2 100644 --- a/win32/fmplayer.mak +++ b/win32/fmplayer.mak @@ -28,6 +28,8 @@ SSEOBJBASE=opnassg-sinc-sse2 \  OBJBASE=main \          toneview \          oscilloview \ +        wavesave \ +        wavewrite \          soundout \          dsoundout \          waveout \ @@ -35,6 +37,8 @@ OBJBASE=main \          guid \          fmplayer_file \          fmplayer_file_win \ +        fmplayer_drumrom_win \ +        fmplayer_work_opna \          about \          $(FMDRIVER_OBJS) \          $(LIBOPNA_OBJS) \ diff --git a/win32/main.c b/win32/main.c index 76cd883..66a1bf0 100644 --- a/win32/main.c +++ b/win32/main.c @@ -20,6 +20,8 @@  #include "oscillo/oscillo.h"  #include "oscilloview.h"  #include "about.h" +#include "common/fmplayer_common.h" +#include "wavesave.h"  enum {    ID_OPENFILE = 0x10, @@ -28,6 +30,7 @@ enum {    ID_TONEVIEW,    ID_OSCILLOVIEW,    ID_ABOUT, +  ID_WAVESAVE,  };  #define FMPLAYER_CLASSNAME L"myon_fmplayer_ym2608_win32" @@ -58,12 +61,10 @@ static struct {    struct fmdsp_font font;    uint8_t fontrom[FONT_ROM_FILESIZE];    bool font_loaded; -  void *drum_rom;    uint8_t opna_adpcm_ram[OPNA_ADPCM_RAM_SIZE];    bool paused;    HWND mainwnd;    WNDPROC btn_defproc; -  HWND driverinfo;    HWND button_2x, button_toneview, button_oscilloview, button_about;    bool toneview_on, oscilloview_on, about_on;    const wchar_t *lastopenpath; @@ -72,39 +73,11 @@ static struct {    UINT mmtimer;    HBITMAP bitmap_vram;    uint8_t *vram32; +  bool drum_loaded;  } g;  HWND g_currentdlg; -static void opna_int_cb(void *userptr) { -  struct fmdriver_work *work = (struct fmdriver_work *)userptr; -  work->driver_opna_interrupt(work); -} - -static void opna_mix_cb(void *userptr, int16_t *buf, unsigned samples) { -  struct ppz8 *ppz8 = (struct ppz8 *)userptr; -  ppz8_mix(ppz8, buf, samples); -} - -static void opna_writereg_libopna(struct fmdriver_work *work, unsigned addr, unsigned data) { -  struct opna_timer *timer = (struct opna_timer *)work->opna; -  opna_timer_writereg(timer, addr, data); -} - -static unsigned opna_readreg_libopna(struct fmdriver_work *work, unsigned addr) { -//  struct opna_timer *timer = (struct opna_timer *)work->opna; -  return opna_readreg(&g.opna, addr); -} - -static uint8_t opna_status_libopna(struct fmdriver_work *work, bool a1) { -  struct opna_timer *timer = (struct opna_timer *)work->opna; -  uint8_t status = opna_timer_status(timer); -  if (!a1) { -    status &= 0x83; -  } -  return status; -} -  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); @@ -159,39 +132,6 @@ static void loadfont(void) {    }  } -static void loadrom(void) { -  const wchar_t *path = L"ym2608_adpcm_rom.bin"; -  wchar_t exepath[MAX_PATH]; -  if (GetModuleFileName(0, exepath, MAX_PATH)) { -    PathRemoveFileSpec(exepath); -    if ((lstrlen(exepath) + lstrlen(path) + 1) < MAX_PATH) { -      lstrcat(exepath, L"\\"); -      lstrcat(exepath, path); -      path = exepath; -    } -  } -  HANDLE file = CreateFile(path, GENERIC_READ, -                            0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); -  if (file == INVALID_HANDLE_VALUE) goto err; -  DWORD filesize = GetFileSize(file, 0); -  if (filesize != OPNA_ROM_SIZE) goto err_file; -  void *buf = HeapAlloc(g.heap, 0, OPNA_ROM_SIZE); -  if (!buf) goto err_file; -  DWORD readbytes; -  if (!ReadFile(file, buf, OPNA_ROM_SIZE, &readbytes, 0) -      || readbytes != OPNA_ROM_SIZE) goto err_buf; -  CloseHandle(file); -  g.drum_rom = buf; -  about_set_adpcmrom_loaded(true); -  return; -err_buf: -  HeapFree(g.heap, 0, buf); -err_file: -  CloseHandle(file); -err: -  return; -} -  static void openfile(HWND hwnd, const wchar_t *path) {    enum fmplayer_file_error error;    struct fmplayer_file *fmfile = fmplayer_file_alloc(path, &error); @@ -213,27 +153,17 @@ static void openfile(HWND hwnd, const wchar_t *path) {    fmplayer_file_free(g.fmfile);    g.fmfile = fmfile;    unsigned mask = opna_get_mask(&g.opna); -  opna_reset(&g.opna); +  fmplayer_init_work_opna(&g.work, &g.ppz8, &g.opna, &g.opna_timer, g.opna_adpcm_ram); +  if (!g.drum_loaded && fmplayer_drum_loaded()) { +    g.drum_loaded = true; +    about_set_adpcmrom_loaded(true); +  }    opna_set_mask(&g.opna, mask); -  if (g.drum_rom) opna_drum_set_rom(&g.opna.drum, g.drum_rom); -  opna_adpcm_set_ram_256k(&g.opna.adpcm, g.opna_adpcm_ram); -  opna_timer_reset(&g.opna_timer, &g.opna); -  ppz8_init(&g.ppz8, SRATE, PPZ8MIX); -  ZeroMemory(&g.work, sizeof(g.work)); -  g.work.opna_writereg = opna_writereg_libopna; -  g.work.opna_readreg = opna_readreg_libopna; -  g.work.opna_status = opna_status_libopna; -  g.work.opna = &g.opna_timer; -  g.work.ppz8 = &g.ppz8; -  g.work.ppz8_functbl = &ppz8_functbl;    WideCharToMultiByte(932, WC_NO_BEST_FIT_CHARS, path, -1, g.work.filename, sizeof(g.work.filename), 0, 0); -  opna_timer_set_int_callback(&g.opna_timer, opna_int_cb, &g.work); -  opna_timer_set_mix_callback(&g.opna_timer, opna_mix_cb, &g.ppz8); -  fmplayer_file_load(&g.work, g.fmfile); +  fmplayer_file_load(&g.work, g.fmfile, 1);    if (!g.sound) {      g.sound = sound_init(hwnd, SRATE, SECTLEN,                           sound_cb, &g.opna_timer); -    SetWindowText(g.driverinfo, g.sound->apiname);      about_setsoundapiname(g.sound->apiname);    }    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram); @@ -516,14 +446,14 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {      50, 25,      hwnd, (HMENU)ID_PAUSE, g.hinst, 0    ); -  g.driverinfo = CreateWindowEx( +  HWND wavesavebutton = CreateWindowEx(      0, -    L"STATIC", -    L"", -    WS_VISIBLE | WS_CHILD, -    110, 15, +    L"BUTTON", +    L"&Wave output", +    WS_TABSTOP | WS_VISIBLE | WS_CHILD, +    110, 10,      100, 25, -    hwnd, 0, g.hinst, 0 +    hwnd, (HMENU)ID_WAVESAVE, g.hinst, 0    );    g.button_2x = CreateWindowEx(      0, @@ -564,6 +494,7 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {    g.btn_defproc = (WNDPROC)GetWindowLongPtr(button, GWLP_WNDPROC);    SetWindowLongPtr(button, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(pbutton, GWLP_WNDPROC, (intptr_t)btn_wndproc); +  SetWindowLongPtr(wavesavebutton, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(g.button_2x, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(g.button_toneview, GWLP_WNDPROC, (intptr_t)btn_wndproc);    SetWindowLongPtr(g.button_oscilloview, GWLP_WNDPROC, (intptr_t)btn_wndproc); @@ -574,12 +505,11 @@ static bool on_create(HWND hwnd, CREATESTRUCT *cs) {    HFONT font = CreateFontIndirect(&ncm.lfMessageFont);    SetWindowFont(button, font, TRUE);    SetWindowFont(pbutton, font, TRUE); -  SetWindowFont(g.driverinfo, font, TRUE); +  SetWindowFont(wavesavebutton, font, TRUE);    SetWindowFont(g.button_2x, font, TRUE);    SetWindowFont(g.button_toneview, font, TRUE);    SetWindowFont(g.button_oscilloview, font, TRUE);    SetWindowFont(g.button_about, font, TRUE); -  loadrom();    loadfont();    fmdsp_init(&g.fmdsp, g.font_loaded ? &g.font : 0);    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram); @@ -658,6 +588,17 @@ static void on_command(HWND hwnd, int id, HWND hwnd_c, UINT code) {        Button_SetCheck(g.button_about, false);      }      break; +  case ID_WAVESAVE: +    { +      if (g.lastopenpath) { +        wchar_t *path = wcsdup(g.lastopenpath); +        if (path) { +          wavesave_dialog(hwnd, path); +          free(path); +        } +      } +    } +    break;    }  } @@ -666,7 +607,6 @@ static void on_destroy(HWND hwnd) {    timeKillEvent(g.mmtimer);    if (g.sound) g.sound->free(g.sound);    fmplayer_file_free(g.fmfile); -  if (g.drum_rom) HeapFree(g.heap, 0, g.drum_rom);    PostQuitMessage(0);  } diff --git a/win32/wavesave.c b/win32/wavesave.c new file mode 100644 index 0000000..cb12f2a --- /dev/null +++ b/win32/wavesave.c @@ -0,0 +1,210 @@ +#include "wavesave.h" +#include <commdlg.h> +#include <stdint.h> +#include "common/fmplayer_file.h" +#include "common/fmplayer_common.h" +#include "libopna/opnatimer.h" +#include "libopna/opna.h" +#include "wavewrite.h" +#include <stdlib.h> +#include <windowsx.h> +#include <commctrl.h> + +enum { +  SRATE = 55467, +  LOOPCNT = 2, +}; + +static struct { +  ATOM class; +} g; + +struct fadeout { +  struct opna_timer *timer; +  struct fmdriver_work *work; +  uint64_t vol; +  uint8_t loopcnt; +}; + +static bool fadeout_mix( +  struct fadeout *fadeout, +  int16_t *buf, unsigned frames +) { +  opna_timer_mix(fadeout->timer, buf, frames); +  for (unsigned i = 0; i < frames; i++) { +    int vol = fadeout->vol >> 16; +    buf[i*2+0] = (buf[i*2+0] * vol) >> 16; +    buf[i*2+1] = (buf[i*2+1] * vol) >> 16; +    if (fadeout->work->loop_cnt >= fadeout->loopcnt) { +      fadeout->vol = (fadeout->vol * 0xffff0000ull) >> 32; +    } +  } +  return fadeout->vol; +} + +struct wavesave_instance { +  struct opna opna; +  struct opna_timer timer; +  struct ppz8 ppz8; +  struct fmdriver_work work; +  struct fadeout fadeout; +  struct fmplayer_file *fmfile; +  struct wavefile *wavefile; +  HWND wnd; +  HWND pbar; +  int ppos; +  HANDLE thread; +  DWORD th_exit; +  uint8_t adpcm_ram[OPNA_ADPCM_RAM_SIZE]; +}; + +static DWORD CALLBACK thread_write(void *ptr) { +  struct wavesave_instance *inst = ptr; +  enum { +    BUFLEN = 1024, +  }; +  int16_t buf[BUFLEN*2]; +  for (;;) { +    if (inst->th_exit) return 0; +    memset(buf, 0, sizeof(buf)); +    bool end = !fadeout_mix(&inst->fadeout, buf, BUFLEN); +    if (wavewrite_write(inst->wavefile, buf, BUFLEN) != BUFLEN) { +      break; +    } +    int newpos = 100 * inst->work.timerb_cnt / inst->work.loop_timerb_cnt; +    if (newpos != inst->ppos) { +      inst->ppos = newpos; +      PostMessage(inst->pbar, PBM_SETPOS, newpos, 0); +    } +    /* +    double newfrac = (double)data->work.timerb_cnt / data->work.loop_timerb_cnt; +    if ((newfrac - data->fraction) > 0.005) { +      data->fraction = newfrac; +      g_idle_add(idle_progress_fraction, ptr); +    } +    */ +    if (end) break; +  } +  PostMessage(inst->wnd, WM_USER, 0, 0); +  return 0; +} + +static void wavesave(HWND parent, +                     struct fmplayer_file *fmfile, +                     const wchar_t *savepath) { +  struct wavefile *wavefile = 0; +  struct wavesave_instance *inst = 0; +  wavefile = wavewrite_open_w(savepath, SRATE); +  if (!wavefile) { +    MessageBox(parent, L"Cannot open output wave file", L"Error", MB_ICONSTOP); +    goto err; +  } +  inst = malloc(sizeof(*inst)); +  if (!inst) { +    MessageBox(parent, L"Cannot allocate memory", L"Error", MB_ICONSTOP); +    goto err; +  } +  *inst = (struct wavesave_instance){0}; +  fmplayer_init_work_opna(&inst->work, &inst->ppz8, &inst->opna, &inst->timer, inst->adpcm_ram); +  fmplayer_file_load(&inst->work, fmfile, LOOPCNT); +  inst->fadeout.timer = &inst->timer; +  inst->fadeout.work = &inst->work; +  inst->fadeout.vol = 1ull<<32; +  inst->fadeout.loopcnt = LOOPCNT; +  inst->fmfile = fmfile; +  inst->wavefile = wavefile; +  CreateWindow(MAKEINTATOM(g.class), +               L"Progress", +               WS_CAPTION | WS_SYSMENU, +               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, +               parent, 0, GetModuleHandle(0), inst); +  return; +err: +  if (wavefile) wavewrite_close(wavefile); +  free(inst); +} + +static bool on_create(HWND hwnd, const CREATESTRUCT *cs) { +  struct wavesave_instance *inst = cs->lpCreateParams; +  SetWindowLongPtr(hwnd, GWLP_USERDATA, (intptr_t)inst); +  inst->wnd = hwnd; +  RECT wr = { +    .left = 0, +    .top = 0, +    .right = 200, +    .bottom = 100, +  }; +  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); + +  inst->pbar = CreateWindow(PROGRESS_CLASS, 0, +                            WS_CHILD | WS_VISIBLE, +                            10, 10, 180, 15, +                            hwnd, 0, cs->hInstance, 0); + +  ShowWindow(hwnd, SW_SHOW); +  inst->thread = CreateThread(0, 0, thread_write, inst, 0, 0); +  return true; +} + +static void on_destroy(HWND hwnd) { +  struct wavesave_instance *inst = (struct wavesave_instance *)GetWindowLongPtr(hwnd, GWLP_USERDATA); +  inst->th_exit = 1; +  WaitForSingleObject(inst->thread, INFINITE); +  fmplayer_file_free(inst->fmfile); +  wavewrite_close(inst->wavefile); +  free(inst); +} + +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); +  case WM_USER: +    DestroyWindow(hwnd); +    return 0; +  } +  return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void wavesave_dialog(HWND parent, const wchar_t *fmpath) { +  HINSTANCE hinst = GetModuleHandle(0); +  if (!g.class) { +    WNDCLASS wc = { +      .lpfnWndProc = wndproc, +      .hInstance = hinst, +      .hIcon = LoadIcon(hinst, MAKEINTRESOURCE(1)), +      .hCursor = LoadCursor(0, IDC_ARROW), +      .hbrBackground = (HBRUSH)(COLOR_BTNFACE+1), +      .lpszClassName = L"myon_fmplayer_ym2608_wavesave_progress", +    }; +    g.class = RegisterClass(&wc); +    if (!g.class) { +      MessageBox(parent, L"Cannot register wavesave window class", L"Error", MB_ICONSTOP); +      return; +    } +  } +  wchar_t path[MAX_PATH] = {0}; + +  OPENFILENAME ofn = { +    .lStructSize = sizeof(ofn), +    .hwndOwner = parent, +    .lpstrFilter = L"RIFF WAVE (*.wav)\0" +                    "*.wav\0\0", +    .lpstrFile = path, +    .nMaxFile = sizeof(path)/sizeof(path[0]), +    .Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY, +  }; +  if (!GetSaveFileName(&ofn)) return; +  struct fmplayer_file *fmfile = fmplayer_file_alloc(fmpath, 0); +  if (!fmfile) { +    MessageBox(parent, L"Cannot open file", L"Error", MB_ICONSTOP); +    return; +  } +  wavesave(parent, fmfile, path); +} diff --git a/win32/wavesave.h b/win32/wavesave.h new file mode 100644 index 0000000..8769c14 --- /dev/null +++ b/win32/wavesave.h @@ -0,0 +1,9 @@ +#ifndef MYON_FMPLAYER_WIN32_WAVESAVE_H_INCLUDED +#define MYON_FMPLAYER_WIN32_WAVESAVE_H_INCLUDED + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +void wavesave_dialog(HWND parent, const wchar_t *fmpath); + +#endif // MYON_FMPLAYER_WIN32_WAVESAVE_H_INCLUDED diff --git a/win32/wavewrite.c b/win32/wavewrite.c new file mode 100644 index 0000000..7c2d6da --- /dev/null +++ b/win32/wavewrite.c @@ -0,0 +1,90 @@ +#include "wavewrite.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdlib.h> + +struct wavefile { +  HANDLE file; +  uint32_t written_frames; +}; + +static void write16le(uint8_t *ptr, uint16_t data) { +  ptr[0] = data; +  ptr[1] = data >> 8; +} + +static void write32le(uint8_t *ptr, uint32_t data) { +  ptr[0] = data; +  ptr[1] = data >> 8; +  ptr[2] = data >> 16; +  ptr[3] = data >> 24; +} + +struct wavefile *wavewrite_open_w(const wchar_t *path, uint32_t samplerate) { +  struct wavefile *wavefile = 0; +  wavefile = malloc(sizeof(*wavefile)); +  if (!wavefile) goto err; +  *wavefile = (struct wavefile){ +    .file = INVALID_HANDLE_VALUE +  }; +  wavefile->file = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); +  if (wavefile->file == INVALID_HANDLE_VALUE) goto err; +  uint8_t waveheader[44] = {0}; +  memcpy(waveheader, "RIFF", 4); +  memcpy(waveheader+8, "WAVE", 4); +  memcpy(waveheader+12, "fmt ", 4); +  write32le(waveheader+16, 16); +  write16le(waveheader+20, 1); +  write16le(waveheader+22, 2); +  write32le(waveheader+24, samplerate); +  write32le(waveheader+28, samplerate * 2 * 2); +  write16le(waveheader+32, 4); +  write16le(waveheader+34, 16); +  memcpy(waveheader+36, "data", 4); +  DWORD written; +  if (!WriteFile(wavefile->file, waveheader, sizeof(waveheader), &written, 0) || (written != sizeof(waveheader))) { +    goto err; +  } +  return wavefile; +err: +  if (wavefile) { +    if (wavefile->file != INVALID_HANDLE_VALUE) CloseHandle(wavefile->file); +    free(wavefile); +  } +  return 0; +} + +size_t wavewrite_write(struct wavefile *wavefile, const int16_t *buf, size_t frames) { +  if (frames >= (1ull<<(32-2))) return 0; +  DWORD written; +  if (!WriteFile(wavefile->file, buf, frames*4, &written, 0)) { +    return 0; +  } +  uint32_t written_frames = written / 4u; +  wavefile->written_frames += written_frames; +  return written_frames; +} + +void wavewrite_close(struct wavefile *wavefile) { +  LONG fp; +  uint32_t size; +  DWORD written; +  if ((SetFilePointer(wavefile->file, 40, &fp, FILE_BEGIN) == INVALID_SET_FILE_POINTER) || (fp != 40)) { +    goto cleanup; +  } +  size = wavefile->written_frames * 4; +  if (!WriteFile(wavefile->file, &size, sizeof(size), &written, 0) || (written != sizeof(size))) { +    goto cleanup; +  } +  if ((SetFilePointer(wavefile->file, 4, &fp, FILE_BEGIN) == INVALID_SET_FILE_POINTER) || (fp != 4)) { +    goto cleanup; +  } +  size += 4 + 8 + 16 + 8; +  if (!WriteFile(wavefile->file, &size, sizeof(size), &written, 0) || (written != sizeof(size))) { +    goto cleanup; +  } +cleanup: +  CloseHandle(wavefile->file); +  free(wavefile); +} diff --git a/win32/wavewrite.h b/win32/wavewrite.h new file mode 100644 index 0000000..58db0aa --- /dev/null +++ b/win32/wavewrite.h @@ -0,0 +1,15 @@ +#ifndef MYON_FMPLAYER_WIN32_WAVEWRITE_H_INCLUDED +#define MYON_FMPLAYER_WIN32_WAVEWRITE_H_INCLUDED + +#include <stdint.h> +#include <stddef.h> + +struct wavefile; + +struct wavefile *wavewrite_open_w(const wchar_t *path, uint32_t samplerate); + +size_t wavewrite_write(struct wavefile *wavefile, const int16_t *buf, size_t frames); + +void wavewrite_close(struct wavefile *wavefile); + +#endif // MYON_FMPLAYER_WIN32_WAVEWRITE_H_INCLUDED | 
