Merge pull request #6417 from matrix-org/travis/voice-messages/play-1
Play only one audio file at a time
This commit is contained in:
commit
34b8870a95
5 changed files with 98 additions and 2 deletions
|
@ -23,6 +23,7 @@ import AudioPlayer from "../audio_messages/AudioPlayer";
|
||||||
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
|
||||||
import MFileBody from "./MFileBody";
|
import MFileBody from "./MFileBody";
|
||||||
import { IBodyProps } from "./IBodyProps";
|
import { IBodyProps } from "./IBodyProps";
|
||||||
|
import { PlaybackManager } from "../../../voice/PlaybackManager";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
error?: Error;
|
error?: Error;
|
||||||
|
@ -62,7 +63,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
|
||||||
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024);
|
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024);
|
||||||
|
|
||||||
// We should have a buffer to work with now: let's set it up
|
// We should have a buffer to work with now: let's set it up
|
||||||
const playback = new Playback(buffer, waveform);
|
const playback = PlaybackManager.instance.createPlaybackInstance(buffer, waveform);
|
||||||
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
|
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
|
||||||
this.setState({ playback });
|
this.setState({ playback });
|
||||||
|
|
||||||
|
|
37
src/voice/ManagedPlayback.ts
Normal file
37
src/voice/ManagedPlayback.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DEFAULT_WAVEFORM, Playback } from "./Playback";
|
||||||
|
import { PlaybackManager } from "./PlaybackManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A managed playback is a Playback instance that is guided by a PlaybackManager.
|
||||||
|
*/
|
||||||
|
export class ManagedPlayback extends Playback {
|
||||||
|
public constructor(private manager: PlaybackManager, buf: ArrayBuffer, seedWaveform = DEFAULT_WAVEFORM) {
|
||||||
|
super(buf, seedWaveform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async play(): Promise<void> {
|
||||||
|
this.manager.playOnly(this);
|
||||||
|
return super.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.manager.destroyPlaybackInstance(this);
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ export enum PlaybackState {
|
||||||
|
|
||||||
export const PLAYBACK_WAVEFORM_SAMPLES = 39;
|
export const PLAYBACK_WAVEFORM_SAMPLES = 39;
|
||||||
const THUMBNAIL_WAVEFORM_SAMPLES = 100; // arbitrary: [30,120]
|
const THUMBNAIL_WAVEFORM_SAMPLES = 100; // arbitrary: [30,120]
|
||||||
const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
export const DEFAULT_WAVEFORM = arraySeed(0, PLAYBACK_WAVEFORM_SAMPLES);
|
||||||
|
|
||||||
function makePlaybackWaveform(input: number[]): number[] {
|
function makePlaybackWaveform(input: number[]): number[] {
|
||||||
// First, convert negative amplitudes to positive so we don't detect zero as "noisy".
|
// First, convert negative amplitudes to positive so we don't detect zero as "noisy".
|
||||||
|
|
|
@ -132,6 +132,10 @@ export class PlaybackClock implements IDestroyable {
|
||||||
|
|
||||||
public flagStop() {
|
public flagStop() {
|
||||||
this.stopped = true;
|
this.stopped = true;
|
||||||
|
|
||||||
|
// Reset the clock time now so that the update going out will trigger components
|
||||||
|
// to check their seek/position information (alongside the clock).
|
||||||
|
this.clipStart = this.context.currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public syncTo(contextTime: number, clipTime: number) {
|
public syncTo(contextTime: number, clipTime: number) {
|
||||||
|
|
54
src/voice/PlaybackManager.ts
Normal file
54
src/voice/PlaybackManager.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DEFAULT_WAVEFORM, Playback } from "./Playback";
|
||||||
|
import { ManagedPlayback } from "./ManagedPlayback";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles management of playback instances to ensure certain functionality, like
|
||||||
|
* one playback operating at any one time.
|
||||||
|
*/
|
||||||
|
export class PlaybackManager {
|
||||||
|
private static internalInstance: PlaybackManager;
|
||||||
|
|
||||||
|
private instances: ManagedPlayback[] = [];
|
||||||
|
|
||||||
|
public static get instance(): PlaybackManager {
|
||||||
|
if (!PlaybackManager.internalInstance) {
|
||||||
|
PlaybackManager.internalInstance = new PlaybackManager();
|
||||||
|
}
|
||||||
|
return PlaybackManager.internalInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops all other playback instances. If no playback is provided, all instances
|
||||||
|
* are stopped.
|
||||||
|
* @param playback Optional. The playback to leave untouched.
|
||||||
|
*/
|
||||||
|
public playOnly(playback?: Playback) {
|
||||||
|
this.instances.filter(p => p !== playback).forEach(p => p.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroyPlaybackInstance(playback: ManagedPlayback) {
|
||||||
|
this.instances = this.instances.filter(p => p !== playback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createPlaybackInstance(buf: ArrayBuffer, waveform = DEFAULT_WAVEFORM): Playback {
|
||||||
|
const instance = new ManagedPlayback(this, buf, waveform);
|
||||||
|
this.instances.push(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue