14

CoreMIDI for iOS に関する情報はあまり見つかりませんでした。デバイス自体にメッセージを送信して MIDI サウンドを再生することさえ可能ですか? iPhone または iPad には MIDI デバイスがインストールされていますか、それともインターフェイスするためにデバイスを接続する必要がありますか?

4

3 に答える 3

10

これは数年遅すぎますが、私を助けてくれたように、他の誰かを助けるかもしれません. このWeb サイトは、外部 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 関数

  • midiInputCallbackMIDI デバイス (キーボード) を介して MIDI イベントが発生したときに呼び出される関数です
    。 注: ここから MIDI 情報の処理を開始できます。

handleMIDIStatus 関数

  • handleMIDIStatusMIDIパケットを取得します(これには、再生されたものに関する情報とMIDIControllerのインスタンスが含まれます)
    注:クラスのプロパティを設定できるように、MIDIControllerへの参照が必要です...私の場合、再生されたすべてのノートをMIDI番号で保存します、後で使用するための配列内

  • が の場合、これstatus0x90ノートがトリガーされたことを意味し、ベロシティが 0 の場合は再生されていないと見なされます...適切に機能していなかったので、この if ステートメントを追加する必要がありました
    注:key onkey offイベントのみを処理します。したがって、より多くの MIDI イベントを処理するために switch ステートメントを拡張します。

updateKeyboardButtonAfterKeyPressed メソッド

  • これは、演奏されたノートを保存するために使用した方法であり、キーが離されると、この配列からノートを削除します

これが役立つことを願っています。

于 2015-07-17T08:05:04.957 に答える
9

pete goodliffe のブログを参照してください。彼はサンプル プロジェクトを惜しみなく提供しています。CoreMIDI のプログラミングを始めるのに大いに役立ちました。

さて、ご質問ですが、iOSでは主にCoreMIDIネットワークセッションを利用しています。同じ「ネットワーク セッション」の参加者は、互いにメッセージを送信します。

たとえば、Mac でネットワーク セッションを構成し (Audio MIDI 設定ツールを使用)、iOS デバイスをそれに接続できます。このようにして、iOS から OSX ホストに、またはその逆にメッセージを送信できます。

CoreMIDI ネットワーク セッションは、RTP プロトコルを使用して MIDI メッセージを転送し、Bonjour を使用してホストを検出します。

その次に、CoreMIDI はシステムに接続された MIDI インターフェイスも処理できますが、iOS デバイスにはデフォルトで物理的な MIDI インターフェイスがありません。iPhone をシンセサイザーに直接接続する場合は、外部ハードウェアを購入する必要があります。ただし、iPad は、カメラ キットを介して USB クラス準拠の Midi インターフェイスに接続できます。

別のこととして、スタンドアロンの iOS デバイスでは、ローカルの CoreMIDI セッションを使用して、別の CoreMIDI 互換アプリケーションとの間でメッセージを送受信できます。

于 2013-01-01T11:57:55.710 に答える
1
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)")

    }

}
于 2021-08-20T07:30:01.247 に答える