initial author: Steve Wang ([email protected])
contributors: Yiler Huang ([email protected]); Renick Bell ([email protected])
This document works as both a documentation and a tutorial about how to use the code and how to install the code. These sections follow in this order:
- The first part covers the installation process.
- Next, it covers the basics of just playing some sounds.
- Next, a part about (controlling music)[#controlling-musc], then how to put those configurations into the musical environment and about assigning players for music synthesizer sessions
- Finally, there is a section that might help if installation or playing music does not work and some configuration specifics about different operating systems.
-
Install the files by downloading the files on this page as a zip or use git to download the files by typing this command in the terminal:
git clone https://github.com/mrname5/Using-Konduktiva-With-MIDI.git
-
Install Nodejs. If npm does not automatically get installed with Nodejs, install npm.
2.1. Check if nodejs is installed by running this command in the terminal:
node -v
2.2. Check if npm is installed by running this command in the terminal:
npm -v
2.3. You can close the terminals you have opened prior to this step.
-
Open the terminal, move to the directory called using-konduktiva-with-midi. This effect can be achieved by using the
cd [directory name command]
command or navigate to that directory with the file manager and right click to open a terminal session there. We will call this terminal session t1. Then, run this command:node installer.js
-
After that code has finished running, the installation should be complete.
-
Launch 4 sessions/instances of your preferred music synthesizer that supports having multiple instances running at the same time (each with a different midi inputs). Yoshimi is suggested for Linux and Surge is recommended for MacOS.
-
Go back to t1 and type this command in it:
node
-
Paste the contents of example-session-with-melodies.js inside t1.
-
Now you can try pasting the code in example-playing-melodies.js(A file where all the other necessary files are loaded) into t1. You will see that now you are able to control the music. Instructions on how to do so are below.
e.play('musicSynthesizerSession1')
e.play('musicSynthesizerSession3')
e.play('musicSynthesizerSession4')
e.play('musicSynthesizerSession2')
e.stopAll()
When installing Surge music synthesizer, you get Surge XT and Surge ET Effects. Open this application called Hosting AU and choose surge as the instrument in the application. Surge will be launched automatically by Hosting AU
To control what a player plays, a configuration object will have to be created. This object will control everything from what to play, when to play and what to filter out.
Here the different properties of the configuration object will be listed:
let newChords2 = chords.map(e => separateOctaveAndRoot(e))
circleOfFifthChords = {
velocity: A.buildArray(30, x => 90),
noteDurationKeyspan: 16,
noteDurationValues: [0, 1, 2, 3],
// noteDurations: [0, 4, 8, 12],
noteDurations: [4, 4, 4, 4],
bools: boolsData,
modeFilter: A.buildArray(12, x=> x),
modeFilterKeyspan: 12,
octave: newChords2.map(x => {
return x.octaveNotes
}),
noteValues: newChords2.map(x => {
return x.rootNotes
}),
total: 12,
// polyphonyMap: A.buildArray(12, x => {return 50}),
rhythmMap: [0, 4, 8, 12],
// noteValues: chords,
rootMap: ['C', 'C', 'C', 'C']
}
//More explanations of each variable below.
The titles of each variable in this documentation are aimed at giving as much information as possible. For example, the first title "bools: boolean[]". "bools" is the variable name and "boolean[]" tells you that the variable bools is supposed to be an array full of booleans.
Those without "(optional)" in the title means that without filling in that variable the configuration object will not be valid or work as expected.
Keyspan is the total
An array of numbers in ascending order.
new QuantizedMap(keyspan, keys, values)
Takes a number as an input and will look for a number in the keys array that is closest to it compared to the others. It will then take the index of that number and return the value array using that index.
The floorLookup method does something similar to nearestLookup but when it looks for the closest it always looks for a number smaller than it.
The wrapLookup method is also similar to the nearestLookup method but when the number provided is greater than the keyspan, it does not return the last item in the values array instead it loops back around.
An array filled with true or false values. If it is false on that beat the sound will not play.
Not filling it in just deactivates this specific feature.
Before a note is played it will go through a filterMode function. The function will check if it needs to filter the notes by checking if the modeFilter for that player exists. If it does it will proceed to check if the root note matches one of the root notes in the mode. If it does, it is allowed to be played. If it does not, it is switched out for the note that is in the mode that is closest to it.
ONLY ADD ROOT NOTES IN THE modeFilter array because before the function gets played the code will combine the root notes and the octaves.
A mode map is a quantized map in which the keys are beats and the values are modes.
Not filling in will make the keyspan of the modeFilter the last number of the array plus 2.
This variable allows the keyspan of the modeFilter variable to be manually set.
Not filling it in will deactivate the automatic modeFilter switching feature. If this is filled in modeMapKeys must be filled in too.
An array of mode names. This is the information the modeFilter will be updated to at some beat. The beats are controlled by modeMapKeys.
This MUST be filled in IF modeMap is filled in. Can only be left empty when modeMap is empty.
The beats numbers to update the modeFilter.
Not filling it in will cause the keyspan of the keyspan variable to be the last number in the modeMapKeys array if modeMap is filled in. If modeMap is not filled in, nothing will happen if this variable is not filled in.
The keyspan for the modeMap Quantized map.
The numbers here define the root note that is played. The final note will be calculated by combining the octave with the root note. The numbers here can be inputed in differnt ways. Currently there are four ways: relativeSemitone, relativeScaleDegree, noteNames, romanNumeral or, rawMidi. The input methods can be switched by editing the notesInputMode
variable of the MusicalEnvironment.
This controls how long a specific note will be played.
Not filling this in will cause the keyspan of the noteDurationMaps to be the length of the noteDuration array.
This variable allows the keyspan of the noteDuration to be manually set.
A noteDurationMap is a way to implement a melody. The keys are beats, and the values are notes without octave or root note.
This variable allows the values of the noteDuration QuantizedMap to be set.
The octaves of the root notes played must be defined here.
Not filling this in will deactivate this specific function.
The numbers here define the amount of notes that can be played on a beat. If there are too many it will randomly drop notes until they do not go over the defined amount.
An array of musical letters.
Controls when each note is played. It is the ioi (interonset interval).
The keyspan of the sub QuantizedMap in the value variable of the main rhythmMap QuantizedMap.
Also the keyspan for maskMap
Volume. From 0-127.
After creating the configuration object, you will need to add it to the musical environment. Then you will need to assign it to a player
To do so, you need a valid configuration object. Then you will need to call the function called recordConfigurationDataIntoMusicalEnvironment
. That function will take 3 arguments. First argument the configuration object, second, the name you want to record those things under. Third, the musical environment.
recordConfigurationDataIntoMusicalEnvironment(circleOfFifthChords, 'p4', e)
After recording you configuration object into the musical environment, you need to assign it to a player. To do so, call the assignPlayerForMusicSynthesizerSession
function. It takes 4 arguments. The last argument is optional. The first one is the musical environment, the second one is the music synthesizer session you want it to be tied to. It should be in the form of a number and it will refer to a specific output point of e.outputs
. Third, the name of the data is recorded under. The last one is anything you want to manually overide before you assign it to the player. This argument will be an object and the variables will be listed below. This should be a string. If there are no errors after running that, you will be able to play the music by doing e.play('musicSynthesizerSession2')
. Change the number 2 to whatever number you inputted for your second argument.
assignPlayerForMusicSynthesizerSession(e, 4, 'p4')
A chord progression map is a quantized map in which the keys are beats and the values are names of chord progressions. This requires a separate key-value store which maps chord progression names to chord maps.
MIDI channel
Function updateMidiOutputList might not work correctly for your midi configuration. That function is in midi.js so you might want to edit it to work properly for you.
To edit this function run this code in nodejs: easymidi.getOutputs()
This will give you all the outputs easymidi detects and you can edit the function so that it works properly for you.
MIDI configuration. Configure your system so that easymidi can detect midi outputs.
That is OS specific.
The final MIDI value which is played is a function of:
- The note value in relative semitones. If the mode contains the relative interval values in semitones of [0,3,7,9], and the note values are [0,0,1,0], the resulting relative interval values would be [0,0,0,0].
- The octave value
- The scale/mode which filters the relative semitones
- A root note ( = the key)
Each of these items can be determined by a map with beat keys.
Use all of this information to calculate the final MIDI value.