訳注: Web Audio API による実装
翻訳したついでに元記事で実装された機能を Web Audio API で作り直してみました。
Part 2
NoteGenerator
class Part2NoteGenerator
constructor: ()->
@context = new AudioContext
@source = @context.createBufferSource()
@processor = @context.createScriptProcessor(1024, 1, 1)
@processor.onaudioprocess = @onaudioprocess
@x = 0
@angle = 2 * Math.PI * 440 / 44100
onaudioprocess: (e)=>
data = e.outputBuffer.getChannelData(0)
for i in [0...e.outputBuffer.length]
data[i] = Math.sin(@x)
@x += @angle
play: ()=>
@source.connect(@processor)
@processor.connect(@context.destination)
pause: ()=>
@processor.disconnect()
@source.disconnect()
part2NoteGenerator = new Part2NoteGenerator
document.querySelector("#part2-note-generator-play").onclick = part2NoteGenerator.play
document.querySelector("#part2-note-generator-pause").onclick = part2NoteGenerator.pause
WaveOutput
class Part2WaveOutput
constructor: (file)->
@file = file
@file.onchange = @onfilechange
@context = new AudioContext()
@source = @context.createBufferSource()
@reader = new FileReader()
@reader.onload = @onfileload
onfilechange: (e)=>
@reader.readAsArrayBuffer(e.target.files[0])
onfileload: ()=>
@context.decodeAudioData @reader.result, (buffer)=>
@source.buffer = buffer
@source.connect(@context.destination)
@source.loop = true
play: ()=>
@source.start(0)
pause: ()=>
@source.stop(0)
part2WaveOutput = new Part2WaveOutput document.querySelector("#part2-wave-output-file")
document.querySelector("#part2-wave-output-play").onclick = part2WaveOutput.play
document.querySelector("#part2-wave-output-pause").onclick = part2WaveOutput.pause
Part 3
PlotExample
class Part3PlotExample
constructor: (file, canvas)->
@plot = new Plot(canvas, 512, 512)
@file = file
@file.onchange = @onfilechange
@context = new AudioContext()
@reader = new FileReader()
@reader.onload = @onfileload
onfilechange: (e)=>
@reader.readAsArrayBuffer(e.target.files[0])
onfileload: ()=>
@context.decodeAudioData @reader.result, (buffer)=>
@plot.plot(buffer.getChannelData(0), 44100 / 100, "red")
file = document.querySelector("#part3-plot-example-file")
canvas = document.querySelector("#part3-plot-example-canvas")
part3PlotExample = new Part3PlotExample file, canvas
Part 4
MP3Output
(Part 2 の WaveOutput 参照)
RealTimePlot
class Part4RealTimePlot
constructor: (file, canvas, marker)->
@SAMPLES_PER_PIXEL = 400
@plot = new Plot(canvas, 512, 512, marker)
@file = file
@file.onchange = @onfilechange
@context = new AudioContext()
@source = @context.createBufferSource()
@reader = new FileReader()
@reader.onload = @onfileload
onfilechange: (e)=>
@reader.readAsArrayBuffer(e.target.files[0])
onfileload: ()=>
@context.decodeAudioData @reader.result, (buffer)=>
@plot.plot(buffer.getChannelData(0), @SAMPLES_PER_PIXEL, "red")
@source.buffer = buffer
@source.connect(@context.destination)
@source.loop = true
play: ()=>
@startTime = @context.currentTime
@source.start(0)
window.requestAnimationFrame(@onAnimationFrame)
onAnimationFrame: ()=>
elapse = @context.currentTime - @startTime
@plot.setMarker(elapse * 44100 / @SAMPLES_PER_PIXEL, "white")
window.requestAnimationFrame(@onAnimationFrame)
file = document.querySelector("#part4-real-time-plot-file")
canvas = document.querySelector("#part4-real-time-plot-canvas")
part4RealTimePlot = new Part4RealTimePlot file, canvas
document.querySelector("#part4-real-time-plot-play").onclick = part4RealTimePlot.play
Part 5
FourierTransformPlot
class Part5FourierTransformPlot
constructor: (canvas)->
@plot = new Plot(canvas, 512, 512)
@context = new AudioContext
@source = @context.createBufferSource()
@analyser = @context.createAnalyser()
@analyser.fftSize = 1024
@processor = @context.createScriptProcessor(1024, 1, 1)
@processor.onaudioprocess = @onaudioprocess
@x = 0
@angle = 2 * Math.PI * 440 / 44100
onaudioprocess: (e)=>
data = e.outputBuffer.getChannelData(0)
for i in [0...e.outputBuffer.length]
data[i] = Math.sin(@x)
@x += @angle
play: ()=>
@source.connect(@processor)
@processor.connect(@analyser)
@analyser.connect(@context.destination)
window.requestAnimationFrame @onAnimationFrame
pause: ()=>
@processor.disconnect()
@source.disconnect()
onAnimationFrame: ()=>
data = new Uint8Array(256)
@analyser.getByteFrequencyData(data)
@plot.plot(data, 0.5, "red")
window.requestAnimationFrame @onAnimationFrame
canvas = document.querySelector("#part5-fourier-transform-plot-canvas")
part5FourierTransformPlot = new Part5FourierTransformPlot(canvas)
document.querySelector("#part5-fourier-transform-plot-play").onclick = part5FourierTransformPlot.play
document.querySelector("#part5-fourier-transform-plot-pause").onclick = part5FourierTransformPlot.pause
Part 6
SpectralFlux
class Part6SpectralFlux
constructor: (file, canvas, marker)->
@SAMPLES_PER_PIXEL = 512
@plot = new Plot(canvas, 512, 512, marker)
@file = file
@file.onchange = @onfilechange
@context = new AudioContext()
@source = @context.createBufferSource()
@reader = new FileReader()
@reader.onload = @onfileload
onfilechange: (e)=>
@reader.readAsArrayBuffer(e.target.files[0])
onfileload: ()=>
@context.decodeAudioData @reader.result, (buffer)=>
data = buffer.getChannelData(0)
spectralFlux = []
lastSpectrum = []
for i in [0...data.length/1024-1]
fft = new FFT(1024, 44100)
samples = []
for j in [i*1024...i*1024+1024]
samples.push data[j]
fft.forward samples
spectrum = fft.spectrum
flux = 0
for j in [0...spectrum.length]
flux += (spectrum[j] - (lastSpectrum[j] || 0))
spectralFlux.push flux
lastSpectrum = spectrum
@plot.plot(spectralFlux, 0.5, "green")
@source.buffer = buffer
@source.connect(@context.destination)
@source.loop = true
start: ()=>
@startTime = @context.currentTime
@source.start(0)
window.requestAnimationFrame(@onAnimationFrame)
stop: ()=>
@source.stop(0)
onAnimationFrame: ()=>
elapse = @context.currentTime - @startTime
@plot.setMarker(elapse * 44100 / @SAMPLES_PER_PIXEL, "white")
window.requestAnimationFrame(@onAnimationFrame)
file = document.querySelector("#part6-spectral-flux-file")
canvas = document.querySelector("#part6-spectral-flux-canvas")
part6SpectralFlux = new Part6SpectralFlux file, canvas
document.querySelector("#part6-spectral-flux-start").onclick = part6SpectralFlux.start
document.querySelector("#part6-spectral-flux-stop").onclick = part6SpectralFlux.stop