1 /// 2 /// Audio engine -- manage current audio player and sources. 3 /// @file engine.h - pianod 4 /// @author Perette Barella 5 /// @date 2014-11-30 6 /// @copyright Copyright (c) 2014-2020 Devious Fish. All rights reserved. 7 /// 8 9 #pragma once 10 11 #include <config.h> 12 13 #include <ctime> 14 15 #include <football.h> 16 17 #include "musictypes.h" 18 #include "retainer.h" 19 #include "connection.h" 20 #include "response.h" 21 #include "mediaunit.h" 22 #include "tuner.h" 23 24 /// Audio engine commands such as start, stop, pause, change playlists or seeds. 25 typedef enum engine_commands_t { 26 TIMESTATUS = CMD_RANGE_ENGINE, 27 GETVOLUME, 28 SETVOLUME, 29 GETCROSSFADETIME, 30 SETCROSSFADETIME, 31 GETCROSSFADELEVEL, 32 SETCROSSFADELEVEL, 33 ADJUSTVOLUME, 34 GETHISTORYSIZE, 35 SETHISTORYSIZE, 36 WAITFORENDOFSONG, 37 WAITFORNEXTSONG, 38 QUERYSTATUS, 39 QUERYHISTORY, 40 QUERYQUEUE, 41 NEXTSONG, 42 STOPPLAYBACK, 43 PAUSEPLAYBACK, 44 RESUMEPLAYBACK, 45 TOGGLEPLAYBACK, 46 PLAYQUEUEMODE, 47 PLAYPLAYLIST, 48 PLAYMIX, 49 PLAYDIRECT, 50 SELECTQUEUEMODE, 51 SELECTMIX, 52 SELECTPLAYLIST, 53 SELECTDIRECT, 54 PLAYLISTRENAME, 55 PLAYLISTDELETE, 56 PLAYLISTCREATE, 57 PLAYLISTFROMFILTER, 58 GETSUGGESTIONS, 59 LISTSONGSBYFILTER, 60 LISTSONGSBYPLAYLIST, 61 REQUESTMUSIC, 62 REQUESTCLEAR, 63 REQUESTCANCEL, 64 RATE, 65 RATEPLAYLIST, 66 SEEDLIST, 67 SEEDALTER, 68 ALTERAUDIOCONFIG, 69 // EXPLAINSONGCHOICE, Not implemented/future 70 // CREATEBOOKMARK, Not implemented/future 71 #ifndef NDEBUG 72 FILTERECHO 73 #endif 74 } ENGINECOMMAND; 75 76 /** Audio engine, responsible for one "room" worth of audio. 77 Each audio engine has its own queue, selected playlist(s), 78 and playback status. 79 */ 80 class AudioEngine : public Football::Interpreter<PianodConnection, ENGINECOMMAND> { 81 private: 82 /// Prerequisites that may be necessary to execute a particular command. 83 typedef enum requirement_t { 84 REQUIRE_SOURCE = 0x01, ///< Marks other flags as source-related 85 REQUIRE_PLAYLIST = 0x02, ///< Marks other flags as playlist-related. 86 REQUIRE_PLAYER = 0x04, ///< Require an audio player exist 87 REQUIRE_REQUEST = 0x10, ///< Require request capability 88 } REQUIREMENT; 89 90 enum class PlaybackState { 91 Paused, 92 Playing 93 }; 94 95 /// States player progresses through when segueing between tracks. 96 enum class TransitionProgress { 97 Playing, ///< the audio player is not transitioning 98 Purged, ///< the queue has een purged in preparation for end-of-song 99 Cueing, ///< a second audio player has been initialized and is initializing/prefetching. 100 Crossfading,///< the audio players are both playing as audio is cross-faded 101 Done ///< transition is done, but old song keeps right on going. 102 }; 103 104 /// Behaviors possible when the player is idle. 105 enum class QueueMode { 106 Stopped, ///< Don't start playing anything. 107 Requests, ///< Play only requested songs 108 RandomPlay, ///< Random selections from the current source if no requests. 109 }; 110 111 /// Whether and how completely track information was announced at start of playback. 112 enum class Announced { 113 Never, // Track was never announced 114 Partially, // Track was announced but playpoint/duration incomplete. 115 Completely // Track was announced with full and accurate data. 116 }; 117 118 /// Structure used to track player progress/status. 119 struct { 120 time_t playback_effective_start = 0; ///< When not stalled, current time minus playback point. 121 time_t onset = 0; ///< Clock time at which a playback stall was detected. 122 time_t onset_playpoint = 0; ///< Playback point at which a playback stall was detected. 123 } stall; 124 125 static const int pause_timeout = 1800; ///< Pause timeout applied to all sources/songs 126 static const int aquire_tracks_retry = 600; ///< Retry period if unable to get random tracks. 127 128 const float prefetch_time = 5; ///< Number of seconds before cuing at which queue is refreshed. 129 130 int history_size = 10; ///< Maximum number of tracks retained in history. 131 132 volatile bool quit_requested = false; ///< Flag for signal handlers to request shutdown. 133 bool quit_initiated = false; ///< Flag set once a shutdown request has been initiated. 134 135 time_t track_acquisition_time = 0; ///< The next time at which random track aquisition may be attempted. 136 137 PianodService *service = nullptr; 138 139 Retainer <PianodSong *> current_song = nullptr; 140 Retainer <PianodSong *> cueing_song = nullptr; 141 SongList requests; 142 SongList random_queue; 143 SongList song_history; 144 145 Retainer <PianodPlaylist *> current_playlist = nullptr; 146 Media::Player *player = nullptr; 147 Media::Player *cueing_player = nullptr; 148 TransitionProgress transition_state = TransitionProgress::Playing; 149 150 Announced track_announced = Announced::Never; 151 PlaybackState playback_state = PlaybackState::Paused; 152 QueueMode queue_mode = QueueMode::Stopped; 153 time_t pause_expiration = 0; 154 bool aborting_playback = false; 155 bool empty_warning_given = false; 156 157 AudioSettings audio; 158 Tuner::Tuner mix; 159 160 // Standard parser things 161 virtual bool hasPermission (PianodConnection &conn, ENGINECOMMAND command) override; 162 virtual void handleCommand (PianodConnection &conn, ENGINECOMMAND command) override; 163 virtual const FB_PARSE_DEFINITION *statements (void) override; 164 165 // Interpreter helpers 166 MusicThingie *getThingOrCurrent (const PianodConnection &conn, 167 Ownership::Action usage = Ownership::Action::USE) const; 168 MusicThingie *getThingOrCurrent (const PianodConnection &conn, 169 MusicThingie::Type want, 170 Ownership::Action usage = Ownership::Action::USE) const; 171 ThingieList getThingsOrCurrent (const PianodConnection &conn, 172 PartialResponse *diagnostics, 173 Ownership::Action usage = Ownership::Action::USE) const; 174 ThingieList getThingsOrCurrent (const PianodConnection &conn, 175 MusicThingie::Type want, 176 PartialResponse *diagnostics, 177 Ownership::Action usage = Ownership::Action::USE) const; 178 SongList getSongsOrCurrent (const PianodConnection &conn, 179 PartialResponse *diagnostics, 180 Ownership::Action usage = Ownership::Action::USE) const; 181 PianodPlaylist *getPlaylistOrCurrent (const PianodConnection &conn, 182 Ownership::Action usage) const; 183 PlaylistList getPlaylistsOrCurrent (const PianodConnection &conn, 184 PartialResponse *diagnostics, 185 Ownership::Action usage = Ownership::Action::USE) const; 186 PlaylistList getPlaylistsOrCurrent (const PianodConnection &conn, 187 PartialResponse *diagnostics, 188 Ownership::Action usage, 189 const ThingieList &default_playlist) const; 190 void require (PianodConnection &conn, unsigned long requirements) const; 191 192 193 // Internal routines queueEmpty(void)194 inline bool queueEmpty (void) const { 195 return requests.empty() && random_queue.empty(); 196 } 197 bool startPlayer (void); 198 void promotePlayer (void); 199 void cleanupPlayer (void); 200 float monitorPlayer (void); 201 202 // Status and other senders 203 void playbackState (PlaybackState state, PianodConnection *conn = nullptr); 204 void queueMode (QueueMode mode, PianodConnection *conn = nullptr); 205 void sendSongLists (PianodConnection &conn, ENGINECOMMAND command); 206 bool sendPlaybackStatus (Football::Thingie &there, bool only_if_accurate = false); 207 void sendQueueMode (Football::Thingie &there); 208 void sendSelectedPlaylist (Football::Thingie &there); 209 210 // Alternate senders 211 inline bool sendPlaybackStatus (bool only_if_accurate = false) { 212 return sendPlaybackStatus (*service, only_if_accurate); 213 }; sendQueueMode(void)214 inline void sendQueueMode (void) { sendQueueMode (*service); }; sendSelectedPlaylist(void)215 inline void sendSelectedPlaylist (void) { sendSelectedPlaylist (*service); }; 216 217 // Helper methods 218 void PurgeUnselectedSongs (void); 219 bool considerCreatingPlayer (void); 220 bool acquireRandomTracks (void); 221 222 // Callback methods and handlers 223 void sourceReady (const Media::Source *); 224 bool sourceRemovalCheck (const Media::Source *source); 225 void sourceRemoved (const Media::Source *source); 226 void sourceOffline (const Media::Source *); 227 void sourceStatus (RESPONSE_CODE status, const char *detail); 228 229 void playlistsChanged (); 230 void mixChanged (bool automatic, const char *why); 231 232 public: 233 AudioEngine (PianodService *svc, const AudioSettings &audio_options); 234 ~AudioEngine (void); 235 void shutdown (bool immediate); 236 float periodic(void); /// Should be called intermittently by the main run-loop 237 void sendStatus (PianodConnection &there); 238 void updateStatus (PianodConnection &there); 239 void registerWithInterpreter (PianodService *service); 240 void usersChangedNotification(); 241 UserList getAutotuneUsers (); audioSettings()242 inline const AudioSettings &audioSettings () { return audio; }; 243 }; 244