以前書いた2つの記事でわかったことは、BLEは少量データ/低頻度での用途に特化することで省電力を実現した規格なので、音声データをやり取りするような用途にはそもそも向かない、ということでした。
ただ、MFiなし、WiFiもなしでデバイスとiOSアプリを無線通信させたい場合、iOSの現状の公開APIで可能な範囲ではやはりCoreBluetooth/BLE一択になってくるので、どうにかならないものかと。
そんなわけで、いろいろと調べたり聞いてみたり試行錯誤してみたことを書いておきます。
Bluetooth4.0イヤホンはどうやって音声データを流してるのか
疑問だったのは、自分がいま使ってるイヤホンは、Bluetooth4.0であることでした。
売り上げランキング: 3,022
自分は Bluetooth4.0 = BLE だと思っていたので、何でBLEで音楽聴けてるんだろう?と。ビットレートをそこまで落としてる感じもしないし。
というわけで、FacebookのWF-BTLEグループで、次のような質問をしました。
BLEのプロファイルについてよくわからない点があるので、質問させてください。
BLEは『少量のデータを低頻度でやりとりする用途に、ハードウェアの低コスト化と、コイン型電池で1〜2年間の連続動作ができる超低消費電力に特化』(こちらの記事より引用)した規格であると認識しています。
なので、BLEのGATTベースのプロファイルは、Heart Rateや、Battery Serviceなど、少量データ・低頻度で済む用途のものが定義されているし、GATTの規格自体も大きいデータを流しっぱなしにするようにはなっていない(アトリビュートのvalueは512バイトまで)と。
ただよくわからないのが、世の中にはBluetooth4.0用のヘッドセットなども出ている点です。
上記ヘッドセットのページにはプロファイルにHFPを使用しているようなことが書いてあります。私の認識ではBLE機器ではGATTベースのプロファイルしか使えず、HFPなどは3.0以前の従来規格でしか使用できない、というものなのですが、そんなことないのでしょうか?もしくは、フレームレートを抑える等の工夫をしてGATTの規格内でHFP相当の通信ができるようにしている、ということなのでしょうか?
見当違いのことを言ってるかもしれませんが、識者のみなさま、ご教示いただけると幸いです。
ちなみにこの質問をする意図としては、iOSのCoreBluetoothで、デバイスと音声データのやり取りをしたい(音質は5kbpsぐらいまで落とせる)、というものになります。BLEはそもそもそういう用途の規格ではないとは思いますが、MFiなし、WiFiもなしでデバイスとiOSアプリを無線通信させたい場合、やはりCoreBluetooth/BLE一択になってくるので。。
そして、回答をいくつかいただきました。(掲載許可は得ていないので)原文を載せるのは避け、要約すると、
- Bluetooth4.0は、3.0までの技術に、Low Energyの技術を統合したもの。なので、3までの技術も使える
- 両方に対応したものをデュアルモードと呼ぶ
- ただ、Core Bluetoothでは、Low Energyの部分しか制御できない
- BLEを音声通信的なものに利用するのは、あり。CoreAudioでBluetothのヘッドセットへルート切り替えが、最近はできるようになったはず。
- 参考リンク
という明快な回答をいただきました。
なるほど、音楽を聴けるヘッドセットやイヤホンは、3.0までの技術を使っていて、それはMFiなしで使えるCoreBluetoothでは制御できないと。
「Bluetoothを扱いたければCoreBluetoothだ!」 *1 とばかり思っていたので、オーディオのルート切り替えは盲点でした。
そういえば、ルート切り替えの定義でBluetoothなんちゃらっての見たことあるな、というのはなんとなく記憶にあったので、改めて見てみると、
kAudioSessionInputRoute_BluetoothHFP kAudioSessionOutputRoute_BluetoothHFP kAudioSessionOutputRoute_BluetoothA2DP
というのがありました。
これができれば、HFPプロファイルに対応したデバイスから音声データを受け取ってそのままAudio Unitとかで波形処理できるので、なかなか良さそうです。iOS7からdeprecatedになってましたが。。
Core Bluetoothでどうにかしてみる
上述したようにオーディオセッションでルーティングを制御することで音声データのやりとりができそう、とわかったものの、デバイス側がいまのところGATTベースのプロファイルにしか対応してないので、結局Core Bluetoothでどうにかすることにしました。
※ペリフェラル側でS/N比などをみて音声データを切って送信する前提です。
[ペリフェラル側]
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { // 音声バッファの続きを渡す request.value = [self soundDataFromCurrentIndex]; [peripheral respondToRequest:request withResult:CBATTErrorSuccess]; }
上記コード内で呼んでいる soundDataFromCurrentIndex というメソッドでは128バイトずつ *2 音声バッファのデータを渡すようにしています。
[セントラル側]
- (void) peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { NSData *data = characteristic.value; NSUInteger bytes = data.length; [self.receivedData appendData:data]; // 次のデータを読み込む if (bytes >= kMaxBytes) { [cbPeripheral readValueForCharacteristic:self.characteristic]; } // 受信完了 else { // receivedDataに対して処理を行う } }
全部読み終わるまでreadし続ける、という(とくにTips要素もない)普通の処理です。*3
試してみた結果
上記方法で試しに 15kB の wavデータ(16bit, 16kHz)を送ってみると、20秒ちょいかかりました。先日計測した速度とだいたい同じぐらいなので妥当そうですが、送ったのは再生時間が1秒にも満たないデータなので、リアルタイムにはほど遠いレベルです。
5kbpsぐらいの通信速度である程度リアルタイムに送るには、1秒当たり600バイトぐらいに収める必要があるので、ビットレートを落とすだけじゃなく、いったんaacとかの圧縮フォーマットにエンコードして送る必要がありそうです。
関連記事
Bluetooth Low Energy (BLE) のサービス/キャラクタリクスの構成例一覧 - Over&Out その後
ios - Reading long characteristic values using CoreBluetooth - Stack Overflow
iOS のオーディオ/サウンド処理について学べる書籍10冊+α - Over&Out その後