mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 12:07:09 +03:00
refactor step 1 - BT
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -65,3 +65,4 @@ libs/
|
||||
|
||||
/cdump.cmd
|
||||
/_codecs
|
||||
sdkconfig
|
||||
|
||||
@@ -1,54 +1,32 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.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_console.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 "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"
|
||||
|
||||
#define STATS_REPORT_DELAY_MS 15000
|
||||
#include "bt_app_core.h"
|
||||
|
||||
#include "platform_esp32.h"
|
||||
|
||||
static const char * TAG = "platform";
|
||||
extern char * get_output_state_desc(output_state state);
|
||||
|
||||
extern struct outputstate output;
|
||||
extern struct buffer *outputbuf;
|
||||
extern struct buffer *streambuf;
|
||||
extern uint8_t * btout;
|
||||
time_t disconnect_time=0;
|
||||
#define LOCK_S pthread_mutex_lock(&(streambuf->mutex))
|
||||
#define UNLOCK_S pthread_mutex_unlock(&(streambuf->mutex))
|
||||
extern int32_t output_bt_data(uint8_t *data, int32_t len);
|
||||
extern void output_bt_tick(void);
|
||||
extern char* output_state_str(void);
|
||||
extern bool output_stopped(void);
|
||||
|
||||
#define LOCK pthread_mutex_lock(&(outputbuf->mutex))
|
||||
#define UNLOCK pthread_mutex_unlock(&(outputbuf->mutex))
|
||||
int64_t connecting_timeout = 0;
|
||||
|
||||
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 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);
|
||||
|
||||
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 */
|
||||
enum {
|
||||
BT_APP_EVT_STACK_UP = 0,
|
||||
@@ -181,16 +133,12 @@ static struct {
|
||||
void hal_bluetooth_init(const char * options)
|
||||
{
|
||||
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.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.end = arg_end(2);
|
||||
|
||||
|
||||
ESP_LOGD(TAG,"Copying parameters");
|
||||
char * opts = strdup(options);
|
||||
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);
|
||||
|
||||
}
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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;
|
||||
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;
|
||||
case APP_AV_STATE_UNCONNECTED:
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -579,7 +491,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param)
|
||||
|
||||
/* initialize A2DP source */
|
||||
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();
|
||||
|
||||
/* 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)
|
||||
{
|
||||
esp_a2d_cb_param_t *a2d = NULL;
|
||||
LOCK;
|
||||
output_state out_state=output.state;
|
||||
UNLOCK;
|
||||
switch (s_media_state) {
|
||||
case APP_AV_MEDIA_STATE_IDLE: {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -671,40 +559,12 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
|
||||
}
|
||||
case APP_AV_MEDIA_STATE_STARTED: {
|
||||
if (event == BT_APP_HEART_BEAT_EVT) {
|
||||
if(out_state <= OUTPUT_STOPPED) {
|
||||
ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", get_output_state_desc(out_state));
|
||||
if(output_stopped()) {
|
||||
ESP_LOGI(TAG,"Output state is %s. Stopping a2dp media ...", output_state_str());
|
||||
s_media_state = APP_AV_MEDIA_STATE_STOPPING;
|
||||
esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
} else {
|
||||
output_bt_tick();
|
||||
}
|
||||
}
|
||||
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 &&
|
||||
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
|
||||
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;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// disconnect_time = gettime_ms()+CONFIG_A2DP_DISCONNECT_MS;
|
||||
// s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT;
|
||||
// }
|
||||
s_media_state = APP_AV_MEDIA_STATE_IDLE;
|
||||
} else {
|
||||
ESP_LOGI(TAG,"a2dp media stopping...");
|
||||
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:{
|
||||
if(gettime_ms()>disconnect_time){
|
||||
// we've reached timeout
|
||||
esp_a2d_source_disconnect(s_peer_bda);
|
||||
s_a2d_state = APP_AV_STATE_DISCONNECTING;
|
||||
}
|
||||
esp_a2d_source_disconnect(s_peer_bda);
|
||||
s_a2d_state = APP_AV_STATE_DISCONNECTING;
|
||||
ESP_LOGI(TAG,"a2dp disconnecting...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_app_av_state_unconnected(uint16_t event, void *param)
|
||||
{
|
||||
// LOCK;
|
||||
// output_state out_state= output.state;
|
||||
// UNLOCK;
|
||||
switch (event) {
|
||||
case 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));
|
||||
break;
|
||||
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()) {
|
||||
case 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:
|
||||
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++){
|
||||
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) {
|
||||
A2DP_TIMER_INIT;
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
}
|
||||
else {
|
||||
// there was an issue connecting... continue to discover
|
||||
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||
// }
|
||||
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) {
|
||||
A2DP_TIMER_INIT;
|
||||
s_a2d_state = APP_AV_STATE_CONNECTING;
|
||||
}
|
||||
else {
|
||||
// there was an issue connecting... continue to discover
|
||||
ESP_LOGE(TAG,"Attempt at connecting failed, restart at discover...");
|
||||
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -928,18 +769,4 @@ static void bt_app_av_state_disconnecting(uint16_t event, void *param)
|
||||
ESP_LOGE(TAG,"%s unhandled evt %d", __func__, event);
|
||||
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_console.h"
|
||||
#include "esp_pthread.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
|
||||
# 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
|
||||
|
||||
@@ -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
|
||||
to a Wi-Fi network.
|
||||
|
||||
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
|
||||
*/
|
||||
/*
|
||||
* 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 "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()
|
||||
{
|
||||
|
||||
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
|
||||
#include "time.h"
|
||||
|
||||
#include "sys/time.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
#define PERF_MAX LONG_MAX
|
||||
#define MIN_MAX_VAL(x) x==PERF_MAX?0:x
|
||||
#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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "bt_app_core.h"
|
||||
#include "perf_trace.h"
|
||||
|
||||
#include "esp_pthread.h"
|
||||
|
||||
#ifndef QUOTE
|
||||
#define QUOTE(name) #name
|
||||
#define STR(macro) QUOTE(macro)
|
||||
#endif
|
||||
|
||||
#define ESP_LOG_DEBUG_EVENT(tag,e) ESP_LOGD(tag,"evt: " e)
|
||||
|
||||
extern void run_command(char * line);
|
||||
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 pthread_cond_t wifi_connect_suspend_cond;
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -134,7 +134,7 @@ menu "Squeezelite-ESP32"
|
||||
|
||||
config OUTPUT_RATES
|
||||
string "Output rates"
|
||||
default "48000,44100"
|
||||
default "44100"
|
||||
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
|
||||
menu "DAC I2S settings"
|
||||
@@ -196,13 +196,6 @@ menu "Squeezelite-ESP32"
|
||||
default 1000
|
||||
help
|
||||
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
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (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/mad \
|
||||
-I$(COMPONENT_PATH)/../components/codecs/inc/alac \
|
||||
@@ -10,7 +10,7 @@ CFLAGS += -O3 -DPOSIX -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DT
|
||||
-I$(COMPONENT_PATH)/../components/codecs/inc/vorbis \
|
||||
-I$(COMPONENT_PATH)/../components/codecs/inc/soxr \
|
||||
-I$(COMPONENT_PATH)/../components/codecs/inc/resample16 \
|
||||
-I$(COMPONENT_PATH)/../components/platform_esp32
|
||||
-I$(COMPONENT_PATH)/../components/platform_esp32
|
||||
|
||||
LDFLAGS += -s
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
mutex_create(decode.mutex);
|
||||
PTHREAD_SET_NAME("decode");
|
||||
#if LINUX || OSX || FREEBSD || POSIX
|
||||
|
||||
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
#ifdef PTHREAD_STACK_MIN
|
||||
@@ -208,6 +208,9 @@ void decode_init(log_level level, const char *include_codecs, const char *exclud
|
||||
#endif
|
||||
pthread_create(&thread, &attr, decode_thread, NULL);
|
||||
pthread_attr_destroy(&attr);
|
||||
#if HAS_PTHREAD_SETNAME_NP
|
||||
pthread_setname_np(thread, "decode");
|
||||
#endif
|
||||
#endif
|
||||
#if WIN
|
||||
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
|
||||
#if defined(ESP_PLATFORM)
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_pthread.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); }
|
||||
#ifndef EMBEDDED_H
|
||||
#define EMBEDDED_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define HAS_MUTEX_CREATE_P 0
|
||||
#define HAS_PTHREAD_SETNAME_NP 1
|
||||
|
||||
#ifndef PTHREAD_STACK_MIN
|
||||
#define PTHREAD_STACK_MIN 256
|
||||
#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"
|
||||
#endif
|
||||
#endif
|
||||
#if EMBEDDED
|
||||
" EMBEDDED"
|
||||
#endif
|
||||
#if EVENTFD
|
||||
" EVENTFD"
|
||||
#endif
|
||||
@@ -381,7 +384,7 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
fprintf(stderr, "\nOption error: -%s\n\n", opt);
|
||||
usage(argv[0]);
|
||||
local_exit(1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (opt[0]) {
|
||||
@@ -432,7 +435,7 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg);
|
||||
usage(argv[0]);
|
||||
local_exit(1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -532,7 +535,7 @@ int main(int argc, char **argv) {
|
||||
pidfile = optarg;
|
||||
break;
|
||||
#endif
|
||||
#if !CONFIG_DACAUDIO && !CONFIG_BTAUDIO
|
||||
#ifndef EMBEDDED
|
||||
case 'l':
|
||||
list_devices();
|
||||
exit(0);
|
||||
@@ -675,12 +678,11 @@ int main(int argc, char **argv) {
|
||||
#endif
|
||||
case 't':
|
||||
license();
|
||||
local_exit(0);
|
||||
break; // mute compiler warning
|
||||
exit(0);
|
||||
case '?':
|
||||
usage(argv[0]);
|
||||
local_exit(0);
|
||||
break; // mute compiler warning
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Arg error: %s\n", argv[optind]);
|
||||
break;
|
||||
@@ -691,7 +693,7 @@ int main(int argc, char **argv) {
|
||||
if (optind < argc) {
|
||||
fprintf(stderr, "\nError: command line argument error\n\n");
|
||||
usage(argv[0]);
|
||||
local_exit(1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
@@ -757,13 +759,9 @@ int main(int argc, char **argv) {
|
||||
#endif
|
||||
|
||||
stream_init(log_stream, stream_buf_size);
|
||||
#ifdef EMBEDDED
|
||||
if(strstr(output_device,"BT")!=NULL || strstr(output_device,"bt")!=NULL) {
|
||||
output_init_bt(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);
|
||||
}
|
||||
|
||||
#if EMBEDDED
|
||||
output_init_embedded(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
|
||||
#else
|
||||
if (!strcmp(output_device, "-")) {
|
||||
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) {
|
||||
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);
|
||||
@@ -812,10 +810,8 @@ else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
||||
decode_close();
|
||||
stream_close();
|
||||
|
||||
#if CONFIG_DACAUDIO
|
||||
output_close_dac();
|
||||
#elif CONFIG_BTAUDIO
|
||||
output_close_bt();
|
||||
#if EMBEDDED
|
||||
output_close_embedded();
|
||||
#else
|
||||
if (!strcmp(output_device, "-")) {
|
||||
output_close_stdout();
|
||||
@@ -848,5 +844,5 @@ else if(strstr(output_device,"DAC")!=NULL || strstr(output_device,"dac")!=NULL){
|
||||
free_ssl_symbols();
|
||||
#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);
|
||||
if (!outputbuf->buf) {
|
||||
LOG_ERROR("unable to malloc output buffer");
|
||||
local_exit(0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
silencebuf = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
|
||||
if (!silencebuf) {
|
||||
LOG_ERROR("unable to malloc silence buffer");
|
||||
local_exit(0);
|
||||
exit(0);
|
||||
}
|
||||
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 {
|
||||
if (!test_open(output.device, output.supported_rates, user_rates)) {
|
||||
LOG_ERROR("unable to open output device: %s", output.device);
|
||||
local_exit(0);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
408
main/output_bt.c
408
main/output_bt.c
@@ -1,352 +1,59 @@
|
||||
#include "squeezelite.h"
|
||||
#include "perf_trace.h"
|
||||
|
||||
static log_level loglevel;
|
||||
|
||||
static bool running = true;
|
||||
|
||||
extern struct outputstate output;
|
||||
extern struct buffer *outputbuf;
|
||||
extern struct buffer *streambuf;
|
||||
|
||||
extern u8_t *silencebuf;
|
||||
|
||||
#define LOCK mutex_lock(outputbuf->mutex)
|
||||
#define UNLOCK mutex_unlock(outputbuf->mutex)
|
||||
|
||||
#ifdef USE_BT_RING_BUFFER
|
||||
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 LOCK_S mutex_lock(streambuf->mutex)
|
||||
#define UNLOCK_S mutex_unlock(streambuf->mutex)
|
||||
|
||||
#define FRAME_BLOCK MAX_SILENCE_FRAMES
|
||||
extern u8_t *silencebuf;
|
||||
|
||||
#define STATS_REPORT_DELAY_MS 15000
|
||||
|
||||
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,
|
||||
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
|
||||
|
||||
#define DECLARE_ALL_MIN_MAX \
|
||||
DECLARE_MIN_MAX(req);\
|
||||
DECLARE_MIN_MAX(rec);\
|
||||
DECLARE_MIN_MAX(o);\
|
||||
DECLARE_MIN_MAX(s);\
|
||||
DECLARE_MIN_MAX(locbtbuff);\
|
||||
DECLARE_MIN_MAX(bt);\
|
||||
DECLARE_MIN_MAX(under);\
|
||||
DECLARE_MIN_MAX_DURATION(mutex1);\
|
||||
DECLARE_MIN_MAX_DURATION(mutex2);\
|
||||
DECLARE_MIN_MAX_DURATION(buffering);\
|
||||
DECLARE_MIN_MAX_DURATION(sleep_time);
|
||||
DECLARE_MIN_MAX(stream_buf);\
|
||||
DECLARE_MIN_MAX_DURATION(lock_out_time)
|
||||
|
||||
#define RESET_ALL_MIN_MAX \
|
||||
RESET_MIN_MAX(o); \
|
||||
RESET_MIN_MAX(s); \
|
||||
RESET_MIN_MAX(locbtbuff); \
|
||||
RESET_MIN_MAX(bt); \
|
||||
RESET_MIN_MAX(req); \
|
||||
RESET_MIN_MAX(rec); \
|
||||
RESET_MIN_MAX(under); \
|
||||
RESET_MIN_MAX_DURATION(mutex1); \
|
||||
RESET_MIN_MAX_DURATION(mutex2); \
|
||||
RESET_MIN_MAX_DURATION(sleep_time); \
|
||||
RESET_MIN_MAX_DURATION(buffering);
|
||||
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
RESET_MIN_MAX(stream_buf); \
|
||||
RESET_MIN_MAX_DURATION(lock_out_time)
|
||||
|
||||
DECLARE_ALL_MIN_MAX;
|
||||
|
||||
void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
|
||||
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);
|
||||
/*
|
||||
* 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.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,
|
||||
s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
|
||||
#ifdef USE_BT_RING_BUFFER
|
||||
|
||||
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) {
|
||||
_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) {
|
||||
_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;
|
||||
memcpy(btout, buf, out_frames * BYTES_PER_FRAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void process_setd(u8_t *pkt, int len) {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(MODEL_NAME)
|
||||
#define MODEL_NAME SqueezeLite
|
||||
#endif
|
||||
@@ -42,16 +43,11 @@
|
||||
#define STR(macro) QUOTE(macro)
|
||||
#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
|
||||
#if defined(linux)
|
||||
// build detection
|
||||
#if defined (EMBEDDED)
|
||||
#undef EMBEDDED
|
||||
#define EMBEDDED 1
|
||||
#elif defined(linux)
|
||||
#define LINUX 1
|
||||
#define OSX 0
|
||||
#define WIN 0
|
||||
@@ -78,27 +74,19 @@
|
||||
#define PA18API 1
|
||||
#define OSX 0
|
||||
#define WIN 0
|
||||
#elif defined (POSIX)
|
||||
#undef POSIX
|
||||
#define POSIX 1
|
||||
#else
|
||||
#error unknown target
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_DACAUDIO)
|
||||
#undef CONFIG_DACAUDIO
|
||||
#define CONFIG_DACAUDIO 1
|
||||
#elif defined(CONFIG_BTAUDIO)
|
||||
#undef CONFIG_BTAUDIO
|
||||
#define CONFIG_BTAUDIO 1
|
||||
#elif LINUX && !defined(PORTAUDIO)
|
||||
#if !EMBEDDED
|
||||
#if LINUX && !defined(PORTAUDIO)
|
||||
#define ALSA 1
|
||||
#define PORTAUDIO 0
|
||||
#else
|
||||
#define ALSA 0
|
||||
#define PORTAUDIO 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LOOPBACK)
|
||||
#if SUN
|
||||
@@ -278,18 +266,18 @@
|
||||
#include <limits.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 <stdbool.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#if POSIX
|
||||
#include <sys/poll.h>
|
||||
#else
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#if !LINKALL
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
@@ -299,14 +287,10 @@
|
||||
#include <sys/types.h>
|
||||
#endif /* SUN */
|
||||
|
||||
#ifndef PTHREAD_STACK_MIN
|
||||
#define PTHREAD_STACK_MIN 256
|
||||
#endif
|
||||
|
||||
#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
|
||||
#define STREAM_THREAD_STACK_SIZE 64 * 1024
|
||||
#define DECODE_THREAD_STACK_SIZE 128 * 1024
|
||||
#define OUTPUT_THREAD_STACK_SIZE 64 * 1024
|
||||
#define IR_THREAD_STACK_SIZE 64 * 1024
|
||||
#if !OSX
|
||||
#define thread_t pthread_t;
|
||||
#endif
|
||||
@@ -314,13 +298,12 @@
|
||||
#define last_error() errno
|
||||
#define ERROR_WOULDBLOCK EWOULDBLOCK
|
||||
|
||||
#if !EMBEDDED
|
||||
#ifdef SUN
|
||||
typedef uint8_t u8_t;
|
||||
typedef uint16_t u16_t;
|
||||
typedef uint32_t u32_t;
|
||||
typedef uint64_t u64_t;
|
||||
#elif POSIX
|
||||
typedef unsigned long long u64_t;
|
||||
#else
|
||||
typedef u_int8_t u8_t;
|
||||
typedef u_int16_t u16_t;
|
||||
@@ -330,29 +313,20 @@ typedef u_int64_t u64_t;
|
||||
typedef int16_t s16_t;
|
||||
typedef int32_t s32_t;
|
||||
typedef int64_t s64_t;
|
||||
#endif
|
||||
|
||||
#define mutex_type pthread_mutex_t
|
||||
#define mutex_create(m) pthread_mutex_init(&m, NULL)
|
||||
#if POSIX
|
||||
#define mutex_create_p(m) mutex_create(m)
|
||||
#else
|
||||
#if HAS_MUTEX_CREATE_P
|
||||
#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
|
||||
#define mutex_lock(m) pthread_mutex_lock(&m)
|
||||
#define mutex_unlock(m) pthread_mutex_unlock(&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
|
||||
#endif
|
||||
#ifdef EMBEDDED
|
||||
#define local_exit(r) {static int ret=r; pthread_exit(&ret);}
|
||||
#else
|
||||
#define local_exit(r) exit(r)
|
||||
#endif
|
||||
|
||||
|
||||
#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_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__)
|
||||
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)
|
||||
typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type;
|
||||
#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
|
||||
typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, S24_BE, S24_3BE, S16_BE, S8_BE } output_format;
|
||||
#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_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir;
|
||||
@@ -763,26 +718,17 @@ void output_close_pa(void);
|
||||
void _pa_open(void);
|
||||
#endif
|
||||
|
||||
// output_dac.c
|
||||
|
||||
void set_volume_dac(unsigned left, unsigned right);
|
||||
// output_embedded.c
|
||||
#if EMBEDDED
|
||||
void set_volume(unsigned left, unsigned right);
|
||||
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_close_dac(void);
|
||||
void hal_dac_init(const char * options);
|
||||
|
||||
//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();
|
||||
|
||||
|
||||
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_embedded(void);
|
||||
#else
|
||||
// output_stdout.c
|
||||
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
|
||||
void output_close_stdout(void);
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -373,7 +373,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
|
||||
buf_init(streambuf, stream_buf_size);
|
||||
if (streambuf->buf == NULL) {
|
||||
LOG_ERROR("unable to malloc buffer");
|
||||
local_exit(0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#if USE_SSL
|
||||
@@ -405,8 +405,8 @@ void stream_init(log_level level, unsigned stream_buf_size) {
|
||||
#if LINUX || FREEBSD
|
||||
touch_memory(streambuf->buf, streambuf->size);
|
||||
#endif
|
||||
PTHREAD_SET_NAME("stream");
|
||||
#if LINUX || OSX || FREEBSD || POSIX
|
||||
|
||||
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
#ifdef PTHREAD_STACK_MIN
|
||||
@@ -414,6 +414,9 @@ PTHREAD_SET_NAME("stream");
|
||||
#endif
|
||||
pthread_create(&thread, &attr, stream_thread, NULL);
|
||||
pthread_attr_destroy(&attr);
|
||||
#if HAS_PTHREAD_SETNAME_NP
|
||||
pthread_setname_np(thread, "stream");
|
||||
#endif
|
||||
#endif
|
||||
#if WIN
|
||||
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"
|
||||
|
||||
#if LINUX || OSX || FREEBSD || POSIX
|
||||
#if LINUX || OSX || FREEBSD || EMBEDDED
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
@@ -103,7 +103,7 @@ u32_t gettime_ms(void) {
|
||||
#if WIN
|
||||
return GetTickCount();
|
||||
#else
|
||||
#if LINUX || FREEBSD || POSIX
|
||||
#if LINUX || FREEBSD || EMBEDDED
|
||||
struct timespec ts;
|
||||
#ifdef CLOCK_MONOTONIC
|
||||
if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
||||
@@ -561,77 +561,3 @@ char *strcasestr(const char *haystack, const char *needle) {
|
||||
return NULL;
|
||||
}
|
||||
#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