Objective C MIDI问题

时间:2013-09-13 17:07:36

标签: audiotoolbox coremidi

我是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播放器代码中。谁能告诉我我做错了什么以及如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

该代码存在许多问题。使用while (1)usleep作为控制结构是一个非常糟糕的主意。它会在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非常强大且使用起来很有趣,但始终会检查错误结果代码(listed here),因为它会无声地失败,如果没有它们,您将无法获得线索。


- (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
*/