/main/audio.c
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "driver/i2s.h"
#include "esp_log.h"

#include "audio.h"
#include "io.h"

QueueHandle_t audio_queue;
SemaphoreHandle_t audio_access_sem;

void audio_task(void *pvParameters) {
	const char TAG[] = "audio_task";
	audio_block_t audio_block;

	while (1) {
		BaseType_t success = xQueueReceive(audio_queue, &audio_block, portMAX_DELAY);
		if (success != pdTRUE) {
			ESP_LOGE(TAG, "Failed to receive from audio queue");
			continue;
		}
		
		size_t bytes_written;
		esp_err_t e = i2s_write(I2S_NUM_0, audio_block.samples, audio_block.count * 2, &bytes_written, portMAX_DELAY);
		if (e != ESP_OK) {
			ESP_LOGE(TAG, "error %d", e);
		}
		if (bytes_written < audio_block.count * 2) {
			ESP_LOGE(TAG, "only wrote %d bytes", bytes_written);
		} else {
			ESP_LOGD(TAG, "wrote %d bytes", bytes_written);
		}
		free(audio_block.samples);
	}
}

int audio_init() {
	const char TAG[] = "audio_init";

	i2s_config_t i2s_config = {
		.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
		.sample_rate = SAMPLE_RATE,
		.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
		.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
		.communication_format = I2S_COMM_FORMAT_STAND_I2S,
		.dma_buf_count = I2S_DMA_COUNT,
		.dma_buf_len = I2S_DMA_LEN,
		.use_apll = false,
		.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1  //Interrupt level 1
	};
	i2s_pin_config_t pin_config = {
		.mck_io_num = I2S_MCK_IO,
		.bck_io_num = I2S_BCK_IO,
		.ws_io_num = I2S_WS_IO,
		.data_out_num = I2S_DO_IO,
		.data_in_num = I2S_DI_IO  //Not used
	};
	i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
	i2s_set_pin(I2S_NUM, &pin_config);

	i2s_stop(I2S_NUM);

	audio_access_sem = xSemaphoreCreateBinary();
	if (audio_access_sem == NULL) {
		ESP_LOGE(TAG, "Could not create audio access semaphore");
		return 0;
	}

	audio_queue = xQueueCreate(AUDIO_QUEUE_SIZE, sizeof(audio_block_t));
	if (audio_queue == NULL) {
		ESP_LOGE(TAG, "Could not create audio queue");
		return 0;
	}

	xTaskCreate(audio_task, "audio", 4096, NULL, 10, NULL);

	xSemaphoreGive(audio_access_sem);

	return 1;
}

int audio_open() {
	const char TAG[] = "audio_open";

	BaseType_t available = xSemaphoreTake(audio_access_sem, 0);
	if (available != pdTRUE) {
		ESP_LOGE(TAG, "tried to open audio twice");
		return 0;
	}

	i2s_start(I2S_NUM);
	io_audio_enable(true);

	return 1;
}

void audio_close() {
	i2s_stop(I2S_NUM);
	io_audio_enable(false);
	xSemaphoreGive(audio_access_sem);
}

void audio_send(uint16_t *buf, size_t n) {
	uint16_t* samples = (uint16_t*) malloc(n * 2);
	memcpy(samples, buf, n * 2);
	audio_block_t block = { samples, n };
	xQueueSend(audio_queue, &block, portMAX_DELAY);
}

void audio_send_ptr(uint16_t *buf, size_t n) {
	audio_block_t block = { buf, n };
	xQueueSend(audio_queue, &block, portMAX_DELAY);
}