New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

cacophony

Package Overview
Dependencies
Maintainers
1
Versions
116
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cacophony - npm Package Compare versions

Comparing version 0.1.5 to 0.1.6

src/cacophony.test.ts

16

package.json
{
"name": "cacophony",
"version": "0.1.5",
"version": "0.1.6",
"description": "Typescript audio library with caching",

@@ -11,3 +11,4 @@ "main": "dist/index.js",

"prepublishOnly": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest",
"ci:test": "jest --no-color --ci"
},

@@ -20,6 +21,17 @@ "keywords": [

"license": "ISC",
"repository": {
"type": "git",
"url": "https://github.com/ctoth/cacophony.git"
},
"devDependencies": {
"@types/jest": "^29.5.8",
"jest": "^29.7.0",
"standardized-audio-context-mock": "^9.6.29",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
},
"dependencies": {

@@ -26,0 +38,0 @@ "standardized-audio-context": "^25.3.55"

3

README.md

@@ -89,5 +89,6 @@ Cacophony is a highly robust and versatile audio library for Typescript leveraging the WebAudio API, crafted to simplify audio management in complex applications. The core concept of Cacophony revolves around the creation and manipulation of three key elements: `Sound`, `Playback`, and `Group`.

All classes representing sound sources (`Sound`, `Playback`, `Group`) offer the following methods for a consistent interface and user-friendly experience:
All classes representing sound sources (`Sound`, `Playback`, `Group`) and the `BaseSound` interface offer the following methods for a consistent interface and user-friendly experience:
- `play()`
- `seek(time: number)`: Seeks the current playback to the specified time in seconds.
- `stop()`

@@ -94,0 +95,0 @@ - `pause()`

@@ -1,2 +0,2 @@

import { AudioContext, IAudioBuffer, IAudioBufferSourceNode, IAudioListener, IBiquadFilterNode, IGainNode, IPannerNode } from 'standardized-audio-context';
import { AudioContext, IAudioBuffer, IAudioBufferSourceNode, IAudioListener, IBiquadFilterNode, IGainNode, IMediaStreamAudioSourceNode, IPannerNode } from 'standardized-audio-context';
import { CacheManager } from './cache';

@@ -10,2 +10,3 @@

type PannerNode = IPannerNode<AudioContext>;
type MediaStreamAudioSourceNode = IMediaStreamAudioSourceNode<AudioContext>;

@@ -20,3 +21,4 @@ export type Position = [number, number, number];

// the stuff you should be able to do with anything that makes sound including groups, sounds, and playbacks.
play(): Playback[];
play(): BaseSound[];
seek?(time: number): void;
stop(): void;

@@ -29,3 +31,3 @@ pause(): void;

position: Position;
loop(loopCount?: LoopCount): LoopCount;
loop?(loopCount?: LoopCount): LoopCount;
}

@@ -116,2 +118,16 @@

getMicrophoneStream(): Promise<MicrophoneStream> {
return new Promise((resolve, reject) => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const microphoneStream = new MicrophoneStream(this.context);
microphoneStream.play();
resolve(microphoneStream);
})
.catch(err => {
reject(err);
});
});
}
}

@@ -145,6 +161,10 @@

playbacks: Playback[] = [];
globalGainNode: GainNode;
private globalGainNode: GainNode;
private _position: Position = [0, 0, 0];
loopCount: LoopCount = 0;
seek(time: number): void {
this.playbacks.forEach(playback => playback.seek(time));
}
constructor(buffer: AudioBuffer, context: AudioContext, globalGainNode: IGainNode<AudioContext>) {

@@ -173,3 +193,3 @@ super();

const playback = this.preplay();
playback.forEach(p => p.source!.start());
playback.forEach(p => p.play());
return playback;

@@ -208,3 +228,3 @@ }

this.loopCount = loopCount;
this.playbacks.forEach(p => p.source!.loop = true);
this.playbacks.forEach(p => p.sourceLoop = true);
return this.loopCount;

@@ -234,10 +254,24 @@ }

class Playback extends FilterManager implements BaseSound {
context: AudioContext;
source?: AudioBufferSourceNode;
gainNode?: GainNode;
panner?: PannerNode;
private context: AudioContext;
private source?: AudioBufferSourceNode;
private gainNode?: GainNode;
private panner?: PannerNode;
loopCount: LoopCount = 0;
currentLoop: number = 0;
buffer: IAudioBuffer | null = null;
private buffer: IAudioBuffer | null = null;
seek(time: number): void {
if (!this.source || !this.buffer || !this.gainNode || !this.panner) {
throw new Error('Cannot seek a sound that has been cleaned up');
}
// Stop the current playback
this.source.stop();
// Create a new source to start from the desired time
this.source = this.context.createBufferSource();
this.source.buffer = this.buffer;
this.refreshFilters();
this.source.connect(this.panner).connect(this.gainNode);
this.source.start(0, time);
}
constructor(source: AudioBufferSourceNode, gainNode: GainNode, context: AudioContext, loopCount: LoopCount = 0) {

@@ -288,2 +322,9 @@ super();

set sourceLoop(loop: boolean) {
if (!this.source) {
throw new Error('Cannot set loop on a sound that has been cleaned up');
}
this.source.loop = loop;
}
fadeIn(time: number, fadeType: FadeType = 'linear'): Promise<void> {

@@ -441,2 +482,6 @@ return new Promise(resolve => {

seek(time: number): void {
this.sounds.forEach(sound => sound.seek(time));
}
addSound(sound: Sound): void {

@@ -511,1 +556,192 @@ this.sounds.push(sound);

export class StreamPlayback extends FilterManager implements BaseSound {
private context: AudioContext;
private source?: MediaStreamAudioSourceNode;
private gainNode?: GainNode;
private panner?: PannerNode;
loopCount: LoopCount = 0;
currentLoop: number = 0;
constructor(source: MediaStreamAudioSourceNode, gainNode: GainNode, context: AudioContext, loopCount: LoopCount = 0) {
super();
this.loopCount = loopCount;
this.source = source;
this.gainNode = gainNode;
this.context = context;
this.panner = context.createPanner();
source.connect(this.panner).connect(this.gainNode);
this.refreshFilters();
}
play() {
if (!this.source) {
throw new Error('Cannot play a sound that has been cleaned up');
}
return [this];
}
get volume(): number {
if (!this.gainNode) {
throw new Error('Cannot get volume of a sound that has been cleaned up');
}
return this.gainNode.gain.value;
}
set volume(v: number) {
if (!this.gainNode) {
throw new Error('Cannot set volume of a sound that has been cleaned up');
}
this.gainNode.gain.value = v;
}
stop(): void {
if (!this.source) {
throw new Error('Cannot stop a sound that has been cleaned up');
}
this.source.mediaStream.getTracks().forEach(track => track.stop());
}
pause(): void {
if (!this.source) {
throw new Error('Cannot pause a sound that has been cleaned up');
}
this.source.mediaStream.getTracks().forEach(track => track.enabled = false);
}
resume(): void {
if (!this.source) {
throw new Error('Cannot resume a sound that has been cleaned up');
}
this.source.mediaStream.getTracks().forEach(track => track.enabled = true);
}
addFilter(filter: BiquadFilterNode): void {
super.addFilter(filter);
this.refreshFilters();
}
removeFilter(filter: BiquadFilterNode): void {
super.removeFilter(filter);
this.refreshFilters();
}
set position(position: Position) {
if (!this.panner) {
throw new Error('Cannot move a sound that has been cleaned up');
}
const [x, y, z] = position;
this.panner.positionX.value = x;
this.panner.positionY.value = y;
this.panner.positionZ.value = z;
}
get position(): Position {
if (!this.panner) {
throw new Error('Cannot get position of a sound that has been cleaned up');
}
return [this.panner.positionX.value, this.panner.positionY.value, this.panner.positionZ.value];
}
private refreshFilters(): void {
if (!this.source || !this.gainNode) {
throw new Error('Cannot update filters on a sound that has been cleaned up');
}
let connection = this.source;
this.source.disconnect();
connection = this.applyFilters(connection);
connection.connect(this.gainNode);
}
}
export class MicrophoneStream extends FilterManager implements BaseSound {
context: AudioContext;
private _position: Position = [0, 0, 0];
loopCount: LoopCount = 0;
private prevVolume: number = 1;
private microphoneGainNode: GainNode;
private streamPlayback?: StreamPlayback;
private stream: MediaStream | undefined;
private streamSource?: MediaStreamAudioSourceNode;
constructor(context: AudioContext) {
super();
this.context = context;
this.microphoneGainNode = this.context.createGain();
}
play(): StreamPlayback[] {
if (!this.stream) {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
this.stream = stream;
this.streamSource = this.context.createMediaStreamSource(this.stream);
this.streamPlayback = new StreamPlayback(this.streamSource, this.microphoneGainNode, this.context);
this.streamPlayback.play();
})
.catch(err => {
console.error('Error initializing microphone stream:', err);
});
}
return this.streamPlayback ? [this.streamPlayback] : [];
}
seek(time: number): void {
// Seeking is not applicable for live microphone stream
}
stop(): void {
if (this.streamPlayback) {
this.streamPlayback.stop();
this.streamPlayback = undefined;
}
}
pause(): void {
if (this.streamPlayback) {
this.streamPlayback.pause();
}
}
resume(): void {
if (this.streamPlayback) {
this.streamPlayback.resume();
}
}
addFilter(filter: BiquadFilterNode): void {
if (this.streamPlayback) {
this.streamPlayback.addFilter(filter);
}
}
removeFilter(filter: BiquadFilterNode): void {
if (this.streamPlayback) {
this.streamPlayback.removeFilter(filter);
}
}
get volume(): number {
return this.streamPlayback ? this.streamPlayback.volume : 0;
}
set volume(volume: number) {
if (this.streamPlayback) {
this.streamPlayback.volume = volume;
}
}
get position(): Position {
// Position is not applicable for live microphone stream
return [0, 0, 0];
}
set position(position: Position) {
// Position is not applicable for live microphone stream
}
loop(loopCount?: LoopCount): LoopCount {
// Looping is not applicable for live microphone stream
return 0;
}
}
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