Giter VIP home page Giter VIP logo

tone.js's Introduction

Tone.js

codecov

Tone.js is a Web Audio framework for creating interactive music in the browser. The architecture of Tone.js aims to be familiar to both musicians and audio programmers creating web-based audio applications. On the high-level, Tone offers common DAW (digital audio workstation) features like a global transport for synchronizing and scheduling events as well as prebuilt synths and effects. Additionally, Tone provides high-performance building blocks to create your own synthesizers, effects, and complex control signals.

Installation

There are two ways to incorporate Tone.js into a project. First, it can be installed locally into a project using npm:

npm install tone      // Install the latest stable version
npm install tone@next // Or, alternatively, use the 'next' version

Add Tone.js to a project using the JavaScript import syntax:

import * as Tone from 'tone';

Tone.js is also hosted at unpkg.com. It can be added directly within an HTML document, as long as it precedes any project scripts. See the example here for more details.

<script src="http://unpkg.com/tone"></script>
<script src="myScript.js"></script>

Hello Tone

//create a synth and connect it to the main output (your speakers)
const synth = new Tone.Synth().toDestination();

//play a middle 'C' for the duration of an 8th note
synth.triggerAttackRelease("C4", "8n");

Tone.Synth

Tone.Synth is a basic synthesizer with a single oscillator and an ADSR envelope.

triggerAttack / triggerRelease

triggerAttack starts the note (the amplitude is rising), and triggerRelease is when the amplitude is going back to 0 (i.e. note off).

const synth = new Tone.Synth().toDestination();
const now = Tone.now()
// trigger the attack immediately
synth.triggerAttack("C4", now)
// wait one second before triggering the release
synth.triggerRelease(now + 1)

triggerAttackRelease

triggerAttackRelease is a combination of triggerAttack and triggerRelease

The first argument to the note which can either be a frequency in hertz (like 440) or as "pitch-octave" notation (like "D#2").

The second argument is the duration that the note is held. This value can either be in seconds, or as a tempo-relative value.

The third (optional) argument of triggerAttackRelease is when along the AudioContext time the note should play. It can be used to schedule events in the future.

const synth = new Tone.Synth().toDestination();
const now = Tone.now()
synth.triggerAttackRelease("C4", "8n", now)
synth.triggerAttackRelease("E4", "8n", now + 0.5)
synth.triggerAttackRelease("G4", "8n", now + 1)

Time

Web Audio has advanced, sample accurate scheduling capabilities. The AudioContext time is what the Web Audio API uses to schedule events, starts at 0 when the page loads and counts up in seconds.

Tone.now() gets the current time of the AudioContext.

setInterval(() => console.log(Tone.now()), 100);

Tone.js abstracts away the AudioContext time. Instead of defining all values in seconds, any method which takes time as an argument can accept a number or a string. For example "4n" is a quarter-note, "8t" is an eighth-note triplet, and "1m" is one measure.

Read about Time encodings.

Starting Audio

IMPORTANT: Browsers will not play any audio until a user clicks something (like a play button). Run your Tone.js code only after calling Tone.start() from a event listener which is triggered by a user action such as "click" or "keydown".

Tone.start() returns a promise, the audio will be ready only after that promise is resolved. Scheduling or playing audio before the AudioContext is running will result in silence or incorrect scheduling.

//attach a click listener to a play button
document.querySelector('button')?.addEventListener('click', async () => {
	await Tone.start()
	console.log('audio is ready')
})

Scheduling

Transport

Tone.Transport is the main timekeeper. Unlike the AudioContext clock, it can be started, stopped, looped and adjusted on the fly. You can think of it like the arrangement view in a Digital Audio Workstation or channels in a Tracker.

Multiple events and parts can be arranged and synchronized along the Transport. Tone.Loop is a simple way to create a looped callback that can be scheduled to start and stop.

// create two monophonic synths
const synthA = new Tone.FMSynth().toDestination();
const synthB = new Tone.AMSynth().toDestination();
//play a note every quarter-note
const loopA = new Tone.Loop(time => {
	synthA.triggerAttackRelease("C2", "8n", time);
}, "4n").start(0);
//play another note every off quarter-note, by starting it "8n"
const loopB = new Tone.Loop(time => {
	synthB.triggerAttackRelease("C4", "8n", time);
}, "4n").start("8n");
// all loops start until the Transport is started
Tone.Transport.start()

Since Javascript callbacks are not precisely timed, the sample-accurate time of the event is passed into the callback function. Use this time value to schedule the events.

Instruments

There are numerous synths to choose from including Tone.FMSynth, Tone.AMSynth and Tone.NoiseSynth.

All of these instruments are monophonic (single voice) which means that they can only play one note at a time.

To create a polyphonic synthesizer, use Tone.PolySynth, which accepts a monophonic synth as its first parameter and automatically handles the note allocation so you can pass in multiple notes. The API is similar to the monophonic synths, except triggerRelease must be given a note or array of notes.

//pass in some initial values for the filter and filter envelope
const synth = new Tone.PolySynth(Tone.Synth).toDestination();
const now = Tone.now()
synth.triggerAttack("D4", now);
synth.triggerAttack("F4", now + 0.5);
synth.triggerAttack("A4", now + 1);
synth.triggerAttack("C4", now + 1.5);
synth.triggerAttack("E4", now + 2);
synth.triggerRelease(["D4", "F4", "A4", "C4", "E4"], now + 4);

Samples

Sound generation is not limited to synthesized sounds. You can also load a sample and play that back in a number of ways. Tone.Player is one way to load and play back an audio file.

const player = new Tone.Player("https://tonejs.github.io/audio/berklee/gong_1.mp3").toDestination();
Tone.loaded().then(() => {
	player.start();
});

Tone.loaded() returns a promise which resolves when all audio files are loaded. It's a helpful shorthand instead of waiting on each individual audio buffer's onload event to resolve.

Sampler

Multiple samples can also be combined into an instrument. If you have audio files organized by note, Tone.Sampler will pitch shift the samples to fill in gaps between notes. So for example, if you only have every 3rd note on a piano sampled, you could turn that into a full piano sample.

Unlike the other synths, Tone.Sampler is polyphonic so doesn't need to be passed into Tone.PolySynth

const sampler = new Tone.Sampler({
	urls: {
		"C4": "C4.mp3",
		"D#4": "Ds4.mp3",
		"F#4": "Fs4.mp3",
		"A4": "A4.mp3",
	},
	baseUrl: "https://tonejs.github.io/audio/salamander/",
}).toDestination();

Tone.loaded().then(() => {
	sampler.triggerAttackRelease(["Eb4", "G4", "Bb4"], 0.5);
})

Effects

In the above examples, the sources were always connected directly to the Destination, but the output of the synth could also be routed through one (or more) effects before going to the speakers.

const player = new Tone.Player({
	url: "https://tonejs.github.io/audio/berklee/gurgling_theremin_1.mp3",
	loop: true,
	autostart: true,
})
//create a distortion effect
const distortion = new Tone.Distortion(0.4).toDestination();
//connect a player to the distortion
player.connect(distortion);

The connection routing is very flexible. For example, you can connect multiple sources to the same effect and then route the effect through a network of other effects either serially or in parallel.

Tone.Gain is very useful in creating complex routing.

Signals

Like the underlying Web Audio API, Tone.js is built with audio-rate signal control over nearly everything. This is a powerful feature which allows for sample-accurate synchronization and scheduling of parameters.

Signal properties have a few built in methods for creating automation curves.

For example, the frequency parameter on Oscillator is a Signal so you can create a smooth ramp from one frequency to another.

const osc = new Tone.Oscillator().toDestination();
// start at "C4"
osc.frequency.value = "C4";
// ramp to "C5" over 2 seconds
osc.frequency.rampTo("C5", 2)

AudioContext

Tone.js creates an AudioContext when it loads and shims it for maximum browser compatibility using standardized-audio-context. The AudioContext can be accessed at Tone.context. Or set your own AudioContext using Tone.setContext(audioContext).

MIDI

To use MIDI files, you'll first need to convert them into a JSON format which Tone.js can understand using Midi.

Performance

Tone.js makes extensive use of the native Web Audio Nodes such as the GainNode and WaveShaperNode for all signal processing, which enables Tone.js to work well on both desktop and mobile browsers.

This wiki article has some suggestions related to performance for best practices.

Testing

Tone.js runs an extensive test suite using mocha and chai with nearly 100% coverage. Each commit and pull request is run on Travis-CI across browsers and versions. Passing builds on the 'dev' branch are published on npm as tone@next.

Contributing

There are many ways to contribute to Tone.js. Check out this wiki if you're interested.

If you have questions (or answers) that are not necessarily bugs/issues, please post them to the forum.

References and Inspiration

tone.js's People

Contributors

aniketaranake avatar benmccann avatar chrisdeaner avatar cjwit avatar colinmorris avatar danferns avatar dependabot[bot] avatar foaly avatar garrensmith avatar garrettmichaelgeorge avatar idicious avatar ixnv avatar jackca avatar jacobclarke92 avatar jaredellison avatar jffng avatar lidiovargas avatar lukephills avatar marcelblum avatar neauoire avatar polyrhythmatic avatar sarahbuisson avatar satelllte avatar stephlow avatar tambien avatar tboie avatar therewasaguy avatar this-fifo avatar vibertthio avatar yifanmai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tone.js's Issues

Move tests to OfflineRenderingContext

Right now they use Recorder (which should probably be deprecated), and are susceptible to all kinds of slowdowns in the main thread which can cause the tests to fail (#9). They should instead be using the OfflineRenderingContext which will be faster and more accurate.

Is there a way to choose which nodes you want disconnect?

Hey Yotam,

Is there a way to disconnect a specific node?

At the moment, when you use the disconnect method it disconnects all the nodes which can be a bit problematic. If you want to only disconnect one node, you then have to reconnect up everything else. Sometimes this can be difficult, especially if you don't know all of the nodes currently connected.

Do you know a way of getting round this?

Really great work on the new release!

Microphone dispose error

Hey there,

Line 93 in Microphone.js gives "Uncaught TypeError: undefined is not a function".
this._stream.disconnect();

Seems to work fine without it. What do you recon?

Both Phaser and Freeverb only coming out the left side.

Hey Yotam,

If you connect an Tone.Oscillator to either one of these effects the sound only comes out the left side.

Strangely though, if you connect a Tone.Noise or a Tone.Microphone to these effects it works and they are in stereo. I can't work out what's causing this. Have you come across this?

Thanks

Tone.Signal crackling sound

I'm trying to use a Tone.Signal as a gain node, but when I set new values I hear a slight crackling sound but no change to the gain. Is this the right way to use Signals?

this.Effect = new Tone.Signal(1, 'db');
this.Effect.value = newValue;

I've tried using different units but I still can't get it work.

Tone.FMSynth

At least two Oscillators chained together with independent Envelopes.

Move Tone.js build to the build folder

I will remove the build which is currently at the top level and all builds (Tone.js, Tone.min.js, Tone.Presets.js) will go to the build folder. References in the examples and documentation will need to be updated.

Make tests fail gracefully

many tests fail and leave code which needs cleaning up, like an uninvoked "dispose" method. sometimes this seems to cause failures in the later tests.

Allow Tone.Envelope to take all parameters as an object

It would be more elegant, and also work better with the instruments like Tone.MonoSynth if Tone.Envelope took its parameters as an object such as:

{
  "attack" : 0.001, 
  "decay" : 0.4,
  ...
}

Eventually perhaps all Tones will be able to accept their arguments as objects for consistency.

need ability to scale signal exponentially

Or maybe this could be an option with Tone.Scale or another class like Tone.ExpScale.

The scaling could easily be done with a WaveShaperNode as long as the input was between -1 and 1.

This would be useful for scaling frequency values with Tone.AutoWah

Chorus depth doesn't smoothly oscillate

Try a chorus and set it's depth to something above 1 and you can hear that one of the LFOs aren't smoothly oscillating. It sounds a bit like it's set to a pulse wave.

Previously you used to be able to set the depth and delaytime high values and it would add interesting harmonies which sounded great.

Signal Logic Operators &&, ! and ||

  • Tone.And(inputs) takes the number of inputs as an argument (defaults to 2) and returns 1 (true) only if all inputs are 1 (i.e. the sum of the inputs is equal to the number of inputs) otherwise returns 0
  • Tone.Not() is exactly the same as Tone.EqualZero. 1 return 0 and 0 returns 1
  • Tone.Or(inputs) takes the number of inputs as an argument (defaults to 2) and returns 1 (true) if at least one input is 1 (i.e. the sum of the inputs is greater than 0)

Envelope attack & release with keyboard not triggering properly

Hey,

When using envelopes with qwertyhancock the attack isn't triggering properly when you switch between notes quickly. On your synth examples if you use the keyboard and switch between notes quickly and hold the second down, you don't hear the sustain. Any way to fix this?

Write LessThan in terms of Threshold and GreaterThan in terms of LessThan

Tone.LessThan(value) could be simplified to Tone.Adder(-value) --> Tone.Threshold(0)

and Tone.GreaterThan(value) could be simplified to Tone.Negate() --> Tone.LessThan(-value)

This will simplify the classes that need to be maintained since logic dealing with WaveShaperNodes have proven to be a little tricky sometimes.

Dynamically set loop boundaries while playing Tone.Player

With Tone.Player, I notice that after using .setLoopStart() and .setLoopEnd(), the changes only go into effect when I execute .start() to restart the player.

It would be great to change this so that .setLoopStart() etc all actually affect Player._source so that users can dynamically change granular playback position without restarting the player. Or, maybe this is outside of the design goals for the Player?

TypeScript support for r4?

Big API changes in r4: moving most of the setSomething / getSomething methods over to ES5 getter/setter interfaces.

Overall it makes the library much more consistent, readable, and expressive. Hopefully this isn't a problem with TypeScript.

@lukephills

make all dispose methods call the parent's dispose method directly

Dispose methods should use Tone.prototype.dispose.call(this) if they extend Tone, and if they extend a subclass, they should call Tone.Parent.prototype.dispose.call(this).

Additionally this scheme makes it possible to remove manual disposing / disconnecting of this.input and this.output.

Chrome Crashing with certain nodes

I believe this is related to either the LowpassCombFilter or the FeedbackCombFilter. Both cause chrome to "Aw, Snap!" occasionally during tests. Classes that use these nodes are also susceptible: Freeverb, JCReverb, and PluckSynth.

Seems to be related to a Chrome 38 release.

I've reported the bug here. Hopefully it is addressed soon.

Envelope doesn't seem to correctly affect sampler

I'm having a blast experimenting with Tone.js, what a great tool.

I'm attempting to use the Sampler object to play a short loop and affect its amplitude with an envelope. In this example, I want the loop to start playing immediately when I click, and fade out over 10 seconds when I release. However, the fade out only lasts about a second no matter what value I specify for the release parameter.

Am I misunderstanding how the sampler and envelope work?

Here's a sample page illustrating the problem: http://etienne.github.io/tonetest/

And here's the relevant code:

var sampler = new Tone.Sampler({
  "url" : 'mp3/A1.mp3',
  "envelope" : {
    "attack" : 0,
    "decay" : 0,
    "sustain" : 1,
    "release" : 10
  }
});
sampler.player.loop = true;
sampler.player.loopStart = 0.1;
sampler.player.loopEnd = 0.11;
sampler.toMaster();

window.onmousedown = function(event) {
    sampler.triggerAttack();
};

window.onmouseup = function(event) {
    sampler.triggerRelease();
};

add Tone.startMobile to examples and tests

examples which start audio processes from the time of load (such as signalProcessing ) require that a user input start the AudioContext. the solution is to bind a button or UI object to Tone.startMobile().

This will also need to be added to the tests so tests will be started manually with a button.

onended events are inconsistent

They seem to work only on certain browsers in certain situations. This can cause bugs like the transport not being able to stop and restart in FireFox.

Replace bindings to the native onended events with a setTimeout instead.

MonoSynth Objects on iOS Mobile

Been going around in circles trying to figure out why this sketch I was making with Tone.js was giving me grief on mobile (using Chrome/Safari on an iPhone) - realized if I swapped plain Oscillators in instead of MonoSynths then it worked.
Ended up rebuilding the MonoSynth chain, I think pretty much piece-by-piece, just in my sketch, and it works. But if I swap in MonoSynths again it breaks. Tested this on a completely simplified 'just touch the canvas' and it plays a note type of scenario and it seems to hold true - works fine on desktop, no output on mobile if it's using MonoSynth. Noticed the Synth Examples don't seem to play on iOS mobile either so wanted to notify that there could be something wrong. I'm really new to programming so I could just be doing something wrong though.

Transport plays extra note on stop.

It's because of the call to setTick in the stop method. i should bind to the onend event of the oscillator so that the tick is set to 0 only after the oscillator has stopped.

setFeedback values

Hey, I'm not sure that that setFeedback is working as it should.

On line 38 of the pingPingDelay.html example change to:
feedbackDelay.setFeedback(2);

This will give you an infinite feedback which you'd expect to get if you set it to 1.
Also setting this to 0 still gives some feedback. Should it not stop feedback completely?

Get frequency from a Tone.Oscillator

Hi, is there anyway to get the current frequency value from an oscillator?

Oscillator.frequency.value is a Tone.Signal so I'm unsure how to get the value as a number.

Thanks

Ability to set AudioContext

this might require setting up an initialize routine for certain modules like Tone.Signal which use the context on init.

All classes need a "get" method

combined with a "set" method it'll be very easy to go to and from JSON representations which will make creating and saving presets very simple.

Tone.Player doesn't loop in Chrome 42

There is a bug in Chrome 42 which causes the AudioBufferSourceNode not to loop when invoked with an offset. It is already fixed on Chrome 43.

I will address the issue on the dev branch, but Chrome 43 will probably be released before the dev branch makes it into master. This is not a problem in FF and Safari.

Convolver not loading

I've got an issue with the Convolver in r4. Seems like the convolver buffer isn't being set.

this.Effect = new Tone.Convolver("../Assets/ImpulseResponses/teufelsberg01.wav");

this.Effect._convolver.buffer = null

PitchShift

Feature request: PitchShift effect, i.e. that could be applied to an input source / media element source node. If you ever have time for it, this would be awesome!

Tone.Transport tick listeners

Feature request
Enable the transport to send its ticks to other/multiple destinations. A function like 'setListener' adds a callback which will be executed at each tick.

Why?
I'd like to create multiple 'smaller' transports (with their own loop points), but have them in the same BPM as the global transport. Creating a new clock for each transport seems unnecessary and using the ticks of the main transport would be perfect!

Thank you!

Signals inherit Signal.connect method

All classes in the signal folder should borrow the Signal.connect method which zeros out the value of the Param or Signal which is it connect to so that the Signal can get full control over the output value without being summed with the current Param or Signal's value. This will save having to manually zero out the connected parameter which is not intuitive.

Not sure what the best way to handle this is. Possibly just doing something like this would work:

Tone.[Class].prototype.connect = Tone.Signal.prototype.connect

Also, not sure how to handle this case in the jsdocs.

create a sin envelope

I'm not sure this is possible in tone.js? I just want a basic sine wave with a sin envelope start from 0 to 1, and end at 1. Or any ohter envelope like triangle

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.