Skip to content

Commit

Permalink
lichen-community-systemsgh-65: Improves Calibrator implementation.
Browse files Browse the repository at this point in the history
Comparison of two calibration methods (current version works reasonably well, commented out version less so).
  • Loading branch information
colinbdclark committed Nov 26, 2023
1 parent 775913c commit 602b370
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 63 deletions.
30 changes: 30 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,36 @@
"svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd",
"type": "cortex-debug"
},
{
"name": "Remote Patch.Init() Calibrator",
"configFiles": [
"interface/stlink.cfg",
"target/stm32h7x.cfg"
],
"cwd": "${workspaceFolder}/hosts/daisy/examples/patch_init/calibrator",
"debuggerArgs": [
"-d",
"${workspaceRoot}/hosts/daisy/examples/patch_init/calibrator"
],
"executable": "${workspaceRoot}/hosts/daisy/examples/patch_init/calibrator/build/patch-init-calibrator.elf",
"interface": "swd",
"openOCDLaunchCommands": [
"init",
"reset init"
],
"preLaunchTask": "Debug Build Patch.Init() Examples",
"preRestartCommands": [
"load",
"enable breakpoint",
"monitor reset"
],
"request": "launch",
"runToEntryPoint": "main",
"servertype": "openocd",
"showDevDebugOutput": "parsed",
"svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd",
"type": "cortex-debug"
},
{
"name": "Remote Versio Filter",
"configFiles": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,9 @@ struct sig_daisy_Host* host;
struct sig_dsp_ConstantValue* ampScale;
struct sig_daisy_SwitchIn* button;
struct sig_daisy_CVIn* cv1In;
struct sig_daisy_CVIn* cv2In;
struct sig_dsp_Calibrator* cv1Calibrator;
struct sig_dsp_Calibrator* cv2Calibrator;

struct sig_dsp_Oscillator* sine1;
struct sig_dsp_LinearToFreq* sine1Voct;
struct sig_dsp_Oscillator* sine2;
struct sig_dsp_LinearToFreq* sine2Voct;

struct sig_daisy_AudioOut* audio1Out;
struct sig_daisy_AudioOut* audio2Out;

Expand All @@ -54,16 +48,6 @@ void buildGraph(struct sig_SignalContext* context, struct sig_Status* status) {
cv1Calibrator->inputs.source = cv1In->outputs.main;
cv1Calibrator->inputs.gate = button->outputs.main;

cv2In = sig_daisy_CVIn_new(&allocator, context, host);
sig_List_append(&signals, cv2In, status);
cv2In->parameters.control = sig_daisy_PatchInit_CV_IN_2;
cv2In->parameters.scale = 5.0f;

cv2Calibrator = sig_dsp_Calibrator_new(&allocator, context);
sig_List_append(&signals, cv2Calibrator, status);
cv2Calibrator->inputs.source = cv2In->outputs.main;
cv2Calibrator->inputs.gate = button->outputs.main;

ampScale = sig_dsp_ConstantValue_new(&allocator, context, 0.5f);

sine1Voct = sig_dsp_LinearToFreq_new(&allocator, context);
Expand All @@ -80,19 +64,11 @@ void buildGraph(struct sig_SignalContext* context, struct sig_Status* status) {
audio1Out->parameters.channel = sig_daisy_AUDIO_OUT_1;
audio1Out->inputs.source = sine1->outputs.main;

sine2Voct = sig_dsp_LinearToFreq_new(&allocator, context);
sig_List_append(&signals, sine2Voct, status);
sine2Voct->inputs.source = cv2Calibrator->outputs.main;

sine2 = sig_dsp_Sine_new(&allocator, context);
sig_List_append(&signals, sine2, status);
sine2->inputs.freq = sine2Voct->outputs.main;
sine2->inputs.mul = ampScale->outputs.main;

audio2Out = sig_daisy_AudioOut_new(&allocator, context, host);
sig_List_append(&signals, audio2Out, status);
audio2Out->parameters.channel = sig_daisy_AUDIO_OUT_2;
audio2Out->inputs.source = sine2->outputs.main;
audio2Out->inputs.source = sine1->outputs.main;

}

int main(void) {
Expand Down
2 changes: 1 addition & 1 deletion hosts/daisy/include/daisy-patch-sm-host.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,4 @@ void sig_daisy_PatchSMHost_destroy(
struct sig_Allocator* allocator,
struct sig_daisy_Host* self);

#endif SIGNALETIC_DAISY_PATCH_SM_H
#endif // SIGNALETIC_DAISY_PATCH_SM_H
20 changes: 13 additions & 7 deletions libsignaletic/include/libsignaletic.h
Original file line number Diff line number Diff line change
Expand Up @@ -1989,25 +1989,31 @@ struct sig_dsp_Calibrator_Inputs {
float_array_ptr gate;
};

struct sig_dsp_Calibrator_RecordingState {
float minValue;
float maxValue;
struct sig_dsp_Calibrator_State {
float target;
size_t numSamplesRecorded;
float min;
float max;
float sum;
float avg;
float diff;
};

void sig_dsp_Calibrator_RecordingState_init(struct
sig_dsp_Calibrator_RecordingState* state);
void sig_dsp_Calibrator_State_init(struct sig_dsp_Calibrator_State* states,
float* targetValues, size_t numStages);

// TODO: Don't hardcode this.
#define sig_dsp_Calibrator_NUM_STAGES 6
#define sig_dsp_Calibrator_TARGET_VALUES {0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 4.75f}

struct sig_dsp_Calibrator {
struct sig_dsp_Signal signal;
struct sig_dsp_Calibrator_Inputs inputs;
struct sig_dsp_Signal_SingleMonoOutput outputs;

struct sig_dsp_Calibrator_RecordingState state;
struct sig_dsp_Calibrator_State states[sig_dsp_Calibrator_NUM_STAGES];
float previousGate;
size_t stage;
float calibrationReadings[3];
};

struct sig_dsp_Calibrator* sig_dsp_Calibrator_new(
Expand Down
76 changes: 47 additions & 29 deletions libsignaletic/src/libsignaletic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2808,25 +2808,30 @@ struct sig_dsp_Calibrator* sig_dsp_Calibrator_new(
return self;
}

inline void sig_dsp_Calibrator_RecordingState_init(
struct sig_dsp_Calibrator_RecordingState* state) {
state->minValue = 1.0f;
state->maxValue = 0.0f;
state->numSamplesRecorded = 0;
state->sum = 0.0f;
inline void sig_dsp_Calibrator_State_init(
struct sig_dsp_Calibrator_State* states,
float* targetValues, size_t numStages) {
for (size_t i = 0; i < numStages; i++) {
struct sig_dsp_Calibrator_State* state = &states[i];
state->target = state->avg = targetValues[i];
state->numSamplesRecorded = 0;
state->min = INFINITY;
state->max = -INFINITY;
state->sum = 0.0f;
state->diff = 0.0f;
}
}

void sig_dsp_Calibrator_init(struct sig_dsp_Calibrator* self,
struct sig_SignalContext* context) {
sig_dsp_Signal_init(self, context, *sig_dsp_Calibrator_generate);

self->previousGate = 0.0f;
self->stage = 0;
self->calibrationReadings[0] = 0.0f;
self->calibrationReadings[1] = 1.0f;
self->calibrationReadings[2] = 3.0f;

sig_dsp_Calibrator_RecordingState_init(&self->state);
float targetValues[sig_dsp_Calibrator_NUM_STAGES] =
sig_dsp_Calibrator_TARGET_VALUES;
sig_dsp_Calibrator_State_init(self->states,
targetValues, sig_dsp_Calibrator_NUM_STAGES);

sig_CONNECT_TO_SILENCE(self, source, context);
sig_CONNECT_TO_SILENCE(self, gate, context);
Expand All @@ -2835,45 +2840,58 @@ void sig_dsp_Calibrator_init(struct sig_dsp_Calibrator* self,
void sig_dsp_Calibrator_generate(void* signal) {
struct sig_dsp_Calibrator* self = (struct sig_dsp_Calibrator*) signal;


for (size_t i = 0; i < self->signal.audioSettings->blockSize; i++) {
float source = FLOAT_ARRAY(self->inputs.source)[i];
float gate = FLOAT_ARRAY(self->inputs.gate)[i];
size_t stage = self->stage;
struct sig_dsp_Calibrator_State* state = &self->states[stage];

if (gate <= 0.0f && self->previousGate > 0.0f) {
// Gate is low; recording has just stopped.
// Calculate offset by discarding the highest and lowest values,
// and then averaging the rest.
self->state.sum -= self->state.minValue;
self->state.sum -= self->state.maxValue;
self->calibrationReadings[self->stage] = self->state.sum /
(self->state.numSamplesRecorded - 2);
sig_dsp_Calibrator_RecordingState_init(&self->state);
self->stage = (self->stage + 1) % 3;
state->sum -= state->min;
state->sum -= state->max;
state->avg = state->sum /
(state->numSamplesRecorded - 2);
state->diff = -(state->target - state->avg);
self->stage = (stage + 1) % sig_dsp_Calibrator_NUM_STAGES;
} else if (gate > 0.0f) {
// Gate is high; we're recording.
self->state.sum += source;
self->state.numSamplesRecorded++;
state->sum += source;
state->numSamplesRecorded++;

if (source < self->state.minValue) {
self->state.minValue = source;
if (source < state->min) {
state->min = source;
}

if (source > self->state.maxValue) {
self->state.maxValue = source;
if (source > state->max) {
state->max = source;
}
}

// Calibrate using piecewise linear fit from
// readings sampled at 1.0V and 3.0V.
float dcOffset = self->calibrationReadings[0];
float yk = self->calibrationReadings[1];
float ykplus1 = self->calibrationReadings[2];
// readings sampled between 0.0 and 4.75V.
// The ADC tops out slightly before 5 volts,
// so this keeps it the in the range of 9 semitones
// above the fourth octave.
// size_t calibrationIdx = (size_t) floorf(fabsf(source));
// struct sig_dsp_Calibrator_State start = self->states[calibrationIdx];
// struct sig_dsp_Calibrator_State end = self->states[calibrationIdx + 1];
// float yk = start.avg;
// float ykplus1 = end.avg;
// float tk = start.target;
// float tkplus1 = end.target;
// float px = yk + ((ykplus1 - yk) / (tkplus1 - tk)) * (source - tk);
// FLOAT_ARRAY(self->outputs.main)[i] = px;

float yk = self->states[1].avg;
float ykplus1 = self->states[3].avg;
float delta = ykplus1 - yk;
float scale = 2.0f / delta;
float offset = 1.0f - scale * yk;
float px = offset + (scale * source);
FLOAT_ARRAY(self->outputs.main)[i] = px - dcOffset;
FLOAT_ARRAY(self->outputs.main)[i] = px;

self->previousGate = gate;
}
Expand Down

0 comments on commit 602b370

Please sign in to comment.