OpenTTD Source 20241224-master-gf74b0cf984
music_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "openttd.h"
12#include "base_media_base.h"
14#include "window_gui.h"
15#include "strings_func.h"
16#include "window_func.h"
17#include "sound_func.h"
18#include "gfx_func.h"
19#include "zoom_func.h"
20#include "core/random_func.hpp"
21#include "core/mem_func.hpp"
22#include "error.h"
24#include "string_func.h"
25#include "settings_type.h"
26#include "settings_gui.h"
27#include "dropdown_func.h"
28#include "dropdown_type.h"
29#include "slider_func.h"
30#include "mixer.h"
31
33
34#include "table/strings.h"
35#include "table/sprites.h"
36
37#include "safeguards.h"
38
39
42 const MusicSet *set;
43 uint set_index;
44
46 bool IsValid() const { return !this->songname.empty(); }
47 };
48 typedef std::vector<PlaylistEntry> Playlist;
49
50 enum PlaylistChoices {
51 PLCH_ALLMUSIC,
52 PLCH_OLDSTYLE,
53 PLCH_NEWSTYLE,
54 PLCH_EZYSTREET,
55 PLCH_CUSTOM1,
56 PLCH_CUSTOM2,
57 PLCH_THEMEONLY,
58 PLCH_MAX,
59 };
60
61 Playlist active_playlist;
63 Playlist music_set;
64
65 PlaylistChoices selected_playlist;
66
67 void BuildPlaylists();
68
69 void ChangePlaylist(PlaylistChoices pl);
70 void ChangeMusicSet(const std::string &set_name);
71 void Shuffle();
72 void Unshuffle();
73
74 void Play();
75 void Stop();
76 void Next();
77 void Prev();
78 void CheckStatus();
79
80 bool IsPlaying() const;
81 bool IsShuffle() const;
83
84 bool IsCustomPlaylist() const;
85 void PlaylistAdd(size_t song_index);
86 void PlaylistRemove(size_t song_index);
87 void PlaylistClear();
88
89private:
90 uint GetSetIndex();
91 void SetPositionBySetIndex(uint set_index);
92 void ChangePlaylistPosition(int ofs);
93 int playlist_position;
94
95 void SaveCustomPlaylist(PlaylistChoices pl);
96
97 Playlist standard_playlists[PLCH_MAX];
98};
99
100MusicSystem _music;
101
102
105{
106 const MusicSet *set = BaseMusic::GetUsedSet();
107
108 /* Clear current playlists */
109 for (auto &playlist : this->standard_playlists) playlist.clear();
110 this->music_set.clear();
111
112 /* Build standard playlists, and a list of available music */
113 for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
114 PlaylistEntry entry(set, i);
115 if (!entry.IsValid()) continue;
116
117 this->music_set.push_back(entry);
118
119 /* Add theme song to theme-only playlist */
120 if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(entry);
121
122 /* Don't add the theme song to standard playlists */
123 if (i > 0) {
124 this->standard_playlists[PLCH_ALLMUSIC].push_back(entry);
125 uint theme = (i - 1) / NUM_SONGS_CLASS;
126 this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(entry);
127 }
128 }
129
130 /* Load custom playlists
131 * Song index offsets are 1-based, zero indicates invalid/end-of-list value */
132 for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) {
134 PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1);
135 if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(entry);
136 }
138 PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1);
139 if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(entry);
140 }
141 }
142}
143
148void MusicSystem::ChangePlaylist(PlaylistChoices pl)
149{
150 assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC);
151
152 if (pl != PLCH_THEMEONLY) _settings_client.music.playlist = pl;
153
154 if (_game_mode != GM_MENU || pl == PLCH_THEMEONLY) {
155 this->displayed_playlist = this->standard_playlists[pl];
157 this->selected_playlist = pl;
158 this->playlist_position = 0;
159
161 if (_settings_client.music.playing) this->Play();
162 }
163
166}
167
172void MusicSystem::ChangeMusicSet(const std::string &set_name)
173{
174 BaseMusic::SetSetByName(set_name);
175 BaseMusic::ini_set = set_name;
176
177 this->BuildPlaylists();
178 this->ChangePlaylist(this->selected_playlist);
179
183}
184
190{
191 auto it = std::ranges::find(this->active_playlist, set_index, &PlaylistEntry::set_index);
192 if (it != std::end(this->active_playlist)) this->playlist_position = std::distance(std::begin(this->active_playlist), it);
193}
194
200{
201 return static_cast<size_t>(this->playlist_position) < this->active_playlist.size()
202 ? this->active_playlist[this->playlist_position].set_index
203 : UINT_MAX;
204}
205
210{
212
213 uint set_index = this->GetSetIndex();
215 for (size_t i = 0; i < this->active_playlist.size(); i++) {
216 size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i);
217 std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]);
218 }
219 this->SetPositionBySetIndex(set_index);
220
223}
224
229{
231
232 uint set_index = this->GetSetIndex();
234 this->SetPositionBySetIndex(set_index);
235
238}
239
242{
243 /* Always set the playing flag, even if there is no music */
246 /* Make sure playlist_position is a valid index, if playlist has changed etc. */
247 this->ChangePlaylistPosition(0);
248
249 /* If there is no music, don't try to play it */
250 if (this->active_playlist.empty()) return;
251
252 MusicSongInfo song = this->active_playlist[this->playlist_position];
253 if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true;
255
257}
258
267
276
285
288{
289 if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) {
290 /* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */
291 this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist);
292 }
293 if (this->active_playlist.empty()) return;
294 /* If we were supposed to be playing, but music has stopped, move to next song */
295 if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next();
296}
297
300{
301 return _settings_client.music.playing && !this->active_playlist.empty();
302}
303
306{
308}
309
312{
313 if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0);
314 return this->active_playlist[this->playlist_position];
315}
316
319{
320 return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2);
321}
322
328void MusicSystem::PlaylistAdd(size_t song_index)
329{
330 if (!this->IsCustomPlaylist()) return;
331
332 /* Pick out song from the music set */
333 if (song_index >= this->music_set.size()) return;
334 PlaylistEntry entry = this->music_set[song_index];
335
336 /* Check for maximum length */
337 if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return;
338
339 /* Add it to the appropriate playlist, and the display */
340 this->standard_playlists[this->selected_playlist].push_back(entry);
341 this->displayed_playlist.push_back(entry);
342
343 /* Add it to the active playlist, if playback is shuffled select a random position to add at */
344 if (this->active_playlist.empty()) {
345 this->active_playlist.push_back(entry);
346 if (this->IsPlaying()) this->Play();
347 } else if (this->IsShuffle()) {
348 /* Generate a random position between 0 and n (inclusive, new length) to insert at */
349 size_t maxpos = this->displayed_playlist.size();
350 size_t newpos = InteractiveRandom() % maxpos;
351 this->active_playlist.insert(this->active_playlist.begin() + newpos, entry);
352 /* Make sure to shift up the current playback position if the song was inserted before it */
353 if ((int)newpos <= this->playlist_position) this->playlist_position++;
354 } else {
355 this->active_playlist.push_back(entry);
356 }
357
358 this->SaveCustomPlaylist(this->selected_playlist);
359
361}
362
367void MusicSystem::PlaylistRemove(size_t song_index)
368{
369 if (!this->IsCustomPlaylist()) return;
370
371 Playlist &pl = this->standard_playlists[this->selected_playlist];
372 if (song_index >= pl.size()) return;
373
374 /* Remove from "simple" playlists */
375 PlaylistEntry song = pl[song_index];
376 pl.erase(pl.begin() + song_index);
377 this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index);
378
379 /* Find in actual active playlist (may be shuffled) and remove,
380 * if it's the current song restart playback */
381 for (size_t i = 0; i < this->active_playlist.size(); i++) {
382 Playlist::iterator s2 = this->active_playlist.begin() + i;
383 if (s2->filename == song.filename && s2->cat_index == song.cat_index) {
384 this->active_playlist.erase(s2);
385 if ((int)i == this->playlist_position && this->IsPlaying()) this->Play();
386 break;
387 }
388 }
389
390 this->SaveCustomPlaylist(this->selected_playlist);
391
393}
394
400{
401 if (!this->IsCustomPlaylist()) return;
402
403 this->standard_playlists[this->selected_playlist].clear();
404 this->ChangePlaylist(this->selected_playlist);
405
406 this->SaveCustomPlaylist(this->selected_playlist);
407}
408
415{
416 if (this->active_playlist.empty()) {
417 this->playlist_position = 0;
418 } else {
419 this->playlist_position += ofs;
420 while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size();
421 while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size();
422 }
423}
424
429void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl)
430{
431 uint8_t *settings_pl;
432 if (pl == PLCH_CUSTOM1) {
433 settings_pl = _settings_client.music.custom_1;
434 } else if (pl == PLCH_CUSTOM2) {
435 settings_pl = _settings_client.music.custom_2;
436 } else {
437 return;
438 }
439
440 size_t num = 0;
441 MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST);
442
443 for (const auto &song : this->standard_playlists[pl]) {
444 /* Music set indices in the settings playlist are 1-based, 0 means unused slot */
445 settings_pl[num++] = (uint8_t)song.set_index + 1;
446 }
447}
448
449
455{
456 _music.CheckStatus();
457}
458
463void ChangeMusicSet(int index)
464{
465 if (BaseMusic::GetIndexOfUsedSet() == index) return;
466 _music.ChangeMusicSet(BaseMusic::GetSet(index)->name);
467}
468
474{
475 _music.BuildPlaylists();
476}
477
478
481 {
482 this->InitNested(number);
487 }
488
489 void SetStringParameters(WidgetID widget) const override
490 {
491 switch (widget) {
492 case WID_MTS_PLAYLIST:
494 break;
495 case WID_MTS_CAPTION:
497 break;
498 }
499 }
500
506 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
507 {
508 if (!gui_scope) return;
509 for (int i = 0; i < 6; i++) {
511 }
513
514 if (data == 1) {
515 this->ReInit();
516 } else {
517 this->SetDirty();
518 }
519 }
520
522 {
523 switch (widget) {
524 case WID_MTS_PLAYLIST: {
525 Dimension d = {0, 0};
526
527 for (int i = 0; i < 6; i++) {
530 }
531 d.width += padding.width;
532 d.height += padding.height;
533 size = maxdim(size, d);
534 break;
535 }
536
538 Dimension d = {0, 0};
539
540 for (const auto &song : _music.music_set) {
541 SetDParam(0, song.tracknr);
542 SetDParam(1, 2);
543 SetDParamStr(2, song.songname);
545 d.width = std::max(d.width, d2.width);
546 d.height += d2.height;
547 }
548 d.width += padding.width;
549 d.height += padding.height;
550 size = maxdim(size, d);
551 break;
552 }
553 }
554 }
555
556 void DrawWidget(const Rect &r, WidgetID widget) const override
557 {
558 switch (widget) {
559 case WID_MTS_LIST_LEFT: {
561
562 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
563 for (const auto &song : _music.music_set) {
564 SetDParam(0, song.tracknr);
565 SetDParam(1, 2);
566 SetDParamStr(2, song.songname);
569 }
570 break;
571 }
572
573 case WID_MTS_LIST_RIGHT: {
575
576 Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
577 for (const auto &song : _music.active_playlist) {
578 SetDParam(0, song.tracknr);
579 SetDParam(1, 2);
580 SetDParamStr(2, song.songname);
583 }
584 break;
585 }
586 }
587 }
588
589 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
590 {
591 switch (widget) {
592 case WID_MTS_LIST_LEFT: { // add to playlist
593 int y = this->GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL));
594 _music.PlaylistAdd(y);
595 break;
596 }
597
598 case WID_MTS_LIST_RIGHT: { // remove from playlist
599 int y = this->GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL));
600 _music.PlaylistRemove(y);
601 break;
602 }
603
604 case WID_MTS_MUSICSET: {
605 int selected = 0;
606 ShowDropDownList(this, BuildSetDropDownList<BaseMusic>(&selected), selected, widget);
607 break;
608 }
609
610 case WID_MTS_CLEAR: // clear
611 _music.PlaylistClear();
612 break;
613
614 case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
615 case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
616 _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL));
617 break;
618 }
619 }
620
621 void OnDropdownSelect(WidgetID widget, int index) override
622 {
623 switch (widget) {
624 case WID_MTS_MUSICSET:
625 ChangeMusicSet(index);
626 break;
627 default:
628 NOT_REACHED();
629 }
630 }
631};
632
633static constexpr NWidgetPart _nested_music_track_selection_widgets[] = {
635 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
636 NWidget(WWT_CAPTION, COLOUR_GREY, WID_MTS_CAPTION), SetDataTip(STR_PLAYLIST_MUSIC_SELECTION_SETNAME, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
637 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_MTS_MUSICSET), SetDataTip(STR_PLAYLIST_CHANGE_SET, STR_PLAYLIST_TOOLTIP_CHANGE_SET),
638 EndContainer(),
639 NWidget(WWT_PANEL, COLOUR_GREY),
640 NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
641 /* Left panel. */
643 NWidget(WWT_LABEL, COLOUR_GREY), SetFill(1, 0), SetDataTip(STR_PLAYLIST_TRACK_INDEX, STR_NULL),
644 NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_LEFT), SetFill(1, 1), SetMinimalSize(180, 194), SetDataTip(0x0, STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK), EndContainer(),
646 EndContainer(),
647 /* Middle buttons. */
649 NWidget(NWID_SPACER), SetMinimalSize(60, 30), // Space above the first button from the title bar.
650 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_ALL), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
651 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_OLD), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
652 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_NEW), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
653 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_EZY), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
654 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM1), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
655 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM2), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
656 NWidget(NWID_SPACER), SetMinimalSize(0, 16), // Space above 'clear' button
657 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_MTS_CLEAR), SetFill(1, 0), SetDataTip(STR_PLAYLIST_CLEAR, STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1),
659 EndContainer(),
660 /* Right panel. */
662 NWidget(WWT_LABEL, COLOUR_GREY, WID_MTS_PLAYLIST), SetFill(1, 0), SetDataTip(STR_PLAYLIST_PROGRAM, STR_NULL),
663 NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_RIGHT), SetFill(1, 1), SetMinimalSize(180, 194), SetDataTip(0x0, STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK), EndContainer(),
665 EndContainer(),
666 EndContainer(),
667 EndContainer(),
668};
669
670static WindowDesc _music_track_selection_desc(
671 WDP_AUTO, nullptr, 0, 0,
673 0,
674 _nested_music_track_selection_widgets
675);
676
677static void ShowMusicTrackSelection()
678{
679 AllocateWindowDescFront<MusicTrackSelectionWindow>(_music_track_selection_desc, 0);
680}
681
682struct MusicWindow : public Window {
683 MusicWindow(WindowDesc &desc, WindowNumber number) : Window(desc)
684 {
685 this->InitNested(number);
688
689 UpdateDisabledButtons();
690 }
691
692 void UpdateDisabledButtons()
693 {
694 /* Disable music control widgets if there is no music
695 * -- except Programme button! So you can still select a music set. */
697 BaseMusic::GetUsedSet()->num_available == 0,
700 );
701 }
702
704 {
705 switch (widget) {
706 /* Make sure that WID_M_SHUFFLE and WID_M_PROGRAMME have the same size.
707 * This can't be done by using NC_EQUALSIZE as the WID_M_INFO is
708 * between those widgets and of different size. */
709 case WID_M_SHUFFLE: case WID_M_PROGRAMME: {
711 d.width += padding.width;
712 d.height += padding.height;
713 size = maxdim(size, d);
714 break;
715 }
716
717 case WID_M_TRACK_NR: {
719 d.width += padding.width;
720 d.height += padding.height;
721 size = maxdim(size, d);
722 break;
723 }
724
725 case WID_M_TRACK_NAME: {
727 for (const auto &song : _music.music_set) {
728 SetDParamStr(0, song.songname);
730 }
731 d.width += padding.width;
732 d.height += padding.height;
733 size = maxdim(size, d);
734 break;
735 }
736
737 /* Hack-ish: set the proper widget data; only needs to be done once
738 * per (Re)Init as that's the only time the language changes. */
739 case WID_M_PREV: this->GetWidget<NWidgetCore>(WID_M_PREV)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_NEXT : SPR_IMG_SKIP_TO_PREV; break;
740 case WID_M_NEXT: this->GetWidget<NWidgetCore>(WID_M_NEXT)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_PREV : SPR_IMG_SKIP_TO_NEXT; break;
741 case WID_M_PLAY: this->GetWidget<NWidgetCore>(WID_M_PLAY)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_PLAY_MUSIC_RTL : SPR_IMG_PLAY_MUSIC; break;
742 }
743 }
744
745 void DrawWidget(const Rect &r, WidgetID widget) const override
746 {
747 switch (widget) {
748 case WID_M_TRACK_NR: {
750 if (BaseMusic::GetUsedSet()->num_available == 0) {
751 break;
752 }
754 if (_music.IsPlaying()) {
755 SetDParam(0, _music.GetCurrentSong().tracknr);
756 SetDParam(1, 2);
758 }
760 break;
761 }
762
763 case WID_M_TRACK_NAME: {
767 if (BaseMusic::GetUsedSet()->num_available == 0) {
769 } else if (_music.IsPlaying()) {
771 SetDParamStr(0, entry.songname);
772 }
774 break;
775 }
776
777 case WID_M_MUSIC_VOL:
779 break;
780
781 case WID_M_EFFECT_VOL:
783 break;
784 }
785 }
786
792 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
793 {
794 if (!gui_scope) return;
795 for (int i = 0; i < 6; i++) {
797 }
798
799 UpdateDisabledButtons();
800
801 if (data == 1) {
802 this->ReInit();
803 } else {
804 this->SetDirty();
805 }
806 }
807
808 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
809 {
810 switch (widget) {
811 case WID_M_PREV: // skip to prev
812 _music.Prev();
813 break;
814
815 case WID_M_NEXT: // skip to next
816 _music.Next();
817 break;
818
819 case WID_M_STOP: // stop playing
820 _music.Stop();
821 break;
822
823 case WID_M_PLAY: // start playing
824 _music.Play();
825 break;
826
827 case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders
829 if (ClickSliderWidget(this->GetWidget<NWidgetBase>(widget)->GetCurrentRect(), pt, 0, INT8_MAX, 0, vol)) {
830 if (widget == WID_M_MUSIC_VOL) {
832 } else {
833 SetEffectVolume(vol);
834 }
835 this->SetWidgetDirty(widget);
837 }
838
839 if (click_count > 0) this->mouse_capture_widget = widget;
840 break;
841 }
842
843 case WID_M_SHUFFLE: // toggle shuffle
844 if (_music.IsShuffle()) {
845 _music.Unshuffle();
846 } else {
847 _music.Shuffle();
848 }
851 break;
852
853 case WID_M_PROGRAMME: // show track selection
854 ShowMusicTrackSelection();
855 break;
856
857 case WID_M_ALL: case WID_M_OLD: case WID_M_NEW:
858 case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist
859 _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL));
860 break;
861 }
862 }
863};
864
865static constexpr NWidgetPart _nested_music_window_widgets[] = {
867 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
868 NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MUSIC_JAZZ_JUKEBOX_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
869 NWidget(WWT_SHADEBOX, COLOUR_GREY),
870 NWidget(WWT_STICKYBOX, COLOUR_GREY),
871 EndContainer(),
872
875 NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
877 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PREV), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_SKIP_TO_PREV, STR_MUSIC_TOOLTIP_SKIP_TO_PREVIOUS_TRACK),
878 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_NEXT), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_SKIP_TO_NEXT, STR_MUSIC_TOOLTIP_SKIP_TO_NEXT_TRACK_IN_SELECTION),
879 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_STOP), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_STOP_MUSIC, STR_MUSIC_TOOLTIP_STOP_PLAYING_MUSIC),
880 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PLAY), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_PLAY_MUSIC, STR_MUSIC_TOOLTIP_START_PLAYING_MUSIC),
881 EndContainer(),
882 NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
883 EndContainer(),
884 NWidget(WWT_PANEL, COLOUR_GREY, WID_M_SLIDERS),
885 NWidget(NWID_HORIZONTAL), SetPIP(4, 0, 4),
887 NWidget(WWT_LABEL, COLOUR_GREY, -1), SetFill(1, 0), SetDataTip(STR_MUSIC_MUSIC_VOLUME, STR_NULL),
888 NWidget(WWT_EMPTY, COLOUR_GREY, WID_M_MUSIC_VOL), SetMinimalSize(67, 0), SetPadding(2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
889 EndContainer(),
891 NWidget(WWT_LABEL, COLOUR_GREY, -1), SetFill(1, 0), SetDataTip(STR_MUSIC_EFFECTS_VOLUME, STR_NULL),
892 NWidget(WWT_EMPTY, COLOUR_GREY, WID_M_EFFECT_VOL), SetMinimalSize(67, 0), SetPadding(2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
893 EndContainer(),
894 EndContainer(),
895 EndContainer(),
896 EndContainer(),
897 NWidget(WWT_PANEL, COLOUR_GREY, WID_M_BACKGROUND),
898 NWidget(NWID_HORIZONTAL), SetPIP(6, 0, 6),
901 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_SHUFFLE), SetMinimalSize(50, 8), SetDataTip(STR_MUSIC_SHUFFLE, STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE),
903 EndContainer(),
904 NWidget(NWID_VERTICAL), SetPadding(0, 0, 3, 3),
905 NWidget(WWT_LABEL, COLOUR_GREY, WID_M_TRACK), SetFill(0, 0), SetDataTip(STR_MUSIC_TRACK, STR_NULL),
907 EndContainer(),
908 NWidget(NWID_VERTICAL), SetPadding(0, 3, 3, 0),
909 NWidget(WWT_LABEL, COLOUR_GREY, WID_M_TRACK_TITLE), SetFill(1, 0), SetDataTip(STR_MUSIC_XTITLE, STR_NULL),
910 NWidget(WWT_PANEL, COLOUR_GREY, WID_M_TRACK_NAME), SetFill(1, 0), EndContainer(),
911 EndContainer(),
914 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_M_PROGRAMME), SetMinimalSize(50, 8), SetDataTip(STR_MUSIC_PROGRAM, STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION),
916 EndContainer(),
917 EndContainer(),
918 EndContainer(),
920 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_ALL), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
921 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_OLD), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
922 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_NEW), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
923 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_EZY), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
924 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM1), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
925 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM2), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
926 EndContainer(),
927};
928
929static WindowDesc _music_window_desc(
930 WDP_AUTO, "music", 0, 0,
932 0,
933 _nested_music_window_widgets
934);
935
936void ShowMusicWindow()
937{
938 AllocateWindowDescFront<MusicWindow>(_music_window_desc, 0);
939}
Generic functions for replacing base data (graphics, sounds).
static const uint NUM_SONGS_AVAILABLE
Maximum number of songs in the full playlist; theme song + the classes.
static const uint NUM_SONGS_CLASS
Maximum number of songs in the 'class' playlists.
static const uint NUM_SONGS_PLAYLIST
Maximum number of songs in the (custom) playlist.
static const MusicSet * GetUsedSet()
Return the used set.
static const MusicSet * GetSet(int index)
Get the name of the graphics set at the specified index.
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
static bool SetSetByName(const std::string &name)
Set the set to be used.
static std::string ini_set
The set as saved in the config file.
virtual void StopSong()=0
Stop playing the current song.
static MusicDriver * GetInstance()
Get the currently active instance of the music driver.
virtual void PlaySong(const MusicSongInfo &song)=0
Play a particular song.
virtual void SetVolume(uint8_t vol)=0
Set the volume, if possible.
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:42
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:40
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition dropdown.cpp:404
Functions related to the drop down widget.
Types related to the drop down widget.
Functions related to errors.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:657
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:114
Functions related to the gfx engine.
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:344
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:210
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
constexpr NWidgetPart SetMinimalTextLines(uint8_t lines, uint8_t spacing, FontSize size=FS_NORMAL)
Widget part function for setting the minimal text lines.
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:940
Functions related to memory operations.
void MemSetT(T *ptr, uint8_t value, size_t num=1)
Type-safe version of memset().
Definition mem_func.hpp:49
Functions to mix sound samples.
Base for all music playback.
void MusicLoop()
Check music playback status and start/stop/song-finished.
void ChangeMusicSet(int index)
Change the configured music set and reset playback.
void InitializeMusic()
Prepare the music system for use.
Types related to the music widgets.
@ WID_MTS_OLD
Old button.
@ WID_MTS_CUSTOM2
Custom2 button.
@ WID_MTS_ALL
All button.
@ WID_MTS_CUSTOM1
Custom1 button.
@ WID_MTS_LIST_LEFT
Left button.
@ WID_MTS_EZY
Ezy button.
@ WID_MTS_CLEAR
Clear button.
@ WID_MTS_LIST_RIGHT
Right button.
@ WID_MTS_NEW
New button.
@ WID_MTS_PLAYLIST
Playlist.
@ WID_MTS_MUSICSET
Music set selection.
@ WID_MTS_CAPTION
Window caption.
@ WID_M_PREV
Previous button.
@ WID_M_TRACK_NAME
Track name.
@ WID_M_NEXT
Next button.
@ WID_M_NEW
New button.
@ WID_M_TRACK_TITLE
Track title.
@ WID_M_STOP
Stop button.
@ WID_M_BACKGROUND
Background of the window.
@ WID_M_ALL
All button.
@ WID_M_CUSTOM2
Custom2 button.
@ WID_M_CUSTOM1
Custom1 button.
@ WID_M_TRACK
Track playing.
@ WID_M_OLD
Old button.
@ WID_M_SLIDERS
Sliders.
@ WID_M_SHUFFLE
Shuffle button.
@ WID_M_EZY
Ezy button.
@ WID_M_MUSIC_VOL
Music volume.
@ WID_M_EFFECT_VOL
Effect volume.
@ WID_M_TRACK_NR
Track number.
@ WID_M_PROGRAMME
Program button.
@ WID_M_PLAY
Play button.
Some generic types.
static const uint8_t PC_BLACK
Black palette colour.
Pseudo random number generator.
A number of safeguards to prevent using unsafe methods.
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Functions for setting GUIs.
Types related to global configuration settings.
void DrawSliderWidget(Rect r, int min_value, int max_value, int nmarks, int value, SliderMarkFunc *mark_func)
Draw a slider widget with knob at given value.
Definition slider.cpp:31
bool ClickSliderWidget(Rect r, Point pt, int min_value, int max_value, int nmarks, int &value)
Handle click on a slider widget to change the value.
Definition slider.cpp:91
Functions related to sound.
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
MusicSettings music
settings related to music/sound
Dimensions (a width and height) of a rectangle in 2D.
All data of a music set.
uint8_t effect_vol
The requested effects volume.
bool playing
Whether music is playing.
uint8_t music_vol
The requested music volume.
uint8_t custom_1[33]
The order of the first custom playlist.
uint8_t custom_2[33]
The order of the second custom playlist.
uint8_t playlist
The playlist (number) to play.
bool shuffle
Whether to shuffle the music.
Metadata about a music track.
std::string songname
name of song displayed in UI
uint8_t tracknr
track number of song displayed in UI
std::string filename
file on disk containing song (when used in MusicSet class)
bool loop
song should play in a tight loop if possible, never ending
int cat_index
entry index in CAT file, for filetype==MTT_MPSMIDI
const MusicSet * set
music set the song comes from
Definition music_gui.cpp:42
uint set_index
index of song in set
Definition music_gui.cpp:43
void SaveCustomPlaylist(PlaylistChoices pl)
Save a custom playlist to settings after modification.
void CheckStatus()
Check that music is playing if it should, and that appropriate playlist is active for game/main menu.
void ChangeMusicSet(const std::string &set_name)
Change to named music set, and reset playback.
void ChangePlaylist(PlaylistChoices pl)
Switch to another playlist, or reload the current one.
void Shuffle()
Enable shuffle mode.
void Stop()
Stop playback and set flag that we don't intend to play music.
void BuildPlaylists()
Rebuild all playlists for the current music set.
void Next()
Skip to next track.
void Play()
Start/restart playback at current song.
bool IsPlaying() const
Is the player getting music right now?
void Unshuffle()
Disable shuffle mode.
void PlaylistRemove(size_t song_index)
Remove a song from a custom playlist.
PlaylistEntry GetCurrentSong() const
Return the current song, or a dummy if none.
void PlaylistAdd(size_t song_index)
Append a song to a custom playlist.
void ChangePlaylistPosition(int ofs)
Change playlist position pointer by the given offset, making sure to keep it within valid range.
Playlist displayed_playlist
current playlist as displayed in GUI, never in shuffled order
Definition music_gui.cpp:62
Playlist active_playlist
current play order of songs, including any shuffle
Definition music_gui.cpp:61
void Prev()
Skip to previous track.
void PlaylistClear()
Remove all songs from the current custom playlist.
void SetPositionBySetIndex(uint set_index)
Set playlist position by set index.
bool IsCustomPlaylist() const
Is one of the custom playlists selected?
bool IsShuffle() const
Is shuffle mode enabled?
Playlist music_set
all songs in current music set, in set order
Definition music_gui.cpp:63
uint GetSetIndex()
Get set index from current playlist position.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void SetStringParameters(WidgetID widget) const override
Initialize string parameters for a widget.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
void OnDropdownSelect(WidgetID widget, int index) override
A dropdown option associated to this window has been selected.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
High level window description.
Definition window_gui.h:159
Data structure for an opened window.
Definition window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition window.cpp:952
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition window.cpp:551
WidgetID mouse_capture_widget
ID of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture.
Definition window_gui.h:326
ResizeInfo resize
Resize information.
Definition window_gui.h:314
void SetWidgetsDisabledState(bool disab_stat, Args... widgets)
Sets the enabled/disabled status of a list of widgets.
Definition window_gui.h:521
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:447
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:213
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:466
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1746
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:387
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
@ WWT_LABEL
Centered label.
Definition widget_type.h:57
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:79
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition widget_type.h:55
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:48
@ WWT_DROPDOWN
Drop down list.
Definition widget_type.h:70
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3127
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3219
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:147
int WidgetID
Widget ID.
Definition window_type.h:18
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition window_type.h:26
int32_t WindowNumber
Number to differentiate different windows of the same class.
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45
@ WC_MUSIC_TRACK_SELECTION
Music track selection; Window numbers:
@ WC_MUSIC_WINDOW
Music window; Window numbers:
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Functions related to zooming.