Aidas Bendoraitis
Aidas Bendoraitis

Reputation: 4003

Convert Sample Rate in Web Audio API

How could I convert sample rate of a buffer from 44100 to 48000 Hz in a browser?

I found a library https://github.com/taisel/XAudioJS/blob/master/resampler.js that should allow me to do that, but don't have an idea how to use it.

Upvotes: 1

Views: 5300

Answers (2)

Aidas Bendoraitis
Aidas Bendoraitis

Reputation: 4003

There seemed to be a bug in mobile safari which didn't decode the loaded audio correctly when the sample rate for the audio context was different than the sample rate for the audio file. Moreover, the sample rate for the audio context changed randomly from 44100 to 48000 usually but not always depending if the website was loading with the iPhone sound switched on or off.

The workaround for this issue is to read the sample rate of the audio context and then to load different audio files for each sample rate, like this:

window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audio_context = new AudioContext();
var actual_sample_rate = audio_context.sampleRate;
if (actual_sample_rate != 48000) {
    actual_sample_rate = 44100;
}

function finished_loading_sounds(sounds) {
    buffers['piano_do'] = {
        buffer: sounds.piano,
        rate: 1
    };
    // ...do something when the sounds are loaded...
}

var buffer_loader = new BufferLoader(
    audio_context,
    {
        piano: "audio/" + actual_sample_rate + "/piano.m4a",
    },
    finished_loading_sounds
);

buffer_loader.load();

The buffer loader is defined like in this tutorial.

To change the sample rate for the audio file, one can use Audacity.


UPDATE

It seems that even when you try to load a file with the correct sample rate, occasionally the sound still gets distorted on iOS devices.

To fix the issue, I found a hack for your AudioContext:

function createAudioContext(desiredSampleRate) {
    var AudioCtor = window.AudioContext || window.webkitAudioContext;

    desiredSampleRate = typeof desiredSampleRate === 'number'
        ? desiredSampleRate
        : 44100;
    var context = new AudioCtor();

    // Check if hack is necessary. Only occurs in iOS6+ devices
    // and only when you first boot the iPhone, or play a audio/video
    // with a different sample rate
    if (/(iPhone|iPad)/i.test(navigator.userAgent) && context.sampleRate !== desiredSampleRate) {
        var buffer = context.createBuffer(1, 1, desiredSampleRate);
        var dummy = context.createBufferSource();
        dummy.buffer = buffer;
        dummy.connect(context.destination);
        dummy.start(0);
        dummy.disconnect();

        context.close(); // dispose old context
        context = new AudioCtor();
    }
    return context;
}

Then to use it, create the audio context as follows:

var audio_context = createAudioContext(44100);

Upvotes: 1

Raymond Toy
Raymond Toy

Reputation: 6056

Use an offline audio context. Something like the following may work:

var c = new OfflineAudioContext(1, len, 48000);
var b = c.createBuffer(1, len, 44100);
b.copyToChannel(yourSourceBuffer, 0);
var s = c.createBufferSource();
s.buffer = b;
s.connect(context.destination);
s.start();
c.startRendering().then(function (result) {
  // result contains the new buffer resampled to 48000
});

Depending the implementation, the quality of the resampled signal can vary quite a bit.

Upvotes: 4

Related Questions