Skip to content
Permalink
Browse files

Merge branch 'melspec'

  • Loading branch information...
jonnor committed Dec 31, 2018
2 parents 182e1ed + 73448e0 commit 848965474104e415e39fd24035ebabddc3d4d607
Showing with 654 additions and 267 deletions.
  1. +5 −1 .travis.yml
  2. +73 −19 bindings/eml_audio.cpp
  3. +35 −6 emlearn.ino
  4. +61 −67 emlearn/eml_audio.h
  5. +64 −18 emlearn/eml_benchmark.h
  6. +89 −27 emlearn/eml_common.h
  7. +20 −72 emlearn/eml_fft.h
  8. +13 −9 emlearn/eml_vector.h
  9. +2 −0 requirements.dev.txt
  10. +36 −32 test/bench.c
  11. +223 −16 test/test_audio.py
  12. +33 −0 test/test_benchmark.py
@@ -1,6 +1,10 @@
language: python
dist: trusty
dist: xenial
sudo: false
addons:
apt:
packages:
- ffmpeg
python:
- '3.6'
install:
@@ -6,6 +6,7 @@
#include <pybind11/stl.h>
#include <pybind11/numpy.h>

#define EML_DEBUG 1

#include <eml_fft.h>
#include <eml_audio.h>
@@ -19,43 +20,90 @@ rfft_py(py::array_t<float, py::array::c_style | py::array::forcecast> in) {
throw std::runtime_error("SFT input must have dimensions 1");
}

if (in.shape(0) != EML_AUDIOFFT_LENGTH) {
throw std::runtime_error("SFT must have length EML_AUDIOFFT_LENGTH");
const int n_fft = in.shape(0);

// Precompute FFT table
const int n_fft_table = n_fft/2;
std::vector<float> fft_sin(n_fft_table);
std::vector<float> fft_cos(n_fft_table);
EmlFFT fft = { n_fft_table, fft_sin.data(), fft_cos.data() };
eml_fft_fill(fft, n_fft);

// Setup working buffers
const float * in_data = (const float *)in.data();
std::vector<float> imag(n_fft);
std::vector<float> real(n_fft);
for (size_t i=0; i<real.size(); i++) {
real[i] = in_data[i];
}
std::fill(imag.begin(), imag.end(), 0);

auto ret = py::array_t<float>(in.shape(0));
// Do FFT
const int status = eml_fft_forward(fft, real.data(), imag.data(), n_fft);

float *samples = (float *)in.data();
float *retdata = (float *)ret.data();
if (status != EmlOk) {
throw std::runtime_error("eml_fft error");
}

float imag_data[EML_AUDIOFFT_LENGTH];
EmlVector real = { samples, EML_AUDIOFFT_LENGTH };
EmlVector imag = { imag_data, EML_AUDIOFFT_LENGTH };
eml_vector_set_value(imag, 0.0f);
// Copy to output
auto ret = py::array_t<float>(n_fft);
for (size_t i=0; i<real.size(); i++) {
((float *)ret.data())[i] = real[i];
}

const int status = eml_audio_fft(real, imag);

EmlVector retv = { retdata, EML_AUDIOFFT_LENGTH };
eml_vector_set(retv, real, 0);
return ret;
}

if (status != 0) {
throw std::runtime_error("SFT returned error");
py::array_t<float>
melfilter_py(py::array_t<float, py::array::c_style | py::array::forcecast> in,
int samplerate, int n_fft, int n_mels, float fmin, float fmax
)
{
if (in.ndim() != 1) {
throw std::runtime_error("melfilter() input must have dimensions 1");
}
if (in.shape(0) != 1+(n_fft/2)) {
throw std::runtime_error("framing not implented");
}

const EmlAudioMel params = { n_mels, fmin, fmax, n_fft, samplerate };

// Copy input to avoid modifying
const int length = in.shape(0);
EmlVector inv = {(float *)in.data(), length};
// Prepare output
auto ret = py::array_t<float>(params.n_mels);
EmlVector outv = { (float *)ret.data(), params.n_mels };
// Run
const EmlError error = eml_audio_melspec(params, inv, outv);
if (error != EmlOk) {
throw std::runtime_error("melspec() returned error: " + std::string(eml_error_str(error)));
}
return ret;
}


py::array_t<float>
melspectrogram_py(py::array_t<float, py::array::c_style | py::array::forcecast> in,
int n_mels, float fmin, float fmax, int n_fft, int samplerate
int samplerate, int n_fft, int n_mels, float fmin, float fmax
)

{
if (in.ndim() != 1) {
throw std::runtime_error("spectrogram input must have dimensions 1");
}

if (in.shape(0) != n_fft) {
throw std::runtime_error("melspectrogram framing not implented");
}

// FFT table
const int n_fft_table = n_fft/2;
std::vector<float> fft_sin(n_fft_table);
std::vector<float> fft_cos(n_fft_table);
EmlFFT fft = { n_fft_table, fft_sin.data(), fft_cos.data() };
eml_fft_fill(fft, n_fft);

const EmlAudioMel params = { n_mels, fmin, fmax, n_fft, samplerate };

// Copy input to avoid modifying
@@ -66,11 +114,12 @@ melspectrogram_py(py::array_t<float, py::array::c_style | py::array::forcecast>
EmlVector tempv = { (float *)temp.data(), length };
EmlVector inv = {(float *)in.data(), length};
eml_vector_set(inoutv, inv, 0);
eml_vector_set_value(tempv, 0);

const int status = eml_audio_melspectrogram(params, inoutv, tempv);
const EmlError error = eml_audio_melspectrogram(params, fft, inoutv, tempv);

if (status != 0) {
throw std::runtime_error("melspectrogram returned error: " + std::to_string(status));
if (error != EmlOk) {
throw std::runtime_error("melspectrogram returned error: " + std::string(eml_error_str(error)));
}

auto ret = py::array_t<float>(params.n_mels);
@@ -80,10 +129,15 @@ melspectrogram_py(py::array_t<float, py::array::c_style | py::array::forcecast>
return ret;
}



PYBIND11_MODULE(eml_audio, m) {
m.doc() = "Audio machine learning for microcontrollers and embedded devices";

m.def("rfft", rfft_py);
m.def("melfilter", melfilter_py);

m.def("melspectrogram", melspectrogram_py);

}

@@ -1,9 +1,10 @@

#include <eml_trees.h>
#include <eml_benchmark.h>
//#include "emlearn/eml_trees.h"
#include "emlearn/eml_audio.h"
#include "emlearn/eml_benchmark.h"

#include "digits.h"

#if 0

void send_reply(int32_t request, int32_t time_taken,
int32_t prediction, int32_t n_repetitions)
@@ -67,11 +68,39 @@ void parse_predict_reply(char *buffer, float *values, int32_t values_length)
send_reply(request, time_taken, prediction, n_repetitions);
}

#endif

#define N_REPS 10
float input_data[EML_AUDIOFFT_LENGTH] = {0};
float temp_data[EML_AUDIOFFT_LENGTH] = {0};
float times[N_REPS];

EmlError
bench_melspec()
{
const int n_reps = N_REPS;
const EmlAudioMel mel = { 64, 0, 20000, 1024, 44100 };

eml_benchmark_melspectrogram(mel, input_data, temp_data, n_reps, times);
EmlVector t = { times, n_reps };

const float mean = eml_vector_mean(t);
printf("melspec ms: %f\n", mean/1000);
return EmlOk;
}



void setup() {
Serial.begin(115200);

Serial.write("starting\n");
delay(100);
bench_melspec();
Serial.write("benchmarks done\n");
}


void loop() {
const int32_t n_features = 64;
const int32_t bytes_per_number = 20; // 32bit integer, plus separator. But better to have too much
@@ -89,15 +118,15 @@ void loop() {

if (receive_idx >= buffer_length-1) {
receive_idx = 0;
memset(receive_buffer, buffer_length, 0);
memset(receive_buffer, 0, buffer_length);
Serial.println("Error, buffer overflow");
}

if (ch == '\n') {
parse_predict_reply();
//parse_predict_reply();

receive_idx = 0;
memset(receive_buffer, buffer_length, 0);
memset(receive_buffer, 0, buffer_length);
}
}
}

0 comments on commit 8489654

Please sign in to comment.
You can’t perform that action at this time.