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

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

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

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

Q&A

解決済

2回答

202閲覧

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

zakky79

総合スコア23

C++

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

0グッド

0クリップ

投稿2018/01/22 08:24

編集2018/01/22 08:53

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

c++

1/***** 楽譜の描画 *****/ 2 forEachTrack(pMIDIData, pMIDITrack) { 3 forEachEvent(pMIDITrack, pMIDIEvent) { 4 5 int lLen = pMIDIEvent->m_lLen; 6 7 if (note_x < SHEET_W - SHEET_GAP * 2 && MIDIEvent_IsNoteOn(pMIDIEvent)/*&& counttest < 30*/) { 8 std::cerr << "Event Size: " << lLen << "(Bytes)" << std::endl; 9 unsigned char status = pMIDIEvent->m_pData[0]; 10 unsigned char data1 = pMIDIEvent->m_pData[1]; 11 unsigned char data2 = pMIDIEvent->m_pData[2]; 12 long duration = MIDIEvent_GetDuration(pMIDIEvent); 13 14 fprintf(stderr, "status, data1, data2 = 0x%02x, 0x%02x, 0x%02x\n", status, data1, data2); 15 16 PutNoteData(0, sheet_base, status, data1, data2, duration, cv::Scalar(0, 0, 0), note_x); 17 18 if (duration == 120) { 19 note_x += NOTE_GAP; 20 } 21 else if (duration == 240) { 22 note_x += NOTE_GAP * 2; 23 } 24 else { 25 //Error 26 } 27 } 28 } 29 } 30 31 note_x = SHEET_GAP + NOTE_GAP / 2; // 初期化 32 note_x_by_midi = SHEET_GAP + NOTE_GAP / 2; // 初期化 33 cv::imshow(winname, sheet_base); 34 cv::moveWindow(winname, 0, 0); 35 int key = cv::waitKey(1); 36 37 /* */ 38 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; }

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

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

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

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

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

PineMatsu

2018/01/22 08:40

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

2018/01/22 08:54

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

回答2

0

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

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

MIDIEvent_GetTime (pNoteOnEvent);

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

MIDIEvent_GetTime (pNoteOnEvent);

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


補足:

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

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

投稿2018/01/22 09:16

編集2018/01/22 10:57
KSwordOfHaste

総合スコア18392

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

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

KSwordOfHaste

2018/01/22 11:11 編集

補足コメントに少々マイナスイメージを持たれるかも知れません。マイナーな規格なら自分で調べた方がよいとは書きましたが、本来はマイナーかどうかには関係なく「それに取り組むならどんな規格であろうが自分で読む」という態度は大切だと思います。 それはそれとして計算機やネットワーク帯域の性能が高くなったため、音楽を扱うのにMIDIより音声信号の方が身近な時代になりました。しかしながら音楽を演奏したりする方々にとってはMIDIは今だ有効なツールだと思います。個人的な思いとしては質問者さんに楽しくMIDIに取り組んでほしいなと思います。
zakky79

2018/01/23 09:02

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

0

ベストアンサー

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

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


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

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

投稿2018/01/22 12:09

編集2018/01/22 12:13
catsforepaw

総合スコア5938

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

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

zakky79

2018/01/23 14:47

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

2018/01/23 14:52

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

2018/01/23 14:55 編集

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

2018/01/23 15:08

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

2018/01/23 15:29

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

2018/01/23 15:34 編集

> long MIDIEvent_GetTime=0;を書き加え いや、そうではなくて、ご使用になっているライブラリーが`MIDIEvent_GetTime`関数を提供しているので、その関数を呼び出すということです。 long time = MIDIEvent_GetTime(pMIDIEvent); という感じでTime値を取得して、それをnote_xの計算で使います。
zakky79

2018/01/23 15:45

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

2018/01/23 15:49

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

2018/01/23 15:52

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

2018/01/23 15:57 編集

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

2018/01/23 16:13

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

2018/01/30 07:06

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

2018/01/30 07:28

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

2018/01/30 07:34

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問