Notice:
This repository is unlicensed on purpose. You may not use, copy, modify, or
distribute this code without permission.
I do not plan on accepting contributions. Please get in contact through
GitHub or info at wooby dot moe before you waste your time writing code and
opening a pull request.
etsuko is a shitty karaoke program written by reinventing the wheel in
unnecessary ways.
It displays lyrics in real time as the song plays, along with a rudimentary
interface that mimics a music player.
My main source of inspiration/plagiarism is Apple Music's fullscreen player.
I may add that I have no idea what I'm doing with the UI itself, nor with OpenGL, so the program might be a little janky and awkward. The main purpose of its existence is for my own enjoyment (I like lyrics) and to sometimes send it to my friends (although they don't like it nearly as much as I do).
A publicly viewable demo is hosted here
using emscripten to target wasm.
Check the console if it takes a while to load to see if there are any errors.
Also, it expects support for WebGL 2/OpenGL ES 3.0, or else it'll fail to run.
If you represent an artist and would like your song taken down from
the website, you may also contact me via the email above.
I make no money out of this whatsoever nor the site shows any ads.
All audio files were acquired legally through iTunes, and lyrics are either
my own translations or the original obtained from public sources.
This project uses CMake, so to build it on your local machine targeting the desktop configuration (not wasm), you can run:
# Configure using the desktop-release preset
cmake --preset desktop-release
# Build the project
cmake --build --preset desktop-release
# Change working dir and run the program
cd ./build/desktop-release
./etsukoThe desktop-debug variant has asan enabled so it'll be a tad slower to run but should still run without major hickups.
Requirements
- CMake
- Ninja
- A C compiler (preferably clang)
- OpenGL (probably already included in your system)
- GLEW, GLFW3 and OpenAL dev libraries
On Linux and mac OS it's probably pretty straightforward to build, you can either install all the dependencies manually or use the vcpkg cmake target. On Windows it's a bit trickier and you'll probably want to use the vcpkg target.
You need to configure the emscripten SDK and compile using cmake and the wasm-release target.
You can then run the project from the generated etsuko.html file inside build/wasm-release using the emrun command, as such:
cd ./build/wasm-release/
emrun ./etsuko.htmlNote that a secret.h file defining CDN_BASE_PATH is required as a way to fetch song files from a (possibly) remote server, but you can just point it to localhost (and whatever port emrun uses on your machine) and it'll fetch the files from the same base path as the .html file is at the time of running the command. So for example, localhost:<port>/assets/my_song.txt is resolved to a file my_song.txt inside the ./assets/ folder in the same directory as etsuko.html. I just said the same thing twice.
No dependencies other than emscripten itself is needed when building this target.
Upon running, the program will probably fail and close because it needs a song to play. It uses a custom format described below.
You can change the default file in config.c. All files in the desktop build are expected to be inside the assets/ folder, which is automatically copied on build and is composed of the contents inside the assets/ (tracked by git) and assets_dbg/ (not tracked by git) in the root directory.
In general, a song needs 3 files to function properly:
- song_name.txt file describing the song
- album_art.jpg image containing the album art, specified inside the .txt
- song_audio.mp3 audio file also specified inside the .txt
This data is contained inside a header portion of the file and includes other metadata. A full list of options (although not all of them are currently used) can be found in song.c.
The structure of the file is as follows:
name=My song
albumArt=img/album.jpg
filePath=files/song.mp3
artist=John Doe
album=Album
alignment=center
offset=0.3
bgColor=972566
bgColorSecondary=000000
bgType=simpleGradient
#timings
0:00
0:05
0:13.1
...
#lyrics
First line of the song
And the second one 光#alignment=right
...
#ass
Dialogue: 0,0:00:00.00,0:00:05.75,Default,,0,0,0,,
Dialogue: 0,0:00:05.75,0:00:13.10,Default,,0,0,0,,First line of the song
Dialogue: 0,0:00:13.10,0:00:17.00,Default,,0,0,0,,And the second one#alignment=right
...
#readings
First=text to show under First,line=Same for line,of=again
one=text to show under one,光=hikari
Notice that all files are expected to be inside the assets/ folder during runtime, and relative paths (e.g. files/) only work in the wasm target, and you need to define a CDN_BASE_PATH pointing to a remote server to retrieve these files from at runtime.
You may use either the combination of #lyrics and #timings (timings must come before lyrics) OR the #ass section which should be filled with only the [Events] part of a .ass file (generated using Aegisub, for example) from the second line onwards (ignoring the one that starts with Format:).
Only timing and line content information are used from the .ass format, so any modifiers that can be applied to an individual line or the player itself are custom-made and handled separately (the full list can be seen in song.c).
Using #timings is the simplest way and can be done by hand, and you can specify a custom offset to be applied during runtime if the timing as a whole is a little off.
The #readings section is composed of one line of comma separated key=value in the exact same order as the lyric lines appear in, and any parts of the string used as keys there will have the corresponding value written under it in a smaller font. This is mainly to support CJK and other writing systems that are not based in the latin alphabet so you can show reading hints under a arbitrary amount of characters. It is possible to skip characters or have spaces in the key and/or value. The keys are processed in ascending order, so any n+1 key will only be looked up after the index where n ends. This is so that lyric lines that have more than one word can still show readings under them, even in the case where they may be read differently. Every occurrence of a key must be specified in the order they appear in, so for example if the lyric line has ABC in it twice, and you set ABC=something in the corresponding reading hint line once, only the first (in case it doesn't appear after another key that is placed after the first ABC) occurrence will show the reading hint under it.
With the file created in the right format and pointed at in the config.c source file, upon rebuilding and running you may get it to start playing.
If your song needs a different font for displaying special characters (e.g. CJK or another non-latin language), or if you just want to use a font other than the default, a fontOverride= can be specified in the header as well.
This repository includes code in the public domain from the following projects, contained inside the contrib/ folder:
- nothings/stb: https://github.com/nothings/stb
- lieff/minimp3: https://github.com/lieff/minimp3
Also includes shaders written by Inigo Quilez, released under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License (original), slightly modified to work in the program's context and under the selected OpenGL version, and as such those modified shaders are available under the same license:
- shaders/am gradient.frag.glsl
- shaders/cloud gradient.frag.glsl
This application bundles the Google Noto fonts in the assets directory, without any modification. Different fonts can be used instead by replacing the files inside that folder and replacing the correspoding setting inside config.c
many Some options are available in the config.c file, and unfortunately it's not currently possible to save and retrieve
them at runtime, so every instantiation uses the default values. Some of these options control the presence or not of certain
features that some may consider undesirable, like the dynamic fill and pulse effect (which depends on the dynamic fill being
enabled). In contrast to the runtime option of showing/hiding certain elements (e.g. the reading hints), setting some of these
options to false will prevent the necessary data for their function from being computed and the resources for them allocated at
all, which makes it impossible for it to be toggled during execution. It's essentially hard-disabling parts of the application.
Although the same isn't true yet even for the config itself, these runtime toggles can have saved states in the future that live in a preferences file or the browser's local storage, and can have, e.g. the reading hints, hidden by default even though they're enabled in the config, so just hitting the shortcut will show them again. The flags in the config are meant to be unchangeable parameters for the runtime.