maybe another fix

This commit is contained in:
Djkáťo 2023-08-10 14:02:23 +02:00
parent ccbec7a26f
commit 4a425a30a8
12 changed files with 117 additions and 60 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "@euterpe.js/euterpe", "name": "@euterpe.js/euterpe",
"version": "2.0.0", "version": "2.0.1",
"type": "module", "type": "module",
"description": "Fully featured solution for playing music on the web. Support for local library, audio visuals and more!", "description": "Fully featured solution for playing music on the web. Support for local library, audio visuals and more!",
"main": "./src/index.js", "main": "./src/index.js",

View file

@ -1,6 +1,6 @@
{ {
"name": "@euterpe.js/player", "name": "@euterpe.js/player",
"version": "2.0.0", "version": "2.0.1",
"type": "module", "type": "module",
"description": "A simple, safe AudioContext web music player", "description": "A simple, safe AudioContext web music player",
"main": "./src/index.js", "main": "./src/index.js",

View file

@ -218,21 +218,28 @@ export class MusicPlayer {
* Will only load metadata of the upcoming song. Need to call try_play_async() afterwards to start the playback * Will only load metadata of the upcoming song. Need to call try_play_async() afterwards to start the playback
* @throws Error if adding element throwed Error or Stalled * @throws Error if adding element throwed Error or Stalled
*/ */
try_new_song(path: string) { async try_new_song(path: string) {
if (this.audio_context.state !== "running") {
try {
await this.audio_context.resume()
} catch (e) {
console.log("loading new song - couldn't resume context before hand", e)
}
}
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
this.audio_element.src = this.current_song_path = path this.audio_element.src = this.current_song_path = path
//Found out today about this. Such a nice new way to mass remove event listeners! //Found out today about this. Such a nice new way to mass remove event listeners!
const controller = new AbortController(); const controller = new AbortController();
this.audio_element.addEventListener("canplay", function canplay_listener(s) { this.audio_element.addEventListener("canplaythrough", function canplay_listener() {
controller.abort() controller.abort()
}, { signal: controller.signal }) }, { signal: controller.signal })
this.audio_element.addEventListener("error", function error_listener(e) { this.audio_element.addEventListener("error", function error_listener() {
controller.abort("new src error") controller.abort("new src error")
}, { signal: controller.signal }) }, { signal: controller.signal })
this.audio_element.addEventListener("stalled", function stalled_listener(e) { this.audio_element.addEventListener("stalled", function stalled_listener() {
controller.abort("new src stalled") controller.abort("new src stalled")
}, { signal: controller.signal }) }, { signal: controller.signal })

View file

@ -1,4 +0,0 @@
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

@ -13,9 +13,16 @@
<script type="module" src="/src/main.ts"> <script type="module" src="/src/main.ts">
</script> </script>
<div class="button-wrapper"> <div class="button-wrapper">
<button id="button">Analyze!</button> <button id="analyze">Analyze!</button>
<button id="create-svg">Create svgs!</button>
<div class="button-wrapper">
<div>
<input id="upload" type="file" />
<p>Upload DB</p>
</div>
<a id="download" href="">Download DB</a> <a id="download" href="">Download DB</a>
</div> </div>
</div>
<div class="canvas-wrapper"> <div class="canvas-wrapper">
<svg id="waveform-canvas" viewBox="0 0 500 500" preserveAspectRatio="none"></svg> <svg id="waveform-canvas" viewBox="0 0 500 500" preserveAspectRatio="none"></svg>
</div> </div>

View file

@ -4,8 +4,8 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"crawl": "node crawler.mjs", "crawl": "cd src && node crawler.mjs",
"process": "node crawler.mjs && vite", "process": "npm run crawl && vite",
"media-process": "cd src && node media_process.js", "media-process": "cd src && node media_process.js",
"serve": "vite", "serve": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",

View file

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

View file

@ -3,7 +3,7 @@ import { songs } from "./songs_list"
export function generate_db() { export function generate_db() {
console.log(songs) console.log(songs)
//construct db // construct db
let db = new DB let db = new DB
let collections: string[] = new Array() let collections: string[] = new Array()
let new_songs = [] let new_songs = []
@ -38,11 +38,11 @@ export function generate_db() {
const last_i = song.song.lastIndexOf("\\") const last_i = song.song.lastIndexOf("\\")
const name = song.song.slice(last_i + 1) const name = song.song.slice(last_i + 1)
const song_url = song.song.replace("\\\\", "/").slice(7) const song_url = song.song.slice(song.song.indexOf("public\\") + 7)
const db_song = new Song({ const db_song = new Song({
name: name.slice(0, name.lastIndexOf(".")), name: name.slice(0, name.lastIndexOf(".")),
artists: [], artists: [],
url: new URL("http://localhost:4201/" + song_url), url: new URL(`${window.location.href}${song_url}`.replaceAll("\\", "/")),
duration: 0, duration: 0,
remix_artists: [], remix_artists: [],
in_collection: new Ref(RefTo.Collections, song.collection_id) in_collection: new Ref(RefTo.Collections, song.collection_id)

View file

@ -1,25 +1,80 @@
import { DB } from "@euterpe.js/music-library" import { DB, from_json } from "@euterpe.js/music-library"
import { generate_db } from "./generate_db" import { generate_db } from "./generate_db"
import { AudioVisualBuilder, SmoothingAlgorythm, ShapeType, WaveformOrientation, WaveformShape } from "@euterpe.js/visualizer" import { AudioVisualBuilder, SmoothingAlgorythm, ShapeType, WaveformOrientation, WaveformShape } from "@euterpe.js/visualizer"
document.getElementById("button")!.addEventListener("click", (ev) => { let result: AnalyzeReturn | undefined;
start()
let db = generate_db()
//Create all audio nodes
const audioEl = document.querySelector("#audio") as HTMLAudioElement
const audioContext = new AudioContext()
const track = audioContext.createMediaElementSource(audioEl)
const gain = audioContext.createGain()
gain.gain.value = 0
const audioContextAnalyser = audioContext.createAnalyser()
audioContextAnalyser.fftSize = 32
audioContextAnalyser.smoothingTimeConstant = 0
const analyserBufferLength = audioContextAnalyser.frequencyBinCount
const FFTDataArray = new Float32Array(analyserBufferLength)
//Connect all audio Nodes
track.connect(audioContextAnalyser).connect(gain).connect(audioContext.destination)
document.getElementById("analyze")!.addEventListener("click", async (ev) => {
audioContext.resume()
result = await analyze()
download(JSON.stringify(result.db), "db.json", "text/plain")
}) })
export async function start() {
analyze().then(async (result) => { document.getElementById("create-svg")!.addEventListener("click", (ev) => {
audioContext.resume()
svg()
})
document.getElementById("upload")!.addEventListener("change", (ev) => {
audioContext.resume()
const fileReader = new FileReader()
fileReader.readAsText(ev.target.files[0])
fileReader.onload = event => {
let str = JSON.parse(event.target.result)
let new_db = from_json(str)
//-infinity get stringified to null, undo that
for (const song of new_db.songs) {
if (song.fft_data) {
for (let i = 0; i < song.fft_data.length; i++) {
if (song.fft_data[i] === null || song.fft_data[i] === undefined) song.fft_data[i] = -Infinity
}
}
}
result = { db: new_db, analyzer_node: audioContextAnalyser }
}
})
async function svg() {
if (!result) {
alert("not analyzed yet!")
return
}
console.log("Creating svgs...") console.log("Creating svgs...")
const waveform_canvas = document.querySelector("#waveform-canvas") as SVGSVGElement const canvas_wrapper = document.querySelector(".canvas-wrapper") as HTMLElement
const waveform_canvas = document.querySelector("#waveform-canvas")?.cloneNode() as SVGSVGElement
canvas_wrapper.childNodes.forEach((c) => c.remove())
canvas_wrapper.appendChild(waveform_canvas)
for (const song of result.db.songs) { for (const song of result.db.songs) {
console.log("creating waveform for -> " + song.name) console.log("creating waveform for -> " + song.name)
const curr_waveform_canvas = waveform_canvas.cloneNode() as SVGSVGElement const curr_waveform_canvas = waveform_canvas.cloneNode() as SVGSVGElement
waveform_canvas.parentElement?.append(curr_waveform_canvas) waveform_canvas.parentElement?.append(curr_waveform_canvas)
const waveform_visual_builder = new AudioVisualBuilder(result.analyzer_node, curr_waveform_canvas) const waveform_visual_builder = new AudioVisualBuilder(result.analyzer_node, curr_waveform_canvas)
.set_fft_data_tresholds({ point_count_i: 100, fft_multiplier_i: .6, fft_offset_i: -75 }) .set_fft_data_tresholds({ point_count_i: 100, fft_multiplier_i: .9, fft_offset_i: -65 })
.set_fft_time_smoothing(0.8) .set_fft_time_smoothing(0.8)
.set_smoothing_algorythm(SmoothingAlgorythm.CatmullRom) .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 }) 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() waveform_visual.draw_once()
await new Promise<void>((done) => setTimeout(() => done(), 300)) // await new Promise<void>((done) => setTimeout(() => done(), 500))
// @ts-ignore // @ts-ignore
song.metadata[0] = curr_waveform_canvas.children[0].getAttribute("d") song.metadata[0] = curr_waveform_canvas.children[0].getAttribute("d")
song.fft_data = [] song.fft_data = []
@ -27,27 +82,14 @@ export async function start() {
waveform_canvas.remove() waveform_canvas.remove()
console.dir(result.db, { depth: null }) console.dir(result.db, { depth: null })
download(JSON.stringify(result.db), "db.json", "text/plain") download(JSON.stringify(result.db), "db.json", "text/plain")
})
} }
async function analyze(): Promise<AnalyzeReturn> { async function analyze(): Promise<AnalyzeReturn> {
console.clear() console.clear()
const audioEl = document.querySelector("#audio") as HTMLAudioElement const audioEl = document.querySelector("#audio") as HTMLAudioElement
console.log("analysing...") console.log("analysing...")
const samplingRate = 100 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()
// db.songs.splice(0, 10) // db.songs.splice(0, 10)
// db.songs.splice(2) // db.songs.splice(2)
console.log(db) console.log(db)
@ -72,6 +114,7 @@ async function analyze(): Promise<AnalyzeReturn> {
currentFFTData.push(Math.round((volume / FFTDataArray.length) * 100) / 100) currentFFTData.push(Math.round((volume / FFTDataArray.length) * 100) / 100)
} }
song.fft_data = currentFFTData song.fft_data = currentFFTData
console.log(song.fft_data)
} }
console.log("Analyzation finished!") console.log("Analyzation finished!")
const result: AnalyzeReturn = { analyzer_node: audioContextAnalyser, db: db } const result: AnalyzeReturn = { analyzer_node: audioContextAnalyser, db: db }
@ -84,7 +127,6 @@ function download(content: BlobPart, fileName: string, contentType: string) {
a.download = fileName; a.download = fileName;
// a.click(); // a.click();
} }
type AnalyzeReturn = { type AnalyzeReturn = {
analyzer_node: AnalyserNode, analyzer_node: AnalyserNode,
db: DB db: DB

View file

@ -63,7 +63,7 @@ function generateNewVideoSizes(file, currentExtention, width_resolutions) {
}) })
} }
let dirs = filehound.create() let dirs = filehound.create()
.path("../public/") .path("../public/samples")
.directory() .directory()
.findSync() .findSync()
console.log(dirs) console.log(dirs)

View file

@ -1 +1 @@
export const songs = ["public\\samples\\bass\\arp bass noise 5.ogg","public\\samples\\bass\\arp bass noise 6.ogg","public\\samples\\bass\\lead bass 7.ogg","public\\samples\\bass\\sub bass 5.ogg","public\\samples\\demos\\nuphory - NVISION (EXTENDED MIX).ogg","public\\samples\\drums\\H2 buildsnares long 11.ogg","public\\samples\\drums\\H2 Claps 21.ogg","public\\samples\\drums\\H2 clubsnares 14.ogg","public\\samples\\drums\\H2 Trancekick 08.ogg","public\\samples\\FX\\H2 Boom Kicks 05.ogg","public\\samples\\FX\\H2 Depth Charge 07.ogg","public\\samples\\FX\\H2 Downlifters 11.ogg","public\\samples\\FX\\H2 Noisesweeps 08.ogg","public\\samples\\kicks\\H2 Trancekick 08.ogg","public\\samples\\kicks\\H2 Trancekick 20.ogg","public\\samples\\kicks\\H2 Trancekick 28.ogg","public\\samples\\kicks\\H2 Trancekick 34.ogg","public\\samples\\loops\\H2 Closed Hat Loop 160BPM 02.ogg","public\\samples\\loops\\H2 loops 04.ogg","public\\samples\\loops\\H2 Open Hat Loop 160BPM 06.ogg","public\\samples\\loops\\H2 Perc Loop 160BPM 03.ogg","public\\samples\\synths\\H2 Pads 01 C Saw.ogg","public\\samples\\synths\\H2 Pads 13 F Voc.ogg","public\\samples\\synths\\H2 Pads 20 G Saw.ogg","public\\samples\\synths\\H2 Pads 23 D String.ogg"] export const songs = ["..\\public\\samples\\bass\\01 HTS Arpeggiated Bass.ogg","..\\public\\samples\\bass\\02 HTS Hardtrance Bass.ogg","..\\public\\samples\\bass\\03 HTS Break Bass.ogg","..\\public\\samples\\bass\\04 HTS Sub Bass.ogg","..\\public\\samples\\drums\\01 HTS Rides and Hats.ogg","..\\public\\samples\\drums\\02 HTS Claps and Hats.ogg","..\\public\\samples\\drums\\03 HTS Club Snares.ogg","..\\public\\samples\\drums\\04 HTS Buildup Snares.ogg","..\\public\\samples\\FX\\01 HTS Boom Kicks.ogg","..\\public\\samples\\FX\\02 HTS Verbclaps.ogg","..\\public\\samples\\FX\\03 HTS Noisesweep.ogg","..\\public\\samples\\FX\\04 HTS Combined FX.ogg","..\\public\\samples\\kicks\\01 HTS Trancekick.ogg","..\\public\\samples\\kicks\\02 HTS Sizzle Layer.ogg","..\\public\\samples\\kicks\\03 HTS Transients.ogg","..\\public\\samples\\kicks\\04 HTS Kick Combined.ogg","..\\public\\samples\\loops\\01 HTS Closed Hat Loop.ogg","..\\public\\samples\\loops\\02 HTS Open Hat Loop.ogg","..\\public\\samples\\loops\\03 HTS Perc Loop.ogg","..\\public\\samples\\loops\\04 HTS Full Loop.ogg","..\\public\\samples\\synths\\01 HTS Pluck Leads and Arp Bass.ogg","..\\public\\samples\\synths\\02 HTS Arp Leads and Saw Pads.ogg","..\\public\\samples\\synths\\03 HTS Pluck Leads and Break Bass.ogg","..\\public\\samples\\synths\\04 HTS Saw Leads and String Pads.ogg"]

View file

@ -24,6 +24,7 @@
] ]
}, },
"include": [ "include": [
"src" "src",
"src/media_process.js"
] ]
} }