tone-rhythm
v2.0.0
Published
Generate an array of Tone.Transport times for use in Tone.Part
Downloads
91
Maintainers
Readme
tone-rhythm
Generate an array of Tone.Transport times given an array of musical rhythms in various formats that Tone.js understands.
Written for exclusive use with Tone.js.
Features
- Works with latest version of Tone (v0.12.8 - v13.4.0)
- Works in-browser (transpiled) or in node (ES6)
- Light footprint (3.6kB minified)
- Intuitive for musicians
- Has a fully-documented API with examples below.
Why use tone-rhythm?
As a musician developing music applications, I wanted an API that would allow me to put rhythms along with a melody into Tone.Part
. The example on Tone's docs shows how this is expected:
var synth = new Tone.FMSynth().toMaster()
//schedule a series of notes to play as soon as the page loads
synth.triggerAttackRelease('C4', '4n', '8n')
synth.triggerAttackRelease('E4', '8n', Tone.Time('4n') + Tone.Time('8n'))
synth.triggerAttackRelease('G4', '16n', '2n')
synth.triggerAttackRelease('B4', '16n', Tone.Time('2n') + Tone.Time('8t'))
synth.triggerAttackRelease('G4', '16', Tone.Time('2n') + Tone.Time('8t')*2)
synth.triggerAttackRelease('E4', '2n', '0:3')
The third parameter in triggerAttackRelease
is the start time of the note we're triggering. Musicians don't normally consider where a note is in a timeline when composing music. I needed a way to calculate this ahead of time.
I found enlightenment after reading Music Theory using Tone.js - Play Rhythms. Based on the author's work, I created algorithms that accumulate rhythmic values in an array of durations to generate start times. The result feels much more intuitive to me (please see the examples below.)
Getting Started
Prerequisites
Tone.js (v0.12.x or higher)
Installing
npm install tone-rhythm
Or clone the repo and copy dist/tone-rhythm.min.js
into your project.
Usage
ES6 / Webpack
// tone is a required peer dependency
import ToneTime from 'tone/Tone/type/Time';
import { toneRhythm } from 'tone-rhythm';
// any or all methods can be used in the instantiated toneRhythm:
const {
getBarsBeats,
addTimes,
getTransportTimes,
mergeMusicDataPart
} = toneRhythm(ToneTime);
Browser
Via node_modules:
<head>
<!-- Get tone from unpkg CDN: -->
<script src="https://unpkg.com/[email protected]/build/Tone.js"></script>
<!-- Import `tone-rhythm.min.js` from node_modules: -->
<script src="node_modules/tone-rhythm/dist/tone-rhythm.min.js"></script>
<!-- OR simply provide your path/to/tone-rhythm.min.js -->
</head>
Via https://unpkg.com/tone-rhythm
<head>
<!-- Get tone from unpkg CDN: -->
<script src="https://unpkg.com/[email protected]/build/Tone.js"></script>
<!-- Get tone-rhythm from unpkg CDN: -->
<script src="https://unpkg.com/[email protected]/dist/tone-rhythm.min.js"></script>
<!-- OR simply provide your path/to/tone-rhythm.min.js -->
</head>
In your js file(s):
const {
getBarsBeats,
addTimes,
getTransportTimes,
mergeMusicDataPart
} = toneRhythm.toneRhythm(Tone.Time); // both `toneRhythm` and `Tone.Time` are available globally from imports above
(Legacy) Pre-bundled with Tone 0.12.8
API
Values which can populate a rhythms array:
- '4n' - a 'notation' value
- ['4n', '8t'] - an array of 'notation' values which will be added together
- ['r', '2n'] - an array that has 'r' at first index will be a rest
- ['r', '2n', '8t'] - (see above about 'r') and the remaining values will be added together
It's not recommended to use Tone's seconds format.
See documentation and below for tone-rhythm library usage.
Examples ("Maria" by Leonard Bernstein)
toneRhythm.getTransportTimes
Given an array of durations (see API), return transport times. Array<String|Number>
const mariaDurations = ['8n', '8n', ['2n', '4n'], '8n', '4t', '4t', '4t', '4t', '4t', '4t', '8n', ['2n', '4n'], '8n', '8n', '8n', '8n', '8n', ['4n', '8n'], '8n', '8n', '8n', '8n', '8n', '4n', '4n', ['2n', '4n', '8n'], '8n', '8n', ['2n', '4n'], '8n', '4t', '4t', '4t', '4t', '4t', '4t', '8n', ['2n', '4n'], '8n', '8n', '8n', '8n', '8n', ['4n', '8n'], '8n', '8n', '8n', '8n', '8n', '4n', '4n', ['2n', '4n', '8n']];
const mariaTransportTimes = toneRhythm.getTransportTimes(mariaDurations); /* ->
Result:
[0, "0:0:2", "0:1:0", "1:0:0", "1:0:2", "1:1:0.667", "1:1:3.334", "1:2:2", "1:3:0.667", "1:3:3.334", "2:0:2", "2:1:0", "3:0:0", "3:0:2", "3:1:0", "3:1:2", "3:2:0", "3:2:2", "4:0:0", "4:0:2", "4:1:0", "4:1:2", "4:2:0", "4:2:2", "4:3:2", "5:0:2", "6:0:0", "6:0:2", "6:1:0", "7:0:0", "7:0:2", "7:1:0.667", "7:1:3.334", "7:2:2", "7:3:0.667", "7:3:3.334", "8:0:2", "8:1:0", "9:0:0", "9:0:2", "9:1:0", "9:1:2", "9:2:0", "9:2:2", "10:0:0", "10:0:2", "10:1:0", "10:1:2", "10:2:0", "10:2:2", "10:3:2", "11:0:2"]
*/
toneRhythm.mergeMusicDataPart
Returns array of objects for consumption by Tone.Part.
const mariaPitches = ["Eb4", "A4", "Bb4", "Eb4", "A4", "Bb4", "C5", "A4", "Bb4", "C5", "A4", "Bb4", "Bb4", "A4", "G4", "F4", "Eb4", "F4", "Bb4", "Ab4", "G4", "F4", "Eb4", "F4", "Eb4", "G4", "Eb4", "A4", "Bb4", "Eb4", "A4", "Bb4", "C5", "A4", "Bb4", "C5", "D5", "Bb4", "D5", "Eb5", "D5", "C5", "Bb4", "D5", "D5", "Eb5", "D5", "C5", "Bb4", "D5", "Eb5", "F5"];
const mergedData = mergeMusicDataPart({
rhythms: mariaDurations,
notes: mariaPitches,
startTime: '0:3:2'
}); /* -> [
{time: "0:3:2", duration: "8n", note: "Eb4", idx: 0},
{time: "1:0:0", duration: "8n", note: "A4", idx: 1},
{time: "1:0:2", duration: "0:3:0", note: "Bb4", idx: 2},
{time: "1:3:2", duration: "8n", note: "Eb4", idx: 3},
{time: "2:0:0", duration: "4t", note: "A4", idx: 4},
{time: "2:0:2.667", duration: "4t", note: "Bb4", idx: 5},
...
]
*/
Use in Tone.Part
const melodyPart = new Tone.Part((time, value) => {
piano.triggerAttackRelease(value.note, value.duration, time);
}, mergedData).start(0);
Contributing
Running the tests:
npm run dev
- build in dev mode and watch for changesnpm run test
- opens the tests in-browser
Acknowledgments
Thank you https://www.guitarland.com and the creator of Tone.js.
Documentation created with jsdoc2md