big merge

This commit is contained in:
Philippe G
2021-12-18 21:04:23 -08:00
parent 955692f8ad
commit 898998efb0
583 changed files with 84472 additions and 1965 deletions

View File

@@ -8,26 +8,18 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
platform_config
driver_bt
services
spotify
raop
display
tools
audio
EMBED_FILES vu.data
EMBED_FILES vu_s.data arrow.data
)
set_source_files_properties(mad.c
set_source_files_properties(mad.c pcm.c flac.c alac.c helix-aac.c vorbis.c opus.c
PROPERTIES COMPILE_FLAGS
-Wno-maybe-uninitialized
)
set_source_files_properties(pcm.c
PROPERTIES COMPILE_FLAGS
-Wno-maybe-uninitialized
)
set_source_files_properties(flac.c
PROPERTIES COMPILE_FLAGS
-Wno-maybe-uninitialized
)
add_definitions(-DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DCUSTOM_VERSION=${BUILD_NUMBER})

View File

@@ -33,6 +33,7 @@ extern const struct adac_s dac_external;
int adac_init(char *config, int i2c_port);
void adac_deinit(void);
esp_err_t adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count);
esp_err_t adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val);
esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val);
uint8_t adac_read_byte(int i2c_addr, uint8_t reg);

View File

@@ -17,6 +17,11 @@
#include "esp_log.h"
#include "adac.h"
#define PARSE_PARAM(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
} while (0)
static const char TAG[] = "DAC core";
static int i2c_port = -1;
@@ -46,9 +51,9 @@ int adac_init(char *config, int i2c_port_num) {
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "i2c")) != NULL) i2c_addr = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
PARSE_PARAM(config, "i2c", '=', i2c_addr);
PARSE_PARAM(config, "sda", '=', i2c_config.sda_io_num);
PARSE_PARAM(config, "scl", '=', i2c_config.scl_io_num);
if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
ESP_LOGW(TAG, "DAC does not use i2c");
@@ -150,8 +155,7 @@ uint16_t adac_read_word(int i2c_addr, uint8_t reg) {
/****************************************************************************************
*
*/
esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val)
{
esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val) {
uint8_t data[] = { i2c_addr << 1, reg,
val >> 8, val & 0xff };
@@ -169,4 +173,26 @@ esp_err_t adac_write_word(int i2c_addr, uint8_t reg, uint16_t val)
}
return ret;
}
}
/****************************************************************************************
*
*/
esp_err_t adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
i2c_master_write(cmd, data, count, I2C_MASTER_NACK);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_port, cmd, 200 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "I2C write failed");
}
return ret;
}

Binary file not shown.

View File

@@ -73,13 +73,11 @@ void _buf_flush(struct buffer *buf) {
// adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary
void buf_adjust(struct buffer *buf, size_t mod) {
size_t size;
mutex_lock(buf->mutex);
size = ((unsigned)(buf->base_size / mod)) * mod;
buf->readp = buf->buf;
buf->writep = buf->buf;
buf->wrap = buf->buf + size;
buf->size = size;
buf->base_size = ((size_t)(buf->size / mod)) * mod;
buf->readp = buf->writep = buf->buf;
buf->wrap = buf->buf + buf->base_size;
buf->size = buf->base_size;
mutex_unlock(buf->mutex);
}
@@ -90,16 +88,23 @@ void _buf_resize(struct buffer *buf, size_t size) {
buf->buf = malloc(size);
if (!buf->buf) {
size = buf->size;
buf->buf= malloc(size);
if (!buf->buf) {
size = 0;
}
buf->buf = malloc(size);
if (!buf->buf) size = 0;
}
buf->readp = buf->buf;
buf->writep = buf->buf;
buf->writep = buf->readp = buf->buf;
buf->wrap = buf->buf + size;
buf->size = size;
buf->base_size = size;
buf->true_size = buf->base_size = buf->size = size;
}
size_t _buf_limit(struct buffer *buf, size_t limit) {
if (limit) {
buf->size = limit;
buf->readp = buf->writep = buf->buf;
} else {
buf->size = buf->base_size;
}
buf->wrap = buf->buf + buf->size;
return buf->base_size - buf->size;
}
void _buf_unwrap(struct buffer *buf, size_t cont) {
@@ -130,7 +135,9 @@ void _buf_unwrap(struct buffer *buf, size_t cont) {
if (len > by) {
memmove(buf->buf, buf->buf + by, len - by);
buf->writep -= by;
} else buf->writep += buf->size - by;
} else {
buf->writep += buf->size - by;
}
return;
}
@@ -157,8 +164,7 @@ void buf_init(struct buffer *buf, size_t size) {
buf->readp = buf->buf;
buf->writep = buf->buf;
buf->wrap = buf->buf + size;
buf->size = size;
buf->base_size = size;
buf->true_size = buf->base_size = buf->size = size;
mutex_create_p(buf->mutex);
}
@@ -166,8 +172,7 @@ void buf_destroy(struct buffer *buf) {
if (buf->buf) {
free(buf->buf);
buf->buf = NULL;
buf->size = 0;
buf->base_size = 0;
buf->size = buf->base_size = buf->true_size = 0;
mutex_destroy(buf->mutex);
}
}

View File

@@ -218,7 +218,6 @@ static void notify(in_addr_t ip, u16_t hport, u16_t cport) {
// close existing CLI connection and open new one
if (cli_sock >= 0) closesocket(cli_sock);
cli_sock = socket(AF_INET, SOCK_STREAM, 0);
connect_cli_socket();
LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);

View File

@@ -11,24 +11,20 @@
#include "platform_config.h"
#include "squeezelite.h"
#include "bt_app_sink.h"
#include "raop_sink.h"
#include <math.h>
#define LOCK_O mutex_lock(outputbuf->mutex)
#define UNLOCK_O mutex_unlock(outputbuf->mutex)
#define LOCK_D mutex_lock(decode.mutex);
#define UNLOCK_D mutex_unlock(decode.mutex);
enum { DECODE_BT = 1, DECODE_RAOP };
extern struct outputstate output;
extern struct decodestate decode;
extern struct buffer *outputbuf;
// this is the only system-wide loglevel variable
extern log_level loglevel;
#if CONFIG_BT_SINK
#include "bt_app_sink.h"
static bool enable_bt_sink;
#endif
#if CONFIG_CSPOT_SINK
#include "cspot_sink.h"
static bool enable_cspot;
#endif
#if CONFIG_AIRPLAY_SINK
#include "raop_sink.h"
static bool enable_airplay;
#define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME)
@@ -44,6 +40,22 @@ static EXT_RAM_ATTR struct {
s32_t len;
u32_t start_time, playtime;
} raop_sync;
#endif
static bool abort_sink ;
#define LOCK_O mutex_lock(outputbuf->mutex)
#define UNLOCK_O mutex_unlock(outputbuf->mutex)
#define LOCK_D mutex_lock(decode.mutex);
#define UNLOCK_D mutex_unlock(decode.mutex);
enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT };
extern struct outputstate output;
extern struct decodestate decode;
extern struct buffer *outputbuf;
// this is the only system-wide loglevel variable
extern log_level loglevel;
/****************************************************************************************
* Common sink data handler
@@ -58,11 +70,12 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
return;
}
// there will always be room at some point
while (len) {
LOCK_O;
LOCK_O;
abort_sink = false;
// there will always be room at some point
while (len && wait && !abort_sink) {
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
bytes = min(len, bytes);
#if BYTES_PER_FRAME == 4
@@ -81,11 +94,14 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
len -= bytes;
data += bytes;
UNLOCK_O;
// allow i2s to empty the buffer if needed
if (len && !space && wait--) usleep(20000);
if (len && !space) {
wait--;
UNLOCK_O; usleep(50000); LOCK_O;
}
}
UNLOCK_O;
if (!wait) {
LOG_WARN("Waited too long, dropping frames");
@@ -95,12 +111,12 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
/****************************************************************************************
* BT sink command handler
*/
#if CONFIG_BT_SINK
static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use BT sink while LMS/AirPlay is controlling player");
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player");
return false;
}
@@ -110,11 +126,12 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
switch(cmd) {
case BT_SINK_AUDIO_STARTED:
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
output.external = DECODE_BT;
output.state = OUTPUT_STOPPED;
output.frames_played = 0;
_buf_flush(outputbuf);
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("BT sink started");
break;
@@ -127,17 +144,18 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
break;
case BT_SINK_PLAY:
output.state = OUTPUT_RUNNING;
LOG_INFO("BT playing");
LOG_INFO("BT play");
break;
case BT_SINK_STOP:
_buf_flush(outputbuf);
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("BT stopped");
abort_sink = true;
LOG_INFO("BT stop");
break;
case BT_SINK_PAUSE:
output.stop_time = gettime_ms();
LOG_INFO("BT paused, just silence");
LOG_INFO("BT pause, just silence");
break;
case BT_SINK_RATE:
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
@@ -158,10 +176,12 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
return true;
}
#endif
/****************************************************************************************
* raop sink data handler
*/
#if CONFIG_AIRPLAY_SINK
static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t playtime) {
raop_sync.playtime = playtime;
@@ -177,7 +197,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use Airplay sink while LMS/BT is controlling player");
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player");
return false;
}
@@ -237,15 +257,21 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
break;
}
case RAOP_SETUP:
// we need a fair bit of space for RTP process
_buf_resize(outputbuf, RAOP_OUTPUT_SIZE);
case RAOP_SETUP: {
uint8_t **buffer = va_arg(args, uint8_t**);
size_t *size = va_arg(args, size_t*);
// steal buffer tail from outputbuf but do not reallocate
*size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
*buffer = outputbuf->writep + RAOP_OUTPUT_SIZE;
output.frames_played = 0;
output.external = DECODE_RAOP;
output.state = OUTPUT_STOPPED;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("resizing buffer %u", outputbuf->size);
break;
}
case RAOP_STREAM:
LOG_INFO("Stream", NULL);
raop_state = event;
@@ -257,11 +283,11 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
break;
case RAOP_STOP:
case RAOP_FLUSH:
if (event == RAOP_FLUSH) { LOG_INFO("Flush", NULL); }
else { LOG_INFO("Stop", NULL); }
LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
_buf_flush(outputbuf);
raop_state = event;
_buf_flush(outputbuf);
if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
abort_sink = true;
output.frames_played = 0;
output.stop_time = gettime_ms();
break;
@@ -292,6 +318,83 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
UNLOCK_D;
return true;
}
#endif
/****************************************************************************************
* cspot sink command handler
*/
#if CONFIG_CSPOT_SINK
static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player");
return false;
}
LOCK_D;
if (cmd != CSPOT_VOLUME) LOCK_O;
switch(cmd) {
case CSPOT_SETUP:
output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
output.external = DECODE_CSPOT;
output.frames_played = 0;
output.state = OUTPUT_STOPPED;
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("CSpot connected");
break;
case CSPOT_DISC:
_buf_flush(outputbuf);
abort_sink = true;
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot disconnected");
break;
case CSPOT_TRACK:
LOG_INFO("CSpot sink new track rate %d", output.next_sample_rate);
break;
case CSPOT_PLAY:
output.state = OUTPUT_RUNNING;
LOG_INFO("CSpot play");
break;
case CSPOT_SEEK:
//TODO: can it be merged with flush (shall we stop)
_buf_flush(outputbuf);
abort_sink = true;
LOG_INFO("CSpot seek by %d", va_arg(args, int));
break;
case CSPOT_FLUSH:
_buf_flush(outputbuf);
abort_sink = true;
__attribute__ ((fallthrough));
case CSPOT_PAUSE:
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot pause/flush");
break;
case CSPOT_VOLUME: {
u32_t volume = va_arg(args, u32_t);
LOG_INFO("CSpot volume %u", volume);
//volume = 65536 * powf(volume / 32768.0f, 3);
// TODO spotify seems to volume normalize crazy high
volume = 4096 * powf(volume / 32768.0f, 3);
set_volume(volume, volume);
break;
default:
break;
}
}
if (cmd != CSPOT_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
}
#endif
/****************************************************************************************
* We provide the generic codec register option
@@ -299,16 +402,28 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
void register_external(void) {
char *p;
#if CONFIG_BT_SINK
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_bt_sink")) != NULL) {
enable_bt_sink = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
free(p);
}
#endif
#if CONFIG_AIRPLAY_SINK
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_airplay")) != NULL) {
enable_airplay = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
free(p);
}
#endif
#if CONFIG_CSPOT_SINK
if ((p = config_alloc_get(NVS_TYPE_STR, "enable_cspot")) != NULL) {
enable_cspot = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
free(p);
}
#endif
#if CONFIG_BT_SINK
if (!strcasestr(output.device, "BT ") ) {
if(enable_bt_sink){
bt_sink_init(bt_sink_cmd_handler, sink_data_handler);
@@ -317,32 +432,61 @@ void register_external(void) {
} else {
LOG_WARN("Cannot be a BT sink and source");
}
#endif
#if CONFIG_AIRPLAY_SINK
if (enable_airplay){
raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
LOG_INFO("Initializing AirPlay sink");
}
#endif
#if CONFIG_CSPOT_SINK
if (enable_cspot){
cspot_sink_init(cspot_cmd_handler, sink_data_handler);
LOG_INFO("Initializing CSpot sink");
}
#endif
}
void deregister_external(void) {
#if CONFIG_BT_SINK
if (!strcasestr(output.device, "BT ") && enable_bt_sink) {
LOG_INFO("Stopping BT sink");
bt_sink_deinit();
}
#endif
#if CONFIG_AIRPLAY_SINK
if (enable_airplay){
LOG_INFO("Stopping AirPlay sink");
raop_sink_deinit();
}
#endif
#if CONFIG_CSPOT_SINK
if (enable_cspot){
LOG_INFO("Stopping CSpot sink");
cspot_sink_deinit();
}
#endif
}
void decode_restore(int external) {
switch (external) {
#if CONFIG_BT_SINK
case DECODE_BT:
bt_disconnect();
break;
#endif
#if CONFIG_AIRPLAY_SINK
case DECODE_RAOP:
raop_disconnect();
raop_state = RAOP_STOP;
break;
#endif
#if CONFIG_CSPOT_SINK
case DECODE_CSPOT:
cspot_disconnect();
break;
#endif
}
}

View File

@@ -146,6 +146,7 @@ static uint32_t *grayMap;
#define VU_WIDTH 160
#define VU_HEIGHT SB_HEIGHT
#define VU_COUNT 48
#define ARROW_WIDTH 11
#define DISPLAY_BW 20000
@@ -210,7 +211,12 @@ static EXT_RAM_ATTR struct {
struct bar_s bars[MAX_BARS] ;
} led_visu;
extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start");
static EXT_RAM_ATTR uint8_t vu_bitmap[VU_WIDTH * VU_HEIGHT];
extern const uint8_t vu_base[] asm("_binary_vu_s_data_start");
extern const struct {
uint8_t offset;
uint8_t data[VU_HEIGHT * ARROW_WIDTH];
} vu_arrow[VU_COUNT] asm("_binary_arrow_data_start");
#define ANIM_NONE 0x00
#define ANIM_TRANSITION 0x01 // A transition animation has finished
@@ -328,6 +334,9 @@ bool sb_displayer_init(void) {
visu.bar_gap = 1;
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
// prepare the VU raw data in PSRAM
memcpy(vu_bitmap, vu_base, sizeof(vu_bitmap));
// size scroller (width + current screen)
scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1);
scroller.scroll.frame = malloc(scroller.scroll.max);
@@ -601,9 +610,13 @@ static void vfdc_handler( u8_t *_data, int bytes_read) {
/****************************************************************************************
* Display VU-Meter (lots of hard-coding)
*/
void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x, int y, int width, bool rotate) {
void draw_VU(struct GDS_Device * display, int level, int x, int y, int width, bool rotate) {
// VU data is by columns and vertical flip to allow block offset
data += level * VU_WIDTH * VU_HEIGHT;
uint8_t *data = vu_bitmap;
int offset = level > 0 ? vu_arrow[level].offset * VU_HEIGHT : 0;
// place the arrow in base VU
memcpy(data + offset, vu_arrow[level].data, sizeof(vu_arrow[level].data));
// adjust to current display window
if (width > VU_WIDTH) {
@@ -649,6 +662,9 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
}
}
// restore base VU
memcpy(vu_bitmap + offset, vu_base + offset, sizeof(vu_arrow[level].data));
// need to manually set dirty flag as DrawPixel does not do it
GDS_SetDirty(display);
}
@@ -978,15 +994,15 @@ void visu_draw(void) {
}
} else if (displayer.width / 2 >= 3 * VU_WIDTH / 4) {
if (visu.rotate) {
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate);
draw_VU(display, visu.bars[0].current, 0, visu.row, visu.height / 2, visu.rotate);
draw_VU(display, visu.bars[1].current, 0, visu.row + visu.height / 2, visu.height / 2, visu.rotate);
} else {
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, visu.width / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, visu.width / 2, visu.row, visu.width / 2, visu.rotate);
draw_VU(display, visu.bars[0].current, 0, visu.row, visu.width / 2, visu.rotate);
draw_VU(display, visu.bars[1].current, visu.width / 2, visu.row, visu.width / 2, visu.rotate);
}
} else {
int level = (visu.bars[0].current + visu.bars[1].current) / 2;
draw_VU(display, vu_bitmap, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate);
draw_VU(display, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate);
}
}

View File

@@ -20,8 +20,8 @@
static const char TAG[] = "DAC external";
static void speaker(bool active) { }
static void headset(bool active) { }
static void speaker(bool active);
static void headset(bool active);
static bool volume(unsigned left, unsigned right) { return false; }
static void power(adac_power_e mode);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
@@ -95,6 +95,22 @@ static void power(adac_power_e mode) {
else i2c_json_execute("poweron");
}
/****************************************************************************************
* speaker
*/
static void speaker(bool active) {
if (active) i2c_json_execute("speakeron");
else i2c_json_execute("speakeroff");
}
/****************************************************************************************
* headset
*/
static void headset(bool active) {
if (active) i2c_json_execute("headseton");
else i2c_json_execute("headsetoff");
}
/****************************************************************************************
*
*/
@@ -104,25 +120,40 @@ bool i2c_json_execute(char *set) {
if (!json_set) return true;
cJSON_ArrayForEach(item, json_set)
{
cJSON_ArrayForEach(item, json_set) {
cJSON *reg = cJSON_GetObjectItemCaseSensitive(item, "reg");
cJSON *val = cJSON_GetObjectItemCaseSensitive(item, "val");
cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode");
if (cJSON_IsArray(val)) {
cJSON *value;
uint8_t *data = malloc(cJSON_GetArraySize(val));
int count = 0;
if (!data) continue;
cJSON_ArrayForEach(value, val) {
data[count++] = value->valueint;
}
adac_write(i2c_addr, reg->valueint, data, count);
free(data);
} else {
cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode");
if (!reg || !val) continue;
if (!reg || !val) continue;
if (!mode) {
adac_write_byte(i2c_addr, reg->valueint, val->valueint);
} else if (!strcasecmp(mode->valuestring, "or")) {
uint8_t data = adac_read_byte(i2c_addr,reg->valueint);
data |= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, reg->valueint, data);
} else if (!strcasecmp(mode->valuestring, "and")) {
uint8_t data = adac_read_byte(i2c_addr, reg->valueint);
data &= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, reg->valueint, data);
}
if (!mode) {
adac_write_byte(i2c_addr, reg->valueint, val->valueint);
} else if (!strcasecmp(mode->valuestring, "or")) {
uint8_t data = adac_read_byte(i2c_addr,reg->valueint);
data |= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, reg->valueint, data);
} else if (!strcasecmp(mode->valuestring, "and")) {
uint8_t data = adac_read_byte(i2c_addr, reg->valueint);
data &= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, reg->valueint, data);
}
}
}
return true;

View File

@@ -354,7 +354,6 @@ void output_init_common(log_level level, const char *device, unsigned output_buf
loglevel = level;
output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME);
output.init_size = output_buf_size;
LOG_DEBUG("outputbuf size: %u", output_buf_size);
buf_init(outputbuf, output_buf_size);

View File

@@ -81,12 +81,14 @@ void output_init_embedded(log_level level, char *device, unsigned output_buf_siz
output.start_frames = FRAME_BLOCK;
output.rate_delay = rate_delay;
#if CONFIG_BT_SINK
if (strcasestr(device, "BT ") || !strcasecmp(device, "BT")) {
LOG_INFO("init Bluetooth");
close_cb = &output_close_bt;
output_init_bt(level, device, output_buf_size, params, rates, rate_delay, idle);
} else {
} else
#endif
{
LOG_INFO("init I2S/SPDIF");
close_cb = &output_close_i2s;
volume_cb = &output_volume_i2s;

View File

@@ -43,6 +43,7 @@ sure that using rate_delay would fix that
#include "led.h"
#include "monitor.h"
#include "platform_config.h"
#include "gpio_exp.h"
#include "accessors.h"
#include "equalizer.h"
#include "globdefs.h"
@@ -129,8 +130,13 @@ static bool handler(u8_t *data, int len){
jack_mutes_amp = pkt->config == 0;
config_set_value(NVS_TYPE_STR, "jack_mutes_amp", jack_mutes_amp ? "y" : "n");
if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
else adac->speaker(true);
if (jack_mutes_amp && jack_inserted_svc()) {
adac->speaker(false);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, !amp_control.active);
} else {
adac->speaker(true);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, amp_control.active);
}
}
LOG_INFO("got AUDO %02x", pkt->config);
@@ -151,13 +157,12 @@ static void jack_handler(bool inserted) {
// jack detection bounces a bit but that seems fine
if (jack_mutes_amp) {
LOG_INFO("switching amplifier %s", inserted ? "OFF" : "ON");
if (inserted) adac->speaker(false);
else adac->speaker(true);
adac->speaker(!inserted);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, inserted ? !amp_control.active : amp_control.active);
}
// activate headset
if (inserted) adac->headset(true);
else adac->headset(false);
adac->headset(inserted);
// and chain if any
if (jack_handler_chain) (jack_handler_chain)(inserted);
@@ -173,9 +178,9 @@ static void set_amp_gpio(int gpio, char *value) {
amp_control.gpio = gpio;
if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1);
gpio_pad_select_gpio(amp_control.gpio);
gpio_set_direction(amp_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level(amp_control.gpio, !amp_control.active);
gpio_pad_select_gpio_x(amp_control.gpio);
gpio_set_direction_x(amp_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level_x(amp_control.gpio, !amp_control.active);
LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
}
@@ -185,11 +190,10 @@ static void set_amp_gpio(int gpio, char *value) {
* Set pin from config string
*/
static void set_i2s_pin(char *config, i2s_pin_config_t *pin_config) {
char *p;
pin_config->bck_io_num = pin_config->ws_io_num = pin_config->data_out_num = pin_config->data_in_num = -1;
if ((p = strcasestr(config, "bck")) != NULL) pin_config->bck_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "ws")) != NULL) pin_config->ws_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "do")) != NULL) pin_config->data_out_num = atoi(strchr(p, '=') + 1);
PARSE_PARAM(config, "bck", '=', pin_config->bck_io_num);
PARSE_PARAM(config, "ws", '=', pin_config->ws_io_num);
PARSE_PARAM(config, "do", '=', pin_config->data_out_num);
}
/****************************************************************************************
@@ -343,12 +347,13 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
jack_handler_chain = jack_handler_svc;
jack_handler_svc = jack_handler;
parse_set_GPIO(set_amp_gpio);
if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
else adac->speaker(true);
adac->headset(jack_inserted_svc());
parse_set_GPIO(set_amp_gpio);
// create task as a FreeRTOS task but uses stack in internal RAM
{
@@ -365,10 +370,11 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
// memory still used but at least task is not created
if (stats) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
// we allocate TCB but stack is static to avoid SPIRAM fragmentation
StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
static EXT_RAM_ATTR StackType_t xStack[STAT_STACK_SIZE] __attribute__ ((aligned (4)));
stats_task = xTaskCreateStatic( (TaskFunction_t) output_thread_i2s_stats, "output_i2s_sts", STAT_STACK_SIZE,
NULL, ESP_TASK_PRIO_MIN, xStack, &xTaskBuffer);
NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer);
}
}
@@ -449,13 +455,16 @@ static void output_thread_i2s(void *arg) {
LOG_INFO("Output state is %d", output.state);
if (output.state == OUTPUT_OFF) {
led_blink(LED_GREEN, 100, 2500);
if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, !amp_control.active);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, !amp_control.active);
LOG_INFO("switching off amp GPIO %d", amp_control.gpio);
} else if (output.state == OUTPUT_STOPPED) {
adac->speaker(false);
led_blink(LED_GREEN, 200, 1000);
} else if (output.state == OUTPUT_RUNNING) {
if (!jack_mutes_amp || !jack_inserted_svc()) adac->speaker(true);
if (!jack_mutes_amp || !jack_inserted_svc()) {
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, amp_control.active);
adac->speaker(true);
}
led_on(LED_GREEN);
}
}
@@ -513,7 +522,6 @@ static void output_thread_i2s(void *arg) {
i2s_zero_dma_buffer(CONFIG_I2S_NUM);
i2s_start(CONFIG_I2S_NUM);
adac->power(ADAC_ON);
if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, amp_control.active);
}
// this does not work well as set_sample_rates resets the fifos (and it's too early)

View File

@@ -391,7 +391,7 @@ static void process_strm(u8_t *pkt, int len) {
#if EMBEDDED
if (output.external) decode_restore(output.external);
output.external = 0;
_buf_resize(outputbuf, output.init_size);
_buf_limit(outputbuf, 0);
#endif
output.threshold = strm->output_threshold;
output.next_replay_gain = unpackN(&strm->replay_gain);

View File

@@ -532,6 +532,7 @@ struct buffer {
u8_t *wrap;
size_t size;
size_t base_size;
size_t true_size;
mutex_type mutex;
};
@@ -547,6 +548,7 @@ void _buf_flush(struct buffer *buf);
void _buf_unwrap(struct buffer *buf, size_t cont);
void buf_adjust(struct buffer *buf, size_t mod);
void _buf_resize(struct buffer *buf, size_t size);
size_t _buf_limit(struct buffer *buf, size_t limit);
void buf_init(struct buffer *buf, size_t size);
void buf_destroy(struct buffer *buf);
@@ -665,7 +667,6 @@ struct outputstate {
u8_t channels;
const char *device;
int external;
u32_t init_size;
#if ALSA
unsigned buffer;
unsigned period;

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.