Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.52k stars 983 forks source link

Pitch bend midi data apply to sampler instrument - Is this possible? #983

Closed rhelsing closed 2 years ago

rhelsing commented 2 years ago

How would the pitch bend data from midi be applied to the sampler.

track.notes.forEach((note) => {
  // track.pitchBends = [{ ticks: 343, value: 0.0234375 }, ...] - size 2560 values - normalized value 0.0-1.0 
  //Tone.js pitch shifter deals in semitones..
            //TODO: handle pitch bend and modwheel signals?
              sampler.triggerAttackRelease(
                  note.name,
                  note.duration*scaler,
                  note.time*scaler,
                  note.velocity
              );
          });
tambien commented 2 years ago

You can pass in frequency values (e.g. 440) instead of note names (e.g. A4) which will give you more fine grained control over pitch to get values between two semitones. Sampler doesn't support pitch curves though. For that, i would suggest using ToneBufferSource and scheduling the playbackRate so that you get the pitch bend that your looking for.

rhelsing commented 2 years ago

@tambien thank you. Is there an example somewhere showing the usage of pitch curves with a monosynth?

rhelsing commented 2 years ago

@tambien It seems like being able to ramp the pitch value on a PitchShift node might be a good way to accomplish this? Is this possible? or would it jump by discrete semitones? How might I go about allowing pitch shift to operate on cent values as opposed to semitones and allow it to be ramped?

update: here's how I am attempting to do it. I'm not sure if my math is quite right yet but this should work on sampler instruments too. Using a PitchShift node (Use case is rendering midi to audiobuffers offline)


var pitch = new Tone.PitchShift()
synth.chain(pitch, channel)

          var stRange = 3 //bend by 3 st up and down
          track.pitchBends.forEach((e) => {
            var seconds = ((60000 / (bpm * 192))*e.ticks)/1000.0
            var frequency_value = 0.0
            var factor = 0.0
            if(e.value >= 0){
              factor = Tone.intervalToFrequencyRatio(stRange) - 1;
            }else{
              factor = Tone.intervalToFrequencyRatio((stRange*-1) - 1) + 1;
            }
            factor = factor*e.value //0.0-1.0 scaler of pitch effect
            frequency_value = factor * (1.2 / pitch._windowSize); //why 1.2 hardcoded
            pitch._frequency.setValueAtTime(frequency_value, seconds);
          });

update 2: this seems to be affecting/effecting the signal in strange ways.. the pitch bend value seems to slow the entire playback rate so the resulting waveform is stretched and not timed with the midi data.. this may be expected behavior. Additionally the amount by which the pitch shift happens is much larger than expected.. by much more than 3 semitones. My math is definitely wrong.

update 3: this seems an adequate way to do it using a synth:

          var stRange = 3
          track.pitchBends.forEach((e) => {
            var seconds = ((60000 / (bpm * 192))*e.ticks)/1000.0
            var frequency_value = 0.0
            var cents = stRange*100.0*e.value //100 cents per semitone
            // console.log(cents, seconds)
            synth.detune.setValueAtTime(cents, seconds);
          });