FLAC streaming playback works
"battery.c"
"play.c"
"file.c"
+ "sd.c"
+ "browse.c"
+ "flac.c"
INCLUDE_DIRS "."
)
QueueHandle_t audio_queue;
SemaphoreHandle_t audio_access_sem;
-audio_block_t audio_block;
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);
#define I2S_DMA_COUNT 2
#define I2S_DMA_LEN 256
-#define AUDIO_BLOCK_SIZE 256 // 16-bit samples
#define AUDIO_QUEUE_SIZE 2 // blocks
typedef struct audio_block {
+#include <dirent.h>
+#include <string.h>
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "cwalk.h"
+#include "esp_vfs_fat.h"
+
+#include "gfx.h"
+#include "keyboard.h"
+#include "sd.h"
+#include "browse.h"
+
+typedef struct browse_file_entry {
+ char* name;
+ unsigned char type;
+} browse_file_entry_t;
+
+typedef struct browse_file_list {
+ browse_file_entry_t** files;
+ size_t count;
+ size_t capacity;
+} browse_file_list_t;
+
+void bfl_init(browse_file_list_t* bfl) {
+ bfl->count = 0;
+ bfl->capacity = 10;
+ bfl->files = (browse_file_entry_t**) malloc(bfl->capacity * sizeof(browse_file_entry_t*));
+}
+
+void bfl_free_entry(browse_file_list_t* bfl, size_t index) {
+ browse_file_entry_t* be = bfl->files[index];
+ free(be->name);
+ free(be);
+}
+
+void bfl_free(browse_file_list_t* bfl) {
+ for (int i = 0; i < bfl->count; i++) {
+ bfl_free_entry(bfl, i);
+ }
+ free(bfl->files);
+}
+
+void bfl_resize(browse_file_list_t* bfl, size_t new_capacity) {
+ if (new_capacity < bfl->count) {
+ for (int i = new_capacity; i < bfl->count; i++) {
+ bfl_free_entry(bfl, i);
+ }
+ bfl->count = new_capacity;
+ }
+
+ bfl->capacity = new_capacity;
+ bfl->files = (browse_file_entry_t**) realloc(bfl->files, bfl->capacity * sizeof(browse_file_entry_t*));
+}
+
+void bfl_clear(browse_file_list_t* bfl) {
+ bfl_free(bfl);
+ bfl_init(bfl);
+}
+
+void bfl_add_entry(browse_file_list_t* bfl, const char* name, unsigned char type) {
+ browse_file_entry_t* be = (browse_file_entry_t*) malloc(sizeof(browse_file_entry_t));
+ size_t l = strlen(name);
+ be->name = (char*) malloc(l + 1);
+ strncpy(be->name, name, l);
+ be->name[l] = 0;
+ be->type = type;
+ if (bfl->count == bfl->capacity) {
+ bfl_resize(bfl, bfl->capacity * 2);
+ }
+ bfl->files[bfl->count] = be;
+ bfl->count++;
+}
+
+void bfl_fetch(browse_file_list_t* bfl, const char* path) {
+ struct dirent *e;
+ DIR* d = opendir(path);
+
+ bfl_clear(bfl);
+ while ((e = readdir(d)) != NULL) {
+ bfl_add_entry(bfl, e->d_name, e->d_type);
+ }
+ closedir(d);
+}
+
+void browse() {
+ const char TAG[] = "browse_files";
+ browse_file_list_t bfl;
+ char* path = (char*) malloc(FILENAME_MAX);
+ strcpy(path, "/sd");
+
+ FATFS* fs = sd_get();
+ if (fs == NULL) {
+ printf("No SD card\n");
+ return;
+ }
+
+ bfl_init(&bfl);
+ bfl_fetch(&bfl, path);
+
+ int cursor = 0;
+ key_event_t k;
+
+ while (1) {
+ gfx_data_t *gd = gfx_acquire_data(SCENE_BROWSE);
+ gd->browse.state = BROWSE_NONE;
+ if (cursor == 0) {
+ gd->browse.state = BROWSE_AT_TOP;
+ }
+ if (cursor >= bfl.count - 4) {
+ gd->browse.state = BROWSE_AT_BOTTOM;
+ }
+ for (int i = 0; i < 4; i++) {
+ if (cursor + i < bfl.count) {
+ gd->browse.names[i] = bfl.files[cursor + i]->name;
+ gd->browse.types[i] = bfl.files[cursor + i]->type;
+ } else {
+ gd->browse.names[i] = NULL;
+ }
+ }
+
+ gfx_release_data();
+
+ int selected = -1;
+ k = key_get_event(portMAX_DELAY);
+ if (k.type == KEY_PRESSED) {
+ switch (k.code) {
+ case KEY_BAND:
+ if (strncmp(path, "/sd", FILENAME_MAX) == 0) {
+ goto browse_end;
+ }
+ size_t l;
+ cwk_path_get_dirname(path, &l);
+ path[l - 1] = 0;
+ ESP_LOGI(TAG, "new path = %s", path);
+ bfl_clear(&bfl);
+ bfl_fetch(&bfl, path);
+ cursor = 0;
+ break;
+ case KEY_TUNE_PLUS:
+ if (cursor >= 4) {
+ cursor -= 4;
+ }
+ break;
+ case KEY_TUNE_MINUS:
+ if (cursor < bfl.count - 4) {
+ cursor += 4;
+ }
+ break;
+ case KEY_1:
+ selected = 0;
+ break;
+ case KEY_2:
+ selected = 1;
+ break;
+ case KEY_3:
+ selected = 2;
+ break;
+ case KEY_4:
+ selected = 3;
+ break;
+ default:
+ break;
+ }
+ }
+ if (selected != -1 && cursor + selected < bfl.count) {
+ char* name = bfl.files[cursor + selected]->name;
+ unsigned char t = bfl.files[cursor + selected]->type;
+
+ if (strlen(path) + strlen(name) + 2 > FILENAME_MAX) {
+ ESP_LOGE(TAG, "File path would be too long; cannot navigate to %s", name);
+ continue;
+ }
+ char* fullpath = (char*) malloc(FILENAME_MAX);
+ cwk_path_join(path, name, fullpath, FILENAME_MAX);
+
+ if (t == DT_REG) {
+ play_file(fullpath);
+ free(fullpath);
+ break;
+ } else if (t == DT_DIR) {
+ free(path);
+ path = fullpath;
+ ESP_LOGI(TAG, "new path = %s", path);
+ bfl_clear(&bfl);
+ bfl_fetch(&bfl, path);
+ cursor = 0;
+ }
+ }
+ }
+
+browse_end:
+ bfl_free(&bfl);
+ free(path);
+}
\ No newline at end of file
+#pragma once
+
+typedef enum file_type {
+ FILE_TYPE_FLAC,
+ FILE_TYPE_MP3,
+} file_type_t;
+
+void browse();
\ No newline at end of file
void file_reader_task(void* pvParameters) {
BaseType_t err;
bool reading = false;
- size_t bytes_read = 0, bytes_sent = 0;
+ size_t bytes_read = 0, bytes_sent = 0, total_bytes = 0;
FILE* f = NULL;
+ int64_t start = 0;
uint8_t *buf = (uint8_t *) malloc(BUFSIZE);
if (buf == NULL) {
file_reader_streambuffer = a.subscribe_sb;
break;
case FILE_READER_START:
+ start = esp_timer_get_time();
reading = true;
break;
case FILE_READER_STOP:
}
if (reading) {
- ESP_LOGI(TAG, "%d sent %d read", bytes_sent, bytes_read);
+ //ESP_LOGI(TAG, "%d sent %d read", bytes_sent, bytes_read);
if (bytes_sent == bytes_read) {
if (f == NULL) {
ESP_LOGE(TAG, "FILE pointer null while reading");
} else if (feof(f)) {
ESP_LOGI(TAG, "end of file");
fclose(f);
- f = NULL;
+ break;
} else {
bytes_sent = 0;
bytes_read = fread(buf, 1, BUFSIZE, f);
- ESP_LOGI(TAG, "read %d bytes", bytes_read);
+ total_bytes += bytes_read;
+ if (total_bytes >= 1048576) {
+ int64_t now = esp_timer_get_time();
+ ESP_LOGI(TAG, "%0.1f KB/sec", 1000.0 * (double)total_bytes / (double)(now - start));
+ start = now;
+ total_bytes = 0;
+ }
+ //ESP_LOGI(TAG, "read %d bytes", bytes_read);
}
}
bytes_sent += xStreamBufferSend(
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/stream_buffer.h"
+#include "esp_log.h"
+#include "FLAC/stream_decoder.h"
+
+#include "audio.h"
+#include "file.h"
+#include "play.h"
+#include "flac.h"
+
+FLAC__StreamDecoderReadStatus flac_read_callback(const FLAC__StreamDecoder *sd, FLAC__byte buffer[], size_t *bytes, void *client_data) {
+ StreamBufferHandle_t sb = (StreamBufferHandle_t) client_data;
+ size_t n = xStreamBufferReceive(sb, buffer, *bytes, portMAX_DELAY);
+ *bytes = n;
+
+ // Check the abort semaphore
+ uint32_t abort = ulTaskNotifyTake(pdFALSE, 0);
+
+ if (abort) {
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderWriteStatus flac_write_callback(const FLAC__StreamDecoder* sd, const FLAC__Frame* frame, const FLAC__int32* const buffer[], void* client_data) {
+ // FLAC header block size is the number of 16-bit stereo samples (4 bytes per sample)
+ uint16_t* outbuf = malloc(256 * sizeof(uint16_t));
+ if (outbuf == NULL) {
+ ESP_LOGE("flac_write_callback", "Failed to allocate output buffer");
+ heap_caps_print_heap_info(MALLOC_CAP_8BIT);
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+
+ int c = 0;
+ for (int i = 0; i < frame->header.blocksize; i++) {
+ outbuf[c ] = (uint16_t) buffer[0][i];
+ outbuf[c+1] = (uint16_t) buffer[1][i];
+ c += 2;
+ if (c == 256) {
+ audio_send_ptr(outbuf, 256);
+ outbuf = malloc(256 * sizeof(uint16_t));
+ if (outbuf == NULL) {
+ ESP_LOGE("flac_write_callback", "Failed to reallocate output buffer");
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+ }
+ c = 0;
+ }
+ }
+
+ audio_send_ptr(outbuf, c);
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+void flac_error_callback(const FLAC__StreamDecoder *sd, FLAC__StreamDecoderErrorStatus status, void *client_data) {
+ ESP_LOGE("flac_error_callback", "%d", status);
+}
+
+void play_flac(void *pvParameters) {
+ const char TAG[] = "play_flac";
+ play_parameters_t* pp = (play_parameters_t*) pvParameters;
+ StreamBufferHandle_t sb = xStreamBufferCreate(4096, 2048);
+
+ file_reader_open(pp->filename);
+ file_reader_subscribe(sb);
+ file_reader_start();
+
+ if (!audio_open()) {
+ ESP_LOGE(TAG, "audio not available");
+ }
+
+ free(pp);
+
+ FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
+ if (sd == NULL) {
+ ESP_LOGE(TAG, "Could not allocate FLAC decoder");
+ return;
+ }
+
+ FLAC__StreamDecoderInitStatus s = FLAC__stream_decoder_init_stream(
+ sd,
+ flac_read_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ flac_write_callback,
+ NULL,
+ flac_error_callback,
+ sb
+ );
+ if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
+ ESP_LOGE(TAG, "Could not initialize FLAC decoder: %d", s);
+ return;
+ }
+
+ while (FLAC__stream_decoder_process_single(sd)) {
+ if (ulTaskNotifyTake(pdTRUE, 0)) {
+ break;
+ }
+ }
+
+ FLAC__stream_decoder_delete(sd);
+
+ audio_close();
+
+ file_reader_stop();
+ file_reader_close();
+
+ vTaskDelete(NULL);
+}
+#pragma once
+
+void play_flac(void* pvParameters);
\ No newline at end of file
/*
Fontname: unknown
Copyright: unknown
- Glyphs: 103/103
+ Glyphs: 127/127
BBX Build Mode: 0
*/
-const uint8_t maximum_overdrift[753] U8G2_FONT_SECTION("maximum_overdrift") =
- "g\0\3\3\3\3\3\4\4\5\6\0\0\6\6\6\1\1&\2,\2\324\1\12-\323%\311\62\311$"
- "\1\2\12-\323\61\215\210R$\11\3\7+\223\23\71\24\4\10+\223\23\271\244\0\5\10+\223\23\241"
- "\244\14\6\7+\223\223\213\1\7\10,\263\61\231\134\10 \5\0}\1!\6)S\61\11\42\7\23\231"
- "\21\221\4#\12-\323\223RI\251\244\0$\13-\323\63I#E\42\23\0%\7-\323\21\313\7&"
- "\12-\323\61\12\232\42\24\0'\5\21Y!(\7*s#I\12)\11*s\21\212D$\0*\7"
- "\33\225\21\311\1+\10\33\225\23\231D\0,\5\21Q!-\5\13\227\61.\5\11S\21/\10-\323"
- "\31\313\21\0\60\11-\323a\232DF\6\61\5\251\323Q\62\7-\323Q<\26\63\6-\323Qt\64"
- "\10-\323\21\223\31\23\65\7-\323a,\32\66\7-\323a\264\31\67\7-\323Q\314\1\70\7-\323"
- "a\273\31\71\7-\323a\63\32:\6\31U\21\11;\6!S\21\21<\7+\223\25IK=\6\33"
- "\225\61\33>\10+\223\21KI\2\77\12-\323Q\14\315\1!\0@\10-\323a\223\20\13A\10-"
- "\323a\273\311\2B\12-\323A\211Ub\25\0C\7-\323aL,D\11-\323A\211\251U\0E"
- "\10-\323a\244\4\13F\11-\323a\244\4\203\0G\10-\323a\14\315\14H\11-\323\21\223\335d"
- "\1I\10-\323Q\12&\25J\7-\323\231\321\0K\13-\323\21\23EF)\261\0L\7-\323\21"
- "\314XM\12-\323!\231Ddj\1N\13-\323\21\33I\42\242Y\0O\7-\323aS\63P\10"
- "-\323a;\6\1Q\11-\323a\223DD\7R\12-\323A\211Ub\262\0S\7-\323a,\32"
- "T\10-\323Q\12f\2U\7-\323\21\323fV\12-\323\21S\213\244\205\0W\11-\323\21\323\22"
- "\231\10X\12-\323\21\213\244\245\244\5Y\12-\323\21\213\244\5\223\0Z\7-\323Q\313V[\7*"
- "s\61I\21\134\7-\323\21\315\1]\7*s!I\31^\6\23\231\223\1_\5\15\323Q`\6\22"
- "y\21\12a\5\0\335\1b\5\0\335\1c\5\0\335\1d\5\0\335\1e\5\0\335\1f\5\0\335"
- "\1g\5\0\335\1h\5\0\335\1i\5\0\335\1j\5\0\335\1k\5\0\335\1l\5\0\335\1m"
- "\5\0\335\1n\5\0\335\1o\5\0\335\1p\5\0\335\1q\5\0\335\1r\5\0\335\1s\5\0"
- "\335\1t\5\0\335\1u\5\0\335\1v\5\0\335\1w\5\0\335\1x\5\0\335\1y\5\0\335\1"
- "z\5\0\335\1{\10+\223#I\13\11|\5)SQ}\12+\223!\212E\42\22\0~\10\24\271"
- "\23\221D\0\177\5\0\335\1\0\0\0\4\377\377\0";
+const uint8_t maximum_overdrift[945] U8G2_FONT_SECTION("maximum_overdrift") =
+ "\177\0\3\3\3\3\1\4\4\5\6\0\0\6\0\6\1\1\251\2\253\3\224\1\12\355tI\262L\62I"
+ "\0\2\12\355tL#\242\24I\2\3\7\353\344D\16\5\4\10\353\344D.)\0\5\10\353\344D("
+ ")\3\6\6\353\344\344b\7\10\354lL&\27\2\10\10\354lL\16\7\1\11\11\354lL\42\24\211"
+ "\1\12\11\354lL\42#Q\1\13\10\354lLJ\7\1\14\11\354lL\42#\211\1\15\11\354lL"
+ "\42\243I\1\16\10\354lLJ\242\2\17\10\354lL\42K\5\20\10\345t\310\16\207\0\21\4@w"
+ "\22\4@w\23\4@w\24\4@w\25\4@w\26\4@w\27\4@w\30\4@w\31\4@w"
+ "\32\4@w\33\4@w\34\4@w\35\4@w\36\4@w\37\4@w \4@_!\6\351T"
+ "L\2\42\7SfD$\1#\12\355\364\244TR*)\0$\13\355\364L\322H\221\310\4\0%\7"
+ "\355t\304\362\1&\12\355t\214\202\246\10\5\0'\5QV\10(\7\352\334H\222\2)\11\352\134\204"
+ "\42\21\11\0*\6[eDr+\10[\345D&\21\0,\5QT\10-\5\313e\14.\5\311T"
+ "\4/\7\355t\306r\4\60\11\355t\230&\221\221\1\61\5\351T\24\62\7\355t\24\217\5\63\6\355"
+ "t\24\35\64\10\355t\304d\306\4\65\7\355t\30\213\6\66\7\355t\30m\6\67\6\355t\24s\70"
+ "\7\355t\330n\6\71\7\355t\330\214\6:\6YUD\2;\6\341TD\4<\7\353dE\322\22"
+ "=\6[e\314\6>\10\353d\304R\222\0\77\12\355t\24Cs@\10\0@\10\355t\330$\304\2"
+ "A\10\355t\330n\262\0B\12\355tPb\225X\5\0C\7\355t\30\23\13D\11\355tPbj"
+ "\25\0E\10\355t\30)\301\2F\11\355t\30)\301 \0G\10\355t\30C\63\3H\11\355t\304"
+ "d\67Y\0I\10\355t\224\202I\5J\6\355tf\64K\13\355t\304D\221QJ,\0L\7\355"
+ "t\4\63\26M\12\355tH&\21\231Z\0N\12\355t\304F\222\210h\26O\7\355t\330\324\14P"
+ "\10\355t\330\216A\0Q\11\355t\330$\21\321\1R\12\355tPb\225\230,\0S\7\355t\30\213"
+ "\6T\10\355t\224\202\231\0U\7\355t\304\264\31V\12\355t\304\324\42i!\0W\11\355t\304\264"
+ "D&\2X\12\355t\304\42i)i\1Y\12\355t\304\42i\301$\0Z\7\355t\324\262\25[\7"
+ "\352\134LR\4\134\6\355tDs]\7\352\134HR\6^\5S\346d_\5\315t\24`\6R^"
+ "\204\2a\6\334\354\220\12b\10\354l\304b\245\2c\6\334l\324\10d\7\354\354\305L\5e\6\334"
+ "l\34\12f\12\353\354H\42\223P\4\0g\7dl\34*\3h\11\354l\304b%Q\0i\6\351"
+ "TD\6j\10r\334\304\42\221\1k\10\353d\204R&\11l\5\351T\24m\10\335tX\42\222\4"
+ "n\7\334l\224D\1o\6\334l\224\12p\7dl\224l\0q\7dl\224j\1r\7\333\344\214"
+ "B\0s\7\334\354\34&\0t\10\343\344D&!\1u\7\334l\204D\5v\11\334l\204D\21\11"
+ "\0w\11\335tD\42\222\210\1x\11\334l\204\42\222P\0y\7dl\204j\5z\7\334lP$"
+ "\4{\10\353\344H\322B\2|\5\351T\24}\12\353d\210b\221\210\4\0~\10T\356D$\21\0"
+ "\177\11\355tMR&\331\6\0\0\0\4\377\377\0";
+#include <dirent.h>
#include <string.h>
#include <time.h>
}
}
+void draw_browse(browse_scene_t* bs) {
+ u8g2_ClearBuffer(&u8g2);
+ u8g2_SetFont(&u8g2, maximum_overdrift);
+
+ for (int i = 0; i < 4; i++) {
+ if (bs->names[i] == 0) {
+ break;
+ }
+ u8g2_DrawXBM(&u8g2, 2, i * 8, btn_1_width, btn_1_height, btn_list[i]);
+ u8g2_DrawStr(&u8g2, 9, i * 8 + 7, bs->types[i] == DT_DIR ? "\x10" : "\x8");
+ u8g2_DrawStr(&u8g2, 16, i * 8 + 7, bs->names[i]);
+ }
+
+ u8g2_SetDrawColor(&u8g2, 0);
+ u8g2_DrawBox(&u8g2, 122, 0, 6, 32);
+ if (bs->state == BROWSE_NONE || bs->state == BROWSE_AT_BOTTOM) {
+ u8g2_SetDrawColor(&u8g2, 1);
+ u8g2_DrawBox(&u8g2, 123, 0, 5, 15);
+ u8g2_SetDrawColor(&u8g2, 0);
+ u8g2_DrawStr(&u8g2, 124, 11, "+");
+ }
+ if (bs->state == BROWSE_NONE || bs->state == BROWSE_AT_TOP) {
+ u8g2_SetDrawColor(&u8g2, 1);
+ u8g2_DrawBox(&u8g2, 123, 17, 5, 15);
+ u8g2_SetDrawColor(&u8g2, 0);
+ u8g2_DrawStr(&u8g2, 124, 28, "-");
+ }
+ u8g2_SetDrawColor(&u8g2, 1);
+}
+
void gfx_thread(void *pvParameters) {
while (1) {
// wait for data update
case SCENE_BT:
draw_bt(&gfx_data.bt);
break;
+ case SCENE_BROWSE:
+ draw_browse(&gfx_data.browse);
+ break;
case SCENE_NO_CHANGE:
ESP_LOGE(TAG, "SCENE_NO_CHANGE somehow set in gfx_data");
esp_system_abort("invalid state");
-#ifndef __GFX_H
-#define __GFX_H
+#pragma once
#include <stdbool.h>
#include "u8g2_esp32_hal.h"
SCENE_MENU,
SCENE_PLAY,
SCENE_BT,
+ SCENE_BROWSE,
SCENE_NO_CHANGE,
} scene_t;
} menu_scene_t;
typedef struct {
- play_state play_state;
+ play_state_t play_state;
} play_scene_t;
+#define BROWSE_NONE 0
+#define BROWSE_AT_TOP 1
+#define BROWSE_AT_BOTTOM 2
+
+typedef struct {
+ char* names[4];
+ unsigned char types[4];
+ uint8_t state;
+} browse_scene_t;
+
typedef struct {
bt_mode mode;
uint32_t confirm;
clock_scene_t clock;
menu_scene_t menu;
play_scene_t play;
+ browse_scene_t browse;
};
} gfx_data_t;
void gfx_set_power(int on);
void gfx_message(const char *str);
void gfx_set_enabled(bool v);
-
-#endif //__GFX_H
-#ifndef __KEYBOARD_H
-#define __KEYBOARD_H
+#pragma once
#include "freertos/FreeRTOS.h"
key_event_t key_get_event(const TickType_t ticksToWait);
keycode_t key_get_pressed();
void key_test();
-
-#endif //__KEYBOARD_H
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h"
+#include "cwalk.h"
#include "driver/i2s.h"
#include "driver/gpio.h"
#include "soc/rtc_cntl_reg.h"
-#include "diskio_impl.h"
-#include "diskio_sdmmc.h"
#include "esp_system.h"
#include "esp_log.h"
-#include "esp_vfs_fat.h"
-#include "sdmmc_cmd.h"
#include "nvs_flash.h"
#include "esp_sleep.h"
#include "mp3dec.h"
-#include "FLAC/stream_decoder.h"
-#include "xmp.h"
#include "u8g2.h"
#include "sin_table.h"
#include "audio.h"
#include "battery.h"
+#include "browse.h"
#include "bt.h"
#include "file.h"
#include "fonts.h"
#include "keyboard.h"
#include "gfx.h"
#include "pm.h"
-
-#define BUFSIZE 16384
-
-sdmmc_host_t host = SDSPI_HOST_DEFAULT();
-const char mount_point[] = "/sd";
-
-int sdspi_init() {
- const char TAG[] = "sdspi_init";
- esp_err_t ret;
-
- ESP_LOGI(TAG, "Initializing SPI bus");
-
- spi_bus_config_t bus_cfg = {
- .mosi_io_num = 23,
- .miso_io_num = 19,
- .sclk_io_num = 18,
- .quadwp_io_num = -1,
- .quadhd_io_num = -1,
- .max_transfer_sz = 4000,
- };
- ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CH_AUTO);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "Failed to initialize bus: %s", esp_err_to_name(ret));
- return 0;
- }
-
- sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
- slot_config.gpio_cs = 5;
- slot_config.host_id = host.slot;
-
- ESP_LOGI(TAG, "Initializing SDSPI device");
- sdspi_dev_handle_t sdspi;
- ret = sdspi_host_init_device(&slot_config, &sdspi);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "Failed to init SDSPI device: %d", ret);
- return 0;
- }
-
- ret = sdspi_host_set_card_clk(sdspi, 1600);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "Failed to set SPI card speed: %d", ret);
- return 0;
- }
-
- return 1;
-}
-
-FATFS * sd_card_mount() {
- esp_err_t ret;
- const char TAG[] = "init_sd";
-
- ESP_LOGI(TAG, "Initializing SD card");
-
- sdmmc_card_t *card = (sdmmc_card_t *) malloc(sizeof(sdmmc_card_t));
- if (card == NULL) {
- ESP_LOGE(TAG, "Could not allocate card struct");
- }
-
- host.max_freq_khz = 1600;
- ret = sdmmc_card_init(&host, card);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "Failed to init SD card: %s", esp_err_to_name(ret));
- return NULL;
- }
-
- sdmmc_card_print_info(stdout, card);
-
- ESP_LOGI(TAG, "Mounting filesystem");
- BYTE pdrv = 0;
- ff_diskio_register_sdmmc(pdrv, card);
- FATFS *fs = NULL;
- ret = esp_vfs_fat_register(mount_point, "0:", 5, &fs);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "Failed to register VFS: %d", ret);
- return NULL;
- }
-
- FRESULT fret = f_mount(fs, "0:", 1);
- if (fret != FR_OK) {
- ESP_LOGE(TAG, "Failed to mount FATFS: %d", fret);
- }
-
- ESP_LOGI(TAG, "Filesystem mounted on %s", mount_point);
-
- return fs;
-}
-
-void sd_card_unmount() {
- const char TAG[] = "deinit_sd";
- esp_err_t ret;
-
- f_unmount("0:");
- ret = esp_vfs_fat_unregister_path(mount_point);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "can't unmount: %s not mounted", mount_point);
- return;
- }
- ESP_LOGI(TAG, "%s unmounted", mount_point);
-
- ff_diskio_register(0, NULL);
- ESP_LOGI(TAG, "disk driver freed");
-}
+#include "sd.h"
size_t fill_from_streambuffer(StreamBufferHandle_t sb, uint8_t *buf, int size) {
size_t n = 0;
}
}
-FLAC__StreamDecoderReadStatus flac_read_callback(const FLAC__StreamDecoder *sd, FLAC__byte buffer[], size_t *bytes, void *client_data) {
- read_task_parameter_t *params = (read_task_parameter_t *) client_data;
-
- size_t n = xStreamBufferReceive(params->sb, buffer, *bytes, portMAX_DELAY);
- *bytes = n;
-
- // Check the abort semaphore
- uint32_t abort = ulTaskNotifyTake(pdFALSE, 0);
-
- if (params->eof || abort) {
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- }
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-FLAC__StreamDecoderWriteStatus flac_write_callback(const FLAC__StreamDecoder *sd, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) {
- uint16_t *outbuf = malloc(frame->header.blocksize * 4);
-
- for (int i = 0; i < frame->header.blocksize; i++) {
- outbuf[i*2 ] = (uint16_t) buffer[0][i];
- outbuf[i*2+1] = (uint16_t) buffer[1][i];
- }
-
- audio_send_ptr(outbuf, frame->header.blocksize * 2);
-
- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
-
-void flac_error_callback(const FLAC__StreamDecoder *sd, FLAC__StreamDecoderErrorStatus status, void *client_data) {
- ESP_LOGE("flac_error_callback", "%d", status);
-}
-
-void play_flac(void *pvParameters) {
- const char TAG[] = "play_flac";
- BaseType_t xerr;
- TaskHandle_t rt;
- StreamBufferHandle_t sb = xStreamBufferCreate(BUFSIZE, 256);
- read_task_parameter_t params = {
- .filename = "/sd/cooler.flac",
- .sb = sb,
- .eof = 0,
- };
- xerr = xTaskCreate(read_task, "read_task", 4096, ¶ms, 2, &rt);
- if (xerr != pdPASS) {
- ESP_LOGE(TAG, "Could not launch read task: %d", xerr);
- }
-
- FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
- ESP_LOGE(TAG, "Could not allocate FLAC decoder");
- return;
- }
-
- FLAC__StreamDecoderInitStatus s = FLAC__stream_decoder_init_stream(
- sd,
- flac_read_callback,
- NULL,
- NULL,
- NULL,
- NULL,
- flac_write_callback,
- NULL,
- flac_error_callback,
- ¶ms
- );
- if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- ESP_LOGE(TAG, "Could not initialize FLAC decoder: %d", s);
- return;
- }
-
- while (FLAC__stream_decoder_process_single(sd)) {
- if (ulTaskNotifyTake(pdTRUE, 0)) {
- break;
- }
- }
-
- ESP_LOGI(TAG, "Shutting down I/O thread");
-
- vTaskDelete(rt);
-
- FLAC__stream_decoder_delete(sd);
-
- SemaphoreHandle_t waiter = *((SemaphoreHandle_t*) pvParameters);
- xSemaphoreGive(waiter);
-
- vTaskDelete(NULL);
-}
-
void play_mod() {
const char TAG[] = "play_mod";
xmp_context c;
gfx_set_enabled(true);
}
-void test_sd() {
- gfx_set_enabled(false);
-
- const char TAG[] = "test_sd";
- char buf[40];
- key_event_t ke;
- int cs_state = 1;
- FATFS *fs = NULL;
-
- while (ke = key_get_event(0), !(ke.type == KEY_PRESSED && ke.code == KEY_BAND)) {
- int new_cs_state = gpio_get_level(GPIO_NUM_13);
- if (!new_cs_state && cs_state) {
- // card inserted
- fs = sd_card_mount();
- if (fs == NULL) {
- ESP_LOGE(TAG, "Could not initialize SD");
- cs_state = new_cs_state;
- continue;
- }
-
- DIR *d = opendir("/sd");
- struct dirent *e;
- while ((e = readdir(d)) != NULL) {
- printf("%02x %s\n", e->d_type, e->d_name);
- }
- closedir(d);
- } else if (new_cs_state && !cs_state) {
- sd_card_unmount();
- fs = NULL;
- // card removed
- }
- cs_state = new_cs_state;
-
- u8g2_ClearBuffer(&u8g2);
-
- sprintf(buf, "CS: %c", cs_state ? 'H' : 'L');
- u8g2_DrawStr(&u8g2, 0, 6, buf);
- sprintf(buf, "FS: %p", fs);
- for (int i = 0; i < strlen(buf); i++) {
- buf[i] = toupper(buf[i]);
- }
- u8g2_DrawStr(&u8g2, 0, 12, buf);
-
- u8g2_SendBuffer(&u8g2);
-
- vTaskDelay(100 / portTICK_PERIOD_MS);
- }
-
- gfx_set_enabled(true);
-}
-
-
void do_menu(const char *title, int n_items, menu_def_t *items) {
assert(n_items > 0 && n_items <= 5);
gfx_data_t *gd = gfx_acquire_data(SCENE_MENU);
esp_restart();
}
+void do_nothing() { }
+
void main_menu() {
menu_def_t menu[] = {
{"PLAY", play_controller},
+ {"FILE", browse},
{"TEST", test_mode},
{"BT", bt_config_mode},
};
- do_menu("MAIN", 3, menu);
+ do_menu("MAIN", 4, menu);
}
void clock_mode() {
printf("\n");
}
+/*
void stream_test() {
const char TAG[] = "stream_test";
uint8_t* buf = malloc(256);
StreamBufferHandle_t h = xStreamBufferCreate(256, 1);
uint32_t addr = 0;
- sd_card_mount();
+ sd_get();
ESP_LOGI(TAG, "-------- start --------");
- file_reader_open("/sd/rock.gcode");
+ FILE* f = fopen("/sd/cooler.flac", "r");
+ int64_t t0 = esp_timer_get_time();
+ for (int i = 0; i < 8 * 1048576; i += 256) {
+ size_t len = fread(buf, 1, 256, f);
+ addr += len;
+ }
+ int64_t t1 = esp_timer_get_time();
+ ESP_LOGI(TAG, "Read %d bytes in %lld us: %0.3f KB/sec", addr, t1 - t0, 1000.0 * (double)addr / (double)(t1 - t0));
+ fclose(f);
+
+ file_reader_open("/sd/cooler.flac");
file_reader_subscribe(h);
file_reader_start();
- for (int i = 0; i < 256 / 16; i++) {
- addr += xStreamBufferReceive(h, buf, 16, portMAX_DELAY);
- print_hexdump_line(addr, buf);
+
+ int64_t t0 = esp_timer_get_time();
+ for (int i = 0; i < 8 * 1048576; i += 256) {
+ size_t len = xStreamBufferReceive(h, buf, 256, portMAX_DELAY);
+ //print_hexdump_line(addr, buf);
+ addr += len;
}
+ int64_t t1 = esp_timer_get_time();
+ ESP_LOGI(TAG, "Read %d bytes in %lld us: %0.3f KB/sec", addr, t1 - t0, 1000.0 * (double)addr / (double)(t1 - t0));
file_reader_seek(0, SEEK_SET);
ESP_LOGI(TAG, "--------- seek --------");
for (int i = 0; i < 256 / 16; i++) {
ESP_LOGI(TAG, "--------- end ---------");
}
+*/
void app_main(void) {
nvs_init();
io_init();
audio_init();
gfx_init();
- sdspi_init();
+ sd_init();
file_init();
battery_init();
bt_init();
//xTaskCreate(print_task_list, "task list", 4096, NULL, 1, NULL);
- stream_test();
+ //stream_test();
clock_mode();
}
+#include <string.h>
#include <sys/time.h>
+
#include "freertos/FreeRTOS.h"
#include "esp_log.h"
+#include "cwalk.h"
+#include "browse.h"
#include "bt.h"
+#include "flac.h"
#include "gfx.h"
#include "keyboard.h"
#include "play.h"
-play_media_type media_type = PLAY_MEDIA_NONE;
+play_media_type_t media_type = PLAY_MEDIA_NONE;
+TaskHandle_t play_task;
-void play_set_media_type(play_media_type t) {
+void play_set_media_type(play_media_type_t t) {
media_type = t;
}
-play_state get_play_state() {
+play_state_t get_play_state() {
switch (media_type) {
case PLAY_MEDIA_BLUETOOTH:
- return (play_state) bt_get_playback_state()->state;
+ return (play_state_t) bt_get_playback_state()->state;
default:
ESP_LOGE("PLAY", "Unfinished media type: %d", media_type);
return PLAY_STATE_ERROR;
}
}
-void play_send_command(play_command c) {
+void play_send_command(play_command_t c) {
switch (media_type) {
case PLAY_MEDIA_BLUETOOTH: {
esp_avrc_pt_cmd_t button;
key_event_t ke;
while (ke = key_get_event(0), !(ke.type == KEY_PRESSED && ke.code == KEY_BAND)) {
- play_state ps = get_play_state();
+ play_state_t ps = get_play_state();
if (ke.type == KEY_PRESSED) {
switch (ke.code) {
case KEY_SNOOZE:
}
}
-/*
-void play_mode() {
- TaskHandle_t decoder_task;
- key_event_t ke;
- struct timeval tv;
- float now, t;
-
- FATFS *fs = sd_card_mount();
- if (fs == NULL) {
- u8g2_ClearBuffer(&u8g2);
- u8g2_DrawStr(&u8g2, 51, 19, "NO SD");
- u8g2_SendBuffer(&u8g2);
- vTaskDelay(1000 / portTICK_PERIOD_MS);
- return;
+void play_start_file_type(const char* filename, file_type_t file_type) {
+ play_parameters_t* pp = (play_parameters_t*) malloc(sizeof(play_parameters_t));
+ strncpy(pp->filename, filename, FILENAME_MAX);
+
+ switch (file_type) {
+ case FILE_TYPE_FLAC:
+ xTaskCreate(play_flac, "play_flac", 4096, pp, 1, &play_task);
+ break;
+ case FILE_TYPE_MP3:
+ printf("MP3 Unimplemented");
+ break;
}
+}
- int ret = audio_open();
- if (!ret) {
- gfx_message("NO AUDIO");
+void play_file(const char* filename) {
+ file_type_t file_type;
+ const char* extension;
+ size_t extension_len;
+ cwk_path_get_extension(filename, &extension, &extension_len);
+ if (strncmp(extension, ".flac", 6) == 0) {
+ file_type = FILE_TYPE_FLAC;
+ } else if (strncmp(extension, ".mp3", 5) == 0) {
+ file_type = FILE_TYPE_MP3;
+ } else {
+ ESP_LOGE("play_file", "Could not play %s", filename);
return;
}
- SemaphoreHandle_t waiter = xSemaphoreCreateBinary();
- xTaskCreate(play_flac, "flac", 4096, &waiter, 5, &decoder_task);
-
- gettimeofday(&tv, NULL);
- now = (float)tv.tv_sec + (float)tv.tv_usec / 1000000.0;
-
- while (ke = key_get_event(0), !(ke.type == KEY_PRESSED && ke.code == KEY_BAND)) {
- if (ke.code == KEY_SNOOZE && ke.type != KEY_NO_EVENT) {
- if (state == ESP_AVRC_PLAYBACK_PLAYING) {
- bt_send_key(ESP_AVRC_PT_CMD_PAUSE, ke.type - 1);
- } else {
- bt_send_key(ESP_AVRC_PT_CMD_PLAY, ke.type - 1);
- }
- }
-
- vTaskDelay(100 / portTICK_PERIOD_MS);
- }
-
- // Kill the decoder
- xTaskNotifyGive(decoder_task);
-
- xSemaphoreTake(waiter, portMAX_DELAY);
-
- sd_card_unmount();
-
- audio_close();
-}
-*/
+ printf("Playing %s\n", filename);
+ play_start_file_type(filename, file_type);
+}
\ No newline at end of file
#pragma once
-typedef enum {
+typedef enum play_media_type {
PLAY_MEDIA_NONE,
PLAY_MEDIA_BLUETOOTH,
PLAY_MEDIA_FLAC,
PLAY_MEDIA_MP3,
-} play_media_type;
+} play_media_type_t;
-typedef enum {
+typedef enum play_state {
PLAY_STATE_STOPPED,
PLAY_STATE_PLAYING,
PLAY_STATE_PAUSED,
PLAY_STATE_FFWD,
PLAY_STATE_RRWD,
PLAY_STATE_ERROR,
-} play_state;
+} play_state_t;
-typedef enum {
+typedef enum play_command {
PLAY_COMMAND_STOP,
PLAY_COMMAND_PLAY,
PLAY_COMMAND_PAUSE,
PLAY_COMMAND_RREV,
PLAY_COMMAND_FTRK,
PLAY_COMMAND_RTRK,
-} play_command;
+} play_command_t;
-void play_set_media_type(play_media_type t);
-void play_send_command(play_command c);
+typedef struct play_parameters {
+ char filename[FILENAME_MAX];
+} play_parameters_t;
+
+void play_set_media_type(play_media_type_t t);
+void play_send_command(play_command_t c);
void play_controller();
+void play_file(const char* filename);
+#include <ctype.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "diskio_impl.h"
+#include "diskio_sdmmc.h"
+#include "esp_vfs_fat.h"
+#include "sdmmc_cmd.h"
+
+#include "gfx.h"
+#include "keyboard.h"
+#include "sd.h"
+
+#define TAG "sd"
+
+sdmmc_host_t host = SDSPI_HOST_DEFAULT();
+const char mount_point[] = "/sd";
+FATFS* fs;
+
+bool sd_card_detect() {
+ return !(bool)gpio_get_level(GPIO_NUM_13);
+}
+
+int sd_init() {
+ esp_err_t ret;
+
+ ESP_LOGI(TAG, "Initializing SPI bus");
+
+ spi_bus_config_t bus_cfg = {
+ .mosi_io_num = 23,
+ .miso_io_num = 19,
+ .sclk_io_num = 18,
+ .quadwp_io_num = -1,
+ .quadhd_io_num = -1,
+ .max_transfer_sz = 4000,
+ };
+ ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CH_AUTO);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to initialize bus: %s", esp_err_to_name(ret));
+ return 0;
+ }
+
+ sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
+ slot_config.gpio_cs = 5;
+ slot_config.host_id = host.slot;
+
+ ESP_LOGI(TAG, "Initializing SDSPI device");
+ sdspi_dev_handle_t sdspi;
+ ret = sdspi_host_init_device(&slot_config, &sdspi);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to init SDSPI device: %d", ret);
+ return 0;
+ }
+
+ ret = sdspi_host_set_card_clk(sdspi, 20000);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set SPI card speed: %d", ret);
+ return 0;
+ }
+
+ return 1;
+}
+
+FATFS * sd_card_mount() {
+ esp_err_t ret;
+
+ ESP_LOGI(TAG, "Initializing SD card");
+
+ sdmmc_card_t *card = (sdmmc_card_t *) malloc(sizeof(sdmmc_card_t));
+ if (card == NULL) {
+ ESP_LOGE(TAG, "Could not allocate card struct");
+ }
+
+ host.max_freq_khz = 1600;
+ ret = sdmmc_card_init(&host, card);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to init SD card: %s", esp_err_to_name(ret));
+ return NULL;
+ }
+
+ sdmmc_card_print_info(stdout, card);
+
+ ESP_LOGI(TAG, "Mounting filesystem");
+ BYTE pdrv = 0;
+ ff_diskio_register_sdmmc(pdrv, card);
+ FATFS *fs = NULL;
+ ret = esp_vfs_fat_register(mount_point, "0:", 5, &fs);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to register VFS: %d", ret);
+ return NULL;
+ }
+
+ FRESULT fret = f_mount(fs, "0:", 1);
+ if (fret != FR_OK) {
+ ESP_LOGE(TAG, "Failed to mount FATFS: %d", fret);
+ }
+
+ ESP_LOGI(TAG, "Filesystem mounted on %s", mount_point);
+
+ return fs;
+}
+
+void sd_card_unmount() {
+ esp_err_t ret;
+
+ f_unmount("0:");
+ ret = esp_vfs_fat_unregister_path(mount_point);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "can't unmount: %s not mounted", mount_point);
+ return;
+ }
+ ESP_LOGI(TAG, "%s unmounted", mount_point);
+
+ ff_diskio_register(0, NULL);
+ ESP_LOGI(TAG, "disk driver freed");
+}
+
+FATFS* sd_get() {
+ if (!sd_card_detect()) {
+ sd_card_unmount();
+ fs = NULL;
+ return NULL;
+ }
+
+ if (fs == NULL) {
+ fs = sd_card_mount();
+ }
+ return fs;
+}
+
+bool sd_ready() {
+ return sd_card_detect() && fs != NULL;
+}
+
+void test_sd() {
+ gfx_set_enabled(false);
+
+ char buf[40];
+ key_event_t ke;
+ int cs_state = 1;
+ FATFS *fs = NULL;
+
+ while (ke = key_get_event(0), !(ke.type == KEY_PRESSED && ke.code == KEY_BAND)) {
+ int new_cs_state = gpio_get_level(GPIO_NUM_13);
+ if (!new_cs_state && cs_state) {
+ // card inserted
+ fs = sd_card_mount();
+ if (fs == NULL) {
+ ESP_LOGE(TAG, "Could not initialize SD");
+ cs_state = new_cs_state;
+ continue;
+ }
+
+ DIR *d = opendir("/sd");
+ struct dirent *e;
+ while ((e = readdir(d)) != NULL) {
+ printf("%02x %s\n", e->d_type, e->d_name);
+ }
+ closedir(d);
+ } else if (new_cs_state && !cs_state) {
+ sd_card_unmount();
+ fs = NULL;
+ // card removed
+ }
+ cs_state = new_cs_state;
+
+ u8g2_ClearBuffer(&u8g2);
+
+ sprintf(buf, "CS: %c", cs_state ? 'H' : 'L');
+ u8g2_DrawStr(&u8g2, 0, 6, buf);
+ sprintf(buf, "FS: %p", fs);
+ for (int i = 0; i < strlen(buf); i++) {
+ buf[i] = toupper(buf[i]);
+ }
+ u8g2_DrawStr(&u8g2, 0, 12, buf);
+
+ u8g2_SendBuffer(&u8g2);
+
+ vTaskDelay(100 / portTICK_PERIOD_MS);
+ }
+
+ gfx_set_enabled(true);
+}
+#pragma once
+
+#include "esp_vfs_fat.h"
+
+int sd_init();
+FATFS* sd_get();
+bool sd_ready();
+void test_sd();