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

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

ただいまの
回答率

90.50%

  • C++

    3455questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 339

zakky79

score 9

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

#include <windows.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdio.h>
#include <time.h>
#include <process.h>

#include "MIDIIO.h"
#include "MIDIData.h"
#include "MIDIClock.h"
#include <opencv2/opencv.hpp>

SYSTEMTIME stTime;             /* 現在時刻獲得用変数         */
static char strTime[128];      /* 現在時刻表示用の文字列     */
double cur_time;               /* 現在時刻の,当日午前0時からの秒数 */


#define SHEET_GAP 20 //楽譜の表示位置の初期の値
#define NOTE_GAP 60 //音符の表示位置の初期の値

#define NOTE_H 12 //四分音符の表示する高さの初期の値
#define NOTE_W (NOTE_H * 1.05) //二分音符の表示する高さの初期の値
#define NOTE_ANGLE (-20) //音符の大きさ

#define SHEET_W (SHEET_GAP * 2 + NOTE_GAP * 4 * 4) //楽譜の横の長さ
#define SHEET_H (SHEET_GAP * 2 + NOTE_H * 4 + SHEET_GAP * 2) //楽譜の縦の長さ

#define NOTE_OFFSET (12 * 3) //音符表示できる数

#define USE_KEYBOARD 1 //0ならキーボード不使用、1ならキーボード使用

char* winname = "Sheet";

bool g_bContinue = true;

MIDIIn* pMIDIIn; //MIDIの初期化
MIDIOut* pMIDIOut;

HANDLE hGetNotesMutex; //ミューテックスのハンドル
HANDLE hNoteXMutex;

int note_x;
int note_x_by_midi;
int note_y[11];
int flag[2] = { 1, 1 }; // [0] for midi input, [1] for keyboard input

cv::Mat sheet_base;
cv::Mat sheet_note;
cv::Mat sheet_draw;
cv::Mat sheet_base2[4];


int current_x = SHEET_GAP;

std::vector<int> note_correct_x;
std::vector<int> note_correct_y;

std::vector<int> note_player_x;
std::vector<int> note_player_y;

int hitcount = 0;
double avelen = 0.0;
double avelen2[4] = { 0.0, 0.0, 0.0, 0.0 };

void pseudoColor(double index, unsigned char& B, unsigned char& G, unsigned char& R) { //擬似カラーの設定
                                                                                       // 0.0 <= index <= 1.0
    if (0.0 <= index && index <= 0.25) {
        B = 255;
        R = 0;
        G = (int)(256.0 / 64.0 * index * 255.0);
    }
    else if (0.25 < index && index <= 0.5) {
        G = 255;
        R = 0;
        B = (int)(-256.0 / 64.0 * index * 255.0 + 511.0);

    }
    else if (0.5 < index && index <= 0.75) {
        B = 0;
        G = 255;
        R = (int)(256.0 / 64.0 * index * 255.0 - 511.0);
    }
    else if (0.75 < index && index <= 1.0) {
        R = 255;
        B = 0;
        G = (int)(-256.0 / 64.0 * index * 255.0 + 256 * 3 - 1);
    }
    else {
        // Error
    }
}

void PutNote(cv::Mat& sheet, unsigned char status, unsigned char data1, unsigned char data2, long duration, cv::Scalar color, int note_x, int& index) { //音符の配置

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

        if (data1 == (0x18 + NOTE_OFFSET)) { // Do C
            index = 0;
        }
        else if (data1 == (0x26 + NOTE_OFFSET)) { // Re D
            index = 1;
        }
        else if (data1 == (0x1C + NOTE_OFFSET)) { // Mi E
            index = 2;
        }
        else if (data1 == (0x1D + NOTE_OFFSET)) { // Fa F
            index = 3;
        }
        else if (data1 == (0x1F + NOTE_OFFSET)) { // So G
            index = 4;
        }
        else if (data1 == (0x21 + NOTE_OFFSET)) { // Ra
            index = 5;
        }
        else if (data1 == (0x23 + NOTE_OFFSET)) { //Shi
            index = 6;
        }
        else if (data1 == (0x24 + NOTE_OFFSET)) { // Do
            index = 7;
        }
        else if (data1 == (0x1A + NOTE_OFFSET)) { // Re
            index = 8;
        }
        else if (data1 == (0x28 + NOTE_OFFSET)) {
            index = 9;
        }
        else if (data1 == (0x19 + NOTE_OFFSET)) {
            index = 10;
        }
        if (index != -1) {
            if (duration == 120) {
                cv::ellipse(sheet, cv::Point(note_x, note_y[index]), cv::Size(NOTE_W, NOTE_H / 2), NOTE_ANGLE, 0, 360, color, -1);
            }
            else if (duration == 240) {
                cv::ellipse(sheet, cv::Point(note_x, note_y[index]), cv::Size(NOTE_W, NOTE_H / 2), NOTE_ANGLE, 0, 360, color, 2);
            }
            else {
            }
        }
    }
    else if (status == 0x80 || data2 == 0x00) {
    }
}

void PutNoteData(int mode, cv::Mat& sheet, unsigned char status, unsigned char data1, unsigned char data2, long duration, cv::Scalar color, int note_x) {
    int index = -1;
    PutNote(sheet, status, data1, data2, duration, color, note_x, index);
    if (mode == 0) { // 楽譜構築モード
        note_correct_x.push_back(note_x);
        note_correct_y.push_back(index);
    }
    else if (mode == 1) { // ユーザによる演奏音符反映モード
        note_player_x.push_back(note_x);
        note_player_y.push_back(index);
    }
    else {
        // mode == -1 // 時刻に対応する音符反映モード
    }
}

unsigned __stdcall GetNotesThread(void *p)
{

    long lLen;
    unsigned char byMessage[256];

    /* MIDIメッセージの取得ループ */
    while (g_bContinue) {
        WaitForSingleObject(hGetNotesMutex, INFINITE); //mutex 間は他のスレッドから変数を変更できない
        lLen = MIDIIn_GetMIDIMessage(pMIDIIn, byMessage, 256);
        ReleaseMutex(hGetNotesMutex);

        /* MIDIメッセージを取得した */
        if (lLen > 0) {

            /* スレッド */
            WaitForSingleObject(hGetNotesMutex, INFINITE);
            MIDIOut_PutMIDIMessage(pMIDIOut, byMessage, lLen);
            for (int i = 0; i < lLen; i++) {
                std::cerr << "0x" << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)byMessage[i] << " ";
            }
            std::cerr << "/ ";;
            ReleaseMutex(hGetNotesMutex);

            unsigned char status = byMessage[0];
            unsigned char data1 = byMessage[1];
            unsigned char data2 = byMessage[2];
            if ((status & 0xF0) == 0x99 && data2 != 0x00) {
                hitcount++;

                WaitForSingleObject(hNoteXMutex, INFINITE); //mutex 間は他のスレッドから変数を変更できない
                PutNoteData(1, sheet_base2[0], status, data1, data2, 120, cv::Scalar(127, 127, 127), current_x);

                int key_x = note_player_x[note_player_x.size() - 1];
                int key_y = note_player_y[note_player_y.size() - 1];

                int nearest_index = 0;
                int nearest_length = 99999;

                for (int i = 0; i < note_correct_x.size(); i++) {
                    if (abs(note_correct_x[i] - key_x) < nearest_length) {
                        nearest_index = i;
                        nearest_length = abs(note_correct_x[i] - key_x);
                    }
                }

                int near_x = note_correct_x[nearest_index];
                int near_y = note_correct_y[nearest_index];
                double diff;

                // CalcPseudoColor
                unsigned char b, g, r;

                //ずれ情報なし
                diff = 0.0;
                pseudoColor(diff, b, g, r);
                PutNoteData(-1, sheet_base2[0], status, data1, data2, 120, cv::Scalar(b, g, r), current_x);
                avelen2[0] += diff;

                //時間のずれ
                diff = abs(key_x - near_x) / 30.0; // 0 ~ 30
                if (diff > 1.0) {
                    diff = 1.0;
                }
                pseudoColor(diff, b, g, r);
                PutNoteData(-1, sheet_base2[1], status, data1, data2, 120, cv::Scalar(b, g, r), current_x);
                avelen2[1] += diff;

                //音程のずれ    
                diff = abs((double)key_y - (double)near_y) / 4.0; //高さのindexの差
                fprintf(stderr, "diff0 = %f\n", diff);
                if (diff > 1.0) {
                    diff = 1.0;
                }
                pseudoColor(diff, b, g, r);
                PutNoteData(-1, sheet_base2[2], status, data1, data2, 120, cv::Scalar(b, g, r), current_x);
                avelen2[2] += diff;
                fprintf(stderr, "diff = %f\n", diff);
                fprintf(stderr, "hitcount = %d\n", hitcount);

                //音程と時間両方のずれ
                double diff0 = abs(key_x - near_x) / 30.0;
                double diff1 = abs((double)key_y - (double)near_y) / 4.0;
                diff = sqrt(diff0 * diff0 + diff1 * diff1) / sqrt(2.0);
                if (diff > 1.0) {
                    diff = 1.0;
                }
                pseudoColor(diff, b, g, r);
                PutNoteData(-1, sheet_base2[3], status, data1, data2, 120, cv::Scalar(b, g, r), current_x);
                avelen2[3] += diff;

                ReleaseMutex(hNoteXMutex);
            }
        }
        /* MIDIメッセージを取得しなかった */
        else {
            /* スリープ処理 */
            Sleep(1);
        }
    }

    _endthreadex(0);

    return 0; //コンパイラの警告を殺す
}


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

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 23:04

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

    キャンセル

  • 2017/12/21 23:09

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

    キャンセル

  • 2017/12/21 23:29

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

    キャンセル

  • 2017/12/21 23:31

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

    キャンセル

  • 2017/12/21 23:42

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

    キャンセル

  • 2017/12/22 00:18

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

    キャンセル

  • 2017/12/22 00:18 編集

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

    キャンセル

  • 2017/12/22 00: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%がおかしいんですかね?

    キャンセル

  • 2017/12/22 00:23

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

    キャンセル

  • 2017/12/22 00: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なので。

    キャンセル

  • 2017/12/22 01:05

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

    キャンセル

  • 2017/12/22 09:58

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

    キャンセル

  • 2017/12/22 11:16

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

    キャンセル

  • 2017/12/22 11:19

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

    キャンセル

  • 2017/12/22 11:29

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

    キャンセル

  • 2017/12/22 11:30 編集

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

    キャンセル

  • 2017/12/22 12:36

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

    キャンセル

  • 2017/12/22 12:39

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

    キャンセル

  • 2018/01/10 16:17

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

    キャンセル

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 受付中

    C:大きな数の計算方法,オーバーフロー回避

    C言語で3^80の計算をしたいのですが、数が大きすぎてオーバーフローしてしまいます。各桁ごとに配列を置けばいいのかとも思いましたが、いまいちよくわかりません。 解決方法が分かる方

  • 解決済

    C言語のunsigned doubleとは何か?

    C言語にはunsigned doubleという型があるらしいという情報を入手しました。しかし、C言語規格上はそのような型は存在しませんし、手元のコンパイラ(LLVMとGCC)ではコ

  • 受付中

    プログラムを見やすく改良したい

    正常に動くプルグラムを見やすく改良したい。 具体的に教えていただければありがたいです。セグメンテーションフォルトでベスト7まで表示して停止します。173行あたりだと思うのですが、よ

  • 解決済

    fscanfがうまく動作しない

    前提・実現したいこと テキストファイルから情報を取り込みたい。 中身は Dbrenlhsij,11,162.2,55.9 Ijnpwthy,14,163.8,62.6 Csaztv

  • 解決済

    android opencv で色検出

    私はandroidstudioでアプリ開発を行なっており今opencvを使っています。 フレームの中に入っている緑の色をしている領域の中で一番面積が大きいものを選び出し、その輪郭を

  • 解決済

    pygameを用いたMIDIの制御

    実現したい内容 pygameを用いてMIDIデバイスの入力を受け取り、リアルタイムでSin波で演奏 複数音の出力および、ベロシティの対応  発生してる問題 キーを押した時と離し

  • 解決済

    C言語で複数の音を同時に鳴らす。

     前提・実現したいこと C言語を用いて、Beep関数を使用し、周波数と再生時間を指定して実装していましたが、同時に鳴らす場合はどの様にすればよいのだろうかと思い、質問させていただき

  • 解決済

    opencvの重心を求めるプログラムにopencvの関数ではない重心を求めるプログラムを入れたいです...

    <実行した環境> Windows10 64bit visual studio 2017 opencv 3.4.1 opencvの重心を求めるプログラムにopen

同じタグがついた質問を見る

  • C++

    3455questions

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