29.11.24
This is a feeble attempt to document my current algorithmic process. Using Pure data I'm developing an audio visual patch that has relied on scales and randomness but I have lots of music I have made that was composed on a guitar. These chords exist in a DAW and I have been meaning to use these chords in my algorithmic music to make my AV sound more like me and less randon.
So in Bitwig (A nice DAW that works on linux) I extracted all the chords from music I had produced as a single track and saved a midi file (.mid extension).
Using a node js script I converted this MIDI file into a representation that can be loaded into pure data. This is the format I want to use via the [text] Pd object...
72 68 58 53; 72 68 63 58; 70 66 61 56; 70 65 61 54; 79 75 68 65; 84 80 73 70; 82 78 71 68; [truncated]
This is a list of MIDI notes without velocity seperated with spaces and ending with a semi colon. This is because Pd treats semi-colons as a message deliminator.
Here is the node js script I used to convert this MIDI file into that representation...
You will need nodejs and to "npm init" and "npm install @tonejs/midi" to get this to work with a midi file called "all.mid" within the same folder. Save the script as "extract_chords.js" and run "node extract_chords".
const fs = require('fs'); const { Midi } = require('@tonejs/midi'); // Function to extract MIDI numbers grouped by chords async function extractChordsFromMidi(filePath) { // Read the MIDI file const data = fs.readFileSync(filePath); const midi = new Midi(data); const chords = [] // Define a small time window to group notes into chords const chordTimeWindow = 0.1; // in seconds // Iterate over each track in the MIDI file midi.tracks.forEach(track => { let currentChord = []; let lastNoteTime = null; track.notes.forEach(note => { if (lastNoteTime === null || (note.time - lastNoteTime) <= chordTimeWindow) { // Add note to the current chord currentChord.push(note.midi); } else { // Output the current chord and start a new one if (currentChord.length > 0) { //console.log('Chord:', currentChord); chords.push(currentChord) } currentChord = [note.midi]; } lastNoteTime = note.time; }); // Output the last chord if any if (currentChord.length > 0) { //console.log('Chord:', currentChord); chords.push(currentChord) } }); //console.log(chords) var out = `` chords.forEach(function (chord) { out += (chord.toString().replace(/,/g, ' ') + ';\n') }) console.log(out) fs.writeFileSync('output.txt', out) } // Example usage extractChordsFromMidi('all.mid');
I extracted and adapted the chord functionality used in this idiotique pd patch which is a brilliant re-creation of radiohead's track from Kid A. It uses a [text] object to play 3 or 4 chords in the same representation above. I've extracted this to load in my much bigger MIDI and adapted it so that it will play a much larger list of chords that I can feed into my own synths.
The way this patch works it will spit out the notes from the chord in quick succession and the number box at the top is basically triggering the chord at that paticular index so I constrain that using modulo to be the number of lines within the chord file. So if you only have 10 chords you would add a [% 10] object before sending the [text get chords] to make sure you output a chord. If you send a number greater than the the number of chords saved in [text define -k chords] it won't output anything.
You can double click the [text define...] object to paste in your chords.
I will show examples of musical output and document using my existing algorithmic objects to trigger chords and contrain patterns using these chords. I will also share a nice standalone patch in the coming days too so watch this space.