動作環境
- Windows 10 Pro
- C++ Builder Alexandria
- VCLプロジェクト
概要
マウスクリックで音符を指定して、MIDI音として鳴らす。
参考
動作例
bg.bmp
Qiitaにbmpファイルを貼ろうとしてエラーになるので説明だけ。
600 x 390のサイズで画像を用意した。
各音階のY方向は30としている。
上位が「ド」の音に対応するように鍵盤を描画している。
実装
Main.h
//---------------------------------------------------------------------------
#ifndef MainH
#define MainH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ColorGrd.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TFormMain : public TForm
{
__published: // IDE で管理されるコンポーネント
TButton *B_play;
void __fastcall FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
int X, int Y);
void __fastcall B_playClick(TObject *Sender);
private: // ユーザー宣言
public: // ユーザー宣言
__fastcall TFormMain(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormMain *FormMain;
//---------------------------------------------------------------------------
#endif
Main.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <MMSystem.h> // for MIDI
#include "Main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormMain *FormMain;
//---------------------------------------------------------------------------
// 音符シート
#define XMAP (600)
#define YMAP (390)
// 音符サイズ
#define XDIV (30)
#define YDIV (30)
// 音符の数
#define XSIZE (XMAP / XDIV)
#define YSIZE (YMAP / YDIV)
// 音符
static bool s_sound[YSIZE][XSIZE] = {0};
// 音符のスプライト
#define XSPR (20) // スプライトサイズ
#define YSPR (10) // スプライトサイズ
__fastcall TFormMain::TFormMain(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFormMain::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift,
int X, int Y)
{
if (X > XMAP || Y > YMAP) {
return;
}
Graphics::TBitmap *Bitmap = new Graphics::TBitmap();
Bitmap->LoadFromFile("bg.bmp");
FormMain->Canvas->Brush->Bitmap = Bitmap;
FormMain->Canvas->FillRect( Rect(0,0,XMAP,YMAP) );
int posx = X / XDIV;
int posy = Y / YDIV;
s_sound[posy][posx] = !s_sound[posy][posx];
for(int idy=0; idy < YSIZE; idy++) {
for(int idx=0; idx < XSIZE; idx++) {
if (s_sound[idy][idx]) {
int xst = idx * XDIV + XDIV / 2 - XSPR / 2;
int yst = idy * YDIV + YDIV / 2 - YSPR / 2;
FormMain->Canvas->Brush->Color = clRed;
FormMain->Canvas->FillRect( Rect(xst,yst,xst+XSPR,yst+YSPR) );
}
}
}
}
//---------------------------------------------------------------------------
// 和音を出す
// https://teratail.com/questions/123462
HMIDIOUT ghMidiOut;
void noteOn(unsigned iNote, unsigned iVelocity)
{
unsigned packdata = 0x90 | (iNote << 8) | (iVelocity << 16);
midiOutShortMsg(ghMidiOut, packdata);
}
void noteOff(unsigned iNote)
{
unsigned packdata = 0x80 | (iNote << 8);
midiOutShortMsg(ghMidiOut, packdata);
}
void __fastcall TFormMain::B_playClick(TObject *Sender)
{
// // A. 単音
//
// Beep(1046.502, 250); // do
// Beep(1174.659, 250); // re
// Beep(1318.510, 250); // mi
// Beep(1396.913, 250); // fa
// Beep(1567.982 ,250); // so
// Beep(1760.000, 250); // ra
// Beep(1975.533, 250); // si
// Beep(2093.005, 250); // do
// B. 和音可能
// https://teratail.com/questions/123462
// http://www.aislab.org/multimedia_exp/sample4.1.html
MMRESULT mmres = midiOutOpen(&ghMidiOut, MIDI_MAPPER, NULL, NULL, CALLBACK_NULL);
if(mmres != MMSYSERR_NOERROR) {
fprintf(stderr, "MIDIが利用できません。\n");
return;
}
static int solfa[ ] = { 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81 }; // do re mi fa so la si do ...
int size_solfa = sizeof(solfa) / sizeof(solfa[0]);
Sleep(300);
for(int idx=0; idx < XSIZE; idx++) {
// 1. ON
for(int idy=0; idy < YSIZE; idy++) {
if (idy < size_solfa) {
if (s_sound[idy][idx]) {
noteOn(solfa[idy], 127);
}
}
}
// 2. wait
Sleep(300);
// 3. OFF
for(int idy=0; idy < YSIZE; idy++) {
if (idy < size_solfa) {
if (s_sound[idy][idx]) {
noteOff(solfa[idy]);
}
}
}
}
Sleep(300);
midiOutClose(ghMidiOut);
}
//---------------------------------------------------------------------------
備考
Beep()を使うと和音ができないため、MIDIの処理を実装するようにした。