Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

This project is used to audibly debug [tuna.js](https://github.com/Theodeus/tuna). All effects are bypassed by default - just untick the bypass checkbox to enable each effect.

Run ```npm install express``` to use the included local server. Run ```node server.js``` to start it and then navigate to http://localhost:8000 to run the app.

The server is setup to have the [tuna](https://github.com/Theodeus/tuna) folder and the tunatest folder on the same level (as in Tuna is referenced as such: ```<script src="../tuna/tuna.js"></script>```).
Run ```npm install express``` to use the included local server. Run ```npm start``` to start it and then navigate to http://localhost:8000 to run the app.

Silly main_loop.wav by yours truly.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added audio/impulse_guitar.wav
Binary file not shown.
Binary file added audio/ir_rev_short.wav
Binary file not shown.
File renamed without changes.
File renamed without changes.
Binary file added favicon.ico
Binary file not shown.
214 changes: 22 additions & 192 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="shortcut icon" href="./favicon.ico">
<title>Tuna testbed</title>
<style>
div {
button {
width: 100px;
height: 30px;
}
div, ul {
margin: 40px;
padding: 10px;
border: 1px solid black;
Expand All @@ -17,205 +22,30 @@
span {
float: right;
}
ul {
list-style: none;
}
</style>
</head>
<body>

<button onclick="output.gain.value = output.gain.value === 1 ? 0 : 1">Mute</button>
<button onclick="start()">Start</button>

<script src="../tuna/tuna.js"></script>
<script>
let context, tuna, output, tunaNode;
let nodeTypes = [];
let nodes = [];

function start() {
context = new AudioContext();
tuna = new Tuna(context);
for (let nodeType in tuna) {
if (tuna[nodeType].prototype.defaults) {
nodeTypes.push(nodeType);
}
}

fetch("main_loop.wav")
// fetch("aah.wav")
// fetch("crazy.wav")
//fetch("sine.wav")
.then(res => res.arrayBuffer())
.then(buffer => context.decodeAudioData(buffer))
.then(buffer => {
let bufferSource = context.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.loop = true;

let input = context.createGain();
output = context.createGain();
let previousNode = input;
nodeTypes.forEach(type => {
if (type === "Cabinet") {
tunaNode = new tuna[type]({ bypass: true, impulsePath: "../tuna/impulses/impulse_guitar.wav" });
}
else if (type === "Convolver") {
tunaNode = new tuna[type]({ bypass: true, impulse: "../tuna/impulses/ir_rev_short.wav" });
}
else if (type !== "LFO" && type !== "EnvelopeFollower") {
tunaNode = new tuna[type]({ bypass: true });
} else {
return;
}
previousNode.connect(tunaNode);
previousNode = tunaNode;
nodes.push(tunaNode);

//Create UI controllers for each parameter
let div = document.createElement("div");
let elem, control;
let effectLabel = document.createElement("label");
effectLabel.textContent = type;
div.appendChild(effectLabel);
for (let val in tunaNode.defaults) {
control = document.createElement("div");
if (tunaNode.defaults[val].type === "float") {
elem = createFloatSlider(tunaNode, val, tunaNode.defaults[val].min || 0, tunaNode.defaults[val].max || tunaNode.defaults[val].value, tunaNode.defaults[val].value);
} else if (tunaNode.defaults[val].type === "int") {
elem = createIntSlider(tunaNode, val, tunaNode.defaults[val].min || 0, tunaNode.defaults[val].max || tunaNode.defaults[val].value, tunaNode.defaults[val].value);
} else if (tunaNode.defaults[val].type === "boolean") {
elem = createCheckbox(tunaNode, val, tunaNode[val]);
} else if (tunaNode.defaults[val].type === "string") {
elem = createStringInput(tunaNode, val, tunaNode.defaults[val].value);
} else {
console.error("Unsupported param type", tunaNode, val, tunaNode.defaults[val]);
continue;
}
elem.forEach(el => control.appendChild(el));
div.appendChild(control);
}
document.body.appendChild(div);
});

bufferSource.connect(input);
previousNode.connect(output);
output.connect(context.destination);
bufferSource.start(0);
})

}

/*

UI GENERATION

*/
<h2>Audio</h2>
<ul>
<li><label><input type="radio" name="audio" value="main_loop.wav" checked>main_loop.wav</label></li>
<li><label><input type="radio" name="audio" value="aah.wav">aah.wav</label></li>
<li><label><input type="radio" name="audio" value="crazy.wav">crazy.wav</label></li>
<li><label><input type="radio" name="audio" value="deeerp.wav">deeerp.wav</label></li>
<li><label><input type="radio" name="audio" value="sine.wav">sine.wav</label></li>
</ul>

function createFloatSlider(node, name, min, max, val) {
let sliderLabel = document.createElement("label");
sliderLabel.textContent = name;
let slider = document.createElement("input");
slider.type = "range";
slider.min = min;
slider.max = max;
slider.value = val;
slider.step = (max - min) / 100;
let valueLabel = document.createElement("span");
valueLabel.innerText = val;
slider.oninput = _ => {
node[name] = parseFloat(slider.value);
valueLabel.innerText = slider.value;
};
<h2>Effects</h2>
You can apply effects by unchecking bypass.

if (node.defaults[name].automatable) {
let button = document.createElement("button");
let automating = false;
let update = _ => {
if (automating) {
requestAnimationFrame(update);
}
valueLabel.innerText = node[name].value
};
button.innerText = "Automate";
button.onclick = _ => {
automating = true;
if (node[name].value !== min) {
node.automate(name, min, 1000, 0);
} else {
node.automate(name, max, 1000, 0);
}
update();
setTimeout(_ => automating = false, 1100);
}
return [sliderLabel, valueLabel, slider, button];
} else {
console.log("not automatable", typeof node[name], node[name] instanceof AudioParam ? name : "", node[name] instanceof AudioParam ? node : "")
return [sliderLabel, valueLabel, slider];
}
<div id="effects"></div>

}

function createIntSlider(node, name, min, max, val) {
let sliderLabel = document.createElement("label");
sliderLabel.textContent = name;
let slider = document.createElement("input");
slider.type = "range";
slider.min = min;
slider.max = max;
slider.value = val;
slider.step = 1;
let valueLabel = document.createElement("span");
valueLabel.innerText = val;
slider.oninput = _ => {
node[name] = parseInt(slider.value)
valueLabel.innerText = slider.value;
};

if (node.defaults[name].automatable) {
let button = document.createElement("button");
let automating = false;
let update = _ => {
if (automating) {
requestAnimationFrame(update);
}
valueLabel.innerText = node[name].value
};
button.innerText = "Automate";
button.onclick = _ => {
automating = true;
if (node[name].value !== min) {
node.automate(name, min, 1000, 0);
} else {
node.automate(name, max, 1000, 0);
}
update();
setTimeout(_ => automating = false, 1100);
}
return [sliderLabel, valueLabel, slider, button];
} else {
console.log("not automatable", typeof node[name], node[name] instanceof AudioParam ? name : "", node[name] instanceof AudioParam ? node : "")
return [sliderLabel, valueLabel, slider];
}
}

function createCheckbox(node, name, val) {
let boxLabel = document.createElement("label");
boxLabel.textContent = name;
let box = document.createElement("input");
box.type = "checkbox";
box.checked = val;
box.onchange = _ => node[name] = box.checked;

return [boxLabel, box];
}

function createStringInput(node, name, val) {
let stringInputLabel = document.createElement("label");
stringInputLabel.textContent = name;
let stringInput = document.createElement("input");
stringInput.value = val;
stringInput.onchange = _ => node[name] = stringInput.value;

return [stringInputLabel, stringInput];
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tunajs/1.0.10/tuna-min.js"></script>
<script src="main.js"></script>
</body>
</html>
Loading