Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions user/NCO/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Project Name
TARGET = PodNcoMain

# Sources
CPP_SOURCES = nco.cpp harmonicNco.cpp PodNcoMain.cpp

# Library Locations
LIBDAISY_DIR = ../../libdaisy
DAISYSP_DIR = ../../DaisySP

# Core location, and generic Makefile.
SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core
include $(SYSTEM_FILES_DIR)/Makefile
226 changes: 226 additions & 0 deletions user/NCO/PodNcoMain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#include "daisysp.h"
#include "daisy_pod.h"
#include "nco.h"
#include "harmonicNco.h"

#define MIDI_C4 60
#define MIDI_A4 69
#define FREQ_A4 440
#define NUM_MIDI_NOTES_PER_OCTAVE 12

using namespace daisysp;
using namespace daisy;

enum NoteInterval
{
ROOT,
THIRD,
FIFTH,
NUM_INTERVALS
};

enum ChordSequence
{
CHORD_I,
CHORD_V,
CHORD_VI_M,
CHORD_IV,
CHORD_SEQ_LEN
};

enum BasicChordType
{
MAJOR,
MINOR
};

static void UpdateChord(uint16_t root_midi_note, BasicChordType chord_type);
static void AdvanceChord(uint16_t root_midi_note, int direction);
static int getButtonDir(void);

static DaisyPod pod;
static HarmonicNCO harmonicNco[NUM_INTERVALS];
static float amp = 0.5f;
static uint16_t s_key_root_note = MIDI_C4;

static float harmonicAmps[] =
{
1.0,
0.7,
0.6,
0.3,
0.0,
0.0,
0.0,
0.0
};

static float harmonicPhases[] =
{
0.0,
0.9,
0.125,
0.45,
0.65,
0.95,
0.55,
0.6
};

static void AudioCallback(AudioHandle::InterleavingInputBuffer in,
AudioHandle::InterleavingOutputBuffer out,
size_t size)
{
float sample = 0;
int encoder_dir = 0;
int button_dir = 0;

pod.ProcessAllControls();

button_dir = getButtonDir();

if (button_dir)
{
AdvanceChord(s_key_root_note, button_dir);
}

encoder_dir = pod.encoder.Increment();

if (encoder_dir)
{
s_key_root_note += encoder_dir;
AdvanceChord(s_key_root_note, 0);
}

// Audio Loop
for (size_t ndx = 0; ndx < size; ndx += 2)
{
for (uint8_t note = ROOT; note < NUM_INTERVALS; note++)
{
sample += (amp / NUM_INTERVALS) * harmonicNco[note].NextSample();
}
// left out
out[ndx] = sample;
// right out
out[ndx + 1] = sample;

sample = 0;
}
}

static void InitChordNcos(uint16_t root_midi_note, uint32_t sample_rate)
{
for (uint8_t note = ROOT; note < NUM_INTERVALS; note++)
{
harmonicNco[note].SetSampleRate(sample_rate);
harmonicNco[note].SetAmplitudes(harmonicAmps);
harmonicNco[note].SetPhases(harmonicPhases);
}

// Set the frequencies of each NCO
UpdateChord(root_midi_note, MAJOR);
}

// Sets the NCO's to play a major chord
static void UpdateChord(uint16_t root_midi_note, BasicChordType chord_type)
{
float freq = 0;
uint16_t midi_note = root_midi_note;

for (uint8_t note = ROOT; note < NUM_INTERVALS; note++)
{
// Apply offset based on interval
midi_note = root_midi_note + (3 * note);

if (chord_type == MAJOR)
{
midi_note += (note != ROOT) ? 1 : 0;
}
else
{
midi_note += (note == FIFTH) ? 1 : 0;
}
// Keep chord notes w/in an octave of the key root note
midi_note = ((midi_note - s_key_root_note) % NUM_MIDI_NOTES_PER_OCTAVE) + s_key_root_note;

freq = mtof(midi_note);
harmonicNco[note].SetFrequency(freq);
}
}

static void AdvanceChord(uint16_t root_midi_note, int direction)
{
static uint8_t current_chord = CHORD_I;

// Advance to next chord
if (direction > 0)
{
current_chord = (current_chord + 1) % CHORD_SEQ_LEN;
}
else if (direction < 0)
{
current_chord = (current_chord + CHORD_SEQ_LEN - 1) % CHORD_SEQ_LEN;
}

switch (current_chord)
{
case CHORD_I:
UpdateChord(root_midi_note, MAJOR);
break;

case CHORD_V:
UpdateChord((root_midi_note + 7), MAJOR);
break;

case CHORD_VI_M:
UpdateChord((root_midi_note + 9), MINOR);
break;

case CHORD_IV:
UpdateChord((root_midi_note + 5), MAJOR);
break;

default:
break;
}
}

static int getButtonDir(void)
{
bool button_1_state = pod.button1.RisingEdge();
bool button_2_state = pod.button2.RisingEdge();
int button_dir = 0;

if (button_1_state && !button_2_state)
{
button_dir = -1;
}
else if (!button_1_state && button_2_state)
{
button_dir = 1;
}
else
{
button_dir = 0;
}

return button_dir;
}

int main(void)
{
// Initialize pod hardware and oscillator daisysp module
float sample_rate;

pod.Init();
sample_rate = pod.AudioSampleRate();

InitChordNcos(s_key_root_note, (uint32_t)sample_rate);

// Start audio callback
pod.StartAudio(AudioCallback);


while(1)
; // Infinite Loop
}
82 changes: 82 additions & 0 deletions user/NCO/harmonicNco.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "harmonicNco.h"

// HarmonicNCO default constructor
HarmonicNCO::HarmonicNCO()
{
uint8_t nHarmonic;

for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
nco[nHarmonic].SetSampleRate(NCO::DEFAULT_FS);
amp[nHarmonic] = 1.0;
}
}

HarmonicNCO::~HarmonicNCO() {
// TODO
}

void HarmonicNCO::SetSampleRate(uint32_t sample_freq)
{
uint8_t nHarmonic;

for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
nco[nHarmonic].SetSampleRate(sample_freq);
}
}

void HarmonicNCO::SetFrequency(float freq)
{
uint8_t nHarmonic;
float harmonicFreq = 0.0;

for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
harmonicFreq = (nHarmonic + 1) * freq;
nco[nHarmonic].SetFrequency(harmonicFreq);
}
}

void HarmonicNCO::SetAmplitudes(float *amplitudes)
{
uint8_t nHarmonic;

if (amplitudes)
{
for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
amp[nHarmonic] = amplitudes[nHarmonic];
}
}
}

void HarmonicNCO::SetPhases(float *phases)
{
uint8_t nHarmonic;

if (phases)
{
for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
nco[nHarmonic].SetPhase(phases[nHarmonic]);
}
}
}

float HarmonicNCO::NextSample()
{
uint8_t nHarmonic;
float sample = 0.0;
float ampTotal = 0.0;

for (nHarmonic = 0; nHarmonic < NUM_HARMONICS; nHarmonic++)
{
sample += (amp[nHarmonic] * nco[nHarmonic].NextSample());
ampTotal += amp[nHarmonic];
}
// Normalize amplitude of sample
sample /= ampTotal;

return sample;
}
33 changes: 33 additions & 0 deletions user/NCO/harmonicNco.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// harmonicNco.h
#pragma once
#ifndef HARMONIC_NCO_H
#define HARMONIC_NCO_H

#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "daisysp.h"
#include "nco.h"

using namespace std;

class HarmonicNCO
{
public:
static const uint8_t NUM_HARMONICS = 8;

HarmonicNCO();
~HarmonicNCO();

void SetSampleRate(uint32_t sample_freq);
void SetFrequency(float freq);
void SetAmplitudes(float *amplitudes);
void SetPhases(float *phases);
float NextSample();

private:
NCO nco[NUM_HARMONICS]; // The NCO for each harmonic
float amp[NUM_HARMONICS]; // The amplitude for each harmonic
};

#endif
Loading