Pierwsze kroki z interfejsem Web Audio API

Przed elementem <audio> HTML5 wymagany był Flash lub inny dodatek. aby przerwać ciszę internetu. Chociaż dźwięk w internecie nie jest już dostępny wymaga wtyczki, tag audio niesie ze sobą znaczne ograniczenia zaawansowanych gier i interaktywnych aplikacji.

Web Audio API to wysokopoziomowy interfejs API JavaScript służący do przetwarzania o syntetyzowaniu dźwięku w aplikacjach internetowych. Celem tego interfejsu API jest obejmują możliwości stosowane we współczesnych silnikach dźwięku w grach. miksowanie, przetwarzanie i filtrowanie, które można znaleźć na nowoczesnych komputerach do produkcji dźwięku. Poniższy opis to subtelne wprowadzenie do za pomocą tego zaawansowanego interfejsu API.

Pierwsze kroki z AudioContext

Interfejs AudioContext służy do odtwarzania wszystkich dźwięków i zarządzania nimi. Aby produkować dźwięku przy użyciu interfejsu Web Audio API, utwórz co najmniej jedno źródło dźwięku. i połącz je z miejscem docelowym dźwięku zapewnianym przez AudioContext instancji. To połączenie nie musi być bezpośrednie i może przez dowolną liczbę pośrednich elementów AudioNodes, które pełnią funkcję przetwarzania dla sygnału dźwiękowego. Taka trasa jest opisana w większym w specyfikacji audio w internecie.

Pojedyncza instancja instancji AudioContext może obsługiwać wiele wejść dźwięku i złożonych wykresów audio, dlatego do każdego z nich użyjemy tylko jednej z nich. aplikację audio, którą tworzymy.

Ten fragment kodu tworzy AudioContext:

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

W przypadku starszych przeglądarek opartych na silniku WebKit użyj prefiksu webkit, tak jak w przypadku webkitAudioContext

Wiele ciekawych funkcji interfejsu Web Audio API, takich jak tworzenie AudioContext obsługuje węzły AudioNode i dekodowanie danych plików audio.

Wczytuję dźwięki

Interfejs Web Audio API używa AudioBuffer do obsługi krótkich i średnich filmów dźwięki. Podstawowym sposobem jest użycie obiektu XMLHttpRequest dla argumentu pobierania plików dźwiękowych.

Interfejs API obsługuje wczytywanie danych z plików audio w różnych formatach, takich jak: WAV, MP3, AAC, OGG i inne. Obsługa przeglądarek w różnych usługach formaty audio różnią się.

Ten fragment kodu pokazuje wczytywanie próbki dźwięku:

var dogBarkingBuffer = null;
var context = new AudioContext();

function loadDogSound(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
        dogBarkingBuffer = buffer;
    }, onError);
    }
    request.send();
}

Dane pliku audio są binarne (nie tekstowe), dlatego ustawiamy responseType prośby do: 'arraybuffer'. Więcej informacji na temat: ArrayBuffers, przeczytaj ten artykuł o XHR2.

Po zebraniu (niezakodowanych) danych z pliku audio można je zachować do późniejszego dekodowania lub można go zdekodować od razu za pomocą Metoda AudioContext decodeAudioData(). Ta metoda pobiera ArrayBuffer danych plików audio zapisanych w katalogach request.response i dekoduje go asynchronicznie (nie blokuje głównego wykonania JavaScriptu) w wątku).

Po zakończeniu decodeAudioData() wywołuje funkcję wywołania zwrotnego, która udostępnia zdekodowane dane audio PCM w postaci AudioBuffer.

Odtwarzam dźwięki

Prosty wykres audio
Prosty wykres audio

Gramy po załadowaniu co najmniej 1 AudioBuffers dźwięki. Załóżmy, że wczytaliśmy już AudioBuffer z dźwiękiem szczekanie psa i że proces ładowania się zakończył. Możemy wtedy grać w tym buforze kodem podanym poniżej.

var context = new AudioContext();

function playSound(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.noteOn(0);                          // play the source now
}

Ta funkcja playSound() może być wywoływana za każdym razem, gdy ktoś naciśnie klawisz lub kliknie coś myszą.

Funkcja noteOn(time) ułatwia ustawienie dokładnego harmonogramu dźwięku w przypadku gier i innych aplikacji, które mają kluczowe znaczenie. Aby jednak uzyskać działa zgodnie z planem, upewnij się, że bufory dźwięku jest wstępnie załadowany.

Abstrakcja interfejsu Web Audio API

Oczywiście lepiej byłoby stworzyć bardziej ogólny system wczytywania, które nie jest zakodowane na stałe, żeby wczytać taki dźwięk. Jest ich wiele różnych sposobów radzenia sobie z wieloma krótkimi i średnimi dźwiękami, Aplikacja lub gra audio mogłaby używać – oto jeden ze sposobów użycia interfejsu BufferLoader (nie jest to część standardu internetowego).

Poniżej znajdziesz przykład użycia klasy BufferLoader. Utwórzmy 2 elementy: AudioBuffers. a gdy tylko zostaną wczytane, zagrajmy je w tym samym czasie.

window.onload = init;
var context;
var bufferLoader;

function init() {
    context = new AudioContext();

    bufferLoader = new BufferLoader(
    context,
    [
        '../sounds/hyper-reality/br-jam-loop.wav',
        '../sounds/hyper-reality/laughter.wav',
    ],
    finishedLoading
    );

    bufferLoader.load();
}

function finishedLoading(bufferList) {
    // Create two sources and play them both together.
    var source1 = context.createBufferSource();
    var source2 = context.createBufferSource();
    source1.buffer = bufferList[0];
    source2.buffer = bufferList[1];

    source1.connect(context.destination);
    source2.connect(context.destination);
    source1.noteOn(0);
    source2.noteOn(0);
}

Jak radzić sobie z czasem: granie rytmicznie

Interfejs Web Audio API umożliwia programistom precyzyjne planowanie odtwarzania. Do jak to zademonstrować, nagrajmy prosty utwór rytmiczny. Prawdopodobnie najpowszechniej znany wzorzec perkusji to:

Prosty wzór bębnów rockowych
Prosty wzór bębnów rockowych

w których hihat gra co ósmą nutę, a kopnięcie i werbel grane są grane na zmianę co kwartał, po 4/4 sekund.

Przyjmijmy, że załadowaliśmy bufory kick, snare i hihat, jest to proste:

for (var bar = 0; bar < 2; bar++) {
    var time = startTime + bar * 8 * eighthNoteTime;
    // Play the bass (kick) drum on beats 1, 5
    playSound(kick, time);
    playSound(kick, time + 4 * eighthNoteTime);

    // Play the snare drum on beats 3, 7
    playSound(snare, time + 2 * eighthNoteTime);
    playSound(snare, time + 6 * eighthNoteTime);

    // Play the hi-hat every eighth note.
    for (var i = 0; i < 8; ++i) {
    playSound(hihat, time + i * eighthNoteTime);
    }
}

W tym przykładzie robimy tylko jedno powtórzenie, a nie niekończącą się pętlę, nuty. Funkcja playSound to metoda, która odtwarza w określonym czasie, w ten sposób:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

Zmienianie głośności dźwięku

Jedną z najbardziej podstawowych operacji na dźwięku jest zmienić jego głośność. Za pomocą interfejsu Web Audio API możemy kierować nasze źródło do jego miejsca docelowego za pomocą węzła AudioGainNode, aby manipulować głośność:

Wykres audio z węzłem wzmocnienia
Wykres audio z węzłem wzmocnienia

Tę konfigurację połączenia można skonfigurować w ten sposób:

// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);

Po skonfigurowaniu wykresu możesz programowo zmienić woluminu, modyfikując gainNode.gain.value w ten sposób:

// Reduce the volume.
gainNode.gain.value = 0.5;

Przenikanie między dwoma dźwiękami

Załóżmy teraz, że mamy nieco bardziej skomplikowany scenariusz, Włączasz wiele dźwięków, ale chcesz się między nimi przenikać. To jest jest to typowy przypadek dla DJ-a, w którym mamy dwa gramofony chcesz mieć możliwość przełączania się między źródłami dźwięku.

Możesz to zrobić za pomocą tego wykresu audio:

Wykres audio z 2 źródłami połączonymi przez węzły wzmocnienia
Wykres audio z 2 źródłami połączonymi przez węzły wzmocnienia

Aby to skonfigurować, tworzymy 2 węzły AudioGainNodes oraz za pomocą węzłów, używając mniej więcej takiej funkcji:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    source.buffer = buffer;
    // Turn on looping.
    source.loop = true;
    // Connect source to gain.
    source.connect(gainNode);
    // Connect gain to destination.
    gainNode.connect(context.destination);

    return {
    source: source,
    gainNode: gainNode
    };
}

Przenikanie o równej mocy

Naiwne, liniowe przenikanie powoduje spadek głośności podczas przesuwania między próbkami.

Przenikanie liniowe
Liniowy przenikanie

Aby rozwiązać ten problem, stosujemy równomierną krzywą mocy, w której odpowiednie krzywe wzmocnienia są nieliniowe i przecinają się w większym amplituda. Zmniejsza to liczbę spadków głośności między regionami audio, bardziej równomiernie przenikają się między regionami, które mogą być nieco bardziej co się zmienia.

Równe przejście mocy.
Równe przenikanie mocy

Przenikanie playlisty

Inną popularną aplikacją typu crossfader jest aplikacja do odtwarzania muzyki. Gdy zmienia się utwór, chcemy zanikać bieżący utwór i przyciemniać w celu uniknięcia drażniącego przejścia. Aby to zrobić, zaplanuj przechodzą w przyszłość. Możemy w tym celu użyć narzędzia setTimeout w harmonogramie, to nie jest dokładny. Dzięki interfejsowi Web Audio API mogą skorzystać z interfejsu AudioParam, aby zaplanować przyszłe wartości dla takie jak wartość wzmocnienia AudioGainNode.

W przypadku playlisty możemy więc przełączać się między utworami, planując na odtwarzany obecnie utworze oraz wzmocnienie następny, oba nieco przed zakończeniem odtwarzania bieżącego utworu:

function playHelper(bufferNow, bufferLater) {
    var playNow = createSource(bufferNow);
    var source = playNow.source;
    var gainNode = playNow.gainNode;
    var duration = bufferNow.duration;
    var currTime = context.currentTime;
    // Fade the playNow track in.
    gainNode.gain.linearRampToValueAtTime(0, currTime);
    gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
    // Play the playNow track.
    source.noteOn(0);
    // At the end of the track, fade it out.
    gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
    gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
    // Schedule a recursive track change with the tracks swapped.
    var recurse = arguments.callee;
    ctx.timer = setTimeout(function() {
    recurse(bufferLater, bufferNow);
    }, (duration - ctx.FADE_TIME) - 1000);
}

Interfejs Web Audio API zapewnia wygodny zestaw metod RampToValue do stopniowo zmieniać wartość parametru, np. linearRampToValueAtTime i exponentialRampToValueAtTime.

Choć funkcję czasu przejścia można wybrać z wbudowanej funkcji linearnej, i wykładniczych (jak powyżej), możesz też określić własną wartość, przez tablicę wartości przy użyciu funkcji setValueCurveAtTime.

Stosowanie prostego efektu filtra do dźwięku

Wykres audio z urządzeniem BiquadFilterNode
Wykres audio z użyciem BiquadFilterNode

Interfejs Web Audio API pozwala przesyłać dźwięk z jednego węzła audio do innego, tworząc potencjalnie złożony łańcuch procesorów, który będzie wymagał w formie efektów dźwiękowych.

Jednym ze sposobów jest umieszczenie obiektów BiquadFilterNode między dźwiękiem między źródłem a miejscem docelowym. Ten typ węzła audio może wykonywać różne dzięki filtrom niskobudżetowym, które można wykorzystać do tworzenia korektorów graficznych, a nawet o bardziej złożonych efektach. Chodzi głównie o wybór elementów, widmo częstotliwości dźwięku, który ma zostać uwydatniony i stłumiony.

Obsługiwane typy filtrów:

  • Filtr dolnoprzepustowy
  • Filtr górnoprzepustowy
  • Filtr pasma
  • Filtr niskich półek
  • Filtr wysokiej półki
  • Filtr szczytu
  • Filtr z wycięciem
  • Filtr wszystkich kart

Wszystkie filtry zawierają parametry określające liczbę zysk, częstotliwość stosowania filtra oraz współczynnik jakości. Filtr dolnoprzepustowy zachowuje niższy zakres częstotliwości, ale odrzuca wysoki częstotliwości. Punkt przerwania jest określany na podstawie wartości częstotliwości, a współczynnik Q jest bez jednostek i określa kształt wykres. Zysk dotyczy tylko niektórych filtrów, takich jak „Niska półka” czy a nie filtr dolnoprzepustowy.

Skonfigurujmy prosty filtr dolnoprzepustowy, który wyodrębni tylko podstawy z próbka dźwięku:

// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

Ustawienia częstotliwości trzeba dostosować tak, by działało skala logarytmiczna, ponieważ słyszenie człowieka działa na tej samej zasadzie (np. A4 to 440 Hz, a A5 880 Hz). Więcej informacji: FilterSample.changeFrequency w powyższym linku do kodu źródłowego.

Przykładowy kod umożliwia łączenie i odłączanie dynamicznie zmienia wykres AudioContext. Możemy odłączyć Węzły audio z wykresu po wywołaniu funkcji node.disconnect(outputNumber). Aby na przykład przekierować wykres z filtra, bezpośredniego połączenia, dzięki czemu:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

Dalsze nasłuchiwanie

Omówiliśmy podstawy interfejsu API, w tym m.in. wczytywanie i odtwarzanie dźwięku. przykłady. Stworzyliśmy wykresy audio z węzłami wzmocnienia i filtrami. optymalizowanie zaplanowanych dźwięków i parametrów audio, aby włączyć niektóre typowe dźwięki efekty. W tym momencie możesz przejść do tworzenia czytelnej sieci aplikacji audio!

Jeśli szukasz inspiracji, wielu deweloperów już świetnej pracy przy użyciu interfejsu Web Audio API. Jedne z moich ulubionych uwzględnij:

  • AudioJedit – działające w przeglądarce narzędzie do przycinania dźwięku, które wykorzystuje Linki bezpośrednie SoundCloud.
  • ToneCraft – sekwencer dźwięku, w którym dźwięki są tworzone przez nakładanie bloków 3D.
  • Plink – gra zespołowa do tworzenia muzyki, która korzysta z Web Audio i internetu Gniazda.