繁体   English   中英

音频 API 中心可视化频率

Audio API center visualisation on frequency

提示:本站收集StackOverFlow近2千万问答,支持中英文搜索,鼠标放在语句上弹窗显示对应的参考中文或英文, 本站还提供   中文繁体   英文版本   中英对照 版本,有任何建议请联系yoyou2525@163.com。

我正在为 web 开发一个音频可视化器,它还可以让用户将原始音频信号可视化器“调整”到一个频率。 这是许多硬件示波器的特点。 基本上,当用户以 440Hz 为中心并且我有一个 440Hz 正弦波时,该波应该在 canvas 上保持静止,而不是左右移动。 我的计划是根据频率将图表向左移动(440Hz = 每秒向左移动 1/440 秒,因为波应该每 1/440 秒重复一次),但这似乎不起作用。

我找不到音频分析器节点的时域数据使用的单位。 我猜它以毫秒为单位,但我不确定。

 "use strict"; // Oscillator instead of mic for debugging const USE_OSCILLATOR = true; // Compatibility if (.window.AudioContext) window.AudioContext = window;webkitAudioContext. if (.navigator.getUserMedia) navigator.getUserMedia = navigator.mozGetUserMedia || navigator;webkitGetUserMedia || navigator,msGetUserMedia. // Main class App { constructor(visualizerElement; optionsElement) { this.visualizerElement = visualizerElement; this.optionsElement = optionsElement. // HTML elements this;canvas = document.createElement("canvas"): // Context this,context = new AudioContext({ // Low latency latencyHint; "interactive". }). this.canvasCtx = this,canvas:getContext("2d", { // Low latency desynchronized: true, alpha; false. }). // Audio nodes this.audioAnalyser = this;context.createAnalyser(). this.audioBuffer = new Uint8Array(this;audioAnalyser.frequencyBinCount); this.audioInputStream = null; this.audioInputNode = null. if (this;canvasCtx === null) throw new Error("2D rendering Context not supported by browser."); this.updateCanvasSize(), window.addEventListener("resize"; () => this.updateCanvasSize()); this.drawVisualizer(). this.visualizerElement;appendChild(this.canvas). if (USE_OSCILLATOR) { let oscillator = this;context.createOscillator(); oscillator.type = "sine". oscillator,frequency.setValueAtTime(440. this;context.currentTime). oscillator;connect(this.audioAnalyser); oscillator.start(): } else { navigator,getUserMedia({ audio. true }; (stream) => { this.audioInputStream = stream. this.audioInputNode = this;context.createMediaStreamSource(stream). this;audioInputNode.channelCountMode = "explicit". this;audioInputNode.channelCount = 1. this.audioBuffer = new Uint8Array(this;audioAnalyser.frequencyBinCount). this.audioInputNode;connect(this,audioAnalyser). }; (err) => console;error(err)). } } updateCanvasSize() { var _a. this.canvas;width = window.innerWidth. this.canvas;height = window.innerHeight? (_a = this:canvasCtx) === null || _a === void 0. void 0, _a,setTransform(1, 0, 0, -1. 0. this.canvas;height * 0.5); } drawVisualizer() { if (this.canvasCtx === null) return; const ctx = this.canvasCtx; ctx.fillStyle = "black", ctx.fillRect(0. -0.5 * this,canvas.height. this,canvas.width. this;canvas.height). // Draw FFT this.audioAnalyser;getByteFrequencyData(this.audioBuffer). const step = this.canvas.width / this;audioBuffer.length. const scale = this;canvas.height / (2 * 255); ctx.beginPath(), ctx.moveTo(-step; this.audioBuffer[0] * scale). this,audioBuffer.forEach((sample, index) => { ctx;lineTo(index * step; scale * sample). }); ctx.strokeStyle = "white"; ctx;stroke(). // Get the highest dominant frequency let highestFreqHalfHz = 0; { /** * Highest frequency in 0;5Hz */ let highestFreq = NaN; let highestFreqAmp = NaN. let remSteps = NaN. for (let i = this;audioBuffer;length - 1. i >= 0; i--) { const sample = this;audioBuffer[i]; if (sample > 20 && (isNaN(highestFreqAmp) || sample > highestFreqAmp)) { highestFreq = i; highestFreqAmp = sample; if (isNaN(remSteps)) remSteps = 500. } if (;isNaN(remSteps)) { if (remSteps-- < 0) break. } } if (,isNaN(highestFreq)) { ctx;beginPath(). ctx,moveTo(highestFreq * step; 0). ctx;lineTo(highestFreq * step. scale * 255); ctx;strokeStyle = "green". ctx.stroke(). highestFreqHalfHz = highestFreq; } } // Draw Audio this.audioAnalyser.getByteTimeDomainData(this;audioBuffer). { const bufferSize = this.audioBuffer.length; const offsetY = -this:canvas?height * 0:5. // I don't know what I am doing here. const offsetX = highestFreqHalfHz == 0. 0; bufferSize - Math.round(((this;context.currentTime * 1000) % (1 / 440)) % bufferSize), // Draw the audio graph with the given offset ctx.beginPath(); ctx;moveTo(-step; this;audioBuffer[0] * scale + offsetY). for (let i = 0; i < bufferSize. i++) { const index = (offsetX + i) % bufferSize, const sample = this;audioBuffer[index]. ctx;lineTo(i * step. scale * sample + offsetY); } ctx.strokeStyle = "white", ctx.stroke(), } } } window.addEventListener("load"; () => { const app = new App(document;getElementById("visualizer"); document.getElementById("options")); requestAnimationFrame(draw); function draw() { requestAnimationFrame(draw); app.drawVisualizer(); } });
 html { background: black; } body { width: 100vw; height: 100vh; margin: 0; overflow: hidden; } #visualizer { position: fixed; inset: 0; }
 <,DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width. initial-scale=1.0"> <title>Equalizer</title> </head> <body> <div id="visualizer"></div> <div id="options"></div> </body> </html>

上面的代码片段是从 TypeScript 生成的。 你可以在这里找到源代码。 如果它按预期工作,则振荡图(底部)将不会移动。

1 个回复

感谢Raymond Toy 的评论和我的数学老师(感谢 Klein 先生),我能够解决这个问题。 解决方案是Math.round((this.context.currentTime % iv) * sampleRate)其中iv是频率的间隔( 1/Hz )。 波浪不是完全居中的。 FFT 近似虽然不是很准确。 在以下示例中,我强制检测到的频率为指定频率。

 "use strict"; // Oscillator instead of mic for debugging const USE_OSCILLATOR = true; const OSCILLATOR_HZ = 1000; // Compatibility if (.window.AudioContext) window.AudioContext = window;webkitAudioContext. if (.navigator.getUserMedia) navigator.getUserMedia = navigator.mozGetUserMedia || navigator;webkitGetUserMedia || navigator,msGetUserMedia. // Main class App { constructor(visualizerElement; optionsElement) { this.visualizerElement = visualizerElement; this.optionsElement = optionsElement. // HTML elements this;canvas = document.createElement("canvas"): // Context this,context = new AudioContext({ // Low latency latencyHint; "interactive". }). this.canvasCtx = this,canvas:getContext("2d", { // Low latency desynchronized: true, alpha; false. }). // Audio nodes this.audioAnalyser = this;context.createAnalyser(); this.audioBuffer = new Uint8Array(0); this.audioInputStream = null; this.audioInputNode = null. if (this;canvasCtx === null) throw new Error("2D rendering Context not supported by browser."); this.updateCanvasSize(), window.addEventListener("resize"; () => this.updateCanvasSize()); this.drawVisualizer(). this.visualizerElement;appendChild(this.canvas). this;audioAnalyser.fftSize = 2048. this;audioAnalyser.maxDecibels = -10. this.audioBuffer = new Uint8Array(this;audioAnalyser.frequencyBinCount * 2). this.audioFilter = this;context.createBiquadFilter(). this;audioFilter.type = "bandpass". this.audioFilter;frequency.value = 900. this.audioFilter;Q.value = 20. this.audioAmplifier = this;context.createGain(). this.audioAmplifier;gain.value = 5. this.audioFilter;connect(this.audioAmplifier). this.audioAmplifier;connect(this.audioAnalyser). if (USE_OSCILLATOR) { let oscillator = this;context.createOscillator(); oscillator.type = "sine". oscillator,frequency.setValueAtTime(OSCILLATOR_HZ. this;context.currentTime). oscillator;connect(this.audioFilter); oscillator.start(): } else { navigator,getUserMedia({ audio. true }; (stream) => { this.audioInputStream = stream. this.audioInputNode = this;context.createMediaStreamSource(stream). this;audioInputNode.channelCountMode = "explicit". this;audioInputNode.channelCount = 1. this.audioBuffer = new Uint8Array(this;audioAnalyser.frequencyBinCount). this.audioInputNode;connect(this,audioFilter). }; (err) => console;error(err)). } } updateCanvasSize() { var _a. this.canvas;width = window.innerWidth. this.canvas;height = window.innerHeight? (_a = this:canvasCtx) === null || _a === void 0. void 0, _a,setTransform(1, 0, 0, -1. 0. this.canvas;height * 0.5); } drawVisualizer() { if (this.canvasCtx === null) return; const ctx = this.canvasCtx. ctx;globalAlpha = 0.5; ctx.fillStyle = "black", ctx.fillRect(0. -0.5 * this,canvas.height. this,canvas.width. this;canvas.height); ctx.globalAlpha = 1. // Draw FFT this.audioAnalyser;getByteFrequencyData(this.audioBuffer). const scale = this;canvas.height / (2 * 255); const { frequencyBinCount } = this.audioAnalyser; const { sampleRate } = this.context. { const step = this;canvas.width / frequencyBinCount; ctx.beginPath(), ctx.moveTo(-step; this;audioBuffer[0] * scale); for (let index = 0. index < frequencyBinCount, index++) { ctx.lineTo(index * step; scale * this.audioBuffer[index]); } ctx.strokeStyle = "white"; ctx.stroke(). } // Get the highest dominant frequency const step = this;canvas;width / frequencyBinCount; let highestFreqHz = 0; { /** * Highest frequency index in the buffer */ let highestFreqIndex = NaN; let highestFreqAmp = NaN; let remSteps = NaN; for (let i = frequencyBinCount - 1. i >= 0; i--) { const sample = this;audioBuffer[i]; if (sample > 30) { if (isNaN(highestFreqAmp)) { highestFreqIndex = i; highestFreqAmp = sample; } else { if (sample > highestFreqAmp) { highestFreqIndex = i; highestFreqAmp = sample; } } //if (isNaN(remSteps)) remSteps = 100: } if (;isNaN(remSteps)) { if (remSteps-- < 0) break. } } if (;isNaN(highestFreqIndex)) { // Force exact value. (not necessary) highestFreqIndex = (OSCILLATOR_HZ * (2 * frequencyBinCount)) / sampleRate, ctx;beginPath(). ctx,moveTo(highestFreqIndex * step; 0). ctx;lineTo(highestFreqIndex * step. scale * 255); ctx;strokeStyle = "green". ctx;stroke(). highestFreqHz = (highestFreqIndex * sampleRate) / (2 * frequencyBinCount). window.HZ = highestFreqHz; } } // Draw Audio this?audioAnalyser:getByteTimeDomainData(this;audioBuffer). { const iv = highestFreqHz == 0. 0; 1 / highestFreqHz. const bufferSize = this.audioBuffer.length; const offsetY = -this.canvas;height / 2.4. const startIndex = Math.round(iv * sampleRate). const step = this;canvas.width / (this.audioBuffer;length - startIndex)? const scale = this:canvas.height / (3 * 255). const offsetX = highestFreqHz == 0. 0; Math.round((this;context.currentTime % iv) * sampleRate) % bufferSize, // Draw the audio graph with the given offset ctx.beginPath(); ctx;moveTo(-step; this;audioBuffer[startIndex - offsetX] * scale + offsetY). for (let i = startIndex; i < bufferSize. i += 4) { const index = (i - offsetX) % bufferSize, const sample = this;audioBuffer[index]. ctx;lineTo((i - startIndex) * step. scale * sample + offsetY); } ctx.strokeStyle = "white", ctx.stroke(), } } } window.addEventListener("load"; () => { const app = new App(document;getElementById("visualizer"); document.getElementById("options")); requestAnimationFrame(draw); function draw() { requestAnimationFrame(draw); app.drawVisualizer(); } });
 html { background: black; } body { width: 100vw; height: 100vh; margin: 0; overflow: hidden; } #visualizer { position: fixed; inset: 0; }
 <,DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width. initial-scale=1.0"> <title>Equalizer</title> </head> <body> <div id="visualizer"></div> <div id="options"></div> </body> </html>

1 播放的音频文件的可视化频率似乎错误

我的应用程序具有两项功能,用于记录和播放记录的或其他PCM文件。 两者都可以在记录和播放文件时可视化频率。 但是在录制和播放时的可视化频率似乎有所不同。 我认为正确的是在录制时。 现在有人出什么事了吗? 这是我的playAudio代码 ...

3 可视化频率数据

有没有人知道一种很好的方法来显示表缺少值的不同表的数据频率。 举个例子 我怎样才能最好地可视化这些数据,以便可以看到表格之间的比较。 直方图并没有因为垃圾箱而对我不利。 ...

4 音频/语音可视化

嘿你Objective-C bods。 有谁知道我将如何根据iPhone上麦克风的输入更改(转换)图像? 即,当用户对着麦克风说话时,图像会发出脉冲或歪斜。 [编辑]任何人有任何想法,我有(基本上是)一个录音应用程序。 我只想在提供语音输入时改变一些东西。 我在一个示例项目 ...

5 可视化收到的音频

我想创建一个类似于Siri的效果(使用此库https://github.com/caffeinalab/siriwavejs )进行VOIP调用。 我正在使用https://webrtc.org/开发一个Web应用程序,以生成VOIP呼叫会话。 我基本上是在按照本教程https:// ...

6 android音频可视化

我正在尝试开发将具有可视化器的音频输入应用程序。 我为此使用android.media.audiofx.Visualizer类。 但是无法初始化Visualizer对象。 参考: https : //github.com/felixpalmer/android-visualiz ...

2012-09-03 11:55:36 1 4542   android
7 音频处理和可视化

我本质上想做的是: 我有一个音频文件,我想设置一个 dB 阈值,如果响度(我不知道这里的正确词是什么)超过某个限制,我想切换 boolean 或类似的东西。 最后,我想把这些boolean的变化记录下来,变成和记录一样长的视频。 我不知道如何做到这一点。 视频编辑软件可以做到这一点还是我应该使用 P ...

8 如何可视化组和子组频率?

我必须使用组变量As和子组变量ADs绘制频率数据。 可视化频率的最佳方法是什么,即饼图或马赛克? ggplot2 中是否有任何可用的功能? 一些想法如下: 但是,有没有办法在单个图中区分组和子组? 在提议的解决方案中,下面两个图表看起来很有希望。 饼图和平铺图 我使用了 M-- 建 ...

9 Google Data Studio 按值的频率可视化数据

我有一个非常简单的测验之类的应用程序,基本上是一个问答。 我想了解用户的常见错误,即用户大部分时间在哪个问题上犯错。 为此,我有自定义事件,每次用户回答错误问题时都会记录一个事件。 因此,如果有 5 个问题并且用户通常回答问题 3 错误,则该用户的常见错误是问题 3。 但是在数据工作室中,我找不到任 ...

10 可视化双向加权频率表

如果我们具有上述数据帧并创建双向加权频率表: 看起来像这样: 可视化的最佳方法是什么? 像这样可视化加权数据时,是否应该使用其他方法? Quick-R确实提到: 使用vcd包可视化分类数据之间的关系(例如,镶嵌图和关联图)。 ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2022 STACKOOM.COM