ESP32 是一款微控制器,提供蓝牙 A2DP API,可用于接收声音数据(例如来自手机的声音数据),并通过回调方法使其可用。输出是从 SBC 格式解码的 PCM 数据流。该文档可以在这里找到。
I2S 是一种电气串行总线接口标准,用于将数字音频设备连接在一起。它用于在电子设备中的集成电路之间传送 PCM 音频数据。
因此,我们可以将蓝牙的输入提供给 I2S 输出:可以在 Github 上找到 Espressif 的示例。
不幸的是,这个例子并没有让我高兴,所以我决定将其转换为一个简单的Arduino 库,该库非常容易从 Arduino 软件 IDE 中使用。
顾名思义,它支持 A2DP 蓝牙协议,仅提供音频流!
它还支持音频/视频远程控制配置文件 (AVRCP) 以及 A2DP。
不支持免提配置文件 (HFP)、耳机配置文件 (HSP) 和不带 A2DP 的独立 AVRCP!
Espressif 即将淘汰旧版 I2S API:因此,对于 Arduino v3.0.0 (IDF v5),我旧的 I2S 集成将不再可用。只要您不升级,旧语法仍然有效。
为了支持与版本无关的独特输出 API,建议安装和使用 AudioTools 库。因此,文档和所有示例都已更新以使用这种新方法。
但是,您也可以输出到继承自 Arduino Print 的任何其他类:例如 Arduino ESP32 I2SClass,或者您可以使用下面描述的数据回调。
例如,这可以用来构建您自己的蓝牙扬声器。
这是最简单的示例,仅使用正确的默认设置:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
这将创建一个名为“MyMusic”的新蓝牙设备,输出将发送到以下需要连接到外部 DAC 的默认 I2S 引脚:
请注意,与旧版 API 相比,这些默认引脚已发生变化!
您可以在start
之前轻松定义自己的引脚。
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
auto cfg = i2s. defaultConfig ();
cfg. pin_bck = 14 ;
cfg. pin_ws = 15 ;
cfg. pin_data = 22 ;
i2s. begin (cfg);
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
您还可以使用 Arduino ESP32 I2S API:您不需要为此安装任何额外的库。
# include " ESP_I2S.h "
# include " BluetoothA2DPSink.h "
const uint8_t I2S_SCK = 5 ; /* Audio data bit clock */
const uint8_t I2S_WS = 25 ; /* Audio data left and right clock */
const uint8_t I2S_SDOUT = 26 ; /* ESP32 audio data output (to speakers) */
I2SClass i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
i2s. setPins (I2S_SCK, I2S_WS, I2S_SDOUT);
if (!i2s. begin (I2S_MODE_STD, 44100 , I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
Serial. println ( " Failed to initialize I2S! " );
while ( 1 ); // do nothing
}
a2dp_sink. start ( " MyMusic " );
}
void loop () {}
请注意,此 API 还取决于安装的版本:上面的示例适用于 ESP32 >= 3.0.0!
您还可以使用 AudioTools 中的 AnalogAudioStream 将输出直接发送到 ESP32 的内部 DAC:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
输出现在进入 DAC 引脚 GPIO25(通道 1)和 GPIO26(通道 2)。
收到数据包时您会收到通知。 API 使用通常格式为 44.1kHz 采样率、两通道 16 位采样数据的 PCM 数据。
// In the setup function:
a2dp_sink.set_on_data_received(data_received_callback);
// Then somewhere in your sketch:
void data_received_callback () {
Serial. println ( " Data packet received " );
}
或者您可以访问数据包:
// In the setup function:
a2dp_sink.set_stream_reader(read_data_stream);
// Then somewhere in your sketch:
void read_data_stream ( const uint8_t *data, uint32_t length)
{
int16_t *samples = ( int16_t *) data;
uint32_t sample_count = length/ 2 ;
// Do something with the data packet
}
在a2dp_sink.set_stream_reader()
方法中,您可以提供一个可选参数,该参数定义您希望 I2S 的输出处于活动状态还是非活动状态 - 因此您可以使用此方法,例如通过调用a2dp_sink.set_stream_reader(read_data_stream, false)
您可以注册一个方法,当系统收到任何 AVRC 元数据 ( esp_avrc_md_attr_mask_t
) 时将调用该方法。这是一个例子
void avrc_metadata_callback ( uint8_t data1, const uint8_t *data2) {
Serial. printf ( " AVRC metadata rsp: attribute id 0x%x, %s n " , data1, data2);
}
a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
a2dp_sink.start( " BT " );
默认情况下,您应该获得最重要的信息,但是您可以通过调用set_avrc_metadata_attribute_mask
方法来调整此信息,例如,如果您只需要标题和播放时间,您可以调用:
set_avrc_metadata_attribute_mask (ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
在启动 A2DP 接收器之前。请注意,data2 实际上是一个 char* 字符串,因此即使ESP_AVRC_MD_ATTR_PLAYING_TIME
被记录为媒体持续时间的毫秒数,您也需要在对其进行数学运算之前对其进行解析。有关更多信息,请参阅元数据示例。
与avrc_metadata_callback
类似,ESP IDF v4+ 支持选定的esp_avrc_rn_param_t
回调,如set_avrc_rn_playstatus_callback
、 set_avrc_rn_play_pos_callback
和set_avrc_rn_track_change_callback
可用于获取esp_avrc_playback_stat_t playback
状态、 uint32_t play_pos
分别为播放位置和uint8_t elm_id
曲目更改标志。有关更多详细信息,请参阅playing_status_callbacks 示例。
我添加了以下 AVRC 命令,您可以使用它们来“控制”您的 A2DP 源:
这可用于向蓝牙扬声器等提供音频数据。
我们还可以生成声音并将其发送到蓝牙扬声器等。
ESP32 A2DP 支持的音频编解码器是 SBC:该 API 使用通常格式为 44.1kHz 采样率、两通道 16 位采样数据的 PCM 数据。
当您启动BluetoothA2DPSource时,您需要传递要连接的蓝牙名称和生成声音数据的“回调函数”:
# include " BluetoothA2DPSource.h "
BluetoothA2DPSource a2dp_source;
// callback
int32_t get_sound_data (Frame *data, int32_t frameCount) {
// generate your sound data
// return the effective length (in frames) of the generated sound (which usually is identical with the requested len)
// 1 frame is 2 channels * 2 bytes = 4 bytes
return frameCount;
}
void setup () {
a2dp_source. start ( " MyMusic " , get_sound_data);
}
void loop () {
}
在示例中,您可以找到在 sin() 函数的帮助下生成声音的实现。您还可以指定多个备用蓝牙名称。系统仅连接到第一个可用的系统:
void setup () {
static std::vector< char *> bt_names = { " MyMusic " , " RadioPlayer " , " MusicPlayer " };
a2dp_source. start (bt_names, get_sound_data);
}
您还可以直接以 uint8_t 的简单数组形式提供数据:
# include " BluetoothA2DPSource.h "
extern const uint8_t StarWars10_raw[];
extern const unsigned int StarWars10_raw_len;
BluetoothA2DPSource a2dp_source;
SoundData *music = new OneChannelSoundData(( int16_t *)StarWars30_raw, StarWars30_raw_len/ 2 );
void setup () {
a2dp_source. start ( " RadioPlayer " );
a2dp_source. write_data (music);
}
void loop () {
}
例如可以通过以下方式制备阵列:
您可能想使用分区方案进行编译:巨大的应用程序!
在上面的示例中,我们通过一个通道提供数据。这样做的优点是它使用的空间比 2 通道录音少得多,您可以通过以下方式使用它:
SoundData *data = new TwoChannelSoundData((Frame*)StarWars10_raw,StarWars10_raw_len/4);
在构造函数中,您可以传递附加参数:
TwoChannelSoundData (Frame *data, int32_t frameCount, bool loop= false );
OneChannelSoundData ( int16_t *data, int32_t frameCount, bool loop= false , ChannelInfo channelInfo=Both);
OneChannel8BitSoundData ( int8_t *data, int32_t frameCount, bool loop= false , ChannelInfo channelInfo=Both);
该库使用 ESP32 记录器,您可以在 Arduino 的“工具”-“核心调试日志”中激活该记录器。
当前代码完全依赖于 ESP-IDF(也由 Arduino ESP32 内核提供)。没有其他依赖项,其中包括 Arduino API!
因此我们支持:
然而,该限制限制了所提供的示例。
在克隆项目之前,请阅读可在 Wiki 中找到的以下信息。
您可以单独使用这个库,但它是我的音频工具项目的一部分。因此,您可以轻松地通过声音效果、使用滤波器、使用替代音频接收器或音频源、执行 FFT 等来增强此功能。这是一个如何使用 FFT 分析音频数据的简单示例。
我花了很多时间来提供全面且完整的文档。因此,在 Github 上发布任何新问题之前,请先阅读文档并检查问题和讨论。
仅针对错误开放问题,如果不是错误,请使用讨论:提供有关以下方面的足够信息
使其他人能够理解并重现您的问题。
最后,最重要的是不要向我发送任何电子邮件或在我的个人网站上发布问题!
从使用此库的项目中获取一些灵感,并与社区分享您的项目。
对于 Arduino,您可以将库下载为 zip 并调用 include Library -> zip library。或者你可以 git 克隆这个项目到 Arduino 库文件夹中,例如
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
对于提供的示例,您还需要安装音频工具库。
其他框架请参见 Wiki
变更历史可以在 Wiki 中找到
这个软件是完全免费的,但是你可以通过奖励我一些款待来让我开心