Derivatives sound like a highpass filter.

This commit is contained in:
MindfulMinun committed 2020-05-20 02:08:27 -05:00
1 parent af3250f695
commit 2d6a517e93
10 files changed
+4358

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2020 MindfulMinun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+72
View File
@@ -0,0 +1,72 @@
<!doctype html>
<!--
Made with love by MindfulMinun
https://benjic.xyz
-->
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<title>D♭</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#448aff">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
<style media="screen">
*, *::before, *::after { box-sizing: border-box; }
html, body {
font-family: "Roboto", Helvetica, Arial, sans-serif;
height: 100%;
margin: 0;
background-color: #1e1e1e;
color: white;
}
body { display: flex; }
a[href] { color: var(--accent); }
input, audio {
display: block;
margin: 1em 0;
}
.container {
width: 840px;
max-width: 100%;
margin: 0 auto;
padding: 1em;
}
</style>
</head>
<body>
<div class="container">
<p>
Drop a file anywhere or <button id="choose">choose one from your filesystem.</button>
</p>
<select id="" disabled>
<option value="derivative-processor">derivative-processor</option>
<option value="integral-processor">integral-processor</option>
<option value="subtract-overlap" selected>subtract-overlap</option>
</select>
<audio controls></audio>
<ul>
<li>
<code>derivative-processor</code>
<p>Approximates the first derivative of the audio as a function respect to time.</p>
</li>
<li>
<code>integral-processor</code>
<p>Approximates the first integral of the audio as a function respect to time. Plus C of zero.</p>
</li>
<li>
<code>subtract-overlap</code>
<p>Inverts the amplitude of every even-numbered channel and merges all channels to mono. This effectively cancels the sounds the left and right channels share in common. Quick-and-dirty way of removing vocals.</p>
</li>
</ul>
</div>
<input id="file" type="file" hidden>
<script src="./dist/main.js"></script>
</body>
</html>
+3999
View File
File diff suppressed because it is too large. Load diff
+29
View File
@@ -0,0 +1,29 @@
{
"name": "d-flat",
"version": "0.1.0",
"description": "What does the derivative of a song sound like?",
"main": "dist/main.js",
"author": "MindfulMinun <[email protected]> (https://benjic.xyz)",
"contributors": [],
"homepage": "https://github.com/MindfulMinun/dy-dx-d-flat",
"repository": "github:MindfulMinun/dy-dx-d-flat",
"license": "MIT",
"private": true,
"scripts": {
"build": "rm -rf dist && npx webpack --config webpack.config.js --mode=production",
"watch": "npm run serve & npx webpack --config webpack.config.js --mode=development --watch",
"serve": "npx http-server . -p 3000",
"start": "npm run serve",
"test": "echo \"Error: no test specified\" && exit 1"
},
"engines": {
"node": ">=8.0.0 <11.0.0"
},
"devDependencies": {
"http-server": "^0.12.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"worklet-loader": "^1.0.0"
},
"dependencies": {}
}
+5
View File
@@ -0,0 +1,5 @@
# D-flat
> *Have you ever wondered what the derivative of a song would sound like?*
Mess around with different audio effects, brought to you by the Web Audio API and viewers like you.
+73
View File
@@ -0,0 +1,73 @@
//@ts-ignore
import workletUrl from './processors.worklet.js'
document.getElementById('choose').addEventListener('click', ev => {
document.getElementById('file').click()
})
document.getElementById('file').addEventListener('input', ev => {
fileGuard(ev.target.files)
})
window.addEventListener('drop', async ev => {
ev.preventDefault()
fileGuard(ev.dataTransfer.files)
})
document.addEventListener('dragover', ev => {
ev.preventDefault()
})
function fileGuard(files) {
const candidates = [...files]
const file = candidates.find(file => file.type.startsWith('audio/'))
if (!file) {
alert("Be sure to drop an audio file your browser supports :)")
return
}
init(file)
}
/**
* @param {File} audioFile
*/
async function init(audioFile) {
const audioEl = document.querySelector('audio')
const audio = new AudioContext()
await audio.audioWorklet.addModule('/dist/' + workletUrl)
const dFlat = new AudioWorkletNode(audio, 'subtract-overlap', {
processorOptions: {
maxChannels: audio.destination.maxChannelCount
}
})
const audioSrcNode = audio.createMediaElementSource(audioEl)
audioSrcNode.connect(dFlat)
dFlat.connect(audio.destination)
audioEl.src = URL.createObjectURL(audioFile)
//@ts-ignore
if (navigator['mediaSession'] && typeof MediaMetadata !== 'undefined') {
//@ts-ignore
navigator['mediaSession'].metadata = new MediaMetadata({
title: audioFile.name
});
navigator['mediaSession'].setActionHandler('play', () => {
audioEl.play()
})
navigator['mediaSession'].setActionHandler('pause', () => {
audioEl.pause()
})
navigator['mediaSession'].setActionHandler('seekbackward', () => {
audioEl.currentTime -= 5
})
navigator['mediaSession'].setActionHandler('seekforward', () => {
audioEl.currentTime += 5
})
}
}
+114
View File
@@ -0,0 +1,114 @@
/// <reference path="./types.d.ts" />
const carry = Symbol()
class DerivativeProcessor extends AudioWorkletProcessor {
constructor(options) {
super()
this[carry] = new Float32Array(options.processorOptions.maxChannels)
}
/**
* @param {Float32Array[][]} inputs
* @param {Float32Array[][]} outputs
*/
process(inputs, outputs) {
const input = inputs[0]
const output = outputs[0]
for (let channel = 0; channel < input.length; channel++) {
output[channel][0] = this[carry][channel]
for (let sample = 1; sample < input[channel].length; sample++) {
// dy/dx: (s2 - s1) / time
output[channel][sample] = input[channel][sample] - input[channel][sample - 1]
}
this[carry][channel] = input[channel][input.length - 1]
}
return true;
}
}
registerProcessor('derivative-processor', DerivativeProcessor)
class IntegralProcessor extends AudioWorkletProcessor {
constructor(options) {
super()
this[carry] = new Float32Array(options.processorOptions.maxChannels)
}
/**
* @param {Float32Array[][]} inputs
* @param {Float32Array[][]} outputs
*/
process(inputs, outputs) {
const input = inputs[0]
const output = outputs[0]
for (let channel = 0; channel < input.length; channel++) {
output[channel][0] = (this[carry][channel] + input[channel][0]) / 2
for (let sample = 1; sample < input[channel].length; sample++) {
// Integral of two samples: (time * (s1 + s2)) / 2
output[channel][sample] = (input[channel][sample - 1] + input[channel][sample]) / 2
}
this[carry][channel] = input[channel][input.length - 1]
}
return true;
}
}
registerProcessor('integral-processor', IntegralProcessor)
// class BitCrusher extends AudioWorkletProcessor {
// /**
// * @param {Float32Array[][]} inputs
// * @param {Float32Array[][]} outputs
// */
// process(inputs, outputs) {
// const input = inputs[0]
// const output = outputs[0]
// for (let channel = 0; channel < input.length; channel++) {
// for (let sample = 0; sample < input[channel].length; sample++) {
// output[channel][sample] = input[channel][sample] ** 2
// }
// }
// return true;
// }
// }
// registerProcessor('bit-crusher', BitCrusher)
/**
* Inverts the amplitude of every even-numbered channel and merges them to mono.
* Quick-and-dirty way of removing vocals
*/
class SubtractOverlap extends AudioWorkletProcessor {
/**
* @param {Float32Array[][]} inputs
* @param {Float32Array[][]} outputs
*/
process(inputs, outputs) {
const input = inputs[0]
const output = outputs[0]
for (let channel = 0; channel < input.length; channel++) {
for (let sample = 0; sample < input[0].length; sample++) {
output[0][sample] += (channel % 2 === 0 ? 1 : -1) * input[channel][sample]
}
if (channel !== 0) {
output[channel].set(output[0])
}
}
return true;
}
}
registerProcessor('subtract-overlap', SubtractOverlap)
+16
View File
@@ -0,0 +1,16 @@
/// <reference lib="webworker" />
class AudioWorkletProcessor {
readonly port: MessagePort
process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Map<string, Float32Array>): void;
}
interface AudioWorkletProcessorConstructor {
prototype: AudioWorkletProcessor;
new(options?: AudioWorkletNodeOptions): AudioWorkletProcessor;
}
function registerProcessor(
name: string,
processorCtor: AudioWorkletProcessorConstructor
): void
+16
View File
@@ -0,0 +1,16 @@
{
// Change this to match your project
"include": ["src/game.js"],
"compilerOptions": {
// Tells TypeScript to read JS files, as normally they are ignored as source files
"allowJs": true,
// Generate d.ts files
"declaration": true,
// This compiler run should only output d.ts files
"emitDeclarationOnly": true,
// Types should go into this directory.
// Removing this would place the definition files next to the .js files
"outDir": "dist",
}
}
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
entry: [
"./src/main.js"
],
module: {
rules: [{
test: /\.worklet\.js$/,
use: {
loader: "worklet-loader",
options: { name: "[name].js" }
}
}]
}
}