@@ -70,7 +70,7 @@ class AudioPlayer extends React.PureComponent {
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.audioPlayer) {
|
||||
this.audioPlayer.oncanplay = null;
|
||||
this.audioPlayer.onloadedmetadata = null;
|
||||
this.audioPlayer.ontimeupdate = null;
|
||||
this.audioPlayer.onended = null;
|
||||
this.audioPlayer.pause();
|
||||
@@ -94,7 +94,7 @@ class AudioPlayer extends React.PureComponent {
|
||||
|
||||
initAudio() {
|
||||
this.audioPlayer = new Audio(this.props.src);
|
||||
this.audioPlayer.oncanplay = _ => this.setState({canPlay: true});
|
||||
this.audioPlayer.onloadedmetadata = _ => this.setState({canPlay: true});
|
||||
this.audioPlayer.ontimeupdate = _ => this.setState({
|
||||
currentTime: secondsToTime(this.audioPlayer.currentTime, this.state.longMin)
|
||||
});
|
||||
@@ -149,7 +149,7 @@ class AudioPlayer extends React.PureComponent {
|
||||
this.canvasContext.strokeStyle = BAR_COLOR_DARK;
|
||||
for (let i = 0; i < this.viewBuffer.length; i++) {
|
||||
let x = 1 + i * (LINE_WIDTH + SPACING) + LINE_WIDTH * 0.5;
|
||||
let y = this.viewBuffer[i] * height * 0.9;
|
||||
let y = Math.max(this.viewBuffer[i] * height * 0.9, 1);
|
||||
|
||||
const color = x < thumbAt ? BAR_COLOR_DARK : BAR_COLOR;
|
||||
if (this.canvasContext.strokeStyle != color) {
|
||||
@@ -159,7 +159,7 @@ class AudioPlayer extends React.PureComponent {
|
||||
}
|
||||
|
||||
this.canvasContext.moveTo(x, (height - y) * 0.5);
|
||||
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
|
||||
this.canvasContext.lineTo(x, (height + y) * 0.5);
|
||||
}
|
||||
// Actually draw the bars on canvas.
|
||||
this.canvasContext.stroke();
|
||||
@@ -216,10 +216,11 @@ class AudioPlayer extends React.PureComponent {
|
||||
if (!this.state.canPlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state.playing) {
|
||||
this.audioPlayer.pause();
|
||||
this.setState({playing: false});
|
||||
} else {
|
||||
} else if (this.audioPlayer.readyState >= 2) {
|
||||
this.audioPlayer.play();
|
||||
this.setState({playing: true}, this.visualize);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@ const VISUALIZATION_BARS = 96;
|
||||
// Maximum number of samples per bar.
|
||||
const MAX_SAMPLES_PER_BAR = 10;
|
||||
|
||||
// Recording format.
|
||||
const AUDIO_MIME_TYPE = 'audio/webm';
|
||||
// Default recording format (FF, Chrome except on iOS).
|
||||
const DEFAULT_AUDIO_MIME_TYPE = 'audio/webm';
|
||||
// Safari supports only mp4 as audio recording format.
|
||||
const SAFARI_AUDIO_MIME_TYPE = 'audio/mp4';
|
||||
const AUDIO_MIME_TYPES = [DEFAULT_AUDIO_MIME_TYPE, SAFARI_AUDIO_MIME_TYPE, ''];
|
||||
|
||||
const messages = defineMessages({
|
||||
icon_title_delete: {
|
||||
@@ -55,6 +58,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Send message',
|
||||
description: 'Icon tool tip for sending a message'
|
||||
},
|
||||
failed_to_init_audio: {
|
||||
id: 'failed_to_init_audio',
|
||||
defaultMessage: 'Failed to initialize audio recording',
|
||||
description: 'Error message when audio is not available'
|
||||
}
|
||||
});
|
||||
|
||||
class AudioRecorder extends React.PureComponent {
|
||||
@@ -188,10 +196,10 @@ class AudioRecorder extends React.PureComponent {
|
||||
this.canvasContext.beginPath();
|
||||
for (let i = 0; i < this.viewBuffer.length; i++) {
|
||||
let x = i * (LINE_WIDTH + SPACING) - dx;
|
||||
let y = Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height;
|
||||
let y = Math.max(Math.min(this.viewBuffer[i] / BAR_SCALE, 0.9) * height, 1);
|
||||
|
||||
this.canvasContext.moveTo(x, (height - y) * 0.5);
|
||||
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
|
||||
this.canvasContext.lineTo(x, (height + y) * 0.5);
|
||||
}
|
||||
// Actually draw the bars on canvas.
|
||||
this.canvasContext.stroke();
|
||||
@@ -261,11 +269,29 @@ class AudioRecorder extends React.PureComponent {
|
||||
|
||||
initMediaRecording(stream) {
|
||||
this.stream = stream;
|
||||
this.mediaRecorder = new MediaRecorder(stream, {mimeType: AUDIO_MIME_TYPE, audioBitsPerSecond: 24_000});
|
||||
AUDIO_MIME_TYPES.some(mimeType => {
|
||||
if (MediaRecorder.isTypeSupported(mimeType)) {
|
||||
this.mediaRecorder = new MediaRecorder(stream, {mimeType: mimeType, audioBitsPerSecond: 24_000});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!this.mediaRecorder) {
|
||||
console.warn('MediaRecorder failed to initialize: no supported audio formats');
|
||||
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
|
||||
return;
|
||||
}
|
||||
|
||||
// The following code is needed for visualization.
|
||||
this.audioContext = new AudioContext();
|
||||
this.audioInput = this.audioContext.createMediaStreamSource(stream);
|
||||
if (!this.audioInput) {
|
||||
console.warn('createMediaStreamSource returned null: audio input unavailable');
|
||||
this.props.onError(this.props.intl.formatMessage(messages.failed_to_init_audio));
|
||||
return;
|
||||
}
|
||||
|
||||
this.analyser = this.audioContext.createAnalyser();
|
||||
this.analyser.fftSize = BUFFER_SIZE;
|
||||
this.audioInput.connect(this.analyser);
|
||||
@@ -305,11 +331,14 @@ class AudioRecorder extends React.PureComponent {
|
||||
}
|
||||
|
||||
// Obtain data in a form sutable for sending or playing back.
|
||||
// If duration is valid, apply fix for Chrome's WebM duration bug.
|
||||
getRecording(mimeType) {
|
||||
mimeType = mimeType || AUDIO_MIME_TYPE;
|
||||
mimeType = mimeType || DEFAULT_AUDIO_MIME_TYPE;
|
||||
let blob = new Blob(this.audioChunks, {type: mimeType});
|
||||
return fixWebmDuration(blob, mimeType)
|
||||
// If duration is valid, apply fix for Chrome's WebM duration bug.
|
||||
const result = mimeType == DEFAULT_AUDIO_MIME_TYPE ?
|
||||
fixWebmDuration(blob, mimeType) :
|
||||
Promise.resolve(blob);
|
||||
return result
|
||||
.then(fixedBlob => { blob = fixedBlob; return fixedBlob.arrayBuffer(); })
|
||||
.then(arrayBuff => this.audioContext.decodeAudioData(arrayBuff))
|
||||
.then(decoded => this.createPreview(decoded))
|
||||
@@ -347,7 +376,9 @@ class AudioRecorder extends React.PureComponent {
|
||||
}
|
||||
|
||||
cleanUp() {
|
||||
this.audioInput.disconnect();
|
||||
if (this.audioInput) {
|
||||
this.audioInput.disconnect();
|
||||
}
|
||||
this.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user