3030#include "modaudio.h"
3131#include "modmicrobit.h"
3232
33- #define audio_source_iter MP_STATE_PORT(audio_source)
33+ // Convenience macros to access the root-pointer state.
34+ #define audio_source_frame MP_STATE_PORT(audio_source_frame_state)
35+ #define audio_source_iter MP_STATE_PORT(audio_source_iter_state)
3436
3537#define LOG_AUDIO_CHUNK_SIZE (5)
3638#define AUDIO_CHUNK_SIZE (1 << LOG_AUDIO_CHUNK_SIZE)
@@ -47,15 +49,18 @@ typedef enum {
4749static uint8_t audio_output_buffer [OUT_CHUNK_SIZE ];
4850static volatile audio_output_state_t audio_output_state ;
4951static volatile bool audio_fetcher_scheduled ;
52+ static size_t audio_raw_offset ;
5053
5154microbit_audio_frame_obj_t * microbit_audio_frame_make_new (size_t size );
5255
5356static inline bool audio_is_running (void ) {
54- return audio_source_iter != NULL ;
57+ return audio_source_frame != NULL || audio_source_iter != MP_OBJ_NULL ;
5558}
5659
5760void microbit_audio_stop (void ) {
61+ audio_source_frame = NULL ;
5862 audio_source_iter = NULL ;
63+ audio_raw_offset = 0 ;
5964}
6065
6166STATIC void audio_buffer_ready (void ) {
@@ -64,50 +69,77 @@ STATIC void audio_buffer_ready(void) {
6469 audio_output_state = AUDIO_OUTPUT_STATE_DATA_READY ;
6570 MICROPY_END_ATOMIC_SECTION (atomic_state );
6671 if (old_state == AUDIO_OUTPUT_STATE_IDLE ) {
67- microbit_hal_audio_ready_callback ();
72+ microbit_hal_audio_raw_ready_callback ();
6873 }
6974}
7075
7176STATIC void audio_data_fetcher (void ) {
7277 audio_fetcher_scheduled = false;
73- if (audio_source_iter == NULL ) {
74- return ;
75- }
76- mp_obj_t buffer_obj ;
77- nlr_buf_t nlr ;
78- if (nlr_push (& nlr ) == 0 ) {
79- buffer_obj = mp_iternext_allow_raise (audio_source_iter );
80- nlr_pop ();
81- } else {
82- if (!mp_obj_is_subclass_fast (MP_OBJ_FROM_PTR (((mp_obj_base_t * )nlr .ret_val )-> type ),
83- MP_OBJ_FROM_PTR (& mp_type_StopIteration ))) {
84- mp_sched_exception (MP_OBJ_FROM_PTR (nlr .ret_val ));
78+
79+ if (audio_source_frame != NULL ) {
80+ // An existing AudioFrame is being played, see if there's any data left.
81+ if (audio_raw_offset >= audio_source_frame -> size ) {
82+ // AudioFrame is exhausted.
83+ audio_source_frame = NULL ;
8584 }
86- buffer_obj = MP_OBJ_STOP_ITERATION ;
8785 }
88- if (buffer_obj == MP_OBJ_STOP_ITERATION ) {
89- // End of audio iterator
90- microbit_audio_stop ();
91- } else if (mp_obj_get_type (buffer_obj ) != & microbit_audio_frame_type ) {
92- // Audio iterator did not return an AudioFrame
93- microbit_audio_stop ();
94- mp_sched_exception (mp_obj_new_exception_msg (& mp_type_TypeError , MP_ERROR_TEXT ("not an AudioFrame" )));
95- } else {
96- microbit_audio_frame_obj_t * buffer = (microbit_audio_frame_obj_t * )buffer_obj ;
97- uint8_t * dest = & audio_output_buffer [0 ];
98- uint32_t last = dest [BUFFER_EXPANSION * AUDIO_CHUNK_SIZE - 1 ];
99- for (int i = 0 ; i < AUDIO_CHUNK_SIZE ; ++ i ) {
100- uint32_t cur = buffer -> data [i ];
101- for (int j = 0 ; j < BUFFER_EXPANSION ; ++ j ) {
102- // Get next sample with linear interpolation.
103- uint32_t sample = ((BUFFER_EXPANSION - 1 - j ) * last + (j + 1 ) * cur ) / BUFFER_EXPANSION ;
104- // Write sample to the buffer.
105- * dest ++ = sample ;
86+
87+ if (audio_source_frame == NULL ) {
88+ // There is no AudioFrame, so try to get one from the audio iterator.
89+
90+ if (audio_source_iter == MP_OBJ_NULL ) {
91+ // Audio iterator is already exhausted.
92+ microbit_audio_stop ();
93+ return ;
94+ }
95+
96+ // Get the next item from the audio iterator.
97+ nlr_buf_t nlr ;
98+ mp_obj_t frame_obj ;
99+ if (nlr_push (& nlr ) == 0 ) {
100+ frame_obj = mp_iternext_allow_raise (audio_source_iter );
101+ nlr_pop ();
102+ } else {
103+ if (!mp_obj_is_subclass_fast (MP_OBJ_FROM_PTR (((mp_obj_base_t * )nlr .ret_val )-> type ),
104+ MP_OBJ_FROM_PTR (& mp_type_StopIteration ))) {
105+ mp_sched_exception (MP_OBJ_FROM_PTR (nlr .ret_val ));
106106 }
107- last = cur ;
107+ frame_obj = MP_OBJ_STOP_ITERATION ;
108+ }
109+ if (frame_obj == MP_OBJ_STOP_ITERATION ) {
110+ // End of audio iterator.
111+ microbit_audio_stop ();
112+ return ;
113+ }
114+ if (!mp_obj_is_type (frame_obj , & microbit_audio_frame_type )) {
115+ // Audio iterator did not return an AudioFrame.
116+ microbit_audio_stop ();
117+ mp_sched_exception (mp_obj_new_exception_msg (& mp_type_TypeError , MP_ERROR_TEXT ("not an AudioFrame" )));
118+ return ;
108119 }
109- audio_buffer_ready ();
120+
121+ // We have the next AudioFrame.
122+ audio_source_frame = MP_OBJ_TO_PTR (frame_obj );
123+ audio_raw_offset = 0 ;
110124 }
125+
126+ const uint8_t * src = & audio_source_frame -> data [audio_raw_offset ];
127+ audio_raw_offset += AUDIO_CHUNK_SIZE ;
128+
129+ uint8_t * dest = & audio_output_buffer [0 ];
130+ uint32_t last = dest [OUT_CHUNK_SIZE - 1 ];
131+ for (int i = 0 ; i < AUDIO_CHUNK_SIZE ; ++ i ) {
132+ uint32_t cur = src [i ];
133+ for (int j = 0 ; j < BUFFER_EXPANSION ; ++ j ) {
134+ // Get next sample with linear interpolation.
135+ uint32_t sample = ((BUFFER_EXPANSION - 1 - j ) * last + (j + 1 ) * cur ) / BUFFER_EXPANSION ;
136+ // Write sample to the buffer.
137+ * dest ++ = sample ;
138+ }
139+ last = cur ;
140+ }
141+
142+ audio_buffer_ready ();
111143}
112144
113145STATIC mp_obj_t audio_data_fetcher_wrapper (mp_obj_t arg ) {
@@ -116,10 +148,10 @@ STATIC mp_obj_t audio_data_fetcher_wrapper(mp_obj_t arg) {
116148}
117149STATIC MP_DEFINE_CONST_FUN_OBJ_1 (audio_data_fetcher_wrapper_obj , audio_data_fetcher_wrapper );
118150
119- void microbit_hal_audio_ready_callback (void ) {
151+ void microbit_hal_audio_raw_ready_callback (void ) {
120152 if (audio_output_state == AUDIO_OUTPUT_STATE_DATA_READY ) {
121153 // there is data ready to send out to the audio pipeline, so send it
122- microbit_hal_audio_write_data (& audio_output_buffer [0 ], OUT_CHUNK_SIZE );
154+ microbit_hal_audio_raw_write_data (& audio_output_buffer [0 ], OUT_CHUNK_SIZE );
123155 audio_output_state = AUDIO_OUTPUT_STATE_DATA_WRITTEN ;
124156 } else {
125157 // no data ready, need to call this function later when data is ready
@@ -134,7 +166,7 @@ void microbit_hal_audio_ready_callback(void) {
134166static void audio_init (uint32_t sample_rate ) {
135167 audio_fetcher_scheduled = false;
136168 audio_output_state = AUDIO_OUTPUT_STATE_IDLE ;
137- microbit_hal_audio_init (BUFFER_EXPANSION * sample_rate );
169+ microbit_hal_audio_raw_init (BUFFER_EXPANSION * sample_rate );
138170}
139171
140172void microbit_audio_play_source (mp_obj_t src , mp_obj_t pin_select , bool wait , uint32_t sample_rate ) {
@@ -150,6 +182,9 @@ void microbit_audio_play_source(mp_obj_t src, mp_obj_t pin_select, bool wait, ui
150182 sound_expr_data = sound -> name ;
151183 } else if (mp_obj_is_type (src , & microbit_soundeffect_type )) {
152184 sound_expr_data = microbit_soundeffect_get_sound_expr_data (src );
185+ } else if (mp_obj_is_type (src , & microbit_audio_frame_type )) {
186+ audio_source_frame = MP_OBJ_TO_PTR (src );
187+ audio_raw_offset = 0 ;
153188 } else if (mp_obj_is_type (src , & mp_type_tuple ) || mp_obj_is_type (src , & mp_type_list )) {
154189 // A tuple/list passed in. Need to check if it contains SoundEffect instances.
155190 size_t len ;
@@ -167,7 +202,13 @@ void microbit_audio_play_source(mp_obj_t src, mp_obj_t pin_select, bool wait, ui
167202 }
168203 // Replace last "," with a string null terminator.
169204 data [-1 ] = '\0' ;
205+ } else {
206+ // A tuple/list of AudioFrame instances.
207+ audio_source_iter = mp_getiter (src , NULL );
170208 }
209+ } else {
210+ // An iterator of AudioFrame instances.
211+ audio_source_iter = mp_getiter (src , NULL );
171212 }
172213
173214 if (sound_expr_data != NULL ) {
@@ -190,9 +231,8 @@ void microbit_audio_play_source(mp_obj_t src, mp_obj_t pin_select, bool wait, ui
190231 return ;
191232 }
192233
193- // Get the iterator and start the audio running.
234+ // Start the audio running.
194235 // The scheduler must be locked because audio_data_fetcher() can also be called from the scheduler.
195- audio_source_iter = mp_getiter (src , NULL );
196236 mp_sched_lock ();
197237 audio_data_fetcher ();
198238 mp_sched_unlock ();
@@ -440,4 +480,5 @@ microbit_audio_frame_obj_t *microbit_audio_frame_make_new(size_t size) {
440480 return res ;
441481}
442482
443- MP_REGISTER_ROOT_POINTER (void * audio_source );
483+ MP_REGISTER_ROOT_POINTER (struct _microbit_audio_frame_obj_t * audio_source_frame_state );
484+ MP_REGISTER_ROOT_POINTER (mp_obj_t audio_source_iter_state );
0 commit comments