Создание звука на лету с помощью JavaScript / HTML5 - PullRequest
64 голосов
/ 14 июня 2011

Можно ли генерировать постоянный звуковой поток с помощью javascript / html5? Например, чтобы генерировать вечную синусоидальную волну, у меня будет функция обратного вызова, которая будет вызываться всякий раз, когда выходной буфер становится пустым:

function getSampleAt(timestep)
{
    return Math.sin(timestep);
}

(Идея состоит в том, чтобы использовать это для создания интерактивного синтезатора. Я заранее не знаю, как долго будет нажиматься клавиша, поэтому я не могу использовать буфер фиксированной длины)

Ответы [ 7 ]

52 голосов
/ 15 мая 2013

Вы можете использовать Web Audio API в большинстве браузеров (, исключая IE и Opera Mini ).

Попробуйте этот код:

// one context per document
var context = new (window.AudioContext || window.webkitAudioContext)();
var osc = context.createOscillator(); // instantiate an oscillator
osc.type = 'sine'; // this is the default - also square, sawtooth, triangle
osc.frequency.value = 440; // Hz
osc.connect(context.destination); // connect it to the destination
osc.start(); // start the oscillator
osc.stop(context.currentTime + 2); // stop 2 seconds after the current time

Если вы хотите уменьшить громкость, вы можете сделать что-то вроде этого:

var context = new webkitAudioContext();
var osc = context.createOscillator();
var vol = context.createGain();

vol.gain.value = 0.1; // from 0 to 1, 1 full volume, 0 is muted
osc.connect(vol); // connect osc to vol
vol.connect(context.destination); // connect vol to context destination
osc.start(context.currentTime + 3); // start it three seconds from now

Большую часть этого я получил, экспериментируя с хромом, читая Рабочий проект API Web Audio *1018*, который я нашел по ссылке @brainjam.

Надеюсь, это поможет. Наконец, очень полезно проверять различные объекты в Chrome Inspector (ctrl-shift-i).

25 голосов
/ 14 июня 2011

Использование аудиоэлемента HTML5

Создание кросс-браузерного устойчивого звука с использованием JavaScript и элемента audio в настоящее время невозможно, как отмечает Стивен Виттенс в своем блоге о создании синтезатора JavaScript:

«... нет способа поставить в очередь фрагменты синтезированного звука для бесшовного воспроизведения».

Использование API Web Audio

API Web Audio был разработан для упрощения синтеза аудио JavaScript. Сеть разработчиков Mozilla имеет веб-генератор тонов , который работает в Firefox 4+ [ demo 1 ]. Добавьте эти две строки в этот код, и вы получите работающий синтезатор с генерируемым устойчивым звуком при нажатии клавиш [ demo 2 - работает только в Firefox 4, сначала щелкните область «Результаты», а затем нажмите любую клавишу]:

window.onkeydown = start;  
window.onkeyup = stop;

Страница BBC в API Web Audio также заслуживает рассмотрения. К сожалению, поддержка API Web Audio пока не распространяется на другие браузеры.

Возможные обходные пути

Чтобы создать кросс-браузерный синтезатор в настоящее время, вам, вероятно, придется прибегнуть к предварительно записанному аудио:

  1. Используя длинные предварительно записанные сэмплы ogg / mp3, встраивая их в отдельные audio элементы, запуская и останавливая их при нажатии клавиши.
  2. Встраивание SWF-файла, содержащего аудиоэлементы, и управление воспроизведением через JavaScript. (Похоже, что этот метод используется Google Les Paul Doodle .)
11 голосов
/ 14 июня 2011

API Web Audio входит в Chrome. Смотри http://googlechrome.github.io/web-audio-samples/samples/audio/index.html

Следуйте инструкциям в разделе «Начало работы» и ознакомьтесь с очень впечатляющими демонстрациями.

Обновление (2017): на данный момент это гораздо более зрелый интерфейс. API задокументирован на https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

8 голосов
/ 12 декабря 2016

Конечно! Вы можете использовать синтезатор тонов в этой демонстрации:

enter image description here

audioCtx = new(window.AudioContext || window.webkitAudioContext)();

show();

function show() {
  frequency = document.getElementById("fIn").value;
  document.getElementById("fOut").innerHTML = frequency + ' Hz';

  switch (document.getElementById("tIn").value * 1) {
    case 0: type = 'sine'; break;
    case 1: type = 'square'; break;
    case 2: type = 'sawtooth'; break;
    case 3: type = 'triangle'; break;
  }
  document.getElementById("tOut").innerHTML = type;

  volume = document.getElementById("vIn").value / 100;
  document.getElementById("vOut").innerHTML = volume;

  duration = document.getElementById("dIn").value;
  document.getElementById("dOut").innerHTML = duration + ' ms';
}

function beep() {
  var oscillator = audioCtx.createOscillator();
  var gainNode = audioCtx.createGain();

  oscillator.connect(gainNode);
  gainNode.connect(audioCtx.destination);

  gainNode.gain.value = volume;
  oscillator.frequency.value = frequency;
  oscillator.type = type;

  oscillator.start();

  setTimeout(
    function() {
      oscillator.stop();
    },
    duration
  );
};
frequency
<input type="range" id="fIn" min="40" max="6000" oninput="show()" />
<span id="fOut"></span><br>
type
<input type="range" id="tIn" min="0" max="3" oninput="show()" />
<span id="tOut"></span><br>
volume
<input type="range" id="vIn" min="0" max="100" oninput="show()" />
<span id="vOut"></span><br>
duration
<input type="range" id="dIn" min="1" max="5000" oninput="show()" />
<span id="dOut"></span>
<br>
<button onclick='beep();'>Play</button>

Веселись!

Я получил решение от Хоушальтера здесь: Как сделать звуковой сигнал Javascript?

Вы можете клонировать и настроить код здесь: Демонстрация тонального синтезатора на JS Bin

Совместимые браузеры:

  • Chrome для мобильных и настольных ПК
  • Firefox для мобильных и настольных ПК Opera для мобильных, мини и настольных
  • Android-браузер
  • Браузер Microsoft Edge
  • Safari на iPhone или iPad

Не совместимо

  • Internet Explorer версии 11 (но работает в браузере Edge)
2 голосов
/ 07 мая 2019

Вы можете сгенерировать файл wav-e на лету и воспроизвести его ( src )

// Legend
// DUR - duration in seconds   SPS - sample per second (default 44100)
// NCH - number of channels    BPS - bytes per sample

// t - is number from range [0, DUR), return number in range [0, 1]
function getSampleAt(t,DUR,SPS)
{
    return Math.sin(6000*t); 
}

function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  let size = DUR*NCH*SPS*BPS; 
  let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
  let p = (...a) => a.map( b=> put(...[b].flat()) ).join(''); 
  let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
  
  for (let i = 0; i < DUR*SPS; i++) {
    let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
    data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
  }
  
  return "data:Audio/WAV;base64," + btoa(data);
}


var WAV = new Audio( genWAVUrl(getSampleAt,5) ); // 5s
WAV.setAttribute("controls", "controls");
document.body.appendChild(WAV);
//WAV.play()

Вот визуализация

function getSampleAt(t,DUR,SPS)
{
    return 0.5+Math.sin(15*t)/(1+t*t); 
}


// ----------------------------------------------

function genWAVUrl(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  let size = DUR*NCH*SPS*BPS; 
  let put = (n,l=4) => [(n<<24),(n<<16),(n<<8),n].filter((x,i)=>i<l).map(x=> String.fromCharCode(x>>>24)).join('');
  let p = (...a) => a.map( b=> put(...[b].flat()) ).join(''); 
  let data = `RIFF${put(44+size)}WAVEfmt ${p(16,[1,2],[NCH,2],SPS,NCH*BPS*SPS,[NCH*BPS,2],[BPS*8,2])}data${put(size)}`
  
  for (let i = 0; i < DUR*SPS; i++) {
    let f= Math.min(Math.max(fun(i/SPS,DUR,SPS),0),1);
    data += put(Math.floor( f * (2**(BPS*8)-1)), BPS);
  }
  
  return "data:Audio/WAV;base64," + btoa(data);
}

function draw(fun, DUR=1, NCH=1, SPS=44100, BPS=1) {
  time.innerHTML=DUR+'s';
  time.setAttribute('x',DUR-0.3);
  svgCh.setAttribute('viewBox',`0 0 ${DUR} 1`);
  let p='', n=100; // n how many points to ommit
  for (let i = 0; i < DUR*SPS/n; i++) p+= ` ${DUR*(n*i/SPS)/DUR}, ${1-fun(n*i/SPS, DUR,SPS)}`;
  chart.setAttribute('points', p);
}

function frame() {
  let t=WAV.currentTime;
  point.setAttribute('cx',t)
  point.setAttribute('cy',1-getSampleAt(t))
  window.requestAnimationFrame(frame);
}

function changeStart(e) {
  var r = e.target.getBoundingClientRect();
  var x = e.clientX - r.left;
  WAV.currentTime = dur*x/r.width;
  WAV.play()
}

var dur=5; // seconds 
var WAV = new Audio(genWAVUrl(getSampleAt,dur));
draw(getSampleAt,dur);
frame();
.chart { border: 1px dashed #ccc; }
.axis { font-size: 0.2px}
audio { outline: none; }
Click at blue line (make volume to max):
<svg class="chart" id="svgCh" onclick="changeStart(event)">    
  <circle cx="0" cy="-1" r="0.05" style="fill: rgba(255,0,0,1)" id="point"></circle>
  <polyline id="chart" fill="none" stroke="#0074d9" stroke-width="0.01" points=""/>
  <text x="0.03" y="0.9" class="axis">0</text>
  <text x="0.03" y="0.2" class="axis">1</text>
  <text x="4.8" y="0.9" class="axis" id="time"></text>
</svg><br>
1 голос
/ 16 декабря 2015

Это не настоящий ответ на ваш вопрос, потому что вы запросили решение JavaScript, но вы можете использовать ActionScript.Он должен работать во всех основных браузерах.

Вы можете вызывать функции ActionScript из JavaScript.*

Вызов API-интерфейса ActionScript. JavaScript

Таким образом, вы можете обернуть функции генерации звука в ActionScript и реализовать их в JavaScript.Просто используйте Adobe Flex для создания крошечного SWF-файла, а затем используйте его в качестве бэкенда для своего кода JavaScript.

0 голосов
/ 29 марта 2017

Это то, что я искал вечно, и в конце концов мне удалось сделать это самому, как я хотел. Может тебе тоже понравится. Простой слайдер с частотой и нажатием вкл / выкл:

buttonClickResult = function () {
	var button = document.getElementById('btn1');

	button.onclick = function buttonClicked()  {

		if(button.className=="off")  {
			button.className="on";
			oscOn ();
		}

		else if(button.className=="on")  {
			button.className="off";
			oscillator.disconnect();
		}
	}
};

buttonClickResult();

var oscOn = function(){

	window.AudioContext = window.AudioContext || window.webkitAudioContext;
	var context = new AudioContext();
	var gainNode = context.createGain ? context.createGain() : context.createGainNode();

	//context = new window.AudioContext();
	oscillator = context.createOscillator(),
			oscillator.type ='sine';

	oscillator.frequency.value = document.getElementById("fIn").value;
	//gainNode = createGainNode();
	oscillator.connect(gainNode);
	gainNode.connect(context.destination);
	gainNode.gain.value = 1;
	oscillator.start(0);
};
<p class="texts">Frekvence [Hz]</p>
<input type="range" id="fIn" min="20" max="20000" step="100" value="1234" oninput="show()" />
<span id="fOut"></span><br>
<input class="off" type="button" id="btn1" value="Start / Stop" />
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...