翻訳したついでに元記事で実装された機能を 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

Part 7