mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-21 23:18:22 +03:00
Compare commits
34 Commits
I2S-4MFlas
...
Muse.16.13
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef692b1b50 | ||
|
|
93a2c0969c | ||
|
|
bc4e56eabc | ||
|
|
fa91879535 | ||
|
|
2f9b506e9b | ||
|
|
806cb054ba | ||
|
|
ff663a20b6 | ||
|
|
8bad480112 | ||
|
|
a38cb55468 | ||
|
|
c0a9fd3100 | ||
|
|
8cf9c16140 | ||
|
|
12147eb570 | ||
|
|
3afb2615c3 | ||
|
|
e0e7e718ba | ||
|
|
f0527f70ac | ||
|
|
3ecc09b989 | ||
|
|
7dbed7a67b | ||
|
|
043d73dd6e | ||
|
|
dc62afd788 | ||
|
|
d51df83981 | ||
|
|
f4388a8c0a | ||
|
|
4ee9878a6f | ||
|
|
025b5d8c75 | ||
|
|
0bebaccf57 | ||
|
|
7e8313baf3 | ||
|
|
ce4cebd994 | ||
|
|
ba81c2ecd5 | ||
|
|
131b1d36b0 | ||
|
|
0d37c270e2 | ||
|
|
6054affb81 | ||
|
|
196a1d179a | ||
|
|
7574054e22 | ||
|
|
cd088d2500 | ||
|
|
d16ce964d6 |
5
.github/workflows/Platform_build.yml
vendored
5
.github/workflows/Platform_build.yml
vendored
@@ -13,7 +13,6 @@ on:
|
|||||||
description: 'Force a Release build. When not forced, the system will check for release word in the last commit message to trigger a release'
|
description: 'Force a Release build. When not forced, the system will check for release word in the last commit message to trigger a release'
|
||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
bootstrap:
|
bootstrap:
|
||||||
name: Global setup
|
name: Global setup
|
||||||
@@ -228,5 +227,7 @@ jobs:
|
|||||||
update_web_installer:
|
update_web_installer:
|
||||||
name: Update Web Installer After Release
|
name: Update Web Installer After Release
|
||||||
needs: [ bootstrap, build ]
|
needs: [ bootstrap, build ]
|
||||||
if: ${{( always() && !cancelled() ) && needs.bootstrap.outputs.release_flag == 1 && needs.bootstrap.outputs.mock == 0 }}
|
if: ${{ always() && !cancelled() && needs.bootstrap.outputs.release_flag == 1 && needs.bootstrap.outputs.mock == 0 }}
|
||||||
uses: ./.github/workflows/web_deploy.yml
|
uses: ./.github/workflows/web_deploy.yml
|
||||||
|
secrets:
|
||||||
|
WEB_INSTALLER: ${{ secrets.WEB_INSTALLER }}
|
||||||
16
.github/workflows/codeql-analysis.yml
vendored
16
.github/workflows/codeql-analysis.yml
vendored
@@ -19,6 +19,7 @@ on:
|
|||||||
branches: [ master-cmake ]
|
branches: [ master-cmake ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '19 12 * * 4'
|
- cron: '19 12 * * 4'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
@@ -39,7 +40,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -50,7 +51,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -62,6 +63,13 @@ jobs:
|
|||||||
#- run: |
|
#- run: |
|
||||||
# make bootstrap
|
# make bootstrap
|
||||||
# make release
|
# make release
|
||||||
|
# Exclude specific artifacts from analysis
|
||||||
|
- name: Exclude Artifacts
|
||||||
|
run: |
|
||||||
|
# Exclude components/wifi-manager/webapp/dist/js/index* from analysis
|
||||||
|
echo 'components/wifi-manager/webapp/dist/js/index*' >> .codeql-exclude-paths
|
||||||
|
echo 'components/wifi-manager/webapp/dist/js/index*' >> .codeql-exclude-paths.txt
|
||||||
|
echo 'components/wifi-manager/webapp/dist/index*' >> .codeql-exclude-paths
|
||||||
|
echo 'components/wifi-manager/webapp/dist/index*' >> .codeql-exclude-paths.txt
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|||||||
3
.github/workflows/web_deploy.yml
vendored
3
.github/workflows/web_deploy.yml
vendored
@@ -1,6 +1,9 @@
|
|||||||
name: Update Web Installer
|
name: Update Web Installer
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
WEB_INSTALLER:
|
||||||
|
required: true
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
update_web_installer:
|
update_web_installer:
|
||||||
|
|||||||
203
README.md
203
README.md
@@ -1,22 +1,23 @@
|
|||||||

|
[](https://github.com/sle118/squeezelite-esp32/actions/workflows/Platform_build.yml)
|
||||||

|
|
||||||
# Squeezelite-esp32
|
# Squeezelite-esp32
|
||||||
## What is this
|
|
||||||
|
## What is this?
|
||||||
Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wifi (b/g/n) and bluetooth chipset. It offers the following capabilities
|
Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wifi (b/g/n) and bluetooth chipset. It offers the following capabilities
|
||||||
|
|
||||||
- Stream your local music and connect to all major on-line music providers (Spotify, Deezer, Tidal, Qobuz) using [Logitech Media Server - a.k.a LMS](https://forums.slimdevices.com/) and enjoy multi-room audio synchronization. LMS can be extended by numerous plugins and can be controlled using a Web browser or dedicated applications (iPhone, Android). It can also send audio to UPnP, Sonos, ChromeCast and AirPlay speakers/devices.
|
- Stream your local music and connect to all major on-line music providers (Spotify, Deezer, Tidal, Qobuz) using [Logitech Media Server - a.k.a LMS](https://forums.slimdevices.com/) and enjoy multi-room audio synchronization. LMS can be extended by numerous plugins and can be controlled using a Web browser or dedicated applications (iPhone, Android). It can also send audio to UPnP, Sonos, ChromeCast and AirPlay speakers/devices.
|
||||||
- Stream directly from a Bluetooth device (iPhone, Android)
|
- Stream from a **Bluetooth** device (iPhone, Android)
|
||||||
- Stream directly from an AirPlay controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
|
- Stream from an **AirPlay** controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
|
||||||
- Stream direcly from Spotify using SpotifyConnect (thanks to [cspot](https://github.com/feelfreelinux/cspot)
|
- Stream direcly from **Spotify** using SpotifyConnect (thanks to [cspot](https://github.com/feelfreelinux/cspot)
|
||||||
|
|
||||||
Depending on the hardware connected to the ESP32, you can send audio to a local DAC, to SPDIF or to a Bluetooth speaker. The bare minimum required hardware is a WROVER module with 4MB of Flash and 4MB of PSRAM (https://www.espressif.com/en/products/modules/esp32). With that module standalone, just apply power and you can stream to a Bluetooth speaker. You can also send audio to most I2S DAC as well as to SPDIF receivers using just a cable or an optical transducer.
|
Depending on the hardware connected to the ESP32, you can send audio to a local DAC, to SPDIF or to a Bluetooth speaker. The bare minimum required hardware is a WROVER module with 4MB of Flash and 4MB of PSRAM (https://www.espressif.com/en/products/modules/esp32). With that module standalone, just apply power and you can stream to a Bluetooth speaker. You can also send audio to most I2S DAC as well as to SPDIF receivers using just a cable or an optical transducer.
|
||||||
|
|
||||||
But squeezelite-esp32 is highly extensible and you can add
|
But squeezelite-esp32 is highly extensible and you can add
|
||||||
|
|
||||||
- Buttons and Rotary Encoder and map/combine them to various functions (play, pause, volume, next ...)
|
- [Buttons](#buttons) and [Rotary Encoder](#rotary-encoder) and map/combine them to various functions (play, pause, volume, next ...)
|
||||||
- IR receiver (no pullup resistor or capacitor needed, just the 38kHz receiver)
|
- [GPIO expander](#gpio-expanders) (buttons, led and rotary)
|
||||||
- Monochrome, GrayScale or Color displays using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341).
|
- [IR receiver](#infrared) (no pullup resistor or capacitor needed, just the 38kHz receiver)
|
||||||
- Ethernet using a Microchip LAN8720 with RMII interface or Davicom DM9051 over SPI.
|
- [Monochrome, GrayScale or Color displays](#display) using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341).
|
||||||
|
- [Ethernet](#ethernet-required-unpublished-version-43) using a Microchip LAN8720 with RMII interface or Davicom DM9051/W5500 over SPI.
|
||||||
|
|
||||||
Other features include
|
Other features include
|
||||||
|
|
||||||
@@ -51,29 +52,44 @@ The esp32 must run at 240 MHz, with Quad-SPI I/O at 80 MHz and a clock of 40 Mhz
|
|||||||
In 16 bits mode, although 192 kHz is reported as max rate, it's highly recommended to limit reported sampling rate to 96k (-Z 96000). Note that some high-speed 24/96k on-line streams might stutter because of TCP/IP stack performances. It is usually due to the fact that the server sends small packets of data and the esp32 cannot receive encoded audio fast enough, regardless of task priority settings (I've tried to tweak that a fair bit). The best option in that case is to let LMS proxy the stream as it will provide larger chunks and a "smoother" stream that can then be handled.
|
In 16 bits mode, although 192 kHz is reported as max rate, it's highly recommended to limit reported sampling rate to 96k (-Z 96000). Note that some high-speed 24/96k on-line streams might stutter because of TCP/IP stack performances. It is usually due to the fact that the server sends small packets of data and the esp32 cannot receive encoded audio fast enough, regardless of task priority settings (I've tried to tweak that a fair bit). The best option in that case is to let LMS proxy the stream as it will provide larger chunks and a "smoother" stream that can then be handled.
|
||||||
|
|
||||||
Note as well that some codecs consume more CPU than others or have not been optimized as much. I've done my best to tweak these, but that level of optimization includes writing some assembly which is painful. One very demanding codec is AAC when files are encoded with SBR. It allows reconstruction of upper part of spectrum and thus higher sampling rate, but the codec spec is such that this is optional, you can decode simply lower band and accept lower sampling rate - See the AAC_DISABLE_SBR option below.
|
Note as well that some codecs consume more CPU than others or have not been optimized as much. I've done my best to tweak these, but that level of optimization includes writing some assembly which is painful. One very demanding codec is AAC when files are encoded with SBR. It allows reconstruction of upper part of spectrum and thus higher sampling rate, but the codec spec is such that this is optional, you can decode simply lower band and accept lower sampling rate - See the AAC_DISABLE_SBR option below.
|
||||||
|
|
||||||
## Supported Hardware
|
## Supported Hardware
|
||||||
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work. You can find various help & instructions [here](https://forums.slimdevices.com/showthread.php?112697-ANNOUNCE-Squeezelite-ESP32-(dedicated-thread))
|
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work. You can find various help & instructions [here](https://forums.slimdevices.com/showthread.php?112697-ANNOUNCE-Squeezelite-ESP32-(dedicated-thread))
|
||||||
|
|
||||||
**For the sake of clarity, WROOM modules DO NOT work as they don't include PSRAM. Some designs might add it externally, but it's (very) unlikely.**
|
**For the sake of clarity, WROOM modules DO NOT work as they don't include PSRAM. Some designs might add it externally, but it's (very) unlikely.**
|
||||||
|
|
||||||
### Raw WROVER module
|
### Raw WROVER module
|
||||||
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
|
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
|
||||||
|
|
||||||
Please note that when sending to a Bluetooth speaker (source), only 44.1 kHz can be used, so you either let LMS do the resampling, but you must make sure it only sends 44.1kHz tracks or enable internal resampling (using -R) option. If you connect a DAC, choice of sample rates will depends on its capabilities. See below for more details.
|
Please note that when sending to a Bluetooth speaker (source), only 44.1 kHz can be used, so you either let LMS do the resampling, but you must make sure it only sends 44.1kHz tracks or enable internal resampling (using -R) option. If you connect a DAC, choice of sample rates will depends on its capabilities. See below for more details.
|
||||||
|
|
||||||
Most DAC will work out-of-the-box with simply an I2S connection, but some require specific commands to be sent using I2C. See DAC option below to understand how to send these dedicated commands. There is build-in support for TAS575x, TAS5780, TAS5713 and AC101 DAC.
|
Most DAC will work out-of-the-box with simply an I2S connection, but some require specific commands to be sent using I2C. See DAC option below to understand how to send these dedicated commands. There is build-in support for TAS575x, TAS5780, TAS5713 and AC101 DAC.
|
||||||
|
|
||||||
### SqueezeAMP
|
### SqueezeAMP
|
||||||
This is the main hardware companion of Squeezelite-esp32 and has been developped together. Details on capabilities can be found [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP).
|
This is the main hardware companion of Squeezelite-esp32 and has been developped together. Details on capabilities can be found [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP).
|
||||||
|
|
||||||
if you want to rebuild, use the `squeezelite-esp32-SqueezeAmp-sdkconfig.defaults` configuration file.
|
If you want to rebuild, use the `squeezelite-esp32-SqueezeAmp-sdkconfig.defaults` configuration file.
|
||||||
|
|
||||||
NB: You can use the pre-build binaries SqueezeAMP4MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
|
NB: You can use the pre-build binaries SqueezeAMP4MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
|
||||||
- set_GPIO: 12=green,13=red,34=jack,2=spkfault
|
- set_GPIO: `12=green,13=red,34=jack,2=spkfault`
|
||||||
- batt_config: channel=7,scale=20.24
|
- bat_config: `channel=7,scale=20.24`
|
||||||
- dac_config: model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0
|
- dac_config: `model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0`
|
||||||
- spdif_config: bck=33,ws=25,do=15
|
- spdif_config: `bck=33,ws=25,do=15`
|
||||||
|
|
||||||
|
### MuseLuxe
|
||||||
|
This portable battery-powered [speaker](https://raspiaudio.com/produit/esp-muse-luxe) is compatible with squeezelite-esp32 for which there is a dedicated build supplied with every update. If you want to rebuild, use the `squeezelite-esp32-Muse-sdkconfig.defaults` configuration file.
|
||||||
|
|
||||||
|
NB: You can use the pre-build binaries Muse4MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
|
||||||
|
- target: `muse`
|
||||||
|
- bat_config: `channel=5,scale=7.48,atten=3,cells=1`
|
||||||
|
- spi_config: `"mosi=15,miso=2,clk=14` *(this one is probably optional)*
|
||||||
|
- dac_config: `model=I2S,bck=5,ws=25,do=26,di=35,i2c=16,sda=18,scl=23,mck`
|
||||||
|
- dac_controlset: `{"init":[ {"reg":0,"val":128}, {"reg":0,"val":0}, {"reg":25,"val":4}, {"reg":1,"val":80}, {"reg":2,"val":0}, {"reg":8,"val":0}, {"reg":4,"val":192}, {"reg":0,"val":18}, {"reg":1,"val":0}, {"reg":23,"val":24}, {"reg":24,"val":2}, {"reg":38,"val":9}, {"reg":39,"val":144}, {"reg":42,"val":144}, {"reg":43,"val":128}, {"reg":45,"val":128}, {"reg":27,"val":0}, {"reg":26,"val":0}, {"reg":2,"val":240}, {"reg":2,"val":0}, {"reg":29,"val":28}, {"reg":4,"val":48}, {"reg":25,"val":0}, {"reg":46,"val":33}, {"reg":47,"val":33} ]}`
|
||||||
|
- actrls_config: buttons
|
||||||
|
- define a "buttons" variable with: `[{"gpio":32, "pull":true, "debounce":10, "normal":{"pressed":"ACTRLS_VOLDOWN"}}, {"gpio":19, "pull":true, "debounce":40, "normal":{"pressed":"ACTRLS_VOLUP"}}, {"gpio":12, "pull":true, "debounce":40, "long_press":1000, "normal":{"pressed":"ACTRLS_TOGGLE"},"longpress":{"pressed":"ACTRLS_POWER"}}]`
|
||||||
|
|
||||||
### ESP32-A1S
|
### ESP32-A1S
|
||||||
Works with [ESP32-A1S](https://docs.ai-thinker.com/esp32-a1s) module that includes audio codec and headset output. You still need to use a demo board like [this](https://www.aliexpress.com/item/4001060963585.html) or an external amplifier if you want direct speaker connection. Note that there is a version with AC101 codec and another one with ES8388 (see below)
|
Works with [ESP32-A1S](https://docs.ai-thinker.com/esp32-a1s) module that includes audio codec and headset output. You still need to use a demo board like [this](https://www.aliexpress.com/item/4001060963585.html) or an external amplifier if you want direct speaker connection. Note that there is a version with AC101 codec and another one with ES8388 with probably two variants - these boards are a mess (see below)
|
||||||
|
|
||||||
The board shown above has the following IO set
|
The board shown above has the following IO set
|
||||||
- amplifier: GPIO21
|
- amplifier: GPIO21
|
||||||
@@ -89,23 +105,30 @@ The board shown above has the following IO set
|
|||||||
(note that some GPIO need pullups)
|
(note that some GPIO need pullups)
|
||||||
|
|
||||||
So a possible config would be
|
So a possible config would be
|
||||||
- set_GPIO: 21=amp,22=green:0,39=jack:0
|
- set_GPIO: `21=amp,22=green:0,39=jack:0`
|
||||||
- a button mapping:
|
- a button mapping:
|
||||||
```
|
```json
|
||||||
[{"gpio":5,"normal":{"pressed":"ACTRLS_TOGGLE"}},{"gpio":18,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLUP"}, "shifted":{"pressed":"ACTRLS_NEXT"}}, {"gpio":23,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLDOWN"},"shifted":{"pressed":"ACTRLS_PREV"}}]
|
[{"gpio":5,"normal":{"pressed":"ACTRLS_TOGGLE"}},{"gpio":18,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLUP"}, "shifted":{"pressed":"ACTRLS_NEXT"}}, {"gpio":23,"pull":true,"shifter_gpio":5,"normal":{"pressed":"ACTRLS_VOLDOWN"},"shifted":{"pressed":"ACTRLS_PREV"}}]
|
||||||
```
|
```
|
||||||
for AC101
|
for **AC101**
|
||||||
- dac_config: model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32
|
- dac_config: `model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32`
|
||||||
|
|
||||||
|
for **ES8388** (it seems that there are variants with same version number - a total mess)
|
||||||
|
- dac_config: `model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16`
|
||||||
|
or
|
||||||
|
- dac_config: `model=ES8388,bck=27,ws=25,do=26,sda=33,scl=32,i2c=16`
|
||||||
|
|
||||||
for ES8388
|
|
||||||
- dac_config: model=ES8388,bck=5,ws=25,do=26,sda=18,scl=23,i2c=16
|
|
||||||
### T-WATCH2020 by LilyGo
|
### T-WATCH2020 by LilyGo
|
||||||
This is a fun [smartwatch](http://www.lilygo.cn/prod_view.aspx?TypeId=50036&Id=1290&FId=t3:50036:3) based on ESP32. It has a 240x240 ST7789 screen and onboard audio. Not very useful to listen to anything but it works. This is an example of a device that requires an I2C set of commands for its dac (see below). There is a build-option if you decide to rebuild everything by yourself, otherwise the I2S default option works with the following parameters
|
This is a fun [smartwatch](http://www.lilygo.cn/prod_view.aspx?TypeId=50036&Id=1290&FId=t3:50036:3) based on ESP32. It has a 240x240 ST7789 screen and onboard audio. Not very useful to listen to anything but it works. This is an example of a device that requires an I2C set of commands for its dac (see below). There is a build-option if you decide to rebuild everything by yourself, otherwise the I2S default option works with the following parameters
|
||||||
|
|
||||||
- dac_config: model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22
|
- dac_config: `model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22`
|
||||||
- dac_controlset: { "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and"} ] }
|
- dac_controlset:
|
||||||
- spi_config: dc=27,data=19,clk=18
|
```json
|
||||||
- display_config: SPI,driver=ST7789,width=240,height=240,cs=5,back=12,speed=16000000,HFlip,VFlip
|
{ "init": [ {"reg":41, "val":128}, {"reg":18, "val":255} ], "poweron": [ {"reg":18, "val":64, "mode":"or"} ], "poweroff": [ {"reg":18, "val":191, "mode":"and"} ] }
|
||||||
|
```
|
||||||
|
- spi_config: `dc=27,data=19,clk=18`
|
||||||
|
- display_config: `SPI,driver=ST7789,width=240,height=240,cs=5,back=12,speed=16000000,HFlip,VFlip`
|
||||||
|
|
||||||
### ESP32-WROVER + I2S DAC
|
### ESP32-WROVER + I2S DAC
|
||||||
Squeezelite-esp32 requires esp32 chipset and 4MB PSRAM. ESP32-WROVER meets these requirements. To get an audio output an I2S DAC can be used. Cheap PCM5102 I2S DACs work others may also work. PCM5012 DACs can be hooked up via:
|
Squeezelite-esp32 requires esp32 chipset and 4MB PSRAM. ESP32-WROVER meets these requirements. To get an audio output an I2S DAC can be used. Cheap PCM5102 I2S DACs work others may also work. PCM5012 DACs can be hooked up via:
|
||||||
|
|
||||||
@@ -131,18 +154,23 @@ And the super cool project https://github.com/rochuck/squeeze-amp-too
|
|||||||
## Configuration
|
## Configuration
|
||||||
To access NVS, in the webUI, go to credits and select "shows nvs editor". Go into the NVS editor tab to change NFS parameters. In syntax description below \<\> means a value while \[\] describe optional parameters.
|
To access NVS, in the webUI, go to credits and select "shows nvs editor". Go into the NVS editor tab to change NFS parameters. In syntax description below \<\> means a value while \[\] describe optional parameters.
|
||||||
|
|
||||||
|
As mentionned above, there are a few dedicated builds that are provided today: SqueezeAMP and Muse but if you build it yourself, you can also create a build for T-WATCH2020. The default build is a generic firmware named I2S which can be configured through NVS to produce *exactly* the same results than dedicated builds. The difference is that parameters must be entered and can accidently be erased. The GUI provides a great help to load "known config sets" as well.
|
||||||
|
|
||||||
|
By design choice, there is no code that is only embedded for a given version, all code is always there. The philosophy is to minimize as much as possible platform-specific code and use of specific `#ifdef` is prohibited, no matter what. So if you want to add your own platfrom, please look **very hard** at the `main\KConfig.projbuild` to see how you can, using parameters below, make your device purely a configuration-based solution. When there is really no other option, look at `targets\<target>` to add your own code. I will not accept PR for code that can avoid creating such dedicated code whenever possible. The NVS "target" will be used to call target-specific code then, but again this is purely runtime, not compile-time.
|
||||||
|
|
||||||
### I2C
|
### I2C
|
||||||
The NVS parameter "i2c_config" set the i2c's gpio used for generic purpose (e.g. display). Leave it blank to disable I2C usage. Note that on SqueezeAMP, port must be 1. Default speed is 400000 but some display can do up to 800000 or more. Syntax is
|
The NVS parameter "i2c_config" set the i2c's gpio used for generic purpose (e.g. display). Leave it blank to disable I2C usage. Note that on SqueezeAMP, port must be 1. Default speed is 400000 but some display can do up to 800000 or more. Syntax is
|
||||||
```
|
```
|
||||||
sda=<gpio>,scl=<gpio>[,port=0|1][,speed=<speed>]
|
sda=<gpio>,scl=<gpio>[,port=0|1][,speed=<speed>]
|
||||||
```
|
```
|
||||||
<strong>Please note that you can not use the same GPIO or port as the DAC</strong>
|
**Please note that you can not use the same GPIO or port as the DAC.**
|
||||||
|
|
||||||
### SPI
|
### SPI
|
||||||
The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
|
The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The NVS parameter "spi_config" set the spi's gpio used for generic purpose (e.g. display). Leave it blank to disable SPI usage. The DC parameter is needed for displays. Syntax is
|
||||||
```
|
```
|
||||||
data|mosi=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2][,miso=<gpio>]
|
data|mosi=<gpio>,clk=<gpio>[,dc=<gpio>][,host=1|2][,miso=<gpio>]
|
||||||
```
|
```
|
||||||
Default "host" is 1. The "miso" parameter is only used when SPI bus is to be shared with other peripheral (e.g. ethernet, see below), otherwise it can be omitted. Note that "data" can also be named "mosi".
|
Default and only "host" is 1 as others are used already by flash and spiram. The optional "miso" (MasterInSlaveOut) parameter is only used when SPI bus is bi-directional and shared with other peripheral like ethernet, gpio expander. Note that "data" can also be named "mosi" (MasterOutSlaveIn).
|
||||||
### DAC/I2S
|
### DAC/I2S
|
||||||
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is
|
The NVS parameter "dac_config" set the gpio used for i2s communication with your DAC. You can define the defaults at compile time but nvs parameter takes precedence except for SqueezeAMP and A1S where these are forced at runtime. Syntax is
|
||||||
```
|
```
|
||||||
@@ -150,17 +178,20 @@ bck=<gpio>,ws=<gpio>,do=<gpio>[,mck][,mute=<gpio>[:0|1][,model=TAS57xx|TAS5713|A
|
|||||||
```
|
```
|
||||||
if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK and be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
|
if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). Only GPIO0 can be used as MCLK and be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
|
||||||
|
|
||||||
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power on and off using a JSON syntax:
|
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power, speakder and headset on and off using a JSON syntax:
|
||||||
|
```json
|
||||||
|
{ <command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
||||||
|
<command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
||||||
|
... }
|
||||||
```
|
```
|
||||||
{ init: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff
|
||||||
poweron: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
|
||||||
poweroff: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ] }
|
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. The 'val parameter can be an array [v1, v2,...] to write a serie of bytes in a single i2c burst (in that case 'mode' is ignored). **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
||||||
```
|
|
||||||
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
|
||||||
|
|
||||||
NB: For specific builds (all except I2S), all this is ignored. For know codecs, the built-in sequences can be overwritten using dac_controlset
|
NB: For specific builds (all except I2S), all this is ignored. For know codecs, the built-in sequences can be overwritten using dac_controlset
|
||||||
|
|
||||||
<strong>Please note that you can not use the same GPIO or port as the I2C</strong>
|
**Please note that you can not use the same GPIO or port as the I2C.**
|
||||||
|
|
||||||
### SPDIF
|
### SPDIF
|
||||||
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
|
The NVS parameter "spdif_config" sets the i2s's gpio needed for SPDIF.
|
||||||
|
|
||||||
@@ -185,26 +216,26 @@ GPIO ----210ohm-----------||---- coax S/PDIF signal out
|
|||||||
|
|
|
|
||||||
Ground -------------------------- coax signal ground
|
Ground -------------------------- coax signal ground
|
||||||
```
|
```
|
||||||
|
|
||||||
### Display
|
### Display
|
||||||
The NVS parameter "display_config" sets the parameters for an optional display. Syntax is
|
The NVS parameter "display_config" sets the parameters for an optional display. It can be I2C (see [here](#i2c) for shared bus) or SPI (see [here](#spi) for shared bus) Syntax is
|
||||||
```
|
```
|
||||||
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,reset=<gpio>][,HFlip][,VFlip][driver=SSD1306|SSD1326[:1|4]|SSD1327|SH1106]
|
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,reset=<gpio>][,HFlip][,VFlip][driver=SSD1306|SSD1326[:1|4]|SSD1327|SH1106]
|
||||||
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,reset=<gpio>][,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1322|SSD1326[:1|4]|SSD1327|SH1106|SSD1675|ST7735|ST7789|ILI9341[:16|18][,rotate]][,mode=<mode>]
|
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,reset=<gpio>][,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1322|SSD1326[:1|4]|SSD1327|SH1106|SSD1675|ST7735[:x=<offset>][:y=<offset>]|ST7789|ILI9341[:16|18][,rotate]]
|
||||||
```
|
```
|
||||||
- back: a LED backlight used by some older devices (ST7735). It is PWM controlled for brightness
|
- back: a LED backlight used by some older devices (ST7735). It is PWM controlled for brightness
|
||||||
- reset: some display have a reset pin that is should normally be pulled up if unused
|
- reset: some display have a reset pin that is should normally be pulled up if unused. Most displays require reset and will not initialize well otherwise.
|
||||||
- VFlip and HFlip are optional can be used to change display orientation
|
- VFlip and HFlip are optional can be used to change display orientation
|
||||||
- rotate: for non-square *drivers*, move to portrait mode. Note that *width* and *height* must be inverted then
|
- rotate: for non-square *drivers*, move to portrait mode. Note that *width* and *height* must be inverted then
|
||||||
- Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz
|
- Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz
|
||||||
- mode: Default mode = 0. Some display modules use different transaction line timings. Check the module documentation if a non-standard mode is required.
|
- SH1106 is 128x64 monochrome I2C/SPI [here](https://www.waveshare.com/wiki/1.3inch_OLED_HAT)
|
||||||
- SH1106 is 128x64 monochrome I2C/SPI [here]((https://www.waveshare.com/wiki/1.3inch_OLED_HAT))
|
|
||||||
- SSD1306 is 128x32 monochrome I2C/SPI [here](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi)
|
- SSD1306 is 128x32 monochrome I2C/SPI [here](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi)
|
||||||
- SSD1322 is 256x64 grayscale 16-levels SPI in multiple sizes [here](https://www.buydisplay.com/oled-display/oled-display-module?resolution=159) - it is very nice
|
- SSD1322 is 256x64 grayscale 16-levels SPI in multiple sizes [here](https://www.buydisplay.com/oled-display/oled-display-module?resolution=159) - it is very nice
|
||||||
- SSD1326 is 256x32 monochrome or grayscale 16-levels SPI [here](https://www.aliexpress.com/item/32833603664.html?spm=a2g0o.productlist.0.0.2d19776cyQvsBi&algo_pvid=c7a3db92-e019-4095-8a28-dfdf0a087f98&algo_expid=c7a3db92-e019-4095-8a28-dfdf0a087f98-1&btsid=0ab6f81e15955375483301352e4208&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_)
|
- SSD1326 is 256x32 monochrome or grayscale 16-levels SPI [here](https://www.aliexpress.com/item/32833603664.html?spm=a2g0o.productlist.0.0.2d19776cyQvsBi&algo_pvid=c7a3db92-e019-4095-8a28-dfdf0a087f98&algo_expid=c7a3db92-e019-4095-8a28-dfdf0a087f98-1&btsid=0ab6f81e15955375483301352e4208&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_)
|
||||||
- SSD1327 is 128x128 16-level grayscale SPI [here](https://www.amazon.com/gp/product/B079N1LLG8/ref=ox_sc_act_title_1?smid=A1N6DLY3NQK2VM&psc=1) - artwork can be up to 96x96 with vertical vu-meter/spectrum
|
- SSD1327 is 128x128 16-level grayscale SPI [here](https://www.amazon.com/gp/product/B079N1LLG8/ref=ox_sc_act_title_1?smid=A1N6DLY3NQK2VM&psc=1) - artwork can be up to 96x96 with vertical vu-meter/spectrum
|
||||||
- SSD1351 is 128x128 65k/262k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.5inch-rgb-oled-module.htm)
|
- SSD1351 is 128x128 65k/262k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.5inch-rgb-oled-module.htm)
|
||||||
- SSD1675 is an e-ink paper and is experimental as e-ink is really not suitable for LMS du to its very low refresh rate
|
- SSD1675 is an e-ink paper and is experimental as e-ink is really not suitable for LMS du to its very low refresh rate
|
||||||
- ST7735 is a 128x160 65k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.8inch-lcd-module.htm). This needs a backlight control
|
- ST7735 is a 128x160 65k color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/1.8inch-lcd-module.htm). This needs a backlight control. Some have X/Y offsets betwen the driver and the glass (green/black/red models) that can be added using "x" and "y" options (case sensitive!)
|
||||||
- ST7789 is a 240x320 65k (262k not enabled) color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/2inch-lcd-module.htm). It also exist with 240x240 displays. See **rotate** for use in portrait mode
|
- ST7789 is a 240x320 65k (262k not enabled) color SPI [here](https://www.waveshare.com/product/displays/lcd-oled/lcd-oled-3/2inch-lcd-module.htm). It also exist with 240x240 displays. See **rotate** for use in portrait mode
|
||||||
- ILI9341 is another 240x320 65k (262k capable) color SPI. I've not used it much, the driver it has been provided by one external contributor to the project
|
- ILI9341 is another 240x320 65k (262k capable) color SPI. I've not used it much, the driver it has been provided by one external contributor to the project
|
||||||
|
|
||||||
@@ -212,13 +243,12 @@ You can tweak how the vu-meter and spectrum analyzer are displayed, as well as s
|
|||||||
|
|
||||||
The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is
|
The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is
|
||||||
```
|
```
|
||||||
[format=<display_content>][,speed=<speed>][,pause=<pause>]
|
[format=<display_content>][,speed=<speed>][,pause=<pause>][,artwork[:0|1]]
|
||||||
```
|
```
|
||||||
- 'speed' is the scrolling speed in ms (default is 33ms)
|
- 'speed' is the scrolling speed in ms (default is 33ms)
|
||||||
|
|
||||||
- 'pause' is the pause time between scrolls in ms (default is 3600ms)
|
- 'pause' is the pause time between scrolls in ms (default is 3600ms)
|
||||||
|
- 'format' can contain free text and any of the 3 keywords `%artist%`, `%album%`, `%title%`. Using that format string, the keywords are replaced by their value to build the string to be displayed. Note that the plain text following a keyword that happens to be empty during playback of a track will be removed. For example, if you have set format=`%artist% - %title%` and there is no artist in the metadata then only `<title>` will be displayed not ` - <title>`.
|
||||||
- 'format' can contain free text and any of the 3 keywords %artist%, %album%, %title%. Using that format string, the keywords are replaced by their value to build the string to be displayed. Note that the plain text following a keyword that happens to be empty during playback of a track will be removed. For example, if you have set format=%artist% - %title% and there is no artist in the metadata then only <title> will be displayed not " - <title>".
|
- 'artwork' enables coverart display, if available (does not work for Bluetooth). The optional parameter indicates if the artwork should be resized (1) to fit the available space. Note that the built-in resizer can only do 2,4 and 8 downsizing, so fit is not optimal. The artwork will be placed at the right of the display for landscape displays and underneath the two information lines for others (there is no user option to tweak that).
|
||||||
|
|
||||||
### Infrared
|
### Infrared
|
||||||
You can use any IR receiver compatible with NEC protocol (38KHz). Vcc, GND and output are the only pins that need to be connected, no pullup, no filtering capacitor, it's a straight connection.
|
You can use any IR receiver compatible with NEC protocol (38KHz). Vcc, GND and output are the only pins that need to be connected, no pullup, no filtering capacitor, it's a straight connection.
|
||||||
@@ -234,13 +264,13 @@ The parameter "set_GPIO" is used to assign GPIO to various functions.
|
|||||||
|
|
||||||
GPIO can be set to GND provide or Vcc at boot. This is convenient to power devices that consume less than 40mA from the side connector. Be careful because there is no conflict checks being made wrt which GPIO you're changing, so you might damage your board or create a conflict here.
|
GPIO can be set to GND provide or Vcc at boot. This is convenient to power devices that consume less than 40mA from the side connector. Be careful because there is no conflict checks being made wrt which GPIO you're changing, so you might damage your board or create a conflict here.
|
||||||
|
|
||||||
The \<amp\> parameter can use used to assign a GPIO that will be set to active level (default 1) when playback starts. It will be reset when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through -C \<timeout\>
|
The `<amp>` parameter can use used to assign a GPIO that will be set to active level (default 1) when playback starts. It will be reset when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through `-C <timeout>`
|
||||||
|
|
||||||
If you have an audio jack that supports insertion (use :0 or :1 to set the level when inserted), you can specify which GPIO it's connected to. Using the parameter jack_mutes_amp allows to mute the amp when headset (e.g.) is inserted.
|
If you have an audio jack that supports insertion (use :0 or :1 to set the level when inserted), you can specify which GPIO it's connected to. Using the parameter jack_mutes_amp allows to mute the amp when headset (e.g.) is inserted.
|
||||||
|
|
||||||
You can set the Green and Red status led as well with their respective active state (:0 or :1)
|
You can set the Green and Red status led as well with their respective active state (:0 or :1)
|
||||||
|
|
||||||
The \<ir\> parameter set the GPIO associated to an IR receiver. No need to add pullup or capacitor
|
The `<ir>` parameter set the GPIO associated to an IR receiver. No need to add pullup or capacitor
|
||||||
|
|
||||||
Syntax is:
|
Syntax is:
|
||||||
|
|
||||||
@@ -249,12 +279,34 @@ Syntax is:
|
|||||||
```
|
```
|
||||||
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for well-known configurations where these are forced at runtime.
|
You can define the defaults for jack, spkfault leds at compile time but nvs parameter takes precedence except for well-known configurations where these are forced at runtime.
|
||||||
**Note that gpio 36 and 39 are input only and cannot use interrupt. When set to jack or speaker fault, a 100ms polling checks their value but that's expensive**
|
**Note that gpio 36 and 39 are input only and cannot use interrupt. When set to jack or speaker fault, a 100ms polling checks their value but that's expensive**
|
||||||
|
|
||||||
|
### GPIO expanders
|
||||||
|
It is possible to add GPIO expanders using I2C or SPI bus. They should mainly be used for buttons but they can support generic-purpose outputs as well. These additional GPIOs can be numbered starting from an arbitrary value (40 and above as esp32 has GPIO 0..39). Then these new "virtual" GPIOs from (e.g) 100 to 115 can be used in [button](#Buttons) configuration, [set_GPIO](#set-gpio) or other config settings.
|
||||||
|
|
||||||
|
Each expander can support up to 32 GPIO. To use an expander for buttons, an interrupt must be provided, polling mode is not acceptable. An expander w/o interruption can still be configured, but only output will be usable. Note that the same interrupt can be shared accross expanders, as long as they are using open drain or open collectors (which they probably all do)
|
||||||
|
|
||||||
|
The parameter "gpio_exp_config" is a semicolon (;) separated list with following syntax for each expander
|
||||||
|
```
|
||||||
|
model=<model>,addr=<addr>,[,port=system|dac][,base=<n>|100][,count=<n>|16][,intr=<gpio>][,cs=<gpio>][,speed=<Hz>]
|
||||||
|
```
|
||||||
|
- model: pca9535, pca85xx, mcp23017 and mcp23s17 (SPI version)
|
||||||
|
- addr: chip i2c/spi address (decimal)
|
||||||
|
- port (I2C): use either "system" port (shared with display for example) or "dac" port (system is default)
|
||||||
|
- cs (SPI): gpio used for Chip Select
|
||||||
|
- speed (SPI): speed of the SPI bus for that device (in Hz)
|
||||||
|
- base: GPIO numbering offset to use everywhere else (default 40)
|
||||||
|
- count: number of GPIO of expander (default 16 - might be obsolted if model if sufficient to decide)
|
||||||
|
- intr: real GPIO to use as interrupt.
|
||||||
|
|
||||||
|
Note that PWM ("led_brightness" below) is not supported for expanded GPIOs and they cannot be used for high speed or precise timing signals like CS, D/C, Reset and Ready. Buttons, rotary encoder, amplifier control and power are supported. Depending on the actual chipset, pullup or pulldown might be supported so you might have to add external resistors (only MCP23x17 does pullup). The pca8575 is not a great chip, it generate a fair bit of spurious interrupts when used for GPIO out. When using a SPI expander, the bus must be configured using shared [SPI](#SPI) bus
|
||||||
|
|
||||||
### LED
|
### LED
|
||||||
See §**set_GPIO** for how to set the green and red LEDs. In addition, their brightness can be controlled using the "led_brigthness" parameter. The syntax is
|
See [set_GPIO](#set-gpio) for how to set the green and red LEDs. In addition, their brightness can be controlled using the "led_brigthness" parameter. The syntax is
|
||||||
```
|
```
|
||||||
[green=0..100][,red=0..100]
|
[green=0..100][,red=0..100]
|
||||||
```
|
```
|
||||||
NB: For well-known configuration, this is ignored
|
NB: For well-known configuration, this is ignored
|
||||||
|
|
||||||
### Rotary Encoder
|
### Rotary Encoder
|
||||||
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.
|
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.
|
||||||
|
|
||||||
@@ -280,9 +332,10 @@ The SW gpio is optional, you can re-affect it to a pure button if you prefer but
|
|||||||
See also the "IMPORTANT NOTE" on the "Buttons" section and remember that when 'lms_ctrls_raw' (see below) is activated, none of these knobonly,volume,longpress options apply, raw button codes (not actions) are simply sent to LMS
|
See also the "IMPORTANT NOTE" on the "Buttons" section and remember that when 'lms_ctrls_raw' (see below) is activated, none of these knobonly,volume,longpress options apply, raw button codes (not actions) are simply sent to LMS
|
||||||
|
|
||||||
**Note that gpio 36 and 39 are input only and cannot use interrupt, so they cannot be set to A or B. When using them for SW, a 100ms polling is used which is expensive**
|
**Note that gpio 36 and 39 are input only and cannot use interrupt, so they cannot be set to A or B. When using them for SW, a 100ms polling is used which is expensive**
|
||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
Buttons are described using a JSON string with the following syntax
|
Buttons are described using a JSON string with the following syntax
|
||||||
```
|
```json
|
||||||
[
|
[
|
||||||
{"gpio":<num>,
|
{"gpio":<num>,
|
||||||
"type":"BUTTON_LOW | BUTTON_HIGH",
|
"type":"BUTTON_LOW | BUTTON_HIGH",
|
||||||
@@ -311,7 +364,7 @@ Where (all parameters are optionals except gpio)
|
|||||||
- "shifted": action to take when a button is pressed/released and shifted (see above/below)
|
- "shifted": action to take when a button is pressed/released and shifted (see above/below)
|
||||||
- "longshifted": action to take when a button is long-pressed/released and shifted (see above/below)
|
- "longshifted": action to take when a button is long-pressed/released and shifted (see above/below)
|
||||||
|
|
||||||
Where \<action\> is either the name of another configuration to load (remap) or one amongst
|
Where `<action>` is either the name of another configuration to load (remap) or one amongst
|
||||||
|
|
||||||
```
|
```
|
||||||
ACTRLS_NONE, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
|
ACTRLS_NONE, ACTRLS_POWER, ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
|
||||||
@@ -324,7 +377,7 @@ KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
|
|||||||
One you've created such a string, use it to fill a new NVS parameter with any name below 16(?) characters. You can have as many of these configs as you can. Then set the config parameter "actrls_config" with the name of your default config
|
One you've created such a string, use it to fill a new NVS parameter with any name below 16(?) characters. You can have as many of these configs as you can. Then set the config parameter "actrls_config" with the name of your default config
|
||||||
|
|
||||||
For example a config named "buttons" :
|
For example a config named "buttons" :
|
||||||
```
|
```json
|
||||||
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,"normal":{"pressed":"ACTRLS_VOLDOWN"},"longpress":{"pressed":"buttons_remap"}},
|
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,"normal":{"pressed":"ACTRLS_VOLDOWN"},"longpress":{"pressed":"buttons_remap"}},
|
||||||
{"gpio":5,"type":"BUTTON_LOW","pull":true,"shifter_gpio":4,"normal":{"pressed":"ACTRLS_VOLUP"}, "shifted":{"pressed":"ACTRLS_TOGGLE"}}]
|
{"gpio":5,"type":"BUTTON_LOW","pull":true,"shifter_gpio":4,"normal":{"pressed":"ACTRLS_VOLUP"}, "shifted":{"pressed":"ACTRLS_TOGGLE"}}]
|
||||||
```
|
```
|
||||||
@@ -333,7 +386,7 @@ Defines two buttons
|
|||||||
- second on GPIO 5, active low. When pressed it triggers a volume up command. If first button is pressed together with this button, then a play/pause toggle command is generated.
|
- second on GPIO 5, active low. When pressed it triggers a volume up command. If first button is pressed together with this button, then a play/pause toggle command is generated.
|
||||||
|
|
||||||
While the config named "buttons_remap"
|
While the config named "buttons_remap"
|
||||||
```
|
```json
|
||||||
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,"normal":{"pressed":"BCTRLS_DOWN"},"longpress":{"pressed":"buttons"}},
|
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,"normal":{"pressed":"BCTRLS_DOWN"},"longpress":{"pressed":"buttons"}},
|
||||||
{"gpio":5,"type":"BUTTON_LOW","pull":true,"shifter_gpio":4,"normal":{"pressed":"BCTRLS_UP"}}]
|
{"gpio":5,"type":"BUTTON_LOW","pull":true,"shifter_gpio":4,"normal":{"pressed":"BCTRLS_UP"}}]
|
||||||
```
|
```
|
||||||
@@ -341,10 +394,14 @@ Defines two buttons
|
|||||||
- first on GPIO 4, active low. When pressed, it triggers a navigation down command. When pressed more than 1000ms, it changes the button configuration for the one described above
|
- first on GPIO 4, active low. When pressed, it triggers a navigation down command. When pressed more than 1000ms, it changes the button configuration for the one described above
|
||||||
- second on GPIO 5, active low. When pressed it triggers a navigation up command. That button, in that configuration, has no shift option
|
- second on GPIO 5, active low. When pressed it triggers a navigation up command. That button, in that configuration, has no shift option
|
||||||
|
|
||||||
Below is a difficult but functional 2-buttons interface for your decoding pleasure
|
Below is a difficult but functional 2-buttons interface for your decoding pleasure:
|
||||||
|
|
||||||
*buttons*
|
`actrls_config`:
|
||||||
```
|
```
|
||||||
|
buttons
|
||||||
|
```
|
||||||
|
`buttons`:
|
||||||
|
```json
|
||||||
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,
|
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,
|
||||||
"normal":{"pressed":"ACTRLS_VOLDOWN"},
|
"normal":{"pressed":"ACTRLS_VOLDOWN"},
|
||||||
"longpress":{"pressed":"buttons_remap"}},
|
"longpress":{"pressed":"buttons_remap"}},
|
||||||
@@ -354,8 +411,8 @@ Below is a difficult but functional 2-buttons interface for your decoding pleasu
|
|||||||
"longpress":{"pressed":"ACTRLS_NEXT"}}
|
"longpress":{"pressed":"ACTRLS_NEXT"}}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
*buttons_remap*
|
`buttons_remap`:
|
||||||
```
|
```json
|
||||||
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,
|
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,
|
||||||
"normal":{"pressed":"BCTRLS_DOWN"},
|
"normal":{"pressed":"BCTRLS_DOWN"},
|
||||||
"longpress":{"pressed":"buttons"}},
|
"longpress":{"pressed":"buttons"}},
|
||||||
@@ -366,7 +423,7 @@ Below is a difficult but functional 2-buttons interface for your decoding pleasu
|
|||||||
"longshifted":{"pressed":"BCTRLS_LEFT"}}
|
"longshifted":{"pressed":"BCTRLS_LEFT"}}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
<strong>IMPORTANT NOTE</strong>: LMS also supports the possibility to send 'raw' button codes. It's a bit complicated, so bear with me. Buttons can either be processed by SqueezeESP32 and mapped to a "function" like play/pause or they can be just sent to LMS as plain (raw) code and the full logic of press/release/longpress is handled by LMS, you don't have any control on that.
|
**IMPORTANT NOTE**: LMS also supports the possibility to send 'raw' button codes. It's a bit complicated, so bear with me. Buttons can either be processed by SqueezeESP32 and mapped to a "function" like play/pause or they can be just sent to LMS as plain (raw) code and the full logic of press/release/longpress is handled by LMS, you don't have any control on that.
|
||||||
|
|
||||||
The benefit of the "raw" mode is that you can build a player which is as close as possible to a Boom (e.g.) but you can't use the remapping function nor longress or shift logics to do your own mapping when you have a limited set of buttons. In 'raw' mode, all you really need to define is the mapping between the gpio and the button. As far as LMS is concerned, any other option in these JSON payloads does not matter. Now, when you use BT or AirPlay, the full JSON construct described above fully applies, so the shift, longpress, remapping options still work.
|
The benefit of the "raw" mode is that you can build a player which is as close as possible to a Boom (e.g.) but you can't use the remapping function nor longress or shift logics to do your own mapping when you have a limited set of buttons. In 'raw' mode, all you really need to define is the mapping between the gpio and the button. As far as LMS is concerned, any other option in these JSON payloads does not matter. Now, when you use BT or AirPlay, the full JSON construct described above fully applies, so the shift, longpress, remapping options still work.
|
||||||
|
|
||||||
@@ -375,8 +432,10 @@ The benefit of the "raw" mode is that you can build a player which is as close a
|
|||||||
There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctrls_raw" to change that option
|
There is no good or bad option, it's your choice. Use the NVS parameter "lms_ctrls_raw" to change that option
|
||||||
|
|
||||||
**Note that gpio 36 and 39 are input only and cannot use interrupt. When using them for a button, a 100ms polling is started which is expensive. Long press is also likely to not work very well**
|
**Note that gpio 36 and 39 are input only and cannot use interrupt. When using them for a button, a 100ms polling is started which is expensive. Long press is also likely to not work very well**
|
||||||
### Ethernet (coming soon)
|
### Ethernet
|
||||||
Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a Microchip LAN8720 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or Davicom DM9051 over SPI like [that](https://www.amazon.com/dp/B08JLFWX9Z).
|
Wired ethernet is supported by esp32 with various options but squeezelite is only supporting a Microchip LAN8720 with a RMII interface like [this](https://www.aliexpress.com/item/32858432526.html) or SPI-ethernet bridges like Davicom DM9051 [that](https://www.amazon.com/dp/B08JLFWX9Z) or W5500 like [this](https://www.aliexpress.com/item/32312441357.html).
|
||||||
|
|
||||||
|
**Note:** Touch buttons that can be find on some board like the LyraT V4.3 are not supported currently.
|
||||||
|
|
||||||
#### RMII (LAN8720)
|
#### RMII (LAN8720)
|
||||||
- RMII PHY wiring is fixed and can not be changed
|
- RMII PHY wiring is fixed and can not be changed
|
||||||
@@ -389,6 +448,7 @@ Wired ethernet is supported by esp32 with various options but squeezelite is onl
|
|||||||
| GPIO25 | RX0 | EMAC_RXD0 |
|
| GPIO25 | RX0 | EMAC_RXD0 |
|
||||||
| GPIO26 | RX1 | EMAC_RXD1 |
|
| GPIO26 | RX1 | EMAC_RXD1 |
|
||||||
| GPIO27 | CRS_DV | EMAC_RX_DRV |
|
| GPIO27 | CRS_DV | EMAC_RX_DRV |
|
||||||
|
| GPIO0 | REF_CLK | 50MHz clock |
|
||||||
|
|
||||||
- SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "eth_config" parameter with the following syntax:
|
- SMI (Serial Management Interface) wiring is not fixed and you can change it either in the configuration or using "eth_config" parameter with the following syntax:
|
||||||
```
|
```
|
||||||
@@ -398,30 +458,31 @@ Connecting a reset pin for the LAN8720 is optional but recommended to avoid that
|
|||||||
- Clock
|
- Clock
|
||||||
|
|
||||||
The APLL of the esp32 is required for the audio codec, so we **need** a LAN8720 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this.
|
The APLL of the esp32 is required for the audio codec, so we **need** a LAN8720 that provides a 50MHz clock. That clock **must** be connected to GPIO0, there is no alternative. This means that if your DAC requires an MCLK, then you are out of luck. It is not possible to have both to work together. There might be some workaround using CLK_OUT2 and GPIO3, but I don't have time for this.
|
||||||
#### SPI (DM9051)
|
#### SPI (DM9051 or W5500)
|
||||||
Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. Another benefit is that the SPI bus can be shared with the display, but it's also possible to have a dedicated SPI interface. The esp32 has 4 SPI sub-systems, one is unaccessible so numbering is 0..2 and SPI0 is reserved for Flash/PSRAM. The "eth_config" parameter syntax becomes:
|
Ethernet over SPI is supported as well and requires less GPIOs but is obvsiously slower. SPI is the shared bus set with [spi_config](#spi). The "eth_config" parameter syntax becomes:
|
||||||
```
|
```
|
||||||
model=dm9051,cs=<gpio>,speed=<clk_in_Hz>,intr=<gpio>[,host=<-1|1|2>][,rst=<gpio>][,mosi=<gpio>,miso=<gpio>,clk=<gpio>]
|
model=dm9051|w5500,cs=<gpio>,speed=<clk_in_Hz>,intr=<gpio>[,rst=<gpio>]
|
||||||
```
|
```
|
||||||
- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config). The default "host" is 2 to avoid conflicting wiht default "spi_config" settings.
|
- To use the system SPI, shared with display (see spi_config) "host" must be set to -1. Any other value will reserve the SPI interface (careful of conflict with spi_config). The default "host" is 2 to avoid conflicting wiht default "spi_config" settings.
|
||||||
- When not using system SPI, "mosi" for data out, "miso" for data in and "clk" **must** be set
|
- When not using system SPI, "mosi" for data out, "miso" for data in and "clk" **must** be set
|
||||||
- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, which depends on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) for more details)
|
- The esp32 has a special I/O multiplexer for faster speed (up to 80 MHz) but that requires using specific GPIOs, which depends on SPI bus (See [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) for more details)
|
||||||
|
|
||||||
| Pin Name | SPI2 | SPI3 |
|
| Pin Name | SPI1 | SPI2 |
|
||||||
| -------- | ---- | ---- |
|
| -------- | ---- | ---- |
|
||||||
| CS0* | 15 | 5 |
|
| CS | 15 | 5 |
|
||||||
| SCLK | 14 | 18 |
|
| SCLK | 14 | 18 |
|
||||||
| MISO | 12 | 19 |
|
| MISO | 12 | 19 |
|
||||||
| MOSI | 13 | 23 |
|
| MOSI | 13 | 23 |
|
||||||
|
|
||||||
** THIS IS NOT AVAILABLE YET, SO MORE TO COME ON HOW TO USE WIRED ETHERNET***
|
|
||||||
### Battery / ADC
|
### Battery / ADC
|
||||||
The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. The "atten" value attenuates the input voltage to the ADC input (the read value maintains a 0-1V rage) where: 0=no attenuation(0..800mV), 1=2.5dB attenuation(0..1.1V), 2=6dB attenuation(0..1.35V), 3=11dB attenuation(0..2.6V). Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is
|
The NVS parameter "bat_config" sets the ADC1 channel used to measure battery/DC voltage. The "atten" value attenuates the input voltage to the ADC input (the read value maintains a 0-1V rage) where: 0=no attenuation(0..800mV), 1=2.5dB attenuation(0..1.1V), 2=6dB attenuation(0..1.35V), 3=11dB attenuation(0..2.6V). Scale is a float ratio applied to every sample of the 12 bits ADC. A measure is taken every 10s and an average is made every 5 minutes (not a sliding window). Syntax is
|
||||||
```
|
```
|
||||||
channel=0..7,scale=<scale>,cells=<2|3>[,atten=<0|1|2|3>]
|
channel=0..7,scale=<scale>,cells=<2|3>[,atten=<0|1|2|3>]
|
||||||
```
|
```
|
||||||
NB: Set parameter to empty to disable battery reading. For well-known configuration, this is ignored (except for SqueezeAMP where number of cells is required)
|
NB: Set parameter to empty to disable battery reading. For well-known configuration, this is ignored (except for SqueezeAMP where number of cells is required)
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
## Setup WiFi
|
## Setup WiFi
|
||||||
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
|
- Boot the esp, look for a new wifi access point showing up and connect to it. Default build ssid and passwords are "squeezelite"/"squeezelite".
|
||||||
- Once connected, navigate to 192.168.4.1
|
- Once connected, navigate to 192.168.4.1
|
||||||
@@ -430,7 +491,6 @@ NB: Set parameter to empty to disable battery reading. For well-known configurat
|
|||||||
- Once connection is established, note down the address the device received; this is the address you will use to configure it going forward
|
- Once connection is established, note down the address the device received; this is the address you will use to configure it going forward
|
||||||
|
|
||||||
## Setup squeezelite command line (optional)
|
## Setup squeezelite command line (optional)
|
||||||
|
|
||||||
At this point, the device should have disabled its built-in access point and should be connected to a known WiFi network.
|
At this point, the device should have disabled its built-in access point and should be connected to a known WiFi network.
|
||||||
- navigate to the address that was noted in step #1
|
- navigate to the address that was noted in step #1
|
||||||
- Using the list of predefined options, choose the mode in which you want squeezelite to start
|
- Using the list of predefined options, choose the mode in which you want squeezelite to start
|
||||||
@@ -443,7 +503,6 @@ At this point, the device should have disabled its built-in access point and sho
|
|||||||
- You can enable accessto NVS parameters under 'credits'
|
- You can enable accessto NVS parameters under 'credits'
|
||||||
|
|
||||||
## Monitor
|
## Monitor
|
||||||
|
|
||||||
In addition of the esp-idf serial link monitor option, you can also enable a telnet server (see NVS parameters) where you'll have access to a ton of logs of what's happening inside the WROVER.
|
In addition of the esp-idf serial link monitor option, you can also enable a telnet server (see NVS parameters) where you'll have access to a ton of logs of what's happening inside the WROVER.
|
||||||
|
|
||||||
## Update Squeezelite
|
## Update Squeezelite
|
||||||
@@ -475,20 +534,23 @@ See squeezlite command line, but keys options are
|
|||||||
- r "<minrate>-<maxrate>"
|
- r "<minrate>-<maxrate>"
|
||||||
- C <sec> : set timeout to switch off amp gpio
|
- C <sec> : set timeout to switch off amp gpio
|
||||||
- W : activate WAV and AIFF header parsing
|
- W : activate WAV and AIFF header parsing
|
||||||
|
|
||||||
# Building everything yourself
|
# Building everything yourself
|
||||||
|
|
||||||
## Setting up ESP-IDF
|
## Setting up ESP-IDF
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
A simple alternative to building the project's binaries is to leverage the same docker image that is being used on the GitHub Actions to build our releases. The instructions below assume that you have cloned the squeezelite-esp32 code that you want to build locally and that you have opened a command line/bash session in the folder that contains the code.
|
A simple alternative to building the project's binaries is to leverage the same docker image that is being used on the GitHub Actions to build our releases. The instructions below assume that you have cloned the squeezelite-esp32 code that you want to build locally and that you have opened a command line/bash session in the folder that contains the code.
|
||||||
Pull the most recent docker image for the environment:
|
Pull the most recent docker image for the environment:
|
||||||
```
|
```
|
||||||
docker pull sle118/squeezelite-esp32-idfv4-master
|
docker pull sle118/squeezelite-esp32-idfv43
|
||||||
```
|
```
|
||||||
Then run the container interactively :
|
Then run the container interactively :
|
||||||
```
|
```
|
||||||
for windows:
|
for windows:
|
||||||
docker run -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv4-master
|
docker run -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv43
|
||||||
for linux:
|
for linux:
|
||||||
docker run -it -v `pwd`:/workspace/squeezelite-esp32 sle118/squeezelite-esp32-idfv4-master
|
docker run -it -v `pwd`:/workspace/squeezelite-esp32 sle118/squeezelite-esp32-idfv43
|
||||||
```
|
```
|
||||||
The above command will mount this repo into the docker container and start a bash terminal. From there, simply run idf.py build to build, etc. Note that at the time of writing these lines, flashing is not possible for docker running under windows https://github.com/docker/for-win/issues/1018.
|
The above command will mount this repo into the docker container and start a bash terminal. From there, simply run idf.py build to build, etc. Note that at the time of writing these lines, flashing is not possible for docker running under windows https://github.com/docker/for-win/issues/1018.
|
||||||
|
|
||||||
@@ -511,6 +573,7 @@ Use `idf.py monitor` to monitor the application (see esp-idf documentation)
|
|||||||
Note: You can use `idf.py build -DDEPTH=32` to build the 32 bits version and add the `-DVERSION=<your_version>` to add a custom version name (it will be 0.0-<your_version>). If you want to change the whole version string, see squeezelite.h. You can also disable the SBR extension of AAC codecs as it consumes a lot of CPU and might overload the esp32. Use `-DAAC_DISABLE_SBR=1` for that
|
Note: You can use `idf.py build -DDEPTH=32` to build the 32 bits version and add the `-DVERSION=<your_version>` to add a custom version name (it will be 0.0-<your_version>). If you want to change the whole version string, see squeezelite.h. You can also disable the SBR extension of AAC codecs as it consumes a lot of CPU and might overload the esp32. Use `-DAAC_DISABLE_SBR=1` for that
|
||||||
|
|
||||||
If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location: `git submodule update --init --recursive`
|
If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location: `git submodule update --init --recursive`
|
||||||
|
|
||||||
### Rebuild codecs (highly recommended to NOT try that)
|
### Rebuild codecs (highly recommended to NOT try that)
|
||||||
- for codecs libraries, add -mlongcalls if you want to rebuild them, but you should not (use the provided ones in codecs/lib). if you really want to rebuild them, open an issue
|
- for codecs libraries, add -mlongcalls if you want to rebuild them, but you should not (use the provided ones in codecs/lib). if you really want to rebuild them, open an issue
|
||||||
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
|
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ static void squeezelite_thread(void *arg){
|
|||||||
|
|
||||||
cmd_send_messaging("cfg-audio-tmpl",ret > 1 ? MESSAGING_ERROR : MESSAGING_WARNING,"squeezelite exited with error code %d\n", ret);
|
cmd_send_messaging("cfg-audio-tmpl",ret > 1 ? MESSAGING_ERROR : MESSAGING_WARNING,"squeezelite exited with error code %d\n", ret);
|
||||||
|
|
||||||
if (ret == 1) {
|
if (ret <= 1) {
|
||||||
int wait = 60;
|
int wait = 60;
|
||||||
wait_for_commit();
|
wait_for_commit();
|
||||||
cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Rebooting in %d sec\n", wait);
|
cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Rebooting in %d sec\n", wait);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ set(BELL_DISABLE_SINKS ON)
|
|||||||
set(BELL_DISABLE_FMT ON)
|
set(BELL_DISABLE_FMT ON)
|
||||||
set(BELL_DISABLE_REGEX ON)
|
set(BELL_DISABLE_REGEX ON)
|
||||||
set(BELL_ONLY_CJSON ON)
|
set(BELL_ONLY_CJSON ON)
|
||||||
|
set(BELL_DISABLE_MQTT ON)
|
||||||
|
set(BELL_DISABLE_WEBSERVER ON)
|
||||||
set(CSPOT_TARGET_ESP32 ON)
|
set(CSPOT_TARGET_ESP32 ON)
|
||||||
|
|
||||||
# because CMake is so broken, the cache set below overrides a normal "set" for the first build
|
# because CMake is so broken, the cache set below overrides a normal "set" for the first build
|
||||||
|
|||||||
@@ -16,7 +16,10 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <ApResolve.h>
|
#include <ApResolve.h>
|
||||||
|
|
||||||
|
#include "BellTask.h"
|
||||||
#include "MDNSService.h"
|
#include "MDNSService.h"
|
||||||
|
#include "TrackPlayer.h"
|
||||||
|
#include "CSpotContext.h"
|
||||||
#include "SpircHandler.h"
|
#include "SpircHandler.h"
|
||||||
#include "LoginBlob.h"
|
#include "LoginBlob.h"
|
||||||
#include "CentralAudioBuffer.h"
|
#include "CentralAudioBuffer.h"
|
||||||
@@ -31,70 +34,6 @@
|
|||||||
|
|
||||||
static class cspotPlayer *player;
|
static class cspotPlayer *player;
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Chunk manager class (task)
|
|
||||||
*/
|
|
||||||
|
|
||||||
class chunkManager : public bell::Task {
|
|
||||||
public:
|
|
||||||
std::atomic<bool> isRunning = true;
|
|
||||||
std::atomic<bool> isPaused = true;
|
|
||||||
chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer, std::function<void()> trackHandler,
|
|
||||||
std::function<void(const uint8_t*, size_t)> dataHandler);
|
|
||||||
void teardown();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer;
|
|
||||||
std::function<void()> trackHandler;
|
|
||||||
std::function<void(const uint8_t*, size_t)> dataHandler;
|
|
||||||
std::mutex runningMutex;
|
|
||||||
|
|
||||||
void runTask() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
chunkManager::chunkManager(std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer,
|
|
||||||
std::function<void()> trackHandler, std::function<void(const uint8_t*, size_t)> dataHandler)
|
|
||||||
: bell::Task("chunker", 4 * 1024, 0, 0) {
|
|
||||||
this->centralAudioBuffer = centralAudioBuffer;
|
|
||||||
this->trackHandler = trackHandler;
|
|
||||||
this->dataHandler = dataHandler;
|
|
||||||
startTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkManager::teardown() {
|
|
||||||
isRunning = false;
|
|
||||||
std::scoped_lock lock(runningMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkManager::runTask() {
|
|
||||||
std::scoped_lock lock(runningMutex);
|
|
||||||
size_t lastHash = 0;
|
|
||||||
|
|
||||||
while (isRunning) {
|
|
||||||
|
|
||||||
if (isPaused) {
|
|
||||||
BELL_SLEEP_MS(100);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto chunk = centralAudioBuffer->readChunk();
|
|
||||||
|
|
||||||
if (!chunk || chunk->pcmSize == 0) {
|
|
||||||
BELL_SLEEP_MS(50);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// receiving first chunk of new track from Spotify server
|
|
||||||
if (lastHash != chunk->trackHash) {
|
|
||||||
CSPOT_LOG(info, "hash update %x => %x", lastHash, chunk->trackHash);
|
|
||||||
lastHash = chunk->trackHash;
|
|
||||||
trackHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
dataHandler(chunk->pcmData, chunk->pcmSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Player's main class & task
|
* Player's main class & task
|
||||||
*/
|
*/
|
||||||
@@ -103,20 +42,21 @@ class cspotPlayer : public bell::Task {
|
|||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
bell::WrappedSemaphore clientConnected;
|
bell::WrappedSemaphore clientConnected;
|
||||||
std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer;
|
std::atomic<bool> isPaused, isConnected;
|
||||||
|
|
||||||
int startOffset, volume = 0, bitrate = 160;
|
int startOffset, volume = 0, bitrate = 160;
|
||||||
httpd_handle_t serverHandle;
|
httpd_handle_t serverHandle;
|
||||||
int serverPort;
|
int serverPort;
|
||||||
cspot_cmd_cb_t cmdHandler;
|
cspot_cmd_cb_t cmdHandler;
|
||||||
cspot_data_cb_t dataHandler;
|
cspot_data_cb_t dataHandler;
|
||||||
|
std::string lastTrackId;
|
||||||
|
|
||||||
std::shared_ptr<cspot::LoginBlob> blob;
|
std::shared_ptr<cspot::LoginBlob> blob;
|
||||||
std::unique_ptr<cspot::SpircHandler> spirc;
|
std::unique_ptr<cspot::SpircHandler> spirc;
|
||||||
std::unique_ptr<chunkManager> chunker;
|
|
||||||
|
|
||||||
void eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event);
|
void eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event);
|
||||||
void trackHandler(void);
|
void trackHandler(void);
|
||||||
|
size_t pcmWrite(uint8_t *pcm, size_t bytes, std::string_view trackId);
|
||||||
|
|
||||||
void runTask();
|
void runTask();
|
||||||
|
|
||||||
@@ -145,6 +85,17 @@ cspotPlayer::cspotPlayer(const char* name, httpd_handle_t server, int port, cspo
|
|||||||
if (bitrate != 96 && bitrate != 160 && bitrate != 320) bitrate = 160;
|
if (bitrate != 96 && bitrate != 160 && bitrate != 320) bitrate = 160;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t cspotPlayer::pcmWrite(uint8_t *pcm, size_t bytes, std::string_view trackId) {
|
||||||
|
if (lastTrackId != trackId) {
|
||||||
|
CSPOT_LOG(info, "new track started <%s> => <%s>", lastTrackId.c_str(), trackId.data());
|
||||||
|
lastTrackId = trackId;
|
||||||
|
trackHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
dataHandler(pcm, bytes);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static esp_err_t handleGET(httpd_req_t *request) {
|
static esp_err_t handleGET(httpd_req_t *request) {
|
||||||
return player->handleGET(request);
|
return player->handleGET(request);
|
||||||
@@ -223,8 +174,7 @@ esp_err_t cspotPlayer::handlePOST(httpd_req_t *request) {
|
|||||||
void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event) {
|
void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event) {
|
||||||
switch (event->eventType) {
|
switch (event->eventType) {
|
||||||
case cspot::SpircHandler::EventType::PLAYBACK_START: {
|
case cspot::SpircHandler::EventType::PLAYBACK_START: {
|
||||||
centralAudioBuffer->clearBuffer();
|
lastTrackId.clear();
|
||||||
|
|
||||||
// we are not playing anymore
|
// we are not playing anymore
|
||||||
trackStatus = TRACK_INIT;
|
trackStatus = TRACK_INIT;
|
||||||
// memorize position for when track's beginning will be detected
|
// memorize position for when track's beginning will be detected
|
||||||
@@ -237,13 +187,12 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cspot::SpircHandler::EventType::PLAY_PAUSE: {
|
case cspot::SpircHandler::EventType::PLAY_PAUSE: {
|
||||||
bool pause = std::get<bool>(event->data);
|
isPaused = std::get<bool>(event->data);
|
||||||
cmdHandler(pause ? CSPOT_PAUSE : CSPOT_PLAY);
|
cmdHandler(isPaused ? CSPOT_PAUSE : CSPOT_PLAY);
|
||||||
chunker->isPaused = pause;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cspot::SpircHandler::EventType::TRACK_INFO: {
|
case cspot::SpircHandler::EventType::TRACK_INFO: {
|
||||||
auto trackInfo = std::get<cspot::CDNTrackStream::TrackInfo>(event->data);
|
auto trackInfo = std::get<cspot::TrackInfo>(event->data);
|
||||||
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
|
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
|
||||||
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
|
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
|
||||||
spirc->updatePositionMs(startOffset);
|
spirc->updatePositionMs(startOffset);
|
||||||
@@ -254,17 +203,14 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
|
|||||||
case cspot::SpircHandler::EventType::PREV:
|
case cspot::SpircHandler::EventType::PREV:
|
||||||
case cspot::SpircHandler::EventType::FLUSH: {
|
case cspot::SpircHandler::EventType::FLUSH: {
|
||||||
// FLUSH is sent when there is no next, just clean everything
|
// FLUSH is sent when there is no next, just clean everything
|
||||||
centralAudioBuffer->clearBuffer();
|
|
||||||
cmdHandler(CSPOT_FLUSH);
|
cmdHandler(CSPOT_FLUSH);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cspot::SpircHandler::EventType::DISC:
|
case cspot::SpircHandler::EventType::DISC:
|
||||||
centralAudioBuffer->clearBuffer();
|
|
||||||
cmdHandler(CSPOT_DISC);
|
cmdHandler(CSPOT_DISC);
|
||||||
chunker->teardown();
|
isConnected = false;
|
||||||
break;
|
break;
|
||||||
case cspot::SpircHandler::EventType::SEEK: {
|
case cspot::SpircHandler::EventType::SEEK: {
|
||||||
centralAudioBuffer->clearBuffer();
|
|
||||||
cmdHandler(CSPOT_SEEK, std::get<int>(event->data));
|
cmdHandler(CSPOT_SEEK, std::get<int>(event->data));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -283,10 +229,9 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
|
|||||||
|
|
||||||
void cspotPlayer::trackHandler(void) {
|
void cspotPlayer::trackHandler(void) {
|
||||||
// this is just informative
|
// this is just informative
|
||||||
auto trackInfo = spirc->getTrackPlayer()->getCurrentTrackInfo();
|
|
||||||
uint32_t remains;
|
uint32_t remains;
|
||||||
cmdHandler(CSPOT_QUERY_REMAINING, &remains);
|
cmdHandler(CSPOT_QUERY_REMAINING, &remains);
|
||||||
CSPOT_LOG(info, "next track <%s> will play in %d ms", trackInfo.name.c_str(), remains);
|
CSPOT_LOG(info, "next track will play in %d ms", remains);
|
||||||
|
|
||||||
// inform sink of track beginning
|
// inform sink of track beginning
|
||||||
trackStatus = TRACK_NOTIFY;
|
trackStatus = TRACK_NOTIFY;
|
||||||
@@ -307,7 +252,8 @@ void cspotPlayer::command(cspot_event_t event) {
|
|||||||
break;
|
break;
|
||||||
// setPause comes back through cspot::event with PLAY/PAUSE
|
// setPause comes back through cspot::event with PLAY/PAUSE
|
||||||
case CSPOT_TOGGLE:
|
case CSPOT_TOGGLE:
|
||||||
spirc->setPause(!chunker->isPaused);
|
isPaused = !isPaused;
|
||||||
|
spirc->setPause(isPaused);
|
||||||
break;
|
break;
|
||||||
case CSPOT_STOP:
|
case CSPOT_STOP:
|
||||||
case CSPOT_PAUSE:
|
case CSPOT_PAUSE:
|
||||||
@@ -316,12 +262,11 @@ void cspotPlayer::command(cspot_event_t event) {
|
|||||||
case CSPOT_PLAY:
|
case CSPOT_PLAY:
|
||||||
spirc->setPause(false);
|
spirc->setPause(false);
|
||||||
break;
|
break;
|
||||||
// calling spirc->disconnect() might have been logical but it does not
|
/* Calling spirc->disconnect() might have been logical but it does not
|
||||||
// generate any cspot::event, so we need to manually force exiting player
|
* generate any cspot::event */
|
||||||
// loop through chunker which will eventually do the disconnect
|
|
||||||
case CSPOT_DISC:
|
case CSPOT_DISC:
|
||||||
cmdHandler(CSPOT_DISC);
|
cmdHandler(CSPOT_DISC);
|
||||||
chunker->teardown();
|
isConnected = false;
|
||||||
break;
|
break;
|
||||||
// spirc->setRemoteVolume does not generate a cspot::event so call cmdHandler
|
// spirc->setRemoteVolume does not generate a cspot::event so call cmdHandler
|
||||||
case CSPOT_VOLUME_UP:
|
case CSPOT_VOLUME_UP:
|
||||||
@@ -369,7 +314,6 @@ void cspotPlayer::runTask() {
|
|||||||
|
|
||||||
CSPOT_LOG(info, "Spotify client connected for %s", name.c_str());
|
CSPOT_LOG(info, "Spotify client connected for %s", name.c_str());
|
||||||
|
|
||||||
centralAudioBuffer = std::make_shared<bell::CentralAudioBuffer>(32);
|
|
||||||
auto ctx = cspot::Context::createFromBlob(blob);
|
auto ctx = cspot::Context::createFromBlob(blob);
|
||||||
|
|
||||||
if (bitrate == 320) ctx->config.audioFormat = AudioFormat_OGG_VORBIS_320;
|
if (bitrate == 320) ctx->config.audioFormat = AudioFormat_OGG_VORBIS_320;
|
||||||
@@ -382,11 +326,12 @@ void cspotPlayer::runTask() {
|
|||||||
// Auth successful
|
// Auth successful
|
||||||
if (token.size() > 0) {
|
if (token.size() > 0) {
|
||||||
spirc = std::make_unique<cspot::SpircHandler>(ctx);
|
spirc = std::make_unique<cspot::SpircHandler>(ctx);
|
||||||
|
isConnected = true;
|
||||||
|
|
||||||
// set call back to calculate a hash on trackId
|
// set call back to calculate a hash on trackId
|
||||||
spirc->getTrackPlayer()->setDataCallback(
|
spirc->getTrackPlayer()->setDataCallback(
|
||||||
[this](uint8_t* data, size_t bytes, std::string_view trackId, size_t sequence) {
|
[this](uint8_t* data, size_t bytes, std::string_view trackId) {
|
||||||
return centralAudioBuffer->writePCM(data, bytes, sequence);
|
return pcmWrite(data, bytes, trackId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// set event (PLAY, VOLUME...) handler
|
// set event (PLAY, VOLUME...) handler
|
||||||
@@ -398,20 +343,11 @@ void cspotPlayer::runTask() {
|
|||||||
// Start handling mercury messages
|
// Start handling mercury messages
|
||||||
ctx->session->startTask();
|
ctx->session->startTask();
|
||||||
|
|
||||||
// Create a player, pass the tack handler
|
|
||||||
chunker = std::make_unique<chunkManager>(centralAudioBuffer,
|
|
||||||
[this](void) {
|
|
||||||
return trackHandler();
|
|
||||||
},
|
|
||||||
[this](const uint8_t* data, size_t bytes) {
|
|
||||||
return dataHandler(data, bytes);
|
|
||||||
});
|
|
||||||
|
|
||||||
// set volume at connection
|
// set volume at connection
|
||||||
cmdHandler(CSPOT_VOLUME, volume);
|
cmdHandler(CSPOT_VOLUME, volume);
|
||||||
|
|
||||||
// exit when player has stopped (received a DISC)
|
// exit when player has stopped (received a DISC)
|
||||||
while (chunker->isRunning) {
|
while (isConnected) {
|
||||||
ctx->session->handlePacket();
|
ctx->session->handlePacket();
|
||||||
|
|
||||||
// low-accuracy polling events
|
// low-accuracy polling events
|
||||||
@@ -444,7 +380,6 @@ void cspotPlayer::runTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we want to release memory ASAP and for sure
|
// we want to release memory ASAP and for sure
|
||||||
centralAudioBuffer.reset();
|
|
||||||
ctx.reset();
|
ctx.reset();
|
||||||
token.clear();
|
token.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
|
|
||||||
CompileFlags:
|
CompileFlags:
|
||||||
CompilationDatabase: example/build # Search build/ directory for compile_commands.json
|
CompilationDatabase: example/build # Search build/ directory for compile_commands.json
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ project(bell)
|
|||||||
option(BELL_DISABLE_CODECS "Disable the entire audio codec wrapper" OFF)
|
option(BELL_DISABLE_CODECS "Disable the entire audio codec wrapper" OFF)
|
||||||
option(BELL_CODEC_AAC "Support libhelix-aac codec" ON)
|
option(BELL_CODEC_AAC "Support libhelix-aac codec" ON)
|
||||||
option(BELL_CODEC_MP3 "Support libhelix-mp3 codec" ON)
|
option(BELL_CODEC_MP3 "Support libhelix-mp3 codec" ON)
|
||||||
|
option(BELL_DISABLE_MQTT "Disable the built-in MQTT wrapper" OFF)
|
||||||
|
option(BELL_DISABLE_WEBSERVER "Disable the built-in Web server" OFF)
|
||||||
option(BELL_CODEC_VORBIS "Support tremor Vorbis codec" ON)
|
option(BELL_CODEC_VORBIS "Support tremor Vorbis codec" ON)
|
||||||
option(BELL_CODEC_ALAC "Support Apple ALAC codec" ON)
|
option(BELL_CODEC_ALAC "Support Apple ALAC codec" ON)
|
||||||
option(BELL_CODEC_OPUS "Support Opus codec" ON)
|
option(BELL_CODEC_OPUS "Support Opus codec" ON)
|
||||||
@@ -63,13 +65,15 @@ endif()
|
|||||||
|
|
||||||
message(STATUS " Use cJSON only: ${BELL_ONLY_CJSON}")
|
message(STATUS " Use cJSON only: ${BELL_ONLY_CJSON}")
|
||||||
message(STATUS " Disable Fmt: ${BELL_DISABLE_FMT}")
|
message(STATUS " Disable Fmt: ${BELL_DISABLE_FMT}")
|
||||||
|
message(STATUS " Disable Mqtt: ${BELL_DISABLE_MQTT}")
|
||||||
message(STATUS " Disable Regex: ${BELL_DISABLE_REGEX}")
|
message(STATUS " Disable Regex: ${BELL_DISABLE_REGEX}")
|
||||||
|
message(STATUS " Disable Web server: ${BELL_DISABLE_WEBSERVER}")
|
||||||
|
|
||||||
# Include nanoPB library
|
# Include nanoPB library
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external/nanopb/extra")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external/nanopb/extra")
|
||||||
find_package(Nanopb REQUIRED)
|
find_package(Nanopb REQUIRED)
|
||||||
message(${NANOPB_INCLUDE_DIRS})
|
message(${NANOPB_INCLUDE_DIRS})
|
||||||
list(APPEND EXTRA_INCLUDES ${NANOPB_INCLUDE_DIRS})
|
list(APPEND EXTERNAL_INCLUDES ${NANOPB_INCLUDE_DIRS})
|
||||||
|
|
||||||
# CMake options
|
# CMake options
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
@@ -84,7 +88,7 @@ set(IO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/main/io")
|
|||||||
set(PLATFORM_DIR "${CMAKE_CURRENT_SOURCE_DIR}/main/platform")
|
set(PLATFORM_DIR "${CMAKE_CURRENT_SOURCE_DIR}/main/platform")
|
||||||
set(UTILITIES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/main/utilities")
|
set(UTILITIES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/main/utilities")
|
||||||
|
|
||||||
add_definitions("-DUSE_DEFAULT_STDLIB=1")
|
add_definitions("-DUSE_DEFAULT_STDLIB=1 -DTARGET_OS_IPHONE=0")
|
||||||
|
|
||||||
# Main library sources
|
# Main library sources
|
||||||
file(GLOB SOURCES
|
file(GLOB SOURCES
|
||||||
@@ -93,8 +97,6 @@ file(GLOB SOURCES
|
|||||||
"main/io/*.cpp" "main/io/*.c"
|
"main/io/*.cpp" "main/io/*.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
list(REMOVE_ITEM SOURCES "${IO_DIR}/BellTar.cpp" "${IO_DIR}/BellHTTPServer.cpp")
|
|
||||||
|
|
||||||
list(APPEND EXTRA_INCLUDES "main/audio-codec/include")
|
list(APPEND EXTRA_INCLUDES "main/audio-codec/include")
|
||||||
list(APPEND EXTRA_INCLUDES "main/audio-dsp/include")
|
list(APPEND EXTRA_INCLUDES "main/audio-dsp/include")
|
||||||
list(APPEND EXTRA_INCLUDES "main/audio-sinks/include")
|
list(APPEND EXTRA_INCLUDES "main/audio-sinks/include")
|
||||||
@@ -111,7 +113,7 @@ endif()
|
|||||||
if(APPLE)
|
if(APPLE)
|
||||||
file(GLOB APPLE_PLATFORM_SOURCES "main/platform/apple/*.cpp" "main/platform/apple/*.c")
|
file(GLOB APPLE_PLATFORM_SOURCES "main/platform/apple/*.cpp" "main/platform/apple/*.c")
|
||||||
list(APPEND SOURCES ${APPLE_PLATFORM_SOURCES})
|
list(APPEND SOURCES ${APPLE_PLATFORM_SOURCES})
|
||||||
list(APPEND EXTRA_INCLUDES "/usr/local/opt/mbedtls@3/include")
|
list(APPEND EXTERNAL_INCLUDES "/usr/local/opt/mbedtls@3/include")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
@@ -122,7 +124,7 @@ endif()
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
file(GLOB WIN32_PLATFORM_SOURCES "main/platform/win32/*.cpp" "main/platform/win32/*.c")
|
file(GLOB WIN32_PLATFORM_SOURCES "main/platform/win32/*.cpp" "main/platform/win32/*.c")
|
||||||
list(APPEND SOURCES ${WIN32_PLATFORM_SOURCES})
|
list(APPEND SOURCES ${WIN32_PLATFORM_SOURCES})
|
||||||
list(APPEND EXTRA_INCLUDES "main/platform/win32")
|
list(APPEND EXTERNAL_INCLUDES "main/platform/win32")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# A hack to make Opus keep quiet
|
# A hack to make Opus keep quiet
|
||||||
@@ -139,7 +141,7 @@ if(ESP_PLATFORM)
|
|||||||
else()
|
else()
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(MbedTLS REQUIRED)
|
find_package(MbedTLS REQUIRED)
|
||||||
list(APPEND EXTRA_INCLUDES ${MBEDTLS_INCLUDE_DIRS})
|
list(APPEND EXTERNAL_INCLUDES ${MBEDTLS_INCLUDE_DIRS})
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
list(APPEND EXTRA_LIBS ${MBEDTLS_LIBRARIES} Threads::Threads)
|
list(APPEND EXTRA_LIBS ${MBEDTLS_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
@@ -149,6 +151,14 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (NOT BELL_DISABLE_MQTT)
|
||||||
|
file(GLOB MQTT_SOURCES "external/mqtt/*.c")
|
||||||
|
list(APPEND SOURCES ${MQTT_SOURCES})
|
||||||
|
list(APPEND EXTRA_INCLUDES "external/mqtt/include")
|
||||||
|
else()
|
||||||
|
list(REMOVE_ITEM SOURCES "${IO_DIR}/BellMQTTClient.cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT BELL_DISABLE_CODECS)
|
if(NOT BELL_DISABLE_CODECS)
|
||||||
file(GLOB EXTRA_SOURCES "main/audio-containers/*.cpp" "main/audio-codec/*.cpp" "main/audio-codec/*.c" "main/audio-dsp/*.cpp" "main/audio-dsp/*.c")
|
file(GLOB EXTRA_SOURCES "main/audio-containers/*.cpp" "main/audio-codec/*.cpp" "main/audio-codec/*.c" "main/audio-dsp/*.cpp" "main/audio-dsp/*.c")
|
||||||
|
|
||||||
@@ -162,7 +172,7 @@ if(NOT BELL_DISABLE_CODECS)
|
|||||||
if(BELL_CODEC_AAC)
|
if(BELL_CODEC_AAC)
|
||||||
file(GLOB LIBHELIX_AAC_SOURCES "external/libhelix-aac/*.c")
|
file(GLOB LIBHELIX_AAC_SOURCES "external/libhelix-aac/*.c")
|
||||||
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_AAC_SOURCES})
|
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_AAC_SOURCES})
|
||||||
list(APPEND EXTRA_INCLUDES "external/libhelix-aac")
|
list(APPEND EXTERNAL_INCLUDES "external/libhelix-aac")
|
||||||
list(APPEND SOURCES "${AUDIO_CODEC_DIR}/AACDecoder.cpp")
|
list(APPEND SOURCES "${AUDIO_CODEC_DIR}/AACDecoder.cpp")
|
||||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_AAC")
|
list(APPEND CODEC_FLAGS "-DBELL_CODEC_AAC")
|
||||||
endif()
|
endif()
|
||||||
@@ -171,7 +181,7 @@ if(NOT BELL_DISABLE_CODECS)
|
|||||||
if(BELL_CODEC_MP3)
|
if(BELL_CODEC_MP3)
|
||||||
file(GLOB LIBHELIX_MP3_SOURCES "external/libhelix-mp3/*.c")
|
file(GLOB LIBHELIX_MP3_SOURCES "external/libhelix-mp3/*.c")
|
||||||
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_MP3_SOURCES})
|
list(APPEND LIBHELIX_SOURCES ${LIBHELIX_MP3_SOURCES})
|
||||||
list(APPEND EXTRA_INCLUDES "external/libhelix-mp3")
|
list(APPEND EXTERNAL_INCLUDES "external/libhelix-mp3")
|
||||||
list(APPEND SOURCES "${AUDIO_CODEC_DIR}/MP3Decoder.cpp")
|
list(APPEND SOURCES "${AUDIO_CODEC_DIR}/MP3Decoder.cpp")
|
||||||
list(APPEND CODEC_FLAGS "-DBELL_CODEC_MP3")
|
list(APPEND CODEC_FLAGS "-DBELL_CODEC_MP3")
|
||||||
endif()
|
endif()
|
||||||
@@ -230,7 +240,7 @@ else()
|
|||||||
file(GLOB TREMOR_SOURCES "external/tremor/*.c")
|
file(GLOB TREMOR_SOURCES "external/tremor/*.c")
|
||||||
list(REMOVE_ITEM TREMOR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/external/tremor/ivorbisfile_example.c")
|
list(REMOVE_ITEM TREMOR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/external/tremor/ivorbisfile_example.c")
|
||||||
list(APPEND SOURCES ${TREMOR_SOURCES})
|
list(APPEND SOURCES ${TREMOR_SOURCES})
|
||||||
list(APPEND EXTRA_INCLUDES "external/tremor")
|
list(APPEND EXTERNAL_INCLUDES "external/tremor")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BELL_DISABLE_SINKS)
|
if(NOT BELL_DISABLE_SINKS)
|
||||||
@@ -247,7 +257,7 @@ if(NOT BELL_DISABLE_SINKS)
|
|||||||
# Find ALSA if required, else remove the sink
|
# Find ALSA if required, else remove the sink
|
||||||
if(BELL_SINK_ALSA)
|
if(BELL_SINK_ALSA)
|
||||||
find_package(ALSA REQUIRED)
|
find_package(ALSA REQUIRED)
|
||||||
list(APPEND EXTRA_INCLUDES ${ALSA_INCLUDE_DIRS})
|
list(APPEND EXTERNAL_INCLUDES ${ALSA_INCLUDE_DIRS})
|
||||||
list(APPEND EXTRA_LIBS ${ALSA_LIBRARIES})
|
list(APPEND EXTRA_LIBS ${ALSA_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_SINKS_DIR}/unix/ALSAAudioSink.cpp")
|
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_SINKS_DIR}/unix/ALSAAudioSink.cpp")
|
||||||
@@ -256,7 +266,7 @@ if(NOT BELL_DISABLE_SINKS)
|
|||||||
# Find PortAudio if required, else remove the sink
|
# Find PortAudio if required, else remove the sink
|
||||||
if(BELL_SINK_PORTAUDIO)
|
if(BELL_SINK_PORTAUDIO)
|
||||||
find_package(Portaudio REQUIRED)
|
find_package(Portaudio REQUIRED)
|
||||||
list(APPEND EXTRA_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
|
list(APPEND EXTERNAL_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
|
||||||
list(APPEND EXTRA_LIBS ${PORTAUDIO_LIBRARIES})
|
list(APPEND EXTRA_LIBS ${PORTAUDIO_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_SINKS_DIR}/unix/PortAudioSink.cpp")
|
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_SINKS_DIR}/unix/PortAudioSink.cpp")
|
||||||
@@ -266,6 +276,7 @@ if(NOT BELL_DISABLE_SINKS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT BELL_ONLY_CJSON)
|
if(NOT BELL_ONLY_CJSON)
|
||||||
|
set(JSON_SystemInclude ON CACHE INTERNAL "")
|
||||||
add_subdirectory(external/nlohmann_json)
|
add_subdirectory(external/nlohmann_json)
|
||||||
list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json)
|
list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json)
|
||||||
endif()
|
endif()
|
||||||
@@ -274,22 +285,25 @@ if(BELL_EXTERNAL_CJSON)
|
|||||||
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
|
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
|
||||||
else()
|
else()
|
||||||
list(APPEND SOURCES "external/cJSON/cJSON.c")
|
list(APPEND SOURCES "external/cJSON/cJSON.c")
|
||||||
list(APPEND EXTRA_INCLUDES "external/cJSON")
|
list(APPEND EXTERNAL_INCLUDES "external/cJSON")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT BELL_DISABLE_FMT)
|
if (NOT BELL_DISABLE_FMT)
|
||||||
list(APPEND EXTRA_INCLUDES "external/fmt/include")
|
list(APPEND EXTERNAL_INCLUDES "external/fmt/include")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32 OR UNIX)
|
if(WIN32 OR UNIX)
|
||||||
list(APPEND SOURCES "external/mdnssvc/mdns.c" "external/mdnssvc/mdnsd.c")
|
list(APPEND SOURCES "external/mdnssvc/mdns.c" "external/mdnssvc/mdnsd.c")
|
||||||
list(APPEND EXTRA_INCLUDES "external/mdnssvc")
|
list(APPEND EXTERNAL_INCLUDES "external/mdnssvc")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# file(GLOB CIVET_SRC "external/civetweb/*.c" "external/civetweb/*.inl" "external/civetweb/*.cpp")
|
if(NOT BELL_DISABLE_WEBSERVER)
|
||||||
|
file(GLOB CIVET_SRC "external/civetweb/*.c" "external/civetweb/*.inl" "external/civetweb/*.cpp")
|
||||||
# list(APPEND SOURCES ${CIVET_SRC})
|
list(APPEND SOURCES ${CIVET_SRC})
|
||||||
# list(APPEND EXTRA_INCLUDES "external/civetweb/include")
|
list(APPEND EXTRA_INCLUDES "external/civetweb/include")
|
||||||
|
else()
|
||||||
|
list(REMOVE_ITEM SOURCES "${IO_DIR}/BellHTTPServer.cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(bell STATIC ${SOURCES})
|
add_library(bell STATIC ${SOURCES})
|
||||||
|
|
||||||
@@ -305,6 +319,7 @@ endif()
|
|||||||
# PUBLIC to propagate esp-idf includes to bell dependents
|
# PUBLIC to propagate esp-idf includes to bell dependents
|
||||||
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
|
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
|
||||||
target_include_directories(bell PUBLIC ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(bell PUBLIC ${EXTRA_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_include_directories(bell SYSTEM PUBLIC ${EXTERNAL_INCLUDES})
|
||||||
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC FMT_HEADER_ONLY)
|
target_compile_definitions(bell PUBLIC PB_ENABLE_MALLOC FMT_HEADER_ONLY)
|
||||||
|
|
||||||
if(BELL_DISABLE_CODECS)
|
if(BELL_DISABLE_CODECS)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.18)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ ${CMAKE_CURRENT_BINARY_DIR}/bell)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ ${CMAKE_CURRENT_BINARY_DIR}/bell)
|
||||||
|
|
||||||
file(GLOB SOURCES "*.cpp")
|
file(GLOB SOURCES "*.cpp")
|
||||||
|
|||||||
@@ -1,26 +1,14 @@
|
|||||||
#include <memory.h>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <string>
|
||||||
#include "AudioCodecs.h"
|
#include <type_traits>
|
||||||
#include "AudioContainers.h"
|
|
||||||
#include "BellHTTPServer.h"
|
|
||||||
#include "BellTar.h"
|
|
||||||
#include "BellTask.h"
|
#include "BellTask.h"
|
||||||
#include "CentralAudioBuffer.h"
|
#include "CentralAudioBuffer.h"
|
||||||
#include "Compressor.h"
|
|
||||||
#include "DecoderGlobals.h"
|
|
||||||
#include "EncodedAudioStream.h"
|
|
||||||
#include "HTTPClient.h"
|
|
||||||
#include "PortAudioSink.h"
|
#include "PortAudioSink.h"
|
||||||
#define DEBUG_LEVEL 4
|
#include "StreamInfo.h"
|
||||||
#include "X509Bundle.h"
|
|
||||||
#include "mbedtls/debug.h"
|
|
||||||
|
|
||||||
|
#define DEBUG_LEVEL 4
|
||||||
#include <BellDSP.h>
|
#include <BellDSP.h>
|
||||||
#include <BellLogger.h>
|
#include <BellLogger.h>
|
||||||
|
|
||||||
@@ -58,13 +46,8 @@ class AudioPlayer : bell::Task {
|
|||||||
int main() {
|
int main() {
|
||||||
bell::setDefaultLogger();
|
bell::setDefaultLogger();
|
||||||
|
|
||||||
std::fstream file("system.tar", std::ios::in | std::ios::binary);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
std::cout << "file not open" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
BellTar::reader reader(file);
|
BELL_LOG(info, "cock", "Published?");
|
||||||
reader.extract_all_files("./dupa2");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,13 +118,16 @@
|
|||||||
#
|
#
|
||||||
#=============================================================================
|
#=============================================================================
|
||||||
|
|
||||||
|
|
||||||
function(NANOPB_GENERATE_CPP SRCS HDRS)
|
function(NANOPB_GENERATE_CPP SRCS HDRS)
|
||||||
cmake_parse_arguments(NANOPB_GENERATE_CPP "" "RELPATH" "" ${ARGN})
|
cmake_parse_arguments(NANOPB_GENERATE_CPP "" "RELPATH" "" ${ARGN})
|
||||||
if(NOT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS)
|
if(NOT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(CUSTOM_COMMAND_PREFIX call)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NANOPB_GENERATE_CPP_APPEND_PATH)
|
if(NANOPB_GENERATE_CPP_APPEND_PATH)
|
||||||
# Create an include path for each file specified
|
# Create an include path for each file specified
|
||||||
foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS})
|
foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS})
|
||||||
@@ -184,7 +187,7 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
|
|||||||
set(GENERATOR_CORE_PYTHON_SRC ${GENERATOR_CORE_PYTHON_SRC} ${output})
|
set(GENERATOR_CORE_PYTHON_SRC ${GENERATOR_CORE_PYTHON_SRC} ${output})
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${output}
|
OUTPUT ${output}
|
||||||
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
|
COMMAND ${CUSTOM_COMMAND_PREFIX} ${PROTOBUF_PROTOC_EXECUTABLE}
|
||||||
ARGS -I${GENERATOR_PATH}/proto
|
ARGS -I${GENERATOR_PATH}/proto
|
||||||
--python_out=${GENERATOR_CORE_DIR} ${ABS_FIL}
|
--python_out=${GENERATOR_CORE_DIR} ${ABS_FIL}
|
||||||
DEPENDS ${ABS_FIL}
|
DEPENDS ${ABS_FIL}
|
||||||
@@ -276,7 +279,7 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.c"
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.c"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.h"
|
"${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.h"
|
||||||
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
|
COMMAND ${CUSTOM_COMMAND_PREFIX} ${PROTOBUF_PROTOC_EXECUTABLE}
|
||||||
ARGS -I${GENERATOR_PATH} -I${GENERATOR_CORE_DIR}
|
ARGS -I${GENERATOR_PATH} -I${GENERATOR_CORE_DIR}
|
||||||
-I${CMAKE_CURRENT_BINARY_DIR} ${_nanopb_include_path}
|
-I${CMAKE_CURRENT_BINARY_DIR} ${_nanopb_include_path}
|
||||||
--plugin=protoc-gen-nanopb=${NANOPB_GENERATOR_PLUGIN}
|
--plugin=protoc-gen-nanopb=${NANOPB_GENERATOR_PLUGIN}
|
||||||
@@ -293,6 +296,10 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
|
|||||||
set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE)
|
set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE)
|
||||||
set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
|
set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
unset(CUSTOM_COMMAND_PREFIX)
|
||||||
|
endif()
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
#include "AACDecoder.h"
|
#include "AACDecoder.h"
|
||||||
#include <iostream>
|
|
||||||
|
#include <stdlib.h> // for free, malloc
|
||||||
|
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
|
||||||
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
#include "AudioCodecs.h"
|
#include "AudioCodecs.h"
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
#include <map> // for map, operator!=, map<>::iterator, map<>:...
|
||||||
#include <map>
|
#include <type_traits> // for remove_extent_t
|
||||||
|
|
||||||
|
#include "AudioContainer.h" // for AudioContainer
|
||||||
|
|
||||||
|
namespace bell {
|
||||||
|
class BaseCodec;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
#ifdef BELL_CODEC_AAC
|
#ifdef BELL_CODEC_AAC
|
||||||
#include "AACDecoder.h"
|
#include "AACDecoder.h" // for AACDecoder
|
||||||
|
|
||||||
static std::shared_ptr<AACDecoder> codecAac;
|
static std::shared_ptr<AACDecoder> codecAac;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BELL_CODEC_MP3
|
#ifdef BELL_CODEC_MP3
|
||||||
#include "MP3Decoder.h"
|
#include "MP3Decoder.h" // for MP3Decoder
|
||||||
|
|
||||||
static std::shared_ptr<MP3Decoder> codecMp3;
|
static std::shared_ptr<MP3Decoder> codecMp3;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BELL_CODEC_VORBIS
|
#ifdef BELL_CODEC_VORBIS
|
||||||
#include "VorbisDecoder.h"
|
#include "VorbisDecoder.h" // for VorbisDecoder
|
||||||
|
|
||||||
static std::shared_ptr<VorbisDecoder> codecVorbis;
|
static std::shared_ptr<VorbisDecoder> codecVorbis;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BELL_CODEC_OPUS
|
#ifdef BELL_CODEC_OPUS
|
||||||
#include "OPUSDecoder.h"
|
#include "OPUSDecoder.h" // for OPUSDecoder
|
||||||
|
|
||||||
static std::shared_ptr<OPUSDecoder> codecOpus;
|
static std::shared_ptr<OPUSDecoder> codecOpus;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "BaseCodec.h"
|
#include "BaseCodec.h"
|
||||||
#include <iostream>
|
|
||||||
|
#include "AudioContainer.h" // for AudioContainer
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
bell::DecodersInstance* bell::decodersInstance;
|
bell::DecodersInstance* bell::decodersInstance;
|
||||||
|
|
||||||
void bell::createDecoders()
|
void bell::createDecoders() {
|
||||||
{
|
|
||||||
bell::decodersInstance = new bell::DecodersInstance();
|
bell::decodersInstance = new bell::DecodersInstance();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
#include "MP3Decoder.h"
|
#include "MP3Decoder.h"
|
||||||
|
|
||||||
|
#include <stdlib.h> // for free, malloc
|
||||||
|
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
|
||||||
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
MP3Decoder::MP3Decoder() {
|
MP3Decoder::MP3Decoder() {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#include "OPUSDecoder.h"
|
#include "OPUSDecoder.h"
|
||||||
#include "opus.h"
|
|
||||||
|
#include <stdlib.h> // for free, malloc
|
||||||
|
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
#include "opus.h" // for opus_decoder_destroy, opus_decode, opus_decod...
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
#include "VorbisDecoder.h"
|
#include "VorbisDecoder.h"
|
||||||
#include "AudioCodecs.h"
|
|
||||||
|
#include <stdlib.h> // for free, malloc
|
||||||
|
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
#include "config_types.h" // for ogg_int16_t
|
||||||
|
|
||||||
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseCodec.h"
|
#include <stdint.h> // for uint8_t, uint32_t, int16_t
|
||||||
#include "aacdec.h"
|
|
||||||
|
#include "BaseCodec.h" // for BaseCodec
|
||||||
|
#include "aacdec.h" // for AACFrameInfo, HAACDecoder
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
|
||||||
class AACDecoder : public BaseCodec {
|
class AACDecoder : public BaseCodec {
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory> // for shared_ptr
|
||||||
#include "BaseCodec.h"
|
|
||||||
#include "AudioContainer.h"
|
#include "AudioContainer.h" // for AudioContainer
|
||||||
|
#include "BaseCodec.h" // for BaseCodec
|
||||||
|
#include "CodecType.h" // for AudioCodec
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
|
||||||
class AudioCodecs {
|
class AudioCodecs {
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<BaseCodec> getCodec(AudioCodec type);
|
static std::shared_ptr<BaseCodec> getCodec(AudioCodec type);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "AudioContainer.h"
|
#include <stdint.h> // for uint32_t, uint8_t
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
|
||||||
class BaseCodec {
|
class BaseCodec {
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -5,20 +5,16 @@
|
|||||||
#define AAC_READBUF_SIZE (4 * AAC_MAINBUF_SIZE * AAC_MAX_NCHANS)
|
#define AAC_READBUF_SIZE (4 * AAC_MAINBUF_SIZE * AAC_MAX_NCHANS)
|
||||||
#define MP3_READBUF_SIZE (2 * 1024);
|
#define MP3_READBUF_SIZE (2 * 1024);
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h> // for NULL
|
||||||
#include <stdlib.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "aacdec.h"
|
|
||||||
#include "mp3dec.h"
|
|
||||||
|
|
||||||
namespace bell
|
#include "aacdec.h" // for AACFreeDecoder, AACInitDecoder, HAACDecoder
|
||||||
{
|
#include "mp3dec.h" // for MP3FreeDecoder, MP3InitDecoder, HMP3Decoder
|
||||||
class DecodersInstance
|
|
||||||
{
|
namespace bell {
|
||||||
|
class DecodersInstance {
|
||||||
public:
|
public:
|
||||||
DecodersInstance(){};
|
DecodersInstance(){};
|
||||||
~DecodersInstance()
|
~DecodersInstance() {
|
||||||
{
|
|
||||||
MP3FreeDecoder(mp3Decoder);
|
MP3FreeDecoder(mp3Decoder);
|
||||||
AACFreeDecoder(aacDecoder);
|
AACFreeDecoder(aacDecoder);
|
||||||
};
|
};
|
||||||
@@ -26,18 +22,14 @@ namespace bell
|
|||||||
HAACDecoder aacDecoder = NULL;
|
HAACDecoder aacDecoder = NULL;
|
||||||
HMP3Decoder mp3Decoder = NULL;
|
HMP3Decoder mp3Decoder = NULL;
|
||||||
|
|
||||||
void ensureAAC()
|
void ensureAAC() {
|
||||||
{
|
if (aacDecoder == NULL) {
|
||||||
if (aacDecoder == NULL)
|
|
||||||
{
|
|
||||||
aacDecoder = AACInitDecoder();
|
aacDecoder = AACInitDecoder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensureMP3()
|
void ensureMP3() {
|
||||||
{
|
if (mp3Decoder == NULL) {
|
||||||
if (mp3Decoder == NULL)
|
|
||||||
{
|
|
||||||
mp3Decoder = MP3InitDecoder();
|
mp3Decoder = MP3InitDecoder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,7 +38,7 @@ namespace bell
|
|||||||
extern bell::DecodersInstance* decodersInstance;
|
extern bell::DecodersInstance* decodersInstance;
|
||||||
|
|
||||||
void createDecoders();
|
void createDecoders();
|
||||||
}
|
} // namespace bell
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseCodec.h"
|
#include <stdint.h> // for uint8_t, uint32_t, int16_t
|
||||||
#include "mp3dec.h"
|
|
||||||
|
#include "BaseCodec.h" // for BaseCodec
|
||||||
|
#include "mp3dec.h" // for HMP3Decoder, MP3FrameInfo
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
|
||||||
class MP3Decoder : public BaseCodec {
|
class MP3Decoder : public BaseCodec {
|
||||||
private:
|
private:
|
||||||
HMP3Decoder mp3;
|
HMP3Decoder mp3;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseCodec.h"
|
#include <stdint.h> // for uint8_t, uint32_t, int16_t
|
||||||
|
|
||||||
|
#include "BaseCodec.h" // for BaseCodec
|
||||||
|
|
||||||
struct OpusDecoder;
|
struct OpusDecoder;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "BaseCodec.h"
|
#include <stdint.h> // for uint8_t, uint32_t, int16_t
|
||||||
#include "ivorbiscodec.h"
|
|
||||||
|
#include "BaseCodec.h" // for BaseCodec
|
||||||
|
#include "ivorbiscodec.h" // for vorbis_comment, vorbis_dsp_state, vorbis_info
|
||||||
|
#include "ogg.h" // for ogg_packet
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
|
||||||
class VorbisDecoder : public BaseCodec {
|
class VorbisDecoder : public BaseCodec {
|
||||||
private:
|
private:
|
||||||
vorbis_info* vi = nullptr;
|
vorbis_info* vi = nullptr;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#include "AACContainer.h"
|
#include "AACContainer.h"
|
||||||
#include "iostream"
|
|
||||||
|
#include <cstring> // for memmove
|
||||||
|
|
||||||
|
#include "StreamInfo.h" // for BitWidth, BitWidth::BW_16, SampleRate, Sampl...
|
||||||
|
#include "aacdec.h" // for AACFindSyncWord
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
#define SYNC_WORLD_LEN 4
|
#define SYNC_WORLD_LEN 4
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
#include "AudioContainers.h"
|
#include "AudioContainers.h"
|
||||||
|
|
||||||
|
#include <string.h> // for memcmp
|
||||||
|
#include <cstddef> // for byte
|
||||||
|
|
||||||
|
#include "AACContainer.h" // for AACContainer
|
||||||
|
#include "CodecType.h" // for bell
|
||||||
|
#include "MP3Container.h" // for MP3Container
|
||||||
|
|
||||||
|
namespace bell {
|
||||||
|
class AudioContainer;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
std::unique_ptr<bell::AudioContainer> AudioContainers::guessAudioContainer(
|
std::unique_ptr<bell::AudioContainer> AudioContainers::guessAudioContainer(
|
||||||
@@ -7,8 +18,7 @@ std::unique_ptr<bell::AudioContainer> AudioContainers::guessAudioContainer(
|
|||||||
std::byte tmp[14];
|
std::byte tmp[14];
|
||||||
istr.read((char*)tmp, sizeof(tmp));
|
istr.read((char*)tmp, sizeof(tmp));
|
||||||
|
|
||||||
if (memcmp(tmp, "\xFF\xF1", 2) == 0 ||
|
if (memcmp(tmp, "\xFF\xF1", 2) == 0 || memcmp(tmp, "\xFF\xF9", 2) == 0) {
|
||||||
memcmp(tmp, "\xFF\xF9", 2) == 0) {
|
|
||||||
// AAC found
|
// AAC found
|
||||||
std::cout << "AAC" << std::endl;
|
std::cout << "AAC" << std::endl;
|
||||||
return std::make_unique<bell::AACContainer>(istr);
|
return std::make_unique<bell::AACContainer>(istr);
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#include "MP3Container.h"
|
#include "MP3Container.h"
|
||||||
|
|
||||||
|
#include <cstring> // for memmove
|
||||||
|
|
||||||
|
#include "StreamInfo.h" // for BitWidth, BitWidth::BW_16, SampleRate, Sampl...
|
||||||
|
#include "mp3dec.h" // for MP3FindSyncWord
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
MP3Container::MP3Container(std::istream& istr) : bell::AudioContainer(istr) {}
|
MP3Container::MP3Container(std::istream& istr) : bell::AudioContainer(istr) {}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstring>
|
#include <stdint.h> // for uint32_t
|
||||||
#include <cstddef>
|
#include <cstddef> // for byte, size_t
|
||||||
#include <vector>
|
#include <istream> // for istream
|
||||||
#include "AudioContainer.h"
|
#include <vector> // for vector
|
||||||
#include "aacdec.h"
|
|
||||||
|
#include "AudioContainer.h" // for AudioContainer
|
||||||
|
#include "CodecType.h" // for AudioCodec, AudioCodec::AAC
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
class AACContainer : public AudioContainer {
|
class AACContainer : public AudioContainer {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <istream>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <istream>
|
||||||
#include "CodecType.h"
|
#include "CodecType.h"
|
||||||
#include "StreamInfo.h"
|
#include "StreamInfo.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream> // for istream
|
||||||
#include <memory>
|
#include <memory> // for unique_ptr
|
||||||
#include "AACContainer.h"
|
|
||||||
#include "AudioContainer.h"
|
namespace bell {
|
||||||
#include "MP3Container.h"
|
class AudioContainer;
|
||||||
|
} // namespace bell
|
||||||
|
|
||||||
namespace bell::AudioContainers {
|
namespace bell::AudioContainers {
|
||||||
std::unique_ptr<bell::AudioContainer> guessAudioContainer(std::istream& istr);
|
std::unique_ptr<bell::AudioContainer> guessAudioContainer(std::istream& istr);
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstring>
|
#include <stdint.h> // for uint32_t
|
||||||
#include <cstddef>
|
#include <cstddef> // for byte, size_t
|
||||||
#include <vector>
|
#include <istream> // for istream
|
||||||
#include "AudioContainer.h"
|
#include <vector> // for vector
|
||||||
#include "mp3dec.h"
|
|
||||||
|
#include "AudioContainer.h" // for AudioContainer
|
||||||
|
#include "CodecType.h" // for AudioCodec, AudioCodec::MP3
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
class MP3Container : public AudioContainer {
|
class MP3Container : public AudioContainer {
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
#include "AudioMixer.h"
|
#include "AudioMixer.h"
|
||||||
|
|
||||||
|
#include <mutex> // for scoped_lock
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
AudioMixer::AudioMixer() {
|
AudioMixer::AudioMixer() {}
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> AudioMixer::process(std::unique_ptr<StreamInfo> info) {
|
std::unique_ptr<StreamInfo> AudioMixer::process(
|
||||||
|
std::unique_ptr<StreamInfo> info) {
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
if (info->numChannels != from) {
|
if (info->numChannels != from) {
|
||||||
throw std::runtime_error("AudioMixer: Input channel count does not match configuration");
|
throw std::runtime_error(
|
||||||
|
"AudioMixer: Input channel count does not match configuration");
|
||||||
}
|
}
|
||||||
info->numChannels = to;
|
info->numChannels = to;
|
||||||
|
|
||||||
@@ -19,7 +22,8 @@ std::unique_ptr<StreamInfo> AudioMixer::process(std::unique_ptr<StreamInfo> info
|
|||||||
}
|
}
|
||||||
// Copy channel
|
// Copy channel
|
||||||
for (int i = 0; i < info->numSamples; i++) {
|
for (int i = 0; i < info->numSamples; i++) {
|
||||||
info->data[singleConf.destination][i] = info->data[singleConf.source[0]][i];
|
info->data[singleConf.destination][i] =
|
||||||
|
info->data[singleConf.source[0]][i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Mix channels
|
// Mix channels
|
||||||
@@ -30,7 +34,8 @@ std::unique_ptr<StreamInfo> AudioMixer::process(std::unique_ptr<StreamInfo> info
|
|||||||
sample += info->data[source][i];
|
sample += info->data[source][i];
|
||||||
}
|
}
|
||||||
|
|
||||||
info->data[singleConf.destination][i] = sample / (float) singleConf.source.size();
|
info->data[singleConf.destination][i] =
|
||||||
|
sample / (float)singleConf.source.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#include "AudioPipeline.h"
|
#include "AudioPipeline.h"
|
||||||
#include <iostream>
|
|
||||||
#include "BellLogger.h"
|
#include <type_traits> // for remove_extent_t
|
||||||
|
#include <utility> // for move
|
||||||
|
|
||||||
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
|
#include "BellLogger.h" // for AbstractLogger, BELL_LOG
|
||||||
|
#include "TransformConfig.h" // for TransformConfig
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
@@ -37,7 +42,8 @@ void AudioPipeline::volumeUpdated(int volume) {
|
|||||||
BELL_LOG(debug, "AudioPipeline", "Volume applied, DSP reconfigured");
|
BELL_LOG(debug, "AudioPipeline", "Volume applied, DSP reconfigured");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> AudioPipeline::process(std::unique_ptr<StreamInfo> data) {
|
std::unique_ptr<StreamInfo> AudioPipeline::process(
|
||||||
|
std::unique_ptr<StreamInfo> data) {
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
for (auto& transform : transforms) {
|
for (auto& transform : transforms) {
|
||||||
data = transform->process(std::move(data));
|
data = transform->process(std::move(data));
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#include "BellDSP.h"
|
#include "BellDSP.h"
|
||||||
#include <iostream>
|
|
||||||
#include "CentralAudioBuffer.h"
|
#include <type_traits> // for remove_extent_t
|
||||||
|
#include <utility> // for move
|
||||||
|
|
||||||
|
#include "AudioPipeline.h" // for CentralAudioBuffer
|
||||||
|
#include "CentralAudioBuffer.h" // for CentralAudioBuffer
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
#include "Biquad.h"
|
#include "Biquad.h"
|
||||||
|
|
||||||
|
#include <cmath> // for pow, cosf, sinf, M_PI, sqrtf, tanf, logf, sinh
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
Biquad::Biquad()
|
Biquad::Biquad() {
|
||||||
{
|
|
||||||
this->filterType = "biquad";
|
this->filterType = "biquad";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Biquad::sampleRateChanged(uint32_t sampleRate)
|
void Biquad::sampleRateChanged(uint32_t sampleRate) {
|
||||||
{
|
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
//this->configure(this->type, this->currentConfig);
|
//this->configure(this->type, this->currentConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Biquad::configure(Type type, std::map<std::string, float> &newConf)
|
void Biquad::configure(Type type, std::map<std::string, float>& newConf) {
|
||||||
{
|
|
||||||
this->type = type;
|
this->type = type;
|
||||||
this->currentConfig = newConf;
|
this->currentConfig = newConf;
|
||||||
|
|
||||||
switch (type)
|
switch (type) {
|
||||||
{
|
|
||||||
case Type::Free:
|
case Type::Free:
|
||||||
coeffs[0] = newConf["a1"];
|
coeffs[0] = newConf["a1"];
|
||||||
coeffs[1] = newConf["a2"];
|
coeffs[1] = newConf["a2"];
|
||||||
@@ -41,12 +39,10 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
|
|||||||
break;
|
break;
|
||||||
case Type::Highshelf:
|
case Type::Highshelf:
|
||||||
// check if config has slope key
|
// check if config has slope key
|
||||||
if (newConf.find("slope") != newConf.end())
|
if (newConf.find("slope") != newConf.end()) {
|
||||||
{
|
highShelfCoEffsSlope(newConf["freq"], newConf["gain"],
|
||||||
highShelfCoEffsSlope(newConf["freq"], newConf["gain"], newConf["slope"]);
|
newConf["slope"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
highShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
highShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -55,12 +51,9 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
|
|||||||
break;
|
break;
|
||||||
case Type::Lowshelf:
|
case Type::Lowshelf:
|
||||||
// check if config has slope key
|
// check if config has slope key
|
||||||
if (newConf.find("slope") != newConf.end())
|
if (newConf.find("slope") != newConf.end()) {
|
||||||
{
|
|
||||||
lowShelfCoEffsSlope(newConf["freq"], newConf["gain"], newConf["slope"]);
|
lowShelfCoEffsSlope(newConf["freq"], newConf["gain"], newConf["slope"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
lowShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
lowShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -69,42 +62,32 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
|
|||||||
break;
|
break;
|
||||||
case Type::Peaking:
|
case Type::Peaking:
|
||||||
// check if config has bandwidth key
|
// check if config has bandwidth key
|
||||||
if (newConf.find("bandwidth") != newConf.end())
|
if (newConf.find("bandwidth") != newConf.end()) {
|
||||||
{
|
peakCoEffsBandwidth(newConf["freq"], newConf["gain"],
|
||||||
peakCoEffsBandwidth(newConf["freq"], newConf["gain"], newConf["bandwidth"]);
|
newConf["bandwidth"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
peakCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
peakCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Notch:
|
case Type::Notch:
|
||||||
if (newConf.find("bandwidth") != newConf.end())
|
if (newConf.find("bandwidth") != newConf.end()) {
|
||||||
{
|
notchCoEffsBandwidth(newConf["freq"], newConf["gain"],
|
||||||
notchCoEffsBandwidth(newConf["freq"], newConf["gain"], newConf["bandwidth"]);
|
newConf["bandwidth"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
notchCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
notchCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Bandpass:
|
case Type::Bandpass:
|
||||||
if (newConf.find("bandwidth") != newConf.end())
|
if (newConf.find("bandwidth") != newConf.end()) {
|
||||||
{
|
|
||||||
bandPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
|
bandPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
bandPassCoEffs(newConf["freq"], newConf["q"]);
|
bandPassCoEffs(newConf["freq"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Allpass:
|
case Type::Allpass:
|
||||||
if (newConf.find("bandwidth") != newConf.end())
|
if (newConf.find("bandwidth") != newConf.end()) {
|
||||||
{
|
|
||||||
allPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
|
allPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
allPassCoEffs(newConf["freq"], newConf["q"]);
|
allPassCoEffs(newConf["freq"], newConf["q"]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -115,8 +98,7 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coefficients for a high pass biquad filter
|
// coefficients for a high pass biquad filter
|
||||||
void Biquad::highPassCoEffs(float f, float q)
|
void Biquad::highPassCoEffs(float f, float q) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
float s = sinf(w0);
|
float s = sinf(w0);
|
||||||
@@ -133,8 +115,7 @@ void Biquad::highPassCoEffs(float f, float q)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coefficients for a high pass first order biquad filter
|
// coefficients for a high pass first order biquad filter
|
||||||
void Biquad::highPassFOCoEffs(float f)
|
void Biquad::highPassFOCoEffs(float f) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float k = tanf(w0 / 2.0);
|
float k = tanf(w0 / 2.0);
|
||||||
|
|
||||||
@@ -151,8 +132,7 @@ void Biquad::highPassFOCoEffs(float f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coefficients for a low pass biquad filter
|
// coefficients for a low pass biquad filter
|
||||||
void Biquad::lowPassCoEffs(float f, float q)
|
void Biquad::lowPassCoEffs(float f, float q) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
float s = sinf(w0);
|
float s = sinf(w0);
|
||||||
@@ -169,8 +149,7 @@ void Biquad::lowPassCoEffs(float f, float q)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coefficients for a low pass first order biquad filter
|
// coefficients for a low pass first order biquad filter
|
||||||
void Biquad::lowPassFOCoEffs(float f)
|
void Biquad::lowPassFOCoEffs(float f) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float k = tanf(w0 / 2.0);
|
float k = tanf(w0 / 2.0);
|
||||||
|
|
||||||
@@ -187,8 +166,7 @@ void Biquad::lowPassFOCoEffs(float f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coefficients for a peak biquad filter
|
// coefficients for a peak biquad filter
|
||||||
void Biquad::peakCoEffs(float f, float gain, float q)
|
void Biquad::peakCoEffs(float f, float gain, float q) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
float s = sinf(w0);
|
float s = sinf(w0);
|
||||||
@@ -204,8 +182,7 @@ void Biquad::peakCoEffs(float f, float gain, float q)
|
|||||||
|
|
||||||
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
||||||
}
|
}
|
||||||
void Biquad::peakCoEffsBandwidth(float f, float gain, float bandwidth)
|
void Biquad::peakCoEffsBandwidth(float f, float gain, float bandwidth) {
|
||||||
{
|
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
float s = sinf(w0);
|
float s = sinf(w0);
|
||||||
@@ -222,8 +199,7 @@ void Biquad::peakCoEffsBandwidth(float f, float gain, float bandwidth)
|
|||||||
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Biquad::highShelfCoEffs(float f, float gain, float q)
|
void Biquad::highShelfCoEffs(float f, float gain, float q) {
|
||||||
{
|
|
||||||
float A = std::pow(10.0f, gain / 40.0f);
|
float A = std::pow(10.0f, gain / 40.0f);
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
@@ -239,8 +215,7 @@ void Biquad::highShelfCoEffs(float f, float gain, float q)
|
|||||||
|
|
||||||
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
||||||
}
|
}
|
||||||
void Biquad::highShelfCoEffsSlope(float f, float gain, float slope)
|
void Biquad::highShelfCoEffsSlope(float f, float gain, float slope) {
|
||||||
{
|
|
||||||
float A = std::pow(10.0f, gain / 40.0f);
|
float A = std::pow(10.0f, gain / 40.0f);
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float c = cosf(w0);
|
float c = cosf(w0);
|
||||||
@@ -257,8 +232,7 @@ void Biquad::highShelfCoEffsSlope(float f, float gain, float slope)
|
|||||||
|
|
||||||
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
||||||
}
|
}
|
||||||
void Biquad::highShelfFOCoEffs(float f, float gain)
|
void Biquad::highShelfFOCoEffs(float f, float gain) {
|
||||||
{
|
|
||||||
float A = std::pow(10.0f, gain / 40.0f);
|
float A = std::pow(10.0f, gain / 40.0f);
|
||||||
float w0 = 2 * M_PI * f / this->sampleRate;
|
float w0 = 2 * M_PI * f / this->sampleRate;
|
||||||
float tn = tanf(w0 / 2.0);
|
float tn = tanf(w0 / 2.0);
|
||||||
@@ -433,8 +407,8 @@ void Biquad::allPassFOCoEffs(float f) {
|
|||||||
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
this->normalizeCoEffs(a0, a1, a2, b0, b1, b2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Biquad::normalizeCoEffs(float a0, float a1, float a2, float b0, float b1, float b2)
|
void Biquad::normalizeCoEffs(float a0, float a1, float a2, float b0, float b1,
|
||||||
{
|
float b2) {
|
||||||
coeffs[0] = b0 / a0;
|
coeffs[0] = b0 / a0;
|
||||||
coeffs[1] = b1 / a0;
|
coeffs[1] = b1 / a0;
|
||||||
coeffs[2] = b2 / a0;
|
coeffs[2] = b2 / a0;
|
||||||
@@ -442,8 +416,8 @@ void Biquad::normalizeCoEffs(float a0, float a1, float a2, float b0, float b1, f
|
|||||||
coeffs[4] = a2 / a0;
|
coeffs[4] = a2 / a0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> Biquad::process(std::unique_ptr<StreamInfo> stream)
|
std::unique_ptr<StreamInfo> Biquad::process(
|
||||||
{
|
std::unique_ptr<StreamInfo> stream) {
|
||||||
std::scoped_lock lock(accessMutex);
|
std::scoped_lock lock(accessMutex);
|
||||||
|
|
||||||
auto input = stream->data[this->channel];
|
auto input = stream->data[this->channel];
|
||||||
@@ -453,8 +427,7 @@ std::unique_ptr<StreamInfo> Biquad::process(std::unique_ptr<StreamInfo> stream)
|
|||||||
dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w);
|
dsps_biquad_f32_ae32(input, input, numSamples, coeffs, w);
|
||||||
#else
|
#else
|
||||||
// Apply the set coefficients
|
// Apply the set coefficients
|
||||||
for (int i = 0; i < numSamples; i++)
|
for (int i = 0; i < numSamples; i++) {
|
||||||
{
|
|
||||||
float d0 = input[i] - coeffs[3] * w[0] - coeffs[4] * w[1];
|
float d0 = input[i] - coeffs[3] * w[0] - coeffs[4] * w[1];
|
||||||
input[i] = coeffs[0] * d0 + coeffs[1] * w[0] + coeffs[2] * w[1];
|
input[i] = coeffs[0] * d0 + coeffs[1] * w[0] + coeffs[2] * w[1];
|
||||||
w[1] = w[0];
|
w[1] = w[0];
|
||||||
|
|||||||
@@ -1,31 +1,28 @@
|
|||||||
#include "BiquadCombo.h"
|
#include "BiquadCombo.h"
|
||||||
|
|
||||||
|
#include <stdio.h> // for printf
|
||||||
|
#include <cmath> // for sinf, M_PI
|
||||||
|
#include <utility> // for move
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
BiquadCombo::BiquadCombo()
|
BiquadCombo::BiquadCombo() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BiquadCombo::sampleRateChanged(uint32_t sampleRate)
|
void BiquadCombo::sampleRateChanged(uint32_t sampleRate) {
|
||||||
{
|
for (auto& biquad : biquads) {
|
||||||
for (auto &biquad : biquads)
|
|
||||||
{
|
|
||||||
biquad->sampleRateChanged(sampleRate);
|
biquad->sampleRateChanged(sampleRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> BiquadCombo::calculateBWQ(int order)
|
std::vector<float> BiquadCombo::calculateBWQ(int order) {
|
||||||
{
|
|
||||||
|
|
||||||
std::vector<float> qValues;
|
std::vector<float> qValues;
|
||||||
for (int n = 0; n < order / 2; n++)
|
for (int n = 0; n < order / 2; n++) {
|
||||||
{
|
|
||||||
float q = 1.0f / (2.0f * sinf(M_PI / order * (((float)n) + 0.5)));
|
float q = 1.0f / (2.0f * sinf(M_PI / order * (((float)n) + 0.5)));
|
||||||
qValues.push_back(q);
|
qValues.push_back(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (order % 2 > 0)
|
if (order % 2 > 0) {
|
||||||
{
|
|
||||||
qValues.push_back(-1.0);
|
qValues.push_back(-1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,37 +31,28 @@ std::vector<float> BiquadCombo::calculateBWQ(int order)
|
|||||||
return qValues;
|
return qValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> BiquadCombo::calculateLRQ(int order)
|
std::vector<float> BiquadCombo::calculateLRQ(int order) {
|
||||||
{
|
|
||||||
auto qValues = calculateBWQ(order / 2);
|
auto qValues = calculateBWQ(order / 2);
|
||||||
|
|
||||||
if (order % 4 > 0)
|
if (order % 4 > 0) {
|
||||||
{
|
|
||||||
qValues.pop_back();
|
qValues.pop_back();
|
||||||
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
|
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
|
||||||
qValues.push_back(0.5);
|
qValues.push_back(0.5);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
|
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return qValues;
|
return qValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BiquadCombo::butterworth(float freq, int order, FilterType type)
|
void BiquadCombo::butterworth(float freq, int order, FilterType type) {
|
||||||
{
|
|
||||||
std::vector<float> qValues = calculateBWQ(order);
|
std::vector<float> qValues = calculateBWQ(order);
|
||||||
for (auto &q : qValues)
|
for (auto& q : qValues) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BiquadCombo::linkwitzRiley(float freq, int order, FilterType type)
|
void BiquadCombo::linkwitzRiley(float freq, int order, FilterType type) {
|
||||||
{
|
|
||||||
std::vector<float> qValues = calculateLRQ(order);
|
std::vector<float> qValues = calculateLRQ(order);
|
||||||
for (auto &q : qValues)
|
for (auto& q : qValues) {
|
||||||
{
|
|
||||||
auto filter = std::make_unique<Biquad>();
|
auto filter = std::make_unique<Biquad>();
|
||||||
filter->channel = channel;
|
filter->channel = channel;
|
||||||
|
|
||||||
@@ -72,25 +60,16 @@ void BiquadCombo::linkwitzRiley(float freq, int order, FilterType type)
|
|||||||
config["freq"] = freq;
|
config["freq"] = freq;
|
||||||
config["q"] = q;
|
config["q"] = q;
|
||||||
|
|
||||||
if (q >= 0.0)
|
if (q >= 0.0) {
|
||||||
{
|
if (type == FilterType::Highpass) {
|
||||||
if (type == FilterType::Highpass)
|
|
||||||
{
|
|
||||||
filter->configure(Biquad::Type::Highpass, config);
|
filter->configure(Biquad::Type::Highpass, config);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
filter->configure(Biquad::Type::Lowpass, config);
|
filter->configure(Biquad::Type::Lowpass, config);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
if (type == FilterType::Highpass) {
|
||||||
{
|
|
||||||
if (type == FilterType::Highpass)
|
|
||||||
{
|
|
||||||
filter->configure(Biquad::Type::HighpassFO, config);
|
filter->configure(Biquad::Type::HighpassFO, config);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
filter->configure(Biquad::Type::LowpassFO, config);
|
filter->configure(Biquad::Type::LowpassFO, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +78,8 @@ void BiquadCombo::linkwitzRiley(float freq, int order, FilterType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> BiquadCombo::process(std::unique_ptr<StreamInfo> data) {
|
std::unique_ptr<StreamInfo> BiquadCombo::process(
|
||||||
|
std::unique_ptr<StreamInfo> data) {
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
for (auto& transform : this->biquads) {
|
for (auto& transform : this->biquads) {
|
||||||
data = transform->process(std::move(data));
|
data = transform->process(std::move(data));
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "Compressor.h"
|
#include "Compressor.h"
|
||||||
|
|
||||||
|
#include <cstdlib> // for abs
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
float log2f_approx(float X) {
|
float log2f_approx(float X) {
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
#include "Gain.h"
|
#include "Gain.h"
|
||||||
|
|
||||||
|
#include <cmath> // for pow
|
||||||
|
#include <string> // for string
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
Gain::Gain() : AudioTransform()
|
Gain::Gain() : AudioTransform() {
|
||||||
{
|
|
||||||
this->gainFactor = 1.0f;
|
this->gainFactor = 1.0f;
|
||||||
this->filterType = "gain";
|
this->filterType = "gain";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gain::configure(std::vector<int> channels, float gainDB)
|
void Gain::configure(std::vector<int> channels, float gainDB) {
|
||||||
{
|
|
||||||
this->channels = channels;
|
this->channels = channels;
|
||||||
this->gainDb = gainDB;
|
this->gainDb = gainDB;
|
||||||
this->gainFactor = std::pow(10.0f, gainDB / 20.0f);
|
this->gainFactor = std::pow(10.0f, gainDB / 20.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> Gain::process(std::unique_ptr<StreamInfo> data)
|
std::unique_ptr<StreamInfo> Gain::process(std::unique_ptr<StreamInfo> data) {
|
||||||
{
|
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
for (int i = 0; i < data->numSamples; i++)
|
for (int i = 0; i < data->numSamples; i++) {
|
||||||
{
|
|
||||||
// Apply gain to all channels
|
// Apply gain to all channels
|
||||||
for (auto &channel : channels)
|
for (auto& channel : channels) {
|
||||||
{
|
|
||||||
data->data[channel][i] *= gainFactor;
|
data->data[channel][i] *= gainFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <cJSON.h> // for cJSON_GetObjectItem, cJSON, cJSON_IsArray
|
||||||
#include <algorithm>
|
#include <stddef.h> // for NULL
|
||||||
#include <cJSON.h>
|
#include <algorithm> // for find
|
||||||
|
#include <cstdint> // for uint8_t
|
||||||
|
#include <memory> // for unique_ptr
|
||||||
|
#include <stdexcept> // for invalid_argument
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "AudioTransform.h"
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class AudioMixer : public bell::AudioTransform {
|
||||||
class AudioMixer : public bell::AudioTransform
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum DownmixMode
|
enum DownmixMode { DEFAULT };
|
||||||
{
|
|
||||||
DEFAULT
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MixerConfig
|
struct MixerConfig {
|
||||||
{
|
|
||||||
std::vector<int> source;
|
std::vector<int> source;
|
||||||
int destination;
|
int destination;
|
||||||
};
|
};
|
||||||
@@ -33,50 +32,42 @@ namespace bell
|
|||||||
// Configuration of each channels in the mixer
|
// Configuration of each channels in the mixer
|
||||||
std::vector<MixerConfig> mixerConfig;
|
std::vector<MixerConfig> mixerConfig;
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
|
std::unique_ptr<StreamInfo> process(
|
||||||
|
std::unique_ptr<StreamInfo> data) override;
|
||||||
|
|
||||||
void reconfigure() override
|
void reconfigure() override {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void fromJSON(cJSON *json)
|
void fromJSON(cJSON* json) {
|
||||||
{
|
|
||||||
cJSON* mappedChannels = cJSON_GetObjectItem(json, "mapped_channels");
|
cJSON* mappedChannels = cJSON_GetObjectItem(json, "mapped_channels");
|
||||||
|
|
||||||
if (mappedChannels == NULL || !cJSON_IsArray(mappedChannels))
|
if (mappedChannels == NULL || !cJSON_IsArray(mappedChannels)) {
|
||||||
{
|
|
||||||
throw std::invalid_argument("Mixer configuration invalid");
|
throw std::invalid_argument("Mixer configuration invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mixerConfig = std::vector<MixerConfig>();
|
this->mixerConfig = std::vector<MixerConfig>();
|
||||||
|
|
||||||
cJSON* iterator = NULL;
|
cJSON* iterator = NULL;
|
||||||
cJSON_ArrayForEach(iterator, mappedChannels)
|
cJSON_ArrayForEach(iterator, mappedChannels) {
|
||||||
{
|
|
||||||
std::vector<int> sources(0);
|
std::vector<int> sources(0);
|
||||||
cJSON* iteratorNested = NULL;
|
cJSON* iteratorNested = NULL;
|
||||||
cJSON_ArrayForEach(iteratorNested, cJSON_GetObjectItem(iterator, "source"))
|
cJSON_ArrayForEach(iteratorNested,
|
||||||
{
|
cJSON_GetObjectItem(iterator, "source")) {
|
||||||
sources.push_back(iteratorNested->valueint);
|
sources.push_back(iteratorNested->valueint);
|
||||||
}
|
}
|
||||||
|
|
||||||
int destination = cJSON_GetObjectItem(iterator, "destination")->valueint;
|
int destination = cJSON_GetObjectItem(iterator, "destination")->valueint;
|
||||||
|
|
||||||
this->mixerConfig.push_back(MixerConfig{
|
this->mixerConfig.push_back(
|
||||||
.source = sources,
|
MixerConfig{.source = sources, .destination = destination});
|
||||||
.destination = destination
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> sources(0);
|
std::vector<uint8_t> sources(0);
|
||||||
|
|
||||||
for (auto &config : mixerConfig)
|
for (auto& config : mixerConfig) {
|
||||||
{
|
|
||||||
|
|
||||||
for (auto &source : config.source)
|
for (auto& source : config.source) {
|
||||||
{
|
if (std::find(sources.begin(), sources.end(), source) ==
|
||||||
if (std::find(sources.begin(), sources.end(), source) == sources.end())
|
sources.end()) {
|
||||||
{
|
|
||||||
sources.push_back(source);
|
sources.push_back(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,4 +77,4 @@ namespace bell
|
|||||||
this->to = mixerConfig.size();
|
this->to = mixerConfig.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "AudioTransform.h"
|
#include <memory> // for shared_ptr, unique_ptr
|
||||||
#include "StreamInfo.h"
|
#include <mutex> // for mutex
|
||||||
#include <memory>
|
#include <vector> // for vector
|
||||||
#include "Gain.h"
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace bell
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
{
|
|
||||||
class AudioPipeline
|
namespace bell {
|
||||||
{
|
class AudioTransform;
|
||||||
|
class Gain;
|
||||||
|
|
||||||
|
class AudioPipeline {
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Gain> headroomGainTransform;
|
std::shared_ptr<Gain> headroomGainTransform;
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include "StreamInfo.h"
|
#include "StreamInfo.h"
|
||||||
#include "TransformConfig.h"
|
#include "TransformConfig.h"
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class AudioTransform {
|
||||||
class AudioTransform
|
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
std::mutex accessMutex;
|
std::mutex accessMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) = 0;
|
virtual std::unique_ptr<StreamInfo> process(
|
||||||
|
std::unique_ptr<StreamInfo> data) = 0;
|
||||||
virtual void sampleRateChanged(uint32_t sampleRate){};
|
virtual void sampleRateChanged(uint32_t sampleRate){};
|
||||||
virtual float calculateHeadroom() { return 0; };
|
virtual float calculateHeadroom() { return 0; };
|
||||||
|
|
||||||
@@ -26,4 +25,4 @@ namespace bell
|
|||||||
AudioTransform() = default;
|
AudioTransform() = default;
|
||||||
virtual ~AudioTransform() = default;
|
virtual ~AudioTransform() = default;
|
||||||
};
|
};
|
||||||
};
|
}; // namespace bell
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <stddef.h> // for size_t
|
||||||
#include <mutex>
|
#include <stdint.h> // for uint32_t, uint8_t
|
||||||
#include <vector>
|
#include <functional> // for function
|
||||||
#include "AudioPipeline.h"
|
#include <memory> // for shared_ptr, unique_ptr
|
||||||
#include "CentralAudioBuffer.h"
|
#include <mutex> // for mutex
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
|
#include "StreamInfo.h" // for BitWidth
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
|
class AudioPipeline;
|
||||||
|
class CentralAudioBuffer;
|
||||||
|
|
||||||
#define MAX_INT16 32767
|
#define MAX_INT16 32767
|
||||||
|
|
||||||
class BellDSP {
|
class BellDSP {
|
||||||
@@ -19,15 +25,18 @@ class BellDSP {
|
|||||||
AudioEffect() = default;
|
AudioEffect() = default;
|
||||||
~AudioEffect() = default;
|
~AudioEffect() = default;
|
||||||
size_t duration;
|
size_t duration;
|
||||||
virtual void apply(float* sampleData, size_t samples, size_t relativePosition) = 0;
|
virtual void apply(float* sampleData, size_t samples,
|
||||||
|
size_t relativePosition) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FadeEffect : public AudioEffect {
|
class FadeEffect : public AudioEffect {
|
||||||
private:
|
private:
|
||||||
std::function<void()> onFinish;
|
std::function<void()> onFinish;
|
||||||
bool isFadeIn;
|
bool isFadeIn;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FadeEffect(size_t duration, bool isFadeIn, std::function<void()> onFinish = nullptr);
|
FadeEffect(size_t duration, bool isFadeIn,
|
||||||
|
std::function<void()> onFinish = nullptr);
|
||||||
~FadeEffect(){};
|
~FadeEffect(){};
|
||||||
|
|
||||||
void apply(float* sampleData, size_t samples, size_t relativePosition);
|
void apply(float* sampleData, size_t samples, size_t relativePosition);
|
||||||
@@ -38,8 +47,8 @@ class BellDSP {
|
|||||||
|
|
||||||
std::shared_ptr<AudioPipeline> getActivePipeline();
|
std::shared_ptr<AudioPipeline> getActivePipeline();
|
||||||
|
|
||||||
size_t process(uint8_t* data, size_t bytes, int channels,
|
size_t process(uint8_t* data, size_t bytes, int channels, uint32_t sampleRate,
|
||||||
uint32_t sampleRate, BitWidth bitWidth);
|
BitWidth bitWidth);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<AudioPipeline> activePipeline;
|
std::shared_ptr<AudioPipeline> activePipeline;
|
||||||
@@ -48,7 +57,6 @@ class BellDSP {
|
|||||||
std::vector<float> dataLeft = std::vector<float>(1024);
|
std::vector<float> dataLeft = std::vector<float>(1024);
|
||||||
std::vector<float> dataRight = std::vector<float>(1024);
|
std::vector<float> dataRight = std::vector<float>(1024);
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<AudioEffect> underflowEffect = nullptr;
|
std::unique_ptr<AudioEffect> underflowEffect = nullptr;
|
||||||
std::unique_ptr<AudioEffect> startEffect = nullptr;
|
std::unique_ptr<AudioEffect> startEffect = nullptr;
|
||||||
std::unique_ptr<AudioEffect> instantEffect = nullptr;
|
std::unique_ptr<AudioEffect> instantEffect = nullptr;
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cmath>
|
#include <stdint.h> // for uint32_t
|
||||||
#include <mutex>
|
#include <map> // for map
|
||||||
#include <map>
|
#include <memory> // for unique_ptr, allocator
|
||||||
#include <unordered_map>
|
#include <mutex> // for scoped_lock
|
||||||
|
#include <stdexcept> // for invalid_argument
|
||||||
|
#include <string> // for string, operator<, hash, operator==
|
||||||
|
#include <unordered_map> // for operator!=, unordered_map, __hash_map_c...
|
||||||
|
#include <utility> // for pair
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "AudioTransform.h"
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
extern "C" int dsps_biquad_f32_ae32(const float *input, float *output, int len, float *coef, float *w);
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
|
#include "TransformConfig.h" // for TransformConfig
|
||||||
|
|
||||||
namespace bell
|
extern "C" int dsps_biquad_f32_ae32(const float* input, float* output, int len,
|
||||||
{
|
float* coef, float* w);
|
||||||
class Biquad : public bell::AudioTransform
|
|
||||||
{
|
namespace bell {
|
||||||
|
class Biquad : public bell::AudioTransform {
|
||||||
public:
|
public:
|
||||||
Biquad();
|
Biquad();
|
||||||
~Biquad(){};
|
~Biquad(){};
|
||||||
|
|
||||||
enum class Type
|
enum class Type {
|
||||||
{
|
|
||||||
Free,
|
Free,
|
||||||
Highpass,
|
Highpass,
|
||||||
Lowpass,
|
Lowpass,
|
||||||
@@ -58,14 +64,14 @@ namespace bell
|
|||||||
int channel;
|
int channel;
|
||||||
Biquad::Type type;
|
Biquad::Type type;
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
|
std::unique_ptr<StreamInfo> process(
|
||||||
|
std::unique_ptr<StreamInfo> data) override;
|
||||||
|
|
||||||
void configure(Type type, std::map<std::string, float>& config);
|
void configure(Type type, std::map<std::string, float>& config);
|
||||||
|
|
||||||
void sampleRateChanged(uint32_t sampleRate) override;
|
void sampleRateChanged(uint32_t sampleRate) override;
|
||||||
|
|
||||||
void reconfigure() override
|
void reconfigure() override {
|
||||||
{
|
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
std::map<std::string, float> biquadConfig;
|
std::map<std::string, float> biquadConfig;
|
||||||
this->channel = config->getChannels()[0];
|
this->channel = config->getChannels()[0];
|
||||||
@@ -80,11 +86,8 @@ namespace bell
|
|||||||
float q = config->getFloat("q", false, invalid);
|
float q = config->getFloat("q", false, invalid);
|
||||||
|
|
||||||
if (currentConfig["bandwidth"] == bandwidth &&
|
if (currentConfig["bandwidth"] == bandwidth &&
|
||||||
currentConfig["slope"] == slope &&
|
currentConfig["slope"] == slope && currentConfig["gain"] == gain &&
|
||||||
currentConfig["gain"] == gain &&
|
currentConfig["frequency"] == frequency && currentConfig["q"] == q) {
|
||||||
currentConfig["frequency"] == frequency &&
|
|
||||||
currentConfig["q"] == q)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +102,7 @@ namespace bell
|
|||||||
if (q != invalid)
|
if (q != invalid)
|
||||||
biquadConfig["q"] = q;
|
biquadConfig["q"] = q;
|
||||||
|
|
||||||
if (type == "free")
|
if (type == "free") {
|
||||||
{
|
|
||||||
biquadConfig["a1"] = config->getFloat("a1");
|
biquadConfig["a1"] = config->getFloat("a1");
|
||||||
biquadConfig["a2"] = config->getFloat("a2");
|
biquadConfig["a2"] = config->getFloat("a2");
|
||||||
biquadConfig["b0"] = config->getFloat("b0");
|
biquadConfig["b0"] = config->getFloat("b0");
|
||||||
@@ -109,12 +111,9 @@ namespace bell
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto typeElement = strMapType.find(type);
|
auto typeElement = strMapType.find(type);
|
||||||
if (typeElement != strMapType.end())
|
if (typeElement != strMapType.end()) {
|
||||||
{
|
|
||||||
this->configure(typeElement->second, biquadConfig);
|
this->configure(typeElement->second, biquadConfig);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::invalid_argument("No biquad of type " + type);
|
throw std::invalid_argument("No biquad of type " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,8 @@ namespace bell
|
|||||||
void allPassCoEffsBandwidth(float f, float bandwidth);
|
void allPassCoEffsBandwidth(float f, float bandwidth);
|
||||||
void allPassFOCoEffs(float f);
|
void allPassFOCoEffs(float f);
|
||||||
|
|
||||||
void normalizeCoEffs(float a0, float a1, float a2, float b0, float b1, float b2);
|
void normalizeCoEffs(float a0, float a1, float a2, float b0, float b1,
|
||||||
|
float b2);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
} // namespace bell
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <stdint.h> // for uint32_t
|
||||||
#include <memory>
|
#include <map> // for map
|
||||||
#include <cmath>
|
#include <memory> // for unique_ptr, allocator
|
||||||
#include <mutex>
|
#include <mutex> // for scoped_lock
|
||||||
#include <map>
|
#include <stdexcept> // for invalid_argument
|
||||||
|
#include <string> // for string, operator==, char_traits, basic_...
|
||||||
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "Biquad.h"
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
#include "AudioTransform.h"
|
#include "Biquad.h" // for Biquad
|
||||||
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
|
#include "TransformConfig.h" // for TransformConfig
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class BiquadCombo : public bell::AudioTransform {
|
||||||
class BiquadCombo : public bell::AudioTransform
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::unique_ptr<bell::Biquad>> biquads;
|
std::vector<std::unique_ptr<bell::Biquad>> biquads;
|
||||||
|
|
||||||
@@ -25,32 +27,25 @@ namespace bell
|
|||||||
~BiquadCombo(){};
|
~BiquadCombo(){};
|
||||||
int channel;
|
int channel;
|
||||||
|
|
||||||
std::map<std::string, float> paramCache = {
|
std::map<std::string, float> paramCache = {{"order", 0.0f},
|
||||||
{"order", 0.0f},
|
{"frequency", 0.0f}};
|
||||||
{"frequency", 0.0f}
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FilterType
|
enum class FilterType { Highpass, Lowpass };
|
||||||
{
|
|
||||||
Highpass,
|
|
||||||
Lowpass
|
|
||||||
};
|
|
||||||
|
|
||||||
void linkwitzRiley(float freq, int order, FilterType type);
|
void linkwitzRiley(float freq, int order, FilterType type);
|
||||||
void butterworth(float freq, int order, FilterType type);
|
void butterworth(float freq, int order, FilterType type);
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
|
std::unique_ptr<StreamInfo> process(
|
||||||
|
std::unique_ptr<StreamInfo> data) override;
|
||||||
void sampleRateChanged(uint32_t sampleRate) override;
|
void sampleRateChanged(uint32_t sampleRate) override;
|
||||||
|
|
||||||
void reconfigure() override
|
void reconfigure() override {
|
||||||
{
|
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
|
|
||||||
float freq = config->getFloat("frequency");
|
float freq = config->getFloat("frequency");
|
||||||
int order = config->getInt("order");
|
int order = config->getInt("order");
|
||||||
|
|
||||||
if (paramCache["frequency"] == freq && paramCache["order"] == order)
|
if (paramCache["frequency"] == freq && paramCache["order"] == order) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
paramCache["frequency"] = freq;
|
paramCache["frequency"] = freq;
|
||||||
@@ -60,26 +55,17 @@ namespace bell
|
|||||||
this->channel = config->getChannels()[0];
|
this->channel = config->getChannels()[0];
|
||||||
this->biquads = std::vector<std::unique_ptr<bell::Biquad>>();
|
this->biquads = std::vector<std::unique_ptr<bell::Biquad>>();
|
||||||
auto type = config->getString("combo_type");
|
auto type = config->getString("combo_type");
|
||||||
if (type == "lr_lowpass")
|
if (type == "lr_lowpass") {
|
||||||
{
|
|
||||||
this->linkwitzRiley(freq, order, FilterType::Lowpass);
|
this->linkwitzRiley(freq, order, FilterType::Lowpass);
|
||||||
}
|
} else if (type == "lr_highpass") {
|
||||||
else if (type == "lr_highpass")
|
|
||||||
{
|
|
||||||
this->linkwitzRiley(freq, order, FilterType::Highpass);
|
this->linkwitzRiley(freq, order, FilterType::Highpass);
|
||||||
}
|
} else if (type == "bw_highpass") {
|
||||||
else if (type == "bw_highpass")
|
|
||||||
{
|
|
||||||
this->butterworth(freq, order, FilterType::Highpass);
|
this->butterworth(freq, order, FilterType::Highpass);
|
||||||
}
|
} else if (type == "bw_lowpass") {
|
||||||
else if (type == "bw_lowpass")
|
|
||||||
{
|
|
||||||
this->butterworth(freq, order, FilterType::Highpass);
|
this->butterworth(freq, order, FilterType::Highpass);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::invalid_argument("Invalid combo filter type");
|
throw std::invalid_argument("Invalid combo filter type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}; // namespace bell
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "BellUtils.h"
|
#include "BellUtils.h"
|
||||||
#include "CircularBuffer.h"
|
#include "CircularBuffer.h"
|
||||||
@@ -70,8 +70,9 @@ class CentralAudioBuffer {
|
|||||||
*/
|
*/
|
||||||
void clearBuffer() {
|
void clearBuffer() {
|
||||||
std::scoped_lock lock(this->dataAccessMutex);
|
std::scoped_lock lock(this->dataAccessMutex);
|
||||||
//size_t exceptSize = currentSampleRate + (sizeof(AudioChunk) - (currentSampleRate % sizeof(AudioChunk)));
|
|
||||||
audioBuffer->emptyBuffer();
|
audioBuffer->emptyBuffer();
|
||||||
|
hasChunk = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void emptyCompletely() {
|
void emptyCompletely() {
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <math.h> // for expf
|
||||||
#include <memory>
|
#include <stdint.h> // for uint32_t
|
||||||
#include <algorithm>
|
#include <map> // for map
|
||||||
#include <cmath>
|
#include <memory> // for unique_ptr
|
||||||
#include <math.h>
|
#include <mutex> // for scoped_lock
|
||||||
#include <iostream>
|
#include <string> // for string, operator<
|
||||||
#include <mutex>
|
#include <vector> // for vector
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "Biquad.h"
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
#include "AudioTransform.h"
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
|
#include "TransformConfig.h" // for TransformConfig
|
||||||
|
|
||||||
#define pow10f(x) expf(2.302585092994046f * x)
|
#define pow10f(x) expf(2.302585092994046f * x)
|
||||||
|
|
||||||
@@ -20,10 +20,8 @@ float log2f_approx(float X);
|
|||||||
|
|
||||||
#define log10f_fast(x) (log2f_approx(x) * 0.3010299956639812f)
|
#define log10f_fast(x) (log2f_approx(x) * 0.3010299956639812f)
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class Compressor : public bell::AudioTransform {
|
||||||
class Compressor : public bell::AudioTransform
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::vector<int> channels;
|
std::vector<int> channels;
|
||||||
std::vector<float> tmp;
|
std::vector<float> tmp;
|
||||||
@@ -45,7 +43,8 @@ namespace bell
|
|||||||
Compressor();
|
Compressor();
|
||||||
~Compressor(){};
|
~Compressor(){};
|
||||||
|
|
||||||
void configure(std::vector<int> channels, float attack, float release, float threshold, float factor, float makeupGain);
|
void configure(std::vector<int> channels, float attack, float release,
|
||||||
|
float threshold, float factor, float makeupGain);
|
||||||
|
|
||||||
void sumChannels(std::unique_ptr<StreamInfo>& data);
|
void sumChannels(std::unique_ptr<StreamInfo>& data);
|
||||||
void calLoudness();
|
void calLoudness();
|
||||||
@@ -53,8 +52,7 @@ namespace bell
|
|||||||
|
|
||||||
void applyGain(std::unique_ptr<StreamInfo>& data);
|
void applyGain(std::unique_ptr<StreamInfo>& data);
|
||||||
|
|
||||||
void reconfigure() override
|
void reconfigure() override {
|
||||||
{
|
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
auto newChannels = config->getChannels();
|
auto newChannels = config->getChannels();
|
||||||
|
|
||||||
@@ -68,12 +66,9 @@ namespace bell
|
|||||||
paramCache["release"] == newRelease &&
|
paramCache["release"] == newRelease &&
|
||||||
paramCache["threshold"] == newThreshold &&
|
paramCache["threshold"] == newThreshold &&
|
||||||
paramCache["factor"] == newFactor &&
|
paramCache["factor"] == newFactor &&
|
||||||
paramCache["makeup_gain"] == newMakeupGain)
|
paramCache["makeup_gain"] == newMakeupGain) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
paramCache["attack"] = newAttack;
|
paramCache["attack"] = newAttack;
|
||||||
paramCache["release"] = newRelease;
|
paramCache["release"] = newRelease;
|
||||||
@@ -82,7 +77,8 @@ namespace bell
|
|||||||
paramCache["makeup_gain"] = newMakeupGain;
|
paramCache["makeup_gain"] = newMakeupGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->configure(newChannels, newAttack, newRelease, newThreshold, newFactor, newMakeupGain);
|
this->configure(newChannels, newAttack, newRelease, newThreshold, newFactor,
|
||||||
|
newMakeupGain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// void fromJSON(cJSON* json) override {
|
// void fromJSON(cJSON* json) override {
|
||||||
@@ -97,7 +93,10 @@ namespace bell
|
|||||||
// this->configure(attack, release, clipLimit, threshold, factor, makeupGain);
|
// this->configure(attack, release, clipLimit, threshold, factor, makeupGain);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
|
std::unique_ptr<StreamInfo> process(
|
||||||
void sampleRateChanged(uint32_t sampleRate) override { this->sampleRate = sampleRate; };
|
std::unique_ptr<StreamInfo> data) override;
|
||||||
|
void sampleRateChanged(uint32_t sampleRate) override {
|
||||||
|
this->sampleRate = sampleRate;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
}; // namespace bell
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cmath>
|
#include <memory> // for unique_ptr
|
||||||
#include <mutex>
|
#include <mutex> // for scoped_lock
|
||||||
#include <iostream>
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "AudioTransform.h"
|
#include "AudioTransform.h" // for AudioTransform
|
||||||
|
#include "StreamInfo.h" // for StreamInfo
|
||||||
|
#include "TransformConfig.h" // for TransformConfig
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class Gain : public bell::AudioTransform {
|
||||||
class Gain : public bell::AudioTransform
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
float gainFactor = 1.0f;
|
float gainFactor = 1.0f;
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ namespace bell
|
|||||||
|
|
||||||
void configure(std::vector<int> channels, float gainDB);
|
void configure(std::vector<int> channels, float gainDB);
|
||||||
|
|
||||||
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
|
std::unique_ptr<StreamInfo> process(
|
||||||
|
std::unique_ptr<StreamInfo> data) override;
|
||||||
|
|
||||||
void reconfigure() override {
|
void reconfigure() override {
|
||||||
std::scoped_lock lock(this->accessMutex);
|
std::scoped_lock lock(this->accessMutex);
|
||||||
@@ -37,4 +38,4 @@ namespace bell
|
|||||||
this->configure(channels, gain);
|
this->configure(channels, gain);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
@@ -3,45 +3,34 @@
|
|||||||
#include "TransformConfig.h"
|
#include "TransformConfig.h"
|
||||||
#include "cJSON.h"
|
#include "cJSON.h"
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class JSONTransformConfig : public bell::TransformConfig {
|
||||||
class JSONTransformConfig : public bell::TransformConfig
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
cJSON* json;
|
cJSON* json;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
JSONTransformConfig(cJSON *body)
|
JSONTransformConfig(cJSON* body) { this->json = body; };
|
||||||
{
|
|
||||||
this->json = body;
|
|
||||||
};
|
|
||||||
~JSONTransformConfig(){};
|
~JSONTransformConfig(){};
|
||||||
|
|
||||||
std::string rawGetString(const std::string &field) override
|
std::string rawGetString(const std::string& field) override {
|
||||||
{
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsString(value))
|
if (value != NULL && cJSON_IsString(value)) {
|
||||||
{
|
|
||||||
return std::string(value->valuestring);
|
return std::string(value->valuestring);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "invalid";
|
return "invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> rawGetIntArray(const std::string &field) override
|
std::vector<int> rawGetIntArray(const std::string& field) override {
|
||||||
{
|
|
||||||
std::vector<int> result;
|
std::vector<int> result;
|
||||||
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsArray(value))
|
if (value != NULL && cJSON_IsArray(value)) {
|
||||||
{
|
for (int i = 0; i < cJSON_GetArraySize(value); i++) {
|
||||||
for (int i = 0; i < cJSON_GetArraySize(value); i++)
|
|
||||||
{
|
|
||||||
cJSON* item = cJSON_GetArrayItem(value, i);
|
cJSON* item = cJSON_GetArrayItem(value, i);
|
||||||
if (item != NULL && cJSON_IsNumber(item))
|
if (item != NULL && cJSON_IsNumber(item)) {
|
||||||
{
|
|
||||||
result.push_back(item->valueint);
|
result.push_back(item->valueint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,19 +39,15 @@ namespace bell
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> rawGetFloatArray(const std::string &field) override
|
std::vector<float> rawGetFloatArray(const std::string& field) override {
|
||||||
{
|
|
||||||
std::vector<float> result;
|
std::vector<float> result;
|
||||||
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsArray(value))
|
if (value != NULL && cJSON_IsArray(value)) {
|
||||||
{
|
for (int i = 0; i < cJSON_GetArraySize(value); i++) {
|
||||||
for (int i = 0; i < cJSON_GetArraySize(value); i++)
|
|
||||||
{
|
|
||||||
cJSON* item = cJSON_GetArrayItem(value, i);
|
cJSON* item = cJSON_GetArrayItem(value, i);
|
||||||
if (item != NULL && cJSON_IsNumber(item))
|
if (item != NULL && cJSON_IsNumber(item)) {
|
||||||
{
|
|
||||||
result.push_back(item->valuedouble);
|
result.push_back(item->valuedouble);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,40 +56,34 @@ namespace bell
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rawGetInt(const std::string &field) override
|
int rawGetInt(const std::string& field) override {
|
||||||
{
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsNumber(value))
|
if (value != NULL && cJSON_IsNumber(value)) {
|
||||||
{
|
|
||||||
return (int)value->valueint;
|
return (int)value->valueint;
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidInt;
|
return invalidInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isArray(const std::string &field) override
|
bool isArray(const std::string& field) override {
|
||||||
{
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsArray(value))
|
if (value != NULL && cJSON_IsArray(value)) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float rawGetFloat(const std::string &field) override
|
float rawGetFloat(const std::string& field) override {
|
||||||
{
|
|
||||||
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
|
||||||
|
|
||||||
if (value != NULL && cJSON_IsNumber(value))
|
if (value != NULL && cJSON_IsNumber(value)) {
|
||||||
{
|
|
||||||
return (float)value->valuedouble;
|
return (float)value->valuedouble;
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidInt;
|
return invalidInt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
@@ -1,36 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
enum class Channels { LEFT, RIGHT, LEFT_RIGHT };
|
||||||
enum class Channels {
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
LEFT_RIGHT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SampleRate : uint32_t
|
enum class SampleRate : uint32_t {
|
||||||
{
|
|
||||||
SR_44100 = 44100,
|
SR_44100 = 44100,
|
||||||
SR_48000 = 48000,
|
SR_48000 = 48000,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BitWidth : uint32_t
|
enum class BitWidth : uint32_t {
|
||||||
{
|
|
||||||
BW_16 = 16,
|
BW_16 = 16,
|
||||||
BW_24 = 24,
|
BW_24 = 24,
|
||||||
BW_32 = 32,
|
BW_32 = 32,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
float** data;
|
float** data;
|
||||||
BitWidth bitwidth;
|
BitWidth bitwidth;
|
||||||
int numChannels;
|
int numChannels;
|
||||||
SampleRate sampleRate;
|
SampleRate sampleRate;
|
||||||
size_t numSamples;
|
size_t numSamples;
|
||||||
} StreamInfo;
|
} StreamInfo;
|
||||||
};
|
}; // namespace bell
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class TransformConfig {
|
||||||
class TransformConfig
|
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
int invalidInt = -0x7C;
|
int invalidInt = -0x7C;
|
||||||
std::string invalidString = "_invalid";
|
std::string invalidString = "_invalid";
|
||||||
@@ -33,102 +31,82 @@ namespace bell
|
|||||||
typedef std::variant<int, float, std::string> Value;
|
typedef std::variant<int, float, std::string> Value;
|
||||||
std::map<std::string, std::vector<Value>> rawValues;
|
std::map<std::string, std::vector<Value>> rawValues;
|
||||||
|
|
||||||
Value getRawValue(const std::string &field)
|
Value getRawValue(const std::string& field) {
|
||||||
{
|
|
||||||
int index = this->currentVolume * (rawValues[field].size()) / 100;
|
int index = this->currentVolume * (rawValues[field].size()) / 100;
|
||||||
if (index >= rawValues[field].size())
|
if (index >= rawValues[field].size())
|
||||||
index = rawValues[field].size() - 1;
|
index = rawValues[field].size() - 1;
|
||||||
return rawValues[field][index];
|
return rawValues[field][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getString(const std::string &field, bool isRequired = false, std::string defaultValue = "")
|
std::string getString(const std::string& field, bool isRequired = false,
|
||||||
{
|
std::string defaultValue = "") {
|
||||||
if (rawValues.count(field) == 0)
|
if (rawValues.count(field) == 0) {
|
||||||
{
|
|
||||||
rawValues[field] = std::vector<Value>({Value(rawGetString(field))});
|
rawValues[field] = std::vector<Value>({Value(rawGetString(field))});
|
||||||
}
|
}
|
||||||
auto val = std::get<std::string>(getRawValue(field));
|
auto val = std::get<std::string>(getRawValue(field));
|
||||||
if (val == invalidString)
|
if (val == invalidString) {
|
||||||
{
|
|
||||||
if (isRequired)
|
if (isRequired)
|
||||||
throw std::invalid_argument("Field " + field + " is required");
|
throw std::invalid_argument("Field " + field + " is required");
|
||||||
else
|
else
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getInt(const std::string &field, bool isRequired = false, int defaultValue = 0)
|
int getInt(const std::string& field, bool isRequired = false,
|
||||||
{
|
int defaultValue = 0) {
|
||||||
if (rawValues.count(field) == 0)
|
if (rawValues.count(field) == 0) {
|
||||||
{
|
if (isArray(field)) {
|
||||||
if (isArray(field))
|
|
||||||
{
|
|
||||||
rawValues[field] = std::vector<Value>();
|
rawValues[field] = std::vector<Value>();
|
||||||
for (auto f : rawGetIntArray(field))
|
for (auto f : rawGetIntArray(field)) {
|
||||||
{
|
|
||||||
rawValues[field].push_back(f);
|
rawValues[field].push_back(f);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
rawValues[field] = std::vector<Value>({Value(rawGetInt(field))});
|
rawValues[field] = std::vector<Value>({Value(rawGetInt(field))});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto val = std::get<int>(getRawValue(field));
|
auto val = std::get<int>(getRawValue(field));
|
||||||
if (val == invalidInt)
|
if (val == invalidInt) {
|
||||||
{
|
|
||||||
if (isRequired)
|
if (isRequired)
|
||||||
throw std::invalid_argument("Field " + field + " is required");
|
throw std::invalid_argument("Field " + field + " is required");
|
||||||
else
|
else
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getFloat(const std::string &field, bool isRequired = false, float defaultValue = 0)
|
float getFloat(const std::string& field, bool isRequired = false,
|
||||||
{
|
float defaultValue = 0) {
|
||||||
if (rawValues.count(field) == 0)
|
if (rawValues.count(field) == 0) {
|
||||||
{
|
if (isArray(field)) {
|
||||||
if (isArray(field))
|
|
||||||
{
|
|
||||||
|
|
||||||
rawValues[field] = std::vector<Value>();
|
rawValues[field] = std::vector<Value>();
|
||||||
for (auto f : rawGetFloatArray(field))
|
for (auto f : rawGetFloatArray(field)) {
|
||||||
{
|
|
||||||
rawValues[field].push_back(f);
|
rawValues[field].push_back(f);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
rawValues[field] = std::vector<Value>({Value(rawGetFloat(field))});
|
rawValues[field] = std::vector<Value>({Value(rawGetFloat(field))});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto val = std::get<float>(getRawValue(field));
|
auto val = std::get<float>(getRawValue(field));
|
||||||
if (val == invalidInt)
|
if (val == invalidInt) {
|
||||||
{
|
|
||||||
if (isRequired)
|
if (isRequired)
|
||||||
throw std::invalid_argument("Field " + field + " is required");
|
throw std::invalid_argument("Field " + field + " is required");
|
||||||
else
|
else
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> getChannels()
|
std::vector<int> getChannels() {
|
||||||
{
|
|
||||||
auto channel = getInt("channel", false, invalidInt);
|
auto channel = getInt("channel", false, invalidInt);
|
||||||
|
|
||||||
if (channel != invalidInt)
|
if (channel != invalidInt) {
|
||||||
{
|
|
||||||
return std::vector<int>({channel});
|
return std::vector<int>({channel});
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawGetIntArray("channels");
|
return rawGetIntArray("channels");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
|
|
||||||
AC101AudioSink::AC101AudioSink()
|
AC101AudioSink::AC101AudioSink() {
|
||||||
{
|
|
||||||
// Disable software volume control, all handled by ::volumeChanged
|
// Disable software volume control, all handled by ::volumeChanged
|
||||||
softwareVolumeControl = false;
|
softwareVolumeControl = false;
|
||||||
|
|
||||||
@@ -37,9 +36,7 @@ AC101AudioSink::AC101AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
AC101AudioSink::~AC101AudioSink()
|
AC101AudioSink::~AC101AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AC101AudioSink::volumeChanged(uint16_t volume) {
|
void AC101AudioSink::volumeChanged(uint16_t volume) {
|
||||||
dac->volume(volume, volume);
|
dac->volume(volume, volume);
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
#include "BufferedAudioSink.h"
|
#include "BufferedAudioSink.h"
|
||||||
|
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/ringbuf.h"
|
#include "freertos/ringbuf.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
RingbufHandle_t dataBuffer;
|
RingbufHandle_t dataBuffer;
|
||||||
|
|
||||||
static void i2sFeed(void *pvParameters)
|
static void i2sFeed(void* pvParameters) {
|
||||||
{
|
while (true) {
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
size_t itemSize;
|
size_t itemSize;
|
||||||
char *item = (char *)xRingbufferReceiveUpTo(dataBuffer, &itemSize, portMAX_DELAY, 512);
|
char* item = (char*)xRingbufferReceiveUpTo(dataBuffer, &itemSize,
|
||||||
if (item != NULL)
|
portMAX_DELAY, 512);
|
||||||
{
|
if (item != NULL) {
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
while (written < itemSize)
|
while (written < itemSize) {
|
||||||
{
|
|
||||||
i2s_write((i2s_port_t)0, item, itemSize, &written, portMAX_DELAY);
|
i2s_write((i2s_port_t)0, item, itemSize, &written, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
vRingbufferReturnItem(dataBuffer, (void*)item);
|
vRingbufferReturnItem(dataBuffer, (void*)item);
|
||||||
@@ -24,24 +21,25 @@ static void i2sFeed(void *pvParameters)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedAudioSink::startI2sFeed(size_t buf_size)
|
void BufferedAudioSink::startI2sFeed(size_t buf_size) {
|
||||||
{
|
|
||||||
dataBuffer = xRingbufferCreate(buf_size, RINGBUF_TYPE_BYTEBUF);
|
dataBuffer = xRingbufferCreate(buf_size, RINGBUF_TYPE_BYTEBUF);
|
||||||
xTaskCreatePinnedToCore(&i2sFeed, "i2sFeed", 4096, NULL, 10, NULL, tskNO_AFFINITY);
|
xTaskCreatePinnedToCore(&i2sFeed, "i2sFeed", 4096, NULL, 10, NULL,
|
||||||
|
tskNO_AFFINITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
void BufferedAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
|
||||||
{
|
|
||||||
feedPCMFramesInternal(buffer, bytes);
|
feedPCMFramesInternal(buffer, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSize)
|
void BufferedAudioSink::feedPCMFramesInternal(const void* pvItem,
|
||||||
{
|
size_t xItemSize) {
|
||||||
xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
|
xRingbufferSend(dataBuffer, pvItem, xItemSize, portMAX_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferedAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
bool BufferedAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
|
uint8_t bitDepth) {
|
||||||
// TODO override this for sinks with custom mclk
|
// TODO override this for sinks with custom mclk
|
||||||
i2s_set_clk((i2s_port_t)0, sampleRate, (i2s_bits_per_sample_t)bitDepth, (i2s_channel_t)channelCount);
|
i2s_set_clk((i2s_port_t)0, sampleRate, (i2s_bits_per_sample_t)bitDepth,
|
||||||
|
(i2s_channel_t)channelCount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,22 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include "es8311.h"
|
#include "es8311.h"
|
||||||
}
|
}
|
||||||
ES8311AudioSink::ES8311AudioSink()
|
ES8311AudioSink::ES8311AudioSink() {
|
||||||
{
|
|
||||||
this->softwareVolumeControl = false;
|
this->softwareVolumeControl = false;
|
||||||
esp_err_t ret_val = ESP_OK;
|
esp_err_t ret_val = ESP_OK;
|
||||||
Es8311Config cfg = {
|
Es8311Config cfg = {
|
||||||
.esMode = ES_MODE_SLAVE,
|
.esMode = ES_MODE_SLAVE,
|
||||||
.i2c_port_num = I2C_NUM_0,
|
.i2c_port_num = I2C_NUM_0,
|
||||||
.i2c_cfg = {
|
.i2c_cfg =
|
||||||
|
{
|
||||||
.mode = I2C_MODE_MASTER,
|
.mode = I2C_MODE_MASTER,
|
||||||
.sda_io_num = 1,
|
.sda_io_num = 1,
|
||||||
.scl_io_num = 2,
|
.scl_io_num = 2,
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||||
},
|
},
|
||||||
.dacOutput = (DacOutput) (DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2),
|
.dacOutput = (DacOutput)(DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 |
|
||||||
|
DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2),
|
||||||
.adcInput = ADC_INPUT_LINPUT1_RINPUT1,
|
.adcInput = ADC_INPUT_LINPUT1_RINPUT1,
|
||||||
};
|
};
|
||||||
cfg.i2c_cfg.master.clk_speed = 100000;
|
cfg.i2c_cfg.master.clk_speed = 100000;
|
||||||
@@ -68,14 +69,12 @@ ES8311AudioSink::ES8311AudioSink()
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
err = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||||
if (err != ESP_OK)
|
if (err != ESP_OK) {
|
||||||
{
|
|
||||||
ESP_LOGE("OI", "i2s driver installation error: %d", err);
|
ESP_LOGE("OI", "i2s driver installation error: %d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = i2s_set_pin((i2s_port_t)0, &pin_config);
|
err = i2s_set_pin((i2s_port_t)0, &pin_config);
|
||||||
if (err != ESP_OK)
|
if (err != ESP_OK) {
|
||||||
{
|
|
||||||
ESP_LOGE("OI", "i2s set pin error: %d", err);
|
ESP_LOGE("OI", "i2s set pin error: %d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,21 +85,17 @@ ES8311AudioSink::ES8311AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES8311AudioSink::volumeChanged(uint16_t volume)
|
void ES8311AudioSink::volumeChanged(uint16_t volume) {
|
||||||
{
|
|
||||||
Es8311SetVoiceVolume(volume);
|
Es8311SetVoiceVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES8311AudioSink::writeReg(uint8_t reg_add, uint8_t data)
|
void ES8311AudioSink::writeReg(uint8_t reg_add, uint8_t data) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ES8311AudioSink::setSampleRate(uint32_t sampleRate) {
|
void ES8311AudioSink::setSampleRate(uint32_t sampleRate) {
|
||||||
std::cout << "ES8311AudioSink::setSampleRate(" << sampleRate << ")" << std::endl;
|
std::cout << "ES8311AudioSink::setSampleRate(" << sampleRate << ")"
|
||||||
|
<< std::endl;
|
||||||
// i2s set sample rate
|
// i2s set sample rate
|
||||||
es8311_Codec_Startup(0, sampleRate);
|
es8311_Codec_Startup(0, sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
ES8311AudioSink::~ES8311AudioSink()
|
ES8311AudioSink::~ES8311AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ struct es8388_cmd_s {
|
|||||||
uint8_t value;
|
uint8_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
ES8388AudioSink::ES8388AudioSink()
|
ES8388AudioSink::ES8388AudioSink() {
|
||||||
{
|
|
||||||
// configure i2c
|
// configure i2c
|
||||||
i2c_config = {
|
i2c_config = {
|
||||||
.mode = I2C_MODE_MASTER,
|
.mode = I2C_MODE_MASTER,
|
||||||
@@ -29,8 +28,7 @@ ES8388AudioSink::ES8388AudioSink()
|
|||||||
.dma_buf_len = 512,
|
.dma_buf_len = 512,
|
||||||
.use_apll = true,
|
.use_apll = true,
|
||||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||||
.fixed_mclk = 256 * 44100
|
.fixed_mclk = 256 * 44100};
|
||||||
};
|
|
||||||
|
|
||||||
i2s_pin_config_t pin_config = {
|
i2s_pin_config_t pin_config = {
|
||||||
.bck_io_num = 27,
|
.bck_io_num = 27,
|
||||||
@@ -125,8 +123,7 @@ ES8388AudioSink::ES8388AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data)
|
void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data) {
|
||||||
{
|
|
||||||
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
@@ -145,6 +142,4 @@ void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ES8388AudioSink::~ES8388AudioSink()
|
ES8388AudioSink::~ES8388AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
|
|
||||||
ES9018AudioSink::ES9018AudioSink()
|
ES9018AudioSink::ES9018AudioSink() {
|
||||||
{
|
|
||||||
i2s_config_t i2s_config = {
|
i2s_config_t i2s_config = {
|
||||||
|
|
||||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||||
@@ -16,8 +15,7 @@ ES9018AudioSink::ES9018AudioSink()
|
|||||||
.dma_buf_len = 512,
|
.dma_buf_len = 512,
|
||||||
.use_apll = true,
|
.use_apll = true,
|
||||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||||
.fixed_mclk = 384 * 44100
|
.fixed_mclk = 384 * 44100};
|
||||||
};
|
|
||||||
|
|
||||||
i2s_pin_config_t pin_config = {
|
i2s_pin_config_t pin_config = {
|
||||||
.bck_io_num = 27,
|
.bck_io_num = 27,
|
||||||
@@ -31,6 +29,4 @@ ES9018AudioSink::ES9018AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
ES9018AudioSink::~ES9018AudioSink()
|
ES9018AudioSink::~ES9018AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
#include "InternalAudioSink.h"
|
#include "InternalAudioSink.h"
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
|
|
||||||
InternalAudioSink::InternalAudioSink()
|
InternalAudioSink::InternalAudioSink() {
|
||||||
{
|
|
||||||
softwareVolumeControl = true;
|
softwareVolumeControl = true;
|
||||||
usign = true;
|
usign = true;
|
||||||
#ifdef I2S_MODE_DAC_BUILT_IN
|
#ifdef I2S_MODE_DAC_BUILT_IN
|
||||||
|
|
||||||
i2s_config_t i2s_config = {
|
i2s_config_t i2s_config = {
|
||||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), // Only TX
|
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX |
|
||||||
|
I2S_MODE_DAC_BUILT_IN), // Only TX
|
||||||
.sample_rate = (i2s_bits_per_sample_t)44100,
|
.sample_rate = (i2s_bits_per_sample_t)44100,
|
||||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||||
@@ -18,8 +18,7 @@ InternalAudioSink::InternalAudioSink()
|
|||||||
.dma_buf_len = 512,
|
.dma_buf_len = 512,
|
||||||
.use_apll = true,
|
.use_apll = true,
|
||||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||||
.fixed_mclk=-1
|
.fixed_mclk = -1};
|
||||||
};
|
|
||||||
|
|
||||||
//install and start i2s driver
|
//install and start i2s driver
|
||||||
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
|
||||||
@@ -30,6 +29,4 @@ InternalAudioSink::InternalAudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalAudioSink::~InternalAudioSink()
|
InternalAudioSink::~InternalAudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
|
|
||||||
PCM5102AudioSink::PCM5102AudioSink()
|
PCM5102AudioSink::PCM5102AudioSink() {
|
||||||
{
|
|
||||||
i2s_config_t i2s_config = {
|
i2s_config_t i2s_config = {
|
||||||
|
|
||||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||||
@@ -16,8 +15,7 @@ PCM5102AudioSink::PCM5102AudioSink()
|
|||||||
.dma_buf_len = 512,
|
.dma_buf_len = 512,
|
||||||
.use_apll = true,
|
.use_apll = true,
|
||||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||||
.fixed_mclk = 384 * 44100
|
.fixed_mclk = 384 * 44100};
|
||||||
};
|
|
||||||
|
|
||||||
i2s_pin_config_t pin_config = {
|
i2s_pin_config_t pin_config = {
|
||||||
.bck_io_num = 27,
|
.bck_io_num = 27,
|
||||||
@@ -31,6 +29,4 @@ PCM5102AudioSink::PCM5102AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
PCM5102AudioSink::~PCM5102AudioSink()
|
PCM5102AudioSink::~PCM5102AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,38 +5,35 @@
|
|||||||
// See http://www.hardwarebook.info/S/PDIF for more info on this protocol
|
// See http://www.hardwarebook.info/S/PDIF for more info on this protocol
|
||||||
// Conversion table to biphase code mark (LSB first, ending in 1)
|
// Conversion table to biphase code mark (LSB first, ending in 1)
|
||||||
static const uint16_t bmc_convert[256] = {
|
static const uint16_t bmc_convert[256] = {
|
||||||
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33,
|
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33, 0xcd33,
|
||||||
0xcd33, 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533,
|
0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533, 0xccb3, 0x4cb3,
|
||||||
0xccb3, 0x4cb3, 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3,
|
0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3, 0x32b3, 0xb2b3, 0xd2b3,
|
||||||
0x32b3, 0xb2b3, 0xd2b3, 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3,
|
0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3, 0xccd3, 0x4cd3, 0x2cd3, 0xacd3,
|
||||||
0xccd3, 0x4cd3, 0x2cd3, 0xacd3, 0x34d3, 0xb4d3, 0xd4d3, 0x54d3,
|
0x34d3, 0xb4d3, 0xd4d3, 0x54d3, 0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3,
|
||||||
0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3, 0x4ad3, 0x2ad3, 0xaad3,
|
0x4ad3, 0x2ad3, 0xaad3, 0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53,
|
||||||
0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53, 0x2b53, 0xab53,
|
0x2b53, 0xab53, 0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553,
|
||||||
0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553, 0x5553,
|
0x5553, 0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
|
||||||
0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
|
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb, 0x334b,
|
||||||
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb,
|
0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b, 0xcd4b, 0x4d4b,
|
||||||
0x334b, 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b,
|
0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b, 0x332b, 0xb32b, 0xd32b,
|
||||||
0xcd4b, 0x4d4b, 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b,
|
0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b, 0xcd2b, 0x4d2b, 0x2d2b, 0xad2b,
|
||||||
0x332b, 0xb32b, 0xd32b, 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b,
|
0x352b, 0xb52b, 0xd52b, 0x552b, 0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab,
|
||||||
0xcd2b, 0x4d2b, 0x2d2b, 0xad2b, 0x352b, 0xb52b, 0xd52b, 0x552b,
|
0xb4ab, 0xd4ab, 0x54ab, 0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab,
|
||||||
0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab, 0xb4ab, 0xd4ab, 0x54ab,
|
0x2aab, 0xaaab, 0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd,
|
||||||
0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab, 0x2aab, 0xaaab,
|
0x54cd, 0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
|
||||||
0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd, 0x54cd,
|
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d, 0xcd4d,
|
||||||
0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
|
0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d, 0x332d, 0xb32d,
|
||||||
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d,
|
0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d, 0xcd2d, 0x4d2d, 0x2d2d,
|
||||||
0xcd4d, 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d,
|
0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d, 0xccad, 0x4cad, 0x2cad, 0xacad,
|
||||||
0x332d, 0xb32d, 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d,
|
0x34ad, 0xb4ad, 0xd4ad, 0x54ad, 0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad,
|
||||||
0xcd2d, 0x4d2d, 0x2d2d, 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d,
|
0x4aad, 0x2aad, 0xaaad, 0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35,
|
||||||
0xccad, 0x4cad, 0x2cad, 0xacad, 0x34ad, 0xb4ad, 0xd4ad, 0x54ad,
|
0x2b35, 0xab35, 0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535,
|
||||||
0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad, 0x4aad, 0x2aad, 0xaaad,
|
0x5535, 0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
|
||||||
0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35, 0x2b35, 0xab35,
|
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5, 0xccd5,
|
||||||
0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535, 0x5535,
|
0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5, 0x32d5, 0xb2d5,
|
||||||
0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
|
0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5, 0x3355, 0xb355, 0xd355,
|
||||||
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5,
|
0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55, 0xcd55, 0x4d55, 0x2d55, 0xad55,
|
||||||
0xccd5, 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5,
|
0x3555, 0xb555, 0xd555, 0x5555,
|
||||||
0x32d5, 0xb2d5, 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5,
|
|
||||||
0x3355, 0xb355, 0xd355, 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55,
|
|
||||||
0xcd55, 0x4d55, 0x2d55, 0xad55, 0x3555, 0xb555, 0xd555, 0x5555,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug
|
#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug
|
||||||
@@ -53,21 +50,18 @@ static const uint16_t bmc_convert[256] = {
|
|||||||
static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE];
|
static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE];
|
||||||
static uint32_t* spdif_ptr;
|
static uint32_t* spdif_ptr;
|
||||||
|
|
||||||
static void spdif_buf_init(void)
|
static void spdif_buf_init(void) {
|
||||||
{
|
|
||||||
// first bllock has W preamble
|
// first bllock has W preamble
|
||||||
spdif_buf[0] = BMC_B;
|
spdif_buf[0] = BMC_B;
|
||||||
|
|
||||||
// all other blocks are alternating M, then W preamble
|
// all other blocks are alternating M, then W preamble
|
||||||
uint32_t bmc_mw = BMC_M;
|
uint32_t bmc_mw = BMC_M;
|
||||||
for (int i = 2; i < SPDIF_BUF_ARRAY_SIZE; i += 2)
|
for (int i = 2; i < SPDIF_BUF_ARRAY_SIZE; i += 2) {
|
||||||
{
|
|
||||||
spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
|
spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
|
SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin) {
|
||||||
{
|
|
||||||
// initialize S/PDIF buffer
|
// initialize S/PDIF buffer
|
||||||
spdif_buf_init();
|
spdif_buf_init();
|
||||||
spdif_ptr = spdif_buf;
|
spdif_ptr = spdif_buf;
|
||||||
@@ -76,8 +70,10 @@ SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
|
|||||||
startI2sFeed(SPDIF_BUF_SIZE * 16);
|
startI2sFeed(SPDIF_BUF_SIZE * 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
if (bitDepth != 16 || channelCount != 2) // TODO support mono playback and different bit widths
|
uint8_t bitDepth) {
|
||||||
|
if (bitDepth != 16 ||
|
||||||
|
channelCount != 2) // TODO support mono playback and different bit widths
|
||||||
return false;
|
return false;
|
||||||
int sample_rate = (int)sampleRate * 2;
|
int sample_rate = (int)sampleRate * 2;
|
||||||
int bclk = sample_rate * 64 * 2;
|
int bclk = sample_rate * 64 * 2;
|
||||||
@@ -118,10 +114,8 @@ SPDIFAudioSink::~SPDIFAudioSink() {
|
|||||||
|
|
||||||
int num_frames = 0;
|
int num_frames = 0;
|
||||||
|
|
||||||
void SPDIFAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
void SPDIFAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
|
||||||
{
|
for (int i = 0; i < bytes; i += 2) {
|
||||||
for (int i = 0; i < bytes; i += 2)
|
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* What is this, and why does it work?
|
* What is this, and why does it work?
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "TAS5711AudioSink.h"
|
#include "TAS5711AudioSink.h"
|
||||||
|
|
||||||
|
|
||||||
struct tas5711_cmd_s {
|
struct tas5711_cmd_s {
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
@@ -17,8 +16,7 @@ static const struct tas5711_cmd_s tas5711_init_sequence[] = {
|
|||||||
};
|
};
|
||||||
i2c_ack_type_t ACK_CHECK_EN = (i2c_ack_type_t)0x1;
|
i2c_ack_type_t ACK_CHECK_EN = (i2c_ack_type_t)0x1;
|
||||||
|
|
||||||
TAS5711AudioSink::TAS5711AudioSink()
|
TAS5711AudioSink::TAS5711AudioSink() {
|
||||||
{
|
|
||||||
i2s_config_t i2s_config = {
|
i2s_config_t i2s_config = {
|
||||||
|
|
||||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
|
||||||
@@ -31,9 +29,7 @@ TAS5711AudioSink::TAS5711AudioSink()
|
|||||||
.dma_buf_len = 512,
|
.dma_buf_len = 512,
|
||||||
.use_apll = true,
|
.use_apll = true,
|
||||||
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
|
||||||
.fixed_mclk = 256 * 44100
|
.fixed_mclk = 256 * 44100};
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
i2s_pin_config_t pin_config = {
|
i2s_pin_config_t pin_config = {
|
||||||
.bck_io_num = 5,
|
.bck_io_num = 5,
|
||||||
@@ -75,15 +71,13 @@ TAS5711AudioSink::TAS5711AudioSink()
|
|||||||
|
|
||||||
if (ret == ESP_OK) {
|
if (ret == ESP_OK) {
|
||||||
ESP_LOGI("RR", "Detected TAS");
|
ESP_LOGI("RR", "Detected TAS");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ESP_LOGI("RR", "Unable to detect dac");
|
ESP_LOGI("RR", "Unable to detect dac");
|
||||||
}
|
}
|
||||||
|
|
||||||
writeReg(0x1b, 0x00);
|
writeReg(0x1b, 0x00);
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; tas5711_init_sequence[i].reg != 0xff; i++) {
|
for (int i = 0; tas5711_init_sequence[i].reg != 0xff; i++) {
|
||||||
writeReg(tas5711_init_sequence[i].reg, tas5711_init_sequence[i].value);
|
writeReg(tas5711_init_sequence[i].reg, tas5711_init_sequence[i].value);
|
||||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||||
@@ -92,8 +86,7 @@ TAS5711AudioSink::TAS5711AudioSink()
|
|||||||
startI2sFeed();
|
startI2sFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TAS5711AudioSink::writeReg(uint8_t reg, uint8_t value)
|
void TAS5711AudioSink::writeReg(uint8_t reg, uint8_t value) {
|
||||||
{
|
|
||||||
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
|
||||||
|
|
||||||
i2c_master_start(i2c_cmd);
|
i2c_master_start(i2c_cmd);
|
||||||
@@ -101,17 +94,14 @@ void TAS5711AudioSink::writeReg(uint8_t reg, uint8_t value)
|
|||||||
i2c_master_write_byte(i2c_cmd, reg, ACK_CHECK_EN);
|
i2c_master_write_byte(i2c_cmd, reg, ACK_CHECK_EN);
|
||||||
i2c_master_write_byte(i2c_cmd, value, ACK_CHECK_EN);
|
i2c_master_write_byte(i2c_cmd, value, ACK_CHECK_EN);
|
||||||
|
|
||||||
|
|
||||||
i2c_master_stop(i2c_cmd);
|
i2c_master_stop(i2c_cmd);
|
||||||
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_PERIOD_MS);
|
esp_err_t res =
|
||||||
|
i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
if (res != ESP_OK) {
|
if (res != ESP_OK) {
|
||||||
ESP_LOGE("RR", "Unable to write to TAS5711");
|
ESP_LOGE("RR", "Unable to write to TAS5711");
|
||||||
}
|
}
|
||||||
i2c_cmd_link_delete(i2c_cmd);
|
i2c_cmd_link_delete(i2c_cmd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TAS5711AudioSink::~TAS5711AudioSink()
|
TAS5711AudioSink::~TAS5711AudioSink() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,16 +22,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include "ac101.h"
|
||||||
#include <esp_log.h>
|
|
||||||
#include <esp_types.h>
|
|
||||||
#include <esp_system.h>
|
|
||||||
#include <freertos/FreeRTOS.h>
|
|
||||||
#include <freertos/task.h>
|
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <driver/i2s.h>
|
#include <driver/i2s.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_system.h>
|
||||||
|
#include <esp_types.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <string.h>
|
||||||
#include "adac.h"
|
#include "adac.h"
|
||||||
#include "ac101.h"
|
|
||||||
|
|
||||||
const static char TAG[] = "AC101";
|
const static char TAG[] = "AC101";
|
||||||
|
|
||||||
@@ -43,8 +43,7 @@ const static char TAG[] = "AC101";
|
|||||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
#define AC_ASSERT(a, format, b, ...) \
|
#define AC_ASSERT(a, format, b, ...) \
|
||||||
if ((a) != 0) \
|
if ((a) != 0) { \
|
||||||
{ \
|
|
||||||
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
|
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
|
||||||
return b; \
|
return b; \
|
||||||
}
|
}
|
||||||
@@ -71,8 +70,7 @@ static int i2c_port;
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
|
static bool init(int i2c_port_num, int i2s_num, i2s_config_t* i2s_config) {
|
||||||
{
|
|
||||||
esp_err_t res = ESP_OK;
|
esp_err_t res = ESP_OK;
|
||||||
|
|
||||||
i2c_port = i2c_port_num;
|
i2c_port = i2c_port_num;
|
||||||
@@ -92,59 +90,79 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
|
|||||||
|
|
||||||
res = i2c_read_reg(CHIP_AUDIO_RS);
|
res = i2c_read_reg(CHIP_AUDIO_RS);
|
||||||
|
|
||||||
if (!res)
|
if (!res) {
|
||||||
{
|
|
||||||
ESP_LOGW(TAG, "No AC101 detected");
|
ESP_LOGW(TAG, "No AC101 detected");
|
||||||
i2c_driver_delete(i2c_port);
|
i2c_driver_delete(i2c_port);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "AC101 DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num);
|
ESP_LOGI(TAG, "AC101 DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num,
|
||||||
|
i2c_config.scl_io_num);
|
||||||
|
|
||||||
res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
|
res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
|
||||||
// huh?
|
// huh?
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
// enable the PLL from BCLK source
|
// enable the PLL from BCLK source
|
||||||
i2c_write_reg(PLL_CTRL1, BIN(0000, 0001, 0100, 1111)); // F=1,M=1,PLL,INT=31 (medium)
|
i2c_write_reg(PLL_CTRL1,
|
||||||
i2c_write_reg(PLL_CTRL2, BIN(1000, 0110, 0000, 0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
|
BIN(0000, 0001, 0100, 1111)); // F=1,M=1,PLL,INT=31 (medium)
|
||||||
|
i2c_write_reg(PLL_CTRL2, BIN(1000, 0110, 0000,
|
||||||
|
0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
|
||||||
// i2c_write_reg(PLL_CTRL2, BIN(1000,0011,1100,0000));
|
// i2c_write_reg(PLL_CTRL2, BIN(1000,0011,1100,0000));
|
||||||
|
|
||||||
// clocking system
|
// clocking system
|
||||||
i2c_write_reg(SYSCLK_CTRL, BIN(1010, 1010, 0000, 1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK
|
i2c_write_reg(SYSCLK_CTRL, BIN(1010, 1010, 0000,
|
||||||
|
1000)); // PLLCLK, BCLK1, IS1CLK, PLL, SYSCLK
|
||||||
i2c_write_reg(MOD_CLK_ENA, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
i2c_write_reg(MOD_CLK_ENA, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
||||||
i2c_write_reg(MOD_RST_CTRL, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
i2c_write_reg(MOD_RST_CTRL, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
|
||||||
i2c_write_reg(I2S_SR_CTRL, BIN(0111, 0000, 0000, 0000)); // 44.1kHz
|
i2c_write_reg(I2S_SR_CTRL, BIN(0111, 0000, 0000, 0000)); // 44.1kHz
|
||||||
|
|
||||||
// analogue config
|
// analogue config
|
||||||
i2c_write_reg(I2S1LCK_CTRL, BIN(1000, 1000, 0101, 0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo
|
i2c_write_reg(I2S1LCK_CTRL,
|
||||||
|
BIN(1000, 1000, 0101,
|
||||||
|
0000)); // Slave, BCLK=I2S/8,LRCK=32,16bits,I2Smode, Stereo
|
||||||
i2c_write_reg(I2S1_SDOUT_CTRL, BIN(1100, 0000, 0000, 0000)); // I2S1ADC (R&L)
|
i2c_write_reg(I2S1_SDOUT_CTRL, BIN(1100, 0000, 0000, 0000)); // I2S1ADC (R&L)
|
||||||
i2c_write_reg(I2S1_SDIN_CTRL, BIN(1100, 0000, 0000, 0000)); // IS21DAC (R&L)
|
i2c_write_reg(I2S1_SDIN_CTRL, BIN(1100, 0000, 0000, 0000)); // IS21DAC (R&L)
|
||||||
i2c_write_reg(I2S1_MXR_SRC, BIN(0010, 0010, 0000, 0000)); // ADCL, ADCR
|
i2c_write_reg(I2S1_MXR_SRC, BIN(0010, 0010, 0000, 0000)); // ADCL, ADCR
|
||||||
i2c_write_reg(ADC_SRCBST_CTRL, BIN(0100, 0100, 0100, 0000)); // disable all boost (default)
|
i2c_write_reg(ADC_SRCBST_CTRL,
|
||||||
|
BIN(0100, 0100, 0100, 0000)); // disable all boost (default)
|
||||||
#if ENABLE_ADC
|
#if ENABLE_ADC
|
||||||
i2c_write_reg(ADC_SRC, BIN(0000, 0100, 0000, 1000)); // source=linein(R/L)
|
i2c_write_reg(ADC_SRC, BIN(0000, 0100, 0000, 1000)); // source=linein(R/L)
|
||||||
i2c_write_reg(ADC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable digital ADC
|
i2c_write_reg(ADC_DIG_CTRL,
|
||||||
i2c_write_reg(ADC_ANA_CTRL, BIN(1011, 1011, 0000, 0000)); // enable analogue R/L, 0dB
|
BIN(1000, 0000, 0000, 0000)); // enable digital ADC
|
||||||
|
i2c_write_reg(ADC_ANA_CTRL,
|
||||||
|
BIN(1011, 1011, 0000, 0000)); // enable analogue R/L, 0dB
|
||||||
#else
|
#else
|
||||||
i2c_write_reg(ADC_SRC, BIN(0000, 0000, 0000, 0000)); // source=none
|
i2c_write_reg(ADC_SRC, BIN(0000, 0000, 0000, 0000)); // source=none
|
||||||
i2c_write_reg(ADC_DIG_CTRL, BIN(0000, 0000, 0000, 0000)); // disable digital ADC
|
i2c_write_reg(ADC_DIG_CTRL,
|
||||||
i2c_write_reg(ADC_ANA_CTRL, BIN(0011, 0011, 0000, 0000)); // disable analogue R/L, 0dB
|
BIN(0000, 0000, 0000, 0000)); // disable digital ADC
|
||||||
|
i2c_write_reg(ADC_ANA_CTRL,
|
||||||
|
BIN(0011, 0011, 0000, 0000)); // disable analogue R/L, 0dB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Path Configuration
|
//Path Configuration
|
||||||
i2c_write_reg(DAC_MXR_SRC, BIN(1000, 1000, 0000, 0000)); // DAC from I2S
|
i2c_write_reg(DAC_MXR_SRC, BIN(1000, 1000, 0000, 0000)); // DAC from I2S
|
||||||
i2c_write_reg(DAC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable DAC
|
i2c_write_reg(DAC_DIG_CTRL, BIN(1000, 0000, 0000, 0000)); // enable DAC
|
||||||
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 0000, 0000, 0000)); // enable DAC/Analogue (see note on offset removal and PA)
|
i2c_write_reg(
|
||||||
i2c_write_reg(OMIXER_DACA_CTRL, BIN(1111, 1111, 0000, 0000)); // this toggle is needed for headphone PA offset
|
OMIXER_DACA_CTRL,
|
||||||
|
BIN(1111, 0000, 0000,
|
||||||
|
0000)); // enable DAC/Analogue (see note on offset removal and PA)
|
||||||
|
i2c_write_reg(OMIXER_DACA_CTRL,
|
||||||
|
BIN(1111, 1111, 0000,
|
||||||
|
0000)); // this toggle is needed for headphone PA offset
|
||||||
#if ENABLE_ADC
|
#if ENABLE_ADC
|
||||||
i2c_write_reg(OMIXER_SR, BIN(0000, 0001, 0000, 0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?)
|
i2c_write_reg(
|
||||||
|
OMIXER_SR,
|
||||||
|
BIN(0000, 0001, 0000,
|
||||||
|
0010)); // source=DAC(R/L) (are DACR and DACL really inverted in bitmap?)
|
||||||
#else
|
#else
|
||||||
i2c_write_reg(OMIXER_SR, BIN(0000, 0101, 0000, 1010)); // source=DAC(R/L) and LINEIN(R/L)
|
i2c_write_reg(OMIXER_SR, BIN(0000, 0101, 0000,
|
||||||
|
1010)); // source=DAC(R/L) and LINEIN(R/L)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// configure I2S pins & install driver
|
// configure I2S pins & install driver
|
||||||
i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t){.bck_io_num = 27, .ws_io_num = 26, .data_out_num = 25, .data_in_num = -1};
|
i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t){
|
||||||
|
.bck_io_num = 27, .ws_io_num = 26, .data_out_num = 25, .data_in_num = -1};
|
||||||
res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL);
|
res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL);
|
||||||
res |= i2s_set_pin(i2s_num, &i2s_pin_config);
|
res |= i2s_set_pin(i2s_num, &i2s_pin_config);
|
||||||
|
|
||||||
@@ -156,7 +174,8 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
|
|||||||
ac101_set_spk_volume(70);
|
ac101_set_spk_volume(70);
|
||||||
ac101_set_earph_volume(70);
|
ac101_set_earph_volume(70);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
|
ESP_LOGI(TAG, "DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num,
|
||||||
|
i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
|
||||||
|
|
||||||
return (res == ESP_OK);
|
return (res == ESP_OK);
|
||||||
}
|
}
|
||||||
@@ -164,16 +183,14 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* init
|
* init
|
||||||
*/
|
*/
|
||||||
static void deinit(void)
|
static void deinit(void) {
|
||||||
{
|
|
||||||
i2c_driver_delete(i2c_port);
|
i2c_driver_delete(i2c_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* change volume
|
* change volume
|
||||||
*/
|
*/
|
||||||
static void volume(unsigned left, unsigned right)
|
static void volume(unsigned left, unsigned right) {
|
||||||
{
|
|
||||||
ac101_set_earph_volume(left);
|
ac101_set_earph_volume(left);
|
||||||
// nothing at that point, volume is handled by backend
|
// nothing at that point, volume is handled by backend
|
||||||
}
|
}
|
||||||
@@ -181,10 +198,8 @@ static void volume(unsigned left, unsigned right)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* power
|
* power
|
||||||
*/
|
*/
|
||||||
static void power(adac_power_e mode)
|
static void power(adac_power_e mode) {
|
||||||
{
|
switch (mode) {
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case ADAC_STANDBY:
|
case ADAC_STANDBY:
|
||||||
case ADAC_OFF:
|
case ADAC_OFF:
|
||||||
ac101_stop();
|
ac101_stop();
|
||||||
@@ -201,8 +216,7 @@ static void power(adac_power_e mode)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* speaker
|
* speaker
|
||||||
*/
|
*/
|
||||||
static void speaker(bool active)
|
static void speaker(bool active) {
|
||||||
{
|
|
||||||
uint16_t value = i2c_read_reg(SPKOUT_CTRL);
|
uint16_t value = i2c_read_reg(SPKOUT_CTRL);
|
||||||
if (active)
|
if (active)
|
||||||
i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN);
|
i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN);
|
||||||
@@ -213,8 +227,7 @@ static void speaker(bool active)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* headset
|
* headset
|
||||||
*/
|
*/
|
||||||
static void headset(bool active)
|
static void headset(bool active) {
|
||||||
{
|
|
||||||
// there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure
|
// there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure
|
||||||
uint16_t value = i2c_read_reg(HPOUT_CTRL);
|
uint16_t value = i2c_read_reg(HPOUT_CTRL);
|
||||||
if (active)
|
if (active)
|
||||||
@@ -226,8 +239,7 @@ static void headset(bool active)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val)
|
static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val) {
|
||||||
{
|
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
esp_err_t ret = 0;
|
esp_err_t ret = 0;
|
||||||
uint8_t send_buff[4];
|
uint8_t send_buff[4];
|
||||||
@@ -246,8 +258,7 @@ static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static uint16_t i2c_read_reg(uint8_t reg)
|
static uint16_t i2c_read_reg(uint8_t reg) {
|
||||||
{
|
|
||||||
uint8_t data[2] = {0};
|
uint8_t data[2] = {0};
|
||||||
|
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
@@ -255,7 +266,8 @@ static uint16_t i2c_read_reg(uint8_t reg)
|
|||||||
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
|
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN);
|
||||||
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
|
||||||
i2c_master_start(cmd);
|
i2c_master_start(cmd);
|
||||||
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | READ_BIT, ACK_CHECK_EN); //check or not
|
i2c_master_write_byte(cmd, (AC101_ADDR << 1) | READ_BIT,
|
||||||
|
ACK_CHECK_EN); //check or not
|
||||||
i2c_master_read(cmd, data, 2, ACK_VAL);
|
i2c_master_read(cmd, data, 2, ACK_VAL);
|
||||||
i2c_master_stop(cmd);
|
i2c_master_stop(cmd);
|
||||||
i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
|
i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
|
||||||
@@ -268,8 +280,7 @@ static uint16_t i2c_read_reg(uint8_t reg)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void set_sample_rate(int rate)
|
void set_sample_rate(int rate) {
|
||||||
{
|
|
||||||
if (rate == 8000)
|
if (rate == 8000)
|
||||||
rate = SAMPLE_RATE_8000;
|
rate = SAMPLE_RATE_8000;
|
||||||
else if (rate == 11025)
|
else if (rate == 11025)
|
||||||
@@ -292,8 +303,7 @@ void set_sample_rate(int rate)
|
|||||||
rate = SAMPLE_RATE_96000;
|
rate = SAMPLE_RATE_96000;
|
||||||
else if (rate == 192000)
|
else if (rate == 192000)
|
||||||
rate = SAMPLE_RATE_192000;
|
rate = SAMPLE_RATE_192000;
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
ESP_LOGW(TAG, "Unknown sample rate %hu", rate);
|
ESP_LOGW(TAG, "Unknown sample rate %hu", rate);
|
||||||
rate = SAMPLE_RATE_44100;
|
rate = SAMPLE_RATE_44100;
|
||||||
}
|
}
|
||||||
@@ -303,16 +313,14 @@ void set_sample_rate(int rate)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Get normalized (0..100) speaker volume
|
* Get normalized (0..100) speaker volume
|
||||||
*/
|
*/
|
||||||
static int ac101_get_spk_volume(void)
|
static int ac101_get_spk_volume(void) {
|
||||||
{
|
|
||||||
return ((i2c_read_reg(SPKOUT_CTRL) & 0x1f) * 100) / 0x1f;
|
return ((i2c_read_reg(SPKOUT_CTRL) & 0x1f) * 100) / 0x1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Set normalized (0..100) volume
|
* Set normalized (0..100) volume
|
||||||
*/
|
*/
|
||||||
static void ac101_set_spk_volume(uint8_t volume)
|
static void ac101_set_spk_volume(uint8_t volume) {
|
||||||
{
|
|
||||||
uint16_t value = min(volume, 100);
|
uint16_t value = min(volume, 100);
|
||||||
value = ((int)value * 0x1f) / 100;
|
value = ((int)value * 0x1f) / 100;
|
||||||
value |= i2c_read_reg(SPKOUT_CTRL) & ~0x1f;
|
value |= i2c_read_reg(SPKOUT_CTRL) & ~0x1f;
|
||||||
@@ -322,16 +330,14 @@ static void ac101_set_spk_volume(uint8_t volume)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Get normalized (0..100) earphone volume
|
* Get normalized (0..100) earphone volume
|
||||||
*/
|
*/
|
||||||
static int ac101_get_earph_volume(void)
|
static int ac101_get_earph_volume(void) {
|
||||||
{
|
|
||||||
return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
|
return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Set normalized (0..100) earphone volume
|
* Set normalized (0..100) earphone volume
|
||||||
*/
|
*/
|
||||||
static void ac101_set_earph_volume(uint8_t volume)
|
static void ac101_set_earph_volume(uint8_t volume) {
|
||||||
{
|
|
||||||
uint16_t value = min(volume, 255);
|
uint16_t value = min(volume, 255);
|
||||||
value = (((int)value * 0x3f) / 255) << 4;
|
value = (((int)value * 0x3f) / 255) << 4;
|
||||||
value |= i2c_read_reg(HPOUT_CTRL) & ~(0x3f << 4);
|
value |= i2c_read_reg(HPOUT_CTRL) & ~(0x3f << 4);
|
||||||
@@ -341,12 +347,11 @@ static void ac101_set_earph_volume(uint8_t volume)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain, ac_output_mixer_source_t source)
|
static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain,
|
||||||
{
|
ac_output_mixer_source_t source) {
|
||||||
uint16_t regval, temp, clrbit;
|
uint16_t regval, temp, clrbit;
|
||||||
regval = i2c_read_reg(OMIXER_BST1_CTRL);
|
regval = i2c_read_reg(OMIXER_BST1_CTRL);
|
||||||
switch (source)
|
switch (source) {
|
||||||
{
|
|
||||||
case SRC_MIC1:
|
case SRC_MIC1:
|
||||||
temp = (gain & 0x7) << 6;
|
temp = (gain & 0x7) << 6;
|
||||||
clrbit = ~(0x7 << 6);
|
clrbit = ~(0x7 << 6);
|
||||||
@@ -370,24 +375,22 @@ static void ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain, ac_output_m
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void ac101_start(ac_module_t mode)
|
static void ac101_start(ac_module_t mode) {
|
||||||
{
|
if (mode == AC_MODULE_LINE) {
|
||||||
if (mode == AC_MODULE_LINE)
|
|
||||||
{
|
|
||||||
i2c_write_reg(0x51, 0x0408);
|
i2c_write_reg(0x51, 0x0408);
|
||||||
i2c_write_reg(0x40, 0x8000);
|
i2c_write_reg(0x40, 0x8000);
|
||||||
i2c_write_reg(0x50, 0x3bc0);
|
i2c_write_reg(0x50, 0x3bc0);
|
||||||
}
|
}
|
||||||
if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE)
|
if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC ||
|
||||||
{
|
mode == AC_MODULE_LINE) {
|
||||||
// I2S1_SDOUT_CTRL
|
// I2S1_SDOUT_CTRL
|
||||||
// i2c_write_reg(PLL_CTRL2, 0x8120);
|
// i2c_write_reg(PLL_CTRL2, 0x8120);
|
||||||
i2c_write_reg(0x04, 0x800c);
|
i2c_write_reg(0x04, 0x800c);
|
||||||
i2c_write_reg(0x05, 0x800c);
|
i2c_write_reg(0x05, 0x800c);
|
||||||
// res |= i2c_write_reg(0x06, 0x3000);
|
// res |= i2c_write_reg(0x06, 0x3000);
|
||||||
}
|
}
|
||||||
if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE)
|
if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC ||
|
||||||
{
|
mode == AC_MODULE_LINE) {
|
||||||
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
||||||
value |= 0x8000;
|
value |= 0x8000;
|
||||||
i2c_write_reg(PLL_CTRL2, value);
|
i2c_write_reg(PLL_CTRL2, value);
|
||||||
@@ -397,8 +400,7 @@ static void ac101_start(ac_module_t mode)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void ac101_stop(void)
|
static void ac101_stop(void) {
|
||||||
{
|
|
||||||
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
uint16_t value = i2c_read_reg(PLL_CTRL2);
|
||||||
value &= ~0x8000;
|
value &= ~0x8000;
|
||||||
i2c_write_reg(PLL_CTRL2, value);
|
i2c_write_reg(PLL_CTRL2, value);
|
||||||
@@ -407,16 +409,14 @@ static void ac101_stop(void)
|
|||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void ac101_deinit(void)
|
static void ac101_deinit(void) {
|
||||||
{
|
|
||||||
i2c_write_reg(CHIP_AUDIO_RS, 0x123); //soft reset
|
i2c_write_reg(CHIP_AUDIO_RS, 0x123); //soft reset
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Don't know when this one is supposed to be called
|
* Don't know when this one is supposed to be called
|
||||||
*/
|
*/
|
||||||
static void ac101_i2s_config_clock(ac_i2s_clock_t *cfg)
|
static void ac101_i2s_config_clock(ac_i2s_clock_t* cfg) {
|
||||||
{
|
|
||||||
uint16_t regval = 0;
|
uint16_t regval = 0;
|
||||||
regval = i2c_read_reg(I2S1LCK_CTRL);
|
regval = i2c_read_reg(I2S1LCK_CTRL);
|
||||||
regval &= 0xe03f;
|
regval &= 0xe03f;
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "es8311.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "es8311.h"
|
|
||||||
// #include "board.h"
|
// #include "board.h"
|
||||||
|
|
||||||
/* ES8311 address
|
/* ES8311 address
|
||||||
@@ -93,9 +93,12 @@ struct _coeff_div {
|
|||||||
static const struct _coeff_div coeff_div[] = {
|
static const struct _coeff_div coeff_div[] = {
|
||||||
//mclk rate prediv mult adcdiv dacdiv fsmode lrch lrcl bckdiv osr
|
//mclk rate prediv mult adcdiv dacdiv fsmode lrch lrcl bckdiv osr
|
||||||
/* 8k */
|
/* 8k */
|
||||||
{12288000, 8000 , 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 8000, 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 8000 , 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10},
|
0x10},
|
||||||
{16384000, 8000 , 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 8000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10,
|
||||||
|
0x10},
|
||||||
|
{16384000, 8000, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
{8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||||
{6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||||
{4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||||
@@ -105,91 +108,156 @@ static const struct _coeff_div coeff_div[] = {
|
|||||||
{1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
||||||
|
|
||||||
/* 11.025k */
|
/* 11.025k */
|
||||||
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{2822400 , 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1411200 , 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{2822400, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1411200, 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 12k */
|
/* 12k */
|
||||||
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{3072000 , 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1536000 , 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{3072000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 16k */
|
/* 16k */
|
||||||
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
0x10},
|
||||||
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10,
|
||||||
{8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{4096000 , 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{3072000 , 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{2048000 , 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1024000 , 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{4096000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{2048000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1024000, 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 22.05k */
|
/* 22.05k */
|
||||||
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{2822400 , 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1411200 , 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{2822400, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1411200, 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 24k */
|
/* 24k */
|
||||||
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{3072000 , 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{6144000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 32k */
|
/* 32k */
|
||||||
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
|
0x10},
|
||||||
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10,
|
||||||
{8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{4096000 , 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{3072000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{2048000 , 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
{6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1024000 , 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{4096000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{2048000, 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1024000, 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 44.1k */
|
/* 44.1k */
|
||||||
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{2822400 , 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1411200 , 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
|
{2822400, 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1411200, 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 48k */
|
/* 48k */
|
||||||
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{3072000 , 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{6144000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 64k */
|
/* 64k */
|
||||||
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
0x10},
|
||||||
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10,
|
||||||
{8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{4096000 , 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{3072000 , 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
|
{8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{2048000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
|
{6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10,
|
||||||
{1024000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
0x10},
|
||||||
|
{4096000, 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10,
|
||||||
|
0x10},
|
||||||
|
{2048000, 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18,
|
||||||
|
0x18},
|
||||||
|
{1024000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 88.2k */
|
/* 88.2k */
|
||||||
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{2822400 , 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{1411200 , 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
0x10},
|
||||||
|
{2822400, 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1411200, 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10,
|
||||||
|
0x10},
|
||||||
|
|
||||||
/* 96k */
|
/* 96k */
|
||||||
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{6144000 , 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
{3072000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
|
0x10},
|
||||||
{1536000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
|
{6144000, 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{3072000, 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
|
||||||
|
0x10},
|
||||||
|
{1536000, 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10,
|
||||||
|
0x10},
|
||||||
};
|
};
|
||||||
|
|
||||||
static char* TAG = "DRV8311";
|
static char* TAG = "DRV8311";
|
||||||
@@ -200,8 +268,7 @@ static char *TAG = "DRV8311";
|
|||||||
return b; \
|
return b; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Es8311WriteReg(uint8_t regAdd, uint8_t data)
|
static int Es8311WriteReg(uint8_t regAdd, uint8_t data) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
res |= i2c_master_start(cmd);
|
res |= i2c_master_start(cmd);
|
||||||
@@ -215,8 +282,7 @@ static int Es8311WriteReg(uint8_t regAdd, uint8_t data)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311ReadReg(uint8_t regAdd)
|
int Es8311ReadReg(uint8_t regAdd) {
|
||||||
{
|
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
@@ -240,8 +306,7 @@ int Es8311ReadReg(uint8_t regAdd)
|
|||||||
return (int)data;
|
return (int)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Es7243WriteReg(uint8_t regAdd, uint8_t data)
|
static int Es7243WriteReg(uint8_t regAdd, uint8_t data) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
res |= i2c_master_start(cmd);
|
res |= i2c_master_start(cmd);
|
||||||
@@ -255,9 +320,7 @@ static int Es7243WriteReg(uint8_t regAdd, uint8_t data)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Es7243ReadReg(uint8_t regAdd) {
|
||||||
int Es7243ReadReg(uint8_t regAdd)
|
|
||||||
{
|
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||||
@@ -281,8 +344,7 @@ int Es7243ReadReg(uint8_t regAdd)
|
|||||||
return (int)data;
|
return (int)data;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t Es7243Init(void)
|
esp_err_t Es7243Init(void) {
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
ret |= Es7243WriteReg(0x00, 0x01);
|
ret |= Es7243WriteReg(0x00, 0x01);
|
||||||
ret |= Es7243WriteReg(0x06, 0x00);
|
ret |= Es7243WriteReg(0x06, 0x00);
|
||||||
@@ -297,8 +359,7 @@ esp_err_t Es7243Init(void)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int I2cInit(i2c_config_t *conf, int i2cMasterPort)
|
static int I2cInit(i2c_config_t* conf, int i2cMasterPort) {
|
||||||
{
|
|
||||||
int res;
|
int res;
|
||||||
res = i2c_param_config(i2cMasterPort, conf);
|
res = i2c_param_config(i2cMasterPort, conf);
|
||||||
res |= i2c_driver_install(i2cMasterPort, conf->mode, 0, 0, 0);
|
res |= i2c_driver_install(i2cMasterPort, conf->mode, 0, 0, 0);
|
||||||
@@ -309,8 +370,7 @@ static int I2cInit(i2c_config_t *conf, int i2cMasterPort)
|
|||||||
/*
|
/*
|
||||||
* look for the coefficient in coeff_div[] table
|
* look for the coefficient in coeff_div[] table
|
||||||
*/
|
*/
|
||||||
static int get_coeff(uint32_t mclk, uint32_t rate)
|
static int get_coeff(uint32_t mclk, uint32_t rate) {
|
||||||
{
|
|
||||||
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
|
||||||
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
|
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
|
||||||
return i;
|
return i;
|
||||||
@@ -320,14 +380,14 @@ static int get_coeff(uint32_t mclk, uint32_t rate)
|
|||||||
/*
|
/*
|
||||||
* set es8311 clock parameter and PCM/I2S interface
|
* set es8311 clock parameter and PCM/I2S interface
|
||||||
*/
|
*/
|
||||||
static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck)
|
static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck) {
|
||||||
{
|
|
||||||
int coeff;
|
int coeff;
|
||||||
uint8_t regv, datmp;
|
uint8_t regv, datmp;
|
||||||
ESP_LOGI(TAG, "Enter into es8311_pcm_hw_params()\n");
|
ESP_LOGI(TAG, "Enter into es8311_pcm_hw_params()\n");
|
||||||
coeff = get_coeff(mclk, lrck);
|
coeff = get_coeff(mclk, lrck);
|
||||||
if (coeff < 0) {
|
if (coeff < 0) {
|
||||||
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK\n", lrck, mclk);
|
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK\n", lrck,
|
||||||
|
mclk);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,8 +472,7 @@ static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck)
|
|||||||
* if mute = 0, dac un-mute
|
* if mute = 0, dac un-mute
|
||||||
* if mute = 1, dac mute
|
* if mute = 1, dac mute
|
||||||
*/
|
*/
|
||||||
static void es8311_mute(int mute)
|
static void es8311_mute(int mute) {
|
||||||
{
|
|
||||||
uint8_t regv;
|
uint8_t regv;
|
||||||
ESP_LOGI(TAG, "Enter into es8311_mute(), mute = %d\n", mute);
|
ESP_LOGI(TAG, "Enter into es8311_mute(), mute = %d\n", mute);
|
||||||
regv = Es8311ReadReg(ES8311_DAC_REG31) & 0x9f;
|
regv = Es8311ReadReg(ES8311_DAC_REG31) & 0x9f;
|
||||||
@@ -450,8 +509,7 @@ static void es8311_mute(int mute)
|
|||||||
/*
|
/*
|
||||||
* initialize es8311 codec
|
* initialize es8311 codec
|
||||||
*/
|
*/
|
||||||
static void es8311_init(uint32_t mclk_freq, uint32_t lrck_freq)
|
static void es8311_init(uint32_t mclk_freq, uint32_t lrck_freq) {
|
||||||
{
|
|
||||||
int regv;
|
int regv;
|
||||||
Es8311WriteReg(ES8311_GP_REG45, 0x00);
|
Es8311WriteReg(ES8311_GP_REG45, 0x00);
|
||||||
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30);
|
Es8311WriteReg(ES8311_CLK_MANAGER_REG01, 0x30);
|
||||||
@@ -561,8 +619,7 @@ static void es8311_init(uint32_t mclk_freq, uint32_t lrck_freq)
|
|||||||
/*
|
/*
|
||||||
* set codec private data and initialize codec
|
* set codec private data and initialize codec
|
||||||
*/
|
*/
|
||||||
void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq)
|
void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq) {
|
||||||
{
|
|
||||||
ESP_LOGI(TAG, "Enter into es8311_Codec_Startup()\n");
|
ESP_LOGI(TAG, "Enter into es8311_Codec_Startup()\n");
|
||||||
es8311_priv->dmic_enable = false;
|
es8311_priv->dmic_enable = false;
|
||||||
es8311_priv->mclkinv = false;
|
es8311_priv->mclkinv = false;
|
||||||
@@ -596,8 +653,7 @@ void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq)
|
|||||||
// return res;
|
// return res;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
esp_err_t Es8311GetRef(bool flag)
|
esp_err_t Es8311GetRef(bool flag) {
|
||||||
{
|
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
uint8_t regv = 0;
|
uint8_t regv = 0;
|
||||||
if (flag) {
|
if (flag) {
|
||||||
@@ -610,23 +666,20 @@ esp_err_t Es8311GetRef(bool flag)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311Init(Es8311Config *cfg)
|
int Es8311Init(Es8311Config* cfg) {
|
||||||
{
|
|
||||||
es8311_priv = calloc(1, sizeof(struct es8311_private));
|
es8311_priv = calloc(1, sizeof(struct es8311_private));
|
||||||
I2cInit(&cfg->i2c_cfg, cfg->i2c_port_num); // ESP32 in master mode
|
I2cInit(&cfg->i2c_cfg, cfg->i2c_port_num); // ESP32 in master mode
|
||||||
es8311_Codec_Startup(11289600, 44100);
|
es8311_Codec_Startup(11289600, 44100);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Es8311Uninit()
|
void Es8311Uninit() {
|
||||||
{
|
|
||||||
Es8311WriteReg(ES8311_RESET_REG00, 0x3f);
|
Es8311WriteReg(ES8311_RESET_REG00, 0x3f);
|
||||||
free(es8311_priv);
|
free(es8311_priv);
|
||||||
es8311_priv = NULL;
|
es8311_priv = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt)
|
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
uint8_t regAdc = 0, regDac = 0;
|
uint8_t regAdc = 0, regDac = 0;
|
||||||
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
||||||
@@ -665,14 +718,12 @@ int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311I2sConfigClock(ESCodecI2sClock cfg)
|
int Es8311I2sConfigClock(ESCodecI2sClock cfg) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample)
|
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
uint8_t reg = 0;
|
uint8_t reg = 0;
|
||||||
int bits = (int)bitPerSample;
|
int bits = (int)bitPerSample;
|
||||||
@@ -690,32 +741,31 @@ int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311Start(ESCodecModule mode)
|
int Es8311Start(ESCodecModule mode) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
||||||
res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF);
|
res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF);
|
||||||
}
|
}
|
||||||
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
|
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
|
||||||
res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) & 0xfd);
|
res |= Es8311WriteReg(ES8311_SYSTEM_REG12,
|
||||||
|
Es8311ReadReg(ES8311_SYSTEM_REG12) & 0xfd);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311Stop(ESCodecModule mode)
|
int Es8311Stop(ESCodecModule mode) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
|
||||||
res |= Es8311WriteReg(ES8311_ADC_REG17, 0x00);
|
res |= Es8311WriteReg(ES8311_ADC_REG17, 0x00);
|
||||||
}
|
}
|
||||||
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
|
if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) {
|
||||||
res |= Es8311WriteReg(ES8311_SYSTEM_REG12, Es8311ReadReg(ES8311_SYSTEM_REG12) | 0x02);
|
res |= Es8311WriteReg(ES8311_SYSTEM_REG12,
|
||||||
|
Es8311ReadReg(ES8311_SYSTEM_REG12) | 0x02);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311SetVoiceVolume(int volume)
|
int Es8311SetVoiceVolume(int volume) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
if (volume == 0) {
|
if (volume == 0) {
|
||||||
@@ -726,8 +776,7 @@ int Es8311SetVoiceVolume(int volume)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311GetVoiceVolume(int *volume)
|
int Es8311GetVoiceVolume(int* volume) {
|
||||||
{
|
|
||||||
int res = ESP_OK;
|
int res = ESP_OK;
|
||||||
int regv = Es8311ReadReg(ES8311_DAC_REG32);
|
int regv = Es8311ReadReg(ES8311_DAC_REG32);
|
||||||
if (regv == ESP_FAIL) {
|
if (regv == ESP_FAIL) {
|
||||||
@@ -740,16 +789,14 @@ int Es8311GetVoiceVolume(int *volume)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311SetVoiceMute(int enable)
|
int Es8311SetVoiceMute(int enable) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
ESP_LOGI(TAG, "Es8311SetVoiceMute volume:%d\n", enable);
|
ESP_LOGI(TAG, "Es8311SetVoiceMute volume:%d\n", enable);
|
||||||
es8311_mute(enable);
|
es8311_mute(enable);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311GetVoiceMute(int *mute)
|
int Es8311GetVoiceMute(int* mute) {
|
||||||
{
|
|
||||||
int res = -1;
|
int res = -1;
|
||||||
uint8_t reg = 0;
|
uint8_t reg = 0;
|
||||||
res = Es8311ReadReg(ES8311_DAC_REG31);
|
res = Es8311ReadReg(ES8311_DAC_REG31);
|
||||||
@@ -760,8 +807,7 @@ int Es8311GetVoiceMute(int *mute)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311SetMicGain(MicGain gain)
|
int Es8311SetMicGain(MicGain gain) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
uint8_t gain_n = Es8311ReadReg(ES8311_ADC_REG16) & 0x07;
|
uint8_t gain_n = Es8311ReadReg(ES8311_ADC_REG16) & 0x07;
|
||||||
gain_n |= gain / 6;
|
gain_n |= gain / 6;
|
||||||
@@ -769,26 +815,22 @@ int Es8311SetMicGain(MicGain gain)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311ConfigAdcInput(AdcInput input)
|
int Es8311ConfigAdcInput(AdcInput input) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Es8311SetAdcVolume(uint8_t adc_vol)
|
int Es8311SetAdcVolume(uint8_t adc_vol) {
|
||||||
{
|
|
||||||
int res = 0;
|
int res = 0;
|
||||||
res = Es8311WriteReg(ES8311_ADC_REG17, adc_vol); // MIC ADC Volume
|
res = Es8311WriteReg(ES8311_ADC_REG17, adc_vol); // MIC ADC Volume
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ES8311WriteReg(uint8_t regAdd, uint8_t data)
|
int ES8311WriteReg(uint8_t regAdd, uint8_t data) {
|
||||||
{
|
|
||||||
return Es8311WriteReg(regAdd, data);
|
return Es8311WriteReg(regAdd, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Es8311ReadAll()
|
void Es8311ReadAll() {
|
||||||
{
|
|
||||||
for (int i = 0; i < 0x4A; i++) {
|
for (int i = 0; i < 0x4A; i++) {
|
||||||
uint8_t reg = Es8311ReadReg(i);
|
uint8_t reg = Es8311ReadReg(i);
|
||||||
// ets_printf("REG:%02x, %02x\n", reg, i);
|
// ets_printf("REG:%02x, %02x\n", reg, i);
|
||||||
|
|||||||
@@ -5,15 +5,17 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class AudioSink
|
class AudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
AudioSink() {}
|
AudioSink() {}
|
||||||
virtual ~AudioSink() {}
|
virtual ~AudioSink() {}
|
||||||
virtual void feedPCMFrames(const uint8_t* buffer, size_t bytes) = 0;
|
virtual void feedPCMFrames(const uint8_t* buffer, size_t bytes) = 0;
|
||||||
virtual void volumeChanged(uint16_t volume) {}
|
virtual void volumeChanged(uint16_t volume) {}
|
||||||
// Return false if the sink doesn't support reconfiguration.
|
// Return false if the sink doesn't support reconfiguration.
|
||||||
virtual bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) { return false; }
|
virtual bool setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
|
uint8_t bitDepth) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Deprecated. Implement/use setParams() instead.
|
// Deprecated. Implement/use setParams() instead.
|
||||||
virtual inline bool setRate(uint16_t sampleRate) {
|
virtual inline bool setRate(uint16_t sampleRate) {
|
||||||
return setParams(sampleRate, 2, 16);
|
return setParams(sampleRate, 2, 16);
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
#ifndef AC101AUDIOSINK_H
|
#ifndef AC101AUDIOSINK_H
|
||||||
#define AC101AUDIOSINK_H
|
#define AC101AUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include "esp_err.h"
|
#include <sys/unistd.h>
|
||||||
#include "esp_log.h"
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "ac101.h"
|
#include "ac101.h"
|
||||||
#include "adac.h"
|
#include "adac.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
class AC101AudioSink : public BufferedAudioSink
|
class AC101AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
AC101AudioSink();
|
AC101AudioSink();
|
||||||
~AC101AudioSink();
|
~AC101AudioSink();
|
||||||
void volumeChanged(uint16_t volume);
|
void volumeChanged(uint16_t volume);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
adac_s* dac;
|
adac_s* dac;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
#ifndef BUFFEREDAUDIOSINK_H
|
#ifndef BUFFEREDAUDIOSINK_H
|
||||||
#define BUFFEREDAUDIOSINK_H
|
#define BUFFEREDAUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "AudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "AudioSink.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class BufferedAudioSink : public AudioSink
|
class BufferedAudioSink : public AudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
|
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
|
||||||
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
bool setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
|
uint8_t bitDepth) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void startI2sFeed(size_t buf_size = 4096 * 8);
|
void startI2sFeed(size_t buf_size = 4096 * 8);
|
||||||
void feedPCMFramesInternal(const void* pvItem, size_t xItemSize);
|
void feedPCMFramesInternal(const void* pvItem, size_t xItemSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
#ifndef ES8311AUDIOSINK_H
|
#ifndef ES8311AUDIOSINK_H
|
||||||
#define ES8311AUDIOSINK_H
|
#define ES8311AUDIOSINK_H
|
||||||
|
|
||||||
#include "driver/i2s.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "driver/i2c.h"
|
#include "driver/i2c.h"
|
||||||
#include <sys/unistd.h>
|
#include "driver/i2s.h"
|
||||||
#include <sys/stat.h>
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class ES8311AudioSink : public BufferedAudioSink
|
class ES8311AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ES8311AudioSink();
|
ES8311AudioSink();
|
||||||
~ES8311AudioSink();
|
~ES8311AudioSink();
|
||||||
void writeReg(uint8_t reg_add, uint8_t data);
|
void writeReg(uint8_t reg_add, uint8_t data);
|
||||||
void volumeChanged(uint16_t volume);
|
void volumeChanged(uint16_t volume);
|
||||||
void setSampleRate(uint32_t sampleRate);
|
void setSampleRate(uint32_t sampleRate);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
#ifndef ES8388AUDIOSINK_H
|
#ifndef ES8388AUDIOSINK_H
|
||||||
#define ES8388AUDIOSINK_H
|
#define ES8388AUDIOSINK_H
|
||||||
|
|
||||||
#include "driver/i2s.h"
|
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <vector>
|
#include <stdint.h>
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
|
#include "driver/i2s.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
|
||||||
#define ES8388_ADDR 0x20
|
#define ES8388_ADDR 0x20
|
||||||
|
|
||||||
#define ACK_CHECK_EN 0x1
|
#define ACK_CHECK_EN 0x1
|
||||||
@@ -78,16 +77,14 @@
|
|||||||
#define ES8388_DACCONTROL29 0x33
|
#define ES8388_DACCONTROL29 0x33
|
||||||
#define ES8388_DACCONTROL30 0x34
|
#define ES8388_DACCONTROL30 0x34
|
||||||
|
|
||||||
class ES8388AudioSink : public BufferedAudioSink
|
class ES8388AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ES8388AudioSink();
|
ES8388AudioSink();
|
||||||
~ES8388AudioSink();
|
~ES8388AudioSink();
|
||||||
|
|
||||||
bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U);
|
bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U);
|
||||||
|
|
||||||
enum ES8388_OUT
|
enum ES8388_OUT {
|
||||||
{
|
|
||||||
ES_MAIN, // this is the DAC output volume (both outputs)
|
ES_MAIN, // this is the DAC output volume (both outputs)
|
||||||
ES_OUT1, // this is the additional gain for OUT1
|
ES_OUT1, // this is the additional gain for OUT1
|
||||||
ES_OUT2 // this is the additional gain for OUT2
|
ES_OUT2 // this is the additional gain for OUT2
|
||||||
@@ -97,6 +94,7 @@ public:
|
|||||||
void volume(const ES8388_OUT out, const uint8_t vol);
|
void volume(const ES8388_OUT out, const uint8_t vol);
|
||||||
|
|
||||||
void writeReg(uint8_t reg_add, uint8_t data);
|
void writeReg(uint8_t reg_add, uint8_t data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
i2c_config_t i2c_config;
|
i2c_config_t i2c_config;
|
||||||
i2c_port_t i2c_port = 0;
|
i2c_port_t i2c_port = 0;
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
#ifndef ES9018AUDIOSINK_H
|
#ifndef ES9018AUDIOSINK_H
|
||||||
#define ES9018AUDIOSINK_H
|
#define ES9018AUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class ES9018AudioSink : public BufferedAudioSink
|
class ES9018AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ES9018AudioSink();
|
ES9018AudioSink();
|
||||||
~ES9018AudioSink();
|
~ES9018AudioSink();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
#ifndef INTERNALAUDIOSINK_H
|
#ifndef INTERNALAUDIOSINK_H
|
||||||
#define INTERNALAUDIOSINK_H
|
#define INTERNALAUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class InternalAudioSink : public BufferedAudioSink
|
class InternalAudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
InternalAudioSink();
|
InternalAudioSink();
|
||||||
~InternalAudioSink();
|
~InternalAudioSink();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
#ifndef PCM5102AUDIOSINK_H
|
#ifndef PCM5102AUDIOSINK_H
|
||||||
#define PCM5102AUDIOSINK_H
|
#define PCM5102AUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class PCM5102AudioSink : public BufferedAudioSink
|
class PCM5102AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
PCM5102AudioSink();
|
PCM5102AudioSink();
|
||||||
~PCM5102AudioSink();
|
~PCM5102AudioSink();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
#ifndef SPDIFAUDIOSINK_H
|
#ifndef SPDIFAUDIOSINK_H
|
||||||
#define SPDIFAUDIOSINK_H
|
#define SPDIFAUDIOSINK_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class SPDIFAudioSink : public BufferedAudioSink
|
class SPDIFAudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
uint8_t spdifPin;
|
uint8_t spdifPin;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SPDIFAudioSink(uint8_t spdifPin);
|
explicit SPDIFAudioSink(uint8_t spdifPin);
|
||||||
~SPDIFAudioSink() override;
|
~SPDIFAudioSink() override;
|
||||||
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
|
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
|
||||||
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
|
bool setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
|
uint8_t bitDepth) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
#ifndef TAS5711AUDIOSINK_H
|
#ifndef TAS5711AUDIOSINK_H
|
||||||
#define TAS5711AUDIOSINK_H
|
#define TAS5711AUDIOSINK_H
|
||||||
|
|
||||||
|
|
||||||
#include "driver/i2s.h"
|
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include "BufferedAudioSink.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "BufferedAudioSink.h"
|
||||||
|
#include "driver/i2s.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
class TAS5711AudioSink : public BufferedAudioSink
|
class TAS5711AudioSink : public BufferedAudioSink {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
TAS5711AudioSink();
|
TAS5711AudioSink();
|
||||||
~TAS5711AudioSink();
|
~TAS5711AudioSink();
|
||||||
|
|
||||||
|
|
||||||
void writeReg(uint8_t reg, uint8_t value);
|
void writeReg(uint8_t reg, uint8_t value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
i2c_config_t i2c_config;
|
i2c_config_t i2c_config;
|
||||||
i2c_port_t i2c_port = 0;
|
i2c_port_t i2c_port = 0;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
|
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
/*
|
/*
|
||||||
* Clock Scheme Register definition
|
* Clock Scheme Register definition
|
||||||
*/
|
*/
|
||||||
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
|
#define ES8311_CLK_MANAGER_REG01 \
|
||||||
|
0x01 /* select clk src for mclk, enable clock for codec */
|
||||||
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
|
||||||
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
|
||||||
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
|
||||||
@@ -42,7 +43,8 @@
|
|||||||
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
#define ES8311_SYSTEM_REG11 0x11 /* system */
|
||||||
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
|
||||||
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
#define ES8311_SYSTEM_REG13 0x13 /* system */
|
||||||
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
|
#define ES8311_SYSTEM_REG14 \
|
||||||
|
0x14 /* system, select DMIC, select analog pga gain */
|
||||||
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
|
||||||
#define ES8311_ADC_REG16 0x16 /* ADC */
|
#define ES8311_ADC_REG16 0x16 /* ADC */
|
||||||
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
|
||||||
@@ -66,7 +68,6 @@
|
|||||||
|
|
||||||
#define ES8311_MAX_REGISTER 0xFF
|
#define ES8311_MAX_REGISTER 0xFF
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ESCodecMode esMode;
|
ESCodecMode esMode;
|
||||||
i2c_port_t i2c_port_num;
|
i2c_port_t i2c_port_num;
|
||||||
@@ -75,20 +76,19 @@ typedef struct {
|
|||||||
AdcInput adcInput;
|
AdcInput adcInput;
|
||||||
} Es8311Config;
|
} Es8311Config;
|
||||||
|
|
||||||
|
#define AUDIO_CODEC_ES8311_DEFAULT() \
|
||||||
#define AUDIO_CODEC_ES8311_DEFAULT(){ \
|
{ \
|
||||||
.esMode = ES_MODE_SLAVE, \
|
.esMode = ES_MODE_SLAVE, \
|
||||||
.i2c_port_num = I2C_NUM_0, \
|
.i2c_port_num = I2C_NUM_0, \
|
||||||
.i2c_cfg = { \
|
.i2c_cfg = {.mode = I2C_MODE_MASTER, \
|
||||||
.mode = I2C_MODE_MASTER, \
|
|
||||||
.sda_io_num = IIC_DATA, \
|
.sda_io_num = IIC_DATA, \
|
||||||
.scl_io_num = IIC_CLK, \
|
.scl_io_num = IIC_CLK, \
|
||||||
.sda_pullup_en = GPIO_PULLUP_ENABLE, \
|
.sda_pullup_en = GPIO_PULLUP_ENABLE, \
|
||||||
.scl_pullup_en = GPIO_PULLUP_ENABLE, \
|
.scl_pullup_en = GPIO_PULLUP_ENABLE, \
|
||||||
.master.clk_speed = 100000\
|
.master.clk_speed = 100000}, \
|
||||||
}, \
|
|
||||||
.adcInput = ADC_INPUT_LINPUT1_RINPUT1, \
|
.adcInput = ADC_INPUT_LINPUT1_RINPUT1, \
|
||||||
.dacOutput = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2,\
|
.dacOutput = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | \
|
||||||
|
DAC_OUTPUT_ROUT2, \
|
||||||
};
|
};
|
||||||
|
|
||||||
int Es8311Init(Es8311Config* cfg);
|
int Es8311Init(Es8311Config* cfg);
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <BellTask.h>
|
||||||
#include <fstream>
|
|
||||||
#include "AudioSink.h"
|
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <BellTask.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
#include "AudioSink.h"
|
||||||
|
|
||||||
#define PCM_DEVICE "default"
|
#define PCM_DEVICE "default"
|
||||||
|
|
||||||
template <typename T, int SIZE>
|
template <typename T, int SIZE>
|
||||||
class RingbufferPointer
|
class RingbufferPointer {
|
||||||
{
|
|
||||||
typedef std::unique_ptr<T> TPointer;
|
typedef std::unique_ptr<T> TPointer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RingbufferPointer()
|
explicit RingbufferPointer() {
|
||||||
{
|
|
||||||
// create objects
|
// create objects
|
||||||
for (int i = 0; i < SIZE; i++)
|
for (int i = 0; i < SIZE; i++) {
|
||||||
{
|
|
||||||
buf_[i] = std::make_unique<T>();
|
buf_[i] = std::make_unique<T>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool push(TPointer &item)
|
bool push(TPointer& item) {
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
if (full())
|
if (full())
|
||||||
return false;
|
return false;
|
||||||
@@ -44,8 +40,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pop(TPointer &item)
|
bool pop(TPointer& item) {
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
if (empty())
|
if (empty())
|
||||||
return false;
|
return false;
|
||||||
@@ -58,34 +53,22 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
void reset() {
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
head_ = tail_;
|
head_ = tail_;
|
||||||
full_ = false;
|
full_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const { return (!full_ && (head_ == tail_)); }
|
||||||
{
|
|
||||||
return (!full_ && (head_ == tail_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool full() const
|
bool full() const { return full_; }
|
||||||
{
|
|
||||||
return full_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int capacity() const
|
int capacity() const { return max_size_; }
|
||||||
{
|
|
||||||
return max_size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size() const
|
int size() const {
|
||||||
{
|
|
||||||
int size = max_size_;
|
int size = max_size_;
|
||||||
|
|
||||||
if (!full_)
|
if (!full_) {
|
||||||
{
|
|
||||||
if (head_ >= tail_)
|
if (head_ >= tail_)
|
||||||
size = head_ - tail_;
|
size = head_ - tail_;
|
||||||
else
|
else
|
||||||
@@ -105,8 +88,7 @@ private:
|
|||||||
bool full_ = 0;
|
bool full_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ALSAAudioSink : public AudioSink, public bell::Task
|
class ALSAAudioSink : public AudioSink, public bell::Task {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ALSAAudioSink();
|
ALSAAudioSink();
|
||||||
~ALSAAudioSink();
|
~ALSAAudioSink();
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <stddef.h> // for size_t
|
||||||
#include <fstream>
|
#include <stdint.h> // for uint8_t
|
||||||
#include "AudioSink.h"
|
#include <fstream> // for ofstream
|
||||||
|
|
||||||
class NamedPipeAudioSink : public AudioSink
|
#include "AudioSink.h" // for AudioSink
|
||||||
{
|
|
||||||
|
class NamedPipeAudioSink : public AudioSink {
|
||||||
public:
|
public:
|
||||||
NamedPipeAudioSink();
|
NamedPipeAudioSink();
|
||||||
~NamedPipeAudioSink();
|
~NamedPipeAudioSink();
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
#include "ALSAAudioSink.h"
|
#include "ALSAAudioSink.h"
|
||||||
|
|
||||||
ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0)
|
ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0) {
|
||||||
{
|
|
||||||
/* Open the PCM device in playback mode */
|
/* Open the PCM device in playback mode */
|
||||||
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
|
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) <
|
||||||
SND_PCM_STREAM_PLAYBACK, 0) < 0)
|
0) {
|
||||||
{
|
printf("ERROR: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE,
|
||||||
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
|
snd_strerror(pcm));
|
||||||
PCM_DEVICE, snd_strerror(pcm));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate parameters object and fill it with default values*/
|
/* Allocate parameters object and fill it with default values*/
|
||||||
@@ -57,44 +55,37 @@ ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0)
|
|||||||
this->startTask();
|
this->startTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
ALSAAudioSink::~ALSAAudioSink()
|
ALSAAudioSink::~ALSAAudioSink() {
|
||||||
{
|
|
||||||
snd_pcm_drain(pcm_handle);
|
snd_pcm_drain(pcm_handle);
|
||||||
snd_pcm_close(pcm_handle);
|
snd_pcm_close(pcm_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALSAAudioSink::runTask()
|
void ALSAAudioSink::runTask() {
|
||||||
{
|
|
||||||
std::unique_ptr<std::vector<uint8_t>> dataPtr;
|
std::unique_ptr<std::vector<uint8_t>> dataPtr;
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
if (!this->ringbuffer.pop(dataPtr)) {
|
||||||
if (!this->ringbuffer.pop(dataPtr))
|
|
||||||
{
|
|
||||||
usleep(100);
|
usleep(100);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (pcm = snd_pcm_writei(pcm_handle, dataPtr->data(), this->frames) == -EPIPE)
|
if (pcm = snd_pcm_writei(pcm_handle, dataPtr->data(), this->frames) ==
|
||||||
{
|
-EPIPE) {
|
||||||
|
|
||||||
snd_pcm_prepare(pcm_handle);
|
snd_pcm_prepare(pcm_handle);
|
||||||
}
|
} else if (pcm < 0) {
|
||||||
else if (pcm < 0)
|
|
||||||
{
|
|
||||||
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
|
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALSAAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
void ALSAAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
|
||||||
{
|
|
||||||
|
|
||||||
buff.insert(buff.end(), buffer, buffer + bytes);
|
buff.insert(buff.end(), buffer, buffer + bytes);
|
||||||
while (buff.size() > this->buff_size)
|
while (buff.size() > this->buff_size) {
|
||||||
{
|
auto ptr = std::make_unique<std::vector<uint8_t>>(
|
||||||
auto ptr = std::make_unique<std::vector<uint8_t>>(this->buff.begin(), this->buff.begin() + this->buff_size);
|
this->buff.begin(), this->buff.begin() + this->buff_size);
|
||||||
this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size, this->buff.end());
|
this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size,
|
||||||
while (!this->ringbuffer.push(ptr))
|
this->buff.end());
|
||||||
{
|
while (!this->ringbuffer.push(ptr)) {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
#include "NamedPipeAudioSink.h"
|
#include "NamedPipeAudioSink.h"
|
||||||
|
|
||||||
NamedPipeAudioSink::NamedPipeAudioSink()
|
#include <stdio.h> // for printf
|
||||||
{
|
|
||||||
|
NamedPipeAudioSink::NamedPipeAudioSink() {
|
||||||
printf("Start\n");
|
printf("Start\n");
|
||||||
this->namedPipeFile = std::ofstream("outputFifo", std::ios::binary);
|
this->namedPipeFile = std::ofstream("outputFifo", std::ios::binary);
|
||||||
printf("stop\n");
|
printf("stop\n");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NamedPipeAudioSink::~NamedPipeAudioSink()
|
NamedPipeAudioSink::~NamedPipeAudioSink() {
|
||||||
{
|
|
||||||
this->namedPipeFile.close();
|
this->namedPipeFile.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NamedPipeAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
void NamedPipeAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
|
||||||
{
|
|
||||||
// Write the actual data
|
// Write the actual data
|
||||||
this->namedPipeFile.write((char*)buffer, (long)bytes);
|
this->namedPipeFile.write((char*)buffer, (long)bytes);
|
||||||
this->namedPipeFile.flush();
|
this->namedPipeFile.flush();
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "PortAudioSink.h"
|
#include "PortAudioSink.h"
|
||||||
|
|
||||||
PortAudioSink::PortAudioSink()
|
PortAudioSink::PortAudioSink() {
|
||||||
{
|
|
||||||
Pa_Initialize();
|
Pa_Initialize();
|
||||||
this->setParams(44100, 2, 16);
|
this->setParams(44100, 2, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PortAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
|
bool PortAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount,
|
||||||
|
uint8_t bitDepth) {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
Pa_StopStream(stream);
|
Pa_StopStream(stream);
|
||||||
}
|
}
|
||||||
@@ -39,27 +39,19 @@ bool PortAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t
|
|||||||
outputParameters.suggestedLatency = 0.050;
|
outputParameters.suggestedLatency = 0.050;
|
||||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
|
|
||||||
PaError err = Pa_OpenStream(
|
PaError err = Pa_OpenStream(&stream, NULL, &outputParameters, sampleRate,
|
||||||
&stream,
|
4096 / (channelCount * bitDepth / 8), paClipOff,
|
||||||
NULL,
|
|
||||||
&outputParameters,
|
|
||||||
sampleRate,
|
|
||||||
4096 / (channelCount * bitDepth / 8),
|
|
||||||
paClipOff,
|
|
||||||
NULL, // blocking api
|
NULL, // blocking api
|
||||||
NULL
|
NULL);
|
||||||
);
|
|
||||||
Pa_StartStream(stream);
|
Pa_StartStream(stream);
|
||||||
return !err;
|
return !err;
|
||||||
}
|
}
|
||||||
|
|
||||||
PortAudioSink::~PortAudioSink()
|
PortAudioSink::~PortAudioSink() {
|
||||||
{
|
|
||||||
Pa_StopStream(stream);
|
Pa_StopStream(stream);
|
||||||
Pa_Terminate();
|
Pa_Terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
|
void PortAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
|
||||||
{
|
|
||||||
Pa_WriteStream(stream, buffer, bytes / 4);
|
Pa_WriteStream(stream, buffer, bytes / 4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
#include "BellHTTPServer.h"
|
#include "BellHTTPServer.h"
|
||||||
#include <mutex>
|
|
||||||
#include <regex>
|
#include <string.h> // for memcpy
|
||||||
#include "CivetServer.h"
|
#include <cassert> // for assert
|
||||||
#include "civetweb.h"
|
#include <exception> // for exception
|
||||||
|
#include <mutex> // for scoped_lock
|
||||||
|
#include <regex> // for sregex_token_iterator, regex
|
||||||
|
|
||||||
|
#include "BellLogger.h" // for AbstractLogger, BELL_LOG, bell
|
||||||
|
#include "CivetServer.h" // for CivetServer, CivetWebSocketHandler
|
||||||
|
#include "civetweb.h" // for mg_get_request_info, mg_printf, mg_set_user...
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
@@ -195,7 +201,8 @@ std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeJsonResponse(
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeEmptyResponse() {
|
std::unique_ptr<BellHTTPServer::HTTPResponse>
|
||||||
|
BellHTTPServer::makeEmptyResponse() {
|
||||||
auto response = std::make_unique<BellHTTPServer::HTTPResponse>();
|
auto response = std::make_unique<BellHTTPServer::HTTPResponse>();
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -225,8 +232,10 @@ void BellHTTPServer::registerNotFound(HTTPHandler handler) {
|
|||||||
|
|
||||||
std::unordered_map<std::string, std::string> BellHTTPServer::extractParams(
|
std::unordered_map<std::string, std::string> BellHTTPServer::extractParams(
|
||||||
struct mg_connection* conn) {
|
struct mg_connection* conn) {
|
||||||
|
void* data = mg_get_user_connection_data(conn);
|
||||||
|
assert(data != nullptr);
|
||||||
std::unordered_map<std::string, std::string>& params =
|
std::unordered_map<std::string, std::string>& params =
|
||||||
*(std::unordered_map<std::string, std::string>*)
|
*(std::unordered_map<std::string, std::string>*)data;
|
||||||
mg_get_user_connection_data(conn);
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
#include "BellTar.h"
|
#include "BellTar.h"
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fstream>
|
#include <sys/stat.h> // for mkdir
|
||||||
|
|
||||||
using namespace bell::BellTar;
|
using namespace bell::BellTar;
|
||||||
|
|
||||||
#include <cassert>
|
#include <algorithm> // for min
|
||||||
#include <cstdio> // for sprintf, snprintf and sscanf
|
#include <cassert> // for assert
|
||||||
|
#include <cstdint> // for uint8_t
|
||||||
|
#include <cstdio> // for sprintf, size_t, sscanf, EOF, NULL
|
||||||
#include <cstdlib> // for rand
|
#include <cstdlib> // for rand
|
||||||
#include <cstring> // for strlen and memset
|
#include <cstring> // for memset, strlen
|
||||||
#include <ctime> // for time
|
#include <ctime> // for time
|
||||||
|
#include <fstream> // for ofstream
|
||||||
|
#include <vector> // for vector
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -284,7 +288,11 @@ void reader::extract_all_files(std::string dest_directory) {
|
|||||||
auto fileName = get_next_file_name();
|
auto fileName = get_next_file_name();
|
||||||
|
|
||||||
// 0 is the normal file type, skip apple's ._ files
|
// 0 is the normal file type, skip apple's ._ files
|
||||||
|
#if __cplusplus >= 202002L
|
||||||
if (fileType == '0' && !fileName.starts_with("._")) {
|
if (fileType == '0' && !fileName.starts_with("._")) {
|
||||||
|
#else
|
||||||
|
if (fileType == '0' && fileName.find("._") != 0) {
|
||||||
|
#endif
|
||||||
std::string path = dest_directory + "/" + fileName;
|
std::string path = dest_directory + "/" + fileName;
|
||||||
|
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#include "BinaryReader.h"
|
#include "BinaryReader.h"
|
||||||
#include <stdlib.h>
|
|
||||||
|
#include <stdlib.h> // for size_t
|
||||||
|
#include <cstdint> // for uint8_t
|
||||||
|
#include <type_traits> // for remove_extent_t
|
||||||
|
|
||||||
|
#include "ByteStream.h" // for ByteStream
|
||||||
|
|
||||||
bell::BinaryReader::BinaryReader(std::shared_ptr<ByteStream> stream) {
|
bell::BinaryReader::BinaryReader(std::shared_ptr<ByteStream> stream) {
|
||||||
this->stream = stream;
|
this->stream = stream;
|
||||||
@@ -27,10 +32,7 @@ int32_t bell::BinaryReader::readInt() {
|
|||||||
if (stream->read((uint8_t*)b, 4) != 4)
|
if (stream->read((uint8_t*)b, 4) != 4)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return static_cast<int32_t>(
|
return static_cast<int32_t>((b[3]) | (b[2] << 8) | (b[1] << 16) |
|
||||||
(b[3]) |
|
|
||||||
(b[2] << 8) |
|
|
||||||
(b[1] << 16)|
|
|
||||||
(b[0] << 24));
|
(b[0] << 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,12 +41,9 @@ int16_t bell::BinaryReader::readShort() {
|
|||||||
if (stream->read((uint8_t*)b, 2) != 2)
|
if (stream->read((uint8_t*)b, 2) != 2)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return static_cast<int16_t>(
|
return static_cast<int16_t>((b[1]) | (b[0] << 8));
|
||||||
(b[1]) |
|
|
||||||
(b[0] << 8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t bell::BinaryReader::readUInt() {
|
uint32_t bell::BinaryReader::readUInt() {
|
||||||
return readInt() & 0xffffffffL;
|
return readInt() & 0xffffffffL;
|
||||||
}
|
}
|
||||||
@@ -66,7 +65,5 @@ long long bell::BinaryReader::readLong() {
|
|||||||
long high = readInt();
|
long high = readInt();
|
||||||
long low = readInt();
|
long low = readInt();
|
||||||
|
|
||||||
return static_cast<long long>(
|
return static_cast<long long>(((long long)high << 32) | low);
|
||||||
((long long) high << 32) | low );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include <BinaryStream.h>
|
#include <BinaryStream.h>
|
||||||
#include <sstream>
|
#include <stdexcept> // for runtime_error
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
#include "BufferedStream.h"
|
#include "BufferedStream.h"
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
BufferedStream::BufferedStream(
|
#include <stdlib.h> // for free, malloc
|
||||||
const std::string &taskName,
|
#include <algorithm> // for min
|
||||||
uint32_t bufferSize,
|
#include <cstdint> // for uint32_t
|
||||||
uint32_t readThreshold,
|
#include <cstring> // for memcpy
|
||||||
uint32_t readSize,
|
#include <type_traits> // for remove_extent_t
|
||||||
|
|
||||||
|
BufferedStream::BufferedStream(const std::string& taskName, uint32_t bufferSize,
|
||||||
|
uint32_t readThreshold, uint32_t readSize,
|
||||||
uint32_t readyThreshold,
|
uint32_t readyThreshold,
|
||||||
uint32_t notReadyThreshold,
|
uint32_t notReadyThreshold, bool waitForReady)
|
||||||
bool waitForReady)
|
|
||||||
: bell::Task(taskName, 4096, 5, 0) {
|
: bell::Task(taskName, 4096, 5, 0) {
|
||||||
this->bufferSize = bufferSize;
|
this->bufferSize = bufferSize;
|
||||||
this->readAt = bufferSize - readThreshold;
|
this->readAt = bufferSize - readThreshold;
|
||||||
@@ -53,7 +54,8 @@ bool BufferedStream::open(const std::shared_ptr<bell::ByteStream> &stream) {
|
|||||||
return source.get();
|
return source.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferedStream::open(const StreamReader &newReader, uint32_t initialOffset) {
|
bool BufferedStream::open(const StreamReader& newReader,
|
||||||
|
uint32_t initialOffset) {
|
||||||
if (this->running)
|
if (this->running)
|
||||||
this->close();
|
this->close();
|
||||||
reset();
|
reset();
|
||||||
@@ -97,16 +99,19 @@ uint32_t BufferedStream::lengthBetween(uint8_t *me, uint8_t *other) {
|
|||||||
|
|
||||||
size_t BufferedStream::read(uint8_t* dst, size_t len) {
|
size_t BufferedStream::read(uint8_t* dst, size_t len) {
|
||||||
if (waitForReady && isNotReady()) {
|
if (waitForReady && isNotReady()) {
|
||||||
while ((source || reader) && !isReady()) {} // end waiting after termination
|
while ((source || reader) && !isReady()) {
|
||||||
|
} // end waiting after termination
|
||||||
}
|
}
|
||||||
if (!running && !readAvailable) {
|
if (!running && !readAvailable) {
|
||||||
reset();
|
reset();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uint32_t read = 0;
|
uint32_t read = 0;
|
||||||
uint32_t toReadTotal = std::min(readAvailable.load(), static_cast<uint32_t>(len));
|
uint32_t toReadTotal =
|
||||||
|
std::min(readAvailable.load(), static_cast<uint32_t>(len));
|
||||||
while (toReadTotal > 0) {
|
while (toReadTotal > 0) {
|
||||||
uint32_t toRead = std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
|
uint32_t toRead =
|
||||||
|
std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
|
||||||
if (dst) {
|
if (dst) {
|
||||||
memcpy(dst, bufReadPtr, toRead);
|
memcpy(dst, bufReadPtr, toRead);
|
||||||
dst += toRead;
|
dst += toRead;
|
||||||
@@ -145,7 +150,8 @@ void BufferedStream::runTask() {
|
|||||||
uint32_t len;
|
uint32_t len;
|
||||||
bool wasReady = isReady();
|
bool wasReady = isReady();
|
||||||
do {
|
do {
|
||||||
uint32_t toRead = std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
|
uint32_t toRead =
|
||||||
|
std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
|
||||||
if (!source) {
|
if (!source) {
|
||||||
len = 0;
|
len = 0;
|
||||||
break;
|
break;
|
||||||
@@ -156,7 +162,11 @@ void BufferedStream::runTask() {
|
|||||||
bufWritePtr += len;
|
bufWritePtr += len;
|
||||||
if (bufWritePtr >= bufEnd) // TODO is == enough here?
|
if (bufWritePtr >= bufEnd) // TODO is == enough here?
|
||||||
bufWritePtr = buf;
|
bufWritePtr = buf;
|
||||||
} while (len && readSize < bufferSize - readAvailable); // loop until there's no more free space in the buffer
|
} while (
|
||||||
|
len &&
|
||||||
|
readSize <
|
||||||
|
bufferSize -
|
||||||
|
readAvailable); // loop until there's no more free space in the buffer
|
||||||
if (!len && reader)
|
if (!len && reader)
|
||||||
source = reader(bufferTotal);
|
source = reader(bufferTotal);
|
||||||
else if (!len)
|
else if (!len)
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
#include "CircularBuffer.h"
|
#include "CircularBuffer.h"
|
||||||
|
|
||||||
|
#include <algorithm> // for min
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
CircularBuffer::CircularBuffer(size_t dataCapacity)
|
CircularBuffer::CircularBuffer(size_t dataCapacity) {
|
||||||
{
|
|
||||||
this->dataCapacity = dataCapacity;
|
this->dataCapacity = dataCapacity;
|
||||||
buffer = std::vector<uint8_t>(dataCapacity);
|
buffer = std::vector<uint8_t>(dataCapacity);
|
||||||
this->dataSemaphore = std::make_unique<bell::WrappedSemaphore>(5);
|
this->dataSemaphore = std::make_unique<bell::WrappedSemaphore>(5);
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t CircularBuffer::write(const uint8_t *data, size_t bytes)
|
size_t CircularBuffer::write(const uint8_t* data, size_t bytes) {
|
||||||
{
|
|
||||||
if (bytes == 0)
|
if (bytes == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> guard(bufferMutex);
|
std::lock_guard<std::mutex> guard(bufferMutex);
|
||||||
size_t bytesToWrite = std::min(bytes, dataCapacity - dataSize);
|
size_t bytesToWrite = std::min(bytes, dataCapacity - dataSize);
|
||||||
// Write in a single step
|
// Write in a single step
|
||||||
if (bytesToWrite <= dataCapacity - endIndex)
|
if (bytesToWrite <= dataCapacity - endIndex) {
|
||||||
{
|
|
||||||
memcpy(buffer.data() + endIndex, data, bytesToWrite);
|
memcpy(buffer.data() + endIndex, data, bytesToWrite);
|
||||||
endIndex += bytesToWrite;
|
endIndex += bytesToWrite;
|
||||||
if (endIndex == dataCapacity)
|
if (endIndex == dataCapacity)
|
||||||
@@ -58,8 +57,7 @@ void CircularBuffer::emptyExcept(size_t sizeToSet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CircularBuffer::read(uint8_t *data, size_t bytes)
|
size_t CircularBuffer::read(uint8_t* data, size_t bytes) {
|
||||||
{
|
|
||||||
if (bytes == 0)
|
if (bytes == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -67,16 +65,14 @@ size_t CircularBuffer::read(uint8_t *data, size_t bytes)
|
|||||||
size_t bytesToRead = std::min(bytes, dataSize);
|
size_t bytesToRead = std::min(bytes, dataSize);
|
||||||
|
|
||||||
// Read in a single step
|
// Read in a single step
|
||||||
if (bytesToRead <= dataCapacity - begIndex)
|
if (bytesToRead <= dataCapacity - begIndex) {
|
||||||
{
|
|
||||||
memcpy(data, buffer.data() + begIndex, bytesToRead);
|
memcpy(data, buffer.data() + begIndex, bytesToRead);
|
||||||
begIndex += bytesToRead;
|
begIndex += bytesToRead;
|
||||||
if (begIndex == dataCapacity)
|
if (begIndex == dataCapacity)
|
||||||
begIndex = 0;
|
begIndex = 0;
|
||||||
}
|
}
|
||||||
// Read in two steps
|
// Read in two steps
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
size_t firstChunkSize = dataCapacity - begIndex;
|
size_t firstChunkSize = dataCapacity - begIndex;
|
||||||
memcpy(data, buffer.data() + begIndex, firstChunkSize);
|
memcpy(data, buffer.data() + begIndex, firstChunkSize);
|
||||||
size_t secondChunkSize = bytesToRead - firstChunkSize;
|
size_t secondChunkSize = bytesToRead - firstChunkSize;
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
#include "EncodedAudioStream.h"
|
#include "EncodedAudioStream.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <string.h> // for memcpy, memmove
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
#include <type_traits> // for remove_extent_t
|
||||||
|
#include <utility> // for move
|
||||||
|
|
||||||
|
#include "BellLogger.h" // for AbstractLogger, BELL_LOG, bell
|
||||||
|
#include "ByteStream.h" // for ByteStream
|
||||||
|
#include "DecoderGlobals.h" // for DecodersInstance, decodersInstance, AAC_...
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
EncodedAudioStream::EncodedAudioStream() {
|
EncodedAudioStream::EncodedAudioStream() {
|
||||||
@@ -171,5 +179,4 @@ void EncodedAudioStream::guessDataFormat() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodedAudioStream::readFully(uint8_t* dst, size_t nbytes) {
|
void EncodedAudioStream::readFully(uint8_t* dst, size_t nbytes) {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,55 +1,48 @@
|
|||||||
#include "FileStream.h"
|
#include "FileStream.h"
|
||||||
|
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
|
||||||
|
#include "BellLogger.h" // for bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
FileStream::FileStream(const std::string& path, std::string read)
|
FileStream::FileStream(const std::string& path, std::string read) {
|
||||||
{
|
|
||||||
file = fopen(path.c_str(), "rb");
|
file = fopen(path.c_str(), "rb");
|
||||||
if (file == NULL)
|
if (file == NULL) {
|
||||||
{
|
|
||||||
throw std::runtime_error("Could not open file: " + path);
|
throw std::runtime_error("Could not open file: " + path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileStream::~FileStream()
|
FileStream::~FileStream() {
|
||||||
{
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FileStream::read(uint8_t *buf, size_t nbytes)
|
size_t FileStream::read(uint8_t* buf, size_t nbytes) {
|
||||||
{
|
if (file == NULL) {
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Stream is closed");
|
throw std::runtime_error("Stream is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return fread(buf, 1, nbytes, file);
|
return fread(buf, 1, nbytes, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FileStream::skip(size_t nbytes)
|
size_t FileStream::skip(size_t nbytes) {
|
||||||
{
|
if (file == NULL) {
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Stream is closed");
|
throw std::runtime_error("Stream is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return fseek(file, nbytes, SEEK_CUR);
|
return fseek(file, nbytes, SEEK_CUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FileStream::position()
|
size_t FileStream::position() {
|
||||||
{
|
if (file == NULL) {
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Stream is closed");
|
throw std::runtime_error("Stream is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ftell(file);
|
return ftell(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FileStream::size()
|
size_t FileStream::size() {
|
||||||
{
|
if (file == NULL) {
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Stream is closed");
|
throw std::runtime_error("Stream is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +53,8 @@ size_t FileStream::size()
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileStream::close()
|
void FileStream::close() {
|
||||||
{
|
if (file != NULL) {
|
||||||
if (file != NULL)
|
|
||||||
{
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
file = NULL;
|
file = NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
#include "HTTPClient.h"
|
#include "HTTPClient.h"
|
||||||
|
|
||||||
|
#include <string.h> // for memcpy
|
||||||
|
#include <algorithm> // for transform
|
||||||
|
#include <cassert> // for assert
|
||||||
|
#include <cctype> // for tolower
|
||||||
|
#include <ostream> // for operator<<, basic_ostream
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
|
||||||
|
#include "BellSocket.h" // for bell
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
void HTTPClient::Response::connect(const std::string& url) {
|
void HTTPClient::Response::connect(const std::string& url) {
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
#include "SocketStream.h"
|
#include "SocketStream.h"
|
||||||
|
|
||||||
|
#include <stdint.h> // for uint8_t
|
||||||
|
#include <cstdio> // for NULL, ssize_t
|
||||||
|
|
||||||
|
#include "TCPSocket.h" // for TCPSocket
|
||||||
|
#include "TLSSocket.h" // for TLSSocket
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
|
|
||||||
int SocketBuffer::open(const std::string& hostname, int port, bool isSSL) {
|
int SocketBuffer::open(const std::string& hostname, int port, bool isSSL) {
|
||||||
if (internalSocket != nullptr) { close(); }
|
if (internalSocket != nullptr) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
if (isSSL) {
|
if (isSSL) {
|
||||||
internalSocket = std::make_unique<bell::TLSSocket>();
|
internalSocket = std::make_unique<bell::TLSSocket>();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
#include "TLSSocket.h"
|
#include "TLSSocket.h"
|
||||||
#include "X509Bundle.h"
|
|
||||||
|
#include <mbedtls/ctr_drbg.h> // for mbedtls_ctr_drbg_free, mbedtls_ctr_...
|
||||||
|
#include <mbedtls/entropy.h> // for mbedtls_entropy_free, mbedtls_entro...
|
||||||
|
#include <mbedtls/net_sockets.h> // for mbedtls_net_connect, mbedtls_net_free
|
||||||
|
#include <mbedtls/ssl.h> // for mbedtls_ssl_conf_authmode, mbedtls_...
|
||||||
|
#include <cstring> // for strlen, NULL
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
|
||||||
|
#include "BellLogger.h" // for AbstractLogger, BELL_LOG
|
||||||
|
#include "X509Bundle.h" // for shouldVerify, attach
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform TLSSocket implementation for the mbedtls
|
* Platform TLSSocket implementation for the mbedtls
|
||||||
|
|||||||
@@ -11,33 +11,43 @@ void URLParser::parse(const char* url, std::vector<std::string>& match) {
|
|||||||
* below. This needs to be changed if you update that regex */
|
* below. This needs to be changed if you update that regex */
|
||||||
|
|
||||||
// get the schema
|
// get the schema
|
||||||
if (sscanf(url, "%[^:]:/", scratch) > 0) match[1] = scratch;
|
if (sscanf(url, "%[^:]:/", scratch) > 0)
|
||||||
|
match[1] = scratch;
|
||||||
|
|
||||||
// get the host
|
// get the host
|
||||||
if (sscanf(url, "htt%*[^:]://%512[^/#?]", scratch) > 0) match[2] = scratch;
|
if (sscanf(url, "htt%*[^:]://%512[^/#?]", scratch) > 0)
|
||||||
|
match[2] = scratch;
|
||||||
|
|
||||||
// get the path
|
// get the path
|
||||||
url = strstr(url, match[2].c_str()) + match[2].size();
|
url = strstr(url, match[2].c_str()) + match[2].size();
|
||||||
if (sscanf(url, "/%512[^?]", scratch) > 0) match[3] = scratch;
|
if (sscanf(url, "/%512[^?]", scratch) > 0)
|
||||||
else if (*url && *url != '?' && *url != '#') url++;
|
match[3] = scratch;
|
||||||
|
else if (*url && *url != '?' && *url != '#')
|
||||||
|
url++;
|
||||||
|
|
||||||
// get the query
|
// get the query
|
||||||
if (match[3].size()) url += match[3].size() + 1;
|
if (match[3].size())
|
||||||
if (sscanf(url, "?%512[^#]", scratch) > 0) match[4] = scratch;
|
url += match[3].size() + 1;
|
||||||
|
if (sscanf(url, "?%512[^#]", scratch) > 0)
|
||||||
|
match[4] = scratch;
|
||||||
|
|
||||||
// get the hash
|
// get the hash
|
||||||
if (match[4].size()) url += match[4].size() + 1;
|
if (match[4].size())
|
||||||
if (sscanf(url, "#%512s", scratch) > 0) match[5] = scratch;
|
url += match[4].size() + 1;
|
||||||
|
if (sscanf(url, "#%512s", scratch) > 0)
|
||||||
|
match[5] = scratch;
|
||||||
|
|
||||||
// fix the acquired items
|
// fix the acquired items
|
||||||
match[3] = "/" + match[3];
|
match[3] = "/" + match[3];
|
||||||
if (match[4].size()) match[4] = "?" + match[4];
|
if (match[4].size())
|
||||||
|
match[4] = "?" + match[4];
|
||||||
|
|
||||||
// need at least schema and host
|
// need at least schema and host
|
||||||
if (match[1].size() == 0 || match[2].size() == 0) match.clear();
|
if (match[1].size() == 0 || match[2].size() == 0)
|
||||||
|
match.clear();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
const std::regex URLParser::urlParseRegex = std::regex(
|
const std::regex URLParser::urlParseRegex = std::regex(
|
||||||
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?");
|
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?");
|
||||||
#endif
|
#endif
|
||||||
}
|
} // namespace bell
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
#include "X509Bundle.h"
|
#include "X509Bundle.h"
|
||||||
|
|
||||||
|
#include <mbedtls/md.h> // for mbedtls_md, mbedtls_md_get_size
|
||||||
|
#include <mbedtls/pk.h> // for mbedtls_pk_can_do, mbedtls_pk_pa...
|
||||||
|
#include <mbedtls/ssl.h> // for mbedtls_ssl_conf_ca_chain, mbedt...
|
||||||
|
#include <mbedtls/x509.h> // for mbedtls_x509_buf, MBEDTLS_ERR_X5...
|
||||||
|
#include <stdlib.h> // for free, calloc
|
||||||
|
#include <string.h> // for memcmp, memcpy
|
||||||
|
#include <stdexcept> // for runtime_error
|
||||||
|
|
||||||
|
#include "BellLogger.h" // for AbstractLogger, BELL_LOG
|
||||||
|
|
||||||
using namespace bell::X509Bundle;
|
using namespace bell::X509Bundle;
|
||||||
|
|
||||||
static mbedtls_x509_crt s_dummy_crt;
|
static mbedtls_x509_crt s_dummy_crt;
|
||||||
@@ -21,7 +31,8 @@ int bell::X509Bundle::crtCheckCertificate(mbedtls_x509_crt* child,
|
|||||||
|
|
||||||
if ((ret = mbedtls_pk_parse_public_key(&parent.pk, pub_key_buf,
|
if ((ret = mbedtls_pk_parse_public_key(&parent.pk, pub_key_buf,
|
||||||
pub_key_len)) != 0) {
|
pub_key_len)) != 0) {
|
||||||
BELL_LOG(error, TAG, "PK parse failed with error %X", ret);
|
BELL_LOG(error, TAG, "PK parse failed with error 0x%04x, key len = %d", ret,
|
||||||
|
pub_key_len);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +121,8 @@ int bell::X509Bundle::crtVerifyCallback(void* buf, mbedtls_x509_crt* crt,
|
|||||||
ret = crtCheckCertificate(
|
ret = crtCheckCertificate(
|
||||||
child, s_crt_bundle.crts[middle] + CRT_HEADER_OFFSET + name_len,
|
child, s_crt_bundle.crts[middle] + CRT_HEADER_OFFSET + name_len,
|
||||||
key_len);
|
key_len);
|
||||||
|
} else {
|
||||||
|
BELL_LOG(error, TAG, "Certificate not found in bundle");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@@ -138,10 +151,13 @@ void bell::X509Bundle::init(const uint8_t* x509_bundle, size_t bundle_size) {
|
|||||||
throw std::runtime_error("Unable to allocate memory for bundle");
|
throw std::runtime_error("Unable to allocate memory for bundle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bundleBytes.resize(bundle_size);
|
||||||
|
memcpy(bundleBytes.data(), x509_bundle, bundle_size);
|
||||||
|
|
||||||
const uint8_t* cur_crt;
|
const uint8_t* cur_crt;
|
||||||
/* This is the maximum region that is allowed to access */
|
/* This is the maximum region that is allowed to access */
|
||||||
const uint8_t* bundle_end = x509_bundle + bundle_size;
|
const uint8_t* bundle_end = bundleBytes.data() + bundle_size;
|
||||||
cur_crt = x509_bundle + BUNDLE_HEADER_OFFSET;
|
cur_crt = bundleBytes.data() + BUNDLE_HEADER_OFFSET;
|
||||||
|
|
||||||
for (int i = 0; i < num_certs; i++) {
|
for (int i = 0; i < num_certs; i++) {
|
||||||
crts[i] = cur_crt;
|
crts[i] = cur_crt;
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BellLogger.h>
|
#include <BellLogger.h> // for bell
|
||||||
#include <stdlib.h>
|
#include <stdint.h> // for uint8_t
|
||||||
#include <sys/types.h>
|
#include <stdlib.h> // for free, size_t
|
||||||
#include <fstream>
|
#include <functional> // for function
|
||||||
#include <functional>
|
#include <map> // for map
|
||||||
#include <iostream>
|
#include <memory> // for unique_ptr
|
||||||
#include <map>
|
#include <mutex> // for mutex
|
||||||
#include <memory>
|
#include <string> // for string, hash, operator==, operator<
|
||||||
#include <utility>
|
#include <unordered_map> // for unordered_map
|
||||||
#include <optional>
|
#include <utility> // for pair
|
||||||
#include <regex>
|
#include <vector> // for vector
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include "CivetServer.h" // for CivetServer, CivetHandler
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include "CivetServer.h"
|
|
||||||
#include "civetweb.h"
|
|
||||||
|
|
||||||
using namespace bell;
|
using namespace bell;
|
||||||
namespace bell {
|
namespace bell {
|
||||||
@@ -46,7 +42,9 @@ class BellHTTPServer : public CivetHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
typedef std::function<std::unique_ptr<HTTPResponse>(struct mg_connection* conn)> HTTPHandler;
|
typedef std::function<std::unique_ptr<HTTPResponse>(
|
||||||
|
struct mg_connection* conn)>
|
||||||
|
HTTPHandler;
|
||||||
typedef std::function<void(struct mg_connection* conn, WSState)>
|
typedef std::function<void(struct mg_connection* conn, WSState)>
|
||||||
WSStateHandler;
|
WSStateHandler;
|
||||||
typedef std::function<void(struct mg_connection* conn, char*, size_t)>
|
typedef std::function<void(struct mg_connection* conn, char*, size_t)>
|
||||||
@@ -79,7 +77,8 @@ class BellHTTPServer : public CivetHandler {
|
|||||||
std::vector<int> getListeningPorts() { return server->getListeningPorts(); };
|
std::vector<int> getListeningPorts() { return server->getListeningPorts(); };
|
||||||
void close() { server->close(); }
|
void close() { server->close(); }
|
||||||
|
|
||||||
std::unique_ptr<HTTPResponse> makeJsonResponse(const std::string& json, int status = 200);
|
std::unique_ptr<HTTPResponse> makeJsonResponse(const std::string& json,
|
||||||
|
int status = 200);
|
||||||
std::unique_ptr<HTTPResponse> makeEmptyResponse();
|
std::unique_ptr<HTTPResponse> makeEmptyResponse();
|
||||||
|
|
||||||
void registerNotFound(HTTPHandler handler);
|
void registerNotFound(HTTPHandler handler);
|
||||||
@@ -88,7 +87,8 @@ class BellHTTPServer : public CivetHandler {
|
|||||||
void registerWS(const std::string&, WSDataHandler dataHandler,
|
void registerWS(const std::string&, WSDataHandler dataHandler,
|
||||||
WSStateHandler stateHandler);
|
WSStateHandler stateHandler);
|
||||||
|
|
||||||
static std::unordered_map<std::string, std::string> extractParams(struct mg_connection* conn);
|
static std::unordered_map<std::string, std::string> extractParams(
|
||||||
|
struct mg_connection* conn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<CivetServer> server;
|
std::unique_ptr<CivetServer> server;
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ class Socket {
|
|||||||
virtual size_t read(uint8_t* buf, size_t len) = 0;
|
virtual size_t read(uint8_t* buf, size_t len) = 0;
|
||||||
virtual bool isOpen() = 0;
|
virtual bool isOpen() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
virtual int getFd() = 0;
|
||||||
};
|
};
|
||||||
} // namespace bell
|
} // namespace bell
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream> // for istream, ostream
|
||||||
#include <string>
|
#include <string> // for string
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace bell::BellTar {
|
namespace bell::BellTar {
|
||||||
typedef long long unsigned file_size_t;
|
typedef long long unsigned file_size_t;
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdint.h> // for uint8_t, int16_t, int32_t, uint32_t
|
||||||
#include <iostream>
|
#include <stdlib.h> // for size_t
|
||||||
#include <fstream>
|
#include <memory> // for shared_ptr
|
||||||
#include <vector>
|
#include <vector> // for vector
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include "ByteStream.h"
|
|
||||||
|
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class ByteStream;
|
||||||
class BinaryReader
|
|
||||||
{
|
class BinaryReader {
|
||||||
std::shared_ptr<ByteStream> stream;
|
std::shared_ptr<ByteStream> stream;
|
||||||
size_t currentPos = 0;
|
size_t currentPos = 0;
|
||||||
|
|
||||||
@@ -28,4 +25,4 @@ namespace bell
|
|||||||
std::vector<uint8_t> readBytes(size_t);
|
std::vector<uint8_t> readBytes(size_t);
|
||||||
void skip(size_t);
|
void skip(size_t);
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef ESP_PLATFORM
|
#ifndef ESP_PLATFORM
|
||||||
#include <bit>
|
#include <bit> // for endian
|
||||||
#endif
|
#endif
|
||||||
#include <iostream>
|
#include <stdint.h> // for int16_t, int32_t, int64_t, uint16_t, uint32_t
|
||||||
#include <vector>
|
#include <cstddef> // for byte
|
||||||
|
#include <iostream> // for istream, ostream
|
||||||
|
|
||||||
namespace bell {
|
namespace bell {
|
||||||
class BinaryStream {
|
class BinaryStream {
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ByteStream.h"
|
#include <stddef.h> // for size_t
|
||||||
#include "BellTask.h"
|
#include <stdint.h> // for uint32_t, uint8_t
|
||||||
#include "WrappedSemaphore.h"
|
#include <atomic> // for atomic
|
||||||
#include <atomic>
|
#include <functional> // for function
|
||||||
#include <functional>
|
#include <memory> // for shared_ptr
|
||||||
#include <memory>
|
#include <mutex> // for mutex
|
||||||
#include <mutex>
|
#include <string> // for string
|
||||||
|
|
||||||
|
#include "BellTask.h" // for Task
|
||||||
|
#include "ByteStream.h" // for ByteStream
|
||||||
|
#include "WrappedSemaphore.h" // for WrappedSemaphore
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements a wrapper around an arbitrary bell::ByteStream,
|
* This class implements a wrapper around an arbitrary bell::ByteStream,
|
||||||
@@ -41,13 +45,9 @@ class BufferedStream : public bell::ByteStream, bell::Task {
|
|||||||
* @param waitForReady whether to wait for the buffer to be ready during reading
|
* @param waitForReady whether to wait for the buffer to be ready during reading
|
||||||
* @param endWithSource whether to end the streaming as soon as source returns 0 from read()
|
* @param endWithSource whether to end the streaming as soon as source returns 0 from read()
|
||||||
*/
|
*/
|
||||||
BufferedStream(
|
BufferedStream(const std::string& taskName, uint32_t bufferSize,
|
||||||
const std::string &taskName,
|
uint32_t readThreshold, uint32_t readSize,
|
||||||
uint32_t bufferSize,
|
uint32_t readyThreshold, uint32_t notReadyThreshold,
|
||||||
uint32_t readThreshold,
|
|
||||||
uint32_t readSize,
|
|
||||||
uint32_t readyThreshold,
|
|
||||||
uint32_t notReadyThreshold,
|
|
||||||
bool waitForReady = false);
|
bool waitForReady = false);
|
||||||
~BufferedStream() override;
|
~BufferedStream() override;
|
||||||
bool open(const StreamPtr& stream);
|
bool open(const StreamPtr& stream);
|
||||||
@@ -105,8 +105,10 @@ class BufferedStream : public bell::ByteStream, bell::Task {
|
|||||||
std::mutex runningMutex;
|
std::mutex runningMutex;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
bool terminate = false;
|
bool terminate = false;
|
||||||
bell::WrappedSemaphore readSem; // signal to start writing to buffer after reading from it
|
bell::WrappedSemaphore
|
||||||
std::mutex readMutex; // mutex for locking read operations during writing, and vice versa
|
readSem; // signal to start writing to buffer after reading from it
|
||||||
|
std::mutex
|
||||||
|
readMutex; // mutex for locking read operations during writing, and vice versa
|
||||||
uint32_t bufferSize;
|
uint32_t bufferSize;
|
||||||
uint32_t readAt;
|
uint32_t readAt;
|
||||||
uint32_t readSize;
|
uint32_t readSize;
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
#ifndef BELL_BYTE_READER_H
|
#ifndef BELL_BYTE_READER_H
|
||||||
#define BELL_BYTE_READER_H
|
#define BELL_BYTE_READER_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for reading bytes from a stream. Further implemented in HTTPStream.h
|
* A class for reading bytes from a stream. Further implemented in HTTPStream.h
|
||||||
*/
|
*/
|
||||||
namespace bell
|
namespace bell {
|
||||||
{
|
class ByteStream {
|
||||||
class ByteStream
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ByteStream(){};
|
ByteStream(){};
|
||||||
virtual ~ByteStream() = default;
|
virtual ~ByteStream() = default;
|
||||||
@@ -22,6 +20,6 @@ namespace bell
|
|||||||
virtual size_t size() = 0;
|
virtual size_t size() = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
};
|
};
|
||||||
}
|
} // namespace bell
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user