Yan*_*der 5 midi objective-c ios coremidi
我在接收MIDI时钟计算准确的BPM时遇到了一些麻烦(在我的测试中使用Ableton Live发送MIDI时钟).
我正在使用Pete Goodliffe的CoreMIDI和PGMidi.
在PGMidi lib中,有一种方法在接收MIDI消息时调用.从doc开始,这是从高优先级的后台线程发生的.
这是我当前用于计算BPM的实现
double BPM;
double currentClockInterval;
uint64_t startClockTime;
- (void) midiSource:(PGMidiSource*)input midiReceived:(const MIDIPacketList *)packetList
{
[self onTick:nil];
MIDIPacket *packet = MIDIPacketListInit((MIDIPacketList*)packetList);
int statusByte = packet->data[0];
int status = statusByte >= 0xf0 ? statusByte : statusByte >> 4 << 4;
switch (status) {
case 0xb0: //cc
//NSLog(@"CC working!");
break;
case 0x90: // Note on, etc...
//NSLog(@"Note on/off working!");
break;
case 0xf8: // Clock tick
if (startClockTime != 0)
{
uint64_t currentClockTime = mach_absolute_time();
currentClockInterval = convertTimeInMilliseconds(currentClockTime - startClockTime);
BPM = (1000 / currentClockInterval / 24) * 60;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"BPM: %f",BPM);
});
}
startClockTime = mach_absolute_time();
break;
}
}
uint64_t convertTimeInMilliseconds(uint64_t time)
{
const int64_t kOneMillion = 1000 * 1000;
static mach_timebase_info_data_t s_timebase_info;
if (s_timebase_info.denom == 0) {
(void) mach_timebase_info(&s_timebase_info);
}
// mach_absolute_time() returns billionth of seconds,
// so divide by one million to get milliseconds
return (uint64_t)((time * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
}
Run Code Online (Sandbox Code Playgroud)
但由于某些原因,计算出的BPM并不准确.当我的Ableton现场发送一个BPM低于70它是好的,但更多的我送更高的BPM不太准确它是例子:
有人可以帮我这个吗?我相信我可能没有使用好的策略来计算BPM.我首先使用mach_absolute_time()计算每个midi时钟之间经过的时间.
谢谢你的帮助!
在Kurt回答之后,这是一个更加准确的例程,适用于iOS(因为我没有使用CoreAudio/HostTime.h,它仅在OSX上可用)
double currentClockTime;
double previousClockTime;
- (void) midiSource:(PGMidiSource*)input midiReceived:(const MIDIPacketList *)packetList
{
MIDIPacket *packet = (MIDIPacket*)&packetList->packet[0];
for (int i = 0; i < packetList->numPackets; ++i)
{
int statusByte = packet->data[0];
int status = statusByte >= 0xf0 ? statusByte : statusByte & 0xF0;
if(status == 0xf8)
{
previousClockTime = currentClockTime;
currentClockTime = packet->timeStamp;
if(previousClockTime > 0 && currentClockTime > 0)
{
double intervalInNanoseconds = convertTimeInNanoseconds(currentClockTime-previousClockTime);
BPM = (1000000 / intervalInNanoseconds / 24) * 60;
}
}
packet = MIDIPacketNext(packet);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"BPM: %f",BPM);
});
}
uint64_t convertTimeInNanoseconds(uint64_t time)
{
const int64_t kOneThousand = 1000;
static mach_timebase_info_data_t s_timebase_info;
if (s_timebase_info.denom == 0)
{
(void) mach_timebase_info(&s_timebase_info);
}
// mach_absolute_time() returns billionth of seconds,
// so divide by one thousand to get nanoseconds
return (uint64_t)((time * s_timebase_info.numer) / (kOneThousand * s_timebase_info.denom));
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,我现在依赖于MidiPacket timeStamp而不是mach_absolute_time(),这可能是一个不定的数量.而不是使用毫秒进行我的BPM计算,我现在使用纳秒来获得更好的准确性.
通过这个例程,我现在得到了更准确的东西,但它仍然低于150的BPM的一小部分,并且在非常高的BPM(例如> 400 BPM)下可以达到10 BPM:
还有什么需要考虑才能获得更准确的东西吗?
谢谢你的帮助Kurt!很有帮助 !
我分叉了PGMidi并添加了一些功能,如BPM计算和量化.回购在这里https://github.com/yderidde/PGMidi
我相信它可以被优化以更准确.此外,量化程序并不完美...所以,如果有人在我的代码中看到一些错误或有建议使整个事情更稳定/准确,请告诉我!
这里有一些错误,其中一些比其他错误更重要。
最重要的是:您正在使用整数毫秒,这不够精确,无法获得准确的节拍/分钟。我们以 120 次/分钟为例。在 120 次/分钟和 24 个时钟/节拍时,每个时钟到达时间为 20.833 毫秒。由于您计算的是整数毫秒,因此看起来是 20 或 21 毫秒。当您进行数学运算(使用双倍!)返回 BPM 时,您会得到 125 次/分钟或 119.0476 次/分钟。两者都不是您所期望的。
如果您使用积分微秒或纳秒进行数学计算,您将获得更准确的值。我建议使用AudioConvertHostTimeToNanos()中定义的 来<CoreAudio/HostTime.h>将 a 转换MIDITimeStamp为纳秒整数,然后转换为double并从那里开始。你不应该使用mach_timebase_info自己。
还:
MIDIPackets 有一个timeStamp值来标记它们何时被接收。CoreAudio 费了很大的劲才给你这个时间戳,所以使用它吧!
不要依赖对 的调用mach_absolute_time(),该调用稍后会出现不一致的时间,具体取决于您无法控制的许多因素。
别打电话MIDIPacketListInit。
要迭代 中的每个MIDIPacket,MIDIPacketList请直接使用以下代码MIDIServices.h:
MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; ++i) {
/* your code to use the packet goes here */
packet = MIDIPacketNext(packet);
}
Run Code Online (Sandbox Code Playgroud)statusByte >> 4 << 4看着很痛苦。你的意思是statusByte & 0xF0。
| 归档时间: |
|
| 查看次数: |
4503 次 |
| 最近记录: |