首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在iOS上使用CoreMIDI?

如何在iOS上使用CoreMIDI?
EN

Stack Overflow用户
提问于 2012-12-19 20:06:38
回答 3查看 14.5K关注 0票数 14

我在iOS的CoreMIDI上找不到太多信息。是否可以通过向设备本身发送消息来播放MIDI声音。iPhone或iPad是否安装了MIDI设备,或者必须将设备连接到接口?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-01-01 19:57:56

你应该看看pete goodliffe's blog,他慷慨地提供了一个示例项目。开始编写CoreMIDI对我有很大的帮助。

现在关于您的问题,在iOS上,主要使用CoreMIDI网络会话。同一“网络会话”的参与者互相发送消息。

例如,您可以在Mac上配置一个网络会话(使用Audio MIDI设置工具),您可以将iOS设备连接到该会话。这样,您就可以将消息从iOS发送到您的OSX主机,反之亦然。

CoreMIDI网络会话依赖RTP协议来传输MIDI消息,而Bonjour协议来发现主机。

此外,CoreMIDI还可以处理连接到系统的MIDI接口,但iOS设备默认没有物理MIDI接口。如果你想直接将你的iPhone连接到合成器上,你必须购买外部硬件。但是,iPad可以通过相机套件连接到符合USB的Midi接口。

另一件事是,在独立的iOS设备上,您可以使用本地CoreMIDI会话发送消息到另一个兼容CoreMIDI的应用程序,或者从另一个兼容的应用程序接收消息。

票数 9
EN

Stack Overflow用户

发布于 2015-07-17 16:05:05

这已经晚了几年了,但它可能会帮助其他人,就像它帮助了我一样。这个website帮助我从外部MIDI键盘读取MIDI数据。连接是最棘手的部分,但本教程将带您完成它。

这是我创建的类。

MIDIController.h

代码语言:javascript
复制
#import <Foundation/Foundation.h>

@interface MIDIController : NSObject

@property NSMutableArray *notes;

@end

MIDIController.m

代码语言:javascript
复制
#import "MIDIController.h"

#include <CoreFoundation/CoreFoundation.h>
#import <CoreMIDI/CoreMIDI.h>

#define SYSEX_LENGTH 1024
#define KEY_ON 1
#define KEY_OFF 0

@implementation MIDIController

- (id)init {
    if (self = [super init]) {
        _notes = [[NSMutableArray alloc] init];
        [self setupMidi];
    }
    return self;
}

- (void) setupMidi {
    MIDIClientRef midiClient;
    checkError(MIDIClientCreate(CFSTR("MIDI client"), NULL, NULL, &midiClient), "MIDI client creation error");

    MIDIPortRef inputPort;
    checkError(MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, (__bridge_retained void *)self, &inputPort), "MIDI input port error");

    checkError(connectMIDIInputSource(inputPort), "connect MIDI Input Source error");

}

OSStatus connectMIDIInputSource(MIDIPortRef inputPort) {
    unsigned long sourceCount = MIDIGetNumberOfSources();
    for (int i = 0; i < sourceCount; ++i) {
        MIDIEndpointRef endPoint = MIDIGetSource(i);
        CFStringRef endpointName = NULL;
        checkError(MIDIObjectGetStringProperty(endPoint, kMIDIPropertyName, &endpointName), "String property not found");
        checkError(MIDIPortConnectSource(inputPort, endPoint, NULL), "MIDI not connected");
    }

    return noErr;
}

void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
    MIDIController *midiController = (__bridge MIDIController*)procRef;

    UInt16 nBytes;
    const MIDIPacket *packet = &list->packet[0]; //gets first packet in list

    for(unsigned int i = 0; i < list->numPackets; i++) {
        nBytes = packet->length; //number of bytes in a packet

        handleMIDIStatus(packet, midiController);

        packet = MIDIPacketNext(packet);
    }
}



void handleMIDIStatus(const MIDIPacket *packet, MIDIController *midiController) {
    int status = packet->data[0];
    //unsigned char messageChannel = status & 0xF; //16 possible MIDI channels

    switch (status & 0xF0) {
        case 0x80:
            updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_OFF);
            break;
        case 0x90:
            //data[2] represents the velocity of a note
            if (packet->data[2] != 0) {
                updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_ON);
            }//note off also occurs if velocity is 0
            else {
                updateKeyboardButtonAfterKeyPressed(midiController, packet->data[1], KEY_OFF);
            }
            break;
        default:
            //NSLog(@"Some other message");
            break;
    }

}

void updateKeyboardButtonAfterKeyPressed(MIDIController *midiController, int key, bool keyStatus) {
    NSMutableArray *notes = [midiController notes];

    //key is being pressed
    if(keyStatus) {
        [notes addObject:[NSNumber numberWithInt:key]];
    }
    else {//key has been released
        for (int i = 0; i < [notes count]; i++) {
            if ([[notes objectAtIndex:i] integerValue] == key) {
                [notes removeObjectAtIndex:i];
            }
        }
    }
}

void checkError(OSStatus error, const char* task) {
    if(error == noErr) return;

    char errorString[20];
    *(UInt32 *)(errorString + 1) = CFSwapInt32BigToHost(error);
    if(isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) {
        errorString[0] = errorString[5] = '\'';
        errorString[6] = '\0';
    }
    else
        sprintf(errorString, "%d", (int)error);

    fprintf(stderr, "Error: %s (%s)\n", task, errorString);
    exit(1);
}


@end

补充说明

midiInputCallback函数

  • midiInputCallback是通过MIDI设备(键盘)发生MIDI事件时调用的函数

注意:这是您可以开始处理MIDI信息的地方

handleMIDIStatus函数

  • handleMIDIStatus获取MIDI包(其中包含有关播放的内容和MIDIController实例的信息

注意:您需要引用MIDIController,以便可以填充class...in的属性my case我按MIDI编号将所有播放的音符存储在一个数组中,以便稍后在

  • status0x90时使用,这意味着已经触发了一个音符,如果它的速度为0,则认为它没有播放...我需要添加这个if语句,因为它没有正常工作

注意:我只处理key onkey off事件,所以您可以扩充switch语句来处理更多的MIDI事件

updateKeyboardButtonAfterKeyPressed方法

  • 这是我用来存储播放的音符的方法,一旦释放了

,我就从这个数组中删除音符

我希望这能帮到你。

票数 10
EN

Stack Overflow用户

发布于 2021-08-20 07:30:01

代码语言:javascript
复制
import UIKit
import CoreMIDI

class ViewController : UIViewController {
    
    // MARK: - Properties -
    
    var inputPort : MIDIPortRef = 0

    var source : MIDIDeviceRef = 0

    var client = MIDIClientRef()

    var connRefCon : UnsafeMutableRawPointer?

    var endpoint : MIDIEndpointRef?
    
    // MARK: - Lifecycle -

    override func viewDidLoad() {
        
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        
        print("viewDidLoad")
        
        // endpoint
        
        self.endpoint = MIDIGetSource(MIDIGetNumberOfSources()-1)
        
        // USB Device References
        
        let sources = getUSBDeviceReferences()
        
        if sources.count > 0 {
        
            self.source = sources.first!
            
        }
        
        print("source: \(source)")

        // create client
        
        DispatchQueue.global().async {
            
            self.createClient()
            
        }

    }
    
    // MARK: - USB Device References -
    
    /// Filters all `MIDIDeviceRef`'s for USB-Devices
    
    private func getUSBDeviceReferences() -> [MIDIDeviceRef] {
        
        var devices = [MIDIDeviceRef]()
        
        for index in 0 ..< MIDIGetNumberOfDevices() {

            print("index: \(index)")
            
            let device = MIDIGetDevice(index)
            
            var list : Unmanaged<CFPropertyList>?

            MIDIObjectGetProperties(device, &list, true)
            
            if let list = list {

                let dict = list.takeRetainedValue() as! NSDictionary
                
                print("dict: \(dict)")

                if dict["USBLocationID"] != nil {

                    print("USB MIDI DEVICE")

                    devices.append(device)


                }

            }

        }

        return devices

    }
    
    // MARK: - Client -
    
    func createClient() {
        
        print("createClient")
        
        let clientName = "Client" as CFString
        
        let err = MIDIClientCreateWithBlock(clientName, &client) { (notificationPtr: UnsafePointer<MIDINotification>) in
            let notification = notificationPtr.pointee
            
            print("notification.messageID: \(notification.messageID)")
            
            switch notification.messageID {
                
                case .msgSetupChanged: // Can ignore, really
                    break
                    
                case .msgObjectAdded:
                    let rawPtr = UnsafeRawPointer(notificationPtr)
                    let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee
                    print("MIDI \(message.childType) added: \(message.child)")
                    
                case .msgObjectRemoved:
                    let rawPtr = UnsafeRawPointer(notificationPtr)
                    let message = rawPtr.assumingMemoryBound(to: MIDIObjectAddRemoveNotification.self).pointee
                    print("MIDI \(message.childType) removed: \(message.child)")
                    
                case .msgPropertyChanged:
                    let rawPtr = UnsafeRawPointer(notificationPtr)
                    let message = rawPtr.assumingMemoryBound(to: MIDIObjectPropertyChangeNotification.self).pointee
                    print("MIDI \(message.object) property \(message.propertyName.takeUnretainedValue()) changed.")
                    
                case .msgThruConnectionsChanged:
                    fallthrough
                    
                case .msgSerialPortOwnerChanged:
                    print("MIDI Thru connection was created or destroyed")
                    
                case .msgIOError:
                    let rawPtr = UnsafeRawPointer(notificationPtr)
                    let message = rawPtr.assumingMemoryBound(to: MIDIIOErrorNotification.self).pointee
                    print("MIDI I/O error \(message.errorCode) occurred")
                    
                default:
                    break

            }
            
        }
        
        // createInputPort from client
        
        self.createInputPort(midiClient: self.client)
        
        if err != noErr {
            print("Error creating MIDI client: \(err)")
        }
        
        // run on background for connect / disconnect
        
        let rl = RunLoop.current

        while true {
            rl.run(mode: .default, before: .distantFuture)
        }
        
    }
    
    // MARK: - Input Port -
    
    func createInputPort(midiClient: MIDIClientRef) {
                
        print("createInputPort: midiClient: \(midiClient)")
        
        MIDIInputPortCreateWithProtocol(
            midiClient,
            "Input Port" as CFString,
            MIDIProtocolID._1_0,
            &self.inputPort) {  [weak self] eventList, srcConnRefCon in
            
            //
                        
            let midiEventList: MIDIEventList = eventList.pointee

            //print("srcConnRefCon: \(srcConnRefCon)")
            //print("midiEventList.protocol: \(midiEventList.protocol)")
            
            var packet = midiEventList.packet
            
            //print("packet: \(packet)")
                       
            (0 ..< midiEventList.numPackets).forEach { _ in
                                
                //print("\(packet)")
                                
                let words = Mirror(reflecting: packet.words).children
                words.forEach { word in
                    
                    let uint32 = word.value as! UInt32
                    guard uint32 > 0 else { return }
                    
                    let midiPacket = MidiPacket(
                        command: UInt8((uint32 & 0xFF000000) >> 24),
                        channel: UInt8((uint32 & 0x00FF0000) >> 16),
                        note: UInt8((uint32 & 0x0000FF00) >> 8),
                        velocity: UInt8(uint32 & 0x000000FF))

                    print("----------")
                    print("MIDIPACKET")
                    print("----------")
                    midiPacket.printValues()
                    
                }
                
            }

        }
                        
        MIDIPortConnectSource(self.inputPort, self.endpoint ?? MIDIGetSource(MIDIGetNumberOfSources()-1), &self.connRefCon)

    }
    
}

class MidiPacket : NSObject {
    
    var command : UInt8 = 0
    var channel : UInt8 = 0
    var note : UInt8 = 0
    var velocity : UInt8 = 0
    
    init(command: UInt8, channel: UInt8, note: UInt8, velocity: UInt8) {
        
        super.init()
        
        self.command = command
        self.channel = channel
        self.note = note
        self.velocity = velocity
        
    }
    
    func printValues(){
        
        print("command: \(self.command)")
        print("channel: \(self.channel)")
        print("note: \(self.note)")
        print("velocity: \(self.velocity)")

    }

}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13952151

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档