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

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

ただいまの
回答率

90.75%

  • C++

    3243questions

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

二つの音を同時に表示させたい

解決済

回答 2

投稿 編集

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

zakky79

score 9

ドラムのmidiファイルを読み込ませ、それを楽譜に表示させようとしています。
現段階で表示自体はできているのですが、ハイハットとバスドラムの音が同時に鳴っている場合、ハイハットしか表示されない。ハイハットとスネアドラムの音が同時に鳴っている場合、ハイハットしか表示されない状態になっています。(下記の図のようになります。)
![![イメージ説明
元々のコードが単音のみしか表示させるようになっていないので、二つ同時に表示させるように変更したいのですが、どうしたらいいですか?アドバイスお願いします!

/***** 楽譜の描画 *****/
    forEachTrack(pMIDIData, pMIDITrack) {
        forEachEvent(pMIDITrack, pMIDIEvent) {

            int lLen = pMIDIEvent->m_lLen;

            if (note_x < SHEET_W - SHEET_GAP * 2 && MIDIEvent_IsNoteOn(pMIDIEvent)/*&& counttest < 30*/) {
                std::cerr << "Event Size: " << lLen << "(Bytes)" << std::endl;
                unsigned char status = pMIDIEvent->m_pData[0];
                unsigned char data1 = pMIDIEvent->m_pData[1];
                unsigned char data2 = pMIDIEvent->m_pData[2];
                long duration = MIDIEvent_GetDuration(pMIDIEvent);

                fprintf(stderr, "status, data1, data2 = 0x%02x, 0x%02x, 0x%02x\n", status, data1, data2);

                PutNoteData(0, sheet_base, status, data1, data2, duration, cv::Scalar(0, 0, 0), note_x);

                if (duration == 120) {
                    note_x += NOTE_GAP;
                }
                else if (duration == 240) {
                    note_x += NOTE_GAP * 2;
                }
                else {
                    //Error
                }
            }
        }
    }

    note_x = SHEET_GAP + NOTE_GAP / 2; // 初期化
    note_x_by_midi = SHEET_GAP + NOTE_GAP / 2; // 初期化
    cv::imshow(winname, sheet_base);
    cv::moveWindow(winname, 0, 0);
    int key = cv::waitKey(1);

    /* */
    long lRet;

MIDIデータ自体は、プログラムで作りました。フリーなどでダウンロードできるものではありません。TIMEが同じ時にその音が二つ鳴ります。チャンネルの42はハイハット。36はバスドラム。38はスネアドラムです。

#include <stdio.h>
#include <MIDIData.h>
int main() {
    MIDIData* pMIDIData;
    MIDITrack* pMIDITrack;
    /* MIDIデータの生成(フォーマット0,トラック数1,TPQNベース,120) */
    pMIDIData = MIDIData_Create(MIDIDATA_FORMAT0, 1, MIDIDATA_TPQNBASE, 120);
    if (pMIDIData == NULL) {
        printf("MIDIデータの生成に失敗しました。\n");
        return 0;
    }
    /* 最初のトラックへのポインタを取得 */
    pMIDITrack = MIDIData_GetFirstTrack(pMIDIData);
    /* イベントを挿入 */
    MIDITrack_InsertTrackName(pMIDITrack, 0, "ちょうちょ"); /* タイトル */
    MIDITrack_InsertTempo(pMIDITrack, 0, 60000000 / 120); /* 120BPM */
    MIDITrack_InsertProgramChange(pMIDITrack, 0, 9, 0); /* Piano1 */
   /* ノートイベントを挿入            Time  ch  key  vel  dur */
    MIDITrack_InsertNote(pMIDITrack, 0, 9, 42, 100, 120); 
    MIDITrack_InsertNote(pMIDITrack, 0, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 120, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 240, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 240, 9, 38, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 360, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 480, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 480, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 600, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 600, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 720, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 720, 9, 38, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 840, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 960, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 960, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1080, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1200, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1200, 9, 38, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1320, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1440, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1440, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1560, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1560, 9, 36, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1680, 9, 42, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1680, 9, 38, 100, 120);
    MIDITrack_InsertNote(pMIDITrack, 1800, 9, 42, 100, 120);

                                                             /* エンドオブトラックイベントを挿入 */
    MIDITrack_InsertEndofTrack(pMIDITrack, 1920);
    /* MIDIデータを保存 */
    MIDIData_SaveAsSMF(pMIDIData, "eightbeat.mid");
    /* MIDIデータをメモリ上から削除 */
    MIDIData_Delete(pMIDIData);
    pMIDIData = NULL;
    return 1;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • PineMatsu

    2018/01/22 17:40

    知っている人なら別ですが、MIDIデータの構造がわからないと答えようがないと思います。teratailにMIDIを扱った人がいれば別ですが、少ないんじゃないかな?

    キャンセル

  • zakky79

    2018/01/22 17:54

    追記させてもらいました。再び何かあれば追記しますのでよろしくお願いします。

    キャンセル

回答 2

+4

MIDIのトラックからMIDIイベント情報を拾ってきてそれで音符を描画というプログラムのようですが、「どの時点で発音するか」について考慮されておらず単純に「順番に一定の時間で発音する」という前提でコードを書いておられるのでご質問のような譜面になってしまいます。

ポイントは「そのノートが何時発音するべきものか」によってX座標を決めることです。ちょっと調べてみたところ

MIDIEvent_GetTime (pNoteOnEvent);

で曲の頭からの発音タイミングが得られそうです。それに基づき適当にX座標を決めるようにすればよいのではないでしょうか。なお、自分はお使いのライブラリーの仕様を知らないのですがMIDIの規格についていえば各MIDIイベントに記録されているのは絶対時間ではなく「ある可変の単位時間」によるカウント値のはずです。ですので、

MIDIEvent_GetTime (pNoteOnEvent);

の仕様を調べたり実験したりして適当な係数によりX座標を決めてやればよいでしょう。


補足:

PineMatsuさんがコメントしておられるように、MIDI規格はプログラマーが誰でも知っているような規格ではないと思います。自分が知る限りではここteratailでMIDIプログラミングの経験がおありだろうと気づいた方はおひとりでした。実際にはご存知の方がもう少しいらっしゃるかも知れませんがマイナーな分野であるのは間違いないと思います。

こういうものについて取り組む場合はなるべく自分自身で解決方法を探れるよう「仕様書を探しそれを見て内容を把握する」というプロセスは不可欠だと思います。そういう心構えもしておいたほうがよいのではないでしょうか?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/22 20:10 編集

    補足コメントに少々マイナスイメージを持たれるかも知れません。マイナーな規格なら自分で調べた方がよいとは書きましたが、本来はマイナーかどうかには関係なく「それに取り組むならどんな規格であろうが自分で読む」という態度は大切だと思います。

    それはそれとして計算機やネットワーク帯域の性能が高くなったため、音楽を扱うのにMIDIより音声信号の方が身近な時代になりました。しかしながら音楽を演奏したりする方々にとってはMIDIは今だ有効なツールだと思います。個人的な思いとしては質問者さんに楽しくMIDIに取り組んでほしいなと思います。

    キャンセル

  • 2018/01/23 18:02

    ありがとうございます。まだまだ自分で知識を増やす姿勢というものが低かったです。そして調べるという作業が下手で見当違いな調べ方をしてしまっていたりとなかなか進まなかったりという状況でした。
    質問するときの準備も怠らずこれからは取り組もうと思います。続けて作業進め試してみますのでまた質問するときは回答のほどよろしくお願いします。

    キャンセル

checkベストアンサー

0

note_xに対してMIDIデータのノートごとにdurationを加算しているのが良くないですね。それだと同じ位置にノートを描画することは不可能です。

ノートONのタイミングがMIDIデータのTimeに設定されているようなので、そのTimeをそのままnote_xに渡せばよろしいかと思います。


ちょっと間違えました。訂正です。

duration120に対してnote_xNOTE_GAPですね。なので、note_x = Time値 * NOTE_GAP / 120という感じにスケーリングする必要がありますね。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/23 23:47

    なるほど。。。するとTimeを読み取るようにしないといけなくて、それをnote_xに渡さないといけないってことですね。Timeっていうのは、midiファイルを読み込んでいる時点で、取得されていると考えてもいいのですか?

    キャンセル

  • 2018/01/23 23:52

    質問の下に貼り付けてあるコードの中に、`/* ノートイベントを挿入 Time ch key vel dur */`とコメントがあるので、その下に並んでいるノートイベントデータの最初の数字がTimeだと思うのですが。それを取得すれば良いように思います。

    キャンセル

  • 2018/01/23 23:55 編集

    `#include <MIDIData.h>`でMIDIデータ操作用の関数が提供されていると思うのですが、その中身を私は知らないので、具体的に`Time`をどうやって取得するかまでは判りかねます。

    キャンセル

  • 2018/01/24 00:06

    すいません。ありがとうございます。ライブラリはおーぷんMIDIぷろじぇくと(http://openmidiproject.osdn.jp/)を使用しています。こちらにMIDIデータの表示があるので、それ使ってみます!

    キャンセル

  • 2018/01/24 00:08

    試しにダウンロードしてヘッダーを見てみましたが、`MIDIEvent_GetTime`というのがありますね。コメントに「イベントの時刻取得」と書かれているので、これでTime値が取得できるのではないでしょうか。

    キャンセル

  • 2018/01/24 00:29

    long MIDIEvent_GetTime=0;を書き加え、note_x += MIDIEvent_GetTime*NOTE_GAP/120;と変更して実行したところ、一つ目のところにスネア、バスドラム、ハイハット全ての音符が表示され、そのあとは、一つずつ一瞬だけ表示されるようになりました。下の行も変更しないといけないですかね。

    キャンセル

  • 2018/01/24 00:34 編集

    > long MIDIEvent_GetTime=0;を書き加え

    いや、そうではなくて、ご使用になっているライブラリーが`MIDIEvent_GetTime`関数を提供しているので、その関数を呼び出すということです。

    long time = MIDIEvent_GetTime(pMIDIEvent);

    という感じでTime値を取得して、それをnote_xの計算で使います。

    キャンセル

  • 2018/01/24 00:45

    理解薄くてすいません。。。
    long time = MIDIEvent_GetTime(pMIDIEvent);とし、note_x += time*NOTE_GAP/120;にすると、一つ目のハイハットとバスドラムの描画はできたんですが、そのあとぽつぽつとしか表示されなくなりました。これはある一定の時間でしか表示されないようになっているんですかね。

    キャンセル

  • 2018/01/24 00:49

    > note_x += time*NOTE_GAP/120

    +は不要です。note_x = time*NOTE_GAP/120としてください。

    キャンセル

  • 2018/01/24 00:52

    音符の位置が全体的に左にずれましたけど、すべて表示されるようになりました!ありがとうございます!

    キャンセル

  • 2018/01/24 00:57 編集

    左にずれたのは、元々の音符の一番左の位置が0ではないところから始まっているからですね。
    おそらく、ループに入る前に`note_x = 初期値`のように設定しているはずなので、その値をnote_xの計算式に加算してあげれば良いでしょう。

    キャンセル

  • 2018/01/24 01:13

    +120でいけました!ありがとうございます!!!またよろしくお願いします!遅い時間までありがとうございました!

    キャンセル

  • 2018/01/30 16:06

    すいません!続きになって申し訳ないのですが、楽譜を表示したときに、1拍目に表示されるはずのハイハットとバスドラムの音が2拍目に表示され、鳴っていないはずのハイハットだけが1拍目に表示されてしまいます。これはなぜでしょうか。そのあとは楽譜通りに表示されます。

    キャンセル

  • 2018/01/30 16:28

    処理内容が判らないのでなんとも答えようがありません。
    質問内容が変わる場合は、別の質問として投げる方が良いです。私へのコメントとして質問すると、私が答えられないと他の誰も気づいてくれませんから、回答を得る機会が失われてしまいますよ。

    キャンセル

  • 2018/01/30 16:34

    すいません。改めて質問させていただきます。

    キャンセル

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

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

関連した質問

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

  • C++

    3243questions

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