Compare commits

..

34 Commits

Author SHA1 Message Date
philippe44
ef692b1b50 more aggressive handling of Spotify loudness war... - release 2023-05-12 21:16:20 +02:00
philippe44
93a2c0969c synchronizing with host versions 2023-05-10 16:45:50 +02:00
philippe44
bc4e56eabc unify Host and Embedded versions 2023-05-10 12:26:43 +02:00
philippe44
fa91879535 more missing stuff 2023-05-07 11:50:46 +02:00
philippe44
2f9b506e9b include typos 2023-05-07 00:28:17 +02:00
philippe44
806cb054ba add missing files, removing un-nedded ones 2023-05-07 00:12:02 +02:00
philippe44
ff663a20b6 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-05-06 23:50:32 +02:00
philippe44
8bad480112 new cspot/bell 2023-05-06 23:50:26 +02:00
Sébastien
a38cb55468 Update codeql-analysis.yml [skip actions] 2023-04-26 18:02:51 -04:00
Sébastien
c0a9fd3100 Update codeql-analysis.yml [skip actions] 2023-04-26 17:39:13 -04:00
Sébastien
8cf9c16140 allow manual run - [skip actions] 2023-04-26 13:54:00 -04:00
Sébastien
12147eb570 Exclude bundles form analysis [skip actions] 2023-04-26 13:51:52 -04:00
github-actions
3afb2615c3 Update prebuilt objects [skip actions] 2023-04-19 22:26:14 +00:00
philippe44
e0e7e718ba fix vorbis & opus never ending - release 2023-04-20 00:23:23 +02:00
philippe44
f0527f70ac fix compile error 2023-04-19 12:39:48 +02:00
philippe44
3ecc09b989 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-04-19 00:43:11 +02:00
philippe44
7dbed7a67b trying to follow cspot... 2023-04-19 00:43:06 +02:00
github-actions
043d73dd6e Update prebuilt objects [skip actions] 2023-04-18 16:23:07 +00:00
philippe44
dc62afd788 backport typo - release 2023-04-18 18:19:28 +02:00
github-actions
d51df83981 Update prebuilt objects [skip actions] 2023-04-17 17:37:39 +00:00
philippe44
f4388a8c0a proper solution to track "plop" - release 2023-04-17 19:34:49 +02:00
philippe44
4ee9878a6f fix no reboot on squeezelite connection loss 2023-04-16 17:16:07 +02:00
Sébastien
025b5d8c75 Update README.md 2023-04-14 20:55:31 -04:00
Sebastien L
0bebaccf57 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-04-12 14:44:09 -04:00
Sebastien L
7e8313baf3 Merge readme with master 2023-04-12 14:44:02 -04:00
philippe44
ce4cebd994 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-04-09 10:31:01 -07:00
philippe44
ba81c2ecd5 fix plop when switchign track (still need to fix it when seeking) - release 2023-04-09 10:30:56 -07:00
github-actions
131b1d36b0 Update prebuilt objects [skip actions] 2023-04-09 13:48:46 +00:00
Sebastien L
0d37c270e2 more fixing 2023-04-09 09:43:54 -04:00
Sebastien L
6054affb81 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-04-09 09:38:05 -04:00
Sebastien L
196a1d179a Attempt to fix build [skip actions] 2023-04-08 12:36:16 -04:00
Sebastien L
7574054e22 Attempt to fix build [skip actions] 2023-04-08 12:34:19 -04:00
Sébastien
cd088d2500 Update Platform_build.yml [skip actions] 2023-04-08 12:23:37 -04:00
github-actions
d16ce964d6 Update prebuilt objects [skip actions] 2023-04-08 14:44:47 +00:00
213 changed files with 8233 additions and 7405 deletions

View File

@@ -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'
required: true
type: boolean
jobs:
bootstrap:
name: Global setup
@@ -228,5 +227,7 @@ jobs:
update_web_installer:
name: Update Web Installer After Release
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
secrets:
WEB_INSTALLER: ${{ secrets.WEB_INSTALLER }}

View File

@@ -19,6 +19,7 @@ on:
branches: [ master-cmake ]
schedule:
- cron: '19 12 * * 4'
workflow_dispatch:
jobs:
analyze:
@@ -39,7 +40,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# 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).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -62,6 +63,13 @@ jobs:
#- run: |
# make bootstrap
# 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
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

View File

@@ -1,6 +1,9 @@
name: Update Web Installer
on:
workflow_call:
secrets:
WEB_INSTALLER:
required: true
workflow_dispatch:
jobs:
update_web_installer:

207
README.md
View File

@@ -1,22 +1,23 @@
![Cross-Build](https://github.com/sle118/squeezelite-esp32/workflows/Cross-Build/badge.svg?branch=master-cmake)
![ESP-IDF v4.3.1](https://github.com/sle118/squeezelite-esp32/actions/workflows/esp-idf-v4.3-build.yml/badge.svg?branch=master-v4.3)
[![Platform Build](https://github.com/sle118/squeezelite-esp32/actions/workflows/Platform_build.yml/badge.svg)](https://github.com/sle118/squeezelite-esp32/actions/workflows/Platform_build.yml)
# 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
- 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 directly 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 from a **Bluetooth** device (iPhone, Android)
- 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)
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
- Buttons and 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)
- Monochrome, GrayScale or Color displays using SPI or I2C (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735, ST7789 and ILI9341).
- Ethernet using a Microchip LAN8720 with RMII interface or Davicom DM9051 over SPI.
- [Buttons](#buttons) and [Rotary Encoder](#rotary-encoder) and map/combine them to various functions (play, pause, volume, next ...)
- [GPIO expander](#gpio-expanders) (buttons, led and rotary)
- [IR receiver](#infrared) (no pullup resistor or capacitor needed, just the 38kHz receiver)
- [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
@@ -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.
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
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.**
### 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.)
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.
### 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
- set_GPIO: 12=green,13=red,34=jack,2=spkfault
- batt_config: channel=7,scale=20.24
- 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
- set_GPIO: `12=green,13=red,34=jack,2=spkfault`
- bat_config: `channel=7,scale=20.24`
- 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`
### 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
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
- amplifier: GPIO21
@@ -89,23 +105,30 @@ The board shown above has the following IO set
(note that some GPIO need pullups)
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:
```
[{"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
- dac_config: model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32
```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"}}]
```
for **AC101**
- 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
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_controlset: { "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
- dac_config: `model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22`
- dac_controlset:
```json
{ "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
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
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
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>]
```
<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
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>]
```
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
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.
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"} ],
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. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff
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
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
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
```
### 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]
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
- 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
- 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
- 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)
- 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_)
- 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)
- 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
- 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
```
[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)
- '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
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.
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.
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:
@@ -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.
**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
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]
```
NB: For well-known configuration, this is ignored
### 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.
@@ -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
**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 are described using a JSON string with the following syntax
```
```json
[
{"gpio":<num>,
"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)
- "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,
@@ -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
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":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.
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":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
- 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,
"normal":{"pressed":"ACTRLS_VOLDOWN"},
"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"}}
]
```
*buttons_remap*
```
`buttons_remap`:
```json
[{"gpio":4,"type":"BUTTON_LOW","pull":true,"long_press":1000,
"normal":{"pressed":"BCTRLS_DOWN"},
"longpress":{"pressed":"buttons"}},
@@ -366,7 +423,7 @@ Below is a difficult but functional 2-buttons interface for your decoding pleasu
"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.
@@ -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
**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)
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).
### 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 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 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 |
| GPIO26 | RX1 | EMAC_RXD1 |
| 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:
```
@@ -398,30 +458,31 @@ Connecting a reset pin for the LAN8720 is optional but recommended to avoid that
- 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.
#### SPI (DM9051)
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:
#### SPI (DM9051 or W5500)
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.
- 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)
| Pin Name | SPI2 | SPI3 |
| Pin Name | SPI1 | SPI2 |
| -------- | ---- | ---- |
| CS0* | 15 | 5 |
| CS | 15 | 5 |
| SCLK | 14 | 18 |
| MISO | 12 | 19 |
| MOSI | 13 | 23 |
** THIS IS NOT AVAILABLE YET, SO MORE TO COME ON HOW TO USE WIRED ETHERNET***
### 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
```
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)
# Configuration
## 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".
- 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
## 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.
- 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
@@ -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'
## 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.
## Update Squeezelite
@@ -475,20 +534,23 @@ See squeezlite command line, but keys options are
- r "<minrate>-<maxrate>"
- C <sec> : set timeout to switch off amp gpio
- W : activate WAV and AIFF header parsing
# Building everything yourself
## Setting up ESP-IDF
### 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.
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 :
```
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:
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.
@@ -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
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)
- 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

View File

@@ -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);
if (ret == 1) {
if (ret <= 1) {
int wait = 60;
wait_for_commit();
cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Rebooting in %d sec\n", wait);

View File

@@ -17,6 +17,8 @@ set(BELL_DISABLE_SINKS ON)
set(BELL_DISABLE_FMT ON)
set(BELL_DISABLE_REGEX ON)
set(BELL_ONLY_CJSON ON)
set(BELL_DISABLE_MQTT ON)
set(BELL_DISABLE_WEBSERVER ON)
set(CSPOT_TARGET_ESP32 ON)
# because CMake is so broken, the cache set below overrides a normal "set" for the first build

View File

@@ -16,7 +16,10 @@
#include <stdarg.h>
#include <ApResolve.h>
#include "BellTask.h"
#include "MDNSService.h"
#include "TrackPlayer.h"
#include "CSpotContext.h"
#include "SpircHandler.h"
#include "LoginBlob.h"
#include "CentralAudioBuffer.h"
@@ -31,70 +34,6 @@
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
*/
@@ -103,20 +42,21 @@ class cspotPlayer : public bell::Task {
private:
std::string name;
bell::WrappedSemaphore clientConnected;
std::shared_ptr<bell::CentralAudioBuffer> centralAudioBuffer;
std::atomic<bool> isPaused, isConnected;
int startOffset, volume = 0, bitrate = 160;
httpd_handle_t serverHandle;
int serverPort;
cspot_cmd_cb_t cmdHandler;
cspot_data_cb_t dataHandler;
std::string lastTrackId;
std::shared_ptr<cspot::LoginBlob> blob;
std::unique_ptr<cspot::SpircHandler> spirc;
std::unique_ptr<chunkManager> chunker;
void eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event);
void trackHandler(void);
size_t pcmWrite(uint8_t *pcm, size_t bytes, std::string_view trackId);
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;
}
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" {
static esp_err_t handleGET(httpd_req_t *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) {
switch (event->eventType) {
case cspot::SpircHandler::EventType::PLAYBACK_START: {
centralAudioBuffer->clearBuffer();
lastTrackId.clear();
// we are not playing anymore
trackStatus = TRACK_INIT;
// 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;
}
case cspot::SpircHandler::EventType::PLAY_PAUSE: {
bool pause = std::get<bool>(event->data);
cmdHandler(pause ? CSPOT_PAUSE : CSPOT_PLAY);
chunker->isPaused = pause;
isPaused = std::get<bool>(event->data);
cmdHandler(isPaused ? CSPOT_PAUSE : CSPOT_PLAY);
break;
}
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(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
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::FLUSH: {
// FLUSH is sent when there is no next, just clean everything
centralAudioBuffer->clearBuffer();
cmdHandler(CSPOT_FLUSH);
break;
}
case cspot::SpircHandler::EventType::DISC:
centralAudioBuffer->clearBuffer();
cmdHandler(CSPOT_DISC);
chunker->teardown();
isConnected = false;
break;
case cspot::SpircHandler::EventType::SEEK: {
centralAudioBuffer->clearBuffer();
cmdHandler(CSPOT_SEEK, std::get<int>(event->data));
break;
}
@@ -283,10 +229,9 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
void cspotPlayer::trackHandler(void) {
// this is just informative
auto trackInfo = spirc->getTrackPlayer()->getCurrentTrackInfo();
uint32_t 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
trackStatus = TRACK_NOTIFY;
@@ -307,7 +252,8 @@ void cspotPlayer::command(cspot_event_t event) {
break;
// setPause comes back through cspot::event with PLAY/PAUSE
case CSPOT_TOGGLE:
spirc->setPause(!chunker->isPaused);
isPaused = !isPaused;
spirc->setPause(isPaused);
break;
case CSPOT_STOP:
case CSPOT_PAUSE:
@@ -316,12 +262,11 @@ void cspotPlayer::command(cspot_event_t event) {
case CSPOT_PLAY:
spirc->setPause(false);
break;
// calling spirc->disconnect() might have been logical but it does not
// generate any cspot::event, so we need to manually force exiting player
// loop through chunker which will eventually do the disconnect
/* Calling spirc->disconnect() might have been logical but it does not
* generate any cspot::event */
case CSPOT_DISC:
cmdHandler(CSPOT_DISC);
chunker->teardown();
isConnected = false;
break;
// spirc->setRemoteVolume does not generate a cspot::event so call cmdHandler
case CSPOT_VOLUME_UP:
@@ -369,7 +314,6 @@ void cspotPlayer::runTask() {
CSPOT_LOG(info, "Spotify client connected for %s", name.c_str());
centralAudioBuffer = std::make_shared<bell::CentralAudioBuffer>(32);
auto ctx = cspot::Context::createFromBlob(blob);
if (bitrate == 320) ctx->config.audioFormat = AudioFormat_OGG_VORBIS_320;
@@ -382,11 +326,12 @@ void cspotPlayer::runTask() {
// Auth successful
if (token.size() > 0) {
spirc = std::make_unique<cspot::SpircHandler>(ctx);
isConnected = true;
// set call back to calculate a hash on trackId
spirc->getTrackPlayer()->setDataCallback(
[this](uint8_t* data, size_t bytes, std::string_view trackId, size_t sequence) {
return centralAudioBuffer->writePCM(data, bytes, sequence);
[this](uint8_t* data, size_t bytes, std::string_view trackId) {
return pcmWrite(data, bytes, trackId);
});
// set event (PLAY, VOLUME...) handler
@@ -398,20 +343,11 @@ void cspotPlayer::runTask() {
// Start handling mercury messages
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
cmdHandler(CSPOT_VOLUME, volume);
// exit when player has stopped (received a DISC)
while (chunker->isRunning) {
while (isConnected) {
ctx->session->handlePacket();
// low-accuracy polling events
@@ -444,7 +380,6 @@ void cspotPlayer::runTask() {
}
// we want to release memory ASAP and for sure
centralAudioBuffer.reset();
ctx.reset();
token.clear();

View File

@@ -1,2 +1,3 @@
CompileFlags:
CompilationDatabase: example/build # Search build/ directory for compile_commands.json

View File

@@ -7,6 +7,8 @@ project(bell)
option(BELL_DISABLE_CODECS "Disable the entire audio codec wrapper" OFF)
option(BELL_CODEC_AAC "Support libhelix-aac 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_ALAC "Support Apple ALAC 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 " Disable Fmt: ${BELL_DISABLE_FMT}")
message(STATUS " Disable Mqtt: ${BELL_DISABLE_MQTT}")
message(STATUS " Disable Regex: ${BELL_DISABLE_REGEX}")
message(STATUS " Disable Web server: ${BELL_DISABLE_WEBSERVER}")
# Include nanoPB library
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external/nanopb/extra")
find_package(Nanopb REQUIRED)
message(${NANOPB_INCLUDE_DIRS})
list(APPEND EXTRA_INCLUDES ${NANOPB_INCLUDE_DIRS})
list(APPEND EXTERNAL_INCLUDES ${NANOPB_INCLUDE_DIRS})
# CMake options
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(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
file(GLOB SOURCES
@@ -93,8 +97,6 @@ file(GLOB SOURCES
"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-dsp/include")
list(APPEND EXTRA_INCLUDES "main/audio-sinks/include")
@@ -111,7 +113,7 @@ endif()
if(APPLE)
file(GLOB APPLE_PLATFORM_SOURCES "main/platform/apple/*.cpp" "main/platform/apple/*.c")
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()
if(UNIX AND NOT APPLE)
@@ -122,7 +124,7 @@ endif()
if(WIN32)
file(GLOB WIN32_PLATFORM_SOURCES "main/platform/win32/*.cpp" "main/platform/win32/*.c")
list(APPEND SOURCES ${WIN32_PLATFORM_SOURCES})
list(APPEND EXTRA_INCLUDES "main/platform/win32")
list(APPEND EXTERNAL_INCLUDES "main/platform/win32")
endif()
# A hack to make Opus keep quiet
@@ -139,7 +141,7 @@ if(ESP_PLATFORM)
else()
find_package(Threads 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)
list(APPEND EXTRA_LIBS ${MBEDTLS_LIBRARIES} Threads::Threads)
@@ -149,6 +151,14 @@ else()
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)
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)
file(GLOB LIBHELIX_AAC_SOURCES "external/libhelix-aac/*.c")
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 CODEC_FLAGS "-DBELL_CODEC_AAC")
endif()
@@ -171,7 +181,7 @@ if(NOT BELL_DISABLE_CODECS)
if(BELL_CODEC_MP3)
file(GLOB LIBHELIX_MP3_SOURCES "external/libhelix-mp3/*.c")
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 CODEC_FLAGS "-DBELL_CODEC_MP3")
endif()
@@ -230,7 +240,7 @@ else()
file(GLOB TREMOR_SOURCES "external/tremor/*.c")
list(REMOVE_ITEM TREMOR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/external/tremor/ivorbisfile_example.c")
list(APPEND SOURCES ${TREMOR_SOURCES})
list(APPEND EXTRA_INCLUDES "external/tremor")
list(APPEND EXTERNAL_INCLUDES "external/tremor")
endif()
if(NOT BELL_DISABLE_SINKS)
@@ -247,7 +257,7 @@ if(NOT BELL_DISABLE_SINKS)
# Find ALSA if required, else remove the sink
if(BELL_SINK_ALSA)
find_package(ALSA REQUIRED)
list(APPEND EXTRA_INCLUDES ${ALSA_INCLUDE_DIRS})
list(APPEND EXTERNAL_INCLUDES ${ALSA_INCLUDE_DIRS})
list(APPEND EXTRA_LIBS ${ALSA_LIBRARIES})
else()
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
if(BELL_SINK_PORTAUDIO)
find_package(Portaudio REQUIRED)
list(APPEND EXTRA_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
list(APPEND EXTERNAL_INCLUDES ${PORTAUDIO_INCLUDE_DIRS})
list(APPEND EXTRA_LIBS ${PORTAUDIO_LIBRARIES})
else()
list(REMOVE_ITEM SINK_SOURCES "${AUDIO_SINKS_DIR}/unix/PortAudioSink.cpp")
@@ -266,6 +276,7 @@ if(NOT BELL_DISABLE_SINKS)
endif()
if(NOT BELL_ONLY_CJSON)
set(JSON_SystemInclude ON CACHE INTERNAL "")
add_subdirectory(external/nlohmann_json)
list(APPEND EXTRA_LIBS nlohmann_json::nlohmann_json)
endif()
@@ -274,22 +285,25 @@ if(BELL_EXTERNAL_CJSON)
list(APPEND EXTRA_LIBS ${BELL_EXTERNAL_CJSON})
else()
list(APPEND SOURCES "external/cJSON/cJSON.c")
list(APPEND EXTRA_INCLUDES "external/cJSON")
list(APPEND EXTERNAL_INCLUDES "external/cJSON")
endif()
if (NOT BELL_DISABLE_FMT)
list(APPEND EXTRA_INCLUDES "external/fmt/include")
list(APPEND EXTERNAL_INCLUDES "external/fmt/include")
endif()
if(WIN32 OR UNIX)
list(APPEND SOURCES "external/mdnssvc/mdns.c" "external/mdnssvc/mdnsd.c")
list(APPEND EXTRA_INCLUDES "external/mdnssvc")
list(APPEND EXTERNAL_INCLUDES "external/mdnssvc")
endif()
# file(GLOB CIVET_SRC "external/civetweb/*.c" "external/civetweb/*.inl" "external/civetweb/*.cpp")
# list(APPEND SOURCES ${CIVET_SRC})
# list(APPEND EXTRA_INCLUDES "external/civetweb/include")
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 EXTRA_INCLUDES "external/civetweb/include")
else()
list(REMOVE_ITEM SOURCES "${IO_DIR}/BellHTTPServer.cpp")
endif()
add_library(bell STATIC ${SOURCES})
@@ -305,6 +319,7 @@ endif()
# PUBLIC to propagate esp-idf includes to bell dependents
target_link_libraries(bell PUBLIC ${EXTRA_LIBS})
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)
if(BELL_DISABLE_CODECS)

View File

@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.18)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_BUILD_TYPE Debug)
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)
file(GLOB SOURCES "*.cpp")

View File

@@ -1,26 +1,14 @@
#include <memory.h>
#include <atomic>
#include <cmath>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <vector>
#include "AudioCodecs.h"
#include "AudioContainers.h"
#include "BellHTTPServer.h"
#include "BellTar.h"
#include <string>
#include <type_traits>
#include "BellTask.h"
#include "CentralAudioBuffer.h"
#include "Compressor.h"
#include "DecoderGlobals.h"
#include "EncodedAudioStream.h"
#include "HTTPClient.h"
#include "PortAudioSink.h"
#define DEBUG_LEVEL 4
#include "X509Bundle.h"
#include "mbedtls/debug.h"
#include "StreamInfo.h"
#define DEBUG_LEVEL 4
#include <BellDSP.h>
#include <BellLogger.h>
@@ -58,13 +46,8 @@ class AudioPlayer : bell::Task {
int main() {
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);
reader.extract_all_files("./dupa2");
BELL_LOG(info, "cock", "Published?");
return 0;
}

View File

@@ -118,13 +118,16 @@
#
#=============================================================================
function(NANOPB_GENERATE_CPP SRCS HDRS)
cmake_parse_arguments(NANOPB_GENERATE_CPP "" "RELPATH" "" ${ARGN})
if(NOT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS)
return()
endif()
if(MSVC)
set(CUSTOM_COMMAND_PREFIX call)
endif()
if(NANOPB_GENERATE_CPP_APPEND_PATH)
# Create an include path for each file specified
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})
add_custom_command(
OUTPUT ${output}
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
COMMAND ${CUSTOM_COMMAND_PREFIX} ${PROTOBUF_PROTOC_EXECUTABLE}
ARGS -I${GENERATOR_PATH}/proto
--python_out=${GENERATOR_CORE_DIR} ${ABS_FIL}
DEPENDS ${ABS_FIL}
@@ -276,7 +279,7 @@ function(NANOPB_GENERATE_CPP SRCS HDRS)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.c"
"${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}
-I${CMAKE_CURRENT_BINARY_DIR} ${_nanopb_include_path}
--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(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
if(MSVC)
unset(CUSTOM_COMMAND_PREFIX)
endif()
endfunction()

View File

@@ -1,5 +1,12 @@
#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;

View File

@@ -1,27 +1,37 @@
#include "AudioCodecs.h"
#include <cstring>
#include <iostream>
#include <map>
#include <map> // for map, operator!=, map<>::iterator, map<>:...
#include <type_traits> // for remove_extent_t
#include "AudioContainer.h" // for AudioContainer
namespace bell {
class BaseCodec;
} // namespace bell
using namespace bell;
#ifdef BELL_CODEC_AAC
#include "AACDecoder.h"
#include "AACDecoder.h" // for AACDecoder
static std::shared_ptr<AACDecoder> codecAac;
#endif
#ifdef BELL_CODEC_MP3
#include "MP3Decoder.h"
#include "MP3Decoder.h" // for MP3Decoder
static std::shared_ptr<MP3Decoder> codecMp3;
#endif
#ifdef BELL_CODEC_VORBIS
#include "VorbisDecoder.h"
#include "VorbisDecoder.h" // for VorbisDecoder
static std::shared_ptr<VorbisDecoder> codecVorbis;
#endif
#ifdef BELL_CODEC_OPUS
#include "OPUSDecoder.h"
#include "OPUSDecoder.h" // for OPUSDecoder
static std::shared_ptr<OPUSDecoder> codecOpus;
#endif

View File

@@ -1,5 +1,7 @@
#include "BaseCodec.h"
#include <iostream>
#include "AudioContainer.h" // for AudioContainer
#include "CodecType.h" // for bell
using namespace bell;

View File

@@ -2,7 +2,6 @@
bell::DecodersInstance* bell::decodersInstance;
void bell::createDecoders()
{
void bell::createDecoders() {
bell::decodersInstance = new bell::DecodersInstance();
}

View File

@@ -1,5 +1,13 @@
#include "MP3Decoder.h"
#include <stdlib.h> // for free, malloc
#include "CodecType.h" // for bell
namespace bell {
class AudioContainer;
} // namespace bell
using namespace bell;
MP3Decoder::MP3Decoder() {

View File

@@ -1,5 +1,9 @@
#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;

View File

@@ -1,5 +1,13 @@
#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;

View File

@@ -1,9 +1,12 @@
#pragma once
#include "BaseCodec.h"
#include "aacdec.h"
#include <stdint.h> // for uint8_t, uint32_t, int16_t
#include "BaseCodec.h" // for BaseCodec
#include "aacdec.h" // for AACFrameInfo, HAACDecoder
namespace bell {
class AudioContainer;
class AACDecoder : public BaseCodec {
private:

View File

@@ -1,11 +1,12 @@
#pragma once
#include <memory>
#include "BaseCodec.h"
#include "AudioContainer.h"
#include <memory> // for shared_ptr
#include "AudioContainer.h" // for AudioContainer
#include "BaseCodec.h" // for BaseCodec
#include "CodecType.h" // for AudioCodec
namespace bell {
class AudioCodecs {
public:
static std::shared_ptr<BaseCodec> getCodec(AudioCodec type);

View File

@@ -1,8 +1,9 @@
#pragma once
#include "AudioContainer.h"
#include <stdint.h> // for uint32_t, uint8_t
namespace bell {
class AudioContainer;
class BaseCodec {
private:

View File

@@ -5,20 +5,16 @@
#define AAC_READBUF_SIZE (4 * AAC_MAINBUF_SIZE * AAC_MAX_NCHANS)
#define MP3_READBUF_SIZE (2 * 1024);
#include <stdio.h>
#include <stdlib.h>
#include <memory>
#include "aacdec.h"
#include "mp3dec.h"
#include <stdio.h> // for NULL
namespace bell
{
class DecodersInstance
{
#include "aacdec.h" // for AACFreeDecoder, AACInitDecoder, HAACDecoder
#include "mp3dec.h" // for MP3FreeDecoder, MP3InitDecoder, HMP3Decoder
namespace bell {
class DecodersInstance {
public:
DecodersInstance(){};
~DecodersInstance()
{
~DecodersInstance() {
MP3FreeDecoder(mp3Decoder);
AACFreeDecoder(aacDecoder);
};
@@ -26,27 +22,23 @@ namespace bell
HAACDecoder aacDecoder = NULL;
HMP3Decoder mp3Decoder = NULL;
void ensureAAC()
{
if (aacDecoder == NULL)
{
void ensureAAC() {
if (aacDecoder == NULL) {
aacDecoder = AACInitDecoder();
}
}
void ensureMP3()
{
if (mp3Decoder == NULL)
{
void ensureMP3() {
if (mp3Decoder == NULL) {
mp3Decoder = MP3InitDecoder();
}
}
};
};
extern bell::DecodersInstance* decodersInstance;
extern bell::DecodersInstance* decodersInstance;
void createDecoders();
}
void createDecoders();
} // namespace bell
#endif
#endif

View File

@@ -1,9 +1,13 @@
#pragma once
#include "BaseCodec.h"
#include "mp3dec.h"
#include <stdint.h> // for uint8_t, uint32_t, int16_t
#include "BaseCodec.h" // for BaseCodec
#include "mp3dec.h" // for HMP3Decoder, MP3FrameInfo
namespace bell {
class AudioContainer;
class MP3Decoder : public BaseCodec {
private:
HMP3Decoder mp3;

View File

@@ -1,6 +1,8 @@
#pragma once
#include "BaseCodec.h"
#include <stdint.h> // for uint8_t, uint32_t, int16_t
#include "BaseCodec.h" // for BaseCodec
struct OpusDecoder;

View File

@@ -1,9 +1,14 @@
#pragma once
#include "BaseCodec.h"
#include "ivorbiscodec.h"
#include <stdint.h> // for uint8_t, uint32_t, int16_t
#include "BaseCodec.h" // for BaseCodec
#include "ivorbiscodec.h" // for vorbis_comment, vorbis_dsp_state, vorbis_info
#include "ogg.h" // for ogg_packet
namespace bell {
class AudioContainer;
class VorbisDecoder : public BaseCodec {
private:
vorbis_info* vi = nullptr;

View File

@@ -1,5 +1,10 @@
#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;
#define SYNC_WORLD_LEN 4

View File

@@ -1,5 +1,16 @@
#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;
std::unique_ptr<bell::AudioContainer> AudioContainers::guessAudioContainer(
@@ -7,8 +18,7 @@ std::unique_ptr<bell::AudioContainer> AudioContainers::guessAudioContainer(
std::byte tmp[14];
istr.read((char*)tmp, sizeof(tmp));
if (memcmp(tmp, "\xFF\xF1", 2) == 0 ||
memcmp(tmp, "\xFF\xF9", 2) == 0) {
if (memcmp(tmp, "\xFF\xF1", 2) == 0 || memcmp(tmp, "\xFF\xF9", 2) == 0) {
// AAC found
std::cout << "AAC" << std::endl;
return std::make_unique<bell::AACContainer>(istr);

View File

@@ -1,5 +1,10 @@
#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;
MP3Container::MP3Container(std::istream& istr) : bell::AudioContainer(istr) {}

View File

@@ -1,10 +1,12 @@
#pragma once
#include <cstring>
#include <cstddef>
#include <vector>
#include "AudioContainer.h"
#include "aacdec.h"
#include <stdint.h> // for uint32_t
#include <cstddef> // for byte, size_t
#include <istream> // for istream
#include <vector> // for vector
#include "AudioContainer.h" // for AudioContainer
#include "CodecType.h" // for AudioCodec, AudioCodec::AAC
namespace bell {
class AACContainer : public AudioContainer {

View File

@@ -1,8 +1,8 @@
#pragma once
#include <cstddef>
#include <istream>
#include <cstring>
#include <istream>
#include "CodecType.h"
#include "StreamInfo.h"

View File

@@ -1,10 +1,11 @@
#pragma once
#include <iostream>
#include <memory>
#include "AACContainer.h"
#include "AudioContainer.h"
#include "MP3Container.h"
#include <iostream> // for istream
#include <memory> // for unique_ptr
namespace bell {
class AudioContainer;
} // namespace bell
namespace bell::AudioContainers {
std::unique_ptr<bell::AudioContainer> guessAudioContainer(std::istream& istr);

View File

@@ -1,10 +1,12 @@
#pragma once
#include <cstring>
#include <cstddef>
#include <vector>
#include "AudioContainer.h"
#include "mp3dec.h"
#include <stdint.h> // for uint32_t
#include <cstddef> // for byte, size_t
#include <istream> // for istream
#include <vector> // for vector
#include "AudioContainer.h" // for AudioContainer
#include "CodecType.h" // for AudioCodec, AudioCodec::MP3
namespace bell {
class MP3Container : public AudioContainer {

View File

@@ -1,36 +1,41 @@
#include "AudioMixer.h"
#include <mutex> // for scoped_lock
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);
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;
for (auto &singleConf : mixerConfig) {
for (auto& singleConf : mixerConfig) {
if (singleConf.source.size() == 1) {
if (singleConf.source[0] == singleConf.destination) {
continue;
}
// Copy channel
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 {
// Mix channels
float sample = 0.0f;
for (int i = 0; i < info->numSamples; i++) {
sample = 0.0;
for (auto &source : singleConf.source) {
for (auto& source : singleConf.source) {
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();
}
}
}

View File

@@ -1,10 +1,15 @@
#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;
AudioPipeline::AudioPipeline() {
AudioPipeline::AudioPipeline(){
// this->headroomGainTransform = std::make_shared<Gain>(Channels::LEFT_RIGHT);
// this->transforms.push_back(this->headroomGainTransform);
};
@@ -37,9 +42,10 @@ void AudioPipeline::volumeUpdated(int volume) {
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);
for (auto &transform : transforms) {
for (auto& transform : transforms) {
data = transform->process(std::move(data));
}

View File

@@ -1,6 +1,10 @@
#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;

View File

@@ -1,25 +1,23 @@
#include "Biquad.h"
#include <cmath> // for pow, cosf, sinf, M_PI, sqrtf, tanf, logf, sinh
using namespace bell;
Biquad::Biquad()
{
Biquad::Biquad() {
this->filterType = "biquad";
}
void Biquad::sampleRateChanged(uint32_t sampleRate)
{
void Biquad::sampleRateChanged(uint32_t sampleRate) {
this->sampleRate = sampleRate;
//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->currentConfig = newConf;
switch (type)
{
switch (type) {
case Type::Free:
coeffs[0] = newConf["a1"];
coeffs[1] = newConf["a2"];
@@ -41,12 +39,10 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
break;
case Type::Highshelf:
// check if config has slope key
if (newConf.find("slope") != newConf.end())
{
highShelfCoEffsSlope(newConf["freq"], newConf["gain"], newConf["slope"]);
}
else
{
if (newConf.find("slope") != newConf.end()) {
highShelfCoEffsSlope(newConf["freq"], newConf["gain"],
newConf["slope"]);
} else {
highShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
}
break;
@@ -55,12 +51,9 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
break;
case Type::Lowshelf:
// 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"]);
}
else
{
} else {
lowShelfCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
}
break;
@@ -69,42 +62,32 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
break;
case Type::Peaking:
// check if config has bandwidth key
if (newConf.find("bandwidth") != newConf.end())
{
peakCoEffsBandwidth(newConf["freq"], newConf["gain"], newConf["bandwidth"]);
}
else
{
if (newConf.find("bandwidth") != newConf.end()) {
peakCoEffsBandwidth(newConf["freq"], newConf["gain"],
newConf["bandwidth"]);
} else {
peakCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
}
break;
case Type::Notch:
if (newConf.find("bandwidth") != newConf.end())
{
notchCoEffsBandwidth(newConf["freq"], newConf["gain"], newConf["bandwidth"]);
}
else
{
if (newConf.find("bandwidth") != newConf.end()) {
notchCoEffsBandwidth(newConf["freq"], newConf["gain"],
newConf["bandwidth"]);
} else {
notchCoEffs(newConf["freq"], newConf["gain"], newConf["q"]);
}
break;
case Type::Bandpass:
if (newConf.find("bandwidth") != newConf.end())
{
if (newConf.find("bandwidth") != newConf.end()) {
bandPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
}
else
{
} else {
bandPassCoEffs(newConf["freq"], newConf["q"]);
}
break;
case Type::Allpass:
if (newConf.find("bandwidth") != newConf.end())
{
if (newConf.find("bandwidth") != newConf.end()) {
allPassCoEffsBandwidth(newConf["freq"], newConf["bandwidth"]);
}
else
{
} else {
allPassCoEffs(newConf["freq"], newConf["q"]);
}
break;
@@ -115,8 +98,7 @@ void Biquad::configure(Type type, std::map<std::string, float> &newConf)
}
// 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 c = cosf(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
void Biquad::highPassFOCoEffs(float f)
{
void Biquad::highPassFOCoEffs(float f) {
float w0 = 2 * M_PI * f / this->sampleRate;
float k = tanf(w0 / 2.0);
@@ -151,8 +132,7 @@ void Biquad::highPassFOCoEffs(float f)
}
// 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 c = cosf(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
void Biquad::lowPassFOCoEffs(float f)
{
void Biquad::lowPassFOCoEffs(float f) {
float w0 = 2 * M_PI * f / this->sampleRate;
float k = tanf(w0 / 2.0);
@@ -187,8 +166,7 @@ void Biquad::lowPassFOCoEffs(float f)
}
// 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 c = cosf(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);
}
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 c = cosf(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);
}
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 w0 = 2 * M_PI * f / this->sampleRate;
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);
}
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 w0 = 2 * M_PI * f / this->sampleRate;
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);
}
void Biquad::highShelfFOCoEffs(float f, float gain)
{
void Biquad::highShelfFOCoEffs(float f, float gain) {
float A = std::pow(10.0f, gain / 40.0f);
float w0 = 2 * M_PI * f / this->sampleRate;
float tn = tanf(w0 / 2.0);
@@ -433,8 +407,8 @@ void Biquad::allPassFOCoEffs(float f) {
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[1] = b1 / 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;
}
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);
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);
#else
// 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];
input[i] = coeffs[0] * d0 + coeffs[1] * w[0] + coeffs[2] * w[1];
w[1] = w[0];

View File

@@ -1,31 +1,28 @@
#include "BiquadCombo.h"
#include <stdio.h> // for printf
#include <cmath> // for sinf, M_PI
#include <utility> // for move
using namespace bell;
BiquadCombo::BiquadCombo()
{
}
BiquadCombo::BiquadCombo() {}
void BiquadCombo::sampleRateChanged(uint32_t sampleRate)
{
for (auto &biquad : biquads)
{
void BiquadCombo::sampleRateChanged(uint32_t sampleRate) {
for (auto& biquad : biquads) {
biquad->sampleRateChanged(sampleRate);
}
}
std::vector<float> BiquadCombo::calculateBWQ(int order)
{
std::vector<float> BiquadCombo::calculateBWQ(int order) {
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)));
qValues.push_back(q);
}
if (order % 2 > 0)
{
if (order % 2 > 0) {
qValues.push_back(-1.0);
}
@@ -34,37 +31,28 @@ std::vector<float> BiquadCombo::calculateBWQ(int order)
return qValues;
}
std::vector<float> BiquadCombo::calculateLRQ(int order)
{
std::vector<float> BiquadCombo::calculateLRQ(int order) {
auto qValues = calculateBWQ(order / 2);
if (order % 4 > 0)
{
if (order % 4 > 0) {
qValues.pop_back();
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
qValues.push_back(0.5);
}
else
{
} else {
qValues.insert(qValues.end(), qValues.begin(), qValues.end());
}
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);
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);
for (auto &q : qValues)
{
for (auto& q : qValues) {
auto filter = std::make_unique<Biquad>();
filter->channel = channel;
@@ -72,25 +60,16 @@ void BiquadCombo::linkwitzRiley(float freq, int order, FilterType type)
config["freq"] = freq;
config["q"] = q;
if (q >= 0.0)
{
if (type == FilterType::Highpass)
{
if (q >= 0.0) {
if (type == FilterType::Highpass) {
filter->configure(Biquad::Type::Highpass, config);
}
else
{
} else {
filter->configure(Biquad::Type::Lowpass, config);
}
}
else
{
if (type == FilterType::Highpass)
{
} else {
if (type == FilterType::Highpass) {
filter->configure(Biquad::Type::HighpassFO, config);
}
else
{
} else {
filter->configure(Biquad::Type::LowpassFO, config);
}
}
@@ -99,9 +78,10 @@ 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);
for (auto &transform : this->biquads) {
for (auto& transform : this->biquads) {
data = transform->process(std::move(data));
}

View File

@@ -1,5 +1,7 @@
#include "Compressor.h"
#include <cstdlib> // for abs
using namespace bell;
float log2f_approx(float X) {
@@ -19,11 +21,11 @@ float log2f_approx(float X) {
Compressor::Compressor() {}
void Compressor::sumChannels(std::unique_ptr<StreamInfo> &data) {
void Compressor::sumChannels(std::unique_ptr<StreamInfo>& data) {
tmp.resize(data->numSamples);
for (int i = 0; i < data->numSamples; i++) {
float sum = 0.0f;
for (auto &channel : channels) {
for (auto& channel : channels) {
sum += data->data[channel][i];
}
tmp[i] = sum;
@@ -31,7 +33,7 @@ void Compressor::sumChannels(std::unique_ptr<StreamInfo> &data) {
}
void Compressor::calLoudness() {
for (auto &value : tmp) {
for (auto& value : tmp) {
value = 20 * log10f_fast(std::abs(value) + 1.0e-9f);
if (value >= lastLoudness) {
value = attack * lastLoudness + (1.0 - attack) * value;
@@ -44,7 +46,7 @@ void Compressor::calLoudness() {
}
void Compressor::calGain() {
for (auto &value : tmp) {
for (auto& value : tmp) {
if (value > threshold) {
value = -(value - threshold) * (factor - 1.0) / factor;
} else {
@@ -58,9 +60,9 @@ void Compressor::calGain() {
}
}
void Compressor::applyGain(std::unique_ptr<StreamInfo> &data) {
void Compressor::applyGain(std::unique_ptr<StreamInfo>& data) {
for (int i = 0; i < data->numSamples; i++) {
for (auto &channel : channels) {
for (auto& channel : channels) {
data->data[channel][i] *= tmp[i];
}
}

View File

@@ -1,28 +1,26 @@
#include "Gain.h"
#include <cmath> // for pow
#include <string> // for string
using namespace bell;
Gain::Gain() : AudioTransform()
{
Gain::Gain() : AudioTransform() {
this->gainFactor = 1.0f;
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->gainDb = gainDB;
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);
for (int i = 0; i < data->numSamples; i++)
{
for (int i = 0; i < data->numSamples; i++) {
// Apply gain to all channels
for (auto &channel : channels)
{
for (auto& channel : channels) {
data->data[channel][i] *= gainFactor;
}
}

View File

@@ -1,23 +1,22 @@
#pragma once
#include <vector>
#include <algorithm>
#include <cJSON.h>
#include <cJSON.h> // for cJSON_GetObjectItem, cJSON, cJSON_IsArray
#include <stddef.h> // for NULL
#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
{
class AudioMixer : public bell::AudioTransform
{
namespace bell {
class AudioMixer : public bell::AudioTransform {
public:
enum DownmixMode
{
DEFAULT
};
enum DownmixMode { DEFAULT };
struct MixerConfig
{
struct MixerConfig {
std::vector<int> source;
int destination;
};
@@ -33,50 +32,42 @@ namespace bell
// Configuration of each channels in the mixer
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)
{
cJSON *mappedChannels = cJSON_GetObjectItem(json, "mapped_channels");
void fromJSON(cJSON* json) {
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");
}
this->mixerConfig = std::vector<MixerConfig>();
cJSON *iterator = NULL;
cJSON_ArrayForEach(iterator, mappedChannels)
{
cJSON* iterator = NULL;
cJSON_ArrayForEach(iterator, mappedChannels) {
std::vector<int> sources(0);
cJSON *iteratorNested = NULL;
cJSON_ArrayForEach(iteratorNested, cJSON_GetObjectItem(iterator, "source"))
{
cJSON* iteratorNested = NULL;
cJSON_ArrayForEach(iteratorNested,
cJSON_GetObjectItem(iterator, "source")) {
sources.push_back(iteratorNested->valueint);
}
int destination = cJSON_GetObjectItem(iterator, "destination")->valueint;
this->mixerConfig.push_back(MixerConfig{
.source = sources,
.destination = destination
});
this->mixerConfig.push_back(
MixerConfig{.source = sources, .destination = destination});
}
std::vector<uint8_t> sources(0);
for (auto &config : mixerConfig)
{
for (auto& config : mixerConfig) {
for (auto &source : config.source)
{
if (std::find(sources.begin(), sources.end(), source) == sources.end())
{
for (auto& source : config.source) {
if (std::find(sources.begin(), sources.end(), source) ==
sources.end()) {
sources.push_back(source);
}
}
@@ -85,5 +76,5 @@ namespace bell
this->from = sources.size();
this->to = mixerConfig.size();
}
};
}
};
} // namespace bell

View File

@@ -1,15 +1,16 @@
#pragma once
#include "AudioTransform.h"
#include "StreamInfo.h"
#include <memory>
#include "Gain.h"
#include <mutex>
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <vector> // for vector
namespace bell
{
class AudioPipeline
{
#include "StreamInfo.h" // for StreamInfo
namespace bell {
class AudioTransform;
class Gain;
class AudioPipeline {
private:
std::shared_ptr<Gain> headroomGainTransform;
@@ -24,5 +25,5 @@ namespace bell
void addTransform(std::shared_ptr<AudioTransform> transform);
void volumeUpdated(int volume);
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data);
};
};
}; // namespace bell

View File

@@ -1,29 +1,28 @@
#pragma once
#include <memory>
#include <thread>
#include <mutex>
#include <thread>
#include "StreamInfo.h"
#include "TransformConfig.h"
namespace bell
{
class AudioTransform
{
namespace bell {
class AudioTransform {
protected:
std::mutex accessMutex;
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 float calculateHeadroom() { return 0; };
virtual void reconfigure() {};
virtual void reconfigure(){};
std::string filterType;
std::unique_ptr<TransformConfig> config;
AudioTransform() = default;
virtual ~AudioTransform() = default;
};
};
}; // namespace bell

View File

@@ -1,34 +1,43 @@
#pragma once
#include <memory>
#include <mutex>
#include <vector>
#include "AudioPipeline.h"
#include "CentralAudioBuffer.h"
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint8_t
#include <functional> // for function
#include <memory> // for shared_ptr, unique_ptr
#include <mutex> // for mutex
#include <vector> // for vector
#include "StreamInfo.h" // for BitWidth
namespace bell {
class AudioPipeline;
class CentralAudioBuffer;
#define MAX_INT16 32767
class BellDSP {
public:
BellDSP(std::shared_ptr<CentralAudioBuffer> centralAudioBuffer);
~BellDSP() {};
~BellDSP(){};
class AudioEffect {
public:
AudioEffect() = default;
~AudioEffect() = default;
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:
std::function<void()> onFinish;
bool isFadeIn;
public:
FadeEffect(size_t duration, bool isFadeIn, std::function<void()> onFinish = nullptr);
~FadeEffect() {};
FadeEffect(size_t duration, bool isFadeIn,
std::function<void()> onFinish = nullptr);
~FadeEffect(){};
void apply(float* sampleData, size_t samples, size_t relativePosition);
};
@@ -38,8 +47,8 @@ class BellDSP {
std::shared_ptr<AudioPipeline> getActivePipeline();
size_t process(uint8_t* data, size_t bytes, int channels,
uint32_t sampleRate, BitWidth bitWidth);
size_t process(uint8_t* data, size_t bytes, int channels, uint32_t sampleRate,
BitWidth bitWidth);
private:
std::shared_ptr<AudioPipeline> activePipeline;
@@ -48,7 +57,6 @@ class BellDSP {
std::vector<float> dataLeft = std::vector<float>(1024);
std::vector<float> dataRight = std::vector<float>(1024);
std::unique_ptr<AudioEffect> underflowEffect = nullptr;
std::unique_ptr<AudioEffect> startEffect = nullptr;
std::unique_ptr<AudioEffect> instantEffect = nullptr;

View File

@@ -1,23 +1,29 @@
#pragma once
#include <cmath>
#include <mutex>
#include <map>
#include <unordered_map>
#include <stdint.h> // for uint32_t
#include <map> // for map
#include <memory> // for unique_ptr, allocator
#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"
extern "C" int dsps_biquad_f32_ae32(const float *input, float *output, int len, float *coef, float *w);
#include "AudioTransform.h" // for AudioTransform
#include "StreamInfo.h" // for StreamInfo
#include "TransformConfig.h" // for TransformConfig
namespace bell
{
class Biquad : public bell::AudioTransform
{
extern "C" int dsps_biquad_f32_ae32(const float* input, float* output, int len,
float* coef, float* w);
namespace bell {
class Biquad : public bell::AudioTransform {
public:
Biquad();
~Biquad(){};
enum class Type
{
enum class Type {
Free,
Highpass,
Lowpass,
@@ -58,14 +64,14 @@ namespace bell
int channel;
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 reconfigure() override
{
void reconfigure() override {
std::scoped_lock lock(this->accessMutex);
std::map<std::string, float> biquadConfig;
this->channel = config->getChannels()[0];
@@ -80,11 +86,8 @@ namespace bell
float q = config->getFloat("q", false, invalid);
if (currentConfig["bandwidth"] == bandwidth &&
currentConfig["slope"] == slope &&
currentConfig["gain"] == gain &&
currentConfig["frequency"] == frequency &&
currentConfig["q"] == q)
{
currentConfig["slope"] == slope && currentConfig["gain"] == gain &&
currentConfig["frequency"] == frequency && currentConfig["q"] == q) {
return;
}
@@ -99,8 +102,7 @@ namespace bell
if (q != invalid)
biquadConfig["q"] = q;
if (type == "free")
{
if (type == "free") {
biquadConfig["a1"] = config->getFloat("a1");
biquadConfig["a2"] = config->getFloat("a2");
biquadConfig["b0"] = config->getFloat("b0");
@@ -109,12 +111,9 @@ namespace bell
}
auto typeElement = strMapType.find(type);
if (typeElement != strMapType.end())
{
if (typeElement != strMapType.end()) {
this->configure(typeElement->second, biquadConfig);
}
else
{
} else {
throw std::invalid_argument("No biquad of type " + type);
}
}
@@ -152,7 +151,8 @@ namespace bell
void allPassCoEffsBandwidth(float f, float bandwidth);
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

View File

@@ -1,18 +1,20 @@
#pragma once
#include <vector>
#include <memory>
#include <cmath>
#include <mutex>
#include <map>
#include <stdint.h> // for uint32_t
#include <map> // for map
#include <memory> // for unique_ptr, allocator
#include <mutex> // for scoped_lock
#include <stdexcept> // for invalid_argument
#include <string> // for string, operator==, char_traits, basic_...
#include <vector> // for vector
#include "Biquad.h"
#include "AudioTransform.h"
#include "AudioTransform.h" // for AudioTransform
#include "Biquad.h" // for Biquad
#include "StreamInfo.h" // for StreamInfo
#include "TransformConfig.h" // for TransformConfig
namespace bell
{
class BiquadCombo : public bell::AudioTransform
{
namespace bell {
class BiquadCombo : public bell::AudioTransform {
private:
std::vector<std::unique_ptr<bell::Biquad>> biquads;
@@ -25,32 +27,25 @@ namespace bell
~BiquadCombo(){};
int channel;
std::map<std::string, float> paramCache = {
{"order", 0.0f},
{"frequency", 0.0f}
};
std::map<std::string, float> paramCache = {{"order", 0.0f},
{"frequency", 0.0f}};
enum class FilterType
{
Highpass,
Lowpass
};
enum class FilterType { Highpass, Lowpass };
void linkwitzRiley(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 reconfigure() override
{
void reconfigure() override {
std::scoped_lock lock(this->accessMutex);
float freq = config->getFloat("frequency");
int order = config->getInt("order");
if (paramCache["frequency"] == freq && paramCache["order"] == order)
{
if (paramCache["frequency"] == freq && paramCache["order"] == order) {
return;
} else {
paramCache["frequency"] = freq;
@@ -60,26 +55,17 @@ namespace bell
this->channel = config->getChannels()[0];
this->biquads = std::vector<std::unique_ptr<bell::Biquad>>();
auto type = config->getString("combo_type");
if (type == "lr_lowpass")
{
if (type == "lr_lowpass") {
this->linkwitzRiley(freq, order, FilterType::Lowpass);
}
else if (type == "lr_highpass")
{
} else if (type == "lr_highpass") {
this->linkwitzRiley(freq, order, FilterType::Highpass);
}
else if (type == "bw_highpass")
{
} else if (type == "bw_highpass") {
this->butterworth(freq, order, FilterType::Highpass);
}
else if (type == "bw_lowpass")
{
} else if (type == "bw_lowpass") {
this->butterworth(freq, order, FilterType::Highpass);
}
else
{
} else {
throw std::invalid_argument("Invalid combo filter type");
}
}
};
};
}; // namespace bell

View File

@@ -2,10 +2,10 @@
#include <atomic>
#include <cmath>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <functional>
#include "BellUtils.h"
#include "CircularBuffer.h"
@@ -70,8 +70,9 @@ class CentralAudioBuffer {
*/
void clearBuffer() {
std::scoped_lock lock(this->dataAccessMutex);
//size_t exceptSize = currentSampleRate + (sizeof(AudioChunk) - (currentSampleRate % sizeof(AudioChunk)));
audioBuffer->emptyBuffer();
hasChunk = false;
}
void emptyCompletely() {
@@ -105,10 +106,10 @@ class CentralAudioBuffer {
}
}
AudioChunk currentChunk = { };
AudioChunk currentChunk = {};
bool hasChunk = false;
AudioChunk lastReadChunk = { };
AudioChunk lastReadChunk = {};
AudioChunk* readChunk() {
std::scoped_lock lock(this->dataAccessMutex);

View File

@@ -1,29 +1,27 @@
#pragma once
#include <vector>
#include <memory>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <iostream>
#include <mutex>
#include <map>
#include <math.h> // for expf
#include <stdint.h> // for uint32_t
#include <map> // for map
#include <memory> // for unique_ptr
#include <mutex> // for scoped_lock
#include <string> // for string, operator<
#include <vector> // for vector
#include "Biquad.h"
#include "AudioTransform.h"
#include "AudioTransform.h" // for AudioTransform
#include "StreamInfo.h" // for StreamInfo
#include "TransformConfig.h" // for TransformConfig
#define pow10f(x) expf(2.302585092994046f*x)
#define pow10f(x) expf(2.302585092994046f * x)
// This is a fast approximation to log2()
// Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E;
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
{
class Compressor : public bell::AudioTransform
{
namespace bell {
class Compressor : public bell::AudioTransform {
private:
std::vector<int> channels;
std::vector<float> tmp;
@@ -45,16 +43,16 @@ namespace bell
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 calGain();
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);
auto newChannels = config->getChannels();
@@ -68,12 +66,9 @@ namespace bell
paramCache["release"] == newRelease &&
paramCache["threshold"] == newThreshold &&
paramCache["factor"] == newFactor &&
paramCache["makeup_gain"] == newMakeupGain)
{
paramCache["makeup_gain"] == newMakeupGain) {
return;
}
else
{
} else {
paramCache["attack"] = newAttack;
paramCache["release"] = newRelease;
@@ -82,7 +77,8 @@ namespace bell
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 {
@@ -97,7 +93,10 @@ namespace bell
// this->configure(attack, release, clipLimit, threshold, factor, makeupGain);
// }
std::unique_ptr<StreamInfo> process(std::unique_ptr<StreamInfo> data) override;
void sampleRateChanged(uint32_t sampleRate) override { this->sampleRate = sampleRate; };
std::unique_ptr<StreamInfo> process(
std::unique_ptr<StreamInfo> data) override;
void sampleRateChanged(uint32_t sampleRate) override {
this->sampleRate = sampleRate;
};
};
}; // namespace bell

View File

@@ -1,15 +1,15 @@
#pragma once
#include <cmath>
#include <mutex>
#include <iostream>
#include <memory> // for unique_ptr
#include <mutex> // for scoped_lock
#include <vector> // for vector
#include "AudioTransform.h"
#include "AudioTransform.h" // for AudioTransform
#include "StreamInfo.h" // for StreamInfo
#include "TransformConfig.h" // for TransformConfig
namespace bell
{
class Gain : public bell::AudioTransform
{
namespace bell {
class Gain : public bell::AudioTransform {
private:
float gainFactor = 1.0f;
@@ -17,13 +17,14 @@ namespace bell
public:
Gain();
~Gain() {};
~Gain(){};
float gainDb = 0.0;
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 {
std::scoped_lock lock(this->accessMutex);
@@ -36,5 +37,5 @@ namespace bell
this->configure(channels, gain);
}
};
}
};
} // namespace bell

View File

@@ -3,45 +3,34 @@
#include "TransformConfig.h"
#include "cJSON.h"
namespace bell
{
class JSONTransformConfig : public bell::TransformConfig
{
namespace bell {
class JSONTransformConfig : public bell::TransformConfig {
private:
cJSON *json;
cJSON* json;
public:
JSONTransformConfig(cJSON *body)
{
this->json = body;
};
JSONTransformConfig(cJSON* body) { this->json = body; };
~JSONTransformConfig(){};
std::string rawGetString(const std::string &field) override
{
cJSON *value = cJSON_GetObjectItem(json, field.c_str());
std::string rawGetString(const std::string& field) override {
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 "invalid";
}
std::vector<int> rawGetIntArray(const std::string &field) override
{
std::vector<int> rawGetIntArray(const std::string& field) override {
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))
{
for (int i = 0; i < cJSON_GetArraySize(value); i++)
{
cJSON *item = cJSON_GetArrayItem(value, i);
if (item != NULL && cJSON_IsNumber(item))
{
if (value != NULL && cJSON_IsArray(value)) {
for (int i = 0; i < cJSON_GetArraySize(value); i++) {
cJSON* item = cJSON_GetArrayItem(value, i);
if (item != NULL && cJSON_IsNumber(item)) {
result.push_back(item->valueint);
}
}
@@ -50,19 +39,15 @@ namespace bell
return result;
}
std::vector<float> rawGetFloatArray(const std::string &field) override
{
std::vector<float> rawGetFloatArray(const std::string& field) override {
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))
{
for (int i = 0; i < cJSON_GetArraySize(value); i++)
{
cJSON *item = cJSON_GetArrayItem(value, i);
if (item != NULL && cJSON_IsNumber(item))
{
if (value != NULL && cJSON_IsArray(value)) {
for (int i = 0; i < cJSON_GetArraySize(value); i++) {
cJSON* item = cJSON_GetArrayItem(value, i);
if (item != NULL && cJSON_IsNumber(item)) {
result.push_back(item->valuedouble);
}
}
@@ -71,40 +56,34 @@ namespace bell
return result;
}
int rawGetInt(const std::string &field) override
{
cJSON *value = cJSON_GetObjectItem(json, field.c_str());
int rawGetInt(const std::string& field) override {
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 invalidInt;
}
bool isArray(const std::string &field) override
{
cJSON *value = cJSON_GetObjectItem(json, field.c_str());
bool isArray(const std::string& field) override {
cJSON* value = cJSON_GetObjectItem(json, field.c_str());
if (value != NULL && cJSON_IsArray(value))
{
if (value != NULL && cJSON_IsArray(value)) {
return true;
}
return false;
}
float rawGetFloat(const std::string &field) override
{
cJSON *value = cJSON_GetObjectItem(json, field.c_str());
float rawGetFloat(const std::string& field) override {
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 invalidInt;
}
};
}
};
} // namespace bell

View File

@@ -1,36 +1,28 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <vector>
namespace bell
{
enum class Channels {
LEFT,
RIGHT,
LEFT_RIGHT
};
namespace bell {
enum class Channels { LEFT, RIGHT, LEFT_RIGHT };
enum class SampleRate : uint32_t
{
enum class SampleRate : uint32_t {
SR_44100 = 44100,
SR_48000 = 48000,
};
};
enum class BitWidth : uint32_t
{
enum class BitWidth : uint32_t {
BW_16 = 16,
BW_24 = 24,
BW_32 = 32,
};
};
typedef struct
{
typedef struct {
float** data;
BitWidth bitwidth;
int numChannels;
SampleRate sampleRate;
size_t numSamples;
} StreamInfo;
};
} StreamInfo;
}; // namespace bell

View File

@@ -1,16 +1,14 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <variant>
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <variant>
#include <vector>
namespace bell
{
class TransformConfig
{
namespace bell {
class TransformConfig {
protected:
int invalidInt = -0x7C;
std::string invalidString = "_invalid";
@@ -21,114 +19,94 @@ namespace bell
int currentVolume = 60;
virtual std::string rawGetString(const std::string &field) = 0;
virtual std::string rawGetString(const std::string& field) = 0;
virtual int rawGetInt(const std::string &field) = 0;
virtual bool isArray(const std::string &field) = 0;
virtual int rawGetInt(const std::string& field) = 0;
virtual bool isArray(const std::string& field) = 0;
virtual float rawGetFloat(const std::string &field) = 0;
virtual std::vector<float> rawGetFloatArray(const std::string &field) = 0;
virtual std::vector<int> rawGetIntArray(const std::string &field) = 0;
virtual float rawGetFloat(const std::string& field) = 0;
virtual std::vector<float> rawGetFloatArray(const std::string& field) = 0;
virtual std::vector<int> rawGetIntArray(const std::string& field) = 0;
typedef std::variant<int, float, std::string> Value;
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;
if (index >= rawValues[field].size())
index = rawValues[field].size() - 1;
return rawValues[field][index];
}
std::string getString(const std::string &field, bool isRequired = false, std::string defaultValue = "")
{
if (rawValues.count(field) == 0)
{
std::string getString(const std::string& field, bool isRequired = false,
std::string defaultValue = "") {
if (rawValues.count(field) == 0) {
rawValues[field] = std::vector<Value>({Value(rawGetString(field))});
}
auto val = std::get<std::string>(getRawValue(field));
if (val == invalidString)
{
if (val == invalidString) {
if (isRequired)
throw std::invalid_argument("Field " + field + " is required");
else
return defaultValue;
}
else
} else
return val;
}
int getInt(const std::string &field, bool isRequired = false, int defaultValue = 0)
{
if (rawValues.count(field) == 0)
{
if (isArray(field))
{
int getInt(const std::string& field, bool isRequired = false,
int defaultValue = 0) {
if (rawValues.count(field) == 0) {
if (isArray(field)) {
rawValues[field] = std::vector<Value>();
for (auto f : rawGetIntArray(field))
{
for (auto f : rawGetIntArray(field)) {
rawValues[field].push_back(f);
}
}
else
{
} else {
rawValues[field] = std::vector<Value>({Value(rawGetInt(field))});
}
}
auto val = std::get<int>(getRawValue(field));
if (val == invalidInt)
{
if (val == invalidInt) {
if (isRequired)
throw std::invalid_argument("Field " + field + " is required");
else
return defaultValue;
}
else
} else
return val;
}
float getFloat(const std::string &field, bool isRequired = false, float defaultValue = 0)
{
if (rawValues.count(field) == 0)
{
if (isArray(field))
{
float getFloat(const std::string& field, bool isRequired = false,
float defaultValue = 0) {
if (rawValues.count(field) == 0) {
if (isArray(field)) {
rawValues[field] = std::vector<Value>();
for (auto f : rawGetFloatArray(field))
{
for (auto f : rawGetFloatArray(field)) {
rawValues[field].push_back(f);
}
}
else
{
rawValues[field] = std::vector<Value>({ Value(rawGetFloat(field)) });
} else {
rawValues[field] = std::vector<Value>({Value(rawGetFloat(field))});
}
}
auto val = std::get<float>(getRawValue(field));
if (val == invalidInt)
{
if (val == invalidInt) {
if (isRequired)
throw std::invalid_argument("Field " + field + " is required");
else
return defaultValue;
}
else
} else
return val;
}
std::vector<int> getChannels()
{
std::vector<int> getChannels() {
auto channel = getInt("channel", false, invalidInt);
if (channel != invalidInt)
{
if (channel != invalidInt) {
return std::vector<int>({channel});
}
return rawGetIntArray("channels");
}
};
}
};
} // namespace bell

View File

@@ -2,8 +2,7 @@
#include "driver/i2s.h"
AC101AudioSink::AC101AudioSink()
{
AC101AudioSink::AC101AudioSink() {
// Disable software volume control, all handled by ::volumeChanged
softwareVolumeControl = false;
@@ -37,9 +36,7 @@ AC101AudioSink::AC101AudioSink()
startI2sFeed();
}
AC101AudioSink::~AC101AudioSink()
{
}
AC101AudioSink::~AC101AudioSink() {}
void AC101AudioSink::volumeChanged(uint16_t volume) {
dac->volume(volume, volume);

View File

@@ -1,47 +1,45 @@
#include "BufferedAudioSink.h"
#include "driver/i2s.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"
#include "freertos/task.h"
RingbufHandle_t dataBuffer;
static void i2sFeed(void *pvParameters)
{
while (true)
{
static void i2sFeed(void* pvParameters) {
while (true) {
size_t itemSize;
char *item = (char *)xRingbufferReceiveUpTo(dataBuffer, &itemSize, portMAX_DELAY, 512);
if (item != NULL)
{
char* item = (char*)xRingbufferReceiveUpTo(dataBuffer, &itemSize,
portMAX_DELAY, 512);
if (item != NULL) {
size_t written = 0;
while (written < itemSize)
{
while (written < itemSize) {
i2s_write((i2s_port_t)0, item, itemSize, &written, portMAX_DELAY);
}
vRingbufferReturnItem(dataBuffer, (void *)item);
vRingbufferReturnItem(dataBuffer, (void*)item);
}
}
}
void BufferedAudioSink::startI2sFeed(size_t buf_size)
{
void BufferedAudioSink::startI2sFeed(size_t buf_size) {
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);
}
void BufferedAudioSink::feedPCMFramesInternal(const void *pvItem, size_t xItemSize)
{
void BufferedAudioSink::feedPCMFramesInternal(const void* pvItem,
size_t xItemSize) {
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
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;
}

View File

@@ -1,22 +1,23 @@
#include "ES8311AudioSink.h"
extern "C" {
#include "es8311.h"
#include "es8311.h"
}
ES8311AudioSink::ES8311AudioSink()
{
ES8311AudioSink::ES8311AudioSink() {
this->softwareVolumeControl = false;
esp_err_t ret_val = ESP_OK;
Es8311Config cfg = {
.esMode = ES_MODE_SLAVE,
.i2c_port_num = I2C_NUM_0,
.i2c_cfg = {
.i2c_cfg =
{
.mode = I2C_MODE_MASTER,
.sda_io_num = 1,
.scl_io_num = 2,
.sda_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,
};
cfg.i2c_cfg.master.clk_speed = 100000;
@@ -68,14 +69,12 @@ ES8311AudioSink::ES8311AudioSink()
int err;
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);
}
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);
}
@@ -86,21 +85,17 @@ ES8311AudioSink::ES8311AudioSink()
startI2sFeed();
}
void ES8311AudioSink::volumeChanged(uint16_t volume)
{
void ES8311AudioSink::volumeChanged(uint16_t 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) {
std::cout << "ES8311AudioSink::setSampleRate(" << sampleRate << ")" << std::endl;
std::cout << "ES8311AudioSink::setSampleRate(" << sampleRate << ")"
<< std::endl;
// i2s set sample rate
es8311_Codec_Startup(0, sampleRate);
}
ES8311AudioSink::~ES8311AudioSink()
{
}
ES8311AudioSink::~ES8311AudioSink() {}

View File

@@ -5,8 +5,7 @@ struct es8388_cmd_s {
uint8_t value;
};
ES8388AudioSink::ES8388AudioSink()
{
ES8388AudioSink::ES8388AudioSink() {
// configure i2c
i2c_config = {
.mode = I2C_MODE_MASTER,
@@ -29,8 +28,7 @@ ES8388AudioSink::ES8388AudioSink()
.dma_buf_len = 512,
.use_apll = true,
.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 = {
.bck_io_num = 27,
@@ -125,8 +123,7 @@ ES8388AudioSink::ES8388AudioSink()
startI2sFeed();
}
void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data)
{
void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data) {
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
@@ -140,11 +137,9 @@ void ES8388AudioSink::writeReg(uint8_t reg_add, uint8_t data)
if (res != ESP_OK) {
ESP_LOGE("RR", "Unable to write to ES8388: %d", res);
}else{
} else {
ESP_LOGE("RR", "register successfull written.");
}
}
ES8388AudioSink::~ES8388AudioSink()
{
}
ES8388AudioSink::~ES8388AudioSink() {}

View File

@@ -2,8 +2,7 @@
#include "driver/i2s.h"
ES9018AudioSink::ES9018AudioSink()
{
ES9018AudioSink::ES9018AudioSink() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
@@ -16,8 +15,7 @@ ES9018AudioSink::ES9018AudioSink()
.dma_buf_len = 512,
.use_apll = true,
.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 = {
.bck_io_num = 27,
@@ -31,6 +29,4 @@ ES9018AudioSink::ES9018AudioSink()
startI2sFeed();
}
ES9018AudioSink::~ES9018AudioSink()
{
}
ES9018AudioSink::~ES9018AudioSink() {}

View File

@@ -1,35 +1,32 @@
#include "InternalAudioSink.h"
#include "driver/i2s.h"
InternalAudioSink::InternalAudioSink()
{
InternalAudioSink::InternalAudioSink() {
softwareVolumeControl = true;
usign = true;
#ifdef I2S_MODE_DAC_BUILT_IN
#ifdef I2S_MODE_DAC_BUILT_IN
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,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = 0,//ESP_INTR_FLAG_LEVEL1
.intr_alloc_flags = 0, //ESP_INTR_FLAG_LEVEL1
.dma_buf_count = 6,
.dma_buf_len = 512,
.use_apll = true,
.tx_desc_auto_clear = true, //Auto clear tx descriptor on underflow
.fixed_mclk=-1
};
.fixed_mclk = -1};
//install and start i2s driver
i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
//init DAC
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
#endif
#endif
startI2sFeed();
}
InternalAudioSink::~InternalAudioSink()
{
}
InternalAudioSink::~InternalAudioSink() {}

View File

@@ -2,8 +2,7 @@
#include "driver/i2s.h"
PCM5102AudioSink::PCM5102AudioSink()
{
PCM5102AudioSink::PCM5102AudioSink() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
@@ -16,8 +15,7 @@ PCM5102AudioSink::PCM5102AudioSink()
.dma_buf_len = 512,
.use_apll = true,
.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 = {
.bck_io_num = 27,
@@ -31,6 +29,4 @@ PCM5102AudioSink::PCM5102AudioSink()
startI2sFeed();
}
PCM5102AudioSink::~PCM5102AudioSink()
{
}
PCM5102AudioSink::~PCM5102AudioSink() {}

View File

@@ -5,44 +5,41 @@
// See http://www.hardwarebook.info/S/PDIF for more info on this protocol
// Conversion table to biphase code mark (LSB first, ending in 1)
static const uint16_t bmc_convert[256] = {
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33,
0xcd33, 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533,
0xccb3, 0x4cb3, 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3,
0x32b3, 0xb2b3, 0xd2b3, 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3,
0xccd3, 0x4cd3, 0x2cd3, 0xacd3, 0x34d3, 0xb4d3, 0xd4d3, 0x54d3,
0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3, 0x4ad3, 0x2ad3, 0xaad3,
0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53, 0x2b53, 0xab53,
0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553, 0x5553,
0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb,
0x334b, 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b,
0xcd4b, 0x4d4b, 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b,
0x332b, 0xb32b, 0xd32b, 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b,
0xcd2b, 0x4d2b, 0x2d2b, 0xad2b, 0x352b, 0xb52b, 0xd52b, 0x552b,
0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab, 0xb4ab, 0xd4ab, 0x54ab,
0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab, 0x2aab, 0xaaab,
0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd, 0x54cd,
0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d,
0xcd4d, 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d,
0x332d, 0xb32d, 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d,
0xcd2d, 0x4d2d, 0x2d2d, 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d,
0xccad, 0x4cad, 0x2cad, 0xacad, 0x34ad, 0xb4ad, 0xd4ad, 0x54ad,
0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad, 0x4aad, 0x2aad, 0xaaad,
0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35, 0x2b35, 0xab35,
0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535, 0x5535,
0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5,
0xccd5, 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5,
0x32d5, 0xb2d5, 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5,
0x3355, 0xb355, 0xd355, 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55,
0xcd55, 0x4d55, 0x2d55, 0xad55, 0x3555, 0xb555, 0xd555, 0x5555,
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33, 0xcd33,
0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533, 0xccb3, 0x4cb3,
0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3, 0x32b3, 0xb2b3, 0xd2b3,
0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3, 0xccd3, 0x4cd3, 0x2cd3, 0xacd3,
0x34d3, 0xb4d3, 0xd4d3, 0x54d3, 0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3,
0x4ad3, 0x2ad3, 0xaad3, 0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53,
0x2b53, 0xab53, 0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553,
0x5553, 0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb, 0x334b,
0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b, 0xcd4b, 0x4d4b,
0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b, 0x332b, 0xb32b, 0xd32b,
0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b, 0xcd2b, 0x4d2b, 0x2d2b, 0xad2b,
0x352b, 0xb52b, 0xd52b, 0x552b, 0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab,
0xb4ab, 0xd4ab, 0x54ab, 0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab,
0x2aab, 0xaaab, 0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd,
0x54cd, 0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d, 0xcd4d,
0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d, 0x332d, 0xb32d,
0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d, 0xcd2d, 0x4d2d, 0x2d2d,
0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d, 0xccad, 0x4cad, 0x2cad, 0xacad,
0x34ad, 0xb4ad, 0xd4ad, 0x54ad, 0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad,
0x4aad, 0x2aad, 0xaaad, 0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35,
0x2b35, 0xab35, 0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535,
0x5535, 0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5, 0xccd5,
0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5, 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 BITS_PER_SUBFRAME 64
#define FRAMES_PER_BLOCK 192
#define SPDIF_BUF_SIZE (BITS_PER_SUBFRAME/8 * 2 * FRAMES_PER_BLOCK)
#define SPDIF_BUF_SIZE (BITS_PER_SUBFRAME / 8 * 2 * FRAMES_PER_BLOCK)
#define SPDIF_BUF_ARRAY_SIZE (SPDIF_BUF_SIZE / sizeof(uint32_t))
#define BMC_B 0x33173333 // block start
@@ -51,23 +48,20 @@ static const uint16_t bmc_convert[256] = {
#define BMC_MW_DIF (BMC_M ^ BMC_W)
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
spdif_buf[0] = BMC_B;
// all other blocks are alternating M, then W preamble
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;
}
}
SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
{
SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin) {
// initialize S/PDIF buffer
spdif_buf_init();
spdif_ptr = spdif_buf;
@@ -76,8 +70,10 @@ SPDIFAudioSink::SPDIFAudioSink(uint8_t spdifPin)
startI2sFeed(SPDIF_BUF_SIZE * 16);
}
bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) {
if (bitDepth != 16 || channelCount != 2) // TODO support mono playback and different bit widths
bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount,
uint8_t bitDepth) {
if (bitDepth != 16 ||
channelCount != 2) // TODO support mono playback and different bit widths
return false;
int sample_rate = (int)sampleRate * 2;
int bclk = sample_rate * 64 * 2;
@@ -86,9 +82,9 @@ bool SPDIFAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
.sample_rate = (uint32_t) sample_rate,
.sample_rate = (uint32_t)sample_rate,
#else
.sample_rate = (int) sample_rate,
.sample_rate = (int)sample_rate,
#endif
.bits_per_sample = (i2s_bits_per_sample_t)(bitDepth * 2),
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
@@ -118,10 +114,8 @@ SPDIFAudioSink::~SPDIFAudioSink() {
int num_frames = 0;
void SPDIFAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
{
for (int i = 0; i < bytes; i += 2)
{
void SPDIFAudioSink::feedPCMFrames(const uint8_t* buffer, size_t bytes) {
for (int i = 0; i < bytes; i += 2) {
/**
* What is this, and why does it work?
*
@@ -172,7 +166,7 @@ void SPDIFAudioSink::feedPCMFrames(const uint8_t *buffer, size_t bytes)
* github.com/amedes/esp_a2dp_sink_spdif
*/
uint32_t lo = ((uint32_t)(bmc_convert[buffer[i]]) << 16);
uint32_t hi = (uint32_t)((int16_t)bmc_convert[buffer[i+1]]);
uint32_t hi = (uint32_t)((int16_t)bmc_convert[buffer[i + 1]]);
*(spdif_ptr + 1) = ((lo ^ hi) << 1) >> 1;

View File

@@ -1,24 +1,22 @@
#include "TAS5711AudioSink.h"
struct tas5711_cmd_s {
uint8_t reg;
uint8_t value;
};
static const struct tas5711_cmd_s tas5711_init_sequence[] = {
{ 0x00, 0x6c }, // 0x6c - 256 x mclk
{ 0x04, 0x03 }, // 0x03 - 16 bit i2s
{ 0x05, 0x00 }, // system control 0x00 is audio playback
{ 0x06, 0x00 }, // disable mute
{ 0x07, 0x50 }, // volume register
{ 0xff, 0xff }
{0x00, 0x6c}, // 0x6c - 256 x mclk
{0x04, 0x03}, // 0x03 - 16 bit i2s
{0x05, 0x00}, // system control 0x00 is audio playback
{0x06, 0x00}, // disable mute
{0x07, 0x50}, // volume register
{0xff, 0xff}
};
i2c_ack_type_t ACK_CHECK_EN = (i2c_ack_type_t)0x1;
TAS5711AudioSink::TAS5711AudioSink()
{
TAS5711AudioSink::TAS5711AudioSink() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), // Only TX
@@ -31,9 +29,7 @@ TAS5711AudioSink::TAS5711AudioSink()
.dma_buf_len = 512,
.use_apll = true,
.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 = {
.bck_io_num = 5,
@@ -75,15 +71,13 @@ TAS5711AudioSink::TAS5711AudioSink()
if (ret == ESP_OK) {
ESP_LOGI("RR", "Detected TAS");
}
else {
} else {
ESP_LOGI("RR", "Unable to detect dac");
}
writeReg(0x1b, 0x00);
vTaskDelay(100 / portTICK_PERIOD_MS);
for (int i = 0; tas5711_init_sequence[i].reg != 0xff; i++) {
writeReg(tas5711_init_sequence[i].reg, tas5711_init_sequence[i].value);
vTaskDelay(1 / portTICK_PERIOD_MS);
@@ -92,8 +86,7 @@ TAS5711AudioSink::TAS5711AudioSink()
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_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, value, ACK_CHECK_EN);
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) {
ESP_LOGE("RR", "Unable to write to TAS5711");
}
i2c_cmd_link_delete(i2c_cmd);
}
TAS5711AudioSink::~TAS5711AudioSink()
{
}
TAS5711AudioSink::~TAS5711AudioSink() {}

View File

@@ -22,16 +22,16 @@
*
*/
#include <string.h>
#include <esp_log.h>
#include <esp_types.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "ac101.h"
#include <driver/i2c.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 "ac101.h"
const static char TAG[] = "AC101";
@@ -43,13 +43,12 @@ const static char TAG[] = "AC101";
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define AC_ASSERT(a, format, b, ...) \
if ((a) != 0) \
{ \
if ((a) != 0) { \
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
return b; \
}
static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config);
static bool init(int i2c_port_num, int i2s_num, i2s_config_t* config);
static void deinit(void);
static void speaker(bool active);
static void headset(bool active);
@@ -71,8 +70,7 @@ static int i2c_port;
/****************************************************************************************
* 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;
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);
if (!res)
{
if (!res) {
ESP_LOGW(TAG, "No AC101 detected");
i2c_driver_delete(i2c_port);
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);
// huh?
vTaskDelay(100 / portTICK_PERIOD_MS);
// 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_CTRL2, BIN(1000, 0110, 0000, 0000)); // PLL, F=96,N_i=1024-96,F=0,N_f=0*0.2;
i2c_write_reg(PLL_CTRL1,
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));
// 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_RST_CTRL, BIN(1000, 0000, 0000, 1100)); // IS21, ADC, DAC
i2c_write_reg(I2S_SR_CTRL, BIN(0111, 0000, 0000, 0000)); // 44.1kHz
// 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_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(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
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_ANA_CTRL, BIN(1011, 1011, 0000, 0000)); // enable analogue R/L, 0dB
i2c_write_reg(ADC_DIG_CTRL,
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
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_ANA_CTRL, BIN(0011, 0011, 0000, 0000)); // disable analogue R/L, 0dB
i2c_write_reg(ADC_DIG_CTRL,
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
//Path Configuration
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(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
i2c_write_reg(
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
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
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
// 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_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_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);
}
@@ -164,16 +183,14 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)
/****************************************************************************************
* init
*/
static void deinit(void)
{
static void deinit(void) {
i2c_driver_delete(i2c_port);
}
/****************************************************************************************
* change volume
*/
static void volume(unsigned left, unsigned right)
{
static void volume(unsigned left, unsigned right) {
ac101_set_earph_volume(left);
// nothing at that point, volume is handled by backend
}
@@ -181,10 +198,8 @@ static void volume(unsigned left, unsigned right)
/****************************************************************************************
* power
*/
static void power(adac_power_e mode)
{
switch (mode)
{
static void power(adac_power_e mode) {
switch (mode) {
case ADAC_STANDBY:
case ADAC_OFF:
ac101_stop();
@@ -201,8 +216,7 @@ static void power(adac_power_e mode)
/****************************************************************************************
* speaker
*/
static void speaker(bool active)
{
static void speaker(bool active) {
uint16_t value = i2c_read_reg(SPKOUT_CTRL);
if (active)
i2c_write_reg(SPKOUT_CTRL, value | SPKOUT_EN);
@@ -213,8 +227,7 @@ static void speaker(bool active)
/****************************************************************************************
* headset
*/
static void headset(bool active)
{
static void headset(bool active) {
// there might be aneed to toggle OMIXER_DACA_CTRL 11:8, not sure
uint16_t value = i2c_read_reg(HPOUT_CTRL);
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();
esp_err_t ret = 0;
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};
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, reg, ACK_CHECK_EN);
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_stop(cmd);
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)
rate = SAMPLE_RATE_8000;
else if (rate == 11025)
@@ -292,8 +303,7 @@ void set_sample_rate(int rate)
rate = SAMPLE_RATE_96000;
else if (rate == 192000)
rate = SAMPLE_RATE_192000;
else
{
else {
ESP_LOGW(TAG, "Unknown sample rate %hu", rate);
rate = SAMPLE_RATE_44100;
}
@@ -303,16 +313,14 @@ void set_sample_rate(int rate)
/****************************************************************************************
* 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;
}
/****************************************************************************************
* 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);
value = ((int)value * 0x1f) / 100;
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
*/
static int ac101_get_earph_volume(void)
{
static int ac101_get_earph_volume(void) {
return (((i2c_read_reg(HPOUT_CTRL) >> 4) & 0x3f) * 100) / 0x3f;
}
/****************************************************************************************
* 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);
value = (((int)value * 0x3f) / 255) << 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;
regval = i2c_read_reg(OMIXER_BST1_CTRL);
switch (source)
{
switch (source) {
case SRC_MIC1:
temp = (gain & 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)
{
if (mode == AC_MODULE_LINE)
{
static void ac101_start(ac_module_t mode) {
if (mode == AC_MODULE_LINE) {
i2c_write_reg(0x51, 0x0408);
i2c_write_reg(0x40, 0x8000);
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
// i2c_write_reg(PLL_CTRL2, 0x8120);
i2c_write_reg(0x04, 0x800c);
i2c_write_reg(0x05, 0x800c);
// 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);
value |= 0x8000;
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);
value &= ~0x8000;
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
}
/****************************************************************************************
* 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;
regval = i2c_read_reg(I2S1LCK_CTRL);
regval &= 0xe03f;

View File

@@ -22,9 +22,9 @@
*
*/
#include "es8311.h"
#include <string.h>
#include "esp_log.h"
#include "es8311.h"
// #include "board.h"
/* ES8311 address
@@ -70,7 +70,7 @@ struct es8311_private {
uint8_t pcm_resolution;
uint8_t mclk_src;
};
static struct es8311_private *es8311_priv;
static struct es8311_private* es8311_priv;
/*
* Clock coefficient structer
@@ -93,115 +93,182 @@ struct _coeff_div {
static const struct _coeff_div coeff_div[] = {
//mclk rate prediv mult adcdiv dacdiv fsmode lrch lrcl bckdiv osr
/* 8k */
{12288000, 8000 , 0x06, 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},
{6144000 , 8000 , 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000 , 8000 , 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000 , 8000 , 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1024000 , 8000 , 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{12288000, 8000, 0x06, 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},
{6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 8000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 11.025k */
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10,
0x10},
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 24000, 0x03, 0x01, 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},
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 24000, 0x03, 0x01, 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 */
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10,
0x10},
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 48000, 0x03, 0x02, 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},
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 48000, 0x03, 0x02, 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 */
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 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},
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10,
0x10},
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 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 */
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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 */
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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},
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10,
0x10},
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 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";
#define ES_ASSERT(a, format, b, ...) \
if ((a) != 0) { \
ESP_LOGE(TAG, format, ##__VA_ARGS__); \
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;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
@@ -215,8 +282,7 @@ static int Es8311WriteReg(uint8_t regAdd, uint8_t data)
return res;
}
int Es8311ReadReg(uint8_t regAdd)
{
int Es8311ReadReg(uint8_t regAdd) {
uint8_t data;
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
@@ -240,8 +306,7 @@ int Es8311ReadReg(uint8_t regAdd)
return (int)data;
}
static int Es7243WriteReg(uint8_t regAdd, uint8_t data)
{
static int Es7243WriteReg(uint8_t regAdd, uint8_t data) {
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
res |= i2c_master_start(cmd);
@@ -255,9 +320,7 @@ static int Es7243WriteReg(uint8_t regAdd, uint8_t data)
return res;
}
int Es7243ReadReg(uint8_t regAdd)
{
int Es7243ReadReg(uint8_t regAdd) {
uint8_t data;
int res = 0;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
@@ -281,8 +344,7 @@ int Es7243ReadReg(uint8_t regAdd)
return (int)data;
}
esp_err_t Es7243Init(void)
{
esp_err_t Es7243Init(void) {
esp_err_t ret = ESP_OK;
ret |= Es7243WriteReg(0x00, 0x01);
ret |= Es7243WriteReg(0x06, 0x00);
@@ -297,8 +359,7 @@ esp_err_t Es7243Init(void)
return ret;
}
static int I2cInit(i2c_config_t *conf, int i2cMasterPort)
{
static int I2cInit(i2c_config_t* conf, int i2cMasterPort) {
int res;
res = i2c_param_config(i2cMasterPort, conf);
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
*/
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++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
return i;
@@ -320,14 +380,14 @@ static int get_coeff(uint32_t mclk, uint32_t rate)
/*
* 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;
uint8_t regv, datmp;
ESP_LOGI(TAG, "Enter into es8311_pcm_hw_params()\n");
coeff = get_coeff(mclk, lrck);
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;
}
@@ -412,8 +472,7 @@ static void es8311_pcm_hw_params(uint32_t mclk, uint32_t lrck)
* if mute = 0, dac un-mute
* if mute = 1, dac mute
*/
static void es8311_mute(int mute)
{
static void es8311_mute(int mute) {
uint8_t regv;
ESP_LOGI(TAG, "Enter into es8311_mute(), mute = %d\n", mute);
regv = Es8311ReadReg(ES8311_DAC_REG31) & 0x9f;
@@ -450,8 +509,7 @@ static void es8311_mute(int mute)
/*
* 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;
Es8311WriteReg(ES8311_GP_REG45, 0x00);
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
*/
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");
es8311_priv->dmic_enable = false;
es8311_priv->mclkinv = false;
@@ -596,8 +653,7 @@ void es8311_Codec_Startup(uint32_t mclk_freq, uint32_t lrck_freq)
// return res;
// }
esp_err_t Es8311GetRef(bool flag)
{
esp_err_t Es8311GetRef(bool flag) {
esp_err_t ret = ESP_OK;
uint8_t regv = 0;
if (flag) {
@@ -610,23 +666,20 @@ esp_err_t Es8311GetRef(bool flag)
return ret;
}
int Es8311Init(Es8311Config *cfg)
{
int Es8311Init(Es8311Config* cfg) {
es8311_priv = calloc(1, sizeof(struct es8311_private));
I2cInit(&cfg->i2c_cfg, cfg->i2c_port_num); // ESP32 in master mode
es8311_Codec_Startup(11289600, 44100);
return 0;
}
void Es8311Uninit()
{
void Es8311Uninit() {
Es8311WriteReg(ES8311_RESET_REG00, 0x3f);
free(es8311_priv);
es8311_priv = NULL;
}
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt)
{
int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt) {
int res = 0;
uint8_t regAdc = 0, regDac = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
@@ -665,14 +718,12 @@ int Es8311ConfigFmt(ESCodecModule mode, ESCodecI2SFmt fmt)
return res;
}
int Es8311I2sConfigClock(ESCodecI2sClock cfg)
{
int Es8311I2sConfigClock(ESCodecI2sClock cfg) {
int res = 0;
return res;
}
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample)
{
int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample) {
int res = 0;
uint8_t reg = 0;
int bits = (int)bitPerSample;
@@ -690,32 +741,31 @@ int Es8311SetBitsPerSample(ESCodecModule mode, BitsLength bitPerSample)
return res;
}
int Es8311Start(ESCodecModule mode)
{
int Es8311Start(ESCodecModule mode) {
int res = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_ADC_REG17, 0xBF);
}
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;
}
int Es8311Stop(ESCodecModule mode)
{
int Es8311Stop(ESCodecModule mode) {
int res = 0;
if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) {
res |= Es8311WriteReg(ES8311_ADC_REG17, 0x00);
}
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;
}
int Es8311SetVoiceVolume(int volume)
{
int Es8311SetVoiceVolume(int volume) {
int res = 0;
if (volume == 0) {
@@ -726,8 +776,7 @@ int Es8311SetVoiceVolume(int volume)
return res;
}
int Es8311GetVoiceVolume(int *volume)
{
int Es8311GetVoiceVolume(int* volume) {
int res = ESP_OK;
int regv = Es8311ReadReg(ES8311_DAC_REG32);
if (regv == ESP_FAIL) {
@@ -740,16 +789,14 @@ int Es8311GetVoiceVolume(int *volume)
return res;
}
int Es8311SetVoiceMute(int enable)
{
int Es8311SetVoiceMute(int enable) {
int res = 0;
ESP_LOGI(TAG, "Es8311SetVoiceMute volume:%d\n", enable);
es8311_mute(enable);
return res;
}
int Es8311GetVoiceMute(int *mute)
{
int Es8311GetVoiceMute(int* mute) {
int res = -1;
uint8_t reg = 0;
res = Es8311ReadReg(ES8311_DAC_REG31);
@@ -760,8 +807,7 @@ int Es8311GetVoiceMute(int *mute)
return res;
}
int Es8311SetMicGain(MicGain gain)
{
int Es8311SetMicGain(MicGain gain) {
int res = 0;
uint8_t gain_n = Es8311ReadReg(ES8311_ADC_REG16) & 0x07;
gain_n |= gain / 6;
@@ -769,26 +815,22 @@ int Es8311SetMicGain(MicGain gain)
return res;
}
int Es8311ConfigAdcInput(AdcInput input)
{
int Es8311ConfigAdcInput(AdcInput input) {
int res = 0;
return res;
}
int Es8311SetAdcVolume(uint8_t adc_vol)
{
int Es8311SetAdcVolume(uint8_t adc_vol) {
int res = 0;
res = Es8311WriteReg(ES8311_ADC_REG17, adc_vol); // MIC ADC Volume
return res;
}
int ES8311WriteReg(uint8_t regAdd, uint8_t data)
{
int ES8311WriteReg(uint8_t regAdd, uint8_t data) {
return Es8311WriteReg(regAdd, data);
}
void Es8311ReadAll()
{
void Es8311ReadAll() {
for (int i = 0; i < 0x4A; i++) {
uint8_t reg = Es8311ReadReg(i);
// ets_printf("REG:%02x, %02x\n", reg, i);

View File

@@ -5,15 +5,17 @@
#include <cstdlib>
#include <vector>
class AudioSink
{
class AudioSink {
public:
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) {}
// 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.
virtual inline bool setRate(uint16_t sampleRate) {
return setParams(sampleRate, 2, 16);

View File

@@ -1,26 +1,26 @@
#ifndef AC101AUDIOSINK_H
#define AC101AUDIOSINK_H
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_err.h"
#include "esp_log.h"
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "BufferedAudioSink.h"
#include "ac101.h"
#include "adac.h"
#include "esp_err.h"
#include "esp_log.h"
class AC101AudioSink : public BufferedAudioSink
{
public:
class AC101AudioSink : public BufferedAudioSink {
public:
AC101AudioSink();
~AC101AudioSink();
void volumeChanged(uint16_t volume);
private:
adac_s *dac;
private:
adac_s* dac;
};
#endif

View File

@@ -1,25 +1,27 @@
#ifndef BUFFEREDAUDIOSINK_H
#define BUFFEREDAUDIOSINK_H
#include <vector>
#include <iostream>
#include "AudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "AudioSink.h"
#include "esp_err.h"
#include "esp_log.h"
class BufferedAudioSink : public AudioSink
{
public:
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
protected:
class BufferedAudioSink : public AudioSink {
public:
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
bool setParams(uint32_t sampleRate, uint8_t channelCount,
uint8_t bitDepth) override;
protected:
void startI2sFeed(size_t buf_size = 4096 * 8);
void feedPCMFramesInternal(const void *pvItem, size_t xItemSize);
private:
void feedPCMFramesInternal(const void* pvItem, size_t xItemSize);
private:
};
#endif

View File

@@ -1,28 +1,28 @@
#ifndef ES8311AUDIOSINK_H
#define ES8311AUDIOSINK_H
#include "driver/i2s.h"
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.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/i2c.h"
#include <sys/unistd.h>
#include <sys/stat.h>
#include "driver/i2s.h"
#include "esp_err.h"
#include "esp_log.h"
class ES8311AudioSink : public BufferedAudioSink
{
public:
class ES8311AudioSink : public BufferedAudioSink {
public:
ES8311AudioSink();
~ES8311AudioSink();
void writeReg(uint8_t reg_add, uint8_t data);
void volumeChanged(uint16_t volume);
void setSampleRate(uint32_t sampleRate);
private:
private:
};
#endif

View File

@@ -1,20 +1,19 @@
#ifndef ES8388AUDIOSINK_H
#define ES8388AUDIOSINK_H
#include "driver/i2s.h"
#include <driver/i2c.h>
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/unistd.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_log.h"
#define ES8388_ADDR 0x20
#define ACK_CHECK_EN 0x1
@@ -78,16 +77,14 @@
#define ES8388_DACCONTROL29 0x33
#define ES8388_DACCONTROL30 0x34
class ES8388AudioSink : public BufferedAudioSink
{
public:
class ES8388AudioSink : public BufferedAudioSink {
public:
ES8388AudioSink();
~ES8388AudioSink();
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_OUT1, // this is the additional gain for OUT1
ES_OUT2 // this is the additional gain for OUT2
@@ -97,7 +94,8 @@ public:
void volume(const ES8388_OUT out, const uint8_t vol);
void writeReg(uint8_t reg_add, uint8_t data);
private:
private:
i2c_config_t i2c_config;
i2c_port_t i2c_port = 0;
};

View File

@@ -1,22 +1,22 @@
#ifndef ES9018AUDIOSINK_H
#define ES9018AUDIOSINK_H
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "BufferedAudioSink.h"
#include "esp_err.h"
#include "esp_log.h"
class ES9018AudioSink : public BufferedAudioSink
{
public:
class ES9018AudioSink : public BufferedAudioSink {
public:
ES9018AudioSink();
~ES9018AudioSink();
private:
private:
};
#endif

View File

@@ -1,22 +1,22 @@
#ifndef INTERNALAUDIOSINK_H
#define INTERNALAUDIOSINK_H
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "BufferedAudioSink.h"
#include "esp_err.h"
#include "esp_log.h"
class InternalAudioSink : public BufferedAudioSink
{
public:
class InternalAudioSink : public BufferedAudioSink {
public:
InternalAudioSink();
~InternalAudioSink();
private:
private:
};
#endif

View File

@@ -1,22 +1,22 @@
#ifndef PCM5102AUDIOSINK_H
#define PCM5102AUDIOSINK_H
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "BufferedAudioSink.h"
#include "esp_err.h"
#include "esp_log.h"
class PCM5102AudioSink : public BufferedAudioSink
{
public:
class PCM5102AudioSink : public BufferedAudioSink {
public:
PCM5102AudioSink();
~PCM5102AudioSink();
private:
private:
};
#endif

View File

@@ -1,26 +1,28 @@
#ifndef SPDIFAUDIOSINK_H
#define SPDIFAUDIOSINK_H
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <iostream>
#include <vector>
#include "BufferedAudioSink.h"
#include "esp_err.h"
#include "esp_log.h"
class SPDIFAudioSink : public BufferedAudioSink
{
private:
class SPDIFAudioSink : public BufferedAudioSink {
private:
uint8_t spdifPin;
public:
public:
explicit SPDIFAudioSink(uint8_t spdifPin);
~SPDIFAudioSink() override;
void feedPCMFrames(const uint8_t *buffer, size_t bytes) override;
bool setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t bitDepth) override;
private:
void feedPCMFrames(const uint8_t* buffer, size_t bytes) override;
bool setParams(uint32_t sampleRate, uint8_t channelCount,
uint8_t bitDepth) override;
private:
};
#endif

View File

@@ -1,28 +1,26 @@
#ifndef TAS5711AUDIOSINK_H
#define TAS5711AUDIOSINK_H
#include "driver/i2s.h"
#include <driver/i2c.h>
#include <vector>
#include <iostream>
#include "BufferedAudioSink.h"
#include <stdio.h>
#include <string.h>
#include <sys/unistd.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_log.h"
class TAS5711AudioSink : public BufferedAudioSink
{
public:
class TAS5711AudioSink : public BufferedAudioSink {
public:
TAS5711AudioSink();
~TAS5711AudioSink();
void writeReg(uint8_t reg, uint8_t value);
private:
private:
i2c_config_t i2c_config;
i2c_port_t i2c_port = 0;
};

View File

@@ -89,7 +89,7 @@
#define AC_DAC_DAPOPT 0xb1
#define DAC_DAP_ENA 0xb5
typedef enum{
typedef enum {
SAMPLE_RATE_8000 = 0x0000,
SAMPLE_RATE_11052 = 0x1000,
SAMPLE_RATE_12000 = 0x2000,
@@ -103,7 +103,7 @@ typedef enum{
SAMPLE_RATE_192000 = 0xa000,
} ac_adda_fs_i2s1_t;
typedef enum{
typedef enum {
BCLK_DIV_1 = 0x0,
BCLK_DIV_2 = 0x1,
BCLK_DIV_4 = 0x2,
@@ -120,12 +120,12 @@ typedef enum{
BCLK_DIV_192 = 0xd,
} ac_i2s1_bclk_div_t;
typedef enum{
LRCK_DIV_16 =0x0,
LRCK_DIV_32 =0x1,
LRCK_DIV_64 =0x2,
LRCK_DIV_128 =0x3,
LRCK_DIV_256 =0x4,
typedef enum {
LRCK_DIV_16 = 0x0,
LRCK_DIV_32 = 0x1,
LRCK_DIV_64 = 0x2,
LRCK_DIV_128 = 0x3,
LRCK_DIV_256 = 0x4,
} ac_i2s1_lrck_div_t;
typedef enum {
@@ -151,11 +151,11 @@ typedef enum {
AC_MODULE_MAX
} ac_module_t;
typedef enum{
typedef enum {
SRC_MIC1 = 1,
SRC_MIC2 = 2,
SRC_LINEIN = 3,
}ac_output_mixer_source_t;
} ac_output_mixer_source_t;
typedef enum {
GAIN_N45DB = 0,

View File

@@ -9,13 +9,13 @@
*
*/
#include "freertos/FreeRTOS.h"
#include "driver/i2s.h"
#include "freertos/FreeRTOS.h"
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
struct adac_s {
bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t *config);
bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t* config);
void (*deinit)(void);
void (*power)(adac_power_e mode);
void (*speaker)(bool active);

View File

@@ -23,7 +23,8 @@
/*
* 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_REG03 0x03 /* adc fsmode and osr */
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
@@ -42,7 +43,8 @@
#define ES8311_SYSTEM_REG11 0x11 /* system */
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
#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_REG16 0x16 /* ADC */
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
@@ -66,7 +68,6 @@
#define ES8311_MAX_REGISTER 0xFF
typedef struct {
ESCodecMode esMode;
i2c_port_t i2c_port_num;
@@ -75,23 +76,22 @@ typedef struct {
AdcInput adcInput;
} Es8311Config;
#define AUDIO_CODEC_ES8311_DEFAULT(){ \
#define AUDIO_CODEC_ES8311_DEFAULT() \
{ \
.esMode = ES_MODE_SLAVE, \
.i2c_port_num = I2C_NUM_0, \
.i2c_cfg = { \
.mode = I2C_MODE_MASTER, \
.i2c_cfg = {.mode = I2C_MODE_MASTER, \
.sda_io_num = IIC_DATA, \
.scl_io_num = IIC_CLK, \
.sda_pullup_en = GPIO_PULLUP_ENABLE,\
.scl_pullup_en = GPIO_PULLUP_ENABLE,\
.master.clk_speed = 100000\
}, \
.adcInput = ADC_INPUT_LINPUT1_RINPUT1,\
.dacOutput = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2,\
};
.sda_pullup_en = GPIO_PULLUP_ENABLE, \
.scl_pullup_en = GPIO_PULLUP_ENABLE, \
.master.clk_speed = 100000}, \
.adcInput = ADC_INPUT_LINPUT1_RINPUT1, \
.dacOutput = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | \
DAC_OUTPUT_ROUT2, \
};
int Es8311Init(Es8311Config *cfg);
int Es8311Init(Es8311Config* cfg);
void Es8311Uninit();
esp_err_t Es8311GetRef(bool flag);
esp_err_t Es7243Init(void);
@@ -107,9 +107,9 @@ int Es8311Start(ESCodecModule mode);
int Es8311Stop(ESCodecModule mode);
int Es8311SetVoiceVolume(int volume);
int Es8311GetVoiceVolume(int *volume);
int Es8311GetVoiceVolume(int* volume);
int Es8311SetVoiceMute(int enable);
int Es8311GetVoiceMute(int *mute);
int Es8311GetVoiceMute(int* mute);
int Es8311SetMicGain(MicGain gain);
int Es8311ConfigAdcInput(AdcInput input);

View File

@@ -1,34 +1,30 @@
#pragma once
#include <vector>
#include <fstream>
#include "AudioSink.h"
#include <BellTask.h>
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <BellTask.h>
#include <unistd.h>
#include <fstream>
#include <memory>
#include <mutex>
#include <vector>
#include "AudioSink.h"
#define PCM_DEVICE "default"
template <typename T, int SIZE>
class RingbufferPointer
{
class RingbufferPointer {
typedef std::unique_ptr<T> TPointer;
public:
explicit RingbufferPointer()
{
public:
explicit RingbufferPointer() {
// create objects
for (int i = 0; i < SIZE; i++)
{
for (int i = 0; i < SIZE; i++) {
buf_[i] = std::make_unique<T>();
}
}
bool push(TPointer &item)
{
bool push(TPointer& item) {
std::lock_guard<std::mutex> lock(mutex_);
if (full())
return false;
@@ -44,8 +40,7 @@ public:
return true;
}
bool pop(TPointer &item)
{
bool pop(TPointer& item) {
std::lock_guard<std::mutex> lock(mutex_);
if (empty())
return false;
@@ -58,34 +53,22 @@ public:
return true;
}
void reset()
{
void reset() {
std::lock_guard<std::mutex> lock(mutex_);
head_ = tail_;
full_ = false;
}
bool empty() const
{
return (!full_ && (head_ == tail_));
}
bool empty() const { return (!full_ && (head_ == tail_)); }
bool full() const
{
return full_;
}
bool full() const { return full_; }
int capacity() const
{
return max_size_;
}
int capacity() const { return max_size_; }
int size() const
{
int size() const {
int size = max_size_;
if (!full_)
{
if (!full_) {
if (head_ >= tail_)
size = head_ - tail_;
else
@@ -95,7 +78,7 @@ public:
return size;
}
private:
private:
TPointer buf_[SIZE];
std::mutex mutex_;
@@ -105,19 +88,18 @@ private:
bool full_ = 0;
};
class ALSAAudioSink : public AudioSink, public bell::Task
{
public:
class ALSAAudioSink : public AudioSink, public bell::Task {
public:
ALSAAudioSink();
~ALSAAudioSink();
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
void feedPCMFrames(const uint8_t* buffer, size_t bytes);
void runTask();
private:
private:
RingbufferPointer<std::vector<uint8_t>, 3> ringbuffer;
unsigned int pcm;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
snd_pcm_t* pcm_handle;
snd_pcm_hw_params_t* params;
snd_pcm_uframes_t frames;
int buff_size;
std::vector<uint8_t> buff;

View File

@@ -1,16 +1,17 @@
#pragma once
#include <vector>
#include <fstream>
#include "AudioSink.h"
#include <stddef.h> // for size_t
#include <stdint.h> // for uint8_t
#include <fstream> // for ofstream
class NamedPipeAudioSink : public AudioSink
{
public:
#include "AudioSink.h" // for AudioSink
class NamedPipeAudioSink : public AudioSink {
public:
NamedPipeAudioSink();
~NamedPipeAudioSink();
void feedPCMFrames(const uint8_t *buffer, size_t bytes);
void feedPCMFrames(const uint8_t* buffer, size_t bytes);
private:
private:
std::ofstream namedPipeFile;
};

View File

@@ -1,13 +1,11 @@
#include "ALSAAudioSink.h"
ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0)
{
ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0) {
/* Open the PCM device in playback mode */
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
SND_PCM_STREAM_PLAYBACK, 0) < 0)
{
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) <
0) {
printf("ERROR: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE,
snd_strerror(pcm));
}
/* Allocate parameters object and fill it with default values*/
@@ -57,44 +55,37 @@ ALSAAudioSink::ALSAAudioSink() : Task("", 0, 0, 0)
this->startTask();
}
ALSAAudioSink::~ALSAAudioSink()
{
ALSAAudioSink::~ALSAAudioSink() {
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
}
void ALSAAudioSink::runTask()
{
void ALSAAudioSink::runTask() {
std::unique_ptr<std::vector<uint8_t>> dataPtr;
while (true)
{
if (!this->ringbuffer.pop(dataPtr))
{
while (true) {
if (!this->ringbuffer.pop(dataPtr)) {
usleep(100);
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);
}
else if (pcm < 0)
{
} else if (pcm < 0) {
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);
while (buff.size() > this->buff_size)
{
auto ptr = std::make_unique<std::vector<uint8_t>>(this->buff.begin(), this->buff.begin() + this->buff_size);
this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size, this->buff.end());
while (!this->ringbuffer.push(ptr))
{
while (buff.size() > this->buff_size) {
auto ptr = std::make_unique<std::vector<uint8_t>>(
this->buff.begin(), this->buff.begin() + this->buff_size);
this->buff = std::vector<uint8_t>(this->buff.begin() + this->buff_size,
this->buff.end());
while (!this->ringbuffer.push(ptr)) {
usleep(100);
};
}

View File

@@ -1,20 +1,18 @@
#include "NamedPipeAudioSink.h"
NamedPipeAudioSink::NamedPipeAudioSink()
{
#include <stdio.h> // for printf
NamedPipeAudioSink::NamedPipeAudioSink() {
printf("Start\n");
this->namedPipeFile = std::ofstream("outputFifo", std::ios::binary);
printf("stop\n");
}
NamedPipeAudioSink::~NamedPipeAudioSink()
{
NamedPipeAudioSink::~NamedPipeAudioSink() {
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
this->namedPipeFile.write((char*)buffer, (long)bytes);
this->namedPipeFile.flush();

View File

@@ -1,12 +1,12 @@
#include "PortAudioSink.h"
PortAudioSink::PortAudioSink()
{
PortAudioSink::PortAudioSink() {
Pa_Initialize();
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) {
Pa_StopStream(stream);
}
@@ -39,27 +39,19 @@ bool PortAudioSink::setParams(uint32_t sampleRate, uint8_t channelCount, uint8_t
outputParameters.suggestedLatency = 0.050;
outputParameters.hostApiSpecificStreamInfo = NULL;
PaError err = Pa_OpenStream(
&stream,
NULL,
&outputParameters,
sampleRate,
4096 / (channelCount * bitDepth / 8),
paClipOff,
PaError err = Pa_OpenStream(&stream, NULL, &outputParameters, sampleRate,
4096 / (channelCount * bitDepth / 8), paClipOff,
NULL, // blocking api
NULL
);
NULL);
Pa_StartStream(stream);
return !err;
}
PortAudioSink::~PortAudioSink()
{
PortAudioSink::~PortAudioSink() {
Pa_StopStream(stream);
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);
}

View File

@@ -1,8 +1,14 @@
#include "BellHTTPServer.h"
#include <mutex>
#include <regex>
#include "CivetServer.h"
#include "civetweb.h"
#include <string.h> // for memcpy
#include <cassert> // for assert
#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;
@@ -195,7 +201,8 @@ std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeJsonResponse(
return response;
}
std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeEmptyResponse() {
std::unique_ptr<BellHTTPServer::HTTPResponse>
BellHTTPServer::makeEmptyResponse() {
auto response = std::make_unique<BellHTTPServer::HTTPResponse>();
return response;
}
@@ -225,8 +232,10 @@ void BellHTTPServer::registerNotFound(HTTPHandler handler) {
std::unordered_map<std::string, std::string> BellHTTPServer::extractParams(
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>*)
mg_get_user_connection_data(conn);
*(std::unordered_map<std::string, std::string>*)data;
return params;
}

View File

@@ -1,14 +1,18 @@
#include "BellTar.h"
#include <sys/stat.h>
#include <fstream>
#include <sys/stat.h> // for mkdir
using namespace bell::BellTar;
#include <cassert>
#include <cstdio> // for sprintf, snprintf and sscanf
#include <algorithm> // for min
#include <cassert> // for assert
#include <cstdint> // for uint8_t
#include <cstdio> // for sprintf, size_t, sscanf, EOF, NULL
#include <cstdlib> // for rand
#include <cstring> // for strlen and memset
#include <cstring> // for memset, strlen
#include <ctime> // for time
#include <fstream> // for ofstream
#include <vector> // for vector
#ifdef _WIN32
#include <direct.h>
#endif
@@ -59,7 +63,7 @@ void header_set_metadata(tar_header* header) {
std::memset(header, 0, sizeof(tar_header));
std::sprintf(header->magic, "ustar");
std::sprintf(header->mtime, "%011lo", (unsigned long) std::time(NULL));
std::sprintf(header->mtime, "%011lo", (unsigned long)std::time(NULL));
std::sprintf(header->mode, "%07o", 0644);
std::sprintf(header->uname, "unkown"); // ... a bit random
std::sprintf(header->gname, "users");
@@ -284,7 +288,11 @@ void reader::extract_all_files(std::string dest_directory) {
auto fileName = get_next_file_name();
// 0 is the normal file type, skip apple's ._ files
#if __cplusplus >= 202002L
if (fileType == '0' && !fileName.starts_with("._")) {
#else
if (fileType == '0' && fileName.find("._") != 0) {
#endif
std::string path = dest_directory + "/" + fileName;
size_t pos = 0;

View File

@@ -1,5 +1,10 @@
#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) {
this->stream = stream;
@@ -24,34 +29,28 @@ void bell::BinaryReader::skip(size_t pos) {
int32_t bell::BinaryReader::readInt() {
uint8_t b[4];
if (stream->read((uint8_t *) b,4) != 4)
if (stream->read((uint8_t*)b, 4) != 4)
return 0;
return static_cast<int32_t>(
(b[3]) |
(b[2] << 8) |
(b[1] << 16)|
(b[0] << 24) );
return static_cast<int32_t>((b[3]) | (b[2] << 8) | (b[1] << 16) |
(b[0] << 24));
}
int16_t bell::BinaryReader::readShort() {
uint8_t b[2];
if (stream->read((uint8_t *) b,2) != 2)
if (stream->read((uint8_t*)b, 2) != 2)
return 0;
return static_cast<int16_t>(
(b[1]) |
(b[0] << 8));
return static_cast<int16_t>((b[1]) | (b[0] << 8));
}
uint32_t bell::BinaryReader::readUInt() {
return readInt() & 0xffffffffL;
}
uint8_t bell::BinaryReader::readByte() {
uint8_t b[1];
if (stream->read((uint8_t *) b,1) != 1)
if (stream->read((uint8_t*)b, 1) != 1)
return 0;
return b[0];
}
@@ -66,7 +65,5 @@ long long bell::BinaryReader::readLong() {
long high = readInt();
long low = readInt();
return static_cast<long long>(
((long long) high << 32) | low );
return static_cast<long long>(((long long)high << 32) | low);
}

View File

@@ -1,5 +1,5 @@
#include <BinaryStream.h>
#include <sstream>
#include <stdexcept> // for runtime_error
using namespace bell;

View File

@@ -1,14 +1,15 @@
#include "BufferedStream.h"
#include <cstring>
BufferedStream::BufferedStream(
const std::string &taskName,
uint32_t bufferSize,
uint32_t readThreshold,
uint32_t readSize,
#include <stdlib.h> // for free, malloc
#include <algorithm> // for min
#include <cstdint> // for uint32_t
#include <cstring> // for memcpy
#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 notReadyThreshold,
bool waitForReady)
uint32_t notReadyThreshold, bool waitForReady)
: bell::Task(taskName, 4096, 5, 0) {
this->bufferSize = bufferSize;
this->readAt = bufferSize - readThreshold;
@@ -16,7 +17,7 @@ BufferedStream::BufferedStream(
this->readyThreshold = readyThreshold;
this->notReadyThreshold = notReadyThreshold;
this->waitForReady = waitForReady;
this->buf = static_cast<uint8_t *>(malloc(bufferSize));
this->buf = static_cast<uint8_t*>(malloc(bufferSize));
this->bufEnd = buf + bufferSize;
reset();
}
@@ -44,7 +45,7 @@ void BufferedStream::reset() {
this->terminate = false;
}
bool BufferedStream::open(const std::shared_ptr<bell::ByteStream> &stream) {
bool BufferedStream::open(const std::shared_ptr<bell::ByteStream>& stream) {
if (this->running)
this->close();
reset();
@@ -53,7 +54,8 @@ bool BufferedStream::open(const std::shared_ptr<bell::ByteStream> &stream) {
return source.get();
}
bool BufferedStream::open(const StreamReader &newReader, uint32_t initialOffset) {
bool BufferedStream::open(const StreamReader& newReader,
uint32_t initialOffset) {
if (this->running)
this->close();
reset();
@@ -83,7 +85,7 @@ size_t BufferedStream::size() {
return source->size();
}
uint32_t BufferedStream::lengthBetween(uint8_t *me, uint8_t *other) {
uint32_t BufferedStream::lengthBetween(uint8_t* me, uint8_t* other) {
const std::lock_guard lock(readMutex);
if (other <= me) {
// buf .... other ...... me ........ bufEnd
@@ -95,18 +97,21 @@ 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()) {
while ((source || reader) && !isReady()) {} // end waiting after termination
while ((source || reader) && !isReady()) {
} // end waiting after termination
}
if (!running && !readAvailable) {
reset();
return 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) {
uint32_t toRead = std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
uint32_t toRead =
std::min(toReadTotal, lengthBetween(bufReadPtr, bufWritePtr));
if (dst) {
memcpy(dst, bufReadPtr, toRead);
dst += toRead;
@@ -145,7 +150,8 @@ void BufferedStream::runTask() {
uint32_t len;
bool wasReady = isReady();
do {
uint32_t toRead = std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
uint32_t toRead =
std::min(readSize, lengthBetween(bufWritePtr, bufReadPtr));
if (!source) {
len = 0;
break;
@@ -156,7 +162,11 @@ void BufferedStream::runTask() {
bufWritePtr += len;
if (bufWritePtr >= bufEnd) // TODO is == enough here?
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)
source = reader(bufferTotal);
else if (!len)

View File

@@ -1,24 +1,23 @@
#include "CircularBuffer.h"
#include <algorithm> // for min
using namespace bell;
CircularBuffer::CircularBuffer(size_t dataCapacity)
{
CircularBuffer::CircularBuffer(size_t dataCapacity) {
this->dataCapacity = dataCapacity;
buffer = std::vector<uint8_t>(dataCapacity);
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)
return 0;
std::lock_guard<std::mutex> guard(bufferMutex);
size_t bytesToWrite = std::min(bytes, dataCapacity - dataSize);
// Write in a single step
if (bytesToWrite <= dataCapacity - endIndex)
{
if (bytesToWrite <= dataCapacity - endIndex) {
memcpy(buffer.data() + endIndex, data, bytesToWrite);
endIndex += bytesToWrite;
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)
return 0;
@@ -67,16 +65,14 @@ size_t CircularBuffer::read(uint8_t *data, size_t bytes)
size_t bytesToRead = std::min(bytes, dataSize);
// Read in a single step
if (bytesToRead <= dataCapacity - begIndex)
{
if (bytesToRead <= dataCapacity - begIndex) {
memcpy(data, buffer.data() + begIndex, bytesToRead);
begIndex += bytesToRead;
if (begIndex == dataCapacity)
begIndex = 0;
}
// Read in two steps
else
{
else {
size_t firstChunkSize = dataCapacity - begIndex;
memcpy(data, buffer.data() + begIndex, firstChunkSize);
size_t secondChunkSize = bytesToRead - firstChunkSize;

View File

@@ -1,6 +1,14 @@
#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;
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) {}

View File

@@ -1,55 +1,48 @@
#include "FileStream.h"
#include <stdexcept> // for runtime_error
#include "BellLogger.h" // for 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");
if (file == NULL)
{
if (file == NULL) {
throw std::runtime_error("Could not open file: " + path);
}
}
FileStream::~FileStream()
{
FileStream::~FileStream() {
close();
}
size_t FileStream::read(uint8_t *buf, size_t nbytes)
{
if (file == NULL)
{
size_t FileStream::read(uint8_t* buf, size_t nbytes) {
if (file == NULL) {
throw std::runtime_error("Stream is closed");
}
return fread(buf, 1, nbytes, file);
}
size_t FileStream::skip(size_t nbytes)
{
if (file == NULL)
{
size_t FileStream::skip(size_t nbytes) {
if (file == NULL) {
throw std::runtime_error("Stream is closed");
}
return fseek(file, nbytes, SEEK_CUR);
}
size_t FileStream::position()
{
if (file == NULL)
{
size_t FileStream::position() {
if (file == NULL) {
throw std::runtime_error("Stream is closed");
}
return ftell(file);
}
size_t FileStream::size()
{
if (file == NULL)
{
size_t FileStream::size() {
if (file == NULL) {
throw std::runtime_error("Stream is closed");
}
@@ -60,10 +53,8 @@ size_t FileStream::size()
return size;
}
void FileStream::close()
{
if (file != NULL)
{
void FileStream::close() {
if (file != NULL) {
fclose(file);
file = NULL;
}

View File

@@ -1,5 +1,14 @@
#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;
void HTTPClient::Response::connect(const std::string& url) {

View File

@@ -1,9 +1,17 @@
#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;
int SocketBuffer::open(const std::string& hostname, int port, bool isSSL) {
if (internalSocket != nullptr) { close(); }
if (internalSocket != nullptr) {
close();
}
if (isSSL) {
internalSocket = std::make_unique<bell::TLSSocket>();
} else {

View File

@@ -1,5 +1,14 @@
#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

View File

@@ -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 */
// get the schema
if (sscanf(url, "%[^:]:/", scratch) > 0) match[1] = scratch;
if (sscanf(url, "%[^:]:/", scratch) > 0)
match[1] = scratch;
// 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
url = strstr(url, match[2].c_str()) + match[2].size();
if (sscanf(url, "/%512[^?]", scratch) > 0) match[3] = scratch;
else if (*url && *url != '?' && *url != '#') url++;
if (sscanf(url, "/%512[^?]", scratch) > 0)
match[3] = scratch;
else if (*url && *url != '?' && *url != '#')
url++;
// get the query
if (match[3].size()) url += match[3].size() + 1;
if (sscanf(url, "?%512[^#]", scratch) > 0) match[4] = scratch;
if (match[3].size())
url += match[3].size() + 1;
if (sscanf(url, "?%512[^#]", scratch) > 0)
match[4] = scratch;
// get the hash
if (match[4].size()) url += match[4].size() + 1;
if (sscanf(url, "#%512s", scratch) > 0) match[5] = scratch;
if (match[4].size())
url += match[4].size() + 1;
if (sscanf(url, "#%512s", scratch) > 0)
match[5] = scratch;
// fix the acquired items
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
if (match[1].size() == 0 || match[2].size() == 0) match.clear();
if (match[1].size() == 0 || match[2].size() == 0)
match.clear();
}
#else
const std::regex URLParser::urlParseRegex = std::regex(
"^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(\\?(?:[^#]*))?(#(?:.*))?");
#endif
}
} // namespace bell

View File

@@ -1,5 +1,15 @@
#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;
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,
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;
}
@@ -110,6 +121,8 @@ int bell::X509Bundle::crtVerifyCallback(void* buf, mbedtls_x509_crt* crt,
ret = crtCheckCertificate(
child, s_crt_bundle.crts[middle] + CRT_HEADER_OFFSET + name_len,
key_len);
} else {
BELL_LOG(error, TAG, "Certificate not found in bundle");
}
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");
}
bundleBytes.resize(bundle_size);
memcpy(bundleBytes.data(), x509_bundle, bundle_size);
const uint8_t* cur_crt;
/* This is the maximum region that is allowed to access */
const uint8_t* bundle_end = x509_bundle + bundle_size;
cur_crt = x509_bundle + BUNDLE_HEADER_OFFSET;
const uint8_t* bundle_end = bundleBytes.data() + bundle_size;
cur_crt = bundleBytes.data() + BUNDLE_HEADER_OFFSET;
for (int i = 0; i < num_certs; i++) {
crts[i] = cur_crt;

View File

@@ -1,22 +1,18 @@
#pragma once
#include <BellLogger.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <utility>
#include <optional>
#include <regex>
#include <sstream>
#include <string>
#include <mutex>
#include <unordered_map>
#include "CivetServer.h"
#include "civetweb.h"
#include <BellLogger.h> // for bell
#include <stdint.h> // for uint8_t
#include <stdlib.h> // for free, size_t
#include <functional> // for function
#include <map> // for map
#include <memory> // for unique_ptr
#include <mutex> // for mutex
#include <string> // for string, hash, operator==, operator<
#include <unordered_map> // for unordered_map
#include <utility> // for pair
#include <vector> // for vector
#include "CivetServer.h" // for CivetServer, CivetHandler
using 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)>
WSStateHandler;
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(); };
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();
void registerNotFound(HTTPHandler handler);
@@ -88,7 +87,8 @@ class BellHTTPServer : public CivetHandler {
void registerWS(const std::string&, WSDataHandler dataHandler,
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:
std::unique_ptr<CivetServer> server;

View File

@@ -14,6 +14,6 @@ class Socket {
virtual size_t read(uint8_t* buf, size_t len) = 0;
virtual bool isOpen() = 0;
virtual void close() = 0;
virtual int getFd() = 0;
};
} // namespace bell

View File

@@ -1,8 +1,7 @@
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <iostream> // for istream, ostream
#include <string> // for string
namespace bell::BellTar {
typedef long long unsigned file_size_t;

View File

@@ -1,17 +1,14 @@
#pragma once
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <memory>
#include "ByteStream.h"
#include <stdint.h> // for uint8_t, int16_t, int32_t, uint32_t
#include <stdlib.h> // for size_t
#include <memory> // for shared_ptr
#include <vector> // for vector
namespace bell
{
class BinaryReader
{
namespace bell {
class ByteStream;
class BinaryReader {
std::shared_ptr<ByteStream> stream;
size_t currentPos = 0;
@@ -27,5 +24,5 @@ namespace bell
size_t position();
std::vector<uint8_t> readBytes(size_t);
void skip(size_t);
};
}
};
} // namespace bell

View File

@@ -1,10 +1,11 @@
#pragma once
#ifndef ESP_PLATFORM
#include <bit>
#include <bit> // for endian
#endif
#include <iostream>
#include <vector>
#include <stdint.h> // for int16_t, int32_t, int64_t, uint16_t, uint32_t
#include <cstddef> // for byte
#include <iostream> // for istream, ostream
namespace bell {
class BinaryStream {

View File

@@ -1,12 +1,16 @@
#pragma once
#include "ByteStream.h"
#include "BellTask.h"
#include "WrappedSemaphore.h"
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, uint8_t
#include <atomic> // for atomic
#include <functional> // for function
#include <memory> // for shared_ptr
#include <mutex> // for 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,
@@ -41,17 +45,13 @@ class BufferedStream : public bell::ByteStream, bell::Task {
* @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()
*/
BufferedStream(
const std::string &taskName,
uint32_t bufferSize,
uint32_t readThreshold,
uint32_t readSize,
uint32_t readyThreshold,
uint32_t notReadyThreshold,
BufferedStream(const std::string& taskName, uint32_t bufferSize,
uint32_t readThreshold, uint32_t readSize,
uint32_t readyThreshold, uint32_t notReadyThreshold,
bool waitForReady = false);
~BufferedStream() override;
bool open(const StreamPtr &stream);
bool open(const StreamReader &newReader, uint32_t initialOffset = 0);
bool open(const StreamPtr& stream);
bool open(const StreamReader& newReader, uint32_t initialOffset = 0);
void close() override;
// inherited methods
@@ -65,7 +65,7 @@ class BufferedStream : public bell::ByteStream, bell::Task {
* if the buffer does not contain len bytes available), or 0 if the source
* stream is already closed and there is no reader attached.
*/
size_t read(uint8_t *dst, size_t len) override;
size_t read(uint8_t* dst, size_t len) override;
size_t skip(size_t len) override;
size_t position() override;
size_t size() override;
@@ -105,21 +105,23 @@ class BufferedStream : public bell::ByteStream, bell::Task {
std::mutex runningMutex;
bool running = false;
bool terminate = false;
bell::WrappedSemaphore readSem; // signal to start writing to buffer after reading from it
std::mutex readMutex; // mutex for locking read operations during writing, and vice versa
bell::WrappedSemaphore
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 readAt;
uint32_t readSize;
uint32_t readyThreshold;
uint32_t notReadyThreshold;
bool waitForReady;
uint8_t *buf;
uint8_t *bufEnd;
uint8_t *bufReadPtr;
uint8_t *bufWritePtr;
uint8_t* buf;
uint8_t* bufEnd;
uint8_t* bufReadPtr;
uint8_t* bufWritePtr;
StreamPtr source;
StreamReader reader;
void runTask() override;
void reset();
uint32_t lengthBetween(uint8_t *me, uint8_t *other);
uint32_t lengthBetween(uint8_t* me, uint8_t* other);
};

Some files were not shown because too many files have changed in this diff Show More