LoginSignup
1
0

More than 1 year has passed since last update.

2022-05-07 sound > 音階を指定してMIDIの音として鳴らす (C++ Builder Alexandria実装)

Last updated at Posted at 2022-05-07

動作環境

  • Windows 10 Pro
  • C++ Builder Alexandria
  • VCLプロジェクト

概要

マウスクリックで音符を指定して、MIDI音として鳴らす。

参考

動作例

image.png

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の処理を実装するようにした。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0