【问题标题】:IOS How record midi file with Midi input callback?IOS 如何用Midi输入回调记录midi文件?
【发布时间】:2014-05-14 19:35:48
【问题描述】:

我尝试用 Ipad 录制 MIDI 文件。 我的 Ipad 插上了我的电钢琴的 USB 输出。

我已阅读 Apple 核心 midi 文档,并且了解: 为了录制文件,我应该创建一个 MusicSequence。所以我尝试这样做,但它不起作用:(

这是我的代码:

首先,我设置了我的 MIDI 连接:

-(void) setupMIDI {

MIDIClientRef client = nil;
MIDIClientCreate(CFSTR("Core MIDI to System Sounds Demo"), MyMIDINotifyProc, (__bridge void *)(self), &client);

inputPort = nil;
MIDIInputPortCreate(client, CFSTR("Input port"), MyMIDIReadProc, (__bridge void *)(self), &inputPort);

sequence = nil;
NewMusicSequence(&(sequence));

unsigned long sourceCount = MIDIGetNumberOfSources();
[self appendToTextView:[NSString stringWithFormat:@"%ld sources\n", sourceCount]];
for (int i = 0; i < sourceCount; ++i) {
    MIDIEndpointRef src = MIDIGetSource(i);
    CFStringRef endpointName = NULL;
    OSStatus nameErr = MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endpointName);
    if (noErr == nameErr) {
        [self appendToTextView: [NSString stringWithFormat:@"  source %d: %@\n", i, endpointName]];
    }
    MIDIPortConnectSource(inputPort, src, NULL);
    MusicSequenceSetMIDIEndpoint(sequence, src);
}

}

之后,我通过 MyMIDIReadProc 接收到我的 Midi 事件,这是我的输入端口的回调函数:

static void MyMIDIReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{

AppViewController *vc = (__bridge AppViewController*) refCon;

    MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
    for (int i=0; i < pktlist->numPackets; i++) {
    Byte midiStatus = packet->data[0];
    Byte midiCommand = midiStatus >> 4;
    // is it a note-on or note-off
    if ((midiCommand == 0x09) ||
        (midiCommand == 0x08)) {
        Byte note = packet->data[1] & 0x7F;
        Byte velocity = packet->data[2] & 0x7F;
        NSLog(@"midiCommand=%d. Note=%d, Velocity=%d\n", midiCommand, note, velocity);

        MIDINoteMessage noteMessage;
        noteMessage.releaseVelocity = 0;
        noteMessage.velocity = velocity;
        noteMessage.note = note;

        MusicTrackNewMIDINoteEvent(vc->musicTrack, packet->timeStamp, &noteMessage);

        packet = MIDIPacketNext(packet);
    }


}

我尝试在 MIDINoteMessage 上转换 MIDIPklist 以将其添加到我的音轨中。

完成后,我使用此函数创建文件:

-(void) createMidiFile
{
// init sequence
NewMusicSequence(&sequence);
CFURLRef pathUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:self.path];

//set track to sequence
MusicSequenceNewTrack(sequence, &musicTrack);

// write sequence in file
MusicSequenceFileCreate(sequence,
                        pathUrl,
                        kMusicSequenceFile_MIDIType,
                        kMusicSequenceFileFlags_EraseFile,
                        0);
}

文件已创建,但数据不正确。它每次都具有相同的大小。

谢谢你能帮我调试一下!我不明白我必须做什么来填充轨道和序列对象以创建一个好的中间文件......

对不起我的英语家伙.. :)

【问题讨论】:

    标签: ios objective-c midi coremidi


    【解决方案1】:

    我正在尝试解决同样的问题。从我所看到的 - MIDINoteMessage 需要有一个持续时间,该持续时间对应于音符开和随后的音符关呼叫的增量。您必须跟踪这一点。

    回调应该在主线程上执行,您需要使用 CACurrentMediaTime 在转储 midi 文件之前存储时间 midi 时间戳。下面的一些代码。

    另一种替代方法来自苹果论坛

    "创建一个MusicSequence,添加一个MusicTrack,通过MusicTrackNewMidiNoteEvent向轨道添加一些midi事件,在一个新创建的MusicPlayer上设置MusicSequence,然后启动播放器。现在你已经播放了那个播放器,你可以查询它通过 MusicPlayerGetTime 函数以节拍为单位的当前时间。为您发送到 MusicTrackNewMidiNoteEvent 的 MIDI 消息的 MusicTimeStamp 设置该时间。

    **重要提示 - 您必须为正在查询时间戳的 MusicPlayer 填充 MusicTrack,否则它将无法播放!您会收到一个错误(可能是(-50),具体取决于您是否正确设置了其他所有内容)。我用一个循环来做这个,添加一个时间戳从零开始的消息,最多四个。我猜你甚至不必走那么高,但赛道必须有一些东西让玩家玩。不要担心它会跑完,因为我们想要的只是 MusicTimeStamp。 MusicPlayer 将继续播放,直到您告诉它停止为止。”

    这段代码是我来回答的最接近的 - 尽管这是针对 IOS。

    https://github.com/benweitzman/ReTune/blob/ee47009999298c2b03527302c3fb6d7be17b10e2/Return4/ViewController.m

    @interface NoteObject : NSObject
    
    @property (nonatomic) int time;
    @property (nonatomic) int note;
    @property (nonatomic) int velocity;
    @property (nonatomic) bool noteOn;
    
    @end
    
    - (void) midiSource:(PGMidiSource*)midi midiReceived:(const MIDIPacketList *)packetList
    {
        [self performSelectorOnMainThread:@selector(addString:)
                               withObject:@"MIDI received:"
                            waitUntilDone:NO];
    
        const MIDIPacket *packet = &packetList->packet[0];
        for (int i = 0; i < packetList->numPackets; ++i)
        {
            //[self performSelectorOnMainThread:@selector(addString:)
            //                      withObject:[self StringFromPacket:packet]
            //                    waitUntilDone:NO];
            if (packet->length == 3) {
                if ((packet->data[0]&0xF0) == 0x90) {
                    if (packet->data[2] != 0) {
                        [self noteOn:packet->data[1] withVelocity:packet->data[2]];
                    } else {
                        [self noteOff:packet->data[1]];
                    }
                } else if ((packet->data[0]&0xF0) == 0x80) {
                    [self noteOff:packet->data[1]];
                }
            }
            packet = MIDIPacketNext(packet);
        }
    }
    
    
        - (void) noteOff:(int)noteValue {
            //NSLog(@"off");
            if (noteValue>=0 && noteValue<127) {
                ALSource * source = [sources objectAtIndex:noteValue];
                ALSource *loopSource = [loopSources objectAtIndex:noteValue];
                if (source.playing || loopSource.playing) {
                    [[fadingOut objectAtIndex:noteValue] release];
                    [fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:YES]];
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        float timeDone = 0;
                        float duration = 0.2;
                        float timeStep = 0.01;
                        float valStep = source.gain*timeStep/duration;
                        float loopStep = loopSource.gain*timeStep/duration;
                        while (timeDone < duration) {
                            if (![[fadingOut objectAtIndex:noteValue] boolValue]) break;
                            source.gain -= valStep;
                            loopSource.gain -= loopStep;
                            [NSThread sleepForTimeInterval:timeStep];
                            timeDone += timeStep;
                        }
                        if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
                            [source stop];
                            [loopSource stop];
                        }
                        //source.gain = 1;
                    });
    
                    if (recording) {
                        double currentTime = CACurrentMediaTime();
                        int deltaTime = (int)(currentTime*1000-recordTimer*1000);
                        NoteObject * recordedNote = [[NoteObject alloc] init];
                        recordedNote.note = noteValue;
                        recordedNote.time = deltaTime;
                        recordedNote.noteOn = false;
                        [recordedNotes addObject:recordedNote];
                        recordTimer = currentTime;
                    }
                }
            }
        }
    
        - (void) finishFadeIn:(ALSource*)source {
    
        }
    
        - (void) noteOn:(int)noteValue withVelocity:(int)velocity {
            if (noteValue>=0 && noteValue<127) {
                if (recording) {
                    double currentTime = CACurrentMediaTime();
                    int deltaTime = (int)(currentTime*1000-recordTimer*1000);
                    NoteObject * recordedNote = [[NoteObject alloc] init];
                    recordedNote.note = noteValue;
                    recordedNote.time = deltaTime;
                    recordedNote.noteOn = true;
                    [recordedNotes addObject:recordedNote];
                    recordTimer = currentTime;
                }
                while(loadingScale || changingPitch);
                float pitchToPlay = [[ratios objectAtIndex:noteValue] floatValue];
                [[fadingOut objectAtIndex:noteValue] release];
                [fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:NO]];
                ALSource * source = [sources objectAtIndex:noteValue];
                [source stop];
                source.gain = velocity/127.0f;
                source.pitch = pitchToPlay;
                [source play:[buffers objectAtIndex:noteValue]];
                if ([loopBuffers objectAtIndex:noteValue] != (id)[NSNull null]) {
                    ALSource *loopSource = [loopSources objectAtIndex:noteValue];
                    [loopSource stop];
                    loopSource.gain = 0;
                    loopSource.pitch = source.pitch;
                    [loopSource play:[loopBuffers objectAtIndex:noteValue] loop:YES];
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        float timeDone = 0;
                        float duration = [(ALBuffer*)[buffers objectAtIndex:noteValue] duration]-.4;
                        float timeStep = 0.01;
                        float valStep = source.gain*timeStep/duration;
                        float loopStep = valStep;
                        while (timeDone < duration) {
                            if ([[fadingOut objectAtIndex:noteValue] boolValue]) break;
                            source.gain -= valStep;
                            loopSource.gain += loopStep;
                            [NSThread sleepForTimeInterval:timeStep];
                            timeDone += timeStep;
                        }
                        /*if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
                            [source stop];
                            [loopSource stop];
                        }*/
                        //source.gain = 1;
                    });
                }
                /*
                [source play];*/
                //[[sources objectAtIndex:noteValue] play:toPlay gain:velocity/127.0f pitch:pitchToPlay pan:0.0f loop:FALSE];
            }
        }
    

    【讨论】:

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