コード:
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <string.h> //memset, strcat, strcpy
#include <stdio.h> //sprintf
#include <math.h> //atof
#include <stdlib.h> //atof
#include "resource.h"
#include <mmsystem.h>
//-----------------------------------------------------
// 定数
//-----------------------------------------------------
#define MAX_NOTE 512
#define MIDIMSG(status,channel,data1,data2) ( (DWORD)((status<<4) | channel | (data1<<8) | (data2<<16)) )
const int NOTE_POS_WIDTH = 16; // 楽譜の横サイズ
const int NOTE_POS_TO_TIME = 128; // 座標を時間に換算するマジックナンバー
//-----------------------------------------------------
// 音符データ構造体
//-----------------------------------------------------
typedef struct {
bool ExistFlag; //存在フラグ
int program; //プログラム番号
int note; //ノート番号
int stime; //開始時間
POINT pt; //座標
int length; //長さ
} NoteData_t;
//-----------------------------------------------------
// 音符データ管理クラス
//-----------------------------------------------------
class CNoteDataMng
{
private://メンバ変数はprivateにして、カプセル化
NoteData_t m_Note[MAX_NOTE]; // 音符データ管理配列
public:
// コンストラクタ
CNoteDataMng() {
//コンストラクタ(クラス生成時(インスタンス化した時)に呼び出される戻り値のない特殊なメソッド
for( int i=0; i < MAX_NOTE; i++ ) {
m_Note[i] .ExistFlag= false; //無効
}
}
// テーブル登録
void Register( POINT pt,int length,int note ) {
for( int i=0; i < MAX_NOTE; i++ ) {
// 線形探索でExistFlag[i]がfalseの配列を探す
if( m_Note[i].ExistFlag == false ) {
m_Note[i].ExistFlag = true;
m_Note[i].pt = pt;
m_Note[i].program = 0; //プログラム番号
m_Note[i].note = note; //ノート番号
m_Note[i].stime = pt.y - NOTE_POS_TO_TIME; //開始時間
m_Note[i].length = length; //長さ
break;
}
}
}
// 演奏?
void PlayNoteOfMidi() {
}
// 座標を返す。
bool GetPoint( int index,POINT &start_pt,POINT &end_pt ) {
// 有効?
if( m_Note[index].ExistFlag == true ) {
start_pt = m_Note[index].pt;
end_pt = m_Note[index].pt;
end_pt.x += NOTE_POS_WIDTH;
end_pt.y += m_Note[index].length;
// 有効
return true;
}
// 無効
return false;
}
int GetNote( int index ) {
// 有効?
if( m_Note[index].ExistFlag == true ) {
return m_Note[index].note;
}
}
};
//-----------------------------------------------------
// マウス座標管理クラス? あるいは 入力中の音符管理
//-----------------------------------------------------
typedef struct {
int MoveY;//動いている座標Y
int DragStartFlag;//ドラッグスタートフラグ
int MoveFlag;//動いているかのフラグ
int ClickFlag;//クリックフラグ
POINT start; //クリック初期の座標
POINT end; //クリック終了の座標
} Mouse_t;
//----------------------------------------------
// グローバル変数
//----------------------------------------------
Mouse_t mouse;//クラスのインスタンス化
CNoteDataMng noteMng;//クラスのインスタンス化
HINSTANCE hInst;
// プロトタイプ
LRESULT CALLBACK MyDlgProc( HWND, UINT, WPARAM, LPARAM );
//----------------------------------------------
// マウス座標をクライアント座標にする。
//----------------------------------------------
POINT ClickOfPic2( HWND hWnd )
{
POINT pt;
RECT rc;
POINT client_pt;
GetCursorPos( &pt ); //マウスポインタの位置取得
ScreenToClient( hWnd, &pt ); //スクリーン座標→クライアント座標
client_pt.x = pt.x;
client_pt.y = pt.y;
GetWindowRect( hWnd, &rc ); //コントロールの位置取得
pt.x = rc.left;
pt.y = rc.top;
ScreenToClient( hWnd, &pt ); //スクリーン座標→クライアント座標
client_pt.x -= pt.x;
client_pt.y -= pt.y;
return client_pt;
}
//-----------------------------------------------------
// WinMain
//-----------------------------------------------------
int WINAPI WinMain( HINSTANCE hCurInst, HINSTANCE hPrevInst,
LPSTR lpsCmdLine, int nCmdShow )
{
AllocConsole();
freopen( "CONOUT$", "w", stdout ); //標準出力をコンソールにする
printf( "これでprintfが使えます\n" ); //←お試し用
hInst = hCurInst;
DialogBox( hCurInst, TEXT( "MYDLG" ), NULL, ( DLGPROC )MyDlgProc ); //ダイアログボックス"MYDLG"を呼び出す
return 0;
}
//-----------------------------------------------------
// 水平スクロール
//-----------------------------------------------------
void HScroll( WPARAM wp, int &nPos )
{
switch( LOWORD( wp ) ) {
case SB_LINELEFT:
nPos--;
if( nPos < 0 ) nPos = 0;
break;
case SB_LINERIGHT:
nPos++;
if( nPos > 743 ) {
nPos = 743;
}
break;
case SB_PAGELEFT:
nPos -= 16;
if( nPos < 0 ) nPos = 0;
break;
case SB_PAGERIGHT:
nPos += 16;
if( nPos > 743 ) nPos = 743;
break;
case SB_THUMBTRACK:
nPos = HIWORD( wp );
break;
case SB_THUMBPOSITION:
nPos = HIWORD( wp );
break;
}
}
//-----------------------------------------------------
// 垂直スクロール
//-----------------------------------------------------
void VScroll( WPARAM wp, int &vPos )
{
switch( LOWORD( wp ) ) {
case SB_LINELEFT:
vPos--;
if( vPos < 0 ) vPos = 0;
break;
case SB_LINERIGHT:
vPos++;
if( vPos > 743 ) {
vPos = 743;
}
break;
case SB_PAGELEFT:
vPos -= 16;
if( vPos < 0 ) vPos = 0;
break;
case SB_PAGERIGHT:
vPos += 16;
if( vPos > 743 ) vPos = 743;
break;
case SB_THUMBTRACK:
vPos = HIWORD( wp );
break;
case SB_THUMBPOSITION:
vPos = HIWORD( wp );
break;
}
}
//----------------------------------------------
// 座標をノート番号にする
//----------------------------------------------
int PointToNote( LPARAM lp,int nPos,POINTS &clc )
{
BYTE note = 0x3C;
int tbl3[] = {0, 2, 4 , 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35,
36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59,
60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95,
// c3 d3 e3 f3 g3 a3 b3 c4 d4 e4 f4 g4 a4 b4 c5 d5 e5 f5 g5 a5 b5
96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115, 117, 119,
// c6 d6 e6 f6 g6 a6 b6 c7 d7 e7 f7 g7 a7 b7
120, 122, 124, 125, 127
};//#♭なしの音
int tbl4[] = {1, 3, -1, 6, 8, 10, -1, 13, 15, -1, 18, 20, 22, -1, 25, 27, -1, 30, 32, 34, -1,
37, 39, 42, 44, 46, -1, 49, 51, -1, 54, 56, 58, -1,
61, 63, -1, 66, 68, 70, -1, 73, 75, -1, 78, 80, 82, -1, 85, 87, -1, 90, 92, 94, -1,
97, 99, -1, 102, 104, 106, -1, 109, 111, -1, 114, 116, 118, -1,
121, 123, -1, 126
};//#の音
clc = MAKEPOINTS( lp );
clc.x = ( ( clc.x + 5 ) / 16 ) * 16;
note = tbl3[( clc.x + nPos ) / 16 - 2];
if( clc.y < 64 ) {
note = tbl4[( clc.x + nPos - 8 ) / 16 - 1];
}
return note;
}
//-----------------------------------------------------
// DlgProc
//-----------------------------------------------------
LRESULT CALLBACK MyDlgProc( HWND hWnd, UINT msg, WPARAM wp, LPARAM lp )
{
static HMIDIOUT hMidi;
static HBITMAP hBmp, hBmp2; //HBITMAP型のhBmp,hBmp2を宣言
static BITMAP bmp_info, bmp_info2;
static int w, h, w2, h2; //画像の高さ、幅を宣言
static HDC hdc, hdc_mem1, hdc_mem2; //デバイスコンテキストハンドルhdc,hdc_mem1,hdc_mem2を宣言
static PAINTSTRUCT ps;
static HWND hPic, hPic2, hScroll1, vScroll1; //ウィンドウハンドルの宣言
static BYTE note = 0x3C, velocity = 0x40; //note番号、べろシティを宣言
static BYTE program = 0; //プログラム番号は0(ピアノ)
static int nPos = 371, vPos = 0; //スクロール変数nPosと、vPos
int i;//iを宣言
HPEN hPen1, hPen2; //HPEN型宣言
static int length = 0; //♪の長さ
switch ( msg ) {
case WM_HSCROLL:
//---------------------------------------
// 横スクロールバーのイベント
//---------------------------------------
if( lp != ( LPARAM )hScroll1 )
return FALSE;
// 水平スクロール処理関数
HScroll(wp,nPos);
// スクロール値をセットする
ScrollBar_SetPos( hScroll1, nPos, TRUE );
InvalidateRect( hPic, NULL, TRUE );
InvalidateRect( hPic2, NULL, TRUE );
return TRUE;
case WM_VSCROLL:
//---------------------------------------
// 縦スクロールバーのイベント
//---------------------------------------
if( lp != ( LPARAM )vScroll1 )
return FALSE;
// 垂直スクロール処理関数
VScroll(wp,vPos);
// スクロール値をセットする
ScrollBar_SetPos( vScroll1, vPos, TRUE );
InvalidateRect( hPic2, NULL, TRUE );
return TRUE;
case WM_INITDIALOG:
//---------------------------------------
// ダイアログの初期化
//---------------------------------------
//IDからウィンドウハンドルを得る
hPic = GetDlgItem( hWnd, IDC_STATIC01 );
hPic2 = GetDlgItem( hWnd, IDC_STATIC02 );
hScroll1 = GetDlgItem( hWnd, IDC_SCROLLBAR1 );
vScroll1 = GetDlgItem( hWnd, IDC_SCROLLBAR2 );
//リソースBMPをロード
hBmp = LoadBitmap( hInst, TEXT( "MYBMP" ) );
hBmp2 = LoadBitmap( hInst, TEXT( "MYBMP2" ) );
//タイマーをセット
SetTimer( hWnd, 100, 100, NULL );
//デバイスコンテキストを得る
hdc = GetDC( hWnd );
hdc_mem1 = CreateCompatibleDC( hdc ); //互換性のあるBMPを作成
SelectObject( hdc_mem1, hBmp ); //オブジェクトを選択
DeleteObject( hBmp ); //削除
hdc_mem2 = CreateCompatibleDC( hdc ); //互換性のあるBMPを作成
SelectObject( hdc_mem2, hBmp2 ); //オブジェクトを選択
DeleteObject( hBmp2 ); //削除
ReleaseDC( hWnd, hdc ); //デバイスコンテキストをリリース
midiOutOpen( &hMidi , MIDIMAPPER , 0 , 0 , 0 ); //MIDIMAPPERをオープン
midiOutShortMsg( hMidi, MIDIMSG( 0xC, 0, program, 0 ) ); //最初の初期化
ScrollBar_SetRange( hScroll1, 0, 743, TRUE ); //範囲を選択
ScrollBar_SetPos( hScroll1, nPos, TRUE ); //位置を選択
ScrollBar_SetRange( vScroll1, 0, 16 * 4 * 80, TRUE );
ScrollBar_SetPos( vScroll1, vPos, TRUE );
return TRUE;
case WM_DESTROY:
//---------------------------------------
// ウィンドウが破棄されたら
//---------------------------------------
DeleteDC( hdc_mem1 );
DeleteDC( hdc_mem2 );
midiOutReset( hMidi );
midiOutClose( hMidi );
PostQuitMessage( 0 );
break;
case WM_CLOSE:
//---------------------------------------
// ウィンドウを閉じる
//---------------------------------------
DeleteDC( hdc_mem1 );
DeleteDC( hdc_mem2 );
midiOutReset( hMidi );
midiOutClose( hMidi );
DestroyWindow( hWnd );
break;
case WM_TIMER://SetTimerでタイマーがセットされる
//---------------------------------------
// タイマ
//---------------------------------------
InvalidateRect( hWnd, NULL, TRUE ); //WM_PAINTを呼び出す(無効領域を作り出す)
break;
case WM_RBUTTONUP: {
//---------------------------------------
// デバッグ用イベント
//---------------------------------------
POINT pic2_pt = ClickOfPic2( hPic2 );
char str[100];
sprintf( str, "x=%d y=%d", pic2_pt.x, pic2_pt.y );
MessageBox( hWnd, "OK", str, MB_OK );
}
break;
case WM_LBUTTONDOWN: {
//---------------------------------------
// 鍵盤を押す
//---------------------------------------
POINT pic2_pt = ClickOfPic2( hPic2 );
POINTS clc;
note = PointToNote(lp,nPos,clc);
if( note != -1 ) //-1じゃなければ音を出す
midiOutShortMsg( hMidi, MIDIMSG( 0x9, 0x0, note, velocity ) );
if( clc.y > 128 ) { //clcが128以上なら
mouse.ClickFlag = 1; //クリックしているフラグをON
mouse.start.x = pic2_pt.x / 16 * 16;
mouse.start.y = pic2_pt.y / 16 * 16;
}
InvalidateRect( hPic2, NULL, TRUE );
printf( "mouse.start.x(%d), mouse.start.y(%d)\n", mouse.start.x, mouse.start.y );
}
return 0;
case WM_LBUTTONUP: {
//---------------------------------------
// 鍵盤を離す
//---------------------------------------
POINT pic2_pt = ClickOfPic2( hPic2 );
midiOutShortMsg( hMidi, MIDIMSG( 0x9, 0x0, note, 0 ) );
mouse.ClickFlag = 0; //クリックしているフラグをOFF
mouse.end.x = pic2_pt.x;
mouse.end.y = pic2_pt.y;
InvalidateRect( hPic2, NULL, TRUE );
mouse.DragStartFlag = 0; //
mouse.MoveFlag = 0;
POINT pt = {mouse.start.x + nPos,mouse.start.y + vPos};
// 楽譜に登録
noteMng.Register( pt, (mouse.end.y-mouse.start.y), note );
}
return 0;
case WM_MOUSEMOVE: {
//---------------------------------------
// マウスの移動
//---------------------------------------
POINT pic2_pt = ClickOfPic2( hPic2 );
mouse.MoveY = pic2_pt.y / 16 * 16; //16で割って、16をかける
mouse.MoveFlag = 1; //MoveFlagをON
POINTS clc;
note = PointToNote(lp,nPos,clc);
if( note != -1 )
if( mouse.ClickFlag == 1 && clc.y < 128 )
midiOutShortMsg( hMidi, MIDIMSG( 0x9, 0x0, note, velocity ) );
InvalidateRect( hPic2, NULL, TRUE );
}
return 0;
case WM_PAINT: {
//---------------------------------------
// 描画
//---------------------------------------
HPEN hPen, hOldPen;
HBRUSH hBrush, hOldBrush;
HBRUSH hBrush2, hOldBrush2;
int tbl[] = {2, 2, 1, 2, 2, 2, 1};
int X0 = nPos / 16, X1 = ( nPos + 440 ) / 16;
hdc = BeginPaint( hPic, &ps );
GetObject( hBmp, ( int )sizeof( BITMAP ), &bmp_info );
w = bmp_info.bmWidth;
h = bmp_info.bmHeight;
for( i = X0; i < X1; i++ )
BitBlt( hdc, i * 16 - nPos, 0, w, h, hdc_mem1, 0, 0, SRCCOPY );
GetObject( hBmp2, ( int )sizeof( BITMAP ), &bmp_info2 );
w = bmp_info2.bmWidth;
h = bmp_info2.bmHeight;
for( i = X0; i < X1; i++ )
BitBlt( hdc, i * 16 + tbl[i % 7] * 16 - 8 - nPos, 0, w, h, hdc_mem2, 0, 0, SRCCOPY );
EndPaint( hPic, &ps );
hdc = BeginPaint( hPic2, &ps );
hBrush = CreateSolidBrush( RGB( 245, 245, 245 ) );
hOldBrush = ( HBRUSH )SelectObject( hdc, hBrush );
Rectangle( hdc, 0, 0, 440, 120 );
hPen1 = CreatePen( PS_SOLID, 1, RGB( 0, 0, 0 ) );
SelectObject( hdc, hPen1 );
for( i = X0; i < X1; i++ ) {
MoveToEx( hdc, i * 16 - nPos, 0, NULL );
LineTo( hdc, i * 16 - nPos, 120 );
}
hBrush2 = CreateSolidBrush( RGB( 0, 120, 255 ) );
hOldBrush2 = ( HBRUSH )SelectObject( hdc, hBrush2 );
if( mouse.ClickFlag == 1 && mouse.MoveFlag == 1 ) {
Rectangle( hdc, mouse.start.x, mouse.start.y, mouse.start.x + 16, mouse.MoveY );
printf( " mouse.ClickFlag == 1 mouse.start.x(%d), mouse.start.y(%d)\n", mouse.start.x, mouse.start.y );
} else {
if( mouse.start.y - vPos > 0 ) {
// Rectangle( hdc, mouse.start.x - nPos + 371, mouse.start.y - vPos, mouse.start.x + 16 - nPos + 371, mouse.end.y - vPos );
printf( " mouse.ClickFlag == 0 mouse.start.x(%d), mouse.start.y(%d)\n", mouse.start.x, mouse.start.y );
}
}
// 楽譜の表示
for( i = 0; i < MAX_NOTE; i++ ) {
POINT start_pt,end_pt;
if( noteMng.GetPoint(i,start_pt,end_pt) ) {
// 表示座標に補正。
start_pt.x -= nPos;
start_pt.y -= vPos;
end_pt.x -= nPos;
end_pt.y -= vPos;
// 表示ガード
if( (start_pt.x>=0)&&(start_pt.y>=0)&&(end_pt.x<440)&&(end_pt.y<240) ) { //座標は適当です。
Rectangle( hdc, start_pt.x, start_pt.y, end_pt.x, end_pt.y);
printf( " start_pt=(%d,%d)\n", start_pt.x, start_pt.y );
}
}
}
DeleteObject( hPen1 );
DeleteObject( hBrush );
DeleteObject( hBrush2 );
EndPaint( hPic2, &ps );
}
return TRUE;
case WM_COMMAND:
switch ( LOWORD( wp ) ) {
case IDOK:
EndDialog( hWnd, 0 );
return TRUE;
case IDCANCEL:
return TRUE;
}
break;
}
return FALSE;
}