lancaster-university / codal-microbit-v2

CODAL target for the micro:bit v2.x series of devices
MIT License
41 stars 50 forks source link

If a DataSink sampling rate is low (<143) it might end up not pulling any data #428

Open microbit-carlos opened 2 months ago

microbit-carlos commented 2 months ago

From this MicroPython bug:

Trying to do an audio recording with a sampling rate of 142 or lower results in no data being pulled from the DataSource.

This is replicable with the default StreamRecording, however before being able to set a sampling rate of 142, we are first hit with issue https://github.com/lancaster-university/codal-microbit-v2/issues/427 (triggered with a sample rate lower than 320) which hides this problem.

Using a custom DataSink, like MicroPython, we can confirm that no data is pulled from upstream if the sampling rate is set to 142 or lower:

Source code with custom DataSink ```cpp #include "MicroBit.h" #include "StreamRecording.h" MicroBit uBit; const int SAMPLE_RATE = 142; const int BUFFER_LEN = SAMPLE_RATE * 5; int cur_len = 1; uint8_t buffer[BUFFER_LEN]; class MyStreamRecording : public DataSink { public: SplitterChannel *upStream; public: int *dest_pos_ptr; uint8_t *dest; size_t dest_max; bool request_stop; MyStreamRecording(SplitterChannel *source); virtual ~MyStreamRecording(); virtual int pullRequest(); bool isRecording(); void recordAsync(); void erase(); }; MyStreamRecording::MyStreamRecording(SplitterChannel *source) : upStream(source) { } MyStreamRecording::~MyStreamRecording() { } int MyStreamRecording::pullRequest() { uint8_t *pull_buf = this->dest + *this->dest_pos_ptr; size_t n = this->dest_max - *this->dest_pos_ptr; if (n > 0) { n = this->upStream->pullInto(pull_buf, n) - pull_buf; } if (n == 0 || this->request_stop) { this->upStream->disconnect(); this->request_stop = false; } else { // Convert signed 8-bit to unsigned 8-bit data. for (size_t i = 0; i < n; ++i) { pull_buf[i] += 128; } *this->dest_pos_ptr += n; } return DEVICE_OK; } bool MyStreamRecording::isRecording() { return upStream->isConnected(); } void MyStreamRecording::recordAsync() { upStream->connect(*this); } void MyStreamRecording::erase() { *dest_pos_ptr = 0; request_stop = false; // set buf to zero using memset memset(this->dest, 0, this->dest_max); } int main() { uBit.init(); SplitterChannel *splitterChannel = uBit.audio.splitter->createChannel(); splitterChannel->setFormat(DATASTREAM_FORMAT_8BIT_UNSIGNED); splitterChannel->requestSampleRate( SAMPLE_RATE ); MyStreamRecording *recording = new MyStreamRecording(splitterChannel); recording->dest = buffer; recording->dest_pos_ptr = &cur_len; *recording->dest_pos_ptr = 0; recording->dest_max = BUFFER_LEN; recording->request_stop = false; MicroBitAudio::requestActivation(); while (true) { if (uBit.buttonA.isPressed()) { recording->erase(); recording->recordAsync(); while (recording->isRecording()) { uBit.display.print("R"); uBit.sleep(5); } uBit.display.clear(); } uBit.sleep(200); } } ```

Essentially it comes down to SplitterChannel::resample():

Also interesting to note that samplesPerOut is set to zero, but the division in StreamSplitter.cpp#L84 doesn't cause an exception 🤷‍♂️