updated Audio libs

This commit is contained in:
Jason2866 2021-12-27 14:24:58 +01:00 committed by GitHub
parent cb52c4cf35
commit e84124f035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 574 additions and 173 deletions

View File

@ -86,3 +86,27 @@ jobs:
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'
# Validate orthography
code-spell:
name: Check spelling
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Run codespell
uses: codespell-project/actions-codespell@master
with:
skip: ./src/libmad,./src/libhelix-aac,./src/libopus
ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans,inout,numer,hist

View File

@ -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.

View File

@ -8,7 +8,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).

View File

@ -25,6 +25,7 @@ void setup()
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
SPIFFS.begin();
Serial.println("Starting up...\n");
audioLogger = &Serial;

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

@ -0,0 +1,142 @@
#include <Arduino.h>
#ifdef 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();
}
}

View File

@ -14,7 +14,7 @@
"type": "git",
"url": "https://github.com/earlephilhower/ESP8266Audio"
},
"version": "1.9.2",
"version": "1.9.5",
"homepage": "https://github.com/earlephilhower/ESP8266Audio",
"frameworks": "Arduino",
"examples": [

View File

@ -1,5 +1,5 @@
name=ESP8266Audio
version=1.9.2
version=1.9.5
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

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

@ -49,6 +49,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

@ -21,8 +21,6 @@
#ifndef _AUDIOFILESOURCESPIFFS_H
#define _AUDIOFILESOURCESPIFFS_H
#ifndef ESP32 // No LittleFS there, yet
#include <Arduino.h>
#include <LittleFS.h>
@ -39,5 +37,3 @@ class AudioFileSourceLittleFS : public AudioFileSourceFS
#endif
#endif

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

@ -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
@ -172,7 +172,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 +331,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 +364,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;

View File

@ -124,7 +124,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;
@ -565,7 +565,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];
@ -757,12 +757,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]];
}
@ -801,7 +801,7 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])
out = current;
// Integer linear interpolation
out += (next - current) * (Mixer.channelSampleOffset[channel] & ((1 << DIVIDER) - 1)) >> DIVIDER;
out += (next - current) * (Mixer.channelSampleOffset[channel] & ((1 << FIXED_DIVIDER) - 1)) >> FIXED_DIVIDER;
// Upscale to BITDEPTH
out <<= BITDEPTH - 8;

View File

@ -56,14 +56,14 @@ class AudioGeneratorMOD : public AudioGenerator
enum {BITDEPTH = 15};
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); }
enum {ROWS = 64, SAMPLES = 31, CHANNELS = 4, NONOTE = 0xFFFF, NONOTE8 = 0xff };

View File

@ -21,12 +21,10 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#elif defined(ARDUINO_ARCH_RP2040) || defined(ESP8266)
#ifdef ARDUINO_ESP8266_MAJOR //this define was added in ESP8266 Arduino Core version v3.0.1
#include "core_esp8266_i2s.h" //for Arduino core >= 3.0.1
#else
#include "i2s.h" //for Arduino core <= 3.0.0
#endif
#elif defined(ARDUINO_ARCH_RP2040) || ARDUINO_ESP8266_MAJOR >= 3
#include <I2S.h>
#elif ARDUINO_ESP8266_MAJOR < 3
#include <i2s.h>
#endif
#include "AudioOutputI2S.h"
@ -44,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;
@ -150,6 +149,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
@ -170,17 +175,41 @@ 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)
{
comm_fmt = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB);
}
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 = {
@ -191,7 +220,7 @@ 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,
.dma_buf_len = 128,
.use_apll = use_apll // Use audio PLL
};
audioLogger->printf("+%d %p\n", portNo, &i2s_config_dac);
@ -201,8 +230,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
{
@ -273,11 +306,12 @@ bool AudioOutputI2S::ConsumeSample(int16_t sample[2])
{
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
@ -290,7 +324,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++)
{

View File

@ -44,6 +44,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 +52,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;

View File

@ -21,12 +21,10 @@
#include <Arduino.h>
#ifdef ESP32
#include "driver/i2s.h"
#elif defined(ARDUINO_ARCH_RP2040) || defined(ESP8266)
#ifdef ARDUINO_ESP8266_MAJOR //this define was added in ESP8266 Arduino Core version v3.0.1
#include "core_esp8266_i2s.h" //for Arduino core >= 3.0.1
#else
#include "i2s.h" //for Arduino core <= 3.0.0
#endif
#elif defined(ARDUINO_ARCH_RP2040) || ARDUINO_ESP8266_MAJOR >= 3
#include <I2S.h>
#elif ARDUINO_ESP8266_MAJOR < 3
#include <i2s.h>
#endif
#include "AudioOutputI2SNoDAC.h"
@ -99,12 +97,14 @@ bool AudioOutputI2SNoDAC::ConsumeSample(int16_t sample[2])
// Either send complete pulse stream or nothing
#ifdef ESP32
// Deprecated. Use i2s_write
//"i2s_write_bytes" has been removed in the ESP32 Arduino 2.0.0, use "i2s_write" instead.
// 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

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,7 +94,11 @@ 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
@ -265,7 +269,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

@ -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

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.
*/
@ -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

@ -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

@ -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
@ -202,7 +202,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

@ -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

@ -58,7 +58,7 @@ function build_sketches()
local sketchdirname=$(basename $sketchdir)
local sketchname=$(basename $sketch)
if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then
echo "Skipping $sketch, beacause it is not the main sketch file";
echo "Skipping $sketch, because it is not the main sketch file";
continue
fi;
if [[ -f "$sketchdir/.test.skip" ]]; then

View File

@ -83,7 +83,7 @@ CPPOPTS=-g -Wunused-parameter -Wall -std=c++11 -m32 -Wstack-usage=300 -include A
.phony: all
all: mp3 aac wav midi opus flac
all: mp3 aac wav midi opus flac mod
mp3: FORCE
rm -f *.o
@ -106,6 +106,12 @@ flac: FORCE
rm -f *.o
echo valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all ./flac
mod: FORCE
rm -f *.o
g++ $(CPPOPTS) -o mod mod.cpp Serial.cpp ../../src/AudioFileSourcePROGMEM.cpp ../../src/AudioOutputSTDIO.cpp ../../src/AudioGeneratorMOD.cpp ../../src/AudioLogger.cpp -I ../../src/ -I.
rm -f *.o
echo valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all ./mod
wav: FORCE
rm -f *.o
g++ $(CPPOPTS) -o wav wav.cpp Serial.cpp ../../src/AudioFileSourceSTDIO.cpp ../../src/AudioOutputSTDIO.cpp ../../src/AudioGeneratorWAV.cpp ../../src/AudioLogger.cpp -I ../../src/ -I.
@ -128,6 +134,6 @@ opus: FORCE
echo valgrind --leak-check=full --track-origins=yes -v --error-limit=no --show-leak-kinds=all ./opus
clean:
rm -f mp3 aac wav midi opus flac *.o
rm -f mp3 aac wav midi opus flac mod *.o
FORCE:

View File

@ -0,0 +1,26 @@
#include <Arduino.h>
#include "AudioFileSourcePROGMEM.h"
#include "AudioOutputSTDIO.h"
#include "AudioGeneratorMOD.h"
#include "../../examples/PlayMODFromPROGMEMToDAC/enigma.h"
int main(int argc, char **argv)
{
(void) argc;
(void) argv;
AudioFileSourcePROGMEM *file = new AudioFileSourcePROGMEM(enigma_mod, sizeof(enigma_mod));
AudioOutputSTDIO *out = new AudioOutputSTDIO();
out->SetFilename("mod.wav");
AudioGeneratorMOD *mod = new AudioGeneratorMOD();
mod->begin(file, out);
// The MOD plays forever, so only run for ~30 seconds worth
for (int i=0; i<10000; i++) mod->loop();
mod->stop();
delete out;
delete mod;
delete file;
}

View File

@ -23,6 +23,10 @@ Output is fixed at 22050Hz due to some hardcoded delays to help match C64 intern
The voice is formant generated and can be modified by setting things such as speed, pitch, mouth and throat configuration, and even sending in phonetic codes instead of English text. See @s-macke's repository for more information.
## Cool uses of SAM
Jan Derogee has used ESP8266SAM to build a complete VIC-20 compatible speech synthesis cartridge that works with BASIC applications. It also has a great background on early speech synthesis and a neat construction and demo video. Check it out at https://janderogee.com/projects/SerialSpeechSynthesisSAM/SerialSpeechSynthesisSAM.htm
## License
While the ESP8266 wrapper is my own, the SAM software is a reverse-engineered version of a software published more than 34 years ago by "Don't ask Software".

View File

@ -0,0 +1,20 @@
#include <Arduino.h>
#include <ESP8266SAM.h>
#include "AudioOutputI2SNoDAC.h"
AudioOutputI2SNoDAC *out = NULL;
void setup()
{
out = new AudioOutputI2SNoDAC();
out->begin();
}
void loop()
{
ESP8266SAM *sam = new ESP8266SAM;
sam->Say(out, "Can you hear me now?");
delay(500);
sam->Say(out, "I can't hear you!");
delete sam;
}

View File

@ -0,0 +1,129 @@
// In a webbrowser go to http://sam.local/say/{message} to make it speak
// ex: http://sam.local/say/hello world
#include <Arduino.h>
#if !defined(ESP8266)
#error This example is only for the ESP8266
#endif
#include <ESP8266SAM.h>
#include "AudioOutputI2SNoDAC.h"
#include <ESP8266mDNS.h>
#include <ESP8266NetBIOS.h>
#include <ESP8266SSDP.h> //Library for SSDP (Show ESP in Network on Windows)
#include <ESP8266WebServer.h> //Library for WebServer
#include <WiFiManager.h>
#include <uri/UriBraces.h>
AudioOutputI2SNoDAC *out = NULL;
ESP8266WebServer server(80); //Web Server on port 80
WiFiManager wifiManager;
const char* NAME = "SAM";
void setup()
{
Serial.begin(115200);
out = new AudioOutputI2SNoDAC();
out->begin();
wifiManager.autoConnect(NAME);
MDNS.begin(NAME);
MDNS.addService("http", "tcp", 80);
NBNS.begin(NAME);
server.on(UriBraces("/say/{}"), []() {
String message_encoded = server.pathArg(0);
String message_decoded = urldecode(message_encoded);
const char* message = message_decoded.c_str();
Serial.println(message_encoded);
Serial.println(message_decoded);
Serial.println(message);
ESP8266SAM *sam = new ESP8266SAM;
sam->Say(out, message);
delete sam;
server.send(200, "text/plain", "OK");
});
server.on("/description.xml", HTTP_GET, []() {
SSDP.schema(server.client());
});
server.begin();
ssdp();
}
void ssdp() {
//Simple Service Discovery Protocol : Display ESP in Windows Network Tab
SSDP.setSchemaURL("description.xml");
SSDP.setHTTPPort(80);
SSDP.setName(NAME);
SSDP.setDeviceType("upnp: rootdevice");
SSDP.setSerialNumber("000000000001");
SSDP.setURL("/say/connected");
SSDP.setModelName("ESP8266SAM");
SSDP.setModelNumber("0000000000001");
SSDP.setModelURL("https://github.com/earlephilhower/ESP8266SAM");
SSDP.setManufacturer("earlephilhower");
SSDP.setManufacturerURL("https://github.com/earlephilhower/");
SSDP.begin();
}
void loop() {
server.handleClient();
}
char* string2char(String command) {
if (command.length() != 0) {
char *p = const_cast<char*>(command.c_str());
return p;
} else {
return "";
}
}
unsigned char h2int(char c) {
if (c >= '0' && c <= '9') {
return ((unsigned char)c - '0');
}
if (c >= 'a' && c <= 'f') {
return ((unsigned char)c - 'a' + 10);
}
if (c >= 'A' && c <= 'F') {
return ((unsigned char)c - 'A' + 10);
}
return (0);
}
String urldecode(String str)
{
String encodedString = "";
char c;
char code0;
char code1;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == '+') {
encodedString += ' ';
} else if (c == '%') {
i++;
code0 = str.charAt(i);
i++;
code1 = str.charAt(i);
c = (h2int(code0) << 4) | h2int(code1);
encodedString += c;
} else {
encodedString += c;
}
yield();
}
return encodedString;
}

View File

@ -1,7 +1,7 @@
{
"name": "ESP8266SAM",
"description": "Speech synthesizer SAM ported to ESP8266/ESP32",
"keywords": "ESP8266, ESP32, Speecn Synthesis, SAM",
"description": "Speech synthesizer SAM ported to ESP8266/ESP32/RP2040",
"keywords": "ESP8266, ESP32, RP2040, Speecn Synthesis, SAM",
"authors": [
{
"name": "Earle F. Philhower, III",
@ -14,10 +14,9 @@
"type": "git",
"url": "https://github.com/earlephilhower/ESP8266SAM"
},
"version": "1.0",
"version": "1.0.1",
"homepage": "https://github.com/earlephilhower/ESP8266SAM",
"frameworks": "Arduino",
"platforms": ["espressif8266", "espressif32"],
"examples": [
"examples/*/*.ino"
]

View File

@ -1,9 +1,9 @@
name=ESP8266SAM
version=1.0
version=1.0.1
author=Earle F. Philhower, III
maintainer=Earle F. Philhower, III
sentence=Speech synthesis on the ESP8266 and ESP32
paragraph=Speech synthesis program SAM ported to ESP8266/ESP32 and ESP8266Audio
sentence=Speech synthesis on the ESP8266, ESP32, and RP2040
paragraph=Speech synthesis program SAM ported to ESP8266/ESP32/RP2040 and ESP8266Audio
category=Signal Input/Output
url=https://github.com/earlephilhower/ESP8266SAM
architectures=esp8266,esp32
architectures=esp8266,esp32,rp2040

View File

@ -70,3 +70,4 @@ private:
};
#endif

View File

@ -2,8 +2,8 @@
#define RECITERTABS_H
#include <pgmspace.h>
#include "samdebug.h"
#if sam_debug
#include "esp8266sam_debug.h"
#if DEBUG_ESP8266SAM_LIB
#define PROGMEM
#endif

View File

@ -2,8 +2,8 @@
#define RENDERTABS_H
#include <pgmspace.h>
#include "samdebug.h"
#if sam_debug
#include "esp8266sam_debug.h"
#if DEBUG_ESP8266SAM_LIB
#define PROGMEM
#endif

View File

@ -2,8 +2,8 @@
#define SAMTABS_H
#include <pgmspace.h>
#include "samdebug.h"
#if sam_debug
#include "esp8266sam_debug.h"
#if DEBUG_ESP8266SAM_LIB
#define PROGMEM
#endif

View File

@ -1,5 +1,5 @@
#ifndef DEBUG_H
#define DEBUG_H
#ifndef ESP8266SAM_DEBUG_H
#define ESP8266SAM_DEBUG_H
void PrintPhonemes(unsigned char *phonemeindex, unsigned char *phonemeLength, unsigned char *stress);
void PrintOutput(
@ -14,6 +14,6 @@ void PrintOutput(
void PrintRule(int offset);
#define sam_debug 0
#define DEBUG_ESP8266SAM_LIB 0
#endif

View File

@ -2,7 +2,7 @@
#include <string.h>
#include "reciter.h"
#include "ReciterTabs.h"
#include "samdebug.h"
#include "esp8266sam_debug.h"
#include "SamData.h"
unsigned char A, X, Y;
@ -527,7 +527,7 @@ pos37455:
Y = mem64;
mem61 = mem60;
if (sam_debug)
if (DEBUG_ESP8266SAM_LIB)
PrintRule(mem62);
pos37461:

View File

@ -5,8 +5,7 @@
#include "render.h"
#include "RenderTabs.h"
#include "samdebug.h"
//extern int debug;
#include "esp8266sam_debug.h"
#include <pgmspace.h>
#include "SamData.h"
@ -445,7 +444,7 @@ do
mem44++;
} while(mem44 != 0);
yield();
if (sam_debug)
if (DEBUG_ESP8266SAM_LIB)
{
PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches);
}
@ -786,7 +785,7 @@ if (sam_debug)
X = A;
mem38 = A - (A>>2); // 3/4*A ???
yield();
if (sam_debug)
if (DEBUG_ESP8266SAM_LIB)
{
PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches);
}

View File

@ -2,7 +2,7 @@
#include <string.h> // strlen()
//#include <stdlib.h>
#include <stddef.h> // define NULL
#include "samdebug.h"
#include "esp8266sam_debug.h"
#include "sam.h"
#include "render.h"
#include "SamTabs.h"
@ -15,8 +15,6 @@ static unsigned char mouth = 128;
static unsigned char throat = 128;
int singmode = 0;
//extern int debug;
unsigned char mem39;
unsigned char mem44;
unsigned char mem47;
@ -146,7 +144,7 @@ int SAMMain( void (*cb)(void *, unsigned char), void *cbd )
phonemeindex[255] = 32; //to prevent buffer overflow
if (!Parser1()) return 0;
if (sam_debug)
if (DEBUG_ESP8266SAM_LIB)
PrintPhonemes(phonemeindex, phonemeLength, stress);
Parser2();
CopyStress();
@ -168,7 +166,7 @@ int SAMMain( void (*cb)(void *, unsigned char), void *cbd )
InsertBreath();
//mem[40158] = 255;
if (sam_debug)
if (DEBUG_ESP8266SAM_LIB)
{
PrintPhonemes(phonemeindex, phonemeLength, stress);
}
@ -645,7 +643,7 @@ void Code41240()
//void Code41397()
void Parser2()
{
if (sam_debug) printf("Parser2\n");
if (DEBUG_ESP8266SAM_LIB) printf("Parser2\n");
unsigned char pos = 0; //mem66;
unsigned char mem58 = 0;
@ -659,7 +657,7 @@ void Parser2()
A = phonemeindex[pos];
// DEBUG: Print phoneme and index
if (sam_debug && A != 255) printf("%d: %c%c\n", X, signInputTable1[A], signInputTable2[A]);
if (DEBUG_ESP8266SAM_LIB && A != 255) printf("%d: %c%c\n", X, signInputTable1[A], signInputTable2[A]);
// Is phoneme pause?
if (A == 0)
@ -695,8 +693,8 @@ void Parser2()
//pos41443:
// Insert at WX or YX following, copying the stress
if (sam_debug) if (A==20) printf("RULE: insert WX following diphtong NOT ending in IY sound\n");
if (sam_debug) if (A==21) printf("RULE: insert YX following diphtong ending in IY sound\n");
if (DEBUG_ESP8266SAM_LIB) if (A==20) printf("RULE: insert WX following diphtong NOT ending in IY sound\n");
if (DEBUG_ESP8266SAM_LIB) if (A==21) printf("RULE: insert YX following diphtong ending in IY sound\n");
Insert(pos+1, A, mem59, mem58);
X = pos;
// Jump to ???
@ -716,7 +714,7 @@ pos41457:
if (A != 78) goto pos41487; // 'UL'
A = 24; // 'L' //change 'UL' to 'AX L'
if (sam_debug) printf("RULE: UL -> AX L\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: UL -> AX L\n");
pos41466:
// Get current phoneme stress
@ -740,7 +738,7 @@ pos41487:
if (A != 79) goto pos41495; // 'UM'
// Jump up to branch - replaces current phoneme with AX and continues
A = 27; // 'M' //change 'UM' to 'AX M'
if (sam_debug) printf("RULE: UM -> AX M\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: UM -> AX M\n");
goto pos41466;
pos41495:
@ -754,7 +752,7 @@ pos41495:
// Jump up to branch - replaces current phoneme with AX and continues
A = 28; // 'N' //change UN to 'AX N'
if (sam_debug) printf("RULE: UN -> AX N\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: UN -> AX N\n");
goto pos41466;
pos41503:
@ -802,7 +800,7 @@ pos41503:
if (A != 0)
{
// Insert a glottal stop and move forward
if (sam_debug) printf("RULE: Insert glottal stop between two stressed vowels with space between them\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: Insert glottal stop between two stressed vowels with space between them\n");
// 31 = 'Q'
Insert(X, 31, mem59, 0);
pos++;
@ -831,7 +829,7 @@ pos41503:
if (A == 69) // 'T'
{
// Change T to CH
if (sam_debug) printf("RULE: T R -> CH R\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: T R -> CH R\n");
phonemeindex[pos-1] = 42;
goto pos41779;
}
@ -846,7 +844,7 @@ pos41503:
{
// Change D to J
phonemeindex[pos-1] = 44;
if (sam_debug) printf("RULE: D R -> J R\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: D R -> J R\n");
goto pos41788;
}
@ -857,7 +855,7 @@ pos41503:
// If vowel flag is set change R to RX
A = flags[A] & 128;
if (sam_debug) printf("RULE: R -> RX\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: R -> RX\n");
if (A != 0) phonemeindex[pos] = 18; // 'RX'
// continue to next phoneme
@ -876,7 +874,7 @@ pos41611:
// If prior phoneme does not have VOWEL flag set, move to next phoneme
if ((flags[phonemeindex[pos-1]] & 128) == 0) {pos++; continue;}
// Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme
if (sam_debug) printf("RULE: <VOWEL> L -> <VOWEL> LX\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <VOWEL> L -> <VOWEL> LX\n");
phonemeindex[X] = 19; // 'LX'
pos++;
continue;
@ -895,7 +893,7 @@ pos41611:
// If prior phoneme is not G, move to next phoneme
if (phonemeindex[pos-1] != 60) {pos++; continue;}
// Replace S with Z and move on
if (sam_debug) printf("RULE: G S -> G Z\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: G S -> G Z\n");
phonemeindex[pos] = 38; // 'Z'
pos++;
continue;
@ -916,7 +914,7 @@ pos41611:
{
// VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set?
A = flags[Y] & 32;
if (sam_debug) if (A==0) printf("RULE: K <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPHTONG NOT ENDING WITH IY>\n");
if (DEBUG_ESP8266SAM_LIB) if (A==0) printf("RULE: K <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPHTONG NOT ENDING WITH IY>\n");
// Replace with KX
if (A == 0) phonemeindex[pos] = 75; // 'KX'
}
@ -943,7 +941,7 @@ pos41611:
// If diphtong ending with YX, move continue processing next phoneme
if ((flags[index] & 32) != 0) {pos++; continue;}
// replace G with GX and continue processing next phoneme
if (sam_debug) printf("RULE: G <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPHTONG NOT ENDING WITH IY>\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: G <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPHTONG NOT ENDING WITH IY>\n");
phonemeindex[pos] = 63; // 'GX'
pos++;
continue;
@ -968,7 +966,7 @@ pos41611:
goto pos41812;
}
// Replace with softer version
if (sam_debug) printf("RULE: S* %c%c -> S* %c%c\n", signInputTable1[Y], signInputTable2[Y],signInputTable1[Y-12], signInputTable2[Y-12]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: S* %c%c -> S* %c%c\n", signInputTable1[Y], signInputTable2[Y],signInputTable1[Y-12], signInputTable2[Y-12]);
phonemeindex[pos] = Y-12;
pos++;
continue;
@ -991,7 +989,7 @@ pos41749:
A = flags2[Y] & 4;
// If not set, continue processing next phoneme
if (A == 0) {pos++; continue;}
if (sam_debug) printf("RULE: <ALVEOLAR> UW -> <ALVEOLAR> UX\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <ALVEOLAR> UW -> <ALVEOLAR> UX\n");
phonemeindex[X] = 16;
pos++;
continue;
@ -1005,7 +1003,7 @@ pos41779:
if (A == 42) // 'CH'
{
// pos41783:
if (sam_debug) printf("CH -> CH CH+1\n");
if (DEBUG_ESP8266SAM_LIB) printf("CH -> CH CH+1\n");
Insert(X+1, A+1, mem59, stress[X]);
pos++;
continue;
@ -1020,7 +1018,7 @@ pos41788:
if (A == 44) // 'J'
{
if (sam_debug) printf("J -> J J+1\n");
if (DEBUG_ESP8266SAM_LIB) printf("J -> J J+1\n");
Insert(X+1, A+1, mem59, stress[X]);
pos++;
continue;
@ -1060,7 +1058,7 @@ pos41812:
if (stress[X] != 0) {pos++; continue;}
//pos41856:
// Set phonemes to DX
if (sam_debug) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n");
if (DEBUG_ESP8266SAM_LIB) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n");
phonemeindex[pos] = 30; // 'DX'
} else
{
@ -1070,7 +1068,7 @@ pos41812:
else
// Is next phoneme a vowel or ER?
A = flags[A] & 128;
if (sam_debug) if (A != 0) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n");
if (DEBUG_ESP8266SAM_LIB) if (A != 0) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n");
if (A != 0) phonemeindex[pos] = 30; // 'DX'
}
@ -1160,14 +1158,14 @@ pos48644:
// change phoneme length to (length * 1.5) + 1
A = (A >> 1) + A + 1;
if (sam_debug) printf("RULE: Lengthen <FRICATIVE> or <VOICED> between <VOWEL> and <PUNCTUATION> by 1.5\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: Lengthen <FRICATIVE> or <VOICED> between <VOWEL> and <PUNCTUATION> by 1.5\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
phonemeLength[X] = A;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
}
// keep moving forward
@ -1221,15 +1219,15 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonem
// RULE: <VOWEL> RX | LX <CONSONANT>
if (sam_debug) printf("RULE: <VOWEL> <RX | LX> <CONSONANT> - decrease length by 1\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <VOWEL> <RX | LX> <CONSONANT> - decrease length by 1\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]);
// decrease length of vowel by 1 frame
phonemeLength[loopIndex]--;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]);
}
// move ahead
@ -1267,16 +1265,16 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable
// move back
X--;
if (sam_debug) printf("RULE: <VOWEL> <UNVOICED PLOSIVE> - decrease vowel by 1/8th\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <VOWEL> <UNVOICED PLOSIVE> - decrease vowel by 1/8th\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
// decrease length by 1/8th
mem56 = phonemeLength[X] >> 3;
phonemeLength[X] -= mem56;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
// move ahead
loopIndex++;
@ -1286,16 +1284,16 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonem
// RULE: <VOWEL> <VOICED CONSONANT>
// <VOWEL> <WH, R*, L*, W*, Y*, M*, N*, NX, DX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX>
if (sam_debug) printf("RULE: <VOWEL> <VOICED CONSONANT> - increase vowel by 1/2 + 1\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <VOWEL> <VOICED CONSONANT> - increase vowel by 1/2 + 1\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
// decrease length
A = phonemeLength[X-1];
phonemeLength[X-1] = (A >> 2) + A + 1; // 5/4*A + 1
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
// move ahead
loopIndex++;
@ -1335,10 +1333,10 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phon
// B*, D*, G*, GX, P*, T*, K*, KX
{
if (sam_debug) printf("RULE: <NASAL> <STOP CONSONANT> - set nasal = 5, consonant = 6\n");
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <NASAL> <STOP CONSONANT> - set nasal = 5, consonant = 6\n");
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
// set stop consonant length to 6
phonemeLength[X] = 6;
@ -1346,9 +1344,9 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phon
// set nasal length to 5
phonemeLength[X-1] = 5;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
}
// move to next phoneme
@ -1388,10 +1386,10 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phon
}
// RULE: <UNVOICED STOP CONSONANT> {optional silence} <STOP CONSONANT>
if (sam_debug) printf("RULE: <UNVOICED STOP CONSONANT> {optional silence} <STOP CONSONANT> - shorten both to 1/2 + 1\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <UNVOICED STOP CONSONANT> {optional silence} <STOP CONSONANT> - shorten both to 1/2 + 1\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]);
// X gets overwritten, so hold prior X value for debug statement
int debugX = X;
// shorten the prior phoneme length to (length/2 + 1)
@ -1401,9 +1399,9 @@ int debugX = X;
// also shorten this phoneme length to (length/2 +1)
phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", debugX, signInputTable1[phonemeindex[debugX]], signInputTable2[phonemeindex[debugX]], phonemeLength[debugX]);
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", debugX-1, signInputTable1[phonemeindex[debugX-1]], signInputTable2[phonemeindex[debugX-1]], phonemeLength[debugX-1]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", debugX, signInputTable1[phonemeindex[debugX]], signInputTable2[phonemeindex[debugX]], phonemeLength[debugX]);
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", debugX-1, signInputTable1[phonemeindex[debugX-1]], signInputTable2[phonemeindex[debugX-1]], phonemeLength[debugX-1]);
// move ahead
@ -1429,15 +1427,15 @@ if (sam_debug) printf("phoneme %d (%c%c) length %d\n", debugX-1, signInputTable1
if((flags[index] & 2) != 0)
// Rule: <LIQUID CONSONANT> <DIPHTONG>
if (sam_debug) printf("RULE: <LIQUID CONSONANT> <DIPHTONG> - decrease by 2\n");
if (sam_debug) printf("PRE\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("RULE: <LIQUID CONSONANT> <DIPHTONG> - decrease by 2\n");
if (DEBUG_ESP8266SAM_LIB) printf("PRE\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
// decrease the phoneme length by 2 frames (20 ms)
phonemeLength[X] -= 2;
if (sam_debug) printf("POST\n");
if (sam_debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
if (DEBUG_ESP8266SAM_LIB) printf("POST\n");
if (DEBUG_ESP8266SAM_LIB) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]);
}
// move to next phoneme