ホームに戻る
 サウンドライブラリを作ろう

Win32API を使用、ファミコンぽい音がでます。

// bcc32 -W -WM sound_library.cpp

#include <windows.h>
#include <process.h>
#include <math.h>

/*
  ファミコンに近い音が出るサウンドライブラリ
  2チャンネル 矩形波 1チャンネル 三角波 1チャンネル ノイズ
  テンポは120で固定 4小節をループします 8分音符まで有効
*/

#define ID_B1 1000 // ボタン1
#define ID_B2 1100 // ボタン2

#define CHANNELS 2
#define SOUND_BITS 8 // 8bit 以外には対応していません
#define SAMPLE_RATE 44100

typedef struct{
  int done;
  BOOL thread_end;
}DATA, *PDATA;

static DATA data1;
static HANDLE hThread1;
static DWORD thId1;

static HWAVEOUT hWOut;
static WAVEHDR wh[2];
static WAVEFORMATEX wf;

#define BAR 2 // 1つのバッファの小節数
#define SOUND_BUFFER_SIZE (SAMPLE_RATE*2*CHANNELS*BAR)

static unsigned char buffer[2][SOUND_BUFFER_SIZE]; // バッファ

// チャンネル0 矩形波

#define BASE_HZ0 440.0 // 最低音の周波数

static int score0[2][8][8 * BAR] = {
  // バッファ0
  {
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,1,1},
    {0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0},
    {1,1,1,1,0,0,0,0, 1,1,1,1,0,0,0,0},
    {0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,1,1, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}
  },
  // バッファ1
  {
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,1,1,0,0},
    {0,0,0,0,0,0,1,1, 0,0,0,0,0,0,1,1},
    {0,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0},
    {1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}
  }
};

// チャンネル1 矩形波

#define BASE_HZ1 440.0 // 最低音の周波数

static int score1[2][8][8 * BAR] = {
  // バッファ0
  {
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,1,0,1,0,0,0,0},
    {0,0,0,0,0,1,0,1, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 1,0,1,0,0,0,0,0},
    {0,0,0,0,1,0,1,0, 0,0,0,0,0,0,0,0},
    {0,1,0,1,0,0,0,0, 0,0,0,0,0,1,0,1},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {1,0,1,0,0,0,0,0, 0,0,0,0,1,0,1,0},
  },
  // バッファ1
  {
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,1,0,1},
    {0,0,0,0,0,0,0,0, 0,1,0,1,0,0,0,0},
    {0,0,0,0,0,1,0,1, 0,0,0,0,1,0,1,0},
    {0,0,0,0,0,0,0,0, 1,0,1,0,0,0,0,0},
    {0,0,0,0,1,0,1,0, 0,0,0,0,0,0,0,0},
    {0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {1,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0},
  }
};

// チャンネル2 三角波

#define BASE_HZ2 220.0 // 最低音の周波数

static int score2[2][8][8 * BAR] = {
  // バッファ0
  {
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,0},
    {0,0,0,0,1,1,1,1, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {1,1,1,1,0,0,0,0, 0,0,0,0,1,1,1,1},
  },
  // バッファ1
  {
    {1,1,1,1,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 1,1,1,1,0,0,0,0},
    {0,0,0,0,1,1,1,1, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0, 0,0,0,0,1,1,1,1},
  }
};

// チャンネル3 ノイズ

#define NOISE_TYPE  1  // 1 or 6

// 数字はピッチです 1-7 くらいまでが有効
static int score_noise[2][8 * BAR] = {
  // バッファ0
  {6,0,6,0,0,0,6,0, 6,0,6,0,0,0,6,0},
  // バッファ1
  {6,6,0,0,6,6,0,0, 6,6,0,0,6,0,6,0}
};

// バッファに波形を作成する
void make_wave(){
  static double base_hz0 = BASE_HZ0;
  static double base_hz1 = BASE_HZ1;
  static double base_hz2 = BASE_HZ2;
  static int d[8] = {0, 2, 4, 5, 7, 9, 11, 12};

  static int sb = (SOUND_BUFFER_SIZE) / (8 * BAR);

  // 波形の初期化
  for(int i = 0; i < SOUND_BUFFER_SIZE; i++){
    buffer[0][i] = 127;
    buffer[1][i] = 127;
  }

  // チャンネル0
  for(int db = 0; db < 2; db++){
    for(int n = 0; n < 8; n++){
      int note = 7 - n;
      double hz = base_hz0 * pow(2.0, (d[n] / 12.0));
      int wl = SAMPLE_RATE / hz;

      for(int t = 0; t < 16; t++){
        if(score0[db][note][t] == 1){
          for(int i = sb * t; i < (t + 1) * sb; i+=2){
            unsigned char l = (((i / 2) % wl) < wl / 2) ? 127 - 15 : 127 + 15;
            unsigned char r = l;

            buffer[db][i] += (l - 127);
            buffer[db][i + 1] += (r - 127);
          }
        }
      }
    }
  }

  // チャンネル1
  for(int db = 0; db < 2; db++){
    for(int n = 0; n < 8; n++){
      int note = 7 - n;
      double hz = base_hz1 * pow(2.0, (d[n] / 12.0));
      int wl = SAMPLE_RATE / hz;

      for(int t = 0; t < 16; t++){
        if(score1[db][note][t] == 1){
          for(int i = sb * t; i < (t + 1) * sb; i+=2){
            unsigned char l = (((i / 2) % wl) < wl / 2) ? 127 - 15 : 127 + 15;
            unsigned char r = l;

            buffer[db][i] += (l - 127);
            buffer[db][i + 1] += (r - 127);
          }
        }
      }
    }
  }

  // チャンネル2
  for(int db = 0; db < 2; db++){
    for(int n = 0; n < 8; n++){
      int note = 7 - n;
      double hz = base_hz2 * pow(2.0, (d[n] / 12.0));
      int wl = SAMPLE_RATE / hz;

      for(int t = 0; t < 16; t++){
        if(score2[db][note][t] == 1){
          for(int i = sb * t; i < (t + 1) * sb; i+=2){
            unsigned char l = (((i / 2) % wl) < wl / 2) ? ((((i / 2) % (wl / 2)) / (wl / 2) * 15) + 127) : ((15 - ((i / 2) % (wl / 2)) / (wl / 2) * 15) + 127);
            unsigned char r = l;

            buffer[db][i] += (l - 127);
            buffer[db][i + 1] += (r - 127);
          }
        }
      }
    }
  }

  // チャンネル3
  int output;
  int reg = 0x8000;

  for(int db = 0; db < 2; db++){
    for(int t = 0; t < 16; t++){
      if(score_noise[db][t] != 0){
        for(int i = sb * t; i < (t + 1) * sb; i+=2){
          if(i % (1 << score_noise[db][t]) == 0){
            reg >>= 1;
            reg |= ((reg ^ (reg >> NOISE_TYPE)) & 1) << 15;
            output = reg & 1;
          }

          buffer[db][i] += (output * 2 - 1) * 15;
          buffer[db][i + 1] += (output * 2 - 1) * 15;
        }
      }
    }
  }
}

#define MUTEX_STRING "nm_sound_library"

static HANDLE hMutex;

void SoundThread(void *data){
  PDATA pData;
  pData = (PDATA)data;

  static BOOL th_flag = FALSE;
  static int num = 0;

  if(!th_flag){
    th_flag = TRUE;
    while (!pData->thread_end){
      WaitForSingleObject(hMutex, 300);
      if(pData->done == 1){
        waveOutWrite(hWOut, &wh[num % 2], sizeof(WAVEHDR));
        num++;
        pData->done = 0;
      }
      ReleaseMutex(hMutex);
    }
    th_flag = FALSE;
  }

  _endthread();

  return;
}

/*
*   WAVE処理のコールバック
*   この中に wave〜 で始まる関数を書かないこと
*/
void CALLBACK waveProc(HWAVEOUT, UINT msg, DWORD, DWORD, DWORD){
  switch(msg){
    case MM_WOM_OPEN:
    case MM_WOM_CLOSE:
      break;
    case MM_WOM_DONE:
      WaitForSingleObject(hMutex, INFINITE);
      if(data1.done == 0){
        data1.done = 1;
      }
      ReleaseMutex(hMutex);
      break;
    default:
      break;
  }
}

int initSound(){
  int i;

  hMutex = CreateMutex(NULL, FALSE, MUTEX_STRING);

  if(!hMutex || (GetLastError() == ERROR_ALREADY_EXISTS)){
    return 0;
  }

  hWOut = NULL;

  wf.wFormatTag = WAVE_FORMAT_PCM;
  wf.nChannels = CHANNELS;
  wf.nSamplesPerSec = SAMPLE_RATE;
  wf.nAvgBytesPerSec = SAMPLE_RATE * (SOUND_BITS >> 3) * CHANNELS;
  wf.nBlockAlign = (SOUND_BITS >> 3) * CHANNELS;
  wf.wBitsPerSample = SOUND_BITS;
  wf.cbSize = 0;

  if(waveOutOpen(&hWOut, WAVE_MAPPER, &wf, (DWORD)(void*)waveProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR){
    return 0;
  }

  make_wave();

  for(i = 0; i < 2; i++){
    wh[i].lpData = buffer[i];
    wh[i].dwBufferLength = SOUND_BUFFER_SIZE;
    wh[i].dwBytesRecorded = 0;
    wh[i].dwUser = 0;
    wh[i].dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
    wh[i].dwLoops = 1;
    wh[i].lpNext = NULL;
    wh[i].reserved = 0;

    if(waveOutPrepareHeader(hWOut, &wh[i], sizeof(WAVEHDR)) != MMSYSERR_NOERROR){
      return 0;
    }
  }

  return 1;
}

void playSound(){
  int i;

  data1.done = 0;
  data1.thread_end = FALSE;

  _beginthread(SoundThread, 0, &data1);

  for(i = 0; i < 2; i++){
    waveOutWrite(hWOut, &wh[i], sizeof(WAVEHDR));
  }
}

void deinitSound(){
  int i;

  data1.thread_end = TRUE;

  waveOutPause(hWOut);
  waveOutReset(hWOut);

  for(i = 0; i < 2; i++){
    waveOutUnprepareHeader(hWOut , &wh[i] , sizeof(WAVEHDR));
  }

  waveOutClose(hWOut);

  CloseHandle(hMutex);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

//メイン関数
int APIENTRY WinMain(
  HINSTANCE hInstance,  //インスタンスのハンドル
  HINSTANCE hPreInstance, //以前のインスタンスハンドル
  LPSTR cmdLine,  //コマンド行の文字列
  int cmdShow)    //ウィンドウの表示状態
{
  //ウィンドウクラスの宣言
  WNDCLASS wd;

  //ウィンドウクラス名
  char *wdName = "window00";

  //ウィンドウ登録のためのループ
  if (!hPreInstance) {
    //ウィンドウスタイル
    wd.style = CS_HREDRAW | CS_VREDRAW;  //幅、高さの変化に対し再描画する
    //ウィンドウプロシージャ
    wd.lpfnWndProc = (WNDPROC)WndProc;
    //クラスの予備のメモリ領域
    wd.cbClsExtra = 0;
    //ウィンドウの予備のメモリ領域
    wd.cbWndExtra = 0;
    //プログラムのインスタンスハンドル
    wd.hInstance = hInstance;
    //アイコンのハンドル
    wd.hIcon = NULL;
    //カーソルのハンドル
    wd.hCursor = LoadCursor(NULL, IDC_ARROW);
    //バックグラウンドのブラシのハンドル
    wd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    //メニュー名
    wd.lpszMenuName = NULL;
    //クラス名
    wd.lpszClassName = wdName;

    //ウィンドウの登録(成功 非0、失敗 0)
    if (!RegisterClass(&wd))
      return FALSE;
  }

  //ウィンドウの生成(ハンドルを form00 とする)
  HWND form00 = CreateWindowEx(
    NULL,      //拡張スタイル(なし)
    wdName,      //ウィンドウクラス名
    "window",    //ウィンドウの名前
    WS_OVERLAPPEDWINDOW,  //ウィンドウスタイル(オーバーラップ型)
    CW_USEDEFAULT,    //ウィンドウのX座標(ディフォルト)
    CW_USEDEFAULT,    //ウィンドウのY座標(ディフォルト)
    200,      //ウィンドウの幅(80に設定)
    100,      //ウィンドウの高さ(50に設定)
    NULL,      //親ウィンドウ(この場合自身が親)
    NULL,      //メニュー(なし)
    hInstance,    //プログラムのハンドル
    NULL);      //予備パラメータ

  //ウィンドウの表示
  ShowWindow(form00, cmdShow);

  //ウィンドウの更新
  UpdateWindow(form00);

  //イベントのループ
  MSG msg;
  while (GetMessage(&msg, NULL, NULL, NULL))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// ウィンドウプロシージャ(ウィンドウの動作を規定)
LRESULT CALLBACK WndProc(
  HWND hwnd,
  UINT message,
  WPARAM wParam,
  LPARAM lParam)
  {
  //ボタンを2個宣言
  HWND hButton1, hButton2;

  //インスタンスハンドルを得る
  HINSTANCE hInst;
  hInst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);

  switch (message) {
  //ウィンドウを表示するとき処理
    case WM_CREATE:
    //ボタン1の表示
      hButton1 = CreateWindow(
        "BUTTON",
        "Button1",
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        10,
        10,
        80,
        20,
        hwnd,
        (HMENU)ID_B1,
        hInst
        ,NULL);
    //ボタン2の表示
      hButton2 = CreateWindow(
        "BUTTON",
        "Button2",
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        100,
        10,
        80,
        20,
        hwnd,
        (HMENU)ID_B2,
        hInst
        ,NULL);
    
      initSound();
      break;
  //ボタンを押したときの処理
    case WM_COMMAND:
      switch(LOWORD(wParam)){
      //ボタン1を押したとき
        case ID_B1:
          playSound();
          break;
      //ボタン2を押したとき
        case ID_B2:
          MessageBox(hwnd, 
          (LPCSTR)"ボタン2が押されました", 
          (LPCSTR)"button2", 
          MB_OK);
          break;
        default:
          return(DefWindowProc(hwnd, message, wParam, lParam));
      }
      break;
  //ウィンドウを破壊する時の処理
    case WM_DESTROY:
      deinitSound();
      //メイン関数のループを終了
      PostQuitMessage(0);
      break;

  //デフォルトの処理に対してデフォルトの処理を返す
    default:
      return DefWindowProc(hwnd, message, wParam, lParam);
  }
  return NULL;
}

inserted by FC2 system