Untitled
unknown
c_cpp
10 months ago
6.3 kB
9
Indexable
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <MIDI.h>
#include <vector>
#define TOTAL_CHANNELS 5 // Number of waveform types
#define VOICES_PER_CHANNEL 3 // Number of voices per waveform
#define TOTAL_VOICES (TOTAL_CHANNELS * VOICES_PER_CHANNEL) // Total voices
// Audio objects
AudioSynthWaveform waveform[TOTAL_VOICES]; // Waveform generators
AudioEffectEnvelope envelope[TOTAL_VOICES]; // Envelopes for waveforms
AudioMixer4 mixers[TOTAL_CHANNELS]; // Mixer per waveform
AudioMixer4 mainMixer; // Main mixer
AudioOutputI2S i2s1; // Audio output
AudioConnection* patchCords[TOTAL_VOICES * 2 + TOTAL_CHANNELS]; // Dynamic patch cords
AudioConnection patchCordOutL(mainMixer, 0, i2s1, 0);
AudioConnection patchCordOutR(mainMixer, 0, i2s1, 1);
AudioControlSGTL5000 audioShield;
// Waveform types for each channel
const char* waveformNames[TOTAL_CHANNELS] = {"SINE", "SQUARE", "SAWTOOTH", "TRIANGLE", "PULSE"};
const int waveformTypes[TOTAL_CHANNELS] = {
WAVEFORM_SINE, WAVEFORM_SQUARE, WAVEFORM_SAWTOOTH,
WAVEFORM_TRIANGLE, WAVEFORM_PULSE};
// Per-channel envelope settings (Attack, Decay, Sustain, Release)
const float envelopeSettings[TOTAL_CHANNELS][4] = {
{10.0, 50.0, 0.8, 100.0}, // Sine
{20.0, 40.0, 0.6, 200.0}, // Square
{5.0, 30.0, 0.7, 50.0}, // Sawtooth
{15.0, 60.0, 0.5, 150.0}, // Triangle
{10.0, 20.0, 0.9, 80.0} // Pulse
};
// Tracks active notes for each voice
struct Voice {
bool active;
int channel;
int note;
int velocity;
};
Voice voices[TOTAL_VOICES];
float pwmValue = 0.5; // Default PWM value for pulse waveform (50% duty cycle)
void setup() {
// Set up MIDI callbacks
usbMIDI.setHandleNoteOn(handleNoteOnUSB);
usbMIDI.setHandleNoteOff(handleNoteOffUSB);
usbMIDI.setHandleControlChange(handleControlChangeUSB);
usbMIDI.setHandleStop(handleStopEvent);
// Audio setup
AudioMemory(80);
audioShield.enable();
audioShield.volume(0.8);
// Configure the audio shield for line-level output
audioShield.lineOutLevel(13); // Set line-out level to maximum
int patchCordIndex = 0;
// Initialize voices and assign them to mixers
for (int i = 0; i < TOTAL_VOICES; i++) {
int channel = i / VOICES_PER_CHANNEL;
waveform[i].begin(0.5, 440, waveformTypes[channel]);
// Apply per-channel envelope settings
envelope[i].attack(envelopeSettings[channel][0]);
envelope[i].decay(envelopeSettings[channel][1]);
envelope[i].sustain(envelopeSettings[channel][2]);
envelope[i].release(envelopeSettings[channel][3]);
// Set default PWM for pulse waveform (channel 5)
if (waveformTypes[channel] == WAVEFORM_PULSE) {
waveform[i].pulseWidth(pwmValue);
}
patchCords[patchCordIndex++] = new AudioConnection(waveform[i], envelope[i]);
patchCords[patchCordIndex++] = new AudioConnection(envelope[i], 0, mixers[channel], i % VOICES_PER_CHANNEL);
voices[i] = {false, channel, -1, 0}; // Initialize voice state
}
// Connect mixers to the main mixer
for (int i = 0; i < TOTAL_CHANNELS; i++) {
patchCords[patchCordIndex++] = new AudioConnection(mixers[i], 0, mainMixer, i);
mixers[i].gain(0, 0.8); // Set default gain
}
// Debug
Serial.begin(9600);
Serial.println("MIDI Synth Ready with PWM Modulation for Channel 5!");
}
void loop() {
// Process incoming MIDI messages
usbMIDI.read();
}
void handleNoteOnUSB(byte channel, byte note, byte velocity) {
if (channel < 1 || channel > TOTAL_CHANNELS) {
Serial.printf("Invalid MIDI Channel: %d\n", channel);
return; // Ignore invalid channels
}
int channelIndex = channel - 1;
// Find a free voice for this channel
for (int i = channelIndex * VOICES_PER_CHANNEL; i < (channelIndex + 1) * VOICES_PER_CHANNEL; i++) {
if (!voices[i].active) {
voices[i].active = true;
voices[i].note = note;
voices[i].velocity = velocity;
float frequency = pow(2, (note - 69) / 12.0) * 440.0; // MIDI Note to Hz
float amplitude = velocity / 127.0 * 0.5; // Scale amplitude based on velocity
waveform[i].frequency(frequency);
waveform[i].amplitude(amplitude);
// Apply PWM for pulse waveform
if (waveformTypes[channelIndex] == WAVEFORM_PULSE) {
waveform[i].pulseWidth(pwmValue);
}
envelope[i].noteOn();
Serial.printf("Channel %d: Voice %d Note On: Note=%d, Freq=%.2f Hz, Vel=%d, Waveform=%s, PWM=%.2f\n",
channel, i, note, frequency, velocity, waveformNames[channelIndex], pwmValue);
return;
}
}
Serial.printf("Channel %d: No free voices available!\n", channel);
}
void handleNoteOffUSB(byte channel, byte note, byte velocity) {
if (channel < 1 || channel > TOTAL_CHANNELS) {
Serial.printf("Invalid MIDI Channel: %d\n", channel);
return; // Ignore invalid channels
}
int channelIndex = channel - 1;
// Find the voice playing this note
for (int i = channelIndex * VOICES_PER_CHANNEL; i < (channelIndex + 1) * VOICES_PER_CHANNEL; i++) {
if (voices[i].active && voices[i].note == note) {
voices[i].active = false;
envelope[i].noteOff();
Serial.printf("Channel %d: Voice %d Note Off: Note=%d, Waveform=%s\n",
channel, i, note, waveformNames[channelIndex]);
return;
}
}
}
void handleControlChangeUSB(byte channel, byte control, byte value) {
if (channel != 5 || control != 1) return; // Only apply mod wheel (CC1) to channel 5
// Scale MIDI value (0–127) to PWM range (0.05–0.95)
pwmValue = map(value, 0, 127, 5, 95) / 100.0;
// Apply the new PWM value to all pulse voices
for (int i = (TOTAL_CHANNELS - 1) * VOICES_PER_CHANNEL; i < TOTAL_VOICES; i++) {
waveform[i].pulseWidth(pwmValue);
}
Serial.printf("Mod Wheel (Channel 5): PWM Updated to %.2f (MIDI Value: %d)\n", pwmValue, value);
}
void handleStopEvent() {
Serial.println("MIDI Stop Event Received: Killing all notes...");
killAllNotes();
}
void killAllNotes() {
for (int i = 0; i < TOTAL_VOICES; i++) {
if (voices[i].active) {
voices[i].active = false;
envelope[i].noteOff();
}
}
Serial.println("All voices stopped.");
}Editor is loading...
Leave a Comment