The software that [Deater] provides, extends the functionality of the project beyond the chiptunes player. There is a program to use the devices as an alarm clock, CPU meter, electronic organ and even a playable version of Tetris as seen in the demo video below. The blog post is very informative and shows progress in a chronological fashion with pictures of the design at various stages of development. [Deater] provides a full set of instructions as well as the schematic along with code posted on GitHub.
This midi sound module was built as a gift for my much more musically talented cousin and was an absolute blast to put together. The two AY-3-8910 struggle with more than 6 voices but that is to be expected. Maybe I'll build a 3 chip - 9 voice version next? :)
Chiptunes Via USB MIDI With The AY-3-8910
I give you a code modified with Serial Monitor, and startup play a little sound to check:_______________________________________________________________/* * Placed in the public domain by the author, Ian Harvey, 2018. * * Note there is NO WARRANTY. */#include #include "MIDIUSB.h"typedef unsigned short int ushort;typedef unsigned char note_t;#define NOTE_OFF 0typedef unsigned char midictrl_t;// Pin driver ---------------------------------------------static const int dbus[8] = 2,3,4,5,6,7,8,A0 ;static const ushort BC1 = 10, BC2 = 16, BDIR = 14, nRESET = 15, clkOUT = 9;static const ushort DIVISOR = 7; // Set for 1MHz clockstatic void clockSetup() // Timer 1 setup for Mega32U4 devices // // Use CTC mode: WGM13..0 = 0100 // Toggle OC1A on Compare Match: COM1A1..0 = 01 // Use ClkIO with no prescaling: CS12..0 = 001 // Interrupts off: TIMSK0 = 0 // OCR0A = interval value TCCR1A = (1 starting state// AY-3-8910 driver ---------------------------------------class PSGRegs{public: enum TONEALOW=0, TONEAHIGH, TONEBLOW, TONEBHIGH, TONECLOW, TONECHIGH, NOISEGEN, MIXER, TONEAAMPL, TONEBAMPL, TONECAMPL, ENVLOW, ENVHIGH, ENVSHAPE, IOA, IOB ; unsigned char regs[16]; unsigned char lastregs[16]; void init() for (int i=0; i 0) psg.setOff(m_chan); m_ampl = AMPL_MAX; m_adsr = 'X'; m_decay = fxp.timer; psg.setEnvelope(fxp.envdecay, 9); psg.setToneAndNoise(m_chan, fxp.tonefreq, fxp.noisefreq, 31); void stop() if ( m_adsr == 'X' ) return; // Will finish when ready... if ( m_ampl > 0 ) m_adsr = 'R'; else psg.setOff(m_chan); void update100Hz( ) if ( m_ampl == 0 ) return; switch( m_adsr ) case 'D': m_ampl -= m_decay; if ( m_ampl 0 ) m_fxp.tonefreq += m_fxp.freqdecay; psg.setToneAndNoise(m_chan, m_fxp.tonefreq, m_fxp.noisefreq, 31); m_ampl -= m_decay; if ( m_ampl 0 ) psg.setTone(m_chan, m_pitch, m_ampl >> 6); else psg.setOff(m_chan); bool isPlaying() return (m_ampl > 0); void kill() psg.setOff(m_chan); m_ampl = 0; ;const ushort MAX_VOICES = 3;static Voice voices[MAX_VOICES];// MIDI synthesiser ---------------------------------------// Deals with assigning note on/note off to voicesstatic const uint8_t PERC_CHANNEL = 10;static const note_t PERC_MIN = 35, PERC_MAX = 50;static const struct FXParams perc_params[PERC_MAX-PERC_MIN+1] = // Mappings are from the General MIDI spec at -old/item/gm-level-1-sound-set // Params are: noisefreq, tonefreq, envdecay, freqdecay, timer 9, 900, 800, 40, 50 , // 35 Acoustic bass drum 8, 1000, 700, 40, 50 , // 36 (C) Bass Drum 1 4, 0, 300, 0, 80 , // 37 Side Stick 6, 0, 1200, 0, 30 , // 38 Acoustic snare 5, 0, 1500, 0, 90 , // 39 (D#) Hand clap 6, 400, 1200, 11, 30 , // 40 Electric snare 16, 700, 800, 20, 30 , // 41 Low floor tom 0, 0, 300, 0, 80 , // 42 Closed Hi Hat 16, 400, 800, 13, 30 , // 43 (G) High Floor Tom 0, 0, 600, 0, 50 , // 44 Pedal Hi-Hat 16, 800, 1400, 30, 25 , // 45 Low Tom 0, 0, 800, 0, 40 , // 46 Open Hi-Hat 16, 600, 1400, 20, 25 , // 47 (B) Low-Mid Tom 16, 450, 1500, 15, 22 , // 48 Hi-Mid Tom 1, 0, 1800, 0, 25 , // 49 Crash Cymbal 1 16, 300, 1500, 10, 22 , // 50 High Tom;static const int REQ_MAP_SIZE = (N_NOTES+7) / 8;static uint8_t m_requestMap[REQ_MAP_SIZE]; // Bit is set for each note being requestedstatic midictrl_t m_velocity[N_NOTES]; // Requested velocity for each notestatic midictrl_t m_chan[N_NOTES]; // Requested MIDI channel for each notestatic uint8_t m_highest, m_lowest; // Highest and lowest requested notesstatic const uint8_t NO_NOTE = 0xFF;static const uint8_t PERC_NOTE = 0xFE;static uint8_t m_playing[MAX_VOICES]; // Which note each voice is playingstatic const uint8_t NO_VOICE = 0xFF;static uint8_t m_voiceNo[N_NOTES]; // Which voice is playing each notestatic bool startNote( ushort idx ) for (ushort i=0; i if ( m_playing[i]==NO_NOTE ) voices[i].start( MIDI_MIN + idx, m_velocity[idx], m_chan[idx] ); m_playing[i] = idx; m_voiceNo[idx] = i; return true; return false;static bool startPercussion( note_t note ){ ushort i; for (i=0; i { if ( m_playing[i] == NO_NOTE m_playing[i] == PERC_NOTE ) = 1 10 ) update100Hz(); lastUpdate += 10; psg.update();
The boards based on the Cortex M0 processor (SAMD21) like the CPX have 32kB of memory. The M4 (SAMD51) boards with 192kB like the NeoTrellis M4 are likely to be a better choice for larger, more sophisticated programs using the adafruit_midi library. The NeoTrellis M4 also includes stereo 12bit DACs with 3.5mm audio output.
Download the cpx-expressive-midi-controller.py file with the link below. Plug your Circuit Playground Express (CPX) into your computer via a known-good USB data cable. A flash drive named CIRCUITPY should appear in your file explorer/finder program. Copy cpx-expressive-midi-controller.py to the CIRCUITPY drive, renaming it code.py.
The program starts with a major scale at middle C. The touch pad at the top left of CPX will initially send a C4 (MIDI note 60) and next one (counterclockwise) will send a D4 (MIDI note 62) and so on. The code excerpt below shows this code, the MIDI note values for each touch pad are stored in midi_notes[idx], these are based on the middle C value plus offset per scale. This is further adjusted by the current octave and semitone offset selected - a comment informs us of the significance of the "magic value" of 12. The semitone offset can be used to change the key or in chromatic scale to offset a second CPX by +7 semitones to allow two CPXs with 14 notes to cover just over a full octave. 2ff7e9595c
Commentaires