mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 12:07:09 +03:00
big merge
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user