Created
April 24, 2012 20:58
-
-
Save vinniefalco/2483665 to your computer and use it in GitHub Desktop.
Explanation of juce::MidiOutput methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 发送消息块。1参:MIDI缓冲(引用型对象)。2参:开始时间(毫秒)。3参:每秒的采样数 | |
// 本函数用于将MIDI缓冲数据发送至MIDI设备,从而发出声音。是MidiOutput类的核心函数,也是实现播放MIDI文件的功能性函数 | |
void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, | |
const double millisecondCounterToStartAt, | |
double samplesPerSecondForBuffer) | |
{ | |
// 执行此函数之前,必须启动后台线程。如果尚未启动,则首先执行startBackgroundThread()函数,而后再调用本函数. 此处使用了一个断言。如果尚未启动后台线程,则调试时程序代码将停留在此处。 | |
// The thread must be started before calling this function. | |
jassert (isThreadRunning()); | |
// 本函数的2参(开始时间)必须大于0,否则无法执行此函数 | |
// The time to start cannot be negative. | |
jassert (millisecondCounterToStartAt > 0); | |
// 定义一个double常量(时间伸缩的比例值),初始化为1000/本函数的3参 | |
////////////////////////////////////////////////////////////////// | |
// 该值应该是乐曲的速度和100拍/分钟的比例值 | |
// NOTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
// this value should be scale factor of song's tempo and 100BMP ----- I guess----- | |
// but, how to get the MIDI file(song)'s tempo????????????????? it changeable... | |
///////////////////////////////////////////////////////////////// | |
// This scaling factor is used to convert timestamps to milliseconds. | |
const double timeScaleFactor = 1000.0 / samplesPerSecondForBuffer; | |
// 定义一个迭代器对象,用于迭代本函数的1参(MIDI缓冲对象) | |
// Used to step through messages in the buffer. | |
MidiBuffer::Iterator i (buffer); | |
// 定义一个uint8(无符号8位整型)常量,用普通型指针对象data指向该常量.此时,该常量尚未初始化.其内容(值)所位于的堆内存尚未分配. | |
const uint8* data; | |
// 定义两个整型变量 | |
int len, time; | |
// 开始循环遍历 | |
// getNextEvent()的作用:从缓冲中检索下一个事件. 1参data: MIDI消息块的指针, 2参len: MIDI消息块的字节数, 3参time: 事件的位置, 即音符的开始时间. | |
// Go through each event one by one. | |
while (i.getNextEvent (data, len, time)) | |
{ | |
// 定义一个double常量,初始化为本函数的3参(开始时间)+ 时间伸缩的比例值 * 音符的开始时间 | |
// Convert the event timestamp to an absolute time in milliseconds. | |
const double eventTime = millisecondCounterToStartAt + timeScaleFactor * time; | |
// 定义一个PendingMessage(待处理的MIDI消息,其实是一个链表型的数据结构,有两个数据成员,一个是MIDI消息,一个是该类的指针型对象)类型的常指针m,初始化为当前所遍历的MIDI事件(用于初始化的3个参数依次为:MIDI消息块的首地址(指针),消息块的字节数,MIDI事件的位置(音符的开始时间)) | |
// Create a message to pass to our sending thread. | |
PendingMessage* const m = new PendingMessage (data, len, eventTime); | |
// 定义一个作用域锁定对象,初始化为锁定临界对象lock | |
// lock是MidiOutput类(即本类)的数据成员 | |
// The lock is necessary because the sending thread could be accessing the list. | |
const ScopedLock sl (lock); | |
// 判断,如果firstMessage为空指针,或者 firstMessage所包含的MIDI消息的时间戳大于刚刚定义的eventTime | |
// firstMessage是本类的数据成员,PendingMessage类型的指针型对象 | |
if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime) | |
{ | |
// 下一个待处理的消息 = firstMessage | |
m->next = firstMessage; | |
// firstMessage = 当前的待处理消息 | |
firstMessage = m; | |
} | |
// 否则(如果firstMessage不为空指针,或者 firstMessage所包含的MIDI消息的时间戳不大于刚刚定义的eventTime) | |
else | |
{ | |
// 定义一个待处理消息的指针型对象,初始化为firstMessage | |
PendingMessage* mm = firstMessage; | |
// 嵌套循环,循环条件是:下一个待处理消息不是空指针,并且下一个消息的MIDI时间戳不大于eventTime | |
while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime) | |
// mm = 下一个待处理消息 | |
mm = mm->next; | |
// 第40行定义的m的下一条消息 = mm的下一条消息 | |
m->next = mm->next; | |
// mm的下一条消息 = m | |
mm->next = m; | |
} | |
// 作用域锁定sl至此解除锁定 | |
} // 外层循环结束************ | |
// 唤醒线程。如果线程调用了wait(),则此方法将唤醒它。该函数继承自Thread类。 | |
notify(); | |
} | |
//========================================================================================= | |
// 本函数继承并重写Thread类的同名函数 | |
// 启动线程后,该线程锁执行的实际功能,在此函数中完成 | |
void MidiOutput::run() | |
{ | |
// 循环,条件是:线程未收到退出命令 | |
while (! threadShouldExit()) | |
{ | |
// 定义3个uint32型变量,分别是: | |
// 当前时间 | |
uint32 now = Time::getMillisecondCounter(); | |
// 事件时间,初始化为0 | |
uint32 eventTime = 0; | |
// 等待时间,初始化为500 | |
uint32 timeToWait = 500; | |
// 定义一个待处理消息的指针型对象 | |
PendingMessage* message; | |
{ // 作用域 | |
// 作用域锁定开始 | |
const ScopedLock sl (lock); | |
// message赋值为firstMessage | |
message = firstMessage; | |
// 判断,如果刚刚定义的message(待处理消息的指针型对象)不是空指针(有效) | |
if (message != nullptr) | |
{ | |
// 事件时间 = MIDI消息的时间戳(绝对时间),四舍五入为整数 | |
eventTime = (uint32) roundToInt (message->message.getTimeStamp()); | |
// 如果事件时间大于当前时间+20毫秒 | |
if (eventTime > now + 20) | |
{ | |
// 等待时间 = 事件时间 - 当前时间 + 20毫秒 | |
timeToWait = eventTime - (now + 20); | |
// 待处理消息指针置为空指针 | |
message = nullptr; | |
} | |
// 如果事件时间不大于当前时间 + 20毫秒 | |
else | |
{ | |
// firstMessage赋值为下一条消息 | |
firstMessage = message->next; | |
} | |
} | |
} // 作用域解锁 | |
// 继续判断,如果此时message依然为有效指针 | |
if (message != nullptr) | |
{ | |
// 定义一个常量作用域指针,类型为待处理消息,初始化为message | |
const ScopedPointer<PendingMessage> messageDeleter (message); | |
// 如果事件时间大于当前时间 | |
if (eventTime > now) | |
{ | |
// 一直等待,等待时间为事件时间 | |
Time::waitForMillisecondCounter (eventTime); | |
// 此处设置线程退出的时机,也就是说,如果发出线程退出命令,则此处可以退出 | |
if (threadShouldExit()) | |
break; | |
} | |
// 如果事件时间大于当前时间 - 200毫秒 | |
if (eventTime > now - 200) | |
// 发送消息,该函数是MidiOutput类的另一个核心函数,用于发送单条MIDI消息 | |
sendMessageNow (message->message); //***********!!!!!!!!! | |
} | |
else // 如果message是空指针 | |
{ | |
// 断言,等待时间小于30秒 | |
jassert (timeToWait < 1000 * 30); | |
// 线程等待,等待时间为timeToWait | |
wait ((int) timeToWait); | |
} | |
} | |
// 清除所有待处理消息 | |
clearAllPendingMessages(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment