2022-07-28
来源:华纳网
责任编辑:Sunny
人气:
核心提示:大家好,欢迎来到谷雨课堂
本课内容:
大家好,欢迎来到谷雨课堂
const Application = function() {
this.initA4()
this.tuner = new Tuner(this.a4)
this.notes = new Notes('.notes', this.tuner)
this.meter = new Meter('.meter')
this.frequencyBars = new FrequencyBars('.frequency-bars')
this.update({ name: 'A', frequency: this.a4, octave: 4, value: 69, cents: 0 })
}
Application.prototype.initA4 = function () {
this.$a4 = document.querySelector('.a4 span')
this.a4 = parseInt(localStorage.getItem('a4')) || 440
this.$a4.innerHTML = this.a4
}
Application.prototype.start = function() {
const self = this
this.tuner.onNoteDetected = function(note) {
if (self.notes.isAutoMode) {
if (self.lastNote === note.name) {
self.update(note)
} else {
self.lastNote = note.name
}
}
}
swal.fire('Welcome online tuner!').then(function() {
self.tuner.init()
self.frequencyData = new Uint8Array(self.tuner.analyser.frequencyBinCount)
})
this.$a4.addEventListener('click', function () {
swal.fire({
input: 'number',
inputValue: self.a4,
}).then(function ({ value: a4 }) {
if (!parseInt(a4) || a4 === self.a4) {
return
}
self.a4 = a4
self.$a4.innerHTML = a4
self.tuner.middleA = a4
self.notes.createNotes()
self.update({ name: 'A', frequency: self.a4, octave: 4, value: 69, cents: 0 })
localStorage.setItem('a4', a4)
})
})
this.updateFrequencyBars()
}
Application.prototype.updateFrequencyBars = function() {
if (this.tuner.analyser) {
this.tuner.analyser.getByteFrequencyData(this.frequencyData)
this.frequencyBars.update(this.frequencyData)
}
requestAnimationFrame(this.updateFrequencyBars.bind(this))
}
Application.prototype.update = function(note) {
this.notes.update(note)
this.meter.update((note.cents / 50) * 45)
}
// noinspection JSUnusedGlobalSymbols
Application.prototype.toggleAutoMode = function() {
this.notes.toggleAutoMode()
}
const app = new Application()
app.start()
const Tuner = function(a4) {
this.middleA = a4 || 440
this.semitone = 69
this.bufferSize = 4096
this.noteStrings = [
'C',
'C♯',
'D',
'D♯',
'E',
'F',
'F♯',
'G',
'G♯',
'A',
'A♯',
'B'
]
this.initGetUserMedia()
}
Tuner.prototype.initGetUserMedia = function() {
window.AudioContext = window.AudioContext || window.webkitAudioContext
if (!window.AudioContext) {
return alert('AudioContext not supported')
}
// Older browsers might not implement mediaDevices at all, so we set an empty object first
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// Some browsers partially implement mediaDevices. We can't just assign an object
// with getUserMedia as it would overwrite existing properties.
// Here, we will just add the getUserMedia property if it's missing.
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// First get ahold of the legacy getUserMedia, if present
const getUserMedia =
navigator.webkitGetUserMedia || navigator.mozGetUserMedia
// Some browsers just don't implement it - return a rejected promise with an error
// to keep a consistent interface
if (!getUserMedia) {
alert('getUserMedia is not implemented in this browser')
}
// Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
}
Tuner.prototype.startRecord = function () {
const self = this
navigator.mediaDevices
.getUserMedia({ audio: true })
.then(function(stream) {
self.audioContext.createMediaStreamSource(stream).connect(self.analyser)
self.analyser.connect(self.scriptProcessor)
self.scriptProcessor.connect(self.audioContext.destination)
self.scriptProcessor.addEventListener('audioprocess', function(event) {
const frequency = self.pitchDetector.do(
event.inputBuffer.getChannelData(0)
)
if (frequency && self.onNoteDetected) {
const note = self.getNote(frequency)
self.onNoteDetected({
name: self.noteStrings[note % 12],
value: note,
cents: self.getCents(frequency, note),
octave: parseInt(note / 12) - 1,
frequency: frequency
})
}
})
})
.catch(function(error) {
alert(error.name + ': ' + error.message)
})
}
Tuner.prototype.init = function() {
this.audioContext = new window.AudioContext()
this.analyser = this.audioContext.createAnalyser()
this.scriptProcessor = this.audioContext.createScriptProcessor(
this.bufferSize,
1,
1
)
const self = this
aubio().then(function(aubio) {
self.pitchDetector = new aubio.Pitch(
'default',
self.bufferSize,
1,
self.audioContext.sampleRate
)
self.startRecord()
})
}
/**
* get musical note from frequency
*
* @param {number} frequency
* @returns {number}
*/
Tuner.prototype.getNote = function(frequency) {
const note = 12 * (Math.log(frequency / this.middleA) / Math.log(2))
return Math.round(note) + this.semitone
}
/**
* get the musical note's standard frequency
*
* @param note
* @returns {number}
*/
Tuner.prototype.getStandardFrequency = function(note) {
return this.middleA * Math.pow(2, (note - this.semitone) / 12)
}
/**
* get cents difference between given frequency and musical note's standard frequency
*
* @param {number} frequency
* @param {number} note
* @returns {number}
*/
Tuner.prototype.getCents = function(frequency, note) {
return Math.floor(
(1200 * Math.log(frequency / this.getStandardFrequency(note))) / Math.log(2)
)
}
/**
* play the musical note
*
* @param {number} frequency
*/
Tuner.prototype.play = function(frequency) {
if (!this.oscillator) {
this.oscillator = this.audioContext.createOscillator()
this.oscillator.connect(this.audioContext.destination)
this.oscillator.start()
}
this.oscillator.frequency.value = frequency
}
Tuner.prototype.stop = function() {
this.oscillator.stop()
this.oscillator = null
}
完整的源代码可以登录【华纳网】下载。
https://www.worldwarner.com/
免责声明:本文仅代表作者个人观点,与华纳网无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。