euterpe-source/packages/euterpe/README.md

177 lines
5.7 KiB
Markdown
Raw Permalink Normal View History

2023-06-12 15:50:33 +00:00
# Euterpe
Fully featured AudioContext music player for the web.
## Euterpe in production:
- Hypertrance ( [site](https://hypertrance.eu/), [repository](https://github.com/nuphory/hypertrance.eu) )
2023-06-12 15:50:33 +00:00
Features:
- "Local" library/database for songs, collections, artists, waveforms, artist links and much more!
- Queue and history
2023-06-12 15:50:33 +00:00
- Easy way to create Vector based audio visuals
- Library automatization based on folder/file structure, preprocessing and encoding media files for all platforms
2023-06-12 15:50:33 +00:00
- Safe. Provides wrappers for all functions that are either unsafe or don't give a success return. (very Rust inspired, yes.)
- Frontend library agnostic
2023-06-12 15:50:33 +00:00
## How to use:
#### Simple demo [here](https://github.com/euterpe-js/euterpe-source/tree/master/packages/euterpe-web-test)
Since this package is just a compilation of our smaller modules, you can read individual modules' tutorials on their respective npm page:
- [Euterpe Player](https://www.npmjs.com/package/@euterpe.js/player)
- [Euterpe Visualizer](https://www.npmjs.com/package/@euterpe.js/visualizer)
- [Euterpe Music Library](https://www.npmjs.com/package/@euterpe.js/music-library)
You can further check out how to automate database creation from folder structure, auto encode media for all platforms and create waveform svgs for songs here:
- [Euterpe Preprocessor](https://www.npmjs.com/package/@euterpe.js/preprocessor)
This module builds on those, and further adds functions for playing backwards, forwards and managing the queue.
First we create a database with our songs
`db.ts`
```ts
import { DB, Song, Artist, Ref, RefTo, Platforms } from "@euterpe.js/music-library"
export const db = new DB
db.add([
//The IDs are added incrementally & are 0 based., so first artists ID added is 0, next 1 etc...
//You can specify the ID manually if you want
new Artist({
name: "Machinedrum",
}),
new Artist({
name: "Tanerélle",
}),
new Artist({
name: "Mono/Poly",
}),
new Artist({
name: "IMANU",
links: [
[Platforms.Spotify, new URL("https://open.spotify.com/artist/5Y7rFm0tiJTVDzGLMzz0W1?si=DRaZyugTTIqlBHDkMGKVqA&nd=1")]
]
}),
])
db.add([
new Song({
//Refrences are constructed as such. This allows to get to the artist from either collection or song
artists: [new Ref(RefTo.Artists, 2), new Ref(RefTo.Artists, 3), new Ref(RefTo.Artists, 4)],
duration: 252,
name: "Star",
remix_artists: [new Ref(RefTo.Artists, 5)],
url: new URL("http://" + window.location.host + "/Machinedrum, Tanerelle & Mono Poly - Star (IMANU Remix) final.mp3")
}),
])
```
Then we build our Euterpe player and assign the db to it. Then it's just a matter of creating event listeners to the dom and binding them to Euterpes functions.
`main.ts`
```ts
import { db } from "./db";
import { EuterpeBuilder } from "@euterpe.js/euterpe"
let is_seeking = false
const euterpe = new EuterpeBuilder(document.querySelector("#audio")!, db)
.build()
document.querySelector("#seek")?.addEventListener("mouseup", (e) => {
try {
euterpe.try_seek(e.target?.valueAsNumber)
} catch {
alert("Failed seeking! " + e)
}
is_seeking = false
})
euterpe.on_song_change((_, song_name) => {
document.querySelector("#text-playing")!.innerHTML = song_name
})
document.querySelector("#previous")?.addEventListener("click", () => {
euterpe.try_previous_song_looping().catch((e) => alert(e + "Failed to change song"))
})
document.querySelector("#next")?.addEventListener("click", () => {
euterpe.try_next_song_looping().catch((e) => alert(e + "Failed to change song"))
})
document.querySelector("#mute")?.addEventListener("click", () => {
euterpe.mute()
})
document.querySelector("#unmute")?.addEventListener("click", () => {
euterpe.unmute()
})
document.querySelector("#toggle-play")?.addEventListener("click", () => {
euterpe.try_play_toggle().catch((e) => alert("failed to toggle pause/play!" + e))
})
document.querySelector("#volume")?.addEventListener("input", (e) => {
euterpe.change_volume(e.target?.valueAsNumber)
})
//disables time updates so the time slider doesn't slip away from user
document.querySelector("#seek")?.addEventListener("mousedown", () => {
is_seeking = true
})
```
Then we can set up listeners to Euterpes events to keep the UI up todate as well
`main.ts`
```ts
//...
// Subscriptions to song and AudioContext changes, eg. time, name..
euterpe.on_duration_formatted((time) => {
document.querySelector("#duration")!.innerHTML = time
document.querySelector("#seek")!.max = "" + euterpe.current_song_duration
})
euterpe.on_time_tick_formatted((time) => {
document.querySelector("#current")!.innerHTML = time
})
euterpe.on_time_tick((time) => {
if (is_seeking) return
document.querySelector("#seek")!.value = "" + time
dev_queue_update()
dev_history_update()
})
euterpe.on_song_change((_, song_name) => {
document.querySelector("#text-playing")!.innerHTML = song_name
})
//preload after setting all listeners to make sure you capture the song update!
euterpe.try_preload_song(0).catch((e) => console.log(e + " Failed to preload"))
//..
function dev_queue_update() {
const p = document.querySelector("#queue-info") as HTMLParagraphElement
const dev_arr = []
for (const song of euterpe.queue) {
dev_arr.push(`Name: ${song.name}, ID: ${song.id} |`)
}
p.innerHTML = dev_arr.toString()
}
function dev_history_update() {
const p = document.querySelector("#history-info") as HTMLParagraphElement
const dev_arr = []
for (const song of euterpe.played_history) {
dev_arr.push(`Name: ${song.name}, ID: ${song.id} |`)
}
p.innerHTML = dev_arr.toString()
}
```
and it's done!
For vizualizer demo, or how to use the core parts of the Euterpe libraries separately, check out the individual repos readmes.