diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-01-07 13:39:17 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-01-07 13:39:17 +0900 | 
| commit | 09ea1fe272aca6ebb6840f02765acd44ac3ecebc (patch) | |
| tree | 9221a90ae3b6d35394554d5ecae1a1c14cda4ca6 /win32 | |
| parent | e8e394a10937bec18106d2f8c1c0a045c62f458f (diff) | |
win32
Diffstat (limited to 'win32')
| -rw-r--r-- | win32/amd64/Makefile | 42 | ||||
| -rw-r--r-- | win32/dsoundout.c | 180 | ||||
| -rw-r--r-- | win32/dsoundout.h | 13 | ||||
| -rw-r--r-- | win32/fmplayer.ico | bin | 0 -> 318 bytes | |||
| -rw-r--r-- | win32/lnf.manifest | 10 | ||||
| -rw-r--r-- | win32/lnf.rc | 5 | ||||
| -rw-r--r-- | win32/main.c | 484 | ||||
| -rw-r--r-- | win32/soundout.c | 44 | ||||
| -rw-r--r-- | win32/soundout.h | 18 | ||||
| -rw-r--r-- | win32/uc.c | 36 | ||||
| -rw-r--r-- | win32/waveout.c | 100 | ||||
| -rw-r--r-- | win32/waveout.h | 13 | ||||
| -rw-r--r-- | win32/x86/Makefile | 43 | 
13 files changed, 988 insertions, 0 deletions
| diff --git a/win32/amd64/Makefile b/win32/amd64/Makefile new file mode 100644 index 0000000..5c959c5 --- /dev/null +++ b/win32/amd64/Makefile @@ -0,0 +1,42 @@ +vpath %.c ../ +vpath %.c ../../fmdriver +vpath %.c ../../libopna +vpath %.c ../../fmdsp +vpath %.rc .. +TARGET=fmplayer.exe +FMDRIVER_OBJS=fmdriver_fmp.o \ +              ppz8.o +LIBOPNA_OBJS=opna.o \ +             opnatimer.o \ +             opnafm.o \ +             opnassg.o \ +             opnadrum.o \ +             opnaadpcm.o +FMDSP_OBJS=fmdsp.o +OBJS=main.o dsoundout.o soundout.o waveout.o uc.o lnf.o \ +     $(FMDRIVER_OBJS) \ +     $(LIBOPNA_OBJS) \ +     $(FMDSP_OBJS) +ARCH=x86_64 +PREFIX=$(ARCH)-w64-mingw32- +CC=$(PREFIX)gcc +WINDRES=$(PREFIX)windres +STRIP=$(PREFIX)strip +CFLAGS=-std=c99 -Os -Wall -Wextra -pedantic -I../.. \ +       -DUNICODE -D_UNICODE \ +       -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 +LIBS=-nostdlib -s -Wl,-eentry \ +     -Wl,--subsystem,windows \ +     -lgcc -lntdll \ +     -luser32 -lkernel32 -lole32 -ldxguid -luuid -lcomdlg32 \ +     -lgdi32 -lshlwapi -lwinmm -lshell32 + +$(TARGET):	$(OBJS) +	$(CC) -o $@ $(OBJS) $(LIBS) +	$(STRIP) $@ + +%.o:	%.rc +	$(WINDRES) -o $@ -i $< + +clean: +	rm -f $(TARGET) $(OBJS) diff --git a/win32/dsoundout.c b/win32/dsoundout.c new file mode 100644 index 0000000..6fda977 --- /dev/null +++ b/win32/dsoundout.c @@ -0,0 +1,180 @@ +#include "dsoundout.h" +#include <windows.h> +#include <dsound.h> + +struct dsound_state { +  IDirectSound8 *ds; +  DWORD sectlen; +  IDirectSoundBuffer *dsbuf; +  HANDLE e_posnotf; +  HANDLE t_update; +  DWORD terminate; +  DWORD currsect; +  DWORD playing; +  HANDLE mtx_cbproc; +  sound_callback cbfunc; +  void *userptr; +}; + +static DWORD WINAPI bufupdatethread(LPVOID p) { +  DWORD playcur, writecur; +  struct dsound_state *dsound = (struct dsound_state *)p; +  while (1) { +    WaitForSingleObject(dsound->e_posnotf, INFINITE); +    if (dsound->terminate) ExitThread(0); +    dsound->dsbuf->lpVtbl->GetCurrentPosition(dsound->dsbuf, &playcur, &writecur); +    DWORD playsect = playcur / dsound->sectlen; +    if (playsect < dsound->currsect) { +      LPVOID ptr; +      DWORD len; +      DWORD startptr = dsound->currsect * dsound->sectlen; +      DWORD writelen = (dsound->sectlen * (4 - dsound->currsect)); +      dsound->dsbuf->lpVtbl->Lock(dsound->dsbuf, startptr, writelen, &ptr, &len, NULL, NULL, 0); +      int16_t *bufptr = (int16_t *)ptr; + +      if (WaitForSingleObject(dsound->mtx_cbproc, 0) == WAIT_OBJECT_0) { +        if (dsound->playing) { +          (*dsound->cbfunc)(dsound->userptr, bufptr, writelen/4); +        } else { +          ZeroMemory(bufptr, writelen); +        } +        ReleaseMutex(dsound->mtx_cbproc); +      } else { +        ZeroMemory(bufptr, writelen); +      } + +      dsound->dsbuf->lpVtbl->Unlock(dsound->dsbuf, ptr, writelen, NULL, 0); +      dsound->currsect = 0; +    } else if (playsect == dsound->currsect) continue; +    LPVOID ptr; +    DWORD len; +    DWORD startptr = dsound->currsect * dsound->sectlen; +    uint32_t writelen = (dsound->sectlen * (playsect - dsound->currsect)); +    dsound->dsbuf->lpVtbl->Lock(dsound->dsbuf, startptr, writelen, &ptr, &len, NULL, NULL, 0); +    int16_t *bufptr = (int16_t *)ptr; + +    if (WaitForSingleObject(dsound->mtx_cbproc, 0) == WAIT_OBJECT_0) { +      if (dsound->playing) { +        (*dsound->cbfunc)(dsound->userptr, bufptr, writelen/4); +      } else { +        ZeroMemory(bufptr, writelen); +      } +      ReleaseMutex(dsound->mtx_cbproc); +    } else { +      ZeroMemory(bufptr, writelen); +    } + +    dsound->dsbuf->lpVtbl->Unlock(dsound->dsbuf, ptr, writelen, NULL, 0); +    dsound->currsect = playsect; +  } +} + +struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, +                                 sound_callback cbfunc, void *userptr) { +  HANDLE heap = GetProcessHeap(); +  struct dsound_state *dsound = HeapAlloc(heap, HEAP_ZERO_MEMORY, +                                          sizeof(struct dsound_state)); +  if (!dsound) { +    MessageBoxW(hwnd, L"cannot allocate memory for DirectSound", L"Error", MB_ICONSTOP); +    goto err; +  } +  CoInitializeEx(0, COINIT_MULTITHREADED); +  HRESULT hr; +  hr = CoCreateInstance( +    &CLSID_DirectSound8, +    0, +    CLSCTX_INPROC_SERVER, +    &IID_IDirectSound8, +    (void **)&dsound->ds); +  if (hr != S_OK) { +    MessageBoxW(hwnd, L"cannot create instance of DirectSound8", L"Error", MB_ICONSTOP); +    goto err_dsound; +  } +  hr = dsound->ds->lpVtbl->Initialize(dsound->ds, 0); +  if (hr != S_OK) { +    MessageBoxW(hwnd, L"cannot initialize DirectSound8", L"Error", MB_ICONSTOP); +    goto err_dsound; +  } +  dsound->ds->lpVtbl->SetCooperativeLevel(dsound->ds, hwnd, DSSCL_NORMAL); +   +  DSBUFFERDESC bufdesc = {0}; +  bufdesc.dwSize = sizeof(bufdesc); +  bufdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | +      DSBCAPS_GLOBALFOCUS | +      DSBCAPS_CTRLPOSITIONNOTIFY; +  bufdesc.dwBufferBytes = sectlen * 4; +  WAVEFORMATEX format; +  format.wFormatTag = WAVE_FORMAT_PCM; +  format.nChannels = 2; +  format.nSamplesPerSec = srate; +  format.wBitsPerSample = 16; +  format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; +  format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; +  format.cbSize = 0; +  bufdesc.lpwfxFormat = &format; +  bufdesc.guid3DAlgorithm = DS3DALG_DEFAULT; +  bufdesc.dwReserved = 0; + +  if (dsound->ds->lpVtbl->CreateSoundBuffer(dsound->ds, &bufdesc, &dsound->dsbuf, 0) != S_OK) { +    MessageBoxW(hwnd, L"cannot initialize DirectSoundBuffer", L"Error", MB_ICONSTOP); +    goto err_ds; +  } + +  IDirectSoundNotify *dsnotf; +  if (dsound->dsbuf->lpVtbl->QueryInterface(dsound->dsbuf, +                                          &IID_IDirectSoundNotify, +                                          (void **)&dsnotf) != S_OK) { +    goto err_dsbuf; +  } + +  dsound->e_posnotf = CreateEventW(NULL, FALSE, FALSE, L"SNDNOTF"); +  dsound->t_update = CreateThread(NULL, 0, bufupdatethread, dsound, 0, NULL); +  dsound->sectlen = sectlen; +  dsound->playing = 0; +  dsound->terminate = 0; +  dsound->cbfunc = cbfunc; +  dsound->userptr = userptr; +  dsound->mtx_cbproc = CreateMutexW(0, FALSE, 0); +   +   +  DSBPOSITIONNOTIFY posnotf[4] = { +    {(DWORD)dsound->sectlen*0, dsound->e_posnotf}, +    {(DWORD)dsound->sectlen*1, dsound->e_posnotf}, +    {(DWORD)dsound->sectlen*2, dsound->e_posnotf}, +    {(DWORD)dsound->sectlen*3, dsound->e_posnotf}, +  }; +  dsnotf->lpVtbl->SetNotificationPositions(dsnotf, 4, posnotf); +  dsnotf->lpVtbl->Release(dsnotf); +  return dsound; +err_dsbuf: +  dsound->dsbuf->lpVtbl->Release(dsound->dsbuf); +err_ds: +  dsound->ds->lpVtbl->Release(dsound->ds); +err_dsound: +  HeapFree(heap, 0, dsound); +err: +  return 0; +} + +void dsound_pause(struct dsound_state *dsound, int pause) { +  if (pause) { +    dsound->playing = 0; +    WaitForSingleObject(dsound->mtx_cbproc, INFINITE); +    ReleaseMutex(dsound->mtx_cbproc); +  } else { +    dsound->playing = 1; +    dsound->dsbuf->lpVtbl->Play(dsound->dsbuf, 0, 0, DSBPLAY_LOOPING); +  } +} + +void dsound_delete(struct dsound_state *dsound) { +  if (!dsound) return; +  dsound_pause(dsound, 1); +  dsound->terminate = 1; +  SetEvent(dsound->e_posnotf); +  WaitForSingleObject(dsound->t_update, INFINITE); +  CloseHandle(dsound->mtx_cbproc); +  dsound->dsbuf->lpVtbl->Release(dsound->dsbuf); +  dsound->ds->lpVtbl->Release(dsound->ds); +  HeapFree(GetProcessHeap(), 0, dsound); +} diff --git a/win32/dsoundout.h b/win32/dsoundout.h new file mode 100644 index 0000000..10482e6 --- /dev/null +++ b/win32/dsoundout.h @@ -0,0 +1,13 @@ +#ifndef MYON_DSOUNDOUT_H_INCLUDED +#define MYON_DSOUNDOUT_H_INCLUDED + +#include "soundout.h" + +struct dsound_state; + +struct dsound_state *dsound_init(HWND hwnd, unsigned srate, unsigned sectlen, +                                 sound_callback cbfunc, void *userptr); +void dsound_delete(struct dsound_state *dsound); +void dsound_pause(struct dsound_state *dsound, int pause); + +#endif // MYON_DSOUNDOUT_H_INCLUDED diff --git a/win32/fmplayer.ico b/win32/fmplayer.icoBinary files differ new file mode 100644 index 0000000..801126c --- /dev/null +++ b/win32/fmplayer.ico diff --git a/win32/lnf.manifest b/win32/lnf.manifest new file mode 100644 index 0000000..ef951b7 --- /dev/null +++ b/win32/lnf.manifest @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +  <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="CompanyName.ProductName.YourApplication" type="win32" /> +  <description>Your application description here.</description> +  <dependency> +    <dependentAssembly> +       <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" /> +     </dependentAssembly> +  </dependency> +</assembly> diff --git a/win32/lnf.rc b/win32/lnf.rc new file mode 100644 index 0000000..500cb8c --- /dev/null +++ b/win32/lnf.rc @@ -0,0 +1,5 @@ +#include <winuser.h> + +1 ICON "fmplayer.ico" +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "lnf.manifest" + diff --git a/win32/main.c b/win32/main.c new file mode 100644 index 0000000..dd60702 --- /dev/null +++ b/win32/main.c @@ -0,0 +1,484 @@ +#include <stddef.h> +#include <stdbool.h> +#include <windows.h> +#include <shlwapi.h> +#include <windowsx.h> +#include <commctrl.h> + +#include "fmdriver/fmdriver_fmp.h" +#include "libopna/opna.h" +#include "libopna/opnatimer.h" +#include "fmdsp/fmdsp.h" +#include "soundout.h" + +enum { +  ID_OPENFILE = 0x10, +  TIMER_FMDSP = 1, +}; + +enum { +  SRATE = 55467, +  SECTLEN = 4096, +  PPZ8MIX = 0xa000, +  FONT_ROM_SIZE = 0x84000, +  FONT_ROM_FILESIZE = 0x46800, +}; + +#define ENABLE_WM_DROPFILES +// #define ENABLE_IDROPTARGET + +static struct { +  HINSTANCE hinst; +  HANDLE heap; +  struct sound_state *sound; +  struct opna opna; +  struct opna_timer opna_timer; +  struct ppz8 ppz8; +  struct fmdriver_work work; +  struct driver_fmp *fmp; +  struct fmdsp fmdsp; +  uint8_t vram[PC98_W*PC98_H]; +  uint8_t font[FONT_ROM_SIZE]; +  void *drum_rom; +  uint8_t opna_adpcm_ram[OPNA_ADPCM_RAM_SIZE]; +  void *ppz8_buf; +} g; + + +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 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); +  opna_timer_mix(timer, buf, frames); +} + +static void on_timer(HWND hwnd, UINT id) { +  if (id == TIMER_FMDSP) { +    InvalidateRect(hwnd, 0, FALSE); +  } +} + +static HANDLE pvisearch(const wchar_t *filename, const char *pviname_a) { +  enum { +    WPVINAMELEN = 8*2+1+3+1, +  }; +  wchar_t pviname[WPVINAMELEN]; +  wchar_t pvipath[PATH_MAX]; +  if (MultiByteToWideChar(932, MB_ERR_INVALID_CHARS, +                          pviname_a, -1, pviname, WPVINAMELEN) == 0) { +    return INVALID_HANDLE_VALUE; +  } +  lstrcat(pviname, L".PVI"); +  if (lstrlen(filename) >= PATH_MAX) return INVALID_HANDLE_VALUE; +  lstrcpy(pvipath, filename); +  PathRemoveFileSpec(pvipath); +  if (lstrlen(pvipath) + lstrlen(pviname) + 1 >= PATH_MAX) { +    return INVALID_HANDLE_VALUE; +  } +  lstrcat(pvipath, L"\\"); +  lstrcat(pvipath, pviname); +  return CreateFile(pvipath, GENERIC_READ, +                            0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); +} + +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; +  return; +err_buf: +  HeapFree(g.heap, 0, buf); +err_file: +  CloseHandle(file); +err: +  return; +} + +static bool loadpvi(struct fmdriver_work *work, +                    struct driver_fmp *fmp, +                    const wchar_t *filename) { +  if (!fmp->pvi_name[0]) return true; +  HANDLE pvifile = pvisearch(filename, fmp->pvi_name); +  if (pvifile == INVALID_HANDLE_VALUE) goto err; +  DWORD filesize = GetFileSize(pvifile, 0); +  if (filesize == INVALID_FILE_SIZE) goto err_file; +  void *data = HeapAlloc(g.heap, 0, filesize); +  if (!data) goto err_file; +  DWORD readbytes; +  if (!ReadFile(pvifile, data, filesize, &readbytes, 0) +      || readbytes != filesize) goto err_data; +  if (!fmp_adpcm_load(work, data, filesize)) goto err_data; +  HeapFree(g.heap, 0, data); +  CloseHandle(pvifile); +  return true; +err_data: +  HeapFree(g.heap, 0, data); +err_file: +  CloseHandle(pvifile); +err: +  return false; +} + +static bool loadppzpvi(struct fmdriver_work *work, +                    struct driver_fmp *fmp, +                    const wchar_t *filename) { +  if (!fmp->ppz_name[0]) return true; +  HANDLE pvifile = pvisearch(filename, fmp->ppz_name); +  if (pvifile == INVALID_HANDLE_VALUE) goto err; +  DWORD filesize = GetFileSize(pvifile, 0); +  if (filesize == INVALID_FILE_SIZE) goto err_file; +  void *data = HeapAlloc(g.heap, 0, filesize); +  if (!data) goto err_file; +  void *buf = HeapAlloc(g.heap, 0, ppz8_pvi_decodebuf_samples(filesize) * sizeof(int16_t)); +  if (!buf) goto err_data; +  DWORD readbytes; +  if (!ReadFile(pvifile, data, filesize, &readbytes, 0) +      || readbytes != filesize) goto err_buf; +  if (!ppz8_pvi_load(work->ppz8, 0, data, filesize, buf)) goto err_buf; +  if (g.ppz8_buf) HeapFree(g.heap, 0, g.ppz8_buf); +  g.ppz8_buf = buf; +  HeapFree(g.heap, 0, data); +  CloseHandle(pvifile); +  return true; +err_buf: +  HeapFree(g.heap, 0, buf); +err_data: +  HeapFree(g.heap, 0, data); +err_file: +  CloseHandle(pvifile); +err: +  return false; +} + +static void openfile(HWND hwnd, const wchar_t *path) { +  HANDLE file = CreateFile(path, GENERIC_READ, +                            0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); +  if (file == INVALID_HANDLE_VALUE) { +    MessageBox(hwnd, L"Cannot open file", L"Error", MB_ICONSTOP); +    return; +  } +  LARGE_INTEGER li; +  if (!GetFileSizeEx(file, &li)) { +    MessageBox(hwnd, L"Cannot open file", L"Error", MB_ICONSTOP); +    goto err_file; +  } +  if (li.QuadPart > 0xffff) { +    MessageBox(hwnd, L"Invalid File (Filesize too large)", L"Error", MB_ICONSTOP); +    goto err_file; +  } +  void *fmpdata = HeapAlloc(g.heap, 0, li.QuadPart); +  if (!fmpdata) { +    MessageBox(hwnd, L"Cannot allocate memory for file", L"Error", MB_ICONSTOP); +    goto err_file; +  } +  DWORD readbytes; +  if (!ReadFile(file, fmpdata, li.QuadPart, &readbytes, 0) || readbytes != li.QuadPart) { +    MessageBox(hwnd, L"Cannot read file", L"Error", MB_ICONSTOP); +    goto err_fmpdata; +  } +  struct driver_fmp *fmp = HeapAlloc(g.heap, HEAP_ZERO_MEMORY, sizeof(struct driver_fmp)); +  if (!fmp) { +    MessageBox(hwnd, L"Cannot allocate memory for fmp", L"Error", MB_ICONSTOP); +    goto err_fmpdata; +  } +  if (!fmp_load(fmp, fmpdata, li.QuadPart)) { +    MessageBox(hwnd, L"Invalid File (not FMP data)", L"Error", MB_ICONSTOP); +    goto err_fmp; +  } +  if (g.sound) { +    g.sound->pause(g.sound, 1); +  } +  if (g.fmp) HeapFree(g.heap, 0, g.fmp); +  g.fmp = fmp; +  opna_reset(&g.opna); +  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_status = opna_status_libopna; +  g.work.opna = &g.opna_timer; +  g.work.ppz8 = &g.ppz8; +  g.work.ppz8_functbl = &ppz8_functbl; +  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); +  fmp_init(&g.work, g.fmp); +  loadpvi(&g.work, g.fmp, path); +  loadppzpvi(&g.work, g.fmp, path); +  if (!g.sound) { +    g.sound = sound_init(hwnd, SRATE, SECTLEN, +                         sound_cb, &g.opna_timer); +  } +  fmdsp_vram_init(&g.fmdsp, &g.work, g.font, g.vram); +  if (!g.sound) goto err_fmp; +  g.sound->pause(g.sound, 0); +  CloseHandle(file); +  return; +err_fmp: +  HeapFree(g.heap, 0, fmp); +err_fmpdata: +  HeapFree(g.heap, 0, fmpdata); +err_file: +  CloseHandle(file); +} + +static void openfiledialog(HWND hwnd) { +  wchar_t path[MAX_PATH] = {0}; +  OPENFILENAME ofn = {0}; +  ofn.lStructSize = sizeof(ofn); +  ofn.hwndOwner = hwnd; +  ofn.hInstance = g.hinst; +  ofn.lpstrFilter = L"FMP files (*.opi;*.ovi;*.ozi;*.m26;*.m86)\0" +                     "*.opi;*.ovi;*.ozi;*.m26;*.m86\0" +                     "All Files (*.*)\0" +                     "*.*\0\0"; +  ofn.lpstrFile = path; +  ofn.nMaxFile = sizeof(path)/sizeof(path[0]); +  ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; +  if (!GetOpenFileName(&ofn)) return; +  openfile(hwnd, path); +} + +#ifdef ENABLE_IDROPTARGET +struct fmplayer_droptarget { +  IDropTarget idt; +  ULONG refcnt; +}; + +static HRESULT fmplayer_droptarget_addref( +  IDropTarget *ptr) { +  struct fmplayer_droptarget *this_ = (struct fmplayer_droptarget *)ptr; +  return ++this_->refcnt; +} + +static HRESULT fmplayer_droptarget_release( +  IDropTarget *ptr) { +  struct fmplayer_droptarget *this_ = (struct fmplayer_droptarget *)ptr; +  ULONG refcnt = --this_->refcnt; +  if (!refcnt) HeapFree(g.heap, 0, this_); +  return refcnt; +} + +static HRESULT fmplayer_droptarget_queryinterface( +  IDropTarget *ptr, GUID *iid, void **obj) { +  struct fmplayer_droptarget *this_ = (struct fmplayer_droptarget *)ptr; +  if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IDropTarget)) { +    *obj = ptr; +    fmplayer_droptarget_addref(ptr); +    return S_OK; +  } else { +    *obj = 0; +    return E_NOINTERFACE; +  } +} + +struct fmplayer_droptarget *fmplayer_droptarget(void) { +  struct fmplayer_droptarget *fdt = HeapAlloc(g.heap, HEAP_ZERO_MEMORY, sizeof(*fdt)); +  if (!fdt) return 0; +  static bool vtblinit = false; +  static IDropTargetVtbl vtbl; +  if (!vtblinit) { +    vtbl.QueryInterface = fmplayer_droptarget_queryinterface; +    vtbl.AddRef = fmplayer_droptarget_addref; +    vtbl.Release = fmplayer_droptarget_release; +    vtblinit = true; +  } +  fdt->idt.lpVtbl = &vtbl; +  fdt->refcnt = 1; +} +#endif // ENABLE_IDROPTARGET + +#ifdef ENABLE_WM_DROPFILES +static void on_dropfiles(HWND hwnd, HDROP hdrop) { +  wchar_t path[MAX_PATH] = {0}; +  if (DragQueryFile(hdrop, 0, path, sizeof(path)/sizeof(path[0]))) { +    openfile(hwnd, path); +  }   +  DragFinish(hdrop); +} +#endif // ENABLE_WM_DROPFILES + +static bool on_create(HWND hwnd, CREATESTRUCT *cs) { +  (void)cs; +  HWND button = CreateWindowEx( +    0, +    L"BUTTON", +    L"&Open", +    WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, +    10, 10, +    40, 25, +    hwnd, (HMENU)ID_OPENFILE, g.hinst, 0 +  ); +  NONCLIENTMETRICS ncm; +  ncm.cbSize = sizeof(ncm); +  SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); +  HFONT font = CreateFontIndirect(&ncm.lfMessageFont); +  SetWindowFont(button, font, TRUE); +  loadrom(); +  fmdsp_init(&g.fmdsp); +  fmdsp_vram_init(&g.fmdsp, &g.work, g.font, g.vram); +  SetTimer(hwnd, TIMER_FMDSP, 50, 0); +#ifdef ENABLE_WM_DROPFILES +  DragAcceptFiles(hwnd, TRUE); +#endif +  return true; +} + +static void on_command(HWND hwnd, int id, HWND hwnd_c, UINT code) { +  (void)code; +  (void)hwnd_c; +  switch (id) { +  case ID_OPENFILE: +    openfiledialog(hwnd); +    break; +  } +} + +static void on_destroy(HWND hwnd) { +  (void)hwnd; +  if (g.sound) g.sound->delete(g.sound); +  if (g.fmp) HeapFree(g.heap, 0, g.fmp); +  if (g.drum_rom) HeapFree(g.heap, 0, g.drum_rom); +  if (g.ppz8_buf) HeapFree(g.heap, 0, g.ppz8_buf); +  PostQuitMessage(0); +} + +static void on_paint(HWND hwnd) { +  fmdsp_update(&g.fmdsp, &g.work, g.vram); +  PAINTSTRUCT ps; +  static BITMAPINFO *bi = 0; +  if (!bi) { +    bi = HeapAlloc(g.heap, HEAP_ZERO_MEMORY, +              sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*FMDSP_PALETTE_COLORS); +    if (!bi) return; +    bi->bmiHeader.biSize = sizeof(bi->bmiHeader); +    bi->bmiHeader.biWidth = PC98_W; +    bi->bmiHeader.biHeight = -PC98_H; +    bi->bmiHeader.biPlanes = 1; +    bi->bmiHeader.biBitCount = 8; +    bi->bmiHeader.biCompression = BI_RGB; +    bi->bmiHeader.biClrUsed = FMDSP_PALETTE_COLORS; +  } +  for (int p = 0; p < FMDSP_PALETTE_COLORS; p++) { +    bi->bmiColors[p].rgbRed = g.fmdsp.palette[p*3+0]; +    bi->bmiColors[p].rgbGreen = g.fmdsp.palette[p*3+1]; +    bi->bmiColors[p].rgbBlue = g.fmdsp.palette[p*3+2]; +  } +  HDC dc = BeginPaint(hwnd, &ps); +  HDC mdc = CreateCompatibleDC(dc); +  HBITMAP bitmap = CreateDIBitmap( +    dc, +    &bi->bmiHeader, CBM_INIT, +    g.vram, +    bi, DIB_RGB_COLORS); +  SelectObject(mdc, bitmap); +  BitBlt(dc, 0, 80, 640, 400, mdc, 0, 0, SRCCOPY); +  DeleteDC(mdc); +  DeleteObject(bitmap); +  EndPaint(hwnd, &ps); +} + +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_PAINT, on_paint); +  HANDLE_MSG(hwnd, WM_TIMER, on_timer); +#ifdef ENABLE_WM_DROPFILES +  HANDLE_MSG(hwnd, WM_DROPFILES, on_dropfiles); +#endif // ENABLE_WM_DROPFILES +  } +  return DefWindowProc(hwnd, msg, wParam, lParam); +} + +static ATOM register_class(HINSTANCE hinst) { +  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"TWinc"; +  return RegisterClass(&wc); +} + +int CALLBACK wWinMain(HINSTANCE hinst, HINSTANCE hpinst, +                      wchar_t *cmdline, int cmdshow) { +  (void)hpinst; +  (void)cmdline; +  g.hinst = hinst; +  g.heap = GetProcessHeap(); +  ATOM wcatom = register_class(g.hinst); +  DWORD style = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; +  DWORD exStyle = 0; +  RECT wr; +  wr.left = 0; +  wr.right = 640; +  wr.top = 0; +  wr.bottom = 480; +  AdjustWindowRectEx(&wr, style, 0, exStyle); +  HWND hwnd = CreateWindowEx( +    exStyle, +    (wchar_t*)((uintptr_t)wcatom), L"FMPlayer/Win32", +    style, +    CW_USEDEFAULT, CW_USEDEFAULT, +    wr.right-wr.left, wr.bottom-wr.top, +    0, 0, g.hinst, 0 +  ); +  ShowWindow(hwnd, cmdshow); + +  MSG msg = {0}; +  while (GetMessage(&msg, 0, 0, 0)) { +    TranslateMessage(&msg); +    DispatchMessage(&msg); +  } +  return msg.wParam; +} + diff --git a/win32/soundout.c b/win32/soundout.c new file mode 100644 index 0000000..bb57bc1 --- /dev/null +++ b/win32/soundout.c @@ -0,0 +1,44 @@ +#include "soundout.h" +#include "dsoundout.h" +#include "waveout.h" + +static void soundout_dsound_pause(struct sound_state *state, int pause) { +  dsound_pause((struct dsound_state *)state->driver_state, pause); +} + +static void soundout_dsound_delete(struct sound_state *state) { +  dsound_delete((struct dsound_state *)state->driver_state); +  HeapFree(GetProcessHeap(), 0, state); +} + +static void soundout_waveout_pause(struct sound_state *state, int pause) { +  waveout_pause((struct waveout_state *)state->driver_state, pause); +} + +static void soundout_waveout_delete(struct sound_state *state) { +  waveout_delete((struct waveout_state *)state->driver_state); +  HeapFree(GetProcessHeap(), 0, state); +} + +struct sound_state *sound_init(HWND hwnd, unsigned srate, unsigned sectlen, +                               sound_callback cbfunc, void *userptr) { +  HANDLE heap = GetProcessHeap(); +  struct sound_state *sound = HeapAlloc(heap, 0, sizeof(struct sound_state)); +  if (!sound) return 0; +  struct dsound_state *dsound = dsound_init(hwnd, srate, sectlen, cbfunc, userptr); +  if (dsound) { +    sound->driver_state = dsound; +    sound->pause = soundout_dsound_pause; +    sound->delete = soundout_dsound_delete; +    return sound; +  } +  struct waveout_state *waveout = waveout_init(hwnd, srate, sectlen, cbfunc, userptr); +  if (waveout) { +    sound->driver_state = waveout; +    sound->pause = soundout_waveout_pause; +    sound->delete = soundout_waveout_delete; +    return sound; +  } +  HeapFree(heap, 0, sound); +  return 0; +} diff --git a/win32/soundout.h b/win32/soundout.h new file mode 100644 index 0000000..edfa501 --- /dev/null +++ b/win32/soundout.h @@ -0,0 +1,18 @@ +#ifndef MYON_SOUNDOUT_H_INCLUDED +#define MYON_SOUNDOUT_H_INCLUDED + +#include <stdint.h> +#include <windows.h> + +typedef void (*sound_callback)(void *userdata, int16_t *buf, unsigned frames); +struct sound_state { +  void *driver_state; +  void (*pause)(struct sound_state *state, int pause); +  void (*delete)(struct sound_state *state); +  void *userptr; +}; + +struct sound_state *sound_init(HWND hwnd, unsigned srate, unsigned sectlen, +                               sound_callback cbfunc, void *userptr); + +#endif // MYON_SOUNDOUT_H_INCLUDED diff --git a/win32/uc.c b/win32/uc.c new file mode 100644 index 0000000..70d2e85 --- /dev/null +++ b/win32/uc.c @@ -0,0 +1,36 @@ +#include <string.h> +#include <windows.h> + +int memcmp(const void *s1, const void *s2, size_t n) { +  size_t i = RtlCompareMemory(s1, s2, n); +  if (i == n) return 0; +  return ((const unsigned char *)s1)[i] - ((const unsigned char *)s2)[i]; +} + +void *memset(void *s, int c, size_t n) { +  RtlFillMemory(s, n, c); +  return s; +} + +void *memcpy(void *dest, const void *src, size_t n) { +  RtlCopyMemory(dest, src, n); +  return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { +  RtlMoveMemory(dest, src, n); +  return dest; +} + +int CALLBACK wWinMain(HINSTANCE hinst, HINSTANCE hpinst, +                      wchar_t *cmdline, int cmdshow); + +DWORD CALLBACK entry(void *ptr) { +  (void)ptr; +  STARTUPINFO si; +  GetStartupInfo(&si); +  int cmdshow = si.wShowWindow; +  if (si.dwFlags & STARTF_USESHOWWINDOW) cmdshow = SW_SHOWNORMAL; +  DWORD ret = wWinMain(GetModuleHandle(0), 0, 0, cmdshow); +  ExitProcess(ret); +} diff --git a/win32/waveout.c b/win32/waveout.c new file mode 100644 index 0000000..9c96483 --- /dev/null +++ b/win32/waveout.c @@ -0,0 +1,100 @@ +#include "waveout.h" + +struct waveout_state { +  HWAVEOUT wo; +  DWORD firstout; +  sound_callback cbfunc; +  void *userptr; +  WAVEHDR waveheaders[4]; +  int16_t *wavebuf; +}; + +static void CALLBACK waveout_cbproc(HWAVEOUT wo, UINT msg, +                                 DWORD_PTR instance, +                                 DWORD_PTR p1, DWORD_PTR p2) { +  (void)wo; +  (void)p2; +  if (msg != WOM_DONE) return; +  struct waveout_state *waveout = (struct waveout_state *)instance; +  WAVEHDR *wh = (WAVEHDR *)p1; +  waveout->cbfunc(waveout->userptr, (int16_t *)wh->lpData, wh->dwBufferLength/4); +  waveOutWrite(waveout->wo, wh, sizeof(*wh)); +} + +struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, +                                   sound_callback cbfunc, void *userptr) { +  HANDLE heap = GetProcessHeap(); +  struct waveout_state *waveout = HeapAlloc(heap, HEAP_ZERO_MEMORY, +                                            sizeof(struct waveout_state)); +  if (!waveout) { +    MessageBoxW(hwnd, L"cannot allocate memory for WaveOut", L"Error", MB_ICONSTOP); +    goto err; +  } +  waveout->wavebuf = HeapAlloc(heap, HEAP_ZERO_MEMORY, sectlen*4); +  if (!waveout->wavebuf) { +    MessageBoxW(hwnd, L"cannot allocate buffer memory for WaveOut", L"Error", MB_ICONSTOP); +    goto err_waveout; +  } +  WAVEFORMATEX format; +  format.wFormatTag = WAVE_FORMAT_PCM; +  format.nChannels = 2; +  format.nSamplesPerSec = srate; +  format.wBitsPerSample = 16; +  format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; +  format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; +  format.cbSize = 0; +  HRESULT hr; +  hr = waveOutOpen(&waveout->wo, WAVE_MAPPER, &format, +                   (DWORD_PTR)waveout_cbproc, (DWORD_PTR)waveout, CALLBACK_FUNCTION); +  if (hr != MMSYSERR_NOERROR) { +    MessageBoxW(hwnd, L"cannot WaveOutOpen", L"Error", MB_ICONSTOP); +    goto err_wavebuf; +  } +  for (int i = 0; i < 4; i++) { +    WAVEHDR *wh = &waveout->waveheaders[i]; +    wh->lpData = ((char *)waveout->wavebuf) + sectlen*i; +    wh->dwBufferLength = sectlen; +    wh->dwFlags = 0; +    waveOutPrepareHeader(waveout->wo, wh, sizeof(*wh)); +  } +  waveout->firstout = 1; +  waveout->cbfunc = cbfunc; +  waveout->userptr = userptr; +  return waveout; + +err_wavebuf: +  HeapFree(heap, 0, waveout->wavebuf); +err_waveout: +  HeapFree(heap, 0, waveout); +err: +  return 0; +} + +void waveout_pause(struct waveout_state *waveout, int pause) { +  if (pause) { +    waveOutPause(waveout->wo); +  } else { +    if (waveout->firstout) { +      waveout->firstout = 0; +      for (int i = 0; i < 4; i++) { +        WAVEHDR *wh = &waveout->waveheaders[i]; +        waveout->cbfunc(waveout->userptr, (int16_t *)wh->lpData, wh->dwBufferLength/4); +        waveOutWrite(waveout->wo, wh, sizeof(*wh)); +      } +    } else { +      waveOutRestart(waveout->wo); +    } +  } +} + +void waveout_delete(struct waveout_state *waveout) { +  if (!waveout) return; +  waveout_pause(waveout, 1); +  for (int i = 0; i < 4; i++) { +    WAVEHDR *wh = &waveout->waveheaders[i]; +    waveOutUnprepareHeader(waveout->wo, wh, sizeof(*wh)); +  } +  HANDLE heap = GetProcessHeap(); +  HeapFree(heap, 0, waveout->wavebuf); +  HeapFree(heap, 0, waveout); +} diff --git a/win32/waveout.h b/win32/waveout.h new file mode 100644 index 0000000..ff33b01 --- /dev/null +++ b/win32/waveout.h @@ -0,0 +1,13 @@ +#ifndef MYON_WAVEOUT_H_INCLUDED +#define MYON_WAVEOUT_H_INCLUDED + +#include "soundout.h" + +struct waveout_state; + +struct waveout_state *waveout_init(HWND hwnd, unsigned srate, unsigned sectlen, +                                   sound_callback cbfunc, void *userptr); +void waveout_delete(struct waveout_state *waveout); +void waveout_pause(struct waveout_state *waveout, int pause); + +#endif // MYON_WAVEOUT_H_INCLUDED diff --git a/win32/x86/Makefile b/win32/x86/Makefile new file mode 100644 index 0000000..e273569 --- /dev/null +++ b/win32/x86/Makefile @@ -0,0 +1,43 @@ +vpath %.c ../ +vpath %.c ../../fmdriver +vpath %.c ../../libopna +vpath %.c ../../fmdsp +vpath %.rc .. +TARGET=fmplayer.exe +FMDRIVER_OBJS=fmdriver_fmp.o \ +              ppz8.o +LIBOPNA_OBJS=opna.o \ +             opnatimer.o \ +             opnafm.o \ +             opnassg.o \ +             opnadrum.o \ +             opnaadpcm.o +FMDSP_OBJS=fmdsp.o +OBJS=main.o dsoundout.o soundout.o waveout.o uc.o lnf.o \ +     $(FMDRIVER_OBJS) \ +     $(LIBOPNA_OBJS) \ +     $(FMDSP_OBJS) +ARCH=i686 +PREFIX=$(ARCH)-w64-mingw32- +CC=$(PREFIX)gcc +WINDRES=$(PREFIX)windres +STRIP=$(PREFIX)strip +CFLAGS=-std=c99 -Os -Wall -Wextra -pedantic -I../.. \ +       -DUNICODE -D_UNICODE \ +       -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 \ +       -march=i586 +LIBS=-nostdlib -s -Wl,-e_entry@4 \ +     -Wl,--subsystem,windows \ +     -lgcc -lntdll \ +     -luser32 -lkernel32 -lole32 -ldxguid -luuid -lcomdlg32 \ +     -lgdi32 -lshlwapi -lwinmm -lshell32 + +$(TARGET):	$(OBJS) +	$(CC) -o $@ $(OBJS) $(LIBS) +	$(STRIP) $@ + +%.o:	%.rc +	$(WINDRES) -o $@ -i $< + +clean: +	rm -f $(TARGET) $(OBJS) | 
