mirror of
https://github.com/cocos/cocos-engine.git
synced 2026-09-08 00:29:28 +08:00
* 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
63 lines
2.4 KiB
TypeScript
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);
|
|
});
|
|
};
|
|
}
|