├── .gitignore ├── ASYNC.md ├── LICENSE ├── README.md ├── SYNCHRONOUS.md ├── async ├── pbaudio.py ├── pico.py └── vs1053.py ├── plugins └── flac_plugin.bin ├── sdcard.py ├── synchronous ├── esp32audio.py ├── esp8266audio.py ├── pbaudio_syn.py ├── pico_syn.py ├── rectest.py └── vs1053_syn.py └── temp └── ADAFRUIT.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /ASYNC.md: -------------------------------------------------------------------------------- 1 | # 1. Asynchronous device driver for VS1053b 2 | 3 | The driver is asynchronous enabling other tasks (such as a GUI interface) to 4 | run concurrently while playing audio files. 5 | 6 | While the driver is cross-platform, testing on ESP8266 failed. It works on 7 | Pico, Pyboard D, Pyboard 1.x and Pyboard Lite (the latter restricted to 128Kbps 8 | MP3 files). The synchronous driver works on ESP8266. 9 | 10 | ## 1.1 ESP32 issue 11 | 12 | On ESP32 the `vs1053.py` file requires a minor mod to remove or comment out the 13 | `micropython.native` decorator on the `.play` method. With this done it will 14 | play MP3 at up to 256Kbps. I attempted FLAC playback, but this required 15 | overclocking to 240MHz and, while it worked, the margin was minimal. 16 | 17 | # 2. Wiring 18 | 19 | Pin numbers and the SPI bus ID may be changed to suit different platforms. The 20 | test script assumes a Pyboard host with the following wiring. Note `cs` denotes 21 | an active low chip select, `nc` denotes no connection. 22 | 23 | | VS1053 | Pyboard | Signal | 24 | |:-------|:--------|:-------------| 25 | | xdcs | Y2 | Data cs | 26 | | sdcs | Y3 | SD card cs (see below) | 27 | | cs | Y4 | Control cs (chip datasheet xcs) | 28 | | rst | Y5 | VS1053 hardware reset | 29 | | sclk | Y6 | SCK SPI bus 2 | 30 | | mosi | Y8 | MOSI | 31 | | miso | Y7 | MISO | 32 | | gnd | gnd | | 33 | | 3v3 | nc | 3.3V o/p | 34 | | vcc | vcc | 5V | 35 | | dreq | Y1 I/P | Data request | 36 | 37 | If the board's SD card slot is not used the `sdcs` pin may be unconnected. 38 | 39 | Headphones or an audio amplifier may be connected as follows: 40 | 41 | | VS1053 | Audio | 42 | |:-------|:------| 43 | | agnd | gnd | 44 | | lout | left | 45 | | rout | right | 46 | 47 | The Adafruit adaptor may be powered from 5V or 3.3V. 48 | 49 | # 3. Installation 50 | 51 | Copy the following files to the target filesystem: 52 | * `vs1053.py` The driver 53 | * `sdcard.py` SD card driver (in root directory). See below. 54 | Optional test script: 55 | * `pbaudio.py` For Pyboards. 56 | 57 | The test script will need to be adapted to reflect your MP3 files. It assumes 58 | files stored on an SD card in the board's socket. Adapt the script for files 59 | stored elsewhere - this has been tested with MP3's on the Pyboard SD card. 60 | 61 | The SD card driver is provided because the official version currently has 62 | [a bug](https://github.com/micropython/micropython/pull/6007). 63 | 64 | # 4. Typical usage 65 | 66 | This assumes an SD card fitted to the board with a file `music.mp3`: 67 | ```python 68 | from vs1053 import * 69 | from machine import SPI, Pin 70 | import uasyncio as asyncio 71 | 72 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 73 | sdcs = Pin('Y4', Pin.OUT, value=1) # SD card CS 74 | xcs = Pin('Y3', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 75 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 76 | dreq = Pin('Y1', Pin.IN) # Active high data request 77 | player = VS1053(SPI(2), reset, dreq, xdcs, xcs, sdcs, '/fc') 78 | 79 | async def main(): 80 | player.volume(-10, -10) # Volume -10dB (0,0 is loudest) 81 | with open('/fc/music.mp3', 'rb') as f: 82 | await player.play(f) 83 | 84 | asyncio.run(main()) 85 | ``` 86 | 87 | # 5. VS1053 class 88 | 89 | ## 5.1 Constructor 90 | 91 | This takes the following mandatory args: 92 | * `spi` An SPI bus instance. 93 | * `reset` A `Pin` instance defined as `Pin.OUT` with `value=1`. 94 | * `dreq` A `Pin` instance defined as `Pin.IN`. 95 | * `xdcs` A `Pin` instance defined as `Pin.OUT` with `value=1`. 96 | * `xcs` A `Pin` instance defined as `Pin.OUT` with `value=1`. 97 | 98 | Optional args: 99 | * `sdcs=None` A `Pin` instance defined as `Pin.OUT` with `value=1`. 100 | * `mp=None` A string defining the mount point (e.g. `/fc`). 101 | * `buffered=False` Setting this `True` causes the `.play` method to use a 2KiB 102 | buffer. This may improve performance; it is necessary on ESP32 as a firmware 103 | bug causes the normal `.play` method to fail. 104 | 105 | If no SD card is fitted the `sdcs` arg should be `None`. The `mp` arg may still 106 | be required: it should be the mount point of whatever filesystem is used as a 107 | data source. Providing this arg enables patches to be installed. 108 | 109 | ## 5.2 Asynchronous methods 110 | 111 | * `play` Arg `s` a stream providing MP3 data. Plays the stream with the task 112 | pausing until the stream is complete or cancellation occurs. 113 | * `cancel` No args. Cancels the currently playing track. 114 | * `sine_test` Arg `seconds=10` Plays a 517Hz sine wave for the specified time. 115 | The task pauses until complete. This test seems to set the volume to maximum, 116 | leaving it at that level after exit. 117 | 118 | ## 5.3 Synchronous methods 119 | 120 | ##### Audio 121 | 122 | * `volume` Args `left`, `right`, `powerdown=False` The `left` and `right` 123 | values are in dB with 0dB being the maximum and -63.5dB or below being silent. 124 | Out of range values are constrained to those limits. The `powerdown` arg puts 125 | the chip into a low power mode, cleared when `volume` is called with 126 | `powerdown` omitted. 127 | * `response` Sets bass boost and/or treble boost/cut. See 128 | [below](./ASYNC.md#531-setting-the-frequency-response). 129 | 130 | ##### I/O Pins 131 | 132 | * `pins_direction` Arg `bits`. The 8-bit mask defines the I/O pin direction, 1 133 | being output and 0 input. 134 | * `pins` Arg `data=None` If `data` is provided it issues the 8-bit value to 135 | the pins (if they are set to output). Returns the state of the pins. 136 | 137 | ##### Reporting 138 | 139 | * `version` No args. Returns version no. (currently 4). 140 | * `decode_time` No args. Returns the no. of seconds into the stream. 141 | * `byte_rate` No args. Returns the data rate in bytes/sec. 142 | 143 | ##### Special purpose 144 | 145 | * `mode` No args. Return the current mode (a 16 bit integer). See 146 | [below](./ASYNC.md#54-mode). 147 | * `mode_set` Arg `bits` Set specific mode bits. 148 | * `mode_clear` Arg `bits` Clear specific mode bits. 149 | * `reset` No arg. Issues a hardware reset to the VS1053 then `soft_reset`. 150 | * `soft_reset` No arg. Software reset of the VS1053. 151 | * `patch` Optional arg `loc` a directory containing plugin files for the chip. 152 | The default directory is `/plugins` on the mounted flash card. Plugins are 153 | installed in alphabetical order. See [Plugins](./ASYNC.md#7-plugins). 154 | * `enable_i2s` Args `rate=48` `mclock=False`. The `rate` arg may be 48, 96 or 155 | 192 KHz. Invalid rates will be ignored, the rate defaulting to 48KHz. The 156 | `mclock` arg enables an optional 12.288MHz clock to be output on chip pin 25. 157 | 158 | ### 5.3.1 Setting the frequency response 159 | 160 | The `response` synchronous method takes the following optional keyword only 161 | args. If no args are supplied, response will be set to flat. 162 | * `treble_amp` range -12dB to +10.5db. If zero, treble response will be flat. 163 | * `treble_freq` range 1000Hz to 15000Hz: lowest frequency of treble filter. 164 | * `bass_amp` range 0dB to +15dB. If zero, bass response will be flat. 165 | * `bass_freq` range 20Hz to 150Hz. Sets lower limit frequency. The datasheet 166 | section 9.6.3 suggests setting this to 1.5 times the lowest frequency the 167 | audio system can reproduce. 168 | 169 | Out of range args will be constrained to in-range values. 170 | 171 | The datasheet states "The Bass Enhancer ... is a powerful bass boosting DSP 172 | algorithm, which tries to take the most out of the users earphones without 173 | causing clipping". 174 | 175 | ## 5.4 Mode 176 | 177 | The VS1053b provides various configuration options in the form of a 16 bit mode 178 | value. Certain bits are reserved by the driver for its correct operation, but 179 | some are user configurable. The driver exports these as constants which may be 180 | set or cleared as follows. Note the combination of multiple settings using the 181 | logical or `|`. 182 | 183 | ```python 184 | from vs1053 import * 185 | player = VS1053(...) # Args omitted 186 | player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # Constants from driver 187 | # code omitted 188 | player.mode_clear(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # Turn off EarSpeaker 189 | ``` 190 | Do not set or clear arbitrary bits: only use the provided constants. These 191 | comprise: 192 | * `SM_EARSPEAKER_LO` EarSpeaker mode bits: see below. 193 | * `SM_EARSPEAKER_HI` 194 | * `SM_LAYER12` Enable MPEG layer 1 and 2 decode. Untested. 195 | See [section 6](./ASYNC.md#6-data-rates). 196 | * `SM_DIFF` Inverts the left channel. Used for differential mono output. 197 | * `SM_LINE_IN` Line/microphone input. Input is currently unsupported. 198 | 199 | `EarSpeaker` processing claims to move the sound stage in front of the listener 200 | when using headphones. Clearing both bits (the default) disables this. 201 | Increasing values of this 2-bit field denote higher levels of processing, so 202 | setting both bits invokes the maximum degree. 203 | 204 | Users should note the warning in section 9.6.1 of the datasheet: 205 | "If you enable Layer I and Layer II decoding, you are liable for any patent 206 | issues that may arise." 207 | 208 | # 6. Data rates 209 | 210 | The task of reading data and writing it to the VS1053 makes high demands on the 211 | host hardware to support the necessary throughput. While the code is cross 212 | platform it only produces good audio on high performing hosts. 213 | 214 | ## 6.1 Theory 215 | 216 | MP3 files may be created with various data rates. Testing was done with files 217 | having a 128Kbps rate. Note that this is the total rate for a stereo file. The 218 | driver, after initialisation, sets the SPI bus baudrate to 10.752MHz. In 219 | practice the rate may be less than that. Issuing `print(spi_instance)` will 220 | show the actual rate on an individual platform. The Pyboard D runs it at 9MHz, 221 | i.e. 111ns/bit (Pyboard 1.x is slightly faster). The following calculations are 222 | based on 9MHz transfers. 223 | 224 | For an Nbps data stream the time used by the bus in one second is 111N ns, 225 | which is 14.2ms for a 128Kbps stream. However the stream is handled twice: once 226 | in reading it from the SD card and again in writing it to the VS1053 giving an 227 | overhead of 222N ns/s. Consequently the overhead is 28.4ms/s or 2.8%. I have 228 | successfully tested MP3's having a 256Kbps rate and VBR files which have a 229 | slightly higher rate. 230 | 231 | The VS1053 can support lossless FLAC files with a plugin. However the data rate 232 | for FLAC files is about 700Kbps which gives an overhead of 159ms/s or 16%. This 233 | is the irreducible overhead caused by bus transfers, and takes no account of 234 | the Python code. 235 | 236 | ## 6.2 Test results 237 | 238 | Testing was done using the onboard SD card adaptor on the Adafruit board. Stock 239 | CPU frequency was used except where stated. 240 | 241 | On a Pyboard D or Pyboard 1.x the demo script worked with MP3 files including 242 | 256Kbps and VBR (variable bit rate) files. 243 | 244 | The Pyboard Lite worked with 128Kbps files; 256Kbps and VBR (variable bit rate) 245 | files exhibited distortion. 246 | 247 | The ESP32, even at 240MHz, failed properly to decode any file. I suspect this 248 | is a consequence of the underlying OS stealing processor timeslices. 249 | 250 | The ESP8266 also failed at 160MHz. Hard SPI transfers one byte at a time, at a 251 | bit rate of 8Mbps, but with a 7μs gap between each byte. The mean data rate is 252 | about 1Mbps which needs to be divided by two as there are two devices on the 253 | bus: the data source and the data sink. There are also periods of bus 254 | inactivity lasting 3ms. Evidently the net rate is insufficient, even with a 255 | 128Kbps file, to leave time for the Python code. 256 | 257 | ## 6.3 Application design and blocking 258 | 259 | In testing file reads from SD card sometimes block for over 4ms: this occurs 260 | when a 512 byte sector is retrieved. This may vary with the quality of the SD 261 | card. 262 | 263 | Other tasks must limit the length of time for which they monopolise the 264 | MicroPython VM. The VS1053 has a 2KiB buffer. A 128Kbps MP3 has a data rate of 265 | 16KiB/s so the buffer holds 125ms of data. If 256Kbps is used, this drops to 266 | 65ms. Given that `uasyncio` performs round-robin scheduling, this time is the 267 | maximum allowable sum of the blocking time of all running tasks. If this is 268 | exceeded the buffer will empty and sound dropouts will result. Note that FLAC 269 | playback reduces the buffer to 23ms. This places serious limits on the other 270 | tasks. 271 | 272 | The `dreq` signal is asserted when there is sufficient space in the VS1053b 273 | internal buffer for about 1/3 of its capacity, i.e. about 680 bytes. The driver 274 | uses the periods when `dreq` is `False` to allow other tasks to be scheduled. 275 | This ensures that the buffer is nearly full before other tasks are scheduled. 276 | 277 | It is worth monitoring `dreq` in a running application: it should regularly 278 | become `False`. If there are periods where this does not happen, it is likely 279 | that the buffer is becoming empty and dropouts will be heard. Solutions are 280 | to reduce blocking in application tasks, to use lower bit rate MP3 files, or 281 | to use the synchronous driver. 282 | 283 | # 7. Plugins 284 | 285 | These binary files provide a means of installing enhancements and bug fixes on 286 | the VS1053. These are stored in RAM so need to be loaded after a power cycle. 287 | The only plugin I have tested is the FLAC plugin. 288 | 289 | For some reason installing the FLAC plugin takes some 17s on ESP32 while being 290 | almost instant on a Pyboard. Plugins may be found on the 291 | [VLSI solutions](http://www.vlsi.fi/en/support/software/vs10xxpatches.html) site. 292 | The supplied `patch.bin` file enables FLAC decoding and was current at the time 293 | of writing (Aug 2022) See [main README](./README.md#4-plugins) for details of 294 | how to process `.plg` files. 295 | 296 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Peter Hinch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1. Device drivers for VS1053b 2 | 3 | The VS1053b is an audio player capable of handling MP3 files. Development was 4 | done with this [Adafruit breakout](https://www.adafruit.com/product/1381). This 5 | includes an SD card adaptor which may be used to store MP3 files. MP3 bit rates 6 | up to 256Kbps and VBR (variable bit rate) files are supported. Adafruit "Music 7 | Maker" Arduino shields [e.g. this one](https://www.adafruit.com/product/1790) 8 | have also been used with a MicroPython host running these drivers. See 9 | [this ref](https://github.com/peterhinch/micropython-vs1053/issues/4) for use 10 | with the Arduino shield. 11 | 12 | It should be noted that these drivers run the adaptor board's SD card at a 13 | higher clock rate than the official SD card driver. If this slot is used, good 14 | quality SD cards should be used. 15 | 16 | The interface uses seven I/O pins. However the Adafruit card provides 8 GPIO 17 | pins which are supported by the driver. You gain a pin :). 18 | 19 | There are two versions of the driver: synchronous and asynchronous. The 20 | asynchronous version uses `uasyncio` to enable tasks (such as a GUI interface) 21 | to run concurrently while playing audio files. Sadly the overhead of `uasyncio` 22 | prevents audio playback on ESP8266. 23 | 24 | ## 1.1 Version log 25 | 26 | V0.1.5 Aug 2022 Asynchronous version has an optional buffered mode. This may 27 | improve performance. It overcomes an apparent firmware bug which prevents the 28 | normal version from working on ESP32. 29 | 30 | V0.1.4 Aug 2022 The performance of the asynchronous driver is substantially 31 | improved, with some improvement to the synchronous driver. Both drivers have 32 | the means to enable I2S output from the VS1053b chip. 33 | 34 | ### [Asynchronous driver docs](./ASYNC.md) 35 | 36 | The synchronous driver has been tested on ESP8266 and ESP32. On Pyboards and 37 | the Pio CD quality audio may be achieved using FLAC files. 38 | 39 | ### [Synchronous driver docs](./SYNCHRONOUS.md) 40 | 41 | # 2. Compatibility matrix 42 | 43 | Music formats (lower MP3 rates exist but aren't considered music quality). 44 | 45 | | Format | Bits/s | Bytes/s | Notes | 46 | |:---------|:---------|:--------|:-----------------| 47 | | CD | 1.4M | 176.4K | | 48 | | FLAC | 700K | 88.2K | CD quality | 49 | | MP3 320K | 320K | 40K | Maximum MP3 rate | 50 | | MP3 VBR | 220-260K | <= 33K | V0 Variable rate | 51 | | MP3 250K | 250K | 31.25K | | 52 | | MP3 192K | 192K | 24K | | 53 | | MP3 128K | 128K | 16K | | 54 | 55 | The capability of the platform and driver combination is defined by the maximum 56 | rate that can be sustained. All lower rates can be assumed to work. In testing 57 | the following were the highest usable rates: 58 | 59 | | Platform | Synchronous | Asynchronous | 60 | |:-------------|:---------------|:---------------| 61 | | Pyboard 1.x | FLAC | FLAC | 62 | | Pyboard D | FLAC | FLAC | 63 | | Pyboard Lite | Not yet tested | MP3 <= 128Kbps | 64 | | Pico | FLAC | FLAC | 65 | | ESP32 | FLAC | VBR | 66 | | ESP8266 | MP3 <= 256Kbps | Unsupported | 67 | 68 | The synchronous driver also supports recording audio to an IMA ADPCM `wav` file 69 | which can be played by the VS1053b or by other applications. 70 | 71 | [Converting FLAC to MP3](https://wiki.archlinux.org/title/Convert_FLAC_to_MP3) 72 | 73 | # 3. Troubleshooting 74 | 75 | Timeout messages, e.g. "timeout waiting for response", are indicative of an SD 76 | card which cannot handle the required data rate. High quality cards are 77 | essential. 78 | 79 | Dropouts when using the asynchronous driver indicate that the driver can't 80 | supply data at the required rate. This can result from user tasks which demand 81 | too much processor time. Solutions are to reduce blocking, to use a lower MP3 82 | bit rate or to use the synchronous driver. 83 | 84 | # 4. Plugins 85 | 86 | These are available from 87 | [VLSI solutions](http://www.vlsi.fi/en/support/software/vs10xxpatches.html). 88 | They have a `.plg` extension but are C source files, intended for linking with 89 | user C applications. For use with Python these need to be converted to binary 90 | files. The supplied `patch.bin` file provides FLAC decoding and possibly bug 91 | fixes. It is current as of August 2022. 92 | 93 | The following describes the simple hack I used to convert from 94 | `vs1053b-patches-flac.plg` to `patch.bin`: this can be amended for other `.plg` 95 | files. 96 | 97 | Append the following code to the end of the `plg` file: 98 | ```C 99 | #include 100 | void main(){ 101 | FILE *file; 102 | file = fopen("patch.bin", "w"); 103 | char c; 104 | int n; 105 | unsigned short v; 106 | n = 0; 107 | while (n < PLUGIN_SIZE){ 108 | v = plugin[n++]; 109 | c = v & 0xFF; 110 | putc(c, file); 111 | c = v >> 8; 112 | putc(c, file); 113 | } 114 | fclose(file); 115 | } 116 | ``` 117 | Rename the file to have a `.c` extension, compile and run with: 118 | ```bash 119 | $ mv vs1053b-patches-flac.plg vs1053b-patches-flac.c 120 | $ gcc -o rats vs1053b-patches-flac.c 121 | $ chmod a+x rats 122 | $ ./rats 123 | ``` 124 | -------------------------------------------------------------------------------- /SYNCHRONOUS.md: -------------------------------------------------------------------------------- 1 | # 1. Synchronous device driver for VS1053b 2 | 3 | This driver is preferred for slow platforms such as ESP8266. It is a blocking 4 | driver: the `play` command will block for the duration of an MP3 file. It has 5 | been tested on an ESP8266 and ESP32. It will play 128Kbps and VBR MP3 files 6 | with the CPU running at stock frequency. 7 | 8 | Playback of CD quality audio may be achieved using FLAC files. Owing to the 9 | data rates a Pyboard host is required. The supplied plugin is necessary. 10 | 11 | Recording to a file is also supported. 12 | 13 | # 2. Wiring 14 | 15 | Pin numbers and the SPI bus ID may be changed to suit different platforms. The 16 | test script assumes a Pyboard host with the following wiring. Note `cs` denotes 17 | an active low chip select, `nc` denotes no connection. 18 | 19 | | VS1053 | Pyboard | Signal | 20 | |:-------|:--------|:-------------| 21 | | xdcs | Y2 | Data cs | 22 | | sdcs | Y3 | SD card cs (see below) | 23 | | cs | Y4 | Control cs (chip datasheet xcs) | 24 | | rst | Y5 | VS1053 hardware reset | 25 | | sclk | Y6 | SCK SPI bus 2 | 26 | | mosi | Y8 | MOSI | 27 | | miso | Y7 | MISO | 28 | | gnd | gnd | | 29 | | 3v3 | nc | 3.3V o/p | 30 | | vcc | vcc | 5V | 31 | | dreq | Y1 I/P | Data request | 32 | 33 | If the board's SD card slot is not used the `sdcs` pin may be unconnected. 34 | 35 | Headphones or an audio amplifier may be connected as follows: 36 | 37 | | VS1053 | Audio | 38 | |:-------|:------| 39 | | agnd | gnd | 40 | | lout | left | 41 | | rout | right | 42 | 43 | The Adafruit adaptor may be powered from 5V or 3.3V. 44 | 45 | # 3. Installation 46 | 47 | Copy the following files from the `synchronous` directory to the target 48 | filesystem: 49 | * `vs1053_syn.py` The driver 50 | * `sdcard.py` SD card driver (in root directory). See below. 51 | Optional test scripts (these differ in pin numbering): 52 | * `pbaudio_syn.py` For Pyboards. Plays back FLAC files. 53 | * `esp8266_audio.py` For ESP8266. MP3 playback. 54 | * `esp32_audio.py` ESP32. MP3 playback. 55 | * `rectest.py` Recording test script. 56 | 57 | Playback scripts will need to be adapted for your MP3 files. They assume 58 | files stored on an SD card in the board's socket. Adapt scripts for files 59 | stored elsewhere - this has been tested with MP3's on the Pyboard SD card. 60 | 61 | The SD card driver is provided because the official version currently has 62 | [a bug](https://github.com/micropython/micropython/pull/6007). 63 | 64 | # 4. Typical usage 65 | 66 | This assumes an SD card fitted to the board with a file `music.mp3`: 67 | ```python 68 | from vs1053_syn import * 69 | from machine import SPI, Pin 70 | import uasyncio as asyncio 71 | 72 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 73 | sdcs = Pin('Y4', Pin.OUT, value=1) # SD card CS 74 | xcs = Pin('Y3', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 75 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 76 | dreq = Pin('Y1', Pin.IN) # Active high data request 77 | player = VS1053(SPI(2), reset, dreq, xdcs, xcs, sdcs, '/fc') 78 | 79 | def main(): 80 | player.volume(-10, -10) # Volume -10dB (0,0 is loudest) 81 | with open('/fc/music.mp3', 'rb') as f: 82 | player.play(f) 83 | 84 | main() 85 | ``` 86 | ## 4.1 FLAC files 87 | 88 | To play CD quality FLAC files, the supplied plugin must be installed. The 89 | simplest way is to copy the `plugins` directory and its contents to the compact 90 | flash card installed on the Adafruit board. Installation is then done by 91 | issuing `.patch()` after instantiation: 92 | ```python 93 | player = VS1053(SPI(2), reset, dreq, xdcs, xcs, sdcs, '/fc') 94 | player.patch() 95 | ``` 96 | 97 | # 5. VS1053 class 98 | 99 | ## 5.1 Constructor 100 | 101 | This takes the following mandatory args: 102 | * `spi` An SPI bus instance. 103 | * `reset` A `Pin` instance defined as `Pin.OUT` with `value=1`. 104 | * `dreq` A `Pin` instance defined as `Pin.IN`. 105 | * `xdcs` A `Pin` instance defined as `Pin.OUT` with `value=1`. 106 | * `xcs` A `Pin` instance defined as `Pin.OUT` with `value=1`. 107 | 108 | Optional args - supply only if an SD card is fitted: 109 | * `sdcs` A `Pin` instance defined as `Pin.OUT` with `value=1`. 110 | * `mp` A string defining the mount point (e.g. `/fc`). 111 | * `cancb` A callback normally returning `True`. If it returns `False` while an 112 | MP3 is playing, playback will be cancelled. The callback should return as fast 113 | as possible: any delay is likely to affect playback. 114 | 115 | ## 5.2 Methods 116 | 117 | ##### Audio 118 | 119 | * `play` Arg `s` a stream providing MP3 data. Plays the stream. Blocks until 120 | the stream is complete or cancellation occurs. 121 | * `cancel` No args. Cancels the currently playing track. 122 | * `record` Record audio. See [Section 8](./SYNCHRONOUS.md#8-recording). 123 | * `sine_test` Arg `seconds=10` Plays a 517Hz sine wave for the specified time. 124 | Blocks until complete. This test sets the volume to maximum, leaving it at 125 | that level after exit. 126 | * `volume` Args `left`, `right`, `powerdown=False` The `left` and `right` 127 | values are in dB with 0dB being the maximum and -63.5dB or below being silent. 128 | Out of range values are constrained to those limits. The `powerdown` arg puts 129 | the chip into a low power mode, cleared when `volume` is called with 130 | `powerdown` omitted. 131 | * `response` Sets bass boost and/or treble boost/cut. See 132 | [below](./SYNCHRONOUS.md#521-setting-the-frequency-response). 133 | 134 | ##### I/O Pins 135 | 136 | * `pins_direction` Arg `bits`. The 8-bit mask defines the I/O pin direction, 1 137 | being output and 0 input. 138 | * `pins` Arg `data=None` If `data` is provided it issues the 8-bit value to 139 | the pins (if they are set to output). Returns the state of the pins. 140 | 141 | ##### Reporting 142 | 143 | * `version` No args. Returns version no. (currently 4). 144 | * `decode_time` No args. Returns the no. of seconds into the stream. 145 | * `byte_rate` No args. Returns the data rate in bytes/sec. 146 | 147 | ##### Special purpose 148 | 149 | * `mode` No args. Return the current mode (a 16 bit integer). See 150 | [below](./SYNCHRONOUS.md#53-mode). 151 | * `mode_set` Arg `bits` Set specific mode bits. 152 | * `mode_clear` Arg `bits` Clear specific mode bits. 153 | * `reset` No arg. Issues a hardware reset to the VS1053 then `soft_reset`. 154 | * `soft_reset` No arg. Software reset of the VS1053. 155 | * `patch` Optional arg `loc` a directory containing plugin files for the chip. 156 | The default directory is `/plugins` on the mounted flash card. Plugins are 157 | installed in alphabetical order. See [Plugins](./SYNCHRONOUS.md#7-plugins). 158 | * `enable_i2s` Args `rate=48` `mclock=False`. The `rate` arg may be 48, 96 or 159 | 192 KHz. Invalid rates will be ignored, the rate defaulting to 48KHz. The 160 | `mclock` arg enables an optional 12.288MHz clock to be output on chip pin 25. 161 | 162 | ### 5.2.1 Setting the frequency response 163 | 164 | The `response` synchronous method takes the following optional keyword only 165 | args. If no args are supplied, response will be set to flat. 166 | * `treble_amp` range -12dB to +10.5db. If zero, treble response will be flat. 167 | * `treble_freq` range 1000Hz to 15000Hz: lowest frequency of treble filter. 168 | * `bass_amp` range 0dB to +15dB. If zero, bass response will be flat. 169 | * `bass_freq` range 20Hz to 150Hz. Sets lower limit frequency. The datasheet 170 | section 9.6.3 suggests setting this to 1.5 times the lowest frequency the 171 | audio system can reproduce. 172 | 173 | Out of range args will be constrained to in-range values. 174 | 175 | The datasheet states "The Bass Enhancer ... is a powerful bass boosting DSP 176 | algorithm, which tries to take the most out of the users earphones without 177 | causing clipping". 178 | 179 | ## 5.3 Mode 180 | 181 | The VS1053b provides various configuration options in the form of a 16 bit mode 182 | value. Certain bits are reserved by the driver for its correct operation, but 183 | some are user configurable. The driver exports these as constants which may be 184 | set or cleared as follows. Note the combination of multiple settings using the 185 | logical or `|`. 186 | 187 | ```python 188 | from vs1053 import * 189 | player = VS1053(...) # Args omitted 190 | player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # Constants from driver 191 | # code omitted 192 | player.mode_clear(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # Turn off EarSpeaker 193 | ``` 194 | Do not set or clear arbitrary bits: only use the provided constants. These 195 | comprise: 196 | * `SM_EARSPEAKER_LO` EarSpeaker mode bits: see below. 197 | * `SM_EARSPEAKER_HI` 198 | * `SM_LAYER12` Enable MPEG layer 1 and 2 decode. Untested. 199 | See [section 6](./SYNCHRONOUS.md#6-data-rates). 200 | * `SM_DIFF` Inverts the left channel. Used for differential mono output. 201 | 202 | `EarSpeaker` processing claims to move the sound stage in front of the listener 203 | when using headphones. Clearing both bits (the default) disables this. 204 | Increasing values of this 2-bit field denote higher levels of processing, so 205 | setting both bits invokes the maximum degree. 206 | 207 | Users should note the warning in section 9.6.1 of the datasheet: 208 | "If you enable Layer I and Layer II decoding, you are liable for any patent 209 | issues that may arise." 210 | 211 | # 6. Data rates 212 | 213 | The task of reading data and writing it to the VS1053 makes high demands on the 214 | host hardware to support the necessary throughput. 215 | 216 | ## 6.1 Theory 217 | 218 | MP3 files may be created with various data rates. Testing was done with files 219 | having a 128Kbps rate. Note that this is the total rate for a stereo file. The 220 | driver, after initialisation, sets the SPI bus baudrate to 10.752MHz. In 221 | practice the rate may be less than that. Issuing `print(spi_instance)` will 222 | show the actual rate on an individual platform. The Pyboard D runs it at 9MHz, 223 | i.e. 111ns/bit (Pyboard 1.x is slightly faster). The following calculations are 224 | based on 9MHz transfers. 225 | 226 | For an Nbps data stream the time used by the bus in one second is 111N ns, 227 | which is 14.2ms for a 128Kbps stream. However the stream is handled twice: once 228 | in reading it from the SD card and again in writing it to the VS1053 giving an 229 | overhead of 222N ns/s. Consequently the overhead is 28.4ms/s or 2.8%. I have 230 | successfully tested MP3's having a 256Kbps rate and VBR files which have a 231 | slightly higher rate. 232 | 233 | The VS1053 can support lossless FLAC files with a plugin. However the data rate 234 | for FLAC files is about 1Mbps which would give an overhead of 222ms/s or 22.5%. 235 | This is the irreducible overhead caused by bus transfers, and takes no account 236 | of the Python code. WAV files are typically twice as bad. In testing neither 237 | played on an ESP32. 238 | 239 | FLAC files played correctly on a Pyboard. WAV files were not tested. There is 240 | no reason to use them as they may be converted to FLAC without loss of quality. 241 | 242 | ## 6.2 Test results 243 | 244 | Testing was done using the onboard SD card adaptor on the Adafruit board. Stock 245 | CPU frequency was used. 246 | 247 | Pyboards, ESP8266 and ESP32 work with this driver with MP3 files recorded at up 248 | to 256Kbps and VBR. Pyboards also work with FLAC files (using the plugin). 249 | 250 | # 7. Plugins 251 | 252 | These binary files provide a means of installing enhancements and bug fixes on 253 | the VS1053. These are stored in RAM so need to be loaded after a power cycle. 254 | The only plugin I have tested is the FLAC plugin. 255 | 256 | For some reason installing the FLAC plugin takes some 17s on ESP32 while being 257 | almost instant on a Pyboard. Plugins may be found on the 258 | [VLSI solutions](http://www.vlsi.fi/en/support/software/vs10xxpatches.html) site. 259 | The supplied `patch.bin` file enables FLAC decoding and was current at the time 260 | of writing (Aug 2022) See [main README](./README.md#4-plugins) for details of 261 | how to process `.plg` files. 262 | 263 | # 8. Recording 264 | 265 | Mono sound from a microphone or stereo sound from a line input may be recorded. 266 | The only file format supported by this driver is 267 | [IMA ADPCM](https://wiki.multimedia.cx/index.php?title=IMA_ADPCM): the chip 268 | compresses the data, reducing the data rate which must be handled by the host. 269 | Depending on host performance sample rates ranging from 8000sps up to around 270 | 25Ksps may be employed. 271 | 272 | Subjectively 8000sps provides very clear speech, albeit with some loss of high 273 | frequencies. A rate of 25Ksps yields good quality audio with good high 274 | frequency response. However the Nyquist theorem implies that it cannot be 275 | audiophile quality. 276 | 277 | The files produced can be played back on the VS1053; they have also been tested 278 | on Linux audio players with one anomaly. See 279 | [section 8.3](./SYNCHRONOUS.md#83-test-results). 280 | 281 | A 10s mono speech recording from the line input may be done as follows: 282 | ```python 283 | from vs1053_syn import * 284 | from machine import SPI, Pin 285 | 286 | spi = SPI(2) # 2 MOSI Y8 MISO Y7 SCK Y6 287 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 288 | xcs = Pin('Y4', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 289 | sdcs = Pin('Y3', Pin.OUT, value=1) # SD card CS 290 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 291 | dreq = Pin('Y1', Pin.IN) # Active high data request 292 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs=sdcs, mp='/fc') 293 | 294 | fn = '/fc/test_rec.wav' 295 | 296 | def main(t=10): 297 | print('Recording for {}s'.format(t)) 298 | overrun = player.record(fn, True, t * 1000, 8000, stereo=False) 299 | print('Record complete') 300 | if overrun > 768: 301 | print('High data rate: loss may have occurred. Value = {}'.format(overrun)) 302 | player.reset() # Necessary before playback 303 | print('Playback') 304 | player.volume(-10, -10) # -10dB (0dB is loudest) 305 | with open(fn, 'rb') as f: 306 | player.play(f) 307 | 308 | main() 309 | ``` 310 | 311 | ## 8.1 Wiring 312 | 313 | This is as per [section 2](./SYNCHRONOUS.md#2-wiring) with the addition of the 314 | microphone or line connection. The mic is connected between Adafruit JP3 pins 315 | 2 and 3. For line input the two channels are connected to Adafruit JP3 pins 1 316 | and 2 as below. n/c indicates no connection. 317 | 318 | | Chip pin/label | Adafruit | Microphone | Line | 319 | |:---------------|:-------------|:-----------|:-----| 320 | | 48 LINE 2 | JP3/1 LINE 2 | | L | 321 | | 1 MICP/Linein | JP3/2 MIC+ | + | R | 322 | | 2 MICN | JP3/3 MIC- | - | n/c | 323 | | Various | JP3/4 AGND | Gnd | Gnd | 324 | 325 | The Adafruit pins connect directly to the chip. The chip data section 6 326 | recommends circuitry between these audio signals and the chip for capacitive 327 | coupling and filtering. 328 | 329 | Line input signals should be restricted to 2.5Vp-p (data section 4.3) and mic 330 | amplitude should be limited to 48mV p-p to avoid distortion. 331 | 332 | Note microphone inputs are sensitive; precautions should be taken to minimise 333 | noise and hum pickup. 334 | 335 | ## 8.2 The record method 336 | 337 | This takes the following args: 338 | * `fn` Path and name of file for recording. 339 | * `line` `True` for line input, `False` for microphone. 340 | * `stop=10_000` If an integer is passed, recording will continue for that 341 | duration in ms. If a function is passed, recording will stop if the function 342 | returns `True`. The function should execute fast, otherwise the maximum 343 | recording speed will be reduced. 344 | * `sf=8000` Sample rate in samples/sec. 345 | * `agc_gain=None` See **gain** below. 346 | * `gain=None` See **gain** below. 347 | * `stereo=True` Set `False` for mono recording (halves file size). 348 | 349 | Return value: `overrun`. An integer indicating the likelihood of data loss due 350 | to excessive sample rate. Values < 768 indicate success. The closer the value 351 | to 1024 the greater the likelihood of loss. 352 | 353 | After recording, to return to playback mode the `.reset` method should be run. 354 | 355 | #### Gain 356 | 357 | The chip defines unity gain as a value of 1024. The gain range is linear, from 358 | 1 to 65535, with 0 having a special meaning. Hence a value of 1 corresponds to 359 | a gain of 1/1024 and a value of 65535 is a gain of 64. The driver uses values 360 | in dB which it converts to linear. The range is -60dB to +36dB; out of range 361 | values are constrained to in-range figures. A value of `None` produces the 0 362 | value whose meaning is discussed below. 363 | 364 | Recording may be done at fixed gain or using AGC (automatic gain control). The 365 | latter is usually preferred for speech: it adjusts the gain to compensate for 366 | variations in the speaker's volume. 367 | 368 | To use AGC, `gain` should be set to `None`. Then `agc_gain` sets the maximum 369 | gain that may be employed by the AGC. An `agc_gain` value of `None` allows the 370 | full range. For example an `agc_gain` value of 6 would allow the AGC to vary 371 | gain to a maximum of +6dB. 372 | 373 | To use a fixed gain (e.g. for music) `agc_gain` should be set to `None`, with 374 | a numeric value of `gain` specifying the required gain. Thus a value of 10 will 375 | provide a fixed gain of 10dB. 376 | 377 | ## 8.3 Test results 378 | 379 | To date testing has only be done on Pyboards. It is likely that ESP32 and 380 | ESP8266 will only work at low data rates. 381 | 382 | On a Pyboard 1.1 recording worked without loss at rates of up to and including 383 | 25K samples/s stereo. A sample rate of 32Ksps resulted in `record` returning 384 | `overrun` values over 768 and audio with clear artifacts. 385 | 386 | Recording at 8000sps produces about 4KiB/s for mono files, 8KiB/s for stereo. 387 | Both mono and stereo files play back correctly on the VS1053b. Stereo files 388 | also played back on the Linux players tested. Mono files played on VLC but not 389 | on rhythmbox. It is likely that the file header is incorrect but despite some 390 | effort I have failed to identify the problem. 391 | -------------------------------------------------------------------------------- /async/pbaudio.py: -------------------------------------------------------------------------------- 1 | # pbaudio.py VS1053b driver demo/test for Pyboard 2 | 3 | # (C) Peter Hinch 2020 4 | # Released under the MIT licence 5 | 6 | from vs1053 import * 7 | from machine import SPI, Pin 8 | import uasyncio as asyncio 9 | from pyb import LED 10 | 11 | # 128K conversion 12 | # ffmpeg -i yellow.flac -acodec libmp3lame -ab 128k yellow.mp3 13 | # VBR conversion 14 | # ffmpeg -i yellow.flac -acodec libmp3lame -qscale:a 0 yellow_v.mp3 15 | # Yeah, I know. I like Coldplay... 16 | 17 | spi = SPI(2) # 2 MOSI Y8 MISO Y7 SCK Y6 18 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 19 | xcs = Pin('Y4', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 20 | sdcs = Pin('Y3', Pin.OUT, value=1) # SD card CS 21 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 22 | dreq = Pin('Y1', Pin.IN) # Active high data request 23 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs, '/fc') 24 | 25 | async def heartbeat(): 26 | led = LED(1) 27 | while True: 28 | led.toggle() 29 | await asyncio.sleep_ms(300) 30 | 31 | async def main(): 32 | asyncio.create_task(heartbeat()) 33 | player.volume(-10, -10) # -10dB (0dB is loudest) 34 | locn = '/fc/' 35 | fmt = 'pins {} byte rate {} decode time {}s' 36 | # locn = '/sd/music/' 37 | #await player.sine_test() # Cattles volume 38 | #player.volume(-10, -10) # -10dB (0dB is loudest) 39 | with open(locn + 'panic.mp3', 'rb') as f: 40 | # Demo concurrency and cancellation 41 | asyncio.create_task(player.play(f)) 42 | for _ in range(20): 43 | await asyncio.sleep(1) 44 | print(fmt.format(player.pins(), player.byte_rate(), player.decode_time())) 45 | await player.cancel() 46 | print('Cancelled') 47 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 48 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 49 | with open(locn + 'yellow_v.mp3', 'rb') as f: 50 | await player.play(f) 51 | with open(locn + 'panic.mp3', 'rb') as f: 52 | await player.play(f) 53 | print('All done.') 54 | 55 | asyncio.run(main()) 56 | -------------------------------------------------------------------------------- /async/pico.py: -------------------------------------------------------------------------------- 1 | # pico.py VS1053 demo of asynchronous driver running on a RP2 Pico. 2 | # Play all MP3 tracks found in a directory in the breakout board's SD card. 3 | 4 | from machine import SPI, Pin 5 | from vs1053 import * 6 | import uasyncio as asyncio 7 | from primitives import Switch 8 | 9 | xcs = Pin(14, Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 10 | reset = Pin(15, Pin.OUT, value=1) # Active low hardware reset 11 | xdcs = Pin(2, Pin.OUT, value=1) # Data chip select xdcs in datasheet 12 | dreq = Pin(3, Pin.IN) # Active high data request 13 | sdcs = Pin(5, Pin.OUT, value=1) # SD card CS 14 | spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4)) 15 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs, '/fc') 16 | 17 | player.patch() # Optional for FLAC playback. From /fc/plugins 18 | 19 | can = Pin(16, Pin.IN, Pin.PULL_UP) # Link pin 16 to gnd to cancel play 20 | 21 | async def heartbeat(): 22 | led = Pin(25, Pin.OUT) 23 | while(True): 24 | led(not(led())) 25 | await asyncio.sleep_ms(500) 26 | 27 | async def cancel_song(): # Handle cancellation 28 | sw = Switch(can) 29 | sw.close_func(None) 30 | while True: 31 | sw.close.clear() 32 | await sw.close.wait() 33 | print('Cancelling playback') 34 | await player.cancel() 35 | print('Cancelled') 36 | 37 | async def main(locn, cancel): 38 | asyncio.create_task(heartbeat()) 39 | asyncio.create_task(cancel_song()) 40 | if cancel: 41 | asyncio.create_task(can_it()) 42 | player.volume(-10, -10) # -10dB (0dB is loudest) 43 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 44 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 45 | songs = sorted([x[0] for x in os.ilistdir(locn) if x[1] != 0x4000]) 46 | while True: 47 | for song in songs: 48 | print(song) 49 | with open('/'.join((locn, song)), 'rb') as f: 50 | await player.play(f) # Ends or is cancelled 51 | await asyncio.sleep(1) # Gap between tracks 52 | print('All done.') 53 | 54 | #asyncio.run(main('/fc/192kbps', False)) 55 | asyncio.run(main('/fc/flac', False)) 56 | -------------------------------------------------------------------------------- /async/vs1053.py: -------------------------------------------------------------------------------- 1 | # vs1053.py Asynchronous VS1053 driver for MicroPython 2 | # (C) Peter Hinch 2020-2022 3 | # Released under the MIT licence 4 | 5 | # Driver is based on the following sources 6 | # Adafruit https://github.com/adafruit/Adafruit_CircuitPython_VS1053 7 | # Uri Shaked https://github.com/urish/vs1053-circuitpython 8 | # https://bois083.wordpress.com/2014/11/11/playing-flac-files-using-vs1053-audio-decoder-chip/ 9 | # http://www.vlsi.fi/fileadmin/software/VS10XX/vs1053b-peq.pdf 10 | 11 | import time 12 | import os 13 | import uasyncio as asyncio 14 | 15 | # V0.1.5 Buffered read option for ESP32 compatibility. 16 | # V0.1.4 .play efficiency improvements, test with Pico 17 | # V0.1.3 Synchronous code in play loop 18 | # V0.1.2 Add patch facility 19 | # V0.1.1 Bugfix: SPI baudrate was wrong during reset. 20 | __version__ = (0, 1, 5) 21 | 22 | # Before setting, the internal clock runs at 12.288MHz. Data P7: "the 23 | # maximum speed for SCI reads is CLKI/7" hence max initial baudrate is 24 | # 12.288/7 = 1.75MHz 25 | _INITIAL_BAUDRATE = const(1_000_000) 26 | # 12.288*3.5/4 = 10.752MHz for data read (using _SCI_CLOCKF,0x8800) 27 | _DATA_BAUDRATE = const(10_752_000) # Speed for data transfers. On Pyboard D 28 | # actual rate is 9MHz shared with SD card - sdcard.py uses 1.32MHz. 29 | # RP2 rate is 10,416,666 30 | _SCI_BAUDRATE = const(5_000_000) 31 | 32 | # SCI Registers 33 | _SCI_MODE = const(0x0) 34 | _SCI_STATUS = const(0x1) 35 | _SCI_BASS = const(0x2) 36 | _SCI_CLOCKF = const(0x3) 37 | _SCI_DECODE_TIME = const(0x4) 38 | # _SCI_AUDATA = const(0x5) 39 | _SCI_WRAM = const(0x6) 40 | _SCI_WRAMADDR= const(0x7) 41 | _SCI_HDAT0 = const(0x8) 42 | _SCI_HDAT1 = const(0x9) 43 | # _SCI_AIADDR = const(0xa) 44 | _SCI_VOL = const(0xb) 45 | # _SCI_AICTRL0 = const(0xc) 46 | # _SCI_AICTRL1 = const(0xd) 47 | # _SCI_AICTRL2 = const(0xe) 48 | # _SCI_AICTRL3 = const(0xf) 49 | 50 | # Mode register bits: Public 51 | SM_DIFF = const(0x01) # Invert left channel (why?) 52 | SM_LAYER12 = const(0x02) # Enable MPEG 53 | SM_EARSPEAKER_LO = const(0x10) # EarSpeaker spatial processing 54 | SM_EARSPEAKER_HI = const(0x80) 55 | SM_LINE_IN = const(0x4000) # Line/Mic in 56 | # Private bits 57 | _SM_RESET = const(0x04) 58 | _SM_CANCEL = const(0x08) 59 | _SM_TESTS = const(0x20) 60 | _SM_SDINEW = const(0x800) 61 | # Unused and private 62 | # _SM_STREAM = const(0x40) 63 | # _SM_DACT = const(0x100) 64 | # _SM_SDIORD = const(0x200) 65 | # _SM_SDISHARE = const(0x400) 66 | # _SM_ADPCM = const(0x1000) 67 | # _SM_ADPCM_HP = const(0x2000) 68 | # _SM_CLK_RANGE = const(0x8000) 69 | 70 | # Common parameters section 10.11.1 (RAM locations) 71 | _END_FILL_BYTE = const(0x1e06) 72 | _BYTE_RATE = const(0x1e05) 73 | _POS_MS_LS = const(0x1e27) 74 | _POS_MS_MS = const(0x1e28) 75 | _IO_DIRECTION = const(0xc017) # Datasheet 11.10 76 | _IO_READ = const(0xc018) 77 | _IO_WRITE = const(0xc019) 78 | 79 | _BUF_SIZE = 2048 80 | _BUF_MASK = _BUF_SIZE - 1 81 | """ 82 | Buffering: aim is to fill the software buffer during the periods when the VS1053 83 | hardware buffer is more than 2/3 full and unable to accept data. Thus file 84 | reading time has no impact on performance when the hardware buffer is refilled. 85 | 86 | Buffered play does not use native code, ensuring compatibility with ESP32. 87 | """ 88 | # xcs is chip XSS/ 89 | # xdcs is chipXDCS/BSYNC/ 90 | # sdcs is SD card CS/ 91 | class VS1053: 92 | 93 | def __init__(self, spi, reset, dreq, xdcs, xcs, sdcs=None, mp=None, buffered=False): 94 | self._reset = reset 95 | self._dreq = dreq # Data request 96 | self._xdcs = xdcs # Data CS 97 | self._xcs = xcs # Register CS 98 | self._mp = mp 99 | self._spi = spi 100 | self._cbuf = bytearray(4) # Command buffer 101 | self._slow_spi = True 102 | self.reset() 103 | if ((sdcs is not None) and (mp is not None)): 104 | import sdcard 105 | import os 106 | sd = sdcard.SDCard(spi, sdcs) 107 | vfs = os.VfsFat(sd) 108 | os.mount(vfs, mp) 109 | self._cancnt = 0 # If >0 cancellation in progress 110 | self._playing = False 111 | self._spi.init(baudrate=_DATA_BAUDRATE) 112 | if buffered: 113 | self._buf = bytearray(_BUF_SIZE) 114 | self._mvb = memoryview(self._buf) 115 | self.play = self._bplay 116 | else: 117 | self.play = self._uplay 118 | 119 | def _wait_ready(self): 120 | self._xdcs(1) 121 | self._xcs(1) 122 | while not self._dreq(): 123 | pass 124 | 125 | def _write_reg(self, addr, value): # Datasheet 7.4 126 | self._wait_ready() 127 | self._spi.init(baudrate = _INITIAL_BAUDRATE if self._slow_spi else _SCI_BAUDRATE) 128 | b = self._cbuf 129 | b[0] = 2 # WRITE 130 | b[1] = addr & 0xff 131 | b[2] = (value >> 8) & 0xff 132 | b[3] = value & 0xff 133 | self._xcs(0) 134 | self._spi.write(b) 135 | self._xcs(1) 136 | self._spi.init(baudrate=_DATA_BAUDRATE) 137 | 138 | def _read_reg(self, addr): # Datasheet 7.4 139 | self._wait_ready() 140 | self._spi.init(baudrate = _INITIAL_BAUDRATE if self._slow_spi else _SCI_BAUDRATE) 141 | b = self._cbuf 142 | b[0] = 3 # READ 143 | b[1] = addr & 0xff 144 | b[2] = 0xff 145 | b[3] = 0xff 146 | self._xcs(0) 147 | self._spi.write_readinto(b, b) 148 | self._xcs(1) 149 | self._spi.init(baudrate=_DATA_BAUDRATE) 150 | return (b[2] << 8) | b[3] 151 | 152 | def _read_ram(self, addr): 153 | self._write_reg(_SCI_WRAMADDR, addr) 154 | return self._read_reg(_SCI_WRAM) 155 | 156 | def _write_ram(self, addr, data): 157 | self._write_reg(_SCI_WRAMADDR, addr) 158 | return self._write_reg(_SCI_WRAM, data) 159 | 160 | # Datasheet section 10.5.1: procedure for normal end of play 161 | async def _end_play(self, buf): 162 | efb = self._read_ram(_END_FILL_BYTE) & 0xff 163 | for n in range(len(buf)): 164 | buf[n] = efb 165 | for _ in range(65): # send 2080 bytes of end fill byte 166 | self.write(buf) 167 | await asyncio.sleep_ms(0) 168 | self.mode_set(_SM_CANCEL) 169 | for _ in range(64): # send up to 2048 bytes 170 | self.write(buf) 171 | await asyncio.sleep_ms(0) 172 | if not self.mode() & _SM_CANCEL: 173 | break 174 | else: # Cancel has not been acknowledged 175 | self.soft_reset() 176 | return 177 | if self._read_reg(_SCI_HDAT0) or self._read_reg(_SCI_HDAT1): 178 | raise RuntimeError('Invalid HDAT value.') 179 | 180 | def _patch_stream(self, s): 181 | def read_word(s, buf=bytearray(2)): 182 | if s.readinto(buf) != 2: 183 | raise RuntimeError('Invalid file') 184 | return (buf[1] << 8) + buf[0] 185 | 186 | while True: 187 | try: 188 | addr = read_word(s) 189 | except RuntimeError: # Normal EOF 190 | break 191 | count = read_word(s) 192 | if (count & 0x8000): # RLE run, replicate n samples 193 | count &= 0x7fff 194 | val = read_word(s) 195 | for _ in range(count): 196 | self._write_reg(addr, val) 197 | else: # Copy run, copy n samples 198 | for _ in range(count): 199 | val = read_word(s) 200 | self._write_reg(addr, val) 201 | 202 | def write(self, buf): 203 | while not self._dreq(): # minimise for speed 204 | pass 205 | self._xdcs(0) 206 | self._spi.write(buf) 207 | self._xdcs(1) 208 | return len(buf) 209 | 210 | # *** API *** 211 | 212 | def reset(self): # Issue hardware reset to VS1053 213 | self._xcs(1) 214 | self._xdcs(1) 215 | self._reset(0) 216 | time.sleep_ms(20) 217 | self._reset(1) 218 | time.sleep_ms(20) 219 | self.soft_reset() 220 | 221 | def soft_reset(self): 222 | self._slow_spi = True # Use _INITIAL_BAUDRATE 223 | self.mode_set(_SM_RESET) 224 | # This has many interesting settings data P39 225 | time.sleep_ms(20) # Adafruit have a total of 200ms 226 | # Data P42. P7 footnote 4 recommends xtal * 3.5 + 1: using that. 227 | self._write_reg(_SCI_CLOCKF, 0x8800) 228 | if self._read_reg(_SCI_CLOCKF) != 0x8800: 229 | raise OSError('No VS1053 device found.') 230 | time.sleep_ms(1) # Clock setting can take 100us 231 | # Datasheet suggests writing to SPI_BASS. 232 | self._write_reg(_SCI_BASS, 0) # 0 is flat response 233 | self.volume(0, 0) 234 | self._wait_ready() 235 | self._slow_spi = False 236 | 237 | # Range is 0 to -63.5 dB 238 | def volume(self, left, right, powerdown=False): 239 | bits = [0, 0] 240 | obits = 0xffff # powerdown 241 | if not powerdown: 242 | for n, l in enumerate((left, right)): 243 | bits[n] = round(min(max(2 * -l, 0), 127)) 244 | obits = bits[0] << 8 | bits[1] 245 | self._write_reg(_SCI_VOL, obits) 246 | 247 | def response(self, *, bass_freq=10, treble_freq=1000, bass_amp=0, treble_amp=0): 248 | bits = 0 249 | # Treble amplitude in dB range -12..10.5 250 | ta = round(min(max(treble_amp, -12.0), 10.5) / 1.5) & 0x0f 251 | bits |= ta << 12 252 | # Treble freq 1000-15000 253 | tf = round(min(max(treble_freq, 1000), 15000) / 1000) if ta else 0 254 | bits |= tf << 8 255 | # Bass amplitude in dB range 0..15 256 | ba = round(min(max(bass_amp, 0), 15)) 257 | bits |= ba << 4 258 | # Bass freq 20Hz-150Hz 259 | bf = round(min(max(bass_freq, 20), 150) / 10) if ba else 0 260 | bits |= bf 261 | self._write_reg(_SCI_BASS, bits) 262 | 263 | def pins_direction(self, bits): 264 | self._write_ram(_IO_DIRECTION, bits & 0xff) 265 | 266 | def pins(self, data=None): 267 | if data is not None: 268 | self._write_ram(_IO_WRITE, data & 0xff) 269 | return self._read_ram(_IO_READ) & 0x3FF 270 | 271 | def version(self): 272 | return (self._read_reg(_SCI_STATUS) >> 4) & 0x0F 273 | 274 | def decode_time(self): # Number of seconds into the stream 275 | return self._read_reg(_SCI_DECODE_TIME) 276 | 277 | def byte_rate(self): # Data rate in bytes/sec 278 | return self._read_ram(_BYTE_RATE) 279 | 280 | def mode(self): 281 | return self._read_reg(_SCI_MODE) 282 | 283 | def mode_set(self, bits): 284 | bits |= self.mode() | _SM_SDINEW 285 | self._write_reg(_SCI_MODE, bits) 286 | 287 | def mode_clear(self, bits): 288 | bits ^= 0xffff 289 | bits &= self.mode() 290 | self._write_reg(_SCI_MODE, _SM_SDINEW | bits) # Ensure new bit always set 291 | 292 | def enable_i2s(self, rate=48, mclock=False): 293 | v = 0x0C if mclock else 0x04 # Enable I2S and mclock if required 294 | if rate == 96: 295 | v |= 1 296 | elif rate == 192: 297 | v |= 2 298 | self._write_ram(0xC017, 0xF0) 299 | self._write_ram(0xC040, v) 300 | 301 | # Doesn't return anything useful for MP3 302 | # def pos_ms(self): # Position into stream in ms 303 | # return self._read_ram(_POS_MS_LS) | (self._read_ram(_POS_MS_MS) << 16) 304 | 305 | async def cancel(self): 306 | if self._playing: 307 | self._cancnt = 1 # Request 308 | while self._cancnt: # In progress 309 | await asyncio.sleep_ms(50) 310 | 311 | async def _bplay(self, s): # No native decorator for max compatibility 312 | self._playing = True 313 | self._cancnt = 0 314 | dreq = self._dreq 315 | mvb = self._mvb # Memoryview into buffer 316 | cnt = 0 317 | rptr = 0 # Buffer read pointer 318 | bsize = s.readinto(self._buf) # No. of bytes in buffer 319 | wptr = bsize & _BUF_MASK # write pointer (normally 0) 320 | running = True 321 | while running: 322 | cnt += 1 323 | # When running, dreq goes True when on-chip buffer can hold about 640 bytes. 324 | # At 128Kbps dreq will be False for 40ms - at higher rates, less. So this code 325 | # will block for <= 40ms. The cnt ensures it can't lock the scheduler even 326 | # if dreq remains True forever. This is a failing condition where the 327 | # chip is consuming data faster than we can feed it. 328 | while (not dreq()) or cnt > 30: # 960 byte backstop 329 | if cnt: # Read once only. 330 | cnt = 0 331 | if wptr > rptr: # Try to fill to end of buffer 332 | bsize += (n := s.readinto(mvb[wptr: _BUF_SIZE])) 333 | wptr = (wptr + n) & _BUF_MASK 334 | if wptr < rptr: 335 | bsize += (n := s.readinto(mvb[wptr:rptr])) 336 | wptr += n 337 | # Now wptr == rptr but this can't persist for next outer loop pass 338 | await asyncio.sleep_ms(0) # Don't block while waiting on dreq 339 | self._xdcs(0) # Fast write 340 | self._spi.write(mvb[rptr : rptr + 32]) 341 | self._xdcs(1) 342 | rptr = (rptr + 32) & _BUF_MASK # Bump read pointer modulo _BUF_SIZE 343 | bsize -= 32 344 | running = bsize > 0 345 | # Check for cancelling. Datasheet section 10.5.2 346 | if self._cancnt: 347 | if self._cancnt == 1: # Just cancelled 348 | self.mode_set(_SM_CANCEL) 349 | if not self.mode() & _SM_CANCEL: # Cancel done 350 | efb = self._read_ram(_END_FILL_BYTE) & 0xff 351 | for n in range(32): 352 | mvb[n] = efb 353 | for n in range(64): # send 2048 bytes of end fill byte 354 | self.write(mvb[:32]) 355 | self.write(mvb[:4]) # Take to 2052 bytes 356 | if self._read_reg(_SCI_HDAT0) or self._read_reg(_SCI_HDAT1): 357 | raise RuntimeError('Invalid HDAT value.') 358 | break 359 | if self._cancnt > 64: # Cancel has failed 360 | self.soft_reset() 361 | break 362 | self._cancnt += 1 # keep feeding data from stream 363 | else: 364 | await self._end_play(mvb[:32]) 365 | self._playing = False 366 | 367 | @micropython.native 368 | async def _uplay(self, s, buf=bytearray(32)): 369 | self._playing = True 370 | self._cancnt = 0 371 | dreq = self._dreq 372 | cnt = 0 373 | while s.readinto(buf): # Read <=32 bytes 374 | cnt += 1 375 | # When running, dreq goes True when on-chip buffer can hold about 640 bytes. 376 | # At 128Kbps this will take 40ms - at higher rates, less. So this code 377 | # will block for <= 40ms. The cnt ensures it can't lock the scheduler even 378 | # if dreq remains True forever. This is a failing condition where the 379 | # chip is consuming data faster than we can feed it. 380 | while (not dreq()) or cnt > 30: # 960 byte backstop 381 | await asyncio.sleep_ms(0) 382 | cnt = 0 383 | self._xdcs(0) # Fast write 384 | self._spi.write(buf) 385 | self._xdcs(1) 386 | # Check for cancelling. Datasheet section 10.5.2 387 | if self._cancnt: 388 | if self._cancnt == 1: # Just cancelled 389 | self.mode_set(_SM_CANCEL) 390 | if not self.mode() & _SM_CANCEL: # Cancel done 391 | efb = self._read_ram(_END_FILL_BYTE) & 0xff 392 | for n in range(len(buf)): 393 | buf[n] = efb 394 | for n in range(64): # send 2048 bytes of end fill byte 395 | self.write(buf) 396 | self.write(buf[:4]) # Take to 2052 bytes 397 | if self._read_reg(_SCI_HDAT0) or self._read_reg(_SCI_HDAT1): 398 | raise RuntimeError('Invalid HDAT value.') 399 | break 400 | if self._cancnt > 64: # Cancel has failed 401 | self.soft_reset() 402 | break 403 | self._cancnt += 1 # keep feeding data from stream 404 | else: 405 | await self._end_play(buf) 406 | self._cancnt = 0 407 | self._playing = False 408 | 409 | # Produce a 517Hz sine wave 410 | async def sine_test(self, seconds=10): 411 | self.soft_reset() 412 | self.mode_set(_SM_TESTS) 413 | # 0x66-> Sample rate 22050 * 6/128 = 1034Hz 0x63->517Hz 414 | self.write(b'\x53\xef\x6e\x66\0\0\0\0') 415 | await asyncio.sleep(seconds) 416 | self.write(b'\x45\x78\x69\x74\0\0\0\0') 417 | self.mode_clear(_SM_TESTS) 418 | 419 | # Given a directory apply any patch files found. Applied in alphabetical 420 | # order. 421 | def patch(self, loc=None): 422 | if loc is None: 423 | mp = self._mp 424 | if mp is None: 425 | raise ValueError('No patch location') 426 | loc = ''.join((mp, 'plugins')) if mp.endswith('/') else ''.join((mp, '/plugins')) 427 | elif loc.endswith('/'): 428 | loc = loc[:-1] 429 | for f in sorted(os.listdir(loc)): 430 | f = ''.join((loc, '/', f)) 431 | print('Patching', f) 432 | with open(f, 'rb') as s: 433 | self._patch_stream(s) 434 | print('Patching complete.') 435 | -------------------------------------------------------------------------------- /plugins/flac_plugin.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterhinch/micropython-vs1053/ead5ed9b0c28cef5445a512cb1a35e71dfb7299d/plugins/flac_plugin.bin -------------------------------------------------------------------------------- /sdcard.py: -------------------------------------------------------------------------------- 1 | """ 2 | MicroPython driver for SD cards using SPI bus. 3 | 4 | Requires an SPI bus and a CS pin. Provides readblocks and writeblocks 5 | methods so the device can be mounted as a filesystem. 6 | 7 | Example usage on pyboard: 8 | 9 | import pyb, sdcard, os 10 | sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) 11 | pyb.mount(sd, '/sd2') 12 | os.listdir('/') 13 | 14 | Example usage on ESP8266: 15 | 16 | import machine, sdcard, os 17 | sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) 18 | os.mount(sd, '/sd') 19 | os.listdir('/') 20 | 21 | """ 22 | 23 | from micropython import const 24 | import time 25 | 26 | 27 | _CMD_TIMEOUT = const(1000) # PGH (was 100) 28 | 29 | _R1_IDLE_STATE = const(1 << 0) 30 | # R1_ERASE_RESET = const(1 << 1) 31 | _R1_ILLEGAL_COMMAND = const(1 << 2) 32 | # R1_COM_CRC_ERROR = const(1 << 3) 33 | # R1_ERASE_SEQUENCE_ERROR = const(1 << 4) 34 | # R1_ADDRESS_ERROR = const(1 << 5) 35 | # R1_PARAMETER_ERROR = const(1 << 6) 36 | _TOKEN_CMD25 = const(0xFC) 37 | _TOKEN_STOP_TRAN = const(0xFD) 38 | _TOKEN_DATA = const(0xFE) 39 | 40 | 41 | class SDCard: 42 | def __init__(self, spi, cs): 43 | self.spi = spi 44 | self.cs = cs 45 | 46 | self.cmdbuf = bytearray(6) 47 | self.dummybuf = bytearray(512) 48 | self.tokenbuf = bytearray(1) 49 | for i in range(512): 50 | self.dummybuf[i] = 0xFF 51 | self.dummybuf_memoryview = memoryview(self.dummybuf) 52 | 53 | # initialise the card 54 | self.init_card() 55 | 56 | def init_spi(self, baudrate): 57 | try: 58 | master = self.spi.MASTER 59 | except AttributeError: 60 | # on ESP8266 61 | self.spi.init(baudrate=baudrate, phase=0, polarity=0) 62 | else: 63 | # on pyboard 64 | self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) 65 | 66 | def init_card(self): 67 | # init CS pin 68 | self.cs.init(self.cs.OUT, value=1) 69 | 70 | # init SPI bus; use low data rate for initialisation 71 | self.init_spi(100000) 72 | 73 | # clock card at least 100 cycles with cs high 74 | for i in range(16): 75 | self.spi.write(b"\xff") 76 | 77 | # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) 78 | for _ in range(5): 79 | if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: 80 | break 81 | else: 82 | raise OSError("no SD card") 83 | 84 | # CMD8: determine card version 85 | r = self.cmd(8, 0x01AA, 0x87, 4) 86 | if r == _R1_IDLE_STATE: 87 | self.init_card_v2() 88 | elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): 89 | self.init_card_v1() 90 | else: 91 | raise OSError("couldn't determine SD card version") 92 | 93 | # get the number of sectors 94 | # CMD9: response R2 (R1 byte + 16-byte block read) 95 | if self.cmd(9, 0, 0, 0, False) != 0: 96 | raise OSError("no response from SD card") 97 | csd = bytearray(16) 98 | self.readinto(csd) 99 | if csd[0] & 0xC0 == 0x40: # CSD version 2.0 100 | self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 101 | elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) 102 | c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 103 | c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 104 | self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) 105 | else: 106 | raise OSError("SD card CSD format not supported") 107 | # print('sectors', self.sectors) 108 | 109 | # CMD16: set block length to 512 bytes 110 | if self.cmd(16, 512, 0) != 0: 111 | raise OSError("can't set 512 block size") 112 | 113 | # set to high data rate now that it's initialised 114 | self.init_spi(1320000) 115 | 116 | def init_card_v1(self): 117 | for i in range(_CMD_TIMEOUT): 118 | self.cmd(55, 0, 0) 119 | if self.cmd(41, 0, 0) == 0: 120 | self.cdv = 512 121 | # print("[SDCard] v1 card") 122 | return 123 | raise OSError("timeout waiting for v1 card") 124 | 125 | def init_card_v2(self): 126 | for i in range(_CMD_TIMEOUT): 127 | time.sleep_ms(50) 128 | self.cmd(58, 0, 0, 4) 129 | self.cmd(55, 0, 0) 130 | if self.cmd(41, 0x40000000, 0) == 0: 131 | self.cmd(58, 0, 0, 4) 132 | self.cdv = 1 133 | # print("[SDCard] v2 card") 134 | return 135 | raise OSError("timeout waiting for v2 card") 136 | 137 | def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): 138 | self.cs(0) 139 | 140 | # create and send the command 141 | buf = self.cmdbuf 142 | buf[0] = 0x40 | cmd 143 | buf[1] = arg >> 24 144 | buf[2] = arg >> 16 145 | buf[3] = arg >> 8 146 | buf[4] = arg 147 | buf[5] = crc 148 | self.spi.write(buf) 149 | 150 | if skip1: 151 | self.spi.readinto(self.tokenbuf, 0xFF) 152 | 153 | # wait for the response (response[7] == 0) 154 | for i in range(_CMD_TIMEOUT): 155 | self.spi.readinto(self.tokenbuf, 0xFF) 156 | response = self.tokenbuf[0] 157 | if not (response & 0x80): 158 | # this could be a big-endian integer that we are getting here 159 | for j in range(final): 160 | self.spi.write(b"\xff") 161 | if release: 162 | self.cs(1) 163 | self.spi.write(b"\xff") 164 | return response 165 | 166 | # timeout 167 | self.cs(1) 168 | self.spi.write(b"\xff") 169 | return -1 170 | 171 | def readinto(self, buf): 172 | self.cs(0) 173 | 174 | # read until start byte (0xff) 175 | for i in range(_CMD_TIMEOUT): 176 | self.spi.readinto(self.tokenbuf, 0xFF) 177 | if self.tokenbuf[0] == _TOKEN_DATA: 178 | break 179 | else: 180 | self.cs(1) 181 | raise OSError("timeout waiting for response") 182 | 183 | # read data 184 | mv = self.dummybuf_memoryview 185 | if len(buf) != len(mv): 186 | mv = mv[: len(buf)] 187 | self.spi.write_readinto(mv, buf) 188 | 189 | # read checksum 190 | self.spi.write(b"\xff") 191 | self.spi.write(b"\xff") 192 | 193 | self.cs(1) 194 | self.spi.write(b"\xff") 195 | 196 | def write(self, token, buf): 197 | self.cs(0) 198 | 199 | # send: start of block, data, checksum 200 | self.spi.read(1, token) 201 | self.spi.write(buf) 202 | self.spi.write(b"\xff") 203 | self.spi.write(b"\xff") 204 | 205 | # check the response 206 | if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: 207 | self.cs(1) 208 | self.spi.write(b"\xff") 209 | return 210 | 211 | # wait for write to finish 212 | while self.spi.read(1, 0xFF)[0] == 0: 213 | pass 214 | 215 | self.cs(1) 216 | self.spi.write(b"\xff") 217 | 218 | def write_token(self, token): 219 | self.cs(0) 220 | self.spi.read(1, token) 221 | self.spi.write(b"\xff") 222 | # wait for write to finish 223 | while self.spi.read(1, 0xFF)[0] == 0x00: 224 | pass 225 | 226 | self.cs(1) 227 | self.spi.write(b"\xff") 228 | 229 | def readblocks(self, block_num, buf): 230 | self.spi.write(b'\xff') # PGH https://github.com/micropython/micropython/pull/6007 231 | nblocks = len(buf) // 512 232 | assert nblocks and not len(buf) % 512, "Buffer length is invalid" 233 | if nblocks == 1: 234 | # CMD17: set read address for single block 235 | if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: 236 | # release the card 237 | self.cs(1) 238 | raise OSError(5) # EIO 239 | # receive the data and release card 240 | self.readinto(buf) 241 | else: 242 | # CMD18: set read address for multiple blocks 243 | if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: 244 | # release the card 245 | self.cs(1) 246 | raise OSError(5) # EIO 247 | offset = 0 248 | mv = memoryview(buf) 249 | while nblocks: 250 | # receive the data and release card 251 | self.readinto(mv[offset : offset + 512]) 252 | offset += 512 253 | nblocks -= 1 254 | if self.cmd(12, 0, 0xFF, skip1=True): 255 | raise OSError(5) # EIO 256 | 257 | def writeblocks(self, block_num, buf): 258 | # clock card at least 100 cycles with cs high 259 | self.spi.write(b'\xff') # PGH 260 | nblocks, err = divmod(len(buf), 512) 261 | assert nblocks and not err, "Buffer length is invalid" 262 | if nblocks == 1: 263 | # CMD24: set write address for single block 264 | if self.cmd(24, block_num * self.cdv, 0) != 0: 265 | raise OSError(5) # EIO 266 | 267 | # send the data 268 | self.write(_TOKEN_DATA, buf) 269 | else: 270 | # CMD25: set write address for first block 271 | if self.cmd(25, block_num * self.cdv, 0) != 0: 272 | raise OSError(5) # EIO 273 | # send the data 274 | offset = 0 275 | mv = memoryview(buf) 276 | while nblocks: 277 | self.write(_TOKEN_CMD25, mv[offset : offset + 512]) 278 | offset += 512 279 | nblocks -= 1 280 | self.write_token(_TOKEN_STOP_TRAN) 281 | 282 | def ioctl(self, op, arg): 283 | if op == 4: # get number of blocks 284 | return self.sectors 285 | -------------------------------------------------------------------------------- /synchronous/esp32audio.py: -------------------------------------------------------------------------------- 1 | # esp32audio.py VS1053b driver demo/test for ESP32 2 | # Uses synchronous driver. 3 | 4 | # (C) Peter Hinch 2020 5 | # Released under the MIT licence 6 | 7 | from vs1053_syn import * 8 | from machine import SPI, Pin, freq 9 | import uasyncio as asyncio 10 | # Works at stock speed 11 | # freq(240_000_000) 12 | 13 | # 128K conversion 14 | # ffmpeg -i yellow.flac -acodec libmp3lame -ab 128k yellow.mp3 15 | # VBR conversion 16 | # ffmpeg -i yellow.flac -acodec libmp3lame -qscale:a 0 yellow_v.mp3 17 | # Yeah, I know. I like Coldplay... 18 | 19 | spi = SPI(2, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) 20 | reset = Pin(32, Pin.OUT, value=1) # Active low hardware reset 21 | xcs = Pin(33, Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 22 | sdcs = Pin(25, Pin.OUT, value=1) # SD card CS 23 | xdcs = Pin(26, Pin.OUT, value=1) # Data chip select xdcs in datasheet 24 | dreq = Pin(27, Pin.IN) # Active high data request 25 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs, '/fc') 26 | 27 | player.patch() # Optional. From /fc/plugins/ 28 | 29 | def main(locn): 30 | player.volume(-10, -10) # -10dB (0dB is loudest) 31 | # player.sine_test() # Cattles volume 32 | # player.volume(-10, -10) # -10dB (0dB is loudest) 33 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 34 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 35 | songs = sorted([x[0] for x in os.ilistdir(locn) if x[1] != 0x4000]) 36 | while True: 37 | for song in songs: 38 | print(song) 39 | with open('/'.join((locn, song)), 'rb') as f: 40 | player.play(f) 41 | 42 | main('/fc/flac') 43 | -------------------------------------------------------------------------------- /synchronous/esp8266audio.py: -------------------------------------------------------------------------------- 1 | # esp8266audio_syn.py VS1053b driver demo/test for ESP8266 2 | # Uses synchronous driver. 3 | 4 | # (C) Peter Hinch 2020 5 | # Released under the MIT licence 6 | 7 | from vs1053_syn import * 8 | from machine import SPI, Pin, freq 9 | 10 | # It works at stock speed, even with VBR files. 11 | # freq(160_000_000) 12 | 13 | # Available pins 0, 2, 4, 5, 12, 13, 14, 15, 16 14 | # 128K conversion 15 | # ffmpeg -i yellow.flac -acodec libmp3lame -ab 128k yellow.mp3 16 | # VBR conversion 17 | # ffmpeg -i yellow.flac -acodec libmp3lame -qscale:a 0 yellow_v.mp3 18 | # Yeah, I know. I like Coldplay... 19 | 20 | spi = SPI(1) # sck=Pin(14), mosi=Pin(13), miso=Pin(12)) 21 | reset = Pin(5, Pin.OUT, value=1) # Active low hardware reset 22 | xcs = Pin(4, Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 23 | sdcs = Pin(2, Pin.OUT, value=1) # SD card CS 24 | xdcs = Pin(0, Pin.OUT, value=1) # Data chip select xdcs in datasheet 25 | dreq = Pin(15, Pin.IN) # Active high data request 26 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs=sdcs, mp='/fc') 27 | 28 | def main(): 29 | player.volume(-10, -10) # -10dB (0dB is loudest) 30 | locn = '/fc/' 31 | # locn = '/sd/music/' 32 | # player.sine_test() # Cattles volume 33 | # player.volume(-10, -10) # -10dB (0dB is loudest) 34 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 35 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 36 | with open(locn + 'yellow_v.mp3', 'rb') as f: # A VBR file 37 | player.play(f) 38 | with open(locn + 'panic.mp3', 'rb') as f: 39 | player.play(f) 40 | 41 | main() 42 | -------------------------------------------------------------------------------- /synchronous/pbaudio_syn.py: -------------------------------------------------------------------------------- 1 | # pbaudio_syn.py VS1053b driver demo/test for Pyboard 2 | 3 | # (C) Peter Hinch 2020 4 | # Released under the MIT licence 5 | 6 | from vs1053_syn import * 7 | from machine import SPI, Pin 8 | from pyb import Switch # For cancellation 9 | import time 10 | import os 11 | switch = Switch() 12 | 13 | # 128K conversion 14 | # ffmpeg -i yellow.flac -acodec libmp3lame -ab 128k yellow.mp3 15 | # VBR conversion 16 | # ffmpeg -i yellow.flac -acodec libmp3lame -qscale:a 0 yellow.mp3 17 | # Yeah, I know. I like Coldplay... 18 | 19 | spi = SPI(2) # 2 MOSI Y8 MISO Y7 SCK Y6 20 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 21 | xcs = Pin('Y4', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 22 | sdcs = Pin('Y3', Pin.OUT, value=1) # SD card CS 23 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 24 | dreq = Pin('Y1', Pin.IN) # Active high data request 25 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs=sdcs, mp='/fc', cancb=lambda : switch()) 26 | player.patch() # Optional. From /fc/plugins 27 | 28 | def main(): 29 | player.volume(-10, -10) # -10dB (0dB is loudest) 30 | # songs = ('road_to_hell.flac', 'yellow_v.mp3', 'panic.mp3') 31 | songs = sorted([x for x in os.listdir('/fc') if x.endswith('.flac')]) 32 | # locn = '/sd/music/' 33 | # player.sine_test() # Cattles volume 34 | # player.volume(-10, -10) # -10dB (0dB is loudest) 35 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 36 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 37 | for song in songs: 38 | fn = ''.join(('/fc/', song)) 39 | with open(fn, 'rb') as f: 40 | player.play(f) 41 | if switch(): # Was cancelled 42 | while switch(): 43 | pass 44 | time.sleep_ms(200) # Wait out contact bounce 45 | 46 | main() 47 | -------------------------------------------------------------------------------- /synchronous/pico_syn.py: -------------------------------------------------------------------------------- 1 | # pico_syn.py VS1053b driver demo/test for RP2 Pico 2 | 3 | # (C) Peter Hinch 2022 4 | # Released under the MIT licence 5 | 6 | from vs1053_syn import * 7 | from machine import SPI, Pin 8 | import time 9 | import os 10 | 11 | # 128K conversion 12 | # ffmpeg -i yellow.flac -acodec libmp3lame -ab 128k yellow.mp3 13 | # VBR conversion 14 | # ffmpeg -i yellow.flac -acodec libmp3lame -qscale:a 0 yellow.mp3 15 | # Yeah, I know. I like Coldplay... 16 | 17 | can = Pin(16, Pin.IN, Pin.PULL_UP) # Pin 16 cancels play 18 | def cancb(): 19 | return not can() 20 | 21 | xcs = Pin(14, Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 22 | reset = Pin(15, Pin.OUT, value=1) # Active low hardware reset 23 | xdcs = Pin(2, Pin.OUT, value=1) # Data chip select xdcs in datasheet 24 | dreq = Pin(3, Pin.IN) # Active high data request 25 | sdcs = Pin(5, Pin.OUT, value=1) # SD card CS 26 | spi = SPI(0, sck=Pin(6), mosi=Pin(7), miso=Pin(4)) 27 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs, '/fc', cancb) 28 | player.patch() # Optional. From /fc/plugins 29 | 30 | def main(locn): 31 | player.volume(-10, -10) # -10dB (0dB is loudest) 32 | # player.mode_set(SM_EARSPEAKER_LO | SM_EARSPEAKER_HI) # You decide. 33 | # player.response(bass_freq=150, bass_amp=15) # This is extreme. 34 | songs = sorted([x[0] for x in os.ilistdir(locn) if x[1] != 0x4000]) 35 | for song in songs: 36 | print(song) 37 | fn = '/'.join((locn, song)) 38 | with open(fn, 'rb') as f: 39 | player.play(f) 40 | time.sleep(1) 41 | 42 | #main('/fc/192kbps') 43 | main('/fc/flac') 44 | -------------------------------------------------------------------------------- /synchronous/rectest.py: -------------------------------------------------------------------------------- 1 | # rectest.py VS1053b driver demo/test for Pyboard 2 | # Records 10s of audio at 8000sps then replays it 3 | # (C) Peter Hinch 2020 4 | # Released under the MIT licence 5 | 6 | from vs1053_syn import * 7 | from machine import SPI, Pin 8 | 9 | spi = SPI(2) # 2 MOSI Y8 MISO Y7 SCK Y6 10 | reset = Pin('Y5', Pin.OUT, value=1) # Active low hardware reset 11 | xcs = Pin('Y4', Pin.OUT, value=1) # Labelled CS on PCB, xcs on chip datasheet 12 | sdcs = Pin('Y3', Pin.OUT, value=1) # SD card CS 13 | xdcs = Pin('Y2', Pin.OUT, value=1) # Data chip select xdcs in datasheet 14 | dreq = Pin('Y1', Pin.IN) # Active high data request 15 | player = VS1053(spi, reset, dreq, xdcs, xcs, sdcs=sdcs, mp='/fc') 16 | 17 | fn = '/fc/stereo_8k.wav' 18 | 19 | def main(t=10): 20 | print('Recording for {}s'.format(t)) 21 | overrun = player.record(fn, True, t * 1000, 8000) #, stereo=False) 22 | print('Record complete') 23 | if overrun > 768: 24 | print('High data rate: loss may have occurred. Value = {}'.format(overrun)) 25 | player.reset() # Necessary before playback 26 | print('Playback') 27 | with open(fn, 'rb') as f: 28 | player.play(f) 29 | 30 | main() 31 | -------------------------------------------------------------------------------- /synchronous/vs1053_syn.py: -------------------------------------------------------------------------------- 1 | # VS1053_syn Synchronous driver for MicroPython 2 | # (C) Peter Hinch 2020 3 | # Released under the MIT licence 4 | 5 | # Driver is based on the following sources 6 | # Adafruit https://github.com/adafruit/Adafruit_CircuitPython_VS1053 7 | # Uri Shaked https://github.com/urish/vs1053-circuitpython 8 | # https://bois083.wordpress.com/2014/11/11/playing-flac-files-using-vs1053-audio-decoder-chip/ 9 | # http://www.vlsi.fi/fileadmin/software/VS10XX/vs1053b-peq.pdf 10 | 11 | import time 12 | import os 13 | from array import array 14 | 15 | # V0.1.4 .play efficiency improvements, test with Pico 16 | # V0.1.3 Support recording 17 | # V0.1.2 Add patch facility 18 | __version__ = (0, 1, 4) 19 | 20 | # Before setting, the internal clock runs at 12.288MHz. Data P7: "the 21 | # maximum speed for SCI reads is CLKI/7" hence max initial baudrate is 22 | # 12.288/7 = 1.75MHz 23 | _INITIAL_BAUDRATE = const(1_000_000) 24 | # 12.288*3.5/4 = 10.752MHz for data read (using _SCI_CLOCKF,0x8800) 25 | _DATA_BAUDRATE = const(10_752_000) # Speed for data transfers. On Pyboard D 26 | # actual rate is 9MHz shared with SD card - sdcard.py uses 1.32MHz. 27 | _SCI_BAUDRATE = const(5_000_000) 28 | 29 | # SCI Registers 30 | _SCI_MODE = const(0x0) 31 | _SCI_STATUS = const(0x1) 32 | _SCI_BASS = const(0x2) 33 | _SCI_CLOCKF = const(0x3) 34 | _SCI_DECODE_TIME = const(0x4) 35 | # _SCI_AUDATA = const(0x5) 36 | _SCI_WRAM = const(0x6) 37 | _SCI_WRAMADDR = const(0x7) 38 | _SCI_HDAT0 = const(0x8) 39 | _SCI_HDAT1 = const(0x9) 40 | # _SCI_AIADDR = const(0xa) 41 | _SCI_VOL = const(0xb) 42 | _SCI_AICTRL0 = const(0xc) 43 | _SCI_AICTRL1 = const(0xd) 44 | _SCI_AICTRL2 = const(0xe) 45 | _SCI_AICTRL3 = const(0xf) 46 | 47 | # Mode register bits: Public 48 | SM_DIFF = const(0x01) # Invert left channel (why?) 49 | SM_LAYER12 = const(0x02) # Enable MPEG 50 | SM_EARSPEAKER_LO = const(0x10) # EarSpeaker spatial processing 51 | SM_EARSPEAKER_HI = const(0x80) 52 | # Private bits 53 | _SM_RESET = const(0x04) 54 | _SM_CANCEL = const(0x08) 55 | _SM_TESTS = const(0x20) 56 | _SM_SDINEW = const(0x800) 57 | _SM_ADPCM = const(0x1000) 58 | _SM_LINE_IN = const(0x4000) # Line/Mic in 59 | # Unused and private 60 | # _SM_STREAM = const(0x40) 61 | # _SM_DACT = const(0x100) 62 | # _SM_SDIORD = const(0x200) 63 | # _SM_SDISHARE = const(0x400) 64 | # _SM_ADPCM_HP = const(0x2000) 65 | # _SM_CLK_RANGE = const(0x8000) 66 | 67 | # Common parameters section 10.11.1 (RAM locations) 68 | _END_FILL_BYTE = const(0x1e06) 69 | _BYTE_RATE = const(0x1e05) 70 | _POS_MS_LS = const(0x1e27) 71 | _POS_MS_MS = const(0x1e28) 72 | _IO_DIRECTION = const(0xc017) # Datasheet 11.10 73 | _IO_READ = const(0xc018) 74 | _IO_WRITE = const(0xc019) 75 | 76 | # Recording patches. RAM-efficient storage. 77 | _PATCH = array('H', (0x3e12, 0xb817, 0x3e14, 0xf812, 0x3e01, 0xb811, 0x0007, 0x9717, 78 | 0x0020, 0xffd2, 0x0030, 0x11d1, 0x3111, 0x8024, 0x3704, 0xc024, 79 | 0x3b81, 0x8024, 0x3101, 0x8024, 0x3b81, 0x8024, 0x3f04, 0xc024, 80 | 0x2808, 0x4800, 0x36f1, 0x9811)) 81 | _PATCH1 = array('H', (0x2a00, 0x040e)) 82 | # Header for 83 | _HEADER = (b'RIFF\x00\x00\x00\x00WAVEfmt ' 84 | b'\x14\x00\x00\x00\x11\x00\x02\x00\x40\x1f\x00\x00\xae\x1f\x00\x00' 85 | b'\x00\x02\x04\x00\x02\x00\xf9\x01fact\x04\x00\x00\x00' 86 | b'\x00\x00\x00\x00data\x00\x00\x00\x00') # Template. 87 | 88 | # xcs is chip XSS/ 89 | # xdcs is chipXDCS/BSYNC/ 90 | # sdcs is SD card CS/ 91 | class VS1053: 92 | 93 | 94 | def __init__(self, spi, reset, dreq, xdcs, xcs, sdcs=None, mp=None, cancb=lambda : False): 95 | self._reset = reset 96 | self._dreq = dreq # Data request 97 | self._xdcs = xdcs # Data CS 98 | self._xcs = xcs # Register CS 99 | self._mp = mp 100 | self._spi = spi 101 | self._cbuf = bytearray(4) # Command buffer 102 | self._cancb = cancb # Cancellation callback 103 | self._slow_spi = True # Start on low baudrate 104 | self._overrun = 0 # Recording 105 | self.reset() 106 | if ((sdcs is not None) and (mp is not None)): 107 | import sdcard 108 | import os 109 | sd = sdcard.SDCard(spi, sdcs) 110 | vfs = os.VfsFat(sd) 111 | os.mount(vfs, mp) 112 | self._spi.init(baudrate=_DATA_BAUDRATE) 113 | 114 | def _wait_ready(self): 115 | self._xdcs(1) 116 | self._xcs(1) 117 | while not self._dreq(): 118 | pass 119 | 120 | def _write_reg(self, addr, value): # Datasheet 7.4 121 | self._wait_ready() 122 | self._spi.init(baudrate = _INITIAL_BAUDRATE if self._slow_spi else _SCI_BAUDRATE) 123 | b = self._cbuf 124 | b[0] = 2 # WRITE 125 | b[1] = addr & 0xff 126 | b[2] = (value >> 8) & 0xff 127 | b[3] = value & 0xff 128 | self._xcs(0) 129 | self._spi.write(b) 130 | self._xcs(1) 131 | self._spi.init(baudrate=_DATA_BAUDRATE) 132 | 133 | def _read_reg(self, addr): # Datasheet 7.4 134 | self._wait_ready() 135 | self._spi.init(baudrate = _INITIAL_BAUDRATE if self._slow_spi else _SCI_BAUDRATE) 136 | b = self._cbuf 137 | b[0] = 3 # READ 138 | b[1] = addr & 0xff 139 | b[2] = 0xff 140 | b[3] = 0xff 141 | self._xcs(0) 142 | self._spi.write_readinto(b, b) 143 | self._xcs(1) 144 | self._spi.init(baudrate=_DATA_BAUDRATE) 145 | return (b[2] << 8) | b[3] 146 | 147 | def _read_ram(self, addr): 148 | self._write_reg(_SCI_WRAMADDR, addr) 149 | return self._read_reg(_SCI_WRAM) 150 | 151 | def _write_ram(self, addr, data): 152 | self._write_reg(_SCI_WRAMADDR, addr) 153 | return self._write_reg(_SCI_WRAM, data) 154 | 155 | # Datasheet section 10.5.1: procedure for normal end of play 156 | def _end_play(self, buf): 157 | efb = self._read_ram(_END_FILL_BYTE) & 0xff 158 | for n in range(len(buf)): 159 | buf[n] = efb 160 | for n in range(65): # send 2080 bytes of end fill byte 161 | self.write(buf) 162 | self.mode_set(_SM_CANCEL) 163 | for n in range(64): # send up to 2048 bytes 164 | self.write(buf) 165 | if not self.mode() & _SM_CANCEL: 166 | break 167 | else: # Cancel has not been acknowledged 168 | self.soft_reset() 169 | return 170 | if self._read_reg(_SCI_HDAT0) or self._read_reg(_SCI_HDAT1): 171 | raise RuntimeError('Invalid HDAT value.') 172 | 173 | def write(self, buf): 174 | while not self._dreq(): # minimise for speed 175 | pass 176 | self._xdcs(0) 177 | self._spi.write(buf) 178 | self._xdcs(1) 179 | return len(buf) 180 | 181 | def _patch_stream(self, s): 182 | def read_word(s, buf=bytearray(2)): 183 | if s.readinto(buf) != 2: 184 | raise RuntimeError('Invalid file') 185 | return (buf[1] << 8) + buf[0] 186 | 187 | while True: 188 | try: 189 | addr = read_word(s) 190 | except RuntimeError: # Normal EOF 191 | break 192 | count = read_word(s) 193 | if (count & 0x8000): # RLE run, replicate n samples 194 | count &= 0x7fff 195 | val = read_word(s) 196 | for _ in range(count): 197 | self._write_reg(addr, val) 198 | else: # Copy run, copy n samples 199 | for _ in range(count): 200 | val = read_word(s) 201 | self._write_reg(addr, val) 202 | 203 | # Support for recording 204 | 205 | # Optimised for speed 206 | @micropython.native 207 | def _save(self, s, rbuf=bytearray(4), hdat0=b'\x03\x08\xff\xff'): 208 | n = self._read_reg(_SCI_HDAT1) 209 | self._spi.init(baudrate = _SCI_BAUDRATE) 210 | mvr = memoryview(rbuf) 211 | for _ in range(n): 212 | self._xcs(0) 213 | self._spi.write_readinto(hdat0, rbuf) 214 | self._xcs(1) 215 | s.write(mvr[2:]) # Data 10.8.4 MSB first 216 | self._overrun = max(self._overrun, n) 217 | return n # Samples written 218 | 219 | # Patch for recording. Data 10.8.1 220 | def _write_patch(self): 221 | self._write_reg(_SCI_WRAMADDR, 0x8010) 222 | for x in _PATCH: 223 | self._write_reg(_SCI_WRAM, x) 224 | self._write_reg(_SCI_WRAMADDR, 0x8028) 225 | for x in _PATCH1: 226 | self._write_reg(_SCI_WRAM, x) 227 | 228 | # *** PLAYBACK API *** 229 | 230 | def reset(self): # Issue hardware reset to VS1053 231 | self._xcs(1) 232 | self._xdcs(1) 233 | self._reset(0) 234 | time.sleep_ms(20) 235 | self._reset(1) 236 | time.sleep_ms(20) 237 | self.soft_reset() 238 | 239 | def soft_reset(self): 240 | self._slow_spi = True # Use _INITIAL_BAUDRATE 241 | self.mode_set(_SM_RESET) 242 | # This has many interesting settings data P39 243 | time.sleep_ms(20) # Adafruit have a total of 200ms 244 | while not self._dreq(): 245 | pass 246 | # Data P42. P7 footnote 4 recommends xtal * 3.5 + 1: using that. 247 | self._write_reg(_SCI_CLOCKF, 0x8800) 248 | if self._read_reg(_SCI_CLOCKF) != 0x8800: 249 | raise OSError('No VS1053 device found.') 250 | time.sleep_ms(1) # Clock setting can take 100us 251 | # Datasheet suggests writing to SPI_BASS. 252 | self._write_reg(_SCI_BASS, 0) # 0 is flat response 253 | self.volume(0, 0) 254 | while not self._dreq(): 255 | pass 256 | self._slow_spi = False 257 | 258 | # Range is 0 to -63.5 dB 259 | def volume(self, left, right, powerdown=False): 260 | bits = [0, 0] 261 | obits = 0xffff # powerdown 262 | if not powerdown: 263 | for n, l in enumerate((left, right)): 264 | bits[n] = round(min(max(2 * -l, 0), 127)) 265 | obits = bits[0] << 8 | bits[1] 266 | self._write_reg(_SCI_VOL, obits) 267 | 268 | def response(self, *, bass_freq=10, treble_freq=1000, bass_amp=0, treble_amp=0): 269 | bits = 0 270 | # Treble amplitude in dB range -12..10.5 271 | ta = round(min(max(treble_amp, -12.0), 10.5) / 1.5) & 0x0f 272 | bits |= ta << 12 273 | # Treble freq 1000-15000 274 | tf = round(min(max(treble_freq, 1000), 15000) / 1000) if ta else 0 275 | bits |= tf << 8 276 | # Bass amplitude in dB range 0..15 277 | ba = round(min(max(bass_amp, 0), 15)) 278 | bits |= ba << 4 279 | # Bass freq 20Hz-150Hz 280 | bf = round(min(max(bass_freq, 20), 150) / 10) if ba else 0 281 | bits |= bf 282 | self._write_reg(_SCI_BASS, bits) 283 | 284 | def pins_direction(self, bits): 285 | self._write_ram(_IO_DIRECTION, bits & 0xff) 286 | 287 | def pins(self, data=None): 288 | if data is not None: 289 | self._write_ram(_IO_WRITE, data & 0xff) 290 | return self._read_ram(_IO_READ) & 0x3ff 291 | 292 | def version(self): 293 | return (self._read_reg(_SCI_STATUS) >> 4) & 0x0F 294 | 295 | def decode_time(self): # Number of seconds into the stream 296 | return self._read_reg(_SCI_DECODE_TIME) 297 | 298 | def byte_rate(self): # Data rate in bytes/sec 299 | return self._read_ram(_BYTE_RATE) 300 | 301 | def mode(self): 302 | return self._read_reg(_SCI_MODE) 303 | 304 | def mode_set(self, bits): 305 | bits |= self.mode() | _SM_SDINEW 306 | self._write_reg(_SCI_MODE, bits) 307 | 308 | def mode_clear(self, bits): 309 | bits ^= 0xffff 310 | bits &= self.mode() 311 | self._write_reg(_SCI_MODE, _SM_SDINEW | bits) # Ensure new bit always set 312 | 313 | def enable_i2s(self, rate=48, mclock=False): 314 | v = 0x0C if mclock else 0x04 # Enable I2S and mclock if required 315 | if rate == 96: 316 | v |= 1 317 | elif rate == 192: 318 | v |= 2 319 | self._write_ram(0xC017, 0xF0) 320 | self._write_ram(0xC040, v) 321 | 322 | # Doesn't return anything useful for MP3 323 | # def pos_ms(self): # Position into stream in ms 324 | # return self._read_ram(_POS_MS_LS) | (self._read_ram(_POS_MS_MS) << 16) 325 | 326 | 327 | # Should check for short reads at EOF. Loop is time critical so I skip 328 | # this check. Sending a few bytes of old data has no obvious consequence. 329 | @micropython.native 330 | def play(self, s, buf = bytearray(32)): 331 | cancb = self._cancb 332 | cancnt = 0 333 | cnt = 0 334 | dreq = self._dreq 335 | while s.readinto(buf): # Read <=32 bytes 336 | cnt += 1 337 | # When running, dreq goes True when on-chip buffer can hold about 640 bytes. 338 | # At 128Kbps this will take 40ms - at higher rates, less. Call the cancel 339 | # callback during waiting periods or after 960 bytes if dreq never goes False. 340 | # This is a fault condition where the VS1053 wants data faster than we can 341 | # provide it. 342 | while (not dreq()) or cnt > 30: # 960 byte backstop 343 | cnt = 0 344 | if cancnt == 0 and cancb(): # Not cancelling. Check callback when waiting on dreq. 345 | cancnt = 1 # Send at least one more buffer 346 | self._xdcs(0) # Fast write 347 | self._spi.write(buf) 348 | self._xdcs(1) 349 | # cancnt > 0: Cancelling 350 | if cancnt: 351 | if cancnt == 1: # Just cancelled 352 | self.mode_set(_SM_CANCEL) 353 | if not self.mode() & _SM_CANCEL: # Cancel done 354 | efb = self._read_ram(_END_FILL_BYTE) & 0xff 355 | for n in range(len(buf)): 356 | buf[n] = efb 357 | for n in range(64): # send 2048 bytes of end fill byte 358 | self.write(buf) 359 | self.write(buf[:4]) # Take to 2052 bytes 360 | if self._read_reg(_SCI_HDAT0) or self._read_reg(_SCI_HDAT1): 361 | raise RuntimeError('Invalid HDAT value.') 362 | break 363 | if cancnt > 64: # Cancel has failed 364 | self.soft_reset() 365 | break 366 | cancnt += 1 # keep feeding data from stream 367 | else: 368 | self._end_play(buf) 369 | 370 | # Produce a 517Hz sine wave 371 | def sine_test(self, seconds=10): 372 | self.soft_reset() 373 | self.mode_set(_SM_TESTS) 374 | # 0x66-> Sample rate 22050 * 6/128 = 1034Hz 0x63->517Hz 375 | self.write(b'\x53\xef\x6e\x66\0\0\0\0') 376 | time.sleep(seconds) 377 | self.write(b'\x45\x78\x69\x74\0\0\0\0') 378 | self.mode_clear(_SM_TESTS) 379 | 380 | # Given a directory apply any patch files found. Applied in alphabetical 381 | # order. 382 | def patch(self, loc=None): 383 | if loc is None: 384 | mp = self._mp 385 | if mp is None: 386 | raise ValueError('No patch location') 387 | loc = ''.join((mp, 'plugins')) if mp.endswith('/') else ''.join((mp, '/plugins')) 388 | elif loc.endswith('/'): 389 | loc = loc[:-1] 390 | for f in sorted(os.listdir(loc)): 391 | f = ''.join((loc, '/', f)) 392 | print('Patching', f) 393 | with open(f, 'rb') as s: 394 | self._patch_stream(s) 395 | print('Patching complete.') 396 | 397 | # *** RECORD API *** 398 | 399 | # Convert a dB value to a linear gain as recognised by the chip. Unity gain 400 | # is a value of 1024. Range is 1 <= gain <= 65535 with 0 having special 401 | # meaning: this is represented by None 402 | def from_db(self, db): 403 | return 0 if db is None else max(min(round(1024*(10**(db/20))), 65535), 1) 404 | 405 | def record(self, fn, line, stop=10_000, sf=8000, agc_gain=None, gain=None, stereo=True): 406 | self._overrun = 0 407 | with open(fn, 'wb') as f: 408 | file_size = f.write(_HEADER) # Write the header template 409 | old_mode = self._read_reg(_SCI_MODE) # Current mode 410 | mode = old_mode | _SM_RESET | _SM_ADPCM 411 | if line: 412 | mode |= _SM_LINE_IN 413 | self._write_reg(_SCI_AICTRL0, sf) # Sampling freq 414 | self._write_reg(_SCI_AICTRL1, self.from_db(gain)) # None == AGC 415 | self._write_reg(_SCI_AICTRL2, self.from_db(agc_gain)) # Max AGC gain 416 | self._write_reg(_SCI_AICTRL3, 0 if stereo else 2) # Always ADPCM. Mono is left channel. 417 | self._write_reg(_SCI_MODE, mode) # Must start before patch. 418 | self._write_patch() 419 | 420 | nsamples = 0 # Number of samples i.e. 16 bit words. 421 | if callable(stop): 422 | while not stop(): 423 | nsamples += self._save(f) 424 | else: 425 | t = time.ticks_add(time.ticks_ms(), stop) 426 | while time.ticks_diff(time.ticks_ms(), t) < 0: 427 | nsamples += self._save(f) 428 | 429 | self._spi.init(baudrate = _DATA_BAUDRATE) 430 | file_size += nsamples * 2 431 | chans = 2 if stereo else 1 432 | # Now know file size so patch header. Data 10.8.4. Arithmetic could be 433 | # simplified. Keeping it close to datasheet for now. 434 | with open(fn, 'r+b') as f: 435 | # Stereo block is 256 words, mono 128. 436 | nblocks = nsamples // (256 if stereo else 128) 437 | f.seek(4) # Datasheet ref ChunkSize 438 | f.write(int.to_bytes(nblocks * 256 * chans + 52, 4, 'little')) 439 | if not stereo: 440 | f.seek(22) 441 | f.write(b'\x01') 442 | f.seek(33) 443 | f.write(b'\x01') 444 | f.seek(24) # SampleRate 445 | f.write(int.to_bytes(sf, 4, 'little')) 446 | f.seek(28) # ByteRate 447 | f.write(int.to_bytes(round(sf * 256 * chans / 505), 4, 'little')) 448 | f.seek(48) # NumOfSamples 449 | f.write(int.to_bytes(nblocks * 505, 4, 'little')) # Stereo?? 450 | f.seek(56) # SubChunk3Size 451 | f.write(int.to_bytes(nblocks * 256 * chans, 4, 'little')) 452 | # print('nsamples', nsamples) 453 | return self._overrun 454 | -------------------------------------------------------------------------------- /temp/ADAFRUIT.md: -------------------------------------------------------------------------------- 1 | # Review of adafruit_vs1053.py 2 | 3 | I studied this as the initial source of my asynchronous MicroPython solution. I 4 | made significant changes and enhancements some of which which you may wish to 5 | backport. 6 | 7 | These were my objectives: 8 | * Use asynchronous methods where appropriate [there is now a synchronous 9 | version too]. 10 | * Code in a cross-platform manner aiming for efficiency. 11 | * Check all code against the device data. 12 | * Remove CircuitPython dependencies. 13 | * Rename constants to match the datasheet. 14 | * Access SPI bus and pins directly rather than via CircuitPython arbitration. 15 | * Replace properties with methods in accordance with MicroPython practice. 16 | * Support the on-board SD card. 17 | * Add functionality and make some methods more user friendly. 18 | 19 | In the following, `CP` refers to `adafruit_vs1053.py`. 20 | 21 | # Timing 22 | 23 | Timing and SPI bus rate are crucial. An asynchronous solution is at an inherent 24 | disadvantage owing to scheduling overheads: you may prefer the synchronous 25 | design. The asynchronous version does not deliver acceptable audio on ESPx 26 | platforms, whereas the synchronous version does. 27 | 28 | # Baudrate and clock rate 29 | 30 | CP accesses command registers 31 | [at 250Kbps](https://github.com/adafruit/Adafruit_CircuitPython_VS1053/blob/0676a052d65a169ca15f42006c1f5f129634b0be/adafruit_vs1053.py#L69) 32 | with the comment "(MUST be slow)". I found nothing in the datasheet to justify 33 | this. I use 1Mbps initially, increasing it for all transfers once the 34 | `SCI_CLOCKF` register has been set. 35 | 36 | After reset the internal clock runs at 12.288MHz. Datasheet section 7.4.4 37 | states "the maximum speed for SCI reads is CLKI/7". I can't see from the timing 38 | diagram how this figure of 7 is calculated. But they are the designers. Reading 39 | from registers is not performance critical so I use this figure to determine 40 | the rate for all register accesses. 41 | 42 | Hence the maximum initial baudrate is 12.288/7 = 1.75MHz. 43 | 44 | I set the `SCI_CLOCKF` to 0x8800 (a multiplier of 3.5) as this corresponds to a 45 | datasheet recommendation (section 4.2 footnote 4). 46 | [CP uses 0x6000](https://github.com/adafruit/Adafruit_CircuitPython_VS1053/blob/0676a052d65a169ca15f42006c1f5f129634b0be/adafruit_vs1053.py#L171). 47 | 48 | After setting the multiplier to 3.5 the maximum SCI (register) read speed is 49 | 12.288*3.5/7=6.144MHz 50 | 51 | The maximum SDI (data) write speed is 52 | 12.288*3.5/4=10.752MHz (data section 7.3.2). 53 | 54 | I specify 10.752MHz for data, 5MHz for commands. The way I do this causes the 55 | SD card to be clocked at up to 10.752MHz. This is much higher than the rate of 56 | 1.32MHz set by the official SD card driver. My reading suggests that modern SD 57 | cards can be clocked at 25MHz. I would welcome comments on this. 58 | 59 | ## SPI bus 60 | 61 | The actual clock rate is typically less than the specified one. A Pyboard Lite 62 | runs at 6MHz, a Pyboard D at 9MHz, a Pyboard 1.x at 10.5MHz. There is a 63 | difference in the behaviour of hard SPI interfaces between Pyboards and the 64 | ESP8266. All Pyboards clock a buffer out at a constant rate. By contrast the 65 | ESP8266 issues a byte at the specified rate, interposing a 7μs gap between each 66 | byte. This (and probably other latencies) prevents successful operation with 67 | the asynchronous driver. 68 | 69 | # sdcard.py 70 | 71 | The official version doesn't work if the bus is shared. This 72 | [PR](https://github.com/micropython/micropython/pull/6007) is under review to 73 | fix this. Comments welcome. 74 | 75 | # Code review 76 | 77 | * This 78 | [comment](https://github.com/adafruit/Adafruit_CircuitPython_VS1053/blob/0676a052d65a169ca15f42006c1f5f129634b0be/adafruit_vs1053.py#L147) 79 | seems spurious. 80 | * `.reset` At 200ms total the sleep time is conservative. 81 | * `.reset` The setting of `_VS1053_REG_CLOCKF` on 82 | [line 171](https://github.com/adafruit/Adafruit_CircuitPython_VS1053/blob/0676a052d65a169ca15f42006c1f5f129634b0be/adafruit_vs1053.py#L171) 83 | is not optimal (see above). 84 | * `.set_volume` You might like to backport my solution which uses dB values. 85 | *`.ready_for_data` Given that this is performance critical it might be worth 86 | reviewing whether a property is the best approach. Should it be inlined? 87 | * `.decode_time` The code doesn't match the (correct) comment (data 9.6.5). 88 | * `.sine_test` I simplified this without obvious ill-effects. It uses maximum 89 | volume and a fixed frequency, and leaves the volume at max. My view is that 90 | this is OK if documented but you may wish to vary this. It also seemed wise to 91 | clear the `_SM_TESTS` mode bit on exit. 92 | 93 | ## Mode handling 94 | 95 | The use of the `_VS1053_REG_MODE` register should be reviewed. The 96 | `_VS1053_MODE_SM_SDINEW` bit should be set on reset and never cleared. I have 97 | methods to set and clear bits which ensure this. 98 | 99 | ## Playback 100 | 101 | This doesn't match the datasheet and (in my opinion) needs thorough review. 102 | Here is my take on the requirements. 103 | 104 | The device's FIFO control is crude. There is no way to check its state other 105 | than the DREQ pin; when asserted the buffer can accept at least 32 bytes. Not 106 | ideal for high performance. 107 | 108 | The algorithm is therefore to wait on DREQ and send 32 bytes (or fewer, if at 109 | the end of a track) repeating until at the end of data. This suggests that 110 | checking DREQ should be fast and efficient, and the splitting of the datastream 111 | should be fast and non-allocating. In my view these processes should be done in 112 | the driver rather than by the caller, especially in code aimed at being 113 | beginner friendly. 114 | 115 | The end of a track, and cancellation of playback, require rather involved 116 | sequences which I have attempted to implement (data 10.5.1 and 10.5.2). Playing 117 | consecutive tracks and playing a track after cancelling a prior one occur with 118 | no audible problems. But I'd welcome any comments as I may well have missed 119 | something. 120 | 121 | ## Backport options 122 | 123 | You may be interested in checking out other features which I have implemented 124 | including support for the I/O pins. Also the bass and treble control with 125 | meaningful Hz and (relative) dB values. 126 | --------------------------------------------------------------------------------