diff --git a/.gitignore b/.gitignore index 3c3629e..76add87 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +dist \ No newline at end of file diff --git a/.tokeignore b/.tokeignore new file mode 100644 index 0000000..eeeb7be --- /dev/null +++ b/.tokeignore @@ -0,0 +1 @@ +pnpm-lock.yaml \ No newline at end of file diff --git a/package.json b/package.json index 3e39613..c841e62 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,28 @@ { - "name": "osuplayer.js", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "dev": "vite" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "@biomejs/biome": "1.7.2", - "@types/three": "^0.164.0", - "vite": "^5.2.11", - "vite-plugin-node-polyfills": "^0.21.0" - }, - "dependencies": { - "@zip.js/zip.js": "^2.7.43", - "osu-classes": "^3.1.0", - "osu-parsers": "^4.1.7", - "rosu-pp-js": "https://github.com/MaxOhn/rosu-pp-js/releases/download/v1.0.2/rosu_pp_js_web.tar.gz", - "three": "^0.164.1" - } + "name": "osuplayer.js", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@biomejs/biome": "1.7.2", + "@types/three": "^0.164.0", + "vite": "^5.2.11", + "vite-bundle-analyzer": "^0.9.4", + "vite-plugin-node-polyfills": "^0.21.0" + }, + "dependencies": { + "@zip.js/zip.js": "^2.7.43", + "osu-classes": "^3.1.0", + "osu-parsers": "^4.1.7", + "rosu-pp-js": "https://github.com/MaxOhn/rosu-pp-js/releases/download/v1.0.2/rosu_pp_js_web.tar.gz", + "three": "^0.164.1" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d33188d..9fca044 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,6 +31,9 @@ devDependencies: vite: specifier: ^5.2.11 version: 5.2.11 + vite-bundle-analyzer: + specifier: ^0.9.4 + version: 0.9.4 vite-plugin-node-polyfills: specifier: ^0.21.0 version: 0.21.0(vite@5.2.11) @@ -1379,6 +1382,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} dependencies: @@ -1452,6 +1460,13 @@ packages: which-typed-array: 1.1.15 dev: true + /vite-bundle-analyzer@0.9.4: + resolution: {integrity: sha512-a/w4ShKQEbTe7wllLXI+3D8ELgRv7iHDuWEPaXokA+FhWBXJCdtOZYIvLzvnn8vxHf+7pm1c23AeGyW/0Ge0Yw==} + dependencies: + picocolors: 1.0.0 + source-map: 0.7.4 + dev: true + /vite-plugin-node-polyfills@0.21.0(vite@5.2.11): resolution: {integrity: sha512-Sk4DiKnmxN8E0vhgEhzLudfJQfaT8k4/gJ25xvUPG54KjLJ6HAmDKbr4rzDD/QWEY+Lwg80KE85fGYBQihEPQA==} peerDependencies: diff --git a/src/index.ts b/src/index.ts index 75e32fd..69b168e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,8 @@ class OsuPlayer { beatmapSet: BeatmapSet = new BeatmapSet(); diffsByStarRating: [number, number][] = []; diffs: Map = new Map(); + audioTimestamp: number; + audioBuffer: AudioBuffer; constructor(opts?: OsuPlayerOpts) { if (opts) this.opts = opts; @@ -66,29 +68,7 @@ class OsuPlayer { } this.diffsByStarRating.sort((a, b) => a[0] - b[0]); - /* - const listEl = document.createElement("ul"); - listEl.style.margin = "0"; - for (const [stars, id] of this.diffsByStarRating) { - // biome-ignore lint/style/noNonNullAssertion: - const diff = this.diffs.get(id)!; - const liEl = document.createElement("li"); - const aEl = document.createElement("a"); - aEl.href = "javascript:void(0)"; - aEl.text = `${diff.osuInner.metadata.version} (${ - Math.round(stars * 100) / 100 - }*)`; - aEl.addEventListener("click", (evt) => { - this.setBeatmap(id); - }); - liEl.appendChild(aEl); - listEl.appendChild(liEl); - } - - console.log([...this.beatmapSet.assets.entries()]); - this.el?.replaceChildren(listEl); - */ - + // TODO: Debug this.setBeatmap( this.diffsByStarRating[this.diffsByStarRating.length - 1][1], ); @@ -99,6 +79,29 @@ class OsuPlayer { }); } + showDiffSelector() { + const listEl = document.createElement("ul"); + listEl.style.margin = "0"; + for (const [stars, id] of this.diffsByStarRating) { + // biome-ignore lint/style/noNonNullAssertion: + const diff = this.diffs.get(id)!; + const liEl = document.createElement("li"); + const aEl = document.createElement("a"); + aEl.href = "javascript:void(0)"; + aEl.text = `${diff.osuInner.metadata.version} (${ + Math.round(stars * 100) / 100 + }*)`; + aEl.addEventListener("click", (evt) => { + this.setBeatmap(id); + }); + liEl.appendChild(aEl); + listEl.appendChild(liEl); + } + + console.log([...this.beatmapSet.assets.entries()]); + this.el?.replaceChildren(listEl); + } + setBeatmap(id: number) { this.canvasEl = document.createElement("canvas"); this.canvasEl.width = 1024; @@ -111,14 +114,17 @@ class OsuPlayer { const beatmap = this.diffs.get(id)!; const backgroundUrl = URL.createObjectURL(beatmap.background()); + const audioUrl = beatmap.audioFile(); + console.log(audioUrl); const scene = new THREE.Scene(); const camera = new THREE.OrthographicCamera(-512, 512, -384, 384, 1, 1000); scene.add(camera); - scene.add(new THREE.AmbientLight()); + const clock = new THREE.Clock(); const loader = new THREE.TextureLoader(); + const audioContext = new AudioContext(); let backgroundPlane: THREE.Mesh; { @@ -140,15 +146,46 @@ class OsuPlayer { const renderer = new THREE.WebGLRenderer({ canvas: this.canvasEl }); const stats = new Stats(); + this.el?.replaceChildren(this.canvasEl, stats.dom); - function animate() { - requestAnimationFrame(animate); + const loadMusic = async () => { + const audioStream = audioUrl.stream(); + const buffer = await new Response(audioStream).arrayBuffer(); + const musicBuffer = await audioContext.decodeAudioData(buffer); + this.audioBuffer = musicBuffer; + }; - renderer.render(scene, camera); - } + const loadStuff = () => { + return Promise.all([loadMusic()]); + }; - animate(); + const play = () => { + const gainNode = audioContext.createGain(); + gainNode.gain.value = 0.01; + gainNode.connect(audioContext.destination); + + const audioSource = audioContext.createBufferSource(); + audioSource.buffer = this.audioBuffer; + audioSource.connect(gainNode); + audioSource.start(); + + const updateObjects = () => {}; + + const animate = () => { + requestAnimationFrame(animate); + this.audioTimestamp = audioContext.getOutputTimestamp().contextTime; + updateObjects(); + renderer.render(scene, camera); + }; + + animate(); + }; + + loadStuff().then(() => { + console.log("loaded!"); + play(); + }); } } diff --git a/src/osu.ts b/src/osu.ts index 31d0ba5..48df84a 100644 --- a/src/osu.ts +++ b/src/osu.ts @@ -16,6 +16,12 @@ export class Beatmap { return this.osuInner.metadata.beatmapId; } + public audioFile(): Blob { + const filename = this.osuInner.general.audioFilename; + // biome-ignore lint/style/noNonNullAssertion: + return this.set.assets.get(filename)!; + } + public background(): Blob { // biome-ignore lint/style/noNonNullAssertion: const filename = this.osuInner.events.backgroundPath!; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..92f8df7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler" + } +} diff --git a/vite.config.ts b/vite.config.ts index e31954e..42ead81 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,14 @@ +import { analyzer } from "vite-bundle-analyzer"; import { defineConfig } from "vite"; import { nodePolyfills } from "vite-plugin-node-polyfills"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [], + build: { + lib: { + entry: "src/index.ts", + name: "osuplayer", + }, + }, + plugins: [analyzer()], });