Skip to content

Commit

Permalink
The new version supports pickling
Browse files Browse the repository at this point in the history
  • Loading branch information
pzelasko committed Aug 16, 2022
1 parent 2ed17e3 commit 5e443b9
Show file tree
Hide file tree
Showing 8 changed files with 1,317 additions and 454 deletions.
8 changes: 8 additions & 0 deletions extensions/climiter.pxd
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
#cython: language_level=3
from libcpp.vector cimport vector
from libcpp.string cimport string

cdef extern from "limiter.h" nogil:

cdef cppclass CLimiterState

cdef cppclass CLimiter:

CLimiter(float, float, int, float)

@staticmethod
CLimiter read_from_string(string data)

string write_to_string() const

void limit_inplace(vector[float] &)

vector[float] limit(const vector[float] &)
Expand Down
1,674 changes: 1,228 additions & 446 deletions extensions/cylimiter.cpp

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion extensions/cylimiter.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from libcpp.memory cimport unique_ptr
from libcpp.vector cimport vector

from extensions.climiter cimport CLimiter
from extensions.climiter cimport CLimiter, CLimiterState


cdef class Limiter:
Expand All @@ -15,6 +15,16 @@ cdef class Limiter:
assert isinstance(delay, int) and delay > 0, "Delay has to be an integer greater than zero."
self._limiter.reset(new CLimiter(attack, release, delay, threshold))

def __setstate__(self, state: bytes) -> None:
self.__init__()
self._limiter.get().read_from_string(state)

def __getstate__(self) -> bytes:
return self.write_to_string()

def write_to_string(self) -> bytes:
return self._limiter.get().write_to_string()

def _validate_input(self, audio):
if hasattr(audio, "ndim"):
assert audio.ndim == 1, "The input audio array has to be single-dimensional (only mono audio is supported)."
Expand All @@ -29,3 +39,7 @@ cdef class Limiter:

def reset(self):
return self._limiter.get().reset()


def _create_default() -> Limiter:
return Limiter()
31 changes: 30 additions & 1 deletion extensions/limiter.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <cmath>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <utility>

#include "limiter.h"

Expand Down Expand Up @@ -36,7 +38,7 @@ void CLimiter::limit_inplace(vector<float> &audio) {
}
}

std::vector<float> CLimiter::limit(const vector<float> &audio) {
vector<float> CLimiter::limit(const vector<float> &audio) {
vector<float> out;
copy(begin(audio), end(audio), back_inserter(out));
limit_inplace(out);
Expand All @@ -52,3 +54,30 @@ void CLimiter::reset() {
delay_line_[i] = 0.0f;
}
}

void CLimiter::read_from_string(const string &data) {
istringstream str{data};
str >> attack_;
str >> release_;
str >> delay_;
str >> threshold_;
str >> delay_index_;
str >> envelope_;
str >> gain_;
float sample;
delay_line_.empty();
while(str >> sample) {
delay_line_.push_back(sample);
}
}

string CLimiter::write_to_string() const {
ostringstream str;
const auto s = " ";
str << attack_ << s << release_ << s << delay_ << s << threshold_ << s
<< delay_index_ << s << envelope_ << s << gain_ << s;
for (const auto item : delay_line_) {
str << item << s;
}
return str.str();
}
14 changes: 9 additions & 5 deletions extensions/limiter.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <vector>
#include <string>


class CLimiter {
Expand All @@ -9,6 +10,9 @@ class CLimiter {
std::vector<float> limit(const std::vector<float> &audio);
void reset();

void read_from_string(const std::string &data);
std::string write_to_string() const;

// Mutable state
private:
std::vector<float> delay_line_;
Expand All @@ -18,8 +22,8 @@ class CLimiter {

// Settings
private:
const float attack_;
const float release_;
const int delay_;
const float threshold_;
};
float attack_;
float release_;
int delay_;
float threshold_;
};
7 changes: 7 additions & 0 deletions reinstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

set -eou pipefail

pushd extensions; cython -3 --cplus *.pyx; popd
pip uninstall -y cylimiter && pip install .
pytest tests
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def no_cythonize(extensions, **_ignore):
extensions = no_cythonize(extensions)


__version__ = "0.1"
__version__ = "0.2.0"


this_directory = path.abspath(path.dirname(__file__))
Expand Down
19 changes: 19 additions & 0 deletions tests/test_limiter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
import pickle
import pytest
from cylimiter import Limiter

Expand Down Expand Up @@ -66,3 +67,21 @@ def test_limiter_reset():
limiter.reset()
audio_lim_reset = limiter.limit(audio)
assert audio_lim == audio_lim_reset

def test_limiter_pickle_works():
limiter_default = Limiter()
limiter = Limiter(attack=0.555, delay=1000, threshold=0.2, release=0.01)
data = pickle.dumps(limiter)
limiter_unpickled = pickle.loads(data)

audio = get_audio()
audio_lim = limiter.limit(audio)
assert audio != audio_lim
audio_lim_unpickled = limiter_unpickled.limit(audio)
assert audio != audio_lim_unpickled

audio_lim_default = limiter_default.limit(audio)
assert audio_lim != audio_lim_default
assert audio_lim_unpickled != audio_lim_default

assert audio_lim == audio_lim_unpickled

0 comments on commit 5e443b9

Please sign in to comment.