Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix low notes; clean code #7

Merged
merged 18 commits into from
May 11, 2022
Prev Previous commit
Next Next commit
rm c++ namespaces
  • Loading branch information
cvonk committed May 8, 2022
commit 4b5445ac9e1e0b881d2ad2b9765bab7ca7b33a75
2 changes: 1 addition & 1 deletion main/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
// choice: input source
#define SRC_MICR (1)
#define SRC_FILE (2)
#define SRC (SRC_MICR)
#define SRC (SRC_FILE)

// choice: output destination (must be DST_PIANOROLL for USB-MIDI output)
#define DST_STAFF (1)
Expand Down
28 changes: 12 additions & 16 deletions main/main.ino
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ namespace {

#if SRC == SRC_FILE

INLINE uint_least8_t const // return 0 if successful
_readSamplesFromFile(File & f, // file to read samples from [in]
char * const noteName, // note name derived from file name [out]
char * const samples, // samples read from file [out]
amplitude_t * amplitude) // signal amplitude [out]
INLINE uint_least8_t // return 0 if successful
_readSamplesFromFile(File & f, // file to read samples from [in]
char * const noteName, // note name derived from file name [out]
sample_t * const samples, // samples read from file [out]
amplitude_t * amplitude) // signal amplitude [out]
{
strcpy(noteName, f.name());

Expand Down Expand Up @@ -158,12 +158,13 @@ namespace {
* 3. match the frequency to a note
*/

uint_least8_t const // returns 0 when successful
uint_least8_t // returns 0 when successful
_calcNoteFromFile(File & f, // file to read samples from
char * instrument) // name of instrument (for CSV monitor only)
char * instrument) // name of instrument (for CSV monitor only)
{
(void) instrument;
//ASSERT((Debug::getMemFree() > CONFIG_MIDIMIKE_WINDOW_SIZE + 65)); // very rough estimate
char samples[CONFIG_MIDIMIKE_WINDOW_SIZE];
sample_t samples[CONFIG_MIDIMIKE_WINDOW_SIZE];

// read samples from file

Expand All @@ -173,7 +174,7 @@ namespace {
if (_readSamplesFromFile(f, noteName, samples, &amplitude) == 0) {

// find frequency from samples
float freq = Frequency::calculate(samples);
float freq = frequency_calculate(samples);

// find note from frequency
Pitch pitch(freq);
Expand All @@ -186,11 +187,6 @@ namespace {
# elif DST == DST_PIANOROLL

// need it twice, otherwise it doesn't meet the minimum note duration
#if 0
mv.segmentBuf->put(note.getPitch(), amplitude);
mv.segmentBuf->put(note.getPitch(), amplitude);
pianoroll_draw(mv.segment->getLastOffset(), mv.segmentBuf);
#endif
mv.segment->put(millis(), pitch.getPitch(), amplitude, mv.segmentBuf);
mv.segment->put(millis(), pitch.getPitch(), amplitude, mv.segmentBuf);
pianoroll_draw(mv.segment->getLastOffset(), mv.segmentBuf);
Expand Down Expand Up @@ -279,7 +275,7 @@ loop()
samples_t samples = microphone_get_samples(&amplitude);

// find frequency from samples
float freq = Frequency::calculate(samples);
float freq = frequency_calculate(samples);

// no longer need the samples, so reuse it and start gathering samples for next time around
microphone_start(); // async
Expand Down Expand Up @@ -319,7 +315,7 @@ loop()
# elif DST == DST_SERIAL

Pitch pitchIn(noteNr_t::C,0);
note.serialOut("microphone", pitchIn, freq, amplitude);
pitch.serialOut("microphone", pitchIn, freq, amplitude);

# endif

Expand Down
11 changes: 4 additions & 7 deletions main/src/display/staff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
typedef uint_least8_t hStaffPos_t;
typedef int16_t vStaffPos_t; // 2BD not 100% sure if this should be signed

typedef struct note_t {
typedef struct staffnote_t {
uint_least8_t posInOctave; // staff position within octave
bool flat;
} note_t;
} staffnote_t;

typedef struct display_t {
int16_t height;
Expand All @@ -74,12 +74,9 @@ typedef struct position_t {
positionHiLo_t staff;
} position_t;

vStaffPos_t _nr2vStaffPos(noteNr_t const number, octaveNr_t const octave);
vStaffPos_t _freq2vStaffPos(frequency_t const freq);

typedef struct staff_t {
Adafruit_ST7735 * tft;
note_t const notes[static_cast<int>(noteNr_t::COUNT)];
staffnote_t const notes[static_cast<int>(noteNr_t::COUNT)];
display_t display;
distance_t distance;
position_t position;
Expand Down Expand Up @@ -133,7 +130,7 @@ _isFlat(noteNr_t const noteNr)

static INLINE vStaffPos_t
_nr2vStaffPos(noteNr_t const number,
octaveNr_t const octave)
octaveNr_t const octave)
{
uint16_t const staffPositionsInOctave = 7;
return staffPositionsInOctave * octave + _my.notes[static_cast<int>(number)].posInOctave;
Expand Down
2 changes: 2 additions & 0 deletions main/src/display/staff.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include "../../sample_t.h"

#if 0
class Note;
#endif

void staff_init(uint_least8_t tftCS_pin, uint_least8_t dc_pin, uint_least8_t reset_pin);
void staff_draw_note(Pitch & pitch, amplitude_t const amplitude);
6 changes: 3 additions & 3 deletions main/src/display/staffsymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,16 @@ symbol_t _symbols[STAFFSYMBOL_NAME_COUNT] = {
#endif
};

typedef struct my_t {
typedef struct staffsymbol_t {
Adafruit_ST7735 * tft;
symbolCoordXY_t displaySize;
yCoordinate_t noteRadius;
yCoordinate_t bottom2loStaff;
yCoordinate_t top2hiStaff;
yCoordinate_t yG4;
} my_t;
} staffsymbol_t;

static my_t _my = {};
static staffsymbol_t _my = {};

void
staffsymbol_init(Adafruit_ST7735 * const tft,
Expand Down
12 changes: 6 additions & 6 deletions main/src/microphone/microphone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ typedef struct amplitudeRange_t {
sample_t max;
} amplitudeRange_t;

typedef struct my_t {
typedef struct microphone_t {
sampleCnt_t cnt;
amplitudeRange_t range;
sample_t * samples;
uint8_t analogPort;
uint8_t prescaler;
} my_t;
} microphone_t;

my_t volatile _my = {}; // data updates in ISR
microphone_t volatile _my = {}; // data updates in ISR

void
microphone_begin(uint8_t const port)
Expand All @@ -69,7 +69,7 @@ void
microphone_start(void)
{
// init private date for ISR (so we don't have to test for ii==0 inside the ISR)
my_t volatile * const my = &_my;
microphone_t volatile * const my = &_my;
my->cnt = 0;

// start gathering new samples (no need to disable interrupts, after all this interrupt is off, and we're turning it on here)
Expand Down Expand Up @@ -98,7 +98,7 @@ microphone_get_samples(amplitude_t * const amplitudePtr)
// spin wait until all samples are available
}

my_t volatile * const my = &_my;
microphone_t volatile * const my = &_my;

bool clipping = (my->range.max == SCHAR_MIN) || (my->range.max == SCHAR_MAX);
amplitude_t amplitude = (int16_t)my->range.max - my->range.min; // top-top [0..255]
Expand All @@ -111,7 +111,7 @@ microphone_get_samples(amplitude_t * const amplitudePtr)
// interrupt service routine
ISR (ADC_vect)
{
::my_t volatile * const my = &_my;
microphone_t volatile * const my = &_my;
sample_t const s = ADCH + SCHAR_MIN; // remove voltage bias by changing range from [0..255] to [-128..127]

// update min and max, so peak-to-peak value can be determined
Expand Down
2 changes: 2 additions & 0 deletions main/src/midi/midifile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ midifile_init(uint_least8_t const cs)
if (SD.begin(cs) == false) { // declared in SD.cpp
return -1;
}
#else
(void) cs;
#endif
return 0;
}
Expand Down
99 changes: 47 additions & 52 deletions main/src/pitch/frequency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,101 +25,96 @@
#include <Arduino.h>
#include <stdint.h>
#include <limits.h>

#include "../../config.h"
#include "../microphone/microphone.h"
#include "frequency.h"

namespace {

enum class State {
findPosSlope = 0,
findNegSlope,
secondPeak,
};
typedef enum state_t {
STATE_FIND_POS_SLOPE = 0,
STATE_FIND_NEG_SLOPE,
STATE_FIND_SECOND_PEAK,
} state_t;

typedef sampleCnt_t samplesLag_t;
typedef int32_t autoCorr_t;
typedef sampleCnt_t samplesLag_t;
typedef int32_t autoCorr_t;

// Calculate auto correlation for "lag"
// calculate auto correlation for "lag"

INLINE autoCorr_t // (normalized) auto correlation result
_autoCorr( samples_t const samples, // pointer to signed 8-bit data samples
samplesLag_t const lag ) // [in] lag
{
// samples[ii] * samples[ii+lag], results in an int16 term
// sum += term, results in an int32
// To keep the sum to an int16, each time the term could be divided by nrOfSamples.
// to make the division faster, I would round nrOfSamples up to a 2^n boundary. (2BD)
static INLINE autoCorr_t // (normalized) auto correlation result
_auto_corr(samples_t const samples, // pointer to signed 8-bit data samples
samplesLag_t const lag) // lag
{
// samples[ii] * samples[ii+lag], results in an int16 term
// sum += term, results in an int32
// To keep the sum to an int16, each time the term could be divided by nrOfSamples.
// to make the division faster, I would round nrOfSamples up to a 2^n boundary. (2BD)

autoCorr_t ac = 0;
autoCorr_t ac = 0;

for ( sampleCnt_t ii = 0; ii < CONFIG_MIDIMIKE_WINDOW_SIZE - lag; ii++ ) {
ac += ((int16_t)samples[ii] * samples[ii + lag]);
}
return ac;
}

INLINE float // returns interpolated peak adjustment compared to peak location
_quadInterpAdj( autoCorr_t const left, // sample value left of the peak
autoCorr_t const mid, // sample value at the peak
autoCorr_t const right ) // sample value right of the peak
{
float const adj = (float)0.5 * (right - left) / (2 * mid - left - right);
return adj;
for (sampleCnt_t ii = 0; ii < CONFIG_MIDIMIKE_WINDOW_SIZE - lag; ii++) {
ac += ((int16_t)samples[ii] * samples[ii + lag]);
}
return ac;
}

} // name space
static INLINE float // returns interpolated peak adjustment compared to peak location
_quad_interp_adj(autoCorr_t const left, // sample value left of the peak
autoCorr_t const mid, // sample value at the peak
autoCorr_t const right) // sample value right of the peak
{
float const adj = (float)0.5 * (right - left) / (2 * mid - left - right);
return adj;
}

#define INTERPOLATE (1)
#define NORMALIZE (0)

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch"
#define INTERPOLATE (1)
#define NORMALIZE (0)

frequency_t // returns frequency found, 0 when not found [out]
Frequency::calculate( samples_t const samples ) // pointer to signed 8-bit data samples [in]
frequency_t // returns frequency found, 0 when not found [out]
frequency_calculate(samples_t const samples) // pointer to signed 8-bit data samples [in]
{
float period = 0;

if ( samples ) {
if (samples) {

// Search between minimum and maximum frequencies (sampleRate/lagMax, sampleRate/lagMin).
// For 150 samples and a 9615 S/s, this corresponds to [512 .. 3846 Hz]
samplesLag_t const lagMin = CONFIG_MIDIMIKE_LAG_MIN; // SAMPLE_LAG_MIN;
samplesLag_t const lagMax = CONFIG_MIDIMIKE_LAG_MAX;

// determine threshold below we ignore peaks
autoCorr_t const acMax = _autoCorr( samples, 0 ); // initial peak = measure of the energy in the signal
autoCorr_t const acMax = _auto_corr(samples, 0); // initial peak = measure of the energy in the signal
#if NORMALIZE
autoCorr_t const acThreshold = (float)acMax * 4/5; // or .71 empirical value
#else
autoCorr_t const acThreshold = (float)acMax * 2/3; // empirical value
#endif
autoCorr_t acPrev = 0;
State state = State::findPosSlope; // ensure C++11 is enabled
state_t state = STATE_FIND_POS_SLOPE; // ensure C++11 is enabled

for ( samplesLag_t lag = lagMin; (lag < lagMax) && (state != State::secondPeak); lag++ ) {
for (samplesLag_t lag = lagMin; (lag < lagMax) && (state != STATE_FIND_SECOND_PEAK); lag++) {

// unnormalized autocorrelation for time "lag"
autoCorr_t ac = _autoCorr( samples, lag );
autoCorr_t ac = _auto_corr(samples, lag);
#if NORMALIZE
// normalize for introduced zeros
ac = (float)ac * (float)CONFIG_MIDIMIKE_WINDOW_SIZE / (float)(CONFIG_MIDIMIKE_WINDOW_SIZE - lag);
#endif
// find peak after the initial maximum
switch ( state ) {
case State::findPosSlope:
if ( (ac > acThreshold) && (ac > acPrev) ) {
state = State::findNegSlope;
switch (state) {
case STATE_FIND_POS_SLOPE:
if ((ac > acThreshold) && (ac > acPrev)) {
state = STATE_FIND_NEG_SLOPE;
}
break;
case State::findNegSlope:
if ( ac <= acPrev ) {
state = State::secondPeak;
case STATE_FIND_NEG_SLOPE:
if (ac <= acPrev) {
state = STATE_FIND_SECOND_PEAK;
#if INTERPOLATE
period = lag - 1 + _quadInterpAdj( _autoCorr( samples, lag - 2 ),
acPrev, ac );
period = lag - 1 + _quad_interp_adj(_auto_corr(samples, lag - 2),
acPrev, ac);
#else
period = lag - 1;
#endif
Expand Down
8 changes: 1 addition & 7 deletions main/src/pitch/frequency.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,4 @@

#include "../../sample_t.h"

namespace Frequency {

frequency_t // returns frequency found, 0 when not found
calculate( samples_t const samples ); // pointer to signed 8-bit data samples [in]

};

frequency_t frequency_calculate(samples_t const samples);
Loading