diff options
| -rw-r--r-- | gtk/oscilloview.c | 83 | ||||
| -rw-r--r-- | gtk/oscilloview.h | 17 | ||||
| -rw-r--r-- | libopna/opna.c | 18 | ||||
| -rw-r--r-- | libopna/opna.h | 6 | ||||
| -rw-r--r-- | libopna/opnafm.c | 43 | ||||
| -rw-r--r-- | libopna/opnafm.h | 3 | ||||
| -rw-r--r-- | libopna/opnassg.c | 67 | ||||
| -rw-r--r-- | libopna/opnassg.h | 7 | ||||
| -rw-r--r-- | libopna/opnatimer.c | 10 | ||||
| -rw-r--r-- | libopna/opnatimer.h | 2 | ||||
| -rw-r--r-- | oscillo/oscillo.h | 17 | ||||
| -rw-r--r-- | win32/oscilloview.c | 139 | ||||
| -rw-r--r-- | win32/oscilloview.h | 19 | 
13 files changed, 405 insertions, 26 deletions
| diff --git a/gtk/oscilloview.c b/gtk/oscilloview.c new file mode 100644 index 0000000..724ada9 --- /dev/null +++ b/gtk/oscilloview.c @@ -0,0 +1,83 @@ +#include <gtk/gtk.h> +#include <cairo.h> +#include "oscilloview.h" +#include <string.h> + +struct oscilloview oscilloview_g = { +  .flag = ATOMIC_FLAG_INIT +}; + +enum { +  VIEW_SAMPLES = 1024, +  VIEW_SKIP = 2, +  WIDTH = 600, +  HEIGHT = 300, +}; + +static struct { +  GtkWidget *win; +  struct oscillodata oscillodata[LIBOPNA_OSCILLO_TRACK_COUNT]; +} g; + +static void on_destroy(GtkWidget *w, gpointer ptr) { +  (void)w; +  (void)ptr; +  g.win = 0; +} + +static void draw_track(cairo_t *cr, +                       double x, double y, double w, double h, +                       const struct oscillodata *data) { +  int start = OSCILLO_SAMPLE_COUNT - VIEW_SAMPLES; +  start -= (data->offset >> OSCILLO_OFFSET_SHIFT); +  if (start < 0) start = 0; +  for (int i = 0; i < (VIEW_SAMPLES / VIEW_SKIP); i++) { +    cairo_line_to(cr, x + ((i)*w)/(VIEW_SAMPLES / VIEW_SKIP), y + h/2.0 - (data[0].buf[start + i*VIEW_SKIP] / 16384.0) * h/2); +  } +  cairo_stroke(cr); +} + +static gboolean draw_cb(GtkWidget *w, +                        cairo_t *cr, +                        gpointer ptr) { +  (void)w; +  (void)ptr; +  cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); +  if (!atomic_flag_test_and_set_explicit( +    &oscilloview_g.flag, memory_order_acquire)) { +    memcpy(g.oscillodata, +           oscilloview_g.oscillodata, +           sizeof(oscilloview_g.oscillodata)); +    atomic_flag_clear_explicit(&oscilloview_g.flag, memory_order_release); +  } +  cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); +  cairo_paint(cr); +  cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); +  for (int x = 0; x < 3; x++) { +    for (int y = 0; y < 3; y++) { +      draw_track(cr, x*WIDTH, y*HEIGHT, WIDTH, HEIGHT, &g.oscillodata[x*3+y]); +    } +  } +  return FALSE; +} + +static gboolean tick_cb(GtkWidget *w, GdkFrameClock *clock, gpointer ptr) { +  (void)clock; +  (void)ptr; +  gtk_widget_queue_draw(w); +  return G_SOURCE_CONTINUE; +} + +void show_oscilloview(void) { +  if (!g.win) { +    g.win = gtk_window_new(GTK_WINDOW_TOPLEVEL); +    gtk_window_set_title(GTK_WINDOW(g.win), "Oscilloscope view"); +    g_signal_connect(g.win, "destroy", G_CALLBACK(on_destroy), 0); +  } +  GtkWidget *drawarea = gtk_drawing_area_new(); +  gtk_container_add(GTK_CONTAINER(g.win), drawarea); +  gtk_widget_set_size_request(drawarea, WIDTH*3, HEIGHT*3); +  g_signal_connect(drawarea, "draw", G_CALLBACK(draw_cb), 0); +  gtk_widget_add_tick_callback(drawarea, tick_cb, 0, 0); +  gtk_widget_show_all(g.win); +} diff --git a/gtk/oscilloview.h b/gtk/oscilloview.h new file mode 100644 index 0000000..edabcd9 --- /dev/null +++ b/gtk/oscilloview.h @@ -0,0 +1,17 @@ +#ifndef MYON_FMPLAYER_GTK_OSCILLOVIEW_H_INCLUDED +#define MYON_FMPLAYER_GTK_OSCILLOVIEW_H_INCLUDED + +#include "libopna/opna.h" +#include "oscillo/oscillo.h" + +#include <stdatomic.h> + +extern struct oscilloview { +  atomic_flag flag; +  struct oscillodata oscillodata[LIBOPNA_OSCILLO_TRACK_COUNT]; +} oscilloview_g; + +void show_oscilloview(void); + +#endif // MYON_FMPLAYER_GTK_OSCILLOVIEW_H_INCLUDED + diff --git a/libopna/opna.c b/libopna/opna.c index 10155d0..1b0aa6c 100644 --- a/libopna/opna.c +++ b/libopna/opna.c @@ -1,4 +1,6 @@  #include "opna.h" +#include "oscillo/oscillo.h" +#include <string.h>  void opna_reset(struct opna *opna) {    opna_fm_reset(&opna->fm); @@ -23,8 +25,20 @@ unsigned opna_readreg(const struct opna *opna, unsigned reg) {  }  void opna_mix(struct opna *opna, int16_t *buf, unsigned samples) { -  opna_fm_mix(&opna->fm, buf, samples); -  opna_ssg_mix_55466(&opna->ssg, &opna->resampler, buf, samples); +  opna_mix_oscillo(opna, buf, samples, 0); +} + +void opna_mix_oscillo(struct opna *opna, int16_t *buf, unsigned samples, struct oscillodata *oscillo) { +  if (oscillo) { +    for (int i = 0; i < LIBOPNA_OSCILLO_TRACK_COUNT; i++) { +      memmove(&oscillo[i].buf[0], +              &oscillo[i].buf[samples], +              (OSCILLO_SAMPLE_COUNT - samples)*sizeof(oscillo[i].buf[0])); +    } +  } +  unsigned offset = OSCILLO_SAMPLE_COUNT - samples; +  opna_fm_mix(&opna->fm, buf, samples, &oscillo[0], offset); +  opna_ssg_mix_55466(&opna->ssg, &opna->resampler, buf, samples, &oscillo[6], offset);    opna_drum_mix(&opna->drum, buf, samples);    opna_adpcm_mix(&opna->adpcm, buf, samples);  } diff --git a/libopna/opna.h b/libopna/opna.h index 7d7d722..2ebca0d 100644 --- a/libopna/opna.h +++ b/libopna/opna.h @@ -30,6 +30,10 @@ enum {    LIBOPNA_CHAN_ADPCM = 0x8000,  }; +enum { +  LIBOPNA_OSCILLO_TRACK_COUNT = 11 +}; +  struct opna {    struct opna_fm fm;    struct opna_ssg ssg; @@ -43,6 +47,8 @@ void opna_reset(struct opna *opna);  void opna_writereg(struct opna *opna, unsigned reg, unsigned val);  unsigned opna_readreg(const struct opna *opna, unsigned reg);  void opna_mix(struct opna *opna, int16_t *buf, unsigned samples); +struct oscillodata; +void opna_mix_oscillo(struct opna *opna, int16_t *buf, unsigned samples, struct oscillodata *oscillo);  unsigned opna_get_mask(const struct opna *opna);  void opna_set_mask(struct opna *opna, unsigned mask); diff --git a/libopna/opnafm.c b/libopna/opnafm.c index 3f5367f..abc36fa 100644 --- a/libopna/opnafm.c +++ b/libopna/opnafm.c @@ -1,4 +1,5 @@  #include "opnafm.h" +#include "oscillo/oscillo.h"  #include "opnatables.h" @@ -539,7 +540,46 @@ void opna_fm_chan_env(struct opna_fm_channel *chan) {    }  } -void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples) { +static int gcd(int a, int b) { +  if (a < b) { +    int t = a; +    a = b; +    b = t; +  } +  for (;;) { +    int r = a % b; +    if (!r) break; +    a = b; +    b = r; +  } +  return b; +} + + + +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples, +                 struct oscillodata *oscillo, unsigned offset) { +  if (oscillo) { +    for (unsigned c = 0; c < 6; c++) { +      const struct opna_fm_channel *ch = &fm->channel[c]; +      unsigned freq = blkfnum2freq(ch->blk, ch->fnum); +      int mul[4]; +      for (int i = 0; i < 4; i++) { +        mul[i] = ch->slot[i].mul << 1; +        if (!mul[i]) mul[i] = 1; +      } +      freq *= gcd(gcd(gcd(mul[0], mul[1]), mul[2]), mul[3]); +      freq /= 2; +      unsigned period = 0; +      if (freq) period = (1u<<(20+OSCILLO_OFFSET_SHIFT)) / freq; +      if (period) { +        oscillo[c].offset += (samples << OSCILLO_OFFSET_SHIFT); +        oscillo[c].offset %= period; +      } else { +        oscillo[c].offset = 0; +      } +    } +  }    for (unsigned i = 0; i < samples; i++) {      if (!fm->env_div3) {        for (int c = 0; c < 6; c++) { @@ -554,6 +594,7 @@ void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples) {      for (int c = 0; c < 6; c++) {        int16_t o = opna_fm_chanout(&fm->channel[c]); +      if (oscillo) oscillo[c].buf[offset+i] = o*2;        // TODO: CSM        if (c == 2 && fm->ch3.mode != CH3_MODE_NORMAL) {          opna_fm_chan_phase_se(&fm->channel[c], fm); diff --git a/libopna/opnafm.h b/libopna/opnafm.h index 1a7f55a..b62556e 100644 --- a/libopna/opnafm.h +++ b/libopna/opnafm.h @@ -88,7 +88,8 @@ struct opna_fm {  };  void opna_fm_reset(struct opna_fm *fm); -void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples); +struct oscillodata; +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples, struct oscillodata *oscillo, unsigned offset);  void opna_fm_writereg(struct opna_fm *fm, unsigned reg, unsigned val);  // diff --git a/libopna/opnassg.c b/libopna/opnassg.c index 223758c..ec03437 100644 --- a/libopna/opnassg.c +++ b/libopna/opnassg.c @@ -1,4 +1,5 @@  #include "opnassg.h" +#include "oscillo/oscillo.h"  /*  static const float voltable[32] = {    0.0f,           0.0f,           0x1.ae89f9p-8f, 0x1.000000p-7f, @@ -126,10 +127,12 @@ static bool opna_ssg_tone_out(const struct opna_ssg *ssg, int chan) {    return (ssg->ch[chan].out || (reg & 0x1)) && ((ssg->lfsr & 1) || (reg & 0x8));  } +#if 0  static bool opna_ssg_tone_silent(const struct opna_ssg *ssg, int chan) {    unsigned reg = ssg->regs[0x7] >> chan;    return (reg & 0x1) && (reg & 0x8);  } +#endif  static int opna_ssg_noise_period(const struct opna_ssg *ssg) {    return ssg->regs[0x6] & 0x1f; @@ -149,6 +152,10 @@ int opna_ssg_channel_level(const struct opna_ssg *ssg, int ch) {         : (opna_ssg_tone_volume(ssg, ch) << 1) + 1;  } +#define COEFF 0x3fff +#define COEFFSH 14 + +// 3 samples per frame  void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) {    for (int i = 0; i < samples; i++) {      if (((++ssg->noise_counter) >> 1) >= opna_ssg_noise_period(ssg)) { @@ -173,31 +180,38 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) {        }      } -    int16_t out = 0; +    //int16_t out = 0;      for (int ch = 0; ch < 3; ch++) { +      buf[i*3+ch] = 0;        if (++ssg->ch[ch].tone_counter >= opna_ssg_tone_period(ssg, ch)) {          ssg->ch[ch].tone_counter = 0;          ssg->ch[ch].out = !ssg->ch[ch].out;        } -      if (ssg->mask & (1<<ch)) continue;  #if 1 -      // may output DC offset        // YMF288 seems to disable output when 0 <= Tp < 8 +      int32_t previntmp = 0;        if (opna_ssg_tone_out(ssg, ch)) {          int level = opna_ssg_chan_env(ssg, ch)            ? opna_ssg_env_level(ssg)            : (opna_ssg_tone_volume(ssg, ch) << 1) + 1; -        out += voltable[level]; +        //out += voltable[level]; +        previntmp = voltable[level]/2;        } +      previntmp *= COEFF; +      ssg->prevout[ch] = previntmp - ssg->previn[ch] + ((((int64_t)COEFF)*ssg->prevout[ch]) >> COEFFSH); +      ssg->previn[ch] = previntmp; +      buf[i*3+ch] = ssg->prevout[ch] >> COEFFSH; +      //buf[i*3+ch] = voltable[level]/2;  #else        if (!opna_ssg_tone_silent(ssg, ch)) {          int level = opna_ssg_channel_level(ssg, ch); -        out += (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 2; +        //out += (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 2; +        buf[i*3+ch] = (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 4;        }  #endif      } -    buf[i] = out / 2; +    //buf[i] = out / 2;    }  } @@ -205,27 +219,46 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) {  void opna_ssg_mix_55466(    struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, -  int16_t *buf, int samples) { +  int16_t *buf, int samples, +  struct oscillodata *oscillo, unsigned offset +) { +  if (oscillo) { +    for (unsigned c = 0; c < 3; c++) { +      unsigned period = (opna_ssg_tone_period(ssg, c) << OSCILLO_OFFSET_SHIFT) * 2 * 32 / 144; +      if (period) { +        oscillo[c].offset += (samples << OSCILLO_OFFSET_SHIFT); +        oscillo[c].offset %= period; +      } else { +        oscillo[c].offset = 0; +      } +    } +  }    for (int i = 0; i < samples; i++) {      {        int ssg_samples = ((resampler->index + 9)>>1) - ((resampler->index)>>1); -      int16_t ssgbuf[5]; +      int16_t ssgbuf[15];        opna_ssg_generate_raw(ssg, ssgbuf, ssg_samples);        for (int j = 0; j < ssg_samples; j++) { -        resampler->buf[BUFINDEX(j)] = ssgbuf[j]; +        resampler->buf[BUFINDEX(j)*3+0] = ssgbuf[j*3+0]; +        resampler->buf[BUFINDEX(j)*3+1] = ssgbuf[j*3+1]; +        resampler->buf[BUFINDEX(j)*3+2] = ssgbuf[j*3+2];        }        resampler->index += 9;      }      int32_t sample = 0; -    for (int j = 0; j < SINCTABLELEN; j++) { -      unsigned sincindex = j*2; -      if (!(resampler->index&1)) sincindex++; -      bool sincsign = sincindex & (1<<(SINCTABLEBIT)); -      unsigned sincmask = ((1<<(SINCTABLEBIT))-1); -      sincindex = (sincindex & sincmask) ^ (sincsign ? sincmask : 0); -      sample += (resampler->buf[BUFINDEX(j)] * sinctable[sincindex])>>2; +    for (int ch = 0; ch < 3; ch++) { +      int32_t chsample = 0; +      for (int j = 0; j < SINCTABLELEN; j++) { +        unsigned sincindex = j*2; +        if (!(resampler->index&1)) sincindex++; +        bool sincsign = sincindex & (1<<(SINCTABLEBIT)); +        unsigned sincmask = ((1<<(SINCTABLEBIT))-1); +        sincindex = (sincindex & sincmask) ^ (sincsign ? sincmask : 0); +        chsample += (resampler->buf[BUFINDEX(j)*3+ch] * sinctable[sincindex])>>2; +      } +      if (oscillo) oscillo[ch].buf[offset+i] = chsample >> 13; +      if (!(ssg->mask & (1<<ch))) sample += chsample;      } -      sample >>= 16;      sample *= 13000;      sample >>= 14; diff --git a/libopna/opnassg.h b/libopna/opnassg.h index 8a59d91..0321163 100644 --- a/libopna/opnassg.h +++ b/libopna/opnassg.h @@ -25,10 +25,12 @@ struct opna_ssg {    bool env_hld;    bool env_holding;    unsigned mask; +  int32_t previn[3]; +  int32_t prevout[3];  };  struct opna_ssg_resampler { -  int16_t buf[(1<<7)]; +  int16_t buf[(1<<7)*3];    unsigned index;  }; @@ -46,9 +48,10 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples);  // call to buffer written with OPNA output  // samplerate: 7987200/144 Hz  //            (55466.66..) Hz +struct oscillodata;  void opna_ssg_mix_55466(    struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, -  int16_t *buf, int samples); +  int16_t *buf, int samples, struct oscillodata *oscillo, unsigned offset);  void opna_ssg_writereg(struct opna_ssg *ssg, unsigned reg, unsigned val);  unsigned opna_ssg_readreg(const struct opna_ssg *ssg, unsigned reg);  // channel level (0 - 31) diff --git a/libopna/opnatimer.c b/libopna/opnatimer.c index 789c28e..142176f 100644 --- a/libopna/opnatimer.c +++ b/libopna/opnatimer.c @@ -1,5 +1,6 @@  #include "opnatimer.h"  #include "opna.h" +#include "oscillo/oscillo.h"  enum {    TIMERA_BITS = 10, @@ -65,9 +66,12 @@ void opna_timer_writereg(struct opna_timer *timer, unsigned reg, unsigned val) {      }    }  } -#include <stdio.h> -#include <stdlib.h> +  void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples) { +  opna_timer_mix_oscillo(timer, buf, samples, 0); +} + +void opna_timer_mix_oscillo(struct opna_timer *timer, int16_t *buf, unsigned samples, struct oscillodata *oscillo) {    do {      unsigned generate_samples = samples;      if (timer->timerb_enable && timer->timerb_load) { @@ -82,7 +86,7 @@ void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples) {          generate_samples = timera_samples;        }      } -    opna_mix(timer->opna, buf, generate_samples); +    opna_mix_oscillo(timer->opna, buf, generate_samples, oscillo);      if (timer->mix_cb) {        timer->mix_cb(timer->mix_userptr, buf, generate_samples);      } diff --git a/libopna/opnatimer.h b/libopna/opnatimer.h index ad0fe0b..c7b9511 100644 --- a/libopna/opnatimer.h +++ b/libopna/opnatimer.h @@ -37,6 +37,8 @@ void opna_timer_set_mix_callback(struct opna_timer *timer,                                   opna_timer_mix_cb_t func, void *userptr);  void opna_timer_writereg(struct opna_timer *timer, unsigned reg, unsigned val);  void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples); +struct oscillodata; +void opna_timer_mix_oscillo(struct opna_timer *timer, int16_t *buf, unsigned samples, struct oscillodata *oscillo);  #ifdef __cplusplus  } diff --git a/oscillo/oscillo.h b/oscillo/oscillo.h new file mode 100644 index 0000000..451f9f4 --- /dev/null +++ b/oscillo/oscillo.h @@ -0,0 +1,17 @@ +#ifndef MYON_FMPLAYER_OSCILLO_H_INCLUDED +#define MYON_FMPLAYER_OSCILLO_H_INCLUDED + +#include <stdint.h> + +enum { +  OSCILLO_SAMPLE_COUNT = 8192, +  OSCILLO_OFFSET_SHIFT = 10, +}; + +struct oscillodata { +  int16_t buf[OSCILLO_SAMPLE_COUNT]; +  unsigned offset; +   +}; + +#endif // MYON_FMPLAYER_OSCILLO_H_INCLUDED diff --git a/win32/oscilloview.c b/win32/oscilloview.c new file mode 100644 index 0000000..9759cc1 --- /dev/null +++ b/win32/oscilloview.c @@ -0,0 +1,139 @@ +#include "oscilloview.h" +#include <mmsystem.h> +#include <shellapi.h> +#include <windowsx.h> + +enum { +  TIMER_UPDATE = 1 +}; + +struct oscilloview oscilloview_g = { +  .flag = ATOMIC_FLAG_INIT +}; + +enum { +  VIEW_SAMPLES = 1024, +  VIEW_SKIP = 2, +}; + +static struct { +  HINSTANCE hinst; +  HWND parent; +  HWND oscilloview; +  ATOM oscilloview_class; +  struct oscillodata oscillodata[LIBOPNA_OSCILLO_TRACK_COUNT]; +  UINT mmtimer; +} g; + +static void on_destroy(HWND hwnd) { +  g.oscilloview = 0; +  timeKillEvent(g.mmtimer); +} + +static void CALLBACK mmtimer_cb(UINT timerid, UINT msg, +                                DWORD_PTR userptr, +                                DWORD_PTR dw1, DWORD_PTR dw2) { +  PostMessage(g.oscilloview, WM_USER, 0, 0); +} + +static bool on_create(HWND hwnd, const CREATESTRUCT *cs) { +  ShowWindow(hwnd, SW_SHOW); +  //SetTimer(hwnd, TIMER_UPDATE, 16, 0); +  g.mmtimer = timeSetEvent(16, 16, mmtimer_cb, 0, TIME_PERIODIC); +  DragAcceptFiles(hwnd, TRUE); +  return true; +} + +static void draw_track(HDC dc, +                       int x, int y, int w, int h, +                       const struct oscillodata *data) { +  int start = OSCILLO_SAMPLE_COUNT - VIEW_SAMPLES; +  start -= (data->offset >> OSCILLO_OFFSET_SHIFT); +  if (start < 0) start = 0; +  MoveToEx(dc, x, y + h/2.0 - (data->buf[start] / 16384.0) * h/2, 0); +  for (int i = 0; i < (VIEW_SAMPLES / VIEW_SKIP); i++) { +    LineTo(dc, (double)x + ((i)*w)/(VIEW_SAMPLES / VIEW_SKIP), y + h/2.0 - (data->buf[start + i*VIEW_SKIP] / 16384.0) * h/2); +  } +} + +static void on_paint(HWND hwnd) { +  RECT cr; +  GetClientRect(hwnd, &cr); +  PAINTSTRUCT ps; +  HDC dc = BeginPaint(hwnd, &ps); +  HDC mdc = CreateCompatibleDC(dc); +  HBITMAP bitmap = CreateCompatibleBitmap(dc, cr.right, cr.bottom); +  SelectObject(mdc, bitmap); + +  FillRect(mdc, &cr, GetStockObject(BLACK_BRUSH)); +  SelectObject(mdc, GetStockObject(WHITE_PEN)); +  int width = cr.right / 3; +  int height = cr.bottom / 3; +  for (int x = 0; x < 3; x++) { +    for (int y = 0; y < 3; y++) { +      draw_track(mdc, x*width, y*height, width, height, &g.oscillodata[x*3+y]); +    } +  } + +  BitBlt(dc, 0, 0, cr.right, cr.bottom, mdc, 0, 0, SRCCOPY); +  SelectObject(mdc, 0); +  DeleteObject(bitmap); +  DeleteDC(mdc); +  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_TIMER, on_timer); +  HANDLE_MSG(hwnd, WM_PAINT, on_paint); +  case WM_ERASEBKGND: +    return 1; +  case WM_USER: +    if (!atomic_flag_test_and_set_explicit( +      &oscilloview_g.flag, memory_order_acquire)) { +      memcpy(g.oscillodata, +             oscilloview_g.oscillodata, +             sizeof(oscilloview_g.oscillodata)); +      atomic_flag_clear_explicit(&oscilloview_g.flag, memory_order_release); +    } +    InvalidateRect(hwnd, 0, FALSE); +    return 0; +  case WM_DROPFILES: +    return SendMessage(g.parent, msg, wParam, lParam); +  } +  return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void show_oscilloview(HINSTANCE hinst, HWND parent) { +  g.hinst = hinst; +  g.parent = parent; +  if (!g.oscilloview) { +    if (!g.oscilloview_class) { +      WNDCLASS wc = {0}; +      wc.style = 0; +      wc.lpfnWndProc = wndproc; +      wc.hInstance = g.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_oscilloviewer"; +      g.oscilloview_class = RegisterClass(&wc); +    } +    if (!g.oscilloview_class) { +      MessageBox(parent, L"Cannot register oscilloviewer class", L"Error", MB_ICONSTOP); +      return; +    } +    g.oscilloview = CreateWindowEx(0, +                                     MAKEINTATOM(g.oscilloview_class), +                                     L"FMPlayer Oscilloview", +                                     WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN | WS_SIZEBOX | WS_MAXIMIZEBOX, +                                     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, +                                     parent, 0, g.hinst, 0); +  } else { +    SetForegroundWindow(g.oscilloview); +  } +} diff --git a/win32/oscilloview.h b/win32/oscilloview.h new file mode 100644 index 0000000..8849830 --- /dev/null +++ b/win32/oscilloview.h @@ -0,0 +1,19 @@ +#ifndef MYON_FMPLAYER_WIN32_OSCILLOVIEW_H_INCLUDED +#define MYON_FMPLAYER_WIN32_OSCILLOVIEW_H_INCLUDED + +#include "libopna/opna.h" +#include "oscillo/oscillo.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdatomic.h> + +extern struct oscilloview { +  atomic_flag flag; +  struct oscillodata oscillodata[LIBOPNA_OSCILLO_TRACK_COUNT]; +} oscilloview_g; + +void show_oscilloview(HINSTANCE hinst, HWND parent); + +#endif // MYON_FMPLAYER_WIN32_OSCILLOVIEW_H_INCLUDED + | 
