Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
554 changes: 300 additions & 254 deletions Encore/src/menus/SongSelectMenu.cpp

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions Encore/src/menus/SongSelectMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,32 @@
#ifndef SONGSELECTMENU_H
#define SONGSELECTMENU_H
#include "OvershellMenu.h"
#include <filesystem>
#include <map>

class SongSelectMenu : public OvershellMenu {
public:
SongSelectMenu() = default;
~SongSelectMenu() override = default;
~SongSelectMenu() override;
void KeyboardInputCallback(int key, int scancode, int action, int mods) override;
void ControllerInputCallback(int joypadID, GLFWgamepadstate state) override;
void Draw() override;
void Load() override;
void Unload();
void UpdatePreviewVolume(double currentTime);

private:
double previewStartTime = 0.0;
float currentPreviewVolume = 0.0f;
enum class PreviewState { FadeIn, Playing, FadeOut, Pause } previewState = PreviewState::FadeIn;
const float fadeDuration = 2.5f;
const float previewPlayDuration = 30.0f;
const float pauseDuration = 2.5f;
double phaseStartTime = 0.0;
int pendingSongID = -1;
double selectionTime = 0.0;
};



#endif //SONGSELECTMENU_H
#endif //SONGSELECTMENU_H
6 changes: 6 additions & 0 deletions Encore/src/song/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ void Encore::AudioManager::StopPlayback(unsigned int handle) {
CHECK_BASS_ERROR2();
}

void Encore::AudioManager::SetAudioStreamPosition(unsigned int handle, double time) {
int positionBytes = BASS_ChannelSeconds2Bytes(handle, time);
BASS_ChannelSetPosition(handle, positionBytes, BASS_POS_BYTE);
CHECK_BASS_ERROR2();
}

void Encore::AudioManager::loadSample(const std::string &path, const std::string &name) {
HSAMPLE sample = BASS_SampleLoad(false, path.c_str(), 0, 0, 1, 0);
if (sample) {
Expand Down
1 change: 1 addition & 0 deletions Encore/src/song/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace Encore {
void UpdateAudioStreamVolumes();
AudioStream* GetAudioStreamByInstrument(int instrument);
static void SetAudioStreamVolume(unsigned int handle, float volume);
static void SetAudioStreamPosition(unsigned int handle, double time);
static void BeginPlayback(unsigned int handle);
static void StopPlayback(unsigned int handle);

Expand Down
32 changes: 31 additions & 1 deletion Encore/src/song/song.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ class Song {
bool ini = false;
smf::MidiFile midiFile;
void LoadAudioINI(std::filesystem::path songPath);

float previewStartTime = 0.0f;
void LoadAudio(std::filesystem::path jsonPath) {
std::ifstream ifs(jsonPath);

Expand Down Expand Up @@ -406,6 +406,36 @@ class Song {
}
}
}
if (document.HasMember("preview_start_time") && document["preview_start_time"].IsInt()) {
previewStartTime = static_cast<float>(document["preview_start_time"].GetInt());
}
}
if (document.HasMember("stems") && document["stems"].IsObject()) {
for (auto &path : document["stems"].GetObject()) {
std::string stem = std::string(path.name.GetString());
int partIndex = -1;
if (stem == "drums") partIndex = PartDrums;
else if (stem == "bass") partIndex = PartBass;
else if (stem == "lead") partIndex = PartGuitar;
else if (stem == "vocals") partIndex = PartVocals;
else if (stem == "backing") partIndex = 5;

if (path.value.IsString()) {
std::filesystem::path stemPath = jsonPath.parent_path() / path.value.GetString();
if (std::filesystem::exists(stemPath)) {
stemsPath.push_back({ stemPath.string(), partIndex });
}
} else if (path.value.IsArray()) {
for (auto &path2 : path.value.GetArray()) {
if (path2.IsString()) {
std::filesystem::path stemPath = jsonPath.parent_path() / path2.GetString();
if (std::filesystem::exists(stemPath)) {
stemsPath.push_back({ stemPath.string(), partIndex });
}
}
}
}
}
}
if (charters.empty()) {
charters.push_back("Unknown Charter");
Expand Down
173 changes: 128 additions & 45 deletions Encore/src/song/songlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@

#include <set>

#include <algorithm>
#include <nlohmann/json.hpp>
#include <fstream>
#include "util/binary.h"

using json = nlohmann::json;
// sorting
void SongList::Clear() {
listMenuEntries.clear();
Expand Down Expand Up @@ -86,7 +90,6 @@ void SongList::sortList(SortType sortType) {
std::sort(songs.begin(), songs.end(), sortSource);
break;
case SortType::Length:
std::sort(songs.begin(), songs.end(), sortTitle);
std::sort(songs.begin(), songs.end(), sortLen);
break;
case SortType::Year:
Expand All @@ -99,7 +102,11 @@ void SongList::sortList(SortType sortType) {
}

void SongList::sortList(SortType sortType, int &selectedSong) {
Song &curSong = songs[selectedSong];
Song curSong;
bool hasCurrentSong = selectedSong >= 0 && selectedSong < songs.size();
if (hasCurrentSong) {
curSong = songs[selectedSong];
}
selectedSong = 0;
switch (sortType) {
case SortType::Title:
Expand All @@ -114,7 +121,6 @@ void SongList::sortList(SortType sortType, int &selectedSong) {
std::sort(songs.begin(), songs.end(), sortSource);
break;
case SortType::Length:
std::sort(songs.begin(), songs.end(), sortTitle);
std::sort(songs.begin(), songs.end(), sortLen);
break;
case SortType::Year:
Expand All @@ -123,10 +129,12 @@ void SongList::sortList(SortType sortType, int &selectedSong) {
break;
default:;
}
for (int i = 0; i < songs.size(); i++) {
if (songs[i].artist == curSong.artist && songs[i].title == curSong.title) {
selectedSong = i;
break;
if (hasCurrentSong) {
for (size_t i = 0; i < songs.size(); i++) {
if (songs[i].artist == curSong.artist && songs[i].title == curSong.title) {
selectedSong = i;
break;
}
}
}
listMenuEntries = GenerateSongEntriesWithHeaders(songs, sortType);
Expand Down Expand Up @@ -180,82 +188,157 @@ void SongList::ScanSongs(const std::vector<std::filesystem::path> &songsFolder)
}

directoryCount++;
if (std::filesystem::exists(entry.path() / "info.json")) {
Song song;
song.LoadSong(entry.path() / "info.json");
songs.push_back(std::move(song));
songCount++;
Song song;
std::filesystem::path infoPath = entry.path() / "info.json";
if (std::filesystem::exists(infoPath)) {
song.LoadSong(infoPath);
try {
json infoData;
std::ifstream infoFile(infoPath);
infoFile >> infoData;
infoFile.close();

if (infoData.contains("source") && infoData["source"].is_string()) {
song.source = infoData["source"].get<std::string>();
if (song.source.empty()) {
song.source = "Unknown Source";
}
} else {
song.source = "Unknown Source";
}

if (infoData.contains("release_year") && infoData["release_year"].is_string()) {
song.releaseYear = infoData["release_year"].get<std::string>();
if (song.releaseYear.empty()) {
song.releaseYear = "Unknown Year";
}
} else {
song.releaseYear = "Unknown Year";
}

if (infoData.contains("preview_start_time") && infoData["preview_start_time"].is_number_integer()) {
song.previewStartTime = infoData["preview_start_time"].get<int>();
} else {
song.previewStartTime = 3000;
}

} catch (const std::exception& e) {
song.source = "Unknown Source";
song.releaseYear = "Unknown Year";
song.previewStartTime = 500;
}
} else if (std::filesystem::exists(entry.path() / "song.ini")) {
Song song;

song.songInfoPath = (entry.path() / "song.ini").string();
song.songDir = entry.path().string();
song.LoadSongIni(entry.path());
song.ini = true;
songs.push_back(std::move(song));
songCount++;
if (std::filesystem::exists(infoPath)) {
try {
json infoData;
std::ifstream infoFile(infoPath);
infoFile >> infoData;
infoFile.close();

if (infoData.contains("source") && infoData["source"].is_string()) {
song.source = infoData["source"].get<std::string>();
if (song.source.empty()) {
song.source = "Unknown Source";
}
} else {
song.source = "Unknown Source";
}

if (infoData.contains("release_year") && infoData["release_year"].is_string()) {
song.releaseYear = infoData["release_year"].get<std::string>();
if (song.releaseYear.empty()) {
song.releaseYear = "Unknown Year";
}
} else {
song.releaseYear = "Unknown Year";
}

if (infoData.contains("preview_start_time") && infoData["preview_start_time"].is_number_integer()) {
song.previewStartTime = infoData["preview_start_time"].get<int>();
} else {
song.previewStartTime = 500;
}

Encore::EncoreLog(LOG_INFO, TextFormat("CACHE: Read metadata for INI song %s - %s from %s", song.title.c_str(), song.artist.c_str(), infoPath.string().c_str()));
} catch (const std::exception& e) {
Encore::EncoreLog(LOG_ERROR, TextFormat("CACHE: Failed to read metadata for INI song %s - %s from %s: %s", song.title.c_str(), song.artist.c_str(), infoPath.string().c_str(), e.what()));
song.source = "Unknown Source";
song.releaseYear = "Unknown Year";
song.previewStartTime = 500;
}
} else {
song.source = "Unknown Source";
song.releaseYear = "Unknown Year";
song.previewStartTime = 500;
Encore::EncoreLog(LOG_INFO, TextFormat("CACHE: No info.json for INI song %s - %s, using default metadata", song.title.c_str(), song.artist.c_str()));
}
} else {
continue;
}

songs.push_back(std::move(song));
songCount++;
}
}

Encore::EncoreLog(LOG_INFO, "CACHE: Rewriting song cache");
WriteCache();
}

std::string GetLengthHeader(int length) {
if (length < 60) return "< 1:00";
if (length < 120) return "1:00-2:00";
if (length < 180) return "2:00-3:00";
if (length < 240) return "3:00-4:00";
if (length < 300) return "4:00-5:00";
return "5:00+";
}

std::vector<ListMenuEntry> SongList::GenerateSongEntriesWithHeaders(
const std::vector<Song> &songs, SortType sortType
) {
std::vector<ListMenuEntry> songEntries;
std::string currentHeader = "";
int pos = 0;
for (int i = 0; i < songs.size(); i++) {
for (size_t i = 0; i < songs.size(); i++) {
const Song &song = songs[i];
std::string header;
switch (sortType) {
case SortType::Title: {
std::string title = removeArticle(TextToLower(song.title.c_str()));
if (toupper(title[0]) != currentHeader[0]) {
currentHeader = toupper(title[0]);
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
header = title.empty() ? "#" : std::string(1, toupper(title[0]));
break;
}
case SortType::Artist: {
std::string artist = removeArticle(song.artist);
if (artist != currentHeader) {
currentHeader = song.artist;
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
header = artist.empty() ? "#" : artist;
break;
}
case SortType::Source: {
std::string source = removeArticle(song.source);
if (source != currentHeader) {
currentHeader = song.source;
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
header = source.empty() ? "Unknown" : source;
break;
}
case SortType::Length: {
if (std::to_string(song.length) != currentHeader) {
currentHeader = std::to_string(song.length);
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
header = GetLengthHeader(song.length);
break;
}
case SortType::Year: {
std::string year = removeArticle(song.releaseYear);
if (year != currentHeader) {
currentHeader = song.releaseYear;
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
header = song.releaseYear.empty() ? "Unknown Year" : song.releaseYear;
break;
}
default:;
default:
header = "#";
break;
}
if (header != currentHeader) {
currentHeader = header;
songEntries.emplace_back(true, 0, currentHeader, false);
pos++;
}
songEntries.emplace_back(false, i, "", false);
pos++;
Expand Down Expand Up @@ -404,4 +487,4 @@ void SongList::LoadCache(const std::vector<std::filesystem::path> &songsFolder)

// ScanSongs(songsFolder);
sortList(SortType::Title);
}
}
Loading