Files
cocos-engine/pal/audio/operation-queue.ts
PP 831722c121 optimize pal (#8405)
* rename mg

* optimize audioMgr

* remove implement class

update

* add src property for AudioPlayer

* fix multiple audioSource reference the same audioClip

* support operation-queue

update

* fix operation deadlock

* update

* fix esLint

update

update

* remove ensureStop

remove stop in play

* fix audio-dom loading

* optimize operation queue in AudioSource component

* HACK on runtime

* fix esLint

* update esLint

* HACK on WeChat DevTools

* lowercase enqueueOperationDecorator

* remve ! with @ts-expect-error

* Revert "HACK on WeChat DevTools"

This reverts commit 230a2ec922.

* fix esLint

* rename clip.ts to audio-clip.ts

* add audio meta

* use fastRemoveAt

* change ClassDecorator to MethodDecorator

* remove  player reference in audio-clip

* destroy temporary AudioPlayer after loading

* optimize syncPlayer

* fix esLint

* fix esLint

* optimize audio-manager

* optimize operation-queue

update

* fix eslint
2021-04-09 15:21:11 +08:00

63 lines
2.4 KiB
TypeScript

import { EventTarget } from '../../cocos/core';
type OperationMethod = (...args: any[]) => Promise<void>;
export interface OperationInfo {
id: number;
func: OperationMethod;
args: any[],
invoking: boolean,
}
export interface OperationQueueable {
_operationQueue: OperationInfo[];
_eventTarget: EventTarget;
}
let operationId = 0;
function _tryCallingRecursively<T extends OperationQueueable> (target: T, opInfo: OperationInfo) {
if (opInfo.invoking) {
return;
}
opInfo.invoking = true;
opInfo.func.call(target, ...opInfo.args).then(() => {
opInfo.invoking = false;
target._operationQueue.shift();
target._eventTarget.emit(opInfo.id.toString());
const nextOpInfo: OperationInfo = target._operationQueue[0];
nextOpInfo && _tryCallingRecursively(target, nextOpInfo);
}).catch((e) => {});
}
/**
* This is a method decorator for media player class such as Audio or Video.
* Most of the operations in media player are asynchronous.
* When all these asynchronous operations are called concurrently, they need to be queued.
*
* Note: the decorated class need to implement the interface `OperationQueueable`
* and the decorated method should be declared as `(...args: any[]): Promise<void>`.
*
* When you apply `enqueueOperation` on a method, remember to provide a pure operation implementation.
* It means that, for example, you can't call stop in the implementation of play operation,
* because that would cause the operation deadlock.
*/
export function enqueueOperation<T extends OperationQueueable> (target: T, propertyKey: string, descriptor: TypedPropertyDescriptor<OperationMethod>): void {
const originalOperation = descriptor.value!;
descriptor.value = function (...args: any[]): Promise<void> {
return new Promise((resolve) => {
const id = operationId++;
const instance = this as OperationQueueable;
// enqueue operation
instance._operationQueue.push({
id,
func: originalOperation,
args,
invoking: false,
});
// call resolve when this operation id is finishied
instance._eventTarget.once(id.toString(), resolve);
const opInfo: OperationInfo = instance._operationQueue[0];
_tryCallingRecursively(instance, opInfo);
});
};
}