Initial check in of pre-release version 0.1.0

main
Blackaddr 7 months ago
commit b90fc26c1e
  1. BIN
      BlackaddrAudio_Tuner.png
  2. 158
      BlackaddrAudio_Tuner.prj
  3. 21
      LICENSE
  4. 1
      bin/.gitignore
  5. BIN
      gfx/BlackaddrLogo.png
  6. BIN
      gfx/Gold_Marshall_Knob_192x192.png
  7. BIN
      gfx/StompDownLedOnGold.png
  8. BIN
      gfx/StompUpLedOffGold.png
  9. BIN
      gfx/SwitchSlideDownWhite.png
  10. BIN
      gfx/SwitchSlideUpWhite.png
  11. BIN
      gfx/Tuner_base.png
  12. BIN
      gfx/Tuner_icon.png
  13. BIN
      gfx/tuner_cents.png
  14. BIN
      gfx/tuner_cents_158h.png
  15. BIN
      gfx/tuner_notes_158h.png
  16. BIN
      gfx_design/TunerBase.xcf
  17. BIN
      gfx_design/TunerIcon.xcf
  18. BIN
      gfx_design/tuner_cents.knob
  19. BIN
      gfx_design/tuner_cents.xcf
  20. BIN
      gfx_design/tuner_notes.knob
  21. 124
      inc/Tuner.h
  22. 1
      src/.gitignore
  23. 550
      src/Tuner.cpp
  24. 127
      src/TunerBase.cpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

@ -0,0 +1,158 @@
{
"efxFileVersion": "1.4.0",
"company": "Blackaddr Audio",
"effectName": "Tuner",
"effectVersion": "0.1.0",
"coreVersion": "1.5.0",
"effectShortName": "NOISE",
"effectCategory": "Unspecified",
"effectDescription": "A noise gate wtih controllable threshold, opening and closing times and DC removal filter. Basic tuner included. ",
"numInputs": 1,
"numOutputs": 1,
"numControls": 5,
"effectFilename": "BlackaddrAudio_Tuner.efx",
"libraryName": "BlackaddrAudio_Tuner",
"cppClass": "BlackaddrAudio_Tuner::Tuner",
"cppInstBase": "_BlackaddrAudio_Tuner",
"constructorParams": "",
"isSingleton": false,
"controls": [
{
"name": "Bypass",
"shortName": "BYP",
"description": "Bypasses the effect when enabled",
"config": [
0,
0.0,
1.0,
0.0,
1.0
],
"iconOn": "gfx/StompUpLedOffGold.png",
"iconOnHeight": 232,
"iconOff": "gfx/StompDownLedOnGold.png",
"iconOffHeight": 232,
"position": [
187,
460
],
"scalingRatio": 0.75,
"userData": 1
},
{
"name": "Volume",
"shortName": "VOL",
"description": "Sets the effect output volume from -40dB to +10dB",
"config": [
3,
0.0,
10.0,
8.0,
0.1000000014901161
],
"iconPot": "gfx/Gold_Marshall_Knob_192x192.png",
"iconPotHeight": 192,
"supressValueLabel": 1,
"userData": 0
},
{
"name": "Tuner Silent",
"shortName": "T-SIL",
"description": "Mutes the output when tuner is enabled.",
"config": [
0,
0.0,
1.0,
0.0,
1.0
],
"iconOn": "gfx/SwitchSlideUpWhite.png",
"iconOnHeight": 128,
"iconOff": "gfx/SwitchSlideDownWhite.png",
"iconOffHeight": 128,
"position": [
300,
450
],
"scalingRatio": 0.6000000238418579,
"userData": 0
},
{
"name": "Tuner Cents",
"shortName": "Cents",
"description": "Indicates the tuning pitch of the current note.",
"config": [
7,
-50.0,
50.0,
0.0,
1.0
],
"iconPot": "gfx/tuner_cents_158h.png",
"iconPotHeight": 158,
"position": [
186,
150
],
"scalingRatio": 0.800000011920929,
"supressValueLabel": 1,
"userData": 0
},
{
"name": "Tuner Note",
"shortName": "Note",
"description": "Indicates the note being tuned.",
"config": [
8,
0.0,
12.0,
0.0,
1.0
],
"iconEncoder": "gfx/tuner_notes_158h.png",
"iconEncoderHeight": 158,
"enums": [
"C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#",
"A",
"A#",
"B",
"--"
],
"position": [
186,
150
],
"scalingRatio": 0.800000011920929,
"supressValueLabel": 1,
"userData": 0
}
],
"projectFileVersion": "1.0.0",
"companyLogo": "gfx/BlackaddrLogo.png",
"pedalIcon": "gfx/Tuner_icon.png",
"basePedal": "gfx/Tuner_base.png",
"publicHeaderFiles": [
"inc/Tuner.h"
],
"privateSourceFiles": [
"src/TunerBase.cpp",
"src/Tuner.cpp"
],
"modules": null,
"settings": {
"showModules": false,
"isSingleton": false,
"cppDefines": "",
"noWarnings": "",
"enableFastMath": false,
"enableO3": false
}
}

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Blackaddr Audio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
bin/.gitignore vendored

@ -0,0 +1 @@
*.efx

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,124 @@
/*
* Company: Blackaddr Audio
* Effect Name: Tuner
* Description: A noise gate wtih controllable threshold, opening and closing times and DC removal filter. Basic tuner included.
*
* This file was auto-generated by Aviate Audio Effect Creator for the Multiverse.
*/
#pragma once
#include <Audio.h>
#include <arm_math.h>
#include "Aviate/AudioEffectWrapper.h"
//!s - START_USER_INCLUDES - put your #includes below this line before the matching END
//!e - END_USER_INCLUDES
namespace BlackaddrAudio_Tuner {
//!s - START_USER_EFFECT_TYPES - put your effect types below this line before the matching END
//!e - END_USER_EFFECT_TYPES
class Tuner : public AudioStream, public Aviate::AudioEffectWrapper {
public:
static constexpr unsigned NUM_INPUTS = 1;
static constexpr unsigned NUM_OUTPUTS = 1;
// List of effect control names
enum {
Bypass_e = 0,
Volume_e = 1,
TunerSilent_e = 2,
TunerCents_e = 3,
TunerNote_e = 4,
NUM_CONTROLS
};
//!s - START_USER_CLASS_TYPES - put your custom class types below this line before the matching END
//!e - END_USER_CLASS_TYPES
Tuner();
//!s - START_USER_CONSTRUCTORS - put your custom constructors below this line before the matching END
//!e - END_USER_CONSTRUCTORS
virtual ~Tuner();
// Standard EFX interface functions - do not change these declaration
virtual void update(); // main audio processing loop function
void mapMidiControl(int parameter, int midiCC, int midiChannel = 0) override;
void processMidi(int channel, int midiCC, int value) override;
void setParam(int paramIndex, float paramValue) override;
float getUserParamValue(int paramIndex, float normalizedParamValue);
const char* getName() override;
const uint8_t* getRblk() override;
// control value set functions, must take floats between 0.0f and 1.0f - do not change these declarations
void volume(float value) override;
void tunersilent(float value);
void tunercents(float value);
void tunernote(float value);
//!s - START_USER_PUBLIC_MEMBERS - put your public members below this line before the matching END
void bypass(bool byp) override;
//!e - END_USER_PUBLIC_MEMBERS
private:
audio_block_t *m_inputQueueArray[1]; // required by AudioStream base class, array size is num inputs
int m_midiConfig[NUM_CONTROLS][2]; // stores the midi parameter mapping
// m_bypass and m_volume are already provided by the base class AudioEffectWrapper
float m_tunersilent = 0.0f;
float m_tunercents = 0.0f;
float m_tunernote = 0.0f;
audio_block_t* m_basicInputCheck(audio_block_t* inputAudioBlock, unsigned outputChannel);
//!s - START_USER_PRIVATE_MEMBERS - put your private members below this line before the matching END
// Tuner
// code excerpts from Teensy Audio Library analyze_notefreq.*
static constexpr unsigned AUDIO_GUITARTUNER_BLOCKS = 24;
uint64_t running_sum;
uint16_t tau_global;
uint64_t yin_buffer[5];
uint64_t rs_buffer[5];
int16_t AudioBuffer[AUDIO_GUITARTUNER_BLOCKS*128] __attribute__ ( ( aligned ( 4 ) ) );
uint8_t yin_idx, state;
float periodicity, yin_threshold, cpu_usage_max, data;
bool next_buffer, first_run;
volatile bool new_output = false, process_buffer;
audio_block_t *blocklist1[AUDIO_GUITARTUNER_BLOCKS];
audio_block_t *blocklist2[AUDIO_GUITARTUNER_BLOCKS];
bool m_initialized = false;
bool m_tunerEnabled = false;
bool m_silentEnabled = false;
unsigned m_skipUpdateCount = 0;
bool m_signalDetect = false;
float m_dcValueSum = 0.0f;
float m_dcValue = 0.0f;
unsigned m_dcValueCount = 0;
bool m_calibrateEnabled = false;
bool m_startupInProgress = true;
static constexpr float m_ALPHA = 0.95f;
static constexpr float m_ONE_MINUS_ALPHA = 1.0f - m_ALPHA;
float m_centsFiltered = 0.0f;
//SysPlatform::ElapsedMillis m_timer;
bool available( void );
float probability( void );
float read( void );
uint16_t estimate( uint64_t *yin, uint64_t *rs, uint16_t head, uint16_t tau );
void process( void );
void tunerReset();
void init( float thresholdIn );
static void copy_buffer(void *destination, const void *source);
//!e - END_USER_PRIVATE_MEMBERS
};
}

1
src/.gitignore vendored

@ -0,0 +1 @@
*.bak

@ -0,0 +1,550 @@
/*
* Company: Blackaddr Audio
* Effect Name: Noise Gate
* Description: A simple noise gate wtih controllable threshold, opening and closing times.
*
* This file was auto-generated by Aviate Audio Effect Creator for the Multiverse.
*/
#include <cmath>
#include "Aviate/EfxPrint.h"
#include "Aviate/TunerManager.h"
#include "Tuner.h"
using namespace Aviate;
namespace BlackaddrAudio_Tuner {
#define TUNER_A4STANDARD 440.0f
#define TUNER_FREQ_MIN 10.0f
#define TUNER_FREQ_MAX 1500.0f
#define TUNER_TOLERENCE 4.10f
constexpr unsigned STARTUP_CALIB_TIME_SECONDS = 2;
// NOTE FINDER PROTOTYPES
const char *noteToLabel (const uint8_t note);
static void noteBuildTable (const float A4);
const float noteToFreq (const uint8_t note, const uint8_t octave);
float noteFindNearest (float freqIn, uint8_t *noteOut, uint8_t *octaveOut);
float noteGetLower (uint8_t *note, uint8_t *octave);
float noteGetHigher (uint8_t *note, uint8_t *octave);
static void buildOctave (float pitchStandard, int octave);
static void noteBuildTable (const float A4);
Tuner::Tuner()
: AudioStream(NUM_INPUTS, m_inputQueueArray)
{
}
Tuner::~Tuner()
{
}
void Tuner::update(void)
{
if (!m_initialized) { init(0.1f); }
audio_block_t *inputAudioBlock = receiveWritable(); // get the next block of input samples
inputAudioBlock = m_basicInputCheck(inputAudioBlock, 0); // check for disable mode, bypass, or invalid inputs. Transmit to channel 0 in bypass
if (!inputAudioBlock) { // EFX level bypass
// Tuner reset
for (unsigned i=0; i < AUDIO_GUITARTUNER_BLOCKS; i++) {
if (blocklist1[i]) { release(blocklist1[i]); blocklist1[i] = nullptr; }
if (blocklist2[i]) { release(blocklist2[i]); blocklist2[i] = nullptr; }
}
tunerReset();
sendValueReport(TunerCents_e, -50.0f);
sendValueReport(TunerNote_e, static_cast<unsigned>(TunerNote::NO_DETECT));
return;
} // no further processing for this update() call
// You must call m_updateInputPeak() before processing the audio
m_updateInputPeak(inputAudioBlock);
//////////////////////////////
// START OF TUNER PROCESSING
//////////////////////////////
if (m_tunerEnabled) {
audio_block_t *block = allocate();
memcpy((void*)&block->data[0], (void*)&inputAudioBlock->data[0], sizeof(int16_t)*AUDIO_SAMPLES_PER_BLOCK);
if ( next_buffer ) {
blocklist1[state++] = block;
if ( !first_run && process_buffer ) process( );
} else {
blocklist2[state++] = block;
if ( !first_run && process_buffer ) process( );
}
if ( state >= AUDIO_GUITARTUNER_BLOCKS ) {
if ( next_buffer ) {
if ( !first_run && process_buffer ) process( );
for ( unsigned i = 0; i < AUDIO_GUITARTUNER_BLOCKS; i++ ) copy_buffer( AudioBuffer+( i * 0x80 ), blocklist1[i]->data );
for ( unsigned i = 0; i < AUDIO_GUITARTUNER_BLOCKS; i++ ) release( blocklist1[i] );
next_buffer = false;
} else {
if ( !first_run && process_buffer ) process( );
for ( unsigned i = 0; i < AUDIO_GUITARTUNER_BLOCKS; i++ ) copy_buffer( AudioBuffer+( i * 0x80 ), blocklist2[i]->data );
for ( unsigned i = 0; i < AUDIO_GUITARTUNER_BLOCKS; i++ ) release( blocklist2[i] );
next_buffer = true;
}
process_buffer = true;
first_run = false;
state = 0;
}
bool updated = available();
if (updated) {
float notef = read();
if (notef < TUNER_FREQ_MIN)
notef = 0.01f;
else if (notef > TUNER_FREQ_MAX)
notef = TUNER_FREQ_MAX;
// calc surrounding notes
uint8_t noteNearest;
uint8_t octaveNearest;
float delta = noteFindNearest(notef, &noteNearest, &octaveNearest);
float freqN = noteToFreq(noteNearest, octaveNearest);
uint8_t noteOutL = noteNearest;
uint8_t octaveOutL = octaveNearest;
float freqL = noteGetLower(&noteOutL, &octaveOutL);
uint8_t noteOutH = noteNearest;
uint8_t octaveOutH = octaveNearest;
float freqH = noteGetHigher(&noteOutH, &octaveOutH);
// calc note cent off from nearest note
float cent = 0.0f;
if (delta > 0.0f)
cent = ((1.0f / (freqH - freqN)) * delta) * 100.0f;
else if (delta < 0.0f)
cent = -((1.0f / (freqL - freqN)) * delta) * 100.0f;
//efxLogger.printf("Note: %s - cent: %f skipCount:%d\n", noteToLabel(noteNearest), cent, m_skipUpdateCount);
if (noteNearest < 0 || noteNearest > 11) { noteNearest = 12; } // 12 is the 'INVALID' note.
if (tunerManagerPtr) { tunerManagerPtr->sendNoteUpdate(static_cast<Aviate::TunerNote>(noteNearest), cent, !m_signalDetect); }
float newCentValue;
if (cent < -50.0f) { newCentValue = -50.0f; }
else if (cent > 50.0f) { newCentValue = 50.0f; }
else { newCentValue = cent; }
m_tunercents = m_tunercents*m_ALPHA + newCentValue*m_ONE_MINUS_ALPHA;
sendValueReport(TunerCents_e, m_tunercents);
sendValueReport(TunerNote_e, noteNearest);
m_signalDetect = true;
m_skipUpdateCount = 0;
} else if (m_skipUpdateCount > (AUDIO_SAMPLE_RATE_HZ / AUDIO_SAMPLES_PER_BLOCK) / 4) {
if (tunerManagerPtr) { tunerManagerPtr->sendNoteUpdate(TunerNote::NO_DETECT, 0.0, true /* no filtering */); }
sendValueReport(TunerCents_e, -50.0f);
sendValueReport(TunerNote_e, static_cast<unsigned>(TunerNote::NO_DETECT));
m_skipUpdateCount = 0;
m_signalDetect = false;
} else {
m_skipUpdateCount++;
}
} else {
// Tuner reset
for (unsigned i=0; i < AUDIO_GUITARTUNER_BLOCKS; i++) {
if (blocklist1[i]) { release(blocklist1[i]); blocklist1[i] = nullptr; }
if (blocklist2[i]) { release(blocklist2[i]); blocklist2[i] = nullptr; }
}
tunerReset();
sendValueReport(TunerCents_e, -50.0f);
sendValueReport(TunerNote_e, static_cast<unsigned>(TunerNote::NO_DETECT));
}
////////////////////////////
// END OF TUNER PROCESSING
////////////////////////////
for (size_t i=0; i < AUDIO_SAMPLES_PER_BLOCK; i++) {
inputAudioBlock->data[i] = m_silentEnabled ? 0 : std::round((float)inputAudioBlock->data[i] * m_volume);
}
m_updateOutputPeak(inputAudioBlock); // you must call m_upateOutputPeak() at the end of update() before transmit
transmit(inputAudioBlock);
release(inputAudioBlock);
}
void Tuner::volume(float value)
{
float volDbValue = -40.0f + (value * 50.0f); // remap the normalized value to represent -40dB to +10dB
volumeDb(volDbValue); // AudioEffectWrapper has built-in volume function in dB
}
void Tuner::tunercents(float value)
{
// this is a value monitor control, we ignore any incoming requests to set this parameter
}
void Tuner::tunernote(float value)
{
// this is a value monitor control, we ignore any incoming requests to set this parameter
}
void Tuner::tunersilent(float value)
{
m_tunersilent = value;
m_silentEnabled = m_tunersilent ? true : false;
}
void Tuner::bypass(bool value)
{
m_bypass = value;
m_tunerEnabled = !(value > 0.0f);
if (tunerManagerPtr) { tunerManagerPtr->setTunerMode(m_tunerEnabled); }
}
//////////
// TUNER
//////////
#define HALF_BLOCKS AUDIO_GUITARTUNER_BLOCKS * 64
#define NOTE_OCTAVES 6 // number of octaves covered
enum _notes {
NOTE_C = 0,
NOTE_C_SHARP,
NOTE_D,
NOTE_D_SHARP,
NOTE_E,
NOTE_F,
NOTE_F_SHARP,
NOTE_G,
NOTE_G_SHARP,
NOTE_A,
NOTE_A_SHARP,
NOTE_B,
NO_DETECT
};
typedef struct {
uint8_t note;
char label[3];
float freq[NOTE_OCTAVES];
}notetable_t;
static notetable_t notetable[12] = {
{NOTE_C, "C", {}},
{NOTE_C_SHARP, "C#",{}},
{NOTE_D, "D", {}},
{NOTE_D_SHARP, "D#",{}},
{NOTE_E, "E", {}},
{NOTE_F, "F", {}},
{NOTE_F_SHARP, "F#",{}},
{NOTE_G, "G", {}},
{NOTE_G_SHARP, "G#",{}},
{NOTE_A, "A", {}},
{NOTE_A_SHARP, "A#",{}},
{NOTE_B, "B", {}},
};
/**
* Copy internal blocks of data to class buffer
*
* @param destination destination address
* @param source source address
*/
void Tuner::copy_buffer(void *destination, const void *source) {
const uint16_t *src = ( const uint16_t * )source;
uint16_t *dst = ( uint16_t * )destination;
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) *dst++ = (*src++);
}
/**
* Start the Yin algorithm
*
* TODO: Significant speed up would be to use spectral domain to find fundamental frequency.
* This paper explains: https://aubio.org/phd/thesis/brossier06thesis.pdf -> Section 3.2.4
* page 79. Might have to downsample for low fundmental frequencies because of fft buffer
* size limit.
*/
void Tuner::process( void ) {
const int16_t *p;
p = AudioBuffer;
uint16_t cycles = 64;
uint16_t tau = tau_global;
do {
uint16_t x = 0;
uint64_t sum = 0;
do {
int16_t current, lag, delta;
lag = *( ( int16_t * )p + ( x+tau ) );
current = *( ( int16_t * )p+x );
delta = ( current-lag );
sum += delta * delta;
x += 4;
lag = *( ( int16_t * )p + ( x+tau ) );
current = *( ( int16_t * )p+x );
delta = ( current-lag );
sum += delta * delta;
x += 4;
lag = *( ( int16_t * )p + ( x+tau ) );
current = *( ( int16_t * )p+x );
delta = ( current-lag );
sum += delta * delta;
x += 4;
lag = *( ( int16_t * )p + ( x+tau ) );
current = *( ( int16_t * )p+x );
delta = ( current-lag );
sum += delta * delta;
x += 4;
} while ( x < HALF_BLOCKS );
uint64_t rs = running_sum;
rs += sum;
yin_buffer[yin_idx] = sum*tau;
rs_buffer[yin_idx] = rs;
running_sum = rs;
yin_idx = ( ++yin_idx >= 5 ) ? 0 : yin_idx;
tau = estimate( yin_buffer, rs_buffer, yin_idx, tau );
if ( tau == 0 ) {
process_buffer = false;
new_output = true;
yin_idx = 1;
running_sum = 0;
tau_global = 1;
return;
}
} while ( --cycles );
if ( tau >= HALF_BLOCKS ) {
process_buffer = false;
new_output = false;
yin_idx = 1;
running_sum = 0;
tau_global = 1;
return;
}
tau_global = tau;
}
/**
* check the sampled data for fundamental frequency
*
* @param yin buffer to hold sum*tau value
* @param rs buffer to hold running sum for sampled window
* @param head buffer index
* @param tau lag we are currently working on gets incremented
*
* @return tau
*/
uint16_t Tuner::estimate( uint64_t *yin, uint64_t *rs, uint16_t head, uint16_t tau ) {
const uint64_t *y = ( uint64_t * )yin;
const uint64_t *r = ( uint64_t * )rs;
uint16_t _tau, _head;
const float thresh = yin_threshold;
_tau = tau;
_head = head;
if ( _tau > 4 ) {
uint16_t idx0, idx1, idx2;
idx0 = _head;
idx1 = _head + 1;
idx1 = ( idx1 >= 5 ) ? 0 : idx1;
idx2 = head + 2;
idx2 = ( idx2 >= 5 ) ? 0 : idx2;
float s0, s1, s2;
s0 = ( ( float )*( y+idx0 ) / *( r+idx0 ) );
s1 = ( ( float )*( y+idx1 ) / *( r+idx1 ) );
s2 = ( ( float )*( y+idx2 ) / *( r+idx2 ) );
if ( s1 < thresh && s1 < s2 ) {
uint16_t period = _tau - 3;
periodicity = 1 - s1;
data = period + 0.5f * ( s0 - s2 ) / ( s0 - 2.0f * s1 + s2 );
return 0;
}
}
return _tau + 1;
}
void Tuner::tunerReset() {
process_buffer = false;
periodicity = 0.0f;
next_buffer = true;
running_sum = 0;
tau_global = 1;
first_run = true;
yin_idx = 1;
state = 0;
data = 0.0f;
}
void Tuner::init( float thresholdIn ) {
tunerReset();
yin_threshold = thresholdIn;
process_buffer = false;
noteBuildTable(TUNER_A4STANDARD);
m_initialized = true;
}
/**
* available
*
* @return true if data is ready else false
*/
bool Tuner::available( void ) {
bool flag = new_output;
if ( flag ) new_output = false;
return flag;
}
/**
* read processes the data samples for the Yin algorithm.
*
* @return frequency in hertz
*/
float Tuner::read( void ) {
float d = data;
return AUDIO_SAMPLE_RATE_HZ / d;
}
/**
* Periodicity of the sampled signal from Yin algorithm from read function.
*
* @return periodicity
*/
float Tuner::probability( void ) {
float p = periodicity;
return p;
}
// NOTE FINDER
const char *noteToLabel (const uint8_t note)
{
return notetable[note].label;
}
const float noteToFreq (const uint8_t note, const uint8_t octave)
{
return notetable[note].freq[octave];
}
float noteFindNearest (float freqIn, uint8_t *noteOut, uint8_t *octaveOut)
{
float delta = 999999.0f;
uint8_t note = -1;
uint8_t octave = -1;
for (int n = 0; n < 12; n++){
for (int o = 0; o < NOTE_OCTAVES; o++){
const float diff = fabsf(noteToFreq(n, o) - freqIn);
if (diff < delta){
delta = diff;
note = n;
octave = o;
}
}
}
if (note != -1){
*noteOut = note;
*octaveOut = octave;
if (freqIn > noteToFreq(note, octave))
return delta;
else
return -delta;
}else{
*noteOut = -1;
return 0.0f;
}
}
float noteGetLower (uint8_t *note, uint8_t *octave)
{
int8_t octaveLower = *octave;
int8_t noteLower = *note - 1;
if (noteLower < NOTE_C){
noteLower = NOTE_B;
if (--octaveLower < 0)
noteLower = -1;
}
if (noteLower != -1){
float freq = noteToFreq(noteLower, octaveLower);
*note = noteLower;
*octave = octaveLower;
return freq;
}
return 0.0f;
}
float noteGetHigher (uint8_t *note, uint8_t *octave)
{
int8_t octaveHigher = *octave;
int8_t noteHigher = *note + 1;
if (noteHigher > NOTE_B){
noteHigher = NOTE_C;
if (++octaveHigher >= NOTE_OCTAVES){
octaveHigher = -1;
noteHigher = -1;
}
}
if (noteHigher != -1){
float freq = noteToFreq(noteHigher, octaveHigher);
*note = noteHigher;
*octave = octaveHigher;
return freq;
}
return 0.0f;
}
static void buildOctave (float pitchStandard, int octave)
{
octave -= 4;
if (octave < -4){
return;
}else if (octave < 0){
for (int d = octave+1; d <= 0; d++)
pitchStandard /= 2.0f;
}else if (octave > 0){
for (int d = 0; d < octave; d++)
pitchStandard *= 2.0f;
}
int noteIdx = 11;
// A# and B
for (float n = 2; n >= 1; n -= 1.0f){
float freq = pitchStandard * exp2f(n/12.0f);
notetable[noteIdx--].freq[octave+4] = freq;
}
// C to A
for (float n = 0; n <= 9; n += 1.0f){
float freq = pitchStandard / exp2f(n/12.0f);
notetable[noteIdx--].freq[octave+4] = freq;
}
}
static void noteBuildTable (const float A4)
{
for (int o = 0; o < NOTE_OCTAVES; o++){
buildOctave(A4, o);
}
}
}

@ -0,0 +1,127 @@
/*
* Company: Blackaddr Audio
* Effect Name: Tuner
* Description: A noise gate wtih controllable threshold, opening and closing times and DC removal filter. Basic tuner included.
*
* This file was auto-generated by Aviate Audio Effect Creator for the Multiverse.
*/
#include <cmath>
#include "Aviate/LibBasicFunctions.h"
#include "Tuner.h"
using namespace Aviate;
namespace BlackaddrAudio_Tuner {
void Tuner::mapMidiControl(int parameter, int midiCC, int midiChannel)
{
if (parameter >= NUM_CONTROLS) {
return ; // Invalid midi parameter
}
m_midiConfig[parameter][MIDI_CHANNEL] = midiChannel;
m_midiConfig[parameter][MIDI_CONTROL] = midiCC;
}
void Tuner::setParam(int paramIndex, float paramValue)
{
switch(paramIndex) {
case 0 : bypass( (paramValue - 0.000000) / (1.000000 - 0.000000) ); break;
case 1 : volume( (paramValue - 0.000000) / (10.000000 - 0.000000) ); break;
case 2 : tunersilent( (paramValue - 0.000000) / (1.000000 - 0.000000) ); break;
case 3 : tunercents( (paramValue - -50.000000) / (50.000000 - -50.000000) ); break;
case 4 : tunernote( (paramValue - 0.000000) / (12.000000 - 0.000000) ); break;
default : break;
}
}
float Tuner::getUserParamValue(int paramIndex, float normalizedParamValue)
{
switch(paramIndex) {
case 0 : return ( ((1.000000 - 0.000000) * normalizedParamValue) + 0.000000 ); // bypass
case 1 : return ( ((10.000000 - 0.000000) * normalizedParamValue) + 0.000000 ); // volume
case 2 : return ( ((1.000000 - 0.000000) * normalizedParamValue) + 0.000000 ); // tunersilent
case 3 : return ( ((50.000000 - -50.000000) * normalizedParamValue) + -50.000000 ); // tunercents
case 4 : return ( ((12.000000 - 0.000000) * normalizedParamValue) + 0.000000 ); // tunernote
default : return 0.0f;
}
}
void Tuner::processMidi(int channel, int control, int value)
{
float val = (float)value / 127.0f;
if ((m_midiConfig[Bypass_e][MIDI_CHANNEL] == channel) && (m_midiConfig[Bypass_e][MIDI_CONTROL] == control)) {
bypass(val);
return;
}
if ((m_midiConfig[Volume_e][MIDI_CHANNEL] == channel) && (m_midiConfig[Volume_e][MIDI_CONTROL] == control)) {
volume(val);
return;
}
if ((m_midiConfig[TunerSilent_e][MIDI_CHANNEL] == channel) && (m_midiConfig[TunerSilent_e][MIDI_CONTROL] == control)) {
tunersilent(val);
return;
}
if ((m_midiConfig[TunerCents_e][MIDI_CHANNEL] == channel) && (m_midiConfig[TunerCents_e][MIDI_CONTROL] == control)) {
tunercents(val);
return;
}
if ((m_midiConfig[TunerNote_e][MIDI_CHANNEL] == channel) && (m_midiConfig[TunerNote_e][MIDI_CONTROL] == control)) {
tunernote(val);
return;
}
}
audio_block_t* Tuner::m_basicInputCheck(audio_block_t* inputAudioBlock, unsigned outputChannel)
{
// Check if effect is disabled
if (m_enable == false) {
// do not transmit or process any audio, return as quickly as possible after releasing the inputs
if (inputAudioBlock) { release(inputAudioBlock); }
return nullptr; // disabled, no further EFX processing in update()
} // end of enable check
// check if effect is in bypass
if (m_bypass == true) {
// drive input directly to the specified output. ie. bypass
if (inputAudioBlock != nullptr) {
// valid input, drive to outputChannel if specified
if (outputChannel >= 0) {
transmit(inputAudioBlock, outputChannel); // drive to specified output
}
release(inputAudioBlock); // release the input block as we are done with it
} else { // invalid input block, allocate a block and drive silence if specified
if (outputChannel >= 0) {
audio_block_t* silenceBlock = allocate();
if (silenceBlock) {
clearAudioBlock(silenceBlock); // create silence in the buffer
transmit(silenceBlock, outputChannel);
release(silenceBlock);
}
}
}
return nullptr; // bypassed, no further EFX processing in update()
} // end of bypass check
// If not disabled or bypassed, create silence if the input block is invalid then
// return the valid audio block so update() can continue.
if (inputAudioBlock == nullptr) {
inputAudioBlock = allocate();
if (inputAudioBlock == nullptr) { return nullptr; } // check if allocate was unsuccessful
// else
clearAudioBlock(inputAudioBlock);
}
return inputAudioBlock; // inputAudioBLock is valid and ready for update() processing
}
const uint8_t rblk[256] = TEENSY_AUDIO_BLOCK;
const uint8_t* Tuner::getRblk() { return rblk; }
static constexpr char PROGMEM Tuner_name[] = {0x42, 0x6c, 0x61, 0x63, 0x6b, 0x61, 0x64, 0x64, 0x72, 0x20, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x3a, 0x54, 0x75, 0x6e, 0x65, 0x72, 0x0};
const char* Tuner::getName() { return Tuner_name; }
}
Loading…
Cancel
Save