Skip to content

Commit 7c6f010

Browse files
committed
Add record method.
1 parent 05c719f commit 7c6f010

File tree

8 files changed

+88
-12
lines changed

8 files changed

+88
-12
lines changed

ports/raspberrypi/audio_dma.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -677,13 +677,7 @@ uint8_t *audio_dma_get_buffer(audio_dma_t *dma) {
677677
if (!dma->input_register_address || dma->input_index >= 2) {
678678
return NULL;
679679
}
680-
uint8_t *buffer = dma->input_buffer[dma->input_index];
681-
dma->input_index = -1;
682-
return buffer;
683-
}
684-
685-
bool audio_dma_has_buffer(audio_dma_t *dma) {
686-
return dma->input_register_address && dma->input_index < 2;
680+
return dma->input_buffer[dma->input_index];
687681
}
688682

689683
// WARN(tannewt): DO NOT print from here, or anything it calls. Printing calls

ports/raspberrypi/audio_dma.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ void audio_dma_stop_input(audio_dma_t *dma);
100100
bool audio_dma_get_playing(audio_dma_t *dma);
101101
bool audio_dma_get_recording(audio_dma_t *dma);
102102
uint8_t *audio_dma_get_buffer(audio_dma_t *dma);
103-
bool audio_dma_has_buffer(audio_dma_t *dma);
104103
void audio_dma_pause(audio_dma_t *dma);
105104
void audio_dma_resume(audio_dma_t *dma);
106105
bool audio_dma_get_paused(audio_dma_t *dma);

ports/raspberrypi/common-hal/audiobusio/I2S.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,43 @@ void common_hal_audiobusio_i2s_deinit(audiobusio_i2s_obj_t *self) {
164164
audio_dma_deinit(&self->dma);
165165
}
166166

167+
// output_buffer may be a byte buffer or a halfword buffer.
168+
// output_buffer_length is the number of slots, not the number of bytes.
169+
void common_hal_audiobusio_i2s_record_to_buffer(audiobusio_i2s_obj_t *self,
170+
int16_t *output_buffer, uint32_t output_buffer_length) {
171+
if (!self->state_machine.in) {
172+
mp_raise_RuntimeError(MP_ERROR_TEXT("No data in"));
173+
}
174+
175+
// Make sure that dma is running.
176+
i2s_configure_audio_dma(self, self, true, self->sample_rate, self->bits_per_sample);
177+
178+
size_t output_count = 0;
179+
int16_t *buffer;
180+
size_t buffer_length;
181+
182+
while (output_count < output_buffer_length) {
183+
// Do other things while we wait for the buffer to fill.
184+
while (self->last_record_index == self->dma.input_index) {
185+
if (self->state_machine.out) {
186+
common_hal_mcu_delay_us(1000000 / self->sample_rate);
187+
} else {
188+
RUN_BACKGROUND_TASKS;
189+
}
190+
}
191+
self->last_record_index = self->dma.input_index;
192+
193+
buffer = (int16_t *)audio_dma_get_buffer(&self->dma);
194+
buffer_length = MIN((output_buffer_length - output_count), self->buffer_size / sizeof(int16_t));
195+
196+
for (size_t i = 0; i < buffer_length; i++) {
197+
output_buffer[i + output_count] = buffer[i];
198+
}
199+
200+
output_count += buffer_length;
201+
}
202+
}
203+
167204
void common_hal_audiobusio_i2s_play(audiobusio_i2s_obj_t *self,
168205
mp_obj_t sample, bool loop) {
169206
if (!self->state_machine.out) {
@@ -273,6 +310,8 @@ void audiobusio_i2s_reset_buffer(audiobusio_i2s_obj_t *self,
273310
}
274311

275312
i2s_configure_audio_dma(self, self, true, self->sample_rate, self->bits_per_sample);
313+
self->last_record_index = -1;
314+
self->last_sample_index = -1;
276315
}
277316

278317
audioio_get_buffer_result_t audiobusio_i2s_get_buffer(audiobusio_i2s_obj_t *self,
@@ -289,13 +328,14 @@ audioio_get_buffer_result_t audiobusio_i2s_get_buffer(audiobusio_i2s_obj_t *self
289328
}
290329

291330
// Do other things while we wait for the buffer to fill.
292-
while (!audio_dma_has_buffer(&self->dma)) {
331+
while (self->last_sample_index == self->dma.input_index) {
293332
if (self->state_machine.out) {
294333
common_hal_mcu_delay_us(1000000 / self->sample_rate);
295334
} else {
296335
RUN_BACKGROUND_TASKS;
297336
}
298337
}
338+
self->last_sample_index = self->dma.input_index;
299339

300340
*buffer_length = self->buffer_size;
301341
*buffer = audio_dma_get_buffer(&self->dma);

ports/raspberrypi/common-hal/audiobusio/I2S.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ typedef struct {
2626
uint32_t sample_rate;
2727
uint8_t bits_per_sample;
2828
bool samples_signed;
29+
uint8_t last_sample_index;
30+
uint8_t last_record_index;
2931
} audiobusio_i2s_obj_t;
3032

3133
// These are not available from Python because it may be called in an interrupt.

ports/raspberrypi/common-hal/audiobusio/I2SIn.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ void audiobusio_i2sin_reset_buffer(audiobusio_i2sin_obj_t *self,
293293
common_hal_rp2pio_statemachine_stop(&self->state_machine);
294294
mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to allocate buffers for signed conversion"));
295295
}
296+
297+
self->last_index = -1;
296298
}
297299

298300
audioio_get_buffer_result_t audiobusio_i2sin_get_buffer(audiobusio_i2sin_obj_t *self,
@@ -301,10 +303,8 @@ audioio_get_buffer_result_t audiobusio_i2sin_get_buffer(audiobusio_i2sin_obj_t *
301303
uint8_t **buffer,
302304
uint32_t *buffer_length) {
303305

304-
// TODO: single_channel_output
305-
306306
// Do other things while we wait for the buffer to fill.
307-
while (!audio_dma_has_buffer(&self->dma)) {
307+
while (self->last_index == self->dma.input_index) {
308308
RUN_BACKGROUND_TASKS;
309309
}
310310

ports/raspberrypi/common-hal/audiobusio/I2SIn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ typedef struct {
2626
uint32_t sample_rate;
2727
uint8_t bits_per_sample;
2828
bool samples_signed;
29+
uint8_t last_index;
2930
} audiobusio_i2sin_obj_t;
3031

3132

shared-bindings/audiobusio/I2S.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,42 @@ static mp_obj_t audiobusio_i2s_obj___exit__(size_t n_args, const mp_obj_t *args)
179179
}
180180
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_i2s___exit___obj, 4, 4, audiobusio_i2s_obj___exit__);
181181

182+
//| def record(self, destination: WriteableBuffer, destination_length: int) -> None:
183+
//| """Records destination_length bytes of samples to destination. This is
184+
//| blocking.
185+
//|
186+
//| An IOError may be raised when the destination is too slow to record the
187+
//| audio at the given rate. For internal flash, writing all 1s to the file
188+
//| before recording is recommended to speed up writes.
189+
//|
190+
//| :return: The number of samples recorded. If this is less than ``destination_length``,
191+
//| some samples were missed due to processing time."""
192+
//| ...
193+
static mp_obj_t audiobusio_i2s_obj_record(mp_obj_t self_obj, mp_obj_t destination, mp_obj_t destination_length) {
194+
audiobusio_i2s_obj_t *self = MP_OBJ_TO_PTR(self_obj);
195+
check_for_deinit(self);
196+
uint32_t length = mp_arg_validate_type_int(destination_length, MP_QSTR_length);
197+
mp_arg_validate_length_min(length, 0, MP_QSTR_length);
198+
199+
mp_buffer_info_t bufinfo;
200+
if (mp_obj_is_type(destination, &mp_type_fileio)) {
201+
mp_raise_NotImplementedError(MP_ERROR_TEXT("Cannot record to a file"));
202+
} else if (mp_get_buffer(destination, &bufinfo, MP_BUFFER_WRITE)) {
203+
if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) {
204+
mp_raise_ValueError(MP_ERROR_TEXT("Destination capacity is smaller than destination_length."));
205+
}
206+
uint8_t bit_depth = common_hal_audiobusio_i2s_get_bits_per_sample(self);
207+
if (bufinfo.typecode != 'h' && bit_depth == 16) {
208+
mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be an array of type 'h' for bit_depth = 16"));
209+
} else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) {
210+
mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8"));
211+
}
212+
// length is the buffer length in slots, not bytes.
213+
common_hal_audiobusio_i2s_record_to_buffer(self, bufinfo.buf, length);
214+
}
215+
return mp_const_none;
216+
}
217+
MP_DEFINE_CONST_FUN_OBJ_3(audiobusio_i2s_record_obj, audiobusio_i2s_obj_record);
182218

183219
//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None:
184220
//| """Plays the sample once when loop=False and continuously when loop=True.
@@ -280,6 +316,7 @@ static const mp_rom_map_elem_t audiobusio_i2s_locals_dict_table[] = {
280316
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiobusio_i2s_deinit_obj) },
281317
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
282318
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiobusio_i2s___exit___obj) },
319+
{ MP_ROM_QSTR(MP_QSTR_record), MP_ROM_PTR(&audiobusio_i2s_record_obj) },
283320
{ MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiobusio_i2s_play_obj) },
284321
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiobusio_i2s_stop_obj) },
285322
{ MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiobusio_i2s_pause_obj) },

shared-bindings/audiobusio/I2S.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ void common_hal_audiobusio_i2s_construct(audiobusio_i2s_obj_t *self,
2323
void common_hal_audiobusio_i2s_deinit(audiobusio_i2s_obj_t *self);
2424
bool common_hal_audiobusio_i2s_deinited(audiobusio_i2s_obj_t *self);
2525

26+
void common_hal_audiobusio_i2s_record_to_buffer(audiobusio_i2s_obj_t *self,
27+
int16_t *buffer, uint32_t length);
28+
2629
void common_hal_audiobusio_i2s_play(audiobusio_i2s_obj_t *self, mp_obj_t sample, bool loop);
2730
void common_hal_audiobusio_i2s_stop(audiobusio_i2s_obj_t *self);
2831
bool common_hal_audiobusio_i2s_get_playing(audiobusio_i2s_obj_t *self);

0 commit comments

Comments
 (0)