diff --git a/lib/libsamplerate/libsamplerate-0.2.2/include/samplerate.h b/lib/libsamplerate/libsamplerate-0.2.2/include/samplerate.h index a3d5ae1a..c06024d2 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/include/samplerate.h +++ b/lib/libsamplerate/libsamplerate-0.2.2/include/samplerate.h @@ -32,7 +32,7 @@ typedef struct int end_of_input ; - double src_ratio ; + float src_ratio ; } SRC_DATA ; /* @@ -89,7 +89,7 @@ int src_process (SRC_STATE *state, SRC_DATA *data) ; ** Callback based processing function. Read up to frames worth of data from ** the converter int *data and return frames read or -1 on error. */ -long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ; +long src_callback_read (SRC_STATE *state, float src_ratio, long frames, float *data) ; /* ** Simple interface for performing a single conversion from input buffer to @@ -119,7 +119,7 @@ const char *src_get_version (void) ; ** Returns non zero on error. */ -int src_set_ratio (SRC_STATE *state, double new_ratio) ; +int src_set_ratio (SRC_STATE *state, float new_ratio) ; /* ** Get the current channel count. @@ -142,7 +142,7 @@ int src_reset (SRC_STATE *state) ; ** otherwise. */ -int src_is_valid_ratio (double ratio) ; +int src_is_valid_ratio (float ratio) ; /* ** Return an error number. diff --git a/lib/libsamplerate/libsamplerate-0.2.2/src/common.h b/lib/libsamplerate/libsamplerate-0.2.2/src/common.h index c5b7d90b..b9343313 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/src/common.h +++ b/lib/libsamplerate/libsamplerate-0.2.2/src/common.h @@ -156,7 +156,7 @@ struct SRC_STATE_tag { SRC_STATE_VT *vt ; - double last_ratio, last_position ; + float last_ratio, last_position ; SRC_ERROR error ; int channels ; @@ -209,7 +209,7 @@ static inline int #ifdef USE_TARGET_ATTRIBUTE __attribute__((target("sse2"))) #endif -psf_lrint (double x) +psf_lrint (float x) { return _mm_cvtsd_si32 (_mm_load_sd (&x)) ; } @@ -221,7 +221,7 @@ static inline int psf_lrintf (float x) return lrintf (x) ; } /* psf_lrintf */ -static inline int psf_lrint (double x) +static inline int psf_lrint (float x) { return lrint (x) ; } /* psf_lrint */ @@ -231,9 +231,9 @@ static inline int psf_lrint (double x) ** Common static inline functions. */ -static inline double -fmod_one (double x) -{ double res ; +static inline float +fmod_one (float x) +{ float res ; res = x - psf_lrint (x) ; if (res < 0.0) @@ -243,7 +243,7 @@ fmod_one (double x) } /* fmod_one */ static inline int -is_bad_src_ratio (double ratio) +is_bad_src_ratio (float ratio) { return (ratio < (1.0 / SRC_MAX_RATIO) || ratio > (1.0 * SRC_MAX_RATIO)) ; } /* is_bad_src_ratio */ diff --git a/lib/libsamplerate/libsamplerate-0.2.2/src/samplerate.c b/lib/libsamplerate/libsamplerate-0.2.2/src/samplerate.c index 6a51f279..700d42f7 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/src/samplerate.c +++ b/lib/libsamplerate/libsamplerate-0.2.2/src/samplerate.c @@ -143,7 +143,7 @@ src_process (SRC_STATE *state, SRC_DATA *data) } /* src_process */ long -src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) +src_callback_read (SRC_STATE *state, float src_ratio, long frames, float *data) { SRC_DATA src_data ; @@ -238,7 +238,7 @@ src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) */ int -src_set_ratio (SRC_STATE *state, double new_ratio) +src_set_ratio (SRC_STATE *state, float new_ratio) { if (state == NULL) return SRC_ERR_BAD_STATE ; @@ -321,7 +321,7 @@ src_get_version (void) } /* src_get_version */ int -src_is_valid_ratio (double ratio) +src_is_valid_ratio (float ratio) { if (is_bad_src_ratio (ratio)) return SRC_FALSE ; @@ -461,7 +461,7 @@ src_int_to_float_array (const int *in, float *out, int len) void src_float_to_int_array (const float *in, int *out, int len) -{ double scaled_value ; +{ float scaled_value ; for (int i = 0 ; i < len ; i++) { scaled_value = in [i] * (8.0 * 0x10000000) ; diff --git a/lib/libsamplerate/libsamplerate-0.2.2/src/src_linear.c b/lib/libsamplerate/libsamplerate-0.2.2/src/src_linear.c index 43a0fd40..360166a7 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/src/src_linear.c +++ b/lib/libsamplerate/libsamplerate-0.2.2/src/src_linear.c @@ -53,7 +53,7 @@ static SRC_STATE_VT linear_state_vt = static SRC_ERROR linear_vari_process (SRC_STATE *state, SRC_DATA *data) { LINEAR_DATA *priv ; - double src_ratio, input_index, rem ; + float src_ratio, input_index, rem ; int ch ; if (data->input_frames <= 0) @@ -93,7 +93,7 @@ linear_vari_process (SRC_STATE *state, SRC_DATA *data) for (ch = 0 ; ch < state->channels ; ch++) { data->data_out [priv->out_gen] = (float) (priv->last_value [ch] + input_index * - ((double) data->data_in [ch] - priv->last_value [ch])) ; + ((float) data->data_in [ch] - priv->last_value [ch])) ; priv->out_gen ++ ; } ; @@ -120,7 +120,7 @@ linear_vari_process (SRC_STATE *state, SRC_DATA *data) for (ch = 0 ; ch < state->channels ; ch++) { data->data_out [priv->out_gen] = (float) (data->data_in [priv->in_used - state->channels + ch] + input_index * - ((double) data->data_in [priv->in_used + ch] - data->data_in [priv->in_used - state->channels + ch])) ; + ((float) data->data_in [priv->in_used + ch] - data->data_in [priv->in_used - state->channels + ch])) ; priv->out_gen ++ ; } ; diff --git a/lib/libsamplerate/libsamplerate-0.2.2/src/src_sinc.c b/lib/libsamplerate/libsamplerate-0.2.2/src/src_sinc.c index 716c4a40..0a594e47 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/src/src_sinc.c +++ b/lib/libsamplerate/libsamplerate-0.2.2/src/src_sinc.c @@ -26,7 +26,7 @@ #define MAKE_INCREMENT_T(x) ((increment_t) (x)) #define SHIFT_BITS 12 -#define FP_ONE ((double) (((increment_t) 1) << SHIFT_BITS)) +#define FP_ONE ((float) (((increment_t) 1) << SHIFT_BITS)) #define INV_FP_ONE (1.0 / FP_ONE) /* Customixe max channls from Kconfig. */ @@ -61,14 +61,14 @@ typedef struct int coeff_half_len, index_inc ; - double src_ratio, input_index ; + float src_ratio, input_index ; coeff_t const *coeffs ; int b_current, b_end, b_real_end, b_len ; /* Sure hope noone does more than 128 channels at once. */ - double left_calc [MAX_CHANNELS], right_calc [MAX_CHANNELS] ; + float left_calc [MAX_CHANNELS], right_calc [MAX_CHANNELS] ; float *buffer ; } SINC_FILTER ; @@ -131,9 +131,9 @@ static SRC_STATE_VT sinc_mono_state_vt = } ; static inline increment_t -double_to_fp (double x) +float_to_fp (float x) { return (increment_t) (psf_lrint ((x) * FP_ONE)) ; -} /* double_to_fp */ +} /* float_to_fp */ static inline increment_t int_to_fp (int x) @@ -150,10 +150,10 @@ fp_fraction_part (increment_t x) { return ((x) & ((((increment_t) 1) << SHIFT_BITS) - 1)) ; } /* fp_fraction_part */ -static inline double -fp_to_double (increment_t x) +static inline float +fp_to_float (increment_t x) { return fp_fraction_part (x) * INV_FP_ONE ; -} /* fp_to_double */ +} /* fp_to_float */ static inline int int_div_ceil (int divident, int divisor) /* == (int) ceil ((float) divident / divisor) */ @@ -367,9 +367,9 @@ sinc_copy (SRC_STATE *state) ** Beware all ye who dare pass this point. There be dragons here. */ -static inline double +static inline float calc_output_single (SINC_FILTER *filter, increment_t increment, increment_t start_filter_index) -{ double fraction, left, right, icoeff ; +{ float fraction, left, right, icoeff ; increment_t filter_index, max_filter_index ; int data_index, coeff_count, indx ; @@ -391,7 +391,7 @@ calc_output_single (SINC_FILTER *filter, increment_t increment, increment_t star } left = 0.0 ; while (filter_index >= MAKE_INCREMENT_T (0)) - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -411,7 +411,7 @@ calc_output_single (SINC_FILTER *filter, increment_t increment, increment_t star right = 0.0 ; do - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -430,7 +430,7 @@ calc_output_single (SINC_FILTER *filter, increment_t increment, increment_t star static SRC_ERROR sinc_mono_vari_process (SRC_STATE *state, SRC_DATA *data) { SINC_FILTER *filter ; - double input_index, src_ratio, count, float_increment, terminate, rem ; + float input_index, src_ratio, count, float_increment, terminate, rem ; increment_t increment, start_filter_index ; int half_filter_chan_len, samples_in_hand ; @@ -493,9 +493,9 @@ sinc_mono_vari_process (SRC_STATE *state, SRC_DATA *data) src_ratio = state->last_ratio + filter->out_gen * (data->src_ratio - state->last_ratio) / filter->out_count ; float_increment = filter->index_inc * (src_ratio < 1.0 ? src_ratio : 1.0) ; - increment = double_to_fp (float_increment) ; + increment = float_to_fp (float_increment) ; - start_filter_index = double_to_fp (input_index * float_increment) ; + start_filter_index = float_to_fp (input_index * float_increment) ; data->data_out [filter->out_gen] = (float) ((float_increment / filter->index_inc) * calc_output_single (filter, increment, start_filter_index)) ; @@ -521,8 +521,8 @@ sinc_mono_vari_process (SRC_STATE *state, SRC_DATA *data) } /* sinc_mono_vari_process */ static inline void -calc_output_stereo (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, double scale, float * output) -{ double fraction, left [2], right [2], icoeff ; +calc_output_stereo (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, float scale, float * output) +{ float fraction, left [2], right [2], icoeff ; increment_t filter_index, max_filter_index ; int data_index, coeff_count, indx ; @@ -544,7 +544,7 @@ calc_output_stereo (SINC_FILTER *filter, int channels, increment_t increment, in } left [0] = left [1] = 0.0 ; while (filter_index >= MAKE_INCREMENT_T (0)) - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -565,7 +565,7 @@ calc_output_stereo (SINC_FILTER *filter, int channels, increment_t increment, in right [0] = right [1] = 0.0 ; do - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -586,7 +586,7 @@ calc_output_stereo (SINC_FILTER *filter, int channels, increment_t increment, in SRC_ERROR sinc_stereo_vari_process (SRC_STATE *state, SRC_DATA *data) { SINC_FILTER *filter ; - double input_index, src_ratio, count, float_increment, terminate, rem ; + float input_index, src_ratio, count, float_increment, terminate, rem ; increment_t increment, start_filter_index ; int half_filter_chan_len, samples_in_hand ; @@ -649,9 +649,9 @@ sinc_stereo_vari_process (SRC_STATE *state, SRC_DATA *data) src_ratio = state->last_ratio + filter->out_gen * (data->src_ratio - state->last_ratio) / filter->out_count ; float_increment = filter->index_inc * (src_ratio < 1.0 ? src_ratio : 1.0) ; - increment = double_to_fp (float_increment) ; + increment = float_to_fp (float_increment) ; - start_filter_index = double_to_fp (input_index * float_increment) ; + start_filter_index = float_to_fp (input_index * float_increment) ; calc_output_stereo (filter, state->channels, increment, start_filter_index, float_increment / filter->index_inc, data->data_out + filter->out_gen) ; filter->out_gen += 2 ; @@ -676,8 +676,8 @@ sinc_stereo_vari_process (SRC_STATE *state, SRC_DATA *data) } /* sinc_stereo_vari_process */ static inline void -calc_output_quad (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, double scale, float * output) -{ double fraction, left [4], right [4], icoeff ; +calc_output_quad (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, float scale, float * output) +{ float fraction, left [4], right [4], icoeff ; increment_t filter_index, max_filter_index ; int data_index, coeff_count, indx ; @@ -699,7 +699,7 @@ calc_output_quad (SINC_FILTER *filter, int channels, increment_t increment, incr } left [0] = left [1] = left [2] = left [3] = 0.0 ; while (filter_index >= MAKE_INCREMENT_T (0)) - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -720,7 +720,7 @@ calc_output_quad (SINC_FILTER *filter, int channels, increment_t increment, incr right [0] = right [1] = right [2] = right [3] = 0.0 ; do - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -742,7 +742,7 @@ calc_output_quad (SINC_FILTER *filter, int channels, increment_t increment, incr SRC_ERROR sinc_quad_vari_process (SRC_STATE *state, SRC_DATA *data) { SINC_FILTER *filter ; - double input_index, src_ratio, count, float_increment, terminate, rem ; + float input_index, src_ratio, count, float_increment, terminate, rem ; increment_t increment, start_filter_index ; int half_filter_chan_len, samples_in_hand ; @@ -805,9 +805,9 @@ sinc_quad_vari_process (SRC_STATE *state, SRC_DATA *data) src_ratio = state->last_ratio + filter->out_gen * (data->src_ratio - state->last_ratio) / filter->out_count ; float_increment = filter->index_inc * (src_ratio < 1.0 ? src_ratio : 1.0) ; - increment = double_to_fp (float_increment) ; + increment = float_to_fp (float_increment) ; - start_filter_index = double_to_fp (input_index * float_increment) ; + start_filter_index = float_to_fp (input_index * float_increment) ; calc_output_quad (filter, state->channels, increment, start_filter_index, float_increment / filter->index_inc, data->data_out + filter->out_gen) ; filter->out_gen += 4 ; @@ -832,8 +832,8 @@ sinc_quad_vari_process (SRC_STATE *state, SRC_DATA *data) } /* sinc_quad_vari_process */ static inline void -calc_output_hex (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, double scale, float * output) -{ double fraction, left [6], right [6], icoeff ; +calc_output_hex (SINC_FILTER *filter, int channels, increment_t increment, increment_t start_filter_index, float scale, float * output) +{ float fraction, left [6], right [6], icoeff ; increment_t filter_index, max_filter_index ; int data_index, coeff_count, indx ; @@ -855,7 +855,7 @@ calc_output_hex (SINC_FILTER *filter, int channels, increment_t increment, incre } left [0] = left [1] = left [2] = left [3] = left [4] = left [5] = 0.0 ; while (filter_index >= MAKE_INCREMENT_T (0)) - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -876,7 +876,7 @@ calc_output_hex (SINC_FILTER *filter, int channels, increment_t increment, incre right [0] = right [1] = right [2] = right [3] = right [4] = right [5] = 0.0 ; do - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -897,7 +897,7 @@ calc_output_hex (SINC_FILTER *filter, int channels, increment_t increment, incre SRC_ERROR sinc_hex_vari_process (SRC_STATE *state, SRC_DATA *data) { SINC_FILTER *filter ; - double input_index, src_ratio, count, float_increment, terminate, rem ; + float input_index, src_ratio, count, float_increment, terminate, rem ; increment_t increment, start_filter_index ; int half_filter_chan_len, samples_in_hand ; @@ -960,9 +960,9 @@ sinc_hex_vari_process (SRC_STATE *state, SRC_DATA *data) src_ratio = state->last_ratio + filter->out_gen * (data->src_ratio - state->last_ratio) / filter->out_count ; float_increment = filter->index_inc * (src_ratio < 1.0 ? src_ratio : 1.0) ; - increment = double_to_fp (float_increment) ; + increment = float_to_fp (float_increment) ; - start_filter_index = double_to_fp (input_index * float_increment) ; + start_filter_index = float_to_fp (input_index * float_increment) ; calc_output_hex (filter, state->channels, increment, start_filter_index, float_increment / filter->index_inc, data->data_out + filter->out_gen) ; filter->out_gen += 6 ; @@ -987,10 +987,10 @@ sinc_hex_vari_process (SRC_STATE *state, SRC_DATA *data) } /* sinc_hex_vari_process */ static inline void -calc_output_multi (SINC_FILTER *filter, increment_t increment, increment_t start_filter_index, int channels, double scale, float * output) -{ double fraction, icoeff ; +calc_output_multi (SINC_FILTER *filter, increment_t increment, increment_t start_filter_index, int channels, float scale, float * output) +{ float fraction, icoeff ; /* The following line is 1999 ISO Standard C. If your compiler complains, get a better compiler. */ - double *left, *right ; + float *left, *right ; increment_t filter_index, max_filter_index ; int data_index, coeff_count, indx ; @@ -1017,7 +1017,7 @@ calc_output_multi (SINC_FILTER *filter, increment_t increment, increment_t start memset (left, 0, sizeof (left [0]) * channels) ; while (filter_index >= MAKE_INCREMENT_T (0)) - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -1039,7 +1039,7 @@ calc_output_multi (SINC_FILTER *filter, increment_t increment, increment_t start memset (right, 0, sizeof (right [0]) * channels) ; do - { fraction = fp_to_double (filter_index) ; + { fraction = fp_to_float (filter_index) ; indx = fp_to_int (filter_index) ; assert (indx >= 0 && indx + 1 < filter->coeff_half_len + 2) ; icoeff = filter->coeffs [indx] + fraction * (filter->coeffs [indx + 1] - filter->coeffs [indx]) ; @@ -1062,7 +1062,7 @@ calc_output_multi (SINC_FILTER *filter, increment_t increment, increment_t start static SRC_ERROR sinc_multichan_vari_process (SRC_STATE *state, SRC_DATA *data) { SINC_FILTER *filter ; - double input_index, src_ratio, count, float_increment, terminate, rem ; + float input_index, src_ratio, count, float_increment, terminate, rem ; increment_t increment, start_filter_index ; int half_filter_chan_len, samples_in_hand ; @@ -1125,9 +1125,9 @@ sinc_multichan_vari_process (SRC_STATE *state, SRC_DATA *data) src_ratio = state->last_ratio + filter->out_gen * (data->src_ratio - state->last_ratio) / filter->out_count ; float_increment = filter->index_inc * (src_ratio < 1.0 ? src_ratio : 1.0) ; - increment = double_to_fp (float_increment) ; + increment = float_to_fp (float_increment) ; - start_filter_index = double_to_fp (input_index * float_increment) ; + start_filter_index = float_to_fp (input_index * float_increment) ; calc_output_multi (filter, increment, start_filter_index, state->channels, float_increment / filter->index_inc, data->data_out + filter->out_gen) ; filter->out_gen += state->channels ; diff --git a/lib/libsamplerate/libsamplerate-0.2.2/src/src_zoh.c b/lib/libsamplerate/libsamplerate-0.2.2/src/src_zoh.c index 41ba79a1..c389d6cf 100755 --- a/lib/libsamplerate/libsamplerate-0.2.2/src/src_zoh.c +++ b/lib/libsamplerate/libsamplerate-0.2.2/src/src_zoh.c @@ -51,7 +51,7 @@ static SRC_STATE_VT zoh_state_vt = static SRC_ERROR zoh_vari_process (SRC_STATE *state, SRC_DATA *data) { ZOH_DATA *priv ; - double src_ratio, input_index, rem ; + float src_ratio, input_index, rem ; int ch ; if (data->input_frames <= 0) diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 22a0160c..9e50f8ff 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -5,7 +5,7 @@ idf_component_register( SRCS "audio_task.cpp" "chunk.cpp" "fatfs_audio_input.cpp" "stream_message.cpp" "i2s_audio_output.cpp" "stream_buffer.cpp" "track_queue.cpp" - "stream_event.cpp" "stream_info.cpp" "audio_fsm.cpp" + "stream_event.cpp" "stream_info.cpp" "audio_fsm.cpp" "sink_mixer.cpp" INCLUDE_DIRS "include" REQUIRES "codecs" "drivers" "cbor" "result" "tasks" "span" "memory" "tinyfsm" "database" "system_fsm" "playlist" "libsamplerate") diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 7e0fb207..c3498965 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -34,6 +34,7 @@ #include "freertos/queue.h" #include "freertos/ringbuf.h" #include "pipeline.hpp" +#include "sink_mixer.hpp" #include "span.hpp" #include "arena.hpp" @@ -115,14 +116,12 @@ AudioTask::AudioTask(IAudioSource* source, IAudioSink* sink) : source_(source), sink_(sink), codec_(), + mixer_(new SinkMixer(sink->stream())), timer_(), has_begun_decoding_(false), current_input_format_(), current_output_format_(), - sample_buffer_(reinterpret_cast( - heap_caps_malloc(kSampleBufferSize, - MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))), - sample_buffer_len_(kSampleBufferSize) {} + codec_buffer_(new RawStream(kSampleBufferSize)) {} void AudioTask::Main() { for (;;) { @@ -246,13 +245,17 @@ auto AudioTask::BeginDecoding(InputStream& stream) -> bool { return false; } + OutputStream writer{codec_buffer_.get()}; + writer.prepare(new_format, {}); + return true; } auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { while (!stream.data().empty()) { - auto res = codec_->ContinueStream(stream.data(), - {sample_buffer_, sample_buffer_len_}); + OutputStream writer{codec_buffer_.get()}; + + auto res = codec_->ContinueStream(stream.data(), writer.data()); stream.consume(res.first); @@ -263,9 +266,10 @@ auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { return false; } } else { - xStreamBufferSend(sink_->stream(), sample_buffer_, - res.second->bytes_written, portMAX_DELAY); - timer_->AddBytes(res.second->bytes_written); + writer.add(res.second->bytes_written); + + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); } } return true; @@ -284,21 +288,22 @@ auto AudioTask::FinishDecoding(InputStream& stream) -> void { std::unique_ptr mad_buffer; mad_buffer.reset(new RawStream(stream.data().size_bytes() + 8)); - OutputStream writer{mad_buffer.get()}; + OutputStream mad_writer{mad_buffer.get()}; std::copy(stream.data().begin(), stream.data().end(), - writer.data().begin()); - std::fill(writer.data().begin(), writer.data().end(), std::byte{0}); + mad_writer.data().begin()); + std::fill(mad_writer.data().begin(), mad_writer.data().end(), std::byte{0}); InputStream padded_stream{mad_buffer.get()}; - auto res = codec_->ContinueStream(stream.data(), - {sample_buffer_, sample_buffer_len_}); + OutputStream writer{codec_buffer_.get()}; + auto res = codec_->ContinueStream(stream.data(), writer.data()); if (res.second.has_error()) { return; } - xStreamBufferSend(sink_->stream(), sample_buffer_, - res.second->bytes_written, portMAX_DELAY); - timer_->AddBytes(res.second->bytes_written); + writer.add(res.second->bytes_written); + + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); } } @@ -319,24 +324,31 @@ auto AudioTask::ForwardPcmStream(StreamInfo::Pcm& format, xStreamBufferSend(sink_->stream(), samples.data(), samples.size_bytes(), portMAX_DELAY); timer_->AddBytes(samples.size_bytes()); + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); + return true; } auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format, const Duration& duration) -> bool { if (format != current_output_format_) { - // The new format is different to the old one. Wait for the sink to drain - // before continuing. - while (!xStreamBufferIsEmpty(sink_->stream())) { - ESP_LOGI(kTag, "waiting for sink stream to drain..."); - // TODO(jacqueline): Get the sink drain ISR to notify us of this - // via semaphore instead of busy-ish waiting. - vTaskDelay(pdMS_TO_TICKS(100)); - } + current_output_format_ = format; + StreamInfo::Pcm new_sink_format = sink_->PrepareFormat(format); + if (new_sink_format != current_sink_format_) { + current_sink_format_ = new_sink_format; + + // The new format is different to the old one. Wait for the sink to drain + // before continuing. + while (!xStreamBufferIsEmpty(sink_->stream())) { + ESP_LOGI(kTag, "waiting for sink stream to drain..."); + // TODO(jacqueline): Get the sink drain ISR to notify us of this + // via semaphore instead of busy-ish waiting. + vTaskDelay(pdMS_TO_TICKS(10)); + } - ESP_LOGI(kTag, "configuring sink"); - if (!sink_->Configure(format)) { - return false; + ESP_LOGI(kTag, "configuring sink"); + sink_->Configure(new_sink_format); } } @@ -345,4 +357,17 @@ auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format, return true; } +auto AudioTask::SendToSink(InputStream& stream) -> void { + std::size_t bytes_to_send = stream.data().size_bytes(); + std::size_t bytes_sent; + if (stream.info().format_as() == current_sink_format_) { + bytes_sent = xStreamBufferSend(sink_->stream(), stream.data().data(), + bytes_to_send, portMAX_DELAY); + stream.consume(bytes_sent); + } else { + bytes_sent = mixer_->MixAndSend(stream, current_sink_format_.value()); + } + timer_->AddBytes(bytes_sent); +} + } // namespace audio diff --git a/src/audio/i2s_audio_output.cpp b/src/audio/i2s_audio_output.cpp index bb413b38..e53dbe2a 100644 --- a/src/audio/i2s_audio_output.cpp +++ b/src/audio/i2s_audio_output.cpp @@ -115,10 +115,19 @@ auto I2SAudioOutput::AdjustVolumeDown() -> bool { return true; } -auto I2SAudioOutput::Configure(const StreamInfo::Pcm& pcm) -> bool { +auto I2SAudioOutput::PrepareFormat(const StreamInfo::Pcm& orig) + -> StreamInfo::Pcm { + return StreamInfo::Pcm{ + .channels = std::min(orig.channels, 2), + .bits_per_sample = std::clamp(orig.bits_per_sample, 16, 32), + .sample_rate = std::clamp(orig.sample_rate, 8000, 96000), + }; +} + +auto I2SAudioOutput::Configure(const StreamInfo::Pcm& pcm) -> void { if (current_config_ && pcm == *current_config_) { ESP_LOGI(kTag, "ignoring unchanged format"); - return true; + return; } ESP_LOGI(kTag, "incoming audio stream: %u ch %u bpp @ %lu Hz", pcm.channels, @@ -134,7 +143,7 @@ auto I2SAudioOutput::Configure(const StreamInfo::Pcm& pcm) -> bool { break; default: ESP_LOGE(kTag, "dropping stream with out of bounds channels"); - return false; + return; } drivers::I2SDac::BitsPerSample bps; @@ -150,30 +159,36 @@ auto I2SAudioOutput::Configure(const StreamInfo::Pcm& pcm) -> bool { break; default: ESP_LOGE(kTag, "dropping stream with unknown bps"); - return false; + return; } drivers::I2SDac::SampleRate sample_rate; switch (pcm.sample_rate) { + case 8000: + sample_rate = drivers::I2SDac::SAMPLE_RATE_8; + break; + case 32000: + sample_rate = drivers::I2SDac::SAMPLE_RATE_32; + break; case 44100: sample_rate = drivers::I2SDac::SAMPLE_RATE_44_1; break; case 48000: sample_rate = drivers::I2SDac::SAMPLE_RATE_48; break; + case 88200: + sample_rate = drivers::I2SDac::SAMPLE_RATE_88_2; + break; + case 96000: + sample_rate = drivers::I2SDac::SAMPLE_RATE_96; + break; default: ESP_LOGE(kTag, "dropping stream with unknown rate"); - return false; + return; } dac_->Reconfigure(ch, bps, sample_rate); current_config_ = pcm; - - return true; -} - -auto I2SAudioOutput::Send(const cpp::span& data) -> void { - dac_->WriteData(data); } } // namespace audio diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index 261f7c79..20bb6da6 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -38,8 +38,8 @@ class IAudioSink { virtual auto AdjustVolumeUp() -> bool = 0; virtual auto AdjustVolumeDown() -> bool = 0; - virtual auto Configure(const StreamInfo::Pcm& format) -> bool = 0; - virtual auto Send(const cpp::span& data) -> void = 0; + virtual auto PrepareFormat(const StreamInfo::Pcm&) -> StreamInfo::Pcm = 0; + virtual auto Configure(const StreamInfo::Pcm& format) -> void = 0; auto stream() -> StreamBufferHandle_t { return stream_; } }; diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index f6e9789b..b27aa039 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -14,6 +14,7 @@ #include "audio_source.hpp" #include "codec.hpp" #include "pipeline.hpp" +#include "sink_mixer.hpp" #include "stream_info.hpp" namespace audio { @@ -63,18 +64,20 @@ class AudioTask { auto ForwardPcmStream(StreamInfo::Pcm&, cpp::span) -> bool; auto ConfigureSink(const StreamInfo::Pcm&, const Duration&) -> bool; + auto SendToSink(InputStream&) -> void; IAudioSource* source_; IAudioSink* sink_; std::unique_ptr codec_; + std::unique_ptr mixer_; std::unique_ptr timer_; bool has_begun_decoding_; std::optional current_input_format_; std::optional current_output_format_; + std::optional current_sink_format_; - std::byte* sample_buffer_; - std::size_t sample_buffer_len_; + std::unique_ptr codec_buffer_; }; } // namespace audio diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 43155711..e0f791c5 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -35,8 +35,8 @@ class I2SAudioOutput : public IAudioSink { auto AdjustVolumeUp() -> bool override; auto AdjustVolumeDown() -> bool override; - auto Configure(const StreamInfo::Pcm& format) -> bool override; - auto Send(const cpp::span& data) -> void override; + auto PrepareFormat(const StreamInfo::Pcm&) -> StreamInfo::Pcm override; + auto Configure(const StreamInfo::Pcm& format) -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; diff --git a/src/audio/include/sink_mixer.hpp b/src/audio/include/sink_mixer.hpp new file mode 100644 index 00000000..632ffa2e --- /dev/null +++ b/src/audio/include/sink_mixer.hpp @@ -0,0 +1,88 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include + +#include "samplerate.h" + +#include "audio_decoder.hpp" +#include "audio_sink.hpp" +#include "audio_source.hpp" +#include "codec.hpp" +#include "pipeline.hpp" +#include "stream_info.hpp" + +namespace audio { + +/* + * Handles the final downmix + resample + quantisation stage of audio, + * generation sending the result directly to an IAudioSink. + */ +class SinkMixer { + public: + SinkMixer(StreamBufferHandle_t dest); + ~SinkMixer(); + + auto MixAndSend(InputStream&, const StreamInfo::Pcm&) -> std::size_t; + + private: + auto Main() -> void; + + auto SetTargetFormat(const StreamInfo::Pcm& format) -> void; + auto HandleBytes() -> void; + + template + auto ConvertFixedToFloating(InputStream&, OutputStream&) -> void; + auto Resample(float, int, InputStream&, OutputStream&) -> void; + template + auto Quantise(InputStream&) -> std::size_t; + + enum class Command { + kReadBytes, + kSetSourceFormat, + kSetTargetFormat, + }; + + struct Args { + Command cmd; + StreamInfo::Pcm format; + }; + + QueueHandle_t commands_; + SemaphoreHandle_t is_idle_; + + SRC_STATE* resampler_; + + std::unique_ptr input_stream_; + std::unique_ptr floating_point_stream_; + std::unique_ptr resampled_stream_; + + cpp::span quantisation_buffer_; + cpp::span quantisation_buffer_as_shorts_; + cpp::span quantisation_buffer_as_ints_; + + StreamInfo::Pcm target_format_; + StreamBufferHandle_t source_; + StreamBufferHandle_t sink_; +}; + +template <> +auto SinkMixer::ConvertFixedToFloating(InputStream&, OutputStream&) + -> void; +template <> +auto SinkMixer::ConvertFixedToFloating(InputStream&, OutputStream&) + -> void; + +template <> +auto SinkMixer::Quantise(InputStream&) -> std::size_t; +template <> +auto SinkMixer::Quantise(InputStream&) -> std::size_t; + +} // namespace audio diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index d48c39a8..d31e035c 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -56,6 +56,18 @@ class StreamInfo { bool operator==(const Encoded&) const = default; }; + /* + * Two-channel, interleaved, 32-bit floating point pcm samples. + */ + struct FloatingPointPcm { + // Number of channels in this stream. + uint8_t channels; + // The sample rate. + uint32_t sample_rate; + + bool operator==(const FloatingPointPcm&) const = default; + }; + struct Pcm { // Number of channels in this stream. uint8_t channels; @@ -64,10 +76,14 @@ class StreamInfo { // The sample rate. uint32_t sample_rate; + auto real_bytes_per_sample() const -> uint8_t { + return bits_per_sample == 16 ? 2 : 4; + } + bool operator==(const Pcm&) const = default; }; - typedef std::variant Format; + typedef std::variant Format; auto format() const -> const Format& { return format_; } auto set_format(Format f) -> void { format_ = f; } @@ -98,6 +114,12 @@ class RawStream { auto info() -> StreamInfo& { return info_; } auto data() -> cpp::span; + template + auto data_as() -> cpp::span { + auto orig = data(); + return {reinterpret_cast(orig.data()), orig.size_bytes() / sizeof(T)}; + } + auto empty() const -> bool { return info_.bytes_in_stream() == 0; } private: StreamInfo info_; @@ -114,6 +136,12 @@ class InputStream { const StreamInfo& info() const; cpp::span data() const; + template + auto data_as() const -> cpp::span { + auto orig = data(); + return {reinterpret_cast(orig.data()), + orig.size_bytes() / sizeof(T)}; + } private: RawStream* raw_; @@ -131,6 +159,11 @@ class OutputStream { const StreamInfo& info() const; cpp::span data() const; + template + auto data_as() const -> cpp::span { + auto orig = data(); + return {reinterpret_cast(orig.data()), orig.size_bytes() / sizeof(T)}; + } private: RawStream* raw_; diff --git a/src/audio/sink_mixer.cpp b/src/audio/sink_mixer.cpp new file mode 100644 index 00000000..072dc9b7 --- /dev/null +++ b/src/audio/sink_mixer.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "sink_mixer.hpp" + +#include +#include + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "freertos/portmacro.h" +#include "freertos/projdefs.h" +#include "samplerate.h" + +#include "stream_info.hpp" +#include "tasks.hpp" + +static constexpr char kTag[] = "mixer"; + +static constexpr std::size_t kSourceBufferLength = 4 * 1024; +static constexpr std::size_t kInputBufferLength = 4 * 1024; +static constexpr std::size_t kReformatBufferLength = 4 * 1024; +static constexpr std::size_t kResampleBufferLength = kReformatBufferLength; +static constexpr std::size_t kQuantisedBufferLength = 2 * 1024; + +namespace audio { + +SinkMixer::SinkMixer(StreamBufferHandle_t dest) + : commands_(xQueueCreate(1, sizeof(Args))), + is_idle_(xSemaphoreCreateBinary()), + resampler_(nullptr), + source_(xStreamBufferCreate(kSourceBufferLength, 1)), + sink_(dest) { + input_stream_.reset(new RawStream(kInputBufferLength)); + floating_point_stream_.reset(new RawStream(kReformatBufferLength)); + resampled_stream_.reset(new RawStream(kResampleBufferLength)); + + quantisation_buffer_ = { + reinterpret_cast(heap_caps_malloc( + kQuantisedBufferLength, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)), + kQuantisedBufferLength}; + quantisation_buffer_as_ints_ = { + reinterpret_cast(quantisation_buffer_.data()), + quantisation_buffer_.size_bytes() / 4}; + quantisation_buffer_as_shorts_ = { + reinterpret_cast(quantisation_buffer_.data()), + quantisation_buffer_.size_bytes() / 2}; + + tasks::StartPersistent([&]() { Main(); }); +} + +SinkMixer::~SinkMixer() { + vQueueDelete(commands_); + vSemaphoreDelete(is_idle_); + vStreamBufferDelete(source_); + heap_caps_free(quantisation_buffer_.data()); + if (resampler_ != nullptr) { + src_delete(resampler_); + } +} + +auto SinkMixer::MixAndSend(InputStream& input, const StreamInfo::Pcm& target) + -> std::size_t { + if (input.info().format_as() != + input_stream_->info().format_as()) { + xSemaphoreTake(is_idle_, portMAX_DELAY); + Args args{ + .cmd = Command::kSetSourceFormat, + .format = input.info().format_as().value(), + }; + xQueueSend(commands_, &args, portMAX_DELAY); + xSemaphoreGive(is_idle_); + } + if (target_format_ != target) { + xSemaphoreTake(is_idle_, portMAX_DELAY); + Args args{ + .cmd = Command::kSetTargetFormat, + .format = target, + }; + xQueueSend(commands_, &args, portMAX_DELAY); + xSemaphoreGive(is_idle_); + } + + Args args{ + .cmd = Command::kReadBytes, + .format = {}, + }; + xQueueSend(commands_, &args, portMAX_DELAY); + + auto buf = input.data(); + std::size_t bytes_sent = + xStreamBufferSend(source_, buf.data(), buf.size_bytes(), portMAX_DELAY); + input.consume(bytes_sent); + return bytes_sent; +} + +auto SinkMixer::Main() -> void { + OutputStream input_receiver{input_stream_.get()}; + xSemaphoreGive(is_idle_); + + for (;;) { + Args args; + while (!xQueueReceive(commands_, &args, portMAX_DELAY)) { + } + switch (args.cmd) { + case Command::kSetSourceFormat: + ESP_LOGI(kTag, "setting source format"); + input_receiver.prepare(args.format, {}); + break; + case Command::kSetTargetFormat: + ESP_LOGI(kTag, "setting target format"); + target_format_ = args.format; + break; + case Command::kReadBytes: + xSemaphoreTake(is_idle_, 0); + while (!xStreamBufferIsEmpty(source_)) { + auto buf = input_receiver.data(); + std::size_t bytes_received = xStreamBufferReceive( + source_, buf.data(), buf.size_bytes(), portMAX_DELAY); + input_receiver.add(bytes_received); + HandleBytes(); + } + xSemaphoreGive(is_idle_); + break; + } + } +} + +auto SinkMixer::HandleBytes() -> void { + InputStream input{input_stream_.get()}; + auto pcm = input.info().format_as(); + if (!pcm) { + ESP_LOGE(kTag, "mixer got unsupported data"); + return; + } + + if (*pcm == target_format_) { + // The happiest possible case: the input format matches the output + // format already. Streams like this should probably have bypassed the + // mixer. + // TODO(jacqueline): Make this an error; it's slow to use the mixer in this + // case, compared to just writing directly to the sink. + auto buf = input.data(); + std::size_t bytes_sent = + xStreamBufferSend(sink_, buf.data(), buf.size_bytes(), portMAX_DELAY); + input.consume(bytes_sent); + return; + } + + // Work out the resampling ratio using floating point arithmetic, since + // relying on the FPU for this will be much faster, and the difference in + // accuracy is unlikely to be noticeable. + float src_ratio = static_cast(target_format_.sample_rate) / + static_cast(pcm->sample_rate); + + // Loop until we don't have any complete frames left in the input stream, + // where a 'frame' is one complete sample per channel. + while (!input_stream_->empty()) { + // The first step of both resampling and requantising is to convert the + // fixed point pcm input data into 32 bit floating point samples. + OutputStream floating_writer{floating_point_stream_.get()}; + if (pcm->bits_per_sample == 16) { + ConvertFixedToFloating(input, floating_writer); + } else { + // FIXME: We should consider treating 24 bit and 32 bit samples + // differently. + ConvertFixedToFloating(input, floating_writer); + } + + InputStream floating_reader{floating_point_stream_.get()}; + + while (!floating_point_stream_->empty()) { + RawStream* quantisation_source; + if (pcm->sample_rate != target_format_.sample_rate) { + // The input data needs to be resampled before being sent to the sink. + OutputStream resample_writer{resampled_stream_.get()}; + Resample(src_ratio, pcm->channels, floating_reader, resample_writer); + quantisation_source = resampled_stream_.get(); + } else { + // The input data already has an acceptable sample rate. All we need to + // do is quantise it. + quantisation_source = floating_point_stream_.get(); + } + + InputStream quantise_reader{quantisation_source}; + while (!quantisation_source->empty()) { + std::size_t samples_available; + if (target_format_.bits_per_sample == 16) { + samples_available = Quantise(quantise_reader); + } else { + samples_available = Quantise(quantise_reader); + } + + assert(samples_available * target_format_.real_bytes_per_sample() <= + quantisation_buffer_.size_bytes()); + + std::size_t bytes_sent = xStreamBufferSend( + sink_, quantisation_buffer_.data(), + samples_available * target_format_.real_bytes_per_sample(), + portMAX_DELAY); + assert(bytes_sent == + samples_available * target_format_.real_bytes_per_sample()); + } + } + } +} + +template <> +auto SinkMixer::ConvertFixedToFloating(InputStream& in_str, + OutputStream& out_str) -> void { + auto in = in_str.data_as(); + auto out = out_str.data_as(); + std::size_t samples_converted = std::min(in.size(), out.size()); + + src_short_to_float_array(in.data(), out.data(), samples_converted); + + in_str.consume(samples_converted * sizeof(short)); + out_str.add(samples_converted * sizeof(float)); +} + +template <> +auto SinkMixer::ConvertFixedToFloating(InputStream& in_str, + OutputStream& out_str) -> void { + auto in = in_str.data_as(); + auto out = out_str.data_as(); + std::size_t samples_converted = std::min(in.size(), out.size()); + + src_int_to_float_array(in.data(), out.data(), samples_converted); + + in_str.consume(samples_converted * sizeof(int)); + out_str.add(samples_converted * sizeof(float)); +} + +auto SinkMixer::Resample(float src_ratio, + int channels, + InputStream& in, + OutputStream& out) -> void { + if (resampler_ == nullptr || src_get_channels(resampler_) != channels) { + if (resampler_ != nullptr) { + src_delete(resampler_); + } + + ESP_LOGI(kTag, "creating new resampler with %u channels", channels); + + int err = 0; + resampler_ = src_new(SRC_LINEAR, channels, &err); + assert(resampler_ != NULL); + assert(err == 0); + } + + auto in_buf = in.data_as(); + auto out_buf = out.data_as(); + + src_set_ratio(resampler_, src_ratio); + SRC_DATA args{ + .data_in = in_buf.data(), + .data_out = out_buf.data(), + .input_frames = static_cast(in_buf.size()), + .output_frames = static_cast(out_buf.size()), + .input_frames_used = 0, + .output_frames_gen = 0, + .end_of_input = 0, + .src_ratio = src_ratio, + }; + int err = src_process(resampler_, &args); + if (err != 0) { + ESP_LOGE(kTag, "resampler error: %s", src_strerror(err)); + } + + in.consume(args.input_frames_used * sizeof(float)); + out.add(args.output_frames_gen * sizeof(float)); +} + +template <> +auto SinkMixer::Quantise(InputStream& in) -> std::size_t { + auto src = in.data_as(); + cpp::span dest = quantisation_buffer_as_shorts_; + dest = dest.first(std::min(src.size(), dest.size())); + + src_float_to_short_array(src.data(), dest.data(), dest.size()); + + in.consume(dest.size() * sizeof(float)); + return dest.size(); +} + +template <> +auto SinkMixer::Quantise(InputStream& in) -> std::size_t { + auto src = in.data_as(); + cpp::span dest = quantisation_buffer_as_ints_; + dest = dest.first(std::min(src.size(), dest.size())); + + src_float_to_int_array(src.data(), dest.data(), dest.size()); + + in.consume(dest.size() * sizeof(float)); + return dest.size(); +} + +} // namespace audio diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp index 9748522f..a02fa620 100644 --- a/src/drivers/bluetooth.cpp +++ b/src/drivers/bluetooth.cpp @@ -5,7 +5,7 @@ namespace drivers { auto Bluetooth::Enable() -> Bluetooth* { - return nullptr; + return nullptr; } } // namespace drivers diff --git a/src/drivers/i2s_dac.cpp b/src/drivers/i2s_dac.cpp index c835fb1f..885321d1 100644 --- a/src/drivers/i2s_dac.cpp +++ b/src/drivers/i2s_dac.cpp @@ -161,7 +161,6 @@ auto I2SDac::Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate) word_length = 0b10; break; case BPS_32: - // TODO(jacqueline): Error on this? It's not supported anymore. slot_config_.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT; slot_config_.ws_width = 32; word_length = 0b11; diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/bluetooth.hpp index f3a4b2ac..22b58c8b 100644 --- a/src/drivers/include/bluetooth.hpp +++ b/src/drivers/include/bluetooth.hpp @@ -6,14 +6,15 @@ namespace drivers { class Bluetooth { - public: - static auto Enable() -> Bluetooth*; - Bluetooth(); - ~Bluetooth(); + public: + static auto Enable() -> Bluetooth*; + Bluetooth(); + ~Bluetooth(); - struct Device {}; - auto Scan() -> std::vector; - private: - }; + struct Device {}; + auto Scan() -> std::vector; -} + private: +}; + +} // namespace drivers diff --git a/src/drivers/include/i2s_dac.hpp b/src/drivers/include/i2s_dac.hpp index 06c0dc16..889ba68c 100644 --- a/src/drivers/include/i2s_dac.hpp +++ b/src/drivers/include/i2s_dac.hpp @@ -51,14 +51,12 @@ class I2SDac { BPS_32 = I2S_DATA_BIT_WIDTH_32BIT, }; enum SampleRate { - SAMPLE_RATE_11_025 = 11025, - SAMPLE_RATE_16 = 16000, - SAMPLE_RATE_22_05 = 22050, + SAMPLE_RATE_8 = 8000, SAMPLE_RATE_32 = 32000, SAMPLE_RATE_44_1 = 44100, SAMPLE_RATE_48 = 48000, + SAMPLE_RATE_88_2 = 88200, SAMPLE_RATE_96 = 96000, - SAMPLE_RATE_192 = 192000, }; auto Reconfigure(Channels ch, BitsPerSample bps, SampleRate rate) -> void; diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp index dc188780..d30a712c 100644 --- a/src/system_fsm/include/system_fsm.hpp +++ b/src/system_fsm/include/system_fsm.hpp @@ -13,10 +13,10 @@ #include "database.hpp" #include "display.hpp" #include "gpios.hpp" +#include "nvs.hpp" #include "relative_wheel.hpp" #include "samd.hpp" #include "storage.hpp" -#include "nvs.hpp" #include "tag_parser.hpp" #include "tinyfsm.hpp" #include "touchwheel.hpp" diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp index 861c7bf0..34c690f3 100644 --- a/src/tasks/tasks.cpp +++ b/src/tasks/tasks.cpp @@ -34,6 +34,10 @@ auto Name() -> std::string { return "AUDIO"; } template <> +auto Name() -> std::string { + return "MIXER"; +} +template <> auto Name() -> std::string { return "DB"; } @@ -77,6 +81,14 @@ auto AllocateStack() -> cpp::span { size}; } +template <> +auto AllocateStack() -> cpp::span { + std::size_t size = 4 * 1024; + return {static_cast( + heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)), + size}; +} + // Leveldb is designed for non-embedded use cases, where stack space isn't so // much of a concern. It therefore uses an eye-wateringly large amount of stack. template <> @@ -105,6 +117,10 @@ auto Priority() -> UBaseType_t; // Realtime audio is the entire point of this device, so give this task the // highest priority. template <> +auto Priority() -> UBaseType_t { + return 12; +} +template <> auto Priority() -> UBaseType_t { return 11; } diff --git a/src/tasks/tasks.hpp b/src/tasks/tasks.hpp index 742bb3cc..1321aab8 100644 --- a/src/tasks/tasks.hpp +++ b/src/tasks/tasks.hpp @@ -36,6 +36,8 @@ enum class Type { kFileStreamer, // The main audio pipeline task. kAudio, + // TODO + kMixer, // Task for running database queries. kDatabase, // Task for internal database operations