- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在构建一个简单的 html 组件,允许用户录制一条消息,然后立即回放。目的是让他们在保存录音之前不喜欢录音时可以丢弃录音。消息将少于 60 秒。
所以我有一个 UI,允许我将音频录制到 AudioBuffers 数组中。我现在正在尝试将这些缓冲区推回到第二个音频控件中,以便用户可以播放录音。
这是一个codepen .
这是我认为是问题核心的核心播放功能。该解决方案必须在 android 和 ios 上的浏览器中运行,并且不使用第三方库(让您的生活变得非常困难:)
function _playback(){
let context = new AudioContext();
var dest = context.createMediaStreamDestination();
let source = context.createBufferSource(); // creates a sound source
source.buffer = _appendBuffer(context, this._audioBuffer);
source.connect(dest);
let player = window.getElementById("playback");
player.srcObject = dest.stream;
player.play();
}
关键问题是即使播放器控件显示时间进度,音频也不会播放。
我也有疑问,我是否应该使用两个播放器(一个用于录音,一个用于播放)还是我可以只使用一个音频元素?
最佳答案
所以这个有点像噩梦,所以我想在这里发布完整的脚本。
我从著名的 recorder.js 开始,但在适应我的需要时遇到了一些问题。我最终进行了重大重构,以使代码更易于理解。与 recorder.js 不同,此代码不使用工作线程(我真的不需要)。
/**
* Allows recording via the devices microphone.
*/
class Recorder
{
/**
* Constraints setup for mono.
* Currently now to modify them.
* It should be noted that these settings are ignored on most
* systems and we get stereo at a 44K sampleRate regardless of these settings.
*/
static _constraints = {
audio:
{
channelCount: 1,
mimeType: 'audio/wav',
sampleRate: 8192,
sampleSize: 8,
autoGainControl: true,
noiseSuppression: true,
echoCancellation: true,
}
};
constructor(desiredChannels)
{
this._desiredChannels = desiredChannels;
this._reset();
}
/*
* Start recording.
*
* errorCallback(e) - a function that is called if the start fails.
*
*/
start(errorCallback)
{
this._reset();
this._context = new AudioContext();
// request permission and if given
// wire our audio control to the media stream.
navigator
.mediaDevices
.getUserMedia(Recorder._constraints)
.then((stream) => this._wireRecordingStream(stream))
.catch(e => errorCallback(e));
// TODO: consider giving the user the ability to select an input device.
}
/*
* Stops a currently active recording.
*/
stop()
{
if (this._context != null)
{
this._context.close();
this._context = null;
}
}
/**
* check if the user's phone supports media api
*/
hasGetUserMedia()
{
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}
/**
* returns a Blob containing a wav file of the recording.
*/
getWav()
{
if (this._mergedChannelData == null)
this._mergeChannels();
let wav = new Wav(this._mergedChannelData, this._actualChannelCount, this._actualSampleRate);
return wav.getBlob();
}
/**
* resets the Recorder so we can restart the recording.
*/
_reset()
{
this._channels = null;
this._actualChannelCount = -1;
// this will be updated when the recording starts to the actual rate.
this._actualSampleRate = -1;
// after _mergeChannels is called this will contain
// a single float32 array of the underlying channel
// data interleaved to create a single audio stream.
this._mergedChannelData = null;
}
_initChannelBuffers(actualChannels)
{
if (this._channels == null)
{
this._channels = [];
this._actualChannelCount = actualChannels;
for (var i = 0; i < actualChannels; i++)
{
this._channels.push(new Channel());
}
}
}
/**
* The start() method uses this method to initialise the media stream
* and wire up the 'onaudioprocess'or to capture the recording.
*/
_wireRecordingStream(stream)
{
// https://developers.google.com/web/fundamentals/media/recording-audio/
// Setup recording.
this._source = this._context.createMediaStreamSource(stream);
this._node = (this._context.createScriptProcessor || this._context.createJavaScriptNode)
.call(this._context, 4096, this._desiredChannels, this._desiredChannels); // 4K buffer and we prefer a single (mono) channel.
// the context may have ignored our preferred sample rate.
this._actualSampleRate = this._context.sampleRate;
this._node.onaudioprocess = (e) => this._storeAudio(e.inputBuffer);
this._source.connect(this._node);
this._node.connect(this._context.destination);
}
/**
* This is the callback for 'onaudioprocess' where we store the recorded data
* to each channel buffer.
*/
_storeAudio(inputBuffer)
{
this._initChannelBuffers(inputBuffer.numberOfChannels);
for (var i = 0; i < this._actualChannelCount; i++)
{
this._channels[i].storeAudioPacket(inputBuffer.getChannelData(i));
}
}
// Merges all channels into a single float32Array.
// Channels are merged by interleaving data packet from each channel into a single stream.
_mergeChannels()
{
if (this._actualChannelCount === 2)
{
this._mergedChannelData = this._interleave(this._channels[0], this._channels[1]);
}
else
{
this._mergedChannelData = this._channels[0].getAudioData();
}
}
/**
** interleaves two channel buffers into a single float32 array.
*/
_interleave(lhsChannel, rhsChannel)
{
let length = lhsChannel.getLength() + rhsChannel.getLength();
let result = new Float32Array(length);
let index = 0;
let inputIndex = 0;this._channels
let lhsData = lhsChannel.getAudioData();
let rhsData = rhsChannel.getAudioData();
while (index < length)
{
result[index++] = lhsData[inputIndex];
result[index++] = rhsData[inputIndex];
inputIndex++;
}
return result;
}
}
/**
* Used to buffer audio data for a single channel.
*/
class Channel
{
constructor()
{
/**
* the total no of Float32's stored in all of the audio packets.
*/
this._length = 0;
// an array of audio packets (Float32Array) captured as the recording progresses.
//
this._audioPackets = [];
// If flatten has been called this will be a Float32Array
// contain all of the combined audio packets as a single array.
this._flattened = null;
}
getLength()
{
return this._length;
}
/**
* returns a single audio packet stored at the given index.
*/
getAudioPacket(index)
{
return this._audioPackets[index];
}
/**
* returns the entire underlying data (Float32s) as a single Float32 array
* If it hasn't already been done this method will call flatten to
* combine all of the packets into a singl data array.
*/
getAudioData()
{
if (this._flattened == null)
this._flatten();
return this._flattened;
}
// Stores an audioPacket (Float32Array) to _audioPackets
storeAudioPacket(audioPacket)
{
this._audioPackets.push(new Float32Array(audioPacket));
this._length += audioPacket.length;
}
/**
* coalesce all of the _audioPackets into a single float32Array
*/
_flatten()
{
this._flattened = new Float32Array(this._length);
let offset = 0;
for (let i = 0; i < this._audioPackets.length; i++)
{
this._flattened.set(this._audioPackets[i], offset);
offset += this._audioPackets[i].length;
}
}
}
/**
* The logic for creating a wav file (well just the data structure actually) from
* a stream of audioData
*
* audioData - Float32Array containing the interleaved data from all channels.
* channelCount - the number of channels interleaved into the audioData
* sampleRate - the sampleRate of the audioData.
*/
class Wav
{
/**
* expects a single float32array from which it will create a wav file.
*/
constructor(audioData, channelCount, sampleRate)
{
this._audioData = audioData;
this._channelCount = channelCount;
this._sampleRate = sampleRate;
}
/**
* returns the wav file as a blob.
*/
getBlob()
{
let wav = this._encodeAsWAV();
let audioBlob = new Blob([wav], { type: "audio/wav" });
return audioBlob;
}
/**
* Encodes _audioData into a wav file by adding the
* standard wav header.
*/
_encodeAsWAV()
{
let audioData = this._audioData;
var wavBuffer = new ArrayBuffer(44 + audioData.length * 2);
var view = new DataView(wavBuffer);
/* RIFF identifier */
this._writeString(view, 0, 'RIFF');
/* RIFF chunk length */
view.setUint32(4, 36 + audioData.length * 2, true);
/* RIFF type */
this._writeString(view, 8, 'WAVE');
/* format chunk identifier */
this._writeString(view, 12, 'fmt ');
/* format chunk length */
view.setUint32(16, 16, true);
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, this._channelCount, true);
/* sample rate */
view.setUint32(24, this._sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, this._sampleRate * 4, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, this._channelCount * 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
this._writeString(view, 36, 'data');
/* data chunk length */
view.setUint32(40, audioData.length * 2, true);
this._floatTo16BitPCM(view, 44, audioData);
return view;
}
_floatTo16BitPCM(output, offset, input)
{
for (var i = 0; i < input.length; i++, offset += 2)
{
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
}
_writeString(view, offset, string)
{
for (var i = 0; i < string.length; i++)
{
view.setUint8(offset + i, string.charCodeAt(i));
}
}
}
关于javascript - 如何在浏览器中录制和播放(html 5 音频),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56948350/
我正在尝试解决 A/V 同步问题。视频将比音频延迟 1 秒。 (请看我下面的注释) 来自 Android 媒体框架部分, 我可以延迟音频时间戳让它与视频同步,我应该从哪里开始?是音频源吗? MPEG4
我正在使用带有 SignalR 的 MassTransit 请求和响应。该网站向创建文件的 Windows 服务发出请求。创建文件后,Windows 服务会将响应消息发送回网站。该网站将打开该文件并使
我正在尝试创建一个允许用户发出一些声音的应用程序,然后以回放方式使用它。 我想让我的应用程序播放用户将记录的.wav文件。 由于不断出现错误,我在弄清楚如何编写此代码时遇到了麻烦。 ====
有没有办法禁止网页上视频的屏幕共享? 例如,当您尝试录制或屏幕共享(例如通过 Skype)Netflix 视频时,它仅显示黑屏并且没有音频。 我的问题是,他们是如何实现的?我只能想到JavaScrip
我正在尝试使用 html5 .getUserMedia 录制视频,然后在不上传到服务器的情况下再次播放。我尝试了很多教程,我通过使用 canvas 绘制 webp 图像然后使用 Whammy.js 转
我想为我的网站的用户实现屏幕录制功能。这将适用于便士拍卖风格的网站,以便用户可以记录他们的出价,并在拍卖出现问题时提供证据。 这是在线录音机的演示。 http://www.screentoaster.
所以在我的应用程序中,我尝试使用屏幕截图“记录”屏幕。我必须将这些单独的帧作为图像,因为它们稍后会在服务器上进行修改和组合。增加这种复杂性的是,它是在使用 Cocos2D 的慢节奏游戏中。我目前截屏的
是否可以使用单个 ffmpeg 命令同时捕获(记录)RTSP 流和捕获场景变化事件?我几乎可以做我想做的事: ffmpeg -i 'rtsp://mystream' \ -map 0:v -map 0
我是 Objective-c 和 iPhone 编程新手,但我正在开发一个自学应用程序。我一直在尝试弄清楚如何在 iPhone 上录制声音。 Apple 提供了使用 AVAudioRecorder 从
我无法写任何东西来允许这样做,但我希望有人能指出我找到可以做到这一点的代码的正确方向。我擅长 HTML 和 CSS,对 JS 非常陌生。 我需要的是能够使用我的麦克风在单页网站上讲话,并将其流回。这样
想象一下您在浏览器中观看体育赛事直播。这意味着您收到了视频流,对吗?我需要记录这个流并保存到磁盘。问题是我不知道从哪里开始。我对编程并不陌生,但在视频直播方面有一些经验。我看到这个问题分为以下几个部分
我在开始录制时遇到文件未找到异常。此外,我无法在 JMeter 可安装文件夹中找到 RootCA 证书。 最佳答案 根据 TestRecording210 JMeter Wiki 页面当用户(您在其下
我有这个源代码可以在浏览器中录制音频。 Record.js 调用另一个脚本提供录音并将其保存到服务器。 index.html record.js //starts by click on butt
我允许用户按下按钮以通过 SoundPool 播放声音。是否可以录制 SoundPool 正在播放的任何内容,以便用户可以录制一系列声音? 最佳答案 实际上不可能捕捉到播放的声音。我也有同样的愿望,但
我正在尝试使用 xcrun simctl io booted recordVideo recording.mov 录制我的 iOS 11.4 模拟器的屏幕。这将创建一个具有该名称的文件,但不幸的是该文
好的,我将尝试尽可能清楚地说明我的问题,但我很困惑,所以如果我没有传达信息,请告诉我。 我正在尝试使用 getUserMedia 来使用网络摄像头,然后使用这个 http://www.w3.org/T
是否可以使用 html5 录制声音?我已经下载了最新的 canary 版本的 chrome 并使用以下代码: navigator.getUserMedia = navigator.webkitGetU
很多人都在问这个,似乎没有人有答案,所以我也没有。 某些应用程序如何提供记录android系统音频输出的功能?我发现的所有内容都是在 1432 个不同站点上的相同教程,您可以在其中记录 MIC 输入。
不小心撞到了qq而不是 @q ,我的 vim 现在正在记录到寄存器 q . 如果我输入 q再次,它将覆盖以前录制的宏。 有没有办法 取消录制以免覆盖之前的宏或 恢复之前的宏而不从头开始重新录制? 最佳
当我们接到电话时,我们会向来电者播放提示,内容类似于“我们可能会出于质量和培训目的记录通话”。 我们为响应来电而发送的 TWiML 如下所示。 http://domain.tld/may_r
我是一名优秀的程序员,十分优秀!