0

我是Objective C的新手,我正在尝试制作一个在按下按钮时播放midi文件的应用程序。我还希望它在文件播放时不响应额外的按钮按下。我制作了一个自定义按钮类并将此代码(来自教程)放入 .m 文件中:

- (IBAction)MIDItest:(id)sender
{
    MusicSequence s;
    NewMusicSequence(&s);
    NSString *midiFilePath = [[NSBundle mainBundle]
                              pathForResource:@"eDom"
                              ofType:@"mid"];
    NSURL *midiFileURL = [NSURL fileURLWithPath:midiFilePath];
    MusicSequenceFileLoad(s, (__bridge CFURLRef)(midiFileURL), 0, 0);

    MusicPlayer p;
    NewMusicPlayer(&p);
    MusicPlayerSetSequence(p, s);
    MusicPlayerPreroll(p);
    MusicPlayerStart(p);

    MusicTrack t;
    MusicTimeStamp len;
    UInt32 sz = sizeof(MusicTimeStamp);
    MusicSequenceGetIndTrack(s, 1, &t);
    MusicTrackGetProperty(t, kSequenceTrackProperty_TrackLength, &len, &sz);

    while (1) {
        usleep (3 *1000 *1000);
        MusicTimeStamp now = 0;
        MusicPlayerGetTime(p, &now);
        if (now >= len)
            break;
    }

    MusicPlayerStop(p);
    DisposeMusicSequence(s);
    DisposeMusicPlayer(p);
}

但是,当我按下按钮时,什么也没有发生。我确实将按钮设置为自定义类,所以我认为问题出在 MIDI 播放器代码中。谁能告诉我我做错了什么以及如何解决这个问题?

4

1 回答 1

1

该代码存在许多问题。使用while (1)withusleep作为控制结构是一个非常糟糕的主意。它在 MIDI 文件的持续时间内锁定用户界面。我的猜测是,这只是一些糟糕的教程代码,旨在展示如何使用 MusicPlayer API,但无意中展示了一种非常糟糕的实现方式。

正确的做法是将 MusicPlayer 声明为 @property,以便它在应用程序的生命周期内(或无论需要多久)都存在,并根据需要设置和删除 MusicSequences。

您的代码的具体问题是 MusicTracks 是从零开始的(下面的标题定义)并且您要求轨道 1,我猜它不存在。返回一个错误,但您没有检查它。所有 MusicPlayer 函数都会返回一个结果代码,您应该始终检查它们(或者在调试地狱中盲目地受苦...... ;-)

你检查这样的错误:

   OSStatus result = noErr;
   result =  MusicSequenceGetIndTrack(s, 1, &t);
   if (result) {[self printErrorMessage: @"MusicSequenceGetIndTrack" withStatus: result];}

printErrorMessage功能(包括在下面)只是为了方便地记录字符串和结果代码。

至于为什么您的代码不起作用:如果轨道不存在,它的长度将为零,因此您的睡眠代码将不起作用,该方法将立即退出。您可以通过以下方式检查:

 printf(" len    %f\n", len);

MusicPlayer API 非常强大且使用起来很有趣,但始终检查错误结果代码(在此处列出),因为它会静默失败,没有它们你将一无所知。


- (void) printErrorMessage: (NSString *) errorString withStatus: (OSStatus) result
{
    char str[20];
    // see if it appears to be a 4-char-code
    *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(result);
    if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
        str[0] = str[5] = '\'';
        str[6] = '\0';
    } else
        // no, format it as an integer
        sprintf(str, "%d", (int)result);

    //  fprintf(stderr, "Error: %s (%s)\n", operation, str);


    NSLog (
           @"*** %@ error: %s\n",
           errorString,
           str
           );
}

/*!
    @function   MusicSequenceGetIndTrack
    @abstract   Get a track at the specified index
    @discussion Index is zero based. It will return kAudio_ParamError if index is not in the range: 0 < TrackCount
                The track count and accessors exclude the tempo track (which is treated as a special case)
    @param      inSequence      the sequence
    @param      inTrackIndex    the index
    @param      outTrack        the track at that index
*/
于 2013-09-13T22:59:02.200 回答