【问题标题】:How to use CoreMIDI on iOS?如何在 iOS 上使用 CoreMIDI?
【发布时间】:2012-12-19 12:06:38
【问题描述】:

我一直无法找到有关 iOS 版 CoreMIDI 的更多信息。甚至可以通过向设备本身发送消息来播放 MIDI 声音。 iPhone 或 iPad 是否安装了 MIDI 设备,或者您是否必须连接设备才能与之连接?

【问题讨论】:

    标签: ios coremidi


    【解决方案1】:

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

    这是我创建的类。

    MIDIController.h

    #import <Foundation/Foundation.h>
    
    @interface MIDIController : NSObject
    
    @property NSMutableArray *notes;
    
    @end
    

    MIDIController.m

    #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 的引用,以便您可以填充类的属性...在我的情况下,我将所有播放的音符按 MIDI 编号存储在一个数组中以供以后使用

    • status0x90时,表示一个音符已被触发,如果它的力度为0,则认为它没有播放...我需要添加这个if语句,因为它是'无法正常运行
      注意:我只处理 key onkey off 事件,因此您可以增加 switch 语句以处理更多 MIDI 事件

    updateKeyboardButtonAfterKeyPressed 方法

    • 这是我用来存储播放的音符的一种方法,一旦释放键,我就会从这个数组中删除音符

    我希望这会有所帮助。

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      另外,在独立的 iOS 设备上,您可以使用本地 CoreMIDI 会话来发送或接收来自另一个 CoreMIDI 兼容应用程序的消息。

      【讨论】:

        【解决方案3】:
        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)")
        
            }
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-04-02
          • 1970-01-01
          • 2012-05-16
          • 2017-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多