Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

scratch-audio

Package Overview
Dependencies
Maintainers
1
Versions
440
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

scratch-audio - npm Package Compare versions

Comparing version 0.1.0-prerelease.20180621200816 to 0.1.0-prerelease.20180621210133

src/effects/EffectChain.js

2

package.json
{
"name": "scratch-audio",
"version": "0.1.0-prerelease.20180621200816",
"version": "0.1.0-prerelease.20180621210133",
"description": "audio engine for scratch 3.0",

@@ -5,0 +5,0 @@ "main": "dist.js",

@@ -8,6 +8,12 @@ const StartAudioContext = require('./StartAudioContext');

const ADPCMSoundDecoder = require('./ADPCMSoundDecoder');
const AudioPlayer = require('./AudioPlayer');
const Loudness = require('./Loudness');
const SoundPlayer = require('./GreenPlayer');
const SoundPlayer = require('./SoundPlayer');
const EffectChain = require('./effects/EffectChain');
const PanEffect = require('./effects/PanEffect');
const PitchEffect = require('./effects/PitchEffect');
const VolumeEffect = require('./effects/VolumeEffect');
const SoundBank = require('./SoundBank');
/**

@@ -67,2 +73,8 @@ * Wrapper to ensure that audioContext.decodeAudioData is a promise

this.loudness = null;
/**
* Array of effects applied in order, left to right,
* Left is closest to input, Right is closest to output
*/
this.effects = [PanEffect, PitchEffect, VolumeEffect];
}

@@ -191,6 +203,5 @@

* Retrieve the audio buffer as held in memory for a given sound id.
* @param {!string} soundId - the id of the sound buffer to get
* @return {AudioBuffer} the buffer corresponding to the given sound id.
* @todo remove this
*/
getSoundBuffer (soundId) {
getSoundBuffer () {
// todo: Deprecate audioBuffers. If something wants to hold onto the

@@ -201,3 +212,3 @@ // buffer, it should. Otherwise buffers need to be able to release their

// each audio channel.
return this.audioBuffers[soundId];
log.warn('The getSoundBuffer function is no longer available. Use soundBank.getSoundPlayer().buffer.');
}

@@ -207,9 +218,6 @@

* Add or update the in-memory audio buffer to a new one by soundId.
* @param {!string} soundId - the id of the sound buffer to update.
* @param {AudioBuffer} newBuffer - the new buffer to swap in.
* @return {string} The uid of the sound that was updated or added
* @todo remove this
*/
updateSoundBuffer (soundId, newBuffer) {
this.audioBuffers[soundId] = newBuffer;
return soundId;
updateSoundBuffer () {
log.warn('The updateSoundBuffer function is no longer available. Use soundBank.getSoundPlayer().buffer.');
}

@@ -241,12 +249,20 @@

/**
* Create an AudioPlayer. Each sprite or clone has an AudioPlayer.
* It includes a reference to the AudioEngine so it can use global
* functionality such as playing notes.
* @return {AudioPlayer} new AudioPlayer instance
* Deprecated way to create an AudioPlayer
* @todo remove this
*/
createPlayer () {
return new AudioPlayer(this);
log.warn('the createPlayer method is no longer available, please use createBank');
}
createEffectChain () {
const effects = new EffectChain(this, this.effects);
effects.connect(this);
return effects;
}
createBank () {
return new SoundBank(this, this.createEffectChain());
}
}
module.exports = AudioEngine;

@@ -27,2 +27,10 @@ /**

/**
* Return the name of the effect.
* @type {string}
*/
get name () {
throw new Error(`${this.constructor.name}.name is not implemented`);
}
/**
* Default value to set the Effect to when constructed and when clear'ed.

@@ -29,0 +37,0 @@ * @const {number}

@@ -23,2 +23,6 @@ const Effect = require('./Effect');

get name () {
return 'pan';
}
/**

@@ -25,0 +29,0 @@ * Initialize the Effect.

@@ -38,2 +38,6 @@ const Effect = require('./Effect');

get name () {
return 'pitch';
}
/**

@@ -40,0 +44,0 @@ * Should the effect be connected to the audio graph?

@@ -15,2 +15,6 @@ const Effect = require('./Effect');

get name () {
return 'volume';
}
/**

@@ -17,0 +21,0 @@ * Initialize the Effect.

@@ -1,17 +0,68 @@

const log = require('./log');
const {EventEmitter} = require('events');
const VolumeEffect = require('./effects/VolumeEffect');
/**
* A SoundPlayer stores an audio buffer, and plays it
* Name of event that indicates playback has ended.
* @const {string}
*/
class SoundPlayer {
const ON_ENDED = 'ended';
class SoundPlayer extends EventEmitter {
/**
* @param {AudioContext} audioContext - a webAudio context
* Play sounds that stop without audible clipping.
*
* @param {AudioEngine} audioEngine - engine to play sounds on
* @param {object} data - required data for sound playback
* @param {string} data.id - a unique id for this sound
* @param {ArrayBuffer} data.buffer - buffer of the sound's waveform to play
* @constructor
*/
constructor (audioContext) {
this.audioContext = audioContext;
constructor (audioEngine, {id, buffer}) {
super();
this.id = id;
this.audioEngine = audioEngine;
this.buffer = buffer;
this.outputNode = null;
this.buffer = null;
this.bufferSource = null;
this.volumeEffect = null;
this.target = null;
this.initialized = false;
this.isPlaying = false;
this.startingUntil = 0;
this.playbackRate = 1;
// handleEvent is a EventTarget api for the DOM, however the web-audio-test-api we use
// uses an addEventListener that isn't compatable with object and requires us to pass
// this bound function instead
this.handleEvent = this.handleEvent.bind(this);
}
/**
* Is plaback currently starting?
* @type {boolean}
*/
get isStarting () {
return this.isPlaying && this.startingUntil > this.audioEngine.audioContext.currentTime;
}
/**
* Handle any event we have told the output node to listen for.
* @param {Event} event - dom event to handle
*/
handleEvent (event) {
if (event.type === ON_ENDED) {
this.onEnded();
}
}
/**
* Event listener for when playback ends.
*/
onEnded () {
this.emit('stop');
this.isPlaying = false;

@@ -21,72 +72,220 @@ }

/**
* Connect the SoundPlayer to an output node
* @param {GainNode} node - an output node to connect to
* Create the buffer source node during initialization or secondary
* playback.
*/
connect (node) {
this.outputNode = node;
_createSource () {
if (this.outputNode !== null) {
this.outputNode.removeEventListener(ON_ENDED, this.handleEvent);
this.outputNode.disconnect();
}
this.outputNode = this.audioEngine.audioContext.createBufferSource();
this.outputNode.playbackRate.value = this.playbackRate;
this.outputNode.buffer = this.buffer;
this.outputNode.addEventListener(ON_ENDED, this.handleEvent);
if (this.target !== null) {
this.connect(this.target);
}
}
/**
* Set an audio buffer
* @param {AudioBuffer} buffer - Buffer to set
* Initialize the player for first playback.
*/
setBuffer (buffer) {
this.buffer = buffer;
initialize () {
this.initialized = true;
this._createSource();
}
/**
* Set the playback rate for the sound
* @param {number} playbackRate - a ratio where 1 is normal playback, 0.5 is half speed, 2 is double speed, etc.
* Connect the player to the engine or an effect chain.
* @param {object} target - object to connect to
* @returns {object} - return this sound player
*/
setPlaybackRate (playbackRate) {
this.playbackRate = playbackRate;
if (this.bufferSource && this.bufferSource.playbackRate) {
this.bufferSource.playbackRate.value = this.playbackRate;
connect (target) {
if (target === this.volumeEffect) {
this.outputNode.disconnect();
this.outputNode.connect(this.volumeEffect.getInputNode());
return;
}
this.target = target;
if (!this.initialized) {
return;
}
if (this.volumeEffect === null) {
this.outputNode.disconnect();
this.outputNode.connect(target.getInputNode());
} else {
this.volumeEffect.connect(target);
}
return this;
}
/**
* Stop the sound
* Teardown the player.
*/
stop () {
if (this.bufferSource && this.isPlaying) {
this.bufferSource.stop();
dispose () {
if (!this.initialized) {
return;
}
this.stopImmediately();
if (this.volumeEffect !== null) {
this.volumeEffect.dispose();
this.volumeEffect = null;
}
this.outputNode.disconnect();
this.outputNode = null;
this.target = null;
this.initialized = false;
}
/**
* Take the internal state of this player and create a new player from
* that. Restore the state of this player to that before its first playback.
*
* The returned player can be used to stop the original playback or
* continue it without manipulation from the original player.
*
* @returns {SoundPlayer} - new SoundPlayer with old state
*/
take () {
if (this.outputNode) {
this.outputNode.removeEventListener(ON_ENDED, this.handleEvent);
}
const taken = new SoundPlayer(this.audioEngine, this);
taken.playbackRate = this.playbackRate;
if (this.isPlaying) {
taken.startingUntil = this.startingUntil;
taken.isPlaying = this.isPlaying;
taken.initialized = this.initialized;
taken.outputNode = this.outputNode;
taken.outputNode.addEventListener(ON_ENDED, taken.handleEvent);
taken.volumeEffect = this.volumeEffect;
if (taken.volumeEffect) {
taken.volumeEffect.audioPlayer = taken;
}
if (this.target !== null) {
taken.connect(this.target);
}
this.emit('stop');
taken.emit('play');
}
this.outputNode = null;
this.volumeEffect = null;
this.initialized = false;
this.startingUntil = 0;
this.isPlaying = false;
return taken;
}
/**
* Start playing the sound
* The web audio framework requires a new audio buffer source node for each playback
* Start playback for this sound.
*
* If the sound is already playing it will stop playback with a quick fade
* out.
*/
start () {
if (!this.buffer) {
log.warn('tried to play a sound that was not loaded yet');
play () {
if (this.isStarting) {
this.emit('stop');
this.emit('play');
return;
}
this.bufferSource = this.audioContext.createBufferSource();
this.bufferSource.buffer = this.buffer;
this.bufferSource.playbackRate.value = this.playbackRate;
this.bufferSource.connect(this.outputNode);
this.bufferSource.start();
if (this.isPlaying) {
this.stop();
}
if (this.initialized) {
this._createSource();
} else {
this.initialize();
}
this.outputNode.start();
this.isPlaying = true;
this.startingUntil = this.audioEngine.audioContext.currentTime + this.audioEngine.DECAY_TIME;
this.emit('play');
}
/**
* The sound has finished playing. This is called at the correct time even if the playback rate
* has been changed
* @return {Promise} a Promise that resolves when the sound finishes playing
* Stop playback after quickly fading out.
*/
stop () {
if (!this.isPlaying) {
return;
}
// always do a manual stop on a taken / volume effect fade out sound player
// take will emit "stop" as well as reset all of our playing statuses / remove our
// nodes / etc
const taken = this.take();
taken.volumeEffect = new VolumeEffect(taken.audioEngine, taken, null);
taken.volumeEffect.connect(taken.target);
// volumeEffect will recursively connect to us if it needs to, so this happens too:
// taken.connect(taken.volumeEffect);
taken.finished().then(() => taken.dispose());
taken.volumeEffect.set(0);
taken.outputNode.stop(this.audioEngine.audioContext.currentTime + this.audioEngine.DECAY_TIME);
}
/**
* Stop immediately without fading out. May cause audible clipping.
*/
stopImmediately () {
if (!this.isPlaying) {
return;
}
this.outputNode.stop();
this.isPlaying = false;
this.startingUntil = 0;
this.emit('stop');
}
/**
* Return a promise that resolves when the sound next finishes.
* @returns {Promise} - resolves when the sound finishes
*/
finished () {
return new Promise(resolve => {
this.bufferSource.onended = () => {
this.isPlaying = false;
resolve();
};
this.once('stop', resolve);
});
}
/**
* Set the sound's playback rate.
* @param {number} value - playback rate. Default is 1.
*/
setPlaybackRate (value) {
this.playbackRate = value;
if (this.initialized) {
this.outputNode.playbackRate.value = value;
}
}
}
module.exports = SoundPlayer;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc