【问题标题】:iOS & CoreAudio: Calling MusicPlayerSetTime() stops MusicPlayeriOS 和 CoreAudio:调用 MusicPlayerSetTime() 会停止 MusicPlayer
【发布时间】:2012-06-18 08:33:48
【问题描述】:

我在 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 知道我想播放同一个文件两次并禁止它。我必须等到序列完全播放完才能再次播放。令人眼花缭乱。

有什么想法吗?

谢谢

【问题讨论】:

    标签: ios core-audio midi


    【解决方案1】:

    我没有注意到在 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);
    

    【讨论】:

    • 是的,我检查了,注释从 0 开始(我使用 fourmilab.ch/webtools/midicsv 转储文件的内容,我还检查了 Aria Maestosa)。我必须说我无法解释这种行为,除了我在下面给出的解释。
    • 我已经和MusicPlayer摔跤了很多,所以我理解手足无措的感觉!节奏,播放率有什么不寻常的地方吗?想不出别的了。
    • midi 文件非常简单,很难有任何错误,但在这里,请自行判断:dl.dropbox.com/u/3916368/01010111.mid。让我知道您是否设法重新启动它,例如,在第一次播放后 1 秒。谢谢
    • 非常感谢 skinnyTOD,我很高兴(嗯,有点...)您能够重现该问题,至少我知道这是真实的。我在音乐应用程序中使用它,在用户操作后播放和弦。延迟是一个问题,我不愿意在文件中引入人为延迟。如果不是为了我需要播放的其他更复杂的 MIDI 文件,我可以按照你的建议自己进行排序。而且,不得不重做苹果的工作来解决这样一个愚蠢的错误感觉是错误的。但话又说回来,我可能最终会这样做,因为我建议的修复方法不能 100% 起作用。
    【解决方案2】:

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

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

    为什么当我加载一个新序列或启动一个尚未播放的序列时没有发生这种情况我不清楚...

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多