xref: /btstack/example/le_audio_demo_util_source.c (revision 036e10079a77215137b087eeca70f0a6f3513b84)
124b69d49SMatthias Ringwald /*
224b69d49SMatthias Ringwald  * Copyright (C) {copyright_year} BlueKitchen GmbH
324b69d49SMatthias Ringwald  *
424b69d49SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
524b69d49SMatthias Ringwald  * modification, are permitted provided that the following conditions
624b69d49SMatthias Ringwald  * are met:
724b69d49SMatthias Ringwald  *
824b69d49SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
924b69d49SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1024b69d49SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1124b69d49SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1224b69d49SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1324b69d49SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1424b69d49SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1524b69d49SMatthias Ringwald  *    from this software without specific prior written permission.
1624b69d49SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1724b69d49SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1824b69d49SMatthias Ringwald  *    monetary gain.
1924b69d49SMatthias Ringwald  *
2024b69d49SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2124b69d49SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2224b69d49SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2324b69d49SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
2424b69d49SMatthias Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2524b69d49SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2624b69d49SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2724b69d49SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2824b69d49SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2924b69d49SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3024b69d49SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3124b69d49SMatthias Ringwald  * SUCH DAMAGE.
3224b69d49SMatthias Ringwald  *
3324b69d49SMatthias Ringwald  * Please inquire about commercial licensing options at
3424b69d49SMatthias Ringwald  * [email protected]
3524b69d49SMatthias Ringwald  *
3624b69d49SMatthias Ringwald  */
3724b69d49SMatthias Ringwald 
3824b69d49SMatthias Ringwald #define BTSTACK_FILE__ "le_audio_demo_util_source.c"
3924b69d49SMatthias Ringwald 
4024b69d49SMatthias Ringwald #include "le_audio_demo_util_source.h"
4124b69d49SMatthias Ringwald 
429d8b6c89SDirk Helbig #include <stdio.h>
4365e76442SMatthias Ringwald #include <inttypes.h>
4465e76442SMatthias Ringwald #include <math.h>
4565e76442SMatthias Ringwald 
4665e76442SMatthias Ringwald #include "btstack_config.h"
4765e76442SMatthias Ringwald #include "btstack_bool.h"
4865e76442SMatthias Ringwald #include "btstack_debug.h"
4965e76442SMatthias Ringwald #include "btstack_ring_buffer.h"
5024b69d49SMatthias Ringwald 
5124b69d49SMatthias Ringwald #include "hci.h"
5224b69d49SMatthias Ringwald #include "btstack_audio.h"
5324b69d49SMatthias Ringwald #include "btstack_lc3_google.h"
5424b69d49SMatthias Ringwald #include "btstack_lc3plus_fraunhofer.h"
5524b69d49SMatthias Ringwald 
5624b69d49SMatthias Ringwald #include "hxcmod.h"
5724b69d49SMatthias Ringwald #include "mods/mod.h"
5824b69d49SMatthias Ringwald 
5965e76442SMatthias Ringwald #define MAX_CHANNELS 5
6065e76442SMatthias Ringwald #define MAX_SAMPLES_PER_10MS_FRAME 480
6124b69d49SMatthias Ringwald #define MAX_LC3_FRAME_BYTES   155
6224b69d49SMatthias Ringwald 
6365e76442SMatthias Ringwald // live recording
6465e76442SMatthias Ringwald #define RECORDING_PREBUFFER__MS          20
6565e76442SMatthias Ringwald #define RECORDING_BUFFER_MS              40
6624b69d49SMatthias Ringwald 
6724b69d49SMatthias Ringwald // SOURCE
6865e76442SMatthias Ringwald static float sine_frequencies[MAX_CHANNELS] = {
6965e76442SMatthias Ringwald     261.63, // C-4
7065e76442SMatthias Ringwald     311.13, // Es-4
7165e76442SMatthias Ringwald     369.99, // G-4
7265e76442SMatthias Ringwald     493.88, // B-4
7365e76442SMatthias Ringwald     587.33, // D-5
7424b69d49SMatthias Ringwald };
7524b69d49SMatthias Ringwald 
7665e76442SMatthias Ringwald //  48000 / 261.6 = 183.46
7765e76442SMatthias Ringwald #define SINE_MAX_SAMPLES_AT_48KHZ 185
7865e76442SMatthias Ringwald #define PI 3.14159265358979323846
7965e76442SMatthias Ringwald 
8065e76442SMatthias Ringwald static int16_t sine_table[MAX_CHANNELS * SINE_MAX_SAMPLES_AT_48KHZ];
8165e76442SMatthias Ringwald 
8224b69d49SMatthias Ringwald static uint16_t le_audio_demo_source_octets_per_frame;
8324b69d49SMatthias Ringwald static uint16_t le_audio_demo_source_packet_sequence_numbers[MAX_CHANNELS];
8424b69d49SMatthias Ringwald static uint8_t  le_audio_demo_source_iso_frame_counter;
8524b69d49SMatthias Ringwald static uint32_t le_audio_demo_source_sampling_frequency_hz;
8624b69d49SMatthias Ringwald static btstack_lc3_frame_duration_t le_audio_demo_source_frame_duration;
8724b69d49SMatthias Ringwald static uint8_t  le_audio_demo_source_num_channels;
8824b69d49SMatthias Ringwald static uint8_t  le_audio_demo_source_num_streams;
8924b69d49SMatthias Ringwald static uint8_t  le_audio_demo_source_num_channels_per_stream;
9024b69d49SMatthias Ringwald static uint16_t le_audio_demo_source_num_samples_per_frame;
9124b69d49SMatthias Ringwald 
9224b69d49SMatthias Ringwald 
9324b69d49SMatthias Ringwald static uint8_t le_audio_demo_source_iso_payload[MAX_CHANNELS * MAX_LC3_FRAME_BYTES];
9424b69d49SMatthias Ringwald 
9524b69d49SMatthias Ringwald // lc3 encoder
9624b69d49SMatthias Ringwald static const btstack_lc3_encoder_t * le_audio_demo_source_lc3_encoder;
9724b69d49SMatthias Ringwald static btstack_lc3_encoder_google_t  le_audio_demo_source_encoder_contexts[MAX_CHANNELS];
9865e76442SMatthias Ringwald static int16_t                       le_audio_demo_source_pcm[MAX_CHANNELS * MAX_SAMPLES_PER_10MS_FRAME];
9924b69d49SMatthias Ringwald 
10024b69d49SMatthias Ringwald // sine generator
10165e76442SMatthias Ringwald static uint16_t le_audio_demo_source_sine_samples[MAX_CHANNELS];
10224b69d49SMatthias Ringwald static uint16_t le_audio_demo_source_sine_phases[MAX_CHANNELS];
10324b69d49SMatthias Ringwald 
10424b69d49SMatthias Ringwald // mod player
10524b69d49SMatthias Ringwald static bool                 le_audio_demo_source_hxcmod_initialized;
10624b69d49SMatthias Ringwald static modcontext           le_audio_demo_source_hxcmod_context;
10724b69d49SMatthias Ringwald static tracker_buffer_state le_audio_demo_source_hxcmod_trkbuf;
10824b69d49SMatthias Ringwald 
10965e76442SMatthias Ringwald // recording / portaudio
11065e76442SMatthias Ringwald #define SAMPLES_PER_CHANNEL_RECORDING (MAX_SAMPLES_PER_10MS_FRAME * RECORDING_BUFFER_MS / 10)
11165e76442SMatthias Ringwald static uint8_t recording_storage[MAX_CHANNELS * 2 * SAMPLES_PER_CHANNEL_RECORDING];
11265e76442SMatthias Ringwald static btstack_ring_buffer_t le_audio_demo_source_recording_buffer;
11365e76442SMatthias Ringwald static uint16_t le_audio_demo_source_recording_stored_samples;
11465e76442SMatthias Ringwald static uint16_t le_audio_demo_source_recording_prebuffer_samples;
11565e76442SMatthias Ringwald static bool le_audio_demo_source_recording_streaming;
11665e76442SMatthias Ringwald static btstack_audio_source_t const * le_audio_demo_audio_source;
11765e76442SMatthias Ringwald 
11865e76442SMatthias Ringwald // generation method
11965e76442SMatthias Ringwald static le_audio_demo_source_generator le_audio_demo_util_source_generator = AUDIO_SOURCE_SINE;
12065e76442SMatthias Ringwald 
12165e76442SMatthias Ringwald // recording callback has channels interleaved, collect per channel
le_audio_util_source_recording_callback(const int16_t * buffer,uint16_t num_samples)12265e76442SMatthias Ringwald static void le_audio_util_source_recording_callback(const int16_t * buffer, uint16_t num_samples){
12365e76442SMatthias Ringwald     log_info("store %u samples per channel", num_samples);
12465e76442SMatthias Ringwald     uint32_t bytes_to_store = le_audio_demo_source_num_channels * num_samples * 2;
12565e76442SMatthias Ringwald     if (bytes_to_store < btstack_ring_buffer_bytes_free(&le_audio_demo_source_recording_buffer)){
12665e76442SMatthias Ringwald         btstack_ring_buffer_write(&le_audio_demo_source_recording_buffer, (uint8_t *)buffer, bytes_to_store);
12765e76442SMatthias Ringwald         le_audio_demo_source_recording_stored_samples += num_samples;
12865e76442SMatthias Ringwald     }
12965e76442SMatthias Ringwald }
13065e76442SMatthias Ringwald 
le_audio_demo_util_source_init(void)13124b69d49SMatthias Ringwald void le_audio_demo_util_source_init(void) {
13265e76442SMatthias Ringwald     le_audio_demo_audio_source = btstack_audio_source_get_instance();
13365e76442SMatthias Ringwald }
13465e76442SMatthias Ringwald 
le_audio_demo_util_source_recording_start(void)13565e76442SMatthias Ringwald static bool le_audio_demo_util_source_recording_start(void){
13665e76442SMatthias Ringwald     bool ok = false;
13765e76442SMatthias Ringwald     if (le_audio_demo_audio_source != NULL){
13865e76442SMatthias Ringwald         int init_ok = le_audio_demo_audio_source->init(le_audio_demo_source_num_channels, le_audio_demo_source_sampling_frequency_hz,
13965e76442SMatthias Ringwald                                          &le_audio_util_source_recording_callback);
14065e76442SMatthias Ringwald         log_info("recording initialized, ok %u", init_ok);
14165e76442SMatthias Ringwald         if (init_ok){
14265e76442SMatthias Ringwald             btstack_ring_buffer_init(&le_audio_demo_source_recording_buffer, recording_storage, sizeof(recording_storage));
14365e76442SMatthias Ringwald             le_audio_demo_source_recording_prebuffer_samples = le_audio_demo_source_num_channels * le_audio_demo_source_sampling_frequency_hz * RECORDING_PREBUFFER__MS / 1000;
14465e76442SMatthias Ringwald             le_audio_demo_source_recording_streaming = false;
14565e76442SMatthias Ringwald             le_audio_demo_source_recording_stored_samples = 0;
14665e76442SMatthias Ringwald             le_audio_demo_audio_source->start_stream();
14765e76442SMatthias Ringwald             log_info("recording start, %u prebuffer samples per channel", le_audio_demo_source_recording_prebuffer_samples / le_audio_demo_source_num_channels);
14865e76442SMatthias Ringwald             ok = true;
14965e76442SMatthias Ringwald         }
15065e76442SMatthias Ringwald     }
15165e76442SMatthias Ringwald     return ok;
15265e76442SMatthias Ringwald }
15365e76442SMatthias Ringwald 
le_audio_demo_util_source_recording_stop(void)15465e76442SMatthias Ringwald static void le_audio_demo_util_source_recording_stop(void) {
15565e76442SMatthias Ringwald     le_audio_demo_audio_source->stop_stream();
15624b69d49SMatthias Ringwald }
15724b69d49SMatthias Ringwald 
le_audio_demo_source_setup_lc3_encoder(void)15824b69d49SMatthias Ringwald static void le_audio_demo_source_setup_lc3_encoder(void){
15924b69d49SMatthias Ringwald     uint8_t channel;
16024b69d49SMatthias Ringwald     for (channel = 0 ; channel < le_audio_demo_source_num_channels ; channel++){
16124b69d49SMatthias Ringwald         btstack_lc3_encoder_google_t * context = &le_audio_demo_source_encoder_contexts[channel];
16224b69d49SMatthias Ringwald         le_audio_demo_source_lc3_encoder = btstack_lc3_encoder_google_init_instance(context);
16324b69d49SMatthias Ringwald         le_audio_demo_source_lc3_encoder->configure(context, le_audio_demo_source_sampling_frequency_hz, le_audio_demo_source_frame_duration, le_audio_demo_source_octets_per_frame);
16424b69d49SMatthias Ringwald     }
16524b69d49SMatthias Ringwald 
16665e76442SMatthias Ringwald     printf("LC3 Encoder config: %" PRIu32 " hz, frame duration %s ms, num samples %u, num octets %u\n",
16724b69d49SMatthias Ringwald            le_audio_demo_source_sampling_frequency_hz, le_audio_demo_source_frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US ? "7.5" : "10",
16824b69d49SMatthias Ringwald            le_audio_demo_source_num_samples_per_frame, le_audio_demo_source_octets_per_frame);
16924b69d49SMatthias Ringwald }
17024b69d49SMatthias Ringwald 
le_audio_demo_source_setup_mod_player(void)17124b69d49SMatthias Ringwald static void le_audio_demo_source_setup_mod_player(void){
17224b69d49SMatthias Ringwald     if (!le_audio_demo_source_hxcmod_initialized) {
17324b69d49SMatthias Ringwald         le_audio_demo_source_hxcmod_initialized = hxcmod_init(&le_audio_demo_source_hxcmod_context);
17424b69d49SMatthias Ringwald         btstack_assert(le_audio_demo_source_hxcmod_initialized != 0);
17524b69d49SMatthias Ringwald     }
17624b69d49SMatthias Ringwald     hxcmod_unload(&le_audio_demo_source_hxcmod_context);
177*036e1007SMatthias Ringwald     hxcmod_setcfg(&le_audio_demo_source_hxcmod_context, le_audio_demo_source_sampling_frequency_hz, 1, 1);
17824b69d49SMatthias Ringwald     hxcmod_load(&le_audio_demo_source_hxcmod_context, (void *) &mod_data, mod_len);
17924b69d49SMatthias Ringwald }
18024b69d49SMatthias Ringwald 
le_audio_demo_source_setup_sine_generator(void)18165e76442SMatthias Ringwald static void le_audio_demo_source_setup_sine_generator(void){
18265e76442SMatthias Ringwald     // pre-compute sine for all channels
18365e76442SMatthias Ringwald     uint8_t channel;
18465e76442SMatthias Ringwald     for (channel = 0; channel < MAX_CHANNELS ; channel++){
18565e76442SMatthias Ringwald         float frequency_hz =  (float) sine_frequencies[channel];
18665e76442SMatthias Ringwald         float samplerate_hz = (float) le_audio_demo_source_sampling_frequency_hz;
18765e76442SMatthias Ringwald         float period_samples =samplerate_hz / frequency_hz;
18865e76442SMatthias Ringwald         le_audio_demo_source_sine_samples[channel] = (uint16_t) period_samples;
18965e76442SMatthias Ringwald         le_audio_demo_source_sine_phases[channel] = 0;
19065e76442SMatthias Ringwald         uint16_t i;
19165e76442SMatthias Ringwald         for (i=0;i<le_audio_demo_source_sine_samples[channel] ;i++){
19265e76442SMatthias Ringwald             float sine_value = sin(2 * PI * i / period_samples);
19365e76442SMatthias Ringwald             sine_table[channel*SINE_MAX_SAMPLES_AT_48KHZ + i] = (int16_t)(sine_value * 32767);
19465e76442SMatthias Ringwald         }
19565e76442SMatthias Ringwald     }
19665e76442SMatthias Ringwald }
19765e76442SMatthias Ringwald 
le_audio_demo_util_source_configure(uint8_t num_streams,uint8_t num_channels_per_stream,uint32_t sampling_frequency_hz,btstack_lc3_frame_duration_t frame_duration,uint16_t octets_per_frame)19824b69d49SMatthias Ringwald void le_audio_demo_util_source_configure(uint8_t num_streams, uint8_t num_channels_per_stream, uint32_t sampling_frequency_hz,
19924b69d49SMatthias Ringwald                                          btstack_lc3_frame_duration_t frame_duration, uint16_t octets_per_frame) {
20024b69d49SMatthias Ringwald     le_audio_demo_source_sampling_frequency_hz = sampling_frequency_hz;
20124b69d49SMatthias Ringwald     le_audio_demo_source_frame_duration = frame_duration;
20224b69d49SMatthias Ringwald     le_audio_demo_source_octets_per_frame = octets_per_frame;
20324b69d49SMatthias Ringwald     le_audio_demo_source_num_streams = num_streams;
20424b69d49SMatthias Ringwald     le_audio_demo_source_num_channels_per_stream = num_channels_per_stream;
20524b69d49SMatthias Ringwald 
20624b69d49SMatthias Ringwald     le_audio_demo_source_num_channels = num_streams * num_channels_per_stream;
20765e76442SMatthias Ringwald     btstack_assert((le_audio_demo_source_num_channels > 0) || (le_audio_demo_source_num_channels <= MAX_CHANNELS));
20824b69d49SMatthias Ringwald 
20924b69d49SMatthias Ringwald     le_audio_demo_source_num_samples_per_frame = btstack_lc3_samples_per_frame(sampling_frequency_hz, frame_duration);
21065e76442SMatthias Ringwald     btstack_assert(le_audio_demo_source_num_samples_per_frame <= MAX_SAMPLES_PER_10MS_FRAME);
21124b69d49SMatthias Ringwald 
21224b69d49SMatthias Ringwald     // setup encoder
21324b69d49SMatthias Ringwald     le_audio_demo_source_setup_lc3_encoder();
21424b69d49SMatthias Ringwald 
21524b69d49SMatthias Ringwald     // setup sine generator
21665e76442SMatthias Ringwald     le_audio_demo_source_setup_sine_generator();
21724b69d49SMatthias Ringwald 
21824b69d49SMatthias Ringwald     // setup mod player
21924b69d49SMatthias Ringwald     le_audio_demo_source_setup_mod_player();
22065e76442SMatthias Ringwald }
22124b69d49SMatthias Ringwald 
le_audio_demo_util_source_generate_iso_frame(le_audio_demo_source_generator generator)22224b69d49SMatthias Ringwald void le_audio_demo_util_source_generate_iso_frame(le_audio_demo_source_generator generator) {
22324b69d49SMatthias Ringwald     btstack_assert(le_audio_demo_source_octets_per_frame != 0);
22424b69d49SMatthias Ringwald     uint16_t sample;
22524b69d49SMatthias Ringwald     bool encode_pcm = true;
22665e76442SMatthias Ringwald 
22765e76442SMatthias Ringwald     // lazy init of btstack_audio, fallback to sine
22865e76442SMatthias Ringwald     if ((generator == AUDIO_SOURCE_RECORDING) && (le_audio_demo_util_source_generator != AUDIO_SOURCE_RECORDING)){
22965e76442SMatthias Ringwald         bool ok = le_audio_demo_util_source_recording_start();
23065e76442SMatthias Ringwald         if (ok) {
23165e76442SMatthias Ringwald             le_audio_demo_util_source_generator = generator;
23265e76442SMatthias Ringwald         } else {
23365e76442SMatthias Ringwald             generator = AUDIO_SOURCE_SINE;
23465e76442SMatthias Ringwald         }
23565e76442SMatthias Ringwald     }
23665e76442SMatthias Ringwald 
23765e76442SMatthias Ringwald     // stop recording
23865e76442SMatthias Ringwald     if ((generator != AUDIO_SOURCE_RECORDING) && (le_audio_demo_util_source_generator == AUDIO_SOURCE_RECORDING)) {
23965e76442SMatthias Ringwald         le_audio_demo_util_source_recording_stop();
24065e76442SMatthias Ringwald     }
24165e76442SMatthias Ringwald 
24224b69d49SMatthias Ringwald     switch (generator){
24324b69d49SMatthias Ringwald         case AUDIO_SOURCE_COUNTER:
24424b69d49SMatthias Ringwald             encode_pcm = false;
24524b69d49SMatthias Ringwald             memset(le_audio_demo_source_iso_payload, le_audio_demo_source_iso_frame_counter++, sizeof(le_audio_demo_source_iso_payload));
24624b69d49SMatthias Ringwald             break;
24724b69d49SMatthias Ringwald         case AUDIO_SOURCE_SINE:
24865e76442SMatthias Ringwald             // use pre-computed sine for all channels
24924b69d49SMatthias Ringwald             for (sample = 0 ; sample < le_audio_demo_source_num_samples_per_frame ; sample++){
25024b69d49SMatthias Ringwald                 uint8_t i;
25124b69d49SMatthias Ringwald                 for (i = 0; i < le_audio_demo_source_num_channels; i++) {
25265e76442SMatthias Ringwald                     int16_t value = sine_table[i*SINE_MAX_SAMPLES_AT_48KHZ + le_audio_demo_source_sine_phases[i]];
25324b69d49SMatthias Ringwald                     le_audio_demo_source_pcm[sample * le_audio_demo_source_num_channels + i] = value;
25465e76442SMatthias Ringwald                     le_audio_demo_source_sine_phases[i] += 1;
25565e76442SMatthias Ringwald                     if (le_audio_demo_source_sine_phases[i] >= le_audio_demo_source_sine_samples[i]) {
25624b69d49SMatthias Ringwald                         le_audio_demo_source_sine_phases[i] = 0;
25724b69d49SMatthias Ringwald                     }
25824b69d49SMatthias Ringwald                 }
25924b69d49SMatthias Ringwald             }
26024b69d49SMatthias Ringwald             break;
26124b69d49SMatthias Ringwald         case AUDIO_SOURCE_MODPLAYER:
26224b69d49SMatthias Ringwald             // mod player configured for stereo
263*036e1007SMatthias Ringwald             hxcmod_fillbuffer(&le_audio_demo_source_hxcmod_context, le_audio_demo_source_pcm, le_audio_demo_source_num_samples_per_frame, &le_audio_demo_source_hxcmod_trkbuf);
26424b69d49SMatthias Ringwald             // stereo -> mono
26565e76442SMatthias Ringwald             if (le_audio_demo_source_num_channels == 1) {
266b5232cd8SMatthias Ringwald                 uint16_t i;
26724b69d49SMatthias Ringwald                 for (i=0;i<le_audio_demo_source_num_samples_per_frame;i++){
26824b69d49SMatthias Ringwald                     le_audio_demo_source_pcm[i] = (le_audio_demo_source_pcm[2*i] / 2) + (le_audio_demo_source_pcm[2*i+1] / 2);
26924b69d49SMatthias Ringwald                 }
27024b69d49SMatthias Ringwald             }
27165e76442SMatthias Ringwald             // duplicate stereo channels
27265e76442SMatthias Ringwald             if (le_audio_demo_source_num_channels > 2) {
27365e76442SMatthias Ringwald                 int16_t i;
27465e76442SMatthias Ringwald                 for (i = le_audio_demo_source_num_samples_per_frame - 1; i >= 0; i--) {
27565e76442SMatthias Ringwald                     uint16_t channel_dst;
27665e76442SMatthias Ringwald                     for (channel_dst=0; channel_dst < le_audio_demo_source_num_channels; channel_dst++){
27765e76442SMatthias Ringwald                         uint16_t channel_src = channel_dst & 1;
27865e76442SMatthias Ringwald                         le_audio_demo_source_pcm[i * le_audio_demo_source_num_channels + channel_dst] =
27965e76442SMatthias Ringwald                                 le_audio_demo_source_pcm[i * 2 + channel_src];
28065e76442SMatthias Ringwald                     }
28165e76442SMatthias Ringwald                 }
28265e76442SMatthias Ringwald             }
28365e76442SMatthias Ringwald             break;
28465e76442SMatthias Ringwald         case AUDIO_SOURCE_RECORDING:
28565e76442SMatthias Ringwald             if (le_audio_demo_source_recording_streaming == false){
28665e76442SMatthias Ringwald                 if (le_audio_demo_source_recording_stored_samples >= le_audio_demo_source_recording_prebuffer_samples){
28765e76442SMatthias Ringwald                     // start streaming audio
28865e76442SMatthias Ringwald                     le_audio_demo_source_recording_streaming = true;
28965e76442SMatthias Ringwald                     log_info("Streaming started");
29065e76442SMatthias Ringwald 
29165e76442SMatthias Ringwald                 }
29265e76442SMatthias Ringwald             }
29365e76442SMatthias Ringwald             if (le_audio_demo_source_recording_streaming){
29465e76442SMatthias Ringwald                 uint32_t bytes_needed = le_audio_demo_source_num_channels * le_audio_demo_source_num_samples_per_frame * 2;
29565e76442SMatthias Ringwald                 if (btstack_ring_buffer_bytes_available(&le_audio_demo_source_recording_buffer) >= bytes_needed){
29665e76442SMatthias Ringwald                     uint32_t bytes_read;
29765e76442SMatthias Ringwald                     btstack_ring_buffer_read(&le_audio_demo_source_recording_buffer, (uint8_t*) le_audio_demo_source_pcm, bytes_needed, &bytes_read);
29865e76442SMatthias Ringwald                     btstack_assert(bytes_needed == bytes_needed);
29965e76442SMatthias Ringwald                     log_info("Read %u samples per channel", le_audio_demo_source_num_samples_per_frame);
30065e76442SMatthias Ringwald                     le_audio_demo_source_recording_stored_samples -= le_audio_demo_source_num_samples_per_frame;
30165e76442SMatthias Ringwald                 } else {
30265e76442SMatthias Ringwald                     le_audio_demo_source_recording_streaming = false;
30365e76442SMatthias Ringwald                     log_info("Streaming underrun");
30465e76442SMatthias Ringwald                 }
30565e76442SMatthias Ringwald             } else {
30665e76442SMatthias Ringwald                 memset(le_audio_demo_source_pcm, 0, sizeof(le_audio_demo_source_pcm));
30765e76442SMatthias Ringwald             }
30824b69d49SMatthias Ringwald             break;
30924b69d49SMatthias Ringwald         default:
31024b69d49SMatthias Ringwald             btstack_unreachable();
31124b69d49SMatthias Ringwald             break;
31224b69d49SMatthias Ringwald     }
31324b69d49SMatthias Ringwald 
31424b69d49SMatthias Ringwald     if (encode_pcm){
31524b69d49SMatthias Ringwald         uint8_t i;
31624b69d49SMatthias Ringwald         for (i=0;i<le_audio_demo_source_num_channels;i++){
31724b69d49SMatthias Ringwald             le_audio_demo_source_lc3_encoder->encode_signed_16(&le_audio_demo_source_encoder_contexts[i], &le_audio_demo_source_pcm[i], le_audio_demo_source_num_channels, &le_audio_demo_source_iso_payload[i * MAX_LC3_FRAME_BYTES]);
31824b69d49SMatthias Ringwald         }
31924b69d49SMatthias Ringwald     }
32065e76442SMatthias Ringwald 
32165e76442SMatthias Ringwald     le_audio_demo_util_source_generator = generator;
32265e76442SMatthias Ringwald }
32324b69d49SMatthias Ringwald 
le_audio_demo_util_source_send(uint8_t stream_index,hci_con_handle_t con_handle)32424b69d49SMatthias Ringwald void le_audio_demo_util_source_send(uint8_t stream_index, hci_con_handle_t con_handle){
32524b69d49SMatthias Ringwald     btstack_assert(le_audio_demo_source_octets_per_frame != 0);
32624b69d49SMatthias Ringwald 
32783dfe5ceSMatthias Ringwald     hci_reserve_packet_buffer();
32824b69d49SMatthias Ringwald 
32924b69d49SMatthias Ringwald     uint8_t * buffer = hci_get_outgoing_packet_buffer();
33024b69d49SMatthias Ringwald     // complete SDU, no TimeStamp
33124b69d49SMatthias Ringwald     little_endian_store_16(buffer, 0, ((uint16_t) con_handle) | (2 << 12));
33224b69d49SMatthias Ringwald     // len
33324b69d49SMatthias Ringwald     little_endian_store_16(buffer, 2, 0 + 4 + le_audio_demo_source_num_channels_per_stream * le_audio_demo_source_octets_per_frame);
33424b69d49SMatthias Ringwald     // TimeStamp if TS flag is set
33524b69d49SMatthias Ringwald     // packet seq nr
33624b69d49SMatthias Ringwald     little_endian_store_16(buffer, 4, le_audio_demo_source_packet_sequence_numbers[stream_index]);
33724b69d49SMatthias Ringwald     // iso sdu len
33824b69d49SMatthias Ringwald     little_endian_store_16(buffer, 6, le_audio_demo_source_num_channels_per_stream * le_audio_demo_source_octets_per_frame);
33924b69d49SMatthias Ringwald     uint16_t offset = 8;
34024b69d49SMatthias Ringwald     // copy encoded payload
34124b69d49SMatthias Ringwald     uint8_t i;
34224b69d49SMatthias Ringwald     for (i=0; i<le_audio_demo_source_num_channels_per_stream;i++) {
3430ab066e8SMatthias Ringwald         uint8_t effective_channel = (stream_index * le_audio_demo_source_num_channels_per_stream) + i;
3440ab066e8SMatthias Ringwald         memcpy(&buffer[offset], &le_audio_demo_source_iso_payload[effective_channel * MAX_LC3_FRAME_BYTES], le_audio_demo_source_octets_per_frame);
34524b69d49SMatthias Ringwald         offset += le_audio_demo_source_octets_per_frame;
34624b69d49SMatthias Ringwald     }
34724b69d49SMatthias Ringwald     // send
34824b69d49SMatthias Ringwald     hci_send_iso_packet_buffer(offset);
34924b69d49SMatthias Ringwald 
35024b69d49SMatthias Ringwald     le_audio_demo_source_packet_sequence_numbers[stream_index]++;
35124b69d49SMatthias Ringwald }
35224b69d49SMatthias Ringwald 
le_audio_demo_util_source_close(void)35324b69d49SMatthias Ringwald void le_audio_demo_util_source_close(void){
35465e76442SMatthias Ringwald     if (le_audio_demo_audio_source != NULL){
35565e76442SMatthias Ringwald         le_audio_demo_audio_source->close();
35665e76442SMatthias Ringwald     }
35724b69d49SMatthias Ringwald }
358