arduino_midi_player/Midi/arduino-midi-player-master/arduino-midi-player.ino
2025-03-24 14:30:56 +08:00

136 lines
3.4 KiB
C++

/*
* Arduino MIDI Player
*
* Setup Arduino and use timer2 to synthesize and output sine wave
*
* 2016 by ilufang
*/
/*
* Part of this file contains code modified/referenced from
* http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-dds-sinewave-generator/
*
* DDS Sine Generator mit ATMEGS 168
* Timer2 generates the 31250 KHz Clock Interrupt
*
* KHM 2009 / Martin Nawrath
* Kunsthochschule fuer Medien Koeln
* Academy of Media Arts Cologne
*/
#include "avr/pgmspace.h"
#include "midi2wave.h"
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define POW2_32 4294967296
#define refclk 31376.6 // Reference clock
// variables in interrupt service
volatile int timer_tick = 0; // seeking position in wave
volatile unsigned char timer_micro = 0; // timing counter in microseconds
volatile unsigned short timer_milli = 0; // timing counter in milliseconds
volatile unsigned long phaccu_1, phaccu_2, phaccu_3, phaccu_4, phaccu_5, phaccu_6, phaccu_7, phaccu_8; // phase accumulator
volatile unsigned long tword_m_1, tword_m_2, tword_m_3, tword_m_4, tword_m_5, tword_m_6, tword_m_7, tword_m_8; // DDS tuning word m
unsigned long phaccu_all;
void setup()
{
Serial.begin(9600);
Serial.println("Hello");
for (int i = 2; i <= 8; ++i)
pinMode(i,OUTPUT); // LED output
pinMode(11,OUTPUT); // PWM Wave output
setupMidi();
setupTimer2();
}
void loop()
{
while(true) {
if (timer_milli > event_length) { // wait for the next midi event
cbi (TIMSK2,TOIE2);
loadNextEvent();
// calculate new DDS tuning word
tword_m_1=POW2_32*PIANO(active_keys[0])/refclk;
tword_m_2=POW2_32*PIANO(active_keys[1])/refclk;
tword_m_3=POW2_32*PIANO(active_keys[2])/refclk;
tword_m_4=POW2_32*PIANO(active_keys[3])/refclk;
if (!tword_m_1) phaccu_1 = 0;
if (!tword_m_2) phaccu_2 = 0;
if (!tword_m_3) phaccu_3 = 0;
if (!tword_m_4) phaccu_4 = 0;
timer_milli=0;
sbi (TIMSK2,TOIE2);
}
}
}
/*
* timer2 setup
*
* set pre-scaler to 1, PWM mode to phase correct PWM, 16000000/510 = 31372.55 Hz clock
*/
void setupTimer2() {
// Timer2 Clock Pre-scaler to : 1
sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
cbi (TCCR2B, CS22);
// Timer2 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0); // clear Compare Match
sbi (TCCR2A, COM2A1);
sbi (TCCR2A, WGM20); // Mode 1 / Phase Correct PWM
cbi (TCCR2A, WGM21);
cbi (TCCR2B, WGM22);
// initialize DDS tuning word
tword_m_1=0;
tword_m_2=0;
tword_m_3=0;
tword_m_4=0;
// disable Timer0 interrupts to avoid timing distortion
cbi (TIMSK0,TOIE0);
// start Timer2!
sbi (TIMSK2,TOIE2);
}
/*
* Timer2 Interrupt Service
*
* Running at 31372,550 KHz = 32uSec
* this is the timebase REFCLOCK for the DDS generator
* FOUT = (M (REFCLK)) / (2 exp 32)
* runtime : 8 microseconds ( inclusive push and pop)
*/
ISR(TIMER2_OVF_vect) {
// soft DDS, phase accumulator with 32 bits
phaccu_1 += tword_m_1;
phaccu_2 += tword_m_2;
phaccu_3 += tword_m_3;
phaccu_4 += tword_m_4;
phaccu_5 += tword_m_5;
// use upper 8 bits for phase accumulator as frequency information
int phaccu_all = sine[phaccu_1>>24];
phaccu_all += sine[phaccu_2>>24];
phaccu_all += sine[phaccu_3>>24];
phaccu_all += sine[phaccu_4>>24];
phaccu_all += sine[phaccu_5>>24];
// Write to PWM port 11
OCR2A = phaccu_all/KEYBUF_SIZE;
// Increment timing counter
if(++timer_micro == 31) {
++timer_milli;
timer_micro=0;
}
}