aboutsummaryrefslogtreecommitdiff
path: root/win32/wavesave.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/wavesave.c')
-rw-r--r--win32/wavesave.c210
1 files changed, 210 insertions, 0 deletions
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);
+}