123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- /*
- * Squeezelite for esp32
- *
- * (c) Sebastien 2019
- * Philippe G. 2019, philippe_44@outlook.com
- *
- * This software is released under the MIT License.
- * https://opensource.org/licenses/MIT
- *
- */
- #include <assert.h>
- #include "driver/gpio.h"
- #include "squeezelite.h"
- #include "equalizer.h"
- #include "perf_trace.h"
- #include "platform_config.h"
- #include "services.h"
- #include "led.h"
- 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)
- #define LOCK_S mutex_lock(streambuf->mutex)
- #define UNLOCK_S mutex_unlock(streambuf->mutex)
- #define FRAME_BLOCK MAX_SILENCE_FRAMES
- #define STATS_REPORT_DELAY_MS 15000
- extern void hal_bluetooth_init(const char * options);
- extern void hal_bluetooth_stop(void);
- extern u8_t config_spdif_gpio;
- static log_level loglevel;
- static bool running = false;
- static uint8_t *btout;
- static frames_t oframes;
- static bool stats;
- static uint32_t bt_idle_since;
- static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
- 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(bt);\
- DECLARE_MIN_MAX(under);\
- DECLARE_MIN_MAX(stream_buf);\
- DECLARE_MIN_MAX_DURATION(lock_out_time)
-
- #define RESET_ALL_MIN_MAX \
- RESET_MIN_MAX(bt); \
- RESET_MIN_MAX(req); \
- RESET_MIN_MAX(rec); \
- RESET_MIN_MAX(under); \
- RESET_MIN_MAX(stream_buf); \
- RESET_MIN_MAX_DURATION(lock_out_time)
-
- DECLARE_ALL_MIN_MAX;
- /****************************************************************************************
- * Get inactivity callback
- */
- static uint32_t bt_idle_callback(void) {
- return output.state <= OUTPUT_STOPPED ? pdTICKS_TO_MS(xTaskGetTickCount()) - bt_idle_since : 0;
- }
-
- /****************************************************************************************
- * Init BT sink
- */
- void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
- loglevel = level;
-
- // idle counter
- bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
- services_sleep_setsleeper(bt_idle_callback);
-
- // even BT has a right to use led :-)
- led_blink(LED_GREEN, 200, 1000);
- running = true;
- output.write_cb = &_write_frames;
- hal_bluetooth_init(device);
- char *p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0);
- stats = p && (*p == '1' || *p == 'Y' || *p == 'y');
- free(p);
- equalizer_set_samplerate(output.current_sample_rate);
- }
- /****************************************************************************************
- * Close BT sink
- */
- void output_close_bt(void) {
- LOCK;
- running = false;
- UNLOCK;
- hal_bluetooth_stop();
- equalizer_close();
- }
- /****************************************************************************************
- * Data framing callback
- */
- static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags,
- s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) {
-
- assert(btout != NULL);
-
- 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);
- }
- _apply_gain(outputbuf, out_frames, gainL, gainR, flags);
- #if BYTES_PER_FRAME == 4
- memcpy(btout + oframes * BYTES_PER_FRAME, outputbuf->readp, out_frames * BYTES_PER_FRAME);
- #else
- {
- frames_t count = out_frames;
- s32_t *_iptr = (s32_t*) outputbuf->readp;
- s16_t *_optr = (s16_t*) (btout + oframes * BYTES_PER_FRAME);
- while (count--) {
- *_optr++ = *_iptr++ >> 16;
- *_optr++ = *_iptr++ >> 16;
- }
- }
- #endif
- } else {
- u8_t *buf = silencebuf;
- memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
- }
- // don't update visu if we don't have enough data in buffer (500 ms)
- if (silence || _buf_used(outputbuf) > BYTES_PER_FRAME * output.current_sample_rate / 2) {
- output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
- }
-
- oframes += out_frames;
- return (int)out_frames;
- }
- /****************************************************************************************
- * Data callback for BT stack
- */
- int32_t output_bt_data(uint8_t *data, int32_t len) {
- int32_t iframes = len / BYTES_PER_FRAME, start_timer = 0;
- if (iframes <= 0 || data == NULL || !running) {
- return 0;
- }
-
- btout = data;
- oframes = 0;
- // 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
- SET_MIN_MAX(len,req);
- TIME_MEASUREMENT_START(start_timer);
-
- LOCK;
- SET_MIN_MAX_SIZED(_buf_used(outputbuf),bt,outputbuf->size);
- output.device_frames = 0;
- output.updated = gettime_ms();
- output.frames_played_dmp = output.frames_played;
- _output_frames(iframes);
- output.frames_in_process = oframes;
- UNLOCK;
-
- equalizer_process(data, oframes * BYTES_PER_FRAME);
- SET_MIN_MAX(TIME_MEASUREMENT_GET(start_timer),lock_out_time);
- SET_MIN_MAX((len-oframes*BYTES_PER_FRAME), rec);
- TIME_MEASUREMENT_START(start_timer);
- return oframes * BYTES_PER_FRAME;
- }
- /****************************************************************************************
- * Tick for BT
- */
- void output_bt_tick(void) {
- static time_t lastTime=0;
-
- if (!running) return;
-
- LOCK_S;
- SET_MIN_MAX_SIZED(_buf_used(streambuf), stream_buf, streambuf->size);
- UNLOCK_S;
-
- if (stats && 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;
- }
- }
- /****************************************************************************************
- * BT playback stop
- */
- void output_bt_stop(void) {
- led_blink(LED_GREEN, 200, 1000);
- bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
- }
- /****************************************************************************************
- * BT playback start
- */
- void output_bt_start(void) {
- led_on(LED_GREEN);
- bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
- }
|