Fix playing audio from SD card

This commit is contained in:
Dan Halbert 2018-06-14 18:47:40 -04:00
parent 618943d90a
commit e724bc1c4e
9 changed files with 142 additions and 45 deletions

View File

@ -131,8 +131,17 @@ DRESULT disk_read (
} else { } else {
vfs->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector); vfs->readblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
vfs->readblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), buff); vfs->readblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), buff);
mp_call_method_n_kw(2, 0, vfs->readblocks); nlr_buf_t nlr;
// TODO handle error return if (nlr_push(&nlr) == 0) {
mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->readblocks);
nlr_pop();
if (mp_obj_get_int(ret) != 0) {
return RES_ERROR;
}
} else {
// Exception thrown by readblocks or something it calls.
return RES_ERROR;
}
} }
return RES_OK; return RES_OK;
@ -167,8 +176,17 @@ DRESULT disk_write (
} else { } else {
vfs->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector); vfs->writeblocks[2] = MP_OBJ_NEW_SMALL_INT(sector);
vfs->writeblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), (void*)buff); vfs->writeblocks[3] = mp_obj_new_bytearray_by_ref(count * SECSIZE(&vfs->fatfs), (void*)buff);
mp_call_method_n_kw(2, 0, vfs->writeblocks); nlr_buf_t nlr;
// TODO handle error return if (nlr_push(&nlr) == 0) {
mp_obj_t ret = mp_call_method_n_kw(2, 0, vfs->writeblocks);
nlr_pop();
if (mp_obj_get_int(ret) != 0) {
return RES_ERROR;
}
} else {
// Exception thrown by writeblocks or something it calls.
return RES_ERROR;
}
} }
return RES_OK; return RES_OK;

8
main.c
View File

@ -233,7 +233,7 @@ bool start_mp(safe_mode_t safe_mode) {
} }
} }
void run_boot_py(safe_mode_t safe_mode) { void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
// If not in safe mode, run boot before initing USB and capture output in a // If not in safe mode, run boot before initing USB and capture output in a
// file. // file.
if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) { if (filesystem_present() && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) {
@ -258,10 +258,12 @@ void run_boot_py(safe_mode_t safe_mode) {
// This saves wear and tear on the flash and also prevents filesystem damage if power is lost // This saves wear and tear on the flash and also prevents filesystem damage if power is lost
// during the write, which may happen due to bobbling the power connector or weak power. // during the write, which may happen due to bobbling the power connector or weak power.
static const size_t NUM_CHARS_TO_COMPARE = 160;
if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) {
char file_contents[512];
char file_contents[NUM_CHARS_TO_COMPARE];
UINT chars_read = 0; UINT chars_read = 0;
f_read(boot_output_file, file_contents, 512, &chars_read); f_read(boot_output_file, file_contents, NUM_CHARS_TO_COMPARE, &chars_read);
f_close(boot_output_file); f_close(boot_output_file);
skip_boot_output = skip_boot_output =
// + 2 accounts for \r\n. // + 2 accounts for \r\n.

View File

@ -102,8 +102,10 @@ endif
ifeq ($(DEBUG), 1) ifeq ($(DEBUG), 1)
# Turn on Python modules useful for debugging (e.g. uheap, ustack). # Turn on Python modules useful for debugging (e.g. uheap, ustack).
CFLAGS += -ggdb CFLAGS += -ggdb
## CFLAGS += -flto # You may want to disable -flto if it interferes with debugging.
CFLAGS += -fno-inline -fno-ipa-sra CFLAGS += -flto
# You may want to enable these flags to make setting breakpoints easier.
## CFLAGS += -fno-inline -fno-ipa-sra
ifeq ($(CHIP_FAMILY), samd21) ifeq ($(CHIP_FAMILY), samd21)
CFLAGS += -DENABLE_MICRO_TRACE_BUFFER CFLAGS += -DENABLE_MICRO_TRACE_BUFFER
endif endif

View File

@ -34,6 +34,11 @@
#include "py/mpstate.h" #include "py/mpstate.h"
static audio_dma_t* audio_dma_state[AUDIO_DMA_CHANNEL_COUNT];
// This cannot be in audio_dma_state because it's volatile.
static volatile bool audio_dma_pending[AUDIO_DMA_CHANNEL_COUNT];
uint32_t audiosample_sample_rate(mp_obj_t sample_obj) { uint32_t audiosample_sample_rate(mp_obj_t sample_obj) {
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
@ -70,7 +75,7 @@ uint8_t audiosample_channel_count(mp_obj_t sample_obj) {
return 1; return 1;
} }
void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) { static void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t audio_channel) {
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
audioio_rawsample_reset_buffer(sample, single_channel, audio_channel); audioio_rawsample_reset_buffer(sample, single_channel, audio_channel);
@ -81,7 +86,10 @@ void audiosample_reset_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t
} }
} }
bool audiosample_get_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t channel, uint8_t** buffer, uint32_t* buffer_length) { static audioio_get_buffer_result_t audiosample_get_buffer(mp_obj_t sample_obj,
bool single_channel,
uint8_t channel,
uint8_t** buffer, uint32_t* buffer_length) {
if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) { if (MP_OBJ_IS_TYPE(sample_obj, &audioio_rawsample_type)) {
audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj); audioio_rawsample_obj_t* sample = MP_OBJ_TO_PTR(sample_obj);
return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length); return audioio_rawsample_get_buffer(sample, single_channel, channel, buffer, buffer_length);
@ -90,7 +98,7 @@ bool audiosample_get_buffer(mp_obj_t sample_obj, bool single_channel, uint8_t ch
audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj); audioio_wavefile_obj_t* file = MP_OBJ_TO_PTR(sample_obj);
return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length); return audioio_wavefile_get_buffer(file, single_channel, channel, buffer, buffer_length);
} }
return true; return GET_BUFFER_DONE;
} }
static void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel, static void audiosample_get_buffer_structure(mp_obj_t sample_obj, bool single_channel,
@ -117,7 +125,6 @@ uint8_t find_free_audio_dma_channel(void) {
return channel; return channel;
} }
audio_dma_t* audio_dma_state[AUDIO_DMA_CHANNEL_COUNT];
void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer_length, void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer_length,
uint8_t** output_buffer, uint32_t* output_buffer_length, uint8_t** output_buffer, uint32_t* output_buffer_length,
uint8_t* output_spacing) { uint8_t* output_spacing) {
@ -163,7 +170,8 @@ void audio_dma_convert_signed(audio_dma_t* dma, uint8_t* buffer, uint32_t buffer
void audio_dma_load_next_block(audio_dma_t* dma) { void audio_dma_load_next_block(audio_dma_t* dma) {
uint8_t* buffer; uint8_t* buffer;
uint32_t buffer_length; uint32_t buffer_length;
bool last_buffer = audiosample_get_buffer(dma->sample, dma->single_channel, dma->audio_channel, audioio_get_buffer_result_t get_buffer_result =
audiosample_get_buffer(dma->sample, dma->single_channel, dma->audio_channel,
&buffer, &buffer_length); &buffer, &buffer_length);
DmacDescriptor* descriptor = dma->second_descriptor; DmacDescriptor* descriptor = dma->second_descriptor;
@ -172,6 +180,11 @@ void audio_dma_load_next_block(audio_dma_t* dma) {
} }
dma->first_descriptor_free = !dma->first_descriptor_free; dma->first_descriptor_free = !dma->first_descriptor_free;
if (get_buffer_result == GET_BUFFER_ERROR) {
audio_dma_stop(dma);
return;
}
uint8_t* output_buffer; uint8_t* output_buffer;
uint32_t output_buffer_length; uint32_t output_buffer_length;
uint8_t output_spacing; uint8_t output_spacing;
@ -180,7 +193,7 @@ void audio_dma_load_next_block(audio_dma_t* dma) {
descriptor->BTCNT.reg = output_buffer_length / dma->beat_size / output_spacing; descriptor->BTCNT.reg = output_buffer_length / dma->beat_size / output_spacing;
descriptor->SRCADDR.reg = ((uint32_t) output_buffer) + output_buffer_length; descriptor->SRCADDR.reg = ((uint32_t) output_buffer) + output_buffer_length;
if (last_buffer) { if (get_buffer_result == GET_BUFFER_DONE) {
if (dma->loop) { if (dma->loop) {
audiosample_reset_buffer(dma->sample, dma->single_channel, dma->audio_channel); audiosample_reset_buffer(dma->sample, dma->single_channel, dma->audio_channel);
} else { } else {
@ -347,6 +360,7 @@ void audio_dma_init(audio_dma_t* dma) {
void audio_dma_reset(void) { void audio_dma_reset(void) {
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) { for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
audio_dma_state[i] = NULL; audio_dma_state[i] = NULL;
audio_dma_pending[i] = false;
dma_disable_channel(i); dma_disable_channel(i);
dma_descriptor(i)->BTCTRL.bit.VALID = false; dma_descriptor(i)->BTCTRL.bit.VALID = false;
MP_STATE_PORT(playing_audio)[i] = NULL; MP_STATE_PORT(playing_audio)[i] = NULL;
@ -367,8 +381,12 @@ bool audio_dma_get_playing(audio_dma_t* dma) {
// WARN(tannewt): DO NOT print from here. Printing calls background tasks such as this and causes a // WARN(tannewt): DO NOT print from here. Printing calls background tasks such as this and causes a
// stack overflow. // stack overflow.
void audio_dma_background(void) { void audio_dma_background(void) {
for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) { for (uint8_t i = 0; i < AUDIO_DMA_CHANNEL_COUNT; i++) {
if (audio_dma_pending[i]) {
continue;
}
audio_dma_t* dma = audio_dma_state[i]; audio_dma_t* dma = audio_dma_state[i];
if (dma == NULL) { if (dma == NULL) {
continue; continue;
@ -379,6 +397,10 @@ void audio_dma_background(void) {
continue; continue;
} }
// audio_dma_load_next_block() can call Python code, which can call audio_dma_background()
// recursively at the next background processing time. So disallow recursive calls to here.
audio_dma_pending[i] = true;
audio_dma_load_next_block(dma); audio_dma_load_next_block(dma);
audio_dma_pending[i] = false;
} }
} }

View File

@ -66,7 +66,7 @@ void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t* self,
uint8_t channel) { uint8_t channel) {
} }
bool audioio_rawsample_get_buffer(audioio_rawsample_obj_t* self, audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel, uint8_t channel,
uint8_t** buffer, uint8_t** buffer,
@ -77,7 +77,7 @@ bool audioio_rawsample_get_buffer(audioio_rawsample_obj_t* self,
} else { } else {
*buffer = self->buffer; *buffer = self->buffer;
} }
return true; return GET_BUFFER_DONE;
} }
void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t* self, bool single_channel, void audioio_rawsample_get_buffer_structure(audioio_rawsample_obj_t* self, bool single_channel,

View File

@ -29,6 +29,8 @@
#include "py/obj.h" #include "py/obj.h"
#include "shared-module/audioio/__init__.h"
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
uint8_t* buffer; uint8_t* buffer;
@ -45,7 +47,7 @@ typedef struct {
void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t* self, void audioio_rawsample_reset_buffer(audioio_rawsample_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel); uint8_t channel);
bool audioio_rawsample_get_buffer(audioio_rawsample_obj_t* self, audioio_get_buffer_result_t audioio_rawsample_get_buffer(audioio_rawsample_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel, uint8_t channel,
uint8_t** buffer, uint8_t** buffer,

View File

@ -29,6 +29,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "py/mperrno.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-module/audioio/WaveFile.h" #include "shared-module/audioio/WaveFile.h"
@ -50,20 +51,26 @@ void common_hal_audioio_wavefile_construct(audioio_wavefile_obj_t* self,
uint8_t chunk_header[16]; uint8_t chunk_header[16];
f_rewind(&self->file->fp); f_rewind(&self->file->fp);
UINT bytes_read; UINT bytes_read;
f_read(&self->file->fp, chunk_header, 16, &bytes_read); if (f_read(&self->file->fp, chunk_header, 16, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != 16 || if (bytes_read != 16 ||
memcmp(chunk_header, "RIFF", 4) != 0 || memcmp(chunk_header, "RIFF", 4) != 0 ||
memcmp(chunk_header + 8, "WAVEfmt ", 8) != 0) { memcmp(chunk_header + 8, "WAVEfmt ", 8) != 0) {
mp_raise_ValueError("Invalid wave file"); mp_raise_ValueError("Invalid wave file");
} }
uint32_t format_size; uint32_t format_size;
f_read(&self->file->fp, &format_size, 4, &bytes_read); if (f_read(&self->file->fp, &format_size, 4, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != 4 || if (bytes_read != 4 ||
format_size > sizeof(struct wave_format_chunk)) { format_size > sizeof(struct wave_format_chunk)) {
mp_raise_ValueError("Invalid format chunk size"); mp_raise_ValueError("Invalid format chunk size");
} }
struct wave_format_chunk format; struct wave_format_chunk format;
f_read(&self->file->fp, &format, format_size, &bytes_read); if (f_read(&self->file->fp, &format, format_size, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != format_size) { if (bytes_read != format_size) {
} }
@ -83,14 +90,18 @@ void common_hal_audioio_wavefile_construct(audioio_wavefile_obj_t* self,
// TODO(tannewt): Skip any extra chunks that occur before the data section. // TODO(tannewt): Skip any extra chunks that occur before the data section.
uint8_t data_tag[4]; uint8_t data_tag[4];
f_read(&self->file->fp, &data_tag, 4, &bytes_read); if (f_read(&self->file->fp, &data_tag, 4, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != 4 || if (bytes_read != 4 ||
memcmp((uint8_t *) data_tag, "data", 4) != 0) { memcmp((uint8_t *) data_tag, "data", 4) != 0) {
mp_raise_ValueError("Data chunk must follow fmt chunk"); mp_raise_ValueError("Data chunk must follow fmt chunk");
} }
uint32_t data_length; uint32_t data_length;
f_read(&self->file->fp, &data_length, 4, &bytes_read); if (f_read(&self->file->fp, &data_length, 4, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != 4) { if (bytes_read != 4) {
mp_raise_ValueError("Invalid file"); mp_raise_ValueError("Invalid file");
} }
@ -152,7 +163,7 @@ void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t* self,
self->right_read_count = 0; self->right_read_count = 0;
} }
bool audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self, audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel, uint8_t channel,
uint8_t** buffer, uint8_t** buffer,
@ -171,7 +182,7 @@ bool audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self,
if (self->bytes_remaining == 0 && need_more_data) { if (self->bytes_remaining == 0 && need_more_data) {
*buffer = NULL; *buffer = NULL;
*buffer_length = 0; *buffer_length = 0;
return true; return GET_BUFFER_DONE;
} }
if (need_more_data) { if (need_more_data) {
@ -185,7 +196,9 @@ bool audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self,
} else { } else {
*buffer = self->buffer; *buffer = self->buffer;
} }
f_read(&self->file->fp, *buffer, num_bytes_to_load, &length_read); if (f_read(&self->file->fp, *buffer, num_bytes_to_load, &length_read) != FR_OK) {
return GET_BUFFER_ERROR;
}
*buffer_length = length_read; *buffer_length = length_read;
if (self->buffer_index % 2 == 1) { if (self->buffer_index % 2 == 1) {
self->second_buffer_length = length_read; self->second_buffer_length = length_read;
@ -213,7 +226,7 @@ bool audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self,
*buffer = *buffer + self->bits_per_sample / 8; *buffer = *buffer + self->bits_per_sample / 8;
} }
return self->bytes_remaining == 0; return self->bytes_remaining == 0 ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA;
} }
void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t* self, bool single_channel, void audioio_wavefile_get_buffer_structure(audioio_wavefile_obj_t* self, bool single_channel,

View File

@ -29,6 +29,8 @@
#include "py/obj.h" #include "py/obj.h"
#include "shared-module/audioio/__init__.h"
typedef struct { typedef struct {
mp_obj_base_t base; mp_obj_base_t base;
uint8_t* buffer; uint8_t* buffer;
@ -56,7 +58,7 @@ typedef struct {
void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t* self, void audioio_wavefile_reset_buffer(audioio_wavefile_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel); uint8_t channel);
bool audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self, audioio_get_buffer_result_t audioio_wavefile_get_buffer(audioio_wavefile_obj_t* self,
bool single_channel, bool single_channel,
uint8_t channel, uint8_t channel,
uint8_t** buffer, uint8_t** buffer,

View File

@ -0,0 +1,36 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H
typedef enum {
GET_BUFFER_DONE, // No more data to read
GET_BUFFER_MORE_DATA, // More data to read.
GET_BUFFER_ERROR, // Error while reading data.
} audioio_get_buffer_result_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO__INIT__H