refactor step 1 - BT

This commit is contained in:
philippe44
2019-06-28 23:08:46 -07:00
parent 0e3bc18748
commit 6c5ce6ba80
21 changed files with 452 additions and 1120 deletions

1
.gitignore vendored
View File

@@ -65,3 +65,4 @@ libs/
/cdump.cmd /cdump.cmd
/_codecs /_codecs
sdkconfig

View File

@@ -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; s_media_state = APP_AV_MEDIA_STATE_IDLE;
// if(CONFIG_A2DP_DISCONNECT_MS==0){
// we're not going to disconnect.
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){ esp_a2d_source_disconnect(s_peer_bda);
// we've reached timeout s_a2d_state = APP_AV_STATE_DISCONNECTING;
esp_a2d_source_disconnect(s_peer_bda); ESP_LOGI(TAG,"a2dp disconnecting...");
s_a2d_state = APP_AV_STATE_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,21 +631,18 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param)
default: default:
break; break;
} }
// if(out_state > OUTPUT_STOPPED){ for(uint8_t l=0;art_a2dp_connecting[l][0]!='\0';l++){
// only attempt a connect when playback isn't stopped ESP_LOGI(TAG,"%s",art_a2dp_connecting[l]);
for(uint8_t l=0;art_a2dp_connecting[l][0]!='\0';l++){ }
ESP_LOGI(TAG,"%s",art_a2dp_connecting[l]); ESP_LOGI(TAG,"Device: %s", s_peer_bdname);
} if(esp_a2d_source_connect(s_peer_bda)==ESP_OK) {
ESP_LOGI(TAG,"Device: %s", s_peer_bdname); A2DP_TIMER_INIT;
if(esp_a2d_source_connect(s_peer_bda)==ESP_OK) { s_a2d_state = APP_AV_STATE_CONNECTING;
A2DP_TIMER_INIT; }
s_a2d_state = APP_AV_STATE_CONNECTING; else {
} // there was an issue connecting... continue to discover
else { ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
// there was an issue connecting... continue to discover esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
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(&lt);
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;
}

View File

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

View File

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

View File

@@ -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();
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
View 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

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