Compare commits

...

7 Commits

Author SHA1 Message Date
818dfd5cb7 fix SPECIAL mode compile error 2023-04-16 21:43:43 +02:00
adbed6b207 update to 1.9.7 2023-01-04 14:01:20 +01:00
6110583190 Add gitignore and cleanup 2021-12-14 19:20:57 +01:00
f47f87cd5f restored midi generators 2021-12-10 17:14:26 +01:00
25d5183a73 esp_v2 & previous version compat 2021-11-29 16:51:48 +01:00
dc7c4bbb17 update for arduino-esp v2 2021-11-27 11:07:49 +01:00
cd47e00a7d esp32_s2 2021-11-26 11:12:56 +01:00
59 changed files with 636 additions and 281 deletions

View File

@@ -1,95 +0,0 @@
# Run whenever a PR is generated or updated.
# Most jobs check out the code, ensure Python3 is installed, and for build
# tests the ESP8266 toolchain is cached when possible to speed up execution.
name: ESP8266Audio
on:
push:
branches:
- master
pull_request:
jobs:
build-esp8266:
name: Build ESP8266
runs-on: ubuntu-latest
strategy:
matrix:
chunk: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Build Sketches
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
TRAVIS_TAG: ${{ github.ref }}
BUILD_TYPE: build
BUILD_MOD: 5
BUILD_REM: ${{ matrix.chunk }}
run: |
bash ./tests/common.sh
build-esp32:
name: Build ESP-32
runs-on: ubuntu-latest
strategy:
matrix:
chunk: [0, 1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Build Sketches
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
TRAVIS_TAG: ${{ github.ref }}
BUILD_TYPE: build_esp32
BUILD_MOD: 5
BUILD_REM: ${{ matrix.chunk }}
run: |
bash ./tests/common.sh
# Run host test suite under valgrind for runtime checking of code.
host-tests:
name: Host tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Run host tests
env:
TRAVIS_BUILD_DIR: ${{ github.workspace }}
TRAVIS_TAG: ${{ github.ref }}
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install valgrind lcov gcc-multilib g++-multilib libc6-dbg:i386
cd ./tests/host/
make
valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all --error-exitcode=999 ./mp3
valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all --error-exitcode=999 ./aac
valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all --error-exitcode=999 ./wav
valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all --error-exitcode=999 ./midi
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
with:
library-manager: 'update'

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
.DS_Store
.pio
.vscode
# kicad Temporary files
*.000
*.bak
*.bck
*.kicad_pcb-bak
*.kicad_sch-bak
*.kicad_prl
*.sch-bak
*~
_autosave-*
*.tmp
*-save.pro
*-save.kicad_pcb
fp-info-cache
# Netlist files (exported from Eeschema)
*.net
# Autorouter files (exported from Pcbnew)
*.dsn
*.ses
# Exported BOM files
*.xml
*.csv
# other files
CAD/Leo_muziekdoos_ESP32/~$ESP32 Pins.xlsx

1
.piopm
View File

@@ -1 +0,0 @@
{"type": "library", "name": "ESP8266Audio", "version": "1.9.2", "spec": {"owner": "earlephilhower", "id": 1964, "name": "ESP8266Audio", "requirements": null, "url": null}}

View File

@@ -1,4 +1,4 @@
# ESP8266Audio - supports ESP8266 & ESP32 & Raspberry Pi RP2040[![Gitter](https://badges.gitter.im/ESP8266Audio/community.svg)](https://gitter.im/ESP8266Audio/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
# ESP8266Audio - supports ESP8266 & ESP32 & Raspberry Pi RP2040 [![Gitter](https://badges.gitter.im/ESP8266Audio/community.svg)](https://gitter.im/ESP8266Audio/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Arduino library for parsing and decoding MOD, WAV, MP3, FLAC, MIDI, AAC, and RTTL files and playing them on an I2S DAC or even using a software-simulated delta-sigma DAC with dynamic 32x-128x oversampling.
ESP8266 is fully supported and most mature, but ESP32 is also mostly there with built-in DAC as well as external ones.
@@ -31,7 +31,9 @@ JohannesMTC has built a similar project especially for model trains: https://git
A neat MQTT-driven ESP8266 light-and-sound device (alarm? toy? who can say!) was built by @CosmicMac, available at https://github.com/CosmicMac/ESParkle
A very interesting "linear clock" with a stepper motor, NTP time keeping, and configurable recorded chimes with schematics, 3D printer plans, and source code, is now available http://home.kpn.nl/bderogee1980/projects/linear_clock/linear_clock.html
A very interesting "linear clock" with a stepper motor, NTP time keeping, and configurable recorded chimes with schematics, 3D printer plans, and source code, is now available https://janderogee.com/projects/linear_clock/linear_clock.htm
Source and instructions for a gorgeous wooden MP3-playing clock, FM radio and a walkie-talkie using the ESP8266 and AVR microcontrollers is available https://github.com/zduka/mp3-player
## Prerequisites
First, make sure you are running the 2.6.3/later or GIT head version of the Arduino libraries for ESP8266, or the latest ESP32 SDK from Espressif.
@@ -100,7 +102,7 @@ AudioFileSourcePROGMEM: Reads a file from a PROGMEM array. Under UNIX you can
AudioFileSourceHTTPStream: Simple implementation of a streaming HTTP reader for ShoutCast-type MP3 streaming. Not yet resilient, and at 44.1khz 128bit stutters due to CPU limitations, but it works more or less.
## AudioFileSourceBuffer - Double buffering, useful for HTTP streams
AudioFileSourceBuffer is an input source that simpy adds an additional RAM buffer of the output of any other AudioFileSource. This is particularly useful for web streaming where you need to have 1-2 packets in memory to ensure hiccup-free playback.
AudioFileSourceBuffer is an input source that simply adds an additional RAM buffer of the output of any other AudioFileSource. This is particularly useful for web streaming where you need to have 1-2 packets in memory to ensure hiccup-free playback.
Create your standard input file source, create the buffer with the original source as its input, and pass this buffer object to the generator.
```cpp
@@ -158,7 +160,15 @@ AudioOutputSPIFFSWAV: Writes a binary WAV format with headers to a SPIFFS files
AudioOutputNull: Just dumps samples to /dev/null. Used for speed testing as it doesn't artificially limit the AudioGenerator output speed since there are no buffers to fill/drain.
## I2S DACs
I've used both the Adafruit [I2S +3W amp DAC](https://www.adafruit.com/product/3006) and a generic PCM5102 based DAC with success. The biggest problems I've seen from users involve pinouts from the ESP8266 for GPIO and hooking up all necessary pins on the DAC board.
I've used both the Adafruit [I2S +3W amp DAC](https://www.adafruit.com/product/3006) and a generic PCM5102 based DAC with success. The biggest problems I've seen from users involve pinouts from the ESP8266 for GPIO and hooking up all necessary pins on the DAC board. The essential pins are:
I2S pin | Common label* | ESP8266 pin
--------|---------------|-------------
LRC | D4 | GPIO2
BCLK | D8 | GPIO15
DIN | RX | GPIO3
\* The "common label" column applies to common NodeMCU and D1 Mini development boards. Unfortunately some manufacturers use different mappings so the labels listed here might not apply to your particular model.
### Adafruit I2S DAC
This is quite simple and only needs the GND, VIN, LRC, BCLK< and DIN pins to be wired. Be sure to use +5V on the VIN to get the loudest sound. See the [Adafruit example page](https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp) for more info.
@@ -171,7 +181,7 @@ I've used several versions of PCM5102 DAC boards purchased from eBay. They've a
### Others
There are many other variants out there, and they should all work reasonably well with this code and the ESP8266. Please be certain you've read the datasheet and are applying proper input voltages, and be sure to tie off any unused inputs to GND or VCC as appropriate. LEaving an input pin floating on any integrated circuit can cause unstable operation as it may pick up noise from the environment (very low input capacitance) and cause havoc with internal IC settings.
There are many other variants out there, and they should all work reasonably well with this code and the ESP8266. Please be certain you've read the datasheet and are applying proper input voltages, and be sure to tie off any unused inputs to GND or VCC as appropriate. Leaving an input pin floating on any integrated circuit can cause unstable operation as it may pick up noise from the environment (very low input capacitance) and cause havoc with internal IC settings.
## Software I2S Delta-Sigma DAC (i.e. playing music with a single transistor and speaker)
For the best fidelity, and stereo to boot, spend the money on a real I2S DAC. Adafruit makes a great mono one with amplifier, and you can find stereo unamplified ones on eBay or elsewhere quite cheaply. However, thanks to the software delta-sigma DAC with 32x oversampling (up to 128x if the audio rate is low enough) you can still have pretty good sound!
@@ -206,7 +216,7 @@ USB-5V -- Speaker + Terminal
*NOTE*: A prior version of this schematic had a direct connection from the ESP8266 to the base of the transistor. While this does provide the maximum amplitude, it also can draw more current from the 8266 than is safe, and can also cause the transistor to overheat.
As of the latest ESP8266Audio release, with the software delta-sigma DAC the LRCLK and BCLK pins *can* be used by an application. Simply use normal `pinMode` and `dicitalWrite` or `digitalRead` as desired.
As of the latest ESP8266Audio release, with the software delta-sigma DAC the LRCLK and BCLK pins *can* be used by an application. Simply use normal `pinMode` and `digitalWrite` or `digitalRead` as desired.
### High pitched buzzing with the 1-T circuit
The 1-T amp can _NOT_ drive any sort of amplified speaker. If there is a power or USB input to the speaker, or it has lights or Bluetooth or a battery, it can _NOT_ be used with this circuit.
@@ -239,7 +249,7 @@ ESP Pin -------|____|--------+
|
Ground ---------------------+
```
For ESP8266 with red LED (~1.9Vf drop) you need minimum 150Ohm resistor (12mA max per pin), and output pin is fixed (GPIO3/RX0).On ESP32 it is confgurable with `AudioOutputSPDIF(gpio_num)`.
For ESP8266 with red LED (~1.9Vf drop) you need minimum 150Ohm resistor (12mA max per pin), and output pin is fixed (GPIO3/RX0).On ESP32 it is configurable with `AudioOutputSPDIF(gpio_num)`.
## Using external SPI RAM to increase buffer
A class allows you to use a 23lc1024 SPI RAM from Microchip as input buffer. This chip connects to ESP8266 HSPI port and provides a large buffer to help avoid hiccus in playback of web streams.

View File

@@ -1,5 +1,10 @@
#include <Arduino.h>
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
#define WIFI_OFF
class __x { public: __x() {}; void mode() {}; };
__x WiFi;
#elif defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>

View File

@@ -1,4 +1,8 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_RP2040
void setup() {}
void loop() {}
#else
#include "AudioFileSourceSD.h"
#include "AudioOutputSPDIF.h"
#include "AudioGeneratorFLAC.h"
@@ -8,7 +12,7 @@
// Espressif Audio Development Framework at:
// https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/audio-samples.html
//
// On ESP8266 you might need to reencode FLAC files with max '-2' compression level
// On ESP8266 you might need to re-encode FLAC files with max '-2' compression level
// (i.e. 1152 maximum block size) or you will run out of memory. FLAC files will be
// slightly bigger but you don't loose audio quality with reencoding (lossles codec).
@@ -61,8 +65,10 @@ void loop() {
}
}
} else {
Serial.println(F("Playback form SD card done\n"));
Serial.println(F("Playback from SD card done\n"));
delay(1000);
}
}
}
#endif

View File

@@ -6,7 +6,13 @@
}
void loop() {}
#else
#include <ESP8266WiFi.h>
#if defined(ARDUINO_ARCH_RP2040)
#define WIFI_OFF
class __x { public: __x() {}; void mode() {}; };
__x WiFi;
#else
#include <ESP8266WiFi.h>
#endif
#include <AudioOutputI2S.h>
#include <AudioGeneratorMIDI.h>
#include <AudioFileSourceLittleFS.h>
@@ -42,7 +48,6 @@ void loop()
{
if (midi->isRunning()) {
if (!midi->loop()) {
uint32_t e = millis();
midi->stop();
}
} else {

View File

@@ -1,4 +1,11 @@
#include <Arduino.h>
// Do not build on GCC8, GCC8 has a compiler bug
#if defined(ARDUINO_ARCH_RP2040) || (__GNUC__ == 8)
void setup() {}
void loop() {}
#else
#ifdef ESP32
#include <WiFi.h>
#include "SPIFFS.h"
@@ -25,6 +32,7 @@ void setup()
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
SPIFFS.begin();
Serial.println("Starting up...\n");
audioLogger = &Serial;
@@ -43,7 +51,6 @@ void loop()
{
if (midi->isRunning()) {
if (!midi->loop()) {
uint32_t e = millis();
midi->stop();
}
} else {
@@ -52,4 +59,4 @@ void loop()
}
}
#endif

View File

@@ -2,7 +2,11 @@
#include "AudioFileSourcePROGMEM.h"
#include "AudioGeneratorMOD.h"
#include "AudioOutputI2S.h"
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
#define WIFI_OFF
class __x { public: __x() {}; void mode() {}; };
__x WiFi;
#elif defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>

View File

@@ -1,4 +1,9 @@
#include <Arduino.h>
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
#ifdef ESP32
#include <WiFi.h>
#include "SPIFFS.h"
@@ -70,3 +75,4 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -1,4 +1,8 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_RP2040
void setup() {}
void loop() {}
#else
#ifdef ESP32
#include "SPIFFS.h"
#endif
@@ -97,3 +101,4 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -1,4 +1,8 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_RP2040
void setup() {}
void loop() {}
#else
#ifdef ESP32
#include <WiFi.h>
#include "SPIFFS.h"
@@ -39,3 +43,5 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -44,7 +44,7 @@ void setup() {
// param : float (current time [sec] of the song)
// return : float (the amplitude of sound which varies from -1.f to +1.f)
//
// sound function can be registerd only one or the same number with channels
// sound function can be registered only one or the same number with channels
// if the channels > 1 && the number of function == 1,
// same function are used to generate the sound in every channel
//

View File

@@ -1,6 +1,11 @@
#include <Arduino.h>
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
@@ -104,4 +109,4 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -0,0 +1,148 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_RP2040
void setup() {}
void loop() {}
#else
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
//#include "AudioOutputI2SNoDAC.h"
#include "AudioOutputSPDIF.h"
//
// Stream MP3 from HTTP to SPDIF
//
// To run, set your ESP8266 build to 160MHz, update the SSID info, and upload.
// Note:
// If using ESP8266 NodeMCU connect LED to RX pin and GND pin
// Enter your WiFi setup here:
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
const char* ssid = STASSID;
const char* password = STAPSK;
// Examples URLs
//const char *URL="http://kvbstreams.dyndns.org:8000/wkvi-am";
// Italian Rock Radio
const char *URL="http://streamingv2.shoutcast.com/radiofreccia";
// Stream URL of Logitech Media Server, aka LMS, Version: 8.2.0 (August 2021)
// const char *URL="http://192.168.1.121:9000/stream.mp3";
AudioGeneratorMP3 *mp3;
AudioFileSourceICYStream *file;
AudioFileSourceBuffer *buff;
// Output device is SPDIF
AudioOutputSPDIF *out;
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) isUnicode; // Punt this ball for now
// Note that the type and string may be in PROGMEM, so copy them to RAM for printf
char s1[32], s2[64];
strncpy_P(s1, type, sizeof(s1));
s1[sizeof(s1)-1]=0;
strncpy_P(s2, string, sizeof(s2));
s2[sizeof(s2)-1]=0;
Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, s1, s2);
Serial.flush();
}
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
// Note that the string may be in PROGMEM, so copy it to RAM for printf
char s1[64];
strncpy_P(s1, string, sizeof(s1));
s1[sizeof(s1)-1]=0;
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
Serial.flush();
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("Connecting to WiFi");
WiFi.disconnect();
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// Try forever
while (WiFi.status() != WL_CONNECTED) {
Serial.println("...Connecting to WiFi");
delay(1000);
}
Serial.println("Connected");
audioLogger = &Serial;
file = new AudioFileSourceICYStream(URL);
// Commented out for performance issues with high rate MP3 stream
//file->RegisterMetadataCB(MDCallback, (void*)"ICY");
buff = new AudioFileSourceBuffer(file, 4096); // Doubled form default 2048
// Commented out for performance issues with high rate MP3 stream
//buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
// Set SPDIF output
out = new AudioOutputSPDIF();
mp3 = new AudioGeneratorMP3();
// Commented out for performance issues with high rate MP3 stream
//mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
mp3->begin(buff, out);
}
void loop()
{
// Commented out
//static int lastms = 0;
if (mp3->isRunning()) {
/* Commented out
if (millis()-lastms > 1000) {
lastms = millis();
Serial.printf("Running for %d ms...\n", lastms);
Serial.flush();
}
*/
if (!mp3->loop()) {
mp3->stop();
}
} else {
Serial.printf("MP3 done\n");
// Restart ESP when streaming is done or errored
delay(10000);
ESP.restart();
}
}
#endif

View File

@@ -1,5 +1,9 @@
#include <Arduino.h>
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
@@ -101,4 +105,4 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -30,6 +30,7 @@ class AudioOutputNullSlow : public AudioOutput
~AudioOutputNullSlow() {};
virtual bool begin() { samples = 0; startms = millis(); return true; }
virtual bool ConsumeSample(int16_t sample[2]) {
(void) sample;
// return false (= output buffer full)
// sometimes to let the main loop running
constexpr int everylog2 = 10;

View File

@@ -1,6 +1,10 @@
#include <Arduino.h>
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
@@ -9,10 +13,10 @@
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#if AUDIO
#pragma message("Outputting audio")
// #pragma message("Outputting audio")
#include "AudioOutputLinuxDSP.h"
#else
#pragma message("No audio")
// #pragma message("No audio")
#include "AudioOutputNullSlow.h"
#endif
@@ -115,4 +119,4 @@ void loop()
delay(1000);
}
}
#endif

View File

@@ -2,7 +2,14 @@
// https://github.com/going-digital/Talkie/blob/master/Talkie/examples/Vocab_US_Clock/Vocab_US_Clock.ino
// Released under GPL v2
#ifdef ESP32
#include <Arduino.h>
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
@@ -183,3 +190,4 @@ void loop()
sayTime(tmstruct.tm_hour, tmstruct.tm_min, talkie);
delay(1000);
}
#endif

View File

@@ -19,7 +19,15 @@
*/
#include <Arduino.h>
#ifdef ESP32
#if defined(ARDUINO_ARCH_RP2040)
void setup() {}
void loop() {}
#else
// ESP8266 server.available() is now server.accept()
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#if defined(ESP32)
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
@@ -439,3 +447,4 @@ void loop()
}
}
#endif

View File

@@ -19,6 +19,9 @@
*/
#include <Arduino.h>
#if defined(ARDUINO_ARCH_RP2040)
// Nothing here
#else
#ifdef ESP32
#include <WiFi.h>
#else
@@ -308,7 +311,4 @@ void Read4Int(char *str, byte *p)
str += ParseInt(str, &i); p[2] = i; if (*str) str++;
str += ParseInt(str, &i); p[3] = i;
}
#endif

View File

@@ -14,10 +14,13 @@
"type": "git",
"url": "https://github.com/earlephilhower/ESP8266Audio"
},
"version": "1.9.2",
"version": "1.9.7",
"homepage": "https://github.com/earlephilhower/ESP8266Audio",
"frameworks": "Arduino",
"examples": [
"examples/*/*.ino"
]
],
"build": {
"libLDFMode": "deep"
}
}

View File

@@ -1,5 +1,5 @@
name=ESP8266Audio
version=1.9.2
version=1.9.7
author=Earle F. Philhower, III
maintainer=Earle F. Philhower, III
sentence=Audio file and I2S sound playing routines for ESP8266, ESP32, and Raspberry Pi Pico RP2040

View File

@@ -1,6 +1,6 @@
/*
AudioFileSourceFunction
Audio ouptut generator which can generate WAV file data from function
Audio output generator which can generate WAV file data from function
Copyright (C) 2021 Hideaki Tai
@@ -25,14 +25,14 @@ AudioFileSourceFunction::AudioFileSourceFunction(float sec, uint16_t channels, u
uint32_t len = uint32_t(sec * (float)bytes_per_sec);
// RIFF chunk
strncpy(wav_header.riff.chunk_id, "RIFF", 4);
memcpy(wav_header.riff.chunk_id, "RIFF", 4);
wav_header.riff.chunk_size = 4 // size of riff chunk w/o chunk_id and chunk_size
+ 8 + 16 // size of format chunk
+ 8 + len; // size of data chunk
strncpy(wav_header.riff.format, "WAVE", 4);
memcpy(wav_header.riff.format, "WAVE", 4);
// format chunk
strncpy(wav_header.format.chunk_id, "fmt ", 4);
memcpy(wav_header.format.chunk_id, "fmt ", 4);
wav_header.format.chunk_size = 16;
wav_header.format.format_tag = 0x0001; // PCM
wav_header.format.channels = channels;
@@ -42,7 +42,7 @@ AudioFileSourceFunction::AudioFileSourceFunction(float sec, uint16_t channels, u
wav_header.format.bits_per_sample = bits_per_sample;
// data chunk
strncpy(wav_header.data.chunk_id, "data", 4);
memcpy(wav_header.data.chunk_id, "data", 4);
wav_header.data.chunk_size = len;
funcs.reserve(channels);

View File

@@ -1,6 +1,6 @@
/*
AudioFileSourceFunction
Audio ouptut generator which can generate WAV file data from function
Audio output generator which can generate WAV file data from function
Copyright (C) 2021 Hideaki Tai

View File

@@ -20,6 +20,9 @@
#if defined(ESP32) || defined(ESP8266)
#ifdef _GNU_SOURCE
#undef _GNU_SOURCE
#endif
#define _GNU_SOURCE
#include "AudioFileSourceICYStream.h"
@@ -47,6 +50,7 @@ bool AudioFileSourceICYStream::open(const char *url)
http.addHeader("Icy-MetaData", "1");
http.collectHeaders( hdr, 4 );
http.setReuse(true);
http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
int code = http.GET();
if (code != HTTP_CODE_OK) {
http.end();

View File

@@ -18,13 +18,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _AUDIOFILESOURCESPIFFS_H
#define _AUDIOFILESOURCESPIFFS_H
//#ifndef ESP32 // No LittleFS there, yet
#ifndef _AUDIOFILESOURCELITTLEFS_H
#define _AUDIOFILESOURCELITTLEFS_H
#include <Arduino.h>
#include <LITTLEFS.h>
#include <LittleFS.h>
#include "AudioFileSource.h"
#include "AudioFileSourceFS.h"
@@ -32,12 +30,10 @@
class AudioFileSourceLittleFS : public AudioFileSourceFS
{
public:
AudioFileSourceLittleFS() : AudioFileSourceFS(LITTLEFS) { };
AudioFileSourceLittleFS(const char *filename) : AudioFileSourceFS(LITTLEFS, filename) {};
AudioFileSourceLittleFS() : AudioFileSourceFS(LittleFS) { };
AudioFileSourceLittleFS(const char *filename) : AudioFileSourceFS(LittleFS, filename) {};
// Others are inherited from base
};
//#endif
#endif

View File

@@ -27,6 +27,9 @@
#include "AudioFileSource.h"
#include "AudioFileSourceFS.h"
// Yes, I know SPIFFS is deprecated
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
class AudioFileSourceSPIFFS : public AudioFileSourceFS
{
public:

View File

@@ -105,7 +105,7 @@ uint32_t AudioFileSourceSPIRAMBuffer::read(void *data, uint32_t len)
}
// Read up to the entire buffer from RAM
uint32_t toReadFromBuffer = std::min(len, writePtr - readPtr);
uint32_t toReadFromBuffer = std::min(len, (uint32_t)(writePtr - readPtr));
uint8_t *ptr = reinterpret_cast<uint8_t*>(data);
if (toReadFromBuffer > 0) {
#ifdef FAKERAM

View File

@@ -62,6 +62,9 @@ bool AudioGeneratorFLAC::begin(AudioFileSource *source, AudioOutput *output)
output->begin();
running = true;
lastSample[0] = 0;
lastSample[1] = 0;
channels = 0;
return true;
}
@@ -71,7 +74,7 @@ bool AudioGeneratorFLAC::loop()
if (!running) goto done;
if (!output->ConsumeSample(lastSample)) goto done; // Try and send last buffered sample
if (channels && !output->ConsumeSample(lastSample)) goto done; // Try and send last buffered sample
do {
if (buffPtr == buffLen) {

View File

@@ -1,6 +1,6 @@
/*
AudioGeneratorMIDI
Audio output generator that plays MIDI files using a SF2 SoundFont
Audio output generator that plays MIDI files using an SF2 SoundFont
Copyright (C) 2017 Earle F. Philhower, III
@@ -58,6 +58,10 @@
#include "AudioGeneratorMIDI.h"
#if __GNUC__ == 8
// Do not build, GCC8 has a compiler bug
#else // __GNUC__ == 8
#pragma GCC optimize ("O3")
#define TSF_NO_STDIO
@@ -172,7 +176,7 @@ unsigned int AudioGeneratorMIDI::buffer_int32 (int offset) {
unsigned long AudioGeneratorMIDI::get_varlen (int *ptr) {
/* Get a 1-4 byte variable-length value and adjust the pointer past it.
These are a succession of 7-bit values with a MSB bit of zero marking the end */
These are a succession of 7-bit values with an MSB bit of zero marking the end */
unsigned long val;
int i, byte;
@@ -331,7 +335,7 @@ note_off:
}
// Open file, parse headers, get ready tio process MIDI
// Open file, parse headers, get ready to process MIDI
void AudioGeneratorMIDI::PrepareMIDI(AudioFileSource *src)
{
MakeStreamFromAFS(src, &afsMIDI);
@@ -364,7 +368,7 @@ void AudioGeneratorMIDI::PrepareMIDI(AudioFileSource *src)
int AudioGeneratorMIDI::PlayMIDI()
{
/* Continue processing all tracks, in an order based on the simulated time.
This is not unlike multiway merging used for tape sorting algoritms in the 50's! */
This is not unlike multiway merging used for tape sorting algorithms in the 50's! */
do { /* while there are still track notes to process */
static struct track_status *trk;
@@ -637,3 +641,5 @@ void AudioGeneratorMIDI::MakeStreamFromAFS(AudioFileSource *src, tsf_stream *afs
afs->size = &afs_size;
}
#endif //__GNUC__ == 8

View File

@@ -21,6 +21,10 @@
#ifndef _AUDIOGENERATORMIDI_H
#define _AUDIOGENERATORMIDI_H
#if __GNUC__ == 8
// Do not build, GCC8 has a compiler bug
#else // __GNUC__ == 8
#include "AudioGenerator.h"
#define TSF_NO_STDIO
@@ -176,6 +180,7 @@ class AudioGeneratorMIDI : public AudioGenerator
short samplesRendered[256];
};
#endif //__GNUC__ == 8
#endif

View File

@@ -69,6 +69,11 @@ bool AudioGeneratorMOD::stop()
free(FatBuffer.channels[i]);
FatBuffer.channels[i] = NULL;
}
if(running || ((file != NULL) && (file->isOpen() == true))) {
output->flush(); //flush I2S output buffer, if the player was actually running before.
}
if (file) file->close();
running = false;
output->stop();
@@ -124,7 +129,7 @@ bool AudioGeneratorMOD::begin(AudioFileSource *source, AudioOutput *out)
UpdateAmiga();
for (int i = 0; i < CHANNELS; i++) {
FatBuffer.channels[i] = reinterpret_cast<uint8_t*>(malloc(fatBufferSize));
FatBuffer.channels[i] = reinterpret_cast<uint8_t*>(calloc(fatBufferSize, 1));
if (!FatBuffer.channels[i]) {
stop();
return false;
@@ -232,6 +237,11 @@ bool AudioGeneratorMOD::LoadHeader()
Mod.numberOfChannels = (temp[0] - '0') * 10 + temp[1] - '0';
else
Mod.numberOfChannels = 4;
if (Mod.numberOfChannels > CHANNELS) {
audioLogger->printf("\nAudioGeneratorMOD::LoadHeader abort - too many channels (configured: %d, needed: %d)\n", CHANNELS, Mod.numberOfChannels);
return(false);
}
return true;
}
@@ -417,7 +427,7 @@ bool AudioGeneratorMOD::ProcessRow()
if (sampleNumber) {
Player.lastSampleNumber[channel] = sampleNumber - 1;
if (!(effectParameter == 0xE && effectParameterX == NOTEDELAY))
if (!(effectNumber == 0xE && effectParameterX == NOTEDELAY))
Player.volume[channel] = Mod.samples[Player.lastSampleNumber[channel]].volume;
}
@@ -565,7 +575,7 @@ bool AudioGeneratorMOD::ProcessRow()
Mixer.channelFrequency[channel] = Player.amiga / Player.lastAmigaPeriod[channel];
if (note != NONOTE)
Mixer.channelSampleOffset[channel] = sampleOffset << DIVIDER;
Mixer.channelSampleOffset[channel] = sampleOffset << FIXED_DIVIDER;
if (sampleNumber)
Mixer.channelSampleNumber[channel] = Player.lastSampleNumber[channel];
@@ -735,13 +745,14 @@ bool AudioGeneratorMOD::RunPlayer()
void AudioGeneratorMOD::GetSample(int16_t sample[2])
{
int16_t sumL;
int16_t sumR;
int32_t sumL;
int32_t sumR;
uint8_t channel;
uint32_t samplePointer;
int8_t current;
int8_t next;
int16_t out;
int32_t out32;
if (!running) return;
@@ -757,12 +768,12 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])
if (!Mixer.channelVolume[channel]) continue;
samplePointer = Mixer.sampleBegin[Mixer.channelSampleNumber[channel]] +
(Mixer.channelSampleOffset[channel] >> DIVIDER);
(Mixer.channelSampleOffset[channel] >> FIXED_DIVIDER);
if (Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]]) {
if (samplePointer >= Mixer.sampleLoopEnd[Mixer.channelSampleNumber[channel]]) {
Mixer.channelSampleOffset[channel] -= Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]] << DIVIDER;
Mixer.channelSampleOffset[channel] -= Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]] << FIXED_DIVIDER;
samplePointer -= Mixer.sampleLoopLength[Mixer.channelSampleNumber[channel]];
}
@@ -779,8 +790,8 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])
samplePointer >= FatBuffer.samplePointer[channel] + fatBufferSize - 1 ||
Mixer.channelSampleNumber[channel] != FatBuffer.channelSampleNumber[channel]) {
uint16_t toRead = Mixer.sampleEnd[Mixer.channelSampleNumber[channel]] - samplePointer + 1;
if (toRead > fatBufferSize) toRead = fatBufferSize;
uint32_t toRead = Mixer.sampleEnd[Mixer.channelSampleNumber[channel]] - samplePointer + 1;
if (toRead > (uint32_t)fatBufferSize) toRead = fatBufferSize;
if (!file->seek(samplePointer, SEEK_SET)) {
stop();
@@ -797,30 +808,55 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])
current = FatBuffer.channels[channel][(samplePointer - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];
next = FatBuffer.channels[channel][(samplePointer + 1 - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];
// preserve a few more bits from sample interpolation, by upscaling input values.
// This does (slightly) reduce quantization noise in higher frequencies, typically above 8kHz.
// Actually we could could even gain more bits, I was just not sure if more bits would cause overflows in other conputations.
int16_t current16 = (int16_t) current << 2;
int16_t next16 = (int16_t) next << 2;
out = current16;
out = current;
// Integer linear interpolation - only works correctly in 16bit
out += (next16 - current16) * (Mixer.channelSampleOffset[channel] & ((1 << FIXED_DIVIDER) - 1)) >> FIXED_DIVIDER;
// Integer linear interpolation
out += (next - current) * (Mixer.channelSampleOffset[channel] & ((1 << DIVIDER) - 1)) >> DIVIDER;
// Upscale to BITDEPTH
out <<= BITDEPTH - 8;
// Upscale to BITDEPTH, considering the we already gained two bits in the previous step
out32 = (int32_t)out << (BITDEPTH - 10);
// Channel volume
out = out * Mixer.channelVolume[channel] >> 6;
out32 = out32 * Mixer.channelVolume[channel] >> 6;
// Channel panning
sumL += out * min(128 - Mixer.channelPanning[channel], 64) >> 6;
sumR += out * min(Mixer.channelPanning[channel], 64) >> 6;
sumL += out32 * min(128 - Mixer.channelPanning[channel], 64) >> 6;
sumR += out32 * min(Mixer.channelPanning[channel], 64) >> 6;
}
// Downscale to BITDEPTH
sumL /= Mod.numberOfChannels;
sumR /= Mod.numberOfChannels;
// Downscale to BITDEPTH - a bit faster because the compiler can replaced division by constants with proper "right shift" + correct handling of sign bit
if (Mod.numberOfChannels <= 4) {
// up to 4 channels
sumL /= 4;
sumR /= 4;
} else {
if (Mod.numberOfChannels <= 6) {
// 5 or 6 channels - pre-multiply be 1.5, then divide by 8 -> same as division by 6
sumL = (sumL + (sumL/2)) / 8;
sumR = (sumR + (sumR/2)) / 8;
} else {
// 7,8, or more channels
sumL /= 8;
sumR /= 8;
}
}
// Fill the sound buffer with unsigned values
sample[AudioOutput::LEFTCHANNEL] = sumL + (1 << (BITDEPTH - 1));
sample[AudioOutput::RIGHTCHANNEL] = sumR + (1 << (BITDEPTH - 1));
// clip samples to 16bit (with saturation in case of overflow)
if(sumL <= INT16_MIN) sumL = INT16_MIN;
else if (sumL >= INT16_MAX) sumL = INT16_MAX;
if(sumR <= INT16_MIN) sumR = INT16_MIN;
else if (sumR >= INT16_MAX) sumR = INT16_MAX;
// Fill the sound buffer with signed values
sample[AudioOutput::LEFTCHANNEL] = sumL;
sample[AudioOutput::RIGHTCHANNEL] = sumR;
}
bool AudioGeneratorMOD::LoadMOD()

View File

@@ -53,19 +53,25 @@ class AudioGeneratorMOD : public AudioGenerator
protected:
int mixerTick;
enum {BITDEPTH = 15};
enum {BITDEPTH = 16};
int sampleRate;
int fatBufferSize; //(6*1024) // File system buffers per-CHANNEL (i.e. total mem required is 4 * FATBUFFERSIZE)
enum {DIVIDER = 10}; // Fixed-point mantissa used for integer arithmetic
enum {FIXED_DIVIDER = 10}; // Fixed-point mantissa used for integer arithmetic
int stereoSeparation; //STEREOSEPARATION = 32; // 0 (max) to 64 (mono)
bool usePAL;
// Hz = 7093789 / (amigaPeriod * 2) for PAL
// Hz = 7159091 / (amigaPeriod * 2) for NTSC
int AMIGA;
void UpdateAmiga() { AMIGA = ((usePAL?7159091:7093789) / 2 / sampleRate << DIVIDER); }
void UpdateAmiga() { AMIGA = ((usePAL?7159091:7093789) / 2 / sampleRate << FIXED_DIVIDER); }
#ifdef ESP8266 // Not sure if C3/C2 have RAM constraints, maybe add them here?
// support max 4 channels
enum {ROWS = 64, SAMPLES = 31, CHANNELS = 4, NONOTE = 0xFFFF, NONOTE8 = 0xff };
#else
// support max 8 channels
enum {ROWS = 64, SAMPLES = 31, CHANNELS = 8, NONOTE = 0xFFFF, NONOTE8 = 0xff };
#endif
typedef struct Sample {
uint16_t length;

View File

@@ -41,7 +41,10 @@ AudioGeneratorRTTTL::~AudioGeneratorRTTTL()
bool AudioGeneratorRTTTL::stop()
{
if (!running) return true;
if (!file || !output)
{
return false;
}
running = false;
output->stop();
return file->close();

View File

@@ -1,7 +1,7 @@
/*
AudioOutputI2S
Base class for I2S interface port
Copyright (C) 2017 Earle F. Philhower, III
This program is free software: you can redistribute it and/or modify
@@ -21,7 +21,9 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#elif defined(ARDUINO_ARCH_RP2040) || defined(ESP8266)
#elif defined(ARDUINO_ARCH_RP2040) || ARDUINO_ESP8266_MAJOR >= 3
#include <I2S.h>
#elif ARDUINO_ESP8266_MAJOR < 3
#include <i2s.h>
#endif
#include "AudioOutputI2S.h"
@@ -40,6 +42,7 @@ AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int
//set defaults
mono = false;
lsb_justified = false;
bps = 16;
channels = 2;
hertz = 44100;
@@ -49,37 +52,6 @@ AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int
SetGain(1.0);
}
bool AudioOutputI2S::SetPinout()
{
#ifdef ESP32
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
return false; // Not allowed
i2s_pin_config_t pins = {
.bck_io_num = bclkPin,
.ws_io_num = wclkPin,
.data_out_num = doutPin,
.data_in_num = I2S_PIN_NO_CHANGE};
i2s_set_pin((i2s_port_t)portNo, &pins);
return true;
#else
(void)bclkPin;
(void)wclkPin;
(void)doutPin;
return false;
#endif
}
bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout)
{
bclkPin = bclk;
wclkPin = wclk;
doutPin = dout;
if (i2sOn)
return SetPinout();
return true;
}
#elif defined(ARDUINO_ARCH_RP2040)
AudioOutputI2S::AudioOutputI2S(long sampleRate, pin_size_t sck, pin_size_t data) {
i2sOn = false;
@@ -109,6 +81,38 @@ AudioOutputI2S::~AudioOutputI2S()
i2sOn = false;
}
bool AudioOutputI2S::SetPinout()
{
#ifdef ESP32
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
return false; // Not allowed
i2s_pin_config_t pins = {
.mck_io_num = 0, // Unused
.bck_io_num = bclkPin,
.ws_io_num = wclkPin,
.data_out_num = doutPin,
.data_in_num = I2S_PIN_NO_CHANGE};
i2s_set_pin((i2s_port_t)portNo, &pins);
return true;
#else
(void)bclkPin;
(void)wclkPin;
(void)doutPin;
return false;
#endif
}
bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout)
{
bclkPin = bclk;
wclkPin = wclk;
doutPin = dout;
if (i2sOn)
return SetPinout();
return true;
}
bool AudioOutputI2S::SetRate(int hz)
{
// TODO - have a list of allowable rates from constructor, check them
@@ -120,7 +124,7 @@ bool AudioOutputI2S::SetRate(int hz)
#elif defined(ESP8266)
i2s_set_rate(AdjustI2SRate(hz));
#elif defined(ARDUINO_ARCH_RP2040)
I2S.setFrequency(hz);
i2s.setFrequency(hz);
#endif
}
return true;
@@ -146,6 +150,12 @@ bool AudioOutputI2S::SetOutputModeMono(bool mono)
return true;
}
bool AudioOutputI2S::SetLsbJustified(bool lsbJustified)
{
this->lsb_justified = lsbJustified;
return true;
}
bool AudioOutputI2S::begin(bool txDAC)
{
#ifdef ESP32
@@ -166,17 +176,45 @@ bool AudioOutputI2S::begin(bool txDAC)
i2s_mode_t mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
if (output_mode == INTERNAL_DAC)
{
#if CONFIG_IDF_TARGET_ESP32
mode = (i2s_mode_t)(mode | I2S_MODE_DAC_BUILT_IN);
#else
return false;
#endif
}
else if (output_mode == INTERNAL_PDM)
{
#if CONFIG_IDF_TARGET_ESP32
mode = (i2s_mode_t)(mode | I2S_MODE_PDM);
#else
return false;
#endif
}
i2s_comm_format_t comm_fmt = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB);
i2s_comm_format_t comm_fmt;
if (output_mode == INTERNAL_DAC)
{
comm_fmt = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
comm_fmt = (i2s_comm_format_t) I2S_COMM_FORMAT_STAND_MSB;
#else
comm_fmt = (i2s_comm_format_t) I2S_COMM_FORMAT_I2S_MSB;
#endif
}
else if (lsb_justified)
{
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
comm_fmt = (i2s_comm_format_t) I2S_COMM_FORMAT_STAND_MSB;
#else
comm_fmt = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB);
#endif
}
else
{
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
comm_fmt = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S);
#else
comm_fmt = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB);
#endif
}
i2s_config_t i2s_config_dac = {
@@ -187,8 +225,12 @@ bool AudioOutputI2S::begin(bool txDAC)
.communication_format = comm_fmt,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // lowest interrupt priority
.dma_buf_count = dma_buf_count,
.dma_buf_len = 64,
.use_apll = use_apll // Use audio PLL
.dma_buf_len = 128,
.use_apll = use_apll, // Use audio PLL
.tx_desc_auto_clear = true, // Silence on underflow
.fixed_mclk = 0, // Unused
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // Unused
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT // Use bits per sample
};
audioLogger->printf("+%d %p\n", portNo, &i2s_config_dac);
if (i2s_driver_install((i2s_port_t)portNo, &i2s_config_dac, 0, NULL) != ESP_OK)
@@ -197,8 +239,12 @@ bool AudioOutputI2S::begin(bool txDAC)
}
if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
{
#if CONFIG_IDF_TARGET_ESP32
i2s_set_pin((i2s_port_t)portNo, NULL);
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
#else
return false;
#endif
}
else
{
@@ -229,9 +275,9 @@ bool AudioOutputI2S::begin(bool txDAC)
#elif defined(ARDUINO_ARCH_RP2040)
(void)txDAC;
if (!i2sOn) {
I2S.setBCLK(bclkPin);
I2S.setDOUT(doutPin);
I2S.begin(hertz);
i2s.setBCLK(bclkPin);
i2s.setDATA(doutPin);
i2s.begin(hertz);
}
#endif
i2sOn = true;
@@ -263,22 +309,24 @@ bool AudioOutputI2S::ConsumeSample(int16_t sample[2])
{
int16_t l = Amplify(ms[LEFTCHANNEL]) + 0x8000;
int16_t r = Amplify(ms[RIGHTCHANNEL]) + 0x8000;
s32 = (r << 16) | (l & 0xffff);
s32 = ((r & 0xffff) << 16) | (l & 0xffff);
}
else
{
s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
}
// Deprecated. Use i2s_write
//"i2s_write_bytes" has been removed in the ESP32 Arduino 2.0.0, use "i2s_write" instead.
// return i2s_write_bytes((i2s_port_t)portNo, (const char *)&s32, sizeof(uint32_t), 0);
size_t bytes_written;
i2s_write((i2s_port_t)portNo, (const char*)&s32, sizeof(uint32_t), &bytes_written, 0);
return bytes_written;
size_t i2s_bytes_written;
i2s_write((i2s_port_t)portNo, (const char*)&s32, sizeof(uint32_t), &i2s_bytes_written, 0);
return i2s_bytes_written;
#elif defined(ESP8266)
uint32_t s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
return i2s_write_sample_nb(s32); // If we can't store it, return false. OTW true
#elif defined(ARDUINO_ARCH_RP2040)
return !!I2S.write((void*)ms, 4);
uint32_t s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
return !!i2s.write((int32_t)s32, false);
#endif
}
@@ -286,7 +334,7 @@ void AudioOutputI2S::flush()
{
#ifdef ESP32
// makes sure that all stored DMA samples are consumed / played
int buffersize = 64 * this->dma_buf_count;
int buffersize = 128 * this->dma_buf_count;
int16_t samples[2] = {0x0, 0x0};
for (int i = 0; i < buffersize; i++)
{
@@ -296,7 +344,7 @@ void AudioOutputI2S::flush()
}
}
#elif defined(ARDUINO_ARCH_RP2040)
I2S.flush();
i2s.flush();
#endif
}
@@ -308,8 +356,8 @@ bool AudioOutputI2S::stop()
#ifdef ESP32
i2s_zero_dma_buffer((i2s_port_t)portNo);
#elif defined(ARDUINO_ARCH_RP2040)
I2S.end();
i2s.end();
#endif
i2sOn = false;
return true;
}
}

View File

@@ -22,17 +22,22 @@
#include "AudioOutput.h"
#if defined(ARDUINO_ARCH_RP2040)
#include <Arduino.h>
#include <I2S.h>
#endif
class AudioOutputI2S : public AudioOutput
{
public:
#if defined(ESP32) || defined(ESP8266)
AudioOutputI2S(int port=0, int output_mode=EXTERNAL_I2S, int dma_buf_count = 8, int use_apll=APLL_DISABLE);
bool SetPinout(int bclkPin, int wclkPin, int doutPin);
enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
#elif defined(ARDUINO_ARCH_RP2040)
AudioOutputI2S(long sampleRate = 44100, pin_size_t sck = 26, pin_size_t data = 28);
#endif
bool SetPinout(int bclkPin, int wclkPin, int doutPin);
virtual ~AudioOutputI2S() override;
virtual bool SetRate(int hz) override;
virtual bool SetBitsPerSample(int bits) override;
@@ -44,6 +49,7 @@ class AudioOutputI2S : public AudioOutput
bool begin(bool txDAC);
bool SetOutputModeMono(bool mono); // Force mono output no matter the input
bool SetLsbJustified(bool lsbJustified); // Allow supporting non-I2S chips, e.g. PT8211
protected:
bool SetPinout();
@@ -51,6 +57,7 @@ class AudioOutputI2S : public AudioOutput
uint8_t portNo;
int output_mode;
bool mono;
int lsb_justified;
bool i2sOn;
int dma_buf_count;
int use_apll;
@@ -61,4 +68,8 @@ class AudioOutputI2S : public AudioOutput
uint8_t bclkPin;
uint8_t wclkPin;
uint8_t doutPin;
#if defined(ARDUINO_ARCH_RP2040)
I2S i2s;
#endif
};

View File

@@ -1,7 +1,7 @@
/*
AudioOutputI2SNoDAC
Audio player using SW delta-sigma to generate "analog" on I2S data
Copyright (C) 2017 Earle F. Philhower, III
This program is free software: you can redistribute it and/or modify
@@ -21,12 +21,33 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#elif defined(ARDUINO_ARCH_RP2040) || defined(ESP8266)
#elif defined(ARDUINO_ARCH_RP2040) || ARDUINO_ESP8266_MAJOR >= 3
#include <I2S.h>
#elif ARDUINO_ESP8266_MAJOR < 3
#include <i2s.h>
#endif
#include "AudioOutputI2SNoDAC.h"
#if defined(ARDUINO_ARCH_RP2040)
//
// Create an alternate constructor for the RP2040. The AudioOutputI2S has an alternate
// constructor for the RP2040, so the code was passing port to the sampleRate and false to sck.
//
// AudioOutputI2S(long sampleRate = 44100, pin_size_t sck = 26, pin_size_t data = 28);
//
// So this new constructor adds the ability to pass both port and sck to the underlying class, but
// uses the same defaults in the AudioOutputI2S constructor.
//
AudioOutputI2SNoDAC::AudioOutputI2SNoDAC(int port, int sck) : AudioOutputI2S(44100, sck, port)
{
SetOversampling(32);
lastSamp = 0;
cumErr = 0;
}
#else
AudioOutputI2SNoDAC::AudioOutputI2SNoDAC(int port) : AudioOutputI2S(port, false)
{
SetOversampling(32);
@@ -36,7 +57,10 @@ AudioOutputI2SNoDAC::AudioOutputI2SNoDAC(int port) : AudioOutputI2S(port, false)
WRITE_PERI_REG(PERIPHS_IO_MUX_MTDO_U, orig_bck);
WRITE_PERI_REG(PERIPHS_IO_MUX_GPIO2_U, orig_ws);
#endif
}
#endif
AudioOutputI2SNoDAC::~AudioOutputI2SNoDAC()
{
@@ -66,7 +90,7 @@ void AudioOutputI2SNoDAC::DeltaSigma(int16_t sample[2], uint32_t dsBuff[8])
for (int j = 0; j < oversample32; j++) {
uint32_t bits = 0; // The bits we convert the sample into, MSB to go on the wire first
for (int i = 32; i > 0; i--) {
bits = bits << 1;
if (cumErr < 0) {
@@ -95,12 +119,11 @@ bool AudioOutputI2SNoDAC::ConsumeSample(int16_t sample[2])
// Either send complete pulse stream or nothing
#ifdef ESP32
// Deprecated. Use i2s_write
// if (!i2s_write_bytes((i2s_port_t)portNo, (const char *)dsBuff, sizeof(uint32_t) * (oversample/32), 0))
size_t bytes_written;
i2s_write((i2s_port_t)portNo, (const char *)dsBuff, sizeof(uint32_t) * (oversample/32), &bytes_written, 0);
if (!bytes_written)
size_t i2s_bytes_written;
i2s_write((i2s_port_t)portNo, (const char *)dsBuff, sizeof(uint32_t) * (oversample/32), &i2s_bytes_written, 0);
if (!i2s_bytes_written){
return false;
}
#elif defined(ESP8266)
if (!i2s_write_sample_nb(dsBuff[0])) return false; // No room at the inn
// At this point we've sent in first of possibly 8 32-bits, need to send
@@ -108,10 +131,9 @@ bool AudioOutputI2SNoDAC::ConsumeSample(int16_t sample[2])
for (int i = 32; i < oversample; i+=32)
i2s_write_sample( dsBuff[i / 32]);
#elif defined(ARDUINO_ARCH_RP2040)
int16_t *p = (int16_t *) dsBuff;
for (int i = 0; i < oversample / 16; i++) {
I2S.write(*(p++));
for (int i = 0; i < oversample / 32; i++) {
i2s.write((int32_t)dsBuff[i], true);
}
#endif
return true;
}
}

View File

@@ -25,7 +25,16 @@
class AudioOutputI2SNoDAC : public AudioOutputI2S
{
public:
//
// Define a different constructor for the RP2040, as this class calls the constructor
// of the AudioOutputI2S which has an alternate constructor for the RP2040
//
#if defined(ARDUINO_ARCH_RP2040)
AudioOutputI2SNoDAC(int port = 28,int sck = 26);
#else
AudioOutputI2SNoDAC(int port = 0);
#endif
virtual ~AudioOutputI2SNoDAC() override;
virtual bool begin() override { return AudioOutputI2S::begin(false); }
virtual bool ConsumeSample(int16_t sample[2]) override;

View File

@@ -3,7 +3,7 @@
S/PDIF output via I2S
Needs transciever from CMOS level to either optical or coaxial interface
Needs transceiver from CMOS level to either optical or coaxial interface
See: https://www.epanorama.net/documents/audio/spdif.html
Original idea and sources:
@@ -94,11 +94,19 @@ AudioOutputSPDIF::AudioOutputSPDIF(int dout_pin, int port, int dma_buf_count)
.sample_rate = 88200, // 2 x sampling_rate
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // 32bit words
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // Right than left
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
#else
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
#endif
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // lowest interrupt priority
.dma_buf_count = dma_buf_count,
.dma_buf_len = DMA_BUF_SIZE_DEFAULT, // bigger buffers, reduces interrupts
.use_apll = true // Audio PLL is needed for low clock jitter
.use_apll = true, // Audio PLL is needed for low clock jitter
.tx_desc_auto_clear = true, // Silence on underflow
.fixed_mclk = 0, // Unused
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // Unused
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT // Use bits per sample
};
if (i2s_driver_install((i2s_port_t)portNo, &i2s_config_spdif, 0, NULL) != ESP_OK) {
audioLogger->println(F("ERROR: Unable to install I2S drivers"));
@@ -143,6 +151,7 @@ bool AudioOutputSPDIF::SetPinout(int bclk, int wclk, int dout)
{
#if defined(ESP32)
i2s_pin_config_t pins = {
.mck_io_num = 0, // unused
.bck_io_num = bclk,
.ws_io_num = wclk,
.data_out_num = dout,
@@ -265,7 +274,7 @@ bool AudioOutputSPDIF::ConsumeSample(int16_t sample[2])
#if defined(ESP32)
// Assume DMA buffers are multiples of 16 bytes. Either we write all bytes or none.
uint32_t bytes_written;
size_t bytes_written;
esp_err_t ret = i2s_write((i2s_port_t)portNo, (const char*)&buf, 8 * channels, &bytes_written, 0);
// If we didn't write all bytes, return false early and do not increment frame_num
if ((ret != ESP_OK) || (bytes_written != (8 * channels))) return false;

View File

@@ -3,7 +3,7 @@
S/PDIF output via I2S
Needs transciever from CMOS level to either optical or coaxial interface
Needs transceiver from CMOS level to either optical or coaxial interface
See: https://www.epanorama.net/documents/audio/spdif.html
Original idea and sources:

View File

@@ -18,12 +18,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(ARDUINO_ARCH_RP2040)
#include <Arduino.h>
#include <FS.h>
#ifdef ESP32
#include "SPIFFS.h"
#endif
// Yes, I know SPIFFS is deprecated
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include "AudioOutputSPIFFSWAV.h"
static const uint8_t wavHeaderTemplate[] PROGMEM = { // Hardcoded simple WAV header with 0xffffffff lengths all around
@@ -110,4 +116,6 @@ bool AudioOutputSPIFFSWAV::stop()
f.close();
return true;
}
#endif

View File

@@ -21,6 +21,8 @@
#ifndef _AUDIOOUTPUTSPIFFSWAV_H
#define _AUDIOOUTPUTSPIFFSWAV_H
#if !defined(ARDUINO_ARCH_RP2040)
#include <Arduino.h>
#include <FS.h>
@@ -43,3 +45,4 @@ class AudioOutputSPIFFSWAV : public AudioOutput
#endif
#endif

View File

@@ -54,6 +54,11 @@ bool AudioOutputSTDIO::begin()
bool AudioOutputSTDIO::ConsumeSample(int16_t sample[2])
{
static int avail = 100;
if (!(--avail)) {
avail = 100;
return false;
}
for (int i=0; i<channels; i++) {
if (bps == 8) {
uint8_t l = sample[i] & 0xff;

View File

@@ -18,7 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef ESP32
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#include "AudioOutputULP.h"
#include <esp32/ulp.h>

View File

@@ -3,7 +3,7 @@
ESP8266Audio I2S Minimal driver
Most of this code is taken and reworked from ESP8266 Arduino core,
which itsef is reworked from Espessif's I2S examples.
which itself is reworked from Espessif's I2S examples.
Original code is licensed under LGPL 2.1 or above
Reasons for rewrite:
@@ -196,7 +196,7 @@ void SinglePinI2SDriver::setDividers(uint8_t div1, uint8_t div2)
// Ensure dividers fit in bit fields
div1 &= I2SBDM;
div2 &= I2SCDM;
// trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers
// trans master(active low), recv master(active_low), !bits mod(==16 bits/channel), clear clock dividers
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
// I2SRF = Send/recv right channel first
// I2SMR = MSB recv/xmit first
@@ -249,7 +249,7 @@ void SinglePinI2SDriver::startI2S()
I2SFC |= I2SDE; // Enable DMA
// I2STXCMM, I2SRXCMM=0 => Dual channel mode, RX/TX CHAN_MOD=0
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM));
// Set dividers to something resonable
// Set dividers to something reasonable
currentRate = 0;
setRate(44100);
// Start I2S peripheral
@@ -280,4 +280,4 @@ int SinglePinI2SDriver::getUnderflowCount()
// Global instance
SinglePinI2SDriver I2SDriver;
#endif // defined(ESP8266)
#endif // defined(ESP8266)

View File

@@ -2199,14 +2199,14 @@ FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder)
* Three kinds of things can go wrong when reading the frame header:
* 1) We may have sync'ed incorrectly and not landed on a frame header.
* If we don't find a sync code, it can end up looking like we read
* a valid but unparseable header, until getting to the frame header
* a valid but unparsable header, until getting to the frame header
* CRC. Even then we could get a false positive on the CRC.
* 2) We may have sync'ed correctly but on an unparseable frame (from a
* 2) We may have sync'ed correctly but on an unparsable frame (from a
* future encoder).
* 3) We may be on a damaged frame which appears valid but unparseable.
* 3) We may be on a damaged frame which appears valid but unparsable.
*
* For all these reasons, we try and read a complete frame header as
* long as it seems valid, even if unparseable, up until the frame
* long as it seems valid, even if unparsable, up until the frame
* header CRC.
*/
@@ -2783,7 +2783,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_
if(rice_parameter < pesc) {
partitioned_rice_contents->raw_bits[partition] = 0;
u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order;
if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter))
if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, (int *)(residual + sample), u, rice_parameter))
return false; /* read_callback_ sets the state for us */
sample += u;
}
@@ -2792,7 +2792,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_
return false; /* read_callback_ sets the state for us */
partitioned_rice_contents->raw_bits[partition] = rice_parameter;
for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) {
if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter))
if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, (FLAC__int32 *)&i, rice_parameter))
return false; /* read_callback_ sets the state for us */
residual[sample] = i;
}
@@ -2839,7 +2839,7 @@ FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data)
* FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its
* unparseable_frame_count. But there is a remote possibility
* that it is properly synced at such a "future-codec frame",
* so to make sure, we wait to see many "unparseable" errors in
* so to make sure, we wait to see many "unparsable" errors in
* a row before bailing out.
*/
if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) {
@@ -3146,7 +3146,7 @@ FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 s
return false;
}
/* Now we need to get a frame. First we need to reset our
* unparseable_frame_count; if we get too many unparseable
* unparseable_frame_count; if we get too many unparsable
* frames in a row, the read callback will return
* FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing
* FLAC__stream_decoder_process_single() to return false.

View File

@@ -558,7 +558,7 @@ static __inline int CLZ(int x)
typedef union _U64 {
Word64 w64;
struct {
#ifdef __XTENSA__
#if defined(__XTENSA__) || defined (__riscv)
unsigned int lo32;
signed int hi32;
#else

View File

@@ -222,7 +222,8 @@ static void PostMultiply64(int *fft1, int nSampsOut)
* Notes: this is carefully written to be efficient on ARM
* use the assembly code version in sbrqmfak.s when building for ARM!
**************************************************************************************/
#if (defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__))
//TODO - ADD IN .S SOURCES SOMEHOW
#if 0 //(defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__))
#ifdef __cplusplus
extern "C"
#endif
@@ -395,7 +396,7 @@ int QMFAnalysis(int *inbuf, int *delay, int *XBuf, int fBitsIn, int *delayIdx, i
* Notes: this is carefully written to be efficient on ARM
* use the assembly code version in sbrqmfsk.s when building for ARM!
**************************************************************************************/
#if (defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__))
#if 0 //(defined (__arm) && defined (__ARMCC_VERSION)) || (defined (_WIN32) && defined (_WIN32_WCE) && defined (ARM)) || (defined(__GNUC__) && defined(__arm__))
#ifdef __cplusplus
extern "C"
#endif

View File

@@ -201,7 +201,7 @@ and 3, above.
4.2 Compatible Source Licenses. Software modules that have been independently
developed without any use of Covered Code and which contain no portion of the
Covered Code, Modifications or other Derivative Works, but are used or combined
in any way wtih the Covered Code or any Derivative Work to form a larger
in any way with the Covered Code or any Derivative Work to form a larger
Derivative Work, are exempt from the conditions described in Section 4.1 but
only to the extent that: the software module, including any software that is
linked to, integrated with, or part of the same applications as, the software

View File

@@ -40,7 +40,7 @@
*
* assembly.h - assembly language functions and prototypes for supported platforms
*
* - inline rountines with access to 64-bit multiply results
* - inline routines with access to 64-bit multiply results
* - x86 (_WIN32) and ARM (ARM_ADS, _WIN32_WCE) versions included
* - some inline functions are mix of asm and C for speed
* - some functions are in native asm files, so only the prototype is given here
@@ -241,7 +241,7 @@ static __inline int MULSHIFT32(int x, int y)
static __inline int FASTABS(int x)
{
int t=0; /*Really is not necessary to initialiaze only to avoid warning*/
int t=0; /*Really is not necessary to initialize only to avoid warning*/
__asm {
eor t, x, x, asr #31

View File

@@ -74,7 +74,7 @@ static const char SFLenTab[16][2] = {
* Return: none
*
* Notes: set order of short blocks to s[band][window] instead of s[window][band]
* so that we index through consectutive memory locations when unpacking
* so that we index through consecutive memory locations when unpacking
* (make sure dequantizer follows same convention)
* Illegal Intensity Position = 7 (always) for MPEG1 scale factors
**************************************************************************************/

View File

@@ -20,6 +20,7 @@
*/
#pragma GCC optimize ("O3")
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#include <pgmspace.h>
# include "config.h"

View File

@@ -7,7 +7,7 @@
Ogg project codecs use the Ogg bitstream format to arrange the raw,
compressed bitstream into a more robust, useful form. For example,
the Ogg bitstream makes seeking, time stamping and error recovery
possible, as well as mixing several sepearate, concurrent media
possible, as well as mixing several separate, concurrent media
streams into a single physical bitstream.
## What's here ##
@@ -18,7 +18,7 @@ use with Ogg bitstreams.
Directory:
- `src` The source for libogg, a BSD-license inplementation of the public domain Ogg bitstream format
- `src` The source for libogg, a BSD-license implementation of the public domain Ogg bitstream format
- `include` Library API headers

View File

@@ -28,7 +28,7 @@ Even with the caching, it was found that SPIFFS, while having great
functionality, was horrbly slow. So I wrote a new "faster" ROM filesystem
called, surprisingly, FastROMFilesystem.
https://github.com/earlephilhower/ESP8266FastROMFS
If you are getting choppy playback, try this new filesytem or using a SD
If you are getting choppy playback, try this new filesystem or using a SD
card (not tested by myself, but it'd be hard top be slower than SPIFFS).
Simply going from SPIFFS to FastROMFilesystem took my testing of
FURELISE.MID and 1MGM.SF2 from 0.5x realtime to 2.5x (i.e. from unusable

View File

@@ -104,7 +104,7 @@ TSFDEF tsf* tsf_load(struct tsf_stream* stream);
// Free the memory related to this tsf instance
TSFDEF void tsf_close(tsf* f);
// Stop all playing notes immediatly and reset all channel parameters
// Stop all playing notes immediately and reset all channel parameters
TSFDEF void tsf_reset(tsf* f);
// Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont
@@ -201,7 +201,7 @@ TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning);
TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel);
TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key);
TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release
TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediatly
TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediately
// Apply a MIDI control change to the channel (not all controllers are supported!)
TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value);

View File

@@ -1422,7 +1422,7 @@ static int op_open_seekable2_impl(OggOpusFile *_of){
_of->end=sr[0].offset+sr[0].size;
if(OP_UNLIKELY(_of->end<data_offset)){free(sr); return OP_EBADLINK;}
/*Now enumerate the bitstream structure.*/
ret = op_bisect_forward_serialno(_of,data_offset,sr,sizeof(sr)/sizeof(*sr),
ret = op_bisect_forward_serialno(_of,data_offset,sr,64,
&_of->serialnos,&_of->nserialnos,&_of->cserialnos);
free(sr);
return ret;

View File

@@ -729,7 +729,7 @@ struct OpusServerInfo{
/**The software used by the origin server (Server).
This is <code>NULL</code> if there was no <code>Server</code> header.*/
char *server;
/**The media type of the entity sent to the recepient (Content-Type).
/**The media type of the entity sent to the recipient (Content-Type).
This is <code>NULL</code> if there was no <code>Content-Type</code>
header.*/
char *content_type;
@@ -1436,7 +1436,7 @@ void op_free(OggOpusFile *_of);
Some of these functions may be used successfully on the partially open
streams returned by op_test_callbacks() or one of the associated
convenience functions.
Their documention will indicate so explicitly.*/
Their documentation will indicate so explicitly.*/
/*@{*/
/**Returns whether or not the stream being read is seekable.

View File

@@ -247,6 +247,10 @@ class ESP8266SPIRAM {
#include <SPI.h>
#ifndef SPECIAL
#define UNDEF_SPECIAL
#define SPECIAL 0xf0
#endif
class ESP8266SPIRAM {
private:
uint8_t csPin;
@@ -332,9 +336,9 @@ class ESP8266SPIRAM {
}
digitalWrite(csPin, HIGH);
pinMode(sck, SPECIAL);
pinMode(miso, SPECIAL);
pinMode(mosi, SPECIAL);
pinMode(sck, OUTPUT);
pinMode(miso, INPUT);
pinMode(mosi, OUTPUT);
pinMode(csPin, OUTPUT);
// Enable streaming read/write mode
@@ -364,4 +368,4 @@ class ESP8266SPIRAM {
#endif // ESP32
#endif
#endif