mirror of https://github.com/arendst/Tasmota.git
updated Audio libs
This commit is contained in:
parent
cb52c4cf35
commit
e84124f035
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ void setup()
|
|||
WiFi.mode(WIFI_OFF);
|
||||
|
||||
Serial.begin(115200);
|
||||
SPIFFS.begin();
|
||||
Serial.println("Starting up...\n");
|
||||
|
||||
audioLogger = &Serial;
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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": [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
**************************************************************************************/
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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".
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -70,3 +70,4 @@ private:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue