Skip to content

Instantly share code, notes, and snippets.

@reepush
Last active August 29, 2015 14:27
Show Gist options
  • Save reepush/57bb074ab874fbc9e8bd to your computer and use it in GitHub Desktop.
Save reepush/57bb074ab874fbc9e8bd to your computer and use it in GitHub Desktop.
ШРИ-2015 (задание 3)
mixin info-item(name)
.item
span.name #{name}:
span(bind='app.info.#{name}')
.App(ondrop='app.loadFile(event)')
.container
label.load-file
span.fa.fa-music
input(onchange='app.loadFile(event)' type='file')
.info-container
.info
+info-item('file')
+info-item('artist')
+info-item('title')
+info-item('album')
.controls(bind-class='app.player.status')
a.play(onclick='app.player.play()'
class='fa fa-play')
a.pause(onclick='app.player.pause()'
class='fa fa-pause')
canvas.visualization
function init() {
window.app = new App()
setupBindings()
app.loadFile({
type: 'url',
data: {
file: 'Fleetwood Mac - Little Lies.mp3',
artist: 'Fleetwood Mac',
title: 'Little Lies',
album: 'Tango In The Night',
url: 'http://push.org.ru/grisha/files/Fleetwood Mac - Little Lies.mp3'
}
})
}
function App() {
var canvas = $('canvas')[0]
var player = this.player = new Player(canvas)
this.info = {}
}
App.prototype.loadFile = function(event) {
var instance = this
var file, info, buffer
if (event.type != 'url') {
var reader = new FileReader()
var file = (event.type == 'change')
? event.target.files[0]
: event.dataTransfer.files[0]
this.player.getInfo(file, this.info)
this.info.file = file.name
reader.onload = function(event) {
instance.player.load(event.target.result)
}
reader.readAsArrayBuffer(file)
} else {
var data = event.data
this.info.file = data.file
this.info.artist = data.artist
this.info.title = data.title
this.info.album = data.album
request = new XMLHttpRequest()
request.open('GET', data.url, true)
request.responseType = 'arraybuffer'
request.onload = function() {
instance.player.load(request.response)
}
request.send()
}
return false
}
function Player(canvas) {
this.context = new AudioContext()
this.time = 0
this.analyser = this.context.createAnalyser()
this.visualizer = new Visualizer(this.analyser, canvas)
this.status = 'playing'
}
Player.prototype.load = function(audioData) {
var context = this.context
var instance = this
this.pause()
context.decodeAudioData(audioData, function(buffer) {
instance.buffer = buffer
instance.time = 0
instance.play()
})
}
Player.prototype.pause = function() {
this.time = this.context.currentTime
this.status = 'paused'
this.source && this.source.stop()
this.visualizer.stop()
}
Player.prototype.play = function() {
var context = this.context
var source = this.source = context.createBufferSource()
source.buffer = this.buffer
source.loop = true
source.connect(this.analyser)
this.analyser.connect(context.destination)
this.status = 'playing'
source.start(0, this.time)
this.visualizer.start()
}
Player.prototype.getInfo = function(file, info) {
id3(file, function(err, tags) {
info.title = tags.title
info.artist = tags.artist
info.album = tags.album
})
}
function Visualizer(analyser, canvas) {
this.width = canvas.width
this.height = canvas.height
this.canvas = canvas.getContext('2d')
this.analyser = analyser
this.RAF = undefined
}
Visualizer.prototype.start = function() {
var instance = this
var width = this.width
var height = this.height
var analyser = this.analyser
var canvas = this.canvas
analyser.fftSize = 512
var bufferLength = analyser.frequencyBinCount
var dataArray = new Uint8Array(bufferLength)
canvas.clearRect(0, 0, width, height)
draw()
function draw() {
instance.RAF = requestAnimationFrame(draw)
analyser.getByteFrequencyData(dataArray)
canvas.fillStyle = 'rgb(255, 255, 255)'
canvas.fillRect(0, 0, width, height)
var barWidth = (width / bufferLength) * 2.5
barWidth = Math.floor(barWidth)
var barHeight
var x = 0
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i]
canvas.fillStyle = 'rgb(0, 0, 0)'
canvas.fillRect(x, height-barHeight/2, barWidth, barHeight/2)
x += barWidth + 1
}
}
}
Visualizer.prototype.stop = function() {
cancelAnimationFrame(this.RAF)
}
function setupBindings() {
$('[ondrop]').on('drop dragover dragenter', function(event) {
event.preventDefault()
})
function deepObject(object, properties) {
properties
.split('.')
.slice(0, -1)
.forEach(function(property) {
object = object[property]
})
return {
object: object,
property: properties.split('.').slice(-1)
}
}
$('[bind]').each(function(index, bindable) {
var _value, _element = bindable
var variable = $(bindable).attr('bind')
var deep = deepObject(window, variable)
Object.defineProperty(
deep.object,
deep.property, {
set: function(value) {
_value = value
_element.innerHTML = value
},
get: function() {
return _value
}
}
)
})
$('[bind-class]').each(function(index, bindable) {
var _value, _element = bindable
var variable = $(bindable).attr('bind-class')
var deep = deepObject(window, variable)
var initial = deep.object[deep.property]
Object.defineProperty(
deep.object,
deep.property, {
set: function(value) {
$(_element)
.removeClass(_value)
.addClass(value)
_value = value
},
get: function() {
return _value
}
}
)
deep.object[deep.property] = initial
})
}
init()
*
box-sizing: border-box
a
cursor: pointer
html
font-family: 'VT323', monospace
font-size: 22px
text-transform: uppercase
body
background-color: white
color: black
.App
position: relative
min-height: 100vh
padding-bottom: 150px
.container
width: 80%
margin: auto
padding-top: 2em
canvas
position: absolute
width: 100%
height: 150px
bottom: 0
.load-file
display: block
text-align: right
span
cursor: pointer
input[type='file']
display: none
.controls
text-align: center
&.playing .play
display: none
&.paused .pause
display: none
.info
display: inline-block
&-container
text-align: center
margin-bottom: 2em
.item
text-align: left
.name
display: inline-block
width: 75px
.fa
font-size: 36px
&:hover
text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment