Change: Modernise music control logic implementation (#6839)

Rewrite of almost the entire music control logic to a more modern style, hopefully also easier to understand. The old playlist handling made it look like arcane magic, which it doesn't have to be.

- Playlists are now stored in std::vector of objects instead of arrays of bytes with magic sentinel values, that need to be rotated around all the time. Position in playlist is stored as a simple index.
- The theme song is now reserved for the title screen, it doesn't play on any of the standard playlists, but is still available for use on custom playlists.
- When the player enters/leaves the game from the main menu, the music always restarts.
- Playback state (playing or not) is kept even if music becomes unavailable due to an empty playlist (or an empty music set), so it can restart immediately if music becomes available again.
- The shuffle algorithm was changed to a standard Fisher-Yates.
- Possibly better behavior when editing a custom playlist while it's playing.
- Custom playlists should be compatible.
- Framework for supporting custom playlists with songs from multiple music sets.
This commit is contained in:
Niels Martin Hansen 2018-06-24 20:06:05 +02:00 committed by Michael Lutz
parent 889175f7ad
commit 6298b96571
3 changed files with 387 additions and 322 deletions

View File

@ -126,7 +126,8 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
IniGroup *names = ini->GetGroup("names");
IniGroup *catindex = ini->GetGroup("catindex");
IniGroup *timingtrim = ini->GetGroup("timingtrim");
for (uint i = 0, j = 1; i < lengthof(this->songinfo); i++) {
uint tracknr = 1;
for (uint i = 0; i < lengthof(this->songinfo); i++) {
const char *filename = this->files[i].filename;
if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) {
this->songinfo[i].songname[0] = '\0';
@ -175,7 +176,12 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
}
this->num_available++;
this->songinfo[i].tracknr = j++;
/* Number the theme song (if any) track 0, rest are normal */
if (i == 0) {
this->songinfo[i].tracknr = 0;
} else {
this->songinfo[i].tracknr = tracknr++;
}
item = timingtrim->GetItem(trimmed_filename, false);
if (item != NULL && !StrEmpty(item->value)) {

View File

@ -10,6 +10,7 @@
/** @file music_gui.cpp GUI for the music playback. */
#include "stdafx.h"
#include <vector>
#include "openttd.h"
#include "base_media_base.h"
#include "music/music_driver.hpp"
@ -35,247 +36,385 @@
#include "safeguards.h"
/**
* Get the name of the song.
* @param index of the song.
* @return the name of the song.
*/
static const char *GetSongName(int index)
{
return BaseMusic::GetUsedSet()->songinfo[index].songname;
}
/**
* Get the track number of the song.
* @param index of the song.
* @return the track number of the song.
*/
static int GetTrackNumber(int index)
{
return BaseMusic::GetUsedSet()->songinfo[index].tracknr;
}
struct MusicSystem {
struct PlaylistEntry : MusicSongInfo {
const MusicSet *set; ///< music set the song comes from
uint set_index; ///< index of song in set
/** The currently played song */
static byte _music_wnd_cursong = 1;
/** Whether a song is currently played */
static bool _song_is_active = false;
PlaylistEntry(const MusicSet *set, uint set_index) : MusicSongInfo(set->songinfo[set_index]), set(set), set_index(set_index) { }
bool IsValid() const { return !StrEmpty(this->songname); }
};
typedef std::vector<PlaylistEntry> Playlist;
/** Indices of the songs in the current playlist */
static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1];
enum PlaylistChoices {
PLCH_ALLMUSIC,
PLCH_OLDSTYLE,
PLCH_NEWSTYLE,
PLCH_EZYSTREET,
PLCH_CUSTOM1,
PLCH_CUSTOM2,
PLCH_THEMEONLY,
PLCH_MAX,
};
/** Indices of all songs */
static byte _playlist_all[NUM_SONGS_AVAILABLE + 1];
/** Indices of all old style songs */
static byte _playlist_old_style[NUM_SONGS_CLASS + 1];
/** Indices of all new style songs */
static byte _playlist_new_style[NUM_SONGS_CLASS + 1];
/** Indices of all ezy street songs */
static byte _playlist_ezy_street[NUM_SONGS_CLASS + 1];
Playlist active_playlist; ///< current play order of songs, including any shuffle
Playlist displayed_playlist; ///< current playlist as displayed in GUI, never in shuffled order
Playlist music_set; ///< all songs in current music set, in set order
assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1);
assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1);
PlaylistChoices selected_playlist;
/** The different playlists that can be played. */
static byte * const _playlists[] = {
_playlist_all,
_playlist_old_style,
_playlist_new_style,
_playlist_ezy_street,
_settings_client.music.custom_1,
_settings_client.music.custom_2,
void BuildPlaylists();
void ChangePlaylist(PlaylistChoices pl);
void ChangeMusicSet(const char *set_name);
void Shuffle();
void Unshuffle();
void Play();
void Stop();
void Next();
void Prev();
void CheckStatus();
bool IsPlaying() const;
bool IsShuffle() const;
PlaylistEntry GetCurrentSong() const;
bool IsCustomPlaylist() const;
void PlaylistAdd(size_t song_index);
void PlaylistRemove(size_t song_index);
void PlaylistClear();
private:
void ChangePlaylistPosition(int ofs);
int playlist_position;
void SaveCustomPlaylist(PlaylistChoices pl);
Playlist standard_playlists[PLCH_MAX];
};
/**
* Validate a playlist.
* @param playlist The playlist to validate.
* @param last The last location in the list.
*/
void ValidatePlaylist(byte *playlist, byte *last)
{
while (*playlist != 0 && playlist <= last) {
/* Song indices are saved off-by-one so 0 is "nothing". */
if (*playlist <= NUM_SONGS_AVAILABLE && !StrEmpty(GetSongName(*playlist - 1))) {
playlist++;
continue;
}
for (byte *p = playlist; *p != 0 && p <= last; p++) {
p[0] = p[1];
}
}
MusicSystem _music;
/* Make sure the list is null terminated. */
*last = 0;
}
/** Prepare the playlists */
void InitializeMusic()
/** Rebuild all playlists for the current music set */
void MusicSystem::BuildPlaylists()
{
uint j = 0;
const MusicSet *set = BaseMusic::GetUsedSet();
/* Clear current playlists */
for (size_t i = 0; i < lengthof(this->standard_playlists); ++i) this->standard_playlists[i].clear();
this->music_set.clear();
/* Build standard playlists, and a list of available music */
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
if (StrEmpty(GetSongName(i))) continue;
_playlist_all[j++] = i + 1;
}
/* Terminate the list */
_playlist_all[j] = 0;
PlaylistEntry entry(set, i);
if (!entry.IsValid()) continue;
/* Now make the 'styled' playlists */
for (uint k = 0; k < NUM_SONG_CLASSES; k++) {
j = 0;
for (uint i = 0; i < NUM_SONGS_CLASS; i++) {
int id = k * NUM_SONGS_CLASS + i + 1;
if (StrEmpty(GetSongName(id))) continue;
_playlists[k + 1][j++] = id + 1;
this->music_set.push_back(entry);
/* Add theme song to theme-only playlist */
if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(entry);
/* Don't add the theme song to standard playlists */
if (i > 0) {
this->standard_playlists[PLCH_ALLMUSIC].push_back(entry);
uint theme = (i - 1) / NUM_SONGS_CLASS;
this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(entry);
}
/* Terminate the list */
_playlists[k + 1][j] = 0;
}
ValidatePlaylist(_settings_client.music.custom_1, lastof(_settings_client.music.custom_1));
ValidatePlaylist(_settings_client.music.custom_2, lastof(_settings_client.music.custom_2));
if (BaseMusic::GetUsedSet()->num_available < _music_wnd_cursong) {
/* If there are less songs than the currently played song,
* just pause and reset to no song. */
_music_wnd_cursong = 0;
_song_is_active = false;
}
}
static void SkipToPrevSong()
{
byte *b = _cur_playlist;
byte *p = b;
byte t;
if (b[0] == 0) return; // empty playlist
do p++; while (p[0] != 0); // find the end
t = *--p; // and copy the bytes
while (p != b) {
p--;
p[1] = p[0];
}
*b = t;
_song_is_active = false;
}
static void SkipToNextSong()
{
byte *b = _cur_playlist;
byte t;
t = b[0];
if (t != 0) {
while (b[1] != 0) {
b[0] = b[1];
b++;
/* Load custom playlists
* Song index offsets are 1-based, zero indicates invalid/end-of-list value */
for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) {
if (_settings_client.music.custom_1[i] > 0) {
PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1);
if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(entry);
}
b[0] = t;
if (_settings_client.music.custom_2[i] > 0) {
PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1);
if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(entry);
}
}
}
/**
* Switch to another playlist, or reload the current one.
* @param pl Playlist to select
*/
void MusicSystem::ChangePlaylist(PlaylistChoices pl)
{
assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC);
this->displayed_playlist = this->standard_playlists[pl];
this->active_playlist = this->displayed_playlist;
this->selected_playlist = pl;
this->playlist_position = 0;
if (this->selected_playlist != PLCH_THEMEONLY) _settings_client.music.playlist = this->selected_playlist;
if (_settings_client.music.shuffle) {
this->Shuffle();
/* Shuffle() will also Play() if necessary, only start once */
} else if (_settings_client.music.playing) {
this->Play();
}
_song_is_active = false;
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
static void MusicVolumeChanged(byte new_vol)
/**
* Change to named music set, and reset playback.
* @param set_name Name of music set to select
*/
void MusicSystem::ChangeMusicSet(const char *set_name)
{
MusicDriver::GetInstance()->SetVolume(new_vol);
BaseMusic::SetSet(set_name);
this->BuildPlaylists();
this->ChangePlaylist(this->selected_playlist);
InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
}
static void DoPlaySong()
/** Enable shuffle mode and restart playback */
void MusicSystem::Shuffle()
{
char filename[MAX_PATH];
MusicSongInfo songinfo = BaseMusic::GetUsedSet()->songinfo[_music_wnd_cursong - 1]; // copy
if (FioFindFullPath(filename, lastof(filename), BASESET_DIR, songinfo.filename) == NULL) {
FioFindFullPath(filename, lastof(filename), OLD_GM_DIR, songinfo.filename);
_settings_client.music.shuffle = true;
this->active_playlist = this->displayed_playlist;
for (size_t i = 0; i < this->active_playlist.size(); i++) {
size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i);
std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]);
}
songinfo.filename = filename; // non-owned pointer
songinfo.loop = (_game_mode == GM_MENU) && (_music_wnd_cursong == 1);
MusicDriver::GetInstance()->PlaySong(songinfo);
SetWindowDirty(WC_MUSIC_WINDOW, 0);
if (_settings_client.music.playing) this->Play();
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
static void DoStopMusic()
/** Disable shuffle and restart playback */
void MusicSystem::Unshuffle()
{
_settings_client.music.shuffle = false;
this->active_playlist = this->displayed_playlist;
if (_settings_client.music.playing) this->Play();
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
/** Start/restart playback at current song */
void MusicSystem::Play()
{
/* Always set the playing flag, even if there is no music */
_settings_client.music.playing = true;
MusicDriver::GetInstance()->StopSong();
/* Make sure playlist_position is a valid index, if playlist has changed etc. */
this->ChangePlaylistPosition(0);
/* If there is no music, don't try to play it */
if (this->active_playlist.empty()) return;
MusicSongInfo song = this->active_playlist[this->playlist_position];
if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true;
MusicDriver::GetInstance()->PlaySong(song);
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
/** Stop playback and set flag that we don't intend to play music */
void MusicSystem::Stop()
{
MusicDriver::GetInstance()->StopSong();
SetWindowDirty(WC_MUSIC_WINDOW, 0);
_settings_client.music.playing = false;
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
/** Reload the active playlist data from playlist selection and shuffle setting */
static void ResetPlaylist()
/** Skip to next track */
void MusicSystem::Next()
{
uint i = 0;
uint j = 0;
this->ChangePlaylistPosition(+1);
if (_settings_client.music.playing) this->Play();
memset(_cur_playlist, 0, sizeof(_cur_playlist));
do {
/* File is the index into the file table of the music set. The play list uses 0 as 'no entry',
* so we need to subtract 1. In case of 'no entry' (file = -1), just skip adding it outright. */
int file = _playlists[_settings_client.music.playlist][i] - 1;
if (file >= 0) {
const char *filename = BaseMusic::GetUsedSet()->files[file].filename;
/* We are now checking for the existence of that file prior
* to add it to the list of available songs */
if (!StrEmpty(filename) && FioCheckFileExists(filename, BASESET_DIR)) {
_cur_playlist[j] = _playlists[_settings_client.music.playlist][i];
j++;
}
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
/** Skip to previous track */
void MusicSystem::Prev()
{
this->ChangePlaylistPosition(-1);
if (_settings_client.music.playing) this->Play();
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
}
/** Check that music is playing if it should, and that appropriate playlist is active for game/main menu */
void MusicSystem::CheckStatus()
{
if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) {
/* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */
this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist);
}
if (this->active_playlist.empty()) return;
/* If we were supposed to be playing, but music has stopped, move to next song */
if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next();
}
/** Is the player getting music right now? */
bool MusicSystem::IsPlaying() const
{
return _settings_client.music.playing && !this->active_playlist.empty();
}
/** Is shuffle mode enabled? */
bool MusicSystem::IsShuffle() const
{
return _settings_client.music.shuffle;
}
/** Return the current song, or a dummy if none */
MusicSystem::PlaylistEntry MusicSystem::GetCurrentSong() const
{
if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0);
return this->active_playlist[this->playlist_position];
}
/** Is one of the custom playlists selected? */
bool MusicSystem::IsCustomPlaylist() const
{
return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2);
}
/**
* Append a song to a custom playlist.
* Always adds to the currently active playlist.
* @param song_index Index of song in the current music set to add
*/
void MusicSystem::PlaylistAdd(size_t song_index)
{
if (!this->IsCustomPlaylist()) return;
/* Pick out song from the music set */
if (song_index >= this->music_set.size()) return;
PlaylistEntry entry = this->music_set[song_index];
/* Check for maximum length */
if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return;
/* Add it to the appropriate playlist, and the display */
this->standard_playlists[this->selected_playlist].push_back(entry);
this->displayed_playlist.push_back(entry);
/* Add it to the active playlist, if playback is shuffled select a random position to add at */
if (this->active_playlist.empty()) {
this->active_playlist.push_back(entry);
if (this->IsPlaying()) this->Play();
} else if (this->IsShuffle()) {
/* Generate a random position between 0 and n (inclusive, new length) to insert at */
size_t maxpos = this->displayed_playlist.size();
size_t newpos = InteractiveRandom() % maxpos;
this->active_playlist.insert(this->active_playlist.begin() + newpos, entry);
/* Make sure to shift up the current playback position if the song was inserted before it */
if ((int)newpos <= this->playlist_position) this->playlist_position++;
} else {
this->active_playlist.push_back(entry);
}
this->SaveCustomPlaylist(this->selected_playlist);
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
}
/**
* Remove a song from a custom playlist.
* @param song_index Index in the custom playlist to remove.
*/
void MusicSystem::PlaylistRemove(size_t song_index)
{
if (!this->IsCustomPlaylist()) return;
Playlist &pl = this->standard_playlists[this->selected_playlist];
if (song_index >= pl.size()) return;
/* Remove from "simple" playlists */
PlaylistEntry song = pl[song_index];
pl.erase(pl.begin() + song_index);
this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index);
/* Find in actual active playlist (may be shuffled) and remove,
* if it's the current song restart playback */
for (size_t i = 0; i < this->active_playlist.size(); i++) {
Playlist::iterator s2 = this->active_playlist.begin() + i;
if (s2->filename == song.filename && s2->cat_index == song.cat_index) {
this->active_playlist.erase(s2);
if ((int)i == this->playlist_position && this->IsPlaying()) this->Play();
break;
}
} while (_playlists[_settings_client.music.playlist][++i] != 0 && j < lengthof(_cur_playlist) - 1);
}
/* Do not shuffle when on the intro-start window, as the song to play has to be the original TTD Theme*/
if (_settings_client.music.shuffle && _game_mode != GM_MENU) {
i = 500;
do {
uint32 r = InteractiveRandom();
byte *a = &_cur_playlist[GB(r, 0, 5)];
byte *b = &_cur_playlist[GB(r, 8, 5)];
this->SaveCustomPlaylist(this->selected_playlist);
if (*a != 0 && *b != 0) {
byte t = *a;
*a = *b;
*b = t;
}
} while (--i);
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
}
/**
* Remove all songs from the current custom playlist.
* Effectively stops playback too.
*/
void MusicSystem::PlaylistClear()
{
if (!this->IsCustomPlaylist()) return;
this->standard_playlists[this->selected_playlist].clear();
this->ChangePlaylist(this->selected_playlist);
this->SaveCustomPlaylist(this->selected_playlist);
}
/**
* Change playlist position pointer by the given offset, making sure to keep it within valid range.
* If the playlist is empty, position is always set to 0.
* @param ofs Amount to move playlist position by.
*/
void MusicSystem::ChangePlaylistPosition(int ofs)
{
if (this->active_playlist.empty()) {
this->playlist_position = 0;
} else {
this->playlist_position += ofs;
while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size();
while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size();
}
}
static void StopMusic()
/**
* Save a custom playlist to settings after modification.
* @param pl Playlist to store back
*/
void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl)
{
_music_wnd_cursong = 0;
DoStopMusic();
_song_is_active = false;
SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
}
/** Begin playing the next song on the playlist */
static void PlayPlaylistSong()
{
if (_cur_playlist[0] == 0) {
ResetPlaylist();
/* if there is not songs in the playlist, it may indicate
* no file on the gm folder, or even no gm folder.
* Stop the playback, then */
if (_cur_playlist[0] == 0) {
_song_is_active = false;
_music_wnd_cursong = 0;
_settings_client.music.playing = false;
return;
}
byte *settings_pl;
if (pl == PLCH_CUSTOM1) {
settings_pl = _settings_client.music.custom_1;
} else if (pl == PLCH_CUSTOM2) {
settings_pl = _settings_client.music.custom_2;
} else {
return;
}
_music_wnd_cursong = _cur_playlist[0];
DoPlaySong();
_song_is_active = true;
SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
size_t num = 0;
MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST);
for (Playlist::const_iterator song = this->standard_playlists[pl].begin(); song != this->standard_playlists[pl].end(); ++song) {
/* Music set indices in the settings playlist are 1-based, 0 means unused slot */
settings_pl[num++] = (byte)song->set_index + 1;
}
}
void ResetMusic()
{
_music_wnd_cursong = 1;
DoPlaySong();
}
/**
* Check music playback status and start/stop/song-finished.
@ -283,30 +422,7 @@ void ResetMusic()
*/
void MusicLoop()
{
if (!_settings_client.music.playing && _song_is_active) {
StopMusic();
} else if (_settings_client.music.playing && !_song_is_active) {
PlayPlaylistSong();
}
if (!_song_is_active) return;
if (!MusicDriver::GetInstance()->IsSongPlaying()) {
if (_game_mode != GM_MENU) {
StopMusic();
SkipToNextSong();
PlayPlaylistSong();
} else {
ResetMusic();
}
}
}
static void SelectPlaylist(byte list)
{
_settings_client.music.playlist = list;
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
_music.CheckStatus();
}
/**
@ -316,29 +432,20 @@ static void SelectPlaylist(byte list)
void ChangeMusicSet(int index)
{
if (BaseMusic::GetIndexOfUsedSet() == index) return;
/* Resume playback after switching?
* Always if music is already playing, and also if the user is switching
* away from an empty music set.
* If the user switches away from an empty set, assume it's because they
* want to hear music now. */
bool shouldplay = _song_is_active || (BaseMusic::GetUsedSet()->num_available == 0);
StopMusic();
const char *name = BaseMusic::GetSet(index)->name;
BaseMusic::SetSet(name);
free(BaseMusic::ini_set);
BaseMusic::ini_set = stredup(name);
InitializeMusic();
ResetPlaylist();
_settings_client.music.playing = shouldplay;
InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
InvalidateWindowData(WC_MUSIC_WINDOW, 0);
InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
_music.ChangeMusicSet(name);
}
/**
* Prepare the music system for use.
* Called from \c InitializeGame
*/
void InitializeMusic()
{
_music.BuildPlaylists();
}
struct MusicTrackSelectionWindow : public Window {
MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
{
@ -395,13 +502,10 @@ struct MusicTrackSelectionWindow : public Window {
case WID_MTS_LIST_LEFT: case WID_MTS_LIST_RIGHT: {
Dimension d = {0, 0};
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
const char *song_name = GetSongName(i);
if (StrEmpty(song_name)) continue;
SetDParam(0, GetTrackNumber(i));
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
SetDParam(0, song->tracknr);
SetDParam(1, 2);
SetDParamStr(2, GetSongName(i));
SetDParamStr(2, song->songname);
Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
d.width = max(d.width, d2.width);
d.height += d2.height;
@ -421,13 +525,10 @@ struct MusicTrackSelectionWindow : public Window {
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
int y = r.top + WD_FRAMERECT_TOP;
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
const char *song_name = GetSongName(i);
if (StrEmpty(song_name)) continue;
SetDParam(0, GetTrackNumber(i));
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
SetDParam(0, song->tracknr);
SetDParam(1, 2);
SetDParamStr(2, song_name);
SetDParamStr(2, song->songname);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL;
}
@ -438,11 +539,10 @@ struct MusicTrackSelectionWindow : public Window {
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
int y = r.top + WD_FRAMERECT_TOP;
for (const byte *p = _playlists[_settings_client.music.playlist]; *p != 0; p++) {
uint i = *p - 1;
SetDParam(0, GetTrackNumber(i));
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
SetDParam(0, song->tracknr);
SetDParam(1, 2);
SetDParamStr(2, GetSongName(i));
SetDParamStr(2, song->songname);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL;
}
@ -456,42 +556,13 @@ struct MusicTrackSelectionWindow : public Window {
switch (widget) {
case WID_MTS_LIST_LEFT: { // add to playlist
int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
if (_settings_client.music.playlist < 4) return;
if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;
byte *p = _playlists[_settings_client.music.playlist];
for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
if (p[i] == 0) {
/* Find the actual song number */
for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
if (GetTrackNumber(j) == y + 1) {
p[i] = j + 1;
break;
}
}
p[i + 1] = 0;
this->SetDirty();
ResetPlaylist();
break;
}
}
_music.PlaylistAdd(y);
break;
}
case WID_MTS_LIST_RIGHT: { // remove from playlist
int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
if (_settings_client.music.playlist < 4) return;
if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;
byte *p = _playlists[_settings_client.music.playlist];
for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
p[i] = p[i + 1];
}
this->SetDirty();
ResetPlaylist();
_music.PlaylistRemove(y);
break;
}
@ -503,17 +574,12 @@ struct MusicTrackSelectionWindow : public Window {
}
case WID_MTS_CLEAR: // clear
for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0;
this->SetDirty();
StopMusic();
ResetPlaylist();
_music.PlaylistClear();
break;
case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
SelectPlaylist(widget - WID_MTS_ALL);
StopMusic();
ResetPlaylist();
_music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL));
break;
}
}
@ -628,8 +694,8 @@ struct MusicWindow : public Window {
case WID_M_TRACK_NAME: {
Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE);
for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
SetDParamStr(0, GetSongName(i));
for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) {
SetDParamStr(0, song->songname);
d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME));
}
d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
@ -655,8 +721,8 @@ struct MusicWindow : public Window {
break;
}
StringID str = STR_MUSIC_TRACK_NONE;
if (_song_is_active != 0 && _music_wnd_cursong != 0) {
SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
if (_music.IsPlaying()) {
SetDParam(0, _music.GetCurrentSong().tracknr);
SetDParam(1, 2);
str = STR_MUSIC_TRACK_DIGIT;
}
@ -669,9 +735,9 @@ struct MusicWindow : public Window {
StringID str = STR_MUSIC_TITLE_NONE;
if (BaseMusic::GetUsedSet()->num_available == 0) {
str = STR_MUSIC_TITLE_NOMUSIC;
} else if (_song_is_active != 0 && _music_wnd_cursong != 0) {
} else if (_music.IsPlaying()) {
str = STR_MUSIC_TITLE_NAME;
SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
SetDParamStr(0, _music.GetCurrentSong().songname);
}
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
break;
@ -711,23 +777,19 @@ struct MusicWindow : public Window {
{
switch (widget) {
case WID_M_PREV: // skip to prev
if (!_song_is_active) return;
SkipToPrevSong();
this->SetDirty();
_music.Prev();
break;
case WID_M_NEXT: // skip to next
if (!_song_is_active) return;
SkipToNextSong();
this->SetDirty();
_music.Next();
break;
case WID_M_STOP: // stop playing
_settings_client.music.playing = false;
_music.Stop();
break;
case WID_M_PLAY: // start playing
_settings_client.music.playing = true;
_music.Play();
break;
case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders
@ -742,7 +804,7 @@ struct MusicWindow : public Window {
if (new_vol < 3) new_vol = 0;
if (new_vol != *vol) {
*vol = new_vol;
if (widget == WID_M_MUSIC_VOL) MusicVolumeChanged(new_vol);
if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(new_vol);
this->SetDirty();
}
@ -751,12 +813,13 @@ struct MusicWindow : public Window {
}
case WID_M_SHUFFLE: // toggle shuffle
_settings_client.music.shuffle ^= 1;
this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle);
if (_music.IsShuffle()) {
_music.Unshuffle();
} else {
_music.Shuffle();
}
this->SetWidgetLoweredState(WID_M_SHUFFLE, _music.IsShuffle());
this->SetWidgetDirty(WID_M_SHUFFLE);
StopMusic();
ResetPlaylist();
this->SetDirty();
break;
case WID_M_PROGRAMME: // show track selection
@ -765,10 +828,7 @@ struct MusicWindow : public Window {
case WID_M_ALL: case WID_M_OLD: case WID_M_NEW:
case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist
SelectPlaylist(widget - WID_M_ALL);
StopMusic();
ResetPlaylist();
this->SetDirty();
_music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL));
break;
}
}

View File

@ -341,8 +341,7 @@ static void LoadIntroGame(bool load_newgrfs = true)
CheckForMissingGlyphs();
/* Play main theme */
if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic();
MusicLoop(); // ensure music is correct
}
void MakeNewgameSettingsLive()