質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

676閲覧

MIDIデータの読み込みで音符の表示

zakky79

総合スコア23

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/12/21 11:46

編集2017/12/21 14:30

MIDIデータを読み込んで、それを楽譜の模範演奏として表示したいと考えています。
この質問のひとつ前に質問をした時のちょうちょ(音はピアノ)を楽譜で表示すると画像のようになります。

c++

1#include <windows.h> 2#include <iostream> 3#include <fstream> 4#include <iomanip> 5#include <stdio.h> 6#include <time.h> 7#include <process.h> 8 9#include "MIDIIO.h" 10#include "MIDIData.h" 11#include "MIDIClock.h" 12#include <opencv2/opencv.hpp> 13 14SYSTEMTIME stTime; /* 現在時刻獲得用変数 */ 15static char strTime[128]; /* 現在時刻表示用の文字列 */ 16double cur_time; /* 現在時刻の,当日午前0時からの秒数 */ 17 18 19#define SHEET_GAP 20 //楽譜の表示位置の初期の値 20#define NOTE_GAP 60 //音符の表示位置の初期の値 21 22#define NOTE_H 12 //四分音符の表示する高さの初期の値 23#define NOTE_W (NOTE_H * 1.05) //二分音符の表示する高さの初期の値 24#define NOTE_ANGLE (-20) //音符の大きさ 25 26#define SHEET_W (SHEET_GAP * 2 + NOTE_GAP * 4 * 4) //楽譜の横の長さ 27#define SHEET_H (SHEET_GAP * 2 + NOTE_H * 4 + SHEET_GAP * 2) //楽譜の縦の長さ 28 29#define NOTE_OFFSET (12 * 3) //音符表示できる数 30 31#define USE_KEYBOARD 1 //0ならキーボード不使用、1ならキーボード使用 32 33char* winname = "Sheet"; 34 35bool g_bContinue = true; 36 37MIDIIn* pMIDIIn; //MIDIの初期化 38MIDIOut* pMIDIOut; 39 40HANDLE hGetNotesMutex; //ミューテックスのハンドル 41HANDLE hNoteXMutex; 42 43int note_x; 44int note_x_by_midi; 45int note_y[11]; 46int flag[2] = { 1, 1 }; // [0] for midi input, [1] for keyboard input 47 48cv::Mat sheet_base; 49cv::Mat sheet_note; 50cv::Mat sheet_draw; 51cv::Mat sheet_base2[4]; 52 53 54int current_x = SHEET_GAP; 55 56std::vector<int> note_correct_x; 57std::vector<int> note_correct_y; 58 59std::vector<int> note_player_x; 60std::vector<int> note_player_y; 61 62int hitcount = 0; 63double avelen = 0.0; 64double avelen2[4] = { 0.0, 0.0, 0.0, 0.0 }; 65 66void pseudoColor(double index, unsigned char& B, unsigned char& G, unsigned char& R) { //擬似カラーの設定 67 // 0.0 <= index <= 1.0 68 if (0.0 <= index && index <= 0.25) { 69 B = 255; 70 R = 0; 71 G = (int)(256.0 / 64.0 * index * 255.0); 72 } 73 else if (0.25 < index && index <= 0.5) { 74 G = 255; 75 R = 0; 76 B = (int)(-256.0 / 64.0 * index * 255.0 + 511.0); 77 78 } 79 else if (0.5 < index && index <= 0.75) { 80 B = 0; 81 G = 255; 82 R = (int)(256.0 / 64.0 * index * 255.0 - 511.0); 83 } 84 else if (0.75 < index && index <= 1.0) { 85 R = 255; 86 B = 0; 87 G = (int)(-256.0 / 64.0 * index * 255.0 + 256 * 3 - 1); 88 } 89 else { 90 // Error 91 } 92} 93 94void PutNote(cv::Mat& sheet, unsigned char status, unsigned char data1, unsigned char data2, long duration, cv::Scalar color, int note_x, int& index) { //音符の配置 95 96 if ((status & 0xF0) == 0x90 && data2 != 0x00) { 97 98 if (data1 == (0x18 + NOTE_OFFSET)) { // Do C 99 index = 0; 100 } 101 else if (data1 == (0x26 + NOTE_OFFSET)) { // Re D 102 index = 1; 103 } 104 else if (data1 == (0x1C + NOTE_OFFSET)) { // Mi E 105 index = 2; 106 } 107 else if (data1 == (0x1D + NOTE_OFFSET)) { // Fa F 108 index = 3; 109 } 110 else if (data1 == (0x1F + NOTE_OFFSET)) { // So G 111 index = 4; 112 } 113 else if (data1 == (0x21 + NOTE_OFFSET)) { // Ra 114 index = 5; 115 } 116 else if (data1 == (0x23 + NOTE_OFFSET)) { //Shi 117 index = 6; 118 } 119 else if (data1 == (0x24 + NOTE_OFFSET)) { // Do 120 index = 7; 121 } 122 else if (data1 == (0x1A + NOTE_OFFSET)) { // Re 123 index = 8; 124 } 125 else if (data1 == (0x28 + NOTE_OFFSET)) { 126 index = 9; 127 } 128 else if (data1 == (0x19 + NOTE_OFFSET)) { 129 index = 10; 130 } 131 if (index != -1) { 132 if (duration == 120) { 133 cv::ellipse(sheet, cv::Point(note_x, note_y[index]), cv::Size(NOTE_W, NOTE_H / 2), NOTE_ANGLE, 0, 360, color, -1); 134 } 135 else if (duration == 240) { 136 cv::ellipse(sheet, cv::Point(note_x, note_y[index]), cv::Size(NOTE_W, NOTE_H / 2), NOTE_ANGLE, 0, 360, color, 2); 137 } 138 else { 139 } 140 } 141 } 142 else if (status == 0x80 || data2 == 0x00) { 143 } 144} 145 146void PutNoteData(int mode, cv::Mat& sheet, unsigned char status, unsigned char data1, unsigned char data2, long duration, cv::Scalar color, int note_x) { 147 int index = -1; 148 PutNote(sheet, status, data1, data2, duration, color, note_x, index); 149 if (mode == 0) { // 楽譜構築モード 150 note_correct_x.push_back(note_x); 151 note_correct_y.push_back(index); 152 } 153 else if (mode == 1) { // ユーザによる演奏音符反映モード 154 note_player_x.push_back(note_x); 155 note_player_y.push_back(index); 156 } 157 else { 158 // mode == -1 // 時刻に対応する音符反映モード 159 } 160} 161 162unsigned __stdcall GetNotesThread(void *p) 163{ 164 165 long lLen; 166 unsigned char byMessage[256]; 167 168 /* MIDIメッセージの取得ループ */ 169 while (g_bContinue) { 170 WaitForSingleObject(hGetNotesMutex, INFINITE); //mutex 間は他のスレッドから変数を変更できない 171 lLen = MIDIIn_GetMIDIMessage(pMIDIIn, byMessage, 256); 172 ReleaseMutex(hGetNotesMutex); 173 174 /* MIDIメッセージを取得した */ 175 if (lLen > 0) { 176 177 /* スレッド */ 178 WaitForSingleObject(hGetNotesMutex, INFINITE); 179 MIDIOut_PutMIDIMessage(pMIDIOut, byMessage, lLen); 180 for (int i = 0; i < lLen; i++) { 181 std::cerr << "0x" << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)byMessage[i] << " "; 182 } 183 std::cerr << "/ ";; 184 ReleaseMutex(hGetNotesMutex); 185 186 unsigned char status = byMessage[0]; 187 unsigned char data1 = byMessage[1]; 188 unsigned char data2 = byMessage[2]; 189 if ((status & 0xF0) == 0x99 && data2 != 0x00) { 190 hitcount++; 191 192 WaitForSingleObject(hNoteXMutex, INFINITE); //mutex 間は他のスレッドから変数を変更できない 193 PutNoteData(1, sheet_base2[0], status, data1, data2, 120, cv::Scalar(127, 127, 127), current_x); 194 195 int key_x = note_player_x[note_player_x.size() - 1]; 196 int key_y = note_player_y[note_player_y.size() - 1]; 197 198 int nearest_index = 0; 199 int nearest_length = 99999; 200 201 for (int i = 0; i < note_correct_x.size(); i++) { 202 if (abs(note_correct_x[i] - key_x) < nearest_length) { 203 nearest_index = i; 204 nearest_length = abs(note_correct_x[i] - key_x); 205 } 206 } 207 208 int near_x = note_correct_x[nearest_index]; 209 int near_y = note_correct_y[nearest_index]; 210 double diff; 211 212 // CalcPseudoColor 213 unsigned char b, g, r; 214 215 //ずれ情報なし 216 diff = 0.0; 217 pseudoColor(diff, b, g, r); 218 PutNoteData(-1, sheet_base2[0], status, data1, data2, 120, cv::Scalar(b, g, r), current_x); 219 avelen2[0] += diff; 220 221 //時間のずれ 222 diff = abs(key_x - near_x) / 30.0; // 0 ~ 30 223 if (diff > 1.0) { 224 diff = 1.0; 225 } 226 pseudoColor(diff, b, g, r); 227 PutNoteData(-1, sheet_base2[1], status, data1, data2, 120, cv::Scalar(b, g, r), current_x); 228 avelen2[1] += diff; 229 230 //音程のずれ 231 diff = abs((double)key_y - (double)near_y) / 4.0; //高さのindexの差 232 fprintf(stderr, "diff0 = %f\n", diff); 233 if (diff > 1.0) { 234 diff = 1.0; 235 } 236 pseudoColor(diff, b, g, r); 237 PutNoteData(-1, sheet_base2[2], status, data1, data2, 120, cv::Scalar(b, g, r), current_x); 238 avelen2[2] += diff; 239 fprintf(stderr, "diff = %f\n", diff); 240 fprintf(stderr, "hitcount = %d\n", hitcount); 241 242 //音程と時間両方のずれ 243 double diff0 = abs(key_x - near_x) / 30.0; 244 double diff1 = abs((double)key_y - (double)near_y) / 4.0; 245 diff = sqrt(diff0 * diff0 + diff1 * diff1) / sqrt(2.0); 246 if (diff > 1.0) { 247 diff = 1.0; 248 } 249 pseudoColor(diff, b, g, r); 250 PutNoteData(-1, sheet_base2[3], status, data1, data2, 120, cv::Scalar(b, g, r), current_x); 251 avelen2[3] += diff; 252 253 ReleaseMutex(hNoteXMutex); 254 } 255 } 256 /* MIDIメッセージを取得しなかった */ 257 else { 258 /* スリープ処理 */ 259 Sleep(1); 260 } 261 } 262 263 _endthreadex(0); 264 265 return 0; //コンパイラの警告を殺す 266}

これをドラムの音でも同じように楽譜に表示させたいと思うんですが、0x00を0x09に変え、スネアドラムは16進数で0x26なので、ドの0x18を0x26に変えて実行したところ、何も表示されませんでした。midiの音も鳴っていないので、midiの読み込みから失敗しているのでしょうか。ピアノの音のmidiは鳴っています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

statusは多分MIDIメッセージのステータスですよね?Note Onは

0x9X

で、Xはチャネル番号ですので、ドラムパートをCH#10で演奏する場合は

status = 0x99

になると思います。上のコードでは

if (status == 0x90 && data2 != 0x00)

としていますが、これは

if ((status & 0xF0) == 0x90 && data2 != 0x00)

とすべきではないでしょうか?

投稿2017/12/21 12:49

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zakky79

2017/12/21 14:04

書き換えて実行しましたが、変わらずでした。。。
KSwordOfHaste

2017/12/21 14:09

元のピアノ用プログラムで音符が表示されるとすればこのくらしか思いつきませんでした。上の関数が呼び出される部分も含めて処理の流れが把握できる程度の範囲を提示すれば閲覧者が問題に気づきやすくなると思いますがいかがでしょう?
zakky79

2017/12/21 14:29

音は鳴るようになりました!音符の表示がされないので、少しプログラム多めに記載します。
KSwordOfHaste

2017/12/21 14:31

失礼、 この関数が呼び出されたときに引数をデバッグプリントするようにしてデバッグした方が早い気がします。
zakky79

2017/12/21 14:42

すいません。デバッグプリントに関して勉強します。。。
KSwordOfHaste

2017/12/21 15:18

そう難しく考えなくてもいいです。PutNote関数の先頭で引数をprintfなどで出力すればいいのです。実行すると引数の値が出力されますので、どんな引数が渡ってくるかの「事実」がわかります。その引数の値を元に自分の関数がどう動くかを考えれば原因に気づきやすくなります。またわからない場合でも引数がこうで、関数の定義がこうなっていると質問に書けば閲覧者からは原因がつかみやすくなります。
KSwordOfHaste

2017/12/21 15:19 編集

--入力ミスのため削除--
zakky79

2017/12/21 15:22

もしかして、 if (status == 0x90) { int lRet = pMIDIEvent->m_lLen; /* MIDI出力デバイスからメッセージを送出する */ WaitForSingleObject(hGetNotesMutex, INFINITE); MIDIOut_PutMIDIMessage(pMIDIOut, pMIDIEvent->m_pData, lRet); for (int i = 0; i < lRet; i++) { fprintf(stderr, "0x%02X ", pMIDIEvent->m_pData[i]); } printf("/ "); ReleaseMutex(hGetNotesMutex); } この部分の0x%02%がおかしいんですかね?
zakky79

2017/12/21 15:23

なるほど。。。参考になります!
KSwordOfHaste

2017/12/21 15:48

> この部分の0x%02X Note Onイベントの場合にlRetが3になり、m_pDataがunsigned char相当の型なら正しいと思いますよ。CH#10へA4、ベロシティー127でNote Onならこのfprintfで 0x99 0x45 0x7F とでれば期待通りと思います。keyがA4なら=>69=0x45, 127=>0x7Fなので。
zakky79

2017/12/21 16:05

常に0xF8と出力され電子ドラムを叩いた時に0x99 0x26 0x00と出力されます。他に音符を表示する部分で怪しいところが見当たらないです・・・
KSwordOfHaste

2017/12/22 00:58

F8はMIDI機器のTiming Clockなのでそれはスルーした方がよいのでは?肝心な部分のデバッグプリントが流れちゃいます。ドラムをたたいたときにベロシティーが0になってますね。PutNoteではdata2!=0つまりベロシティーが0じゃないというのが条件になってるのでそれが原因じゃないでしょうか?お持ちのMIDIドラムの仕様なのかもですが、ベロシティー=0にしかならないパートがあるんでしょうか・・・ドラムも強弱をベロシティーへ反映されるものかと思ってましたが・・・
zakky79

2017/12/22 02:16

確認したところ、0x00にならないパートもありました。 叩く強さによって0x36や0x40になることを確認しました。
zakky79

2017/12/22 02:19

どのパートも叩くのが少し弱いと0x00になってしまようです。
KSwordOfHaste

2017/12/22 02:29

なるほど、そういうことならドラムパートだけは0x00でも音符を表示するようコードを変更する必要がありそうですね!ナンチャッテな判定ですが、 if ((status & 0xF0) == 0x90 && data2 != 0x00) ==> if ((status & 0xF0) == 0x90 && ((status & 0x0F) == 9 || data2 != 0x00)) のように「CH#10だけはベロシティーが0でも描画する」ように変更する必要があろうかと思います。
KSwordOfHaste

2017/12/22 02:31 編集

あ・・・でもベロシティー0で音源の方から音がでるんでしょうか(w; もし音がでないならMIDI-INから0x99 xx 0x00を受信した際にベロシティーを少し底上げするといった対処の方がよいかも知れません。
zakky79

2017/12/22 03:36

0x00の時でも小さく音は鳴っています。0x99 0x00といった受信はないです。必ず0x99 0x2B 0x00といった3Byteで送られてきています。
KSwordOfHaste

2017/12/22 03:39

音が鳴っているなら3つ上のif文の変更でいけそうだと思います。 > 0x99 0x00といった受信はないです。 いえ、そういう意味ではありません。0x99 xx 0x00の真ん中のxxは任意のバイトという意図でした。 0x99 0xXX 0x00と書いた方がよかったですかね。
zakky79

2018/01/10 07:17

返信遅くなりすいません。結局音符の描画がうまくいきませんでした。現状まとめ、再度質問立て直します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問