Skip to content

Commit

Permalink
Note volume behavior fixed. Closes #19
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxLaumeister committed Dec 24, 2019
1 parent a2b0c14 commit 6b39d5b
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 30 deletions.
23 changes: 1 addition & 22 deletions src/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,7 @@ class Grid { // eslint-disable-line no-unused-vars
if (this.getTileValue(x, y)) return;
// Turning on, schedule note

const highVolume = -10; // When one note is playing
const lowVolume = -35; // When all notes are playing (lower volume to prevent peaking)

const volume = ((this.height - this.countNotesInColumn(x)) / this.height)
* (highVolume - lowVolume) + lowVolume;

this.data[Util.coordToIndex(x, y, this.width)] = this.player.scheduleNote(x, y, volume);
this.data[Util.coordToIndex(x, y, this.width)] = this.player.scheduleNote(x, y);
} else {
if (!this.getTileValue(x, y)) return;
// Turning off, unschedule note
Expand All @@ -84,21 +78,6 @@ class Grid { // eslint-disable-line no-unused-vars
this.data = Array(this.width * this.height).fill(false);
}

/**
* Counts up the number of armed tiles (notes) in a grid column.
* This tells you the degree of polyphony at time x.
* @param {number} x - The grid column for which to count up the number of notes
* @returns {number} - The number of notes in the grid column
*/
countNotesInColumn(x) {
Util.assert(arguments.length === 1);
let count = 0;
for (let i = 0; i < this.height; i += 1) {
if (this.getTileValue(x, i)) count += 1;
}
return count;
}

/**
* Saves the grid's current state into a savestate string
* @returns {string} savestate - The base64-encoded URL-encoded savestate string,
Expand Down
1 change: 1 addition & 0 deletions src/GridRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GridRenderer { // eslint-disable-line no-unused-vars

/**
* Draw the current state of the app to the canvas element.
* @private
* @param {Grid} grid - The grid to be rendered
* @param {number} mouseX - The x position of the mouse on the canvas
* @param {number} mouseY - The y position of the mouse on the canvas
Expand Down
35 changes: 27 additions & 8 deletions src/NotePlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
class NotePlayer { // eslint-disable-line no-unused-vars
/**
* Creates a NotePlayer. One player is sufficient for any number of instruments and notes.
* @param {*} gridWidth - The width of the grid, in tiles
* @param {*} gridHeight - The height of the grid, in tiles
* @param {number} gridWidth - The width of the grid, in tiles
* @param {number} gridHeight - The height of the grid, in tiles
*/
constructor(gridWidth, gridHeight) {
Util.assert(arguments.length === 2);
Expand All @@ -14,6 +14,7 @@ class NotePlayer { // eslint-disable-line no-unused-vars
this.gridHeight = gridHeight;

// Construct scale array

const pentatonic = ['B#', 'D', 'F', 'G', 'A'];
const octave = 3; // base octave
const octaveoffset = 4;
Expand Down Expand Up @@ -61,27 +62,41 @@ class NotePlayer { // eslint-disable-line no-unused-vars
this.players.push(new Tone.Player(buffer).toMaster());
}
});

// Init polyphony tracker. More notes playing at the same time
// means that each note needs to play quieter

this.polyphony = Array(gridWidth).fill(0);
this.notes = []; // Sparse array
}

/**
* Schedules a note at an (x, y) grid coordinate to automatically play at the appropriate time
* @param {*} gridX - The x position of the note, in grid tiles
* @param {*} gridY - The y position of the note, in grid tiles
* @param {*} volume - The volume to play the note at
* Schedules a note at an (x, y) grid coordinate
* to automatically play at the appropriate time and pitch
* @param {number} gridX - The x position of the note, in grid tiles
* @param {number} gridY - The y position of the note, in grid tiles
* @returns {noteId} - The id of the note that's been scheduled, for use with unscheduleNote()
*/
scheduleNote(gridX, gridY, volume) {
Util.assert(arguments.length === 3);
scheduleNote(gridX, gridY) {
Util.assert(arguments.length === 2);
// Cycle through the voices
try {
const noteDuration = Tone.Time('1m') / this.gridWidth;
const playEvent = Tone.Transport.schedule((time) => {
const highVolume = -10; // When one note is playing
const lowVolume = -20; // When all notes are playing (lower volume to prevent peaking)

const volume = ((this.gridHeight - this.polyphony[gridX]) / this.gridHeight)
* (highVolume - lowVolume) + lowVolume;

this.players[this.currentPlayer].volume.value = volume;
this.players[this.currentPlayer].start(
time, gridY * this.noteOffset, this.noteOffset,
);
this.currentPlayer = (this.currentPlayer + 1) % this.players.length;
}, gridX * noteDuration);
this.notes[playEvent] = { x: gridX, y: gridY };
this.polyphony[gridX] += 1;
return playEvent;
} catch (e) {
// eslint-disable-next-line no-console
Expand All @@ -96,6 +111,10 @@ class NotePlayer { // eslint-disable-line no-unused-vars
*/
unscheduleNote(id) { // eslint-disable-line class-methods-use-this
Util.assert(arguments.length === 1);
const { x } = this.notes[id];
delete this.notes[id];
this.polyphony[x] -= 1;
Util.assert(this.polyphony[x] >= 0);
Tone.Transport.clear(id);
}

Expand Down
3 changes: 3 additions & 0 deletions src/ToneMatrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ class ToneMatrix { // eslint-disable-line no-unused-vars
const dpr = devicePixelRatio || 1;
this.c.width = rect.width * dpr;
this.c.height = rect.height * dpr;

/**
* The main canvas element's 2d drawing context
* @type {CanvasRenderingContext2D}
*/
this.ctx = this.c.getContext('2d');

/**
* The width of the grid, measured in grid tiles
* @const {number}
*/
this.WIDTH = 16;

/**
* The height of the grid, measured in grid tiles
* @const {number}
Expand Down

0 comments on commit 6b39d5b

Please sign in to comment.