1
0
mirror of https://github.com/psychopy/psychojs.git synced 2025-05-11 16:18:10 +00:00

sound: enforce formatting rules

This commit is contained in:
Sotiri Bakagiannis 2021-07-09 14:07:55 +01:00
parent 57a590c536
commit 5468898716
8 changed files with 325 additions and 409 deletions

View File

@ -7,11 +7,10 @@
* @license Distributed under the terms of the MIT License
*/
import {PsychObject} from '../util/PsychObject.js';
import {PsychoJS} from '../core/PsychoJS.js';
import {ExperimentHandler} from '../data/ExperimentHandler.js';
import * as util from '../util/Util.js';
import { PsychoJS } from "../core/PsychoJS.js";
import { ExperimentHandler } from "../data/ExperimentHandler.js";
import { PsychObject } from "../util/PsychObject.js";
import * as util from "../util/Util.js";
/**
* <p>AudioClip encapsulates an audio recording.</p>
@ -28,20 +27,19 @@ import * as util from '../util/Util.js';
*/
export class AudioClip extends PsychObject
{
constructor({psychoJS, name, sampleRateHz, format, data, autoLog} = {})
constructor({ psychoJS, name, sampleRateHz, format, data, autoLog } = {})
{
super(psychoJS);
this._addAttribute('name', name, 'audioclip');
this._addAttribute('format', format);
this._addAttribute('sampleRateHz', sampleRateHz);
this._addAttribute('data', data);
this._addAttribute('autoLog', false, autoLog);
this._addAttribute('status', AudioClip.Status.CREATED);
this._addAttribute("name", name, "audioclip");
this._addAttribute("format", format);
this._addAttribute("sampleRateHz", sampleRateHz);
this._addAttribute("data", data);
this._addAttribute("autoLog", false, autoLog);
this._addAttribute("status", AudioClip.Status.CREATED);
// add a volume attribute, for playback:
this._addAttribute('volume', 1.0);
this._addAttribute("volume", 1.0);
if (this._autoLog)
{
@ -52,7 +50,6 @@ export class AudioClip extends PsychObject
this._decodeAudio();
}
/**
* Set the volume of the playback.
*
@ -66,7 +63,6 @@ export class AudioClip extends PsychObject
this._volume = volume;
}
/**
* Start playing the audio clip.
*
@ -76,7 +72,7 @@ export class AudioClip extends PsychObject
*/
async startPlayback()
{
this._psychoJS.logger.debug('request to play the audio clip');
this._psychoJS.logger.debug("request to play the audio clip");
// wait for the decoding to complete:
await this._decodeAudio();
@ -103,7 +99,6 @@ export class AudioClip extends PsychObject
this._source.start();
}
/**
* Stop playing the audio clip.
*
@ -120,7 +115,6 @@ export class AudioClip extends PsychObject
this._source.stop();
}
/**
* Get the duration of the audio clip, in seconds.
*
@ -137,7 +131,6 @@ export class AudioClip extends PsychObject
return this._audioBuffer.duration;
}
/**
* Upload the audio clip to the pavlovia server.
*
@ -147,17 +140,18 @@ export class AudioClip extends PsychObject
*/
upload()
{
this._psychoJS.logger.debug('request to upload the audio clip to pavlovia.org');
this._psychoJS.logger.debug("request to upload the audio clip to pavlovia.org");
// add a format-dependent audio extension to the name:
const filename = this._name + util.extensionFromMimeType(this._format);
// if the audio recording cannot be uploaded, e.g. the experiment is running locally, or
// if it is piloting mode, then we offer the audio clip as a file for download:
if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER ||
this._psychoJS.config.experiment.status !== 'RUNNING' ||
this._psychoJS._serverMsg.has('__pilotToken'))
if (
this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
|| this._psychoJS.config.experiment.status !== "RUNNING"
|| this._psychoJS._serverMsg.has("__pilotToken")
)
{
return this.download(filename);
}
@ -166,8 +160,6 @@ export class AudioClip extends PsychObject
return this._psychoJS.serverManager.uploadAudio(this._data, filename);
}
/**
* Offer the audio clip to the participant as a sound file to download.
*
@ -175,9 +167,9 @@ export class AudioClip extends PsychObject
* @function
* @public
*/
download(filename = 'audio.webm')
download(filename = "audio.webm")
{
const anchor = document.createElement('a');
const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(this._data);
anchor.download = filename;
document.body.appendChild(anchor);
@ -185,7 +177,6 @@ export class AudioClip extends PsychObject
document.body.removeChild(anchor);
}
/**
* Transcribe the audio clip.
*
@ -196,10 +187,10 @@ export class AudioClip extends PsychObject
* @return {Promise<>} a promise resolving to the transcript and associated
* transcription confidence
*/
async transcribe({engine, languageCode} = {})
async transcribe({ engine, languageCode } = {})
{
const response = {
origin: 'AudioClip.transcribe',
origin: "AudioClip.transcribe",
context: `when transcribing audio clip: ${this._name}`,
};
@ -215,11 +206,11 @@ export class AudioClip extends PsychObject
transcriptionKey = key.value;
}
}
if (typeof transcriptionKey === 'undefined')
if (typeof transcriptionKey === "undefined")
{
throw {
...response,
error: `missing key for engine: ${fullEngineName}`
error: `missing key for engine: ${fullEngineName}`,
};
}
@ -235,13 +226,11 @@ export class AudioClip extends PsychObject
{
throw {
...response,
error: `unsupported speech-to-text engine: ${engine}`
error: `unsupported speech-to-text engine: ${engine}`,
};
}
}
/**
* Transcribe the audio clip using the Google Cloud Speech-To-Text Engine.
*
@ -272,31 +261,31 @@ export class AudioClip extends PsychObject
// query the Google speech-to-text service:
const body = {
config: {
encoding: 'LINEAR16',
encoding: "LINEAR16",
sampleRateHertz: this._sampleRateHz,
languageCode
languageCode,
},
audio: {
content: base64Data
content: base64Data,
},
};
const url = `https://speech.googleapis.com/v1/speech:recognize?key=${transcriptionKey}`;
const response = await fetch(url, {
method: 'POST',
method: "POST",
headers: {
'Content-Type': 'application/json',
"Content-Type": "application/json",
},
body: JSON.stringify(body)
body: JSON.stringify(body),
});
// convert the response to json:
const decodedResponse = await response.json();
this._psychoJS.logger.debug('speech.googleapis.com response:', JSON.stringify(decodedResponse));
this._psychoJS.logger.debug("speech.googleapis.com response:", JSON.stringify(decodedResponse));
// TODO deal with more than one results and/or alternatives
if (('results' in decodedResponse) && (decodedResponse.results.length > 0))
if (("results" in decodedResponse) && (decodedResponse.results.length > 0))
{
resolve(decodedResponse.results[0].alternatives[0]);
}
@ -304,21 +293,20 @@ export class AudioClip extends PsychObject
{
// no transcription available:
resolve({
transcript: '',
confidence: -1
transcript: "",
confidence: -1,
});
}
});
}
/**
* Decode the formatted audio data (e.g. webm) into a 32bit float PCM audio buffer.
*
*/
_decodeAudio()
{
this._psychoJS.logger.debug('request to decode the data of the audio clip');
this._psychoJS.logger.debug("request to decode the data of the audio clip");
// if the audio clip is ready, the PCM audio data is available in _audioData, a Float32Array:
if (this._status === AudioClip.Status.READY)
@ -326,12 +314,11 @@ export class AudioClip extends PsychObject
return;
}
// if we are already decoding, wait until the process completed:
if (this._status === AudioClip.Status.DECODING)
{
const self = this;
return new Promise(function (resolve, reject)
return new Promise(function(resolve, reject)
{
self._decodingCallbacks.push(resolve);
@ -339,7 +326,6 @@ export class AudioClip extends PsychObject
}.bind(this));
}
// otherwise, start decoding the input formatted audio data:
this._status = AudioClip.Status.DECODING;
this._audioData = null;
@ -348,7 +334,7 @@ export class AudioClip extends PsychObject
this._decodingCallbacks = [];
this._audioContext = new (window.AudioContext || window.webkitAudioContext)({
sampleRate: this._sampleRateHz
sampleRate: this._sampleRateHz,
});
const reader = new window.FileReader();
@ -383,12 +369,11 @@ export class AudioClip extends PsychObject
reader.onerror = (error) =>
{
// TODO
}
};
reader.readAsArrayBuffer(this._data);
}
/**
* Convert an array buffer to a base64 string.
*
@ -403,63 +388,65 @@ export class AudioClip extends PsychObject
*/
_base64ArrayBuffer(arrayBuffer)
{
let base64 = '';
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let base64 = "";
const encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const bytes = new Uint8Array(arrayBuffer);
const byteLength = bytes.byteLength;
const byteRemainder = byteLength % 3;
const mainLength = byteLength - byteRemainder;
const bytes = new Uint8Array(arrayBuffer);
const byteLength = bytes.byteLength;
const byteRemainder = byteLength % 3;
const mainLength = byteLength - byteRemainder;
let a;
let b;
let c;
let d;
let chunk;
let a;
let b;
let c;
let d;
let chunk;
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i += 3) {
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i += 3)
{
// Combine the three bytes into a single integer
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
// Convert the raw binary segments to the appropriate ASCII encoding
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
}
// Deal with the remaining bytes and padding
if (byteRemainder === 1)
{
chunk = bytes[mainLength];
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4; // 3 = 2^2 - 1
base64 += `${encodings[a]}${encodings[b]}==`;
}
else if (byteRemainder === 2)
{
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2; // 15 = 2^4 - 1
base64 += `${encodings[a]}${encodings[b]}${encodings[c]}=`;
}
return base64;
}
// Deal with the remaining bytes and padding
if (byteRemainder === 1) {
chunk = bytes[mainLength];
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4; // 3 = 2^2 - 1
base64 += `${encodings[a]}${encodings[b]}==`;
} else if (byteRemainder === 2) {
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2; // 15 = 2^4 - 1
base64 += `${encodings[a]}${encodings[b]}${encodings[c]}=`;
}
return base64;
}
}
/**
* Recognition engines.
*
@ -472,10 +459,9 @@ AudioClip.Engine = {
/**
* Google Cloud Speech-to-Text.
*/
GOOGLE: Symbol.for('GOOGLE')
GOOGLE: Symbol.for("GOOGLE"),
};
/**
* AudioClip status.
*
@ -484,9 +470,9 @@ AudioClip.Engine = {
* @public
*/
AudioClip.Status = {
CREATED: Symbol.for('CREATED'),
CREATED: Symbol.for("CREATED"),
DECODING: Symbol.for('DECODING'),
DECODING: Symbol.for("DECODING"),
READY: Symbol.for('READY')
READY: Symbol.for("READY"),
};

View File

@ -7,9 +7,8 @@
* @license Distributed under the terms of the MIT License
*/
import {SoundPlayer} from './SoundPlayer.js';
import {AudioClip} from "./AudioClip.js";
import { AudioClip } from "./AudioClip.js";
import { SoundPlayer } from "./SoundPlayer.js";
/**
* <p>This class handles the playback of an audio clip, e.g. a microphone recording.</p>
@ -29,28 +28,27 @@ import {AudioClip} from "./AudioClip.js";
export class AudioClipPlayer extends SoundPlayer
{
constructor({
psychoJS,
audioClip,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 0,
loops = 0
} = {})
psychoJS,
audioClip,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 0,
loops = 0,
} = {})
{
super(psychoJS);
this._addAttribute('audioClip', audioClip);
this._addAttribute('startTime', startTime);
this._addAttribute('stopTime', stopTime);
this._addAttribute('stereo', stereo);
this._addAttribute('loops', loops);
this._addAttribute('volume', volume);
this._addAttribute("audioClip", audioClip);
this._addAttribute("startTime", startTime);
this._addAttribute("stopTime", stopTime);
this._addAttribute("stereo", stereo);
this._addAttribute("loops", loops);
this._addAttribute("volume", volume);
this._currentLoopIndex = -1;
}
/**
* Determine whether this player can play the given sound.
*
@ -73,7 +71,7 @@ export class AudioClipPlayer extends SoundPlayer
stopTime: sound.stopTime,
stereo: sound.stereo,
loops: sound.loops,
volume: sound.volume
volume: sound.volume,
});
return player;
}
@ -82,7 +80,6 @@ export class AudioClipPlayer extends SoundPlayer
return undefined;
}
/**
* Get the duration of the AudioClip, in seconds.
*
@ -96,7 +93,6 @@ export class AudioClipPlayer extends SoundPlayer
return this._audioClip.getDuration();
}
/**
* Set the duration of the audio clip.
*
@ -110,13 +106,12 @@ export class AudioClipPlayer extends SoundPlayer
// TODO
throw {
origin: 'AudioClipPlayer.setDuration',
context: 'when setting the duration of the playback for audio clip player: ' + this._name,
error: 'not implemented yet'
origin: "AudioClipPlayer.setDuration",
context: "when setting the duration of the playback for audio clip player: " + this._name,
error: "not implemented yet",
};
}
/**
* Set the volume of the playback.
*
@ -133,7 +128,6 @@ export class AudioClipPlayer extends SoundPlayer
this._audioClip.setVolume((mute) ? 0.0 : volume);
}
/**
* Set the number of loops.
*
@ -150,7 +144,6 @@ export class AudioClipPlayer extends SoundPlayer
// TODO
}
/**
* Start playing the sound.
*
@ -162,7 +155,7 @@ export class AudioClipPlayer extends SoundPlayer
*/
play(loops, fadeDuration = 17)
{
if (typeof loops !== 'undefined')
if (typeof loops !== "undefined")
{
this.setLoops(loops);
}
@ -176,7 +169,6 @@ export class AudioClipPlayer extends SoundPlayer
this._audioClip.startPlayback();
}
/**
* Stop playing the sound immediately.
*
@ -189,5 +181,4 @@ export class AudioClipPlayer extends SoundPlayer
{
this._audioClip.stopPlayback(fadeDuration);
}
}

View File

@ -7,12 +7,12 @@
* @license Distributed under the terms of the MIT License
*/
import {Clock} from "../util/Clock.js";
import {PsychObject} from "../util/PsychObject.js";
import {PsychoJS} from "../core/PsychoJS.js";
import * as util from '../util/Util.js';
import {ExperimentHandler} from "../data/ExperimentHandler.js";
import {AudioClip} from "./AudioClip.js";
import { PsychoJS } from "../core/PsychoJS.js";
import { ExperimentHandler } from "../data/ExperimentHandler.js";
import { Clock } from "../util/Clock.js";
import { PsychObject } from "../util/PsychObject.js";
import * as util from "../util/Util.js";
import { AudioClip } from "./AudioClip.js";
/**
* <p>This manager handles the recording of audio signal.</p>
@ -29,18 +29,17 @@ import {AudioClip} from "./AudioClip.js";
*/
export class Microphone extends PsychObject
{
constructor({win, name, format, sampleRateHz, clock, autoLog} = {})
constructor({ win, name, format, sampleRateHz, clock, autoLog } = {})
{
super(win._psychoJS);
this._addAttribute('win', win, undefined);
this._addAttribute('name', name, 'microphone');
this._addAttribute('format', format, 'audio/webm;codecs=opus', this._onChange);
this._addAttribute('sampleRateHz', sampleRateHz, 48000, this._onChange);
this._addAttribute('clock', clock, new Clock());
this._addAttribute('autoLog', false, autoLog);
this._addAttribute('status', PsychoJS.Status.NOT_STARTED);
this._addAttribute("win", win, undefined);
this._addAttribute("name", name, "microphone");
this._addAttribute("format", format, "audio/webm;codecs=opus", this._onChange);
this._addAttribute("sampleRateHz", sampleRateHz, 48000, this._onChange);
this._addAttribute("clock", clock, new Clock());
this._addAttribute("autoLog", false, autoLog);
this._addAttribute("status", PsychoJS.Status.NOT_STARTED);
// prepare the recording:
this._prepareRecording();
@ -51,7 +50,6 @@ export class Microphone extends PsychObject
}
}
/**
* Submit a request to start the recording.
*
@ -68,19 +66,18 @@ export class Microphone extends PsychObject
// with a new recording:
if (this._status === PsychoJS.Status.PAUSED)
{
return this.resume({clear: true});
return this.resume({ clear: true });
}
if (this._status !== PsychoJS.Status.STARTED)
{
this._psychoJS.logger.debug('request to start audio recording');
this._psychoJS.logger.debug("request to start audio recording");
try
{
if (!this._recorder)
{
throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
this._recorder.start();
@ -96,21 +93,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
this._psychoJS.logger.error('unable to start the audio recording: ' + JSON.stringify(error));
this._psychoJS.logger.error("unable to start the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
origin: 'Microphone.start',
context: 'when starting the audio recording for microphone: ' + this._name,
error
origin: "Microphone.start",
context: "when starting the audio recording for microphone: " + this._name,
error,
};
}
}
}
/**
* Submit a request to stop the recording.
*
@ -122,14 +116,14 @@ export class Microphone extends PsychObject
* @return {Promise} promise fulfilled when the recording actually stopped, and the recorded
* data was made available
*/
stop({filename} = {})
stop({ filename } = {})
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
this._psychoJS.logger.debug('request to stop audio recording');
this._psychoJS.logger.debug("request to stop audio recording");
this._stopOptions = {
filename
filename,
};
// note: calling the stop method of the MediaRecorder will first raise a dataavailable event,
@ -148,7 +142,6 @@ export class Microphone extends PsychObject
}
}
/**
* Submit a request to pause the recording.
*
@ -160,13 +153,13 @@ export class Microphone extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED)
{
this._psychoJS.logger.debug('request to pause audio recording');
this._psychoJS.logger.debug("request to pause audio recording");
try
{
if (!this._recorder)
{
throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
// note: calling the pause method of the MediaRecorder raises a pause event
@ -182,20 +175,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
self._psychoJS.logger.error('unable to pause the audio recording: ' + JSON.stringify(error));
self._psychoJS.logger.error("unable to pause the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
origin: 'Microphone.pause',
context: 'when pausing the audio recording for microphone: ' + this._name,
error
origin: "Microphone.pause",
context: "when pausing the audio recording for microphone: " + this._name,
error,
};
}
}
}
/**
* Submit a request to resume the recording.
*
@ -207,17 +198,17 @@ export class Microphone extends PsychObject
* resuming the recording
* @return {Promise} promise fulfilled when the recording actually resumed
*/
resume({clear = false } = {})
resume({ clear = false } = {})
{
if (this._status === PsychoJS.Status.PAUSED)
{
this._psychoJS.logger.debug('request to resume audio recording');
this._psychoJS.logger.debug("request to resume audio recording");
try
{
if (!this._recorder)
{
throw 'the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio';
throw "the recorder has not been created yet, possibly because the participant has not given the authorisation to record audio";
}
// empty the audio buffer is needed:
@ -239,20 +230,18 @@ export class Microphone extends PsychObject
}
catch (error)
{
self._psychoJS.logger.error('unable to resume the audio recording: ' + JSON.stringify(error));
self._psychoJS.logger.error("unable to resume the audio recording: " + JSON.stringify(error));
this._status = PsychoJS.Status.ERROR;
throw {
origin: 'Microphone.resume',
context: 'when resuming the audio recording for microphone: ' + this._name,
error
origin: "Microphone.resume",
context: "when resuming the audio recording for microphone: " + this._name,
error,
};
}
}
}
/**
* Submit a request to flush the recording.
*
@ -264,7 +253,7 @@ export class Microphone extends PsychObject
{
if (this._status === PsychoJS.Status.STARTED || this._status === PsychoJS.Status.PAUSED)
{
this._psychoJS.logger.debug('request to flush audio recording');
this._psychoJS.logger.debug("request to flush audio recording");
// note: calling the requestData method of the MediaRecorder will raise a
// dataavailable event
@ -281,7 +270,6 @@ export class Microphone extends PsychObject
}
}
/**
* Offer the audio recording to the participant as a sound file to download.
*
@ -290,11 +278,11 @@ export class Microphone extends PsychObject
* @public
* @param {string} filename the filename
*/
download(filename = 'audio.webm')
download(filename = "audio.webm")
{
const audioBlob = new Blob(this._audioBuffer);
const anchor = document.createElement('a');
const anchor = document.createElement("a");
anchor.href = window.URL.createObjectURL(audioBlob);
anchor.download = filename;
document.body.appendChild(anchor);
@ -302,7 +290,6 @@ export class Microphone extends PsychObject
document.body.removeChild(anchor);
}
/**
* Upload the audio recording to the pavlovia server.
*
@ -311,10 +298,10 @@ export class Microphone extends PsychObject
* @public
* @param {string} tag an optional tag for the audio file
*/
async upload({tag} = {})
async upload({ tag } = {})
{
// default tag: the name of this Microphone object
if (typeof tag === 'undefined')
if (typeof tag === "undefined")
{
tag = this._name;
}
@ -322,12 +309,13 @@ export class Microphone extends PsychObject
// add a format-dependent audio extension to the tag:
tag += util.extensionFromMimeType(this._format);
// if the audio recording cannot be uploaded, e.g. the experiment is running locally, or
// if it is piloting mode, then we offer the audio recording as a file for download:
if (this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER ||
this._psychoJS.config.experiment.status !== 'RUNNING' ||
this._psychoJS._serverMsg.has('__pilotToken'))
if (
this._psychoJS.getEnvironment() !== ExperimentHandler.Environment.SERVER
|| this._psychoJS.config.experiment.status !== "RUNNING"
|| this._psychoJS._serverMsg.has("__pilotToken")
)
{
return this.download(tag);
}
@ -337,7 +325,6 @@ export class Microphone extends PsychObject
return this._psychoJS.serverManager.uploadAudio(audioBlob, tag);
}
/**
* Get the current audio recording as an AudioClip in the given format.
*
@ -347,27 +334,25 @@ export class Microphone extends PsychObject
* @param {string} tag an optional tag for the audio clip
* @param {boolean} [flush=false] whether or not to first flush the recording
*/
async getRecording({tag, flush = false} = {})
async getRecording({ tag, flush = false } = {})
{
// default tag: the name of this Microphone object
if (typeof tag === 'undefined')
if (typeof tag === "undefined")
{
tag = this._name;
}
const audioClip = new AudioClip({
psychoJS: this._psychoJS,
name: tag,
format: this._format,
sampleRateHz: this._sampleRateHz,
data: new Blob(this._audioBuffer)
data: new Blob(this._audioBuffer),
});
return audioClip;
}
/**
* Callback for changes to the recording settings.
*
@ -389,7 +374,6 @@ export class Microphone extends PsychObject
this.start();
}
/**
* Prepare the recording.
*
@ -409,15 +393,15 @@ export class Microphone extends PsychObject
advanced: [
{
channelCount: 1,
sampleRate: this._sampleRateHz
}
]
}
sampleRate: this._sampleRateHz,
},
],
},
});
// check that the specified format is supported, use default if it is not:
let options;
if (typeof this._format === 'string' && MediaRecorder.isTypeSupported(this._format))
if (typeof this._format === "string" && MediaRecorder.isTypeSupported(this._format))
{
options = { type: this._format };
}
@ -428,7 +412,6 @@ export class Microphone extends PsychObject
this._recorder = new MediaRecorder(stream, options);
// setup the callbacks:
const self = this;
@ -440,7 +423,7 @@ export class Microphone extends PsychObject
self._audioBuffer.length = 0;
self._clock.reset();
self._status = PsychoJS.Status.STARTED;
self._psychoJS.logger.debug('audio recording started');
self._psychoJS.logger.debug("audio recording started");
// resolve the Microphone.start promise:
if (self._startCallback)
@ -453,7 +436,7 @@ export class Microphone extends PsychObject
this._recorder.onpause = () =>
{
self._status = PsychoJS.Status.PAUSED;
self._psychoJS.logger.debug('audio recording paused');
self._psychoJS.logger.debug("audio recording paused");
// resolve the Microphone.pause promise:
if (self._pauseCallback)
@ -466,7 +449,7 @@ export class Microphone extends PsychObject
this._recorder.onresume = () =>
{
self._status = PsychoJS.Status.STARTED;
self._psychoJS.logger.debug('audio recording resumed');
self._psychoJS.logger.debug("audio recording resumed");
// resolve the Microphone.resume promise:
if (self._resumeCallback)
@ -482,7 +465,7 @@ export class Microphone extends PsychObject
// add data to the buffer:
self._audioBuffer.push(data);
self._psychoJS.logger.debug('audio data added to the buffer');
self._psychoJS.logger.debug("audio data added to the buffer");
// resolve the data available promise, if needed:
if (self._dataAvailableCallback)
@ -494,7 +477,7 @@ export class Microphone extends PsychObject
// called upon Microphone.stop(), after data has been made available:
this._recorder.onstop = () =>
{
self._psychoJS.logger.debug('audio recording stopped');
self._psychoJS.logger.debug("audio recording stopped");
self._status = PsychoJS.Status.NOT_STARTED;
// resolve the Microphone.stop promise:
@ -506,7 +489,7 @@ export class Microphone extends PsychObject
// treat stop options if there are any:
// download to a file, immediately offered to the participant:
if (typeof self._stopOptions.filename === 'string')
if (typeof self._stopOptions.filename === "string")
{
self.download(self._stopOptions.filename);
}
@ -516,12 +499,8 @@ export class Microphone extends PsychObject
this._recorder.onerror = (event) =>
{
// TODO
self._psychoJS.logger.error('audio recording error: ' + JSON.stringify(event));
self._psychoJS.logger.error("audio recording error: " + JSON.stringify(event));
self._status = PsychoJS.Status.ERROR;
};
}
}

View File

@ -8,12 +8,11 @@
* @license Distributed under the terms of the MIT License
*/
import {PsychoJS} from '../core/PsychoJS.js';
import {PsychObject} from '../util/PsychObject.js';
import {TonePlayer} from './TonePlayer.js';
import {TrackPlayer} from './TrackPlayer.js';
import {AudioClipPlayer} from './AudioClipPlayer.js';
import { PsychoJS } from "../core/PsychoJS.js";
import { PsychObject } from "../util/PsychObject.js";
import { AudioClipPlayer } from "./AudioClipPlayer.js";
import { TonePlayer } from "./TonePlayer.js";
import { TrackPlayer } from "./TrackPlayer.js";
/**
* <p>This class handles sound playing (tones and tracks)</p>
@ -54,35 +53,35 @@ import {AudioClipPlayer} from './AudioClipPlayer.js';
export class Sound extends PsychObject
{
constructor({
name,
win,
value = 'C',
octave = 4,
secs = 0.5,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 1.0,
loops = 0,
//hamming = true,
autoLog = true
} = {})
name,
win,
value = "C",
octave = 4,
secs = 0.5,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 1.0,
loops = 0,
// hamming = true,
autoLog = true,
} = {})
{
super(win._psychoJS, name);
// the SoundPlayer, e.g. TonePlayer:
this._player = undefined;
this._addAttribute('win', win);
this._addAttribute('value', value);
this._addAttribute('octave', octave);
this._addAttribute('secs', secs);
this._addAttribute('startTime', startTime);
this._addAttribute('stopTime', stopTime);
this._addAttribute('stereo', stereo);
this._addAttribute('volume', volume);
this._addAttribute('loops', loops);
this._addAttribute('autoLog', autoLog);
this._addAttribute("win", win);
this._addAttribute("value", value);
this._addAttribute("octave", octave);
this._addAttribute("secs", secs);
this._addAttribute("startTime", startTime);
this._addAttribute("stopTime", stopTime);
this._addAttribute("stereo", stereo);
this._addAttribute("volume", volume);
this._addAttribute("loops", loops);
this._addAttribute("autoLog", autoLog);
// identify an appropriate player:
this._getPlayer();
@ -90,7 +89,6 @@ export class Sound extends PsychObject
this.status = PsychoJS.Status.NOT_STARTED;
}
/**
* Start playing the sound.
*
@ -107,7 +105,6 @@ export class Sound extends PsychObject
this._player.play(loops);
}
/**
* Stop playing the sound immediately.
*
@ -116,14 +113,13 @@ export class Sound extends PsychObject
* @param {boolean} [options.log= true] - whether or not to log
*/
stop({
log = true
} = {})
log = true,
} = {})
{
this._player.stop();
this.status = PsychoJS.Status.STOPPED;
}
/**
* Get the duration of the sound, in seconds.
*
@ -135,7 +131,6 @@ export class Sound extends PsychObject
return this._player.getDuration();
}
/**
* Set the playing volume of the sound.
*
@ -146,15 +141,14 @@ export class Sound extends PsychObject
*/
setVolume(volume, mute = false, log = true)
{
this._setAttribute('volume', volume, log);
this._setAttribute("volume", volume, log);
if (typeof this._player !== 'undefined')
if (typeof this._player !== "undefined")
{
this._player.setVolume(volume, mute);
}
}
/**
* Set the sound value on demand past initialisation.
*
@ -166,9 +160,9 @@ export class Sound extends PsychObject
{
if (sound instanceof Sound)
{
this._setAttribute('value', sound.value, log);
this._setAttribute("value", sound.value, log);
if (typeof this._player !== 'undefined')
if (typeof this._player !== "undefined")
{
this._player = this._player.constructor.accept(this);
}
@ -178,13 +172,12 @@ export class Sound extends PsychObject
}
throw {
origin: 'Sound.setSound',
context: 'when replacing the current sound',
error: 'invalid input, need an instance of the Sound class.'
origin: "Sound.setSound",
context: "when replacing the current sound",
error: "invalid input, need an instance of the Sound class.",
};
}
/**
* Set the number of loops.
*
@ -194,15 +187,14 @@ export class Sound extends PsychObject
*/
setLoops(loops = 0, log = true)
{
this._setAttribute('loops', loops, log);
this._setAttribute("loops", loops, log);
if (typeof this._player !== 'undefined')
if (typeof this._player !== "undefined")
{
this._player.setLoops(loops);
}
}
/**
* Set the duration (in seconds)
*
@ -212,15 +204,14 @@ export class Sound extends PsychObject
*/
setSecs(secs = 0.5, log = true)
{
this._setAttribute('secs', secs, log);
this._setAttribute("secs", secs, log);
if (typeof this._player !== 'undefined')
if (typeof this._player !== "undefined")
{
this._player.setDuration(secs);
}
}
/**
* Identify the appropriate player for the sound.
*
@ -231,26 +222,24 @@ export class Sound extends PsychObject
_getPlayer()
{
const acceptFns = [
sound => TonePlayer.accept(sound),
sound => TrackPlayer.accept(sound),
sound => AudioClipPlayer.accept(sound)
(sound) => TonePlayer.accept(sound),
(sound) => TrackPlayer.accept(sound),
(sound) => AudioClipPlayer.accept(sound),
];
for (const acceptFn of acceptFns)
{
this._player = acceptFn(this);
if (typeof this._player !== 'undefined')
if (typeof this._player !== "undefined")
{
return this._player;
}
}
throw {
origin: 'SoundPlayer._getPlayer',
context: 'when finding a player for the sound',
error: 'could not find an appropriate player.'
origin: "SoundPlayer._getPlayer",
context: "when finding a player for the sound",
error: "could not find an appropriate player.",
};
}
}

View File

@ -7,8 +7,7 @@
* @license Distributed under the terms of the MIT License
*/
import {PsychObject} from '../util/PsychObject.js';
import { PsychObject } from "../util/PsychObject.js";
/**
* <p>SoundPlayer is an interface for the sound players, who are responsible for actually playing the sounds, i.e. the tracks or the tones.</p>
@ -25,7 +24,6 @@ export class SoundPlayer extends PsychObject
super(psychoJS);
}
/**
* Determine whether this player can play the given sound.
*
@ -40,13 +38,12 @@ export class SoundPlayer extends PsychObject
static accept(sound)
{
throw {
origin: 'SoundPlayer.accept',
context: 'when evaluating whether this player can play a given sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.accept",
context: "when evaluating whether this player can play a given sound",
error: "this method is abstract and should not be called.",
};
}
/**
* Start playing the sound.
*
@ -59,13 +56,12 @@ export class SoundPlayer extends PsychObject
play(loops)
{
throw {
origin: 'SoundPlayer.play',
context: 'when starting the playback of a sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.play",
context: "when starting the playback of a sound",
error: "this method is abstract and should not be called.",
};
}
/**
* Stop playing the sound immediately.
*
@ -77,13 +73,12 @@ export class SoundPlayer extends PsychObject
stop()
{
throw {
origin: 'SoundPlayer.stop',
context: 'when stopping the playback of a sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.stop",
context: "when stopping the playback of a sound",
error: "this method is abstract and should not be called.",
};
}
/**
* Get the duration of the sound, in seconds.
*
@ -95,13 +90,12 @@ export class SoundPlayer extends PsychObject
getDuration()
{
throw {
origin: 'SoundPlayer.getDuration',
context: 'when getting the duration of the sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.getDuration",
context: "when getting the duration of the sound",
error: "this method is abstract and should not be called.",
};
}
/**
* Set the duration of the sound, in seconds.
*
@ -113,13 +107,12 @@ export class SoundPlayer extends PsychObject
setDuration(duration_s)
{
throw {
origin: 'SoundPlayer.setDuration',
context: 'when setting the duration of the sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.setDuration",
context: "when setting the duration of the sound",
error: "this method is abstract and should not be called.",
};
}
/**
* Set the number of loops.
*
@ -132,13 +125,12 @@ export class SoundPlayer extends PsychObject
setLoops(loops)
{
throw {
origin: 'SoundPlayer.setLoops',
context: 'when setting the number of loops',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.setLoops",
context: "when setting the number of loops",
error: "this method is abstract and should not be called.",
};
}
/**
* Set the volume of the tone.
*
@ -152,10 +144,9 @@ export class SoundPlayer extends PsychObject
setVolume(volume, mute = false)
{
throw {
origin: 'SoundPlayer.setVolume',
context: 'when setting the volume of the sound',
error: 'this method is abstract and should not be called.'
origin: "SoundPlayer.setVolume",
context: "when setting the volume of the sound",
error: "this method is abstract and should not be called.",
};
}
}

View File

@ -7,10 +7,9 @@
* @license Distributed under the terms of the MIT License
*/
import * as Tone from 'tone';
import * as Tone from "tone";
import { isNumeric } from "../util/Util.js";
import {SoundPlayer} from './SoundPlayer.js';
import { SoundPlayer } from "./SoundPlayer.js";
/**
* <p>This class handles the playing of tones.</p>
@ -28,23 +27,23 @@ import {SoundPlayer} from './SoundPlayer.js';
export class TonePlayer extends SoundPlayer
{
constructor({
psychoJS,
note = 'C4',
duration_s = 0.5,
volume = 1.0,
loops = 0,
soundLibrary = TonePlayer.SoundLibrary.TONE_JS,
autoLog = true
} = {})
psychoJS,
note = "C4",
duration_s = 0.5,
volume = 1.0,
loops = 0,
soundLibrary = TonePlayer.SoundLibrary.TONE_JS,
autoLog = true,
} = {})
{
super(psychoJS);
this._addAttribute('note', note);
this._addAttribute('duration_s', duration_s);
this._addAttribute('volume', volume);
this._addAttribute('loops', loops);
this._addAttribute('soundLibrary', soundLibrary);
this._addAttribute('autoLog', autoLog);
this._addAttribute("note", note);
this._addAttribute("duration_s", duration_s);
this._addAttribute("volume", volume);
this._addAttribute("loops", loops);
this._addAttribute("soundLibrary", soundLibrary);
this._addAttribute("autoLog", autoLog);
// initialise the sound library:
this._initSoundLibrary();
@ -58,7 +57,6 @@ export class TonePlayer extends SoundPlayer
}
}
/**
* Determine whether this player can play the given sound.
*
@ -82,32 +80,32 @@ export class TonePlayer extends SoundPlayer
note: sound.value,
duration_s: sound.secs,
volume: sound.volume,
loops: sound.loops
loops: sound.loops,
});
}
// if the sound's value is a string, we check whether it is a note:
if (typeof sound.value === 'string')
if (typeof sound.value === "string")
{
// mapping between the PsychoPY notes and the standard ones:
let psychopyToToneMap = new Map();
for (const note of ['A', 'B', 'C', 'D', 'E', 'F', 'G'])
for (const note of ["A", "B", "C", "D", "E", "F", "G"])
{
psychopyToToneMap.set(note, note);
psychopyToToneMap.set(note + 'fl', note + 'b');
psychopyToToneMap.set(note + 'sh', note + '#');
psychopyToToneMap.set(note + "fl", note + "b");
psychopyToToneMap.set(note + "sh", note + "#");
}
// check whether the sound's value is a recognised note:
const note = psychopyToToneMap.get(sound.value);
if (typeof note !== 'undefined')
if (typeof note !== "undefined")
{
return new TonePlayer({
psychoJS: sound.psychoJS,
note: note + sound.octave,
duration_s: sound.secs,
volume: sound.volume,
loops: sound.loops
loops: sound.loops,
});
}
}
@ -116,7 +114,6 @@ export class TonePlayer extends SoundPlayer
return undefined;
}
/**
* Get the duration of the sound.
*
@ -130,7 +127,6 @@ export class TonePlayer extends SoundPlayer
return this.duration_s;
}
/**
* Set the duration of the tone.
*
@ -144,7 +140,6 @@ export class TonePlayer extends SoundPlayer
this.duration_s = duration_s;
}
/**
* Set the number of loops.
*
@ -158,7 +153,6 @@ export class TonePlayer extends SoundPlayer
this._loops = loops;
}
/**
* Set the volume of the tone.
*
@ -174,7 +168,7 @@ export class TonePlayer extends SoundPlayer
if (this._soundLibrary === TonePlayer.SoundLibrary.TONE_JS)
{
if (typeof this._volumeNode !== 'undefined')
if (typeof this._volumeNode !== "undefined")
{
this._volumeNode.mute = mute;
this._volumeNode.volume.value = -60 + volume * 66;
@ -191,7 +185,6 @@ export class TonePlayer extends SoundPlayer
}
}
/**
* Start playing the sound.
*
@ -202,7 +195,7 @@ export class TonePlayer extends SoundPlayer
*/
play(loops)
{
if (typeof loops !== 'undefined')
if (typeof loops !== "undefined")
{
this._loops = loops;
}
@ -223,7 +216,7 @@ export class TonePlayer extends SoundPlayer
playToneCallback = () =>
{
self._webAudioOscillator = self._audioContext.createOscillator();
self._webAudioOscillator.type = 'sine';
self._webAudioOscillator.type = "sine";
self._webAudioOscillator.frequency.value = 440;
self._webAudioOscillator.connect(self._audioContext.destination);
const contextCurrentTime = self._audioContext.currentTime;
@ -237,7 +230,6 @@ export class TonePlayer extends SoundPlayer
{
playToneCallback();
}
// repeat forever:
else if (this.loops === -1)
{
@ -245,22 +237,21 @@ export class TonePlayer extends SoundPlayer
playToneCallback,
this.duration_s,
Tone.now(),
Infinity
Infinity,
);
}
else
// repeat this._loops times:
else
{
this._toneId = Tone.Transport.scheduleRepeat(
playToneCallback,
this.duration_s,
Tone.now(),
this.duration_s * (this._loops + 1)
this.duration_s * (this._loops + 1),
);
}
}
/**
* Stop playing the sound immediately.
*
@ -288,7 +279,6 @@ export class TonePlayer extends SoundPlayer
}
}
/**
* Initialise the sound library.
*
@ -302,24 +292,24 @@ export class TonePlayer extends SoundPlayer
_initSoundLibrary()
{
const response = {
origin: 'TonePlayer._initSoundLibrary',
context: 'when initialising the sound library'
origin: "TonePlayer._initSoundLibrary",
context: "when initialising the sound library",
};
if (this._soundLibrary === TonePlayer.SoundLibrary.TONE_JS)
{
// check that Tone.js is available:
if (typeof Tone === 'undefined')
if (typeof Tone === "undefined")
{
throw Object.assign(response, {
error: "Tone.js is not available. A different sound library must be selected. Please contact the experiment designer."
error: "Tone.js is not available. A different sound library must be selected. Please contact the experiment designer.",
});
}
// start the Tone Transport if it has not started already:
if (typeof Tone !== 'undefined' && Tone.Transport.state !== 'started')
if (typeof Tone !== "undefined" && Tone.Transport.state !== "started")
{
this.psychoJS.logger.info('[PsychoJS] start Tone Transport');
this.psychoJS.logger.info("[PsychoJS] start Tone Transport");
Tone.Transport.start(Tone.now());
// this is necessary to prevent Tone from introducing a delay when triggering a note
@ -330,14 +320,14 @@ export class TonePlayer extends SoundPlayer
// create a synth: we use a triangular oscillator with hardly any envelope:
this._synthOtions = {
oscillator: {
type: 'square' //'triangle'
type: "square", // 'triangle'
},
envelope: {
attack: 0.001, // 1ms
decay: 0.001, // 1ms
decay: 0.001, // 1ms
sustain: 1,
release: 0.001 // 1ms
}
release: 0.001, // 1ms
},
};
this._synth = new Tone.Synth(this._synthOtions);
@ -346,7 +336,7 @@ export class TonePlayer extends SoundPlayer
this._synth.connect(this._volumeNode);
// connect the volume node to the master output:
if (typeof this._volumeNode.toDestination === 'function')
if (typeof this._volumeNode.toDestination === "function")
{
this._volumeNode.toDestination();
}
@ -358,15 +348,15 @@ export class TonePlayer extends SoundPlayer
else
{
// create an AudioContext:
if (typeof this._audioContext === 'undefined')
if (typeof this._audioContext === "undefined")
{
const AudioContext = window.AudioContext || window.webkitAudioContext;
// if AudioContext is not available (e.g. on IE), we throw an exception:
if (typeof AudioContext === 'undefined')
if (typeof AudioContext === "undefined")
{
throw Object.assign(response, {
error: `AudioContext is not available on your browser, ${this._psychoJS.browser}, please contact the experiment designer.`
error: `AudioContext is not available on your browser, ${this._psychoJS.browser}, please contact the experiment designer.`,
});
}
@ -374,15 +364,13 @@ export class TonePlayer extends SoundPlayer
}
}
}
}
/**
*
* @type {{TONE_JS: *, AUDIO_CONTEXT: *}}
*/
TonePlayer.SoundLibrary = {
AUDIO_CONTEXT: Symbol.for('AUDIO_CONTEXT'),
TONE_JS: Symbol.for('TONE_JS')
AUDIO_CONTEXT: Symbol.for("AUDIO_CONTEXT"),
TONE_JS: Symbol.for("TONE_JS"),
};

View File

@ -7,8 +7,7 @@
* @license Distributed under the terms of the MIT License
*/
import {SoundPlayer} from './SoundPlayer.js';
import { SoundPlayer } from "./SoundPlayer.js";
/**
* <p>This class handles the playback of sound tracks.</p>
@ -30,28 +29,27 @@ import {SoundPlayer} from './SoundPlayer.js';
export class TrackPlayer extends SoundPlayer
{
constructor({
psychoJS,
howl,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 0,
loops = 0
} = {})
psychoJS,
howl,
startTime = 0,
stopTime = -1,
stereo = true,
volume = 0,
loops = 0,
} = {})
{
super(psychoJS);
this._addAttribute('howl', howl);
this._addAttribute('startTime', startTime);
this._addAttribute('stopTime', stopTime);
this._addAttribute('stereo', stereo);
this._addAttribute('loops', loops);
this._addAttribute('volume', volume);
this._addAttribute("howl", howl);
this._addAttribute("startTime", startTime);
this._addAttribute("stopTime", stopTime);
this._addAttribute("stereo", stereo);
this._addAttribute("loops", loops);
this._addAttribute("volume", volume);
this._currentLoopIndex = -1;
}
/**
* Determine whether this player can play the given sound.
*
@ -66,10 +64,10 @@ export class TrackPlayer extends SoundPlayer
static accept(sound)
{
// if the sound's value is a string, we check whether it is the name of a resource:
if (typeof sound.value === 'string')
if (typeof sound.value === "string")
{
const howl = sound.psychoJS.serverManager.getResource(sound.value);
if (typeof howl !== 'undefined')
if (typeof howl !== "undefined")
{
// build the player:
const player = new TrackPlayer({
@ -79,7 +77,7 @@ export class TrackPlayer extends SoundPlayer
stopTime: sound.stopTime,
stereo: sound.stereo,
loops: sound.loops,
volume: sound.volume
volume: sound.volume,
});
return player;
}
@ -89,7 +87,6 @@ export class TrackPlayer extends SoundPlayer
return undefined;
}
/**
* Get the duration of the sound, in seconds.
*
@ -103,7 +100,6 @@ export class TrackPlayer extends SoundPlayer
return this._howl.duration();
}
/**
* Set the duration of the track.
*
@ -114,14 +110,13 @@ export class TrackPlayer extends SoundPlayer
*/
setDuration(duration_s)
{
if (typeof this._howl !== 'undefined')
if (typeof this._howl !== "undefined")
{
// Unfortunately Howler.js provides duration setting method
this._howl._duration = duration_s;
}
}
/**
* Set the volume of the tone.
*
@ -139,7 +134,6 @@ export class TrackPlayer extends SoundPlayer
this._howl.mute(mute);
}
/**
* Set the number of loops.
*
@ -163,7 +157,6 @@ export class TrackPlayer extends SoundPlayer
}
}
/**
* Start playing the sound.
*
@ -175,7 +168,7 @@ export class TrackPlayer extends SoundPlayer
*/
play(loops, fadeDuration = 17)
{
if (typeof loops !== 'undefined')
if (typeof loops !== "undefined")
{
this.setLoops(loops);
}
@ -184,7 +177,7 @@ export class TrackPlayer extends SoundPlayer
if (loops > 0)
{
const self = this;
this._howl.on('end', (event) =>
this._howl.on("end", (event) =>
{
++this._currentLoopIndex;
if (self._currentLoopIndex > self._loops)
@ -205,7 +198,6 @@ export class TrackPlayer extends SoundPlayer
this._howl.fade(0, this._volume, fadeDuration, this._id);
}
/**
* Stop playing the sound immediately.
*
@ -216,11 +208,11 @@ export class TrackPlayer extends SoundPlayer
*/
stop(fadeDuration = 17)
{
this._howl.once('fade', (id) => {
this._howl.once("fade", (id) =>
{
this._howl.stop(id);
this._howl.off('end');
this._howl.off("end");
});
this._howl.fade(this._howl.volume(), 0, fadeDuration, this._id);
}
}

View File

@ -1,9 +1,9 @@
export * from './Sound.js';
export * from './SoundPlayer.js';
export * from './TonePlayer.js';
export * from './TrackPlayer.js';
export * from "./Sound.js";
export * from "./SoundPlayer.js";
export * from "./TonePlayer.js";
export * from "./TrackPlayer.js";
export * from './Microphone.js';
export * from './AudioClip.js';
export * from './AudioClipPlayer.js';
//export * from './Transcriber.js';
export * from "./AudioClip.js";
export * from "./AudioClipPlayer.js";
export * from "./Microphone.js";
// export * from './Transcriber.js';