第1章 RTL8723BU蓝牙模块驱动移植

1.1. 硬件方案

1.2. 蓝牙驱动移植

1.3. 蓝牙耳机规格要求

第2章 Linux音频框架

2.1. ALSA

2.2. Pulseaudio

2.3. GStreamer

2.4. Jack

2.5. FFADO

2.6. Xine

2.7. Phonon

2.8. 其他分支

第3章 蓝牙协议栈Bluez

3.1. Bluez5版本介绍

3.2. Bluez与Linux内核版本

3.3. Bluez蓝牙音频

第4章 蓝牙音频协议

4.1. A2DP协议

4.2. HSP/HFP协议

4.3. HSP和HFP的区别

第5章 蓝牙音频框架移植

5.1. 编译的平台

5.2. 安装交叉编译链

5.3. 交叉编译-bluez-5.50

5.3.1. 相关配置

5.3.2. 编译zlib-1.2.11

5.3.3. 编译libffi-3.2.1

5.3.4. 编译glib-2.40.0

5.3.5. 编译expat-2.0.0

5.3.6. 编译dbus-1.9.2

5.3.7. 编译redline-6.3

5.3.8. 编译gettext-0.19.8.1

5.3.9. 编译ncurses-5.9

5.3.10. 编译libical-1.0.1

5.3.11. 编译eudev-3.2.7

5.3.12. 编译alsa-lib-1.1.8

5.3.13. 编译alsa-utils-1.1.8

5.3.14. 编译bluez-5.50

5.4. 交叉编译-pulseaudio-11.1

5.4.1. 编译m4-1.4.18

5.4.2. 编译perl-5.28.1 不需要交叉编译

5.4.3. 编译XML::Parser-2.34 不需要交叉编译

5.4.4. 编译intltool-0.40.6

5.4.5. 编译libtool-2.4.2

5.4.6. 编译libsndfile-1.0.28

5.4.7. 编译SBC-1.3

5.4.8. 编译speex-1.2.0

5.4.9. 编译speexdsp-1.2rc3

5.4.10. 编译json-c-0.12.1-nodec

5.4.11. 编译ell-0.17

5.4.12. 编译ofono-1.28

5.4.13. 编译pulseaudio

5.5. 启动蓝牙音频服务

5.5.1. 启动D-Bus服务

5.5.2. 启动Bluez服务

5.5.3. 启动Pulseaudio服务

5.5.4. 启用udev支持

5.5.5. 启用ofono支持

5.6. 蓝牙耳机的配对、连接

5.6.1. 蓝牙设备扫描

5.6.2. 蓝牙耳机配对

5.6.3. 蓝牙耳机连接

第6章 蓝牙耳机播放音频文件

6.1. 支持的音频格式

6.2. 播放音频

6.3. 录音

一,RTL8723BU蓝牙模块驱动移植

1,硬件方案

本项目中的RTL8723BU模块包含了WIFI和蓝牙,为2合1的硬件方案,由于WIFI和蓝牙都包含2.4G频段,所以容易互相干扰,不过RTL8723BU通过共存机制解决了这个问题,但并不代表WIFI和蓝牙就互不影响,在调试的过程中发现蓝牙的射频功耗是在WIFI驱动中设定的,也就是说,想要蓝牙满功率运行,需要先加载WIFI驱动,并且执行ifconfig wlan0 up指令将射频功耗调到正常范围,否则蓝牙的信号会很弱。

2,蓝牙驱动移植

首先需要配置内核支持蓝牙子系统,RTL8723BU的蓝牙部分与RTL8723AU几乎一致,因此他们的蓝牙驱动是共用的,从官方下载rtl8723au_bt-kernel.zip驱动包,解压后得到以下文件:

“1”部分为蓝牙驱动源码。“2”为蓝牙模块的固件。

需要将rtl8723b_config.bin文件复制到板子的文件系统路径:/lib/firmware/rtl_bt/ 中。

将“1”的源文件文件替换掉内核源码中的drivers\bluetooth中的源文件,然后执行make ARCH=arm menuconfig命令配置内核:

将蓝牙驱动编译成模块,最后会得到以下文件:

按依赖顺序加载以上驱动

就可以识别到蓝牙模块,执行hciconfig命令可得到hci0的蓝牙设备:

执行指令hciconfig hci0 up可激活蓝牙设备。

3,蓝牙耳机规格要求

要实现蓝牙耳机的录音/播放,由于Linux蓝牙音频框架和蓝牙音频协议的限制,必须支持HSP协议

二,Linux音频框架

Linux音频系统存在一个问题,这不仅仅是偶尔无法工作而已。真正的问题在于它过于繁杂了。从你的扬声器中播放出来这一过程中所用到的技术之间的关系的话,你就会很清楚地看到它就像打结的意大利面一样乱了。这个问题的原因在于音频处理从本质上就要比其他的技术更复杂。声音从某个地方输入你的Linux电脑,又从另一个地方输出。如果我们画出一个用来描述将你的电脑和网络上其他的电脑连接起来的网络架构的OSI模型,我们看到的是分明的层次,每一层都有它自己的进程作用域和功能。层与层之间很少有重合,因此你绝对不会碰到在第7层的最终用户进程和在第1层的原始比特流的电子脉冲相混淆的情况。然而这种情况恰恰在Linux音频架构中存在。这个架构中甚至没有一个明确定义的最底层,各种音频技术在内核和你的硬件里各自为政。和网络模型相比,Linux的音频结构更像是地壳,较低的层偶尔会爆发到表面,引发混乱痛苦,而较高的表面发生移动,取代原本是隐藏的根本技术。比如Open Sound协议原本应该在内核层直接和你的硬件对话,但现在成了一个建立在ALSA上的复杂层。ALSA本身有一个内核层的堆栈和一个给程序员使用的更高级的API,用以将驱动程序和硬件属性配合起来播放环绕立体声或者处理MP3编码。当大部分发行版把PulseAudio和GStreamer置于顶层时,你最终就得到了一个会发生像San Andreas错误这样的潜在破坏的不稳定的混乱系统。

1,ALSA

输入: PulseAudio, Jack, GStreamer, Xine, SDL, ESD

输出: Hardware, OSS

要说起现代Linux音频,最原始的就是高级Linux声音结构,简称ALSA。

它和Linux内核挂接,为系统的其他部分提供音频功能。但它远远要比普通的内核驱动程序更有野心:它可以混合、对其它层提供兼容性、为程序员提供API,并且如同Windows和OS X平台的ASIO和CoreAudio一样作为一个非常底层而稳定的后台程序来运行。

ALSA本来是被设计用于取代OSS的。然而,多亏了ALSA的一个设计用于运行较老的只支持OSS的应用程序的兼容层,OSS并没有灭绝。把ALSA看成一个Linux声音系统的一个设备驱动层是最简便的方法。

你的音频硬件需要一个以“snd_”为前缀的交互式内核模块,而且必需能在任何事情发生时被加载运行。这就是你需要ALSA内核驱动来播放你系统会发出的任何声音,以及在有人想到发明一个这样的驱动程序之前你的笔记本沉默了这么久的原因。

幸运的是,大部分发行版都能自动设置你的设备和模块。ALSA负责把你的音频硬件的功能翻译给软件API,你的系统的其他部分通过它来操作声音。它被设计用于处理OSS(和当时其他大部分的声音驱动)的许多缺陷,其中最值得一提的就是同一时刻只有一个应用程序可以访问硬件的问题。

这就是为什么ALSA中的一个软件组件需要管理音频请求并了解你的硬件能力。比如你想要在用Amarok听音乐的时候玩游戏,ALSA需要获取这两条音频流,在软件中将他们混合起来,或者用你的声卡上的硬件混音器达到同样的效果。

ALSA也可以同时管理8个音频设备,有时还可以处理硬件上的MIDI功能,虽然这取决于你的硬件的音频驱动的规格,而且这一点随着计算机能力的增强而不那么重要了。

ALSA不同于典型的模块/设备驱动程序的地方正是它部分可定制性的原因。这里也正是Linux音频系统开始变得复杂了的地方,因为你可以通过创建你自己的设置文件来对你的ALSA设置作几乎任何的修改——从音频流如何混合和输出到取样率、比特深度及实时效果。

ALSA相对的透明性、高效性和灵活性让他成为了Linux音频系统的标准,也成为了几乎其他所有的音频架构为了和音频硬件交互而必须通过的层。

2,Pulseaudio

输入: GStreamer, Xine, ALSA

输出: ALSA, Jack, ESD, OSS

如果你认为让ALSA在后台安稳地运行,事情就会变得简单,那你就大错特错了。ALSA虽然能处理你的电脑输入和输出音频的具体细节,但你必须操作另一个复杂的层。这就是PulseAudio——致力于连接硬件和软件功能、本地和远程计算机和音频流内容之间的隔阂。它像ALSA处理多个声卡一样处理网络上的音频,而且已经因为它的灵活性而成为了许多Linux发行版的一种标准了。就像ALSA一样,这种灵活性伴随着复杂程度,但PulseAudio的问题更严重,因为它的面向用户性更强。这表示普通用户更容易弄乱它的网络。大部分发行版对它的设置敬而远之:以最新的Ubuntu发行版为例,你甚至可能不会发现PulseAudio被安装了。如果你点击混音器小工具来调整你的声卡的音频等级,你会看到ALSA面板,但你真正看到的其实是一个虚拟设备——通过ALSA进入PulseAudio,再回到ALSA。PulseAudio似乎并没有给Linux音频系统增加任何新东西,因此它也受到了不少反对的呼声。虽然它并没有简化我们已有的东西,也没有让音效更强劲,但它确实增加了几个重要的特性。它也是一个Linux音频应用程序的全能层,不管它们各自的功能和你的硬件的规格如何。如果所有的应用程序都是用PulseAudio,事情就简单了。开发者不需要考虑其他复杂的系统,因为PulseAudio带来了跨平台的兼容性。不过这也是之所以有这么多其他的音频解决方案的主要原因之一。和ALSA不同,PulseAudio可以在多个操作系统中运行,包括其他的POSIX平台和微软的Windows。也就是说如果你建立一个是用PulseAudio的应用程序而非ALSA,把这个应用移植到另一个平台会很容易。但是ALSA和PulseAudio之间有着一个符号链接,这是因为在Linux系统下,后者依赖于前者。PulseAudio将自己作为一个连接到ALSA的虚拟设备进行配置,就像任何其他的硬件一样。这就让PulseAudio更类似于Jack,因为它处于ALSA和桌面之间,来回透明地输送数据。它也有它自己的术语。比如沉降(sink),指最后的目标。这些可以是网络中另一台计算机,也可以是你在虚拟ALSA中的声卡的音频输出。PulseAudio中完成这些沉降(sink)工作的部分被称为“源”(source)——例如你系统中的典型音频生成应用程序、你的声卡的音频输入,或者一段从另一台PulseAudio计算机中传来的网络音频流。和Jack不同,应用程序不直接添加和删除源(source),于是你能对每段流作更好的控制。比如,通过PulseAudio混音器你可以调节每一个通过PulseAudio的应用程序的相对音量,不管那个应用是否有它自己的音量调节器。这在屏蔽那些吵人的网站时很好用。

3,GStreamer

输入: Phonon

输出: ALSA, PulseAudio, Jack, ESD

有了GStreamer以后,Linux音频系统就开始变得越发混乱了。这是因为,和PulseAudio一样,GStreamer似乎没有给混音器增加什么新的东西。它是一个新的多媒体架构,并且在PulseAudio发布的数年前就有数量庞大的开发者,特别是在Gnome桌面下。它是在Linux桌面下安装使用合适的解码器的少数方案之一。他也是GTK开发者的音频架构的选择,你甚至可以找到一个版本专门为Palm Pre处理音频。GSteamer在音频层中插入在PulseAudio(它在大部分发行版都靠它实现音频输出)之上,应用程序之下。GStreamer很独特,因为它不仅是设计用于音频的——它通过插件可以支持多种流媒体格式,包括视频。例如MP3播放,通常是通过下载一个附属于GStreamer插件的额外编码器来安装在你的系统中的。在Linux下唯一官方授权的商业版的Fluendo MP3解码器,是一个GStreamer插件,而它其它适配的编码器,包括MPEG-2、H.264和MPEG也都是这样。

4,Jack

输入: PulseAudio, GStreamer, ALSA,

输出: OSS, FFADO, ALSA

PulseAudio有着开放设置的优点,它们在应用程序之间传递音频,最终还是会直接在输出端处理。Jack却是个中间层——音频和程序的远程进程信号等同,这使得音频应用程序得以通过各种组件建立。最好的例子就是虚拟录音室,这样一个应用程序要能够抓取音频数据,同时对音频作效果处理,最后把得到的音频流通过一个主处理器准备发布。一个真实的录音室可能需要一个电缆网路,有时成为jack,来建立这些连接。而Jack就是在软件中做这件事情的。Jack是“Jack音频连接工具”的简称。它的设计方向是低潜伏期的,这就是说音频上不会有过多的处理工作而拖慢它的进度。但是为了让Jack发挥全力,音频应用程序必须专门设计以使用Jack连接。因此,它不像ALSA和PulseAudio那样可以简单地替换,而且需要在另一个生成声音、提供物理输入的系统的顶端运行。在大部分兼容Jack的应用程序中,你可以随意自由更换音频和输入的方式。比如你可以从VLC的输出直接传送到Audacity中,在播放过程中将这段音频流录制下来。或者你可以通过JackRack这样一款让你能建立包括ping延迟、多道混响和语言编码等多种实时效果的应用程序来发送它。

5,FFADO

输入: Jack

输出: Audio hardware

在专业和半专业音频的世界里,许多音频接口通过一个FireWire接口连接到他们的主机上。

这一方案有多个优点。FireWire速度很快,而且设备可以通过总线供电。许多笔记本和桌面计算机都有未经修改的FireWire接口,而标准的接口具有稳定性,也最成熟。你也可以在路上通过笔记本使用FireWire设备实现远程录音,回到工作室以后再把它们导入你的桌面计算机中。但和USB有一个不需要额外驱动程序就可以处理音频的标准不同,FireWire音频接口需要它们自己的驱动。FireWire接口的这一复杂性使得建立一个ALSA接口不那么简单,所以它们需要它们自己的层。原本这项工作有一个叫FreeBOB的项目。这个项目利用了许多FireWire音频设备都是基于相同的硬件这一优势。FFADO是FreeBOB的继承者,它对多种其他类型的FireWire音频接口开放驱动平台。2009年末它发布了2代,包括了对许多类似Alesis、Apogee、ART、CME、Echo、Edirol、Focusrite、M-Audio、Mackie、Phonic和Terratec的单元的支持。因为设备能否工作实在是个未知数,所以你需要在购入之前好好检查一下,不过很多的厂商都通过向驱动程序开发者提供设备用以使用和测试来协助驱动程序的开发。FFADO另一个优秀的特性是一些硬件的DSP特性被移植到了驱动程序中,整合了一个图形化的混音器来控制不同输入输出的平衡。这和ALSA混音器不同,因为它让音频流能够在第0层被硬件控制。不同于其他的音频层,FFADO只会在Jack和你的音频硬件中切换。它没有给PulseAudio或者GStreamer提供后门,除非你用他们取代Jack。这使得你无法将FFADO作为一个播放音乐或者电影的通用音频层,要么你就要做好因为安装过程和Jack而搞得一团乱的准备。不过这也让驱动程序不会因为支持各种不同的接口而难以控制,特别是因为大部分专业音频应用程序都默认支持Jack。这使得它成为了工作室环境的一个最好的选择。

6,Xine

输入: Phonon

输出: PulseAudio, ALSA, ESD

下面我们要进入Linux音频系统的生态地质学。Xine有点像下面的白垩纪:它是许多其他的音频层被冲刷后遗留下来的成果。大多数用户会在非常强大的DVD影片和媒体播放器中发现这个名字,虽然它很老了,但大部分发行版依然捆绑着它,而这正是Xine长盛不衰的关键。

Xine被开发出来的时候,开发者将它分成两部分,后端库用于处理媒体,前端应用程序处理用户交互。库凭借它能播放数不胜数的封装类型,包括AVI、Matroska和Ogg以及它们包含的数十种格式,例如AAC、Flack、MP3、Vorbis和WMA。

它通过借用许多其他的库来实现这一能力。因此,Xine可以作为开发者的全能架构,不需要考虑专利解码器的版权就能提供最大程度的文件兼容性。

Xine可以再输出端和ALSA与PulseAudio交互,也有不少应用程序能直接和Xine交互。最主流的有Gxine前端和Totem,而且Xine也是KDE的Phonon的默认后端,所以你在从Amarok到Kaffeine的各种应用程序中发现它被捆绑在一起。

七,Phonon

输入: Qt and KDE applications

输出: GStreamer, Xine

Phonon的设计目的是通过移除一些系统中多余的繁杂性来简化开发者和用户的工作。它以KDE 4的应用程序的一个抽象的音频层开始起家,后来人们发现这个主意不错,于是Qt开发者将它直接插入KDE本身所基于的Qt架构中,制作了他们自己的Phonon。

它在跨平台应用程序的开发者中有很大的优势。它使得开发者可以用Qt在Linux上编写一个音乐播放器再简单地重编译给OS X和Windows使用,而不需要考虑音乐会被如何播放、使用的音频硬件的兼容性如何,或者最终操作系统会如何处理音频这些问题。例如把音频传送到OS X的CoreAudio的API中,或者Windows的DirectSound中,这些都会由Qt和Phonon自动完成。

在Linux平台(不同于早先版本KDE的Phonon),Qt的Phonon将大部分它所支持的透明的编码器的音频传送到GStreamer中。Phonon支持在不知不觉中从Qt架构中被分离。

这个系统有许多的批评,最主要的是它太过简单了,没有提供任何新的技术,不过看样子在KDE 4的周期里,KDE还是会将它保留在架构中。

三,蓝牙协议栈Bluez

Bluez是linux官方蓝牙协议栈,分为两个部分:内核代码和用户态程序及工具集。

1,Bluez5版本介绍

经过半年多的开发,BlueZ项目宣布推出BlueZ 5,其中包括许多新功能,API简化和其他改进。在此版本中,BlueZ仅支持Linux 3.4中引入的新蓝牙管理内核接口,因此基本上这是BlueZ 5的最低内核要求。对于低功耗的支持,至少需要linux 3.5版本。新的主线版本API不向后兼容BlueZ 4,这意味着任何应用程序,代理等都需要更新。BlueZ内部测试脚本和工具已经更新,以支持新的API。在此版本中,hcidump(bluez-hcidump.git)和obexd(obexd.git)项目已合并到主BlueZ项目(bluez.git)中,新版本将作为5.x BlueZ版本的一部分。新版本为开发人员提供了单独的BlueZ 5 API介绍和移植指南,但以下是BlueZ 4.x系列以来的一些更高级别的更改:

1、移至标准D-Bus ObjectManager和属性界面

2、删除管理器界面,因为相同的功能来自ObjectManager

3、删除对音频UNIX套接字接口的支持(Media D-Bus接口替换它)SBC库移入了项目

4、将GStreamer元素提交到上游GStreamer项目后,将其删除

5、删除Service接口并引入新的(libbluetooth独立的)Profile接口

6、新的bluetoothctl命令行也可以与BlueZ进行交互

7、新的btmon监控工具

8、删除电话(HFP和HSP)配置文件的内部支持。它们应该使用新的Profile接口实现,最好是由选择的电话子系统实现(例如已经支持此功能的oFono)

9、一般连接建立程序和大大简化的D-Bus API

10、适配器电源状态不会持久存储,并通过蓝牙重启记住。期望外部实体(如ConnMan)处理此问题。

11、libbluetooth默认没有安装,因为它不再有用或推荐了。新的Profile接口进一步降低了它的实用性。

12、所有存储文件的INI样式格式。 BlueZ 4中的旧文件是自动转换的。

13、将obex-client合并到主obexd守护程序中

14、D-Bus接口版本,旨在始终保持至少两个最新版本的支持。

15、新低能耗概况:

  • Cycling Speed
  • Scan Parameters
  • Alert
  • Heartrate
  • HID over GATT (HoG)

2,Bluez与Linux内核版本

由于Bluez协议栈包含了内核态代码,这部分代码跟随内核版本一起升级,所以一般来说不需要自己维护,但是需要注意协议栈的用户态版本与linux内核版本是否匹配,根据Bluez官网的描述,Bluez5至少需要Linux3.4版本,对于低功耗,至少需要Linux3.5版本。

3,Bluez蓝牙音频

Bluez 5.0开始删除了Bluetooth-ALSA部分,成为了一个纯粹的蓝牙协议中间件,因此想要使用蓝牙音频必须借助第三方音频服务程序Pulseaudio。

四,蓝牙音频协议

1,A2DP协议

蓝牙立体声音频传输规范(Advance Audio Distribution Profile),规定了使用蓝牙异步传输信道方式,传输高质量音乐文件数据的协议堆栈软件和使用方法,基于该协议就能通过以蓝牙方式传输高质量的立体声音乐。分为1.1版和1.2版,只要连接双方支持A2DP协议都能以16 bits,44.1 kHz的质量传输声音信号。假如有一方没有支持A2DP的话,只能以8 bits,8 kHz的质量的免手持设备规范(Handsfree Profile)传输模式,声音质量会大打折扣。

2,HSP/HFP协议

HSP协议

HSP 描述了 Bluetooth 耳机如何与计算机或其它 Bluetooth 设备(如手机)通信。连接和配置好后,耳机可以作为远程设备的音频输入和输出接口。 这是最常用的配置,为当前流行支持蓝牙耳机与移动电话使用。 它依赖于在64千比特编码的音频/ s的CVSD的或PCM以及AT命令从GSM 07.07的一个子集,包括环的能力最小的控制,接听来电,挂断以及音量调整。 典型的使用情景是使用无线耳机与手机进行连接。 可能会使用HSP的若干设备类型:耳机、手机、PDA 、个人电脑、手提电脑。

HFP协议

HFP(Hands-free Profile),让蓝牙设备可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要视蓝牙耳机及电话是否支持。

目前HFP的使用场景有车载蓝牙,耳机和PDA,定义了AG和HFP两种角色。 AG(Audio Gate)音频网关—音频设备输入输出网关 HF(Hands Free)免提—该设备作为音频网关的远程音频输入/输出机制,并可提供若干遥控功能。在车载蓝牙中,手机侧是AG,车载蓝牙侧是HF,在android源代码中,将AG侧称为HFP/AG,将HF侧称为HFPClient/HF。

3,HSP和HFP的区别

HFP(Hands-free Profile)和HSP(Headset Profile)都是为了实现蓝牙通话而制定,所实现的功能都和蓝牙通话相关。基本所有的蓝牙耳机、车载蓝牙都会支持这两个协议。

HSP仅实现了最基本的通话操作:接听电话、挂断电话、调节音量、声音在手机/蓝牙耳机之间切换。HFP在功能上是对HSP的扩展,除了上述功能以外,还包括控制三方通话、来电拒接、耳机端来电显示等高级功能,不过实现的方式,如用于控制的AT CMD完全不一样。蓝牙耳机录音

五,蓝牙音频框架移植

由于BlueZ5之后的版本移除了Bluetooth-Alsa部分,成为了一个纯粹的蓝牙协议栈服务程序,即中间件,这对于第三方的音频服务程序来说会有更大的自由度,不再受BlueZ的音频接口束缚,但是也带来了一个问题,如果想要用蓝牙音频协议必须安装第三方的音频服务程序,如Pulseaudio,否则无法使用蓝牙的音频,本章将移植BlueZ5.50+Pulseaudio11.1方案,以实现对蓝牙音频的支持。

1,编译的平台

主机操作系统:Ubuntu 14.04.5 LTS 32bit

交叉编译链: arm-xilinx-linux-gnueabi-

2,安装交叉编译链

64bit操作系统装gcc需要安装32位兼容库lib32ncurses5

进入root模式,由于安装文件只能在bash下运行,所以要将dash改为bash,输入以下指令(选择No,切换到bash)dpkg-reconfigure -plow dash

完成后,路径切换到bin文件的文件夹开始安装(默认软件的安装路径)

./xilinx-2011.09-50-arm-xilinx-linux-gnueabi.bin

安装完成后将编译器的路径加入到环境变量中

export PATH=/root/CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_GNU_Linux/bin:$PATH

(即$PATH要包含arm-xilinx-linux-gnueabi-gcc文件的上级目录)至此交叉编译器安装成功,通过arm-xilinx-linux-gnueabi-gcc -v指令,如果在最后看到gcc version则成功。

3,交叉编译-bluez-5.50

3.1 相关配置

先创建一个目录用于存储编译后的文件

mkdir /opt/bluez

添加库搜索路径到环境

export PKG_CONFIG_PATH=/opt/bluez/lib/pkgconfig/

export PATH=$PATH:/opt/bluez/bin

source /etc/profile

切换到超级用户su

为了减少编译前配置,以下编译过程在同一个终端操作,编译的顺序严格按照以下执行,由于Pulseaudio和Bluez和所依赖的Dbus都需要配置文件,配置文件默认的存储路径为/etc、/usr等系统根目录,对于文件系统为Ramdisk的场景会导致重启后之前的配置丢失,例如Bluez的配对信息重启后丢失等问题,所以在编译时需要配置运行时路径(例如etc、var路径等),注意参数的填写,本章的配置都是独立于根目录,适用于Ramdisk的环境。

3.2编译zlib-1.2.11

解压文件并进入安装文件夹根目录

./configure --prefix=/opt/bluez/ (这个命令一定要先执行,再修改下面的Makefile文件)

zlib不支持host配置,要手动修改Makefile的编译链为arm-xilinx-linux-gnueabi-前缀

修改的文件为Makefile(如果遇到文件是read-only类型,就通过chmod 777 Makefile修改读写权限)

修改的内容CC=arm-xilinx-linux-gnueabi-gcc

……

LDSHARED=arm-xilinx-linux-gnueabi-gcc -shared -Wl,-soname,libz.so.1

……

CPP=arm-xilinx-linux-gnueabi-gcc -E

……

AR=arm-xilinx-linux-gnueabi-ar

……

RANLIB=arm-xilinx-linux-gnueabi-ranlib

然后编译、安装

make

make install

3.3编译libffi-3.2.1

解压文件并进入安装文件夹根目录

./configure --prefix=/opt/bluez/ --host=arm-linux CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/"

然后编译、安装

make

make install

3.4编译glib-2.40.0

解压文件并进入安装文件夹根目录

因为交叉编译后主机无法运行测试程序,所以例如堆栈方向等测试不能运行,手动将检查结果写入cache中

apt-get install libglib2.0-dev

echo ac_cv_type_long_long=yes>arm-linux.cache

echo glib_cv_stack_grows=no>>arm-linux.cache

echo glib_cv_uscore=no>>arm-linux.cache

echo ac_cv_func_posix_getpwuid_r=yes>>arm-linux.cache

echo ac_cv_func_posix_getgrgid_r=yes>>arm-linux.cache

./configure --prefix=/opt/bluez/ --host=arm-linux CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/" --cache-file=arm-linux.cache

然后编译、安装

make

make install

3.5 编译expat-2.0.0

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-linux --prefix=/opt/bluez/

然后编译、安装

make -j6

make install

3.6 编译dbus-1.9.2

解压文件并进入安装文件夹根目录

./configure --host=arm--linux CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/" --prefix=/opt/bluez/

然后编译、安装

make -j6

make install

3.7 编译redline-6.3

解压文件并进入安装文件夹根目录

./configure --prefix=/opt/bluez/ --host=arm-linux bash_cv_wcwidth_broken=yes CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/"

然后编译、安装

make

make install

3.8 编译gettext-0.19.8.1

解压文件并进入安装文件夹根目录

./configure --host=arm--linux CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/" --prefix=/opt/bluez/ CXX="arm-xilinx-linux-gnueabi-g++ -I/opt/bluez/include/ -L/opt/bluez/lib/"

然后编译、安装

make

make install

3.10 编译gettext-0.19.8.1

解压文件并进入安装文件夹根目录

./configure --prefix=/opt/bluez/ --host=arm-linux --with-shared CC="arm-xilinx-linux-gnueabi-gcc"  CFLAGS=-fPIC --without-cxx-binding

然后编译、安装

make

make install

3.11 编译libical-1.0.1

解压文件并进入安装文件夹根目录

apt-get install cmake

export CC=arm-xilinx-linux-gnueabi-gcc

export CXX=arm-xilinx-linux-gnueabi-g++

cmake -DCMAKE_INSTALL_PREFIX=/opt/bluez/

然后编译、安装

make

make install

3.12 编译eudev-3.2.7

解压文件并进入安装文件夹根目录

apt-get install xsltproc

apt-get install libtool

apt-get install autoconf

apt-get install gperf

export PATH=$PATH:/opt/bluez/bin

source /etc/profile

export PKG_CONFIG_PATH=/opt/bluez/lib/pkgconfig/

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared=yes --enable-static=yes

然后编译、安装

make

make install

3.13 编译alsa-lib-1.1.8

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ --enable-shared --with-pcm-plugins=all --with-configdir=/opt/bluez/usr/local/share

然后编译、安装

make

make install

3.14  编译alsa-utils-1.1.8

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ --enable-static --enable-shared CFLAGS="-I/opt/bluez/include/" LDFLAGS="-L/opt/bluez/lib/" --disable-alsamixer  --with-alsa-inc-prefix=/opt/bluez/include/

然后编译、安装

make

make install

3,15 编译bluez-5.50

解压文件并进入安装文件夹根目录

./configure --prefix=/opt/bluez/ --host=arm-linux CC="arm-xilinx-linux-gnueabi-gcc -I/opt/bluez/include/ -L/opt/bluez/lib/ -lncurses" --enable-a2dp --enable-udev --enable-obex --enable-client --enable-network --enable-health --enable-tools --enable-bccmd --enable-cups --enable-test --enable-library --enable-static=yes --enable-shared=yes --enable-threads --enable-pie --enable-audio --enable-deprecated --enable-sap --enable-avrcp --enable-midi --enable-testing --sysconfdir=/opt/bluez/etc --localstatedir=/opt/bluez/var --enable-deprecated --with-systemdsystemunitdir=/lib/systemd/system --with-systemduserunitdir=/usr/lib/system --with-dbusconfdir=/opt/bluez/etc/dbus-1/system.d

然后编译、安装

make

make install

3.16 交叉编译-pulseaudio-11.1

Pulseaudio和BlueZ有一些相同的依赖库,BlueZ编译过的这里就不再重新编译。

3.17  编译m4-1.4.18

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc CXX=arm-xilinx-linux-gnueabi-g++ --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ CPPFLAGS=-I/opt/bluez/include/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/

make

make install

3.18 编译perl-5.28.1 不需要交叉编译

解压文件并进入安装文件夹根目录

./C:onfigure -des -Dprefix=$HOME/localperl

然后编译

make

运行测试程序

make test

安装

make install

3.19 编译XML::Parser-2.34 不需要交叉编译

解压文件并进入安装文件夹根目录

perl Makefile.PL

然后编译、安装

make

(make的时候如果出错,就运行apt-get install libexpat1-dev再次编译)

make install

3.20 编译intltool-0.40.6

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc CXX=arm-xilinx-linux-gnueabi-g++ --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ CPPFLAGS=-I/opt/bluez/include/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/

然后编译、安装

make

make install

3.21 编译libtool-2.4.2

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ CPPFLAGS=-I/opt/bluez/include/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/

然后编译、安装

make

make install

3.22 编译libsndfile-1.0.28

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ CPPFLAGS=-I/opt/bluez/include/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/

然后编译、安装

make

make install

3.23 编译SBC-1.3

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/

然后编译、安装

make

make install

3.24 编译speex-1.2.0

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared  --enable-static

然后编译、安装

make

make install

3.25 编译speexdsp-1.2rc3

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared  --enable-static

然后编译、安装

make

make install

3.26 编译json-c-0.12.1-nodec

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared  --enable-static

然后编译、安装

make

make install

3.27 编译ell-0.17

解压文件并进入安装文件夹根目录

这条命令执行过程会有错误,不必理会,生成configure即可

./bootstrap-configure

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared=yes --enable-static=yes

然后编译、安装

make -j6

make install

3.28 编译ofono-1.28

解压文件并进入安装文件夹根目录

这条命令执行过程会有错误,不必理会,生成configure即可

./bootstrap-configure

./configure CC=arm-xilinx-linux-gnueabi-gcc --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --enable-shared=yes --enable-static=yes

然后编译、安装

make -j6

make install

3.29 编译pulseaudio

解压文件并进入安装文件夹根目录

./configure CC=arm-xilinx-linux-gnueabi-gcc LD=arm-xilinx-linux-gnueabi-ld CXX=arm-xilinx-linux-gnueabi-g++ --host=arm-xilinx-linux-gnueabi --prefix=/opt/bluez/ CPPFLAGS=-I/opt/bluez/include/ LDFLAGS=-L/opt/bluez/lib/ CFLAGS=-I/opt/bluez/include/ --without-caps --enable-dbus --enable-alsa --enable-bluez5 --enable-bluez5-ofono-headset --enable-bluez5-native-headset --disable-avahi --enable-udev --with-speex --enable-shared  --enable-static --disable-openssl

然后编译、安装

make -j6

make install

五,安装到嵌入式环境

上述步骤完成后,在编译环境的/opt目录下会有bluez目录,蓝牙音频框架所有的程序存放在里面,将这个目录压缩打包,拷贝到嵌入式板子,解压即可。对于Ramdisk环境,系统启动完成后创建一个软链接,将解压的路径链接到/opt目录中,例如,解压后bluez目录的路径为/mnt/user/bluez/, 则执行以下命令:ln -s /mnt/user/bluez/ /opt/  即可,这条命令每次启动都要执行。

1,启动蓝牙音频服务

想要使用蓝牙音频,需要Bluez和Pulseaudio的服务支持,所以每次开机都要启动这两者的守护进程,由于本章的编译的配置路径不是默认的,所以启动服务前还需要加入一些环境变量设置:

export LD_LIBRARY_PATH=/opt/bluez/lib:$LD_LIBRARY_PATH

1.1 启动D-Bus服务

执行以下指令:

(1)删除残余的dbus进程缓存

rm /opt/bluez/var/run/dbus/pid

(2)启动dbus守护进程

/opt/bluez/bin/dbus-daemon --system

1.2 启动Bluez服务

执行以下指令:

(1)将dbus的蓝牙配置文件复制到正确路径

cp /opt/bluez/etc/dbus-1/system.d/dbus-1/system.d/bluetooth.conf /opt/bluez/etc/dbus-1/system.d/

(2)启动蓝牙模块hci0

hciconfig hci0 up

(3)启动bluez的守护进程

/opt/bluez/libexec/bluetooth/bluetoothd &

1.3 启动Pulseaudio服务

从bluez5.0开始,蓝牙协议栈的音频支持已被移出,需要第三方音频服务程序才能使用蓝牙的音频功能,所以此服务程序也是本项目的关键,如果该服务没有启动,则会出现蓝牙耳机配对成功却无法连接的情况,第一次运行此服务之前需要修改配置文件,否则守护进程无法正常启动:

修改/opt/bluez/etc/pulse/default.pa文件,将:

.ifexists module-console-kit.so

load-module module-console-kit

.endif

修改为:

.ifexists module-console-kit.so

#load-module module-console-kit

.endif

保存。

执行以下指令启动Pulseaudio守护进程:

./pulseaudio -D --disallow-exit --exit-idle-time=-1

1.4 启用udev支持

该服务的作用不明,与内核的文件设备类有关系,执行以下指令启动其守护进程:

cd /opt/bluez/sbin

./udevd -d

1.5 启用ofono支持

该服务进程可选,在本项目中未使用,执行以下指令启动守护进程:

cd /opt/bluez/sbin

./ofonod

2,蓝牙耳机的配对、连接

从bluez5开始,蓝牙的配对及连接等操作都集成到bluetoothctl程序中,执行指令:

./bluetoothctl

进入操作界面,以下小节都是在此操作界面下进行,使用蓝牙音频之前需要完成对蓝牙耳机的配对、连接。

2.1 蓝牙设备扫描

首次配对前需要进行扫描,将耳机切换到配对模式后,在bluetoothctl中执行指令scan会搜到蓝牙耳机,并显示耳机的mac地址,例如:00:11:67:11:11:67,再执行info 00:11:67:11:11:67指令可得到耳机的设备信息:

[bluetooth]# info 00:11:67:11:11:67

Device 00:11:67:11:11:67 (public)

Name: M18

Alias: M18

Class: 0x00240400

Icon: audio-card

Paired: no

Trusted: no

Blocked: no

Connected: no

LegacyPairing: no

ManufacturerData Key: 0x5349

ManufacturerData Value:

53 43                                            SC

RSSI: -32

2.2 蓝牙耳机配对

蓝牙耳机连接之前需要先进行配对,配对之后会自动保存密钥信息,可以执行指令trust 00:11:67:11:11:67将该设备加入到信任列表中,下一次可直接连接,无需再次配对。执行pair 00:11:67:11:11:67指令进行配对。

2.3 蓝牙耳机连接

要连接蓝牙耳机需要确保pulseaudio服务已启动,否则连接成功后几秒会自动断开。执行指令connect 00:11:67:11:11:67连接蓝牙耳机。

连接之后,执行

info 00:11:67:11:11:67

Device 00:11:67:11:11:67 (public)

Name: M18

Alias: M18

Class: 0x00240400

Icon: audio-card

Paired: yes

Trusted: yes

Blocked: no

Connected: no

LegacyPairing: no

UUID: Headset                   (00001108-0000-1000-8000-00805f9b34fb)

UUID: Audio Sink                (0000110b-0000-1000-8000-00805f9b34fb)

UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)

UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)

UUID: Handsfree                 (0000111e-0000-1000-8000-00805f9b34fb)

UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)

Modalias: bluetooth:v0039p13A4d0104

ManufacturerData Key: 0x5349

ManufacturerData Value:

53 43                              SC

RSSI: -66

可得到该耳机的功能集合设备信息。

六,蓝牙耳机播放音频文件

在蓝牙音频体系中,耳机为Source端,接收器为Sink端。

1,支持的音频格式

Pulseaudio自带的paplay程序支持以下音频格式:

paplay --list-file-formats

aiff AIFF (Apple/SGI)

au AU (Sun/NeXT)

avr AVR (Audio Visual Research)

caf CAF (Apple Core Audio File)

flac FLAC (FLAC Lossless Audio Codec)

htk HTK (HMM Tool Kit)

iff IFF (Amiga IFF/SVX8/SV16)

mat MAT4 (GNU Octave 2.0 / Matlab 4.2)

mat MAT5 (GNU Octave 2.1 / Matlab 5.0)

mpc MPC (Akai MPC 2k)

oga OGG (OGG Container format)

paf PAF (Ensoniq PARIS)

pvf PVF (Portable Voice Format)

raw RAW (header-less)

rf64 RF64 (RIFF 64)

sd2 SD2 (Sound Designer II)

sds SDS (Midi Sample Dump Standard)

sf SF (Berkeley/IRCAM/CARL)

voc VOC (Creative Labs)

w64 W64 (SoundFoundry WAVE 64)

wav WAV (Microsoft)

wav WAV (NIST Sphere)

wav WAVEX (Microsoft)

wve WVE (Psion Series 3)

xi XI (FastTracker 2)

想要扩展更多的音频格式,可移植更上层的音频程序,例如GStreamer。

2,播放音频

连接了蓝牙耳机后,可使用pulseaudio的paplay程序播放音频文件,执行以下命令播放音频文件:

./paplay -v /mnt/sd/test.wav

一般来说默认启用HSP协议,在这个模式下,音频流是单通道的,采样率也比较低,播放音频会有很明显的“沙沙”声,很多声音的细节也会丢失,如果耳机支持A2DP立体声协议,可以将蓝牙切换到A2DP协议,提高音频播放效果,执行以下命令:

./pacmd set-card-profile bluez_card.00_11_67_11_11_67 a2dp_sink

./pacmd set-default-sink bluez_sink.00_11_67_11_11_67.a2dp_sink

请根据蓝牙耳机的实际MAC地址进行修改,具体可以查阅pacmd或pactl程序的帮助文档,该文档可在Pulseaudio官网中找到。需要注意的是A2DP协议是单向的,不支持录音功能。

3,录音

要使用蓝牙耳机进行录音,需要将音频协议切换到HSP模式,该模式支持双向音频流,所以支持录音,执行以下命令切换:

./pacmd set-card-profile bluez_card.00_11_67_11_11_67 headset_head_unit

./pacmd set-default-sink bluez_sink.00_11_67_11_11_67.headset_head_unit

./pacmd set-default-source bluez_source.00_11_67_11_11_67.headset_head_unit

./parecord -v voice.wav --channels=1 --rate=8000

./paplay -v voice.wav

请根据蓝牙耳机的实际MAC地址做相应修改,需要注意的是parecord的--channels和--rate参数,这两个参数可以通过pactl list sources获得,例如:

pactl list sources

Source #0

State: SUSPENDED

Name: alsa_output.pci-0000_02_02.0.analog-stereo.monitor

Description: Monitor of Creative Sound Blaster AudioPCI64V, AudioPCI128 Analog Stereo

Driver: module-alsa-card.c

Sample Specification: s16le 1ch 8000Hz

Channel Map: front-left,front-right

Owner Module: 5

Mute: no

Volume: 0: 100% 1: 100%

0: 0.00 dB 1: 0.00 dB

balance 0.00

Base Volume: 100%

0.00 dB

Monitor of Sink: alsa_output.pci-0000_02_02.0.analog-stereo

Latency: 0 usec, configured 0 usec

Flags: DECIBEL_VOLUME LATENCY

Properties:

device.description = "Monitor of Creative Sound Blaster AudioPCI64V, AudioPCI128 Analog Stereo"

device.class = "monitor"

alsa.card = "0"

alsa.card_name = "Ensoniq AudioPCI"

alsa.long_card_name = "Ensoniq AudioPCI ENS1371 at 0x2040, irq 16"

alsa.driver_name = "snd_ens1371"

device.bus_path = "pci-0000:02:02.0"

sysfs.path = "/devices/pci0000:00/0000:00:11.0/0000:02:02.0/sound/card0"

device.bus = "pci"

device.vendor.id = "1274"

device.vendor.name = "Ensoniq"

device.product.id = "1371"

device.product.name = "Creative Sound Blaster AudioPCI64V, AudioPCI128"

device.string = "0"

module-udev-detect.discovered = "1"

device.icon_name = "audio-card-pci"

Formats:

pcm

ZYNQ平台Linux4.6内核蓝牙音频相关推荐

  1. aac蓝牙编解码协议_蓝牙音频编码哪个音质好?今天我们来逐一解读

    家有影院致力于帮助渴望学习.热爱电影.希望通过自己双手搭建出适合自己的家庭影院的朋友.如果你是这样的人,我们和500位同样热爱家庭影院的伙伴愿意一起帮助你实现梦想.同时,我们还能帮助你找到价格优惠的靠 ...

  2. 基于zynq的千兆网udp项目_基于Zynq平台的EtherCAT主站方案实现

    作者:陈秋苑 谢晓锋 陈海焕 广州虹科电子科技有限公司 摘 要:EtherCAT 是开放的实时以太网通讯协议,由德国倍福自动化有限公司研发.EtherCAT 具有高性能.低成本.容易使用等特点,目前在 ...

  3. 当前市场主流蓝牙音频SOC

    2020年5月9日更: 目前安卓已经全面支持LDAC了,讨论其他格式的蓝牙音频方案已经没多大意义了. 对于真无线耳机方案来说,也就剩高通和苹果了,开发者可选也就高通了. 这个市场已经归一统了~~~~~ ...

  4. BlueZ双模蓝牙音频卡顿问题优化

    一.问题现象 由于设备支持双模蓝牙,设备的BLE需求中,既需要支持作为从机被手机等设备连接,也支持作为主机连接蓝牙手柄等外设,即在播放音频时,允许同时进行低功耗蓝牙相关的功能.实际开发过程中发现在播放 ...

  5. LinUX接收蓝牙音频,面向嵌入式Linux的蓝牙音频技术的研究

    摘要: 近几年来,随着人们需求的不断提高,手持设备,无线设备,数字家庭设备等嵌入式产品迅速的发展,特别是嵌入式Linux的成熟,使本来只属于PC的一些高端应用也出现在这种嵌入式系统上面,并得到广泛的发 ...

  6. CSR8670项目实战:BlueDongle 蓝牙音频测试dongle

    为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板[思度科技CSR开发板]. 技术交流QQ群号:743434463 开发板会员QQ群号:725398389(凭订单号入群,赠PP ...

  7. 一篇深入读懂蓝牙音频!

    一.蓝牙简介 蓝牙是一种无线通讯技术标准,用来让设备之间在短距离内交换资料,最早由瑞典的爱立信公司在 1994 年发布.提出蓝牙标准的目的很简单,就是简化电子设备之间的数据交互过程.那个时候手机刚刚开 ...

  8. petalinux在zynq平台移植和双网口实现

    petalinux在zynq平台的移植 硬件 1.zynq-7z010; 2.两个mavell,88E1510网卡 软件 1.petalinux2018.2; 2.linux-xlnx-xilinx- ...

  9. 听听周报-第二季度无线耳机全球市场出货量 AirPods占到一半|蓝牙音频传输格式AAC

    <听听周报>by伦茨科技 1.蓝牙电子价签 蓝牙电子价签,应用蓝牙5.0技术的电子价签刷新速度更快,1个电子货架标签的刷新用时不到1秒钟,5万片以内电子价签刷新,最快可达10分钟/次,且功 ...

  10. 用matlab怎么画视电阻率拟断面图,在MATLAB平台上实现可控源音频大地电磁反演数据三维可视化显示...

    第29卷 增刊 物探化探计算技术 2007年10月 收稿日期6文章编号:1001-1749(2007)增刊(1)-0068-04 在MAT LAB 平台上实现可控源音频大地 电磁反演数据三维可视化显示 ...

最新文章

  1. ios html 有白色边框,html – 仅在iPad上的桌子的单元格之间非常薄的白色边框
  2. python 日志不会按照日期分割_python实现日志按天分割
  3. 数组先小于等于再大于等于的调整
  4. QT判断操作系统版本
  5. java join 源码_join on 和where 一起使用的细节
  6. Java使用TCP实现群聊 聊天室(多线程和tcp的使用)
  7. java事件绑定,Java编程GUI中的事件绑定代码示例
  8. 笨方法“学习python笔记之输入
  9. IE7下JSON不能有多余的逗号,IE8下创建IMG节点的BUG
  10. 简述计算机绘图的应用领域试卷,计算机绘图试卷A(标准答案)
  11. java编译软件 Eclipse 的安装与使用
  12. python把wav本地转文字_在python中将大型wav文件转换为文本
  13. 数字信号处理digital signal processing经典书籍
  14. flag计算机语言的意思,flag是什么意思-c语言flag的用法
  15. vmware克隆linux虚拟机,报Device eth1 does not seem to be present,delaying initialization.错误
  16. 2019计算机世界语言排名,2019程序语言排行_2019 年 8 月编程语言排行榜
  17. Using setJavaScriptEnabled can Introduce XSS Vulnerabilities into
  18. java kafka设置偏移量_kafka实战宝典:手动修改消费偏移量的两种方式
  19. 用Postman测试网页接口
  20. 指纹识别-(1)基本知识

热门文章

  1. 【Android应用】 九宫格日志
  2. 网络技术人员要知道的100个安全工具
  3. 学习笔记(01):2019软考网络工程师--基础知识视频教程-数据通信基础(一)
  4. CSS3与CSS的区别有哪些?
  5. C盘深度清理(超快简单全面)
  6. 阿帕奇服务器配置文件,阿帕奇服务器基本参数配置
  7. 格力空调售后服务管理系统
  8. android mtklog,Mtklog结构及分析
  9. 国家行政区划数据结构化入库
  10. VMware系列序列号