手动在 iOS 上宣传蓝牙 MIDI,无需 CABTMIDILocalPeripheralViewController
Advertise Bluetooth MIDI on iOS manually, without CABTMIDILocalPeripheralViewController
我刚刚发现 CABTMIDILocalPeripheralViewController
iOS
处理用于启用蓝牙 MIDI 可发现性的用户设置。这是
很好,但为了将蓝牙集成到我应用程序的其余部分
网络 MIDI 连接最好能够处理
直接从我的应用程序代码启用,而不是依赖这个不透明的 VC。
有谁知道这是否可能?
我想你可能正在寻找这个:CABTMIDICentralViewController
此页面上的更多信息:https://developer.apple.com/library/ios/qa/qa1831/_index.html
基本上这可以帮助您通过您的应用程序扫描并连接到设备。我不确定您是只想被发现还是想成为扫描的人。
希望对您有所帮助
没有 public API 来管理此功能。在使用 Instruments 进行调查后,该开关似乎导致了 CBPeripheralManager
的实例化。我假设它将设备设置为蓝牙外围设备,并手动将数据传入和传出也已创建的 MIDIEndpointRef
。
换句话说,没有单行解决方案。如果我沿着这条路走得更远,我将 post 代码,除非其他人想要尝试...
更新
魔术代码...
- (instancetype)init
{
self = [super init];
if (self) {
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}
return self;
}
//---------------------------------------------------------------------
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
return;
}
info(@"_peripheralManager powered on.");
// CBMutableCharacteristic *tx = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:RBL_TX_UUID] properties:CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:CBAttributePermissionsWriteable];
//
rx = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"7772E5DB-3868-4112-A1A9-F2669D106BF3"] properties:CBCharacteristicPropertyRead|CBCharacteristicPropertyWriteWithoutResponse|CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable|CBAttributePermissionsWriteable];
CBMutableService *s = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"] primary:YES];
s.characteristics = @[rx];
[_peripheralManager addService:s];
NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey : BLE_NAME, CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"]]};
[_peripheralManager startAdvertising:advertisingData];
}
正是这些 ID 定义了 MIDI 外围设备。更多信息:
Apple 曾经在
有一个文档
...不再是。我很想找到一份旧副本,因为事实证明接收方代码(模拟 CABTMIDICentralViewController
)更难破解...
所以我拼凑了一个非常 hacky 的解决方案,用于发现用户在 CABTMIDICentralViewController 中单击了哪个 MIDI 设备。我不确定这是否是个好主意——如果 Apple 更改了控制器的内部结构,它就无法再工作了。另外我不确定它是否 "legal" 关于 App Store 指南。有人知道这方面的更多信息吗?
DPBleMidiDeviceManager.h:
#import <CoreAudioKit/CoreAudioKit.h>
@protocol MidiDeviceConnectedDelegate <NSObject>
-(void) onMidiDeviceConnected: (NSString*) deviceName;
@end
@interface DPBleMidiDeviceManager : CABTMIDICentralViewController
@property (weak, nonatomic) id<MidiDeviceConnectedDelegate> midiDeviceDelegate;
@end
DPBleMidiDeviceManager.m:
#import "DPBleMidiDeviceManager.h"
@implementation DPBleMidiDeviceManager
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"midi device selected %@", indexPath);
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
// TODO: this is very bad. apple may change their internal API and this will break.
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell respondsToSelector:@selector(deviceNameLabel)]) {
UILabel* deviceLabel = [cell performSelector:@selector(deviceNameLabel)];
NSLog(@"midi device named %@", deviceLabel.text);
// must wait a couple seconds for it to actually connect.
[self performSelector:@selector(sendMidiDeviceConnected:) withObject:deviceLabel.text afterDelay: 3];
}
}
- (void) sendMidiDeviceConnected: (NSString*) deviceName
{
[self.midiDeviceDelegate onMidiDeviceConnected:deviceName];
}
@end
然后,在您的父视图控制器中,您可以从委托中获取结果并查找与该名称匹配的新 MIDI 设备:
...
DPBleMidiDeviceManager *controller = [DPBleMidiDeviceManager new];
controller.midiDeviceDelegate = self;
// now present the VC as usual
...
-(void) onMidiDeviceConnected: (NSString*) deviceName
{
[self connectMidiDevice: deviceName];
}
/**
Connects to a MIDI source with the given name,
and interprets all notes from that source as notes;
*/
- (void) connectMidiDevice: (NSString*) deviceName
{
NSLog(@"Connecting to MIDI device: %@", deviceName);
PGMidi* midi = [[PGMidi alloc] init];
if (midi != NULL) {
NSArray* sources = midi.sources;
for (PGMidiSource* src in sources) {
NSLog(@"Found midi source: %@", src.name);
if ([src.name containsString: deviceName]) {
NSLog(@"Connecting to midi source: %@", src.name);
[src addDelegate:self];
}
}
}
}
我能想到的唯一其他选择是在显示控制器之前扫描 MIDI 设备,保存设备列表,然后打开控制器。当它关闭时,再次扫描 MIDI 设备,并将新列表与旧列表进行比较。出现的任何新 MIDI 设备都是用户选择的。不知道为什么 Apple 没有让我们更容易...
这是swift5版本。这很好用。
import SpriteKit
import CoreBluetooth
class GameScene: SKScene {
var manager: CBPeripheralManager!
var service: CBMutableService!
// MIDI Service UUID
let serviceID = CBUUID(string: "03B80E5A-EDE8-4B33-A751-6CE34EC4C700")
// MIDI I/O Characteristic UUID
let characteristicID = CBUUID(string: "7772E5DB-3868-4112-A1A9-F2669D106BF3")
override func didMove(to view: SKView) {
super.didMove(to: view)
self.manager = CBPeripheralManager(delegate : self, queue : nil, options: nil)
}
}
extension GameScene: CBPeripheralManagerDelegate {
// Notify the peripheral state
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
guard peripheral.state == .poweredOn else {
print("error: \(peripheral.state)")
return
}
print("bluetooth pwoer on")
// Add service and characteristic
self.service = CBMutableService(type: self.serviceID,
primary: true)
let properties: CBCharacteristicProperties = [.notify, .read, .writeWithoutResponse]
let permissions: CBAttributePermissions = [.readable, .writeable]
let characteristic = CBMutableCharacteristic(type: self.characteristicID,
properties: properties,
value: nil, permissions: permissions)
self.service.characteristics = [characteristic]
self.manager.add(self.service)
}
// Notify add the service
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
guard (error == nil) else {
print("Add service failed")
return
}
print("Add service succeeded")
// Start advertising
let advertisementData = [CBAdvertisementDataLocalNameKey: "Penguin Drums",
CBAdvertisementDataServiceUUIDsKey: [self.serviceID]] as [String : Any]
manager.startAdvertising(advertisementData)
print("Service starts advertising!")
}
}
我刚刚发现 CABTMIDILocalPeripheralViewController
iOS
处理用于启用蓝牙 MIDI 可发现性的用户设置。这是
很好,但为了将蓝牙集成到我应用程序的其余部分
网络 MIDI 连接最好能够处理
直接从我的应用程序代码启用,而不是依赖这个不透明的 VC。
有谁知道这是否可能?
我想你可能正在寻找这个:CABTMIDICentralViewController 此页面上的更多信息:https://developer.apple.com/library/ios/qa/qa1831/_index.html 基本上这可以帮助您通过您的应用程序扫描并连接到设备。我不确定您是只想被发现还是想成为扫描的人。 希望对您有所帮助
没有 public API 来管理此功能。在使用 Instruments 进行调查后,该开关似乎导致了 CBPeripheralManager
的实例化。我假设它将设备设置为蓝牙外围设备,并手动将数据传入和传出也已创建的 MIDIEndpointRef
。
换句话说,没有单行解决方案。如果我沿着这条路走得更远,我将 post 代码,除非其他人想要尝试...
更新
魔术代码...
- (instancetype)init
{
self = [super init];
if (self) {
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
}
return self;
}
//---------------------------------------------------------------------
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
return;
}
info(@"_peripheralManager powered on.");
// CBMutableCharacteristic *tx = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:RBL_TX_UUID] properties:CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:CBAttributePermissionsWriteable];
//
rx = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"7772E5DB-3868-4112-A1A9-F2669D106BF3"] properties:CBCharacteristicPropertyRead|CBCharacteristicPropertyWriteWithoutResponse|CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable|CBAttributePermissionsWriteable];
CBMutableService *s = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"] primary:YES];
s.characteristics = @[rx];
[_peripheralManager addService:s];
NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey : BLE_NAME, CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"]]};
[_peripheralManager startAdvertising:advertisingData];
}
正是这些 ID 定义了 MIDI 外围设备。更多信息:
Apple 曾经在
有一个文档...不再是。我很想找到一份旧副本,因为事实证明接收方代码(模拟 CABTMIDICentralViewController
)更难破解...
所以我拼凑了一个非常 hacky 的解决方案,用于发现用户在 CABTMIDICentralViewController 中单击了哪个 MIDI 设备。我不确定这是否是个好主意——如果 Apple 更改了控制器的内部结构,它就无法再工作了。另外我不确定它是否 "legal" 关于 App Store 指南。有人知道这方面的更多信息吗?
DPBleMidiDeviceManager.h:
#import <CoreAudioKit/CoreAudioKit.h>
@protocol MidiDeviceConnectedDelegate <NSObject>
-(void) onMidiDeviceConnected: (NSString*) deviceName;
@end
@interface DPBleMidiDeviceManager : CABTMIDICentralViewController
@property (weak, nonatomic) id<MidiDeviceConnectedDelegate> midiDeviceDelegate;
@end
DPBleMidiDeviceManager.m:
#import "DPBleMidiDeviceManager.h"
@implementation DPBleMidiDeviceManager
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"midi device selected %@", indexPath);
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
// TODO: this is very bad. apple may change their internal API and this will break.
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell respondsToSelector:@selector(deviceNameLabel)]) {
UILabel* deviceLabel = [cell performSelector:@selector(deviceNameLabel)];
NSLog(@"midi device named %@", deviceLabel.text);
// must wait a couple seconds for it to actually connect.
[self performSelector:@selector(sendMidiDeviceConnected:) withObject:deviceLabel.text afterDelay: 3];
}
}
- (void) sendMidiDeviceConnected: (NSString*) deviceName
{
[self.midiDeviceDelegate onMidiDeviceConnected:deviceName];
}
@end
然后,在您的父视图控制器中,您可以从委托中获取结果并查找与该名称匹配的新 MIDI 设备:
...
DPBleMidiDeviceManager *controller = [DPBleMidiDeviceManager new];
controller.midiDeviceDelegate = self;
// now present the VC as usual
...
-(void) onMidiDeviceConnected: (NSString*) deviceName
{
[self connectMidiDevice: deviceName];
}
/**
Connects to a MIDI source with the given name,
and interprets all notes from that source as notes;
*/
- (void) connectMidiDevice: (NSString*) deviceName
{
NSLog(@"Connecting to MIDI device: %@", deviceName);
PGMidi* midi = [[PGMidi alloc] init];
if (midi != NULL) {
NSArray* sources = midi.sources;
for (PGMidiSource* src in sources) {
NSLog(@"Found midi source: %@", src.name);
if ([src.name containsString: deviceName]) {
NSLog(@"Connecting to midi source: %@", src.name);
[src addDelegate:self];
}
}
}
}
我能想到的唯一其他选择是在显示控制器之前扫描 MIDI 设备,保存设备列表,然后打开控制器。当它关闭时,再次扫描 MIDI 设备,并将新列表与旧列表进行比较。出现的任何新 MIDI 设备都是用户选择的。不知道为什么 Apple 没有让我们更容易...
这是swift5版本。这很好用。
import SpriteKit
import CoreBluetooth
class GameScene: SKScene {
var manager: CBPeripheralManager!
var service: CBMutableService!
// MIDI Service UUID
let serviceID = CBUUID(string: "03B80E5A-EDE8-4B33-A751-6CE34EC4C700")
// MIDI I/O Characteristic UUID
let characteristicID = CBUUID(string: "7772E5DB-3868-4112-A1A9-F2669D106BF3")
override func didMove(to view: SKView) {
super.didMove(to: view)
self.manager = CBPeripheralManager(delegate : self, queue : nil, options: nil)
}
}
extension GameScene: CBPeripheralManagerDelegate {
// Notify the peripheral state
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
guard peripheral.state == .poweredOn else {
print("error: \(peripheral.state)")
return
}
print("bluetooth pwoer on")
// Add service and characteristic
self.service = CBMutableService(type: self.serviceID,
primary: true)
let properties: CBCharacteristicProperties = [.notify, .read, .writeWithoutResponse]
let permissions: CBAttributePermissions = [.readable, .writeable]
let characteristic = CBMutableCharacteristic(type: self.characteristicID,
properties: properties,
value: nil, permissions: permissions)
self.service.characteristics = [characteristic]
self.manager.add(self.service)
}
// Notify add the service
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
guard (error == nil) else {
print("Add service failed")
return
}
print("Add service succeeded")
// Start advertising
let advertisementData = [CBAdvertisementDataLocalNameKey: "Penguin Drums",
CBAdvertisementDataServiceUUIDsKey: [self.serviceID]] as [String : Any]
manager.startAdvertising(advertisementData)
print("Service starts advertising!")
}
}