jstrait / jssynth

Make music in your browser with this synthesizer and sequencer
https://www.joelstrait.com/jssynth
MIT License
36 stars 6 forks source link

Implementation of pitch shift #8

Open rafagan opened 4 years ago

rafagan commented 4 years ago

Hello Joel Strait, your work is amazing.

I'm currently working in an audio engine for low latency in Android and I'm trying to implement pitch shift. Do you have any clue to achieve that?

jstrait commented 4 years ago

Thanks!

Can you give more detail about what type of pitch shift you are trying to implement?

rafagan commented 4 years ago

I don't need time stretch, just some kind of tunning. Per example, at 0.5 rate, the sound would play more bass, and at 2.0 rate, the sound will reproduce like a chipmunk, more treble.

Which kind of info you need to know more to understand better?

Ráfagan Sebástian de Abreu


De: Joel Strait notifications@github.com Enviado: domingo, 12 de julho de 2020 07:10 Para: jstrait/jssynth jssynth@noreply.github.com Cc: Ráfagan Abreu rafagansa@outlook.com; Author author@noreply.github.com Assunto: Re: [jstrait/jssynth] Implementation of pitch shift (#8)

Thanks!

Can you give more detail about what type of pitch shift you are trying to implement?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/jstrait/jssynth/issues/8#issuecomment-657184539, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AARQNOKWPJFNHCG75GFJM6LR3FOVXANCNFSM4OW7HOXQ.

jstrait commented 4 years ago

OK, that makes sense.

Changing the sample rate of an audio file will change the pitch and playback speed. For example, if a file has a sample rate of 44,100, then changing the sample rate to 22,050 will halve the pitch and playback speed.

If you are working with actual sample data, here is how to do it. Let's say you have this list of samples: [4, 7, -1, -2, 1, 5, 6, 2, 0, -3]

Then to change the pitch and playback speed, you need to change how fast you move through those samples.

For example, at 1x speed, you would move through the samples one at a time: 4, then 7, then -1, then -2, etc. At 2x speed, you would move through the samples two at a time: 4, then two after that is -1, then two after that is 1, etc. (The final result being [4, -1, 1, 6, 0]).

However, if the playback rate is not an integer multiple, then you have to interpolate samples. For example, what if the playback rate 1.5x? First you have 4, but then the next sample 1.5 samples away, which is halfway between 7 and -1. So you have to create a "virtual sample" instead. For example, if using linear interpolation, the virtual sample would be 3, because that is halfway between 7 and -1. (The final result of 1.5x speed being [4, 3, -2, 3, 6, 1, -3]);

Here is some pseudocode to show the idea:

playbackRate = 1.5  // Shift pitch up 1.5 times, and play back 1.5 times faster
inputSamples = getInputSamples()
outputSamples = []
sampleIndex = 0.0

while (sampleIndex <= (inputSamples - 1)) {
  // Use linear interpolation to create a "virtual sample" between two adjacent samples
  leftSample = inputSamples[floor(sampleIndex)]
  rightSample = inputSamples[ceiling(sampleIndex)]
  percentage = sampleIndex - floor(sampleIndex)
  interpolatedSample = ((1.0 - percentage) * leftSample) + (percentage * rightSample)

  outputSamples.push(interpolatedSample)

  sampleIndex += playbackRate
}

I don't know if linear interpolation is the ideal way to interpolate, or if there are better interpolation functions for audio. But this should give a result that works.

Like you said, this will change both the pitch and the playback speed. I haven't implemented pitch shift that doesn't change the playback speed, so I can't offer much help with that. 🙂

Hope this helps!

rafagan commented 4 years ago

Very nice solution. I've tried something similar before, but it was very buggy: I was incrementing the sampleIndex using the pitch shift and I was always round down the sampleIndex to get the current frame, so for instance if the pitch shift is working in 0.1 play rate speed, the frame x would play 10 times before it reaches the x + 1 frame.

I'm researching about the linear interpolation and I've found this post:

https://stackoverflow.com/questions/21843564/linear-interpolation-audio-pitch-shift

Someone provided info about a pseudocode using sinc function. Do you know anything about that? http://www.nicholson.com/rhn/dsp.html#3

This algorithm that you provided is what you use for resampling techniques?