-
Notifications
You must be signed in to change notification settings - Fork 68
Open
Description
Something I noticed and wasn't sure if it was by design.
This graph shows a real time plot of a channel created by the uBit.audio.splitter, so that should be the output of the StreamNormalizer.
In this video plotting the serial data, I am playing a sine tone on my phone and getting it close and closer to the micro:bit microphone. At some point we can see that rather than clipping the edges it looks like the values overflow instead and we see the top peaks at the bottom of the graph and vice versa.
overflow.mov
And a screenshot in case the video doesn't load:
Hex: MICROBIT.hex.zip
Source code (rough, was testing multiple things, so not as small as possible)
#include "MicroBit.h"
MicroBit uBit;
// Configured 11 kHz sampling rate results in a real rate of 11111 Hz
const size_t SAMPLING_RATE = 11111;
const size_t MIC_BUFFER_SIZE = 1024; // Around 100 ms buffer
int8_t micBufferRaw[MIC_BUFFER_SIZE] = {0};
int8_t micBufferNormalised[MIC_BUFFER_SIZE] = {0};
class DataSinkCapturer : public DataSink {
public:
SplitterChannel *upstream;
int8_t *dest;
int dest_pos_ptr;
size_t dest_max;
int sample_format;
DataSinkCapturer(SplitterChannel *source) : upstream(source) { }
virtual ~DataSinkCapturer() { }
bool isCapturing() {
return this->upstream->isConnected();
}
void captureAsync(int8_t *buf, size_t max_len) {
this->dest = buf;
this->dest_max = max_len;
this->dest_pos_ptr = 0;
this->sample_format = this->upstream->getFormat();
if (this->sample_format != DATASTREAM_FORMAT_8BIT_SIGNED &&
this->sample_format != DATASTREAM_FORMAT_16BIT_SIGNED) {
uBit.serial.printf("upstream format '%d' invalid, expected signed 8 or 16 bit\n", this->sample_format);
return;
}
if ((size_t)this->upstream->getSampleRate() != SAMPLING_RATE) {
uBit.serial.printf("upstream sample rate '%d' invalid, expected %d\n", (int)this->upstream->getSampleRate(), SAMPLING_RATE);
return;
}
upstream->connect(*this);
}
virtual int pullRequest() {
static bool first_pull = true;
if (first_pull) {
first_pull = false;
uBit.serial.printf("T2: %d\n", uBit.systemTime());
}
int8_t *pull_buf = this->dest + this->dest_pos_ptr;
int n = this->dest_max - this->dest_pos_ptr;
if (n > 0) {
ManagedBuffer buffer = this->upstream->pull();
if (buffer.length() == 0) {
uBit.serial.printf("Warning: No data received at %d sample\n", this->dest_pos_ptr);
}
n = buffer.length() > n ? n : buffer.length();
int8_t *b = (int8_t *)buffer.getBytes();
// If these are 16-bit samples, we need scale them down to 8-bit
if (this->sample_format == DATASTREAM_FORMAT_16BIT_SIGNED) {
// First check for odd number of bytes, which shouldn't happen...
if (n % 2 != 0) {
uBit.serial.printf("Warning: Odd number of bytes (%d) received at %d sample\n", n, this->dest_pos_ptr);
n -= 1;
}
// Buffer contains 16-bit data, convert it to 8-bit unsigned sample
for (int i = 0; i < n; i +=2 ) {
//uint16_t sample = ((uint16_t)(buffer[i + 1]) << 8) | (uint16_t)(buffer[i]);
//pull_buf[i / 2] = (uint8_t)((sample >> 8) & 0xFF);
//uBit.serial.printf("%d\n", pull_buf[i / 2]);
//uBit.serial.printf("%d\n", sample);
// MSB as a scaled 8-bit approximation
pull_buf[i / 2] = b[i + 1];
}
n /= 2; // number of samples is half the number of bytes
//memcpy(pull_buf, buffer.getBytes(), n);
//n /= 2; // number of samples is half the number of bytes
//int16_t *raw_samples = (int16_t *)pull_buf;
//for (int i = 0; i < n; i++) {
// uBit.serial.printf("%d\n", raw_samples[i]);
//}
} else if (this->sample_format == DATASTREAM_FORMAT_8BIT_SIGNED) {
memcpy(pull_buf, b, n);
for (int i = 0; i < n; i++) {
// pull_buf[i] = (uint8_t)((int)buffer[i] + 128);
uBit.serial.printf("%d\n", (int8_t)pull_buf[i]);
}
}
}
if (n <= 0) {
//this->upstream->dataWanted(DATASTREAM_DONT_CARE);
this->upstream->disconnect();
uBit.serial.send("Disconnected from upstream channel.\n");
} else {
//this->dest_pos_ptr += n;
}
return DEVICE_OK;
}
};
int main() {
uBit.init();
uBit.serial.printf("Microphone data capture Test\n");
uBit.sleep(1000);
// RawSplitter is a 16-bit unsigned stream, splitter is a signed 8-bit stream
DataSinkCapturer *micPlotterRaw = new DataSinkCapturer(uBit.audio.rawSplitter->createChannel());
DataSinkCapturer *micPlotterNormalised = new DataSinkCapturer(uBit.audio.splitter->createChannel());
uBit.serial.printf("T1: %d\n", uBit.systemTime());
micPlotterRaw->captureAsync(micBufferRaw, MIC_BUFFER_SIZE);
micPlotterNormalised->captureAsync(micBufferNormalised, MIC_BUFFER_SIZE);
while (micPlotterRaw->isCapturing() || micPlotterNormalised->isCapturing()) {
uBit.sleep(1);
}
// Print the raw and normalised data to be plotted via serial
uBit.serial.printf("Printing buffers.\n");
for (size_t i = 0; i < MIC_BUFFER_SIZE; i++) {
uBit.serial.printf("%d, %d\n", (int)micBufferRaw[i], (int)micBufferNormalised[i]);
}
uBit.serial.printf("Done.");
for(;;);
}