From 8f17172aa388be77d4cc307d7e1a55b9755f517a Mon Sep 17 00:00:00 2001 From: Austin Morlan Date: Mon, 25 May 2020 16:32:31 -0700 Subject: [PATCH] Part 6: Audio --- src/CMakeLists.txt | 1 + src/main.c | 38 +++++++++++++++++++++ src/odroid/audio.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++ src/odroid/audio.h | 8 +++++ 4 files changed, 132 insertions(+) create mode 100644 src/odroid/audio.c create mode 100644 src/odroid/audio.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 53ac103..8f71575 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ idf_component_register( SRCS "main.c" + "odroid/audio.c" "odroid/battery.c" "odroid/display.c" "odroid/input.c" diff --git a/src/main.c b/src/main.c index 1b8e607..b17d8b0 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include "odroid/audio.h" #include "odroid/battery.h" #include "odroid/display.h" #include "odroid/input.h" @@ -12,12 +13,14 @@ static const char* LOG_TAG = "Main"; static uint16_t gFramebuffer[LCD_WIDTH * LCD_HEIGHT]; + void app_main(void) { Odroid_InitializeInput(); Odroid_InitializeDisplay(); Odroid_InitializeSdcard(); Odroid_InitializeBatteryReader(); + Odroid_InitializeAudio(); // Load sprite uint16_t* sprite = (uint16_t*)malloc(64 * 64 * sizeof(uint16_t)); @@ -36,6 +39,28 @@ void app_main(void) fclose(spriteFile); } + // Load sound effect + uint16_t* soundBuffer; + int soundEffectLength = 1441; + { + FILE* soundFile = fopen("/sdcard/jump", "r"); + assert(soundFile); + + uint8_t* soundEffect = malloc(soundEffectLength); + assert(soundEffect); + + soundBuffer = malloc(soundEffectLength*2); + assert(soundBuffer); + + fread(soundEffect, soundEffectLength, 1, soundFile); + + for (int i = 0; i < soundEffectLength; ++i) + { + // 16 bits required but only MSB is actually sent to the DAC + soundBuffer[i] = (soundEffect[i] << 8u); + } + } + ESP_LOGI(LOG_TAG, "Odroid initialization complete - entering main loop"); @@ -44,6 +69,8 @@ void app_main(void) uint16_t color = 0xffff; + int lastState = 0; + for (;;) { memset(gFramebuffer, 0, 320 * 240 * 2); @@ -61,6 +88,17 @@ void app_main(void) else if (input.start) { color = SWAP_ENDIAN_16(RGB565(0, 0, 0xff)); } else if (input.select) { color = SWAP_ENDIAN_16(RGB565(0xff, 0xff, 0xff)); } + + int thisState = input.volume; + + if ((thisState == 1) && (thisState != lastState)) + { + Odroid_PlayAudio(soundBuffer, soundEffectLength*2); + } + + lastState = thisState; + + int spriteRow = 0; int spriteCol = 0; diff --git a/src/odroid/audio.c b/src/odroid/audio.c new file mode 100644 index 0000000..7d62264 --- /dev/null +++ b/src/odroid/audio.c @@ -0,0 +1,85 @@ +#include "audio.h" +#include +#include +#include + + +static const gpio_num_t AUDIO_AMP_SD_PIN = GPIO_NUM_25; + +static QueueHandle_t gQueue; + + +typedef struct +{ + uint16_t* buffer; + int length; +} QueueData; + + +static void PlayTask(void *arg) +{ + for(;;) + { + QueueData data; + + if (xQueueReceive(gQueue, &data, 10)) + { + size_t bytesWritten; + i2s_write(I2S_NUM_0, data.buffer, data.length, &bytesWritten, portMAX_DELAY); + i2s_zero_dma_buffer(I2S_NUM_0); + } + + vTaskDelay(1 / portTICK_PERIOD_MS); + } +} + +void Odroid_InitializeAudio(void) +{ + // Configure the amplifier shutdown signal + { + gpio_config_t gpioConfig = {}; + + gpioConfig.mode = GPIO_MODE_OUTPUT; + gpioConfig.pin_bit_mask = 1ULL << AUDIO_AMP_SD_PIN; + + ESP_ERROR_CHECK(gpio_config(&gpioConfig)); + + gpio_set_level(AUDIO_AMP_SD_PIN, 1); + } + + // Configure the I2S driver + { + i2s_config_t i2sConfig= {}; + + i2sConfig.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN; + i2sConfig.sample_rate = 5512; + i2sConfig.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; + i2sConfig.communication_format = I2S_COMM_FORMAT_I2S_MSB; + i2sConfig.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; + i2sConfig.dma_buf_count = 8; + i2sConfig.dma_buf_len = 64; + + ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_0, &i2sConfig, 0, NULL)); + ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN)); + } + + // Create task for playing sounds so that our main task isn't blocked + { + gQueue = xQueueCreate(1, sizeof(QueueData)); + assert(gQueue); + + BaseType_t result = xTaskCreatePinnedToCore(&PlayTask, "I2S Task", 1024, NULL, 5, NULL, 1); + assert(result == pdPASS); + } +} + +void Odroid_PlayAudio(uint16_t* buffer, int length) +{ + QueueData data = {}; + + data.buffer = buffer; + data.length = length; + + xQueueSendToBack(gQueue, &data, portMAX_DELAY); +} + diff --git a/src/odroid/audio.h b/src/odroid/audio.h new file mode 100644 index 0000000..bd66b82 --- /dev/null +++ b/src/odroid/audio.h @@ -0,0 +1,8 @@ +#pragma once + +#include + + +void Odroid_InitializeAudio(void); +void Odroid_PlayAudio(uint16_t* buffer, int length); +