mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-08 04:27:12 +03:00
refactor step 1 - BT
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -65,3 +65,4 @@ libs/
|
|||||||
|
|
||||||
/cdump.cmd
|
/cdump.cmd
|
||||||
/_codecs
|
/_codecs
|
||||||
|
sdkconfig
|
||||||
|
|||||||
@@ -1,54 +1,32 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
#include "esp_log.h"
|
||||||
#include "esp_bt.h"
|
#include "esp_bt.h"
|
||||||
#include "esp_bt_device.h"
|
#include "esp_bt_device.h"
|
||||||
#include "esp_bt_main.h"
|
#include "esp_bt_main.h"
|
||||||
#include "esp_gap_bt_api.h"
|
#include "esp_gap_bt_api.h"
|
||||||
#include "esp_a2dp_api.h"
|
#include "esp_a2dp_api.h"
|
||||||
#include "esp_avrc_api.h"
|
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
#include "esp_pthread.h"
|
#include "esp_pthread.h"
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/event_groups.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/timers.h"
|
#include "freertos/timers.h"
|
||||||
#include "nvs.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "nvs_utilities.h"
|
|
||||||
#include "pthread.h"
|
|
||||||
#include "string.h"
|
|
||||||
//#include "esp_event.h"
|
|
||||||
#include "sys/socket.h"
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include "platform_esp32.h"
|
|
||||||
#include "../../main/squeezelite.h"
|
|
||||||
#include "argtable3/argtable3.h"
|
#include "argtable3/argtable3.h"
|
||||||
|
|
||||||
#define STATS_REPORT_DELAY_MS 15000
|
#include "bt_app_core.h"
|
||||||
|
|
||||||
|
#include "platform_esp32.h"
|
||||||
|
|
||||||
static const char * TAG = "platform";
|
static const char * TAG = "platform";
|
||||||
extern char * get_output_state_desc(output_state state);
|
|
||||||
|
|
||||||
extern struct outputstate output;
|
extern int32_t output_bt_data(uint8_t *data, int32_t len);
|
||||||
extern struct buffer *outputbuf;
|
extern void output_bt_tick(void);
|
||||||
extern struct buffer *streambuf;
|
extern char* output_state_str(void);
|
||||||
extern uint8_t * btout;
|
extern bool output_stopped(void);
|
||||||
time_t disconnect_time=0;
|
|
||||||
#define LOCK_S pthread_mutex_lock(&(streambuf->mutex))
|
|
||||||
#define UNLOCK_S pthread_mutex_unlock(&(streambuf->mutex))
|
|
||||||
|
|
||||||
#define LOCK pthread_mutex_lock(&(outputbuf->mutex))
|
|
||||||
#define UNLOCK pthread_mutex_unlock(&(outputbuf->mutex))
|
|
||||||
int64_t connecting_timeout = 0;
|
int64_t connecting_timeout = 0;
|
||||||
|
|
||||||
static const char * art_a2dp_connected[]={"\n",
|
static const char * art_a2dp_connected[]={"\n",
|
||||||
@@ -76,34 +54,8 @@ static void bt_app_av_state_connecting(uint16_t event, void *param);
|
|||||||
#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000)
|
#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000)
|
||||||
#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout
|
#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout
|
||||||
|
|
||||||
#define FRAME_TO_BYTES(f) f*BYTES_PER_FRAME
|
|
||||||
#define BYTES_TO_FRAME(b) b/BYTES_PER_FRAME
|
|
||||||
|
|
||||||
|
|
||||||
#define RESET_ALL_MIN_MAX RESET_MIN_MAX(req); RESET_MIN_MAX(rec); RESET_MIN_MAX(bt);RESET_MIN_MAX(under); RESET_MIN_MAX_DURATION(stream_buf); RESET_MIN_MAX_DURATION(lock_out_time)
|
|
||||||
|
|
||||||
DECLARE_MIN_MAX(stream_buf);
|
|
||||||
DECLARE_MIN_MAX(req);
|
|
||||||
DECLARE_MIN_MAX(rec);
|
|
||||||
DECLARE_MIN_MAX(bt);
|
|
||||||
DECLARE_MIN_MAX(under);
|
|
||||||
DECLARE_MIN_MAX_DURATION(lock_out_time);
|
|
||||||
|
|
||||||
static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param);
|
static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param);
|
||||||
|
|
||||||
void get_mac(u8_t mac[]) {
|
|
||||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
|
||||||
}
|
|
||||||
|
|
||||||
_sig_func_ptr signal(int sig, _sig_func_ptr func) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *audio_calloc(size_t nmemb, size_t size) {
|
|
||||||
return calloc(nmemb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* event for handler "bt_av_hdl_stack_up */
|
/* event for handler "bt_av_hdl_stack_up */
|
||||||
enum {
|
enum {
|
||||||
BT_APP_EVT_STACK_UP = 0,
|
BT_APP_EVT_STACK_UP = 0,
|
||||||
@@ -181,16 +133,12 @@ static struct {
|
|||||||
void hal_bluetooth_init(const char * options)
|
void hal_bluetooth_init(const char * options)
|
||||||
{
|
{
|
||||||
ESP_LOGD(TAG,"Initializing Bluetooth HAL");
|
ESP_LOGD(TAG,"Initializing Bluetooth HAL");
|
||||||
//CONFIG_A2DP_SINK_NAME
|
|
||||||
//CONFIG_A2DP_CONTROL_DELAY_MS
|
|
||||||
//CONFIG_A2DP_CONNECT_TIMEOUT_MS
|
|
||||||
|
|
||||||
squeezelite_args.sink_name = arg_str1("n", "name", "<sink name>", "the name of the bluetooth to connect to");
|
squeezelite_args.sink_name = arg_str1("n", "name", "<sink name>", "the name of the bluetooth to connect to");
|
||||||
squeezelite_args.control_delay = arg_int0("d", "delay", "<control delay>", "the delay between each pass at the A2DP control loop");
|
squeezelite_args.control_delay = arg_int0("d", "delay", "<control delay>", "the delay between each pass at the A2DP control loop");
|
||||||
squeezelite_args.connect_timeout_delay = arg_int0("t","timeout", "<timeout>", "the timeout duration for connecting to the A2DP sink");
|
squeezelite_args.connect_timeout_delay = arg_int0("t","timeout", "<timeout>", "the timeout duration for connecting to the A2DP sink");
|
||||||
squeezelite_args.end = arg_end(2);
|
squeezelite_args.end = arg_end(2);
|
||||||
|
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Copying parameters");
|
ESP_LOGD(TAG,"Copying parameters");
|
||||||
char * opts = strdup(options);
|
char * opts = strdup(options);
|
||||||
char **argv = malloc(sizeof(char**)*15);
|
char **argv = malloc(sizeof(char**)*15);
|
||||||
@@ -280,42 +228,7 @@ void hal_bluetooth_init(const char * options)
|
|||||||
esp_bt_gap_set_pin(pin_type, 0, pin_code);
|
esp_bt_gap_set_pin(pin_type, 0, pin_code);
|
||||||
|
|
||||||
}
|
}
|
||||||
static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len)
|
|
||||||
{
|
|
||||||
int32_t avail_data=0,wanted_len=0, start_timer=0;
|
|
||||||
|
|
||||||
if (len < 0 || data == NULL ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
btout=data;
|
|
||||||
|
|
||||||
// This is how the BTC layer calculates the number of bytes to
|
|
||||||
// for us to send. (BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16) - availPcmBytes
|
|
||||||
wanted_len=len;
|
|
||||||
SET_MIN_MAX(len,req);
|
|
||||||
TIME_MEASUREMENT_START(start_timer);
|
|
||||||
LOCK;
|
|
||||||
output.device_frames = 0; // todo: check if this is the right way do to this.
|
|
||||||
output.updated = gettime_ms();
|
|
||||||
output.frames_played_dmp = output.frames_played;
|
|
||||||
SET_MIN_MAX_SIZED(_buf_used(outputbuf),bt,outputbuf->size);
|
|
||||||
do {
|
|
||||||
avail_data = _output_frames( wanted_len/BYTES_PER_FRAME )*BYTES_PER_FRAME; // Keep the transfer buffer full
|
|
||||||
wanted_len-=avail_data;
|
|
||||||
} while (wanted_len > 0 && avail_data != 0);
|
|
||||||
if(wanted_len>0)
|
|
||||||
{
|
|
||||||
SET_MIN_MAX(wanted_len, under);
|
|
||||||
}
|
|
||||||
|
|
||||||
UNLOCK;
|
|
||||||
SET_MIN_MAX(TIME_MEASUREMENT_GET(start_timer),lock_out_time);
|
|
||||||
SET_MIN_MAX((len-wanted_len), rec);
|
|
||||||
TIME_MEASUREMENT_START(start_timer);
|
|
||||||
output_bt_check_buffer();
|
|
||||||
|
|
||||||
return len-wanted_len;
|
|
||||||
}
|
|
||||||
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
|
||||||
{
|
{
|
||||||
bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL);
|
||||||
@@ -415,10 +328,10 @@ static void bt_app_av_sm_hdlr(uint16_t event, void *param)
|
|||||||
{
|
{
|
||||||
switch (s_a2d_state) {
|
switch (s_a2d_state) {
|
||||||
case APP_AV_STATE_DISCOVERING:
|
case APP_AV_STATE_DISCOVERING:
|
||||||
ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", APP_AV_STATE_DESC[s_a2d_state], event, get_output_state_desc(output.state));
|
ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", APP_AV_STATE_DESC[s_a2d_state], event, output_state_str());
|
||||||
break;
|
break;
|
||||||
case APP_AV_STATE_DISCOVERED:
|
case APP_AV_STATE_DISCOVERED:
|
||||||
ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", APP_AV_STATE_DESC[s_a2d_state], event, get_output_state_desc(output.state));
|
ESP_LOGV(TAG,"state %s, evt 0x%x, output state: %s", APP_AV_STATE_DESC[s_a2d_state], event, output_state_str());
|
||||||
break;
|
break;
|
||||||
case APP_AV_STATE_UNCONNECTED:
|
case APP_AV_STATE_UNCONNECTED:
|
||||||
bt_app_av_state_unconnected(event, param);
|
bt_app_av_state_unconnected(event, param);
|
||||||
@@ -562,7 +475,6 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -579,7 +491,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
|||||||
|
|
||||||
/* initialize A2DP source */
|
/* initialize A2DP source */
|
||||||
esp_a2d_register_callback(&bt_app_a2d_cb);
|
esp_a2d_register_callback(&bt_app_a2d_cb);
|
||||||
esp_a2d_source_register_data_callback(bt_app_a2d_data_cb);
|
esp_a2d_source_register_data_callback(&output_bt_data);
|
||||||
esp_a2d_source_init();
|
esp_a2d_source_init();
|
||||||
|
|
||||||
/* set discoverable and connectable mode */
|
/* set discoverable and connectable mode */
|
||||||
@@ -605,39 +517,15 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BTAUDIO
|
|
||||||
bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
|
|
||||||
|
|
||||||
// running_test = true;
|
|
||||||
// while(running_test)
|
|
||||||
// {
|
|
||||||
// // wait until BT playback has started
|
|
||||||
// // this will allow querying the sample rate
|
|
||||||
// usleep(100000);
|
|
||||||
// }
|
|
||||||
|
|
||||||
memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
|
|
||||||
if (!strcmp(device, "BT")) {
|
|
||||||
rates[0] = 44100;
|
|
||||||
} else {
|
|
||||||
unsigned _rates[] = { 96000, 88200, 48000, 44100, 32000, 0 };
|
|
||||||
memcpy(rates, _rates, sizeof(_rates));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
static void bt_app_av_media_proc(uint16_t event, void *param)
|
static void bt_app_av_media_proc(uint16_t event, void *param)
|
||||||
{
|
{
|
||||||
esp_a2d_cb_param_t *a2d = NULL;
|
esp_a2d_cb_param_t *a2d = NULL;
|
||||||
LOCK;
|
|
||||||
output_state out_state=output.state;
|
|
||||||
UNLOCK;
|
|
||||||
switch (s_media_state) {
|
switch (s_media_state) {
|
||||||
case APP_AV_MEDIA_STATE_IDLE: {
|
case APP_AV_MEDIA_STATE_IDLE: {
|
||||||
if (event == BT_APP_HEART_BEAT_EVT) {
|
if (event == BT_APP_HEART_BEAT_EVT) {
|
||||||
if(out_state > OUTPUT_STOPPED)
|
if(!output_stopped())
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG,"Output state is %s, Checking if A2DP is ready.", get_output_state_desc(out_state));
|
ESP_LOGI(TAG,"Output state is %s, Checking if A2DP is ready.", output_state_str());
|
||||||
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY);
|
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,40 +559,12 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
|
|||||||
}
|
}
|
||||||
case APP_AV_MEDIA_STATE_STARTED: {
|
case APP_AV_MEDIA_STATE_STARTED: {
|
||||||
if (event == BT_APP_HEART_BEAT_EVT) {
|
if (event == BT_APP_HEART_BEAT_EVT) {
|
||||||
if(out_state <= OUTPUT_STOPPED) {
|
if(output_stopped()) {
|
||||||
ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", get_output_state_desc(out_state));
|
ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", output_state_str());
|
||||||
s_media_state = APP_AV_MEDIA_STATE_STOPPING;
|
s_media_state = APP_AV_MEDIA_STATE_STOPPING;
|
||||||
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
|
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
|
||||||
}
|
} else {
|
||||||
else
|
output_bt_tick();
|
||||||
{
|
|
||||||
LOCK_S;
|
|
||||||
SET_MIN_MAX_SIZED(_buf_used(streambuf),stream_buf,streambuf->size);
|
|
||||||
UNLOCK_S;
|
|
||||||
static time_t lastTime=0;
|
|
||||||
if (lastTime <= gettime_ms() )
|
|
||||||
{
|
|
||||||
lastTime = gettime_ms() + 15000;
|
|
||||||
ESP_LOGD(TAG, "Statistics over %u secs. " , STATS_REPORT_DELAY_MS/1000);
|
|
||||||
ESP_LOGD(TAG, " +==========+==========+================+=====+================+");
|
|
||||||
ESP_LOGD(TAG, " | max | min | average | avg | count |");
|
|
||||||
ESP_LOGD(TAG, " | (bytes) | (bytes) | (bytes) | pct | |");
|
|
||||||
ESP_LOGD(TAG, " +==========+==========+================+=====+================+");
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("stream avl",stream_buf));
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("output avl",bt));
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("requested",req));
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("received",rec));
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("underrun",under));
|
|
||||||
ESP_LOGD(TAG, " +==========+==========+================+=====+================+");
|
|
||||||
ESP_LOGD(TAG,"\n");
|
|
||||||
ESP_LOGD(TAG," ==========+==========+===========+===========+ ");
|
|
||||||
ESP_LOGD(TAG," max (us) | min (us) | avg(us) | count | ");
|
|
||||||
ESP_LOGD(TAG," ==========+==========+===========+===========+ ");
|
|
||||||
ESP_LOGD(TAG,LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("Out Buf Lock",lock_out_time));
|
|
||||||
ESP_LOGD(TAG," ==========+==========+===========+===========+");
|
|
||||||
RESET_ALL_MIN_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -716,16 +576,7 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
|
|||||||
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
|
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
|
||||||
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
|
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
|
||||||
ESP_LOGI(TAG,"a2dp media stopped successfully...");
|
ESP_LOGI(TAG,"a2dp media stopped successfully...");
|
||||||
//s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT;
|
|
||||||
// if(CONFIG_A2DP_DISCONNECT_MS==0){
|
|
||||||
// we're not going to disconnect.
|
|
||||||
s_media_state = APP_AV_MEDIA_STATE_IDLE;
|
s_media_state = APP_AV_MEDIA_STATE_IDLE;
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// disconnect_time = gettime_ms()+CONFIG_A2DP_DISCONNECT_MS;
|
|
||||||
// s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT;
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG,"a2dp media stopping...");
|
ESP_LOGI(TAG,"a2dp media stopping...");
|
||||||
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
|
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
|
||||||
@@ -735,20 +586,15 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case APP_AV_MEDIA_STATE_WAIT_DISCONNECT:{
|
case APP_AV_MEDIA_STATE_WAIT_DISCONNECT:{
|
||||||
if(gettime_ms()>disconnect_time){
|
|
||||||
// we've reached timeout
|
|
||||||
esp_a2d_source_disconnect(s_peer_bda);
|
esp_a2d_source_disconnect(s_peer_bda);
|
||||||
s_a2d_state = APP_AV_STATE_DISCONNECTING;
|
s_a2d_state = APP_AV_STATE_DISCONNECTING;
|
||||||
}
|
ESP_LOGI(TAG,"a2dp disconnecting...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
||||||
{
|
{
|
||||||
// LOCK;
|
|
||||||
// output_state out_state= output.state;
|
|
||||||
// UNLOCK;
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ESP_A2D_CONNECTION_STATE_EVT:
|
case ESP_A2D_CONNECTION_STATE_EVT:
|
||||||
ESP_LOG_DEBUG_EVENT(TAG,QUOTE(ESP_A2D_CONNECTION_STATE_EVT));
|
ESP_LOG_DEBUG_EVENT(TAG,QUOTE(ESP_A2D_CONNECTION_STATE_EVT));
|
||||||
@@ -772,8 +618,6 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
|||||||
ESP_LOG_DEBUG_EVENT(TAG,QUOTE(ESP_A2D_MEDIA_CTRL_ACK_EVT));
|
ESP_LOG_DEBUG_EVENT(TAG,QUOTE(ESP_A2D_MEDIA_CTRL_ACK_EVT));
|
||||||
break;
|
break;
|
||||||
case BT_APP_HEART_BEAT_EVT: {
|
case BT_APP_HEART_BEAT_EVT: {
|
||||||
// uint8_t *p = s_peer_bda;
|
|
||||||
// ESP_LOGI(TAG,"BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x",p[0], p[1], p[2], p[3], p[4], p[5]);
|
|
||||||
switch (esp_bluedroid_get_status()) {
|
switch (esp_bluedroid_get_status()) {
|
||||||
case ESP_BLUEDROID_STATUS_UNINITIALIZED:
|
case ESP_BLUEDROID_STATUS_UNINITIALIZED:
|
||||||
ESP_LOGV(TAG,"BlueDroid Status is ESP_BLUEDROID_STATUS_UNINITIALIZED.");
|
ESP_LOGV(TAG,"BlueDroid Status is ESP_BLUEDROID_STATUS_UNINITIALIZED.");
|
||||||
@@ -787,8 +631,6 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// if(out_state > OUTPUT_STOPPED){
|
|
||||||
// only attempt a connect when playback isn't stopped
|
|
||||||
for(uint8_t l=0;art_a2dp_connecting[l][0]!='\0';l++){
|
for(uint8_t l=0;art_a2dp_connecting[l][0]!='\0';l++){
|
||||||
ESP_LOGI(TAG,"%s",art_a2dp_connecting[l]);
|
ESP_LOGI(TAG,"%s",art_a2dp_connecting[l]);
|
||||||
}
|
}
|
||||||
@@ -801,7 +643,6 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
|||||||
// there was an issue connecting... continue to discover
|
// there was an issue connecting... continue to discover
|
||||||
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
|
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
|
||||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -929,17 +770,3 @@ static void bt_app_av_state_disconnecting(uint16_t event, void *param)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const char *loc_logtime(void) {
|
|
||||||
static char buf[100];
|
|
||||||
#if WIN
|
|
||||||
SYSTEMTIME lt;
|
|
||||||
GetLocalTime(<);
|
|
||||||
sprintf(buf, "[%02d:%02d:%02d.%03d]", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds);
|
|
||||||
#else
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
strftime(buf, sizeof(buf), "[%T.", localtime(&tv.tv_sec));
|
|
||||||
sprintf(buf+strlen(buf), "%06ld]", (long)tv.tv_usec);
|
|
||||||
#endif
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
|
#include "esp_pthread.h"
|
||||||
#include "argtable3/argtable3.h"
|
#include "argtable3/argtable3.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/event_groups.h"
|
#include "freertos/event_groups.h"
|
||||||
|
|||||||
@@ -6,5 +6,4 @@
|
|||||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||||
# please read the SDK documents if you need to do this.
|
# please read the SDK documents if you need to do this.
|
||||||
#
|
#
|
||||||
CFLAGS += -Os -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4
|
|
||||||
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||||
|
|||||||
@@ -1,147 +1,27 @@
|
|||||||
/* Scan Example
|
|
||||||
|
|
||||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, this
|
|
||||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
||||||
CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This example shows how to use the All Channel Scan or Fast Scan to connect
|
* Squeezelite for esp32
|
||||||
to a Wi-Fi network.
|
*
|
||||||
|
* (c) Sebastien 2019
|
||||||
|
* Philippe G. 2019, philippe_44@outlook.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
In the Fast Scan mode, the scan will stop as soon as the first network matching
|
|
||||||
the SSID is found. In this mode, an application can set threshold for the
|
|
||||||
authentication mode and the Signal strength. Networks that do not meet the
|
|
||||||
threshold requirements will be ignored.
|
|
||||||
|
|
||||||
In the All Channel Scan mode, the scan will end only after all the channels
|
|
||||||
are scanned, and connection will start with the best network. The networks
|
|
||||||
can be sorted based on Authentication Mode or Signal Strength. The priority
|
|
||||||
for the Authentication mode is: WPA2 > WPA > WEP > Open
|
|
||||||
*/
|
|
||||||
#include "platform_esp32.h"
|
#include "platform_esp32.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "esp_bt.h"
|
|
||||||
#include "esp_bt_device.h"
|
|
||||||
#include "esp_bt_main.h"
|
|
||||||
#include "esp_gap_bt_api.h"
|
|
||||||
#include "esp_a2dp_api.h"
|
|
||||||
#include "esp_avrc_api.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_pthread.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/event_groups.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include "freertos/timers.h"
|
|
||||||
#include "nvs.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "nvs_utilities.h"
|
|
||||||
#include "pthread.h"
|
|
||||||
#include "string.h"
|
|
||||||
#include "sys/socket.h"
|
|
||||||
#include <signal.h>
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
/*Set the SSID and Password via "make menuconfig"*/
|
|
||||||
#define DEFAULT_SSID CONFIG_WIFI_SSID
|
|
||||||
#define DEFAULT_PWD CONFIG_WIFI_PASSWORD
|
|
||||||
|
|
||||||
#if CONFIG_WIFI_ALL_CHANNEL_SCAN
|
|
||||||
#define DEFAULT_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
|
|
||||||
#elif CONFIG_WIFI_FAST_SCAN
|
|
||||||
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
|
|
||||||
#else
|
|
||||||
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
|
|
||||||
#endif /*CONFIG_SCAN_METHOD*/
|
|
||||||
|
|
||||||
#if CONFIG_WIFI_CONNECT_AP_BY_SIGNAL
|
|
||||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
|
|
||||||
#elif CONFIG_WIFI_CONNECT_AP_BY_SECURITY
|
|
||||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY
|
|
||||||
#else
|
|
||||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
|
|
||||||
#endif /*CONFIG_SORT_METHOD*/
|
|
||||||
|
|
||||||
#if CONFIG_FAST_SCAN_THRESHOLD
|
|
||||||
#define DEFAULT_RSSI CONFIG_FAST_SCAN_MINIMUM_SIGNAL
|
|
||||||
#if CONFIG_EXAMPLE_OPEN
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
|
||||||
#elif CONFIG_EXAMPLE_WEP
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WEP
|
|
||||||
#elif CONFIG_EXAMPLE_WPA
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA_PSK
|
|
||||||
#elif CONFIG_EXAMPLE_WPA2
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA2_PSK
|
|
||||||
#else
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define DEFAULT_RSSI -127
|
|
||||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
|
||||||
#endif /*CONFIG_FAST_SCAN_THRESHOLD*/
|
|
||||||
extern char current_namespace[];
|
|
||||||
static const char * TAG = "platform_esp32";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//static void event_handler(void* arg, esp_event_base_t event_base,
|
|
||||||
// int32_t event_id, void* event_data)
|
|
||||||
//{
|
|
||||||
// if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
|
||||||
// esp_wifi_connect();
|
|
||||||
// } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
||||||
// esp_wifi_connect();
|
|
||||||
// } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
|
||||||
// ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
|
||||||
// ESP_LOGI(TAG, "got ip: %s.", ip4addr_ntoa(&event->ip_info.ip));
|
|
||||||
// ESP_LOGD(TAG,"Signaling wifi connected. Locking.\n");
|
|
||||||
// pthread_mutex_lock(&wifi_connect_suspend_mutex);
|
|
||||||
// ESP_LOGD(TAG,"Signaling wifi connected. Broadcasting.\n");
|
|
||||||
// pthread_cond_broadcast(&wifi_connect_suspend_cond);
|
|
||||||
// ESP_LOGD(TAG,"Signaling wifi connected. Unlocking.\n");
|
|
||||||
// pthread_mutex_unlock(&wifi_connect_suspend_mutex);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
///* Initialize Wi-Fi as sta and set scan method */
|
|
||||||
//static void wifi_scan(void)
|
|
||||||
//{
|
|
||||||
//
|
|
||||||
// tcpip_adapter_init();
|
|
||||||
// ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
||||||
//
|
|
||||||
// wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
// ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
||||||
//
|
|
||||||
// ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
|
||||||
// ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
|
|
||||||
//
|
|
||||||
// wifi_config_t wifi_config = {
|
|
||||||
// .sta = {
|
|
||||||
// .ssid = DEFAULT_SSID,
|
|
||||||
// .password = DEFAULT_PWD,
|
|
||||||
// .scan_method = DEFAULT_SCAN_METHOD,
|
|
||||||
// .sort_method = DEFAULT_SORT_METHOD,
|
|
||||||
// .threshold.rssi = DEFAULT_RSSI,
|
|
||||||
// .threshold.authmode = DEFAULT_AUTHMODE,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
||||||
// ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
|
|
||||||
// ESP_ERROR_CHECK(esp_wifi_start());
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
void app_main()
|
void app_main()
|
||||||
{
|
{
|
||||||
|
|
||||||
console_start();
|
console_start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite for esp32
|
||||||
|
*
|
||||||
|
* (c) Sebastien 2019
|
||||||
|
* Philippe G. 2019, philippe_44@outlook.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "time.h"
|
|
||||||
#include "sys/time.h"
|
#include "sys/time.h"
|
||||||
#include "esp_system.h"
|
|
||||||
#define PERF_MAX LONG_MAX
|
#define PERF_MAX LONG_MAX
|
||||||
#define MIN_MAX_VAL(x) x==PERF_MAX?0:x
|
#define MIN_MAX_VAL(x) x==PERF_MAX?0:x
|
||||||
#define CURR_SAMPLE_RATE output.current_sample_rate>0?output.current_sample_rate:1
|
#define CURR_SAMPLE_RATE output.current_sample_rate>0?output.current_sample_rate:1
|
||||||
|
|||||||
@@ -1,93 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite for esp32
|
||||||
|
*
|
||||||
|
* (c) Sebastien 2019
|
||||||
|
* Philippe G. 2019, philippe_44@outlook.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
#include "bt_app_core.h"
|
|
||||||
#include "perf_trace.h"
|
|
||||||
#include "esp_pthread.h"
|
#include "esp_pthread.h"
|
||||||
|
|
||||||
#ifndef QUOTE
|
#ifndef QUOTE
|
||||||
#define QUOTE(name) #name
|
#define QUOTE(name) #name
|
||||||
#define STR(macro) QUOTE(macro)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e)
|
||||||
|
|
||||||
extern void run_command(char * line);
|
extern void run_command(char * line);
|
||||||
extern bool wait_for_wifi();
|
extern bool wait_for_wifi();
|
||||||
|
|
||||||
//typedef struct {
|
|
||||||
// char opt_slimproto_logging[11];
|
|
||||||
// char opt_stream_logging[11];
|
|
||||||
// char opt_decode_logging[11];
|
|
||||||
// char opt_output_logging[11];
|
|
||||||
// char opt_player_name[11];
|
|
||||||
// char opt_output_rates[21];
|
|
||||||
// char opt_buffer[11];
|
|
||||||
//} str_squeezelite_options ;
|
|
||||||
extern void console_start();
|
extern void console_start();
|
||||||
extern pthread_cond_t wifi_connect_suspend_cond;
|
extern pthread_cond_t wifi_connect_suspend_cond;
|
||||||
extern pthread_t wifi_connect_suspend_mutex;
|
extern pthread_t wifi_connect_suspend_mutex;
|
||||||
//static const char * art_wifi[]={
|
|
||||||
// "\n",
|
|
||||||
// "o `O ooOoOOo OOooOoO ooOoOOo\n",
|
|
||||||
// "O o O o O \n",
|
|
||||||
// "o O o O o \n",
|
|
||||||
// "O O O oOooO O \n",
|
|
||||||
// "o o o o O o \n",
|
|
||||||
// "O O O O o O \n",
|
|
||||||
// "`o O o O' O o O \n",
|
|
||||||
// " `OoO' `OoO' ooOOoOo O' ooOOoOo\n",
|
|
||||||
// "\n",
|
|
||||||
// ""
|
|
||||||
//};
|
|
||||||
//static const char * art_wifi_connecting[]={
|
|
||||||
// " .oOOOo.",
|
|
||||||
// ".O o o \n",
|
|
||||||
// "o O \n",
|
|
||||||
// "o oOo \n",
|
|
||||||
// "o .oOo. 'OoOo. 'OoOo. .oOo. .oOo o O 'OoOo. .oOoO \n",
|
|
||||||
// "O O o o O o O OooO' O O o o O o O \n",
|
|
||||||
// "`o .o o O O o O o O o o O O o O o \n",
|
|
||||||
// " `OoooO' `OoO' o O o O `OoO' `OoO' `oO o' o O `OoOo \n",
|
|
||||||
// " O \n",
|
|
||||||
// " OoO' \n",
|
|
||||||
// "\n",
|
|
||||||
// ""
|
|
||||||
//};
|
|
||||||
//static const char * art_wifi_connected[]={
|
|
||||||
// " .oOOOo. o oO\n",
|
|
||||||
// ".O o O OO\n",
|
|
||||||
// "o O o oO\n",
|
|
||||||
// "o oOo o Oo\n",
|
|
||||||
// "o .oOo. 'OoOo. 'OoOo. .oOo. .oOo o .oOo. .oOoO oO\n",
|
|
||||||
// "O O o o O o O OooO' O O OooO' o O \n",
|
|
||||||
// "`o .o o O O o O o O o o O O o Oo\n",
|
|
||||||
// " `OoooO' `OoO' o O o O `OoO' `OoO' `oO `OoO' `OoO'o oO\n",
|
|
||||||
// "\n",
|
|
||||||
// ""
|
|
||||||
//};
|
|
||||||
#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e)
|
|
||||||
const char *loc_logtime(void);
|
|
||||||
//#define MY_ESP_LOG
|
|
||||||
#ifdef MY_ESP_LOG
|
|
||||||
#ifdef ESP_LOGI
|
|
||||||
#undef ESP_LOGI
|
|
||||||
#define ESP_LOGI(tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, tag, "%s %d " format,loc_logtime(), __LINE__, ##__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
#ifdef ESP_LOGE
|
|
||||||
#undef ESP_LOGE
|
|
||||||
#define ESP_LOGE(tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, tag, "%s %d " format,loc_logtime(), __LINE__, ##__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
#ifdef ESP_LOGW
|
|
||||||
#undef ESP_LOGW
|
|
||||||
#define ESP_LOGW(tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, tag, "%s %d " format,loc_logtime(), __LINE__, ##__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
#ifdef ESP_LOGD
|
|
||||||
#undef ESP_LOGD
|
|
||||||
#define ESP_LOGD(tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, tag, "%s %d " format,loc_logtime(), __LINE__, ##__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
#ifdef ESP_LOGV
|
|
||||||
#undef ESP_LOGV
|
|
||||||
#define ESP_LOGV(tag, format, ... ) ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, tag, "%s %d " format,loc_logtime(), __LINE__, ##__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ menu "Squeezelite-ESP32"
|
|||||||
|
|
||||||
config OUTPUT_RATES
|
config OUTPUT_RATES
|
||||||
string "Output rates"
|
string "Output rates"
|
||||||
default "48000,44100"
|
default "44100"
|
||||||
help
|
help
|
||||||
<rates>[:<delay>] Sample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms
|
<rates>[:<delay>] Sample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms
|
||||||
menu "DAC I2S settings"
|
menu "DAC I2S settings"
|
||||||
@@ -196,13 +196,6 @@ menu "Squeezelite-ESP32"
|
|||||||
default 1000
|
default 1000
|
||||||
help
|
help
|
||||||
Increasing this value will give more chance for less stable connections to be established.
|
Increasing this value will give more chance for less stable connections to be established.
|
||||||
config A2DP_DISCONNECT_MS
|
|
||||||
int "Time in ms before disconnecting from A2DP audio sink. Set to 0 for no disconnect."
|
|
||||||
default 10000
|
|
||||||
help
|
|
||||||
Controls how long to wait before disconnecting from the A2DP audio sink after playback is stopped
|
|
||||||
Longer delay will ensure better responsiveness at the expense of locking the audio sink for a longer period.
|
|
||||||
A shorter period may cause the player to disconnect between tracks change.
|
|
||||||
endmenu
|
endmenu
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# "main" pseudo-component makefile.
|
# "main" pseudo-component makefile.
|
||||||
#
|
#
|
||||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||||
CFLAGS += -O3 -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
|
CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc \
|
-I$(COMPONENT_PATH)/../components/codecs/inc \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/mad \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/mad \
|
||||||
-I$(COMPONENT_PATH)/../components/codecs/inc/alac \
|
-I$(COMPONENT_PATH)/../components/codecs/inc/alac \
|
||||||
|
|||||||
@@ -199,8 +199,8 @@ void decode_init(log_level level, const char *include_codecs, const char *exclud
|
|||||||
LOG_DEBUG("include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs);
|
LOG_DEBUG("include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs);
|
||||||
|
|
||||||
mutex_create(decode.mutex);
|
mutex_create(decode.mutex);
|
||||||
PTHREAD_SET_NAME("decode");
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
#ifdef PTHREAD_STACK_MIN
|
#ifdef PTHREAD_STACK_MIN
|
||||||
@@ -208,6 +208,9 @@ void decode_init(log_level level, const char *include_codecs, const char *exclud
|
|||||||
#endif
|
#endif
|
||||||
pthread_create(&thread, &attr, decode_thread, NULL);
|
pthread_create(&thread, &attr, decode_thread, NULL);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
|
#if HAS_PTHREAD_SETNAME_NP
|
||||||
|
pthread_setname_np(thread, "decode");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if WIN
|
#if WIN
|
||||||
thread = CreateThread(NULL, DECODE_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&decode_thread, NULL, 0, NULL);
|
thread = CreateThread(NULL, DECODE_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&decode_thread, NULL, 0, NULL);
|
||||||
|
|||||||
44
main/embedded.c
Normal file
44
main/embedded.c
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite for esp32
|
||||||
|
*
|
||||||
|
* (c) Sebastien 2019
|
||||||
|
* Philippe G. 2019, philippe_44@outlook.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "squeezelite.h"
|
||||||
|
#include "esp_pthread.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
|
||||||
|
void get_mac(u8_t mac[]) {
|
||||||
|
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||||
|
}
|
||||||
|
|
||||||
|
_sig_func_ptr signal(int sig, _sig_func_ptr func) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *audio_calloc(size_t nmemb, size_t size) {
|
||||||
|
return calloc(nmemb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_setname_np(pthread_t thread, const char *name) {
|
||||||
|
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
|
||||||
|
cfg.thread_name= name;
|
||||||
|
cfg.inherit_cfg = true;
|
||||||
|
return esp_pthread_set_cfg(&cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,22 @@
|
|||||||
#pragma once
|
#ifndef EMBEDDED_H
|
||||||
#if defined(ESP_PLATFORM)
|
#define EMBEDDED_H
|
||||||
#include "sdkconfig.h"
|
|
||||||
#include "esp_pthread.h"
|
#include <inttypes.h>
|
||||||
#define PTHREAD_SET_NAME(n) { esp_pthread_cfg_t cfg = esp_pthread_get_default_config(); cfg.thread_name= n; cfg.inherit_cfg = true; esp_pthread_set_cfg(&cfg); }
|
|
||||||
|
#define HAS_MUTEX_CREATE_P 0
|
||||||
|
#define HAS_PTHREAD_SETNAME_NP 1
|
||||||
|
|
||||||
|
#ifndef PTHREAD_STACK_MIN
|
||||||
|
#define PTHREAD_STACK_MIN 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef int16_t s16_t;
|
||||||
|
typedef int32_t s32_t;
|
||||||
|
typedef int64_t s64_t;
|
||||||
|
typedef unsigned long long u64_t;
|
||||||
|
|
||||||
|
#define exit(code) { int ret = code; pthread_exit(&ret); }
|
||||||
|
|
||||||
|
int pthread_setname_np(pthread_t thread, const char *name);
|
||||||
|
|
||||||
|
#endif // EMBEDDED_H
|
||||||
|
|||||||
38
main/main.c
38
main/main.c
@@ -169,6 +169,9 @@ static void usage(const char *argv0) {
|
|||||||
"18"
|
"18"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#if EMBEDDED
|
||||||
|
" EMBEDDED"
|
||||||
|
#endif
|
||||||
#if EVENTFD
|
#if EVENTFD
|
||||||
" EVENTFD"
|
" EVENTFD"
|
||||||
#endif
|
#endif
|
||||||
@@ -381,7 +384,7 @@ int main(int argc, char **argv) {
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "\nOption error: -%s\n\n", opt);
|
fprintf(stderr, "\nOption error: -%s\n\n", opt);
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
local_exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (opt[0]) {
|
switch (opt[0]) {
|
||||||
@@ -432,7 +435,7 @@ int main(int argc, char **argv) {
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg);
|
fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg);
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
local_exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -532,7 +535,7 @@ int main(int argc, char **argv) {
|
|||||||
pidfile = optarg;
|
pidfile = optarg;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if !CONFIG_DACAUDIO && !CONFIG_BTAUDIO
|
#ifndef EMBEDDED
|
||||||
case 'l':
|
case 'l':
|
||||||
list_devices();
|
list_devices();
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -675,12 +678,11 @@ int main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
case 't':
|
case 't':
|
||||||
license();
|
license();
|
||||||
local_exit(0);
|
exit(0);
|
||||||
break; // mute compiler warning
|
|
||||||
case '?':
|
case '?':
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
local_exit(0);
|
exit(0);
|
||||||
break; // mute compiler warning
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Arg error: %s\n", argv[optind]);
|
fprintf(stderr, "Arg error: %s\n", argv[optind]);
|
||||||
break;
|
break;
|
||||||
@@ -691,7 +693,7 @@ int main(int argc, char **argv) {
|
|||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
fprintf(stderr, "\nError: command line argument error\n\n");
|
fprintf(stderr, "\nError: command line argument error\n\n");
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
local_exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(SIGINT, sighandler);
|
signal(SIGINT, sighandler);
|
||||||
@@ -757,13 +759,9 @@ int main(int argc, char **argv) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
stream_init(log_stream, stream_buf_size);
|
stream_init(log_stream, stream_buf_size);
|
||||||
#ifdef EMBEDDED
|
|
||||||
if(strstr(output_device,"BT")!=NULL || strstr(output_device,"bt")!=NULL) {
|
#if EMBEDDED
|
||||||
output_init_bt(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
|
output_init_embedded(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
|
||||||
}
|
|
||||||
else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
|
||||||
output_init_dac(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (!strcmp(output_device, "-")) {
|
if (!strcmp(output_device, "-")) {
|
||||||
output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay);
|
output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay);
|
||||||
@@ -804,7 +802,7 @@ else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
|||||||
|
|
||||||
if (name && namefile) {
|
if (name && namefile) {
|
||||||
fprintf(stderr, "-n and -N option should not be used at same time\n");
|
fprintf(stderr, "-n and -N option should not be used at same time\n");
|
||||||
local_exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
slimproto(log_slimproto, server, mac, name, namefile, modelname, maxSampleRate);
|
slimproto(log_slimproto, server, mac, name, namefile, modelname, maxSampleRate);
|
||||||
@@ -812,10 +810,8 @@ else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
|||||||
decode_close();
|
decode_close();
|
||||||
stream_close();
|
stream_close();
|
||||||
|
|
||||||
#if CONFIG_DACAUDIO
|
#if EMBEDDED
|
||||||
output_close_dac();
|
output_close_embedded();
|
||||||
#elif CONFIG_BTAUDIO
|
|
||||||
output_close_bt();
|
|
||||||
#else
|
#else
|
||||||
if (!strcmp(output_device, "-")) {
|
if (!strcmp(output_device, "-")) {
|
||||||
output_close_stdout();
|
output_close_stdout();
|
||||||
@@ -848,5 +844,5 @@ else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
|||||||
free_ssl_symbols();
|
free_ssl_symbols();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
local_exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,13 +352,13 @@ void output_init_common(log_level level, const char *device, unsigned output_buf
|
|||||||
buf_init(outputbuf, output_buf_size);
|
buf_init(outputbuf, output_buf_size);
|
||||||
if (!outputbuf->buf) {
|
if (!outputbuf->buf) {
|
||||||
LOG_ERROR("unable to malloc output buffer");
|
LOG_ERROR("unable to malloc output buffer");
|
||||||
local_exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
silencebuf = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
silencebuf = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
||||||
if (!silencebuf) {
|
if (!silencebuf) {
|
||||||
LOG_ERROR("unable to malloc silence buffer");
|
LOG_ERROR("unable to malloc silence buffer");
|
||||||
local_exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
memset(silencebuf, 0, MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
memset(silencebuf, 0, MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ void output_init_common(log_level level, const char *device, unsigned output_buf
|
|||||||
else {
|
else {
|
||||||
if (!test_open(output.device, output.supported_rates, user_rates)) {
|
if (!test_open(output.device, output.supported_rates, user_rates)) {
|
||||||
LOG_ERROR("unable to open output device: %s", output.device);
|
LOG_ERROR("unable to open output device: %s", output.device);
|
||||||
local_exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
402
main/output_bt.c
402
main/output_bt.c
@@ -1,351 +1,58 @@
|
|||||||
#include "squeezelite.h"
|
#include "squeezelite.h"
|
||||||
#include "perf_trace.h"
|
#include "perf_trace.h"
|
||||||
|
|
||||||
static log_level loglevel;
|
|
||||||
|
|
||||||
static bool running = true;
|
|
||||||
|
|
||||||
extern struct outputstate output;
|
extern struct outputstate output;
|
||||||
extern struct buffer *outputbuf;
|
extern struct buffer *outputbuf;
|
||||||
extern struct buffer *streambuf;
|
extern struct buffer *streambuf;
|
||||||
|
extern u8_t *silencebuf;
|
||||||
|
|
||||||
#define LOCK mutex_lock(outputbuf->mutex)
|
#define LOCK mutex_lock(outputbuf->mutex)
|
||||||
#define UNLOCK mutex_unlock(outputbuf->mutex)
|
#define UNLOCK mutex_unlock(outputbuf->mutex)
|
||||||
|
#define LOCK_S mutex_lock(streambuf->mutex)
|
||||||
#ifdef USE_BT_RING_BUFFER
|
#define UNLOCK_S mutex_unlock(streambuf->mutex)
|
||||||
size_t bt_buffer_size=0;
|
|
||||||
uint8_t bt_buf_used_threshold = 25;
|
|
||||||
uint16_t output_bt_thread_heartbeat_ms=1000;
|
|
||||||
thread_type thread_bt;
|
|
||||||
#define LOCK_BT mutex_lock(btbuf->mutex)
|
|
||||||
#define UNLOCK_BT mutex_unlock(btbuf->mutex)
|
|
||||||
thread_cond_type output_bt_suspend_cond;
|
|
||||||
mutex_type output_bt_suspend_mutex;
|
|
||||||
static struct buffer bt_buf_structure;
|
|
||||||
struct buffer *btbuf=&bt_buf_structure;
|
|
||||||
static void *output_thread_bt();
|
|
||||||
extern void wait_for_frames(size_t frames, uint8_t pct);
|
|
||||||
#else
|
|
||||||
uint8_t * btout;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FRAME_BLOCK MAX_SILENCE_FRAMES
|
#define FRAME_BLOCK MAX_SILENCE_FRAMES
|
||||||
extern u8_t *silencebuf;
|
|
||||||
|
#define STATS_REPORT_DELAY_MS 15000
|
||||||
|
|
||||||
extern void hal_bluetooth_init(const char * options);
|
extern void hal_bluetooth_init(const char * options);
|
||||||
|
|
||||||
|
static log_level loglevel;
|
||||||
|
uint8_t * btout;
|
||||||
|
|
||||||
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
|
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
|
||||||
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
|
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
|
||||||
|
|
||||||
#define DECLARE_ALL_MIN_MAX \
|
#define DECLARE_ALL_MIN_MAX \
|
||||||
DECLARE_MIN_MAX(req);\
|
DECLARE_MIN_MAX(req);\
|
||||||
DECLARE_MIN_MAX(rec);\
|
DECLARE_MIN_MAX(rec);\
|
||||||
DECLARE_MIN_MAX(o);\
|
DECLARE_MIN_MAX(bt);\
|
||||||
DECLARE_MIN_MAX(s);\
|
|
||||||
DECLARE_MIN_MAX(locbtbuff);\
|
|
||||||
DECLARE_MIN_MAX(under);\
|
DECLARE_MIN_MAX(under);\
|
||||||
DECLARE_MIN_MAX_DURATION(mutex1);\
|
DECLARE_MIN_MAX(stream_buf);\
|
||||||
DECLARE_MIN_MAX_DURATION(mutex2);\
|
DECLARE_MIN_MAX_DURATION(lock_out_time)
|
||||||
DECLARE_MIN_MAX_DURATION(buffering);\
|
|
||||||
DECLARE_MIN_MAX_DURATION(sleep_time);
|
|
||||||
#define RESET_ALL_MIN_MAX \
|
#define RESET_ALL_MIN_MAX \
|
||||||
RESET_MIN_MAX(o); \
|
RESET_MIN_MAX(bt); \
|
||||||
RESET_MIN_MAX(s); \
|
|
||||||
RESET_MIN_MAX(locbtbuff); \
|
|
||||||
RESET_MIN_MAX(req); \
|
RESET_MIN_MAX(req); \
|
||||||
RESET_MIN_MAX(rec); \
|
RESET_MIN_MAX(rec); \
|
||||||
RESET_MIN_MAX(under); \
|
RESET_MIN_MAX(under); \
|
||||||
RESET_MIN_MAX_DURATION(mutex1); \
|
RESET_MIN_MAX(stream_buf); \
|
||||||
RESET_MIN_MAX_DURATION(mutex2); \
|
RESET_MIN_MAX_DURATION(lock_out_time)
|
||||||
RESET_MIN_MAX_DURATION(sleep_time); \
|
|
||||||
RESET_MIN_MAX_DURATION(buffering);
|
|
||||||
|
|
||||||
|
DECLARE_ALL_MIN_MAX;
|
||||||
#if CONFIG_BTAUDIO
|
|
||||||
void set_volume_bt(unsigned left, unsigned right) {
|
|
||||||
LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
|
|
||||||
LOCK;
|
|
||||||
output.gainL = left;
|
|
||||||
output.gainR = right;
|
|
||||||
UNLOCK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void output_bt_check_buffer()
|
|
||||||
{
|
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
LOCK_BT;
|
|
||||||
uint8_t tot_buf_used_pct=100*_buf_used(btbuf)/btbuf->size;
|
|
||||||
UNLOCK_BT;
|
|
||||||
if(tot_buf_used_pct<bt_buf_used_threshold)
|
|
||||||
{
|
|
||||||
// tell the thread to resume
|
|
||||||
LOG_SDEBUG("Below threshold. Locking suspend mutex.");
|
|
||||||
mutex_lock(output_bt_suspend_mutex);
|
|
||||||
LOG_SDEBUG("Broadcasting suspend condition.");
|
|
||||||
mutex_broadcast_cond(output_bt_suspend_cond);
|
|
||||||
LOG_SDEBUG("Unlocking suspend mutex.");
|
|
||||||
mutex_unlock(output_bt_suspend_mutex);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
void output_bt_suspend()
|
|
||||||
{
|
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
struct timespec ts;
|
|
||||||
struct timeval tp;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
|
|
||||||
// if suspended, suspend until resumed
|
|
||||||
LOG_SDEBUG("Locking suspend mutex.");
|
|
||||||
mutex_lock(output_bt_suspend_mutex);
|
|
||||||
LOG_SDEBUG("Waiting on condition to be signaled.");
|
|
||||||
|
|
||||||
// suspend for up to a predetermined wait time.
|
|
||||||
// this will allow flushing the BT buffer when the
|
|
||||||
// playback stops.
|
|
||||||
gettimeofday(&tp, NULL);
|
|
||||||
/* Convert from timeval to timespec */
|
|
||||||
ts.tv_sec = tp.tv_sec;
|
|
||||||
ts.tv_nsec = tp.tv_usec * 1000;
|
|
||||||
ts.tv_nsec += output_bt_thread_heartbeat_ms*1000000; // micro seconds to nanosecs
|
|
||||||
|
|
||||||
rc = pthread_cond_timedwait(&output_bt_suspend_cond,&output_bt_suspend_mutex,&ts);
|
|
||||||
if(rc==ETIMEDOUT)
|
|
||||||
{
|
|
||||||
LOG_SDEBUG("Wait timed out. Resuming output.");
|
|
||||||
}
|
|
||||||
LOG_SDEBUG("Unlocking suspend mutex.");
|
|
||||||
mutex_unlock(output_bt_suspend_mutex);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
|
void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
|
||||||
loglevel = level;
|
loglevel = level;
|
||||||
|
|
||||||
LOG_INFO("init output BT");
|
|
||||||
|
|
||||||
memset(&output, 0, sizeof(output));
|
|
||||||
|
|
||||||
// ensure output rate is specified to avoid test open
|
|
||||||
if (!rates[0]) {
|
|
||||||
rates[0] = 44100;
|
|
||||||
}
|
|
||||||
hal_bluetooth_init(device);
|
hal_bluetooth_init(device);
|
||||||
/*
|
|
||||||
* Bluetooth audio source init Start
|
|
||||||
*/
|
|
||||||
// device = CONFIG_OUTPUT_NAME;
|
|
||||||
output_init_common(level, device, output_buf_size, rates, idle);
|
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
LOG_DEBUG("Allocating local BT transfer buffer of %u bytes.",bt_buffer_size);
|
|
||||||
buf_init(btbuf, bt_buffer_size );
|
|
||||||
if (!btbuf->buf) {
|
|
||||||
LOG_ERROR("unable to malloc BT buffer");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
mutex_create_p(output_bt_suspend_mutex);
|
|
||||||
mutex_cond_init(output_bt_suspend_cond);
|
|
||||||
PTHREAD_SET_NAME("output_bt");
|
|
||||||
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
#ifdef PTHREAD_STACK_MIN
|
|
||||||
pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pthread_create(&thread_bt, &attr, output_thread_bt, NULL);
|
|
||||||
#endif
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
|
|
||||||
#if WIN
|
|
||||||
thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread_bt, NULL, 0, NULL);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
output.start_frames = FRAME_BLOCK;
|
|
||||||
output.write_cb = &_write_frames;
|
output.write_cb = &_write_frames;
|
||||||
output.rate_delay = rate_delay;
|
|
||||||
#endif
|
|
||||||
LOG_INFO("Init completed.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************************
|
|
||||||
* Main output thread
|
|
||||||
*/
|
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
static void *output_thread_bt() {
|
|
||||||
|
|
||||||
frames_t frames=0;
|
|
||||||
frames_t requested_frames=0;
|
|
||||||
uint32_t timer_start=0, mutex_start=0;
|
|
||||||
unsigned btbuf_used=0;
|
|
||||||
output_state state;
|
|
||||||
DECLARE_ALL_MIN_MAX;
|
|
||||||
while (running) {
|
|
||||||
frames=0;
|
|
||||||
requested_frames=0;
|
|
||||||
TIME_MEASUREMENT_START(timer_start);
|
|
||||||
|
|
||||||
// Get output state
|
|
||||||
TIME_MEASUREMENT_START(mutex_start);
|
|
||||||
LOCK;
|
|
||||||
state=output.state;
|
|
||||||
SET_MIN_MAX(TIME_MEASUREMENT_GET(mutex_start),mutex1);
|
|
||||||
|
|
||||||
if(state < OUTPUT_STOPPED ){
|
|
||||||
// Flushing the buffer will automatically
|
|
||||||
// lock the mutex
|
|
||||||
LOG_SDEBUG("Flushing BT buffer");
|
|
||||||
buf_flush(btbuf);
|
|
||||||
}
|
|
||||||
if (state == OUTPUT_OFF) {
|
|
||||||
UNLOCK;
|
|
||||||
LOG_SDEBUG("Output state is off.");
|
|
||||||
usleep(200000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
output.device_frames = 0; // todo: check if this is the right way do to this.
|
|
||||||
output.updated = gettime_ms();
|
|
||||||
output.frames_played_dmp = output.frames_played;
|
|
||||||
|
|
||||||
TIME_MEASUREMENT_START(mutex_start);
|
|
||||||
LOCK_BT;
|
|
||||||
SET_MIN_MAX(TIME_MEASUREMENT_GET(mutex_start),mutex2);
|
|
||||||
btbuf_used=_buf_used(btbuf);
|
|
||||||
SET_MIN_MAX_SIZED(btbuf_used,locbtbuff,btbuf->size);
|
|
||||||
|
|
||||||
|
|
||||||
// only output more frames if we need them
|
|
||||||
// so we can release the mutex as quickly as possible
|
|
||||||
requested_frames = min(_buf_space(btbuf), _buf_cont_write(btbuf))/BYTES_PER_FRAME;
|
|
||||||
SET_MIN_MAX( requested_frames*BYTES_PER_FRAME,req);
|
|
||||||
SET_MIN_MAX_SIZED(_buf_used(outputbuf),o,outputbuf->size);
|
|
||||||
SET_MIN_MAX_SIZED(_buf_used(streambuf),s,streambuf->size);
|
|
||||||
if(requested_frames>0)
|
|
||||||
{
|
|
||||||
frames = _output_frames( requested_frames ); // Keep the transfer buffer full
|
|
||||||
SET_MIN_MAX(frames*BYTES_PER_FRAME,rec);
|
|
||||||
if(requested_frames>frames){
|
|
||||||
SET_MIN_MAX((requested_frames-frames)*BYTES_PER_FRAME,under);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNLOCK;
|
|
||||||
UNLOCK_BT;
|
|
||||||
SET_MIN_MAX( TIME_MEASUREMENT_GET(timer_start),buffering);
|
|
||||||
SET_MIN_MAX( requested_frames,req);
|
|
||||||
|
|
||||||
// When playback has started, we want to
|
|
||||||
// hold the BT out thread
|
|
||||||
// so the BT data callback isn't constantly interrupted.
|
|
||||||
TIME_MEASUREMENT_START(timer_start);
|
|
||||||
if(state>OUTPUT_BUFFER){
|
|
||||||
output_bt_suspend();
|
|
||||||
}
|
|
||||||
SET_MIN_MAX(TIME_MEASUREMENT_GET(timer_start),sleep_time);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Statistics reporting
|
|
||||||
*/
|
|
||||||
static time_t lastTime=0;
|
|
||||||
if (lastTime <= gettime_ms() )
|
|
||||||
{
|
|
||||||
#define STATS_PERIOD_MS 15000
|
|
||||||
lastTime = gettime_ms() + STATS_PERIOD_MS;
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_HEAD1);
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_HEAD2);
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_HEAD3);
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_HEAD4);
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_STREAM, LINE_MIN_MAX_STREAM("stream",s));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("output",o));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("local bt buf",locbtbuff));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_FOOTER );
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("requested",req));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("received",rec));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("Underrun",under));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_FORMAT_FOOTER );
|
|
||||||
LOG_DEBUG("");
|
|
||||||
LOG_DEBUG(" ----------+----------+-----------+-----------+ ");
|
|
||||||
LOG_DEBUG(" max (us) | min (us) | avg(us) | count | ");
|
|
||||||
LOG_DEBUG(" ----------+----------+-----------+-----------+ ");
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("Buffering(us)",buffering));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("Output mux(us)",mutex1));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("BT mux(us)",mutex2));
|
|
||||||
LOG_DEBUG(LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("sleep(us)",mutex2));
|
|
||||||
LOG_DEBUG(" ----------+----------+-----------+-----------+");
|
|
||||||
RESET_ALL_MIN_MAX;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* End Statistics reporting
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void output_close_bt(void) {
|
|
||||||
LOG_INFO("close output");
|
|
||||||
LOCK;
|
|
||||||
running = false;
|
|
||||||
UNLOCK;
|
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
LOCK_BT;
|
|
||||||
buf_destroy(btbuf);
|
|
||||||
UNLOCK_BT;
|
|
||||||
#endif
|
|
||||||
output_close_common();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
|
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
|
||||||
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
|
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
|
||||||
#ifdef USE_BT_RING_BUFFER
|
|
||||||
|
assert(btout != NULL);
|
||||||
|
|
||||||
if (!silence ) {
|
if (!silence ) {
|
||||||
DEBUG_LOG_TIMED(200,"Not silence, Writing audio out.");
|
|
||||||
// TODO need 16 bit fix
|
|
||||||
|
|
||||||
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
|
|
||||||
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gainL != FIXED_ONE || gainR!= FIXED_ONE) {
|
|
||||||
_apply_gain(outputbuf, out_frames, gainL, gainR);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BYTES_PER_FRAME == 4
|
|
||||||
|
|
||||||
memcpy(btbuf->writep, outputbuf->readp, out_frames * BYTES_PER_FRAME);
|
|
||||||
_buf_inc_writep(btbuf,out_frames * BYTES_PER_FRAME);
|
|
||||||
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
frames_t count = out_frames;
|
|
||||||
s32_t *_iptr = (s32_t*) outputbuf->readp;
|
|
||||||
s16_t *_optr = (s16_t*) bt_optr;
|
|
||||||
while (count--) {
|
|
||||||
*_optr++ = *_iptr++ >> 16;
|
|
||||||
*_optr++ = *_iptr++ >> 16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} else if(output.state >OUTPUT_BUFFER){
|
|
||||||
// Don't fill our local buffer with silence frames.
|
|
||||||
u8_t *buf = silencebuf;
|
|
||||||
memcpy(btbuf->writep, buf, out_frames * BYTES_PER_FRAME);
|
|
||||||
_buf_inc_writep(btbuf,out_frames * BYTES_PER_FRAME);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
assert(btout!=NULL);
|
|
||||||
if (!silence ) {
|
|
||||||
DEBUG_LOG_TIMED(200,"Not silence, Writing audio out.");
|
|
||||||
// TODO need 16 bit fix
|
|
||||||
|
|
||||||
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
|
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
|
||||||
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
|
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
|
||||||
@@ -374,7 +81,74 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
|
|||||||
u8_t *buf = silencebuf;
|
u8_t *buf = silencebuf;
|
||||||
memcpy(btout, buf, out_frames * BYTES_PER_FRAME);
|
memcpy(btout, buf, out_frames * BYTES_PER_FRAME);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return (int)out_frames;
|
return (int)out_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t output_bt_data(uint8_t *data, int32_t len) {
|
||||||
|
int32_t avail_data = 0, wanted_len = 0, start_timer = 0;
|
||||||
|
|
||||||
|
if (len < 0 || data == NULL ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
btout = data;
|
||||||
|
|
||||||
|
// This is how the BTC layer calculates the number of bytes to
|
||||||
|
// for us to send. (BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16) - availPcmBytes
|
||||||
|
wanted_len=len;
|
||||||
|
SET_MIN_MAX(len,req);
|
||||||
|
TIME_MEASUREMENT_START(start_timer);
|
||||||
|
LOCK;
|
||||||
|
output.device_frames = 0; // todo: check if this is the right way do to this.
|
||||||
|
output.updated = gettime_ms();
|
||||||
|
output.frames_played_dmp = output.frames_played;
|
||||||
|
SET_MIN_MAX_SIZED(_buf_used(outputbuf),bt,outputbuf->size);
|
||||||
|
do {
|
||||||
|
avail_data = _output_frames( wanted_len/BYTES_PER_FRAME )*BYTES_PER_FRAME; // Keep the transfer buffer full
|
||||||
|
wanted_len-=avail_data;
|
||||||
|
} while (wanted_len > 0 && avail_data != 0);
|
||||||
|
|
||||||
|
if (wanted_len > 0) {
|
||||||
|
SET_MIN_MAX(wanted_len, under);
|
||||||
|
}
|
||||||
|
|
||||||
|
UNLOCK;
|
||||||
|
SET_MIN_MAX(TIME_MEASUREMENT_GET(start_timer),lock_out_time);
|
||||||
|
SET_MIN_MAX((len-wanted_len), rec);
|
||||||
|
TIME_MEASUREMENT_START(start_timer);
|
||||||
|
|
||||||
|
return len-wanted_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_bt_tick(void) {
|
||||||
|
static time_t lastTime=0;
|
||||||
|
|
||||||
|
LOCK_S;
|
||||||
|
SET_MIN_MAX_SIZED(_buf_used(streambuf), stream_buf, streambuf->size);
|
||||||
|
UNLOCK_S;
|
||||||
|
|
||||||
|
if (lastTime <= gettime_ms() )
|
||||||
|
{
|
||||||
|
lastTime = gettime_ms() + STATS_REPORT_DELAY_MS;
|
||||||
|
LOG_INFO("Statistics over %u secs. " , STATS_REPORT_DELAY_MS/1000);
|
||||||
|
LOG_INFO(" +==========+==========+================+=====+================+");
|
||||||
|
LOG_INFO(" | max | min | average | avg | count |");
|
||||||
|
LOG_INFO(" | (bytes) | (bytes) | (bytes) | pct | |");
|
||||||
|
LOG_INFO(" +==========+==========+================+=====+================+");
|
||||||
|
LOG_INFO(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("stream avl",stream_buf));
|
||||||
|
LOG_INFO(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("output avl",bt));
|
||||||
|
LOG_INFO(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("requested",req));
|
||||||
|
LOG_INFO(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("received",rec));
|
||||||
|
LOG_INFO(LINE_MIN_MAX_FORMAT,LINE_MIN_MAX("underrun",under));
|
||||||
|
LOG_INFO( " +==========+==========+================+=====+================+");
|
||||||
|
LOG_INFO("\n");
|
||||||
|
LOG_INFO(" ==========+==========+===========+===========+ ");
|
||||||
|
LOG_INFO(" max (us) | min (us) | avg(us) | count | ");
|
||||||
|
LOG_INFO(" ==========+==========+===========+===========+ ");
|
||||||
|
LOG_INFO(LINE_MIN_MAX_DURATION_FORMAT,LINE_MIN_MAX_DURATION("Out Buf Lock",lock_out_time));
|
||||||
|
LOG_INFO(" ==========+==========+===========+===========+");
|
||||||
|
RESET_ALL_MIN_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
#include "squeezelite.h"
|
|
||||||
#include "driver/i2s.h"
|
|
||||||
|
|
||||||
static log_level loglevel;
|
|
||||||
|
|
||||||
static bool running = true;
|
|
||||||
|
|
||||||
extern struct outputstate output;
|
|
||||||
extern struct buffer *outputbuf;
|
|
||||||
extern struct buffer *streambuf;
|
|
||||||
|
|
||||||
#define LOCK mutex_lock(outputbuf->mutex)
|
|
||||||
#define UNLOCK mutex_unlock(outputbuf->mutex)
|
|
||||||
|
|
||||||
#define FRAME_BLOCK MAX_SILENCE_FRAMES
|
|
||||||
|
|
||||||
extern u8_t *silencebuf;
|
|
||||||
|
|
||||||
#define I2S_NUM (0)
|
|
||||||
#define WAVE_FREQ_HZ (100)
|
|
||||||
#define PI (3.14159265)
|
|
||||||
#define I2S_BCK_IO (GPIO_NUM_26)
|
|
||||||
#define I2S_WS_IO (GPIO_NUM_25)
|
|
||||||
#define I2S_DO_IO (GPIO_NUM_22)
|
|
||||||
#define I2S_DI_IO (-1)
|
|
||||||
// buffer length is expressed in number of samples
|
|
||||||
#define I2S_BUF_LEN 60
|
|
||||||
|
|
||||||
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL,
|
|
||||||
s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out,
|
|
||||||
ISAMPLE_T **cross_ptr);
|
|
||||||
|
|
||||||
void set_volume(unsigned left, unsigned right) {
|
|
||||||
LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
|
|
||||||
LOCK;
|
|
||||||
output.gainL = left;
|
|
||||||
output.gainR = right;
|
|
||||||
// TODO
|
|
||||||
output.gainL = FIXED_ONE;
|
|
||||||
output.gainR = FIXED_ONE;
|
|
||||||
UNLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *output_thread(void *arg) {
|
|
||||||
bool start = true;
|
|
||||||
bool output_off = (output.state == OUTPUT_OFF);
|
|
||||||
bool probe_device = (arg != NULL);
|
|
||||||
int err;
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
// todo: implement output off logic?
|
|
||||||
// todo: call i2s_set_clock here if rate is changed
|
|
||||||
LOCK;
|
|
||||||
|
|
||||||
output.device_frames = 0;
|
|
||||||
output.updated = gettime_ms();
|
|
||||||
output.frames_played_dmp = output.frames_played;
|
|
||||||
|
|
||||||
_output_frames(I2S_BUF_LEN*2); // fill at least one DMA buffer with stereo signal
|
|
||||||
|
|
||||||
UNLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_t thread;
|
|
||||||
|
|
||||||
void output_init_dac(log_level level, char *device, unsigned output_buf_size,
|
|
||||||
char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
|
|
||||||
loglevel = level;
|
|
||||||
|
|
||||||
LOG_INFO("init output DAC");
|
|
||||||
|
|
||||||
memset(&output, 0, sizeof(output));
|
|
||||||
|
|
||||||
output.start_frames = 0; //CONFIG_ //FRAME_BLOCK * 2;
|
|
||||||
output.write_cb = &_write_frames;
|
|
||||||
output.rate_delay = rate_delay;
|
|
||||||
|
|
||||||
// ensure output rate is specified to avoid test open
|
|
||||||
if (!rates[0]) {
|
|
||||||
rates[0] = 44100;
|
|
||||||
}
|
|
||||||
device = "DAC";
|
|
||||||
output_init_common(level, device, output_buf_size, rates, idle);
|
|
||||||
|
|
||||||
i2s_config_t i2s_config = {
|
|
||||||
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
|
|
||||||
.sample_rate = output.current_sample_rate,
|
|
||||||
.bits_per_sample = BYTES_PER_FRAME * 8,
|
|
||||||
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels
|
|
||||||
.communication_format = I2S_COMM_FORMAT_I2S
|
|
||||||
| I2S_COMM_FORMAT_I2S_MSB,
|
|
||||||
.dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers
|
|
||||||
.dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth
|
|
||||||
.use_apll = false,
|
|
||||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
|
|
||||||
};
|
|
||||||
i2s_pin_config_t pin_config = { .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_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2);
|
|
||||||
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
#ifdef PTHREAD_STACK_MIN
|
|
||||||
pthread_attr_setstacksize(&attr,
|
|
||||||
PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE);
|
|
||||||
#endif
|
|
||||||
pthread_create(&thread, &attr, output_thread, NULL);
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
#endif
|
|
||||||
#if WIN
|
|
||||||
thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_close_dac(void) {
|
|
||||||
LOG_INFO("close output");
|
|
||||||
LOCK;
|
|
||||||
running = false;
|
|
||||||
UNLOCK;
|
|
||||||
|
|
||||||
output_close_common();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _write_frames(frames_t out_frames, bool silence, s32_t gainL,
|
|
||||||
s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out,
|
|
||||||
ISAMPLE_T **cross_ptr) {
|
|
||||||
|
|
||||||
u8_t *obuf;
|
|
||||||
size_t i2s_bytes_write = 0;
|
|
||||||
|
|
||||||
if (!silence) {
|
|
||||||
|
|
||||||
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
|
|
||||||
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
obuf = outputbuf->readp;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
obuf = silencebuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
//_scale_and_pack_frames(buf + buffill * bytes_per_frame, (s32_t *)(void *)obuf, out_frames, gainL, gainR, output.format);
|
|
||||||
|
|
||||||
// buffill += out_frames;
|
|
||||||
i2s_write(I2S_NUM, obuf, out_frames *BYTES_PER_FRAME, &i2s_bytes_write, 100);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (int)i2s_bytes_write * BYTES_PER_FRAME;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
119
main/output_embedded.c
Normal file
119
main/output_embedded.c
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Squeezelite for esp32
|
||||||
|
*
|
||||||
|
* (c) Sebastien 2019
|
||||||
|
* Philippe G. 2019, philippe_44@outlook.com
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "squeezelite.h"
|
||||||
|
|
||||||
|
extern struct outputstate output;
|
||||||
|
extern struct buffer *outputbuf;
|
||||||
|
|
||||||
|
#define FRAME_BLOCK MAX_SILENCE_FRAMES
|
||||||
|
|
||||||
|
#define LOCK mutex_lock(outputbuf->mutex)
|
||||||
|
#define UNLOCK mutex_unlock(outputbuf->mutex)
|
||||||
|
|
||||||
|
extern void set_volume_i2s(unsigned left, unsigned right);
|
||||||
|
extern void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params,
|
||||||
|
unsigned rates[], unsigned rate_delay, unsigned idle);
|
||||||
|
extern void output_init_i2s(log_level level, char *device, unsigned output_buf_size, char *params,
|
||||||
|
unsigned rates[], unsigned rate_delay, unsigned idle);
|
||||||
|
|
||||||
|
static log_level loglevel;
|
||||||
|
|
||||||
|
static void (*volume_cb)(unsigned left, unsigned right);
|
||||||
|
static void (*close_cb)(void);
|
||||||
|
|
||||||
|
void output_init_embedded(log_level level, char *device, unsigned output_buf_size, char *params,
|
||||||
|
unsigned rates[], unsigned rate_delay, unsigned idle) {
|
||||||
|
loglevel = level;
|
||||||
|
LOG_INFO("init device: %s", device);
|
||||||
|
|
||||||
|
memset(&output, 0, sizeof(output));
|
||||||
|
output_init_common(level, device, output_buf_size, rates, idle);
|
||||||
|
output.start_frames = FRAME_BLOCK;
|
||||||
|
output.rate_delay = rate_delay;
|
||||||
|
|
||||||
|
if (strstr(device, "BT ")) {
|
||||||
|
LOG_INFO("init Bluetooth");
|
||||||
|
output_init_bt(level, device, output_buf_size, params, rates, rate_delay, idle);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("init I2S");
|
||||||
|
//volume_cb = set_volume_i2s;
|
||||||
|
//close_cb = output_close_i2s;
|
||||||
|
//output_init_i2s(level, device, output_buf_size, params, rates, rate_delay, idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("init completed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_close_embedded(void) {
|
||||||
|
LOG_INFO("close output");
|
||||||
|
output_close_common();
|
||||||
|
if (close_cb) (*close_cb)();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_volume(unsigned left, unsigned right) {
|
||||||
|
LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
|
||||||
|
if (!volume_cb) {
|
||||||
|
LOCK;
|
||||||
|
output.gainL = left;
|
||||||
|
output.gainR = right;
|
||||||
|
UNLOCK;
|
||||||
|
} else (*volume_cb)(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
|
||||||
|
memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
|
||||||
|
if (!strcmp(device, "BT")) {
|
||||||
|
rates[0] = 44100;
|
||||||
|
} else {
|
||||||
|
unsigned _rates[] = { 96000, 88200, 48000, 44100, 32000, 0 };
|
||||||
|
memcpy(rates, _rates, sizeof(_rates));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* output_state_str(void){
|
||||||
|
output_state state;
|
||||||
|
LOCK;
|
||||||
|
state = output.state;
|
||||||
|
UNLOCK;
|
||||||
|
switch (state) {
|
||||||
|
case OUTPUT_OFF: return STR(OUTPUT_OFF);
|
||||||
|
case OUTPUT_STOPPED: return STR(OUTPUT_STOPPED);
|
||||||
|
case OUTPUT_BUFFER: return STR(OUTPUT_BUFFER);
|
||||||
|
case OUTPUT_RUNNING: return STR(OUTPUT_RUNNING);
|
||||||
|
case OUTPUT_PAUSE_FRAMES: return STR(OUTPUT_PAUSE_FRAMES);
|
||||||
|
case OUTPUT_SKIP_FRAMES: return STR(OUTPUT_SKIP_FRAMES);
|
||||||
|
case OUTPUT_START_AT: return STR(OUTPUT_START_AT);
|
||||||
|
default: return "OUTPUT_UNKNOWN_STATE";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool output_stopped(void) {
|
||||||
|
output_state state;
|
||||||
|
LOCK;
|
||||||
|
state = output.state;
|
||||||
|
UNLOCK;
|
||||||
|
return state <= OUTPUT_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -436,13 +436,8 @@ static void process_audg(u8_t *pkt, int len) {
|
|||||||
audg->gainR = unpackN(&audg->gainR);
|
audg->gainR = unpackN(&audg->gainR);
|
||||||
|
|
||||||
LOG_DEBUG("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust);
|
LOG_DEBUG("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust);
|
||||||
#if CONFIG_BTAUDIO
|
|
||||||
set_volume_bt(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE);
|
|
||||||
#elif CONFIG_DACAUDIO
|
|
||||||
set_volume_dac(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE);
|
|
||||||
#else
|
|
||||||
set_volume(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE);
|
set_volume(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void process_setd(u8_t *pkt, int len) {
|
static void process_setd(u8_t *pkt, int len) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION
|
#define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if !defined(MODEL_NAME)
|
#if !defined(MODEL_NAME)
|
||||||
#define MODEL_NAME SqueezeLite
|
#define MODEL_NAME SqueezeLite
|
||||||
#endif
|
#endif
|
||||||
@@ -42,16 +43,11 @@
|
|||||||
#define STR(macro) QUOTE(macro)
|
#define STR(macro) QUOTE(macro)
|
||||||
#define MODEL_NAME_STRING STR(MODEL_NAME)
|
#define MODEL_NAME_STRING STR(MODEL_NAME)
|
||||||
|
|
||||||
#if defined(EMBEDDED)
|
|
||||||
#define POSIX 1
|
|
||||||
#include "embedded.h"
|
|
||||||
#endif
|
|
||||||
#ifndef PTHREAD_SET_NAME
|
|
||||||
#define PTHREAD_SET_NAME(n)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// build detection
|
// build detection
|
||||||
#if defined(linux)
|
#if defined (EMBEDDED)
|
||||||
|
#undef EMBEDDED
|
||||||
|
#define EMBEDDED 1
|
||||||
|
#elif defined(linux)
|
||||||
#define LINUX 1
|
#define LINUX 1
|
||||||
#define OSX 0
|
#define OSX 0
|
||||||
#define WIN 0
|
#define WIN 0
|
||||||
@@ -78,27 +74,19 @@
|
|||||||
#define PA18API 1
|
#define PA18API 1
|
||||||
#define OSX 0
|
#define OSX 0
|
||||||
#define WIN 0
|
#define WIN 0
|
||||||
#elif defined (POSIX)
|
|
||||||
#undef POSIX
|
|
||||||
#define POSIX 1
|
|
||||||
#else
|
#else
|
||||||
#error unknown target
|
#error unknown target
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !EMBEDDED
|
||||||
#if defined(CONFIG_DACAUDIO)
|
#if LINUX && !defined(PORTAUDIO)
|
||||||
#undef CONFIG_DACAUDIO
|
|
||||||
#define CONFIG_DACAUDIO 1
|
|
||||||
#elif defined(CONFIG_BTAUDIO)
|
|
||||||
#undef CONFIG_BTAUDIO
|
|
||||||
#define CONFIG_BTAUDIO 1
|
|
||||||
#elif LINUX && !defined(PORTAUDIO)
|
|
||||||
#define ALSA 1
|
#define ALSA 1
|
||||||
#define PORTAUDIO 0
|
#define PORTAUDIO 0
|
||||||
#else
|
#else
|
||||||
#define ALSA 0
|
#define ALSA 0
|
||||||
#define PORTAUDIO 1
|
#define PORTAUDIO 1
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(LOOPBACK)
|
#if !defined(LOOPBACK)
|
||||||
#if SUN
|
#if SUN
|
||||||
@@ -278,18 +266,18 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
#if EMBEDDED
|
||||||
|
#include "embedded.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#if POSIX
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#else
|
|
||||||
#include <poll.h>
|
|
||||||
#endif
|
|
||||||
#if !LINKALL
|
#if !LINKALL
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -299,14 +287,10 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#endif /* SUN */
|
#endif /* SUN */
|
||||||
|
|
||||||
#ifndef PTHREAD_STACK_MIN
|
#define STREAM_THREAD_STACK_SIZE 64 * 1024
|
||||||
#define PTHREAD_STACK_MIN 256
|
#define DECODE_THREAD_STACK_SIZE 128 * 1024
|
||||||
#endif
|
#define OUTPUT_THREAD_STACK_SIZE 64 * 1024
|
||||||
|
#define IR_THREAD_STACK_SIZE 64 * 1024
|
||||||
#define STREAM_THREAD_STACK_SIZE 8 * 1024
|
|
||||||
#define DECODE_THREAD_STACK_SIZE 20 * 1024
|
|
||||||
#define OUTPUT_THREAD_STACK_SIZE 8 * 1024
|
|
||||||
#define IR_THREAD_STACK_SIZE 8 * 1024
|
|
||||||
#if !OSX
|
#if !OSX
|
||||||
#define thread_t pthread_t;
|
#define thread_t pthread_t;
|
||||||
#endif
|
#endif
|
||||||
@@ -314,13 +298,12 @@
|
|||||||
#define last_error() errno
|
#define last_error() errno
|
||||||
#define ERROR_WOULDBLOCK EWOULDBLOCK
|
#define ERROR_WOULDBLOCK EWOULDBLOCK
|
||||||
|
|
||||||
|
#if !EMBEDDED
|
||||||
#ifdef SUN
|
#ifdef SUN
|
||||||
typedef uint8_t u8_t;
|
typedef uint8_t u8_t;
|
||||||
typedef uint16_t u16_t;
|
typedef uint16_t u16_t;
|
||||||
typedef uint32_t u32_t;
|
typedef uint32_t u32_t;
|
||||||
typedef uint64_t u64_t;
|
typedef uint64_t u64_t;
|
||||||
#elif POSIX
|
|
||||||
typedef unsigned long long u64_t;
|
|
||||||
#else
|
#else
|
||||||
typedef u_int8_t u8_t;
|
typedef u_int8_t u8_t;
|
||||||
typedef u_int16_t u16_t;
|
typedef u_int16_t u16_t;
|
||||||
@@ -330,29 +313,20 @@ typedef u_int64_t u64_t;
|
|||||||
typedef int16_t s16_t;
|
typedef int16_t s16_t;
|
||||||
typedef int32_t s32_t;
|
typedef int32_t s32_t;
|
||||||
typedef int64_t s64_t;
|
typedef int64_t s64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define mutex_type pthread_mutex_t
|
#define mutex_type pthread_mutex_t
|
||||||
#define mutex_create(m) pthread_mutex_init(&m, NULL)
|
#define mutex_create(m) pthread_mutex_init(&m, NULL)
|
||||||
#if POSIX
|
#if HAS_MUTEX_CREATE_P
|
||||||
#define mutex_create_p(m) mutex_create(m)
|
|
||||||
#else
|
|
||||||
#define mutex_create_p(m) pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); pthread_mutex_init(&m, &attr); pthread_mutexattr_destroy(&attr)
|
#define mutex_create_p(m) pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); pthread_mutex_init(&m, &attr); pthread_mutexattr_destroy(&attr)
|
||||||
|
#else
|
||||||
|
#define mutex_create_p(m) mutex_create(m)
|
||||||
#endif
|
#endif
|
||||||
#define mutex_lock(m) pthread_mutex_lock(&m)
|
#define mutex_lock(m) pthread_mutex_lock(&m)
|
||||||
#define mutex_unlock(m) pthread_mutex_unlock(&m)
|
#define mutex_unlock(m) pthread_mutex_unlock(&m)
|
||||||
#define mutex_destroy(m) pthread_mutex_destroy(&m)
|
#define mutex_destroy(m) pthread_mutex_destroy(&m)
|
||||||
#define mutex_broadcast_cond(m) pthread_cond_broadcast(&m)
|
|
||||||
#define mutex_cond_wait(c,m) pthread_cond_wait(&c, &m)
|
|
||||||
#define mutex_cond_init(c) pthread_cond_init(&c, NULL)
|
|
||||||
#define thread_cond_type pthread_cond_t
|
|
||||||
#define thread_type pthread_t
|
#define thread_type pthread_t
|
||||||
#endif
|
#endif
|
||||||
#ifdef EMBEDDED
|
|
||||||
#define local_exit(r) {static int ret=r; pthread_exit(&ret);}
|
|
||||||
#else
|
|
||||||
#define local_exit(r) exit(r)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if WIN
|
#if WIN
|
||||||
|
|
||||||
@@ -491,24 +465,7 @@ void logprint(const char *fmt, ...);
|
|||||||
#define LOG_INFO(fmt, ...) if (loglevel >= lINFO) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
#define LOG_INFO(fmt, ...) if (loglevel >= lINFO) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
#define LOG_DEBUG(fmt, ...) if (loglevel >= lDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
#define LOG_DEBUG(fmt, ...) if (loglevel >= lDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
#define LOG_SDEBUG(fmt, ...) if (loglevel >= lSDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
#define LOG_SDEBUG(fmt, ...) if (loglevel >= lSDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
static inline void DEBUG_LOG_TIMED(uint32_t delayms, char * strFmt, ...)
|
|
||||||
{
|
|
||||||
static log_level loglevel;
|
|
||||||
va_list args;
|
|
||||||
va_start(args, strFmt);
|
|
||||||
static uint32_t nextDebugLog=0;
|
|
||||||
if(esp_timer_get_time()>nextDebugLog)
|
|
||||||
{
|
|
||||||
if (loglevel >= lDEBUG)
|
|
||||||
{
|
|
||||||
logprint("%s %s:%d ", logtime(), __FUNCTION__, __LINE__);
|
|
||||||
logprint(strFmt , args);
|
|
||||||
logprint("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
nextDebugLog=esp_timer_get_time()+delayms*1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// utils.c (non logging)
|
// utils.c (non logging)
|
||||||
typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type;
|
typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type;
|
||||||
#if WIN && USE_SSL
|
#if WIN && USE_SSL
|
||||||
@@ -670,8 +627,6 @@ typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, U8, U16_LE, U16_BE, U32_LE, U32_
|
|||||||
#else
|
#else
|
||||||
typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, S24_BE, S24_3BE, S16_BE, S8_BE } output_format;
|
typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, S24_BE, S24_3BE, S16_BE, S8_BE } output_format;
|
||||||
#endif
|
#endif
|
||||||
extern uint8_t get_bytes_per_frame(output_format fmt);
|
|
||||||
|
|
||||||
|
|
||||||
typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state;
|
typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state;
|
||||||
typedef enum { FADE_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir;
|
typedef enum { FADE_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir;
|
||||||
@@ -763,26 +718,17 @@ void output_close_pa(void);
|
|||||||
void _pa_open(void);
|
void _pa_open(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// output_dac.c
|
// output_embedded.c
|
||||||
|
#if EMBEDDED
|
||||||
void set_volume_dac(unsigned left, unsigned right);
|
void set_volume(unsigned left, unsigned right);
|
||||||
bool test_open(const char *device, unsigned rates[], bool userdef_rates);
|
bool test_open(const char *device, unsigned rates[], bool userdef_rates);
|
||||||
void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
|
void output_init_embedded(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
|
||||||
void output_close_dac(void);
|
void output_close_embedded(void);
|
||||||
void hal_dac_init(const char * options);
|
#else
|
||||||
|
|
||||||
//output_bt.c
|
|
||||||
void set_volume_bt(unsigned left, unsigned right);
|
|
||||||
bool test_open(const char *device, unsigned rates[], bool userdef_rates);
|
|
||||||
void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
|
|
||||||
void output_close_bt(void);
|
|
||||||
extern void hal_bluetooth_init(const char * options);
|
|
||||||
void output_bt_check_buffer();
|
|
||||||
|
|
||||||
|
|
||||||
// output_stdout.c
|
// output_stdout.c
|
||||||
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
|
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
|
||||||
void output_close_stdout(void);
|
void output_close_stdout(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
// output_pack.c
|
// output_pack.c
|
||||||
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format);
|
void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format);
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
|
|||||||
buf_init(streambuf, stream_buf_size);
|
buf_init(streambuf, stream_buf_size);
|
||||||
if (streambuf->buf == NULL) {
|
if (streambuf->buf == NULL) {
|
||||||
LOG_ERROR("unable to malloc buffer");
|
LOG_ERROR("unable to malloc buffer");
|
||||||
local_exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_SSL
|
#if USE_SSL
|
||||||
@@ -405,8 +405,8 @@ void stream_init(log_level level, unsigned stream_buf_size) {
|
|||||||
#if LINUX || FREEBSD
|
#if LINUX || FREEBSD
|
||||||
touch_memory(streambuf->buf, streambuf->size);
|
touch_memory(streambuf->buf, streambuf->size);
|
||||||
#endif
|
#endif
|
||||||
PTHREAD_SET_NAME("stream");
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
#ifdef PTHREAD_STACK_MIN
|
#ifdef PTHREAD_STACK_MIN
|
||||||
@@ -414,6 +414,9 @@ PTHREAD_SET_NAME("stream");
|
|||||||
#endif
|
#endif
|
||||||
pthread_create(&thread, &attr, stream_thread, NULL);
|
pthread_create(&thread, &attr, stream_thread, NULL);
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
|
#if HAS_PTHREAD_SETNAME_NP
|
||||||
|
pthread_setname_np(thread, "stream");
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#if WIN
|
#if WIN
|
||||||
thread = CreateThread(NULL, STREAM_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&stream_thread, NULL, 0, NULL);
|
thread = CreateThread(NULL, STREAM_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&stream_thread, NULL, 0, NULL);
|
||||||
|
|||||||
78
main/utils.c
78
main/utils.c
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include "squeezelite.h"
|
#include "squeezelite.h"
|
||||||
|
|
||||||
#if LINUX || OSX || FREEBSD || POSIX
|
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@@ -103,7 +103,7 @@ u32_t gettime_ms(void) {
|
|||||||
#if WIN
|
#if WIN
|
||||||
return GetTickCount();
|
return GetTickCount();
|
||||||
#else
|
#else
|
||||||
#if LINUX || FREEBSD || POSIX
|
#if LINUX || FREEBSD || EMBEDDED
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
#ifdef CLOCK_MONOTONIC
|
#ifdef CLOCK_MONOTONIC
|
||||||
if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
||||||
@@ -561,77 +561,3 @@ char *strcasestr(const char *haystack, const char *needle) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
uint8_t get_bytes_per_frame(output_format fmt)
|
|
||||||
{
|
|
||||||
uint8_t bpf=0;
|
|
||||||
|
|
||||||
switch (fmt) {
|
|
||||||
case S32_LE:
|
|
||||||
bpf=4*2;
|
|
||||||
break;
|
|
||||||
case S24_LE:
|
|
||||||
bpf=3*2;
|
|
||||||
break;
|
|
||||||
case S24_3LE:
|
|
||||||
bpf=3*2;
|
|
||||||
break;
|
|
||||||
case S16_LE:
|
|
||||||
bpf=2*2;
|
|
||||||
break;
|
|
||||||
case S24_BE:
|
|
||||||
bpf=3*2;
|
|
||||||
break;
|
|
||||||
case S24_3BE:
|
|
||||||
bpf=3*2;
|
|
||||||
break;
|
|
||||||
case S16_BE:
|
|
||||||
bpf=2*2;
|
|
||||||
break;
|
|
||||||
case S8_BE:
|
|
||||||
bpf=2*2;
|
|
||||||
break;
|
|
||||||
#if DSD
|
|
||||||
case U8:
|
|
||||||
bpf=1*2;
|
|
||||||
break;
|
|
||||||
case U16_LE:
|
|
||||||
bpf=2*2;
|
|
||||||
break;
|
|
||||||
case U16_BE:
|
|
||||||
bpf=2*2;
|
|
||||||
break;
|
|
||||||
case U32_LE:
|
|
||||||
bpf=4*2;
|
|
||||||
break;
|
|
||||||
case U32_BE:
|
|
||||||
bpf=4*2;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(bpf>0);
|
|
||||||
return bpf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * get_output_state_desc(output_state state){
|
|
||||||
switch (state) {
|
|
||||||
case OUTPUT_OFF:
|
|
||||||
return STR(OUTPUT_OFF);
|
|
||||||
case OUTPUT_STOPPED:
|
|
||||||
return STR(OUTPUT_STOPPED);
|
|
||||||
case OUTPUT_BUFFER:
|
|
||||||
return STR(OUTPUT_BUFFER);
|
|
||||||
case OUTPUT_RUNNING:
|
|
||||||
return STR(OUTPUT_RUNNING);
|
|
||||||
case OUTPUT_PAUSE_FRAMES:
|
|
||||||
return STR(OUTPUT_PAUSE_FRAMES);
|
|
||||||
case OUTPUT_SKIP_FRAMES:
|
|
||||||
return STR(OUTPUT_SKIP_FRAMES);
|
|
||||||
case OUTPUT_START_AT:
|
|
||||||
return STR(OUTPUT_START_AT);
|
|
||||||
default:
|
|
||||||
return "OUTPUT_UNKNOWN_STATE";
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user