Подтвердить что ты не робот

Как найти аудиоустройства Bluetooth в iOS

Хорошо, я работаю над забавным проектом, у которого есть препятствие, когда мне нужно включить поддержку звука Bluetooth для моего приложения для iOS.

Я сталкиваюсь с препятствием в том, что просто не могу начать получать список подключенных аудиоустройств Bluetooth. Несмотря на то, что мой iPhone 5S распознает мой наушник (точнее, 3–4-летний LG HBM-230) и воспроизводит через него звук для телефонных звонков, ОБА Внешний аксессуар и CoreBluetooth не дают мне ничего полезного, когда я запрашиваю оба.

Я основываю свой собственный код на вопросах и ответах, которые я нашел для платформ CoreBluetooth и External Accessory.

Когда мой код просто пытается " scanForPeripheralsWithServices:nil " для любых устройств Bluetooth, которые Settings-> Bluetooth говорят, что они видимы и подключены, приведенный ниже код просто НЕ подходит ни к одному CBCentralManagerStatePoweredOn сообщения " CBCentralManagerStatePoweredOn " в консоли.

И эта строка в моем коде (с действительным экземпляром EAAccessoryManager)

NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];

также возвращается с нулевым массивом.

Что я могу делать не так?

Кстати, я сделал этот код доступным как проект GitHub.

@implementation BluetoothManager

+ (BluetoothManager *)sharedInstance
{
    static dispatch_once_t pred = 0;
    __strong static id _bluetoothMGR = nil;

    dispatch_once(&pred, ^{
        _bluetoothMGR = [[BluetoothManager alloc] init];
    });

    return _bluetoothMGR;
}

- (id)init
{
    self = [super init];
    if(self)
    {
        dispatch_queue_t centralQueue = dispatch_queue_create("com.yo.mycentral", DISPATCH_QUEUE_SERIAL);

        // whether we try this on a queue of "nil" (the main queue) or this separate thread, still not getting results
        self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:nil];
    }
    return self;
}

// this would hit.... if I instantiated this in a storyboard of XIB file
- (void)awakeFromNib
{
    if(!self.cbManager)
        self.cbManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

    NSLog(@"hey I found %@",[advertisementData description]);
}

- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
    NSLog( @"I retrieved CONNECTED peripherals");
}

-(void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals{
    NSLog(@"This is it!");
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    NSString *messtoshow;

    switch (central.state) {
        case CBCentralManagerStateUnknown:
        {
            [email protected]"State unknown, update imminent.";
            break;
        }
        case CBCentralManagerStateResetting:
        {
            [email protected]"The connection with the system service was momentarily lost, update imminent.";
            break;
        }
        case CBCentralManagerStateUnsupported:
        {
            [email protected]"The platform doesn't support Bluetooth Low Energy";
            break;
        }
        case CBCentralManagerStateUnauthorized:
        {
            [email protected]"The app is not authorized to use Bluetooth Low Energy";
            break;
        }
        case CBCentralManagerStatePoweredOff:
        {
            [email protected]"Bluetooth is currently powered off.";
            break;
        }
        case CBCentralManagerStatePoweredOn:
        {
            [email protected]"Bluetooth is currently powered on and available to use.";
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], CBCentralManagerScanOptionAllowDuplicatesKey, nil];

            [_cbManager scanForPeripheralsWithServices:nil options:options];

            break;
        }   

    }
    NSLog(@"%@", messtoshow);
}

@end
4b9b3361

Ответ 1

Сначала вам нужно настроить сеанс аудиозаписей приложений, чтобы разрешить соединения Bluetooth, поддерживающие аудио. Вы можете сделать это, например, в приложении приложения-приложения (BOOL): (UIApplication *) application doneFinishLaunchingWithOptions: (NSDictionary *) метод запускаОпции. Убедитесь, что вы привязываете структуру AVFoundation и импортируете ее в заголовки, которые будут использовать ее.

#import <AVFoundation/AVFoundation.h>// place in .h

[self prepareAudioSession];// called from application didFinishLaunchingWithOptions

- (BOOL)prepareAudioSession {

     // deactivate session
     BOOL success = [[AVAudioSession sharedInstance] setActive:NO error: nil];
     if (!success) {
         NSLog(@"deactivationError");
     }

     // set audio session category AVAudioSessionCategoryPlayAndRecord options AVAudioSessionCategoryOptionAllowBluetooth
     success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
     if (!success) {
         NSLog(@"setCategoryError");
     }

     // activate audio session
     success = [[AVAudioSession sharedInstance] setActive:YES error: nil];
     if (!success) {
         NSLog(@"activationError");
     }

return success;

}

У каждого приложения есть одноэлементный аудио-сеанс, который вы можете настроить. Категория и режим сеансов (в этом примере я не установил режим, чтобы он возвращался к режиму по умолчанию) объявлял ваши приложения о том, как вы хотите обработать аудиомаршрутизацию. Это следует за важным правилом last in wins. Это означает, что если пользователь подключается к гарнитуре или в этом случае устройство Bluetooth, которое является периферийным устройством (HFP), система автоматически направит звук на гарнитуру или устройство Bluetooth. Физические действия пользователей используются для определения маршрутизации звука. Однако, если вы хотите предоставить пользователю список доступных маршрутов, Apple рекомендует использовать класс MPVolumeView.

Пример добавления MPVolumeView может быть помещен в метод UIViewController viewDidLoad подкласса.

#import <MediaPlayer/MediaPlayer.h> // place in .h
// prefered way using MPVolumeView for user selecting audio routes
self.view.backgroundColor = [UIColor clearColor];
CGRect frameForMPVV = CGRectMake(50.0, 50.0, 100.0, 100.0);
MPVolumeView *routeView = [[MPVolumeView alloc] initWithFrame:frameForMPVV];
[routeView setShowsVolumeSlider:NO];
[routeView setShowsRouteButton:YES];
[self.view addSubview: routeView];

Как и в iOS 7, вы можете получить все входы вроде этого

// portDesc.portType could be for example - BluetoothHFP, MicrophoneBuiltIn, MicrophoneWired
NSArray *availInputs = [[AVAudioSession sharedInstance] availableInputs];
int count = [availInputs count];
for (int k = 0; k < count; k++) {
    AVAudioSessionPortDescription *portDesc = [availInputs objectAtIndex:k];
    NSLog(@"input%i port type %@", k+1, portDesc.portType);
    NSLog(@"input%i port name %@", k+1, portDesc.portName);
}

ПортType, который вас интересует, это "BluetoothHFP". Свойством portName обычно является производитель/модель, которую вы показываете пользователю. (Я проверил это с динозавром Motorola, отличным от LE, и он работает)

Из-за правила last in wins вам понадобятся следующие два уведомления (включая iOS 7). Один для обработки прерываний (таких как телефонные звонки или будильник), а второй для уведомления об изменениях маршрута. Уведомления об изменении маршрута относятся к этому вопросу.

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(myInterruptionSelector:)
                                             name:AVAudioSessionInterruptionNotification
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(myRouteChangeSelector:)
                                             name:AVAudioSessionRouteChangeNotification
                                           object:nil];

Для iOS 6.x вы можете прочитать свойство currentRoute для AVAudioSession внутри селектора myRouteChange: для получения нового маршрута, так как это будет вызвано при подключении гарнитуры или Bluetooth-устройства.

- (void)myRouteChangeSelector:(NSNotification*)notification {
 AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
      NSArray *inputsForRoute = currentRoute.inputs;
      NSArray *outputsForRoute = currentRoute.outputs;
      AVAudioSessionPortDescription *outPortDesc = [outputsForRoute objectAtIndex:0];
      NSLog(@"current outport type %@", outPortDesc.portType);
      AVAudioSessionPortDescription *inPortDesc = [inputsForRoute objectAtIndex:0];
      NSLog(@"current inPort type %@", inPortDesc.portType);
}

Любая версия iOS < 6.0 вам понадобится класс "теперь устаревший" AudioSessionServices. Этот класс является C api, который вместо уведомлений позволяет добавлять слушателей свойств.

Я закончу эту заметку - ВЫ НЕ ДОЛЖНЫ ВСЕГДА ПОЛУЧИТЬ ЧТО ВЫ ХОТИТЕ ИЗ СИСТЕМЫ. Уведомления об обработке прерываний необходимо соблюдать, и требуется большая проверка ошибок. Я думаю, что это действительно хороший вопрос, и я надеюсь, что это проливает свет на то, что вы пытаетесь достичь.

Ответ 2

Swift версия

do {
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .allowBluetooth)
        try AVAudioSession.sharedInstance().setActive(true)
    } catch {}
let availableInputs = AVAudioSession.sharedInstance().availableInputs