Spaces:
Running
Running
function _array_like_to_array(arr, len) { | |
if (len == null || len > arr.length) len = arr.length; | |
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; | |
return arr2; | |
} | |
function _array_with_holes(arr) { | |
if (Array.isArray(arr)) return arr; | |
} | |
function _iterable_to_array_limit(arr, i) { | |
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; | |
if (_i == null) return; | |
var _arr = []; | |
var _n = true; | |
var _d = false; | |
var _s, _e; | |
try { | |
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){ | |
_arr.push(_s.value); | |
if (i && _arr.length === i) break; | |
} | |
} catch (err) { | |
_d = true; | |
_e = err; | |
} finally{ | |
try { | |
if (!_n && _i["return"] != null) _i["return"](); | |
} finally{ | |
if (_d) throw _e; | |
} | |
} | |
return _arr; | |
} | |
function _non_iterable_rest() { | |
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | |
} | |
function _sliced_to_array(arr, i) { | |
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest(); | |
} | |
function _unsupported_iterable_to_array(o, minLen) { | |
if (!o) return; | |
if (typeof o === "string") return _array_like_to_array(o, minLen); | |
var n = Object.prototype.toString.call(o).slice(8, -1); | |
if (n === "Object" && o.constructor) n = o.constructor.name; | |
if (n === "Map" || n === "Set") return Array.from(n); | |
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen); | |
} | |
import * as Tone from 'https://esm.sh/tone'; | |
// --- Module State --- | |
var players = null; | |
var isLoaded = false; | |
var sequence = null; | |
var beatIndex = 0; | |
var activeDrums = new Set(); | |
// More varied and syncopated drum patterns | |
var drumPattern = { | |
// Syncopated kick pattern | |
//'kick': [true, false, false, false, true, false, false, false, true, false, false, false, true, false, false, false], | |
'kick': [ | |
true, | |
false, | |
false, | |
false, | |
false, | |
true, | |
false, | |
false, | |
true, | |
false, | |
false, | |
true, | |
false, | |
true, | |
false, | |
false | |
], | |
// Snare on the backbeat (beats 2 and 4) | |
'snare': [ | |
false, | |
false, | |
false, | |
false, | |
true, | |
false, | |
false, | |
false, | |
false, | |
false, | |
false, | |
false, | |
true, | |
false, | |
false, | |
false | |
], | |
// Open hi-hat feel on the off-beats | |
'hihat': [ | |
false, | |
true, | |
false, | |
true, | |
false, | |
true, | |
false, | |
true, | |
false, | |
true, | |
false, | |
true, | |
false, | |
true, | |
false, | |
true | |
], | |
// Clap layered with snare, but with an extra syncopated hit | |
'clap': [ | |
false, | |
false, | |
false, | |
false, | |
true, | |
false, | |
false, | |
true, | |
false, | |
false, | |
false, | |
false, | |
true, | |
false, | |
false, | |
false | |
] | |
}; | |
var fingerToDrumMap = { | |
'index': 'kick', | |
'middle': 'snare', | |
'ring': 'hihat', | |
'pinky': 'clap' | |
}; | |
// --- Exported Functions --- | |
/** | |
* Loads all drum samples and returns a promise that resolves when loading is complete | |
*/ export function loadSamples() { | |
return new Promise(function(resolve, reject) { | |
if (isLoaded) { | |
resolve(); | |
return; | |
} | |
players = new Tone.Players({ | |
urls: { | |
kick: 'assets/kick.wav', | |
snare: 'assets/snare.wav', | |
hihat: 'assets/hihat.wav', | |
clap: 'assets/clap.wav' | |
}, | |
onload: function() { | |
isLoaded = true; | |
// Set volumes after loading | |
players.player('kick').volume.value = -6; // Lowered kick volume | |
players.player('snare').volume.value = 0; | |
players.player('hihat').volume.value = -2; // Softer hi-hat | |
players.player('clap').volume.value = 0; | |
console.log("Drum samples loaded successfully."); | |
resolve(); | |
}, | |
onerror: function(error) { | |
console.error("Error loading drum samples:", error); | |
reject(error); | |
} | |
}).toDestination(); | |
}); | |
} | |
/** | |
* Creates and starts the main 16-step drum loop. | |
* Assumes Tone.Transport has been started elsewhere. | |
*/ export function startSequence() { | |
if (!isLoaded || sequence) { | |
console.warn("Drums not loaded or sequence already started. Cannot start sequence."); | |
return; | |
} | |
sequence = new Tone.Sequence(function(time, step) { | |
beatIndex = step; // Update for visualization | |
Object.entries(drumPattern).forEach(function(param) { | |
var _param = _sliced_to_array(param, 2), drum = _param[0], pattern = _param[1]; | |
// If the drum is active AND its pattern has a note on this step... | |
if (activeDrums.has(drum) && pattern[step]) { | |
players.player(drum).start(time); | |
} | |
}); | |
}, Array.from({ | |
length: 16 | |
}, function(_, i) { | |
return i; | |
}), "16n").start(0); | |
console.log("Drum sequence started."); | |
} | |
/** | |
* Updates which drums are active based on finger positions. | |
* @param {object} fingerStates - An object with finger names as keys and boolean `isUp` as values. | |
*/ export function updateActiveDrums(fingerStates) { | |
activeDrums.clear(); | |
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined; | |
try { | |
for(var _iterator = Object.entries(fingerStates)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){ | |
var _step_value = _sliced_to_array(_step.value, 2), finger = _step_value[0], isUp = _step_value[1]; | |
if (isUp) { | |
var drum = fingerToDrumMap[finger]; | |
if (drum) { | |
activeDrums.add(drum); | |
} | |
} | |
} | |
} catch (err) { | |
_didIteratorError = true; | |
_iteratorError = err; | |
} finally{ | |
try { | |
if (!_iteratorNormalCompletion && _iterator.return != null) { | |
_iterator.return(); | |
} | |
} finally{ | |
if (_didIteratorError) { | |
throw _iteratorError; | |
} | |
} | |
} | |
} | |
/** | |
* Returns the set of currently active drums. | |
* @returns {Set<string>} A set of active drum names. | |
*/ export function getActiveDrums() { | |
return activeDrums; | |
} | |
/** | |
* Returns the mapping of fingers to drums. | |
* @returns {object} The finger-to-drum map. | |
*/ export function getFingerToDrumMap() { | |
return fingerToDrumMap; | |
} | |
/** | |
* Returns the current beat index for external use (like visualization). | |
* @returns {number} The current beat index (0-15). | |
*/ export function getCurrentBeat() { | |
return beatIndex; | |
} | |
/** | |
* Returns the master drum pattern object. | |
* @returns {object} The drum pattern. | |
*/ export function getDrumPattern() { | |
return drumPattern; | |
} |