2

我在 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 知道我想播放同一个文件两次并禁止它。我必须等到序列完全播放完才能再次播放。扑朔迷离。

任何的想法?

谢谢

4

2 回答 2

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);
于 2012-06-28T09:40:52.887 回答
0

好的,这就是我认为正在发生的事情:Core Audio 是基于拉取的,也就是说,当一个单元需要数据时,它会从其源请求它。现在,当我在 MusicPlayer 上重置时间时,它会在某个时间完成,比如 t0。不久之后,在时间 t1,由这个 MusicPlayer 提供的 AU 节点请求数据。MusicPlayer 获取 t1 并计算它在其事件序列中的位置。因为它总是大于 t0,所以任何恰好在 t0 开始的事件(例如,从 MIDI tick 0 开始的事件)都被认为已播放,因此根本不播放。换句话说,midi 文件的第一个音符是/不播放的。

现在,由于我的文件很短,实际上只包含从第 0 点开始的和弦,所以没有播放任何音符,我得到了沉默。

为什么当我加载新序列或在尚未播放时开始序列时没有发生这种情况我不清楚......

无论如何,一种可能的解决方法是:停止播放器,设置顺序,将时间设置为 0,然后将播放器的启动延迟例如 25 毫秒。这对于响应性来说并不理想,但它确实有效。

解决此问题的一种稍微复杂的方法如下:
- 在音乐播放器提供的音频单元上设置回调(AudioUnitAddRenderNotify())
- 在重置序列时,不要重置时间在播放器上但设置一个布尔标志
- 在渲染回调中,如果设置了标志,则重置播放器上的时间并取消设置标志
没有引入延迟并且它也有效,这往往验证我对问题的解释。

于 2012-06-21T09:03:15.973 回答