update to 1.9.7
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -18,10 +18,8 @@
|
||||
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>
|
||||
@@ -37,7 +35,5 @@ class AudioFileSourceLittleFS : public AudioFileSourceFS
|
||||
// Others are inherited from base
|
||||
};
|
||||
|
||||
//#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,8 +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
|
||||
@@ -22,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"
|
||||
@@ -41,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;
|
||||
@@ -50,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;
|
||||
@@ -110,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
|
||||
@@ -121,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;
|
||||
@@ -147,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
|
||||
@@ -167,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 = {
|
||||
@@ -188,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)
|
||||
@@ -198,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
|
||||
{
|
||||
@@ -230,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;
|
||||
@@ -264,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
|
||||
}
|
||||
|
||||
@@ -287,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++)
|
||||
{
|
||||
@@ -297,7 +344,7 @@ void AudioOutputI2S::flush()
|
||||
}
|
||||
}
|
||||
#elif defined(ARDUINO_ARCH_RP2040)
|
||||
I2S.flush();
|
||||
i2s.flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -309,338 +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;
|
||||
}
|
||||
// /*
|
||||
// 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
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// */
|
||||
|
||||
// #include <Arduino.h>
|
||||
// #ifdef ESP32
|
||||
// #include "driver/i2s.h"
|
||||
// #if defined ESP_ARDUINO_VERSION_VAL
|
||||
// #if (ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0)
|
||||
// #include "soc/soc_caps.h"
|
||||
// #define ESP_V2
|
||||
// #define I2S_FORMAT (I2S_COMM_FORMAT_STAND_I2S)
|
||||
// #endif
|
||||
// #else
|
||||
// #define I2S_FORMAT (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB)
|
||||
// #endif
|
||||
// #elif defined(ARDUINO_ARCH_RP2040) || defined(ESP8266)
|
||||
// #include <i2s.h>
|
||||
// #endif
|
||||
// #include "AudioOutputI2S.h"
|
||||
|
||||
// #if defined(ESP32) || defined(ESP8266)
|
||||
// AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int use_apll)
|
||||
// {
|
||||
// this->portNo = port;
|
||||
// this->i2sOn = false;
|
||||
// this->dma_buf_count = dma_buf_count;
|
||||
// if (output_mode != EXTERNAL_I2S && output_mode != INTERNAL_DAC && output_mode != INTERNAL_PDM) {
|
||||
// output_mode = EXTERNAL_I2S;
|
||||
// }
|
||||
// this->output_mode = output_mode;
|
||||
// this->use_apll = use_apll;
|
||||
|
||||
// //set defaults
|
||||
// mono = false;
|
||||
// bps = 16;
|
||||
// channels = 2;
|
||||
// hertz = 44100;
|
||||
// bclkPin = 26;
|
||||
// wclkPin = 25;
|
||||
// doutPin = 22;
|
||||
// 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;
|
||||
// mono = false;
|
||||
// bps = 16;
|
||||
// channels = 2;
|
||||
// hertz = sampleRate;
|
||||
// bclkPin = sck;
|
||||
// doutPin = data;
|
||||
// SetGain(1.0);
|
||||
// }
|
||||
// #endif
|
||||
|
||||
// AudioOutputI2S::~AudioOutputI2S()
|
||||
// {
|
||||
// #ifdef ESP32
|
||||
// if (i2sOn) {
|
||||
// audioLogger->printf("UNINSTALL I2S\n");
|
||||
// i2s_driver_uninstall((i2s_port_t)portNo); //stop & destroy i2s driver
|
||||
// }
|
||||
// #elif defined(ESP8266)
|
||||
// if (i2sOn)
|
||||
// i2s_end();
|
||||
// #elif defined(ARDUINO_ARCH_RP2040)
|
||||
// stop();
|
||||
// #endif
|
||||
// i2sOn = false;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::SetRate(int hz)
|
||||
// {
|
||||
// // TODO - have a list of allowable rates from constructor, check them
|
||||
// this->hertz = hz;
|
||||
// if (i2sOn)
|
||||
// {
|
||||
// #ifdef ESP32
|
||||
// i2s_set_sample_rates((i2s_port_t)portNo, AdjustI2SRate(hz));
|
||||
// #elif defined(ESP8266)
|
||||
// i2s_set_rate(AdjustI2SRate(hz));
|
||||
// #elif defined(ARDUINO_ARCH_RP2040)
|
||||
// I2S.setFrequency(hz);
|
||||
// #endif
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::SetBitsPerSample(int bits)
|
||||
// {
|
||||
// if ( (bits != 16) && (bits != 8) ) return false;
|
||||
// this->bps = bits;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::SetChannels(int channels)
|
||||
// {
|
||||
// if ( (channels < 1) || (channels > 2) ) return false;
|
||||
// this->channels = channels;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::SetOutputModeMono(bool mono)
|
||||
// {
|
||||
// this->mono = mono;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::begin(bool txDAC)
|
||||
// {
|
||||
// #ifdef ESP32
|
||||
// if (!i2sOn)
|
||||
// {
|
||||
// if (use_apll == APLL_AUTO)
|
||||
// {
|
||||
// // don't use audio pll on buggy rev0 chips
|
||||
// use_apll = APLL_DISABLE;
|
||||
// esp_chip_info_t out_info;
|
||||
// esp_chip_info(&out_info);
|
||||
// if (out_info.revision > 0)
|
||||
// {
|
||||
// use_apll = APLL_ENABLE;
|
||||
// }
|
||||
// }
|
||||
|
||||
// i2s_mode_t mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
|
||||
// if (output_mode == INTERNAL_DAC)
|
||||
// {
|
||||
// #if SOC_I2S_SUPPORTS_DAC
|
||||
// mode = (i2s_mode_t)(mode | I2S_MODE_DAC_BUILT_IN);
|
||||
// #endif
|
||||
// }
|
||||
// else if (output_mode == INTERNAL_PDM)
|
||||
// {
|
||||
// mode = (i2s_mode_t)(mode | I2S_MODE_PDM);
|
||||
// }
|
||||
|
||||
// i2s_comm_format_t comm_fmt = (i2s_comm_format_t)(I2S_FORMAT);
|
||||
// if (output_mode == INTERNAL_DAC)
|
||||
// {
|
||||
// #if SOC_I2S_SUPPORTS_DAC
|
||||
// comm_fmt = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB;
|
||||
// #endif
|
||||
// }
|
||||
|
||||
// i2s_config_t i2s_config_dac = {
|
||||
// .mode = mode,
|
||||
// .sample_rate = 44100,
|
||||
// .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
// .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
||||
// .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
|
||||
// };
|
||||
// audioLogger->printf("+%d %p\n", portNo, &i2s_config_dac);
|
||||
// if (i2s_driver_install((i2s_port_t)portNo, &i2s_config_dac, 0, NULL) != ESP_OK)
|
||||
// {
|
||||
// audioLogger->println("ERROR: Unable to install I2S drives\n");
|
||||
// }
|
||||
// if (output_mode == INTERNAL_DAC || output_mode == INTERNAL_PDM)
|
||||
// {
|
||||
// #if SOC_I2S_SUPPORTS_DAC
|
||||
// i2s_set_pin((i2s_port_t)portNo, NULL);
|
||||
// i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
|
||||
// #endif
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// SetPinout();
|
||||
// }
|
||||
// i2s_zero_dma_buffer((i2s_port_t)portNo);
|
||||
// }
|
||||
// #elif defined(ESP8266)
|
||||
// (void)dma_buf_count;
|
||||
// (void)use_apll;
|
||||
// if (!i2sOn)
|
||||
// {
|
||||
// orig_bck = READ_PERI_REG(PERIPHS_IO_MUX_MTDO_U);
|
||||
// orig_ws = READ_PERI_REG(PERIPHS_IO_MUX_GPIO2_U);
|
||||
// #ifdef I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS
|
||||
// if (!i2s_rxtxdrive_begin(false, true, false, txDAC)) {
|
||||
// return false;
|
||||
// }
|
||||
// #else
|
||||
// if (!i2s_rxtx_begin(false, true)) {
|
||||
// return false;
|
||||
// }
|
||||
// if (!txDAC) {
|
||||
// audioLogger->printf_P(PSTR("I2SNoDAC: esp8266 arduino core should be upgraded to avoid conflicts with SPI\n"));
|
||||
// }
|
||||
// #endif
|
||||
// }
|
||||
// #elif defined(ARDUINO_ARCH_RP2040)
|
||||
// (void)txDAC;
|
||||
// if (!i2sOn) {
|
||||
// I2S.setBCLK(bclkPin);
|
||||
// I2S.setDOUT(doutPin);
|
||||
// I2S.begin(hertz);
|
||||
// }
|
||||
// #endif
|
||||
// i2sOn = true;
|
||||
// SetRate(hertz); // Default
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::ConsumeSample(int16_t sample[2])
|
||||
// {
|
||||
|
||||
// //return if we haven't called ::begin yet
|
||||
// if (!i2sOn)
|
||||
// return false;
|
||||
|
||||
// int16_t ms[2];
|
||||
|
||||
// ms[0] = sample[0];
|
||||
// ms[1] = sample[1];
|
||||
// MakeSampleStereo16( ms );
|
||||
|
||||
// if (this->mono) {
|
||||
// // Average the two samples and overwrite
|
||||
// int32_t ttl = ms[LEFTCHANNEL] + ms[RIGHTCHANNEL];
|
||||
// ms[LEFTCHANNEL] = ms[RIGHTCHANNEL] = (ttl>>1) & 0xffff;
|
||||
// }
|
||||
// #ifdef ESP32
|
||||
// uint32_t s32;
|
||||
// if (output_mode == INTERNAL_DAC)
|
||||
// {
|
||||
// int16_t l = Amplify(ms[LEFTCHANNEL]) + 0x8000;
|
||||
// int16_t r = Amplify(ms[RIGHTCHANNEL]) + 0x8000;
|
||||
// s32 = (r << 16) | (l & 0xffff);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// s32 = ((Amplify(ms[RIGHTCHANNEL])) << 16) | (Amplify(ms[LEFTCHANNEL]) & 0xffff);
|
||||
// }
|
||||
// // Deprecated. Use i2s_write
|
||||
// // 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;
|
||||
// #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);
|
||||
// #endif
|
||||
// }
|
||||
|
||||
// void AudioOutputI2S::flush()
|
||||
// {
|
||||
// #ifdef ESP32
|
||||
// // makes sure that all stored DMA samples are consumed / played
|
||||
// int buffersize = 64 * this->dma_buf_count;
|
||||
// int16_t samples[2] = {0x0, 0x0};
|
||||
// for (int i = 0; i < buffersize; i++)
|
||||
// {
|
||||
// while (!ConsumeSample(samples))
|
||||
// {
|
||||
// delay(10);
|
||||
// }
|
||||
// }
|
||||
// #elif defined(ARDUINO_ARCH_RP2040)
|
||||
// I2S.flush();
|
||||
// #endif
|
||||
// }
|
||||
|
||||
// bool AudioOutputI2S::stop()
|
||||
// {
|
||||
// if (!i2sOn)
|
||||
// return false;
|
||||
|
||||
// #ifdef ESP32
|
||||
// i2s_zero_dma_buffer((i2s_port_t)portNo);
|
||||
// #elif defined(ARDUINO_ARCH_RP2040)
|
||||
// I2S.end();
|
||||
// #endif
|
||||
// i2sOn = false;
|
||||
// return true;
|
||||
// }
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
**************************************************************************************/
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#pragma GCC optimize ("O3")
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
|
||||
#include <pgmspace.h>
|
||||
# include "config.h"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -332,9 +332,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 +364,4 @@ class ESP8266SPIRAM {
|
||||
#endif // ESP32
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user