Skip to content

Commit

Permalink
Music and playlists
Browse files Browse the repository at this point in the history
* This PR is a revival of endless-sky#7538.
* Authorship from prior code review is preserved.

Feature Summary
---------------

Adds the possibility to add playlists to the game that are only played in certain locations and if certain conditions are met.

Example:

```ruby
track "test2"
        volume 0.1 #add 0.1 to volume
	idle "music/mainscreen" #idle track, played during normal gameplay
	combat "music/mainscreen" #combat track, played during combat
	landing "music/mainscreen" #landing track, played while landed

track "test1"
	idle "music/haispace"
	combat "music/haispace"
	landing "music/haispace"

playlist "test"
        to play #conditions for playing this playlist
                has "Hai Intro: done"
	location #locationfilter for where to play this
		not government "Hai"
	priority 1 #the priority, higher priority playlist will be chosen over lower priority playlists
	weight 10 #the weight of a playlist inside of its priority layer, higher weight means more probable to be chosen
	tracks "linear"
		"test1" 20
		"test2" 20
```

Detailed Explanation
--------------------

`track NAME` holds the following child keys.

* `NAME` the name of the track
* `volume` (default 0): a value that should be chosen between -1 and 1 that gets added to the current volume of the music. (use this with small values, otherwise users who want dim music will be annoyed.)
* Three categories of music associated with this track. The tracks will be played when the player is in their state. If one is not given, nothing will be played in that state.
  * `idle`
  * `combat`
  * `landing`

`playlist NAME` holds the following child keys.

* `NAME` the name of the playlist.
* `to play` conditions that determine if this playlist can be played.
* A `location` filter for where this playlist will be played.
* A `priority`, higher priorities will always play over lower priorities.
* A `weight`, playlists with higher weight are more likely to be played inside the same priority.
* `tracks`
  * A progression style: `linear`, `random`, or `pick` (pick means that one track will be picked and repeated until the playlist is switched)
  * Various tracks with weights to them, weights only matter for pick or random progression style.

This would play either track one or track two when you are not in Hai government zone.

Testing Done
------------

TBD

Important
---------

* Asset pull request: endless-sky/endless-sky-assets#127

Checklist
---------

- [ ] Update with upstream master including CMake.
- [ ] Do some testing.

Co-authored-by: Hurleveur <[email protected]>
Co-authored-by: tibetiroka <[email protected]>
  • Loading branch information
3 people authored and samrocketman committed Mar 3, 2023
1 parent a80c84a commit 3b9a54b
Show file tree
Hide file tree
Showing 21 changed files with 577 additions and 5 deletions.
12 changes: 12 additions & 0 deletions EndlessSky.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
03DC4253AA8390FE0A8FB4EA /* MaskManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 499B4DA7A9C7351120660643 /* MaskManager.cpp */; };
072599D126A8CB2F007EC229 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072599CC26A8C942007EC229 /* SDL2.framework */; };
072599D426A8CD5D007EC229 /* SDL2.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 072599CC26A8C942007EC229 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0E77AFDF2918751D00864BB5 /* Playlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E77AFDE2918751300864BB5 /* Playlist.cpp */; };
0E77AFE02918751D00864BB5 /* Track.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0E77AFDC2918751300864BB5 /* Track.cpp */; };
1578F883250B5F8A00D318FB /* InfoPanelState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1578F880250B5F8A00D318FB /* InfoPanelState.cpp */; };
16AD4CACA629E8026777EA00 /* truncate.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 2CA44855BD0AFF45DCAEEA5D /* truncate.hpp */; settings = {ATTRIBUTES = (Project, ); }; };
1F884DB590D5152608510676 /* ShipJumpNavigation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 13B64DB8AE730E57C3DEA2F0 /* ShipJumpNavigation.cpp */; };
Expand Down Expand Up @@ -228,6 +230,10 @@
086E48E490C9BAD6660C7274 /* ExclusiveItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExclusiveItem.h; path = source/ExclusiveItem.h; sourceTree = "<group>"; };
0C90483BB01ECD0E3E8DDA44 /* WeightedList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WeightedList.h; path = source/WeightedList.h; sourceTree = "<group>"; };
0DF34095B64BC64F666ECF5F /* CoreStartData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreStartData.cpp; path = source/CoreStartData.cpp; sourceTree = "<group>"; };
0E77AFDB2918751300864BB5 /* Playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Playlist.h; path = source/Playlist.h; sourceTree = "<group>"; };
0E77AFDC2918751300864BB5 /* Track.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Track.cpp; path = source/Track.cpp; sourceTree = "<group>"; };
0E77AFDD2918751300864BB5 /* Track.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Track.h; path = source/Track.h; sourceTree = "<group>"; };
0E77AFDE2918751300864BB5 /* Playlist.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Playlist.cpp; path = source/Playlist.cpp; sourceTree = "<group>"; };
11EA4AD7A889B6AC1441A198 /* StartConditionsPanel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StartConditionsPanel.cpp; path = source/StartConditionsPanel.cpp; sourceTree = "<group>"; };
13B643F6BEC24349F9BC9F42 /* alignment.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = alignment.hpp; path = source/text/alignment.hpp; sourceTree = "<group>"; };
13B64DB8AE730E57C3DEA2F0 /* ShipJumpNavigation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ShipJumpNavigation.cpp; path = source/ShipJumpNavigation.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -591,6 +597,10 @@
654D33611BE92C9200D1E5AB /* source */ = {
isa = PBXGroup;
children = (
0E77AFDE2918751300864BB5 /* Playlist.cpp */,
0E77AFDB2918751300864BB5 /* Playlist.h */,
0E77AFDC2918751300864BB5 /* Track.cpp */,
0E77AFDD2918751300864BB5 /* Track.h */,
A96862CD1AE6FD0A004FE1FE /* Account.cpp */,
A96862CE1AE6FD0A004FE1FE /* Account.h */,
A96862CF1AE6FD0A004FE1FE /* AI.cpp */,
Expand Down Expand Up @@ -1155,6 +1165,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0E77AFDF2918751D00864BB5 /* Playlist.cpp in Sources */,
0E77AFE02918751D00864BB5 /* Track.cpp in Sources */,
A96863AD1AE6FD0E004FE1FE /* Command.cpp in Sources */,
A96863E71AE6FD0E004FE1FE /* PointerShader.cpp in Sources */,
A96863E51AE6FD0E004FE1FE /* PlayerInfo.cpp in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions EndlessSkyLib.cbp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@
<Unit filename="source/PlayerInfo.h" />
<Unit filename="source/PlayerInfoPanel.cpp" />
<Unit filename="source/PlayerInfoPanel.h" />
<Unit filename="source/Playlist.cpp" />
<Unit filename="source/Playlist.h" />
<Unit filename="source/Point.cpp" />
<Unit filename="source/Point.h" />
<Unit filename="source/PointerShader.cpp" />
Expand Down Expand Up @@ -379,6 +381,8 @@
<Unit filename="source/text/Format.h" />
<Unit filename="source/text/Table.cpp" />
<Unit filename="source/text/Table.h" />
<Unit filename="source/text/Track.cpp" />
<Unit filename="source/text/Track.h" />
<Unit filename="source/text/Utf8.cpp" />
<Unit filename="source/text/Utf8.h" />
<Unit filename="source/text/WrappedText.cpp" />
Expand Down
7 changes: 7 additions & 0 deletions data/interfaces.txt
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ interface "load menu"
interface "controls"
sprite "ui/keys panel"
center -65 -20
sprite "ui/music volume"
center 330 -60
button c "_Controls"
center -300 -230
dimensions 90 30
Expand Down Expand Up @@ -455,6 +457,11 @@ interface "preferences"
dimensions 0 -200
color "energy"
size 3
bar "music volume"
from 325 15
dimensions 0 -200
color "energy"
size 3



Expand Down
Binary file added images/ui/music volume.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 118 additions & 0 deletions source/Audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ this program. If not, see <https://www.gnu.org/licenses/>.
#include "Audio.h"

#include "Files.h"
#include "GameData.h"
#include "Logger.h"
#include "Music.h"
#include "Planet.h"
#include "PlayerInfo.h"
#include "Playlist.h"
#include "Point.h"
#include "Preferences.h"
#include "Random.h"
#include "Sound.h"
#include "System.h"

#if !defined(__APPLE__) || defined(ES_CMAKE)
#include <AL/al.h>
Expand Down Expand Up @@ -115,6 +121,12 @@ namespace {
shared_ptr<Music> previousTrack;
int musicFade = 0;
vector<int16_t> fadeBuffer;
double musicVolume = 1.;
double musicVolumeModifier = 0.;

const Playlist *currentPlaylist = nullptr;
const Track *currentPlaylistTrack = nullptr;
Track::GameState oldState = Track::GameState::IDLE;
}


Expand Down Expand Up @@ -181,6 +193,7 @@ void Audio::Init(const vector<string> &sources)
}
alSourceQueueBuffers(musicSource, MUSIC_BUFFERS, musicBuffers);
alSourcePlay(musicSource);
alSourcef(musicSource, AL_GAIN, musicVolume);
}


Expand Down Expand Up @@ -233,6 +246,24 @@ void Audio::SetVolume(double level)



// Get the volume.
double Audio::MusicVolume()
{
return musicVolume;
}



// Set the volume (to a value between 0 and 1).
void Audio::SetMusicVolume(double level)
{
musicVolume = min(1., max(0., level));
if(isInitialized)
alSourcef(musicSource, AL_GAIN, min(1., musicVolume + musicVolumeModifier));
}



// Get a pointer to the named sound. The name is the path relative to the
// "sound/" folder, and without ~ if it's on the end, or the extension.
const Sound *Audio::Get(const string &name)
Expand Down Expand Up @@ -260,6 +291,90 @@ void Audio::Update(const Point &listenerPosition)



void Audio::UpdateMusic(PlayerInfo &player, Track::GameState state)
{
if(!isInitialized)
return;

// Music defined by planet
if(state == Track::GameState::LANDED)
{
if(!player.GetPlanet()->MusicName().empty())
{
PlayMusic(player.GetPlanet()->MusicName());
return;
}
}
else
{
if(player.GetSystem()->MusicName().empty())
{
PlayMusic(player.GetSystem()->MusicName());
return;
}
}

// If the current playlists conditions are not matching anymore, search a new one.
bool currentPlaylistValid = currentPlaylist ?
currentPlaylist->MatchingConditions(player) : false;
// The track has to be updated if the current track is finished or the playlist is not matching
// anymore.
if(currentTrack->IsFinished() || !currentPlaylistValid)
{
if(!currentPlaylistValid)
{
// If the current playlist is not valid, find a new one based on priority and weight.
WeightedList<const Playlist *> validPlaylists;
int priority = 0;
for(auto &playlist : GameData::Playlists())
if(playlist.second.MatchingConditions(player))
{
// Higher priorities always win.
if(playlist.second.Priority() == priority)
validPlaylists.emplace_back(playlist.second.Weight(), &playlist.second);
else if(playlist.second.Priority() > priority)
{
priority = playlist.second.Priority();
validPlaylists.clear();
validPlaylists.emplace_back(playlist.second.Weight(), &playlist.second);
}
}
if(!validPlaylists.empty())
{
// This will return a random playlist, with playlist with a higher weight being more
// probable to be returned.
currentPlaylist = validPlaylists.Get();
currentPlaylist->Activate();
}
else
currentPlaylist = nullptr;
}
// Only switch to a new track if a playlist is set.
if(currentPlaylist)
{
currentPlaylistTrack = currentPlaylist->GetCurrentTrack();
if(currentPlaylistTrack)
{
musicVolumeModifier = currentPlaylistTrack->GetVolumeModifier();
SetMusicVolume(volume);
PlayMusic(currentPlaylistTrack->GetTitle(state));
}
oldState = state;
}
// If no playlist is set this means nothing should be played, so stop everything.
else
currentTrack->Finish();
}
if(oldState != state)
{
oldState = state;
if(currentPlaylistTrack)
PlayMusic(currentPlaylistTrack->GetTitle(state));
}
}



// Play the given sound, at full volume.
void Audio::Play(const Sound *sound)
{
Expand Down Expand Up @@ -301,6 +416,9 @@ void Audio::PlayMusic(const string &name)
// Don't worry about thread safety here, since music will always be started
// by the main thread.
musicFade = 65536;
// Clear of the previous track so that we can use it again without having
// old data to deal with.
previousTrack.reset(new Music());
swap(currentTrack, previousTrack);
// If the name is empty, it means to turn music off.
currentTrack->SetSource(name);
Expand Down
8 changes: 8 additions & 0 deletions source/Audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef AUDIO_H_
#define AUDIO_H_

#include "Track.h"

#include <string>
#include <vector>

class PlayerInfo;
class Point;
class Sound;

Expand All @@ -44,6 +47,10 @@ class Audio {
static double Volume();
static void SetVolume(double level);

// Get or set the music volume (between 0 and 1).
static double MusicVolume();
static void SetMusicVolume(double level);

// Get a pointer to the named sound. The name is the path relative to the
// "sound/" folder, and without ~ if it's on the end, or the extension.
// Do not call this function until Progress() is 100%.
Expand All @@ -53,6 +60,7 @@ class Audio {
// added but deferred because they were added from a thread other than the
// main one (the one that called Init()).
static void Update(const Point &listenerPosition);
static void UpdateMusic(PlayerInfo &player, Track::GameState state);

// Play the given sound, at full volume.
static void Play(const Sound *sound);
Expand Down
4 changes: 4 additions & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ target_sources(EndlessSkyLib PRIVATE
PlayerInfo.h
PlayerInfoPanel.cpp
PlayerInfoPanel.h
Playlist.cpp
Playlist.h
Point.cpp
Point.h
PointerShader.cpp
Expand Down Expand Up @@ -329,6 +331,8 @@ target_sources(EndlessSkyLib PRIVATE
TestData.h
TextReplacements.cpp
TextReplacements.h
Track.cpp
Track.h
Trade.cpp
Trade.h
TradingPanel.cpp
Expand Down
12 changes: 11 additions & 1 deletion source/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.
#include "System.h"
#include "Test.h"
#include "TestContext.h"
#include "Track.h"
#include "Visual.h"
#include "Weather.h"
#include "Wormhole.h"
Expand Down Expand Up @@ -511,6 +512,16 @@ void Engine::Step(bool isActive)
wasActive = isActive;
Audio::Update(center);

if(GameData::IsLoaded())
{
Track::GameState state = Track::GameState::IDLE;
// If there are hostile ships in the system, set the state to COMBAT.
if(hadHostiles)
state = Track::GameState::COMBAT;
Audio::UpdateMusic(player, state);
}


// Update the zoom value now that the calculation thread is paused.
if(nextZoom)
{
Expand Down Expand Up @@ -1212,7 +1223,6 @@ void Engine::EnterSystem()
const Date &today = player.GetDate();

const System *system = flagship->GetSystem();
Audio::PlayMusic(system->MusicName());
GameData::SetHaze(system->Haze(), false);

Messages::Add("Entering the " + system->Name() + " system on "
Expand Down
14 changes: 14 additions & 0 deletions source/GameData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,13 @@ const Set<Planet> &GameData::Planets()



const Set<Playlist> &GameData::Playlists()
{
return objects.playlists;
}



const Set<Ship> &GameData::Ships()
{
return objects.ships;
Expand All @@ -630,6 +637,13 @@ const Set<TestData> &GameData::TestDataSets()



const Set<Track> &GameData::Tracks()
{
return objects.tracks;
}



ConditionsStore &GameData::GlobalConditions()
{
return globalConditions;
Expand Down
4 changes: 4 additions & 0 deletions source/GameData.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class Panel;
class Person;
class Phrase;
class Planet;
class Playlist;
class Politics;
class Ship;
class Sprite;
Expand All @@ -60,6 +61,7 @@ class System;
class Test;
class TestData;
class TextReplacements;
class Track;
class Wormhole;


Expand Down Expand Up @@ -129,11 +131,13 @@ class GameData {
static const Set<Person> &Persons();
static const Set<Phrase> &Phrases();
static const Set<Planet> &Planets();
static const Set<Playlist> &Playlists();
static const Set<Ship> &Ships();
static const Set<Sale<Ship>> &Shipyards();
static const Set<System> &Systems();
static const Set<Test> &Tests();
static const Set<TestData> &TestDataSets();
static const Set<Track> &Tracks();
static const Set<Wormhole> &Wormholes();

static ConditionsStore &GlobalConditions();
Expand Down
2 changes: 1 addition & 1 deletion source/MenuPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ MenuPanel::MenuPanel(PlayerInfo &player, UI &gamePanels)
}

if(player.GetPlanet())
Audio::PlayMusic(player.GetPlanet()->MusicName());
Audio::UpdateMusic(player, Track::GameState::LANDED);
}


Expand Down
Loading

0 comments on commit 3b9a54b

Please sign in to comment.