2015-06-03 5 views
11

Sto lavorando attraverso questo impressionante articolo: https://jackschaedler.github.io/circles-sines-signals/dft_introduction.htmlforme d'onda personalizzato in audio web API

voglio usare oggetto PeriodicWave del Web API Audio per implementare questa demo: enter image description here

Tuttavia, quando ho creato un un'onda periodica con queste impostazioni:

var real = new Float32Array([0,0,1,0,1]); 
var imag = new Float32Array(real.length); 
var customWave = context.createPeriodicWave(real,imag); 
osc.setPeriodicWave(customWave); 

ho uscita un'onda che assomiglia a questo:

enter image description here Ecco il codice completo: http://jsbin.com/zaqojavixo/4/edit Per vedere la forma d'onda, si prega di premere riprodurre il suono alcune volte.

Credo che questi dovrebbero corrispondere, per cui qui sono le mie domande:

  1. mi sto perdendo qualcosa di fondamentale sulla teoria qui o sto semplicemente di attuazione in modo errato? L'oggetto PeriodicWave dovrebbe fare la stessa cosa illustrata nell'articolo?
  2. Se sto prendendo l'approccio sbagliato, come potrei implementare questo diagramma nell'API Web Audio? Sono stato in grado di eguagliare di seguito collegando due diverse onde sinusoidali di frequenze diverse allo stesso nodo di guadagno - come è diverso dall'usare l'oggetto PeriodicWave?
  3. Sono nuovo a DSP e API Web Audio - qualsiasi lettura suggerita sarebbe apprezzata!
  4. In secondo luogo, nel mio esempio, devo premere il pulsante "Riproduci il suono" un paio di volte prima che i dati corretti vengano disegnati sulla tela: l'analizzatore sembra essere dietro l'oscillatore, anche se analyser.getFloatTimeDomainData() è chiamato dopo aver avviato l'oscillatore qualche idea su cosa sta succedendo qui?

Edit: Come notato nei commenti, il mio grafico è a testa in giù (sulla tela 0,0 è l'angolo in alto a sinistra).

risposta

9

noti che il primo array definisce i termini coseno, secondo i termini seno:

Il real parametro rappresenta un array di termini di coseno (tradizionalmente i termini A). Nella terminologia audio, il primo elemento (indice 0) è lo spostamento DC della forma d'onda periodica. Il secondo elemento (indice 1) rappresenta la frequenza fondamentale. Il terzo elemento rappresenta il primo overtone e così via. Il primo elemento viene ignorato e le implementazioni devono impostarlo su zero internamente.

Il parametro imag rappresenta una matrice di termini seno (tradizionalmente i termini B). Il primo elemento (indice 0) deve essere impostato su zero (e verrà ignorato) poiché questo termine non esiste nella serie di Fourier. Il secondo elemento (indice 1) rappresenta la frequenza fondamentale. Il terzo elemento rappresenta il primo soprassegno e così via.

Source

Si vedrà che si ottiene la forma d'onda atteso ma "invertita" (disegnato a testa in giù, grazie al @Julian per la segnalazione nella sua risposta - fissa sotto):

snap

(ho inserito il codice qui con gli array scambiati :)
aggiornato problemi di disegno fisso nel codice originale

//setup audio context 
 
window.AudioContext = window.AudioContext || window.webkitAudioContext; 
 
var context = new window.AudioContext(); 
 

 
//create nodes 
 
var osc; //create in event listener so we can press the button more than once 
 
var masterGain = context.createGain(); 
 
var analyser = context.createAnalyser(); 
 

 
//routing 
 
masterGain.connect(analyser); 
 
analyser.connect(context.destination); 
 

 
var isPlaying = false; 
 

 
//draw function for canvas 
 
function drawWave(analyser, ctx) { 
 
    
 
    var buffer = new Float32Array(1024), 
 
     w = ctx.canvas.width; 
 
    
 
    ctx.strokeStyle = "#777"; 
 
    ctx.setTransform(1,0,0,-1,0,100.5); // flip y-axis and translate to center 
 
    ctx.lineWidth = 2; 
 
    
 
    (function loop() { 
 
    analyser.getFloatTimeDomainData(buffer); 
 
    
 
    ctx.clearRect(0, -100, w, ctx.canvas.height); 
 

 
    ctx.beginPath(); 
 
    ctx.moveTo(0, buffer[0] * 90); 
 
    for (var x = 2; x < w; x += 2) ctx.lineTo(x, buffer[x] * 90); 
 
    ctx.stroke(); 
 
    
 
    if (isPlaying) requestAnimationFrame(loop) 
 
    })(); 
 
} 
 

 
//button trigger 
 
$(function() { 
 
    var c = document.getElementById('scope'), 
 
     ctx = c.getContext("2d"); 
 
    
 
    c.height = 200; 
 
    c.width = 600; 
 
    
 
    // make 0-line permanent as background 
 
    ctx.moveTo(0, 100.5); 
 
    ctx.lineTo(c.width, 100.5); 
 
    ctx.stroke(); 
 
    c.style.backgroundImage = "url(" + c.toDataURL() + ")"; 
 
    
 
    $('button').on('mousedown', function() { 
 
    osc = context.createOscillator(); 
 
    //osc settings 
 
    osc.frequency.value = 220; 
 
    var imag= new Float32Array([0,0,1,0,1]); // sine 
 
    var real = new Float32Array(imag.length); // cos 
 
    var customWave = context.createPeriodicWave(real, imag); // cos,sine 
 
    osc.setPeriodicWave(customWave); 
 

 
    osc.connect(masterGain); 
 
    osc.start(); 
 
    isPlaying = true; 
 
    
 
    drawWave(analyser, ctx); 
 
    }); 
 

 
    $('button').on('mouseup', function() { 
 
    isPlaying = false; 
 
    osc.stop(); 
 
    }); 
 
});
button {position:fixed;left:10px;top:10px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<button>Play the sound</button> 
 
<canvas id='scope'></canvas>

2

L'unica differenza tra la demo e l'output è la relazione di fase tra i due toni. La demo è y=sin(x) + sin(2x) e la tua è y=sin(x) + sin(2x + pi/2). Non mi sembra chiaro da dove provenga questo cambiamento di fase, ma non penso che sia qualcosa che hai fatto.

Ecco alcuni trame da wolframio alfa:

y=sin(x) + sin(2x)

y=sin(x) + sin(2x + pi/2)

1

In createPeriodicWave matrice real è la parte coseno (o termine), mentre il imag matrice (il termine b) è la parte sinusoidale.

See: http://en.wikipedia.org/wiki/Fourier_series

Penso che tu hai appena invertita i due. Nell'articolo si legge:

dove Ai è l'ampiezza del seno-esima e fi è la frequenza di il seno esimo.

Così è il vostro imag array (parte sine) che dovrebbe essere [0,0,1,0,1] e la vostra vera matrice dovrebbe essere [0,0,0,0,0].

Ti piace questa:

var imag = new Float32Array([0,0,1,0,1]); 
var real = new Float32Array(real.length); 

provare in vostro jsbin Potrai funziona, unica cosa che sarà essere invertita, perché si sta disegnando in negativo (cioè 0 coordinare è il top, non il fondo). Per avere ciò che c'è nell'articolo, puoi disegnare al contrario o avere il tuo array immag in questo modo: [0,0, -1,0, -1].

Se si vuole giocare un po 'con diversa configurazione imag e real e vedere i risultati, si può vedere qui: http://themusictoolbox.net/waves/