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

@@ -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
}
}