3535#define audio_source_frame MP_STATE_PORT(audio_source_frame_state)
3636#define audio_source_iter MP_STATE_PORT(audio_source_iter_state)
3737
38+ #ifndef AUDIO_OUTPUT_BUFFER_SIZE
3839#define AUDIO_OUTPUT_BUFFER_SIZE (32)
40+ #endif
41+
3942#define DEFAULT_AUDIO_FRAME_SIZE (32)
4043#define DEFAULT_SAMPLE_RATE (7812)
4144
@@ -46,8 +49,9 @@ typedef enum {
4649} audio_output_state_t ;
4750
4851static uint8_t audio_output_buffer [AUDIO_OUTPUT_BUFFER_SIZE ];
52+ static size_t audio_output_buffer_offset ;
4953static volatile audio_output_state_t audio_output_state ;
50- static size_t audio_raw_offset ;
54+ static size_t audio_source_frame_offset ;
5155static uint32_t audio_current_sound_level ;
5256static mp_sched_node_t audio_data_fetcher_sched_node ;
5357
@@ -56,27 +60,18 @@ static inline bool audio_is_running(void) {
5660}
5761
5862void microbit_audio_stop (void ) {
63+ audio_output_buffer_offset = 0 ;
5964 audio_source_frame = NULL ;
6065 audio_source_iter = NULL ;
61- audio_raw_offset = 0 ;
66+ audio_source_frame_offset = 0 ;
6267 audio_current_sound_level = 0 ;
6368 microbit_hal_audio_stop_expression ();
6469}
6570
66- static void audio_buffer_ready (void ) {
67- uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION ();
68- audio_output_state_t old_state = audio_output_state ;
69- audio_output_state = AUDIO_OUTPUT_STATE_DATA_READY ;
70- MICROPY_END_ATOMIC_SECTION (atomic_state );
71- if (old_state == AUDIO_OUTPUT_STATE_IDLE ) {
72- microbit_hal_audio_raw_ready_callback ();
73- }
74- }
75-
76- static void audio_data_fetcher (mp_sched_node_t * node ) {
71+ static void audio_data_pull_from_source (void ) {
7772 if (audio_source_frame != NULL ) {
7873 // An existing AudioFrame is being played, see if there's any data left.
79- if (audio_raw_offset >= audio_source_frame -> used_size ) {
74+ if (audio_source_frame_offset >= audio_source_frame -> used_size ) {
8075 // AudioFrame is exhausted.
8176 audio_source_frame = NULL ;
8277 }
@@ -118,30 +113,54 @@ static void audio_data_fetcher(mp_sched_node_t *node) {
118113
119114 // We have the next AudioFrame.
120115 audio_source_frame = MP_OBJ_TO_PTR (frame_obj );
121- audio_raw_offset = 0 ;
116+ audio_source_frame_offset = 0 ;
122117 microbit_hal_audio_raw_set_rate (audio_source_frame -> rate );
123118 }
119+ }
124120
125- const uint8_t * src = & audio_source_frame -> data [audio_raw_offset ];
126- size_t src_len = MIN (audio_source_frame -> used_size - audio_raw_offset , AUDIO_OUTPUT_BUFFER_SIZE );
127- audio_raw_offset += src_len ;
128-
129- uint8_t * dest = & audio_output_buffer [0 ];
130- uint32_t sound_level = 0 ;
121+ static void audio_data_fetcher (mp_sched_node_t * node ) {
122+ audio_data_pull_from_source ();
123+ uint8_t * dest = & audio_output_buffer [audio_output_buffer_offset ];
131124
132- for (int i = 0 ; i < src_len ; ++ i ) {
133- // Copy sample to the buffer.
134- * dest ++ = src [i ];
135- // Compute the sound level.
136- sound_level += (src [i ] - 128 ) * (src [i ] - 128 );
125+ if (audio_source_frame == NULL ) {
126+ // Audio source is exhausted.
127+ // Fill any remaining audio_output_buffer bytes with silence.
128+ memset (dest , 128 , AUDIO_OUTPUT_BUFFER_SIZE - audio_output_buffer_offset );
129+ audio_output_buffer_offset = AUDIO_OUTPUT_BUFFER_SIZE ;
130+ } else {
131+ // Copy samples to the buffer.
132+ const uint8_t * src = & audio_source_frame -> data [audio_source_frame_offset ];
133+ size_t src_len = MIN (audio_source_frame -> used_size - audio_source_frame_offset , AUDIO_OUTPUT_BUFFER_SIZE - audio_output_buffer_offset );
134+ memcpy (dest , src , src_len );
135+
136+ // Update output and source offsets.
137+ audio_output_buffer_offset += src_len ;
138+ audio_source_frame_offset += src_len ;
137139 }
138140
139- // Fill any remaining audio_output_buffer bytes with silence.
140- memset (dest , 128 , AUDIO_OUTPUT_BUFFER_SIZE - src_len );
141-
142- audio_current_sound_level = sound_level / AUDIO_OUTPUT_BUFFER_SIZE ;
141+ if (audio_output_buffer_offset < AUDIO_OUTPUT_BUFFER_SIZE ) {
142+ // Output buffer not full yet, so attempt to pull more data from the source.
143+ mp_sched_schedule_node (& audio_data_fetcher_sched_node , audio_data_fetcher );
144+ } else {
145+ // Output buffer is full, process it and prepare for next buffer fill.
146+ audio_output_buffer_offset = 0 ;
143147
144- audio_buffer_ready ();
148+ // Compute the sound level.
149+ uint32_t sound_level = 0 ;
150+ for (int i = 0 ; i < AUDIO_OUTPUT_BUFFER_SIZE ; ++ i ) {
151+ sound_level += (audio_output_buffer [i ] - 128 ) * (audio_output_buffer [i ] - 128 );
152+ }
153+ audio_current_sound_level = sound_level / AUDIO_OUTPUT_BUFFER_SIZE ;
154+
155+ // Send the data to the lower levels of the audio pipeline.
156+ uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION ();
157+ audio_output_state_t old_state = audio_output_state ;
158+ audio_output_state = AUDIO_OUTPUT_STATE_DATA_READY ;
159+ MICROPY_END_ATOMIC_SECTION (atomic_state );
160+ if (old_state == AUDIO_OUTPUT_STATE_IDLE ) {
161+ microbit_hal_audio_raw_ready_callback ();
162+ }
163+ }
145164}
146165
147166void microbit_hal_audio_raw_ready_callback (void ) {
@@ -178,7 +197,7 @@ void microbit_audio_play_source(mp_obj_t src, mp_obj_t pin_select, bool wait, ui
178197 sound_expr_data = microbit_soundeffect_get_sound_expr_data (src );
179198 } else if (mp_obj_is_type (src , & microbit_audio_frame_type )) {
180199 audio_source_frame = MP_OBJ_TO_PTR (src );
181- audio_raw_offset = 0 ;
200+ audio_source_frame_offset = 0 ;
182201 microbit_hal_audio_raw_set_rate (audio_source_frame -> rate );
183202 } else if (mp_obj_is_type (src , & mp_type_tuple ) || mp_obj_is_type (src , & mp_type_list )) {
184203 // A tuple/list passed in. Need to check if it contains SoundEffect instances.
0 commit comments