Skip to content

Commit

Permalink
Makes audio on the CYD much nicer.
Browse files Browse the repository at this point in the history
  • Loading branch information
cgreening committed Oct 18, 2024
1 parent 71f4815 commit 07813c4
Show file tree
Hide file tree
Showing 9 changed files with 476 additions and 8 deletions.
5 changes: 5 additions & 0 deletions player/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"functional": "cpp"
}
}
58 changes: 54 additions & 4 deletions player/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ monitor_filters = ${common.monitor_filters}
[env:atomic14-custom-pcb]
extends = esp32_common
board = esp32-s3-devkitc-1
; board_build.arduino.memory_type = qio_opi
board_build.arduino.memory_type = qio_opi
build_flags =
${common.build_flags}
; -DBOARD_HAS_PSRAM
-DBOARD_HAS_PSRAM
-DUSE_DMA
; Remote Control Pins
-DHAS_IR_REMOTE
; -DHAS_IR_REMOTE
-DIR_RECV_PWR=GPIO_NUM_48
-DIR_RECV_PIN=GPIO_NUM_47
-DIR_RECV_GND=GPIO_NUM_NC
Expand All @@ -65,6 +65,7 @@ build_flags =
-DTFT_CS=42
-DTFT_BL=0
-DLOAD_FONT2=1
-DTFT_RGB_ORDER=TFT_RGB
-DSPI_FREQUENCY=40000000
; audio settings
-DSPK_MODE=GPIO_NUM_12
Expand Down Expand Up @@ -158,7 +159,9 @@ build_flags =
-DSPI_READ_FREQUENCY=20000000
-DSPI_TOUCH_FREQUENCY=2500000
; audio settings - cheap yellow display uses the DAC
-DUSE_DAC_AUDIO
; CYD has terrible audio - but actually is quite good if you use PWM
; -DUSE_DAC_AUDIO
-DPWM_GPIO_NUM=GPIO_NUM_26
; SD card
-DUSE_SDCARD
-DSD_CARD_MISO=GPIO_NUM_19
Expand Down Expand Up @@ -353,3 +356,50 @@ build_flags =
${common.build_flags}
-DLED_MATRIX
-DPDM_GPIO_NUM=GPIO_NUM_33

[env:atomic14-esp32-zxspectrum-v0_1]
extends = esp32_common
board = esp32-s3-devkitc-1
board_build.arduino.memory_type = qio_opi
build_flags =
${common.build_flags}
-DBOARD_HAS_PSRAM
; DMA used to work - now it doesn't :(
; -DUSE_DMA
; We have a touch keyboard!
-DTOUCH_KEYBOARD_V2
; optional TFT power pin
-DUSE_HSPI_PORT
; TFT_eSPI setup
-DTFT_ROTATION=1
-DTFT_RGB_ORDER=TFT_BGR
-DUSER_SETUP_LOADED=1
-DTFT_WIDTH=240
-DTFT_HEIGHT=320
-DST7789_DRIVER=1
-DTFT_SCLK=GPIO_NUM_18
-DTFT_MISO=-1
-DTFT_MOSI=GPIO_NUM_15
-DTFT_RST=GPIO_NUM_8
-DTFT_DC=GPIO_NUM_17
-DTFT_CS=GPIO_NUM_16
-DTFT_BL=GPIO_NUM_NC
-DTOUCH_CS=-1
; -DLOAD_FONT2=1
-DSMOOTH_FONT
-DSPI_FREQUENCY=40000000
; audio settings
; SD card
-DUSE_SDCARD
-DUSE_SDIO=1
-DSD_CARD_D0=GPIO_NUM_39
-DSD_CARD_D1=GPIO_NUM_38
-DSD_CARD_D2=GPIO_NUM_2
-DSD_CARD_D3=GPIO_NUM_42
-DSD_CARD_CMD=GPIO_NUM_41
-DSD_CARD_CLK=GPIO_NUM_40
; SPEAKER
-DPDM_GPIO_NUM=GPIO_NUM_3
; make sure serial output works
-DARDUINO_USB_MODE
-DARDUINO_USB_CDC_ON_BOOT
2 changes: 1 addition & 1 deletion player/src/AudioOutput/PDMTimerOuput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void PDMTimerOutput::onTimer()
// get the first sample from the buffer
int16_t sample = mBuffer[mCurrentIndex];
mCurrentIndex++;
sample *= 20;
// sample *= 2;
if (sample > 90) {
sample = 90;
}
Expand Down
129 changes: 129 additions & 0 deletions player/src/AudioOutput/PWMTimerOuput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#include "PWMTimerOutput.h"
#include "freertos/semphr.h"
#include "driver/sigmadelta.h"
#include <string.h>
#include <Arduino.h>


IRAM_ATTR void onTimerCallbackPWM(void *param)
{
PWMTimerOutput *output = (PWMTimerOutput *)param;
timer_spinlock_take(TIMER_GROUP_0);
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
timer_spinlock_give(TIMER_GROUP_0);
output->onTimer();
}

void PWMTimerOutput::start(uint32_t sample_rate)
{
mSampleRate = sample_rate;

ledcSetup(0, 50000, 8);
ledcAttachPin(mPDMPin, 0);

// create a timer that will fire at the sample rate
timer_config_t timer_config = {
.alarm_en = TIMER_ALARM_EN,
.counter_en = TIMER_PAUSE,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_EN,
.divider = 80};
esp_err_t result = timer_init(TIMER_GROUP_0, TIMER_0, &timer_config);
if (result != ESP_OK)
{
Serial.printf("Error initializing timer: %d\n", result);
}
result = timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL);
if (result != ESP_OK)
{
Serial.printf("Error setting timer counter value: %d\n", result);
}
result = timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000000 / mSampleRate);
if (result != ESP_OK)
{
Serial.printf("Error setting timer alarm value: %d\n", result);
}
// timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000);
result = timer_enable_intr(TIMER_GROUP_0, TIMER_0);
if (result != ESP_OK)
{
Serial.printf("Error enabling timer interrupt: %d\n", result);
}
result = timer_isr_register(TIMER_GROUP_0, TIMER_0, &onTimerCallbackPWM, this, ESP_INTR_FLAG_IRAM, NULL);
if (result != ESP_OK)
{
Serial.printf("Error registering timer interrupt: %d\n", result);
// print the string error
const char *err = esp_err_to_name(result);
Serial.printf("Error: %s\n", err);
}
result = timer_start(TIMER_GROUP_0, TIMER_0);
if (result != ESP_OK)
{
Serial.printf("Error starting timer: %d\n", result);
}
Serial.println("PDM Started");
}

void PWMTimerOutput::write(int8_t *samples, int count)
{
// Serial.printf("Count %d\n", mCount);
while (true)
{
if(xSemaphoreTake(mBufferSemaphore, portMAX_DELAY)) {
// is the second buffer empty?
if (mSecondBufferLength == 0)
{
//Serial.println("Filling second buffer");
// make sure there's enough room for the samples
mSecondBuffer = (int8_t *)realloc(mSecondBuffer, count);
// copy them into the second buffer
memcpy(mSecondBuffer, samples, count);
// second buffer is now full of samples
mSecondBufferLength = count;
// unlock the mutext and return
xSemaphoreGive(mBufferSemaphore);
return;
}
// no room in the second buffer so wait for the first buffer to be emptied
xSemaphoreGive(mBufferSemaphore);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
}

void PWMTimerOutput::onTimer()
{
// output a sample from the buffer if we have one
if (mCurrentIndex < mBufferLength)
{
mCount++;
// get the first sample from the buffer
int16_t sample = mBuffer[mCurrentIndex];
mCurrentIndex++;
ledcWrite(0, sample+128);
}
if(mCurrentIndex >= mBufferLength)
{
// do we have any data in teh second buffer?
BaseType_t xHigherPriorityTaskWoken;
if (xSemaphoreTakeFromISR(mBufferSemaphore, &xHigherPriorityTaskWoken) == pdTRUE)
{
if (mSecondBufferLength > 0) {
// swap the buffers
int8_t *tmp = mBuffer;
mBuffer = mSecondBuffer;
mBufferLength = mSecondBufferLength;
mSecondBuffer = tmp;
mSecondBufferLength = 0;
mCurrentIndex = 0;
}
xSemaphoreGiveFromISR(mBufferSemaphore, &xHigherPriorityTaskWoken);
}
if(xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
}
39 changes: 39 additions & 0 deletions player/src/AudioOutput/PWMTimerOutput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include "AudioOutput.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "driver/sigmadelta.h"
#include "driver/timer.h"

/**
* Base Class for both the ADC and I2S sampler
**/
class PWMTimerOutput : public AudioOutput
{
private:
gpio_num_t mPDMPin;
uint32_t mSampleRate;
SemaphoreHandle_t mBufferSemaphore;
int8_t *mBuffer=NULL;;
int mCurrentIndex=0;
int mBufferLength=0;
int8_t *mSecondBuffer=NULL;;
int mSecondBufferLength=0;
int mCount = 0;
void onTimer();
public:
PWMTimerOutput(gpio_num_t pdm_pin) : AudioOutput()
{
mPDMPin = pdm_pin;
mBufferSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(mBufferSemaphore);
}
void write(int8_t *samples, int count);
void start(uint32_t sample_rate);
void stop() {}

friend void onTimerCallbackPWM(void *arg);
};
Loading

0 comments on commit 07813c4

Please sign in to comment.