Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
microbit-carlos opened this issue May 1, 2024 · 1 comment
Assignees
Milestone

Comments

@microbit-carlos
Copy link
Collaborator

microbit-carlos commented May 1, 2024

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 #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
#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():

  • codal-core/source/streams/StreamSplitter.cpp#L56-L70
  • It gets the upstream sampling rate as 45454
    • 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 🤷‍♂️

@martinwork
Copy link
Collaborator

A simple fix would be to change the zero samplesPerOut to one, unless totalSamples is zero, which would make 177 the minimum rate.

Perhaps slower rates could calculate the skip between readings and remember an index from block to block, something like... if skip is 300 take samples from successive blocks 0, 44 (=300-256), 88, 132, 176, 220, miss one, 8, 52, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants