我在iOS 5上使用CoreAudio播放MIDI文件。我有一切设置,它工作,一切都很好,除非我想从开始重新开始序列。 要做到这一点,我自然会说:
if (MusicPlayerSetTime(musicPlayer, (MusicTimeStamp)0.0) != noErr)
[NSException raise:@"playMIDI" format:@"Can't reset the player"];
我没有收到任何错误,但是进行此调用会完全停止播放,而不是(如文档中所述)继续从新位置播放。 我也试过这个(和许多其他相同类型的组合):
Boolean isPlaying = NO;
if (MusicPlayerIsPlaying(musicPlayer, &isPlaying) != noErr)
[NSException raise:@"playMIDI:" format:@"Can't get MusicPlayer play state"];
if (isPlaying) {
if (MusicPlayerStop(musicPlayer) != noErr)
[NSException raise:@"playMIDI:" format:@"Can't stop MusicPlayer"];
}
if (MusicPlayerSetSequence(musicPlayer, musicSequence) != noErr)
[NSException raise:@"playMIDI:" format:@"Can't set sequence for MusicPlayer"];
if (MusicPlayerSetTime(musicPlayer, (MusicTimeStamp)0.0) != noErr)
[NSException raise:@"playMIDI" format:@"Can't reset the player"];
if (MusicPlayerSetPlayRateScalar(musicPlayer, 1.0) != noErr)
[NSException raise:@"playMIDI" format:@"Can't set speed"];
if (MusicPlayerStart(musicPlayer) != noErr)
[NSException raise:@"playMIDI" format:@"Can't start MusicPlayer"];
没有快乐,同样的效果。
如果我改变了MusicSequence,那么新的MusicSequence就可以了。所以我已经走到极端,从同一个MIDI文件创建两个MusicSequence并从一个切换到另一个。没有快乐,iOS知道我想要播放两次相同的文件并禁止它。我必须等到序列完全播放才能再次播放。令人困惑的。
有什么想法吗?
由于
答案 0 :(得分:2)
我没有注意到在t0开始/停止/重启序列有任何问题。您是否确信序列中的音符是您认为的那样?还是别的什么呢?
使用下面的代码进行快速测试(删除错误检查)停止+重新启动正常。或许我误解了你所描述的问题。 Fwiw - 没有停止/开始播放器的呼叫,它也可以工作,但瞬态音符仍然会发出声音,因此不太干净。
- (void)jump
{
if (player) {
MusicPlayerStop(player)
MusicPlayerSetTime(player, 0.0f);
MusicPlayerStart(player);
}
}
<强>更新强>
我看了一下.mid文件并进行了测试。这就是MusicPlayer查看文件的方式(使用函数CAShow(nameOfYourSequence)
)
Sequence @0x68ad7c0
NumTracks = 1, Type = beats
* * TempoTrack * *
SequenceTrack @0x68ab250
Num Events: 3, Track Length: 0.00 beats
0.000: Time Sig:4/4, 24 clks/qtr, 8 32nds/clk
0.000: SMPTE Offset [33:0:0:0.0]
0.000: Tempo:120.000
* * Track 1 * *
SequenceTrack @0x6a815e0
Num Events: 9, Track Length: 4.11 beats
0.000: MetaEvent:03
0: 30 31 30 31 30 31 31 31 01010111
0.000: MetaEvent:04
0: 30 31 20 43 01 C
0.000: MetaEvent:59
0: 00 00 ..
0.000: MetaEvent:06
0: 31 20 4D 41 4A 45 55 52 53 1 MAJEURS
0.000: MetaEvent:06
0: 31 31 11
0.000: Note:Ch:0 36 121 (4.113)
0.000: Note:Ch:0 60 116 (3.931)
0.000: Note:Ch:0 64 110 (3.931)
0.000: Note:Ch:0 67 105 (3.931)
我能够使用该文件重现问题 - 之前没有看过它,但我工作的文件更长。不知道你想要做什么,但解决方法可能是晚于0.000开始你的事件。
另一方面,您是否考虑直接使用以下方式管理noteOn + noteOff事件:
MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, onVelocity, 0);
答案 1 :(得分:0)
好的,这就是我认为正在发生的事情:Core Audio是基于拉取的,也就是说,当一个单元需要数据时,它从它的来源请求它。现在当我重置MusicPlayer上的时间时,它在某个时间完成,比如t0。不久之后,在时间t1,由此MusicPlayer馈送的AU节点请求数据。 MusicPlayer采用t1并计算它在事件序列中的位置。由于它总是大于t0,所以任何恰好在t0开始的事件(例如,以MIDI tick 0开始的事件)都被认为是播放的,因此根本不播放。换句话说,不播放midi文件的第一个音符。
现在,由于我的文件非常短,实际上只包含一个从0开始的和弦,所以没有任何音符播放,我会沉默。
为什么在我加载新序列时没有发生这种情况或者在没有播放的情况下启动序列时我不清楚...
无论如何,一种可能的解决方法是:停止播放器,设置序列,将时间设置为0,然后通过例如延迟播放器的启动。为25ms。这对于响应性来说并不理想,但它确实有效。
解决此问题的一种稍微复杂的方法如下:
- 在音乐播放器正在馈送的音频单元上设置渲染回调(AudioUnitAddRenderNotify())
- 在重置序列时,不要重置播放器上的时间,而是设置布尔标志
- 在渲染回调中,如果设置了标志,则重置播放器上的时间并取消设置标志
没有引入延迟,它也有效,这有助于验证我对问题的解释。