さて、私はiOSアプリのBluetoothオーディオサポートを有効にする必要があるというハードルがある楽しいプロジェクトに取り組んでいます。
私が直面しているハードルは、接続されているBluetoothオーディオデバイスのリストを取得することすらできないということです。私のiPhone5Sは私のイヤピース(正確には〜3-4歳 LG HBM-2 )を認識し、電話のためにそれを通してオーディオを再生しますが[〜#〜]両方[〜#〜]外部アクセサリとCoreBluetoothは、両方をクエリしても何の役にも立ちません。
CoreBluetooth フレームワークと 外部アクセサリ フレームワークの両方で見つけた質問と回答に基づいて独自のコードを作成しています。
私のコードがanyBluetoothデバイスに対して単に「scanForPeripheralsWithServices:nil
」を試みた場合、[設定]-> [Bluetooth]が表示され、接続されていると表示されますが、以下のコードは単にコンソールの「CBCentralManagerStatePoweredOn
」メッセージを超えたシングルヒット。
そして、私のコードのこの行(有効なEAAccessoryManagerインスタンスを使用)
NSArray * connectedDevices = [self.eAAccessoryManager connectedAccessories];
また、nil配列で戻ってきます。
何が間違っているのでしょうか?
B.T.W。、 このコードを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:
{
messtoshow=@"State unknown, update imminent.";
break;
}
case CBCentralManagerStateResetting:
{
messtoshow=@"The connection with the system service was momentarily lost, update imminent.";
break;
}
case CBCentralManagerStateUnsupported:
{
messtoshow=@"The platform doesn't support Bluetooth Low Energy";
break;
}
case CBCentralManagerStateUnauthorized:
{
messtoshow=@"The app is not authorized to use Bluetooth Low Energy";
break;
}
case CBCentralManagerStatePoweredOff:
{
messtoshow=@"Bluetooth is currently powered off.";
break;
}
case CBCentralManagerStatePoweredOn:
{
messtoshow=@"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
まず、オーディオをサポートするBluetooth接続を許可するようにアプリケーションのオーディオセッションを構成する必要があります。これは、たとえば、アプリケーションデリゲートBOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsメソッドで実行できます。 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;
}
すべてのアプリケーションには、構成可能なオーディオセッションシングルトンがあります。セッションのカテゴリとモード(この例では、モードを設定しなかったため、デフォルトモードに戻ります)は、オーディオルーティングをどのように処理するかについてアプリケーションの意図を宣言します。 ---(最後の勝利という重要なルールに従います。これは、ユーザーがヘッドセット、またはこの場合はハンズフリー周辺機器(HFP)であるBluetoothデバイスを接続すると、システムがオーディオをヘッドセットまたはBluetoothデバイスに自動的にルーティングすることを意味します。ユーザーの物理的なアクションは、オーディオルーティングを決定するために使用されます。ただし、ユーザーに使用可能なルートのリストを提供する場合は、Apple MPVolumeViewクラスの使用をお勧めします。
MPVolumeViewを追加する例は、IViewControllerサブクラスの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);
}
興味のあるportTypeは「BluetoothHFP」です。 portNameプロパティは通常、ユーザーに表示するメーカー/モデルです。 (LE以外のBluetooth Motorola恐竜でこれを確認しましたが、機能します)
最後の勝利ルールのため、これら2つの通知を監視する必要があります(iOS 7を含む)。 1つは中断(電話やアラームなど)を処理し、もう1つはルート変更の通知を受け取ります。ルート変更通知は、この質問に関連するものです。
[[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の場合、myRouteChange:セレクター内のAVAudioSessionのcurrentRouteプロパティを読み取って、新しいルートを取得できます。これは、ヘッドセットまたは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);
}
6.0未満のiOSバージョンでは、「現在非推奨」AudioSessionServicesクラスが必要です。このクラスは、通知の代わりにプロパティリスナーを追加できるCAPIです。
私はこのメモで終わります-あなたは常にあなたがシステムから望むものを手に入れません。監視する中断処理通知があり、多くのエラーチェックが必要です。これは本当に良い質問だと思います。これがあなたが達成しようとしていることを明らかにすることを願っています。
Swiftバージョン
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, options: .allowBluetooth)
try AVAudioSession.sharedInstance().setActive(true)
} catch {}
let availableInputs = AVAudioSession.sharedInstance().availableInputs