Generieren von Sound on the fly mit Javascript/html5

Lesezeit: 9 Minuten

Benutzer-Avatar
Johannes Hof

Ist es möglich, mit Javascript/html5 einen konstanten Soundstream zu erzeugen? Um beispielsweise eine ewige Sinuswelle zu erzeugen, hätte ich eine Callback-Funktion, die immer dann aufgerufen wird, wenn der Ausgabepuffer leer wird:

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

(Die Idee ist, damit einen interaktiven Synthesizer zu erstellen. Ich weiß nicht im Voraus, wie lange eine Taste gedrückt wird, daher kann ich keinen Puffer mit fester Länge verwenden.)

  • Seit diese Frage gestellt wurde, hat sich vieles geändert. Falls es dich noch interessiert, ich habe hier einen Artikel dazu geschrieben: marcgg.com/blog/2016/11/01/javascript-audio

    – Marcgg

    1. November 2016 um 11:17 Uhr

  • Versuchen Tone.js. Ein Web-Audio-Framework zum Erstellen interaktiver Musik im Browser. Diese Bibliothek generiert Audio “on the fly”, wie Sie es angefordert haben. Viel Glück!

    – Ron S

    7. März 2018 um 14:57 Uhr

Benutzer-Avatar
snapfractalpop

Du kannst den … benutzen Web-Audio-API in den meisten Browsern jetzt (außer IE und Opera Mini).

Probieren Sie diesen Code aus:

// 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

Wenn Sie die Lautstärke verringern möchten, können Sie Folgendes tun:

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

Ich habe das meiste davon durch Experimentieren mit Chrom beim Lesen der Arbeitsentwurf der Web-Audio-APIdie ich über den Link von @brainjam gefunden habe.

Ich hoffe das hilft. Schließlich ist es sehr hilfreich, die verschiedenen Objekte im Chrome-Inspektor (Strg-Umschalt-I) zu inspizieren.

  • Wie kann ich wieder anfangen, nachdem ich es gestoppt habe? Es heißt „Start“ konnte nicht auf „AudioScheduledSourceNode“ ausgeführt werden: Start kann nicht mehr als einmal aufgerufen werden. Gibt es alternative Funktionen für den Fall, dass ich abhängig von bestimmten Bedingungen starten und stoppen möchte? Resume() und Suspend() scheinen nicht zu funktionieren

    – devN

    11. August 2017 um 5:35 Uhr


  • @devN das ist beabsichtigt. Sie können jedoch eine Funktion erstellen, die jedes Mal neue Knoten erstellt.

    – snapfractalpop

    11. August 2017 um 13:07 Uhr

Benutzer-Avatar
Nick

Verwenden des HTML5-Audioelements

Browserübergreifendes generatives anhaltendes Audio mit JavaScript und der audio Element ist derzeit nicht möglich, wie Steven Wittens Anmerkungen in einem Blogbeitrag zum Erstellen eines JavaScript-Synthesizers:

“… es gibt keine Möglichkeit, synthetisierte Audioblöcke für eine nahtlose Wiedergabe in die Warteschlange zu stellen”.

Verwenden der Web-Audio-API

Das Web-Audio-API wurde entwickelt, um die JavaScript-Audiosynthese zu erleichtern. Das Mozilla Developer Network hat a Webbasierter Tongenerator das funktioniert in Firefox 4+ [demo 1]. Fügen Sie diese beiden Zeilen zu diesem Code hinzu und Sie haben einen funktionierenden Synthesizer mit generativem, anhaltendem Audio auf Tastendruck [demo 2 – works in Firefox 4 only, click the ‘Results’ area first, then press any key]:

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

Die der BBC Seite auf der Web Audio API ist auch eine Überprüfung wert. Leider erstreckt sich die Unterstützung für die Web Audio API noch nicht auf andere Browser.

Mögliche Problemumgehungen

Um derzeit einen Cross-Browser-Synthesizer zu erstellen, müssen Sie wahrscheinlich auf voraufgezeichnetes Audio zurückgreifen, indem Sie:

  1. Verwendung langer voraufgezeichneter OGG/MP3-Sample-Töne, die separat eingebettet werden audio Elemente und starten und stoppen sie auf Tastendruck.
  2. Einbetten einer SWF-Datei mit den Audioelementen und Steuern der Wiedergabe über JavaScript. (Dies scheint die Methode zu sein, die die Google-Les-Paul-Doodle beschäftigt.)

Benutzer-Avatar
CaptureWiz

Sicher! Sie könnten den Tonsynthesizer in dieser Demo verwenden:

Geben Sie hier die Bildbeschreibung ein

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>

Habe Spaß!

Die Lösung habe ich von Houshalter hier: Wie bringe ich Javascript zum Piepsen?

Sie können den Code hier klonen und optimieren:
Tonsynthesizer-Demo auf JS Bin

Kompatible Browser:

  • Chrome für Mobilgeräte und Desktops
  • Firefox für Mobilgeräte und Desktops Opera für Mobilgeräte, Minis und Desktops
  • Android-Browser
  • Microsoft Edge-Browser
  • Safari auf dem iPhone oder iPad

Nicht kompatibel

  • Internet Explorer Version 11 (funktioniert aber im Edge-Browser)

Benutzer-Avatar
Brainjam

Die Web-Audio-API kommt zu Chrome. Sehen http://googlechrome.github.io/web-audio-samples/samples/audio/index.html

Befolgen Sie dort die Anweisungen in “Getting Started” und sehen Sie sich dann die sehr beeindruckenden Demos an.

Aktualisierung (2017): Inzwischen ist dies eine viel ausgereiftere Schnittstelle. Die API ist unter dokumentiert https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

Benutzer-Avatar
Kamil Kielczewski

Sie können wav-e-Dateien im Handumdrehen generieren und abspielen (Quelle)

// 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()

Hier ist Visualisierung

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
  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>

Benutzer-Avatar
Mara Maya

Dies ist keine wirkliche Antwort auf Ihre Frage, da Sie nach einer JavaScript-Lösung gefragt haben, aber Sie können ActionScript verwenden. Es sollte auf allen gängigen Browsern laufen.

Sie können ActionScript-Funktionen aus JavaScript heraus aufrufen.

Auf diese Weise können Sie die ActionScript-Sounderzeugungsfunktionen umschließen und eine JavaScript-Implementierung daraus erstellen. Verwenden Sie einfach Adobe Flex, um eine winzige SWF-Datei zu erstellen, und verwenden Sie diese dann als Backend für Ihren JavaScript-Code.

Benutzer-Avatar
Jakow L

Das ist es, wonach ich ewig gesucht habe und am Ende habe ich es selbst geschafft, wie ich es wollte. Vielleicht gefällt es dir auch. Einfacher Slider mit Frequenz und Push on/off:

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" />

1005890cookie-checkGenerieren von Sound on the fly mit Javascript/html5

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy