다운로드
소프트웨어 개발
계정
다운로드
소프트웨어 개발
Login
Forgot Account/Password
계정 만들기
언어
도움
언어
도움
×
Login
Login Name
Password
×
Forgot Account/Password
Translation Status of 한국말
Category:
Software
People
PersonalForge
Magazine
Wiki
검색
OSDN
>
브라 우즈 소프트웨어
>
Multimedia
>
Sound/Audio
>
Editors
>
OpenMIDIProject
>
포럼
>
おーぷんMIDIぷろじぇくとフォーラム
>
MIDIプレイヤー(コンソールアプリ)のサンプルコード について
OpenMIDIProject
Description
Project Summary
Developer Dashboard
웹 페이지
Developers
Image Gallery
List of RSS Feeds
Activity
Statistics
History
다운로드
List of Releases
Stats
Communication
List of Forums
おーぷんMIDIぷろじぇくとフォーラム (415)
OpenMIDIProject forum (436)
News
포럼:
おーぷんMIDIぷろじぇくとフォーラム
(Thread #48172)
Return to Thread list
RSS
MIDIプレイヤー(コンソールアプリ)のサンプルコード について (2023-01-30 23:15 by
usa3usa
#93592)
Reply
おーぷんMIDIぷろじぇくと - FAQ にある
/* Simple Console MIDI Player example */
https://openmidiproject.osdn.jp/faq.html
の
lOldTime = lCurTime;
Sleep (5);
ですが、なぜ待ち時間が5msになるのか、理由がわかりません。
次の pMIDIEvent のイベント時刻との時間差dtを
long lTime = MIDIEvent_GetTime (pMIDIEvent);
long dt = MIDITrack_TimeToMillisec (pMIDITrack, lTime) -
MIDITrack_TimeToMillisec (pMIDITrack, lCurTime);
で求めて、
Sleep(dt);
で待ち時間を作るのではだめなのでしょうか?
デバッグ前ですが、感じとしては、
1)全イベントを、同時刻のイベント毎に集計したイベントリストMIDIEventListを作成して、
2)指定した時間区間[t0,t1]のイベントを再生する
プログラムがわかりやすい気がします。
構造体としては、2種類
struct MIDIEventList2 {
MIDIEvent* pMIDIEvent;
struct MIDIEventList2 *next; // 同時刻のイベントリスト
}
struct MIDIEventList {
long EventTime;
MIDIEvent* pMIDIEvent;
struct MIDIEventList *next; // 次の時刻のイベント
struct MIDIEventList2 *child; // 同時刻のイベントリスト
}
を定義し
1)全イベントを、同時刻のイベント毎に集計したイベントリストを作成して
p0 = new_MIDIEventList(); // 先頭を指すダミーのイベントリスト
p0->pMIDIEvent = NULL;
p0->EventTime = 0;
p0->next = NULL;
p0->child = NULL;
forEachTrack (pMIDIData, pMIDITrack) { // 全てのトラックの
p = p0;
forEachEvent (pMIDITrack, pMIDIEvent) { // 全てのイベントに対し
long lTime = MIDIEvent_GetTime (pMIDIEvent);
while(lTime > p->EventTime &&
(q=p->next) != NULL &&
lTime >= q->EventTime) p=q;
if(lTime == p->EventTime) // 同時刻のイベントリストに追加
add_MIDIEventList2(p, pMIDIEvent);
else // 新しい時刻のイベントリストを作成して挿入
p = add_MIDIEventList(p, pMIDIEvent, lTime);
}
}
2)イベントリストに従い、指定した時間区間[t0,t1]のイベントを再生
for(p=p0; p!=NULL || p->time <= t1; p=p->next) {
lCurTime = p->time;
if(lCurTime < t0) continue; // t0まで読み飛ばし
MIDIEvent_exec(p->MIDIEvent);
for(q = p->child; q != NULL; q = q->next) // 同時刻のイベントを実行
MIDIEvent_exec(q->MIDIEvent);
// 次の pMIDIEvent のイベント時刻との時間差dtを求めて、
long lTime = MIDIEvent_GetTime (pMIDIEvent);
long dt = MIDITrack_TimeToMillisec (pMIDITrack, lTime) -
MIDITrack_TimeToMillisec (pMIDITrack, lCurTime);
Sleep(dt);
}
というのではいかがですか?
なおMIDIEvent_exec(p->MIDIEvent)は、
if (MIDIEvent_IsTempo (pMIDIEvent)) {
long lTempo = MIDIEvent_GetTempo (pMIDIEvent);
MIDIClock_SetTempo (pMIDIClock, lTempo);
if (MIDIEvent_IsMIDIEvent (pMIDIEvent) ||
MIDIEvent_IsSysExEvent (pMIDIEvent)) {
unsigned char byMessage[256];
long lLen = MIDIEvent_GetLen (pMIDIEvent);
MIDIEvent_GetData (pMIDIEvent, byMessage, 256);
MIDIOut_PutMIDIMessage (pMIDIOut, byMessage, lLen);
}
の事です。
Reply to #93592
×
Subject
Body
Reply To Message #93592 > おーぷんMIDIぷろじぇくと - FAQ にある > /* Simple Console MIDI Player example */ > https://openmidiproject.osdn.jp/faq.html > > の > lOldTime = lCurTime; > Sleep (5); > ですが、なぜ待ち時間が5msになるのか、理由がわかりません。 > > 次の pMIDIEvent のイベント時刻との時間差dtを > long lTime = MIDIEvent_GetTime (pMIDIEvent); > long dt = MIDITrack_TimeToMillisec (pMIDITrack, lTime) - > MIDITrack_TimeToMillisec (pMIDITrack, lCurTime); > で求めて、 > Sleep(dt); > で待ち時間を作るのではだめなのでしょうか? > > デバッグ前ですが、感じとしては、 > 1)全イベントを、同時刻のイベント毎に集計したイベントリストMIDIEventListを作成して、 > 2)指定した時間区間[t0,t1]のイベントを再生する > プログラムがわかりやすい気がします。 > > 構造体としては、2種類 > struct MIDIEventList2 { > MIDIEvent* pMIDIEvent; > struct MIDIEventList2 *next; // 同時刻のイベントリスト > } > struct MIDIEventList { > long EventTime; > MIDIEvent* pMIDIEvent; > struct MIDIEventList *next; // 次の時刻のイベント > struct MIDIEventList2 *child; // 同時刻のイベントリスト > } > を定義し > > 1)全イベントを、同時刻のイベント毎に集計したイベントリストを作成して > p0 = new_MIDIEventList(); // 先頭を指すダミーのイベントリスト > p0->pMIDIEvent = NULL; > p0->EventTime = 0; > p0->next = NULL; > p0->child = NULL; > forEachTrack (pMIDIData, pMIDITrack) { // 全てのトラックの > p = p0; > forEachEvent (pMIDITrack, pMIDIEvent) { // 全てのイベントに対し > long lTime = MIDIEvent_GetTime (pMIDIEvent); > while(lTime > p->EventTime && > (q=p->next) != NULL && > lTime >= q->EventTime) p=q; > if(lTime == p->EventTime) // 同時刻のイベントリストに追加 > add_MIDIEventList2(p, pMIDIEvent); > else // 新しい時刻のイベントリストを作成して挿入 > p = add_MIDIEventList(p, pMIDIEvent, lTime); > } > } > > 2)イベントリストに従い、指定した時間区間[t0,t1]のイベントを再生 > for(p=p0; p!=NULL || p->time <= t1; p=p->next) { > lCurTime = p->time; > if(lCurTime < t0) continue; // t0まで読み飛ばし > MIDIEvent_exec(p->MIDIEvent); > for(q = p->child; q != NULL; q = q->next) // 同時刻のイベントを実行 > MIDIEvent_exec(q->MIDIEvent); > > // 次の pMIDIEvent のイベント時刻との時間差dtを求めて、 > long lTime = MIDIEvent_GetTime (pMIDIEvent); > long dt = MIDITrack_TimeToMillisec (pMIDITrack, lTime) - > MIDITrack_TimeToMillisec (pMIDITrack, lCurTime); > Sleep(dt); > } > > というのではいかがですか? > なおMIDIEvent_exec(p->MIDIEvent)は、 > if (MIDIEvent_IsTempo (pMIDIEvent)) { > long lTempo = MIDIEvent_GetTempo (pMIDIEvent); > MIDIClock_SetTempo (pMIDIClock, lTempo); > if (MIDIEvent_IsMIDIEvent (pMIDIEvent) || > MIDIEvent_IsSysExEvent (pMIDIEvent)) { > unsigned char byMessage[256]; > long lLen = MIDIEvent_GetLen (pMIDIEvent); > MIDIEvent_GetData (pMIDIEvent, byMessage, 256); > MIDIOut_PutMIDIMessage (pMIDIOut, byMessage, lLen); > } > の事です。 >
You can not use Wiki syntax
You are not logged in. To discriminate your posts from the rest, you need to pick a nickname. (The uniqueness of nickname is not reserved. It is possible that someone else could use the exactly same nickname. If you want assurance of your identity, you are recommended to login before posting.)
Login
Nickname
Preview
Post
Cancel
Re: MIDIプレイヤー(コンソールアプリ)のサンプルコード について (2023-02-05 14:18 by
kuzu
#93664)
Reply
全イベントを完全な時刻順序に並べ替えて、それを演奏することは
より時刻順序に厳密な処理となりますので、問題はありません。
ただし、演奏中にイベントが追加・変更・削除されることがあったり、
トラックのタイム+などが使われることすることがあれば、
リアルタイムで配列を作り直すような処理が必要となります。
また、whileループの間にSleep(5)を挟んでいるのは、
約10ミリ秒につき1回入出力処理を行い、かつOSをロックさせないためです。
while (1) {
処理A; // 例:約3ミリ秒
Sleep (5); // 例:約7ミリ秒
}
時間は例としてコメントしているだけで、計測値ではありません。
処理Aに何ミリ秒かかるのかは書いてあるコードの重さやCPU能力に依存します。
また、処理Aの途中でOSが他のプログラムにCPU処理時間を渡すことも考えられます。
また、Sleep(5)は、5ミリ秒止まるのではなく、
「少なくとも5ミリ秒は、強制的にOSや他プログラムにCPU処理時間を渡す」
という意味です。他に重いプログラムが多く動いていれば、
実際には20ミリ秒止まるかもしれません。
また、Sleep(0)は、
「可能な限り短く強制的にOSや他のプログラムにCPU処理時間を渡す」なので、
実際には10ミリ秒かかるかもしれませんし、0.1ミリ秒で終わるかもしれません。
Sleep(0)を挟まない場合、
OSによるCPU時間の切り替えが行われない限りこのプログラムが処理を独占し続
けます。実際には少し長く独占するとOSにより切り替わってくれますが、
このプログラム以外はほとんどフリーズしているように見えることでしょう。
Sleep時間をユーザー指定できるようにすればよりユーザーフレンドリーです。
Sleepで次のイベントまでの待ち時間を指定する方法は、仮にどんなに正確に
Sleep時間が実行されたとしても、処理Aの時間で狂ってしまうのと、1ミリ秒
以下の単位が丸められてしまい狂ってしまい、さらにループを繰り返すたび
に狂いが累積されるという難点があります。
他にもいろいろな方法があるとは思いますが、サンプルコードは、
簡潔で実際聴覚上違和感がなくOSがフリーズしない一例を示しました。
Reply to
#93592
Reply to #93664
×
Subject
Body
Reply To Message #93664 > 全イベントを完全な時刻順序に並べ替えて、それを演奏することは > より時刻順序に厳密な処理となりますので、問題はありません。 > > ただし、演奏中にイベントが追加・変更・削除されることがあったり、 > トラックのタイム+などが使われることすることがあれば、 > リアルタイムで配列を作り直すような処理が必要となります。 > > また、whileループの間にSleep(5)を挟んでいるのは、 > 約10ミリ秒につき1回入出力処理を行い、かつOSをロックさせないためです。 > > while (1) { > 処理A; // 例:約3ミリ秒 > Sleep (5); // 例:約7ミリ秒 > } > > 時間は例としてコメントしているだけで、計測値ではありません。 > > 処理Aに何ミリ秒かかるのかは書いてあるコードの重さやCPU能力に依存します。 > また、処理Aの途中でOSが他のプログラムにCPU処理時間を渡すことも考えられます。 > > また、Sleep(5)は、5ミリ秒止まるのではなく、 > 「少なくとも5ミリ秒は、強制的にOSや他プログラムにCPU処理時間を渡す」 > という意味です。他に重いプログラムが多く動いていれば、 > 実際には20ミリ秒止まるかもしれません。 > > また、Sleep(0)は、 > 「可能な限り短く強制的にOSや他のプログラムにCPU処理時間を渡す」なので、 > 実際には10ミリ秒かかるかもしれませんし、0.1ミリ秒で終わるかもしれません。 > > Sleep(0)を挟まない場合、 > OSによるCPU時間の切り替えが行われない限りこのプログラムが処理を独占し続 > けます。実際には少し長く独占するとOSにより切り替わってくれますが、 > このプログラム以外はほとんどフリーズしているように見えることでしょう。 > > Sleep時間をユーザー指定できるようにすればよりユーザーフレンドリーです。 > > Sleepで次のイベントまでの待ち時間を指定する方法は、仮にどんなに正確に > Sleep時間が実行されたとしても、処理Aの時間で狂ってしまうのと、1ミリ秒 > 以下の単位が丸められてしまい狂ってしまい、さらにループを繰り返すたび > に狂いが累積されるという難点があります。 > > 他にもいろいろな方法があるとは思いますが、サンプルコードは、 > 簡潔で実際聴覚上違和感がなくOSがフリーズしない一例を示しました。
You can not use Wiki syntax
You are not logged in. To discriminate your posts from the rest, you need to pick a nickname. (The uniqueness of nickname is not reserved. It is possible that someone else could use the exactly same nickname. If you want assurance of your identity, you are recommended to login before posting.)
Login
Nickname
Preview
Post
Cancel
Re: MIDIプレイヤー(コンソールアプリ)のサンプルコード について (2023-02-07 07:55 by
usa3usa
#93752)
Reply
解説ありがとうございます
>また、whileループの間にSleep(5)を挟んでいるのは、
>約10ミリ秒につき1回入出力処理を行い、かつOSをロックさせないためです。
>他にもいろいろな方法があるとは思いますが、サンプルコードは、
>簡潔で実際聴覚上違和感がなくOSがフリーズしない一例を示しました。
なるほど、そういう意味だったのですね、了解しました。
以下は修正を希望するわけではありませんが、私の個人的な感想です。
>while (1) {
> 処理A; // 例:約3ミリ秒
> Sleep (5); // 例:約7ミリ秒
>}
処理Aでは
if (lOldTime <= lTime && lTime < lCurTime) {
のイベントを実行するわけですが、
毎回、全イベントを見て対象イベントを抽出
しているので、処理時間が重く、結果「例:約3ミリ秒」の処理時間になるのですよね。
1)全イベントを、同時刻のイベント毎に集計したイベントリストMIDIEventListを作成して、
2)指定した時間区間[t0,t1]のイベントを再生する
3)演奏中にイベントが追加・変更・削除される場合には、イベントリストを作りなおす
とすれば処理Aは、処理時間を無視できる程度まで軽くなる気がします。
それでもミリ秒単位の処理時間がかかるのであれば、
処理Aの処理時間を計測して待ち時間を調整する
つまり、
while (1) {
時刻計測1
処理A’
時刻計測2
次のイベントのまでの時刻間隔から処理A'の処理時間を引いた値を待ち時間にする
}
の方が素直なプログラムになる気がしました。
なお、イベントリストMIDIEventListの定義と作成は、こんな感じになります。
typedef struct tagMIDIEventList2 {
MIDIEvent* pMIDIEvent;
struct tagMIDIEventList2 *next; // 同時刻のイベントリスト
} MIDIEventList2;
typedef struct tagMIDIEventList {
long EventTime;
long Tempo;
MIDIEvent* pMIDIEvent;
MIDIEventList2 *child; // 同時刻のイベントリスト
struct tagMIDIEventList *next; // 次の時刻のイベント
} MIDIEventList;
// 全イベントを、同時刻のイベント毎に集計したイベントリストを作成
MIDIEventList *MIDIEventList_Make(MIDIData *pMIDIData)
{
<<変数の宣言部は省略>>
p0 = new_MIDIEventList(); // 先頭を指すダミーのイベントリスト
forEachTrack (pMIDIData, pMIDITrack) { // 全てのトラックの
p = p0;
forEachEvent (pMIDITrack, pMIDIEvent) { // 全てのイベントに対し
long lTime = MIDIEvent_GetTime (pMIDIEvent);
while(lTime > p->EventTime &&
(q=p->next) != NULL &&
lTime >= q->EventTime) p=q; // 挿入位置pを求め
if(lTime == p->EventTime) // 同時刻のイベントリストに追加
add_MIDIEventList2(p, pMIDIEvent);
else // 新しい時刻のイベントリストを作成して挿入
p = add_MIDIEventList(p, pMIDIEvent, lTime);
if (MIDIEvent_IsTempo (pMIDIEvent))
p->Tempo = MIDIEvent_GetTempo (pMIDIEvent);
}
}
Reply to
#93664
Reply to #93752
×
Subject
Body
Reply To Message #93752 > 解説ありがとうございます > > >また、whileループの間にSleep(5)を挟んでいるのは、 > >約10ミリ秒につき1回入出力処理を行い、かつOSをロックさせないためです。 > > >他にもいろいろな方法があるとは思いますが、サンプルコードは、 > >簡潔で実際聴覚上違和感がなくOSがフリーズしない一例を示しました。 > > なるほど、そういう意味だったのですね、了解しました。 > > 以下は修正を希望するわけではありませんが、私の個人的な感想です。 > > >while (1) { > > 処理A; // 例:約3ミリ秒 > > Sleep (5); // 例:約7ミリ秒 > >} > > 処理Aでは > if (lOldTime <= lTime && lTime < lCurTime) { > のイベントを実行するわけですが、 > 毎回、全イベントを見て対象イベントを抽出 > しているので、処理時間が重く、結果「例:約3ミリ秒」の処理時間になるのですよね。 > > 1)全イベントを、同時刻のイベント毎に集計したイベントリストMIDIEventListを作成して、 > 2)指定した時間区間[t0,t1]のイベントを再生する > 3)演奏中にイベントが追加・変更・削除される場合には、イベントリストを作りなおす > とすれば処理Aは、処理時間を無視できる程度まで軽くなる気がします。 > > それでもミリ秒単位の処理時間がかかるのであれば、 > 処理Aの処理時間を計測して待ち時間を調整する > つまり、 > > while (1) { > 時刻計測1 > 処理A’ > 時刻計測2 > 次のイベントのまでの時刻間隔から処理A'の処理時間を引いた値を待ち時間にする > } > > の方が素直なプログラムになる気がしました。 > > なお、イベントリストMIDIEventListの定義と作成は、こんな感じになります。 > typedef struct tagMIDIEventList2 { > MIDIEvent* pMIDIEvent; > struct tagMIDIEventList2 *next; // 同時刻のイベントリスト > } MIDIEventList2; > > typedef struct tagMIDIEventList { > long EventTime; > long Tempo; > MIDIEvent* pMIDIEvent; > MIDIEventList2 *child; // 同時刻のイベントリスト > struct tagMIDIEventList *next; // 次の時刻のイベント > } MIDIEventList; > > // 全イベントを、同時刻のイベント毎に集計したイベントリストを作成 > MIDIEventList *MIDIEventList_Make(MIDIData *pMIDIData) > { > <<変数の宣言部は省略>> > p0 = new_MIDIEventList(); // 先頭を指すダミーのイベントリスト > forEachTrack (pMIDIData, pMIDITrack) { // 全てのトラックの > p = p0; > forEachEvent (pMIDITrack, pMIDIEvent) { // 全てのイベントに対し > long lTime = MIDIEvent_GetTime (pMIDIEvent); > while(lTime > p->EventTime && > (q=p->next) != NULL && > lTime >= q->EventTime) p=q; // 挿入位置pを求め > if(lTime == p->EventTime) // 同時刻のイベントリストに追加 > add_MIDIEventList2(p, pMIDIEvent); > else // 新しい時刻のイベントリストを作成して挿入 > p = add_MIDIEventList(p, pMIDIEvent, lTime); > > if (MIDIEvent_IsTempo (pMIDIEvent)) > p->Tempo = MIDIEvent_GetTempo (pMIDIEvent); > } > }
You can not use Wiki syntax
You are not logged in. To discriminate your posts from the rest, you need to pick a nickname. (The uniqueness of nickname is not reserved. It is possible that someone else could use the exactly same nickname. If you want assurance of your identity, you are recommended to login before posting.)
Login
Nickname
Preview
Post
Cancel