Skip to content

Commit

Permalink
feat: sample expression at 8khz and upsample
Browse files Browse the repository at this point in the history
  • Loading branch information
midouest committed Feb 24, 2021
1 parent 672a1e0 commit 64fcd3b
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 16 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ UGen to be parsed and evaluated.

```
(
SynthDef.new(\bytebeat, { arg out;
Out.ar(out, ByteBeat.ar())
SynthDef.new(\bytebeat, {
Out.ar(0, ByteBeat.ar())
}).add;
)
b = ByteBeatController(Synth.new(\bytebeat));
b = ByteBeatController(Synth.new(\bytebeat), 0);
b.setExpression("((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7");
```
Expand Down
8 changes: 7 additions & 1 deletion include/expression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ namespace bb

int evaluate(int t) const
{
return this->left->evaluate(t) / this->right->evaluate(t);
int divisor = this->right->evaluate(t);
// Dividing by zero will crash the SuperCollider server
if (divisor == 0)
{
return 0;
}
return this->left->evaluate(t) / divisor;
};

protected:
Expand Down
39 changes: 32 additions & 7 deletions plugins/ByteBeat/ByteBeat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
#include "ByteBeat.hpp"
#include "parse.hpp"

#define BYTEBEAT_SAMPLERATE 8000

static InterfaceTable *ft;

namespace ByteBeat
{
ByteBeat::ByteBeat()
ByteBeat::ByteBeat() : mSampleStep(BYTEBEAT_SAMPLERATE / sampleRate())
{
mCalcFunc = make_calc_function<ByteBeat, &ByteBeat::next>();

// Initialize with no audio
mExpression = new bb::Constant(0);
// Initialize with no audio to avoid popping/unitialized buffer.
// Expressions are evaluated as 32-bit signed integers, then cast to
// 8-bit unsigned integers, then transformed to floats with a range of
// +/-1.0. An expression that emits a constant value of 128 will
// correspond roughly to 0.0 at the output.
mExpression = new bb::Constant(128);
}

ByteBeat::~ByteBeat()
Expand All @@ -30,10 +36,12 @@ namespace ByteBeat
{
bb::Expression *prevExpr = mExpression;
mExpression = bb::parse(s);
// mNextSample = sampleByteBeat();
delete prevExpr;
}
catch (invalid_argument &ex)
{
Print("%s", ex.what());
// TODO: Send back to client somehow. May not be possible without a
// more general sendResponse interface.
// See: https://scsynth.org/t/scsynth-plugincmd-and-sending-responses/2638
Expand All @@ -51,20 +59,37 @@ namespace ByteBeat

for (int i = 0; i < nSamples; ++i)
{
// TODO: Run expression at 8khz and resample to server samplerate
uint8_t data = mExpression->evaluate(mTime);
++mTime;
outbuf[i] = 2 * (float)data / 255 - 1;
float data = lininterp(mAccumulator, mPrevSample, mNextSample);
outbuf[i] = data;

mAccumulator += mSampleStep;
if (mAccumulator >= 1)
{
mAccumulator -= 1;
++mTime;
mPrevSample = mNextSample;
mNextSample = sampleByteBeat();
}
}
}

inline float ByteBeat::sampleByteBeat() const
{
uint8_t sample = mExpression->evaluate(mTime);
return 2 * (float)sample / 255 - 1;
}

/**
* Unit command callback for the /setexpr command. Expects args to contain
* a single string argument representing the new bytebeat expression.
*/
void setExprCmd(ByteBeat *unit, sc_msg_iter *args)
{
unit->setExpression(args->gets());
if (args->geti(1))
{
unit->restart();
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions plugins/ByteBeat/ByteBeat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ namespace ByteBeat
* samples.
*/
void next(int nSamples);
inline float sampleByteBeat() const;

float mSampleStep;
float mAccumulator = 0;
float mPrevSample = 0;
float mNextSample = 0;

/** Time counter passed to the bytebeat expression */
int mTime = 0;
Expand Down
4 changes: 2 additions & 2 deletions plugins/ByteBeat/ByteBeatController.sc
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ ByteBeatController {
^super.newCopyArgs(synth, synthIndex);
}

setExpression { arg input;
this.sendMsg('/setexpr', input)
setExpression { arg input, restart = 0;
this.sendMsg('/setexpr', input, restart)
}

restart {
Expand Down
11 changes: 8 additions & 3 deletions plugins/ByteBeat/ByteBeatController.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ ByteBeat UGen instance.

CODE::
(
SynthDef.new(\bytebeat, { arg out;
Out.ar(out, ByteBeat.ar())
SynthDef.new(\bytebeat, {
Out.ar(0, ByteBeat.ar())
}).add;
)

b = ByteBeatController(Synth.new(\bytebeat));
b = ByteBeatController(Synth.new(\bytebeat), 0);
b.setExpression("((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7");
b.restart();

Expand All @@ -40,5 +40,10 @@ Set the bytebeat expression used to generate audio samples.
ARGUMENT:: input
The bytebeat expression string

ARGUMENT:: restart
Optionally reset the bytebeat expression's time counter 0 when setting the new
expression. 1 will reset the counter, 0 will preserve the time counter. Defaults
to 0.

METHOD:: restart
Reset the bytebeat expression's time counter to 0

0 comments on commit 64fcd3b

Please sign in to comment.