added preprocessor, changed more db stuff

This commit is contained in:
Djkato 2023-07-19 18:16:50 +02:00
parent 1878d8db85
commit 920ee5152a
14 changed files with 3144 additions and 1415 deletions

4157
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -58,9 +58,9 @@ function ref_from_json(ref: any): Ref {
interface SongConstructor { interface SongConstructor {
name: string name: string
artists: Ref[] artists?: Ref[]
url: URL url: URL
duration: number duration?: number
publish_date?: Date publish_date?: Date
remix_artists?: Ref[] remix_artists?: Ref[]
in_collection?: Ref in_collection?: Ref
@ -69,13 +69,13 @@ interface SongConstructor {
key?: string key?: string
fft_data?: number[] fft_data?: number[]
id?: ID, id?: ID,
metadata: any metadata?: Map<string, any>
} }
class Song { class Song {
name: string name: string
artists: Ref[] artists: Ref[]
url: URL url: URL
duration: number duration?: number
remix_artists: Ref[] remix_artists: Ref[]
publish_date?: Date publish_date?: Date
in_collection?: Ref in_collection?: Ref
@ -83,7 +83,7 @@ class Song {
bpm?: number bpm?: number
key?: string key?: string
fft_data?: number[] fft_data?: number[]
metadata: any metadata: Map<string, any>
/** /**
* The ID is always there, don't worry :) * The ID is always there, don't worry :)
*/ */
@ -101,7 +101,7 @@ class Song {
this.key = data.key this.key = data.key
this.fft_data = data.fft_data this.fft_data = data.fft_data
this.id = data.id this.id = data.id
this.metadata = data.metadata this.metadata = data.metadata || new Map<string, any>
} }
} }
@ -112,15 +112,15 @@ interface ArtistConstructor {
collections?: Ref[] collections?: Ref[]
links?: [Platforms, URL][] links?: [Platforms, URL][]
id?: ID id?: ID
metadata: any metadata?: Map<string, any>
} }
class Artist { class Artist {
name = "" name = ""
pfp?: URL | string pfp?: URL
songs: Ref[] songs: Ref[]
collections: Ref[] collections: Ref[]
links?: [Platforms, URL][] links?: [Platforms, URL][]
metadata: any metadata: Map<string, any>
/** /**
* The ID is always there, don't worry :) * The ID is always there, don't worry :)
*/ */
@ -132,17 +132,17 @@ class Artist {
this.collections = data.collections || [] this.collections = data.collections || []
this.links = data.links this.links = data.links
this.id = data.id this.id = data.id
this.metadata = data.metadata this.metadata = data.metadata || new Map<string, any>
} }
} }
interface CollectionConstructor { interface CollectionConstructor {
artists: Ref[] artists: Ref[]
songs: Ref[] songs: Ref[]
cover: URL cover?: URL
duration: number duration?: number
publish_date?: Date publish_date?: Date
id?: ID id?: ID
metadata: any metadata?: Map<string, any>
name?: string name?: string
type?: CollectionType type?: CollectionType
@ -152,10 +152,10 @@ class Collection {
type?: CollectionType type?: CollectionType
artists: Ref[] artists: Ref[]
songs: Ref[] songs: Ref[]
cover: URL cover?: URL
duration: number duration?: number
publish_date?: Date publish_date?: Date
metadata: any metadata: Map<string, any>
/** /**
* The ID is always there, don't worry :) * The ID is always there, don't worry :)
*/ */
@ -168,7 +168,7 @@ class Collection {
this.publish_date = data.publish_date this.publish_date = data.publish_date
this.id = data.id this.id = data.id
this.name = data.name this.name = data.name
this.metadata = data.metadata this.metadata = data.metadata ? data.metadata : new Map<string, any>
} }
} }
class DB { class DB {

29
packages/preprocessor/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# public samples
public/samples/*
src/song_list.ts
src/db.js

View file

@ -0,0 +1,4 @@
import filehound from "filehound"
import fs from "fs"
const songs = filehound.create().path("./public/samples").ext(["ogg", "mp3"]).findSync()
fs.writeFile('./src/songs_list.ts', `export const songs = ` + JSON.stringify(songs), 'utf8', () => { 1 + 1 })

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FFT generator</title>
</head>
<body>
<audio id="audio"></audio>
<div id="app"></div>
<script type="module" src="/src/main.ts">
</script>
<button id="button">Analyze!</button>
<svg id="waveform-canvas" viewBox="0 0 500 500" preserveAspectRatio="none"></svg>
<style>
body {
width: 100%;
height: 100vh;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
button {
padding: 1.2rem 2rem;
}
svg {
margin-top: 5rem;
width: 500px;
height: 500px;
border: 1px solid black;
}
</style>
</body>
</html>

View file

@ -0,0 +1,21 @@
{
"name": "preprocessor",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"crawl": "node crawler.mjs",
"process": "node crawler.mjs && vite",
"serve": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"typescript": "^5.0.2",
"vite": "^4.4.0"
},
"dependencies": {
"filehound": "^1.17.6",
"@euterpe.js/music-library": "*"
}
}

View file

@ -0,0 +1,70 @@
{
"name": "preprocessor",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "packages/preprocessor/src",
"tags": [],
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": [
"{options.outputPath}"
],
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/packages/preprocessor"
},
"configurations": {
"development": {
"mode": "development"
},
"production": {
"mode": "production"
}
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "preprocessor:build"
},
"configurations": {
"development": {
"buildTarget": "preprocessor:build:development",
"hmr": true
},
"production": {
"buildTarget": "preprocessor:build:production",
"hmr": false
}
}
},
"preview": {
"executor": "@nx/vite:preview-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "preprocessor:build"
},
"configurations": {
"development": {
"buildTarget": "preprocessor:build:development"
},
"production": {
"buildTarget": "preprocessor:build:production"
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": [
"{options.outputFile}"
],
"options": {
"lintFilePatterns": [
"packages/preprocessor/**/*.ts"
]
}
}
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,54 @@
import { Collection, Ref, RefTo, Song, DB, } from "@euterpe.js/music-library"
import { songs } from "./songs_list"
export function generate_db() {
console.log(songs)
//construct db
let db = new DB
let collections: string[] = new Array()
let new_songs = []
//create collections by folder names
for (let i = 0; i < songs.length; i++) {
const song = songs[i]
const last_i = song.lastIndexOf("\\")
const collection_name = song.slice(song.slice(0, last_i).lastIndexOf("\\") + 1, last_i)
/*
const foreforelast_i = song.slice(0, forelast_i - 1)
const foreforeforelast_i = song.slice(0, foreforelast_i - 1).lastIndexOf("\\")
*/
if (!collections.includes(collection_name)) {
console.log(`creating collection ${collection_name}`)
db.add([new Collection({
name: collection_name,
songs: [],
artists: [],
})])
collections.push(collection_name)
}
let col = db.collections.find(col => col.name == collection_name)!
let col_id = col.id
new_songs.push({ song: song, collection_id: col_id })
}
//create songs
for (let i = 0; i < new_songs.length; i++) {
let song = new_songs[i]
const last_i = song.song.lastIndexOf("\\")
const name = song.song.slice(last_i + 1)
const song_url = song.song.replace("\\\\", "/").slice(7)
const db_song = new Song({
name: name.slice(0, name.lastIndexOf(".")),
artists: [],
url: new URL("http://localhost:4200/" + song_url),
duration: 0,
remix_artists: [],
in_collection: new Ref(RefTo.Collections, song.collection_id!)
})
db.add([db_song])
}
console.log(db)
return db
}

View file

@ -0,0 +1,83 @@
import { DB } from "@euterpe.js/music-library"
import { generate_db } from "./generate_db"
import { AudioVisualBuilder, SmoothingAlgorythm, ShapeType, WaveformOrientation, WaveformShape } from "@euterpe.js/visualizer"
document.getElementById("button")!.addEventListener("click", (ev) => {
start()
})
export async function start() {
generate_db()
analyze().then(async (result) => {
console.log("Creating svgs...")
const waveform_canvas = document.querySelector("#waveform-canvas") as SVGSVGElement
for (const song of result.db.songs) {
const waveform_visual_builder = new AudioVisualBuilder(result.analyzer_node, waveform_canvas)
.set_fft_data_tresholds({ point_count_i: 100, fft_multiplier_i: 1, fft_offset_i: -80 })
.set_fft_time_smoothing(0.8)
.set_smoothing_algorythm(SmoothingAlgorythm.CatmullRom)
const waveform_visual = waveform_visual_builder.build(ShapeType.Waveform, true, { fft_data: new Float32Array(new Float64Array(song.fft_data!)), orientation: WaveformOrientation.Horizontal, shape_type: WaveformShape.LineLike })
waveform_visual.draw_once()
await new Promise<void>((done) => setTimeout(() => done(), 300))
song.metadata.set("waveform-svg", waveform_canvas.innerHTML)
}
console.log(result.db)
})
}
async function analyze(): Promise<AnalyzeReturn> {
console.clear()
const audioEl = document.querySelector("#audio") as HTMLAudioElement
console.log("analysing...")
const samplingRate = 100
//Create all audio nodes
const audioContext = new AudioContext()
const track = audioContext.createMediaElementSource(audioEl)
const gain = audioContext.createGain()
gain.gain.value = 0
const audioContextAnalyser = audioContext.createAnalyser()
audioContextAnalyser.fftSize = 64
audioContextAnalyser.smoothingTimeConstant = 0
const analyserBufferLength = audioContextAnalyser.frequencyBinCount
const FFTDataArray = new Float32Array(analyserBufferLength)
//Connect all audio Nodes
track.connect(audioContextAnalyser).connect(gain).connect(audioContext.destination)
let db = generate_db()
for (const song of db.songs) {
// const song = db.songs[db.songs.length - 1]
console.log(`Analyzing ${song.name}, ${db.songs.indexOf(song) + 1}/${db.songs.length}`)
//if not loaded yet keep trying
audioEl.src = song.url.href
await awaitLoad(audioEl)
song.duration = audioEl.duration
let currentFFTData = []
for (let curSecond = 0; curSecond < song.duration; curSecond += song.duration / samplingRate) {
console.log("working...")
audioEl.currentTime = curSecond
await audioEl.play()
await new Promise<void>((done) => setTimeout(() => done(), 100))
audioContextAnalyser.getFloatFrequencyData(FFTDataArray)
let volume = 0
FFTDataArray.forEach((element) => {
volume += element
})
currentFFTData.push(Math.round((volume / FFTDataArray.length) * 100) / 100)
}
song.fft_data = currentFFTData
}
console.log("Analyzation finished!")
const result: AnalyzeReturn = { analyzer_node: audioContextAnalyser, db: db }
return result
}
type AnalyzeReturn = {
analyzer_node: AnalyserNode,
db: DB
}
function awaitLoad(audioEl: HTMLAudioElement) {
return new Promise<void>((resolve, reject) => {
audioEl.addEventListener("loadeddata", function () {
if (audioEl.readyState >= 4) {
resolve()
}
})
})
}

View file

@ -0,0 +1 @@
export const songs = ["public\\samples\\bass\\H2 arp basses 01.ogg","public\\samples\\bass\\H2 arp basses 02.ogg","public\\samples\\bass\\H2 arp basses 03.ogg","public\\samples\\drums\\H2 909 Crash 03.ogg","public\\samples\\drums\\H2 buildsnares cut 01.ogg","public\\samples\\drums\\H2 buildsnares cut 04.ogg","public\\samples\\drums\\H2 buildsnares cut 06.ogg","public\\samples\\drums\\H2 Claps 07.ogg","public\\samples\\drums\\H2 Claps 21.ogg","public\\samples\\drums\\H2 Claps Tails 11.ogg","public\\samples\\drums\\H2 Claps Tails 15.ogg","public\\samples\\drums\\H2 clubsnares 08.ogg","public\\samples\\drums\\H2 clubsnares 15.ogg","public\\samples\\drums\\H2 clubsnares 20.ogg","public\\samples\\drums\\H2 Trancekick 13.ogg","public\\samples\\drums\\H2 Trancekick 24.ogg","public\\samples\\drums\\H2 Trancekick 45.ogg","public\\samples\\loops\\H2 loops 01.ogg","public\\samples\\loops\\H2 loops 02.ogg"]

View file

@ -0,0 +1 @@
/// <reference types="vite/client" />

View file

@ -0,0 +1,29 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": [
"ESNext",
"DOM"
],
"moduleResolution": "Node",
"strict": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true,
"types": [
"vite/client"
]
},
"include": [
"src"
]
}

View file

@ -0,0 +1,33 @@
/// <reference types="vitest" />
import { defineConfig } from "vite"
import viteTsConfigPaths from "vite-tsconfig-paths"
export default defineConfig({
cacheDir: "../../node_modules/.vite/preprocessor",
server: {
port: 4200,
host: "localhost"
},
preview: {
port: 4300,
host: "localhost"
},
plugins: [
viteTsConfigPaths({
root: "."
})
]
// Uncomment this if you are using workers.
// worker: {
// plugins: [
// viteTsConfigPaths({
// root: '../../',
// }),
// ],
// },
})