sh123 / esp32_codec2_arduino Goto Github PK
View Code? Open in Web Editor NEWCodec2 low bit rate audio codec Arduino library for ESP32
License: GNU General Public License v3.0
Codec2 low bit rate audio codec Arduino library for ESP32
License: GNU General Public License v3.0
Hi!
Could you please explain this code part in i2s_lora_walkie_talkie example?
// split by frame, decode and play
for (int i = 0; i < packet_size; i++) {
c2_bits_[i % c2_bytes_per_frame_] = lora_radio_rx_queue_.shift();
if (i % c2_bytes_per_frame_ == c2_bytes_per_frame_ - 1) {
codec2_decode(c2_, c2_samples_, c2_bits_);
i2s_write(I2S_NUM_0, c2_samples_, sizeof(uint16_t) * c2_samples_per_frame_, &bytes_written, portMAX_DELAY);
vTaskDelay(1);
I experimenting with this code and notased that it return every 3-d byte from codec2 queue (450 mode), but we need every byte. Or not? This works fine, but why?
At bottom my extended example of loopback codec2 with packaging by 48 bytes and read back again. Please run it for detail understanding.
Based on this algorithm its possible to add a half-duplex repeater function to loraDV radio...
#include <driver/i2s.h>
#include <codec2.h>
#include <CircularBuffer.h>
// serial
#define SERIAL_BAUD_RATE 115200
// audio speaker
#define AUDIO_SPEAKER_BCLK 26
#define AUDIO_SPEAKER_LRC 25
#define AUDIO_SPEAKER_DIN 22
// audio microphone
#define AUDIO_MIC_SD 27
#define AUDIO_MIC_WS 14
#define AUDIO_MIC_SCK 13
#define AUDIO_SAMPLE_RATE 8000 // 44100
#define QUEUE_LEN 512
CircularBuffer<uint8_t, QUEUE_LEN> tx_rx_queue_;
CircularBuffer<uint8_t, QUEUE_LEN> tx_rx_queue_index_;
TaskHandle_t audio_task_;
struct CODEC2* c2_;
int c2_samples_per_frame_;
int c2_bytes_per_frame_;
int16_t *c2_samples_;
uint8_t *c2_bits_;
void setup() {
Serial.begin(SERIAL_BAUD_RATE);
while (!Serial);
Serial.println(F("Board setup started"));
// create i2s speaker
i2s_config_t i2s_speaker_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = AUDIO_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 1024,
.use_apll=0,
.tx_desc_auto_clear= true,
.fixed_mclk=-1
};
i2s_pin_config_t i2s_speaker_pin_config = {
.bck_io_num = AUDIO_SPEAKER_BCLK,
.ws_io_num = AUDIO_SPEAKER_LRC,
.data_out_num = AUDIO_SPEAKER_DIN,
.data_in_num = I2S_PIN_NO_CHANGE
};
if (i2s_driver_install(I2S_NUM_0, &i2s_speaker_config, 0, NULL) != ESP_OK) {
Serial.println(F("Failed to install i2s speaker driver"));
}
if (i2s_set_pin(I2S_NUM_0, &i2s_speaker_pin_config) != ESP_OK) {
Serial.println(F("Failed to set i2s speaker pins"));
}
// create i2s microphone
i2s_config_t i2s_mic_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = AUDIO_SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 1024,
.use_apll=0,
.tx_desc_auto_clear= true,
.fixed_mclk=-1
};
i2s_pin_config_t i2s_mic_pin_config = {
.bck_io_num = AUDIO_MIC_SCK,
.ws_io_num = AUDIO_MIC_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = AUDIO_MIC_SD
};
if (i2s_driver_install(I2S_NUM_1, &i2s_mic_config, 0, NULL) != ESP_OK) {
Serial.println(F("Failed to install i2s mic driver"));
}
if (i2s_set_pin(I2S_NUM_1, &i2s_mic_pin_config) != ESP_OK) {
Serial.println(F("Failed to set i2s mic pins"));
}
// run codec2 audio loopback on a separate task
xTaskCreate(&audio_task, "audio_task", 32000, NULL, 5, &audio_task_);
Serial.println(F("Board setup completed"));
}
void audio_task(void *param) {
// construct and configure codec2
c2_ = codec2_create(CODEC2_MODE_3200);
if (c2_ == NULL) {
Serial.println(F("Failed to create Codec2"));
return;
} else {
//codec2_set_lpc_post_filter(c2_, 1, 0, 0.8, 0.2);
c2_samples_per_frame_ = codec2_samples_per_frame(c2_);
c2_bytes_per_frame_ = codec2_bytes_per_frame(c2_);
c2_samples_ = (int16_t*)malloc(sizeof(int16_t) * c2_samples_per_frame_);
c2_bits_ = (uint8_t*)malloc(sizeof(uint8_t) * c2_bytes_per_frame_);
Serial.println(F("Codec2 constructed"));
Serial.print(F("Semples per frame-"));
Serial.println(c2_samples_per_frame_);
Serial.print(F("Bytes per frame-"));
Serial.println(c2_bytes_per_frame_);
}
// run loopback record-encode-decode-playback loop
size_t bytes_read, bytes_written;
int packet_size = 0;
Serial.println(F("Audio task started"));
while(true) {
if (tx_rx_queue_index_.size() != 0) { // Then the mark of 48 bytes exsist in index queue
int packet_size_rx = tx_rx_queue_index_.shift(); // take the mark of 48 bytes is present in frames queue
Serial.println(F("Packet Recived"));
// devided by frames and play
for (int i = 0; i < packet_size_rx; i++) { // ??? HOW ITS WORK ???
c2_bits_[i % c2_bytes_per_frame_] = tx_rx_queue_.shift(); // ??? HOW ITS WORK ???
if (i % c2_bytes_per_frame_ == c2_bytes_per_frame_ - 1) { // ??? HOW ITS WORK ???
codec2_decode(c2_, c2_samples_, c2_bits_);
i2s_write(I2S_NUM_0, c2_samples_, sizeof(uint16_t) * c2_samples_per_frame_, &bytes_written, portMAX_DELAY);
Serial.print(F("RX byte - "));
Serial.println(*c2_bits_);
vTaskDelay(1);
} // Why we have extract every 8-th byte from bytes queue? Why? ??? HOW ITS WORK ???
} // All 48 bytes extracted from queue and played
Serial.println(F("Packet Played"));
} // Played all 48 bytes of queue
else {
// If enoth bytes - 48 - we can decodd
if (packet_size + c2_bytes_per_frame_ > 48) {
tx_rx_queue_index_.push(packet_size); // push to queue what we have 48 bytes of packet for decodd by codec2
packet_size = 0; // we have sended 48 bytes packet and start to accumulate new 48 bytes packet
Serial.println(F("Packet Done"));
}
// read and codding one frame
size_t bytes_read;
i2s_read(I2S_NUM_1, c2_samples_, sizeof(uint16_t) * c2_samples_per_frame_, &bytes_read, portMAX_DELAY); // read I2S sending semples to codec2
codec2_encode(c2_, c2_bits_, c2_samples_); // codding semples - return one byte to another
for (int n = 0; n < c2_bytes_per_frame_; n++) {
tx_rx_queue_.push(c2_bits_[n]); // push to circular bufer n byte, n+1 byte, n+2 byte, and etc.
Serial.print(F("TX byte - "));
Serial.println(*c2_bits_);
}
packet_size += c2_bytes_per_frame_; // +3 add to packet size each frame coded (for 450 Codec2 mode, + 8 for 3200 mode)
Serial.print(F("Packet size cumulative - "));
Serial.println(packet_size);
vTaskDelay(1);
}
}
}
void loop() {
delay(100);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.