From 30c59a00956142aafda87c0bdc71c46d1a2218ff Mon Sep 17 00:00:00 2001
From: Takamichi Horikawa <takamichiho@gmail.com>
Date: Mon, 27 Mar 2017 23:33:40 +0900
Subject: add oscilloscope view

---
 win32/oscilloview.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 win32/oscilloview.h |  19 +++++++
 2 files changed, 158 insertions(+)
 create mode 100644 win32/oscilloview.c
 create mode 100644 win32/oscilloview.h

(limited to 'win32')

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
+
-- 
cgit v1.2.3