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():
From an ADC sampling period set to 22 microsec 1000000/22 = 45454, which is configured when the audio pipeline sets the sampling rate to to 44100 and that calculates the period as an int with 1000000/44100=22.67
The DataSource pull contains 256 samples
With the DataSink sampling rate set to 142, you end up with
byteDeficit = 45454-142 = 45312
packetsPerSec = 45454 / 256 = 177
dropPerPacket = 45312 / 177 = 256 <-- Basically drop the entire packet
samplesPerOut = 256 - 256 = 0 <-- Or in other words, keep zero samples
Also interesting to note that samplesPerOut is set to zero, but the division in StreamSplitter.cpp#L84 doesn't cause an exception 🤷♂️
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()
:45454
1000000/22 = 45454
, which is configured when the audio pipeline sets the sampling rate to to 44100 and that calculates the period as an int with1000000/44100=22.67
256
samples142
, you end up withAlso interesting to note that
samplesPerOut
is set to zero, but the division in StreamSplitter.cpp#L84 doesn't cause an exception 🤷♂️