Changeset - r22926:82779a967ee2
[Not reviewed]
master
0 3 0
Niels Martin Hansen - 6 years ago 2018-06-24 18:06:05
nielsm@indvikleren.dk
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.
3 files changed with 383 insertions and 318 deletions:
0 comments (0 inline, 0 general)
src/music.cpp
Show inline comments
 
@@ -126,7 +126,8 @@ bool MusicSet::FillSetDetails(IniFile *i
 
		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 *i
 
			}
 
			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)) {
src/music_gui.cpp
Show inline comments
 
@@ -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;
 
}
 

	
 
struct MusicSystem {
 
	struct PlaylistEntry : MusicSongInfo {
 
		const MusicSet *set;  ///< music set the song comes from
 
		uint set_index;        ///< index of song in set
 

	
 
		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;
 

	
 
/**
 
 * 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;
 
}
 
	enum PlaylistChoices {
 
		PLCH_ALLMUSIC,
 
		PLCH_OLDSTYLE,
 
		PLCH_NEWSTYLE,
 
		PLCH_EZYSTREET,
 
		PLCH_CUSTOM1,
 
		PLCH_CUSTOM2,
 
		PLCH_THEMEONLY,
 
		PLCH_MAX,
 
	};
 

	
 
	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
 

	
 
	PlaylistChoices selected_playlist;
 

	
 
/** The currently played song */
 
static byte _music_wnd_cursong = 1;
 
/** Whether a song is currently played */
 
static bool _song_is_active = false;
 
	void BuildPlaylists();
 

	
 
/** Indices of the songs in the current playlist */
 
static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1];
 
	void ChangePlaylist(PlaylistChoices pl);
 
	void ChangeMusicSet(const char *set_name);
 
	void Shuffle();
 
	void Unshuffle();
 

	
 
	void Play();
 
	void Stop();
 
	void Next();
 
	void Prev();
 
	void CheckStatus();
 

	
 
/** 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];
 
	bool IsPlaying() const;
 
	bool IsShuffle() const;
 
	PlaylistEntry GetCurrentSong() const;
 

	
 
assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1);
 
assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1);
 
	bool IsCustomPlaylist() const;
 
	void PlaylistAdd(size_t song_index);
 
	void PlaylistRemove(size_t song_index);
 
	void PlaylistClear();
 

	
 
/** 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,
 
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)
 
MusicSystem _music;
 

	
 

	
 
/** Rebuild all playlists for the current music set */
 
void MusicSystem::BuildPlaylists()
 
{
 
	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];
 
	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++) {
 
		PlaylistEntry entry(set, i);
 
		if (!entry.IsValid()) continue;
 

	
 
		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);
 
		}
 
	}
 

	
 
	/* Make sure the list is null terminated. */
 
	*last = 0;
 
}
 

	
 
/** Prepare the playlists */
 
void InitializeMusic()
 
{
 
	uint j = 0;
 
	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;
 

	
 
	/* 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;
 
	/* 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);
 
		}
 
		/* 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;
 
		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);
 
		}
 
	}
 
}
 

	
 
static void SkipToPrevSong()
 
/**
 
 * Switch to another playlist, or reload the current one.
 
 * @param pl Playlist to select
 
 */
 
void MusicSystem::ChangePlaylist(PlaylistChoices pl)
 
{
 
	byte *b = _cur_playlist;
 
	byte *p = b;
 
	byte t;
 
	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 (b[0] == 0) return; // empty 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();
 
	}
 

	
 
	InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
 
	InvalidateWindowData(WC_MUSIC_WINDOW, 0);
 
}
 

	
 
	do p++; while (p[0] != 0); // find the end
 
/**
 
 * Change to named music set, and reset playback.
 
 * @param set_name Name of music set to select
 
 */
 
void MusicSystem::ChangeMusicSet(const char *set_name)
 
{
 
	BaseMusic::SetSet(set_name);
 

	
 
	this->BuildPlaylists();
 
	this->ChangePlaylist(this->selected_playlist);
 

	
 
	InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
 
}
 

	
 
	t = *--p; // and copy the bytes
 
	while (p != b) {
 
		p--;
 
		p[1] = p[0];
 
/** Enable shuffle mode and restart playback */
 
void MusicSystem::Shuffle()
 
{
 
	_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]);
 
	}
 
	*b = t;
 

	
 
	_song_is_active = false;
 
	if (_settings_client.music.playing) this->Play();
 

	
 
	InvalidateWindowData(WC_MUSIC_WINDOW, 0);
 
}
 

	
 
static void SkipToNextSong()
 
/** Disable shuffle and restart playback */
 
void MusicSystem::Unshuffle()
 
{
 
	byte *b = _cur_playlist;
 
	byte t;
 
	_settings_client.music.shuffle = false;
 
	this->active_playlist = this->displayed_playlist;
 

	
 
	if (_settings_client.music.playing) this->Play();
 

	
 
	InvalidateWindowData(WC_MUSIC_WINDOW, 0);
 
}
 

	
 
	t = b[0];
 
	if (t != 0) {
 
		while (b[1] != 0) {
 
			b[0] = b[1];
 
			b++;
 
		}
 
		b[0] = t;
 
	}
 
/** 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);
 

	
 
	_song_is_active = false;
 
	/* 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);
 
}
 

	
 
static void MusicVolumeChanged(byte new_vol)
 
/** Stop playback and set flag that we don't intend to play music */
 
void MusicSystem::Stop()
 
{
 
	MusicDriver::GetInstance()->StopSong();
 
	_settings_client.music.playing = false;
 

	
 
	InvalidateWindowData(WC_MUSIC_WINDOW, 0);
 
}
 

	
 
/** Skip to next track */
 
void MusicSystem::Next()
 
{
 
	MusicDriver::GetInstance()->SetVolume(new_vol);
 
	this->ChangePlaylistPosition(+1);
 
	if (_settings_client.music.playing) this->Play();
 

	
 
	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();
 
}
 

	
 
static void DoPlaySong()
 
/** Is the player getting music right now? */
 
bool MusicSystem::IsPlaying() const
 
{
 
	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);
 
	}
 
	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);
 
	return _settings_client.music.playing && !this->active_playlist.empty();
 
}
 

	
 
/** Is shuffle mode enabled? */
 
bool MusicSystem::IsShuffle() const
 
{
 
	return _settings_client.music.shuffle;
 
}
 

	
 
static void DoStopMusic()
 
/** Return the current song, or a dummy if none */
 
MusicSystem::PlaylistEntry MusicSystem::GetCurrentSong() const
 
{
 
	MusicDriver::GetInstance()->StopSong();
 
	SetWindowDirty(WC_MUSIC_WINDOW, 0);
 
	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);
 
}
 

	
 
/** Reload the active playlist data from playlist selection and shuffle setting */
 
static void ResetPlaylist()
 
/**
 
 * 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)
 
{
 
	uint i = 0;
 
	uint j = 0;
 
	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;
 

	
 
	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++;
 
			}
 
	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);
 
	}
 

	
 
	this->SaveCustomPlaylist(this->selected_playlist);
 

	
 
	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;
 

	
 
	/* 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->standard_playlists[this->selected_playlist].clear();
 
	this->ChangePlaylist(this->selected_playlist);
 

	
 
	this->SaveCustomPlaylist(this->selected_playlist);
 
}
 

	
 
			if (*a != 0 && *b != 0) {
 
				byte t = *a;
 
				*a = *b;
 
				*b = t;
 
			}
 
		} while (--i);
 
/**
 
 * 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);
 
	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;
 
	}
 

	
 
	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;
 
	}
 
}
 

	
 
/** 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;
 
		}
 
	}
 
	_music_wnd_cursong = _cur_playlist[0];
 
	DoPlaySong();
 
	_song_is_active = true;
 

	
 
	SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
 
}
 

	
 
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,28 +432,19 @@ 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;
 
	_music.ChangeMusicSet(name);
 
}
 

	
 
	const char *name = BaseMusic::GetSet(index)->name;
 
	BaseMusic::SetSet(name);
 
	free(BaseMusic::ini_set);
 
	BaseMusic::ini_set = stredup(name);
 
/**
 
 * Prepare the music system for use.
 
 * Called from \c InitializeGame
 
 */
 
void InitializeMusic()
 
{
 
	_music.BuildPlaylists();
 
}
 

	
 
	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);
 
}
 

	
 
struct MusicTrackSelectionWindow : public Window {
 
	MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
 
@@ -395,13 +502,10 @@ struct MusicTrackSelectionWindow : publi
 
			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 : publi
 
				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 : publi
 
				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 : publi
 
		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 : publi
 
			}
 

	
 
			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;
 
		}
 
	}
src/openttd.cpp
Show inline comments
 
@@ -341,8 +341,7 @@ static void LoadIntroGame(bool load_newg
 

	
 
	CheckForMissingGlyphs();
 

	
 
	/* Play main theme */
 
	if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic();
 
	MusicLoop(); // ensure music is correct
 
}
 

	
 
void MakeNewgameSettingsLive()
0 comments (0 inline, 0 general)