From b9ea1928b920358dcdd9dbbfd380b9d1189fefdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djk=C3=A1=C5=A5o?= Date: Thu, 3 Aug 2023 23:55:55 +0200 Subject: [PATCH] fix underflowing symmetry lines, better errors --- .gitignore | 14 +++ packages/player/src/index.ts | 76 +++++++---------- packages/preprocessor/.gitignore | 3 +- packages/preprocessor/index.html | 40 +++++++-- packages/preprocessor/package.json | 5 +- packages/preprocessor/src/generate_db.ts | 2 +- packages/preprocessor/src/main.ts | 15 ++-- packages/preprocessor/src/media_process.js | 99 ++++++++++++++++++++++ packages/preprocessor/src/songs_list.ts | 2 +- packages/preprocessor/vite.config.ts | 2 +- packages/visualizer/src/index.ts | 7 +- 11 files changed, 200 insertions(+), 65 deletions(-) create mode 100644 packages/preprocessor/src/media_process.js diff --git a/.gitignore b/.gitignore index 820b1f5..c59b87a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,17 @@ testem.log # System Files .DS_Store Thumbs.db + +# video and audio files +*.mp3 +*.mp4 +*.mov +*.webm +*.webp +*.ogg +*.wav +*.avi +*.png +*.jpeg +*.jpg +*.exr \ No newline at end of file diff --git a/packages/player/src/index.ts b/packages/player/src/index.ts index 3ec0662..3433942 100644 --- a/packages/player/src/index.ts +++ b/packages/player/src/index.ts @@ -124,37 +124,17 @@ export class MusicPlayer { return new Promise((resolve, reject) => { if (this.track.context.state == "closed" || this.track.context.state == "suspended") { this.is_playing = false - reject("Can't seek - track not playing") + reject(new Error("Can't seek - track not playing")) } this.audio_element.currentTime = new_time resolve(null) - /*audio_element.play().then((s) => resolve(s), (r) => { - is_playing = false - reject(r) - })*/ }) } - // THIS MIGHT BE UNNECESSARY? CUZ SEEKING DOESN'T REQUIRE PLAY - // /** - // * Can try to seek even if the audio context was suspended or closed. Best to use try_seek_async() - // */ - // seek_async(new_time: number) { - // return new Promise((resolve, reject) => { - // this.audio_element.currentTime = new_time - // resolve(null) - // /* audio_element.play().then((s) => resolve(s), (r) => { - // is_playing = false - // reject(r) - // })*/ - // }) - // // } - // /** - // * Unsafe, throws error if failed. Use try_seek_async or seek_async unless you don't care about the result. - // */ - + /** + * Unsafe, throws error if failed. Use try_seek_async or seek_async unless you don't care about the result. + */ seek(new_time: number) { this.audio_element.currentTime = new_time - //this.audio_element.play().catch((e) => { throw e }) } /** * Safer play_toggle_async. Normal play_toggle will try to start the player even if the track hasn't started yet, or was previously suspended/closed @@ -163,7 +143,7 @@ export class MusicPlayer { return new Promise((resolve, reject) => { if (this.audio_context.state === "suspended" || this.audio_context.state === "closed") { this.audio_context.resume().then(undefined, (e) => - reject("Context closed or suspended" + e)) + reject(new Error("Context closed or suspended" + JSON.stringify(e)))) } if (this.audio_element.paused) { this.audio_element.play().then((s) => { @@ -171,7 +151,7 @@ export class MusicPlayer { resolve(s) }, (r) => { this.is_playing = false - reject(r) + reject(new Error("failed to play audio elements" + JSON.stringify(r))) }) } else { this.audio_element.pause() @@ -194,7 +174,7 @@ export class MusicPlayer { resolve(s) }, (r) => { this.is_playing = false - reject(r) + reject(new Error(JSON.stringify(r))) }) } else { this.audio_element.pause() @@ -223,18 +203,27 @@ export class MusicPlayer { */ try_play_async() { return new Promise((resolve, reject) => { - if (this.is_playing) reject(Error("Already playing")) + if (this.is_playing) resolve(Error("Already playing")) if (this.audio_context.state === "suspended" || this.audio_context.state === "closed") { - this.audio_context.resume().then(undefined, (e) => - reject("Context closed or suspended" + e)) + this.audio_context.resume().then(() => { + this.audio_element.play().then((s) => { + this.is_playing = true + resolve(s) + }, (r) => { + this.is_playing = false + reject(new Error(JSON.stringify(r))) + }) + }, (e) => + reject(new Error("Context closed or suspended" + JSON.stringify(e)))) + } else { + this.audio_element.play().then((s) => { + this.is_playing = true + resolve(s) + }, (r) => { + this.is_playing = false + reject(new Error(JSON.stringify(r))) + }) } - this.audio_element.play().then((s) => { - this.is_playing = true - resolve(s) - }, (r) => { - this.is_playing = false - reject(r) - }) }) } /** @@ -242,16 +231,13 @@ export class MusicPlayer { */ play_async() { return new Promise((resolve, reject) => { - if (this.is_playing) resolve(null) - if (this.audio_context.state === "suspended" || this.audio_context.state === "closed") { - this.audio_context.resume() - } + if (this.is_playing) resolve(Error("Already playing")) this.audio_element.play().then((s) => { this.is_playing = true resolve(s) }, (r) => { this.is_playing = false - reject(r) + reject(new Error(JSON.stringify(r))) }) }) } @@ -262,7 +248,7 @@ export class MusicPlayer { if (this.is_playing) return this.audio_element.play().catch((r) => { this.is_playing = false - throw r + throw new Error(r) }) } /** @@ -288,12 +274,12 @@ export class MusicPlayer { this.audio_element.addEventListener("error", function error_listener(e) { controller.abort() - reject(e) + reject(new Error("Failed to load new song, error:" + JSON.stringify(e))) }, { signal: controller.signal }) this.audio_element.addEventListener("stalled", function stalled_listener(e) { controller.abort() - reject(e) + reject(new Error("Failed to load new song, stalled: " + JSON.stringify(e))) }, { signal: controller.signal }) //once aborted, try to set current_song_duration diff --git a/packages/preprocessor/.gitignore b/packages/preprocessor/.gitignore index 21a4abc..a651e27 100644 --- a/packages/preprocessor/.gitignore +++ b/packages/preprocessor/.gitignore @@ -25,5 +25,6 @@ dist-ssr # public samples public/samples/* +public/media/* src/song_list.ts -src/db.js \ No newline at end of file +src/db.js diff --git a/packages/preprocessor/index.html b/packages/preprocessor/index.html index 99d9f8f..730b0f5 100644 --- a/packages/preprocessor/index.html +++ b/packages/preprocessor/index.html @@ -12,29 +12,55 @@
- - +
+ + Download DB +
+
+ +
diff --git a/packages/preprocessor/package.json b/packages/preprocessor/package.json index d4ef236..9db2696 100644 --- a/packages/preprocessor/package.json +++ b/packages/preprocessor/package.json @@ -6,6 +6,7 @@ "scripts": { "crawl": "node crawler.mjs", "process": "node crawler.mjs && vite", + "media-process": "cd src && node media_process.js", "serve": "vite", "build": "tsc && vite build", "preview": "vite preview" @@ -15,7 +16,7 @@ "vite": "^4.4.0" }, "dependencies": { - "filehound": "^1.17.6", - "@euterpe.js/music-library": "*" + "@euterpe.js/music-library": "*", + "filehound": "^1.17.6" } } diff --git a/packages/preprocessor/src/generate_db.ts b/packages/preprocessor/src/generate_db.ts index 13037f7..04ee559 100644 --- a/packages/preprocessor/src/generate_db.ts +++ b/packages/preprocessor/src/generate_db.ts @@ -42,7 +42,7 @@ export function generate_db() { const db_song = new Song({ name: name.slice(0, name.lastIndexOf(".")), artists: [], - url: new URL("http://localhost:4200/" + song_url), + url: new URL("http://localhost:4201/" + song_url), duration: 0, remix_artists: [], in_collection: new Ref(RefTo.Collections, song.collection_id) diff --git a/packages/preprocessor/src/main.ts b/packages/preprocessor/src/main.ts index ba404f5..5c680ee 100644 --- a/packages/preprocessor/src/main.ts +++ b/packages/preprocessor/src/main.ts @@ -11,17 +11,20 @@ export async function start() { const waveform_canvas = document.querySelector("#waveform-canvas") as SVGSVGElement for (const song of result.db.songs) { console.log("creating waveform for -> " + song.name) - 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 }) + const curr_waveform_canvas = waveform_canvas.cloneNode() as SVGSVGElement + waveform_canvas.parentElement?.append(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_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((done) => setTimeout(() => done(), 300)) // @ts-ignore - song.metadata[0] = waveform_canvas.innerHTML + song.metadata[0] = curr_waveform_canvas.children[0].getAttribute("d") song.fft_data = [] } + waveform_canvas.remove() console.dir(result.db, { depth: null }) download(JSON.stringify(result.db), "db.json", "text/plain") }) @@ -45,6 +48,8 @@ async function analyze(): Promise { track.connect(audioContextAnalyser).connect(gain).connect(audioContext.destination) let db = generate_db() + // db.songs.splice(0, 10) + // db.songs.splice(2) console.log(db) for (const song of db.songs) { // const song = db.songs[db.songs.length - 1] @@ -73,11 +78,11 @@ async function analyze(): Promise { return result } function download(content: BlobPart, fileName: string, contentType: string) { - var a = document.createElement("a"); + var a = document.querySelector("#download") as HTMLAnchorElement; var file = new Blob([content], { type: contentType }); a.href = URL.createObjectURL(file); a.download = fileName; - a.click(); + // a.click(); } type AnalyzeReturn = { diff --git a/packages/preprocessor/src/media_process.js b/packages/preprocessor/src/media_process.js new file mode 100644 index 0000000..5a8ee1d --- /dev/null +++ b/packages/preprocessor/src/media_process.js @@ -0,0 +1,99 @@ +/** + * TODO: + * -add back -metadata + * + */ + +import filehound from "filehound" +import { execSync, exec } from 'child_process' +import { fstat, unlinkSync } from "fs" + +function generateNewPhotoSizes(file, currentExtention) { + exec(`start ffmpeg -y -i "${file}.${currentExtention}" -lossless 0 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_ogw.webp" -vf scale=1000:-1 -lossless 0 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_1000w.webp" -vf scale=800:-1 -lossless 0 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_800w.webp" -vf scale=500:-1 -lossless 0 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_500w.webp" -vf scale=320:-1 -lossless 0 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_320w.webp" -vf scale=-1:64,gblur=sigma=10:steps=2 -lossless 0 -compression_level 6 -quality 85 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_placeholder.webp"`) +} +function generateNewAnimPhotoSizes(file, currentExtention) { + exec(`start ffmpeg -y -i "${file}.${currentExtention}" -lossless 0 -frames:v 1 -r 1 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_ogw_static.webp" -vf scale=1000:-1 -lossless 0 -frames:v 1 -r 1 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_1000w_static.webp" -vf scale=800:-1 -lossless 0 -frames:v 1 -r 1 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_800w_static.webp" -vf scale=500:-1 -lossless 0 -frames:v 1 -r 1 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_500w_static.webp" -vf scale=320:-1 -lossless 0 -frames:v 1 -r 1 -quality 85 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_320w_static.webp" -vf scale=-1:64,gblur=sigma=10:steps=2 -lossless 0 -frames:v 1 -r 1 -compression_level 6 -quality 85 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_placeholder_static.webp"`) + exec(`start ffmpeg -y -i "${file}.${currentExtention}" -lossless 0 -quality 85 -loop 0 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_ogw.webp" -vf scale=1000:-1 -lossless 0 -quality 85 -loop 0 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_1000w.webp" -vf scale=800:-1 -lossless 0 -quality 85 -loop 0 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_800w.webp" -vf scale=500:-1 -lossless 0 -quality 85 -loop 0 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_500w.webp" -vf scale=320:-1 -lossless 0 -quality 85 -loop 0 -compression_level 6 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_320w.webp" -vf scale=-1:64,gblur=sigma=10:steps=2 -frames:v 1 -lossless 0 -c:v libwebp -compression_level 6 -quality 85 -metadata author="Djkáťo" -metadata copyright="https://djkato.net" "${file}_placeholder.webp"`) +} +function generateNewSounds(file, currentExtention) { + const path = file.substring(0, file.lastIndexOf("\\")) + file = file.substring(file.lastIndexOf("\\") + 1) + + let command = "" + command += `cd "${path}" && start cmd /k "` + command += `ffmpeg -y -i "${file}.${currentExtention}" ` + command += `-c:a libopus -b:a 128k "${file}.ogg"` + command += ` && exit"` + exec(command) + // console.log(command) +} +function generateNewVideoSizes(file, currentExtention, width_resolutions) { + const path = file.substring(0, file.lastIndexOf("\\")) + file = file.substring(file.lastIndexOf("\\") + 1) + + let command = "" + command += `cd "${path}" && ` + command += `del ffmpeg2pass-0.log && ` + command += `ffmpeg -y -i "${file}.${currentExtention}" ` + command += `-vcodec libvpx-vp9 -cpu-used 0 -deadline good -quality good -g 240 -crf 42 -b:v 0 -c:a libopus -row-mt 1 -tile-rows 2 -tile-columns 4 -threads 16 -auto-alt-ref 6 ` + command += `-pass 1 -f webm NUL && exit` + + exec(command).once("exit", () => { + for (const resolution of width_resolutions) { + let res_command = "" + res_command += `start cmd /k "` + res_command += `cd "${path}" && ` + res_command += `ffmpeg -y -i "${file}.${currentExtention}" ` + res_command += `-vcodec libvpx-vp9 -cpu-used 0 -deadline good -quality good -g 240 -vf scale=${resolution}:-1 -crf 42 -b:v 0 -c:a libopus -row-mt 1 -tile-rows 2 -tile-columns 4 -threads 16 -auto-alt-ref 6 -pass 2 "${file}_${resolution}p.webm"` + res_command += "&& exit\"" + exec(res_command) + } + }) +} +let dirs = filehound.create() + .path("../public/") + .directory() + .findSync() +console.log(dirs) + +for (let i = 0; i < dirs.length; i++) { + //gets current name file+ext + let current_folder_files = filehound.create() + .path(`${dirs[i]}`) + .findSync() + + if (current_folder_files[0] != undefined) { + //if previous encode was cancelled and 2pass log not removed, remove it :) + if (current_folder_files[0].includes("ffmpeg2pass-0.log")) { + try { unlinkSync(`${dirs[i]}/ffmpeg2pass-0.log`) } catch (err) { } + current_folder_files = current_folder_files.slice(1) + } + for (let current_media of current_folder_files) { + current_media = [current_media.substring(0, current_media.lastIndexOf(".")), current_media.substring(current_media.lastIndexOf(".") + 1)] + if (current_media[1] == "wav" || current_media[1] == "mp3") { + console.log(`${current_media[0]}.${current_media[1]}\n`) + + generateNewSounds(`${current_media[0]}`, `${current_media[1]}`) + } + if (current_media[1] == "png" || current_media[1] == "jpg") { + console.log(`.\\${current_media[0]}.${current_media[1]}\n`) + + generateNewPhotoSizes(`.\\${current_media[0]}`, `${current_media[1]}`) + } + else if (current_media[1] == "gif") { + console.log(`.\\${current_media[0]}.${current_media[1]}\n`) + + generateNewAnimPhotoSizes(`.\\${current_media[0]}`, `${current_media[1]}`) + } + else if (current_media[1] == "webm" || current_media[1] == "mov" || current_media[1] == "avi" || current_media[1] == "mp4") { + //console.log(`Video: ${current_media[0]}.${current_media[1]}\n`) + + // generateNewVideoSizes(`${current_media[0]}`, `${current_media[1]}`, [2560, 1080, 720, 480]) + + } + } + } +} + + + diff --git a/packages/preprocessor/src/songs_list.ts b/packages/preprocessor/src/songs_list.ts index 5ca1567..6faad0d 100644 --- a/packages/preprocessor/src/songs_list.ts +++ b/packages/preprocessor/src/songs_list.ts @@ -1 +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"] \ No newline at end of file +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"] \ No newline at end of file diff --git a/packages/preprocessor/vite.config.ts b/packages/preprocessor/vite.config.ts index 52729d2..e608aa4 100644 --- a/packages/preprocessor/vite.config.ts +++ b/packages/preprocessor/vite.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ cacheDir: "../../node_modules/.vite/preprocessor", server: { - port: 4200, + port: 4201, host: "localhost" }, diff --git a/packages/visualizer/src/index.ts b/packages/visualizer/src/index.ts index 5392e06..f0f834f 100644 --- a/packages/visualizer/src/index.ts +++ b/packages/visualizer/src/index.ts @@ -215,8 +215,9 @@ export class AudioVisual { case ShapeType.Waveform: { if (this.#shape.waveform_options!.shape_type == WaveformShape.LineLike) { if (this.#shape.symmetry) { - for (let i = 0; i < this.#shape.points.length - 1; i += 2) { - const mutator = this.#convert_range(frequency_data[i / 2] * this.#fft_multiplier + this.#fft_offset, in_range, out_range) + for (let i = 0; i < this.#shape.points.length; i += 2) { + let mutator = this.#convert_range(frequency_data[i / 2] * this.#fft_multiplier + this.#fft_offset, in_range, out_range) + if (mutator <= 0) mutator = 2 if (this.#shape.waveform_options!.orientation == WaveformOrientation.Horizontal) { mutated_points.push({ x: this.#shape.points[i].x, @@ -262,6 +263,7 @@ export class AudioVisual { #convert_range(value: number, r1: number[], r2: number[]) { if (!isFinite(value)) return 0 + if (value < r1[0]) return 0 return ((value - r1[0]) * (r2[1] - r2[0])) / (r1[1] - r1[0]) + r2[0] } @@ -340,6 +342,7 @@ export class AudioVisual { case SmoothingAlgorythm.CatmullRom: { if (this.#shape.shape_type == ShapeType.Waveform && this.#shape.symmetry == true) { //adding points so both halfs ends and start at the same center point + console.log(arr) const first_half = [{ x: 0, y: this.#canvas_height / 2 }] const second_half = [{ x: 0, y: this.#canvas_height / 2 }] for (let i = 0; i < arr.length - 1; i += 2) {