ホームに戻る
サウンドライブラリを作ろう
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;
}