Files
pico-examples/pico_w/bt/btstack_audio_pico.c
graham sanderson 1c5d9aa567 Add Bluetooth examples
Co-authored-by: Peter Harper <77111776+peterharperuk@users.noreply.github.com>
2023-02-10 18:26:53 -06:00

209 lines
6.8 KiB
C

/*
* Copyright (C) 2022 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "btstack_audio_pico.c"
/*
* btstack_audio_pico.c
*
* Implementation of btstack_audio.h using pico_i2s
*
*/
#include "btstack_config.h"
#include "btstack_debug.h"
#include "btstack_audio.h"
#include "btstack_run_loop.h"
#include <stddef.h>
#include <hardware/dma.h>
#include "pico/audio_i2s.h"
#define DRIVER_POLL_INTERVAL_MS 5
#define SAMPLES_PER_BUFFER 512
// client
static void (*playback_callback)(int16_t * buffer, uint16_t num_samples);
// timer to fill output ring buffer
static btstack_timer_source_t driver_timer_sink;
static bool btstack_audio_pico_sink_active;
// from pico-playground/audio/sine_wave/sine_wave.c
static audio_format_t btstack_audio_pico_audio_format;
static audio_buffer_format_t btstack_audio_pico_producer_format;
static audio_buffer_pool_t * btstack_audio_pico_audio_buffer_pool;
static uint8_t btstack_audio_pico_channel_count;
static audio_buffer_pool_t *init_audio(uint32_t sample_frequency, uint8_t channel_count) {
// num channels requested by application
btstack_audio_pico_channel_count = channel_count;
// always use stereo
btstack_audio_pico_audio_format.format = AUDIO_BUFFER_FORMAT_PCM_S16;
btstack_audio_pico_audio_format.sample_freq = sample_frequency;
btstack_audio_pico_audio_format.channel_count = 2;
btstack_audio_pico_producer_format.format = &btstack_audio_pico_audio_format;
btstack_audio_pico_producer_format.sample_stride = 2 * 2;
audio_buffer_pool_t * producer_pool = audio_new_producer_pool(&btstack_audio_pico_producer_format, 3, SAMPLES_PER_BUFFER); // todo correct size
audio_i2s_config_t config;
config.data_pin = PICO_AUDIO_I2S_DATA_PIN;
config.clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE;
config.dma_channel = (int8_t) dma_claim_unused_channel(true);
config.pio_sm = 0;
// audio_i2s_setup claims the channel again https://github.com/raspberrypi/pico-extras/issues/48
dma_channel_unclaim(config.dma_channel);
const audio_format_t * output_format = audio_i2s_setup(&btstack_audio_pico_audio_format, &config);
if (!output_format) {
panic("PicoAudio: Unable to open audio device.\n");
}
bool ok = audio_i2s_connect(producer_pool);
assert(ok);
(void)ok;
return producer_pool;
}
static void btstack_audio_pico_sink_fill_buffers(void){
while (true){
audio_buffer_t * audio_buffer = take_audio_buffer(btstack_audio_pico_audio_buffer_pool, false);
if (audio_buffer == NULL){
break;
}
int16_t * buffer16 = (int16_t *) audio_buffer->buffer->bytes;
(*playback_callback)(buffer16, audio_buffer->max_sample_count);
// duplicate samples for mono
if (btstack_audio_pico_channel_count == 1){
int16_t i;
for (i = SAMPLES_PER_BUFFER - 1 ; i >= 0; i--){
buffer16[2*i ] = buffer16[i];
buffer16[2*i+1] = buffer16[i];
}
}
audio_buffer->sample_count = audio_buffer->max_sample_count;
give_audio_buffer(btstack_audio_pico_audio_buffer_pool, audio_buffer);
}
}
static void driver_timer_handler_sink(btstack_timer_source_t * ts){
// refill
btstack_audio_pico_sink_fill_buffers();
// re-set timer
btstack_run_loop_set_timer(ts, DRIVER_POLL_INTERVAL_MS);
btstack_run_loop_add_timer(ts);
}
static int btstack_audio_pico_sink_init(
uint8_t channels,
uint32_t samplerate,
void (*playback)(int16_t * buffer, uint16_t num_samples)
){
btstack_assert(playback != NULL);
btstack_assert(channels != 0);
playback_callback = playback;
btstack_audio_pico_audio_buffer_pool = init_audio(samplerate, channels);
return 0;
}
static void btstack_audio_pico_sink_set_volume(uint8_t volume){
UNUSED(volume);
}
static void btstack_audio_pico_sink_start_stream(void){
// pre-fill HAL buffers
btstack_audio_pico_sink_fill_buffers();
// start timer
btstack_run_loop_set_timer_handler(&driver_timer_sink, &driver_timer_handler_sink);
btstack_run_loop_set_timer(&driver_timer_sink, DRIVER_POLL_INTERVAL_MS);
btstack_run_loop_add_timer(&driver_timer_sink);
// state
btstack_audio_pico_sink_active = true;
audio_i2s_set_enabled(true);
}
static void btstack_audio_pico_sink_stop_stream(void){
audio_i2s_set_enabled(false);
// stop timer
btstack_run_loop_remove_timer(&driver_timer_sink);
// state
btstack_audio_pico_sink_active = false;
}
static void btstack_audio_pico_sink_close(void){
// stop stream if needed
if (btstack_audio_pico_sink_active){
btstack_audio_pico_sink_stop_stream();
}
}
static const btstack_audio_sink_t btstack_audio_pico_sink = {
.init = &btstack_audio_pico_sink_init,
.set_volume = &btstack_audio_pico_sink_set_volume,
.start_stream = &btstack_audio_pico_sink_start_stream,
.stop_stream = &btstack_audio_pico_sink_stop_stream,
.close = &btstack_audio_pico_sink_close,
};
const btstack_audio_sink_t * btstack_audio_pico_sink_get_instance(void){
return &btstack_audio_pico_sink;
}