aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--INSTALL10
-rw-r--r--Makefile.am1030
-rw-r--r--NEWS18
-rw-r--r--configure.ac165
-rw-r--r--doc/user.xml131
-rw-r--r--m4/ax_append_link_flags.m461
-rw-r--r--m4/ax_check_link_flag.m471
-rw-r--r--m4/ax_cxx_compile_stdcxx_0x.m4107
-rw-r--r--m4/faad.m4128
-rw-r--r--src/AllCommands.cxx387
-rw-r--r--src/AllCommands.hxx (renamed from src/db_internal.h)21
-rw-r--r--src/ArchiveFile.hxx56
-rw-r--r--src/ArchiveList.cxx (renamed from src/archive_list.c)12
-rw-r--r--src/ArchiveList.hxx (renamed from src/archive_list.h)6
-rw-r--r--src/ArchiveLookup.cxx (renamed from src/archive_api.c)4
-rw-r--r--src/ArchiveLookup.hxx (renamed from src/archive_api.h)12
-rw-r--r--src/ArchivePlugin.cxx43
-rw-r--r--src/ArchivePlugin.hxx (renamed from src/archive_plugin.h)64
-rw-r--r--src/ArchiveVisitor.hxx28
-rw-r--r--src/AudioCompress/compress.h9
-rw-r--r--src/AudioConfig.cxx (renamed from src/audio_config.c)19
-rw-r--r--src/AudioConfig.hxx (renamed from src/audio_config.h)8
-rw-r--r--src/AudioParser.cxx (renamed from src/audio_parser.c)2
-rw-r--r--src/AudioParser.hxx (renamed from src/audio_parser.h)10
-rw-r--r--src/Client.cxx (renamed from src/client.c)15
-rw-r--r--src/Client.hxx (renamed from src/client.h)40
-rw-r--r--src/ClientEvent.cxx (renamed from src/song_print.h)25
-rw-r--r--src/ClientExpire.cxx (renamed from src/db_lock.c)32
-rw-r--r--src/ClientFile.cxx (renamed from src/client_file.c)12
-rw-r--r--src/ClientFile.hxx (renamed from src/client_file.h)13
-rw-r--r--src/ClientGlobal.cxx (renamed from src/client_global.c)31
-rw-r--r--src/ClientIdle.cxx75
-rw-r--r--src/ClientInternal.hxx133
-rw-r--r--src/ClientList.cxx56
-rw-r--r--src/ClientList.hxx61
-rw-r--r--src/ClientMessage.cxx41
-rw-r--r--src/ClientMessage.hxx (renamed from src/exclude.h)45
-rw-r--r--src/ClientNew.cxx (renamed from src/client_new.c)105
-rw-r--r--src/ClientProcess.cxx (renamed from src/client_process.c)53
-rw-r--r--src/ClientRead.cxx66
-rw-r--r--src/ClientSubscribe.cxx92
-rw-r--r--src/ClientSubscribe.hxx (renamed from src/client_subscribe.h)25
-rw-r--r--src/ClientWrite.cxx87
-rw-r--r--src/CommandError.cxx134
-rw-r--r--src/CommandError.hxx38
-rw-r--r--src/CommandLine.cxx (renamed from src/cmdline.c)115
-rw-r--r--src/CommandLine.hxx (renamed from src/cmdline.h)8
-rw-r--r--src/CommandListBuilder.cxx43
-rw-r--r--src/CommandListBuilder.hxx109
-rw-r--r--src/ConfigData.cxx142
-rw-r--r--src/ConfigData.hxx142
-rw-r--r--src/ConfigFile.cxx285
-rw-r--r--src/ConfigFile.hxx31
-rw-r--r--src/ConfigGlobal.cxx176
-rw-r--r--src/ConfigGlobal.hxx109
-rw-r--r--src/ConfigOption.hxx90
-rw-r--r--src/ConfigParser.cxx43
-rw-r--r--src/ConfigParser.hxx26
-rw-r--r--src/ConfigQuark.hxx36
-rw-r--r--src/ConfigTemplates.cxx96
-rw-r--r--src/ConfigTemplates.hxx33
-rw-r--r--src/CrossFade.cxx (renamed from src/crossfade.c)16
-rw-r--r--src/CrossFade.hxx (renamed from src/crossfade.h)6
-rw-r--r--src/DatabaseCommands.cxx220
-rw-r--r--src/DatabaseCommands.hxx57
-rw-r--r--src/DatabaseGlue.cxx (renamed from src/database.c)129
-rw-r--r--src/DatabaseGlue.hxx59
-rw-r--r--src/DatabaseHelpers.cxx134
-rw-r--r--src/DatabaseHelpers.hxx41
-rw-r--r--src/DatabaseLock.cxx28
-rw-r--r--src/DatabaseLock.hxx (renamed from src/db_lock.h)29
-rw-r--r--src/DatabasePlaylist.cxx50
-rw-r--r--src/DatabasePlaylist.hxx (renamed from src/db_save.h)19
-rw-r--r--src/DatabasePlugin.hxx148
-rw-r--r--src/DatabasePrint.cxx231
-rw-r--r--src/DatabasePrint.hxx (renamed from src/db_print.h)42
-rw-r--r--src/DatabaseQueue.cxx54
-rw-r--r--src/DatabaseQueue.hxx (renamed from src/inotify_queue.h)16
-rw-r--r--src/DatabaseRegistry.cxx43
-rw-r--r--src/DatabaseRegistry.hxx37
-rw-r--r--src/DatabaseSave.cxx (renamed from src/db_save.c)44
-rw-r--r--src/DatabaseSave.hxx36
-rw-r--r--src/DatabaseSelection.cxx (renamed from src/decoder_print.h)15
-rw-r--r--src/DatabaseSelection.hxx (renamed from src/db_selection.h)32
-rw-r--r--src/DatabaseSimple.hxx (renamed from src/database.h)65
-rw-r--r--src/DatabaseVisitor.hxx (renamed from src/input_internal.h)29
-rw-r--r--src/DecoderAPI.cxx (renamed from src/decoder_api.c)90
-rw-r--r--src/DecoderControl.cxx193
-rw-r--r--src/DecoderControl.hxx297
-rw-r--r--src/DecoderInternal.cxx (renamed from src/decoder_internal.c)39
-rw-r--r--src/DecoderInternal.hxx (renamed from src/decoder_internal.h)24
-rw-r--r--src/DecoderList.cxx (renamed from src/decoder_list.c)26
-rw-r--r--src/DecoderList.hxx (renamed from src/decoder_list.h)8
-rw-r--r--src/DecoderPrint.cxx (renamed from src/decoder_print.c)12
-rw-r--r--src/DecoderPrint.hxx28
-rw-r--r--src/DecoderThread.cxx (renamed from src/decoder_thread.c)144
-rw-r--r--src/DecoderThread.hxx (renamed from src/decoder_thread.h)6
-rw-r--r--src/DespotifyUtils.cxx (renamed from src/despotify_utils.c)51
-rw-r--r--src/DespotifyUtils.hxx (renamed from src/despotify_utils.h)0
-rw-r--r--src/Directory.cxx335
-rw-r--r--src/Directory.hxx264
-rw-r--r--src/DirectorySave.cxx (renamed from src/directory_save.c)77
-rw-r--r--src/DirectorySave.hxx (renamed from src/directory_save.h)17
-rw-r--r--src/ExcludeList.cxx (renamed from src/exclude.c)50
-rw-r--r--src/ExcludeList.hxx80
-rw-r--r--src/FilterConfig.cxx (renamed from src/filter_config.c)32
-rw-r--r--src/FilterConfig.hxx (renamed from src/filter_config.h)15
-rw-r--r--src/FilterInternal.hxx71
-rw-r--r--src/FilterPlugin.cxx (renamed from src/filter_plugin.c)67
-rw-r--r--src/FilterPlugin.hxx70
-rw-r--r--src/FilterRegistry.cxx (renamed from src/filter_registry.c)6
-rw-r--r--src/FilterRegistry.hxx (renamed from src/filter_registry.h)6
-rw-r--r--src/GlobalEvents.cxx114
-rw-r--r--src/GlobalEvents.hxx71
-rw-r--r--src/IOThread.cxx (renamed from src/io_thread.c)104
-rw-r--r--src/IOThread.hxx (renamed from src/io_thread.h)31
-rw-r--r--src/IcyMetaDataParser.cxx (renamed from src/icy_metadata.c)93
-rw-r--r--src/IcyMetaDataParser.hxx83
-rw-r--r--src/IcyMetaDataServer.cxx (renamed from src/icy_server.c)29
-rw-r--r--src/IcyMetaDataServer.hxx (renamed from src/icy_server.h)13
-rw-r--r--src/IdTable.hxx91
-rw-r--r--src/Idle.cxx (renamed from src/idle.c)45
-rw-r--r--src/Idle.hxx (renamed from src/idle.h)18
-rw-r--r--src/InotifyQueue.cxx (renamed from src/inotify_queue.c)74
-rw-r--r--src/InotifyQueue.hxx (renamed from src/filter_internal.h)33
-rw-r--r--src/InotifySource.cxx (renamed from src/inotify_source.c)104
-rw-r--r--src/InotifySource.hxx72
-rw-r--r--src/InotifyUpdate.cxx (renamed from src/inotify_update.c)199
-rw-r--r--src/InotifyUpdate.hxx (renamed from src/inotify_update.h)6
-rw-r--r--src/InputInit.cxx (renamed from src/input_init.c)8
-rw-r--r--src/InputInit.hxx (renamed from src/input_init.h)11
-rw-r--r--src/InputInternal.cxx39
-rw-r--r--src/InputInternal.hxx33
-rw-r--r--src/InputPlugin.hxx (renamed from src/input_plugin.h)9
-rw-r--r--src/InputRegistry.cxx (renamed from src/input_registry.c)20
-rw-r--r--src/InputRegistry.hxx (renamed from src/input_registry.h)8
-rw-r--r--src/InputStream.cxx (renamed from src/input_stream.c)164
-rw-r--r--src/InputStream.hxx114
-rw-r--r--src/Listen.cxx (renamed from src/listen.c)55
-rw-r--r--src/Listen.hxx (renamed from src/listen.h)8
-rw-r--r--src/Log.cxx (renamed from src/log.c)8
-rw-r--r--src/Log.hxx (renamed from src/log.h)6
-rw-r--r--src/Main.cxx (renamed from src/main.c)225
-rw-r--r--src/Main.hxx (renamed from src/main.h)16
-rw-r--r--src/Mapper.cxx302
-rw-r--r--src/Mapper.hxx (renamed from src/mapper.h)68
-rw-r--r--src/MessageCommands.cxx136
-rw-r--r--src/MessageCommands.hxx42
-rw-r--r--src/MixerAll.cxx (renamed from src/mixer_all.c)18
-rw-r--r--src/MixerAll.hxx (renamed from src/mixer_all.h)8
-rw-r--r--src/MixerControl.cxx (renamed from src/mixer_control.c)6
-rw-r--r--src/MixerControl.hxx (renamed from src/mixer_control.h)18
-rw-r--r--src/MixerInternal.cxx (renamed from src/mixer_api.c)4
-rw-r--r--src/MixerInternal.hxx (renamed from src/mixer_api.h)10
-rw-r--r--src/MixerList.hxx (renamed from src/mixer_list.h)6
-rw-r--r--src/MixerPlugin.hxx (renamed from src/mixer_plugin.h)10
-rw-r--r--src/MixerType.cxx (renamed from src/mixer_type.c)4
-rw-r--r--src/MixerType.hxx (renamed from src/mixer_type.h)6
-rw-r--r--src/MusicBuffer.cxx79
-rw-r--r--src/MusicBuffer.hxx (renamed from src/buffer.h)6
-rw-r--r--src/MusicChunk.cxx84
-rw-r--r--src/MusicChunk.hxx (renamed from src/chunk.h)104
-rw-r--r--src/MusicPipe.cxx (renamed from src/pipe.c)77
-rw-r--r--src/MusicPipe.hxx (renamed from src/pipe.h)11
-rw-r--r--src/OtherCommands.cxx307
-rw-r--r--src/OtherCommands.hxx69
-rw-r--r--src/OutputAll.cxx (renamed from src/output_all.c)66
-rw-r--r--src/OutputAll.hxx (renamed from src/output_all.h)11
-rw-r--r--src/OutputCommand.cxx (renamed from src/output_command.c)17
-rw-r--r--src/OutputCommand.hxx (renamed from src/output_command.h)8
-rw-r--r--src/OutputCommands.cxx74
-rw-r--r--src/OutputCommands.hxx36
-rw-r--r--src/OutputControl.cxx (renamed from src/output_control.c)29
-rw-r--r--src/OutputControl.hxx (renamed from src/output_control.h)17
-rw-r--r--src/OutputError.hxx35
-rw-r--r--src/OutputFinish.cxx (renamed from src/output_finish.c)20
-rw-r--r--src/OutputInit.cxx (renamed from src/output_init.c)70
-rw-r--r--src/OutputList.cxx (renamed from src/output_list.c)18
-rw-r--r--src/OutputList.hxx (renamed from src/output_list.h)6
-rw-r--r--src/OutputPlugin.cxx (renamed from src/output_plugin.c)6
-rw-r--r--src/OutputPrint.cxx (renamed from src/output_print.c)12
-rw-r--r--src/OutputPrint.hxx (renamed from src/output_print.h)10
-rw-r--r--src/OutputState.cxx (renamed from src/output_state.c)6
-rw-r--r--src/OutputState.hxx (renamed from src/output_state.h)7
-rw-r--r--src/OutputThread.cxx (renamed from src/output_thread.c)96
-rw-r--r--src/OutputThread.hxx (renamed from src/output_thread.h)6
-rw-r--r--src/Page.cxx (renamed from src/client_list.c)63
-rw-r--r--src/Page.hxx (renamed from src/page.h)92
-rw-r--r--src/Partition.hxx165
-rw-r--r--src/PcmChannels.cxx (renamed from src/pcm_channels.c)116
-rw-r--r--src/PcmChannels.hxx (renamed from src/pcm_channels.h)23
-rw-r--r--src/PcmConvert.cxx326
-rw-r--r--src/PcmConvert.hxx115
-rw-r--r--src/PcmDither.cxx89
-rw-r--r--src/PcmDither.hxx (renamed from src/pcm_dither.h)33
-rw-r--r--src/PcmFormat.cxx (renamed from src/pcm_format.c)239
-rw-r--r--src/PcmFormat.hxx (renamed from src/pcm_format.h)10
-rw-r--r--src/PcmMix.cxx211
-rw-r--r--src/PcmMix.hxx (renamed from src/pcm_mix.h)10
-rw-r--r--src/PcmPrng.hxx (renamed from src/pcm_prng.h)6
-rw-r--r--src/PcmUtils.hxx66
-rw-r--r--src/PcmVolume.cxx (renamed from src/pcm_volume.c)26
-rw-r--r--src/PcmVolume.hxx (renamed from src/pcm_volume.h)9
-rw-r--r--src/Permission.cxx (renamed from src/permission.c)34
-rw-r--r--src/Permission.hxx (renamed from src/permission.h)8
-rw-r--r--src/PlayerCommands.cxx392
-rw-r--r--src/PlayerCommands.hxx90
-rw-r--r--src/PlayerControl.cxx323
-rw-r--r--src/PlayerControl.hxx325
-rw-r--r--src/PlayerThread.cxx (renamed from src/player_thread.c)318
-rw-r--r--src/PlayerThread.hxx (renamed from src/player_thread.h)6
-rw-r--r--src/Playlist.cxx344
-rw-r--r--src/Playlist.hxx256
-rw-r--r--src/PlaylistAny.cxx (renamed from src/playlist_any.c)17
-rw-r--r--src/PlaylistAny.hxx (renamed from src/playlist_any.h)11
-rw-r--r--src/PlaylistCommands.cxx224
-rw-r--r--src/PlaylistCommands.hxx60
-rw-r--r--src/PlaylistControl.cxx264
-rw-r--r--src/PlaylistDatabase.cxx (renamed from src/playlist_database.c)31
-rw-r--r--src/PlaylistDatabase.hxx (renamed from src/playlist_database.h)18
-rw-r--r--src/PlaylistEdit.cxx423
-rw-r--r--src/PlaylistFile.cxx (renamed from src/stored_playlist.c)328
-rw-r--r--src/PlaylistFile.hxx (renamed from src/stored_playlist.h)39
-rw-r--r--src/PlaylistGlobal.cxx (renamed from src/playlist_global.c)31
-rw-r--r--src/PlaylistGlobal.hxx26
-rw-r--r--src/PlaylistInfo.hxx63
-rw-r--r--src/PlaylistMapper.cxx (renamed from src/playlist_mapper.c)40
-rw-r--r--src/PlaylistMapper.hxx (renamed from src/playlist_mapper.h)11
-rw-r--r--src/PlaylistPlugin.hxx (renamed from src/playlist_plugin.h)14
-rw-r--r--src/PlaylistPrint.cxx (renamed from src/playlist_print.c)132
-rw-r--r--src/PlaylistPrint.hxx (renamed from src/playlist_print.h)41
-rw-r--r--src/PlaylistQueue.cxx (renamed from src/playlist_queue.c)37
-rw-r--r--src/PlaylistQueue.hxx (renamed from src/playlist_queue.h)8
-rw-r--r--src/PlaylistRegistry.cxx (renamed from src/playlist_list.c)57
-rw-r--r--src/PlaylistRegistry.hxx (renamed from src/playlist_list.h)15
-rw-r--r--src/PlaylistSave.cxx (renamed from src/playlist_save.c)102
-rw-r--r--src/PlaylistSave.hxx (renamed from src/playlist_save.h)3
-rw-r--r--src/PlaylistSong.cxx (renamed from src/playlist_song.c)50
-rw-r--r--src/PlaylistSong.hxx (renamed from src/playlist_song.h)8
-rw-r--r--src/PlaylistState.cxx (renamed from src/playlist_state.c)105
-rw-r--r--src/PlaylistState.hxx (renamed from src/playlist_state.h)11
-rw-r--r--src/PlaylistVector.cxx68
-rw-r--r--src/PlaylistVector.hxx56
-rw-r--r--src/Queue.cxx499
-rw-r--r--src/Queue.hxx368
-rw-r--r--src/QueueCommands.cxx375
-rw-r--r--src/QueueCommands.hxx84
-rw-r--r--src/QueuePrint.cxx107
-rw-r--r--src/QueuePrint.hxx (renamed from src/queue_print.h)26
-rw-r--r--src/QueueSave.cxx (renamed from src/queue_save.c)65
-rw-r--r--src/QueueSave.hxx (renamed from src/queue_save.h)11
-rw-r--r--src/ReplayGainConfig.cxx (renamed from src/replay_gain_config.c)21
-rw-r--r--src/ReplayGainInfo.cxx (renamed from src/replay_gain_info.c)2
-rw-r--r--src/SignalHandlers.cxx (renamed from src/sig_handlers.c)15
-rw-r--r--src/SignalHandlers.hxx25
-rw-r--r--src/SocketError.hxx156
-rw-r--r--src/SocketUtil.cxx (renamed from src/socket_util.c)31
-rw-r--r--src/SocketUtil.hxx (renamed from src/socket_util.h)10
-rw-r--r--src/Song.cxx (renamed from src/song.c)99
-rw-r--r--src/SongFilter.cxx166
-rw-r--r--src/SongFilter.hxx107
-rw-r--r--src/SongPointer.hxx (renamed from src/notify.h)60
-rw-r--r--src/SongPrint.cxx (renamed from src/song_print.c)54
-rw-r--r--src/SongPrint.hxx32
-rw-r--r--src/SongSave.cxx (renamed from src/song_save.c)19
-rw-r--r--src/SongSave.hxx (renamed from src/song_save.h)15
-rw-r--r--src/SongSticker.cxx (renamed from src/song_sticker.c)47
-rw-r--r--src/SongSticker.hxx (renamed from src/song_sticker.h)17
-rw-r--r--src/SongUpdate.cxx (renamed from src/song_update.c)66
-rw-r--r--src/StateFile.cxx124
-rw-r--r--src/StateFile.hxx (renamed from src/state_file.h)41
-rw-r--r--src/Stats.cxx89
-rw-r--r--src/StickerCommands.cxx176
-rw-r--r--src/StickerCommands.hxx30
-rw-r--r--src/StickerDatabase.cxx (renamed from src/sticker.c)95
-rw-r--r--src/StickerDatabase.hxx (renamed from src/sticker.h)18
-rw-r--r--src/StickerPrint.cxx (renamed from src/sticker_print.c)16
-rw-r--r--src/StickerPrint.hxx (renamed from src/sticker_print.h)13
-rw-r--r--src/Tag.cxx (renamed from src/tag.c)135
-rw-r--r--src/TagFile.cxx (renamed from src/tag_file.c)24
-rw-r--r--src/TagFile.hxx (renamed from src/tag_file.h)6
-rw-r--r--src/TagInternal.hxx (renamed from src/tag_internal.h)8
-rw-r--r--src/TagNames.c44
-rw-r--r--src/TagPool.cxx (renamed from src/tag_pool.c)32
-rw-r--r--src/TagPool.hxx (renamed from src/tag_pool.h)15
-rw-r--r--src/TagPrint.cxx (renamed from src/tag_print.c)12
-rw-r--r--src/TagPrint.hxx (renamed from src/tag_print.h)12
-rw-r--r--src/TagSave.cxx (renamed from src/tag_save.c)6
-rw-r--r--src/TagSave.hxx (renamed from src/tag_save.h)6
-rw-r--r--src/TextFile.cxx (renamed from src/text_file.c)15
-rw-r--r--src/TextFile.hxx67
-rw-r--r--src/TimePrint.cxx (renamed from src/glib_socket.h)33
-rw-r--r--src/TimePrint.hxx33
-rw-r--r--src/UpdateArchive.cxx (renamed from src/update_archive.c)75
-rw-r--r--src/UpdateArchive.hxx (renamed from src/update_archive.h)22
-rw-r--r--src/UpdateContainer.cxx (renamed from src/update_container.c)50
-rw-r--r--src/UpdateContainer.hxx (renamed from src/update_container.h)11
-rw-r--r--src/UpdateDatabase.cxx (renamed from src/update_db.c)30
-rw-r--r--src/UpdateDatabase.hxx (renamed from src/update_db.h)16
-rw-r--r--src/UpdateGlue.cxx (renamed from src/update.c)39
-rw-r--r--src/UpdateGlue.hxx (renamed from src/update.h)8
-rw-r--r--src/UpdateIO.cxx113
-rw-r--r--src/UpdateIO.hxx (renamed from src/update_io.h)19
-rw-r--r--src/UpdateInternal.hxx (renamed from src/update_internal.h)2
-rw-r--r--src/UpdateQueue.cxx (renamed from src/update_queue.c)4
-rw-r--r--src/UpdateQueue.hxx (renamed from src/update_queue.h)8
-rw-r--r--src/UpdateRemove.cxx (renamed from src/update_remove.c)48
-rw-r--r--src/UpdateRemove.hxx (renamed from src/update_remove.h)9
-rw-r--r--src/UpdateSong.cxx (renamed from src/update_song.c)39
-rw-r--r--src/UpdateSong.hxx (renamed from src/update_song.h)11
-rw-r--r--src/UpdateWalk.cxx (renamed from src/update_walk.c)169
-rw-r--r--src/UpdateWalk.hxx (renamed from src/update_walk.h)8
-rw-r--r--src/Volume.cxx (renamed from src/volume.c)21
-rw-r--r--src/Volume.hxx (renamed from src/volume.h)7
-rw-r--r--src/Win32Main.cxx (renamed from src/main_win32.c)35
-rw-r--r--src/ZeroconfAvahi.cxx (renamed from src/zeroconf-avahi.c)27
-rw-r--r--src/ZeroconfAvahi.hxx31
-rw-r--r--src/ZeroconfBonjour.cxx (renamed from src/zeroconf-bonjour.c)69
-rw-r--r--src/ZeroconfBonjour.hxx31
-rw-r--r--src/ZeroconfGlue.cxx (renamed from src/zeroconf.c)22
-rw-r--r--src/ZeroconfGlue.hxx (renamed from src/zeroconf.h)24
-rw-r--r--src/ZeroconfInternal.hxx (renamed from src/zeroconf-internal.h)8
-rw-r--r--src/archive/Bzip2ArchivePlugin.cxx298
-rw-r--r--src/archive/Bzip2ArchivePlugin.hxx (renamed from src/archive/bz2_archive_plugin.h)6
-rw-r--r--src/archive/Iso9660ArchivePlugin.cxx266
-rw-r--r--src/archive/Iso9660ArchivePlugin.hxx (renamed from src/archive/iso9660_archive_plugin.h)6
-rw-r--r--src/archive/ZzipArchivePlugin.cxx230
-rw-r--r--src/archive/ZzipArchivePlugin.hxx (renamed from src/archive/zzip_archive_plugin.h)6
-rw-r--r--src/archive/bz2_archive_plugin.c314
-rw-r--r--src/archive/iso9660_archive_plugin.c290
-rw-r--r--src/archive/zzip_archive_plugin.c246
-rw-r--r--src/archive_internal.h34
-rw-r--r--src/archive_plugin.c94
-rw-r--r--src/audio_check.h10
-rw-r--r--src/audio_format.h27
-rw-r--r--src/buffer.c137
-rw-r--r--src/chunk.c102
-rw-r--r--src/client_event.c108
-rw-r--r--src/client_expire.c90
-rw-r--r--src/client_idle.c96
-rw-r--r--src/client_idle.h45
-rw-r--r--src/client_internal.h175
-rw-r--r--src/client_message.c96
-rw-r--r--src/client_message.h72
-rw-r--r--src/client_read.c113
-rw-r--r--src/client_subscribe.c123
-rw-r--r--src/client_write.c284
-rw-r--r--src/clock.h6
-rw-r--r--src/command.c2298
-rw-r--r--src/command.h49
-rw-r--r--src/conf.c666
-rw-r--r--src/conf.h202
-rw-r--r--src/cue/cue_parser.c2
-rw-r--r--src/db/ProxyDatabasePlugin.cxx475
-rw-r--r--src/db/ProxyDatabasePlugin.hxx27
-rw-r--r--src/db/SimpleDatabasePlugin.cxx349
-rw-r--r--src/db/SimpleDatabasePlugin.hxx102
-rw-r--r--src/db/simple_db_plugin.c357
-rw-r--r--src/dbUtils.c209
-rw-r--r--src/dbUtils.h56
-rw-r--r--src/db_plugin.h156
-rw-r--r--src/db_print.c393
-rw-r--r--src/db_visitor.h54
-rw-r--r--src/decoder/AdPlugDecoderPlugin.cxx148
-rw-r--r--src/decoder/AdPlugDecoderPlugin.h25
-rw-r--r--src/decoder/FLACCommon.cxx (renamed from src/decoder/_flac_common.c)67
-rw-r--r--src/decoder/FLACCommon.hxx (renamed from src/decoder/_flac_common.h)30
-rw-r--r--src/decoder/FLACDecoderPlugin.cxx (renamed from src/decoder/flac_decoder_plugin.c)291
-rw-r--r--src/decoder/FLACDecoderPlugin.h26
-rw-r--r--src/decoder/FLACIOHandle.cxx114
-rw-r--r--src/decoder/FLACIOHandle.hxx45
-rw-r--r--src/decoder/FLACInput.cxx149
-rw-r--r--src/decoder/FLACInput.hxx72
-rw-r--r--src/decoder/FLACMetaData.cxx (renamed from src/decoder/flac_metadata.c)136
-rw-r--r--src/decoder/FLACMetaData.hxx140
-rw-r--r--src/decoder/FLAC_PCM.cxx (renamed from src/decoder/flac_pcm.c)4
-rw-r--r--src/decoder/FLAC_PCM.hxx (renamed from src/decoder/flac_pcm.h)6
-rw-r--r--src/decoder/FfmpegDecoderPlugin.cxx (renamed from src/decoder/ffmpeg_decoder_plugin.c)122
-rw-r--r--src/decoder/FfmpegDecoderPlugin.hxx25
-rw-r--r--src/decoder/FfmpegMetaData.cxx (renamed from src/decoder/ffmpeg_metadata.c)18
-rw-r--r--src/decoder/FfmpegMetaData.hxx (renamed from src/decoder/ffmpeg_metadata.h)16
-rw-r--r--src/decoder/OggCodec.cxx (renamed from src/decoder/_ogg_common.c)20
-rw-r--r--src/decoder/OggCodec.hxx (renamed from src/decoder/_ogg_common.h)16
-rw-r--r--src/decoder/OggFind.cxx37
-rw-r--r--src/decoder/OggFind.hxx38
-rw-r--r--src/decoder/OggSyncState.hxx78
-rw-r--r--src/decoder/OggUtil.cxx118
-rw-r--r--src/decoder/OggUtil.hxx87
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx400
-rw-r--r--src/decoder/OpusDecoderPlugin.h25
-rw-r--r--src/decoder/OpusHead.cxx44
-rw-r--r--src/decoder/OpusHead.hxx30
-rw-r--r--src/decoder/OpusReader.hxx98
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/VorbisComments.cxx (renamed from src/decoder/vorbis_comments.c)16
-rw-r--r--src/decoder/VorbisComments.hxx (renamed from src/decoder/vorbis_comments.h)6
-rw-r--r--src/decoder/VorbisDecoderPlugin.cxx (renamed from src/decoder/vorbis_decoder_plugin.c)157
-rw-r--r--src/decoder/VorbisDecoderPlugin.h25
-rw-r--r--src/decoder/WavpackDecoderPlugin.cxx (renamed from src/decoder/wavpack_decoder_plugin.c)65
-rw-r--r--src/decoder/WavpackDecoderPlugin.hxx25
-rw-r--r--src/decoder/XiphTags.c28
-rw-r--r--src/decoder/XiphTags.h28
-rw-r--r--src/decoder/audiofile_decoder_plugin.c10
-rw-r--r--src/decoder/dsdiff_decoder_plugin.c151
-rw-r--r--src/decoder/dsdlib.c72
-rw-r--r--src/decoder/dsdlib.h5
-rw-r--r--src/decoder/dsf_decoder_plugin.c30
-rw-r--r--src/decoder/faad_decoder_plugin.c72
-rw-r--r--src/decoder/flac_compat.h114
-rw-r--r--src/decoder/flac_metadata.h64
-rw-r--r--src/decoder/mad_decoder_plugin.c21
-rw-r--r--src/decoder/modplug_decoder_plugin.c13
-rw-r--r--src/decoder/mp4ff_decoder_plugin.c448
-rw-r--r--src/decoder/mpcdec_decoder_plugin.c8
-rw-r--r--src/decoder/pcm_decoder_plugin.c13
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx10
-rw-r--r--src/decoder/sndfile_decoder_plugin.c6
-rw-r--r--src/decoder_api.h14
-rw-r--r--src/decoder_control.c190
-rw-r--r--src/decoder_control.h277
-rw-r--r--src/decoder_error.h35
-rw-r--r--src/decoder_plugin.h8
-rw-r--r--src/directory.c314
-rw-r--r--src/directory.h262
-rw-r--r--src/dsd2pcm/dsd2pcm.hpp6
-rw-r--r--src/dsd2pcm/noiseshape.hpp9
-rw-r--r--src/dummy.cxx30
-rw-r--r--src/encoder/OggStream.hxx128
-rw-r--r--src/encoder/OpusEncoderPlugin.cxx429
-rw-r--r--src/encoder/OpusEncoderPlugin.hxx25
-rw-r--r--src/encoder/VorbisEncoderPlugin.cxx (renamed from src/encoder/vorbis_encoder.c)130
-rw-r--r--src/encoder/VorbisEncoderPlugin.hxx25
-rw-r--r--src/encoder/flac_encoder.c35
-rw-r--r--src/encoder/lame_encoder.c7
-rw-r--r--src/encoder/null_encoder.c23
-rw-r--r--src/encoder/twolame_encoder.c9
-rw-r--r--src/encoder/wave_encoder.c24
-rw-r--r--src/encoder_list.c8
-rw-r--r--src/encoder_list.h8
-rw-r--r--src/encoder_plugin.h2
-rw-r--r--src/event/BufferedSocket.cxx150
-rw-r--r--src/event/BufferedSocket.hxx104
-rw-r--r--src/event/FullyBufferedSocket.cxx132
-rw-r--r--src/event/FullyBufferedSocket.hxx63
-rw-r--r--src/event/Loop.hxx88
-rw-r--r--src/event/MultiSocketMonitor.cxx107
-rw-r--r--src/event/MultiSocketMonitor.hxx125
-rw-r--r--src/event/ServerSocket.cxx438
-rw-r--r--src/event/ServerSocket.hxx121
-rw-r--r--src/event/SocketMonitor.cxx167
-rw-r--r--src/event/SocketMonitor.hxx148
-rw-r--r--src/event/TimeoutMonitor.cxx65
-rw-r--r--src/event/TimeoutMonitor.hxx60
-rw-r--r--src/event/WakeFD.cxx225
-rw-r--r--src/event/WakeFD.hxx80
-rw-r--r--src/event_pipe.c164
-rw-r--r--src/event_pipe.h71
-rw-r--r--src/fd_util.c14
-rw-r--r--src/fd_util.h19
-rw-r--r--src/filter/AutoConvertFilterPlugin.cxx132
-rw-r--r--src/filter/AutoConvertFilterPlugin.hxx (renamed from src/filter/autoconvert_filter_plugin.h)12
-rw-r--r--src/filter/ChainFilterPlugin.cxx184
-rw-r--r--src/filter/ChainFilterPlugin.hxx (renamed from src/filter/chain_filter_plugin.h)12
-rw-r--r--src/filter/ConvertFilterPlugin.cxx119
-rw-r--r--src/filter/ConvertFilterPlugin.hxx (renamed from src/filter/convert_filter_plugin.h)11
-rw-r--r--src/filter/NormalizeFilterPlugin.cxx84
-rw-r--r--src/filter/NullFilterPlugin.cxx60
-rw-r--r--src/filter/ReplayGainFilterPlugin.cxx242
-rw-r--r--src/filter/ReplayGainFilterPlugin.hxx (renamed from src/filter/replay_gain_filter_plugin.h)16
-rw-r--r--src/filter/RouteFilterPlugin.cxx (renamed from src/filter/route_filter_plugin.c)177
-rw-r--r--src/filter/VolumeFilterPlugin.cxx148
-rw-r--r--src/filter/VolumeFilterPlugin.hxx (renamed from src/filter/volume_filter_plugin.h)12
-rw-r--r--src/filter/autoconvert_filter_plugin.c169
-rw-r--r--src/filter/chain_filter_plugin.c213
-rw-r--r--src/filter/convert_filter_plugin.c146
-rw-r--r--src/filter/normalize_filter_plugin.c112
-rw-r--r--src/filter/null_filter_plugin.c93
-rw-r--r--src/filter/replay_gain_filter_plugin.c245
-rw-r--r--src/filter/volume_filter_plugin.c158
-rw-r--r--src/filter_plugin.h150
-rw-r--r--src/fs/DirectoryReader.hxx87
-rw-r--r--src/fs/FileSystem.cxx43
-rw-r--r--src/fs/FileSystem.hxx163
-rw-r--r--src/fs/Path.cxx (renamed from src/path.c)98
-rw-r--r--src/fs/Path.hxx267
-rw-r--r--src/gcc.h41
-rw-r--r--src/gerror.h (renamed from src/sig_handlers.h)6
-rw-r--r--src/glib_compat.h11
-rw-r--r--src/icy_metadata.h99
-rw-r--r--src/inotify_source.h61
-rw-r--r--src/input/ArchiveInputPlugin.cxx (renamed from src/input/archive_input_plugin.c)40
-rw-r--r--src/input/ArchiveInputPlugin.hxx (renamed from src/input/archive_input_plugin.h)6
-rw-r--r--src/input/CdioParanoiaInputPlugin.cxx (renamed from src/input/cdio_paranoia_input_plugin.c)114
-rw-r--r--src/input/CdioParanoiaInputPlugin.hxx (renamed from src/input/cdio_paranoia_input_plugin.h)6
-rw-r--r--src/input/CurlInputPlugin.cxx (renamed from src/input/curl_input_plugin.c)596
-rw-r--r--src/input/CurlInputPlugin.hxx (renamed from src/input/curl_input_plugin.h)6
-rw-r--r--src/input/DespotifyInputPlugin.cxx (renamed from src/input/despotify_input_plugin.c)107
-rw-r--r--src/input/DespotifyInputPlugin.hxx (renamed from src/input/despotify_input_plugin.h)6
-rw-r--r--src/input/FfmpegInputPlugin.cxx (renamed from src/input/ffmpeg_input_plugin.c)136
-rw-r--r--src/input/FfmpegInputPlugin.hxx (renamed from src/input/ffmpeg_input_plugin.h)6
-rw-r--r--src/input/FileInputPlugin.cxx (renamed from src/input/file_input_plugin.c)92
-rw-r--r--src/input/FileInputPlugin.hxx (renamed from src/input/file_input_plugin.h)6
-rw-r--r--src/input/MmsInputPlugin.cxx (renamed from src/input/mms_input_plugin.c)81
-rw-r--r--src/input/MmsInputPlugin.hxx (renamed from src/input/mms_input_plugin.h)0
-rw-r--r--src/input/RewindInputPlugin.cxx (renamed from src/input/rewind_input_plugin.c)148
-rw-r--r--src/input/RewindInputPlugin.hxx (renamed from src/input/rewind_input_plugin.h)6
-rw-r--r--src/input/SoupInputPlugin.cxx (renamed from src/input/soup_input_plugin.c)287
-rw-r--r--src/input/SoupInputPlugin.hxx (renamed from src/input/soup_input_plugin.h)6
-rw-r--r--src/input_internal.c73
-rw-r--r--src/input_stream.h112
-rw-r--r--src/io_error.h51
-rw-r--r--src/locate.c239
-rw-r--r--src/locate.h92
-rw-r--r--src/ls.cxx (renamed from src/ls.c)14
-rw-r--r--src/ls.hxx (renamed from src/ls.h)11
-rw-r--r--src/mapper.c275
-rw-r--r--src/mixer/AlsaMixerPlugin.cxx (renamed from src/mixer/alsa_mixer_plugin.c)191
-rw-r--r--src/mixer/OssMixerPlugin.cxx (renamed from src/mixer/oss_mixer_plugin.c)18
-rw-r--r--src/mixer/PulseMixerPlugin.cxx (renamed from src/mixer/pulse_mixer_plugin.c)32
-rw-r--r--src/mixer/PulseMixerPlugin.h (renamed from src/mixer/pulse_mixer_plugin.h)10
-rw-r--r--src/mixer/RoarMixerPlugin.cxx (renamed from src/mixer/roar_mixer_plugin.c)78
-rw-r--r--src/mixer/SoftwareMixerPlugin.cxx (renamed from src/mixer/software_mixer_plugin.c)30
-rw-r--r--src/mixer/SoftwareMixerPlugin.hxx (renamed from src/mixer/software_mixer_plugin.h)10
-rw-r--r--src/mixer/WinmmMixerPlugin.cxx (renamed from src/mixer/winmm_mixer_plugin.c)25
-rw-r--r--src/mpd_error.h1
-rw-r--r--src/notify.c58
-rw-r--r--src/notify.cxx45
-rw-r--r--src/notify.hxx53
-rw-r--r--src/output/AlsaOutputPlugin.cxx (renamed from src/output/alsa_output_plugin.c)212
-rw-r--r--src/output/AlsaOutputPlugin.hxx (renamed from src/output/alsa_output_plugin.h)6
-rw-r--r--src/output/HttpdClient.cxx444
-rw-r--r--src/output/HttpdClient.hxx186
-rw-r--r--src/output/HttpdInternal.hxx (renamed from src/output/httpd_internal.h)122
-rw-r--r--src/output/HttpdOutputPlugin.cxx570
-rw-r--r--src/output/HttpdOutputPlugin.hxx (renamed from src/output/httpd_output_plugin.h)6
-rw-r--r--src/output/NullOutputPlugin.cxx (renamed from src/output/null_output_plugin.c)43
-rw-r--r--src/output/NullOutputPlugin.hxx (renamed from src/output/null_output_plugin.h)6
-rw-r--r--src/output/OSXOutputPlugin.cxx (renamed from src/output/osx_output_plugin.c)89
-rw-r--r--src/output/OSXOutputPlugin.hxx (renamed from src/output/osx_output_plugin.h)6
-rw-r--r--src/output/OssOutputPlugin.cxx (renamed from src/output/oss_output_plugin.c)67
-rw-r--r--src/output/OssOutputPlugin.hxx (renamed from src/output/oss_output_plugin.h)6
-rw-r--r--src/output/RoarOutputPlugin.cxx (renamed from src/output/roar_output_plugin.c)150
-rw-r--r--src/output/RoarOutputPlugin.hxx (renamed from src/output/roar_output_plugin.h)10
-rw-r--r--src/output/WinmmOutputPlugin.cxx (renamed from src/output/winmm_output_plugin.c)87
-rw-r--r--src/output/WinmmOutputPlugin.hxx (renamed from src/output/winmm_output_plugin.h)15
-rw-r--r--src/output/fifo_output_plugin.c1
-rw-r--r--src/output/httpd_client.c764
-rw-r--r--src/output/httpd_client.h71
-rw-r--r--src/output/httpd_output_plugin.c623
-rw-r--r--src/output/pulse_output_plugin.c4
-rw-r--r--src/output/pulse_output_plugin.h12
-rw-r--r--src/output/shout_output_plugin.c104
-rw-r--r--src/output_internal.h29
-rw-r--r--src/output_plugin.h15
-rw-r--r--src/page.c89
-rw-r--r--src/path.h55
-rw-r--r--src/pcm_buffer.h8
-rw-r--r--src/pcm_convert.c379
-rw-r--r--src/pcm_convert.h94
-rw-r--r--src/pcm_dither.c93
-rw-r--r--src/pcm_export.h8
-rw-r--r--src/pcm_mix.c280
-rw-r--r--src/pcm_utils.h94
-rw-r--r--src/player_control.c391
-rw-r--r--src/player_control.h287
-rw-r--r--src/playlist.c452
-rw-r--r--src/playlist.h256
-rw-r--r--src/playlist/AsxPlaylistPlugin.cxx (renamed from src/playlist/asx_playlist_plugin.c)126
-rw-r--r--src/playlist/AsxPlaylistPlugin.hxx (renamed from src/playlist/asx_playlist_plugin.h)6
-rw-r--r--src/playlist/CuePlaylistPlugin.cxx (renamed from src/playlist/cue_playlist_plugin.c)36
-rw-r--r--src/playlist/CuePlaylistPlugin.hxx (renamed from src/playlist/cue_playlist_plugin.h)6
-rw-r--r--src/playlist/DespotifyPlaylistPlugin.cxx146
-rw-r--r--src/playlist/DespotifyPlaylistPlugin.hxx (renamed from src/playlist/despotify_playlist_plugin.h)6
-rw-r--r--src/playlist/EmbeddedCuePlaylistPlugin.cxx (renamed from src/playlist/embcue_playlist_plugin.c)43
-rw-r--r--src/playlist/EmbeddedCuePlaylistPlugin.hxx (renamed from src/playlist/embcue_playlist_plugin.h)6
-rw-r--r--src/playlist/ExtM3uPlaylistPlugin.cxx (renamed from src/playlist/extm3u_playlist_plugin.c)45
-rw-r--r--src/playlist/ExtM3uPlaylistPlugin.hxx (renamed from src/playlist/extm3u_playlist_plugin.h)6
-rw-r--r--src/playlist/LastFMPlaylistPlugin.cxx (renamed from src/playlist/lastfm_playlist_plugin.c)43
-rw-r--r--src/playlist/LastFMPlaylistPlugin.hxx (renamed from src/playlist/lastfm_playlist_plugin.h)6
-rw-r--r--src/playlist/M3uPlaylistPlugin.cxx (renamed from src/playlist/m3u_playlist_plugin.c)36
-rw-r--r--src/playlist/M3uPlaylistPlugin.hxx (renamed from src/playlist/m3u_playlist_plugin.h)6
-rw-r--r--src/playlist/MemoryPlaylistProvider.cxx69
-rw-r--r--src/playlist/MemoryPlaylistProvider.hxx (renamed from src/db/simple_db_plugin.h)29
-rw-r--r--src/playlist/PlsPlaylistPlugin.cxx (renamed from src/playlist/pls_playlist_plugin.c)81
-rw-r--r--src/playlist/PlsPlaylistPlugin.hxx (renamed from src/playlist/pls_playlist_plugin.h)6
-rw-r--r--src/playlist/RssPlaylistPlugin.cxx (renamed from src/playlist/rss_playlist_plugin.c)125
-rw-r--r--src/playlist/RssPlaylistPlugin.hxx (renamed from src/playlist/rss_playlist_plugin.h)6
-rw-r--r--src/playlist/SoundCloudPlaylistPlugin.cxx (renamed from src/playlist/soundcloud_playlist_plugin.c)78
-rw-r--r--src/playlist/SoundCloudPlaylistPlugin.hxx (renamed from src/playlist/soundcloud_playlist_plugin.h)6
-rw-r--r--src/playlist/XspfPlaylistPlugin.cxx (renamed from src/playlist/xspf_playlist_plugin.c)156
-rw-r--r--src/playlist/XspfPlaylistPlugin.hxx (renamed from src/playlist/xspf_playlist_plugin.h)6
-rw-r--r--src/playlist/despotify_playlist_plugin.c217
-rw-r--r--src/playlist_control.c288
-rw-r--r--src/playlist_edit.c487
-rw-r--r--src/playlist_internal.h56
-rw-r--r--src/playlist_vector.c114
-rw-r--r--src/playlist_vector.h80
-rw-r--r--src/protocol/ArgParser.cxx (renamed from src/protocol/argparser.c)18
-rw-r--r--src/protocol/ArgParser.hxx (renamed from src/protocol/argparser.h)20
-rw-r--r--src/protocol/Result.cxx (renamed from src/protocol/result.c)12
-rw-r--r--src/protocol/Result.hxx (renamed from src/protocol/result.h)19
-rw-r--r--src/queue.c603
-rw-r--r--src/queue.h379
-rw-r--r--src/queue_print.c122
-rw-r--r--src/replay_gain_config.h10
-rw-r--r--src/replay_gain_info.h16
-rw-r--r--src/resolver.h14
-rw-r--r--src/server_socket.c483
-rw-r--r--src/server_socket.h92
-rw-r--r--src/song.h73
-rw-r--r--src/state_file.c162
-rw-r--r--src/stats.c128
-rw-r--r--src/stats.h5
-rw-r--r--src/string_util.c36
-rw-r--r--src/string_util.h46
-rw-r--r--src/strset.c148
-rw-r--r--src/strset.h50
-rw-r--r--src/tag.h10
-rw-r--r--src/tag_id3.c5
-rw-r--r--src/tag_id3.h18
-rw-r--r--src/tag_table.h5
-rw-r--r--src/text_file.h39
-rw-r--r--src/text_input_stream.c2
-rw-r--r--src/thread/Cond.hxx37
-rw-r--r--src/thread/CriticalSection.hxx68
-rw-r--r--src/thread/GLibCond.hxx88
-rw-r--r--src/thread/GLibMutex.hxx90
-rw-r--r--src/thread/Mutex.hxx54
-rw-r--r--src/thread/PosixCond.hxx60
-rw-r--r--src/thread/PosixMutex.hxx62
-rw-r--r--src/thread/WindowsCond.hxx63
-rw-r--r--src/timer.h8
-rw-r--r--src/tokenizer.c2
-rw-r--r--src/tokenizer.h2
-rw-r--r--src/update_io.c117
-rw-r--r--src/uri.h10
-rw-r--r--src/util/HugeAllocator.cxx87
-rw-r--r--src/util/HugeAllocator.hxx82
-rw-r--r--src/util/LazyRandomEngine.cxx31
-rw-r--r--src/util/LazyRandomEngine.hxx67
-rw-r--r--src/util/Manual.hxx111
-rw-r--r--src/util/PeakBuffer.cxx143
-rw-r--r--src/util/PeakBuffer.hxx66
-rw-r--r--src/util/RefCount.hxx (renamed from src/refcount.h)44
-rw-r--r--src/util/SliceBuffer.hxx161
-rw-r--r--src/util/bit_reverse.h5
-rw-r--r--src/util/fifo_buffer.c (renamed from src/fifo_buffer.c)8
-rw-r--r--src/util/fifo_buffer.h (renamed from src/fifo_buffer.h)11
-rw-r--r--src/util/growing_fifo.c (renamed from src/growing_fifo.c)0
-rw-r--r--src/util/growing_fifo.h (renamed from src/growing_fifo.h)0
-rw-r--r--src/util/list.h53
-rw-r--r--src/utils.c1
-rw-r--r--src/utils.h12
-rw-r--r--test/DumpDatabase.cxx147
-rw-r--r--test/FakeReplayGainConfig.cxx25
-rw-r--r--test/FakeSong.cxx33
-rw-r--r--test/dump_playlist.cxx (renamed from test/dump_playlist.c)39
-rw-r--r--test/dump_rva2.c9
-rw-r--r--test/dump_text_file.cxx (renamed from test/dump_text_file.c)23
-rw-r--r--test/read_conf.cxx (renamed from test/read_conf.c)13
-rw-r--r--test/read_mixer.cxx (renamed from test/read_mixer.c)26
-rw-r--r--test/read_tags.cxx (renamed from test/read_tags.c)67
-rw-r--r--test/run_convert.cxx (renamed from test/run_convert.c)20
-rw-r--r--test/run_decoder.cxx (renamed from test/run_decoder.c)44
-rw-r--r--test/run_encoder.cxx (renamed from test/run_encoder.c)13
-rw-r--r--test/run_filter.cxx (renamed from test/run_filter.c)62
-rw-r--r--test/run_inotify.cxx (renamed from test/run_inotify.c)31
-rw-r--r--test/run_input.cxx (renamed from test/run_input.c)30
-rw-r--r--test/run_normalize.cxx (renamed from test/run_normalize.c)4
-rw-r--r--test/run_output.cxx (renamed from test/run_output.c)78
-rw-r--r--test/run_tcp_connect.c164
-rw-r--r--test/software_volume.cxx (renamed from test/software_volume.c)9
-rw-r--r--test/test_pcm_all.hxx (renamed from test/test_pcm_all.h)52
-rw-r--r--test/test_pcm_channels.cxx (renamed from test/test_pcm_channels.c)25
-rw-r--r--test/test_pcm_dither.cxx (renamed from test/test_pcm_dither.c)51
-rw-r--r--test/test_pcm_format.cxx130
-rw-r--r--test/test_pcm_main.cxx (renamed from test/test_pcm_main.c)14
-rw-r--r--test/test_pcm_mix.cxx84
-rw-r--r--test/test_pcm_pack.cxx (renamed from test/test_pcm_pack.c)42
-rw-r--r--test/test_pcm_util.hxx85
-rw-r--r--test/test_pcm_volume.cxx (renamed from test/test_pcm_volume.c)101
-rw-r--r--test/test_queue_priority.c175
-rw-r--r--test/test_queue_priority.cxx188
-rw-r--r--test/test_vorbis_encoder.cxx (renamed from test/test_vorbis_encoder.c)8
-rw-r--r--test/visit_archive.cxx123
-rw-r--r--valgrind.suppressions42
688 files changed, 33651 insertions, 27139 deletions
diff --git a/.gitignore b/.gitignore
index 11ac5f9a..05904c1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,3 +72,4 @@ test/dump_rva2
test/dump_text_file
test/test_byte_reverse
test/test_vorbis_encoder
+test/DumpDatabase
diff --git a/INSTALL b/INSTALL
index fdebbe71..5310f637 100644
--- a/INSTALL
+++ b/INSTALL
@@ -81,14 +81,17 @@ Alternative for MP3 support.
Ogg Vorbis - http://www.xiph.org/ogg/vorbis/
For Ogg Vorbis support. You will need libogg and libvorbis.
+libopus - http://www.opus-codec.org/
+Opus codec support
+
FLAC - http://flac.sourceforge.net/
-For FLAC support. You will need version 1.1.0 or higher of libflac.
+For FLAC support. You will need version 1.2 or higher of libFLAC.
Audio File - http://www.68k.org/~michael/audiofile/
For WAVE, AIFF, and AU support. You will need libaudiofile.
FAAD2 - http://www.audiocoding.com/
-For MP4/AAC support. You will need libmp4ff.
+For MP4/AAC support.
libmpcdec - http://www.musepack.net/
For Musepack support.
@@ -114,6 +117,9 @@ WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
+libadplug - http://adplug.sourceforge.net/
+For AdLib playback.
+
despotify - https://github.com/SimonKagstrom/despotify
For Spotify playback.
diff --git a/Makefile.am b/Makefile.am
index a0b32f8b..2e302929 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,9 +9,13 @@ bin_PROGRAMS = src/mpd
noinst_LIBRARIES = \
libutil.a \
+ libevent.a \
libpcm.a \
+ libconf.a \
libtag.a \
libinput.a \
+ libfs.a \
+ libdb_plugins.a \
libplaylist_plugins.a \
libdecoder_plugins.a \
libfilter_plugins.a \
@@ -19,10 +23,12 @@ noinst_LIBRARIES = \
liboutput_plugins.a
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
+ $(LIBMPDCLIENT_CFLAGS) \
$(AVAHI_CFLAGS) \
$(LIBWRAP_CFLAGS) \
$(SQLITE_CFLAGS)
src_mpd_LDADD = \
+ $(DB_LIBS) \
$(PLAYLIST_LIBS) \
$(AVAHI_LIBS) \
$(LIBWRAP_LDFLAGS) \
@@ -35,190 +41,68 @@ src_mpd_LDADD = \
$(FILTER_LIBS) \
$(ENCODER_LIBS) \
$(MIXER_LIBS) \
+ libconf.a \
+ libevent.a \
libutil.a \
+ libfs.a \
$(SYSTEMD_DAEMON_LIBS) \
$(GLIB_LIBS)
mpd_headers = \
src/check.h \
- src/notify.h \
src/ack.h \
src/ape.h \
src/audio_format.h \
src/audio_check.h \
- src/audio_parser.h \
src/output_internal.h \
src/output_api.h \
- src/output_list.h \
- src/output_all.h \
- src/output_thread.h \
- src/output_control.h \
- src/output_state.h \
- src/output_print.h \
- src/output_command.h \
src/filter_internal.h \
- src/filter_config.h \
- src/filter_plugin.h \
- src/filter_registry.h \
- src/filter/autoconvert_filter_plugin.h \
- src/filter/chain_filter_plugin.h \
- src/filter/convert_filter_plugin.h \
- src/filter/replay_gain_filter_plugin.h \
- src/filter/volume_filter_plugin.h \
src/command.h \
- src/idle.h \
- src/cmdline.h \
src/conf.h \
- src/crossfade.h \
- src/dbUtils.h \
- src/decoder_thread.h \
- src/decoder_control.h \
src/decoder_plugin.h \
src/decoder_command.h \
src/decoder_buffer.h \
src/decoder_api.h \
src/decoder_plugin.h \
- src/decoder_internal.h \
- src/directory.h \
- src/directory_save.h \
- src/database.h \
src/encoder_plugin.h \
src/encoder_list.h \
src/encoder_api.h \
- src/exclude.h \
src/fd_util.h \
+ src/gerror.h \
src/glib_compat.h \
- src/update.h \
- src/inotify_source.h \
- src/inotify_queue.h \
- src/inotify_update.h \
src/gcc.h \
- src/decoder_list.h \
- src/decoder_print.h \
- src/decoder/flac_compat.h \
- src/decoder/flac_metadata.h \
- src/decoder/flac_pcm.h \
- src/decoder/_flac_common.h \
- src/decoder/_ogg_common.h \
src/decoder/pcm_decoder_plugin.h \
- src/input_init.h \
- src/input_plugin.h \
- src/input_registry.h \
src/input_stream.h \
- src/input/file_input_plugin.h \
- src/input/ffmpeg_input_plugin.h \
- src/input/curl_input_plugin.h \
- src/input/rewind_input_plugin.h \
- src/input/mms_input_plugin.h \
- src/input/despotify_input_plugin.h \
- src/input/cdio_paranoia_input_plugin.h \
- src/despotify_utils.h \
- src/text_file.h \
src/text_input_stream.h \
- src/icy_server.h \
- src/icy_metadata.h \
- src/client.h \
- src/client_internal.h \
- src/server_socket.h \
- src/listen.h \
- src/log.h \
src/ls.h \
- src/main.h \
- src/mixer_all.h \
- src/mixer_api.h \
- src/mixer_control.h \
- src/mixer_list.h \
- src/event_pipe.h \
src/mixer_plugin.h \
- src/mixer_type.h \
- src/mixer/software_mixer_plugin.h \
- src/mixer/pulse_mixer_plugin.h \
src/daemon.h \
src/AudioCompress/config.h \
src/AudioCompress/compress.h \
- src/buffer.h \
- src/pipe.h \
- src/chunk.h \
- src/path.h \
- src/mapper.h \
src/open.h \
- src/output/httpd_client.h \
- src/output/httpd_internal.h \
- src/page.h \
- src/permission.h \
- src/player_thread.h \
- src/player_control.h \
- src/playlist.h \
+ src/Playlist.hxx \
src/playlist_error.h \
- src/playlist_internal.h \
- src/playlist_print.h \
- src/playlist_save.h \
- src/playlist_state.h \
- src/playlist_plugin.h \
- src/playlist_list.h \
- src/playlist_mapper.h \
- src/playlist_any.h \
- src/playlist_song.h \
- src/playlist_queue.h \
- src/playlist_vector.h \
- src/playlist_database.h \
- src/playlist/extm3u_playlist_plugin.h \
- src/playlist/m3u_playlist_plugin.h \
- src/playlist/pls_playlist_plugin.h \
- src/playlist/xspf_playlist_plugin.h \
- src/playlist/asx_playlist_plugin.h \
- src/playlist/rss_playlist_plugin.h \
- src/playlist/lastfm_playlist_plugin.h \
- src/playlist/despotify_playlist_plugin.h \
- src/playlist/cue_playlist_plugin.h \
src/poison.h \
src/riff.h \
src/aiff.h \
- src/queue.h \
- src/queue_print.h \
- src/queue_save.h \
- src/refcount.h \
src/replay_gain_config.h \
src/replay_gain_info.h \
src/replay_gain_ape.h \
- src/sig_handlers.h \
+ src/TimePrint.cxx src/TimePrint.hxx \
src/song.h \
- src/song_print.h \
- src/song_save.h \
- src/song_sticker.h \
src/song_sort.c src/song_sort.h \
- src/socket_util.h \
- src/state_file.h \
src/stats.h \
- src/sticker.h \
- src/sticker_print.h \
src/tag.h \
src/tag_internal.h \
- src/tag_pool.h \
src/tag_table.h \
src/tag_ape.h \
src/tag_id3.h \
src/tag_rva2.h \
- src/tag_print.h \
- src/tag_save.h \
src/tokenizer.h \
- src/strset.h \
src/uri.h \
src/utils.h \
src/string_util.h \
- src/volume.h \
- src/zeroconf.h src/zeroconf-internal.h \
- src/locate.h \
- src/stored_playlist.h \
src/timer.h \
- src/archive_api.h \
- src/archive_internal.h \
- src/archive_list.h \
- src/archive_plugin.h \
- src/archive/bz2_archive_plugin.h \
- src/archive/iso9660_archive_plugin.h \
- src/archive/zzip_archive_plugin.h \
- src/input/archive_input_plugin.h \
src/mpd_error.h
src_mpd_SOURCES = \
@@ -226,133 +110,149 @@ src_mpd_SOURCES = \
$(DECODER_SRC) \
$(OUTPUT_API_SRC) \
$(MIXER_API_SRC) \
- src/glib_socket.h \
+ src/thread/Mutex.hxx \
+ src/thread/PosixMutex.hxx \
+ src/thread/CriticalSection.hxx \
+ src/thread/GLibMutex.hxx \
+ src/thread/Cond.hxx \
+ src/thread/PosixCond.hxx \
+ src/thread/WindowsCond.hxx \
+ src/thread/GLibCond.hxx \
src/clock.c src/clock.h \
- src/notify.c \
- src/audio_config.c src/audio_config.h \
+ src/notify.cxx src/notify.hxx \
+ src/AudioConfig.cxx src/AudioConfig.hxx \
src/audio_check.c \
src/audio_format.c \
- src/audio_parser.c \
- src/protocol/argparser.c src/protocol/argparser.h \
- src/protocol/result.c src/protocol/result.h \
- src/command.c \
- src/idle.c \
- src/cmdline.c \
- src/conf.c \
- src/crossfade.c \
+ src/AudioParser.cxx src/AudioParser.hxx \
+ src/protocol/ArgParser.cxx src/protocol/ArgParser.hxx \
+ src/protocol/Result.cxx src/protocol/Result.hxx \
+ src/CommandError.cxx src/CommandError.hxx \
+ src/AllCommands.cxx src/AllCommands.hxx \
+ src/QueueCommands.cxx src/QueueCommands.hxx \
+ src/PlayerCommands.cxx src/PlayerCommands.hxx \
+ src/PlaylistCommands.cxx src/PlaylistCommands.hxx \
+ src/DatabaseCommands.cxx src/DatabaseCommands.hxx \
+ src/OutputCommands.cxx src/OutputCommands.hxx \
+ src/MessageCommands.cxx src/MessageCommands.hxx \
+ src/OtherCommands.cxx src/OtherCommands.hxx \
+ src/Idle.cxx src/Idle.hxx \
+ src/CommandLine.cxx src/CommandLine.hxx \
+ src/CrossFade.cxx src/CrossFade.hxx \
src/cue/cue_parser.c src/cue/cue_parser.h \
- src/dbUtils.c \
- src/decoder_thread.c \
- src/decoder_control.c \
- src/decoder_api.c \
- src/decoder_internal.c \
- src/decoder_print.c \
- src/directory.c \
- src/directory_save.c \
- src/database.c \
- src/db_internal.h \
+ src/decoder_error.h \
+ src/DecoderThread.cxx src/DecoderThread.hxx \
+ src/DecoderControl.cxx src/DecoderControl.hxx \
+ src/DecoderAPI.cxx \
+ src/DecoderInternal.cxx src/DecoderInternal.hxx \
+ src/DecoderPrint.cxx src/DecoderPrint.hxx \
+ src/Directory.cxx src/Directory.hxx \
+ src/DirectorySave.cxx src/DirectorySave.hxx \
+ src/DatabaseSimple.hxx \
+ src/DatabaseGlue.cxx src/DatabaseGlue.hxx \
+ src/DatabasePrint.cxx src/DatabasePrint.hxx \
+ src/DatabaseQueue.cxx src/DatabaseQueue.hxx \
+ src/DatabasePlaylist.cxx src/DatabasePlaylist.hxx \
src/db_error.h \
- src/db_lock.c src/db_lock.h \
- src/db_save.c src/db_save.h \
- src/db_print.c src/db_print.h \
- src/db_plugin.h \
- src/db_visitor.h \
- src/db_selection.h \
- src/db/simple_db_plugin.c src/db/simple_db_plugin.h \
- src/exclude.c \
+ src/DatabaseLock.cxx src/DatabaseLock.hxx \
+ src/DatabaseSave.cxx src/DatabaseSave.hxx \
+ src/DatabasePlugin.hxx \
+ src/DatabaseVisitor.hxx \
+ src/DatabaseSelection.cxx src/DatabaseSelection.hxx \
+ src/ExcludeList.cxx src/ExcludeList.hxx \
src/fd_util.c \
- src/fifo_buffer.c src/fifo_buffer.h \
- src/growing_fifo.c src/growing_fifo.h \
- src/filter_config.c \
- src/filter_plugin.c \
- src/filter_registry.c \
- src/update.c \
- src/update_queue.c src/update_queue.h \
- src/update_io.c src/update_io.h \
- src/update_db.c src/update_db.h \
- src/update_walk.c src/update_walk.h \
- src/update_song.c src/update_song.h \
- src/update_container.c src/update_container.h \
- src/update_internal.h \
- src/update_remove.c src/update_remove.h \
- src/client.c \
- src/client_event.c \
- src/client_expire.c \
- src/client_global.c \
- src/client_idle.h \
- src/client_idle.c \
- src/client_list.c \
- src/client_new.c \
- src/client_process.c \
- src/client_read.c \
- src/client_write.c \
- src/client_message.h \
- src/client_message.c \
- src/client_subscribe.h \
- src/client_subscribe.c \
- src/client_file.c src/client_file.h \
- src/server_socket.c \
- src/listen.c \
- src/log.c \
- src/ls.c \
- src/io_thread.c src/io_thread.h \
- src/main.c \
- src/main_win32.c \
- src/event_pipe.c \
+ src/FilterConfig.cxx src/FilterConfig.hxx \
+ src/FilterPlugin.cxx src/FilterPlugin.hxx \
+ src/FilterRegistry.cxx src/FilterRegistry.hxx \
+ src/UpdateGlue.cxx src/UpdateGlue.hxx \
+ src/UpdateQueue.cxx src/UpdateQueue.hxx \
+ src/UpdateIO.cxx src/UpdateIO.hxx \
+ src/UpdateDatabase.cxx src/UpdateDatabase.hxx \
+ src/UpdateWalk.cxx src/UpdateWalk.hxx \
+ src/UpdateSong.cxx src/UpdateSong.hxx \
+ src/UpdateContainer.cxx src/UpdateContainer.hxx \
+ src/UpdateInternal.hxx \
+ src/UpdateRemove.cxx src/UpdateRemove.hxx \
+ src/CommandListBuilder.cxx src/CommandListBuilder.hxx \
+ src/Client.cxx src/Client.hxx \
+ src/ClientInternal.hxx \
+ src/ClientEvent.cxx \
+ src/ClientExpire.cxx \
+ src/ClientGlobal.cxx \
+ src/ClientIdle.cxx \
+ src/ClientList.cxx src/ClientList.hxx \
+ src/ClientNew.cxx \
+ src/ClientProcess.cxx \
+ src/ClientRead.cxx \
+ src/ClientWrite.cxx \
+ src/ClientMessage.cxx src/ClientMessage.hxx \
+ src/ClientSubscribe.cxx src/ClientSubscribe.hxx \
+ src/ClientFile.cxx src/ClientFile.hxx \
+ src/Listen.cxx src/Listen.hxx \
+ src/Log.cxx src/Log.hxx \
+ src/ls.cxx \
+ src/SocketError.hxx \
+ src/io_error.h \
+ src/IOThread.cxx src/IOThread.hxx \
+ src/Main.cxx src/Main.hxx \
+ src/Win32Main.cxx \
+ src/GlobalEvents.cxx src/GlobalEvents.hxx \
src/daemon.c \
src/AudioCompress/compress.c \
- src/buffer.c \
- src/pipe.c \
- src/chunk.c \
- src/path.c \
- src/mapper.c \
- src/page.c \
- src/permission.c \
- src/player_thread.c \
- src/player_control.c \
- src/playlist.c \
- src/playlist_global.c \
- src/playlist_control.c \
- src/playlist_edit.c \
- src/playlist_print.c \
- src/playlist_save.c \
- src/playlist_mapper.c \
- src/playlist_any.c \
- src/playlist_song.c \
- src/playlist_state.c \
- src/playlist_queue.c \
- src/playlist_vector.c \
- src/playlist_database.c \
- src/queue.c \
- src/queue_print.c \
- src/queue_save.c \
- src/replay_gain_config.c \
- src/replay_gain_info.c \
- src/sig_handlers.c \
- src/song.c \
- src/song_update.c \
- src/song_print.c \
- src/song_save.c \
+ src/MusicBuffer.cxx src/MusicBuffer.hxx \
+ src/MusicPipe.cxx src/MusicPipe.hxx \
+ src/MusicChunk.cxx src/MusicChunk.hxx \
+ src/Mapper.cxx src/Mapper.hxx \
+ src/Page.cxx src/Page.hxx \
+ src/Partition.hxx \
+ src/Permission.cxx src/Permission.hxx \
+ src/PlayerThread.cxx src/PlayerThread.hxx \
+ src/PlayerControl.cxx src/PlayerControl.hxx \
+ src/Playlist.cxx \
+ src/PlaylistGlobal.cxx src/PlaylistGlobal.hxx \
+ src/PlaylistControl.cxx \
+ src/PlaylistEdit.cxx \
+ src/PlaylistPrint.cxx src/PlaylistPrint.hxx \
+ src/PlaylistSave.cxx src/PlaylistSave.hxx \
+ src/PlaylistMapper.cxx src/PlaylistMapper.hxx \
+ src/PlaylistAny.cxx src/PlaylistAny.hxx \
+ src/PlaylistSong.cxx src/PlaylistSong.hxx \
+ src/PlaylistState.cxx src/PlaylistState.hxx \
+ src/PlaylistQueue.cxx src/PlaylistQueue.hxx \
+ src/PlaylistVector.cxx src/PlaylistVector.hxx \
+ src/PlaylistInfo.hxx \
+ src/PlaylistDatabase.cxx \
+ src/IdTable.hxx \
+ src/Queue.cxx src/Queue.hxx \
+ src/QueuePrint.cxx src/QueuePrint.hxx \
+ src/QueueSave.cxx src/QueueSave.hxx \
+ src/ReplayGainConfig.cxx \
+ src/ReplayGainInfo.cxx \
+ src/SignalHandlers.cxx src/SignalHandlers.hxx \
+ src/Song.cxx \
+ src/SongUpdate.cxx \
+ src/SongPrint.cxx src/SongPrint.hxx \
+ src/SongSave.cxx src/SongSave.hxx \
src/resolver.c src/resolver.h \
- src/socket_util.c \
- src/state_file.c \
- src/stats.c \
- src/tag.c \
- src/tag_pool.c \
- src/tag_print.c \
- src/tag_save.c \
+ src/SocketUtil.cxx src/SocketUtil.hxx \
+ src/StateFile.cxx src/StateFile.hxx \
+ src/Stats.cxx \
+ src/Tag.cxx \
+ src/TagNames.c \
+ src/TagPool.cxx src/TagPool.hxx \
+ src/TagPrint.cxx src/TagPrint.hxx \
+ src/TagSave.cxx src/TagSave.hxx \
src/tag_handler.c src/tag_handler.h \
- src/tag_file.c src/tag_file.h \
+ src/TagFile.cxx src/TagFile.hxx \
src/tokenizer.c \
- src/text_file.c \
+ src/TextFile.cxx src/TextFile.hxx \
src/text_input_stream.c \
- src/strset.c \
src/uri.c \
src/utils.c \
src/string_util.c \
- src/volume.c \
- src/locate.c \
- src/stored_playlist.c \
+ src/Volume.cxx src/Volume.hxx \
+ src/SongFilter.cxx src/SongFilter.hxx \
+ src/SongPointer.hxx \
+ src/PlaylistFile.cxx src/PlaylistFile.hxx \
src/timer.c
#
@@ -371,51 +271,72 @@ endif
if ENABLE_DESPOTIFY
src_mpd_SOURCES += \
- src/despotify_utils.c
+ src/DespotifyUtils.cxx src/DespotifyUtils.hxx
endif
if ENABLE_INOTIFY
src_mpd_SOURCES += \
- src/inotify_source.c \
- src/inotify_queue.c \
- src/inotify_update.c
+ src/InotifySource.cxx src/InotifySource.hxx \
+ src/InotifyQueue.cxx src/InotifyQueue.hxx \
+ src/InotifyUpdate.cxx src/InotifyUpdate.hxx
endif
if ENABLE_SQLITE
src_mpd_SOURCES += \
- src/sticker.c \
- src/sticker_print.c \
- src/song_sticker.c
+ src/StickerCommands.cxx src/StickerCommands.hxx \
+ src/StickerDatabase.cxx src/StickerDatabase.hxx \
+ src/StickerPrint.cxx src/StickerPrint.hxx \
+ src/SongSticker.cxx src/SongSticker.hxx
endif
# Generic utility library
libutil_a_SOURCES = \
+ src/util/Manual.hxx \
+ src/util/RefCount.hxx \
+ src/util/fifo_buffer.c src/util/fifo_buffer.h \
+ src/util/growing_fifo.c src/util/growing_fifo.h \
+ src/util/LazyRandomEngine.cxx src/util/LazyRandomEngine.hxx \
+ src/util/SliceBuffer.hxx \
+ src/util/HugeAllocator.cxx src/util/HugeAllocator.hxx \
+ src/util/PeakBuffer.cxx src/util/PeakBuffer.hxx \
src/util/list.h \
src/util/list_sort.c src/util/list_sort.h \
src/util/byte_reverse.c src/util/byte_reverse.h \
src/util/bit_reverse.c src/util/bit_reverse.h
+# Event loop library
+
+libevent_a_SOURCES = \
+ src/event/WakeFD.cxx src/event/WakeFD.hxx \
+ src/event/TimeoutMonitor.hxx src/event/TimeoutMonitor.cxx \
+ src/event/SocketMonitor.cxx src/event/SocketMonitor.hxx \
+ src/event/BufferedSocket.cxx src/event/BufferedSocket.hxx \
+ src/event/FullyBufferedSocket.cxx src/event/FullyBufferedSocket.hxx \
+ src/event/MultiSocketMonitor.cxx src/event/MultiSocketMonitor.hxx \
+ src/event/ServerSocket.cxx src/event/ServerSocket.hxx \
+ src/event/Loop.hxx
+
# PCM library
libpcm_a_SOURCES = \
src/pcm_buffer.c src/pcm_buffer.h \
src/pcm_export.c src/pcm_export.h \
- src/pcm_convert.c src/pcm_convert.h \
+ src/PcmConvert.cxx src/PcmConvert.hxx \
src/dsd2pcm/dsd2pcm.c src/dsd2pcm/dsd2pcm.h \
src/pcm_dsd.c src/pcm_dsd.h \
src/pcm_dsd_usb.c src/pcm_dsd_usb.h \
- src/pcm_volume.c src/pcm_volume.h \
- src/pcm_mix.c src/pcm_mix.h \
- src/pcm_channels.c src/pcm_channels.h \
+ src/PcmVolume.cxx src/PcmVolume.hxx \
+ src/PcmMix.cxx src/PcmMix.hxx \
+ src/PcmChannels.cxx src/PcmChannels.hxx \
src/pcm_pack.c src/pcm_pack.h \
- src/pcm_format.c src/pcm_format.h \
+ src/PcmFormat.cxx src/PcmFormat.hxx \
src/pcm_resample.c src/pcm_resample.h \
src/pcm_resample_fallback.c \
src/pcm_resample_internal.h \
- src/pcm_dither.c src/pcm_dither.h \
- src/pcm_prng.h \
- src/pcm_utils.h
+ src/PcmDither.cxx src/PcmDither.hxx \
+ src/PcmPrng.hxx \
+ src/PcmUtils.hxx
libpcm_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(SAMPLERATE_CFLAGS)
@@ -427,6 +348,29 @@ if HAVE_LIBSAMPLERATE
libpcm_a_SOURCES += src/pcm_resample_libsamplerate.c
endif
+# File system library
+
+libfs_a_SOURCES = \
+ src/fs/Path.cxx src/fs/Path.hxx \
+ src/fs/FileSystem.cxx src/fs/FileSystem.hxx \
+ src/fs/DirectoryReader.hxx
+
+# database plugins
+
+libdb_plugins_a_SOURCES = \
+ src/DatabaseRegistry.cxx src/DatabaseRegistry.hxx \
+ src/DatabaseHelpers.cxx src/DatabaseHelpers.hxx \
+ src/db/SimpleDatabasePlugin.cxx src/db/SimpleDatabasePlugin.hxx
+
+if HAVE_LIBMPDCLIENT
+libdb_plugins_a_SOURCES += \
+ src/db/ProxyDatabasePlugin.cxx src/db/ProxyDatabasePlugin.hxx
+endif
+
+DB_LIBS = \
+ libdb_plugins.a \
+ $(LIBMPDCLIENT_LIBS)
+
# archive plugins
if ENABLE_ARCHIVE
@@ -434,13 +378,15 @@ if ENABLE_ARCHIVE
noinst_LIBRARIES += libarchive.a
src_mpd_SOURCES += \
- src/update_archive.c src/update_archive.h
+ src/UpdateArchive.cxx src/UpdateArchive.hxx
libarchive_a_SOURCES = \
- src/archive_api.c \
- src/archive_list.c \
- src/archive_plugin.c \
- src/input/archive_input_plugin.c
+ src/ArchiveLookup.cxx src/ArchiveLookup.hxx \
+ src/ArchiveList.cxx src/ArchiveList.hxx \
+ src/ArchivePlugin.cxx src/ArchivePlugin.hxx \
+ src/ArchiveVisitor.hxx \
+ src/ArchiveFile.hxx \
+ src/input/ArchiveInputPlugin.cxx src/input/ArchiveInputPlugin.hxx
libarchive_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(BZ2_CFLAGS) \
$(ISO9660_CFLAGS) \
@@ -453,21 +399,37 @@ ARCHIVE_LIBS = \
$(ZZIP_LIBS)
if HAVE_BZ2
-libarchive_a_SOURCES += src/archive/bz2_archive_plugin.c
+libarchive_a_SOURCES += \
+ src/archive/Bzip2ArchivePlugin.cxx \
+ src/archive/Bzip2ArchivePlugin.hxx
endif
if HAVE_ZZIP
-libarchive_a_SOURCES += src/archive/zzip_archive_plugin.c
+libarchive_a_SOURCES += \
+ src/archive/ZzipArchivePlugin.cxx \
+ src/archive/ZzipArchivePlugin.hxx
endif
if HAVE_ISO9660
-libarchive_a_SOURCES += src/archive/iso9660_archive_plugin.c
+libarchive_a_SOURCES += \
+ src/archive/Iso9660ArchivePlugin.cxx \
+ src/archive/Iso9660ArchivePlugin.hxx
endif
else
ARCHIVE_LIBS =
endif
+# configuration library
+
+libconf_a_SOURCES = \
+ src/ConfigData.cxx src/ConfigData.hxx \
+ src/ConfigParser.cxx src/ConfigParser.hxx \
+ src/ConfigGlobal.cxx src/ConfigGlobal.hxx \
+ src/ConfigFile.cxx src/ConfigFile.hxx \
+ src/ConfigTemplates.cxx src/ConfigTemplates.hxx \
+ src/ConfigQuark.hxx \
+ src/ConfigOption.hxx
# tag plugins
@@ -501,7 +463,7 @@ libdecoder_plugins_a_SOURCES = \
src/decoder/dsdlib.h \
src/decoder_buffer.c \
src/decoder_plugin.c \
- src/decoder_list.c
+ src/DecoderList.cxx src/DecoderList.hxx
libdecoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(VORBIS_CFLAGS) $(TREMOR_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
@@ -515,8 +477,10 @@ libdecoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(WAVPACK_CFLAGS) \
$(MAD_CFLAGS) \
$(MPG123_CFLAGS) \
+ $(OPUS_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(MPCDEC_CFLAGS) \
+ $(ADPLUG_CFLAGS) \
$(FAAD_CFLAGS)
DECODER_LIBS = \
@@ -532,9 +496,10 @@ DECODER_LIBS = \
$(WAVPACK_LIBS) \
$(MAD_LIBS) \
$(MPG123_LIBS) \
- $(MP4FF_LIBS) \
+ $(OPUS_LIBS) \
$(FFMPEG_LIBS) \
$(MPCDEC_LIBS) \
+ $(ADPLUG_LIBS) \
$(FAAD_LIBS)
DECODER_SRC =
@@ -551,38 +516,57 @@ if HAVE_MPCDEC
libdecoder_plugins_a_SOURCES += src/decoder/mpcdec_decoder_plugin.c
endif
-if HAVE_WAVPACK
-libdecoder_plugins_a_SOURCES += src/decoder/wavpack_decoder_plugin.c
+if HAVE_OPUS
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/OggUtil.cxx \
+ src/decoder/OggUtil.hxx \
+ src/decoder/OggFind.cxx src/decoder/OggFind.hxx \
+ src/decoder/OpusReader.hxx \
+ src/decoder/OpusHead.hxx \
+ src/decoder/OpusHead.cxx \
+ src/decoder/OpusTags.cxx \
+ src/decoder/OpusTags.hxx \
+ src/decoder/OpusDecoderPlugin.cxx \
+ src/decoder/OpusDecoderPlugin.h
endif
-if HAVE_FAAD
-libdecoder_plugins_a_SOURCES += src/decoder/faad_decoder_plugin.c
+if HAVE_WAVPACK
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/WavpackDecoderPlugin.cxx \
+ src/decoder/WavpackDecoderPlugin.hxx
endif
-if HAVE_MP4
-libdecoder_plugins_a_SOURCES += src/decoder/mp4ff_decoder_plugin.c
+if HAVE_ADPLUG
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/AdPlugDecoderPlugin.cxx \
+ src/decoder/AdPlugDecoderPlugin.h
endif
-if HAVE_OGG_COMMON
-libdecoder_plugins_a_SOURCES += src/decoder/_ogg_common.c
+if HAVE_FAAD
+libdecoder_plugins_a_SOURCES += src/decoder/faad_decoder_plugin.c
endif
-if HAVE_FLAC_COMMON
+if HAVE_XIPH
libdecoder_plugins_a_SOURCES += \
- src/decoder/flac_metadata.c \
- src/decoder/flac_pcm.c \
- src/decoder/_flac_common.c
+ src/decoder/XiphTags.c src/decoder/XiphTags.h \
+ src/decoder/OggCodec.cxx src/decoder/OggCodec.hxx
endif
if ENABLE_VORBIS_DECODER
libdecoder_plugins_a_SOURCES += \
- src/decoder/vorbis_comments.c \
- src/decoder/vorbis_comments.h \
- src/decoder/vorbis_decoder_plugin.c
+ src/decoder/VorbisComments.cxx src/decoder/VorbisComments.hxx \
+ src/decoder/VorbisDecoderPlugin.cxx src/decoder/VorbisDecoderPlugin.h
endif
if HAVE_FLAC
-libdecoder_plugins_a_SOURCES += src/decoder/flac_decoder_plugin.c
+libdecoder_plugins_a_SOURCES += \
+ src/decoder/FLACInput.cxx src/decoder/FLACInput.hxx \
+ src/decoder/FLACIOHandle.cxx src/decoder/FLACIOHandle.hxx \
+ src/decoder/FLACMetaData.cxx src/decoder/FLACMetaData.hxx \
+ src/decoder/FLAC_PCM.cxx src/decoder/FLAC_PCM.hxx \
+ src/decoder/FLACCommon.cxx src/decoder/FLACCommon.hxx \
+ src/decoder/FLACDecoderPlugin.cxx \
+ src/decoder/FLACDecoderPlugin.h
endif
if HAVE_AUDIOFILE
@@ -603,7 +587,6 @@ endif
if ENABLE_SIDPLAY
libdecoder_plugins_a_SOURCES += src/decoder/sidplay_decoder_plugin.cxx
-DECODER_SRC += src/dummy.cxx
endif
if ENABLE_FLUIDSYNTH
@@ -616,9 +599,10 @@ endif
if HAVE_FFMPEG
libdecoder_plugins_a_SOURCES += \
- src/decoder/ffmpeg_metadata.c \
- src/decoder/ffmpeg_metadata.h \
- src/decoder/ffmpeg_decoder_plugin.c
+ src/decoder/FfmpegMetaData.cxx \
+ src/decoder/FfmpegMetaData.hxx \
+ src/decoder/FfmpegDecoderPlugin.cxx \
+ src/decoder/FfmpegDecoderPlugin.hxx
endif
if ENABLE_SNDFILE
@@ -639,6 +623,7 @@ libencoder_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(LAME_CFLAGS) \
$(TWOLAME_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \
+ $(OPUS_CFLAGS) \
$(VORBISENC_CFLAGS)
ENCODER_LIBS = \
@@ -646,19 +631,28 @@ ENCODER_LIBS = \
$(LAME_LIBS) \
$(TWOLAME_LIBS) \
$(FLAC_LIBS) \
+ $(OPUS_LIBS) \
$(VORBISENC_LIBS)
-libencoder_plugins_a_SOURCES =
-
-libencoder_plugins_a_SOURCES += src/encoder_list.c
-libencoder_plugins_a_SOURCES += src/encoder/null_encoder.c
+libencoder_plugins_a_SOURCES = \
+ src/encoder/OggStream.hxx \
+ src/encoder/null_encoder.c \
+ src/encoder_list.c
if ENABLE_WAVE_ENCODER
libencoder_plugins_a_SOURCES += src/encoder/wave_encoder.c
endif
if ENABLE_VORBIS_ENCODER
-libencoder_plugins_a_SOURCES += src/encoder/vorbis_encoder.c
+libencoder_plugins_a_SOURCES += \
+ src/encoder/VorbisEncoderPlugin.cxx \
+ src/encoder/VorbisEncoderPlugin.hxx
+endif
+
+if HAVE_OPUS
+libencoder_plugins_a_SOURCES += \
+ src/encoder/OpusEncoderPlugin.cxx \
+ src/encoder/OpusEncoderPlugin.hxx
endif
if ENABLE_LAME_ENCODER
@@ -679,14 +673,16 @@ endif
if HAVE_ZEROCONF
-src_mpd_SOURCES += src/zeroconf.c
+src_mpd_SOURCES += \
+ src/ZeroconfInternal.hxx \
+ src/ZeroconfGlue.cxx src/ZeroconfGlue.hxx
if HAVE_AVAHI
-src_mpd_SOURCES += src/zeroconf-avahi.c
+src_mpd_SOURCES += src/ZeroconfAvahi.cxx src/ZeroconfAvahi.hxx
endif
if HAVE_BONJOUR
-src_mpd_SOURCES += src/zeroconf-bonjour.c
+src_mpd_SOURCES += src/ZeroconfBonjour.cxx src/ZeroconfBonjour.hxx
endif
endif
@@ -695,12 +691,13 @@ endif
#
libinput_a_SOURCES = \
- src/input_init.c \
- src/input_registry.c \
- src/input_stream.c \
- src/input_internal.c src/input_internal.h \
- src/input/rewind_input_plugin.c \
- src/input/file_input_plugin.c
+ src/InputInit.cxx src/InputInit.hxx \
+ src/InputRegistry.cxx src/InputRegistry.hxx \
+ src/InputStream.cxx src/InputStream.hxx \
+ src/InputPlugin.hxx \
+ src/InputInternal.cxx src/InputInternal.hxx \
+ src/input/RewindInputPlugin.cxx src/input/RewindInputPlugin.hxx \
+ src/input/FileInputPlugin.cxx src/input/FileInputPlugin.hxx
libinput_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(CURL_CFLAGS) \
@@ -720,30 +717,36 @@ INPUT_LIBS = \
$(MMS_LIBS)
if ENABLE_CURL
-libinput_a_SOURCES += src/input/curl_input_plugin.c \
- src/icy_metadata.c
+libinput_a_SOURCES += \
+ src/input/CurlInputPlugin.cxx src/input/CurlInputPlugin.hxx \
+ src/IcyMetaDataParser.cxx src/IcyMetaDataParser.hxx
endif
if ENABLE_SOUP
libinput_a_SOURCES += \
- src/input/soup_input_plugin.c \
- src/input/soup_input_plugin.h
+ src/input/SoupInputPlugin.cxx src/input/SoupInputPlugin.hxx
endif
if ENABLE_CDIO_PARANOIA
-libinput_a_SOURCES += src/input/cdio_paranoia_input_plugin.c
+libinput_a_SOURCES += \
+ src/input/CdioParanoiaInputPlugin.cxx \
+ src/input/CdioParanoiaInputPlugin.hxx
endif
if HAVE_FFMPEG
-libinput_a_SOURCES += src/input/ffmpeg_input_plugin.c
+libinput_a_SOURCES += \
+ src/input/FfmpegInputPlugin.cxx src/input/FfmpegInputPlugin.hxx
endif
if ENABLE_MMS
-libinput_a_SOURCES += src/input/mms_input_plugin.c
+libinput_a_SOURCES += \
+ src/input/MmsInputPlugin.cxx src/input/MmsInputPlugin.hxx
endif
if ENABLE_DESPOTIFY
-libinput_a_SOURCES += src/input/despotify_input_plugin.c
+libinput_a_SOURCES += \
+ src/input/DespotifyInputPlugin.cxx \
+ src/input/DespotifyInputPlugin.hxx
endif
@@ -770,47 +773,51 @@ OUTPUT_LIBS = \
$(SHOUT_LIBS)
OUTPUT_API_SRC = \
- src/output_list.c \
- src/output_all.c \
- src/output_thread.c \
- src/output_control.c \
- src/output_state.c \
- src/output_print.c \
- src/output_command.c \
- src/output_plugin.c src/output_plugin.h \
- src/output_finish.c \
- src/output_init.c
+ src/OutputList.cxx src/OutputList.hxx \
+ src/OutputAll.cxx src/OutputAll.hxx \
+ src/OutputThread.cxx src/OutputThread.hxx \
+ src/OutputError.hxx \
+ src/OutputControl.cxx src/OutputControl.hxx \
+ src/OutputState.cxx src/OutputState.hxx \
+ src/OutputPrint.cxx src/OutputPrint.hxx \
+ src/OutputCommand.cxx src/OutputCommand.hxx \
+ src/OutputPlugin.cxx src/output_plugin.h \
+ src/OutputFinish.cxx \
+ src/OutputInit.cxx
liboutput_plugins_a_SOURCES = \
- src/output/null_output_plugin.h \
- src/output/null_output_plugin.c
+ src/output/NullOutputPlugin.cxx \
+ src/output/NullOutputPlugin.hxx
MIXER_LIBS = \
libmixer_plugins.a \
$(PULSE_LIBS)
MIXER_API_SRC = \
- src/mixer_control.c \
- src/mixer_type.c \
- src/mixer_all.c \
- src/mixer_api.c
+ src/MixerList.hxx \
+ src/MixerControl.cxx src/MixerControl.hxx \
+ src/MixerType.cxx src/MixerType.hxx \
+ src/MixerAll.cxx src/MixerAll.hxx \
+ src/MixerInternal.cxx src/MixerInternal.hxx
libmixer_plugins_a_SOURCES = \
- src/mixer/software_mixer_plugin.c
+ src/mixer/SoftwareMixerPlugin.cxx \
+ src/mixer/SoftwareMixerPlugin.hxx
libmixer_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(ALSA_CFLAGS) \
$(PULSE_CFLAGS)
if HAVE_ALSA
liboutput_plugins_a_SOURCES += \
- src/output/alsa_output_plugin.c src/output/alsa_output_plugin.h
-libmixer_plugins_a_SOURCES += src/mixer/alsa_mixer_plugin.c
+ src/output/AlsaOutputPlugin.cxx \
+ src/output/AlsaOutputPlugin.hxx
+libmixer_plugins_a_SOURCES += src/mixer/AlsaMixerPlugin.cxx
endif
if HAVE_ROAR
liboutput_plugins_a_SOURCES += \
- src/output/roar_output_plugin.c src/output/roar_output_plugin.h
-libmixer_plugins_a_SOURCES += src/mixer/roar_mixer_plugin.c
+ src/output/RoarOutputPlugin.cxx src/output/RoarOutputPlugin.hxx
+libmixer_plugins_a_SOURCES += src/mixer/RoarMixerPlugin.cxx
endif
if ENABLE_FFADO_OUTPUT
@@ -845,8 +852,9 @@ endif
if HAVE_OSS
liboutput_plugins_a_SOURCES += \
- src/output/oss_output_plugin.c src/output/oss_output_plugin.h
-libmixer_plugins_a_SOURCES += src/mixer/oss_mixer_plugin.c
+ src/output/OssOutputPlugin.cxx \
+ src/output/OssOutputPlugin.hxx
+libmixer_plugins_a_SOURCES += src/mixer/OssMixerPlugin.cxx
endif
if HAVE_OPENAL
@@ -856,13 +864,15 @@ endif
if HAVE_OSX
liboutput_plugins_a_SOURCES += \
- src/output/osx_output_plugin.c src/output/osx_output_plugin.h
+ src/output/OSXOutputPlugin.cxx \
+ src/output/OSXOutputPlugin.hxx
endif
if HAVE_PULSE
liboutput_plugins_a_SOURCES += \
src/output/pulse_output_plugin.c src/output/pulse_output_plugin.h
-libmixer_plugins_a_SOURCES += src/mixer/pulse_mixer_plugin.c
+libmixer_plugins_a_SOURCES += \
+ src/mixer/PulseMixerPlugin.cxx src/mixer/PulseMixerPlugin.h
endif
if HAVE_SHOUT
@@ -877,9 +887,10 @@ endif
if ENABLE_HTTPD_OUTPUT
liboutput_plugins_a_SOURCES += \
- src/icy_server.c \
- src/output/httpd_client.c \
- src/output/httpd_output_plugin.c src/output/httpd_output_plugin.h
+ src/IcyMetaDataServer.cxx src/IcyMetaDataServer.hxx \
+ src/output/HttpdInternal.hxx \
+ src/output/HttpdClient.cxx src/output/HttpdClient.hxx \
+ src/output/HttpdOutputPlugin.cxx src/output/HttpdOutputPlugin.hxx
endif
if ENABLE_SOLARIS_OUTPUT
@@ -889,8 +900,8 @@ endif
if ENABLE_WINMM_OUTPUT
liboutput_plugins_a_SOURCES += \
- src/output/winmm_output_plugin.c src/output/winmm_output_plugin.h
-libmixer_plugins_a_SOURCES += src/mixer/winmm_mixer_plugin.c
+ src/output/WinmmOutputPlugin.cxx src/output/WinmmOutputPlugin.hxx
+libmixer_plugins_a_SOURCES += src/mixer/WinmmMixerPlugin.cxx
endif
@@ -899,16 +910,26 @@ endif
#
libplaylist_plugins_a_SOURCES = \
- src/playlist/extm3u_playlist_plugin.c \
- src/playlist/m3u_playlist_plugin.c \
- src/playlist/pls_playlist_plugin.c \
- src/playlist/xspf_playlist_plugin.c \
- src/playlist/asx_playlist_plugin.c \
- src/playlist/rss_playlist_plugin.c \
- src/playlist/cue_playlist_plugin.c \
- src/playlist/embcue_playlist_plugin.c \
- src/playlist/embcue_playlist_plugin.h \
- src/playlist_list.c
+ src/PlaylistPlugin.hxx \
+ src/playlist/MemoryPlaylistProvider.cxx \
+ src/playlist/MemoryPlaylistProvider.hxx \
+ src/playlist/ExtM3uPlaylistPlugin.cxx \
+ src/playlist/ExtM3uPlaylistPlugin.hxx \
+ src/playlist/M3uPlaylistPlugin.cxx \
+ src/playlist/M3uPlaylistPlugin.hxx \
+ src/playlist/PlsPlaylistPlugin.cxx \
+ src/playlist/PlsPlaylistPlugin.hxx \
+ src/playlist/XspfPlaylistPlugin.cxx \
+ src/playlist/XspfPlaylistPlugin.hxx \
+ src/playlist/AsxPlaylistPlugin.cxx \
+ src/playlist/AsxPlaylistPlugin.hxx \
+ src/playlist/RssPlaylistPlugin.cxx \
+ src/playlist/RssPlaylistPlugin.hxx \
+ src/playlist/CuePlaylistPlugin.cxx \
+ src/playlist/CuePlaylistPlugin.hxx \
+ src/playlist/EmbeddedCuePlaylistPlugin.cxx \
+ src/playlist/EmbeddedCuePlaylistPlugin.hxx \
+ src/PlaylistRegistry.cxx src/PlaylistRegistry.hxx
libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(YAJL_CFLAGS) \
$(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS))
@@ -918,17 +939,21 @@ PLAYLIST_LIBS = \
$(FLAC_LIBS)
if ENABLE_LASTFM
-libplaylist_plugins_a_SOURCES += src/playlist/lastfm_playlist_plugin.c
+libplaylist_plugins_a_SOURCES += \
+ src/playlist/LastFMPlaylistPlugin.cxx \
+ src/playlist/LastFMPlaylistPlugin.hxx
endif
if ENABLE_DESPOTIFY
-libplaylist_plugins_a_SOURCES += src/playlist/despotify_playlist_plugin.c
+libplaylist_plugins_a_SOURCES += \
+ src/playlist/DespotifyPlaylistPlugin.cxx \
+ src/playlist/DespotifyPlaylistPlugin.hxx
endif
if ENABLE_SOUNDCLOUD
libplaylist_plugins_a_SOURCES += \
- src/playlist/soundcloud_playlist_plugin.h \
- src/playlist/soundcloud_playlist_plugin.c
+ src/playlist/SoundCloudPlaylistPlugin.cxx \
+ src/playlist/SoundCloudPlaylistPlugin.hxx
PLAYLIST_LIBS += $(YAJL_LIBS)
endif
@@ -937,14 +962,19 @@ endif
#
libfilter_plugins_a_SOURCES = \
- src/filter/null_filter_plugin.c \
- src/filter/chain_filter_plugin.c \
- src/filter/autoconvert_filter_plugin.c \
- src/filter/convert_filter_plugin.c \
- src/filter/route_filter_plugin.c \
- src/filter/normalize_filter_plugin.c \
- src/filter/replay_gain_filter_plugin.c \
- src/filter/volume_filter_plugin.c
+ src/filter/NullFilterPlugin.cxx \
+ src/filter/ChainFilterPlugin.cxx \
+ src/filter/ChainFilterPlugin.hxx \
+ src/filter/AutoConvertFilterPlugin.cxx \
+ src/filter/AutoConvertFilterPlugin.hxx \
+ src/filter/ConvertFilterPlugin.cxx \
+ src/filter/ConvertFilterPlugin.hxx \
+ src/filter/RouteFilterPlugin.cxx \
+ src/filter/NormalizeFilterPlugin.cxx \
+ src/filter/ReplayGainFilterPlugin.cxx \
+ src/filter/ReplayGainFilterPlugin.hxx \
+ src/filter/VolumeFilterPlugin.cxx \
+ src/filter/VolumeFilterPlugin.hxx
FILTER_LIBS = \
libfilter_plugins.a \
@@ -998,6 +1028,7 @@ noinst_PROGRAMS = \
$(C_TESTS) \
test/read_conf \
test/run_resolver \
+ test/DumpDatabase \
test/run_input \
test/dump_text_file \
test/dump_playlist \
@@ -1009,6 +1040,10 @@ noinst_PROGRAMS = \
test/run_normalize \
test/software_volume
+if ENABLE_ARCHIVE
+noinst_PROGRAMS += test/visit_archive
+endif
+
if HAVE_ID3TAG
noinst_PROGRAMS += test/dump_rva2
endif
@@ -1019,36 +1054,88 @@ noinst_PROGRAMS += test/read_mixer
endif
test_read_conf_LDADD = \
+ libconf.a \
+ libfs.a \
$(GLIB_LIBS)
-test_read_conf_SOURCES = test/read_conf.c \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c
+test_read_conf_SOURCES = test/read_conf.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c
test_run_resolver_LDADD = \
$(GLIB_LIBS)
test_run_resolver_SOURCES = test/run_resolver.c \
src/resolver.c
+test_DumpDatabase_LDADD = \
+ $(DB_LIBS) \
+ libconf.a \
+ libutil.a \
+ libfs.a \
+ $(GLIB_LIBS)
+test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \
+ src/DatabaseRegistry.cxx \
+ src/DatabaseSelection.cxx \
+ src/Directory.cxx src/DirectorySave.cxx \
+ src/PlaylistVector.cxx src/PlaylistDatabase.cxx \
+ src/DatabaseLock.cxx src/DatabaseSave.cxx \
+ src/Song.cxx src/song_sort.c src/SongSave.cxx \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
+ src/SongFilter.cxx \
+ src/TextFile.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c
+
test_run_input_LDADD = \
$(INPUT_LIBS) \
$(ARCHIVE_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
$(GLIB_LIBS)
-test_run_input_SOURCES = test/run_input.c \
+test_run_input_SOURCES = test/run_input.cxx \
test/stdbin.h \
- src/io_thread.c src/io_thread.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
- src/tag.c src/tag_pool.c src/tag_save.c \
+ src/IOThread.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c\
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
+ src/uri.c \
src/fd_util.c
+if ENABLE_ARCHIVE
+
+test_visit_archive_LDADD = \
+ $(INPUT_LIBS) \
+ $(ARCHIVE_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
+ $(GLIB_LIBS)
+test_visit_archive_SOURCES = test/visit_archive.cxx \
+ src/IOThread.cxx \
+ src/InputStream.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx \
+ src/uri.c \
+ src/fd_util.c
+
+if ENABLE_DESPOTIFY
+test_visit_archive_SOURCES += src/DespotifyUtils.cxx
+endif
+
+endif
+
test_dump_text_file_LDADD = \
$(INPUT_LIBS) \
$(ARCHIVE_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
+ libutil.a \
$(GLIB_LIBS)
-test_dump_text_file_SOURCES = test/dump_text_file.c \
+test_dump_text_file_SOURCES = test/dump_text_file.cxx \
test/stdbin.h \
- src/io_thread.c src/io_thread.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
- src/tag.c src/tag_pool.c \
- src/text_input_stream.c src/fifo_buffer.c \
+ src/IOThread.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c\
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx \
+ src/text_input_stream.c \
+ src/uri.c \
src/fd_util.c
test_dump_playlist_LDADD = \
@@ -1058,24 +1145,27 @@ test_dump_playlist_LDADD = \
$(ARCHIVE_LIBS) \
$(DECODER_LIBS) \
$(TAG_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
libutil.a \
$(GLIB_LIBS)
-test_dump_playlist_SOURCES = test/dump_playlist.c \
+test_dump_playlist_SOURCES = test/dump_playlist.cxx \
$(DECODER_SRC) \
- src/io_thread.c src/io_thread.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
+ src/IOThread.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c\
src/uri.c \
- src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
- src/tag_handler.c src/tag_file.c \
+ src/Song.cxx src/Tag.cxx src/TagNames.c src/TagPool.cxx src/TagSave.cxx \
+ src/tag_handler.c src/TagFile.cxx \
src/audio_check.c src/pcm_buffer.c \
- src/text_input_stream.c src/fifo_buffer.c \
+ src/text_input_stream.c \
src/cue/cue_parser.c src/cue/cue_parser.h \
src/fd_util.c
if HAVE_FLAC
test_dump_playlist_SOURCES += \
- src/replay_gain_info.c \
- src/decoder/flac_metadata.c
+ src/ReplayGainInfo.cxx \
+ src/decoder/FLACMetaData.cxx
endif
test_run_decoder_LDADD = \
@@ -1084,14 +1174,17 @@ test_run_decoder_LDADD = \
$(INPUT_LIBS) \
$(ARCHIVE_LIBS) \
$(TAG_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
libutil.a \
$(GLIB_LIBS)
-test_run_decoder_SOURCES = test/run_decoder.c \
+test_run_decoder_SOURCES = test/run_decoder.cxx \
test/stdbin.h \
- src/io_thread.c src/io_thread.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
- src/tag.c src/tag_pool.c src/tag_handler.c \
- src/replay_gain_info.c \
+ src/IOThread.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx src/tag_handler.c \
+ src/ReplayGainInfo.cxx \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
@@ -1107,13 +1200,16 @@ test_read_tags_LDADD = \
$(INPUT_LIBS) \
$(ARCHIVE_LIBS) \
$(TAG_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
libutil.a \
$(GLIB_LIBS)
-test_read_tags_SOURCES = test/read_tags.c \
- src/io_thread.c src/io_thread.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
- src/tag.c src/tag_pool.c src/tag_handler.c \
- src/replay_gain_info.c \
+test_read_tags_SOURCES = test/read_tags.cxx \
+ src/IOThread.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx src/tag_handler.c \
+ src/ReplayGainInfo.cxx \
src/uri.c \
src/fd_util.c \
src/audio_check.c \
@@ -1132,93 +1228,89 @@ endif
test_run_filter_LDADD = \
$(FILTER_LIBS) \
+ libconf.a \
+ libfs.a \
$(GLIB_LIBS)
-test_run_filter_SOURCES = test/run_filter.c \
+test_run_filter_SOURCES = test/run_filter.cxx \
+ test/FakeReplayGainConfig.cxx \
test/stdbin.h \
- src/filter_plugin.c \
- src/filter_registry.c \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c \
+ src/FilterPlugin.cxx src/FilterRegistry.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c \
src/audio_check.c \
src/audio_format.c \
- src/audio_parser.c \
- src/replay_gain_config.c \
- src/replay_gain_info.c \
+ src/AudioParser.cxx \
+ src/ReplayGainInfo.cxx \
src/AudioCompress/compress.c
if ENABLE_DESPOTIFY
-test_read_tags_SOURCES += \
- src/despotify_utils.c
-test_run_input_SOURCES += \
- src/despotify_utils.c
-test_dump_text_file_SOURCES += \
- src/despotify_utils.c
-test_dump_playlist_SOURCES += \
- src/despotify_utils.c
-test_run_decoder_SOURCES += \
- src/despotify_utils.c
+test_read_tags_SOURCES += src/DespotifyUtils.cxx
+test_run_input_SOURCES += src/DespotifyUtils.cxx
+test_dump_text_file_SOURCES += src/DespotifyUtils.cxx
+test_dump_playlist_SOURCES += src/DespotifyUtils.cxx
+test_run_decoder_SOURCES += src/DespotifyUtils.cxx
endif
if ENABLE_ENCODER
noinst_PROGRAMS += test/run_encoder
-test_run_encoder_SOURCES = test/run_encoder.c \
+test_run_encoder_SOURCES = test/run_encoder.cxx \
test/stdbin.h \
- src/fifo_buffer.c src/growing_fifo.c \
- src/conf.c src/tokenizer.c \
- src/utils.c src/string_util.c \
- src/tag.c src/tag_pool.c \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx \
src/audio_check.c \
src/audio_format.c \
- src/audio_parser.c
+ src/AudioParser.cxx
test_run_encoder_LDADD = \
$(ENCODER_LIBS) \
- libpcm.a \
$(TAG_LIBS) \
+ libconf.a \
+ libpcm.a \
+ libfs.a \
+ libutil.a \
$(GLIB_LIBS)
endif
if ENABLE_VORBIS_ENCODER
noinst_PROGRAMS += test/test_vorbis_encoder
-test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.c \
+test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.cxx \
test/stdbin.h \
- src/conf.c src/tokenizer.c \
- src/utils.c \
- src/string_util.c \
- src/tag.c src/tag_pool.c \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx \
src/audio_check.c \
src/audio_format.c \
- src/audio_parser.c \
+ src/AudioParser.cxx \
src/pcm_buffer.c \
- src/fifo_buffer.c src/growing_fifo.c \
$(ENCODER_SRC)
test_test_vorbis_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
$(ENCODER_CFLAGS)
test_test_vorbis_encoder_LDADD = $(MPD_LIBS) \
$(ENCODER_LIBS) \
+ libconf.a \
+ libfs.a \
+ libutil.a \
$(GLIB_LIBS)
endif
-test_software_volume_SOURCES = test/software_volume.c \
+test_software_volume_SOURCES = test/software_volume.cxx \
test/stdbin.h \
src/audio_check.c \
- src/audio_parser.c
+ src/AudioParser.cxx
test_software_volume_LDADD = \
$(PCM_LIBS) \
$(GLIB_LIBS)
-test_run_normalize_SOURCES = test/run_normalize.c \
+test_run_normalize_SOURCES = test/run_normalize.cxx \
test/stdbin.h \
src/audio_check.c \
- src/audio_parser.c \
+ src/AudioParser.cxx \
src/AudioCompress/compress.c
test_run_normalize_LDADD = \
$(GLIB_LIBS)
-test_run_convert_SOURCES = test/run_convert.c \
+test_run_convert_SOURCES = test/run_convert.cxx \
src/dsd2pcm/dsd2pcm.c \
- src/fifo_buffer.c \
src/audio_format.c \
src/audio_check.c \
- src/audio_parser.c
+ src/AudioParser.cxx
test_run_convert_LDADD = \
$(PCM_LIBS) \
libutil.a \
@@ -1229,44 +1321,49 @@ test_run_output_LDADD = $(MPD_LIBS) \
$(ENCODER_LIBS) \
libmixer_plugins.a \
$(FILTER_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
libutil.a \
$(GLIB_LIBS)
-test_run_output_SOURCES = test/run_output.c \
+test_run_output_SOURCES = test/run_output.cxx \
+ test/FakeReplayGainConfig.cxx \
test/stdbin.h \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
- src/io_thread.c src/io_thread.h \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/IOThread.cxx \
src/audio_check.c \
src/audio_format.c \
- src/audio_parser.c \
+ src/AudioParser.cxx \
src/timer.c src/clock.c \
- src/tag.c src/tag_pool.c \
- src/fifo_buffer.c src/growing_fifo.c \
- src/page.c \
- src/socket_util.c \
+ src/Tag.cxx src/TagNames.c src/TagPool.cxx \
+ src/Page.cxx \
+ src/SocketUtil.cxx \
src/resolver.c \
- src/output_init.c src/output_finish.c src/output_list.c \
- src/output_plugin.c \
- src/mixer_api.c \
- src/mixer_control.c \
- src/mixer_type.c \
- src/filter_plugin.c \
- src/filter_config.c \
+ src/OutputInit.cxx src/OutputFinish.cxx src/OutputList.cxx \
+ src/OutputPlugin.cxx \
+ src/MixerInternal.cxx \
+ src/MixerControl.cxx \
+ src/MixerType.cxx \
+ src/FilterPlugin.cxx \
+ src/FilterConfig.cxx \
src/AudioCompress/compress.c \
- src/replay_gain_info.c \
- src/replay_gain_config.c \
- src/fd_util.c \
- src/server_socket.c
+ src/ReplayGainInfo.cxx \
+ src/fd_util.c
test_read_mixer_LDADD = \
libpcm.a \
libmixer_plugins.a \
$(OUTPUT_LIBS) \
+ libconf.a \
+ libevent.a \
+ libfs.a \
$(GLIB_LIBS)
-test_read_mixer_SOURCES = test/read_mixer.c \
- src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
- src/mixer_control.c src/mixer_api.c \
- src/filter_plugin.c \
- src/filter/volume_filter_plugin.c \
+test_read_mixer_SOURCES = test/read_mixer.cxx \
+ src/tokenizer.c src/utils.c src/string_util.c \
+ src/MixerControl.cxx \
+ src/MixerInternal.cxx \
+ src/FilterPlugin.cxx \
+ src/filter/VolumeFilterPlugin.cxx \
src/fd_util.c
if ENABLE_BZIP2_TEST
@@ -1283,11 +1380,13 @@ endif
if ENABLE_INOTIFY
noinst_PROGRAMS += test/run_inotify
-test_run_inotify_SOURCES = test/run_inotify.c \
+test_run_inotify_SOURCES = test/run_inotify.cxx \
src/fd_util.c \
- src/fifo_buffer.c \
- src/inotify_source.c
-test_run_inotify_LDADD = $(GLIB_LIBS)
+ src/InotifySource.cxx
+test_run_inotify_LDADD = \
+ libevent.a \
+ libutil.a \
+ $(GLIB_LIBS)
endif
test_test_byte_reverse_SOURCES = \
@@ -1297,24 +1396,28 @@ test_test_byte_reverse_LDADD = \
$(GLIB_LIBS)
test_test_pcm_SOURCES = \
- test/test_pcm_dither.c \
- test/test_pcm_pack.c \
- test/test_pcm_channels.c \
- test/test_pcm_volume.c \
- test/test_pcm_all.h \
- test/test_pcm_main.c
+ test/test_pcm_util.hxx \
+ test/test_pcm_dither.cxx \
+ test/test_pcm_pack.cxx \
+ test/test_pcm_channels.cxx \
+ test/test_pcm_format.cxx \
+ test/test_pcm_volume.cxx \
+ test/test_pcm_mix.cxx \
+ test/test_pcm_all.hxx \
+ test/test_pcm_main.cxx
test_test_pcm_LDADD = \
$(PCM_LIBS) \
libutil.a \
$(GLIB_LIBS)
test_test_queue_priority_SOURCES = \
- src/queue.c \
- test/test_queue_priority.c
+ src/Queue.cxx \
+ src/fd_util.c \
+ test/test_queue_priority.cxx
test_test_queue_priority_LDADD = \
+ libutil.a \
$(GLIB_LIBS)
-if HAVE_CXX
noinst_PROGRAMS += src/dsd2pcm/dsd2pcm
src_dsd2pcm_dsd2pcm_SOURCES = \
@@ -1322,7 +1425,6 @@ src_dsd2pcm_dsd2pcm_SOURCES = \
src/dsd2pcm/noiseshape.c src/dsd2pcm/noiseshape.h \
src/dsd2pcm/main.cpp
src_dsd2pcm_dsd2pcm_LDADD = libutil.a
-endif
endif
diff --git a/NEWS b/NEWS
index 7fa7dc03..9ee6791a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,20 @@
+ver 0.18 (2012/??/??)
+* decoder:
+ - adplug: new decoder plugin using libadplug
+ - flac: require libFLAC 1.2 or newer
+ - flac: support FLAC files inside archives
+ - opus: new decoder plugin for the Opus codec
+ - vorbis: skip 16 bit quantisation, provide float samples
+ - mp4ff: obsolete plugin removed
+* encoder:
+ - opus: new encoder plugin for the Opus codec
+ - vorbis: accept floating point input samples
+* output:
+ - new option "tags" may be used to disable sending tags to output
+ - alsa: workaround for noise after manual song change
+* improved decoder/output error reporting
+
+
ver 0.17.4 (2013/??/??)
* protocol:
- allow to omit END in ranges (START:END)
@@ -7,7 +24,6 @@ ver 0.17.4 (2013/??/??)
- implement missing "idle" events on output errors
* clock: fix build failure
-
ver 0.17.3 (2013/01/06)
* output:
- osx: fix pops during playback
diff --git a/configure.ac b/configure.ac
index 05f28882..19246980 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,13 +1,13 @@
AC_PREREQ(2.60)
-AC_INIT(mpd, 0.17.4~git, musicpd-dev-team@lists.sourceforge.net)
+AC_INIT(mpd, 0.18~git, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
-VERSION_MINOR=17
+VERSION_MINOR=18
VERSION_REVISION=0
VERSION_EXTRA=0
-AC_CONFIG_SRCDIR([src/main.c])
+AC_CONFIG_SRCDIR([src/Main.cxx])
AM_INIT_AUTOMAKE([foreign 1.11 dist-bzip2 subdir-objects])
AM_SILENT_RULES
AC_CONFIG_HEADERS(config.h)
@@ -23,22 +23,6 @@ AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_RANLIB
-HAVE_CXX=yes
-if test x$CXX = xg++; then
- # CXX=g++ probably means that autoconf hasn't found any C++
- # compiler; to be sure, we check again
- AC_PATH_PROG(CXX, $CXX, no)
- if test x$CXX = xno; then
- # no, we don't have C++ - the following hack is
- # required because automake insists on using $(CXX)
- # for linking the MPD binary
- AC_MSG_NOTICE([Disabling C++ support])
- CXX="$CC"
- HAVE_CXX=no
- fi
-fi
-AM_CONDITIONAL(HAVE_CXX, test x$HAVE_CXX = xyes)
-
AC_PROG_INSTALL
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
@@ -82,7 +66,8 @@ mingw32* | windows*)
src/win/mpd_win32_rc.rc
])
AC_CHECK_TOOL(WINDRES, windres)
- AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0501"
+ AM_CPPFLAGS="$AM_CPPFLAGS -DWIN32_LEAN_AND_MEAN"
+ AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0600 -D_WIN32_WINNT=0x0600"
LIBS="$LIBS -lws2_32"
HAVE_WINDOWS=1
;;
@@ -126,6 +111,19 @@ if test -z "$prefix" || test "x$prefix" = xNONE; then
fi
dnl ---------------------------------------------------------------------------
+dnl Language Checks
+dnl ---------------------------------------------------------------------------
+
+AC_CXX_COMPILE_STDCXX_0X
+if test "$ax_cv_cxx_compile_cxx0x_native" != yes; then
+ if test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
+ AM_CXXFLAGS="$AM_CXXFLAGS -std=gnu++0x"
+ elif test "$ax_cv_cxx_compile_cxx0x_cxx" = yes; then
+ AM_CXXFLAGS="$AM_CXXFLAGS -std=c++0x"
+ fi
+fi
+
+dnl ---------------------------------------------------------------------------
dnl Header/Library Checks
dnl ---------------------------------------------------------------------------
AC_CHECK_FUNCS(daemon fork)
@@ -136,7 +134,9 @@ AC_SEARCH_LIBS([syslog], [bsd socket inet],
AC_SEARCH_LIBS([socket], [socket])
AC_SEARCH_LIBS([gethostbyname], [nsl])
-AC_CHECK_FUNCS(pipe2 accept4)
+AC_CHECK_FUNCS(pipe2 accept4 eventfd)
+
+AC_CHECK_FUNCS(strnlen strndup)
AC_SEARCH_LIBS([exp], [m],,
[AC_MSG_ERROR([exp() not found])])
@@ -147,6 +147,17 @@ AC_CHECK_HEADERS(valgrind/memcheck.h)
dnl ---------------------------------------------------------------------------
dnl Allow tools to be specifically built
dnl ---------------------------------------------------------------------------
+
+AC_ARG_ENABLE(mpdclient,
+ AS_HELP_STRING([--enable-libmpdclient],
+ [enable support for the MPD client]),,
+ enable_libmpdclient=auto)
+
+AC_ARG_ENABLE(adplug,
+ AS_HELP_STRING([--enable-adplug],
+ [enable the AdPlug decoder plugin (default: auto)]),,
+ enable_adplug=auto)
+
AC_ARG_ENABLE(alsa,
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
[enable_alsa=auto])
@@ -326,6 +337,11 @@ AC_ARG_ENABLE(openal,
[enable OpenAL support (default: disable)]),,
enable_openal=no)
+AC_ARG_ENABLE(opus,
+ AS_HELP_STRING([--enable-opus],
+ [enable Opus codec support (default: auto)]),,
+ enable_opus=auto)
+
AC_ARG_ENABLE(oss,
AS_HELP_STRING([--disable-oss],
[disable OSS support (default: enable)]),,
@@ -456,8 +472,8 @@ AC_ARG_WITH(tremor-includes,
dnl ---------------------------------------------------------------------------
dnl Mandatory Libraries
dnl ---------------------------------------------------------------------------
-PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16 gthread-2.0],,
- [AC_MSG_ERROR([GLib 2.16 is required])])
+PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.24 gthread-2.0],,
+ [AC_MSG_ERROR([GLib 2.24 is required])])
if test x$GCC = xyes; then
# suppress warnings in the GLib headers
@@ -537,6 +553,15 @@ dnl ---------------------------------------------------------------------------
dnl Miscellaneous Libraries
dnl ---------------------------------------------------------------------------
+dnl -------------------------------- libmpdclient --------------------------------
+MPD_AUTO_PKG(libmpdclient, LIBMPDCLIENT, [libmpdclient >= 2.2],
+ [MPD client library], [libmpdclient not found])
+if test x$enable_libmpdclient = xyes; then
+ AC_DEFINE(HAVE_LIBMPDCLIENT, 1, [Define to use libmpdclient])
+fi
+
+AM_CONDITIONAL(HAVE_LIBMPDCLIENT, test x$enable_libmpdclient = xyes)
+
dnl --------------------------------- inotify ---------------------------------
AC_CHECK_FUNCS(inotify_init inotify_init1)
@@ -552,6 +577,27 @@ AM_CONDITIONAL(ENABLE_INOTIFY, test x$enable_inotify = xyes)
dnl --------------------------------- libwrap ---------------------------------
if test x$enable_libwrap != xno; then
AC_CHECK_LIBWRAP(found_libwrap=yes, found_libwrap=no)
+
+ if test x$found_libwrap = xyes; then
+ dnl See if libwrap is compatible with C++; it is
+ dnl broken on many systems
+ AC_MSG_CHECKING(whether libwrap is compatible with C++)
+ AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ #include <tcpd.h>
+ bool CheckLibWrap(int fd, const char &progname) {
+ struct request_info req;
+ request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
+ fromhost(&req);
+ return hosts_access(&req);
+ }
+ ])],
+ AC_MSG_RESULT([yes]),
+ [found_libwrap=no; AC_MSG_RESULT([no]);
+ AC_MSG_WARN([Your version of libwrap is broken with C++])])
+ AC_LANG_POP
+ fi
+
MPD_AUTO_RESULT(libwrap, libwrap, [libwrap not found])
fi
@@ -669,7 +715,7 @@ dnl Input Plugins
dnl ---------------------------------------------------------------------------
dnl ----------------------------------- CURL ----------------------------------
-MPD_AUTO_PKG(curl, CURL, [libcurl],
+MPD_AUTO_PKG(curl, CURL, [libcurl >= 7.18],
[libcurl HTTP streaming], [libcurl not found])
if test x$enable_curl = xyes; then
AC_DEFINE(ENABLE_CURL, 1, [Define when libcurl is used for HTTP streaming])
@@ -806,6 +852,14 @@ dnl ---------------------------------------------------------------------------
dnl Decoder Plugins
dnl ---------------------------------------------------------------------------
+dnl -------------------------------- libadplug --------------------------------
+MPD_AUTO_PKG(adplug, ADPLUG, [adplug],
+ [AdPlug decoder plugin], [libadplug not found])
+if test x$enable_adplug = xyes; then
+ AC_DEFINE(HAVE_ADPLUG, 1, [Define to use libadplug])
+fi
+AM_CONDITIONAL(HAVE_ADPLUG, test x$enable_adplug = xyes)
+
dnl -------------------------------- audiofile --------------------------------
MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.1.7],
[audiofile decoder plugin], [libaudiofile not found])
@@ -818,10 +872,9 @@ dnl ----------------------------------- FAAD ----------------------------------
AM_PATH_FAAD()
AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes)
-AM_CONDITIONAL(HAVE_MP4, test x$enable_mp4 = xyes)
dnl ---------------------------------- ffmpeg ---------------------------------
-MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52.31 libavcodec >= 52.20 libavutil >= 49.15],
+MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 53.2 libavcodec >= 53.5 libavutil >= 51.7],
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
if test x$enable_ffmpeg = xyes; then
@@ -832,7 +885,7 @@ AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
dnl ----------------------------------- FLAC ----------------------------------
-MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
+MPD_AUTO_PKG(flac, FLAC, [flac >= 1.2],
[FLAC decoder], [libFLAC not found])
if test x$enable_flac = xyes; then
@@ -899,9 +952,6 @@ fi
AM_CONDITIONAL(ENABLE_MIKMOD_DECODER, test x$enable_mikmod = xyes)
dnl -------------------------------- libmodplug -------------------------------
-found_modplug=$HAVE_CXX
-MPD_AUTO_PRE(modplug, [modplug decoder plugin], [No C++ compiler found])
-
MPD_AUTO_PKG(modplug, MODPLUG, [libmodplug],
[modplug decoder plugin], [libmodplug not found])
@@ -910,6 +960,14 @@ if test x$enable_modplug = xyes; then
fi
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
+dnl -------------------------------- libopus ----------------------------------
+MPD_AUTO_PKG(opus, OPUS, [opus ogg],
+ [opus decoder plugin], [libopus not found])
+if test x$enable_opus = xyes; then
+ AC_DEFINE(HAVE_OPUS, 1, [Define to use libopus])
+fi
+AM_CONDITIONAL(HAVE_OPUS, test x$enable_opus = xyes)
+
dnl -------------------------------- libsndfile -------------------------------
dnl See above test, which may disable this.
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
@@ -1015,9 +1073,6 @@ fi
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
dnl --------------------------------- sidplay ---------------------------------
-found_sidplay=$HAVE_CXX
-MPD_AUTO_PRE(sidplay, [sidplay decoder plugin], [No C++ compiler found])
-
if test x$enable_sidplay != xno; then
# we're not using pkg-config here
# because libsidplay2's .pc file requires libtool
@@ -1092,9 +1147,9 @@ if
test x$enable_mad = xno &&
test x$enable_mikmod = xno; then
test x$enable_modplug = xno &&
- test x$enable_mp4 = xno &&
test x$enable_mpc = xno &&
test x$enable_mpg123 = xno &&
+ test x$enable_opus = xno &&
test x$enable_sidplay = xno &&
test x$enable_tremor = xno &&
test x$enable_vorbis = xno &&
@@ -1104,11 +1159,8 @@ if
AC_MSG_ERROR([No input plugins supported!])
fi
-AM_CONDITIONAL(HAVE_OGG_COMMON,
- test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
-
-AM_CONDITIONAL(HAVE_FLAC_COMMON,
- test x$enable_flac = xyes)
+AM_CONDITIONAL(HAVE_XIPH,
+ test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes || test x$enable_opus = xyes)
dnl ---------------------------------------------------------------------------
dnl Encoders for Streaming Audio Output Plugins
@@ -1196,6 +1248,7 @@ fi
dnl --------------------------- encoder plugins test --------------------------
if test x$enable_vorbis_encoder != xno ||
+ test x$enable_opus != xno ||
test x$enable_lame_encoder != xno ||
test x$enable_twolame_encoder != xno ||
test x$enable_flac_encoder != xno ||
@@ -1499,6 +1552,22 @@ dnl ---------------------------------------------------------------------------
dnl ---------------------------------- debug ----------------------------------
if test "x$enable_debug" = xno; then
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
+
+ AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
+ AX_APPEND_COMPILE_FLAGS([-fdata-sections])
+ AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
+
+ AC_LANG_PUSH([C++])
+ AX_APPEND_COMPILE_FLAGS([-ffunction-sections])
+ AX_APPEND_COMPILE_FLAGS([-fdata-sections])
+ AX_APPEND_COMPILE_FLAGS([-fvisibility=hidden])
+ AX_APPEND_COMPILE_FLAGS([-fno-threadsafe-statics])
+ AX_APPEND_COMPILE_FLAGS([-fmerge-all-constants])
+ AX_APPEND_COMPILE_FLAGS([-fno-exceptions])
+ AX_APPEND_COMPILE_FLAGS([-fno-rtti])
+ AC_LANG_POP
+
+ AX_APPEND_LINK_FLAGS([-Wl,--gc-sections])
fi
dnl ----------------------------------- GCC -----------------------------------
@@ -1513,6 +1582,17 @@ then
AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
AX_APPEND_COMPILE_FLAGS([-pedantic])
+
+ AC_LANG_PUSH([C++])
+ AX_APPEND_COMPILE_FLAGS([-Wall])
+ AX_APPEND_COMPILE_FLAGS([-Wextra])
+ AX_APPEND_COMPILE_FLAGS([-Wmissing-declarations])
+ AX_APPEND_COMPILE_FLAGS([-Wshadow])
+ AX_APPEND_COMPILE_FLAGS([-Wpointer-arith])
+ AX_APPEND_COMPILE_FLAGS([-Wcast-qual])
+ AX_APPEND_COMPILE_FLAGS([-Wwrite-strings])
+ AX_APPEND_COMPILE_FLAGS([-Wsign-compare])
+ AC_LANG_POP
fi
dnl ---------------------------- warnings as errors ---------------------------
@@ -1545,20 +1625,21 @@ results(un,[UNIX Domain Sockets])
printf '\nFile format support:\n\t'
results(aac, [AAC])
+results(adplug, [AdPlug])
results(sidplay, [C64 SID])
results(ffmpeg, [FFMPEG])
results(flac, [FLAC])
results(fluidsynth, [FluidSynth])
results(gme, [GME])
-results(sndfile, [libsndfile])
printf '\n\t'
+results(sndfile, [libsndfile])
results(mikmod, [MikMod])
results(modplug, [MODPLUG])
results(mad, [MAD])
results(mpg123, [MPG123])
-results(mp4, [MP4])
results(mpc, [Musepack])
printf '\n\t'
+results(opus, [Opus])
results(tremor, [OggTremor])
results(vorbis, [OggVorbis])
results(audiofile, [WAVE])
@@ -1567,6 +1648,7 @@ results(wildmidi, [WildMidi])
printf '\nOther features:\n\t'
results(lsr, [libsamplerate])
+results(libmpdclient, [libmpdclient])
results(inotify, [inotify])
results(sqlite, [SQLite])
@@ -1602,6 +1684,7 @@ if
results(flac_encoder, [FLAC])
results(lame_encoder, [LAME])
results(vorbis_encoder, [Ogg Vorbis])
+ results(opus, [Opus])
results(twolame_encoder, [TwoLAME])
results(wave_encoder, [WAVE])
fi
diff --git a/doc/user.xml b/doc/user.xml
index 38d8a9d8..c83c625a 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -165,6 +165,53 @@ systemctl start mpd.socket</programlisting>
</section>
<section>
+ <title>Configuring database plugins</title>
+
+ <para>
+ If a music directory is configured, one database plugin is
+ used. To configure this plugin, add a
+ <varname>database</varname> block to
+ <filename>mpd.conf</filename>:
+ </para>
+
+ <programlisting>database {
+ plugin "simple"
+ path "/var/lib/mpd/db"
+}
+ </programlisting>
+
+ <para>
+ The following table lists the <varname>database</varname>
+ options valid for all plugins:
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>
+ Name
+ </entry>
+ <entry>
+ Description
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>plugin</varname>
+ </entry>
+ <entry>
+ The name of the plugin.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
<title>Configuring input plugins</title>
<para>
@@ -391,6 +438,18 @@ systemctl start mpd.socket</programlisting>
</row>
<row>
<entry>
+ <varname>tags</varname>
+ <parameter>yes|no</parameter>
+ </entry>
+ <entry>
+ If set to "no", then MPD will not send tags to this
+ output. This is only useful for output plugins that
+ can receive tags, for example the
+ <varname>httpd</varname> output plugin.
+ </entry>
+ </row>
+ <row>
+ <entry>
<varname>always_on</varname>
<parameter>yes|no</parameter>
</entry>
@@ -618,6 +677,78 @@ systemctl start mpd.socket</programlisting>
<title>Plugin reference</title>
<section>
+ <title>Database plugins</title>
+
+ <section>
+ <title><varname>simple</varname></title>
+
+ <para>
+ The default plugin. Stores a copy of the database in
+ memory. A file is used for permanent storage.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>path</varname>
+ </entry>
+ <entry>
+ The path of the database file.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>proxy</varname></title>
+
+ <para>
+ Provides access to the database of another MPD instance
+ using <filename>libmpdclient</filename>. Experimental!
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>host</varname>
+ </entry>
+ <entry>
+ The host name of the "master" MPD instance.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>port</varname>
+ </entry>
+ <entry>
+ The port number of the "master" MPD instance.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+ </section>
+
+ <section>
<title>Input plugins</title>
<section>
diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4
new file mode 100644
index 00000000..4fc43370
--- /dev/null
+++ b/m4/ax_append_link_flags.m4
@@ -0,0 +1,61 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# For every FLAG1, FLAG2 it is checked whether the linker works with the
+# flag. If it does, the flag is added FLAGS-VARIABLE
+#
+# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
+# used. During the check the flag is always added to the linker's flags.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
+# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
+#
+# LICENSE
+#
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program 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, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program 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. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_APPEND_LINK_FLAGS],
+[for flag in $1; do
+ AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3])
+done
+])dnl AX_APPEND_LINK_FLAGS
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
new file mode 100644
index 00000000..e2d0d363
--- /dev/null
+++ b/m4/ax_check_link_flag.m4
@@ -0,0 +1,71 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the linker or gives an error.
+# (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# This program 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, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program 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. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+ ax_check_save_flags=$LDFLAGS
+ LDFLAGS="$LDFLAGS $4 $1"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ LDFLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/ax_cxx_compile_stdcxx_0x.m4 b/m4/ax_cxx_compile_stdcxx_0x.m4
new file mode 100644
index 00000000..a4e556ff
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx_0x.m4
@@ -0,0 +1,107 @@
+# ============================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_0x.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX_0X
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the C++0x
+# standard.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 7
+
+AU_ALIAS([AC_CXX_COMPILE_STDCXX_0X], [AX_CXX_COMPILE_STDCXX_0X])
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_0X], [
+ AC_CACHE_CHECK(if g++ supports C++0x features without additional flags,
+ ax_cv_cxx_compile_cxx0x_native,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_native=yes, ax_cv_cxx_compile_cxx0x_native=no)
+ AC_LANG_RESTORE
+ ])
+
+ AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x,
+ ax_cv_cxx_compile_cxx0x_cxx,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -std=c++0x"
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_cxx=yes, ax_cv_cxx_compile_cxx0x_cxx=no)
+ CXXFLAGS="$ac_save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x,
+ ax_cv_cxx_compile_cxx0x_gxx,
+ [AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ ac_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS -std=gnu++0x"
+ AC_TRY_COMPILE([
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ typedef check<check<bool>> right_angle_brackets;
+
+ int a;
+ decltype(a) b;
+
+ typedef check<int> check_type;
+ check_type c;
+ check_type&& cr = static_cast<check_type&&>(c);],,
+ ax_cv_cxx_compile_cxx0x_gxx=yes, ax_cv_cxx_compile_cxx0x_gxx=no)
+ CXXFLAGS="$ac_save_CXXFLAGS"
+ AC_LANG_RESTORE
+ ])
+
+ if test "$ax_cv_cxx_compile_cxx0x_native" = yes ||
+ test "$ax_cv_cxx_compile_cxx0x_cxx" = yes ||
+ test "$ax_cv_cxx_compile_cxx0x_gxx" = yes; then
+ AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ])
+ fi
+])
diff --git a/m4/faad.m4 b/m4/faad.m4
index 1048c566..5ca520e7 100644
--- a/m4/faad.m4
+++ b/m4/faad.m4
@@ -8,38 +8,14 @@ AC_ARG_ENABLE(aac,
[disable AAC support (default: enable)]),,
enable_aac=yes)
-AC_ARG_WITH(faad,
- AS_HELP_STRING([--with-faad=PFX],
- [prefix where faad2 is installed (optional)]),,
- faad_prefix="")
-AC_ARG_WITH(faad-libraries,
- AS_HELP_STRING([--with-faad-libraries=DIR],
- [directory where faad2 library is installed (optional)]),,
- faad_libraries="")
-AC_ARG_WITH(faad-includes,
- AS_HELP_STRING([--with-faad-includes=DIR],
- [directory where faad2 header files are installed (optional)]),,
- faad_includes="")
-
if test x$enable_aac = xyes; then
- if test "x$faad_libraries" != "x" ; then
- FAAD_LIBS="-L$faad_libraries"
- elif test "x$faad_prefix" != "x" ; then
- FAAD_LIBS="-L$faad_prefix/lib"
- fi
-
- FAAD_LIBS="$FAAD_LIBS -lfaad"
-
- if test "x$faad_includes" != "x" ; then
- FAAD_CFLAGS="-I$faad_includes"
- elif test "x$faad_prefix" != "x" ; then
- FAAD_CFLAGS="-I$faad_prefix/include"
- fi
+ FAAD_LIBS="-lfaad"
+ FAAD_CFLAGS=""
oldcflags=$CFLAGS
oldlibs=$LIBS
oldcppflags=$CPPFLAGS
- CFLAGS="$CFLAGS $FAAD_CFLAGS -I."
+ CFLAGS="$CFLAGS $FAAD_CFLAGS"
LIBS="$LIBS $FAAD_LIBS"
CPPFLAGS=$CFLAGS
AC_CHECK_HEADER(faad.h,,enable_aac=no)
@@ -47,77 +23,36 @@ if test x$enable_aac = xyes; then
AC_CHECK_DECL(FAAD2_VERSION,,enable_aac=no,[#include <faad.h>])
fi
if test x$enable_aac = xyes; then
- AC_CHECK_DECL(faacDecInit2,,enable_aac=no,[#include <faad.h>])
- fi
- if test x$enable_aac = xyes; then
- AC_CHECK_LIB(faad,faacDecInit2,,enable_aac=no)
- if test x$enable_aac = xno; then
- enable_aac=yes
- AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no)
- fi
+ AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no)
fi
if test x$enable_aac = xyes; then
- AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
-#include <faad.h>
-
-int main() {
- char buffer;
- long bufferlen = 0;
- faacDecHandle decoder;
- faacDecFrameInfo frameInfo;
- faacDecConfigurationPtr config;
- unsigned char channels;
- long sampleRate;
- mp4AudioSpecificConfig mp4ASC;
-
- decoder = faacDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
- config->outputFormat = FAAD_FMT_16BIT;
- faacDecSetConfiguration(decoder,config);
- AudioSpecificConfig(&buffer, bufferlen, &mp4ASC);
- faacDecInit(decoder,&buffer,bufferlen,&sampleRate,&channels);
- faacDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
- faacDecDecode(decoder,&frameInfo,&buffer,bufferlen);
-
- return 0;
-}
-])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
AC_MSG_CHECKING(that FAAD2 can even be used)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <faad.h>
int main() {
char buffer;
- faacDecHandle decoder;
- faacDecFrameInfo frameInfo;
- faacDecConfigurationPtr config;
+ NeAACDecHandle decoder;
+ NeAACDecFrameInfo frameInfo;
+ NeAACDecConfigurationPtr config;
unsigned char channels;
long sampleRate;
long bufferlen = 0;
- unsigned long dummy1_32;
- unsigned char dummy2_8, dummy3_8, dummy4_8, dummy5_8, dummy6_8,
- dummy7_8, dummy8_8;
- decoder = faacDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ decoder = NeAACDecOpen();
+ config = NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
- faacDecSetConfiguration(decoder,config);
- AudioSpecificConfig(&buffer,&dummy1_32,&dummy2_8,
- &dummy3_8,&dummy4_8,&dummy5_8,
- &dummy6_8,&dummy7_8,&dummy8_8);
- faacDecInit(decoder,&buffer,&sampleRate,&channels);
- faacDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
- faacDecDecode(decoder,&frameInfo,&buffer);
- faacDecClose(decoder);
+ NeAACDecSetConfiguration(decoder,config);
+ NeAACDecInit(decoder,&buffer,bufferlen,&sampleRate,&channels);
+ NeAACDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels);
+ NeAACDecDecode(decoder,&frameInfo,&buffer,bufferlen);
+ NeAACDecClose(decoder);
return 0;
}
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
- ])
fi
if test x$enable_aac = xyes; then
- AC_CHECK_MEMBERS([faacDecConfiguration.downMatrix,faacDecConfiguration.dontUpSampleImplicitSBR,faacDecFrameInfo.samplerate],,,[#include <faad.h>])
AC_DEFINE(HAVE_FAAD,1,[Define to use FAAD2 for AAC decoding])
else
AC_MSG_WARN([faad2 lib needed for MP4/AAC support -- disabling MP4/AAC support])
@@ -145,7 +80,7 @@ int main() {
unsigned char channels;
uint32_t sample_rate;
- faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
+ NeAACDecInit2(NULL, NULL, 0, &sample_rate, &channels);
return 0;
}
])],
@@ -156,40 +91,9 @@ int main() {
CFLAGS=$oldcflags
LIBS=$oldlibs
CPPFLAGS=$oldcppflags
-fi
-
-if test x$enable_aac = xyes; then
- enable_mp4=yes
- MP4FF_LIBS="-lmp4ff"
-
- oldcflags=$CFLAGS
- oldlibs=$LIBS
- oldcppflags=$CPPFLAGS
- CFLAGS="$CFLAGS $FAAD_CFLAGS"
- LIBS="$LIBS $FAAD_LIBS $MP4FF_LIBS"
- CPPFLAGS=$CFLAGS
-
- AC_CHECK_HEADER(mp4ff.h,,enable_mp4=no)
-
- if test x$enable_mp4 = xyes; then
- AC_CHECK_LIB(mp4ff,mp4ff_open_read,,enable_mp4=no)
- fi
-
- if test x$enable_mp4 = xyes; then
- AC_SUBST(MP4FF_LIBS)
- AC_DEFINE(HAVE_MP4, 1, [Define to use FAAD2+mp4ff for MP4 decoding])
- else
- AC_MSG_WARN([libmp4ff needed for MP4 support -- disabling MP4 support])
- unset MP4FF_LIBS
- fi
-
- CFLAGS=$oldcflags
- LIBS=$oldlibs
- CPPFLAGS=$oldcppflags
else
- enable_mp4=no
- FAAD_CFLAGS=""
FAAD_LIBS=""
+ FAAD_CFLAGS=""
fi
AC_SUBST(FAAD_CFLAGS)
diff --git a/src/AllCommands.cxx b/src/AllCommands.cxx
new file mode 100644
index 00000000..58dcf4db
--- /dev/null
+++ b/src/AllCommands.cxx
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "AllCommands.hxx"
+#include "command.h"
+#include "QueueCommands.hxx"
+#include "PlayerCommands.hxx"
+#include "PlaylistCommands.hxx"
+#include "DatabaseCommands.hxx"
+#include "OutputCommands.hxx"
+#include "MessageCommands.hxx"
+#include "OtherCommands.hxx"
+#include "Permission.hxx"
+#include "tag.h"
+#include "protocol/Result.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "tokenizer.h"
+}
+
+#ifdef ENABLE_SQLITE
+#include "StickerCommands.hxx"
+#include "StickerDatabase.hxx"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+/*
+ * The most we ever use is for search/find, and that limits it to the
+ * number of tags we can have. Add one for the command, and one extra
+ * to catch errors clients may send us
+ */
+#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
+
+/* if min: -1 don't check args *
+ * if max: -1 no max args */
+struct command {
+ const char *cmd;
+ unsigned permission;
+ int min;
+ int max;
+ enum command_return (*handler)(Client *client, int argc, char **argv);
+};
+
+/* don't be fooled, this is the command handler for "commands" command */
+static enum command_return
+handle_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
+
+static enum command_return
+handle_not_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
+
+/**
+ * The command registry.
+ *
+ * This array must be sorted!
+ */
+static const struct command commands[] = {
+ { "add", PERMISSION_ADD, 1, 1, handle_add },
+ { "addid", PERMISSION_ADD, 1, 2, handle_addid },
+ { "channels", PERMISSION_READ, 0, 0, handle_channels },
+ { "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
+ { "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
+ { "close", PERMISSION_NONE, -1, -1, handle_close },
+ { "commands", PERMISSION_NONE, 0, 0, handle_commands },
+ { "config", PERMISSION_ADMIN, 0, 0, handle_config },
+ { "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
+ { "count", PERMISSION_READ, 2, -1, handle_count },
+ { "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
+ { "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
+ { "decoders", PERMISSION_READ, 0, 0, handle_decoders },
+ { "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
+ { "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
+ { "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
+ { "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
+ { "find", PERMISSION_READ, 2, -1, handle_find },
+ { "findadd", PERMISSION_READ, 2, -1, handle_findadd},
+ { "idle", PERMISSION_READ, 0, -1, handle_idle },
+ { "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
+ { "list", PERMISSION_READ, 1, -1, handle_list },
+ { "listall", PERMISSION_READ, 0, 1, handle_listall },
+ { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
+ { "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
+ { "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
+ { "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
+ { "load", PERMISSION_ADD, 1, 2, handle_load },
+ { "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
+ { "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
+ { "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
+ { "move", PERMISSION_CONTROL, 2, 2, handle_move },
+ { "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
+ { "next", PERMISSION_CONTROL, 0, 0, handle_next },
+ { "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
+ { "outputs", PERMISSION_READ, 0, 0, handle_devices },
+ { "password", PERMISSION_NONE, 1, 1, handle_password },
+ { "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
+ { "ping", PERMISSION_NONE, 0, 0, handle_ping },
+ { "play", PERMISSION_CONTROL, 0, 1, handle_play },
+ { "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
+ { "playlist", PERMISSION_READ, 0, 0, handle_playlist },
+ { "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
+ { "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
+ { "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
+ { "playlistfind", PERMISSION_READ, 2, -1, handle_playlistfind },
+ { "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
+ { "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
+ { "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
+ { "playlistsearch", PERMISSION_READ, 2, -1, handle_playlistsearch },
+ { "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
+ { "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
+ { "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
+ { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
+ { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
+ { "random", PERMISSION_CONTROL, 1, 1, handle_random },
+ { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
+ { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
+ { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
+ { "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
+ handle_replay_gain_mode },
+ { "replay_gain_status", PERMISSION_READ, 0, 0,
+ handle_replay_gain_status },
+ { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
+ { "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
+ { "save", PERMISSION_CONTROL, 1, 1, handle_save },
+ { "search", PERMISSION_READ, 2, -1, handle_search },
+ { "searchadd", PERMISSION_ADD, 2, -1, handle_searchadd },
+ { "searchaddpl", PERMISSION_CONTROL, 3, -1, handle_searchaddpl },
+ { "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
+ { "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
+ { "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
+ { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
+ { "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
+ { "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
+ { "single", PERMISSION_CONTROL, 1, 1, handle_single },
+ { "stats", PERMISSION_READ, 0, 0, handle_stats },
+ { "status", PERMISSION_READ, 0, 0, handle_status },
+#ifdef ENABLE_SQLITE
+ { "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
+#endif
+ { "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
+ { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
+ { "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
+ { "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
+ { "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
+ { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
+ { "update", PERMISSION_CONTROL, 0, 1, handle_update },
+ { "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
+};
+
+static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
+
+static bool
+command_available(G_GNUC_UNUSED const struct command *cmd)
+{
+#ifdef ENABLE_SQLITE
+ if (strcmp(cmd->cmd, "sticker") == 0)
+ return sticker_enabled();
+#endif
+
+ return true;
+}
+
+/* don't be fooled, this is the command handler for "commands" command */
+static enum command_return
+handle_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const unsigned permission = client_get_permission(client);
+ const struct command *cmd;
+
+ for (unsigned i = 0; i < num_commands; ++i) {
+ cmd = &commands[i];
+
+ if (cmd->permission == (permission & cmd->permission) &&
+ command_available(cmd))
+ client_printf(client, "command: %s\n", cmd->cmd);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_not_commands(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const unsigned permission = client_get_permission(client);
+ const struct command *cmd;
+
+ for (unsigned i = 0; i < num_commands; ++i) {
+ cmd = &commands[i];
+
+ if (cmd->permission != (permission & cmd->permission))
+ client_printf(client, "command: %s\n", cmd->cmd);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+void command_init(void)
+{
+#ifndef NDEBUG
+ /* ensure that the command list is sorted */
+ for (unsigned i = 0; i < num_commands - 1; ++i)
+ assert(strcmp(commands[i].cmd, commands[i + 1].cmd) < 0);
+#endif
+}
+
+void command_finish(void)
+{
+}
+
+static const struct command *
+command_lookup(const char *name)
+{
+ unsigned a = 0, b = num_commands, i;
+ int cmp;
+
+ /* binary search */
+ do {
+ i = (a + b) / 2;
+
+ cmp = strcmp(name, commands[i].cmd);
+ if (cmp == 0)
+ return &commands[i];
+ else if (cmp < 0)
+ b = i;
+ else if (cmp > 0)
+ a = i + 1;
+ } while (a < b);
+
+ return NULL;
+}
+
+static bool
+command_check_request(const struct command *cmd, Client *client,
+ unsigned permission, int argc, char *argv[])
+{
+ int min = cmd->min + 1;
+ int max = cmd->max + 1;
+
+ if (cmd->permission != (permission & cmd->permission)) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_PERMISSION,
+ "you don't have permission for \"%s\"",
+ cmd->cmd);
+ return false;
+ }
+
+ if (min == 0)
+ return true;
+
+ if (min == max && max != argc) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "wrong number of arguments for \"%s\"",
+ argv[0]);
+ return false;
+ } else if (argc < min) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "too few arguments for \"%s\"", argv[0]);
+ return false;
+ } else if (argc > max && max /* != 0 */ ) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_ARG,
+ "too many arguments for \"%s\"", argv[0]);
+ return false;
+ } else
+ return true;
+}
+
+static const struct command *
+command_checked_lookup(Client *client, unsigned permission,
+ int argc, char *argv[])
+{
+ const struct command *cmd;
+
+ current_command = "";
+
+ if (argc == 0)
+ return NULL;
+
+ cmd = command_lookup(argv[0]);
+ if (cmd == NULL) {
+ if (client != NULL)
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "unknown command \"%s\"", argv[0]);
+ return NULL;
+ }
+
+ current_command = cmd->cmd;
+
+ if (!command_check_request(cmd, client, permission, argc, argv))
+ return NULL;
+
+ return cmd;
+}
+
+enum command_return
+command_process(Client *client, unsigned num, char *line)
+{
+ GError *error = NULL;
+ int argc;
+ char *argv[COMMAND_ARGV_MAX] = { NULL };
+ const struct command *cmd;
+ enum command_return ret = COMMAND_RETURN_ERROR;
+
+ command_list_num = num;
+
+ /* get the command name (first word on the line) */
+
+ argv[0] = tokenizer_next_word(&line, &error);
+ if (argv[0] == NULL) {
+ current_command = "";
+ if (*line == 0)
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "No command given");
+ else {
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "%s", error->message);
+ g_error_free(error);
+ }
+ current_command = NULL;
+
+ return COMMAND_RETURN_ERROR;
+ }
+
+ argc = 1;
+
+ /* now parse the arguments (quoted or unquoted) */
+
+ while (argc < (int)G_N_ELEMENTS(argv) &&
+ (argv[argc] =
+ tokenizer_next_param(&line, &error)) != NULL)
+ ++argc;
+
+ /* some error checks; we have to set current_command because
+ command_error() expects it to be set */
+
+ current_command = argv[0];
+
+ if (argc >= (int)G_N_ELEMENTS(argv)) {
+ command_error(client, ACK_ERROR_ARG, "Too many arguments");
+ current_command = NULL;
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (*line != 0) {
+ command_error(client, ACK_ERROR_ARG,
+ "%s", error->message);
+ current_command = NULL;
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* look up and invoke the command handler */
+
+ cmd = command_checked_lookup(client, client_get_permission(client),
+ argc, argv);
+ if (cmd)
+ ret = cmd->handler(client, argc, argv);
+
+ current_command = NULL;
+ command_list_num = 0;
+
+ return ret;
+}
diff --git a/src/db_internal.h b/src/AllCommands.hxx
index a3335152..a55eb5a3 100644
--- a/src/db_internal.h
+++ b/src/AllCommands.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_INTERNAL_H
-#define MPD_DB_INTERNAL_H
+#ifndef MPD_ALL_COMMANDS_HXX
+#define MPD_ALL_COMMANDS_HXX
-#include "db_plugin.h"
+#include "command.h"
-#include <assert.h>
+class Client;
-static inline void
-db_base_init(struct db *db, const struct db_plugin *plugin)
-{
- assert(plugin != NULL);
+void command_init(void);
- db->plugin = plugin;
-}
+void command_finish(void);
+
+enum command_return
+command_process(Client *client, unsigned num, char *line);
#endif
diff --git a/src/ArchiveFile.hxx b/src/ArchiveFile.hxx
new file mode 100644
index 00000000..c7933ebd
--- /dev/null
+++ b/src/ArchiveFile.hxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ARCHIVE_FILE_HXX
+#define MPD_ARCHIVE_FILE_HXX
+
+class ArchiveFile {
+public:
+ const struct archive_plugin &plugin;
+
+ ArchiveFile(const struct archive_plugin &_plugin)
+ :plugin(_plugin) {}
+
+protected:
+ /**
+ * Use Close() instead of delete.
+ */
+ ~ArchiveFile() {}
+
+public:
+ virtual void Close() = 0;
+
+ /**
+ * Visit all entries inside this archive.
+ */
+ virtual void Visit(ArchiveVisitor &visitor) = 0;
+
+ /**
+ * Opens an input_stream of a file within the archive.
+ *
+ * @param path the path within the archive
+ * @param error_r location to store the error occurring, or
+ * NULL to ignore errors
+ */
+ virtual input_stream *OpenStream(const char *path,
+ Mutex &mutex, Cond &cond,
+ GError **error_r) = 0;
+};
+
+#endif
diff --git a/src/archive_list.c b/src/ArchiveList.cxx
index e23567bd..02b19ce7 100644
--- a/src/archive_list.c
+++ b/src/ArchiveList.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,12 @@
*/
#include "config.h"
-#include "archive_list.h"
-#include "archive_plugin.h"
+#include "ArchiveList.hxx"
+#include "ArchivePlugin.hxx"
#include "string_util.h"
-#include "archive/bz2_archive_plugin.h"
-#include "archive/iso9660_archive_plugin.h"
-#include "archive/zzip_archive_plugin.h"
+#include "archive/Bzip2ArchivePlugin.hxx"
+#include "archive/Iso9660ArchivePlugin.hxx"
+#include "archive/ZzipArchivePlugin.hxx"
#include <string.h>
#include <glib.h>
diff --git a/src/archive_list.h b/src/ArchiveList.hxx
index f944583e..057c351d 100644
--- a/src/archive_list.h
+++ b/src/ArchiveList.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_LIST_H
-#define MPD_ARCHIVE_LIST_H
+#ifndef MPD_ARCHIVE_LIST_HXX
+#define MPD_ARCHIVE_LIST_HXX
struct archive_plugin;
diff --git a/src/archive_api.c b/src/ArchiveLookup.cxx
index be3c35f7..747f5c7e 100644
--- a/src/archive_api.c
+++ b/src/ArchiveLookup.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h" /* must be first for large file support */
-#include "archive_api.h"
+#include "ArchiveLookup.hxx"
#include <stdio.h>
diff --git a/src/archive_api.h b/src/ArchiveLookup.hxx
index 4e0f603f..6e7669cb 100644
--- a/src/archive_api.h
+++ b/src/ArchiveLookup.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_API_H
-#define MPD_ARCHIVE_API_H
+#ifndef MPD_ARCHIVE_LOOKUP_HXX
+#define MPD_ARCHIVE_LOOKUP_HXX
/*
* This is the public API which is used by archive plugins to
@@ -26,12 +26,6 @@
*
*/
-#include "archive_internal.h"
-#include "archive_plugin.h"
-#include "input_stream.h"
-
-#include <stdbool.h>
-
bool archive_lookup(char *pathname, char **archive, char **inpath, char **suffix);
#endif
diff --git a/src/ArchivePlugin.cxx b/src/ArchivePlugin.cxx
new file mode 100644
index 00000000..7c516422
--- /dev/null
+++ b/src/ArchivePlugin.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+
+#include <assert.h>
+
+ArchiveFile *
+archive_file_open(const struct archive_plugin *plugin, const char *path,
+ GError **error_r)
+{
+ assert(plugin != NULL);
+ assert(plugin->open != NULL);
+ assert(path != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ ArchiveFile *file = plugin->open(path, error_r);
+
+ if (file != NULL) {
+ assert(error_r == NULL || *error_r == NULL);
+ } else {
+ assert(error_r == NULL || *error_r != NULL);
+ }
+
+ return file;
+}
diff --git a/src/archive_plugin.h b/src/ArchivePlugin.hxx
index b7b92446..13952940 100644
--- a/src/archive_plugin.h
+++ b/src/ArchivePlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,15 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_PLUGIN_H
-#define MPD_ARCHIVE_PLUGIN_H
+#ifndef MPD_ARCHIVE_PLUGIN_HXX
+#define MPD_ARCHIVE_PLUGIN_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "gerror.h"
struct input_stream;
-struct archive_file;
+class ArchiveFile;
+class ArchiveVisitor;
struct archive_plugin {
const char *name;
@@ -48,38 +49,7 @@ struct archive_plugin {
* returns pointer to handle used is all operations with this archive
* or NULL when opening fails
*/
- struct archive_file *(*open)(const char *path_fs, GError **error_r);
-
- /**
- * reset routine will move current read index in archive to default
- * position and then the filenames from archives can be read
- * via scan_next routine
- */
- void (*scan_reset)(struct archive_file *);
-
- /**
- * the read method will return corresponding files from archive
- * (as pathnames) and move read index to next file. When there is no
- * next file it return NULL.
- */
- char *(*scan_next)(struct archive_file *);
-
- /**
- * Opens an input_stream of a file within the archive.
- *
- * @param path the path within the archive
- * @param error_r location to store the error occurring, or
- * NULL to ignore errors
- */
- struct input_stream *(*open_stream)(struct archive_file *af,
- const char *path,
- GMutex *mutex, GCond *cond,
- GError **error_r);
-
- /**
- * closes archive file.
- */
- void (*close)(struct archive_file *);
+ ArchiveFile *(*open)(const char *path_fs, GError **error_r);
/**
* suffixes handled by this plugin.
@@ -88,22 +58,8 @@ struct archive_plugin {
const char *const*suffixes;
};
-struct archive_file *
+ArchiveFile *
archive_file_open(const struct archive_plugin *plugin, const char *path,
GError **error_r);
-void
-archive_file_close(struct archive_file *file);
-
-void
-archive_file_scan_reset(struct archive_file *file);
-
-char *
-archive_file_scan_next(struct archive_file *file);
-
-struct input_stream *
-archive_file_open_stream(struct archive_file *file, const char *path,
- GMutex *mutex, GCond *cond,
- GError **error_r);
-
#endif
diff --git a/src/ArchiveVisitor.hxx b/src/ArchiveVisitor.hxx
new file mode 100644
index 00000000..e951cb5e
--- /dev/null
+++ b/src/ArchiveVisitor.hxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ARCHIVE_VISITOR_HXX
+#define MPD_ARCHIVE_VISITOR_HXX
+
+class ArchiveVisitor {
+public:
+ virtual void VisitArchiveEntry(const char *path_utf8) = 0;
+};
+
+#endif
diff --git a/src/AudioCompress/compress.h b/src/AudioCompress/compress.h
index 073d4af9..8556d16b 100644
--- a/src/AudioCompress/compress.h
+++ b/src/AudioCompress/compress.h
@@ -19,6 +19,10 @@ struct CompressorConfig {
struct Compressor;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
//! Create a new compressor (use history value of 0 for default)
struct Compressor *Compressor_new(unsigned int history);
@@ -34,7 +38,12 @@ struct CompressorConfig *Compressor_getConfig(struct Compressor *);
//! Process 16-bit signed data
void Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count);
+#ifdef __cplusplus
+}
+#endif
+
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed
//! TODO: functions for getting at the peak/gain/clip history buffers (for monitoring)
+
#endif
diff --git a/src/audio_config.c b/src/AudioConfig.cxx
index 72869c38..e546aed2 100644
--- a/src/audio_config.c
+++ b/src/AudioConfig.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,12 @@
*/
#include "config.h"
-#include "audio_config.h"
+#include "AudioConfig.hxx"
#include "audio_format.h"
-#include "audio_parser.h"
-#include "output_internal.h"
-#include "output_plugin.h"
-#include "output_all.h"
+#include "AudioParser.hxx"
#include "conf.h"
#include "mpd_error.h"
-#include <glib.h>
-
-#include <assert.h>
-#include <stdlib.h>
-
static struct audio_format configured_audio_format;
void getOutputAudioFormat(const struct audio_format *inAudioFormat,
@@ -53,7 +45,6 @@ void initAudioConfig(void)
ret = audio_format_parse(&configured_audio_format, param->value,
true, &error);
if (!ret)
- MPD_ERROR("error parsing \"%s\" at line %i: %s",
- CONF_AUDIO_OUTPUT_FORMAT, param->line,
- error->message);
+ MPD_ERROR("error parsing line %i: %s",
+ param->line, error->message);
}
diff --git a/src/audio_config.h b/src/AudioConfig.hxx
index 85143247..717a8e2e 100644
--- a/src/audio_config.h
+++ b/src/AudioConfig.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_AUDIO_CONFIG_H
-#define MPD_AUDIO_CONFIG_H
-
-#include <stdbool.h>
+#ifndef MPD_AUDIO_CONFIG_HXX
+#define MPD_AUDIO_CONFIG_HXX
struct audio_format;
diff --git a/src/audio_parser.c b/src/AudioParser.cxx
index 152eab5d..9178c3e1 100644
--- a/src/audio_parser.c
+++ b/src/AudioParser.cxx
@@ -23,7 +23,7 @@
*/
#include "config.h"
-#include "audio_parser.h"
+#include "AudioParser.hxx"
#include "audio_format.h"
#include "audio_check.h"
#include "gcc.h"
diff --git a/src/audio_parser.h b/src/AudioParser.hxx
index a963eb46..f7855e8e 100644
--- a/src/audio_parser.h
+++ b/src/AudioParser.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,12 +22,10 @@
* Parser functions for audio related objects.
*/
-#ifndef AUDIO_PARSER_H
-#define AUDIO_PARSER_H
+#ifndef MPD_AUDIO_PARSER_HXX
+#define MPD_AUDIO_PARSER_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "gerror.h"
struct audio_format;
diff --git a/src/client.c b/src/Client.cxx
index 3fa2c9be..be121dfe 100644
--- a/src/client.c
+++ b/src/Client.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,24 +18,19 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
-bool client_is_expired(const struct client *client)
-{
- return client->channel == NULL;
-}
-
-int client_get_uid(const struct client *client)
+int client_get_uid(const Client *client)
{
return client->uid;
}
-unsigned client_get_permission(const struct client *client)
+unsigned client_get_permission(const Client *client)
{
return client->permission;
}
-void client_set_permission(struct client *client, unsigned permission)
+void client_set_permission(Client *client, unsigned permission)
{
client->permission = permission;
}
diff --git a/src/client.h b/src/Client.hxx
index 0302a2e0..1456f1b7 100644
--- a/src/client.h
+++ b/src/Client.hxx
@@ -20,60 +20,60 @@
#ifndef MPD_CLIENT_H
#define MPD_CLIENT_H
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
+
#include <stddef.h>
#include <stdarg.h>
-struct client;
struct sockaddr;
-struct player_control;
+class EventLoop;
+struct Partition;
+class Client;
void client_manager_init(void);
-void client_manager_deinit(void);
-
-void client_new(struct player_control *player_control,
- int fd, const struct sockaddr *sa, size_t sa_length, int uid);
-G_GNUC_PURE
-bool client_is_expired(const struct client *client);
+void
+client_new(EventLoop &loop, Partition &partition,
+ int fd, const struct sockaddr *sa, size_t sa_length, int uid);
/**
* returns the uid of the client process, or a negative value if the
* uid is unknown
*/
-G_GNUC_PURE
-int client_get_uid(const struct client *client);
+gcc_pure
+int client_get_uid(const Client *client);
/**
* Is this client running on the same machine, connected with a local
* (UNIX domain) socket?
*/
-G_GNUC_PURE
+gcc_pure
static inline bool
-client_is_local(const struct client *client)
+client_is_local(const Client *client)
{
return client_get_uid(client) > 0;
}
-G_GNUC_PURE
-unsigned client_get_permission(const struct client *client);
+gcc_pure
+unsigned client_get_permission(const Client *client);
-void client_set_permission(struct client *client, unsigned permission);
+void client_set_permission(Client *client, unsigned permission);
/**
* Write a C string to the client.
*/
-void client_puts(struct client *client, const char *s);
+void client_puts(Client *client, const char *s);
/**
* Write a printf-like formatted string to the client.
*/
-void client_vprintf(struct client *client, const char *fmt, va_list args);
+void client_vprintf(Client *client, const char *fmt, va_list args);
/**
* Write a printf-like formatted string to the client.
*/
-G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...);
+gcc_fprintf
+void
+client_printf(Client *client, const char *fmt, ...);
#endif
diff --git a/src/song_print.h b/src/ClientEvent.cxx
index 8f1f0cc6..905cf0c0 100644
--- a/src/song_print.h
+++ b/src/ClientEvent.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,17 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_PRINT_H
-#define MPD_SONG_PRINT_H
-
-struct client;
-struct song;
-struct songvec;
+#include "config.h"
+#include "ClientInternal.hxx"
void
-song_print_info(struct client *client, struct song *song);
+Client::OnSocketError(GError *error)
+{
+ g_warning("error on client %d: %s", num, error->message);
+ g_error_free(error);
-void
-song_print_uri(struct client *client, struct song *song);
+ SetExpired();
+}
-#endif
+void
+Client::OnSocketClosed()
+{
+ SetExpired();
+}
diff --git a/src/db_lock.c b/src/ClientExpire.cxx
index 53759673..6bb0a43a 100644
--- a/src/db_lock.c
+++ b/src/ClientExpire.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,26 @@
*/
#include "config.h"
-#include "db_lock.h"
-#include "gcc.h"
+#include "ClientInternal.hxx"
-#if GCC_CHECK_VERSION(4, 2)
-/* workaround for a warning caused by G_STATIC_MUTEX_INIT */
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
+void
+Client::SetExpired()
+{
+ if (IsExpired())
+ return;
-GStaticMutex db_mutex = G_STATIC_MUTEX_INIT;
+ FullyBufferedSocket::Close();
+ TimeoutMonitor::Schedule(0);
+}
-#ifndef NDEBUG
-GThread *db_mutex_holder;
-#endif
+bool
+Client::OnTimeout()
+{
+ if (!IsExpired()) {
+ assert(!idle_waiting);
+ g_debug("[%u] timeout", num);
+ }
+
+ Close();
+ return false;
+}
diff --git a/src/client_file.c b/src/ClientFile.cxx
index 2ee43330..ca5acb22 100644
--- a/src/client_file.c
+++ b/src/ClientFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,9 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "client_file.h"
-#include "client.h"
+#include "ClientFile.hxx"
+#include "Client.hxx"
#include "ack.h"
+#include "io_error.h"
#include <sys/stat.h>
#include <sys/types.h>
@@ -27,7 +28,7 @@
#include <unistd.h>
bool
-client_allow_file(const struct client *client, const char *path_fs,
+client_allow_file(const Client *client, const char *path_fs,
GError **error_r)
{
#ifdef WIN32
@@ -53,8 +54,7 @@ client_allow_file(const struct client *client, const char *path_fs,
struct stat st;
if (stat(path_fs, &st) < 0) {
- g_set_error(error_r, g_file_error_quark(), errno,
- "%s", g_strerror(errno));
+ set_error_errno(error_r);
return false;
}
diff --git a/src/client_file.h b/src/ClientFile.hxx
index bc64bd04..48e00c44 100644
--- a/src/client_file.h
+++ b/src/ClientFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_FILE_H
-#define MPD_CLIENT_FILE_H
+#ifndef MPD_CLIENT_FILE_HXX
+#define MPD_CLIENT_FILE_HXX
+
+#include "gerror.h"
-#include <glib.h>
#include <stdbool.h>
-struct client;
+class Client;
/**
* Is this client allowed to use the specified local file?
@@ -36,7 +37,7 @@ struct client;
* @return true if access is allowed
*/
bool
-client_allow_file(const struct client *client, const char *path_fs,
+client_allow_file(const Client *client, const char *path_fs,
GError **error_r);
#endif
diff --git a/src/client_global.c b/src/ClientGlobal.cxx
index adf3b2f9..6115a785 100644
--- a/src/client_global.c
+++ b/src/ClientGlobal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,16 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
#include "conf.h"
#include <assert.h>
#define CLIENT_TIMEOUT_DEFAULT (60)
-#define CLIENT_MAX_CONNECTIONS_DEFAULT (10)
#define CLIENT_MAX_COMMAND_LIST_DEFAULT (2048*1024)
#define CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT (8192*1024)
-/* set this to zero to indicate we have no possible clients */
-unsigned int client_max_connections;
int client_timeout;
size_t client_max_command_list_size;
size_t client_max_output_buffer_size;
@@ -38,9 +36,6 @@ void client_manager_init(void)
{
client_timeout = config_get_positive(CONF_CONN_TIMEOUT,
CLIENT_TIMEOUT_DEFAULT);
- client_max_connections =
- config_get_positive(CONF_MAX_CONN,
- CLIENT_MAX_CONNECTIONS_DEFAULT);
client_max_command_list_size =
config_get_positive(CONF_MAX_COMMAND_LIST_SIZE,
CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024)
@@ -51,23 +46,3 @@ void client_manager_init(void)
CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024)
* 1024;
}
-
-static void client_close_all(void)
-{
- while (!client_list_is_empty()) {
- struct client *client = client_list_get_first();
-
- client_close(client);
- }
-
- assert(client_list_is_empty());
-}
-
-void client_manager_deinit(void)
-{
- client_close_all();
-
- client_max_connections = 0;
-
- client_deinit_expire();
-}
diff --git a/src/ClientIdle.cxx b/src/ClientIdle.cxx
new file mode 100644
index 00000000..71443812
--- /dev/null
+++ b/src/ClientIdle.cxx
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ClientInternal.hxx"
+#include "Idle.hxx"
+
+#include <assert.h>
+
+void
+Client::IdleNotify()
+{
+ assert(idle_waiting);
+ assert(idle_flags != 0);
+
+ unsigned flags = idle_flags;
+ idle_flags = 0;
+ idle_waiting = false;
+
+ const char *const*idle_names = idle_get_names();
+ for (unsigned i = 0; idle_names[i]; ++i) {
+ if (flags & (1 << i) & idle_subscriptions)
+ client_printf(this, "changed: %s\n",
+ idle_names[i]);
+ }
+
+ client_puts(this, "OK\n");
+
+ TimeoutMonitor::ScheduleSeconds(client_timeout);
+}
+
+void
+Client::IdleAdd(unsigned flags)
+{
+ if (IsExpired())
+ return;
+
+ idle_flags |= flags;
+ if (idle_waiting && (idle_flags & idle_subscriptions))
+ IdleNotify();
+}
+
+bool
+Client::IdleWait(unsigned flags)
+{
+ assert(!idle_waiting);
+
+ idle_waiting = true;
+ idle_subscriptions = flags;
+
+ if (idle_flags & idle_subscriptions) {
+ IdleNotify();
+ return true;
+ } else {
+ /* disable timeouts while in "idle" */
+ TimeoutMonitor::Cancel();
+ return false;
+ }
+}
diff --git a/src/ClientInternal.hxx b/src/ClientInternal.hxx
new file mode 100644
index 00000000..c3111868
--- /dev/null
+++ b/src/ClientInternal.hxx
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CLIENT_INTERNAL_HXX
+#define MPD_CLIENT_INTERNAL_HXX
+
+#include "check.h"
+#include "Client.hxx"
+#include "ClientMessage.hxx"
+#include "CommandListBuilder.hxx"
+#include "event/FullyBufferedSocket.hxx"
+#include "event/TimeoutMonitor.hxx"
+#include "command.h"
+
+#include <set>
+#include <string>
+#include <list>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "client"
+
+enum {
+ CLIENT_MAX_SUBSCRIPTIONS = 16,
+ CLIENT_MAX_MESSAGES = 64,
+};
+
+struct Partition;
+
+class Client final : private FullyBufferedSocket, TimeoutMonitor {
+public:
+ Partition &partition;
+ struct playlist &playlist;
+ struct player_control *player_control;
+
+ unsigned permission;
+
+ /** the uid of the client process, or -1 if unknown */
+ int uid;
+
+ CommandListBuilder cmd_list;
+
+ unsigned int num; /* client number */
+
+ /** is this client waiting for an "idle" response? */
+ bool idle_waiting;
+
+ /** idle flags pending on this client, to be sent as soon as
+ the client enters "idle" */
+ unsigned idle_flags;
+
+ /** idle flags that the client wants to receive */
+ unsigned idle_subscriptions;
+
+ /**
+ * A list of channel names this client is subscribed to.
+ */
+ std::set<std::string> subscriptions;
+
+ /**
+ * The number of subscriptions in #subscriptions. Used to
+ * limit the number of subscriptions.
+ */
+ unsigned num_subscriptions;
+
+ /**
+ * A list of messages this client has received.
+ */
+ std::list<ClientMessage> messages;
+
+ Client(EventLoop &loop, Partition &partition,
+ int fd, int uid, int num);
+
+ bool IsConnected() const {
+ return FullyBufferedSocket::IsDefined();
+ }
+
+ gcc_pure
+ bool IsSubscribed(const char *channel_name) const {
+ return subscriptions.find(channel_name) != subscriptions.end();
+ }
+
+ gcc_pure
+ bool IsExpired() const {
+ return !FullyBufferedSocket::IsDefined();
+ }
+
+ void Close();
+ void SetExpired();
+
+ using FullyBufferedSocket::Write;
+
+ /**
+ * Send "idle" response to this client.
+ */
+ void IdleNotify();
+ void IdleAdd(unsigned flags);
+ bool IdleWait(unsigned flags);
+
+private:
+ /* virtual methods from class BufferedSocket */
+ virtual InputResult OnSocketInput(const void *data,
+ size_t length) override;
+ virtual void OnSocketError(GError *error) override;
+ virtual void OnSocketClosed() override;
+
+ /* virtual methods from class TimeoutMonitor */
+ virtual bool OnTimeout() override;
+};
+
+extern int client_timeout;
+extern size_t client_max_command_list_size;
+extern size_t client_max_output_buffer_size;
+
+enum command_return
+client_process_line(Client *client, char *line);
+
+#endif
diff --git a/src/ClientList.cxx b/src/ClientList.cxx
new file mode 100644
index 00000000..37e6f128
--- /dev/null
+++ b/src/ClientList.cxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ClientList.hxx"
+#include "ClientInternal.hxx"
+
+#include <algorithm>
+
+#include <assert.h>
+
+void
+ClientList::Remove(Client &client)
+{
+ assert(size > 0);
+ assert(!list.empty());
+
+ auto i = std::find(list.begin(), list.end(), &client);
+ assert(i != list.end());
+ list.erase(i);
+ --size;
+}
+
+void
+ClientList::CloseAll()
+{
+ while (!list.empty())
+ list.front()->Close();
+
+ assert(size == 0);
+}
+
+void
+ClientList::IdleAdd(unsigned flags)
+{
+ assert(flags != 0);
+
+ for (const auto &client : list)
+ client->IdleAdd(flags);
+}
diff --git a/src/ClientList.hxx b/src/ClientList.hxx
new file mode 100644
index 00000000..e8560af7
--- /dev/null
+++ b/src/ClientList.hxx
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CLIENT_LIST_HXX
+#define MPD_CLIENT_LIST_HXX
+
+#include <list>
+
+class Client;
+
+class ClientList {
+ const unsigned max_size;
+
+ unsigned size;
+ std::list<Client *> list;
+
+public:
+ ClientList(unsigned _max_size)
+ :max_size(_max_size), size(0) {}
+
+ std::list<Client *>::iterator begin() {
+ return list.begin();
+ }
+
+ std::list<Client *>::iterator end() {
+ return list.end();
+ }
+
+ bool IsFull() const {
+ return size >= max_size;
+ }
+
+ void Add(Client &client) {
+ list.push_front(&client);
+ ++size;
+ }
+
+ void Remove(Client &client);
+
+ void CloseAll();
+
+ void IdleAdd(unsigned flags);
+};
+
+#endif
diff --git a/src/ClientMessage.cxx b/src/ClientMessage.cxx
new file mode 100644
index 00000000..619964b3
--- /dev/null
+++ b/src/ClientMessage.cxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ClientMessage.hxx"
+
+#include <glib.h>
+
+G_GNUC_PURE
+static bool
+valid_channel_char(const char ch)
+{
+ return g_ascii_isalnum(ch) ||
+ ch == '_' || ch == '-' || ch == '.' || ch == ':';
+}
+
+bool
+client_message_valid_channel_name(const char *name)
+{
+ do {
+ if (!valid_channel_char(*name))
+ return false;
+ } while (*++name != 0);
+
+ return true;
+}
diff --git a/src/exclude.h b/src/ClientMessage.hxx
index 5b1229e2..2a929d44 100644
--- a/src/exclude.h
+++ b/src/ClientMessage.hxx
@@ -17,35 +17,36 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/*
- * The .mpdignore backend code.
- *
- */
+#ifndef MPD_CLIENT_MESSAGE_H
+#define MPD_CLIENT_MESSAGE_H
-#ifndef MPD_EXCLUDE_H
-#define MPD_EXCLUDE_H
+#include "gcc.h"
-#include <glib.h>
-
-#include <stdbool.h>
+#include <string>
/**
- * Loads and parses a .mpdignore file.
+ * A client-to-client message.
*/
-GSList *
-exclude_list_load(const char *path_fs);
+class ClientMessage {
+ std::string channel, message;
-/**
- * Frees a list returned by exclude_list_load().
- */
-void
-exclude_list_free(GSList *list);
+public:
+ template<typename T, typename U>
+ ClientMessage(T &&_channel, U &&_message)
+ :channel(std::forward<T>(_channel)),
+ message(std::forward<U>(_message)) {}
-/**
- * Checks whether one of the patterns in the .mpdignore file matches
- * the specified file name.
- */
+ const char *GetChannel() const {
+ return channel.c_str();
+ }
+
+ const char *GetMessage() const {
+ return message.c_str();
+ }
+};
+
+gcc_pure
bool
-exclude_list_check(GSList *list, const char *name_fs);
+client_message_valid_channel_name(const char *name);
#endif
diff --git a/src/client_new.c b/src/ClientNew.cxx
index cf28c43c..a416c1f8 100644
--- a/src/client_new.c
+++ b/src/ClientNew.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,15 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
+#include "Partition.hxx"
+#include "Main.hxx"
#include "fd_util.h"
-#include "fifo_buffer.h"
+extern "C" {
#include "resolver.h"
-#include "permission.h"
-#include "glib_socket.h"
+}
+#include "Permission.hxx"
#include <assert.h>
#include <sys/types.h>
@@ -43,15 +46,28 @@
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
+Client::Client(EventLoop &_loop, Partition &_partition,
+ int _fd, int _uid, int _num)
+ :FullyBufferedSocket(_fd, _loop, 16384, client_max_output_buffer_size),
+ TimeoutMonitor(_loop),
+ partition(_partition),
+ playlist(partition.playlist), player_control(&partition.pc),
+ permission(getDefaultPermissions()),
+ uid(_uid),
+ num(_num),
+ idle_waiting(false), idle_flags(0),
+ num_subscriptions(0)
+{
+ TimeoutMonitor::ScheduleSeconds(client_timeout);
+}
+
void
-client_new(struct player_control *player_control,
+client_new(EventLoop &loop, Partition &partition,
int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
static unsigned int next_client_num;
- struct client *client;
char *remote;
- assert(player_control != NULL);
assert(fd >= 0);
#ifdef HAVE_LIBWRAP
@@ -79,53 +95,18 @@ client_new(struct player_control *player_control,
}
#endif /* HAVE_WRAP */
- if (client_list_is_full()) {
+ if (client_list->IsFull()) {
g_warning("Max Connections Reached!");
close_socket(fd);
return;
}
- client = g_new0(struct client, 1);
- client->player_control = player_control;
-
- client->channel = g_io_channel_new_socket(fd);
- /* GLib is responsible for closing the file descriptor */
- g_io_channel_set_close_on_unref(client->channel, true);
- /* NULL encoding means the stream is binary safe; the MPD
- protocol is UTF-8 only, but we are doing this call anyway
- to prevent GLib from messing around with the stream */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
-
- client->permission = getDefaultPermissions();
- client->uid = uid;
-
- client->last_activity = g_timer_new();
-
- client->cmd_list = NULL;
- client->cmd_list_OK = -1;
- client->cmd_list_size = 0;
-
- client->deferred_send = g_queue_new();
- client->deferred_bytes = 0;
- client->num = next_client_num++;
-
- client->send_buf_used = 0;
-
- client->subscriptions = NULL;
- client->messages = NULL;
- client->num_messages = 0;
+ Client *client = new Client(loop, partition, fd, uid,
+ next_client_num++);
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
- client_list_add(client);
+ client_list->Add(*client);
remote = sockaddr_to_string(sa, sa_length, NULL);
g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
@@ -133,33 +114,13 @@ client_new(struct player_control *player_control,
g_free(remote);
}
-static void
-deferred_buffer_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct deferred_buffer *buffer = data;
- g_free(buffer);
-}
-
void
-client_close(struct client *client)
+Client::Close()
{
- client_list_remove(client);
-
- client_set_expired(client);
+ client_list->Remove(*this);
- g_timer_destroy(client->last_activity);
+ SetExpired();
- if (client->cmd_list) {
- free_cmd_list(client->cmd_list);
- client->cmd_list = NULL;
- }
-
- g_queue_foreach(client->deferred_send, deferred_buffer_free, NULL);
- g_queue_free(client->deferred_send);
-
- fifo_buffer_free(client->input);
-
- g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE,
- "[%u] closed", client->num);
- g_free(client);
+ g_log(G_LOG_DOMAIN, LOG_LEVEL_SECURE, "[%u] closed", num);
+ delete this;
}
diff --git a/src/client_process.c b/src/ClientProcess.cxx
index 57a8a782..bcd20d1b 100644
--- a/src/client_process.c
+++ b/src/ClientProcess.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,9 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "ClientInternal.hxx"
+#include "protocol/Result.hxx"
+#include "AllCommands.hxx"
#include <string.h>
@@ -27,19 +29,20 @@
#define CLIENT_LIST_MODE_END "command_list_end"
static enum command_return
-client_process_command_list(struct client *client, bool list_ok, GSList *list)
+client_process_command_list(Client *client, bool list_ok,
+ std::list<std::string> &&list)
{
enum command_return ret = COMMAND_RETURN_OK;
unsigned num = 0;
- for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
- char *cmd = cur->data;
+ for (auto &&i : list) {
+ char *cmd = &*i.begin();
g_debug("command_process_list: process command \"%s\"",
cmd);
ret = command_process(client, num++, cmd);
g_debug("command_process_list: command returned %i", ret);
- if (ret != COMMAND_RETURN_OK || client_is_expired(client))
+ if (ret != COMMAND_RETURN_OK || client->IsExpired())
break;
else if (list_ok)
client_puts(client, "list_OK\n");
@@ -49,7 +52,7 @@ client_process_command_list(struct client *client, bool list_ok, GSList *list)
}
enum command_return
-client_process_line(struct client *client, char *line)
+client_process_line(Client *client, char *line)
{
enum command_return ret;
@@ -58,7 +61,6 @@ client_process_line(struct client *client, char *line)
/* send empty idle response and leave idle mode */
client->idle_waiting = false;
command_success(client);
- client_write_output(client);
}
/* do nothing if the client wasn't idling: the client
@@ -74,55 +76,44 @@ client_process_line(struct client *client, char *line)
return COMMAND_RETURN_CLOSE;
}
- if (client->cmd_list_OK >= 0) {
+ if (client->cmd_list.IsActive()) {
if (strcmp(line, CLIENT_LIST_MODE_END) == 0) {
g_debug("[%u] process command list",
client->num);
- /* for scalability reasons, we have prepended
- each new command; now we have to reverse it
- to restore the correct order */
- client->cmd_list = g_slist_reverse(client->cmd_list);
+ auto &&cmd_list = client->cmd_list.Commit();
ret = client_process_command_list(client,
- client->cmd_list_OK,
- client->cmd_list);
+ client->cmd_list.IsOKMode(),
+ std::move(cmd_list));
g_debug("[%u] process command "
"list returned %i", client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
- client_is_expired(client))
+ client->IsExpired())
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
- client_write_output(client);
- free_cmd_list(client->cmd_list);
- client->cmd_list = NULL;
- client->cmd_list_OK = -1;
+ client->cmd_list.Reset();
} else {
- size_t len = strlen(line) + 1;
- client->cmd_list_size += len;
- if (client->cmd_list_size >
- client_max_command_list_size) {
- g_warning("[%u] command list size (%lu) "
+ if (!client->cmd_list.Add(line)) {
+ g_warning("[%u] command list size "
"is larger than the max (%lu)",
client->num,
- (unsigned long)client->cmd_list_size,
(unsigned long)client_max_command_list_size);
return COMMAND_RETURN_CLOSE;
}
- new_cmd_list_ptr(client, line);
ret = COMMAND_RETURN_OK;
}
} else {
if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) {
- client->cmd_list_OK = 0;
+ client->cmd_list.Begin(false);
ret = COMMAND_RETURN_OK;
} else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) {
- client->cmd_list_OK = 1;
+ client->cmd_list.Begin(true);
ret = COMMAND_RETURN_OK;
} else {
g_debug("[%u] process command \"%s\"",
@@ -132,13 +123,11 @@ client_process_line(struct client *client, char *line)
client->num, ret);
if (ret == COMMAND_RETURN_CLOSE ||
- client_is_expired(client))
+ client->IsExpired())
return COMMAND_RETURN_CLOSE;
if (ret == COMMAND_RETURN_OK)
command_success(client);
-
- client_write_output(client);
}
}
diff --git a/src/ClientRead.cxx b/src/ClientRead.cxx
new file mode 100644
index 00000000..49c698bc
--- /dev/null
+++ b/src/ClientRead.cxx
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ClientInternal.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+BufferedSocket::InputResult
+Client::OnSocketInput(const void *data, size_t length)
+{
+ const char *p = (const char *)data;
+ const char *newline = (const char *)memchr(p, '\n', length);
+ if (newline == NULL)
+ return InputResult::MORE;
+
+ TimeoutMonitor::ScheduleSeconds(client_timeout);
+
+ char *line = g_strndup(p, newline - p);
+ BufferedSocket::ConsumeInput(newline + 1 - p);
+
+ enum command_return result = client_process_line(this, line);
+ g_free(line);
+
+ switch (result) {
+ case COMMAND_RETURN_OK:
+ case COMMAND_RETURN_IDLE:
+ case COMMAND_RETURN_ERROR:
+ break;
+
+ case COMMAND_RETURN_KILL:
+ Close();
+ main_loop->Break();
+ return InputResult::CLOSED;
+
+ case COMMAND_RETURN_CLOSE:
+ Close();
+ return InputResult::CLOSED;
+ }
+
+ if (IsExpired()) {
+ Close();
+ return InputResult::CLOSED;
+ }
+
+ return InputResult::AGAIN;
+}
diff --git a/src/ClientSubscribe.cxx b/src/ClientSubscribe.cxx
new file mode 100644
index 00000000..918a621d
--- /dev/null
+++ b/src/ClientSubscribe.cxx
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ClientSubscribe.hxx"
+#include "ClientInternal.hxx"
+#include "Idle.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+enum client_subscribe_result
+client_subscribe(Client *client, const char *channel)
+{
+ assert(client != NULL);
+ assert(channel != NULL);
+
+ if (!client_message_valid_channel_name(channel))
+ return CLIENT_SUBSCRIBE_INVALID;
+
+ if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
+ return CLIENT_SUBSCRIBE_FULL;
+
+ auto r = client->subscriptions.insert(channel);
+ if (!r.second)
+ return CLIENT_SUBSCRIBE_ALREADY;
+
+ ++client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ return CLIENT_SUBSCRIBE_OK;
+}
+
+bool
+client_unsubscribe(Client *client, const char *channel)
+{
+ const auto i = client->subscriptions.find(channel);
+ if (i == client->subscriptions.end())
+ return false;
+
+ assert(client->num_subscriptions > 0);
+
+ client->subscriptions.erase(i);
+ --client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ assert((client->num_subscriptions == 0) ==
+ client->subscriptions.empty());
+
+ return true;
+}
+
+void
+client_unsubscribe_all(Client *client)
+{
+ client->subscriptions.clear();
+ client->num_subscriptions = 0;
+}
+
+bool
+client_push_message(Client *client, const ClientMessage &msg)
+{
+ assert(client != NULL);
+
+ if (client->messages.size() >= CLIENT_MAX_MESSAGES ||
+ !client->IsSubscribed(msg.GetChannel()))
+ return false;
+
+ if (client->messages.empty())
+ client->IdleAdd(IDLE_MESSAGE);
+
+ client->messages.push_back(msg);
+ return true;
+}
diff --git a/src/client_subscribe.h b/src/ClientSubscribe.hxx
index 09f86441..83c234db 100644
--- a/src/client_subscribe.h
+++ b/src/ClientSubscribe.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CLIENT_SUBSCRIBE_H
-#define MPD_CLIENT_SUBSCRIBE_H
+#ifndef MPD_CLIENT_SUBSCRIBE_HXX
+#define MPD_CLIENT_SUBSCRIBE_HXX
-#include <stdbool.h>
-#include <glib.h>
+#include "gcc.h"
-struct client;
-struct client_message;
+class Client;
+class ClientMessage;
enum client_subscribe_result {
/** success */
@@ -41,19 +40,15 @@ enum client_subscribe_result {
};
enum client_subscribe_result
-client_subscribe(struct client *client, const char *channel);
+client_subscribe(Client *client, const char *channel);
bool
-client_unsubscribe(struct client *client, const char *channel);
+client_unsubscribe(Client *client, const char *channel);
void
-client_unsubscribe_all(struct client *client);
+client_unsubscribe_all(Client *client);
bool
-client_push_message(struct client *client, const struct client_message *msg);
-
-G_GNUC_MALLOC
-GSList *
-client_read_messages(struct client *client);
+client_push_message(Client *client, const ClientMessage &msg);
#endif
diff --git a/src/ClientWrite.cxx b/src/ClientWrite.cxx
new file mode 100644
index 00000000..23b515a3
--- /dev/null
+++ b/src/ClientWrite.cxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ClientInternal.hxx"
+
+#include <string.h>
+#include <stdio.h>
+
+/**
+ * Write a block of data to the client.
+ */
+static void
+client_write(Client *client, const char *data, size_t length)
+{
+ /* if the client is going to be closed, do nothing */
+ if (client->IsExpired() || length == 0)
+ return;
+
+ client->Write(data, length);
+}
+
+void
+client_puts(Client *client, const char *s)
+{
+ client_write(client, s, strlen(s));
+}
+
+void
+client_vprintf(Client *client, const char *fmt, va_list args)
+{
+#ifndef G_OS_WIN32
+ va_list tmp;
+ int length;
+
+ va_copy(tmp, args);
+ length = vsnprintf(NULL, 0, fmt, tmp);
+ va_end(tmp);
+
+ if (length <= 0)
+ /* wtf.. */
+ return;
+
+ char *buffer = (char *)g_malloc(length + 1);
+ vsnprintf(buffer, length + 1, fmt, args);
+ client_write(client, buffer, length);
+ g_free(buffer);
+#else
+ /* On mingw32, snprintf() expects a 64 bit integer instead of
+ a "long int" for "%li". This is not consistent with our
+ expectation, so we're using plain sprintf() here, hoping
+ the static buffer is large enough. Sorry for this hack,
+ but WIN32 development is so painful, I'm not in the mood to
+ do it properly now. */
+
+ static char buffer[4096];
+ vsprintf(buffer, fmt, args);
+ client_write(client, buffer, strlen(buffer));
+#endif
+}
+
+G_GNUC_PRINTF(2, 3)
+void
+client_printf(Client *client, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ client_vprintf(client, fmt, args);
+ va_end(args);
+}
diff --git a/src/CommandError.cxx b/src/CommandError.cxx
new file mode 100644
index 00000000..7e777d82
--- /dev/null
+++ b/src/CommandError.cxx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "CommandError.hxx"
+#include "db_error.h"
+#include "io_error.h"
+#include "protocol/Result.hxx"
+
+#include <assert.h>
+#include <errno.h>
+
+enum command_return
+print_playlist_result(Client *client, enum playlist_result result)
+{
+ switch (result) {
+ case PLAYLIST_RESULT_SUCCESS:
+ return COMMAND_RETURN_OK;
+
+ case PLAYLIST_RESULT_ERRNO:
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(errno));
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_DENIED:
+ command_error(client, ACK_ERROR_PERMISSION, "Access denied");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NO_SUCH_SONG:
+ command_error(client, ACK_ERROR_NO_EXIST, "No such song");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NO_SUCH_LIST:
+ command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_LIST_EXISTS:
+ command_error(client, ACK_ERROR_EXIST,
+ "Playlist already exists");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_BAD_NAME:
+ command_error(client, ACK_ERROR_ARG,
+ "playlist name is invalid: "
+ "playlist names may not contain slashes,"
+ " newlines or carriage returns");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_BAD_RANGE:
+ command_error(client, ACK_ERROR_ARG, "Bad song index");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_NOT_PLAYING:
+ command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_TOO_LARGE:
+ command_error(client, ACK_ERROR_PLAYLIST_MAX,
+ "playlist is at the max size");
+ return COMMAND_RETURN_ERROR;
+
+ case PLAYLIST_RESULT_DISABLED:
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "stored playlist support is disabled");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ assert(0);
+ return COMMAND_RETURN_ERROR;
+}
+
+/**
+ * Send the GError to the client and free the GError.
+ */
+enum command_return
+print_error(Client *client, GError *error)
+{
+ assert(client != NULL);
+ assert(error != NULL);
+
+ g_warning("%s", error->message);
+
+ if (error->domain == playlist_quark()) {
+ enum playlist_result result = (playlist_result)error->code;
+ g_error_free(error);
+ return print_playlist_result(client, result);
+ } else if (error->domain == ack_quark()) {
+ command_error(client, (ack)error->code, "%s", error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ } else if (error->domain == db_quark()) {
+ switch ((enum db_error)error->code) {
+ case DB_DISABLED:
+ command_error(client, ACK_ERROR_NO_EXIST, "%s",
+ error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+
+ case DB_NOT_FOUND:
+ g_error_free(error);
+ command_error(client, ACK_ERROR_NO_EXIST, "Not found");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else if (error->domain == errno_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(error->code));
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ } else if (error->domain == g_file_error_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s", error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ g_error_free(error);
+ command_error(client, ACK_ERROR_UNKNOWN, "error");
+ return COMMAND_RETURN_ERROR;
+}
diff --git a/src/CommandError.hxx b/src/CommandError.hxx
new file mode 100644
index 00000000..5fb02122
--- /dev/null
+++ b/src/CommandError.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_COMMAND_ERROR_HXX
+#define MPD_COMMAND_ERROR_HXX
+
+#include "command.h"
+#include "playlist_error.h"
+#include "gerror.h"
+
+class Client;
+
+enum command_return
+print_playlist_result(Client *client, enum playlist_result result);
+
+/**
+ * Send the GError to the client and free the GError.
+ */
+enum command_return
+print_error(Client *client, GError *error);
+
+#endif
diff --git a/src/cmdline.c b/src/CommandLine.cxx
index cb7eff36..0de211fd 100644
--- a/src/cmdline.c
+++ b/src/CommandLine.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,21 @@
*/
#include "config.h"
-#include "cmdline.h"
-#include "path.h"
-#include "log.h"
+#include "CommandLine.hxx"
+#include "ls.hxx"
+#include "Log.hxx"
#include "conf.h"
-#include "decoder_list.h"
+#include "DecoderList.hxx"
#include "decoder_plugin.h"
-#include "output_list.h"
+#include "OutputList.hxx"
#include "output_plugin.h"
-#include "input_registry.h"
-#include "input_plugin.h"
-#include "playlist_list.h"
-#include "playlist_plugin.h"
-#include "ls.h"
+#include "InputRegistry.hxx"
+#include "InputPlugin.hxx"
+#include "PlaylistRegistry.hxx"
+#include "PlaylistPlugin.hxx"
#include "mpd_error.h"
-#include "glib_compat.h"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
#ifdef ENABLE_ENCODER
#include "encoder_list.h"
@@ -40,8 +40,8 @@
#endif
#ifdef ENABLE_ARCHIVE
-#include "archive_list.h"
-#include "archive_plugin.h"
+#include "ArchiveList.hxx"
+#include "ArchivePlugin.hxx"
#endif
#include <glib.h>
@@ -134,6 +134,16 @@ static void version(void)
static const char *summary =
"Music Player Daemon - a daemon for playing music.";
+gcc_pure
+static Path
+PathBuildChecked(const Path &a, Path::const_pointer b)
+{
+ if (a.IsNull())
+ return Path::Null();
+
+ return Path::Build(a, b);
+}
+
bool
parse_cmdline(int argc, char **argv, struct options *options,
GError **error_r)
@@ -159,7 +169,7 @@ parse_cmdline(int argc, char **argv, struct options *options,
"verbose logging", NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &option_version,
"print version number", NULL },
- { .long_name = NULL }
+ { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
};
options->kill = false;
@@ -192,59 +202,44 @@ parse_cmdline(int argc, char **argv, struct options *options,
return true;
} else if (argc <= 1) {
/* default configuration file path */
- char *path1;
#ifdef G_OS_WIN32
- path1 = g_build_filename(g_get_user_config_dir(),
- CONFIG_FILE_LOCATION, NULL);
- if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
- ret = config_read_file(path1, error_r);
- else {
- int i = 0;
- char *system_path = NULL;
- const char * const *system_config_dirs;
-
- system_config_dirs = g_get_system_config_dirs();
-
- while(system_config_dirs[i] != NULL) {
- system_path = g_build_filename(system_config_dirs[i],
- CONFIG_FILE_LOCATION,
- NULL);
- if(g_file_test(system_path,
- G_FILE_TEST_IS_REGULAR)) {
- ret = config_read_file(system_path,error_r);
- g_free(system_path);
- break;
- } else
- g_free(system_path);
- ++i;
- }
+ Path path = PathBuildChecked(Path::FromUTF8(g_get_user_config_dir()),
+ CONFIG_FILE_LOCATION);
+ if (!path.IsNull() && FileExists(path))
+ return ReadConfigFile(path, error_r);
+
+ const char *const*system_config_dirs =
+ g_get_system_config_dirs();
+
+ for (unsigned i = 0; system_config_dirs[i] != nullptr; ++i) {
+ path = PathBuildChecked(Path::FromUTF8(system_config_dirs[i]),
+ CONFIG_FILE_LOCATION);
+ if (!path.IsNull() && FileExists(path))
+ return ReadConfigFile(path, error_r);
}
#else /* G_OS_WIN32 */
- char *path2;
- path1 = g_build_filename(g_get_home_dir(),
- USER_CONFIG_FILE_LOCATION1, NULL);
- path2 = g_build_filename(g_get_home_dir(),
- USER_CONFIG_FILE_LOCATION2, NULL);
- if (g_file_test(path1, G_FILE_TEST_IS_REGULAR))
- ret = config_read_file(path1, error_r);
- else if (g_file_test(path2, G_FILE_TEST_IS_REGULAR))
- ret = config_read_file(path2, error_r);
- else if (g_file_test(SYSTEM_CONFIG_FILE_LOCATION,
- G_FILE_TEST_IS_REGULAR))
- ret = config_read_file(SYSTEM_CONFIG_FILE_LOCATION,
- error_r);
+ Path path = PathBuildChecked(Path::FromUTF8(g_get_home_dir()),
+ USER_CONFIG_FILE_LOCATION1);
+ if (!path.IsNull() && FileExists(path))
+ return ReadConfigFile(path, error_r);
+
+ path = PathBuildChecked(Path::FromUTF8(g_get_home_dir()),
+ USER_CONFIG_FILE_LOCATION2);
+ if (!path.IsNull() && FileExists(path))
+ return ReadConfigFile(path, error_r);
+
+ path = Path::FromUTF8(SYSTEM_CONFIG_FILE_LOCATION);
+ if (!path.IsNull() && FileExists(path))
+ return ReadConfigFile(path, error_r);
#endif
- g_free(path1);
-#ifndef G_OS_WIN32
- g_free(path2);
-#endif
-
- return ret;
+ g_set_error(error_r, cmdline_quark(), 0,
+ "No configuration file found");
+ return false;
} else if (argc == 2) {
/* specified configuration file */
- return config_read_file(argv[1], error_r);
+ return ReadConfigFile(Path::FromFS(argv[1]), error_r);
} else {
g_set_error(error_r, cmdline_quark(), 0,
"too many arguments");
diff --git a/src/cmdline.h b/src/CommandLine.hxx
index 68f625a6..7a8731f8 100644
--- a/src/cmdline.h
+++ b/src/CommandLine.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CMDLINE_H
-#define CMDLINE_H
+#ifndef MPD_COMMAND_LINE_HXX
+#define MPD_COMMAND_LINE_HXX
#include <glib.h>
-#include <stdbool.h>
-
struct options {
gboolean kill;
gboolean daemon;
diff --git a/src/CommandListBuilder.cxx b/src/CommandListBuilder.cxx
new file mode 100644
index 00000000..cc10f720
--- /dev/null
+++ b/src/CommandListBuilder.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "CommandListBuilder.hxx"
+#include "ClientInternal.hxx"
+
+#include <string.h>
+
+void
+CommandListBuilder::Reset()
+{
+ list.clear();
+ mode = Mode::DISABLED;
+}
+
+bool
+CommandListBuilder::Add(const char *cmd)
+{
+ size_t len = strlen(cmd) + 1;
+ size += len;
+ if (size > client_max_command_list_size)
+ return false;
+
+ list.emplace_back(cmd);
+ return true;
+}
diff --git a/src/CommandListBuilder.hxx b/src/CommandListBuilder.hxx
new file mode 100644
index 00000000..a112ac33
--- /dev/null
+++ b/src/CommandListBuilder.hxx
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_COMMAND_LIST_BUILDER_HXX
+#define MPD_COMMAND_LIST_BUILDER_HXX
+
+#include <list>
+#include <string>
+
+#include <assert.h>
+
+class CommandListBuilder {
+ /**
+ * print OK after each command execution
+ */
+ enum class Mode {
+ /**
+ * Not active.
+ */
+ DISABLED = -1,
+
+ /**
+ * Enabled in normal list mode.
+ */
+ ENABLED = false,
+
+ /**
+ * Enabled in "list_OK" mode.
+ */
+ OK = true,
+ } mode;
+
+ /**
+ * for when in list mode
+ */
+ std::list<std::string> list;
+
+ /**
+ * Memory consumed by the list.
+ */
+ size_t size;
+
+public:
+ CommandListBuilder()
+ :mode(Mode::DISABLED), size(0) {}
+
+ /**
+ * Is a command list currently being built?
+ */
+ bool IsActive() const {
+ return mode != Mode::DISABLED;
+ }
+
+ /**
+ * Is the object in "list_OK" mode?
+ */
+ bool IsOKMode() const {
+ assert(IsActive());
+
+ return (bool)mode;
+ }
+
+ /**
+ * Reset the object: delete the list and clear the mode.
+ */
+ void Reset();
+
+ /**
+ * Begin building a command list.
+ */
+ void Begin(bool ok) {
+ assert(list.empty());
+ assert(mode == Mode::DISABLED);
+
+ mode = (Mode)ok;
+ }
+
+ /**
+ * @return false if the list is full
+ */
+ bool Add(const char *cmd);
+
+ /**
+ * Finishes the list and returns it.
+ */
+ std::list<std::string> &&Commit() {
+ assert(IsActive());
+
+ return std::move(list);
+ }
+};
+
+#endif
diff --git a/src/ConfigData.cxx b/src/ConfigData.cxx
new file mode 100644
index 00000000..48e9612d
--- /dev/null
+++ b/src/ConfigData.cxx
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigData.hxx"
+#include "ConfigParser.hxx"
+#include "mpd_error.h"
+
+extern "C" {
+#include "utils.h"
+}
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+config_param::config_param(const char *_value, int _line)
+ :next(nullptr), value(g_strdup(_value)), line(_line) {}
+
+config_param::~config_param()
+{
+ delete next;
+ g_free(value);
+}
+
+const block_param *
+config_param::GetBlockParam(const char *name) const
+{
+ for (const auto &i : block_params) {
+ if (i.name == name) {
+ i.used = true;
+ return &i;
+ }
+ }
+
+ return NULL;
+}
+
+const char *
+config_get_block_string(const struct config_param *param, const char *name,
+ const char *default_value)
+{
+ if (param == nullptr)
+ return default_value;
+
+ const block_param *bp = param->GetBlockParam(name);
+ if (bp == NULL)
+ return default_value;
+
+ return bp->value.c_str();
+}
+
+char *
+config_dup_block_string(const struct config_param *param, const char *name,
+ const char *default_value)
+{
+ return g_strdup(config_get_block_string(param, name, default_value));
+}
+
+char *
+config_dup_block_path(const struct config_param *param, const char *name,
+ GError **error_r)
+{
+ assert(error_r != NULL);
+ assert(*error_r == NULL);
+
+ if (param == nullptr)
+ return nullptr;
+
+ const block_param *bp = param->GetBlockParam(name);
+ if (bp == NULL)
+ return NULL;
+
+ char *path = parsePath(bp->value.c_str(), error_r);
+ if (G_UNLIKELY(path == NULL))
+ g_prefix_error(error_r,
+ "Invalid path in \"%s\" at line %i: ",
+ name, bp->line);
+
+ return path;
+}
+
+unsigned
+config_get_block_unsigned(const struct config_param *param, const char *name,
+ unsigned default_value)
+{
+ if (param == nullptr)
+ return default_value;
+
+ const block_param *bp = param->GetBlockParam(name);
+ if (bp == NULL)
+ return default_value;
+
+ char *endptr;
+ long value = strtol(bp->value.c_str(), &endptr, 0);
+ if (*endptr != 0)
+ MPD_ERROR("Not a valid number in line %i", bp->line);
+
+ if (value < 0)
+ MPD_ERROR("Not a positive number in line %i", bp->line);
+
+ return (unsigned)value;
+}
+
+bool
+config_get_block_bool(const struct config_param *param, const char *name,
+ bool default_value)
+{
+ if (param == nullptr)
+ return default_value;
+
+ const block_param *bp = param->GetBlockParam(name);
+ bool success, value;
+
+ if (bp == NULL)
+ return default_value;
+
+ success = get_bool(bp->value.c_str(), &value);
+ if (!success)
+ MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
+ "(no, false, 0) on line %i\n",
+ name, bp->line);
+
+ return value;
+}
diff --git a/src/ConfigData.hxx b/src/ConfigData.hxx
new file mode 100644
index 00000000..f3e661b2
--- /dev/null
+++ b/src/ConfigData.hxx
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_DATA_HXX
+#define MPD_CONFIG_DATA_HXX
+
+#include "ConfigOption.hxx"
+#include "gerror.h"
+#include "gcc.h"
+
+#ifdef __cplusplus
+#include <string>
+#include <array>
+#include <vector>
+#endif
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+
+struct block_param {
+ std::string name;
+ std::string value;
+ int line;
+
+ /**
+ * This flag is false when nobody has queried the value of
+ * this option yet.
+ */
+ mutable bool used;
+
+ gcc_nonnull_all
+ block_param(const char *_name, const char *_value, int _line=-1)
+ :name(_name), value(_value), line(_line), used(false) {}
+};
+
+#endif
+
+struct config_param {
+ /**
+ * The next config_param with the same name. The destructor
+ * deletes the whole chain.
+ */
+ struct config_param *next;
+
+ char *value;
+ unsigned int line;
+
+#ifdef __cplusplus
+ std::vector<block_param> block_params;
+
+ /**
+ * This flag is false when nobody has queried the value of
+ * this option yet.
+ */
+ bool used;
+
+ config_param(int _line=-1)
+ :next(nullptr), value(nullptr), line(_line), used(false) {}
+
+ gcc_nonnull_all
+ config_param(const char *_value, int _line=-1);
+
+ config_param(const config_param &) = delete;
+
+ ~config_param();
+
+ config_param &operator=(const config_param &) = delete;
+
+ gcc_nonnull_all
+ void AddBlockParam(const char *_name, const char *_value,
+ int _line=-1) {
+ block_params.emplace_back(_name, _value, _line);
+ }
+
+ gcc_nonnull_all gcc_pure
+ const block_param *GetBlockParam(const char *_name) const;
+#endif
+};
+
+#ifdef __cplusplus
+
+struct ConfigData {
+ std::array<config_param *, std::size_t(CONF_MAX)> params;
+};
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gcc_pure
+const char *
+config_get_block_string(const struct config_param *param, const char *name,
+ const char *default_value);
+
+gcc_malloc
+char *
+config_dup_block_string(const struct config_param *param, const char *name,
+ const char *default_value);
+
+/**
+ * Same as config_dup_path(), but looks up the setting in the
+ * specified block.
+ */
+gcc_malloc
+char *
+config_dup_block_path(const struct config_param *param, const char *name,
+ GError **error_r);
+
+gcc_pure
+unsigned
+config_get_block_unsigned(const struct config_param *param, const char *name,
+ unsigned default_value);
+
+gcc_pure
+bool
+config_get_block_bool(const struct config_param *param, const char *name,
+ bool default_value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/ConfigFile.cxx b/src/ConfigFile.cxx
new file mode 100644
index 00000000..e94f3f23
--- /dev/null
+++ b/src/ConfigFile.cxx
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigFile.hxx"
+#include "ConfigQuark.hxx"
+#include "ConfigData.hxx"
+#include "ConfigTemplates.hxx"
+#include "conf.h"
+
+extern "C" {
+#include "string_util.h"
+#include "tokenizer.h"
+}
+
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "config"
+
+#define MAX_STRING_SIZE MPD_PATH_MAX+80
+
+#define CONF_COMMENT '#'
+
+static bool
+config_read_name_value(struct config_param *param, char *input, unsigned line,
+ GError **error_r)
+{
+ const char *name = tokenizer_next_word(&input, error_r);
+ if (name == NULL) {
+ assert(*input != 0);
+ return false;
+ }
+
+ const char *value = tokenizer_next_string(&input, error_r);
+ if (value == NULL) {
+ if (*input == 0) {
+ assert(error_r == NULL || *error_r == NULL);
+ g_set_error(error_r, config_quark(), 0,
+ "Value missing");
+ } else {
+ assert(error_r == NULL || *error_r != NULL);
+ }
+
+ return false;
+ }
+
+ if (*input != 0 && *input != CONF_COMMENT) {
+ g_set_error(error_r, config_quark(), 0,
+ "Unknown tokens after value");
+ return false;
+ }
+
+ const struct block_param *bp = param->GetBlockParam(name);
+ if (bp != NULL) {
+ g_set_error(error_r, config_quark(), 0,
+ "\"%s\" is duplicate, first defined on line %i",
+ name, bp->line);
+ return false;
+ }
+
+ param->AddBlockParam(name, value, line);
+ return true;
+}
+
+static struct config_param *
+config_read_block(FILE *fp, int *count, char *string, GError **error_r)
+{
+ struct config_param *ret = new config_param(*count);
+ GError *error = NULL;
+
+ while (true) {
+ char *line;
+
+ line = fgets(string, MAX_STRING_SIZE, fp);
+ if (line == NULL) {
+ delete ret;
+ g_set_error(error_r, config_quark(), 0,
+ "Expected '}' before end-of-file");
+ return NULL;
+ }
+
+ (*count)++;
+ line = strchug_fast(line);
+ if (*line == 0 || *line == CONF_COMMENT)
+ continue;
+
+ if (*line == '}') {
+ /* end of this block; return from the function
+ (and from this "while" loop) */
+
+ line = strchug_fast(line + 1);
+ if (*line != 0 && *line != CONF_COMMENT) {
+ delete ret;
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: Unknown tokens after '}'",
+ *count);
+ return nullptr;
+ }
+
+ return ret;
+ }
+
+ /* parse name and value */
+
+ if (!config_read_name_value(ret, line, *count, &error)) {
+ assert(*line != 0);
+ delete ret;
+ g_propagate_prefixed_error(error_r, error,
+ "line %i: ", *count);
+ return NULL;
+ }
+ }
+}
+
+gcc_nonnull_all
+static void
+Append(config_param *&head, config_param *p)
+{
+ assert(p->next == nullptr);
+
+ config_param **i = &head;
+ while (*i != nullptr)
+ i = &(*i)->next;
+
+ *i = p;
+}
+
+static bool
+ReadConfigFile(ConfigData &config_data, FILE *fp, GError **error_r)
+{
+ assert(fp != nullptr);
+
+ char string[MAX_STRING_SIZE + 1];
+ int count = 0;
+ struct config_param *param;
+
+ while (fgets(string, MAX_STRING_SIZE, fp)) {
+ char *line;
+ const char *name, *value;
+ GError *error = NULL;
+
+ count++;
+
+ line = strchug_fast(string);
+ if (*line == 0 || *line == CONF_COMMENT)
+ continue;
+
+ /* the first token in each line is the name, followed
+ by either the value or '{' */
+
+ name = tokenizer_next_word(&line, &error);
+ if (name == NULL) {
+ assert(*line != 0);
+ g_propagate_prefixed_error(error_r, error,
+ "line %i: ", count);
+ return false;
+ }
+
+ /* get the definition of that option, and check the
+ "repeatable" flag */
+
+ const ConfigOption o = ParseConfigOptionName(name);
+ if (o == CONF_MAX) {
+ g_set_error(error_r, config_quark(), 0,
+ "unrecognized parameter in config file at "
+ "line %i: %s\n", count, name);
+ return false;
+ }
+
+ const unsigned i = unsigned(o);
+ const ConfigTemplate &option = config_templates[i];
+ config_param *&head = config_data.params[i];
+
+ if (head != nullptr && !option.repeatable) {
+ param = head;
+ g_set_error(error_r, config_quark(), 0,
+ "config parameter \"%s\" is first defined "
+ "on line %i and redefined on line %i\n",
+ name, param->line, count);
+ return false;
+ }
+
+ /* now parse the block or the value */
+
+ if (option.block) {
+ /* it's a block, call config_read_block() */
+
+ if (*line != '{') {
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: '{' expected", count);
+ return false;
+ }
+
+ line = strchug_fast(line + 1);
+ if (*line != 0 && *line != CONF_COMMENT) {
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: Unknown tokens after '{'",
+ count);
+ return false;
+ }
+
+ param = config_read_block(fp, &count, string, error_r);
+ if (param == NULL) {
+ return false;
+ }
+ } else {
+ /* a string value */
+
+ value = tokenizer_next_string(&line, &error);
+ if (value == NULL) {
+ if (*line == 0)
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: Value missing",
+ count);
+ else {
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: %s", count,
+ error->message);
+ g_error_free(error);
+ }
+
+ return false;
+ }
+
+ if (*line != 0 && *line != CONF_COMMENT) {
+ g_set_error(error_r, config_quark(), 0,
+ "line %i: Unknown tokens after value",
+ count);
+ return false;
+ }
+
+ param = new config_param(value, count);
+ }
+
+ Append(head, param);
+ }
+
+ return true;
+}
+
+bool
+ReadConfigFile(ConfigData &config_data, const Path &path, GError **error_r)
+{
+ assert(!path.IsNull());
+ const std::string path_utf8 = path.ToUTF8();
+
+ g_debug("loading file %s", path_utf8.c_str());
+
+ FILE *fp = FOpen(path, FOpenMode::ReadText);
+ if (fp == nullptr) {
+ g_set_error(error_r, config_quark(), errno,
+ "Failed to open %s: %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ bool result = ReadConfigFile(config_data, fp, error_r);
+ fclose(fp);
+ return result;
+}
diff --git a/src/ConfigFile.hxx b/src/ConfigFile.hxx
new file mode 100644
index 00000000..49c0d31e
--- /dev/null
+++ b/src/ConfigFile.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_FILE_HXX
+#define MPD_CONFIG_FILE_HXX
+
+#include "gerror.h"
+
+class Path;
+struct ConfigData;
+
+bool
+ReadConfigFile(ConfigData &data, const Path &path, GError **error_r);
+
+#endif
diff --git a/src/ConfigGlobal.cxx b/src/ConfigGlobal.cxx
new file mode 100644
index 00000000..9786690d
--- /dev/null
+++ b/src/ConfigGlobal.cxx
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfigGlobal.hxx"
+#include "ConfigParser.hxx"
+#include "ConfigData.hxx"
+#include "ConfigFile.hxx"
+
+extern "C" {
+#include "utils.h"
+}
+
+#include "mpd_error.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "config"
+
+static ConfigData config_data;
+
+void config_global_finish(void)
+{
+ for (auto i : config_data.params)
+ delete i;
+}
+
+void config_global_init(void)
+{
+}
+
+bool
+ReadConfigFile(const Path &path, GError **error_r)
+{
+ return ReadConfigFile(config_data, path, error_r);
+}
+
+static void
+Check(const config_param *param)
+{
+ if (!param->used)
+ /* this whole config_param was not queried at all -
+ the feature might be disabled at compile time?
+ Silently ignore it here. */
+ return;
+
+ for (const auto &i : param->block_params) {
+ if (!i.used)
+ g_warning("option '%s' on line %i was not recognized",
+ i.name.c_str(), i.line);
+ }
+}
+
+void config_global_check(void)
+{
+ for (auto i : config_data.params)
+ for (const config_param *p = i; p != nullptr; p = p->next)
+ Check(p);
+}
+
+const struct config_param *
+config_get_next_param(ConfigOption option, const struct config_param * last)
+{
+ config_param *param = last != nullptr
+ ? last->next
+ : config_data.params[unsigned(option)];
+ if (param != nullptr)
+ param->used = true;
+ return param;
+}
+
+const char *
+config_get_string(ConfigOption option, const char *default_value)
+{
+ const struct config_param *param = config_get_param(option);
+
+ if (param == NULL)
+ return default_value;
+
+ return param->value;
+}
+
+char *
+config_dup_path(ConfigOption option, GError **error_r)
+{
+ assert(error_r != NULL);
+ assert(*error_r == NULL);
+
+ const struct config_param *param = config_get_param(option);
+ if (param == NULL)
+ return NULL;
+
+ char *path = parsePath(param->value, error_r);
+ if (G_UNLIKELY(path == NULL))
+ g_prefix_error(error_r,
+ "Invalid path at line %i: ",
+ param->line);
+
+ return path;
+}
+
+unsigned
+config_get_unsigned(ConfigOption option, unsigned default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ long value;
+ char *endptr;
+
+ if (param == NULL)
+ return default_value;
+
+ value = strtol(param->value, &endptr, 0);
+ if (*endptr != 0 || value < 0)
+ MPD_ERROR("Not a valid non-negative number in line %i",
+ param->line);
+
+ return (unsigned)value;
+}
+
+unsigned
+config_get_positive(ConfigOption option, unsigned default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ long value;
+ char *endptr;
+
+ if (param == NULL)
+ return default_value;
+
+ value = strtol(param->value, &endptr, 0);
+ if (*endptr != 0)
+ MPD_ERROR("Not a valid number in line %i", param->line);
+
+ if (value <= 0)
+ MPD_ERROR("Not a positive number in line %i", param->line);
+
+ return (unsigned)value;
+}
+
+bool
+config_get_bool(ConfigOption option, bool default_value)
+{
+ const struct config_param *param = config_get_param(option);
+ bool success, value;
+
+ if (param == NULL)
+ return default_value;
+
+ success = get_bool(param->value, &value);
+ if (!success)
+ MPD_ERROR("Expected boolean value (yes, true, 1) or "
+ "(no, false, 0) on line %i\n",
+ param->line);
+
+ return value;
+}
diff --git a/src/ConfigGlobal.hxx b/src/ConfigGlobal.hxx
new file mode 100644
index 00000000..9abfb2b5
--- /dev/null
+++ b/src/ConfigGlobal.hxx
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_GLOBAL_HXX
+#define MPD_CONFIG_GLOBAL_HXX
+
+#include "ConfigOption.hxx"
+#include "gerror.h"
+#include "gcc.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
+#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
+
+#ifdef __cplusplus
+class Path;
+#endif
+
+void config_global_init(void);
+void config_global_finish(void);
+
+/**
+ * Call this function after all configuration has been evaluated. It
+ * checks for unused parameters, and logs warnings.
+ */
+void config_global_check(void);
+
+#ifdef __cplusplus
+
+bool
+ReadConfigFile(const Path &path, GError **error_r);
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* don't free the returned value
+ set _last_ to NULL to get first entry */
+gcc_pure
+const struct config_param *
+config_get_next_param(enum ConfigOption option,
+ const struct config_param *last);
+
+gcc_pure
+static inline const struct config_param *
+config_get_param(enum ConfigOption option)
+{
+ return config_get_next_param(option, NULL);
+}
+
+/* Note on gcc_pure: Some of the functions declared pure are not
+ really pure in strict sense. They have side effect such that they
+ validate parameter's value and signal an error if it's invalid.
+ However, if the argument was already validated or we don't care
+ about the argument at all, this may be ignored so in the end, we
+ should be fine with calling those functions pure. */
+
+gcc_pure
+const char *
+config_get_string(enum ConfigOption option, const char *default_value);
+
+/**
+ * Returns an optional configuration variable which contains an
+ * absolute path. If there is a tilde prefix, it is expanded.
+ * Returns NULL if the value is not present. If the path could not be
+ * parsed, returns NULL and sets the error.
+ *
+ * The return value must be freed with g_free().
+ */
+gcc_malloc
+char *
+config_dup_path(enum ConfigOption option, GError **error_r);
+
+gcc_pure
+unsigned
+config_get_unsigned(enum ConfigOption option, unsigned default_value);
+
+gcc_pure
+unsigned
+config_get_positive(enum ConfigOption option, unsigned default_value);
+
+gcc_pure
+bool config_get_bool(enum ConfigOption option, bool default_value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/ConfigOption.hxx b/src/ConfigOption.hxx
new file mode 100644
index 00000000..21a3a02e
--- /dev/null
+++ b/src/ConfigOption.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_OPTION_HXX
+#define MPD_CONFIG_OPTION_HXX
+
+#include "gcc.h"
+
+enum ConfigOption {
+ CONF_MUSIC_DIR,
+ CONF_PLAYLIST_DIR,
+ CONF_FOLLOW_INSIDE_SYMLINKS,
+ CONF_FOLLOW_OUTSIDE_SYMLINKS,
+ CONF_DB_FILE,
+ CONF_STICKER_FILE,
+ CONF_LOG_FILE,
+ CONF_PID_FILE,
+ CONF_STATE_FILE,
+ CONF_RESTORE_PAUSED,
+ CONF_USER,
+ CONF_GROUP,
+ CONF_BIND_TO_ADDRESS,
+ CONF_PORT,
+ CONF_LOG_LEVEL,
+ CONF_ZEROCONF_NAME,
+ CONF_ZEROCONF_ENABLED,
+ CONF_PASSWORD,
+ CONF_DEFAULT_PERMS,
+ CONF_AUDIO_OUTPUT,
+ CONF_AUDIO_OUTPUT_FORMAT,
+ CONF_MIXER_TYPE,
+ CONF_REPLAYGAIN,
+ CONF_REPLAYGAIN_PREAMP,
+ CONF_REPLAYGAIN_MISSING_PREAMP,
+ CONF_REPLAYGAIN_LIMIT,
+ CONF_VOLUME_NORMALIZATION,
+ CONF_SAMPLERATE_CONVERTER,
+ CONF_AUDIO_BUFFER_SIZE,
+ CONF_BUFFER_BEFORE_PLAY,
+ CONF_HTTP_PROXY_HOST,
+ CONF_HTTP_PROXY_PORT,
+ CONF_HTTP_PROXY_USER,
+ CONF_HTTP_PROXY_PASSWORD,
+ CONF_CONN_TIMEOUT,
+ CONF_MAX_CONN,
+ CONF_MAX_PLAYLIST_LENGTH,
+ CONF_MAX_COMMAND_LIST_SIZE,
+ CONF_MAX_OUTPUT_BUFFER_SIZE,
+ CONF_FS_CHARSET,
+ CONF_ID3V1_ENCODING,
+ CONF_METADATA_TO_USE,
+ CONF_SAVE_ABSOLUTE_PATHS,
+ CONF_DECODER,
+ CONF_INPUT,
+ CONF_GAPLESS_MP3_PLAYBACK,
+ CONF_PLAYLIST_PLUGIN,
+ CONF_AUTO_UPDATE,
+ CONF_AUTO_UPDATE_DEPTH,
+ CONF_DESPOTIFY_USER,
+ CONF_DESPOTIFY_PASSWORD,
+ CONF_DESPOTIFY_HIGH_BITRATE,
+ CONF_AUDIO_FILTER,
+ CONF_DATABASE,
+ CONF_MAX
+};
+
+/**
+ * @return #CONF_MAX if not found
+ */
+gcc_pure
+enum ConfigOption
+ParseConfigOptionName(const char *name);
+
+#endif
diff --git a/src/ConfigParser.cxx b/src/ConfigParser.cxx
new file mode 100644
index 00000000..9798b6ed
--- /dev/null
+++ b/src/ConfigParser.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigParser.hxx"
+
+extern "C" {
+#include "string_util.h"
+}
+
+bool
+get_bool(const char *value, bool *value_r)
+{
+ static const char *t[] = { "yes", "true", "1", nullptr };
+ static const char *f[] = { "no", "false", "0", nullptr };
+
+ if (string_array_contains(t, value)) {
+ *value_r = true;
+ return true;
+ }
+
+ if (string_array_contains(f, value)) {
+ *value_r = false;
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/ConfigParser.hxx b/src/ConfigParser.hxx
new file mode 100644
index 00000000..00fd42fe
--- /dev/null
+++ b/src/ConfigParser.hxx
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_PARSER_HXX
+#define MPD_CONFIG_PARSER_HXX
+
+bool
+get_bool(const char *value, bool *value_r);
+
+#endif
diff --git a/src/ConfigQuark.hxx b/src/ConfigQuark.hxx
new file mode 100644
index 00000000..11594f99
--- /dev/null
+++ b/src/ConfigQuark.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_QUARK_HXX
+#define MPD_CONFIG_QUARK_HXX
+
+#include <glib.h>
+
+/**
+ * A GQuark for GError instances, resulting from malformed
+ * configuration.
+ */
+G_GNUC_CONST
+static inline GQuark
+config_quark(void)
+{
+ return g_quark_from_static_string("config");
+}
+
+#endif
diff --git a/src/ConfigTemplates.cxx b/src/ConfigTemplates.cxx
new file mode 100644
index 00000000..6c6bf168
--- /dev/null
+++ b/src/ConfigTemplates.cxx
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "ConfigTemplates.hxx"
+#include "ConfigOption.hxx"
+
+#include <string.h>
+
+const ConfigTemplate config_templates[] = {
+ { "music_directory", false, false },
+ { "playlist_directory", false, false },
+ { "follow_inside_symlinks", false, false },
+ { "follow_outside_symlinks", false, false },
+ { "db_file", false, false },
+ { "sticker_file", false, false },
+ { "log_file", false, false },
+ { "pid_file", false, false },
+ { "state_file", false, false },
+ { "restore_paused", false, false },
+ { "user", false, false },
+ { "group", false, false },
+ { "bind_to_address", true, false },
+ { "port", false, false },
+ { "log_level", false, false },
+ { "zeroconf_name", false, false },
+ { "zeroconf_enabled", false, false },
+ { "password", true, false },
+ { "default_permissions", false, false },
+ { "audio_output", true, true },
+ { "audio_output_format", false, false },
+ { "mixer_type", false, false },
+ { "replaygain", false, false },
+ { "replaygain_preamp", false, false },
+ { "replaygain_missing_preamp", false, false },
+ { "replaygain_limit", false, false },
+ { "volume_normalization", false, false },
+ { "samplerate_converter", false, false },
+ { "audio_buffer_size", false, false },
+ { "buffer_before_play", false, false },
+ { "http_proxy_host", false, false },
+ { "http_proxy_port", false, false },
+ { "http_proxy_user", false, false },
+ { "http_proxy_password", false, false },
+ { "connection_timeout", false, false },
+ { "max_connections", false, false },
+ { "max_playlist_length", false, false },
+ { "max_command_list_size", false, false },
+ { "max_output_buffer_size", false, false },
+ { "filesystem_charset", false, false },
+ { "id3v1_encoding", false, false },
+ { "metadata_to_use", false, false },
+ { "save_absolute_paths_in_playlists", false, false },
+ { "decoder", true, true },
+ { "input", true, true },
+ { "gapless_mp3_playback", false, false },
+ { "playlist_plugin", true, true },
+ { "auto_update", false, false },
+ { "auto_update_depth", false, false },
+ { "despotify_user", false, false },
+ { "despotify_password", false, false},
+ { "despotify_high_bitrate", false, false },
+ { "filter", true, true },
+ { "database", false, true },
+};
+
+static constexpr unsigned n_config_templates =
+ sizeof(config_templates) / sizeof(config_templates[0]);
+
+static_assert(n_config_templates == unsigned(CONF_MAX),
+ "Wrong number of config_templates");
+
+ConfigOption
+ParseConfigOptionName(const char *name)
+{
+ for (unsigned i = 0; i < n_config_templates; ++i)
+ if (strcmp(config_templates[i].name, name) == 0)
+ return ConfigOption(i);
+
+ return CONF_MAX;
+}
diff --git a/src/ConfigTemplates.hxx b/src/ConfigTemplates.hxx
new file mode 100644
index 00000000..4f546046
--- /dev/null
+++ b/src/ConfigTemplates.hxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIG_TEMPLATES_HXX
+#define MPD_CONFIG_TEMPLATES_HXX
+
+#include "ConfigOption.hxx"
+
+struct ConfigTemplate {
+ const char *const name;
+ const bool repeatable;
+ const bool block;
+};
+
+extern const ConfigTemplate config_templates[];
+
+#endif
diff --git a/src/crossfade.c b/src/CrossFade.cxx
index 46a0dff3..0bdcc43d 100644
--- a/src/crossfade.c
+++ b/src/CrossFade.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,13 @@
*/
#include "config.h"
-#include "crossfade.h"
-#include "chunk.h"
+#include "CrossFade.hxx"
+#include "MusicChunk.hxx"
#include "audio_format.h"
#include "tag.h"
+#include <cmath>
+
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -82,9 +84,8 @@ static float mixramp_interpolate(char *ramp_list, float required_db)
}
/* If required db < any stored value, use the least. */
- if (isnan(last_db)) {
+ if (std::isnan(last_db))
return secs;
- }
/* Finally, interpolate linearly. */
secs = last_secs + (required_db - last_db) * (secs - last_secs) / (db - last_db);
@@ -114,13 +115,14 @@ unsigned cross_fade_calc(float duration, float total_time,
chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE;
- if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) {
+ if (std::isnan(mixramp_delay) || !mixramp_start || !mixramp_prev_end) {
chunks = (chunks_f * duration + 0.5);
} else {
/* Calculate mixramp overlap. */
mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db)
+ mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db);
- if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) {
+ if (!std::isnan(mixramp_overlap) &&
+ mixramp_delay <= mixramp_overlap) {
chunks = (chunks_f * (mixramp_overlap - mixramp_delay));
g_debug("will overlap %d chunks, %fs", chunks,
mixramp_overlap - mixramp_delay);
diff --git a/src/crossfade.h b/src/CrossFade.hxx
index d581dbfe..1c467075 100644
--- a/src/crossfade.h
+++ b/src/CrossFade.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CROSSFADE_H
-#define MPD_CROSSFADE_H
+#ifndef MPD_CROSSFADE_HXX
+#define MPD_CROSSFADE_HXX
struct audio_format;
struct music_chunk;
diff --git a/src/DatabaseCommands.cxx b/src/DatabaseCommands.cxx
new file mode 100644
index 00000000..bd5a48b3
--- /dev/null
+++ b/src/DatabaseCommands.cxx
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseCommands.hxx"
+#include "DatabaseQueue.hxx"
+#include "DatabasePlaylist.hxx"
+#include "DatabasePrint.hxx"
+#include "DatabaseSelection.hxx"
+#include "CommandError.hxx"
+#include "ClientInternal.hxx"
+#include "tag.h"
+#include "uri.h"
+#include "SongFilter.hxx"
+#include "protocol/Result.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+enum command_return
+handle_lsinfo2(Client *client, int argc, char *argv[])
+{
+ const char *uri;
+
+ if (argc == 2)
+ uri = argv[1];
+ else
+ /* default is root directory */
+ uri = "";
+
+ const DatabaseSelection selection(uri, false);
+
+ GError *error = NULL;
+ if (!db_selection_print(client, selection, true, &error))
+ return print_error(client, error);
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_match(Client *client, int argc, char *argv[], bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ const DatabaseSelection selection("", true, &filter);
+
+ GError *error = NULL;
+ return db_selection_print(client, selection, true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_find(Client *client, int argc, char *argv[])
+{
+ return handle_match(client, argc, argv, false);
+}
+
+enum command_return
+handle_search(Client *client, int argc, char *argv[])
+{
+ return handle_match(client, argc, argv, true);
+}
+
+static enum command_return
+handle_match_add(Client *client, int argc, char *argv[], bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ const DatabaseSelection selection("", true, &filter);
+ GError *error = NULL;
+ return AddFromDatabase(client->partition, selection, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_findadd(Client *client, int argc, char *argv[])
+{
+ return handle_match_add(client, argc, argv, false);
+}
+
+enum command_return
+handle_searchadd(Client *client, int argc, char *argv[])
+{
+ return handle_match_add(client, argc, argv, true);
+}
+
+enum command_return
+handle_searchaddpl(Client *client, int argc, char *argv[])
+{
+ const char *playlist = argv[1];
+
+ SongFilter filter;
+ if (!filter.Parse(argc - 2, argv + 2, true)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ GError *error = NULL;
+ return search_add_to_playlist("", playlist, &filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_count(Client *client, int argc, char *argv[])
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, false)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ GError *error = NULL;
+ return searchStatsForSongsIn(client, "", &filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_listall(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *directory = "";
+
+ if (argc == 2)
+ directory = argv[1];
+
+ GError *error = NULL;
+ return printAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_list(Client *client, int argc, char *argv[])
+{
+ unsigned tagType = locate_parse_type(argv[1]);
+
+ if (tagType == TAG_NUM_OF_ITEM_TYPES) {
+ command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (tagType == LOCATE_TAG_ANY_TYPE) {
+ command_error(client, ACK_ERROR_ARG,
+ "\"any\" is not a valid return tag type");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* for compatibility with < 0.12.0 */
+ SongFilter *filter;
+ if (argc == 3) {
+ if (tagType != TAG_ALBUM) {
+ command_error(client, ACK_ERROR_ARG,
+ "should be \"%s\" for 3 arguments",
+ tag_item_names[TAG_ALBUM]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ filter = new SongFilter((unsigned)TAG_ARTIST, argv[2]);
+ } else if (argc > 2) {
+ filter = new SongFilter();
+ if (!filter->Parse(argc - 2, argv + 2, false)) {
+ delete filter;
+ command_error(client, ACK_ERROR_ARG,
+ "not able to parse args");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else
+ filter = nullptr;
+
+ GError *error = NULL;
+ enum command_return ret =
+ listAllUniqueTags(client, tagType, filter, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+
+ delete filter;
+
+ return ret;
+}
+
+enum command_return
+handle_listallinfo(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *directory = "";
+
+ if (argc == 2)
+ directory = argv[1];
+
+ GError *error = NULL;
+ return printInfoForAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
diff --git a/src/DatabaseCommands.hxx b/src/DatabaseCommands.hxx
new file mode 100644
index 00000000..335adc4d
--- /dev/null
+++ b/src/DatabaseCommands.hxx
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DATABASE_COMMANDS_HXX
+#define MPD_DATABASE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_lsinfo2(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_find(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_findadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_search(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_searchadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_searchaddpl(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_count(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listall(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_list(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listallinfo(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/database.c b/src/DatabaseGlue.cxx
index 8c903bb4..db7d4b9f 100644
--- a/src/database.c
+++ b/src/DatabaseGlue.cxx
@@ -18,17 +18,20 @@
*/
#include "config.h"
-#include "database.h"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "DatabaseRegistry.hxx"
+#include "DatabaseSave.hxx"
+#include "Directory.hxx"
+#include "conf.h"
+
+extern "C" {
#include "db_error.h"
-#include "db_save.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "db_plugin.h"
-#include "db/simple_db_plugin.h"
-#include "directory.h"
#include "stats.h"
-#include "conf.h"
-#include "glib_compat.h"
+}
+
+#include "DatabasePlugin.hxx"
+#include "db/SimpleDatabasePlugin.hxx"
#include <glib.h>
@@ -42,97 +45,89 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "database"
-static struct db *db;
+static Database *db;
static bool db_is_open;
+static bool is_simple;
bool
-db_init(const struct config_param *path, GError **error_r)
+DatabaseGlobalInit(const config_param *param, GError **error_r)
{
assert(db == NULL);
assert(!db_is_open);
- if (path == NULL)
- return true;
+ const char *plugin_name =
+ config_get_block_string(param, "plugin", "simple");
+ is_simple = strcmp(plugin_name, "simple") == 0;
- struct config_param *param = config_new_param("database", path->line);
- config_add_block_param(param, "path", path->value, path->line);
-
- db = db_plugin_new(&simple_db_plugin, param, error_r);
-
- config_param_free(param);
+ const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
+ if (plugin == NULL) {
+ g_set_error(error_r, db_quark(), 0,
+ "No such database plugin: %s", plugin_name);
+ return false;
+ }
+ db = plugin->create(param, error_r);
return db != NULL;
}
void
-db_finish(void)
+DatabaseGlobalDeinit(void)
{
if (db_is_open)
- db_plugin_close(db);
+ db->Close();
if (db != NULL)
- db_plugin_free(db);
+ delete db;
}
-struct directory *
-db_get_root(void)
+const Database *
+GetDatabase()
{
- assert(db != NULL);
+ assert(db == NULL || db_is_open);
- return simple_db_get_root(db);
+ return db;
}
-struct directory *
-db_get_directory(const char *name)
+const Database *
+GetDatabase(GError **error_r)
{
- if (db == NULL)
- return NULL;
+ assert(db == nullptr || db_is_open);
- struct directory *music_root = db_get_root();
- if (name == NULL)
- return music_root;
+ if (db == nullptr)
+ g_set_error_literal(error_r, db_quark(), DB_DISABLED,
+ "No database");
- struct directory *directory =
- directory_lookup_directory(music_root, name);
- return directory;
+ return db;
}
-struct song *
-db_get_song(const char *file)
+bool
+db_is_simple(void)
{
- assert(file != NULL);
+ assert(db == NULL || db_is_open);
- g_debug("get song: %s", file);
-
- if (db == NULL)
- return NULL;
-
- return db_plugin_get_song(db, file, NULL);
+ return is_simple;
}
-bool
-db_visit(const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
+Directory *
+db_get_root(void)
{
- if (db == NULL) {
- g_set_error_literal(error_r, db_quark(), DB_DISABLED,
- "No database");
- return false;
- }
+ assert(db != NULL);
+ assert(db_is_simple());
- return db_plugin_visit(db, selection, visitor, ctx, error_r);
+ return ((SimpleDatabase *)db)->GetRoot();
}
-bool
-db_walk(const char *uri,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
+Directory *
+db_get_directory(const char *name)
{
- struct db_selection selection;
- db_selection_init(&selection, uri, true);
+ if (db == NULL)
+ return NULL;
+
+ Directory *music_root = db_get_root();
+ if (name == NULL)
+ return music_root;
- return db_visit(&selection, visitor, ctx, error_r);
+ return music_root->LookupDirectory(name);
}
bool
@@ -140,17 +135,18 @@ db_save(GError **error_r)
{
assert(db != NULL);
assert(db_is_open);
+ assert(db_is_simple());
- return simple_db_save(db, error_r);
+ return ((SimpleDatabase *)db)->Save(error_r);
}
bool
-db_load(GError **error)
+DatabaseGlobalOpen(GError **error)
{
assert(db != NULL);
assert(!db_is_open);
- if (!db_plugin_open(db, error))
+ if (!db->Open(error))
return false;
db_is_open = true;
@@ -165,6 +161,7 @@ db_get_mtime(void)
{
assert(db != NULL);
assert(db_is_open);
+ assert(db_is_simple());
- return simple_db_get_mtime(db);
+ return ((SimpleDatabase *)db)->GetLastModified();
}
diff --git a/src/DatabaseGlue.hxx b/src/DatabaseGlue.hxx
new file mode 100644
index 00000000..ea26f324
--- /dev/null
+++ b/src/DatabaseGlue.hxx
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DATABASE_GLUE_HXX
+#define MPD_DATABASE_GLUE_HXX
+
+#include "gcc.h"
+#include "gerror.h"
+
+struct config_param;
+class Database;
+
+/**
+ * Initialize the database library.
+ *
+ * @param param the database configuration block
+ */
+bool
+DatabaseGlobalInit(const config_param *param, GError **error_r);
+
+void
+DatabaseGlobalDeinit(void);
+
+bool
+DatabaseGlobalOpen(GError **error);
+
+/**
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
+ */
+gcc_pure
+const Database *
+GetDatabase();
+
+/**
+ * Returns the global #Database instance. May return NULL if this MPD
+ * configuration has no database (no music_directory was configured).
+ */
+gcc_pure
+const Database *
+GetDatabase(GError **error_r);
+
+#endif
diff --git a/src/DatabaseHelpers.cxx b/src/DatabaseHelpers.cxx
new file mode 100644
index 00000000..dc31a4bc
--- /dev/null
+++ b/src/DatabaseHelpers.cxx
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "DatabaseHelpers.hxx"
+#include "DatabasePlugin.hxx"
+#include "song.h"
+#include "tag.h"
+
+#include <functional>
+#include <set>
+
+#include <string.h>
+
+struct StringLess {
+ gcc_pure
+ bool operator()(const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+ }
+};
+
+typedef std::set<const char *, StringLess> StringSet;
+
+static bool
+CollectTags(StringSet &set, enum tag_type tag_type, song &song)
+{
+ struct tag *tag = song.tag;
+ if (tag == nullptr)
+ return true;
+
+ bool found = false;
+ for (unsigned i = 0; i < tag->num_items; ++i) {
+ if (tag->items[i]->type == tag_type) {
+ set.insert(tag->items[i]->value);
+ found = true;
+ }
+ }
+
+ if (!found)
+ set.insert("");
+
+ return true;
+}
+
+bool
+VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r)
+{
+ StringSet set;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(CollectTags, std::ref(set), tag_type, _1);
+ if (!db.Visit(selection, f, error_r))
+ return false;
+
+ for (auto value : set)
+ if (!visit_string(value, error_r))
+ return false;
+
+ return true;
+}
+
+static void
+StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
+ const struct tag &tag)
+{
+ if (tag.time > 0)
+ stats.total_duration += tag.time;
+
+ for (unsigned i = 0; i < tag.num_items; ++i) {
+ const struct tag_item &item = *tag.items[i];
+
+ switch (item.type) {
+ case TAG_ARTIST:
+ artists.insert(item.value);
+ break;
+
+ case TAG_ALBUM:
+ albums.insert(item.value);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static bool
+StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
+ song &song)
+{
+ ++stats.song_count;
+
+ if (song.tag != nullptr)
+ StatsVisitTag(stats, artists, albums, *song.tag);
+
+ return true;
+}
+
+bool
+GetStats(const Database &db, const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r)
+{
+ stats.Clear();
+
+ StringSet artists, albums;
+ using namespace std::placeholders;
+ const auto f = std::bind(StatsVisitSong,
+ std::ref(stats), std::ref(artists),
+ std::ref(albums), _1);
+ if (!db.Visit(selection, f, error_r))
+ return false;
+
+ stats.artist_count = artists.size();
+ stats.album_count = albums.size();
+ return true;
+}
diff --git a/src/DatabaseHelpers.hxx b/src/DatabaseHelpers.hxx
new file mode 100644
index 00000000..cfcc94ac
--- /dev/null
+++ b/src/DatabaseHelpers.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_MEMORY_DATABASE_PLUGIN_HXX
+#define MPD_MEMORY_DATABASE_PLUGIN_HXX
+
+#include "DatabaseVisitor.hxx"
+#include "tag.h"
+#include "gcc.h"
+
+class Database;
+struct DatabaseSelection;
+struct DatabaseStats;
+
+bool
+VisitUniqueTags(const Database &db, const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r);
+
+bool
+GetStats(const Database &db, const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r);
+
+#endif
diff --git a/src/DatabaseLock.cxx b/src/DatabaseLock.cxx
new file mode 100644
index 00000000..398e5aeb
--- /dev/null
+++ b/src/DatabaseLock.cxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseLock.hxx"
+#include "gcc.h"
+
+Mutex db_mutex;
+
+#ifndef NDEBUG
+GThread *db_mutex_holder;
+#endif
diff --git a/src/db_lock.h b/src/DatabaseLock.hxx
index 4640502f..371a7d7b 100644
--- a/src/db_lock.h
+++ b/src/DatabaseLock.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,16 +23,16 @@
* multi-threading.
*/
-#ifndef MPD_DB_LOCK_H
-#define MPD_DB_LOCK_H
+#ifndef MPD_DB_LOCK_HXX
+#define MPD_DB_LOCK_HXX
#include "check.h"
+#include "thread/Mutex.hxx"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
-extern GStaticMutex db_mutex;
+extern Mutex db_mutex;
#ifndef NDEBUG
@@ -59,7 +59,7 @@ db_lock(void)
{
assert(!holding_db_lock());
- g_static_mutex_lock(&db_mutex);
+ db_mutex.lock();
assert(db_mutex_holder == NULL);
#ifndef NDEBUG
@@ -78,7 +78,22 @@ db_unlock(void)
db_mutex_holder = NULL;
#endif
- g_static_mutex_unlock(&db_mutex);
+ db_mutex.unlock();
}
+#ifdef __cplusplus
+
+class ScopeDatabaseLock {
+public:
+ ScopeDatabaseLock() {
+ db_lock();
+ }
+
+ ~ScopeDatabaseLock() {
+ db_unlock();
+ }
+};
+
+#endif
+
#endif
diff --git a/src/DatabasePlaylist.cxx b/src/DatabasePlaylist.cxx
new file mode 100644
index 00000000..fb477e83
--- /dev/null
+++ b/src/DatabasePlaylist.cxx
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabasePlaylist.hxx"
+#include "DatabaseSelection.hxx"
+#include "PlaylistFile.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <functional>
+
+static bool
+AddSong(const char *playlist_path_utf8,
+ song &song, GError **error_r)
+{
+ return spl_append_song(playlist_path_utf8, &song, error_r);
+}
+
+bool
+search_add_to_playlist(const char *uri, const char *playlist_path_utf8,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection(uri, true, filter);
+
+ using namespace std::placeholders;
+ const auto f = std::bind(AddSong, playlist_path_utf8, _1, _2);
+ return db->Visit(selection, f, error_r);
+}
diff --git a/src/db_save.h b/src/DatabasePlaylist.hxx
index e760ec88..7c6952ff 100644
--- a/src/db_save.h
+++ b/src/DatabasePlaylist.hxx
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_SAVE_H
-#define MPD_DB_SAVE_H
+#ifndef MPD_DATABASE_PLAYLIST_HXX
+#define MPD_DATABASE_PLAYLIST_HXX
-#include <glib.h>
-#include <stdbool.h>
-#include <stdio.h>
+#include "gcc.h"
+#include "gerror.h"
-struct directory;
-
-void
-db_save_internal(FILE *file, const struct directory *root);
+class SongFilter;
+gcc_nonnull(1,2)
bool
-db_load_internal(FILE *file, struct directory *root, GError **error);
+search_add_to_playlist(const char *uri, const char *path_utf8,
+ const SongFilter *filter,
+ GError **error_r);
#endif
diff --git a/src/DatabasePlugin.hxx b/src/DatabasePlugin.hxx
new file mode 100644
index 00000000..a175b3cd
--- /dev/null
+++ b/src/DatabasePlugin.hxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * This header declares the db_plugin class. It describes a
+ * plugin API for databases of song metadata.
+ */
+
+#ifndef MPD_DATABASE_PLUGIN_HXX
+#define MPD_DATABASE_PLUGIN_HXX
+
+#include "DatabaseVisitor.hxx"
+#include "gcc.h"
+
+extern "C" {
+#include "tag.h"
+}
+
+struct config_param;
+struct DatabaseSelection;
+struct db_visitor;
+
+struct DatabaseStats {
+ /**
+ * Number of songs.
+ */
+ unsigned song_count;
+
+ /**
+ * Total duration of all songs (in seconds).
+ */
+ unsigned long total_duration;
+
+ /**
+ * Number of distinct artist names.
+ */
+ unsigned artist_count;
+
+ /**
+ * Number of distinct album names.
+ */
+ unsigned album_count;
+
+ void Clear() {
+ song_count = 0;
+ total_duration = 0;
+ artist_count = album_count = 0;
+ }
+};
+
+class Database {
+public:
+ /**
+ * Free instance data.
+ */
+ virtual ~Database() {}
+
+ /**
+ * Open the database. Read it into memory if applicable.
+ */
+ virtual bool Open(gcc_unused GError **error_r) {
+ return true;
+ }
+
+ /**
+ * Close the database, free allocated memory.
+ */
+ virtual void Close() {}
+
+ /**
+ * Look up a song (including tag data) in the database. When
+ * you don't need this anymore, call ReturnSong().
+ *
+ * @param uri_utf8 the URI of the song within the music
+ * directory (UTF-8)
+ */
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const = 0;
+
+ /**
+ * Mark the song object as "unused". Call this on objects
+ * returned by GetSong().
+ */
+ virtual void ReturnSong(struct song *song) const = 0;
+
+ /**
+ * Visit the selected entities.
+ */
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const = 0;
+
+ bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ GError **error_r) const {
+ return Visit(selection, visit_directory, visit_song,
+ VisitPlaylist(), error_r);
+ }
+
+ bool Visit(const DatabaseSelection &selection, VisitSong visit_song,
+ GError **error_r) const {
+ return Visit(selection, VisitDirectory(), visit_song, error_r);
+ }
+
+ /**
+ * Visit all unique tag values.
+ */
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const = 0;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const = 0;
+};
+
+struct DatabasePlugin {
+ const char *name;
+
+ /**
+ * Allocates and configures a database.
+ */
+ Database *(*create)(const struct config_param *param,
+ GError **error_r);
+};
+
+#endif
diff --git a/src/DatabasePrint.cxx b/src/DatabasePrint.cxx
new file mode 100644
index 00000000..2384d5c1
--- /dev/null
+++ b/src/DatabasePrint.cxx
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabasePrint.hxx"
+#include "DatabaseSelection.hxx"
+#include "SongFilter.hxx"
+#include "PlaylistVector.hxx"
+#include "SongPrint.hxx"
+#include "TimePrint.hxx"
+#include "Directory.hxx"
+#include "Client.hxx"
+#include "tag.h"
+
+extern "C" {
+#include "song.h"
+}
+
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <functional>
+
+static bool
+PrintDirectory(Client *client, const Directory &directory)
+{
+ if (!directory.IsRoot())
+ client_printf(client, "directory: %s\n", directory.GetPath());
+
+ return true;
+}
+
+static void
+print_playlist_in_directory(Client *client,
+ const Directory &directory,
+ const char *name_utf8)
+{
+ if (directory.IsRoot())
+ client_printf(client, "playlist: %s\n", name_utf8);
+ else
+ client_printf(client, "playlist: %s/%s\n",
+ directory.GetPath(), name_utf8);
+}
+
+static bool
+PrintSongBrief(Client *client, song &song)
+{
+ assert(song.parent != NULL);
+
+ song_print_uri(client, &song);
+
+ if (song.tag != NULL && song.tag->has_playlist)
+ /* this song file has an embedded CUE sheet */
+ print_playlist_in_directory(client, *song.parent, song.uri);
+
+ return true;
+}
+
+static bool
+PrintSongFull(Client *client, song &song)
+{
+ assert(song.parent != NULL);
+
+ song_print_info(client, &song);
+
+ if (song.tag != NULL && song.tag->has_playlist)
+ /* this song file has an embedded CUE sheet */
+ print_playlist_in_directory(client, *song.parent, song.uri);
+
+ return true;
+}
+
+static bool
+PrintPlaylistBrief(Client *client,
+ const PlaylistInfo &playlist,
+ const Directory &directory)
+{
+ print_playlist_in_directory(client, directory, playlist.name.c_str());
+ return true;
+}
+
+static bool
+PrintPlaylistFull(Client *client,
+ const PlaylistInfo &playlist,
+ const Directory &directory)
+{
+ print_playlist_in_directory(client, directory, playlist.name.c_str());
+
+ if (playlist.mtime > 0)
+ time_print(client, "Last-Modified", playlist.mtime);
+
+ return true;
+}
+
+bool
+db_selection_print(Client *client, const DatabaseSelection &selection,
+ bool full, GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ using namespace std::placeholders;
+ const auto d = selection.filter == nullptr
+ ? std::bind(PrintDirectory, client, _1)
+ : VisitDirectory();
+ const auto s = std::bind(full ? PrintSongFull : PrintSongBrief,
+ client, _1);
+ const auto p = selection.filter == nullptr
+ ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief,
+ client, _1, _2)
+ : VisitPlaylist();
+
+ return db->Visit(selection, d, s, p, error_r);
+}
+
+struct SearchStats {
+ int numberOfSongs;
+ unsigned long playTime;
+};
+
+static void printSearchStats(Client *client, SearchStats *stats)
+{
+ client_printf(client, "songs: %i\n", stats->numberOfSongs);
+ client_printf(client, "playtime: %li\n", stats->playTime);
+}
+
+static bool
+stats_visitor_song(SearchStats &stats, song &song)
+{
+ stats.numberOfSongs++;
+ stats.playTime += song_get_duration(&song);
+
+ return true;
+}
+
+bool
+searchStatsForSongsIn(Client *client, const char *name,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection(name, true, filter);
+
+ SearchStats stats;
+ stats.numberOfSongs = 0;
+ stats.playTime = 0;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(stats_visitor_song, std::ref(stats),
+ _1);
+ if (!db->Visit(selection, f, error_r))
+ return false;
+
+ printSearchStats(client, &stats);
+ return true;
+}
+
+bool
+printAllIn(Client *client, const char *uri_utf8, GError **error_r)
+{
+ const DatabaseSelection selection(uri_utf8, true);
+ return db_selection_print(client, selection, false, error_r);
+}
+
+bool
+printInfoForAllIn(Client *client, const char *uri_utf8,
+ GError **error_r)
+{
+ const DatabaseSelection selection(uri_utf8, true);
+ return db_selection_print(client, selection, true, error_r);
+}
+
+static bool
+PrintSongURIVisitor(Client *client, song &song)
+{
+ song_print_uri(client, &song);
+
+ return true;
+}
+
+static bool
+PrintUniqueTag(Client *client, enum tag_type tag_type,
+ const char *value)
+{
+ client_printf(client, "%s: %s\n", tag_item_names[tag_type], value);
+ return true;
+}
+
+bool
+listAllUniqueTags(Client *client, int type,
+ const SongFilter *filter,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ const DatabaseSelection selection("", true, filter);
+
+ if (type == LOCATE_TAG_FILE_TYPE) {
+ using namespace std::placeholders;
+ const auto f = std::bind(PrintSongURIVisitor, client, _1);
+ return db->Visit(selection, f, error_r);
+ } else {
+ using namespace std::placeholders;
+ const auto f = std::bind(PrintUniqueTag, client,
+ (enum tag_type)type, _1);
+ return db->VisitUniqueTags(selection, (enum tag_type)type,
+ f, error_r);
+ }
+}
diff --git a/src/db_print.h b/src/DatabasePrint.hxx
index 1b957da1..68551b63 100644
--- a/src/db_print.h
+++ b/src/DatabasePrint.hxx
@@ -21,51 +21,37 @@
#define MPD_DB_PRINT_H
#include "gcc.h"
+#include "gerror.h"
-#include <glib.h>
-#include <stdbool.h>
-
-struct client;
-struct locate_item_list;
-struct db_selection;
+class SongFilter;
+struct DatabaseSelection;
struct db_visitor;
+class Client;
-gcc_nonnull(1,2)
+gcc_nonnull(1)
bool
-db_selection_print(struct client *client, const struct db_selection *selection,
+db_selection_print(Client *client, const DatabaseSelection &selection,
bool full, GError **error_r);
gcc_nonnull(1,2)
bool
-printAllIn(struct client *client, const char *uri_utf8, GError **error_r);
+printAllIn(Client *client, const char *uri_utf8, GError **error_r);
gcc_nonnull(1,2)
bool
-printInfoForAllIn(struct client *client, const char *uri_utf8,
+printInfoForAllIn(Client *client, const char *uri_utf8,
GError **error_r);
-gcc_nonnull(1,2,3)
-bool
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
+gcc_nonnull(1,2)
bool
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
+searchStatsForSongsIn(Client *client, const char *name,
+ const SongFilter *filter,
GError **error_r);
-gcc_nonnull(1,3)
+gcc_nonnull(1)
bool
-listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria,
+listAllUniqueTags(Client *client, int type,
+ const SongFilter *filter,
GError **error_r);
#endif
diff --git a/src/DatabaseQueue.cxx b/src/DatabaseQueue.cxx
new file mode 100644
index 00000000..e22144c0
--- /dev/null
+++ b/src/DatabaseQueue.cxx
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseQueue.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "Partition.hxx"
+
+#include <functional>
+
+static bool
+AddToQueue(Partition &partition, song &song, GError **error_r)
+{
+ enum playlist_result result =
+ partition.playlist.AppendSong(partition.pc, &song, NULL);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ g_set_error(error_r, playlist_quark(), result,
+ "Playlist error");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
+ GError **error_r)
+{
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ using namespace std::placeholders;
+ const auto f = std::bind(AddToQueue, std::ref(partition), _1, _2);
+ return db->Visit(selection, f, error_r);
+}
diff --git a/src/inotify_queue.h b/src/DatabaseQueue.hxx
index cfc28ebf..bae5b1f0 100644
--- a/src/inotify_queue.h
+++ b/src/DatabaseQueue.hxx
@@ -17,16 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_QUEUE_H
-#define MPD_INOTIFY_QUEUE_H
+#ifndef MPD_DATABASE_QUEUE_HXX
+#define MPD_DATABASE_QUEUE_HXX
-void
-mpd_inotify_queue_init(void);
+#include "gerror.h"
-void
-mpd_inotify_queue_finish(void);
+struct Partition;
+struct DatabaseSelection;
-void
-mpd_inotify_enqueue(char *uri_utf8);
+bool
+AddFromDatabase(Partition &partition, const DatabaseSelection &selection,
+ GError **error_r);
#endif
diff --git a/src/DatabaseRegistry.cxx b/src/DatabaseRegistry.cxx
new file mode 100644
index 00000000..cf01decd
--- /dev/null
+++ b/src/DatabaseRegistry.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseRegistry.hxx"
+#include "db/SimpleDatabasePlugin.hxx"
+#include "db/ProxyDatabasePlugin.hxx"
+
+#include <string.h>
+
+const DatabasePlugin *const database_plugins[] = {
+ &simple_db_plugin,
+#ifdef HAVE_LIBMPDCLIENT
+ &proxy_db_plugin,
+#endif
+ NULL
+};
+
+const DatabasePlugin *
+GetDatabasePluginByName(const char *name)
+{
+ for (auto i = database_plugins; *i != nullptr; ++i)
+ if (strcmp((*i)->name, name) == 0)
+ return *i;
+
+ return nullptr;
+}
diff --git a/src/DatabaseRegistry.hxx b/src/DatabaseRegistry.hxx
new file mode 100644
index 00000000..4be58157
--- /dev/null
+++ b/src/DatabaseRegistry.hxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DATABASE_REGISTRY_HXX
+#define MPD_DATABASE_REGISTRY_HXX
+
+#include "gcc.h"
+
+struct DatabasePlugin;
+
+/**
+ * NULL terminated list of all database plugins which were enabled at
+ * compile time.
+ */
+extern const DatabasePlugin *const database_plugins[];
+
+gcc_pure
+const DatabasePlugin *
+GetDatabasePluginByName(const char *name);
+
+#endif
diff --git a/src/db_save.c b/src/DatabaseSave.cxx
index 4af9d58b..dc87c8dd 100644
--- a/src/db_save.c
+++ b/src/DatabaseSave.cxx
@@ -18,15 +18,15 @@
*/
#include "config.h"
-#include "db_save.h"
-#include "db_lock.h"
-#include "directory.h"
-#include "directory_save.h"
+#include "DatabaseSave.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
+#include "DirectorySave.hxx"
#include "song.h"
-#include "path.h"
-#include "text_file.h"
+#include "TextFile.hxx"
+#include "TagInternal.hxx"
#include "tag.h"
-#include "tag_internal.h"
+#include "fs/Path.hxx"
#include <glib.h>
@@ -55,14 +55,15 @@ db_quark(void)
}
void
-db_save_internal(FILE *fp, const struct directory *music_root)
+db_save_internal(FILE *fp, const Directory *music_root)
{
assert(music_root != NULL);
fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
fprintf(fp, DB_FORMAT_PREFIX "%u\n", DB_FORMAT);
fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
- fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, path_get_fs_charset());
+ fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET,
+ Path::GetFSCharset().c_str());
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (!ignore_tag_items[i])
@@ -74,9 +75,8 @@ db_save_internal(FILE *fp, const struct directory *music_root)
}
bool
-db_load_internal(FILE *fp, struct directory *music_root, GError **error)
+db_load_internal(TextFile &file, Directory *music_root, GError **error)
{
- GString *buffer = g_string_sized_new(1024);
char *line;
int format = 0;
bool found_charset = false, found_version = false;
@@ -86,16 +86,15 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
assert(music_root != NULL);
/* get initial info */
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
g_set_error(error, db_quark(), 0, "Database corrupted");
- g_string_free(buffer, true);
return false;
}
memset(tags, false, sizeof(tags));
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, DIRECTORY_INFO_END) != 0) {
if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
@@ -103,33 +102,30 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
if (found_version) {
g_set_error(error, db_quark(), 0,
"Duplicate version line");
- g_string_free(buffer, true);
return false;
}
found_version = true;
} else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) {
- const char *new_charset, *old_charset;
+ const char *new_charset;
if (found_charset) {
g_set_error(error, db_quark(), 0,
"Duplicate charset line");
- g_string_free(buffer, true);
return false;
}
found_charset = true;
new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1;
- old_charset = path_get_fs_charset();
- if (old_charset != NULL
- && strcmp(new_charset, old_charset)) {
+ const std::string &old_charset = Path::GetFSCharset();
+ if (!old_charset.empty()
+ && strcmp(new_charset, old_charset.c_str())) {
g_set_error(error, db_quark(), 0,
"Existing database has charset "
"\"%s\" instead of \"%s\"; "
"discarding database file",
- new_charset, old_charset);
- g_string_free(buffer, true);
+ new_charset, old_charset.c_str());
return false;
}
} else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
@@ -147,7 +143,6 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
} else {
g_set_error(error, db_quark(), 0,
"Malformed line: %s", line);
- g_string_free(buffer, true);
return false;
}
}
@@ -171,9 +166,8 @@ db_load_internal(FILE *fp, struct directory *music_root, GError **error)
g_debug("reading DB");
db_lock();
- success = directory_load(fp, music_root, buffer, error);
+ success = directory_load(file, music_root, error);
db_unlock();
- g_string_free(buffer, true);
return success;
}
diff --git a/src/DatabaseSave.hxx b/src/DatabaseSave.hxx
new file mode 100644
index 00000000..40048f26
--- /dev/null
+++ b/src/DatabaseSave.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DATABASE_SAVE_HXX
+#define MPD_DATABASE_SAVE_HXX
+
+#include "gerror.h"
+
+#include <stdio.h>
+
+struct Directory;
+class TextFile;
+
+void
+db_save_internal(FILE *file, const Directory *root);
+
+bool
+db_load_internal(TextFile &file, Directory *root, GError **error);
+
+#endif
diff --git a/src/decoder_print.h b/src/DatabaseSelection.cxx
index 31713d5d..bd756f5f 100644
--- a/src/decoder_print.h
+++ b/src/DatabaseSelection.cxx
@@ -17,12 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_PRINT_H
-#define MPD_DECODER_PRINT_H
+#include "DatabaseSelection.hxx"
+#include "SongFilter.hxx"
-struct client;
-
-void
-decoder_list_print(struct client *client);
-
-#endif
+bool
+DatabaseSelection::Match(const song &song) const
+{
+ return filter == nullptr || filter->Match(song);
+}
diff --git a/src/db_selection.h b/src/DatabaseSelection.hxx
index 2cebb490..3a81c01e 100644
--- a/src/db_selection.h
+++ b/src/DatabaseSelection.hxx
@@ -17,17 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DB_SELECTION_H
-#define MPD_DB_SELECTION_H
+#ifndef MPD_DATABASE_SELECTION_HXX
+#define MPD_DATABASE_SELECTION_HXX
#include "gcc.h"
#include <assert.h>
+#include <stddef.h>
-struct directory;
+class SongFilter;
struct song;
-struct db_selection {
+struct DatabaseSelection {
/**
* The base URI of the search (UTF-8). Must not begin or end
* with a slash. NULL or an empty string searches the whole
@@ -39,18 +40,17 @@ struct db_selection {
* Recursively search all sub directories?
*/
bool recursive;
-};
-gcc_nonnull(1,2)
-static inline void
-db_selection_init(struct db_selection *selection,
- const char *uri, bool recursive)
-{
- assert(selection != NULL);
- assert(uri != NULL);
-
- selection->uri = uri;
- selection->recursive = recursive;
-}
+ const SongFilter *filter;
+
+ DatabaseSelection(const char *_uri, bool _recursive,
+ const SongFilter *_filter=nullptr)
+ :uri(_uri), recursive(_recursive), filter(_filter) {
+ assert(uri != NULL);
+ }
+
+ gcc_pure
+ bool Match(const song &song) const;
+};
#endif
diff --git a/src/database.h b/src/DatabaseSimple.hxx
index f877b74d..c387a64f 100644
--- a/src/database.h
+++ b/src/DatabaseSimple.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,79 +17,64 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DATABASE_H
-#define MPD_DATABASE_H
+#ifndef MPD_DATABASE_SIMPLE_HXX
+#define MPD_DATABASE_SIMPLE_HXX
#include "gcc.h"
-
-#include <glib.h>
+#include "gerror.h"
#include <sys/time.h>
-#include <stdbool.h>
struct config_param;
-struct directory;
+struct Directory;
struct db_selection;
struct db_visitor;
/**
- * Initialize the database library.
- *
- * @param path the absolute path of the database file
+ * Check whether the default #SimpleDatabasePlugin is used. This
+ * allows using db_get_root(), db_save(), db_get_mtime() and
+ * db_exists().
*/
bool
-db_init(const struct config_param *path, GError **error_r);
-
-void
-db_finish(void);
+db_is_simple(void);
/**
* Returns the root directory object. Returns NULL if there is no
* configured music directory.
+ *
+ * May only be used if db_is_simple() returns true.
*/
-G_GNUC_PURE
-struct directory *
+gcc_pure
+Directory *
db_get_root(void);
/**
* Caller must lock the #db_mutex.
*/
gcc_nonnull(1)
-G_GNUC_PURE
-struct directory *
+gcc_pure
+Directory *
db_get_directory(const char *name);
-gcc_nonnull(1)
-G_GNUC_PURE
-struct song *
-db_get_song(const char *file);
-
-gcc_nonnull(1,2)
-bool
-db_visit(const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
-gcc_nonnull(1,2)
-bool
-db_walk(const char *uri,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
+/**
+ * May only be used if db_is_simple() returns true.
+ */
bool
db_save(GError **error_r);
-bool
-db_load(GError **error);
-
-G_GNUC_PURE
+/**
+ * May only be used if db_is_simple() returns true.
+ */
+gcc_pure
time_t
db_get_mtime(void);
/**
* Returns true if there is a valid database file on the disk.
+ *
+ * May only be used if db_is_simple() returns true.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool
db_exists(void)
{
diff --git a/src/input_internal.h b/src/DatabaseVisitor.hxx
index d95142e4..c9044141 100644
--- a/src/input_internal.h
+++ b/src/DatabaseVisitor.hxx
@@ -17,27 +17,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_INTERNAL_H
-#define MPD_INPUT_INTERNAL_H
+#ifndef MPD_DATABASE_VISITOR_HXX
+#define MPD_DATABASE_VISITOR_HXX
-#include "check.h"
+#include "gerror.h"
-#include <glib.h>
+#include <functional>
-struct input_stream;
-struct input_plugin;
+struct Directory;
+struct song;
+struct PlaylistInfo;
-void
-input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
- const char *uri, GMutex *mutex, GCond *cond);
+typedef std::function<bool(const Directory &, GError **)> VisitDirectory;
+typedef std::function<bool(struct song &, GError **)> VisitSong;
+typedef std::function<bool(const PlaylistInfo &, const Directory &,
+ GError **)> VisitPlaylist;
-void
-input_stream_deinit(struct input_stream *is);
-
-void
-input_stream_signal_client(struct input_stream *is);
-
-void
-input_stream_set_ready(struct input_stream *is);
+typedef std::function<bool(const char *, GError **)> VisitString;
#endif
diff --git a/src/decoder_api.c b/src/DecoderAPI.cxx
index a45d0f1e..d86b93fb 100644
--- a/src/decoder_api.c
+++ b/src/DecoderAPI.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,14 +19,15 @@
#include "config.h"
#include "decoder_api.h"
-#include "decoder_internal.h"
-#include "decoder_control.h"
-#include "audio_config.h"
-#include "song.h"
-#include "buffer.h"
-#include "pipe.h"
-#include "chunk.h"
+#include "AudioConfig.hxx"
#include "replay_gain_config.h"
+#include "MusicChunk.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicPipe.hxx"
+#include "DecoderControl.hxx"
+#include "DecoderInternal.hxx"
+#include "song.h"
+#include "InputStream.hxx"
#include <glib.h>
@@ -60,10 +61,10 @@ decoder_initialized(struct decoder *decoder,
dc->seekable = seekable;
dc->total_time = total_time;
- decoder_lock(dc);
+ dc->Lock();
dc->state = DECODE_STATE_DECODE;
- g_cond_signal(dc->client_cond);
- decoder_unlock(dc);
+ dc->client_cond.signal();
+ dc->Unlock();
g_debug("audio_format=%s, seekable=%s",
audio_format_to_string(&dc->in_audio_format, &af_string),
@@ -151,7 +152,7 @@ decoder_command_finished(struct decoder *decoder)
{
struct decoder_control *dc = decoder->dc;
- decoder_lock(dc);
+ dc->Lock();
assert(dc->command != DECODE_COMMAND_NONE ||
decoder->initial_seek_running);
@@ -167,7 +168,7 @@ decoder_command_finished(struct decoder *decoder)
decoder->initial_seek_running = false;
decoder->timestamp = dc->start_ms / 1000.;
- decoder_unlock(dc);
+ dc->Unlock();
return;
}
@@ -187,8 +188,8 @@ decoder_command_finished(struct decoder *decoder)
}
dc->command = DECODE_COMMAND_NONE;
- g_cond_signal(dc->client_cond);
- decoder_unlock(dc);
+ dc->client_cond.signal();
+ dc->Unlock();
}
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
@@ -280,7 +281,7 @@ size_t decoder_read(struct decoder *decoder,
if (input_stream_available(is))
break;
- g_cond_wait(is->cond, is->mutex);
+ is->cond.wait(is->mutex);
}
nbytes = input_stream_read(is, buffer, length, &error);
@@ -319,7 +320,7 @@ do_send_tag(struct decoder *decoder, const struct tag *tag)
/* there is a partial chunk - flush it, we want the
tag in a new chunk */
decoder_flush_chunk(decoder);
- g_cond_signal(decoder->dc->client_cond);
+ decoder->dc->client_cond.signal();
}
assert(decoder->chunk == NULL);
@@ -362,11 +363,10 @@ update_stream_tag(struct decoder *decoder, struct input_stream *is)
enum decoder_command
decoder_data(struct decoder *decoder,
struct input_stream *is,
- const void *_data, size_t length,
+ const void *data, size_t length,
uint16_t kbit_rate)
{
struct decoder_control *dc = decoder->dc;
- const char *data = _data;
GError *error = NULL;
enum decoder_command cmd;
@@ -374,9 +374,9 @@ decoder_data(struct decoder *decoder,
assert(dc->pipe != NULL);
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
- decoder_lock(dc);
+ dc->Lock();
cmd = decoder_get_virtual_command(decoder);
- decoder_unlock(dc);
+ dc->Unlock();
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
length == 0)
@@ -402,10 +402,11 @@ decoder_data(struct decoder *decoder,
}
if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) {
- data = pcm_convert(&decoder->conv_state,
- &dc->in_audio_format, data, length,
- &dc->out_audio_format, &length,
- &error);
+ data = decoder->conv_state.Convert(&dc->in_audio_format,
+ data, length,
+ &dc->out_audio_format,
+ &length,
+ &error);
if (data == NULL) {
/* the PCM conversion has failed - stop
playback, since we have no better way to
@@ -417,7 +418,6 @@ decoder_data(struct decoder *decoder,
while (length > 0) {
struct music_chunk *chunk;
- char *dest;
size_t nbytes;
bool full;
@@ -427,14 +427,14 @@ decoder_data(struct decoder *decoder,
return dc->command;
}
- dest = music_chunk_write(chunk, &dc->out_audio_format,
- decoder->timestamp -
- dc->song->start_ms / 1000.0,
- kbit_rate, &nbytes);
+ void *dest = chunk->Write(dc->out_audio_format,
+ decoder->timestamp -
+ dc->song->start_ms / 1000.0,
+ kbit_rate, &nbytes);
if (dest == NULL) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- g_cond_signal(dc->client_cond);
+ dc->client_cond.signal();
continue;
}
@@ -449,14 +449,14 @@ decoder_data(struct decoder *decoder,
/* expand the music pipe chunk */
- full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes);
+ full = chunk->Expand(dc->out_audio_format, nbytes);
if (full) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- g_cond_signal(dc->client_cond);
+ dc->client_cond.signal();
}
- data += nbytes;
+ data = (const uint8_t *)data + nbytes;
length -= nbytes;
decoder->timestamp += (double)nbytes /
@@ -517,11 +517,10 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
return cmd;
}
-float
+void
decoder_replay_gain(struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
- float return_db = 0;
assert(decoder != NULL);
if (replay_gain_info != NULL) {
@@ -530,9 +529,13 @@ decoder_replay_gain(struct decoder *decoder,
serial = 1;
if (REPLAY_GAIN_OFF != replay_gain_mode) {
- return_db = 20.0 * log10f(
+ enum replay_gain_mode rgm = replay_gain_mode;
+ if (rgm != REPLAY_GAIN_ALBUM)
+ rgm = REPLAY_GAIN_TRACK;
+
+ decoder->dc->replay_gain_db = 20.0 * log10f(
replay_gain_tuple_scale(
- &replay_gain_info->tuples[replay_gain_get_real_mode()],
+ &replay_gain_info->tuples[rgm],
replay_gain_preamp, replay_gain_missing_preamp,
replay_gain_limit));
}
@@ -545,23 +548,20 @@ decoder_replay_gain(struct decoder *decoder,
replay gain values affect the following
samples */
decoder_flush_chunk(decoder);
- g_cond_signal(decoder->dc->client_cond);
+ decoder->dc->client_cond.signal();
}
} else
decoder->replay_gain_serial = 0;
-
- return return_db;
}
void
-decoder_mixramp(struct decoder *decoder, float replay_gain_db,
+decoder_mixramp(struct decoder *decoder,
char *mixramp_start, char *mixramp_end)
{
assert(decoder != NULL);
struct decoder_control *dc = decoder->dc;
assert(dc != NULL);
- dc->replay_gain_db = replay_gain_db;
- dc_mixramp_start(dc, mixramp_start);
- dc_mixramp_end(dc, mixramp_end);
+ dc->MixRampStart(mixramp_start);
+ dc->MixRampEnd(mixramp_end);
}
diff --git a/src/DecoderControl.cxx b/src/DecoderControl.cxx
new file mode 100644
index 00000000..c2331105
--- /dev/null
+++ b/src/DecoderControl.cxx
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "song.h"
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "decoder_control"
+
+decoder_control::decoder_control()
+ :thread(nullptr),
+ state(DECODE_STATE_STOP),
+ command(DECODE_COMMAND_NONE),
+ song(nullptr),
+ replay_gain_db(0), replay_gain_prev_db(0),
+ mixramp_start(nullptr), mixramp_end(nullptr),
+ mixramp_prev_end(nullptr) {}
+
+decoder_control::~decoder_control()
+{
+ ClearError();
+
+ if (song != NULL)
+ song_free(song);
+
+ g_free(mixramp_start);
+ g_free(mixramp_end);
+ g_free(mixramp_prev_end);
+}
+
+static void
+dc_command_wait_locked(struct decoder_control *dc)
+{
+ while (dc->command != DECODE_COMMAND_NONE)
+ dc->WaitForDecoder();
+}
+
+static void
+dc_command_locked(struct decoder_control *dc, enum decoder_command cmd)
+{
+ dc->command = cmd;
+ dc->Signal();
+ dc_command_wait_locked(dc);
+}
+
+static void
+dc_command(struct decoder_control *dc, enum decoder_command cmd)
+{
+ dc->Lock();
+ dc->ClearError();
+ dc_command_locked(dc, cmd);
+ dc->Unlock();
+}
+
+static void
+dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
+{
+ dc->Lock();
+
+ dc->command = cmd;
+ dc->Signal();
+
+ dc->Unlock();
+}
+
+bool
+decoder_control::IsCurrentSong(const struct song *_song) const
+{
+ assert(_song != NULL);
+
+ switch (state) {
+ case DECODE_STATE_STOP:
+ case DECODE_STATE_ERROR:
+ return false;
+
+ case DECODE_STATE_START:
+ case DECODE_STATE_DECODE:
+ return song_equals(song, _song);
+ }
+
+ assert(false);
+ return false;
+}
+
+void
+decoder_control::Start(struct song *_song,
+ unsigned _start_ms, unsigned _end_ms,
+ music_buffer *_buffer, music_pipe *_pipe)
+{
+ assert(_song != NULL);
+ assert(_buffer != NULL);
+ assert(_pipe != NULL);
+ assert(music_pipe_empty(_pipe));
+
+ if (song != nullptr)
+ song_free(song);
+
+ song = _song;
+ start_ms = _start_ms;
+ end_ms = _end_ms;
+ buffer = _buffer;
+ pipe = _pipe;
+
+ dc_command(this, DECODE_COMMAND_START);
+}
+
+void
+decoder_control::Stop()
+{
+ Lock();
+
+ if (command != DECODE_COMMAND_NONE)
+ /* Attempt to cancel the current command. If it's too
+ late and the decoder thread is already executing
+ the old command, we'll call STOP again in this
+ function (see below). */
+ dc_command_locked(this, DECODE_COMMAND_STOP);
+
+ if (state != DECODE_STATE_STOP && state != DECODE_STATE_ERROR)
+ dc_command_locked(this, DECODE_COMMAND_STOP);
+
+ Unlock();
+}
+
+bool
+decoder_control::Seek(double where)
+{
+ assert(state != DECODE_STATE_START);
+ assert(where >= 0.0);
+
+ if (state == DECODE_STATE_STOP ||
+ state == DECODE_STATE_ERROR || !seekable)
+ return false;
+
+ seek_where = where;
+ seek_error = false;
+ dc_command(this, DECODE_COMMAND_SEEK);
+
+ return !seek_error;
+}
+
+void
+decoder_control::Quit()
+{
+ assert(thread != nullptr);
+
+ quit = true;
+ dc_command_async(this, DECODE_COMMAND_STOP);
+
+ g_thread_join(thread);
+ thread = nullptr;
+}
+
+void
+decoder_control::MixRampStart(char *_mixramp_start)
+{
+ g_free(mixramp_start);
+ mixramp_start = _mixramp_start;
+}
+
+void
+decoder_control::MixRampEnd(char *_mixramp_end)
+{
+ g_free(mixramp_end);
+ mixramp_end = _mixramp_end;
+}
+
+void
+decoder_control::MixRampPrevEnd(char *_mixramp_prev_end)
+{
+ g_free(mixramp_prev_end);
+ mixramp_prev_end = _mixramp_prev_end;
+}
diff --git a/src/DecoderControl.hxx b/src/DecoderControl.hxx
new file mode 100644
index 00000000..c2d7b33a
--- /dev/null
+++ b/src/DecoderControl.hxx
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_CONTROL_HXX
+#define MPD_DECODER_CONTROL_HXX
+
+#include "decoder_command.h"
+#include "audio_format.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+
+enum decoder_state {
+ DECODE_STATE_STOP = 0,
+ DECODE_STATE_START,
+ DECODE_STATE_DECODE,
+
+ /**
+ * The last "START" command failed, because there was an I/O
+ * error or because no decoder was able to decode the file.
+ * This state will only come after START; once the state has
+ * turned to DECODE, by definition no such error can occur.
+ */
+ DECODE_STATE_ERROR,
+};
+
+struct decoder_control {
+ /** the handle of the decoder thread, or NULL if the decoder
+ thread isn't running */
+ GThread *thread;
+
+ /**
+ * This lock protects #state and #command.
+ */
+ mutable Mutex mutex;
+
+ /**
+ * Trigger this object after you have modified #command. This
+ * is also used by the decoder thread to notify the caller
+ * when it has finished a command.
+ */
+ Cond cond;
+
+ /**
+ * The trigger of this object's client. It is signalled
+ * whenever an event occurs.
+ */
+ Cond client_cond;
+
+ enum decoder_state state;
+ enum decoder_command command;
+
+ /**
+ * The error that occurred in the decoder thread. This
+ * attribute is only valid if #state is #DECODE_STATE_ERROR.
+ * The object must be freed when this object transitions to
+ * any other state (usually #DECODE_STATE_START).
+ */
+ GError *error;
+
+ bool quit;
+ bool seek_error;
+ bool seekable;
+ double seek_where;
+
+ /** the format of the song file */
+ struct audio_format in_audio_format;
+
+ /** the format being sent to the music pipe */
+ struct audio_format out_audio_format;
+
+ /**
+ * The song currently being decoded. This attribute is set by
+ * the player thread, when it sends the #DECODE_COMMAND_START
+ * command.
+ *
+ * This is a duplicate, and must be freed when this attribute
+ * is cleared.
+ */
+ struct song *song;
+
+ /**
+ * The initial seek position (in milliseconds), e.g. to the
+ * start of a sub-track described by a CUE file.
+ *
+ * This attribute is set by dc_start().
+ */
+ unsigned start_ms;
+
+ /**
+ * The decoder will stop when it reaches this position (in
+ * milliseconds). 0 means don't stop before the end of the
+ * file.
+ *
+ * This attribute is set by dc_start().
+ */
+ unsigned end_ms;
+
+ float total_time;
+
+ /** the #music_chunk allocator */
+ struct music_buffer *buffer;
+
+ /**
+ * The destination pipe for decoded chunks. The caller thread
+ * owns this object, and is responsible for freeing it.
+ */
+ struct music_pipe *pipe;
+
+ float replay_gain_db;
+ float replay_gain_prev_db;
+ char *mixramp_start;
+ char *mixramp_end;
+ char *mixramp_prev_end;
+
+ decoder_control();
+ ~decoder_control();
+
+ /**
+ * Locks the object.
+ */
+ void Lock() const {
+ mutex.lock();
+ }
+
+ /**
+ * Unlocks the object.
+ */
+ void Unlock() const {
+ mutex.unlock();
+ }
+
+ /**
+ * Signals the object. This function is only valid in the
+ * player thread. The object should be locked prior to
+ * calling this function.
+ */
+ void Signal() {
+ cond.signal();
+ }
+
+ /**
+ * Waits for a signal on the #decoder_control object. This function
+ * is only valid in the decoder thread. The object must be locked
+ * prior to calling this function.
+ */
+ void Wait() {
+ cond.wait(mutex);
+ }
+
+ /**
+ * Waits for a signal from the decoder thread. This object
+ * must be locked prior to calling this function. This method
+ * is only valid in the player thread.
+ */
+ void WaitForDecoder() {
+ client_cond.wait(mutex);
+ }
+
+ bool IsIdle() const {
+ return state == DECODE_STATE_STOP ||
+ state == DECODE_STATE_ERROR;
+ }
+
+ gcc_pure
+ bool LockIsIdle() const {
+ Lock();
+ bool result = IsIdle();
+ Unlock();
+ return result;
+ }
+
+ bool IsStarting() const {
+ return state == DECODE_STATE_START;
+ }
+
+ gcc_pure
+ bool LockIsStarting() const {
+ Lock();
+ bool result = IsStarting();
+ Unlock();
+ return result;
+ }
+
+ bool HasFailed() const {
+ assert(command == DECODE_COMMAND_NONE);
+
+ return state == DECODE_STATE_ERROR;
+ }
+
+ gcc_pure
+ bool LockHasFailed() const {
+ Lock();
+ bool result = HasFailed();
+ Unlock();
+ return result;
+ }
+
+ /**
+ * Checks whether an error has occurred, and if so, returns a newly
+ * allocated copy of the #GError object.
+ *
+ * Caller must lock the object.
+ */
+ GError *GetError() const {
+ assert(command == DECODE_COMMAND_NONE);
+ assert(state != DECODE_STATE_ERROR || error != nullptr);
+
+ return state == DECODE_STATE_ERROR
+ ? g_error_copy(error)
+ : nullptr;
+ }
+
+ /**
+ * Like dc_get_error(), but locks and unlocks the object.
+ */
+ GError *LockGetError() const {
+ Lock();
+ GError *result = GetError();
+ Unlock();
+ return result;
+ }
+
+ /**
+ * Clear the error condition and free the #GError object (if any).
+ *
+ * Caller must lock the object.
+ */
+ void ClearError() {
+ if (state == DECODE_STATE_ERROR) {
+ g_error_free(error);
+ state = DECODE_STATE_STOP;
+ }
+ }
+
+ /**
+ * Check if the specified song is currently being decoded. If the
+ * decoder is not running currently (or being started), then this
+ * function returns false in any case.
+ *
+ * Caller must lock the object.
+ */
+ gcc_pure
+ bool IsCurrentSong(const struct song *_song) const;
+
+ gcc_pure
+ bool LockIsCurrentSong(const struct song *_song) const {
+ Lock();
+ const bool result = IsCurrentSong(_song);
+ Unlock();
+ return result;
+ }
+
+ /**
+ * Start the decoder.
+ *
+ * @param song the song to be decoded; the given instance will be
+ * owned and freed by the decoder
+ * @param start_ms see #decoder_control
+ * @param end_ms see #decoder_control
+ * @param pipe the pipe which receives the decoded chunks (owned by
+ * the caller)
+ */
+ void Start(struct song *song, unsigned start_ms, unsigned end_ms,
+ music_buffer *buffer, music_pipe *pipe);
+
+ void Stop();
+
+ bool Seek(double where);
+
+ void Quit();
+
+ void MixRampStart(char *_mixramp_start);
+ void MixRampEnd(char *_mixramp_end);
+ void MixRampPrevEnd(char *_mixramp_prev_end);
+};
+
+#endif
diff --git a/src/decoder_internal.c b/src/DecoderInternal.cxx
index bc349f2f..e390fdfd 100644
--- a/src/decoder_internal.c
+++ b/src/DecoderInternal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,30 @@
*/
#include "config.h"
-#include "decoder_internal.h"
-#include "decoder_control.h"
-#include "pipe.h"
-#include "input_stream.h"
-#include "buffer.h"
-#include "chunk.h"
+#include "DecoderInternal.hxx"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "tag.h"
#include <assert.h>
+decoder::~decoder()
+{
+ /* caller must flush the chunk */
+ assert(chunk == nullptr);
+
+ if (song_tag != nullptr)
+ tag_free(song_tag);
+
+ if (stream_tag != nullptr)
+ tag_free(stream_tag);
+
+ if (decoder_tag != nullptr)
+ tag_free(decoder_tag);
+}
+
/**
* All chunks are full of decoded data; wait for the player to free
* one.
@@ -39,8 +54,8 @@ need_chunks(struct decoder_control *dc, bool do_wait)
return dc->command;
if (do_wait) {
- decoder_wait(dc);
- g_cond_signal(dc->client_cond);
+ dc->Wait();
+ dc->client_cond.signal();
return dc->command;
}
@@ -71,9 +86,9 @@ decoder_get_chunk(struct decoder *decoder)
return decoder->chunk;
}
- decoder_lock(dc);
+ dc->Lock();
cmd = need_chunks(dc, true);
- decoder_unlock(dc);
+ dc->Unlock();
} while (cmd == DECODE_COMMAND_NONE);
return NULL;
@@ -87,7 +102,7 @@ decoder_flush_chunk(struct decoder *decoder)
assert(decoder != NULL);
assert(decoder->chunk != NULL);
- if (music_chunk_is_empty(decoder->chunk))
+ if (decoder->chunk->IsEmpty())
music_buffer_return(dc->buffer, decoder->chunk);
else
music_pipe_push(dc->pipe, decoder->chunk);
diff --git a/src/decoder_internal.h b/src/DecoderInternal.hxx
index d89e68cf..3423e3f9 100644
--- a/src/decoder_internal.h
+++ b/src/DecoderInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_INTERNAL_H
-#define MPD_DECODER_INTERNAL_H
+#ifndef MPD_DECODER_INTERNAL_HXX
+#define MPD_DECODER_INTERNAL_HXX
#include "decoder_command.h"
-#include "pcm_convert.h"
+#include "PcmConvert.hxx"
#include "replay_gain_info.h"
struct input_stream;
@@ -29,7 +29,7 @@ struct input_stream;
struct decoder {
struct decoder_control *dc;
- struct pcm_convert_state conv_state;
+ PcmConvert conv_state;
/**
* The time stamp of the next data chunk, in seconds.
@@ -80,6 +80,20 @@ struct decoder {
* has changed since the last check.
*/
unsigned replay_gain_serial;
+
+ decoder(decoder_control *_dc, bool _initial_seek_pending,
+ struct tag *_tag)
+ :dc(_dc),
+ timestamp(0),
+ initial_seek_pending(_initial_seek_pending),
+ initial_seek_running(false),
+ seeking(false),
+ song_tag(_tag), stream_tag(nullptr), decoder_tag(nullptr),
+ chunk(nullptr),
+ replay_gain_serial(0) {
+ }
+
+ ~decoder();
};
/**
diff --git a/src/decoder_list.c b/src/DecoderList.cxx
index 177b632a..da9c3fcc 100644
--- a/src/decoder_list.c
+++ b/src/DecoderList.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,19 @@
*/
#include "config.h"
-#include "decoder_list.h"
+#include "DecoderList.hxx"
#include "decoder_plugin.h"
-#include "utils.h"
#include "conf.h"
#include "mpd_error.h"
#include "decoder/pcm_decoder_plugin.h"
#include "decoder/dsdiff_decoder_plugin.h"
#include "decoder/dsf_decoder_plugin.h"
+#include "decoder/FLACDecoderPlugin.h"
+#include "decoder/OpusDecoderPlugin.h"
+#include "decoder/VorbisDecoderPlugin.h"
+#include "decoder/AdPlugDecoderPlugin.h"
+#include "decoder/WavpackDecoderPlugin.hxx"
+#include "decoder/FfmpegDecoderPlugin.hxx"
#include <glib.h>
@@ -33,21 +38,15 @@
extern const struct decoder_plugin mad_decoder_plugin;
extern const struct decoder_plugin mpg123_decoder_plugin;
-extern const struct decoder_plugin vorbis_decoder_plugin;
-extern const struct decoder_plugin flac_decoder_plugin;
-extern const struct decoder_plugin oggflac_decoder_plugin;
extern const struct decoder_plugin sndfile_decoder_plugin;
extern const struct decoder_plugin audiofile_decoder_plugin;
-extern const struct decoder_plugin mp4ff_decoder_plugin;
extern const struct decoder_plugin faad_decoder_plugin;
extern const struct decoder_plugin mpcdec_decoder_plugin;
-extern const struct decoder_plugin wavpack_decoder_plugin;
extern const struct decoder_plugin modplug_decoder_plugin;
extern const struct decoder_plugin mikmod_decoder_plugin;
extern const struct decoder_plugin sidplay_decoder_plugin;
extern const struct decoder_plugin wildmidi_decoder_plugin;
extern const struct decoder_plugin fluidsynth_decoder_plugin;
-extern const struct decoder_plugin ffmpeg_decoder_plugin;
extern const struct decoder_plugin gme_decoder_plugin;
const struct decoder_plugin *const decoder_plugins[] = {
@@ -66,6 +65,9 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_FLAC
&flac_decoder_plugin,
#endif
+#ifdef HAVE_OPUS
+ &opus_decoder_plugin,
+#endif
#ifdef ENABLE_SNDFILE
&sndfile_decoder_plugin,
#endif
@@ -77,9 +79,6 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_FAAD
&faad_decoder_plugin,
#endif
-#ifdef HAVE_MP4
- &mp4ff_decoder_plugin,
-#endif
#ifdef HAVE_MPCDEC
&mpcdec_decoder_plugin,
#endif
@@ -101,6 +100,9 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef ENABLE_FLUIDSYNTH
&fluidsynth_decoder_plugin,
#endif
+#ifdef HAVE_ADPLUG
+ &adplug_decoder_plugin,
+#endif
#ifdef HAVE_FFMPEG
&ffmpeg_decoder_plugin,
#endif
diff --git a/src/decoder_list.h b/src/DecoderList.hxx
index d0a6ade7..8dab8724 100644
--- a/src/decoder_list.h
+++ b/src/DecoderList.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_LIST_H
-#define MPD_DECODER_LIST_H
-
-#include <stdbool.h>
+#ifndef MPD_DECODER_LIST_HXX
+#define MPD_DECODER_LIST_HXX
struct decoder_plugin;
diff --git a/src/decoder_print.c b/src/DecoderPrint.cxx
index e14477ed..719a499e 100644
--- a/src/decoder_print.c
+++ b/src/DecoderPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,15 @@
*/
#include "config.h"
-#include "decoder_print.h"
-#include "decoder_list.h"
+#include "DecoderPrint.hxx"
+#include "DecoderList.hxx"
#include "decoder_plugin.h"
-#include "client.h"
+#include "Client.hxx"
#include <assert.h>
static void
-decoder_plugin_print(struct client *client,
+decoder_plugin_print(Client *client,
const struct decoder_plugin *plugin)
{
const char *const*p;
@@ -46,7 +46,7 @@ decoder_plugin_print(struct client *client,
}
void
-decoder_list_print(struct client *client)
+decoder_list_print(Client *client)
{
decoder_plugins_for_each_enabled(plugin)
decoder_plugin_print(client, plugin);
diff --git a/src/DecoderPrint.hxx b/src/DecoderPrint.hxx
new file mode 100644
index 00000000..d94ba2ce
--- /dev/null
+++ b/src/DecoderPrint.hxx
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_PRINT_HXX
+#define MPD_DECODER_PRINT_HXX
+
+class Client;
+
+void
+decoder_list_print(Client *client);
+
+#endif
diff --git a/src/decoder_thread.c b/src/DecoderThread.cxx
index af80ed45..b7b0bf78 100644
--- a/src/decoder_thread.c
+++ b/src/DecoderThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,24 @@
*/
#include "config.h"
-#include "decoder_thread.h"
-#include "decoder_control.h"
-#include "decoder_internal.h"
-#include "decoder_list.h"
+#include "DecoderThread.hxx"
+#include "DecoderControl.hxx"
+#include "DecoderInternal.hxx"
+#include "decoder_error.h"
#include "decoder_plugin.h"
-#include "decoder_api.h"
-#include "replay_gain_ape.h"
-#include "input_stream.h"
-#include "pipe.h"
#include "song.h"
+#include "mpd_error.h"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+#include "decoder_api.h"
#include "tag.h"
-#include "mapper.h"
-#include "path.h"
+#include "InputStream.hxx"
+#include "DecoderList.hxx"
+
+extern "C" {
+#include "replay_gain_ape.h"
#include "uri.h"
-#include "mpd_error.h"
+}
#include <glib.h>
@@ -55,7 +58,7 @@ decoder_command_finished_locked(struct decoder_control *dc)
dc->command = DECODE_COMMAND_NONE;
- g_cond_signal(dc->client_cond);
+ dc->client_cond.signal();
}
/**
@@ -88,18 +91,18 @@ decoder_input_stream_open(struct decoder_control *dc, const char *uri)
/* wait for the input stream to become ready; its metadata
will be available then */
- decoder_lock(dc);
+ dc->Lock();
input_stream_update(is);
while (!is->ready &&
dc->command != DECODE_COMMAND_STOP) {
- decoder_wait(dc);
+ dc->Wait();
input_stream_update(is);
}
if (!input_stream_check(is, &error)) {
- decoder_unlock(dc);
+ dc->Unlock();
g_warning("%s", error->message);
g_error_free(error);
@@ -107,7 +110,7 @@ decoder_input_stream_open(struct decoder_control *dc, const char *uri)
return NULL;
}
- decoder_unlock(dc);
+ dc->Unlock();
return is;
}
@@ -134,11 +137,11 @@ decoder_stream_decode(const struct decoder_plugin *plugin,
/* rewind the stream, so each plugin gets a fresh start */
input_stream_seek(input_stream, 0, SEEK_SET, NULL);
- decoder_unlock(decoder->dc);
+ decoder->dc->Unlock();
decoder_plugin_stream_decode(plugin, decoder, input_stream);
- decoder_lock(decoder->dc);
+ decoder->dc->Lock();
assert(decoder->dc->state == DECODE_STATE_START ||
decoder->dc->state == DECODE_STATE_DECODE);
@@ -164,11 +167,11 @@ decoder_file_decode(const struct decoder_plugin *plugin,
if (decoder->dc->command == DECODE_COMMAND_STOP)
return true;
- decoder_unlock(decoder->dc);
+ decoder->dc->Unlock();
decoder_plugin_file_decode(plugin, decoder, path);
- decoder_lock(decoder->dc);
+ decoder->dc->Lock();
assert(decoder->dc->state == DECODE_STATE_START ||
decoder->dc->state == DECODE_STATE_DECODE);
@@ -182,12 +185,7 @@ decoder_file_decode(const struct decoder_plugin *plugin,
static inline gpointer
deconst_plugin(const struct decoder_plugin *plugin)
{
- union {
- const struct decoder_plugin *in;
- gpointer out;
- } u = { .in = plugin };
-
- return u.out;
+ return const_cast<struct decoder_plugin *>(plugin);
}
/**
@@ -204,10 +202,11 @@ decoder_run_stream_mime_type(struct decoder *decoder, struct input_stream *is,
const struct decoder_plugin *plugin;
unsigned int next = 0;
- if (is->mime == NULL)
+ if (is->mime.empty())
return false;
- while ((plugin = decoder_plugin_from_mime_type(is->mime, next++))) {
+ while ((plugin = decoder_plugin_from_mime_type(is->mime.c_str(),
+ next++))) {
if (plugin->stream_decode == NULL)
continue;
@@ -282,15 +281,15 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
struct input_stream *input_stream;
bool success;
- decoder_unlock(dc);
+ dc->Unlock();
input_stream = decoder_input_stream_open(dc, uri);
if (input_stream == NULL) {
- decoder_lock(dc);
+ dc->Lock();
return false;
}
- decoder_lock(dc);
+ dc->Lock();
GSList *tried = NULL;
@@ -307,9 +306,9 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
g_slist_free(tried);
- decoder_unlock(dc);
+ dc->Unlock();
input_stream_close(input_stream);
- decoder_lock(dc);
+ dc->Lock();
return success;
}
@@ -339,18 +338,18 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
if (suffix == NULL)
return false;
- decoder_unlock(dc);
+ dc->Unlock();
decoder_load_replay_gain(decoder, path_fs);
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
if (plugin->file_decode != NULL) {
- decoder_lock(dc);
+ dc->Lock();
if (decoder_file_decode(plugin, decoder, path_fs))
return true;
- decoder_unlock(dc);
+ dc->Unlock();
} else if (plugin->stream_decode != NULL) {
struct input_stream *input_stream;
bool success;
@@ -359,23 +358,23 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
if (input_stream == NULL)
continue;
- decoder_lock(dc);
+ dc->Lock();
success = decoder_stream_decode(plugin, decoder,
input_stream);
- decoder_unlock(dc);
+ dc->Unlock();
input_stream_close(input_stream);
if (success) {
- decoder_lock(dc);
+ dc->Lock();
return true;
}
}
}
- decoder_lock(dc);
+ dc->Lock();
return false;
}
@@ -383,69 +382,66 @@ static void
decoder_run_song(struct decoder_control *dc,
const struct song *song, const char *uri)
{
- struct decoder decoder = {
- .dc = dc,
- .initial_seek_pending = dc->start_ms > 0,
- .initial_seek_running = false,
- };
+ decoder decoder(dc, dc->start_ms > 0,
+ song->tag != NULL && song_is_file(song)
+ ? tag_dup(song->tag) : nullptr);
int ret;
- decoder.timestamp = 0.0;
- decoder.seeking = false;
- decoder.song_tag = song->tag != NULL && song_is_file(song)
- ? tag_dup(song->tag) : NULL;
- decoder.stream_tag = NULL;
- decoder.decoder_tag = NULL;
- decoder.chunk = NULL;
-
dc->state = DECODE_STATE_START;
decoder_command_finished_locked(dc);
- pcm_convert_init(&decoder.conv_state);
-
ret = song_is_file(song)
? decoder_run_file(&decoder, uri)
: decoder_run_stream(&decoder, uri);
- decoder_unlock(dc);
-
- pcm_convert_deinit(&decoder.conv_state);
+ dc->Unlock();
/* flush the last chunk */
if (decoder.chunk != NULL)
decoder_flush_chunk(&decoder);
- if (decoder.song_tag != NULL)
- tag_free(decoder.song_tag);
+ dc->Lock();
- if (decoder.stream_tag != NULL)
- tag_free(decoder.stream_tag);
+ if (ret)
+ dc->state = DECODE_STATE_STOP;
+ else {
+ dc->state = DECODE_STATE_ERROR;
- if (decoder.decoder_tag != NULL)
- tag_free(decoder.decoder_tag);
+ const char *error_uri = song->uri;
+ char *allocated = uri_remove_auth(error_uri);
+ if (allocated != NULL)
+ error_uri = allocated;
- decoder_lock(dc);
+ dc->error = g_error_new(decoder_quark(), 0,
+ "Failed to decode %s", error_uri);
+ g_free(allocated);
+ }
- dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
+ dc->client_cond.signal();
}
static void
decoder_run(struct decoder_control *dc)
{
+ dc->ClearError();
+
const struct song *song = dc->song;
char *uri;
assert(song != NULL);
if (song_is_file(song))
- uri = map_song_fs(song);
+ uri = map_song_fs(song).Steal();
else
uri = song_get_uri(song);
if (uri == NULL) {
dc->state = DECODE_STATE_ERROR;
+ dc->error = g_error_new(decoder_quark(), 0,
+ "Failed to map song");
+
decoder_command_finished_locked(dc);
return;
}
@@ -458,9 +454,9 @@ decoder_run(struct decoder_control *dc)
static gpointer
decoder_task(gpointer arg)
{
- struct decoder_control *dc = arg;
+ struct decoder_control *dc = (struct decoder_control *)arg;
- decoder_lock(dc);
+ dc->Lock();
do {
assert(dc->state == DECODE_STATE_STOP ||
@@ -468,8 +464,8 @@ decoder_task(gpointer arg)
switch (dc->command) {
case DECODE_COMMAND_START:
- dc_mixramp_start(dc, NULL);
- dc_mixramp_prev_end(dc, dc->mixramp_end);
+ dc->MixRampStart(nullptr);
+ dc->MixRampPrevEnd(dc->mixramp_end);
dc->mixramp_end = NULL; /* Don't free, it's copied above. */
dc->replay_gain_prev_db = dc->replay_gain_db;
dc->replay_gain_db = 0;
@@ -485,12 +481,12 @@ decoder_task(gpointer arg)
break;
case DECODE_COMMAND_NONE:
- decoder_wait(dc);
+ dc->Wait();
break;
}
} while (dc->command != DECODE_COMMAND_NONE || !dc->quit);
- decoder_unlock(dc);
+ dc->Unlock();
return NULL;
}
diff --git a/src/decoder_thread.h b/src/DecoderThread.hxx
index 78f12a54..8efaa2fc 100644
--- a/src/decoder_thread.h
+++ b/src/DecoderThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DECODER_THREAD_H
-#define MPD_DECODER_THREAD_H
+#ifndef MPD_DECODER_THREAD_HXX
+#define MPD_DECODER_THREAD_HXX
struct decoder_control;
diff --git a/src/despotify_utils.c b/src/DespotifyUtils.cxx
index 7555d105..c9a1edf0 100644
--- a/src/despotify_utils.c
+++ b/src/DespotifyUtils.cxx
@@ -1,9 +1,31 @@
-#include <glib.h>
-#include <despotify.h>
-
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "DespotifyUtils.hxx"
#include "tag.h"
#include "conf.h"
-#include "despotify_utils.h"
+
+#include <glib.h>
+
+extern "C" {
+#include <despotify.h>
+}
static struct despotify_session *g_session;
static void (*registered_callbacks[8])(struct despotify_session *,
@@ -97,25 +119,26 @@ struct despotify_session *mpd_despotify_get_session(void)
if (user == NULL || passwd == NULL) {
g_debug("disabling despotify because account is not configured");
- return NULL;
+ return nullptr;
}
- if (!despotify_init()) {
+
+ if (!despotify_init()) {
g_debug("Can't initialize despotify\n");
- return false;
+ return nullptr;
}
g_session = despotify_init_client(callback, NULL,
- high_bitrate, true);
+ high_bitrate, true);
if (!g_session) {
g_debug("Can't initialize despotify client\n");
- return false;
+ return nullptr;
}
- if (!despotify_authenticate(g_session, user, passwd)) {
- g_debug("Can't authenticate despotify session\n");
- despotify_exit(g_session);
- return false;
- }
+ if (!despotify_authenticate(g_session, user, passwd)) {
+ g_debug("Can't authenticate despotify session\n");
+ despotify_exit(g_session);
+ return nullptr;
+ }
return g_session;
}
diff --git a/src/despotify_utils.h b/src/DespotifyUtils.hxx
index 7e35edc3..7e35edc3 100644
--- a/src/despotify_utils.h
+++ b/src/DespotifyUtils.hxx
diff --git a/src/Directory.cxx b/src/Directory.cxx
new file mode 100644
index 00000000..2b1a34cb
--- /dev/null
+++ b/src/Directory.cxx
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "Directory.hxx"
+#include "SongFilter.hxx"
+#include "PlaylistVector.hxx"
+#include "DatabaseLock.hxx"
+
+extern "C" {
+#include "song.h"
+#include "song_sort.h"
+#include "util/list_sort.h"
+}
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+inline Directory *
+Directory::Allocate(const char *path)
+{
+ assert(path != NULL);
+
+ const size_t path_size = strlen(path) + 1;
+ Directory *directory =
+ (Directory *)g_malloc0(sizeof(*directory)
+ - sizeof(directory->path)
+ + path_size);
+ new(directory) Directory(path);
+
+ return directory;
+}
+
+Directory::Directory()
+{
+ INIT_LIST_HEAD(&children);
+ INIT_LIST_HEAD(&songs);
+
+ path[0] = 0;
+}
+
+Directory::Directory(const char *_path)
+{
+ INIT_LIST_HEAD(&children);
+ INIT_LIST_HEAD(&songs);
+
+ strcpy(path, _path);
+}
+
+Directory::~Directory()
+{
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, this)
+ song_free(song);
+
+ Directory *child, *n;
+ directory_for_each_child_safe(child, n, this)
+ child->Free();
+}
+
+Directory *
+Directory::NewGeneric(const char *path, Directory *parent)
+{
+ assert(path != NULL);
+ assert((*path == 0) == (parent == NULL));
+
+ Directory *directory = Allocate(path);
+
+ directory->parent = parent;
+
+ return directory;
+}
+
+void
+Directory::Free()
+{
+ this->Directory::~Directory();
+ g_free(this);
+}
+
+void
+Directory::Delete()
+{
+ assert(holding_db_lock());
+ assert(parent != nullptr);
+
+ list_del(&siblings);
+ Free();
+}
+
+const char *
+Directory::GetName() const
+{
+ assert(!IsRoot());
+ assert(path != nullptr);
+
+ const char *slash = strrchr(path, '/');
+ assert((slash == nullptr) == parent->IsRoot());
+
+ return slash != NULL
+ ? slash + 1
+ : path;
+}
+
+Directory *
+Directory::CreateChild(const char *name_utf8)
+{
+ assert(holding_db_lock());
+ assert(name_utf8 != NULL);
+ assert(*name_utf8 != 0);
+
+ char *allocated;
+ const char *path_utf8;
+ if (IsRoot()) {
+ allocated = NULL;
+ path_utf8 = name_utf8;
+ } else {
+ allocated = g_strconcat(GetPath(),
+ "/", name_utf8, NULL);
+ path_utf8 = allocated;
+ }
+
+ Directory *child = NewGeneric(path_utf8, this);
+ g_free(allocated);
+
+ list_add_tail(&child->siblings, &children);
+ return child;
+}
+
+const Directory *
+Directory::FindChild(const char *name) const
+{
+ assert(holding_db_lock());
+
+ const Directory *child;
+ directory_for_each_child(child, this)
+ if (strcmp(child->GetName(), name) == 0)
+ return child;
+
+ return NULL;
+}
+
+void
+Directory::PruneEmpty()
+{
+ assert(holding_db_lock());
+
+ Directory *child, *n;
+ directory_for_each_child_safe(child, n, this) {
+ child->PruneEmpty();
+
+ if (child->IsEmpty())
+ child->Delete();
+ }
+}
+
+Directory *
+Directory::LookupDirectory(const char *uri)
+{
+ assert(holding_db_lock());
+ assert(uri != NULL);
+
+ if (isRootDirectory(uri))
+ return this;
+
+ char *duplicated = g_strdup(uri), *name = duplicated;
+
+ Directory *d = this;
+ while (1) {
+ char *slash = strchr(name, '/');
+ if (slash == name) {
+ d = NULL;
+ break;
+ }
+
+ if (slash != NULL)
+ *slash = '\0';
+
+ d = d->FindChild(name);
+ if (d == NULL || slash == NULL)
+ break;
+
+ name = slash + 1;
+ }
+
+ g_free(duplicated);
+
+ return d;
+}
+
+void
+Directory::AddSong(struct song *song)
+{
+ assert(holding_db_lock());
+ assert(song != NULL);
+ assert(song->parent == this);
+
+ list_add_tail(&song->siblings, &songs);
+}
+
+void
+Directory::RemoveSong(struct song *song)
+{
+ assert(holding_db_lock());
+ assert(song != NULL);
+ assert(song->parent == this);
+
+ list_del(&song->siblings);
+}
+
+const song *
+Directory::FindSong(const char *name_utf8) const
+{
+ assert(holding_db_lock());
+ assert(name_utf8 != NULL);
+
+ struct song *song;
+ directory_for_each_song(song, this) {
+ assert(song->parent == this);
+
+ if (strcmp(song->uri, name_utf8) == 0)
+ return song;
+ }
+
+ return NULL;
+}
+
+struct song *
+Directory::LookupSong(const char *uri)
+{
+ char *duplicated, *base;
+
+ assert(holding_db_lock());
+ assert(uri != NULL);
+
+ duplicated = g_strdup(uri);
+ base = strrchr(duplicated, '/');
+
+ Directory *d = this;
+ if (base != NULL) {
+ *base++ = 0;
+ d = d->LookupDirectory(duplicated);
+ if (d == nullptr) {
+ g_free(duplicated);
+ return NULL;
+ }
+ } else
+ base = duplicated;
+
+ struct song *song = d->FindSong(base);
+ assert(song == NULL || song->parent == d);
+
+ g_free(duplicated);
+ return song;
+
+}
+
+static int
+directory_cmp(G_GNUC_UNUSED void *priv,
+ struct list_head *_a, struct list_head *_b)
+{
+ const Directory *a = (const Directory *)_a;
+ const Directory *b = (const Directory *)_b;
+ return g_utf8_collate(a->path, b->path);
+}
+
+void
+Directory::Sort()
+{
+ assert(holding_db_lock());
+
+ list_sort(NULL, &children, directory_cmp);
+ song_list_sort(&songs);
+
+ Directory *child;
+ directory_for_each_child(child, this)
+ child->Sort();
+}
+
+bool
+Directory::Walk(bool recursive, const SongFilter *filter,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ assert(error_r == NULL || *error_r == NULL);
+
+ if (visit_song) {
+ struct song *song;
+ directory_for_each_song(song, this)
+ if ((filter == nullptr || filter->Match(*song)) &&
+ !visit_song(*song, error_r))
+ return false;
+ }
+
+ if (visit_playlist) {
+ for (const PlaylistInfo &p : playlists)
+ if (!visit_playlist(p, *this, error_r))
+ return false;
+ }
+
+ Directory *child;
+ directory_for_each_child(child, this) {
+ if (visit_directory &&
+ !visit_directory(*child, error_r))
+ return false;
+
+ if (recursive &&
+ !child->Walk(recursive, filter,
+ visit_directory, visit_song, visit_playlist,
+ error_r))
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/Directory.hxx b/src/Directory.hxx
new file mode 100644
index 00000000..8fa5c235
--- /dev/null
+++ b/src/Directory.hxx
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DIRECTORY_HXX
+#define MPD_DIRECTORY_HXX
+
+#include "check.h"
+#include "util/list.h"
+#include "gcc.h"
+#include "DatabaseVisitor.hxx"
+#include "PlaylistVector.hxx"
+#include "gerror.h"
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define DEVICE_INARCHIVE (dev_t)(-1)
+#define DEVICE_CONTAINER (dev_t)(-2)
+
+#define directory_for_each_child(pos, directory) \
+ list_for_each_entry(pos, &directory->children, siblings)
+
+#define directory_for_each_child_safe(pos, n, directory) \
+ list_for_each_entry_safe(pos, n, &directory->children, siblings)
+
+#define directory_for_each_song(pos, directory) \
+ list_for_each_entry(pos, &directory->songs, siblings)
+
+#define directory_for_each_song_safe(pos, n, directory) \
+ list_for_each_entry_safe(pos, n, &directory->songs, siblings)
+
+struct song;
+struct db_visitor;
+class SongFilter;
+
+struct Directory {
+ /**
+ * Pointers to the siblings of this directory within the
+ * parent directory. It is unused (undefined) in the root
+ * directory.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head siblings;
+
+ /**
+ * A doubly linked list of child directories.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head children;
+
+ /**
+ * A doubly linked list of songs within this directory.
+ *
+ * This attribute is protected with the global #db_mutex.
+ * Read access in the update thread does not need protection.
+ */
+ struct list_head songs;
+
+ PlaylistVector playlists;
+
+ Directory *parent;
+ time_t mtime;
+ ino_t inode;
+ dev_t device;
+ bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
+ char path[sizeof(long)];
+
+protected:
+ Directory(const char *path);
+
+ gcc_malloc gcc_nonnull_all
+ static Directory *Allocate(const char *path);
+
+public:
+ /**
+ * Default constructor, needed for #detached_root.
+ */
+ Directory();
+ ~Directory();
+
+ /**
+ * Generic constructor for #Directory object.
+ */
+ gcc_malloc
+ static Directory *NewGeneric(const char *path_utf8, Directory *parent);
+
+ /**
+ * Create a new root #Directory object.
+ */
+ gcc_malloc
+ static Directory *NewRoot() {
+ return NewGeneric("", nullptr);
+ }
+
+ /**
+ * Free this #Directory object (and the whole object tree within it),
+ * assuming it was already removed from the parent.
+ */
+ void Free();
+
+ /**
+ * Remove this #Directory object from its parent and free it. This
+ * must not be called with the root Directory.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ void Delete();
+
+ /**
+ * Create a new #Directory object as a child of the given one.
+ *
+ * Caller must lock the #db_mutex.
+ *
+ * @param name_utf8 the UTF-8 encoded name of the new sub directory
+ */
+ gcc_malloc
+ Directory *CreateChild(const char *name_utf8);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ const Directory *FindChild(const char *name) const;
+
+ gcc_pure
+ Directory *FindChild(const char *name) {
+ const Directory *cthis = this;
+ return const_cast<Directory *>(cthis->FindChild(name));
+ }
+
+ /**
+ * Look up a sub directory, and create the object if it does not
+ * exist.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ Directory *MakeChild(const char *name_utf8) {
+ Directory *child = FindChild(name_utf8);
+ if (child == nullptr)
+ child = CreateChild(name_utf8);
+ return child;
+ }
+
+ /**
+ * Looks up a directory by its relative URI.
+ *
+ * @param uri the relative URI
+ * @return the Directory, or NULL if none was found
+ */
+ gcc_pure
+ Directory *LookupDirectory(const char *uri);
+
+ gcc_pure
+ bool IsEmpty() const {
+ return list_empty(&children) &&
+ list_empty(&songs) &&
+ playlists.empty();
+ }
+
+ gcc_pure
+ const char *GetPath() const {
+ return path;
+ }
+
+ /**
+ * Returns the base name of the directory.
+ */
+ gcc_pure
+ const char *GetName() const;
+
+ /**
+ * Is this the root directory of the music database?
+ */
+ gcc_pure
+ bool IsRoot() const {
+ return parent == NULL;
+ }
+
+ /**
+ * Look up a song in this directory by its name.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ const song *FindSong(const char *name_utf8) const;
+
+ gcc_pure
+ song *FindSong(const char *name_utf8) {
+ const Directory *cthis = this;
+ return const_cast<song *>(cthis->FindSong(name_utf8));
+ }
+
+ /**
+ * Looks up a song by its relative URI.
+ *
+ * Caller must lock the #db_mutex.
+ *
+ * @param uri the relative URI
+ * @return the song, or NULL if none was found
+ */
+ gcc_pure
+ song *LookupSong(const char *uri);
+
+ /**
+ * Add a song object to this directory. Its "parent" attribute must
+ * be set already.
+ */
+ void AddSong(song *song);
+
+ /**
+ * Remove a song object from this directory (which effectively
+ * invalidates the song object, because the "parent" attribute becomes
+ * stale), but does not free it.
+ */
+ void RemoveSong(song *song);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ void PruneEmpty();
+
+ /**
+ * Sort all directory entries recursively.
+ *
+ * Caller must lock the #db_mutex.
+ */
+ void Sort();
+
+ /**
+ * Caller must lock #db_mutex.
+ */
+ bool Walk(bool recursive, const SongFilter *match,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const;
+};
+
+static inline bool
+isRootDirectory(const char *name)
+{
+ return name[0] == 0 || (name[0] == '/' && name[1] == 0);
+}
+
+#endif
diff --git a/src/directory_save.c b/src/DirectorySave.cxx
index de1df050..6a5efb05 100644
--- a/src/directory_save.c
+++ b/src/DirectorySave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,12 @@
*/
#include "config.h"
-#include "directory_save.h"
-#include "directory.h"
+#include "DirectorySave.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "text_file.h"
-#include "song_save.h"
-#include "playlist_database.h"
+#include "SongSave.hxx"
+#include "PlaylistDatabase.hxx"
+#include "TextFile.hxx"
#include <assert.h>
#include <string.h>
@@ -43,17 +43,16 @@ directory_quark(void)
}
void
-directory_save(FILE *fp, const struct directory *directory)
+directory_save(FILE *fp, const Directory *directory)
{
- if (!directory_is_root(directory)) {
+ if (!directory->IsRoot()) {
fprintf(fp, DIRECTORY_MTIME "%lu\n",
(unsigned long)directory->mtime);
- fprintf(fp, "%s%s\n", DIRECTORY_BEGIN,
- directory_get_path(directory));
+ fprintf(fp, "%s%s\n", DIRECTORY_BEGIN, directory->GetPath());
}
- struct directory *cur;
+ Directory *cur;
directory_for_each_child(cur, directory) {
char *base = g_path_get_basename(cur->path);
@@ -70,33 +69,31 @@ directory_save(FILE *fp, const struct directory *directory)
directory_for_each_song(song, directory)
song_save(fp, song);
- playlist_vector_save(fp, &directory->playlists);
+ playlist_vector_save(fp, directory->playlists);
- if (!directory_is_root(directory))
- fprintf(fp, DIRECTORY_END "%s\n",
- directory_get_path(directory));
+ if (!directory->IsRoot())
+ fprintf(fp, DIRECTORY_END "%s\n", directory->GetPath());
}
-static struct directory *
-directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
- GString *buffer, GError **error_r)
+static Directory *
+directory_load_subdir(TextFile &file, Directory *parent, const char *name,
+ GError **error_r)
{
- const char *line;
bool success;
- if (directory_get_child(parent, name) != NULL) {
+ if (parent->FindChild(name) != nullptr) {
g_set_error(error_r, directory_quark(), 0,
"Duplicate subdirectory '%s'", name);
return NULL;
}
- struct directory *directory = directory_new_child(parent, name);
+ Directory *directory = parent->CreateChild(name);
- line = read_text_line(fp, buffer);
+ const char *line = file.ReadLine();
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
@@ -105,11 +102,11 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
g_ascii_strtoull(line + sizeof(DIRECTORY_MTIME) - 1,
NULL, 10);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL) {
g_set_error(error_r, directory_quark(), 0,
"Unexpected end of file");
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
}
@@ -117,13 +114,13 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
if (!g_str_has_prefix(line, DIRECTORY_BEGIN)) {
g_set_error(error_r, directory_quark(), 0,
"Malformed line: %s", line);
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
- success = directory_load(fp, directory, buffer, error_r);
+ success = directory_load(file, directory, error_r);
if (!success) {
- directory_delete(directory);
+ directory->Delete();
return NULL;
}
@@ -131,44 +128,42 @@ directory_load_subdir(FILE *fp, struct directory *parent, const char *name,
}
bool
-directory_load(FILE *fp, struct directory *directory,
- GString *buffer, GError **error)
+directory_load(TextFile &file, Directory *directory, GError **error)
{
const char *line;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
!g_str_has_prefix(line, DIRECTORY_END)) {
if (g_str_has_prefix(line, DIRECTORY_DIR)) {
- struct directory *subdir =
- directory_load_subdir(fp, directory,
+ Directory *subdir =
+ directory_load_subdir(file, directory,
line + sizeof(DIRECTORY_DIR) - 1,
- buffer, error);
+ error);
if (subdir == NULL)
return false;
} else if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *name = line + sizeof(SONG_BEGIN) - 1;
struct song *song;
- if (directory_get_song(directory, name) != NULL) {
+ if (directory->FindSong(name) != nullptr) {
g_set_error(error, directory_quark(), 0,
"Duplicate song '%s'", name);
- return NULL;
+ return false;
}
- song = song_load(fp, directory, name,
- buffer, error);
+ song = song_load(file, directory, name, error);
if (song == NULL)
return false;
- directory_add_song(directory, song);
+ directory->AddSong(song);
} else if (g_str_has_prefix(line, PLAYLIST_META_BEGIN)) {
/* duplicate the name, because
playlist_metadata_load() will overwrite the
buffer */
char *name = g_strdup(line + sizeof(PLAYLIST_META_BEGIN) - 1);
- if (!playlist_metadata_load(fp, &directory->playlists,
- name, buffer, error)) {
+ if (!playlist_metadata_load(file, directory->playlists,
+ name, error)) {
g_free(name);
return false;
}
diff --git a/src/directory_save.h b/src/DirectorySave.hxx
index 2d005683..f4b4816f 100644
--- a/src/directory_save.h
+++ b/src/DirectorySave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,21 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DIRECTORY_SAVE_H
-#define MPD_DIRECTORY_SAVE_H
+#ifndef MPD_DIRECTORY_SAVE_HXX
+#define MPD_DIRECTORY_SAVE_HXX
-#include <glib.h>
+#include "gerror.h"
-#include <stdbool.h>
#include <stdio.h>
-struct directory;
+struct Directory;
+class TextFile;
void
-directory_save(FILE *fp, const struct directory *directory);
+directory_save(FILE *fp, const Directory *directory);
bool
-directory_load(FILE *fp, struct directory *directory,
- GString *buffer, GError **error);
+directory_load(TextFile &file, Directory *directory, GError **error);
#endif
diff --git a/src/exclude.c b/src/ExcludeList.cxx
index 438039d3..0daa432d 100644
--- a/src/exclude.c
+++ b/src/ExcludeList.cxx
@@ -23,35 +23,30 @@
*/
#include "config.h"
-#include "exclude.h"
-#include "path.h"
+#include "ExcludeList.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
#include <assert.h>
#include <string.h>
-#include <stdio.h>
#include <errno.h>
-GSList *
-exclude_list_load(const char *path_fs)
+bool
+ExcludeList::LoadFile(const Path &path_fs)
{
- FILE *file;
- char line[1024];
- GSList *list = NULL;
-
- assert(path_fs != NULL);
-
- file = fopen(path_fs, "r");
+ FILE *file = FOpen(path_fs, FOpenMode::ReadText);
if (file == NULL) {
if (errno != ENOENT) {
- char *path_utf8 = fs_charset_to_utf8(path_fs);
+ const char *msg = g_strerror(errno);
+ const auto path_utf8 = path_fs.ToUTF8();
g_debug("Failed to open %s: %s",
- path_utf8, g_strerror(errno));
- g_free(path_utf8);
+ path_utf8.c_str(), msg);
}
- return NULL;
+ return false;
}
+ char line[1024];
while (fgets(line, sizeof(line), file) != NULL) {
char *p = strchr(line, '#');
if (p != NULL)
@@ -59,37 +54,24 @@ exclude_list_load(const char *path_fs)
p = g_strstrip(line);
if (*p != 0)
- list = g_slist_prepend(list, g_pattern_spec_new(p));
+ patterns.emplace_front(p);
}
fclose(file);
- return list;
-}
-
-void
-exclude_list_free(GSList *list)
-{
- while (list != NULL) {
- GPatternSpec *pattern = list->data;
- g_pattern_spec_free(pattern);
- list = g_slist_remove(list, list->data);
- }
+ return true;
}
bool
-exclude_list_check(GSList *list, const char *name_fs)
+ExcludeList::Check(const char *name_fs) const
{
assert(name_fs != NULL);
/* XXX include full path name in check */
- for (; list != NULL; list = list->next) {
- GPatternSpec *pattern = list->data;
-
- if (g_pattern_match_string(pattern, name_fs))
+ for (const auto &i : patterns)
+ if (i.Check(name_fs))
return true;
- }
return false;
}
diff --git a/src/ExcludeList.hxx b/src/ExcludeList.hxx
new file mode 100644
index 00000000..f3dc1f05
--- /dev/null
+++ b/src/ExcludeList.hxx
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * The .mpdignore backend code.
+ *
+ */
+
+#ifndef MPD_EXCLUDE_H
+#define MPD_EXCLUDE_H
+
+#include "gcc.h"
+
+#include <forward_list>
+
+#include <glib.h>
+
+class Path;
+
+class ExcludeList {
+ class Pattern {
+ GPatternSpec *pattern;
+
+ public:
+ Pattern(const char *_pattern)
+ :pattern(g_pattern_spec_new(_pattern)) {}
+
+ Pattern(Pattern &&other)
+ :pattern(other.pattern) {
+ other.pattern = nullptr;
+ }
+
+ ~Pattern() {
+ g_pattern_spec_free(pattern);
+ }
+
+ gcc_pure
+ bool Check(const char *name_fs) const {
+ return g_pattern_match_string(pattern, name_fs);
+ }
+ };
+
+ std::forward_list<Pattern> patterns;
+
+public:
+ gcc_pure
+ bool IsEmpty() const {
+ return patterns.empty();
+ }
+
+ /**
+ * Loads and parses a .mpdignore file.
+ */
+ bool LoadFile(const Path &path_fs);
+
+ /**
+ * Checks whether one of the patterns in the .mpdignore file matches
+ * the specified file name.
+ */
+ bool Check(const char *name_fs) const;
+};
+
+
+#endif
diff --git a/src/filter_config.c b/src/FilterConfig.cxx
index ab9bdb0c..e56c5a98 100644
--- a/src/filter_config.c
+++ b/src/FilterConfig.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,16 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "filter_config.h"
#include "config.h"
+#include "FilterConfig.hxx"
#include "conf.h"
-#include "filter/chain_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
+#include "filter/ChainFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
-#include <string.h>
+#include <glib.h>
+#include <string.h>
static GQuark
filter_quark(void)
@@ -77,37 +78,38 @@ filter_plugin_config(const char *filter_template_name, GError **error_r)
* @return the number of filters which were successfully added
*/
unsigned int
-filter_chain_parse(struct filter *chain, const char *spec, GError **error_r)
+filter_chain_parse(Filter &chain, const char *spec, GError **error_r)
{
// Split on comma
gchar** tokens = g_strsplit_set(spec, ",", 255);
- int added_filters = 0;
+ unsigned added_filters = 0;
// Add each name to the filter chain by instantiating an actual filter
char **template_names = tokens;
while (*template_names != NULL) {
- struct filter *f;
- const struct config_param *cfg;
-
// Squeeze whitespace
g_strstrip(*template_names);
- cfg = filter_plugin_config(*template_names, error_r);
+ const struct config_param *cfg =
+ filter_plugin_config(*template_names, error_r);
if (cfg == NULL) {
// The error has already been set, just stop.
break;
}
// Instantiate one of those filter plugins with the template name as a hint
- f = filter_configured_new(cfg, error_r);
+ Filter *f = filter_configured_new(cfg, error_r);
if (f == NULL) {
// The error has already been set, just stop.
break;
}
- filter_chain_append(chain, f);
+ const char *plugin_name =
+ config_get_block_string(cfg, "plugin", "unknown");
+
+ filter_chain_append(chain, plugin_name, f);
++added_filters;
++template_names;
diff --git a/src/filter_config.h b/src/FilterConfig.hxx
index 920cbc07..bad18635 100644
--- a/src/filter_config.h
+++ b/src/FilterConfig.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,15 +22,12 @@
* Utility functions for filter configuration
*/
-#ifndef MPD_FILTER_CONFIG_H
-#define MPD_FILTER_CONFIG_H
+#ifndef MPD_FILTER_CONFIG_HXX
+#define MPD_FILTER_CONFIG_HXX
-#include "conf.h"
-#include "filter/chain_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
+#include "gerror.h"
+class Filter;
/**
* Builds a filter chain from a configuration string on the form
@@ -42,6 +39,6 @@
* @return the number of filters which were successfully added
*/
unsigned int
-filter_chain_parse(struct filter *chain, const char *spec, GError **error_r);
+filter_chain_parse(Filter &chain, const char *spec, GError **error_r);
#endif
diff --git a/src/FilterInternal.hxx b/src/FilterInternal.hxx
new file mode 100644
index 00000000..cdc2d0ea
--- /dev/null
+++ b/src/FilterInternal.hxx
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * Internal stuff for the filter core and filter plugins.
+ */
+
+#ifndef MPD_FILTER_INTERNAL_HXX
+#define MPD_FILTER_INTERNAL_HXX
+
+struct audio_format;
+
+class Filter {
+public:
+ virtual ~Filter() {}
+
+ /**
+ * Opens the filter, preparing it for FilterPCM().
+ *
+ * @param filter the filter object
+ * @param audio_format the audio format of incoming data; the
+ * plugin may modify the object to enforce another input
+ * format
+ * @param error location to store the error occurring, or NULL
+ * to ignore errors.
+ * @return the format of outgoing data
+ */
+ virtual const audio_format *Open(audio_format &af,
+ GError **error_r) = 0;
+
+ /**
+ * Closes the filter. After that, you may call Open() again.
+ */
+ virtual void Close() = 0;
+
+ /**
+ * Filters a block of PCM data.
+ *
+ * @param filter the filter object
+ * @param src the input buffer
+ * @param src_size the size of #src_buffer in bytes
+ * @param dest_size_r the size of the returned buffer
+ * @param error location to store the error occurring, or NULL
+ * to ignore errors.
+ * @return the destination buffer on success (will be
+ * invalidated by filter_close() or filter_filter()), NULL on
+ * error
+ */
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r,
+ GError **error_r) = 0;
+};
+
+#endif
diff --git a/src/filter_plugin.c b/src/FilterPlugin.cxx
index 7173134b..953f404e 100644
--- a/src/filter_plugin.c
+++ b/src/FilterPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,15 @@
*/
#include "config.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
#include "conf.h"
-
-#ifndef NDEBUG
-#include "audio_format.h"
-#endif
+#include "ConfigQuark.hxx"
#include <assert.h>
-struct filter *
+Filter *
filter_new(const struct filter_plugin *plugin,
const struct config_param *param, GError **error_r)
{
@@ -39,7 +36,7 @@ filter_new(const struct filter_plugin *plugin,
return plugin->init(param, error_r);
}
-struct filter *
+Filter *
filter_configured_new(const struct config_param *param, GError **error_r)
{
const char *plugin_name;
@@ -64,53 +61,3 @@ filter_configured_new(const struct config_param *param, GError **error_r)
return filter_new(plugin, param, error_r);
}
-
-void
-filter_free(struct filter *filter)
-{
- assert(filter != NULL);
-
- filter->plugin->finish(filter);
-}
-
-const struct audio_format *
-filter_open(struct filter *filter, struct audio_format *audio_format,
- GError **error_r)
-{
- const struct audio_format *out_audio_format;
-
- assert(filter != NULL);
- assert(audio_format != NULL);
- assert(audio_format_valid(audio_format));
- assert(error_r == NULL || *error_r == NULL);
-
- out_audio_format = filter->plugin->open(filter, audio_format, error_r);
-
- assert(out_audio_format == NULL || audio_format_valid(audio_format));
- assert(out_audio_format == NULL ||
- audio_format_valid(out_audio_format));
-
- return out_audio_format;
-}
-
-void
-filter_close(struct filter *filter)
-{
- assert(filter != NULL);
-
- filter->plugin->close(filter);
-}
-
-const void *
-filter_filter(struct filter *filter, const void *src, size_t src_size,
- size_t *dest_size_r,
- GError **error_r)
-{
- assert(filter != NULL);
- assert(src != NULL);
- assert(src_size > 0);
- assert(dest_size_r != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- return filter->plugin->filter(filter, src, src_size, dest_size_r, error_r);
-}
diff --git a/src/FilterPlugin.hxx b/src/FilterPlugin.hxx
new file mode 100644
index 00000000..80bb8a0b
--- /dev/null
+++ b/src/FilterPlugin.hxx
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * This header declares the filter_plugin class. It describes a
+ * plugin API for objects which filter raw PCM data.
+ */
+
+#ifndef MPD_FILTER_PLUGIN_HXX
+#define MPD_FILTER_PLUGIN_HXX
+
+#include "gerror.h"
+
+#include <stddef.h>
+
+struct config_param;
+class Filter;
+
+struct filter_plugin {
+ const char *name;
+
+ /**
+ * Allocates and configures a filter.
+ */
+ Filter *(*init)(const struct config_param *param, GError **error_r);
+};
+
+/**
+ * Creates a new instance of the specified filter plugin.
+ *
+ * @param plugin the filter plugin
+ * @param param optional configuration section
+ * @param error location to store the error occurring, or NULL to
+ * ignore errors.
+ * @return a new filter object, or NULL on error
+ */
+Filter *
+filter_new(const struct filter_plugin *plugin,
+ const struct config_param *param, GError **error_r);
+
+/**
+ * Creates a new filter, loads configuration and the plugin name from
+ * the specified configuration section.
+ *
+ * @param param the configuration section
+ * @param error location to store the error occurring, or NULL to
+ * ignore errors.
+ * @return a new filter object, or NULL on error
+ */
+Filter *
+filter_configured_new(const struct config_param *param, GError **error_r);
+
+#endif
diff --git a/src/filter_registry.c b/src/FilterRegistry.cxx
index dc188939..c8aff829 100644
--- a/src/filter_registry.c
+++ b/src/FilterRegistry.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "filter_registry.h"
-#include "filter_plugin.h"
+#include "FilterRegistry.hxx"
+#include "FilterPlugin.hxx"
#include <stddef.h>
#include <string.h>
diff --git a/src/filter_registry.h b/src/FilterRegistry.hxx
index d3949c7c..c21d8a59 100644
--- a/src/filter_registry.h
+++ b/src/FilterRegistry.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,8 +23,8 @@
* compile time.
*/
-#ifndef MPD_FILTER_REGISTRY_H
-#define MPD_FILTER_REGISTRY_H
+#ifndef MPD_FILTER_REGISTRY_HXX
+#define MPD_FILTER_REGISTRY_HXX
extern const struct filter_plugin null_filter_plugin;
extern const struct filter_plugin chain_filter_plugin;
diff --git a/src/GlobalEvents.cxx b/src/GlobalEvents.cxx
new file mode 100644
index 00000000..e4f335c9
--- /dev/null
+++ b/src/GlobalEvents.cxx
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "GlobalEvents.hxx"
+#include "event/WakeFD.hxx"
+#include "mpd_error.h"
+
+#include <atomic>
+
+#include <assert.h>
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "global_events"
+
+namespace GlobalEvents {
+ static WakeFD wake_fd;
+ static guint source_id;
+ static std::atomic_uint flags;
+ static Handler handlers[MAX];
+}
+
+/**
+ * Invoke the callback for a certain event.
+ */
+static void
+InvokeGlobalEvent(GlobalEvents::Event event)
+{
+ assert((unsigned)event < GlobalEvents::MAX);
+ assert(GlobalEvents::handlers[event] != NULL);
+
+ GlobalEvents::handlers[event]();
+}
+
+static gboolean
+GlobalEventCallback(G_GNUC_UNUSED GIOChannel *source,
+ G_GNUC_UNUSED GIOCondition condition,
+ G_GNUC_UNUSED gpointer data)
+{
+ if (!GlobalEvents::wake_fd.Read())
+ return true;
+
+ const unsigned flags = GlobalEvents::flags.fetch_and(0);
+
+ for (unsigned i = 0; i < GlobalEvents::MAX; ++i)
+ if (flags & (1u << i))
+ /* invoke the event handler */
+ InvokeGlobalEvent(GlobalEvents::Event(i));
+
+ return true;
+}
+
+void
+GlobalEvents::Initialize()
+{
+ if (!wake_fd.Create())
+ MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
+
+#ifndef G_OS_WIN32
+ GIOChannel *channel = g_io_channel_unix_new(wake_fd.Get());
+#else
+ GIOChannel *channel = g_io_channel_win32_new_socket(wake_fd.Get());
+#endif
+
+ source_id = g_io_add_watch(channel, G_IO_IN,
+ GlobalEventCallback, NULL);
+ g_io_channel_unref(channel);
+}
+
+void
+GlobalEvents::Deinitialize()
+{
+ g_source_remove(source_id);
+
+ wake_fd.Destroy();
+}
+
+void
+GlobalEvents::Register(Event event, Handler callback)
+{
+ assert((unsigned)event < MAX);
+ assert(handlers[event] == NULL);
+
+ handlers[event] = callback;
+}
+
+void
+GlobalEvents::Emit(Event event)
+{
+ assert((unsigned)event < MAX);
+
+ const unsigned mask = 1u << unsigned(event);
+ if ((GlobalEvents::flags.fetch_or(mask) & mask) == 0)
+ wake_fd.Write();
+}
diff --git a/src/GlobalEvents.hxx b/src/GlobalEvents.hxx
new file mode 100644
index 00000000..2e549305
--- /dev/null
+++ b/src/GlobalEvents.hxx
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_GLOBAL_EVENTS_HXX
+#define MPD_GLOBAL_EVENTS_HXX
+
+#ifdef WIN32
+/* DELETE is a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef DELETE
+#undef DELETE
+#endif
+#endif
+
+namespace GlobalEvents {
+ enum Event {
+ /** database update was finished */
+ UPDATE,
+
+ /** during database update, a song was deleted */
+ DELETE,
+
+ /** an idle event was emitted */
+ IDLE,
+
+ /** must call playlist_sync() */
+ PLAYLIST,
+
+ /** the current song's tag has changed */
+ TAG,
+
+ /** SIGHUP received: reload configuration, roll log file */
+ RELOAD,
+
+ /** a hardware mixer plugin has detected a change */
+ MIXER,
+
+ /** shutdown requested */
+ SHUTDOWN,
+
+ MAX
+ };
+
+ typedef void (*Handler)();
+
+ void Initialize();
+
+ void Deinitialize();
+
+ void Register(Event event, Handler handler);
+
+ void Emit(Event event);
+}
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/io_thread.c b/src/IOThread.cxx
index 7c080adc..192d4cc4 100644
--- a/src/io_thread.c
+++ b/src/IOThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,16 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "io_thread.h"
+#include "config.h"
+#include "IOThread.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "event/Loop.hxx"
#include <assert.h>
static struct {
- GMutex *mutex;
- GCond *cond;
+ Mutex mutex;
+ Cond cond;
- GMainContext *context;
- GMainLoop *loop;
+ EventLoop *loop;
GThread *thread;
} io;
@@ -34,10 +37,9 @@ void
io_thread_run(void)
{
assert(io_thread_inside());
- assert(io.context != NULL);
assert(io.loop != NULL);
- g_main_loop_run(io.loop);
+ io.loop->Run();
}
static gpointer
@@ -45,8 +47,8 @@ io_thread_func(G_GNUC_UNUSED gpointer arg)
{
/* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */
- g_mutex_lock(io.mutex);
- g_mutex_unlock(io.mutex);
+ io.mutex.lock();
+ io.mutex.unlock();
io_thread_run();
return NULL;
@@ -55,26 +57,21 @@ io_thread_func(G_GNUC_UNUSED gpointer arg)
void
io_thread_init(void)
{
- assert(io.context == NULL);
assert(io.loop == NULL);
assert(io.thread == NULL);
- io.mutex = g_mutex_new();
- io.cond = g_cond_new();
- io.context = g_main_context_new();
- io.loop = g_main_loop_new(io.context, false);
+ io.loop = new EventLoop();
}
bool
io_thread_start(GError **error_r)
{
- assert(io.context != NULL);
assert(io.loop != NULL);
assert(io.thread == NULL);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
io.thread = g_thread_create(io_thread_func, NULL, true, error_r);
- g_mutex_unlock(io.mutex);
+ io.mutex.unlock();
if (io.thread == NULL)
return false;
@@ -86,7 +83,7 @@ io_thread_quit(void)
{
assert(io.loop != NULL);
- g_main_loop_quit(io.loop);
+ io.loop->Break();
}
void
@@ -98,20 +95,15 @@ io_thread_deinit(void)
g_thread_join(io.thread);
}
- if (io.loop != NULL)
- g_main_loop_unref(io.loop);
-
- if (io.context != NULL)
- g_main_context_unref(io.context);
-
- g_cond_free(io.cond);
- g_mutex_free(io.mutex);
+ delete io.loop;
}
-GMainContext *
-io_thread_context(void)
+EventLoop &
+io_thread_get()
{
- return io.context;
+ assert(io.loop != nullptr);
+
+ return *io.loop;
}
bool
@@ -120,35 +112,6 @@ io_thread_inside(void)
return io.thread != NULL && g_thread_self() == io.thread;
}
-guint
-io_thread_idle_add(GSourceFunc function, gpointer data)
-{
- GSource *source = g_idle_source_new();
- g_source_set_callback(source, function, data, NULL);
- guint id = g_source_attach(source, io.context);
- g_source_unref(source);
- return id;
-}
-
-GSource *
-io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new(interval_ms);
- g_source_set_callback(source, function, data, NULL);
- g_source_attach(source, io.context);
- return source;
-}
-
-GSource *
-io_thread_timeout_add_seconds(guint interval,
- GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new_seconds(interval);
- g_source_set_callback(source, function, data, NULL);
- g_source_attach(source, io.context);
- return source;
-}
-
struct call_data {
GThreadFunc function;
gpointer data;
@@ -159,15 +122,15 @@ struct call_data {
static gboolean
io_thread_call_func(gpointer _data)
{
- struct call_data *data = _data;
+ struct call_data *data = (struct call_data *)_data;
gpointer result = data->function(data->data);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
data->done = true;
data->result = result;
- g_cond_broadcast(io.cond);
- g_mutex_unlock(io.mutex);
+ io.cond.broadcast();
+ io.mutex.unlock();
return false;
}
@@ -183,17 +146,18 @@ io_thread_call(GThreadFunc function, gpointer _data)
return function(_data);
struct call_data data = {
- .function = function,
- .data = _data,
- .done = false,
+ function,
+ _data,
+ false,
+ nullptr,
};
- io_thread_idle_add(io_thread_call_func, &data);
+ io.loop->AddIdle(io_thread_call_func, &data);
- g_mutex_lock(io.mutex);
+ io.mutex.lock();
while (!data.done)
- g_cond_wait(io.cond, io.mutex);
- g_mutex_unlock(io.mutex);
+ io.cond.wait(io.mutex);
+ io.mutex.unlock();
return data.result;
}
diff --git a/src/io_thread.h b/src/IOThread.hxx
index 8ff5a71e..a9401dc7 100644
--- a/src/io_thread.h
+++ b/src/IOThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_IO_THREAD_H
-#define MPD_IO_THREAD_H
+#ifndef MPD_IO_THREAD_HXX
+#define MPD_IO_THREAD_HXX
+
+#include "gcc.h"
#include <glib.h>
-#include <stdbool.h>
+
+class EventLoop;
void
io_thread_init(void);
@@ -48,29 +51,17 @@ io_thread_quit(void);
void
io_thread_deinit(void);
-G_GNUC_PURE
-GMainContext *
-io_thread_context(void);
+gcc_pure
+EventLoop &
+io_thread_get();
/**
* Is the current thread the I/O thread?
*/
-G_GNUC_PURE
+gcc_pure
bool
io_thread_inside(void);
-guint
-io_thread_idle_add(GSourceFunc function, gpointer data);
-
-G_GNUC_MALLOC
-GSource *
-io_thread_timeout_add(guint interval_ms, GSourceFunc function, gpointer data);
-
-G_GNUC_MALLOC
-GSource *
-io_thread_timeout_add_seconds(guint interval,
- GSourceFunc function, gpointer data);
-
/**
* Call a function synchronously in the I/O thread.
*/
diff --git a/src/icy_metadata.c b/src/IcyMetaDataParser.cxx
index 32953e69..cda63da4 100644
--- a/src/icy_metadata.c
+++ b/src/IcyMetaDataParser.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "icy_metadata.h"
+#include "IcyMetaDataParser.hxx"
#include "tag.h"
#include <glib.h>
@@ -30,46 +30,37 @@
#define G_LOG_DOMAIN "icy_metadata"
void
-icy_deinit(struct icy_metadata *im)
+IcyMetaDataParser::Reset()
{
- if (!icy_defined(im))
+ if (!IsDefined())
return;
- if (im->data_rest == 0 && im->meta_size > 0)
- g_free(im->meta_data);
+ if (data_rest == 0 && meta_size > 0)
+ g_free(meta_data);
- if (im->tag != NULL)
- tag_free(im->tag);
-}
-
-void
-icy_reset(struct icy_metadata *im)
-{
- if (!icy_defined(im))
- return;
-
- icy_deinit(im);
+ if (tag != nullptr)
+ tag_free(tag);
- im->data_rest = im->data_size;
- im->meta_size = 0;
+ data_rest = data_size;
+ meta_size = 0;
}
size_t
-icy_data(struct icy_metadata *im, size_t length)
+IcyMetaDataParser::Data(size_t length)
{
assert(length > 0);
- if (!icy_defined(im))
+ if (!IsDefined())
return length;
- if (im->data_rest == 0)
+ if (data_rest == 0)
return 0;
- if (length >= im->data_rest) {
- length = im->data_rest;
- im->data_rest = 0;
+ if (length >= data_rest) {
+ length = data_rest;
+ data_rest = 0;
} else
- im->data_rest -= length;
+ data_rest -= length;
return length;
}
@@ -94,7 +85,7 @@ icy_parse_tag_item(struct tag *tag, const char *item)
{
gchar **p = g_strsplit(item, "=", 0);
- if (p[0] != NULL && p[1] != NULL) {
+ if (p[0] != nullptr && p[1] != nullptr) {
if (strcmp(p[0], "StreamTitle") == 0)
icy_add_item(tag, TAG_TITLE, p[1]);
else
@@ -110,7 +101,7 @@ icy_parse_tag(const char *p)
struct tag *tag = tag_new();
gchar **items = g_strsplit(p, ";", 0);
- for (unsigned i = 0; items[i] != NULL; ++i)
+ for (unsigned i = 0; items[i] != nullptr; ++i)
icy_parse_tag_item(tag, items[i]);
g_strfreev(items);
@@ -119,21 +110,21 @@ icy_parse_tag(const char *p)
}
size_t
-icy_meta(struct icy_metadata *im, const void *data, size_t length)
+IcyMetaDataParser::Meta(const void *data, size_t length)
{
- const unsigned char *p = data;
+ const unsigned char *p = (const unsigned char *)data;
- assert(icy_defined(im));
- assert(im->data_rest == 0);
+ assert(IsDefined());
+ assert(data_rest == 0);
assert(length > 0);
- if (im->meta_size == 0) {
+ if (meta_size == 0) {
/* read meta_size from the first byte of a meta
block */
- im->meta_size = *p++ * 16;
- if (im->meta_size == 0) {
+ meta_size = *p++ * 16;
+ if (meta_size == 0) {
/* special case: no metadata */
- im->data_rest = im->data_size;
+ data_rest = data_size;
return 1;
}
@@ -143,39 +134,39 @@ icy_meta(struct icy_metadata *im, const void *data, size_t length)
/* initialize metadata reader, allocate enough
memory (+1 for the null terminator) */
- im->meta_position = 0;
- im->meta_data = g_malloc(im->meta_size + 1);
+ meta_position = 0;
+ meta_data = (char *)g_malloc(meta_size + 1);
}
- assert(im->meta_position < im->meta_size);
+ assert(meta_position < meta_size);
- if (length > im->meta_size - im->meta_position)
- length = im->meta_size - im->meta_position;
+ if (length > meta_size - meta_position)
+ length = meta_size - meta_position;
- memcpy(im->meta_data + im->meta_position, p, length);
- im->meta_position += length;
+ memcpy(meta_data + meta_position, p, length);
+ meta_position += length;
if (p != data)
/* re-add the first byte (which contained meta_size) */
++length;
- if (im->meta_position == im->meta_size) {
+ if (meta_position == meta_size) {
/* null-terminate the string */
- im->meta_data[im->meta_size] = 0;
+ meta_data[meta_size] = 0;
/* parse */
- if (im->tag != NULL)
- tag_free(im->tag);
+ if (tag != nullptr)
+ tag_free(tag);
- im->tag = icy_parse_tag(im->meta_data);
- g_free(im->meta_data);
+ tag = icy_parse_tag(meta_data);
+ g_free(meta_data);
/* change back to normal data mode */
- im->meta_size = 0;
- im->data_rest = im->data_size;
+ meta_size = 0;
+ data_rest = data_size;
}
return length;
diff --git a/src/IcyMetaDataParser.hxx b/src/IcyMetaDataParser.hxx
new file mode 100644
index 00000000..6ccc73f5
--- /dev/null
+++ b/src/IcyMetaDataParser.hxx
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ICY_META_DATA_PARSER_HXX
+#define MPD_ICY_META_DATA_PARSER_HXX
+
+#include <stddef.h>
+
+class IcyMetaDataParser {
+ size_t data_size, data_rest;
+
+ size_t meta_size, meta_position;
+ char *meta_data;
+
+ struct tag *tag;
+
+public:
+ IcyMetaDataParser():data_size(0) {}
+ ~IcyMetaDataParser() {
+ Reset();
+ }
+
+ /**
+ * Initialize an enabled icy_metadata object with the specified
+ * data_size (from the icy-metaint HTTP response header).
+ */
+ void Start(size_t _data_size) {
+ data_size = data_rest = _data_size;
+ meta_size = 0;
+ tag = nullptr;
+ }
+
+ /**
+ * Resets the icy_metadata. Call this after rewinding the stream.
+ */
+ void Reset();
+
+ /**
+ * Checks whether the icy_metadata object is enabled.
+ */
+ bool IsDefined() const {
+ return data_size > 0;
+ }
+
+ /**
+ * Evaluates data. Returns the number of bytes of normal data which
+ * can be read by the caller, but not more than "length". If the
+ * return value is smaller than "length", the caller should invoke
+ * icy_meta().
+ */
+ size_t Data(size_t length);
+
+ /**
+ * Reads metadata from the stream. Returns the number of bytes
+ * consumed. If the return value is smaller than "length", the caller
+ * should invoke icy_data().
+ */
+ size_t Meta(const void *data, size_t length);
+
+ struct tag *ReadTag() {
+ struct tag *result = tag;
+ tag = nullptr;
+ return result;
+ }
+};
+
+#endif
diff --git a/src/icy_server.c b/src/IcyMetaDataServer.cxx
index b6c89eaf..b1d07558 100644
--- a/src/icy_server.c
+++ b/src/IcyMetaDataServer.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,9 @@
*/
#include "config.h"
-#include "icy_server.h"
+#include "IcyMetaDataServer.hxx"
+#include "Page.hxx"
+#include "tag.h"
#include <glib.h>
@@ -84,15 +86,13 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
return icy_metadata;
}
-struct page*
-icy_server_metadata_page(const struct tag *tag, ...)
+Page *
+icy_server_metadata_page(const struct tag *tag, const enum tag_type *types)
{
- va_list args;
const gchar *tag_items[TAG_NUM_OF_ITEM_TYPES];
gint last_item, item;
guint position;
gchar *icy_string;
- struct page *icy_metadata;
gchar stream_title[(1 + 255 - 28) * 16]; // Length + Metadata -
// "StreamTitle='';StreamUrl='';"
// = 4081 - 28
@@ -100,22 +100,11 @@ icy_server_metadata_page(const struct tag *tag, ...)
last_item = -1;
- va_start(args, tag);
- while (1) {
- enum tag_type type;
- const gchar *tag_item;
-
- type = va_arg(args, enum tag_type);
-
- if (type == TAG_NUM_OF_ITEM_TYPES)
- break;
-
- tag_item = tag_get_value(tag, type);
-
+ while (*types != TAG_NUM_OF_ITEM_TYPES) {
+ const gchar *tag_item = tag_get_value(tag, *types);
if (tag_item)
tag_items[++last_item] = tag_item;
}
- va_end(args);
position = item = 0;
while (position < sizeof(stream_title) && item <= last_item) {
@@ -141,7 +130,7 @@ icy_server_metadata_page(const struct tag *tag, ...)
if (icy_string == NULL)
return NULL;
- icy_metadata = page_new_copy(icy_string, (icy_string[0] * 16) + 1);
+ Page *icy_metadata = Page::Copy(icy_string, (icy_string[0] * 16) + 1);
g_free(icy_string);
diff --git a/src/icy_server.h b/src/IcyMetaDataServer.hxx
index 04f21d2a..b344c61f 100644
--- a/src/icy_server.h
+++ b/src/IcyMetaDataServer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef ICY_SERVER_H
-#define ICY_SERVER_H
+#ifndef MPD_ICY_META_DATA_SERVER_HXX
+#define MPD_ICY_META_DATA_SERVER_HXX
-#include "page.h"
#include "tag.h"
-#include <stdarg.h>
+class Page;
char*
icy_server_metadata_header(const char *name,
const char *genre, const char *url,
const char *content_type, int metaint);
-struct page*
-icy_server_metadata_page(const struct tag *tag, ...);
+Page *
+icy_server_metadata_page(const struct tag *tag, const enum tag_type *types);
#endif
diff --git a/src/IdTable.hxx b/src/IdTable.hxx
new file mode 100644
index 00000000..8925fe8a
--- /dev/null
+++ b/src/IdTable.hxx
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ID_TABLE_HXX
+#define MPD_ID_TABLE_HXX
+
+#include "gcc.h"
+
+#include <algorithm>
+
+#include <assert.h>
+
+/**
+ * A table that maps id numbers to position numbers.
+ */
+class IdTable {
+ unsigned size;
+
+ unsigned next;
+
+ int *data;
+
+public:
+ IdTable(unsigned _size):size(_size), next(1), data(new int[size]) {
+ std::fill(data, data + size, -1);
+ }
+
+ ~IdTable() {
+ delete[] data;
+ }
+
+ int IdToPosition(unsigned id) const {
+ return id < size
+ ? data[id]
+ : -1;
+ }
+
+ unsigned GenerateId() {
+ assert(next > 0);
+ assert(next < size);
+
+ while (true) {
+ unsigned id = next;
+
+ ++next;
+ if (next == size)
+ next = 1;
+
+ if (data[id] < 0)
+ return id;
+ }
+ }
+
+ unsigned Insert(unsigned position) {
+ unsigned id = GenerateId();
+ data[id] = position;
+ return id;
+ }
+
+ void Move(unsigned id, unsigned position) {
+ assert(id < size);
+ assert(data[id] >= 0);
+
+ data[id] = position;
+ }
+
+ void Erase(unsigned id) {
+ assert(id < size);
+ assert(data[id] >= 0);
+
+ data[id] = -1;
+ }
+};
+
+#endif
diff --git a/src/idle.c b/src/Idle.cxx
index 2d174d78..fefbd2fe 100644
--- a/src/idle.c
+++ b/src/Idle.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,14 +23,14 @@
*/
#include "config.h"
-#include "idle.h"
-#include "event_pipe.h"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+
+#include <atomic>
#include <assert.h>
-#include <glib.h>
-static unsigned idle_flags;
-static GMutex *idle_mutex = NULL;
+static std::atomic_uint idle_flags;
static const char *const idle_names[] = {
"database",
@@ -44,47 +44,24 @@ static const char *const idle_names[] = {
"update",
"subscription",
"message",
- NULL
+ nullptr
};
void
-idle_init(void)
-{
- g_assert(idle_mutex == NULL);
- idle_mutex = g_mutex_new();
-}
-
-void
-idle_deinit(void)
-{
- g_assert(idle_mutex != NULL);
- g_mutex_free(idle_mutex);
- idle_mutex = NULL;
-}
-
-void
idle_add(unsigned flags)
{
assert(flags != 0);
- g_mutex_lock(idle_mutex);
- idle_flags |= flags;
- g_mutex_unlock(idle_mutex);
+ unsigned old_flags = idle_flags.fetch_or(flags);
- event_pipe_emit(PIPE_EVENT_IDLE);
+ if ((old_flags & flags) != flags)
+ GlobalEvents::Emit(GlobalEvents::IDLE);
}
unsigned
idle_get(void)
{
- unsigned flags;
-
- g_mutex_lock(idle_mutex);
- flags = idle_flags;
- idle_flags = 0;
- g_mutex_unlock(idle_mutex);
-
- return flags;
+ return idle_flags.fetch_and(0);
}
const char*const*
diff --git a/src/idle.h b/src/Idle.hxx
index 0156933c..6e4fbf78 100644
--- a/src/idle.h
+++ b/src/Idle.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,8 +22,8 @@
*
*/
-#ifndef MPD_IDLE_H
-#define MPD_IDLE_H
+#ifndef MPD_IDLE_HXX
+#define MPD_IDLE_HXX
enum {
/** song database has been updated*/
@@ -62,18 +62,6 @@ enum {
};
/**
- * Initialize the mutex
- */
-void
-idle_init(void);
-
-/**
- * Destroy the mutex
- */
-void
-idle_deinit(void);
-
-/**
* Adds idle flag (with bitwise "or") and queues notifications to all
* clients.
*/
diff --git a/src/inotify_queue.c b/src/InotifyQueue.cxx
index d5e2228c..3212f95f 100644
--- a/src/inotify_queue.c
+++ b/src/InotifyQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,9 @@
*/
#include "config.h"
-#include "inotify_queue.h"
-#include "update.h"
+#include "InotifyQueue.hxx"
+#include "UpdateGlue.hxx"
+#include "event/Loop.hxx"
#include <glib.h>
@@ -37,37 +38,13 @@ enum {
INOTIFY_UPDATE_DELAY_S = 5,
};
-static GSList *inotify_queue;
-static guint queue_source_id;
-
-void
-mpd_inotify_queue_init(void)
-{
-}
-
-static void
-free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- g_free(data);
-}
-
-void
-mpd_inotify_queue_finish(void)
-{
- if (queue_source_id != 0)
- g_source_remove(queue_source_id);
-
- g_slist_foreach(inotify_queue, free_callback, NULL);
- g_slist_free(inotify_queue);
-}
-
-static gboolean
-mpd_inotify_run_update(G_GNUC_UNUSED gpointer data)
+bool
+InotifyQueue::OnTimeout()
{
unsigned id;
- while (inotify_queue != NULL) {
- char *uri_utf8 = inotify_queue->data;
+ while (!queue.empty()) {
+ const char *uri_utf8 = queue.front().c_str();
id = update_enqueue(uri_utf8, false);
if (id == 0)
@@ -76,13 +53,10 @@ mpd_inotify_run_update(G_GNUC_UNUSED gpointer data)
g_debug("updating '%s' job=%u", uri_utf8, id);
- g_free(uri_utf8);
- inotify_queue = g_slist_delete_link(inotify_queue,
- inotify_queue);
+ queue.pop_front();
}
/* done, remove the timer event by returning false */
- queue_source_id = 0;
return false;
}
@@ -97,39 +71,25 @@ path_in(const char *path, const char *possible_parent)
}
void
-mpd_inotify_enqueue(char *uri_utf8)
+InotifyQueue::Enqueue(const char *uri_utf8)
{
- GSList *old_queue = inotify_queue;
-
- if (queue_source_id != 0)
- g_source_remove(queue_source_id);
- queue_source_id = g_timeout_add_seconds(INOTIFY_UPDATE_DELAY_S,
- mpd_inotify_run_update, NULL);
+ ScheduleSeconds(INOTIFY_UPDATE_DELAY_S);
- inotify_queue = NULL;
- while (old_queue != NULL) {
- char *current_uri = old_queue->data;
+ for (auto i = queue.begin(), end = queue.end(); i != end;) {
+ const char *current_uri = i->c_str();
- if (path_in(uri_utf8, current_uri)) {
+ if (path_in(uri_utf8, current_uri))
/* already enqueued */
- g_free(uri_utf8);
- inotify_queue = g_slist_concat(inotify_queue,
- old_queue);
return;
- }
-
- old_queue = g_slist_delete_link(old_queue, old_queue);
if (path_in(current_uri, uri_utf8))
/* existing path is a sub-path of the new
path; we can dequeue the existing path and
update the new path instead */
- g_free(current_uri);
+ i = queue.erase(i);
else
- /* move the existing path to the new queue */
- inotify_queue = g_slist_prepend(inotify_queue,
- current_uri);
+ ++i;
}
- inotify_queue = g_slist_prepend(inotify_queue, uri_utf8);
+ queue.emplace_back(uri_utf8);
}
diff --git a/src/filter_internal.h b/src/InotifyQueue.hxx
index 4e94599a..761df574 100644
--- a/src/filter_internal.h
+++ b/src/InotifyQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,22 +17,25 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/** \file
- *
- * Internal stuff for the filter core and filter plugins.
- */
+#ifndef MPD_INOTIFY_QUEUE_HXX
+#define MPD_INOTIFY_QUEUE_HXX
-#ifndef MPD_FILTER_INTERNAL_H
-#define MPD_FILTER_INTERNAL_H
+#include "event/TimeoutMonitor.hxx"
+#include "gcc.h"
-struct filter {
- const struct filter_plugin *plugin;
-};
+#include <list>
+#include <string>
+
+class InotifyQueue final : private TimeoutMonitor {
+ std::list<std::string> queue;
-static inline void
-filter_init(struct filter *filter, const struct filter_plugin *plugin)
-{
- filter->plugin = plugin;
-}
+public:
+ InotifyQueue(EventLoop &_loop):TimeoutMonitor(_loop) {}
+
+ void Enqueue(const char *uri_utf8);
+
+private:
+ virtual bool OnTimeout() override;
+};
#endif
diff --git a/src/inotify_source.c b/src/InotifySource.cxx
index e415f5e7..5552ed57 100644
--- a/src/inotify_source.c
+++ b/src/InotifySource.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,35 +18,20 @@
*/
#include "config.h"
-#include "inotify_source.h"
-#include "fifo_buffer.h"
+#include "InotifySource.hxx"
+#include "util/fifo_buffer.h"
#include "fd_util.h"
#include "mpd_error.h"
+#include <glib.h>
+
#include <sys/inotify.h>
#include <unistd.h>
#include <errno.h>
-#include <stdbool.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "inotify"
-struct mpd_inotify_source {
- int fd;
-
- GIOChannel *channel;
-
- /**
- * The channel's source id in the GLib main loop.
- */
- guint id;
-
- struct fifo_buffer *buffer;
-
- mpd_inotify_callback_t callback;
- void *callback_ctx;
-};
-
/**
* A GQuark for GError instances.
*/
@@ -56,34 +41,32 @@ mpd_inotify_quark(void)
return g_quark_from_static_string("inotify");
}
-static gboolean
-mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
- G_GNUC_UNUSED GIOCondition condition,
- gpointer data)
+bool
+InotifySource::OnSocketReady(gcc_unused unsigned flags)
{
- struct mpd_inotify_source *source = data;
void *dest;
size_t length;
ssize_t nbytes;
- const struct inotify_event *event;
- dest = fifo_buffer_write(source->buffer, &length);
+ dest = fifo_buffer_write(buffer, &length);
if (dest == NULL)
MPD_ERROR("buffer full");
- nbytes = read(source->fd, dest, length);
+ nbytes = read(Get(), dest, length);
if (nbytes < 0)
MPD_ERROR("failed to read from inotify: %s",
g_strerror(errno));
if (nbytes == 0)
MPD_ERROR("end of file from inotify");
- fifo_buffer_append(source->buffer, nbytes);
+ fifo_buffer_append(buffer, nbytes);
while (true) {
const char *name;
- event = fifo_buffer_read(source->buffer, &length);
+ const struct inotify_event *event =
+ (const struct inotify_event *)
+ fifo_buffer_read(buffer, &length);
if (event == NULL || length < sizeof(*event) ||
length < sizeof(*event) + event->len)
break;
@@ -93,59 +76,50 @@ mpd_inotify_in_event(G_GNUC_UNUSED GIOChannel *_source,
else
name = NULL;
- source->callback(event->wd, event->mask, name,
- source->callback_ctx);
- fifo_buffer_consume(source->buffer,
- sizeof(*event) + event->len);
+ callback(event->wd, event->mask, name, callback_ctx);
+ fifo_buffer_consume(buffer, sizeof(*event) + event->len);
}
return true;
}
-struct mpd_inotify_source *
-mpd_inotify_source_new(mpd_inotify_callback_t callback, void *callback_ctx,
- GError **error_r)
+inline
+InotifySource::InotifySource(EventLoop &_loop,
+ mpd_inotify_callback_t _callback, void *_ctx,
+ int _fd)
+ :SocketMonitor(_fd, _loop),
+ callback(_callback), callback_ctx(_ctx),
+ buffer(fifo_buffer_new(4096))
{
- struct mpd_inotify_source *source =
- g_new(struct mpd_inotify_source, 1);
+ ScheduleRead();
- source->fd = inotify_init_cloexec();
- if (source->fd < 0) {
+}
+
+InotifySource *
+InotifySource::Create(EventLoop &loop,
+ mpd_inotify_callback_t callback, void *callback_ctx,
+ GError **error_r)
+{
+ int fd = inotify_init_cloexec();
+ if (fd < 0) {
g_set_error(error_r, mpd_inotify_quark(), errno,
"inotify_init() has failed: %s",
g_strerror(errno));
- g_free(source);
return NULL;
}
- source->buffer = fifo_buffer_new(4096);
-
- source->channel = g_io_channel_unix_new(source->fd);
- source->id = g_io_add_watch(source->channel, G_IO_IN,
- mpd_inotify_in_event, source);
-
- source->callback = callback;
- source->callback_ctx = callback_ctx;
-
- return source;
+ return new InotifySource(loop, callback, callback_ctx, fd);
}
-void
-mpd_inotify_source_free(struct mpd_inotify_source *source)
+InotifySource::~InotifySource()
{
- g_source_remove(source->id);
- g_io_channel_unref(source->channel);
- fifo_buffer_free(source->buffer);
- close(source->fd);
- g_free(source);
+ fifo_buffer_free(buffer);
}
int
-mpd_inotify_source_add(struct mpd_inotify_source *source,
- const char *path_fs, unsigned mask,
- GError **error_r)
+InotifySource::Add(const char *path_fs, unsigned mask, GError **error_r)
{
- int wd = inotify_add_watch(source->fd, path_fs, mask);
+ int wd = inotify_add_watch(Get(), path_fs, mask);
if (wd < 0)
g_set_error(error_r, mpd_inotify_quark(), errno,
"inotify_add_watch() has failed: %s",
@@ -155,9 +129,9 @@ mpd_inotify_source_add(struct mpd_inotify_source *source,
}
void
-mpd_inotify_source_rm(struct mpd_inotify_source *source, unsigned wd)
+InotifySource::Remove(unsigned wd)
{
- int ret = inotify_rm_watch(source->fd, wd);
+ int ret = inotify_rm_watch(Get(), wd);
if (ret < 0 && errno != EINVAL)
g_warning("inotify_rm_watch() has failed: %s",
g_strerror(errno));
diff --git a/src/InotifySource.hxx b/src/InotifySource.hxx
new file mode 100644
index 00000000..e8f9ff03
--- /dev/null
+++ b/src/InotifySource.hxx
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_INOTIFY_SOURCE_HXX
+#define MPD_INOTIFY_SOURCE_HXX
+
+#include "event/SocketMonitor.hxx"
+#include "gerror.h"
+#include "gcc.h"
+
+typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
+ const char *name, void *ctx);
+
+class InotifySource final : private SocketMonitor {
+ mpd_inotify_callback_t callback;
+ void *callback_ctx;
+
+ struct fifo_buffer *buffer;
+
+ InotifySource(EventLoop &_loop,
+ mpd_inotify_callback_t callback, void *ctx, int fd);
+
+public:
+ /**
+ * Creates a new inotify source and registers it in the GLib main
+ * loop.
+ *
+ * @param a callback invoked for events received from the kernel
+ */
+ static InotifySource *Create(EventLoop &_loop,
+ mpd_inotify_callback_t callback,
+ void *ctx,
+ GError **error_r);
+
+ ~InotifySource();
+
+
+ /**
+ * Adds a path to the notify list.
+ *
+ * @return a watch descriptor or -1 on error
+ */
+ int Add(const char *path_fs, unsigned mask, GError **error_r);
+
+ /**
+ * Removes a path from the notify list.
+ *
+ * @param wd the watch descriptor returned by mpd_inotify_source_add()
+ */
+ void Remove(unsigned wd);
+
+private:
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/inotify_update.c b/src/InotifyUpdate.cxx
index 3f4a8c0c..2de3bf62 100644
--- a/src/inotify_update.c
+++ b/src/InotifyUpdate.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,21 @@
*/
#include "config.h" /* must be first for large file support */
-#include "inotify_update.h"
-#include "inotify_source.h"
-#include "inotify_queue.h"
-#include "database.h"
-#include "mapper.h"
-#include "path.h"
+#include "InotifyUpdate.hxx"
+#include "InotifySource.hxx"
+#include "InotifyQueue.hxx"
+#include "Mapper.hxx"
+#include "Main.hxx"
+#include "fs/Path.hxx"
+
+#include <glib.h>
+
+#include <map>
+#include <forward_list>
#include <assert.h>
#include <sys/inotify.h>
#include <sys/stat.h>
-#include <stdbool.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
@@ -44,57 +48,73 @@ enum {
#endif
};
-struct watch_directory {
- struct watch_directory *parent;
+struct WatchDirectory {
+ WatchDirectory *parent;
char *name;
int descriptor;
- GList *children;
+ std::forward_list<WatchDirectory> children;
+
+ WatchDirectory(WatchDirectory *_parent, const char *_name,
+ int _descriptor)
+ :parent(_parent), name(g_strdup(_name)),
+ descriptor(_descriptor) {}
+
+ WatchDirectory(const WatchDirectory &) = delete;
+ WatchDirectory &operator=(const WatchDirectory &) = delete;
+
+ ~WatchDirectory() {
+ g_free(name);
+ }
};
-static struct mpd_inotify_source *inotify_source;
+static InotifySource *inotify_source;
+static InotifyQueue *inotify_queue;
static unsigned inotify_max_depth;
-static struct watch_directory inotify_root;
-static GTree *inotify_directories;
+static WatchDirectory *inotify_root;
+static std::map<int, WatchDirectory *> inotify_directories;
-static gint
-compare(gconstpointer a, gconstpointer b)
+static void
+tree_add_watch_directory(WatchDirectory *directory)
{
- if (a < b)
- return -1;
- else if (a > b)
- return 1;
- else
- return 0;
+ inotify_directories.insert(std::make_pair(directory->descriptor,
+ directory));
}
static void
-tree_add_watch_directory(struct watch_directory *directory)
+tree_remove_watch_directory(WatchDirectory *directory)
{
- g_tree_insert(inotify_directories,
- GINT_TO_POINTER(directory->descriptor), directory);
+ auto i = inotify_directories.find(directory->descriptor);
+ assert(i != inotify_directories.end());
+ inotify_directories.erase(i);
}
-static void
-tree_remove_watch_directory(struct watch_directory *directory)
+static WatchDirectory *
+tree_find_watch_directory(int wd)
{
- G_GNUC_UNUSED
- bool found = g_tree_remove(inotify_directories,
- GINT_TO_POINTER(directory->descriptor));
- assert(found);
+ auto i = inotify_directories.find(wd);
+ if (i == inotify_directories.end())
+ return nullptr;
+
+ return i->second;
}
-static struct watch_directory *
-tree_find_watch_directory(int wd)
+static void
+disable_watch_directory(WatchDirectory &directory)
{
- return g_tree_lookup(inotify_directories, GINT_TO_POINTER(wd));
+ tree_remove_watch_directory(&directory);
+
+ for (WatchDirectory &child : directory.children)
+ disable_watch_directory(child);
+
+ inotify_source->Remove(directory.descriptor);
}
static void
-remove_watch_directory(struct watch_directory *directory)
+remove_watch_directory(WatchDirectory *directory)
{
assert(directory != NULL);
@@ -104,23 +124,16 @@ remove_watch_directory(struct watch_directory *directory)
return;
}
- assert(directory->parent->children != NULL);
-
- tree_remove_watch_directory(directory);
+ disable_watch_directory(*directory);
- while (directory->children != NULL)
- remove_watch_directory(directory->children->data);
-
- directory->parent->children =
- g_list_remove(directory->parent->children, directory);
-
- mpd_inotify_source_rm(inotify_source, directory->descriptor);
- g_free(directory->name);
- g_slice_free(struct watch_directory, directory);
+ /* remove it from the parent, which effectively deletes it */
+ directory->parent->children.remove_if([directory](const WatchDirectory &child){
+ return &child == directory;
+ });
}
static char *
-watch_directory_get_uri_fs(const struct watch_directory *directory)
+watch_directory_get_uri_fs(const WatchDirectory *directory)
{
char *parent_uri, *uri;
@@ -146,7 +159,7 @@ static bool skip_path(const char *path)
}
static void
-recursive_watch_subdirectories(struct watch_directory *directory,
+recursive_watch_subdirectories(WatchDirectory *directory,
const char *path_fs, unsigned depth)
{
GError *error = NULL;
@@ -173,7 +186,6 @@ recursive_watch_subdirectories(struct watch_directory *directory,
char *child_path_fs;
struct stat st;
int ret;
- struct watch_directory *child;
if (skip_path(ent->d_name))
continue;
@@ -192,8 +204,7 @@ recursive_watch_subdirectories(struct watch_directory *directory,
continue;
}
- ret = mpd_inotify_source_add(inotify_source, child_path_fs,
- IN_MASK, &error);
+ ret = inotify_source->Add(child_path_fs, IN_MASK, &error);
if (ret < 0) {
g_warning("Failed to register %s: %s",
child_path_fs, error->message);
@@ -203,21 +214,15 @@ recursive_watch_subdirectories(struct watch_directory *directory,
continue;
}
- child = tree_find_watch_directory(ret);
+ WatchDirectory *child = tree_find_watch_directory(ret);
if (child != NULL) {
/* already being watched */
g_free(child_path_fs);
continue;
}
- child = g_slice_new(struct watch_directory);
- child->parent = directory;
- child->name = g_strdup(ent->d_name);
- child->descriptor = ret;
- child->children = NULL;
-
- directory->children = g_list_prepend(directory->children,
- child);
+ directory->children.emplace_front(directory, ent->d_name, ret);
+ child = &directory->children.front();
tree_add_watch_directory(child);
@@ -230,7 +235,7 @@ recursive_watch_subdirectories(struct watch_directory *directory,
G_GNUC_PURE
static unsigned
-watch_directory_depth(const struct watch_directory *d)
+watch_directory_depth(const WatchDirectory *d)
{
assert(d != NULL);
@@ -245,7 +250,7 @@ static void
mpd_inotify_callback(int wd, unsigned mask,
G_GNUC_UNUSED const char *name, G_GNUC_UNUSED void *ctx)
{
- struct watch_directory *directory;
+ WatchDirectory *directory;
char *uri_fs;
/*g_debug("wd=%d mask=0x%x name='%s'", wd, mask, name);*/
@@ -266,7 +271,7 @@ mpd_inotify_callback(int wd, unsigned mask,
(mask & IN_ISDIR) != 0) {
/* a sub directory was changed: register those in
inotify */
- const char *root = mapper_get_music_directory_fs();
+ const char *root = mapper_get_music_directory_fs().c_str();
const char *path_fs;
char *allocated = NULL;
@@ -288,14 +293,14 @@ mpd_inotify_callback(int wd, unsigned mask,
(mask & (IN_CREATE|IN_ISDIR)) == (IN_CREATE|IN_ISDIR))) {
/* a file was changed, or a directory was
moved/deleted: queue a database update */
- char *uri_utf8 = uri_fs != NULL
- ? fs_charset_to_utf8(uri_fs)
- : g_strdup("");
-
- if (uri_utf8 != NULL)
- /* this function will take care of freeing
- uri_utf8 */
- mpd_inotify_enqueue(uri_utf8);
+
+ if (uri_fs != nullptr) {
+ const std::string uri_utf8 = Path::ToUTF8(uri_fs);
+ if (!uri_utf8.empty())
+ inotify_queue->Enqueue(uri_utf8.c_str());
+ }
+ else
+ inotify_queue->Enqueue("");
}
g_free(uri_fs);
@@ -308,14 +313,15 @@ mpd_inotify_init(unsigned max_depth)
g_debug("initializing inotify");
- const char *path = mapper_get_music_directory_fs();
- if (path == NULL) {
+ const Path &path = mapper_get_music_directory_fs();
+ if (path.IsNull()) {
g_debug("no music directory configured");
return;
}
- inotify_source = mpd_inotify_source_new(mpd_inotify_callback, NULL,
- &error);
+ inotify_source = InotifySource::Create(*main_loop,
+ mpd_inotify_callback, nullptr,
+ &error);
if (inotify_source == NULL) {
g_warning("%s", error->message);
g_error_free(error);
@@ -324,40 +330,24 @@ mpd_inotify_init(unsigned max_depth)
inotify_max_depth = max_depth;
- inotify_root.name = g_strdup(path);
- inotify_root.descriptor = mpd_inotify_source_add(inotify_source, path,
- IN_MASK, &error);
- if (inotify_root.descriptor < 0) {
+ int descriptor = inotify_source->Add(path.c_str(), IN_MASK, &error);
+ if (descriptor < 0) {
g_warning("%s", error->message);
g_error_free(error);
- mpd_inotify_source_free(inotify_source);
+ delete inotify_source;
inotify_source = NULL;
return;
}
- inotify_directories = g_tree_new(compare);
- tree_add_watch_directory(&inotify_root);
-
- recursive_watch_subdirectories(&inotify_root, path, 0);
+ inotify_root = new WatchDirectory(nullptr, path.c_str(), descriptor);
- mpd_inotify_queue_init();
+ tree_add_watch_directory(inotify_root);
- g_debug("watching music directory");
-}
-
-static gboolean
-free_watch_directory(G_GNUC_UNUSED gpointer key, gpointer value,
- G_GNUC_UNUSED gpointer data)
-{
- struct watch_directory *directory = value;
+ recursive_watch_subdirectories(inotify_root, path.c_str(), 0);
- g_free(directory->name);
- g_list_free(directory->children);
+ inotify_queue = new InotifyQueue(*main_loop);
- if (directory != &inotify_root)
- g_slice_free(struct watch_directory, directory);
-
- return false;
+ g_debug("watching music directory");
}
void
@@ -366,9 +356,8 @@ mpd_inotify_finish(void)
if (inotify_source == NULL)
return;
- mpd_inotify_queue_finish();
- mpd_inotify_source_free(inotify_source);
-
- g_tree_foreach(inotify_directories, free_watch_directory, NULL);
- g_tree_destroy(inotify_directories);
+ delete inotify_queue;
+ delete inotify_source;
+ delete inotify_root;
+ inotify_directories.clear();
}
diff --git a/src/inotify_update.h b/src/InotifyUpdate.hxx
index ca75c0f4..ceb42155 100644
--- a/src/inotify_update.h
+++ b/src/InotifyUpdate.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INOTIFY_UPDATE_H
-#define MPD_INOTIFY_UPDATE_H
+#ifndef MPD_INOTIFY_UPDATE_HXX
+#define MPD_INOTIFY_UPDATE_HXX
#include "check.h"
diff --git a/src/input_init.c b/src/InputInit.cxx
index 771d648d..64cdfc7a 100644
--- a/src/input_init.c
+++ b/src/InputInit.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,9 @@
*/
#include "config.h"
-#include "input_init.h"
-#include "input_plugin.h"
-#include "input_registry.h"
+#include "InputInit.hxx"
+#include "InputRegistry.hxx"
+#include "InputPlugin.hxx"
#include "conf.h"
#include <assert.h>
diff --git a/src/input_init.h b/src/InputInit.hxx
index ad92cda0..9d503e5a 100644
--- a/src/input_init.h
+++ b/src/InputInit.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_INIT_H
-#define MPD_INPUT_INIT_H
+#ifndef MPD_INPUT_INIT_HXX
+#define MPD_INPUT_INIT_HXX
-#include "check.h"
-
-#include <glib.h>
-#include <stdbool.h>
+#include "gerror.h"
/**
* Initializes this library and all input_stream implementations.
diff --git a/src/InputInternal.cxx b/src/InputInternal.cxx
new file mode 100644
index 00000000..e110ebf0
--- /dev/null
+++ b/src/InputInternal.cxx
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+
+void
+input_stream_signal_client(struct input_stream *is)
+{
+ is->cond.broadcast();
+}
+
+void
+input_stream_set_ready(struct input_stream *is)
+{
+ const ScopeLock protect(is->mutex);
+
+ if (!is->ready) {
+ is->ready = true;
+ input_stream_signal_client(is);
+ }
+}
diff --git a/src/InputInternal.hxx b/src/InputInternal.hxx
new file mode 100644
index 00000000..019ac09c
--- /dev/null
+++ b/src/InputInternal.hxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_INPUT_INTERNAL_HXX
+#define MPD_INPUT_INTERNAL_HXX
+
+#include "check.h"
+
+struct input_stream;
+
+void
+input_stream_signal_client(struct input_stream *is);
+
+void
+input_stream_set_ready(struct input_stream *is);
+
+#endif
diff --git a/src/input_plugin.h b/src/InputPlugin.hxx
index 6b0c77c8..c1660081 100644
--- a/src/input_plugin.h
+++ b/src/InputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_PLUGIN_H
-#define MPD_INPUT_PLUGIN_H
+#ifndef MPD_INPUT_PLUGIN_HXX
+#define MPD_INPUT_PLUGIN_HXX
#include "input_stream.h"
#include <stddef.h>
-#include <stdbool.h>
#include <sys/types.h>
struct config_param;
@@ -49,7 +48,7 @@ struct input_plugin {
void (*finish)(void);
struct input_stream *(*open)(const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r);
void (*close)(struct input_stream *is);
diff --git a/src/input_registry.c b/src/InputRegistry.cxx
index 5987d5da..342720d2 100644
--- a/src/input_registry.c
+++ b/src/InputRegistry.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,35 +18,35 @@
*/
#include "config.h"
-#include "input_registry.h"
-#include "input/file_input_plugin.h"
+#include "InputRegistry.hxx"
+#include "input/FileInputPlugin.hxx"
#ifdef ENABLE_ARCHIVE
-#include "input/archive_input_plugin.h"
+#include "input/ArchiveInputPlugin.hxx"
#endif
#ifdef ENABLE_CURL
-#include "input/curl_input_plugin.h"
+#include "input/CurlInputPlugin.hxx"
#endif
#ifdef ENABLE_SOUP
-#include "input/soup_input_plugin.h"
+#include "input/SoupInputPlugin.hxx"
#endif
#ifdef HAVE_FFMPEG
-#include "input/ffmpeg_input_plugin.h"
+#include "input/FfmpegInputPlugin.hxx"
#endif
#ifdef ENABLE_MMS
-#include "input/mms_input_plugin.h"
+#include "input/MmsInputPlugin.hxx"
#endif
#ifdef ENABLE_CDIO_PARANOIA
-#include "input/cdio_paranoia_input_plugin.h"
+#include "input/CdioParanoiaInputPlugin.hxx"
#endif
#ifdef ENABLE_DESPOTIFY
-#include "input/despotify_input_plugin.h"
+#include "input/DespotifyInputPlugin.hxx"
#endif
#include <glib.h>
diff --git a/src/input_registry.h b/src/InputRegistry.hxx
index 4f5fff8d..a080d108 100644
--- a/src/input_registry.h
+++ b/src/InputRegistry.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_REGISTRY_H
-#define MPD_INPUT_REGISTRY_H
+#ifndef MPD_INPUT_REGISTRY_HXX
+#define MPD_INPUT_REGISTRY_HXX
#include "check.h"
-#include <stdbool.h>
-
/**
* NULL terminated list of all input plugins which were enabled at
* compile time.
diff --git a/src/input_stream.c b/src/InputStream.cxx
index e445dca6..913fcf63 100644
--- a/src/input_stream.c
+++ b/src/InputStream.cxx
@@ -18,10 +18,14 @@
*/
#include "config.h"
-#include "input_stream.h"
-#include "input_registry.h"
-#include "input_plugin.h"
-#include "input/rewind_input_plugin.h"
+#include "InputStream.hxx"
+#include "InputRegistry.hxx"
+#include "InputPlugin.hxx"
+#include "input/RewindInputPlugin.hxx"
+
+extern "C" {
+#include "uri.h"
+}
#include <glib.h>
#include <assert.h>
@@ -34,12 +38,11 @@ input_quark(void)
struct input_stream *
input_stream_open(const char *url,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
GError *error = NULL;
- assert(mutex != NULL);
assert(error_r == NULL || *error_r == NULL);
input_plugins_for_each_enabled(plugin) {
@@ -47,11 +50,10 @@ input_stream_open(const char *url,
is = plugin->open(url, mutex, cond, &error);
if (is != NULL) {
- assert(is->plugin != NULL);
- assert(is->plugin->close != NULL);
- assert(is->plugin->read != NULL);
- assert(is->plugin->eof != NULL);
- assert(!is->seekable || is->plugin->seek != NULL);
+ assert(is->plugin.close != NULL);
+ assert(is->plugin.read != NULL);
+ assert(is->plugin.eof != NULL);
+ assert(!is->seekable || is->plugin.seek != NULL);
is = input_rewind_open(is);
@@ -70,35 +72,31 @@ bool
input_stream_check(struct input_stream *is, GError **error_r)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- return is->plugin->check == NULL ||
- is->plugin->check(is, error_r);
+ return is->plugin.check == NULL ||
+ is->plugin.check(is, error_r);
}
void
input_stream_update(struct input_stream *is)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- if (is->plugin->update != NULL)
- is->plugin->update(is);
+ if (is->plugin.update != NULL)
+ is->plugin.update(is);
}
void
input_stream_wait_ready(struct input_stream *is)
{
assert(is != NULL);
- assert(is->mutex != NULL);
- assert(is->cond != NULL);
while (true) {
input_stream_update(is);
if (is->ready)
break;
- g_cond_wait(is->cond, is->mutex);
+ is->cond.wait(is->mutex);
}
}
@@ -106,12 +104,60 @@ void
input_stream_lock_wait_ready(struct input_stream *is)
{
assert(is != NULL);
- assert(is->mutex != NULL);
- assert(is->cond != NULL);
- g_mutex_lock(is->mutex);
+ const ScopeLock protect(is->mutex);
input_stream_wait_ready(is);
- g_mutex_unlock(is->mutex);
+}
+
+const char *
+input_stream_get_mime_type(const struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->ready);
+
+ return is->mime.empty() ? nullptr : is->mime.c_str();
+}
+
+void
+input_stream_override_mime_type(struct input_stream *is, const char *mime)
+{
+ assert(is != NULL);
+ assert(is->ready);
+
+ is->mime = mime;
+}
+
+goffset
+input_stream_get_size(const struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->ready);
+
+ return is->size;
+}
+
+goffset
+input_stream_get_offset(const struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->ready);
+
+ return is->offset;
+}
+
+bool
+input_stream_is_seekable(const struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->ready);
+
+ return is->seekable;
+}
+
+bool
+input_stream_cheap_seeking(const struct input_stream *is)
+{
+ return is->seekable && !uri_has_scheme(is->uri.c_str());
}
bool
@@ -119,12 +165,11 @@ input_stream_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- if (is->plugin->seek == NULL)
+ if (is->plugin.seek == NULL)
return false;
- return is->plugin->seek(is, offset, whence, error_r);
+ return is->plugin.seek(is, offset, whence, error_r);
}
bool
@@ -132,29 +177,21 @@ input_stream_lock_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- if (is->plugin->seek == NULL)
+ if (is->plugin.seek == NULL)
return false;
- if (is->mutex == NULL)
- /* no locking */
- return input_stream_seek(is, offset, whence, error_r);
-
- g_mutex_lock(is->mutex);
- bool success = input_stream_seek(is, offset, whence, error_r);
- g_mutex_unlock(is->mutex);
- return success;
+ const ScopeLock protect(is->mutex);
+ return input_stream_seek(is, offset, whence, error_r);
}
struct tag *
input_stream_tag(struct input_stream *is)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- return is->plugin->tag != NULL
- ? is->plugin->tag(is)
+ return is->plugin.tag != NULL
+ ? is->plugin.tag(is)
: NULL;
}
@@ -162,29 +199,21 @@ struct tag *
input_stream_lock_tag(struct input_stream *is)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- if (is->plugin->tag == NULL)
- return false;
-
- if (is->mutex == NULL)
- /* no locking */
- return input_stream_tag(is);
+ if (is->plugin.tag == NULL)
+ return nullptr;
- g_mutex_lock(is->mutex);
- struct tag *tag = input_stream_tag(is);
- g_mutex_unlock(is->mutex);
- return tag;
+ const ScopeLock protect(is->mutex);
+ return input_stream_tag(is);
}
bool
input_stream_available(struct input_stream *is)
{
assert(is != NULL);
- assert(is->plugin != NULL);
- return is->plugin->available != NULL
- ? is->plugin->available(is)
+ return is->plugin.available != NULL
+ ? is->plugin.available(is)
: true;
}
@@ -195,7 +224,7 @@ input_stream_read(struct input_stream *is, void *ptr, size_t size,
assert(ptr != NULL);
assert(size > 0);
- return is->plugin->read(is, ptr, size, error_r);
+ return is->plugin.read(is, ptr, size, error_r);
}
size_t
@@ -205,39 +234,26 @@ input_stream_lock_read(struct input_stream *is, void *ptr, size_t size,
assert(ptr != NULL);
assert(size > 0);
- if (is->mutex == NULL)
- /* no locking */
- return input_stream_read(is, ptr, size, error_r);
-
- g_mutex_lock(is->mutex);
- size_t nbytes = input_stream_read(is, ptr, size, error_r);
- g_mutex_unlock(is->mutex);
- return nbytes;
+ const ScopeLock protect(is->mutex);
+ return input_stream_read(is, ptr, size, error_r);
}
void input_stream_close(struct input_stream *is)
{
- is->plugin->close(is);
+ is->plugin.close(is);
}
bool input_stream_eof(struct input_stream *is)
{
- return is->plugin->eof(is);
+ return is->plugin.eof(is);
}
bool
input_stream_lock_eof(struct input_stream *is)
{
assert(is != NULL);
- assert(is->plugin != NULL);
-
- if (is->mutex == NULL)
- /* no locking */
- return input_stream_eof(is);
- g_mutex_lock(is->mutex);
- bool eof = input_stream_eof(is);
- g_mutex_unlock(is->mutex);
- return eof;
+ const ScopeLock protect(is->mutex);
+ return input_stream_eof(is);
}
diff --git a/src/InputStream.hxx b/src/InputStream.hxx
new file mode 100644
index 00000000..96172723
--- /dev/null
+++ b/src/InputStream.hxx
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_INPUT_STREAM_HXX
+#define MPD_INPUT_STREAM_HXX
+
+#include "input_stream.h"
+#include "check.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "gcc.h"
+
+#include <string>
+
+#include <assert.h>
+
+struct input_stream {
+ /**
+ * the plugin which implements this input stream
+ */
+ const struct input_plugin &plugin;
+
+ /**
+ * The absolute URI which was used to open this stream.
+ */
+ std::string uri;
+
+ /**
+ * A mutex that protects the mutable attributes of this object
+ * and its implementation. It must be locked before calling
+ * any of the public methods.
+ *
+ * This object is allocated by the client, and the client is
+ * responsible for freeing it.
+ */
+ Mutex &mutex;
+
+ /**
+ * A cond that gets signalled when the state of this object
+ * changes from the I/O thread. The client of this object may
+ * wait on it. Optional, may be NULL.
+ *
+ * This object is allocated by the client, and the client is
+ * responsible for freeing it.
+ */
+ Cond &cond;
+
+ /**
+ * indicates whether the stream is ready for reading and
+ * whether the other attributes in this struct are valid
+ */
+ bool ready;
+
+ /**
+ * if true, then the stream is fully seekable
+ */
+ bool seekable;
+
+ /**
+ * the size of the resource, or -1 if unknown
+ */
+ goffset size;
+
+ /**
+ * the current offset within the stream
+ */
+ goffset offset;
+
+ /**
+ * the MIME content type of the resource, or empty if unknown.
+ */
+ std::string mime;
+
+ input_stream(const input_plugin &_plugin,
+ const char *_uri, Mutex &_mutex, Cond &_cond)
+ :plugin(_plugin), uri(_uri),
+ mutex(_mutex), cond(_cond),
+ ready(false), seekable(false),
+ size(-1), offset(0) {
+ assert(_uri != NULL);
+ }
+};
+
+gcc_nonnull(1)
+static inline void
+input_stream_lock(struct input_stream *is)
+{
+ is->mutex.lock();
+}
+
+gcc_nonnull(1)
+static inline void
+input_stream_unlock(struct input_stream *is)
+{
+ is->mutex.unlock();
+}
+
+#endif
diff --git a/src/listen.c b/src/Listen.cxx
index 90e13b9c..26c9e100 100644
--- a/src/listen.c
+++ b/src/Listen.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,11 @@
*/
#include "config.h"
-#include "listen.h"
-#include "server_socket.h"
-#include "client.h"
+#include "Listen.hxx"
+#include "Main.hxx"
+#include "Client.hxx"
#include "conf.h"
-#include "main.h"
+#include "event/ServerSocket.hxx"
#include <string.h>
#include <assert.h>
@@ -36,15 +36,20 @@
#define DEFAULT_PORT 6600
-static struct server_socket *listen_socket;
-int listen_port;
+class ClientListener final : public ServerSocket {
+public:
+ ClientListener():ServerSocket(*main_loop) {}
-static void
-listen_callback(int fd, const struct sockaddr *address,
- size_t address_length, int uid, G_GNUC_UNUSED void *ctx)
-{
- client_new(global_player_control, fd, address, address_length, uid);
-}
+private:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) {
+ client_new(*main_loop, *global_partition,
+ fd, &address, address_length, uid);
+ }
+};
+
+static ClientListener *listen_socket;
+int listen_port;
static bool
listen_add_config_param(unsigned int port,
@@ -54,13 +59,11 @@ listen_add_config_param(unsigned int port,
assert(param != NULL);
if (0 == strcmp(param->value, "any")) {
- return server_socket_add_port(listen_socket, port, error_r);
+ return listen_socket->AddPort(port, error_r);
} else if (param->value[0] == '/') {
- return server_socket_add_path(listen_socket, param->value,
- error_r);
+ return listen_socket->AddPath(param->value, error_r);
} else {
- return server_socket_add_host(listen_socket, param->value,
- port, error_r);
+ return listen_socket->AddHost(param->value, port, error_r);
}
}
@@ -78,7 +81,7 @@ listen_systemd_activation(GError **error_r)
for (int i = SD_LISTEN_FDS_START, end = SD_LISTEN_FDS_START + n;
i != end; ++i)
- if (!server_socket_add_fd(listen_socket, i, error_r))
+ if (!listen_socket->AddFD(i, error_r))
return false;
return true;
@@ -91,13 +94,15 @@ listen_systemd_activation(GError **error_r)
bool
listen_global_init(GError **error_r)
{
+ assert(main_loop != nullptr);
+
int port = config_get_positive(CONF_PORT, DEFAULT_PORT);
const struct config_param *param =
config_get_next_param(CONF_BIND_TO_ADDRESS, NULL);
bool success;
GError *error = NULL;
- listen_socket = server_socket_new(listen_callback, NULL);
+ listen_socket = new ClientListener();
if (listen_systemd_activation(&error))
return true;
@@ -114,6 +119,7 @@ listen_global_init(GError **error_r)
do {
success = listen_add_config_param(port, param, &error);
if (!success) {
+ delete listen_socket;
g_propagate_prefixed_error(error_r, error,
"Failed to listen on %s (line %i): ",
param->value, param->line);
@@ -127,8 +133,9 @@ listen_global_init(GError **error_r)
/* no "bind_to_address" configured, bind the
configured port on all interfaces */
- success = server_socket_add_port(listen_socket, port, error_r);
+ success = listen_socket->AddPort(port, error_r);
if (!success) {
+ delete listen_socket;
g_propagate_prefixed_error(error_r, error,
"Failed to listen on *:%d: ",
port);
@@ -136,8 +143,10 @@ listen_global_init(GError **error_r)
}
}
- if (!server_socket_open(listen_socket, error_r))
+ if (!listen_socket->Open(error_r)) {
+ delete listen_socket;
return false;
+ }
listen_port = port;
return true;
@@ -149,5 +158,5 @@ void listen_global_finish(void)
assert(listen_socket != NULL);
- server_socket_free(listen_socket);
+ delete listen_socket;
}
diff --git a/src/listen.h b/src/Listen.hxx
index 246e8370..fd553477 100644
--- a/src/listen.h
+++ b/src/Listen.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_LISTEN_H
-#define MPD_LISTEN_H
+#ifndef MPD_LISTEN_HXX
+#define MPD_LISTEN_HXX
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/log.c b/src/Log.cxx
index 2d3c7caf..8a50e1ef 100644
--- a/src/log.c
+++ b/src/Log.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,8 @@
*/
#include "config.h"
-#include "log.h"
+#include "Log.hxx"
#include "conf.h"
-#include "utils.h"
#include "fd_util.h"
#include "mpd_error.h"
@@ -262,8 +261,7 @@ log_init(bool verbose, bool use_stdout, GError **error_r)
return true;
#else
g_set_error(error_r, log_quark(), 0,
- "config parameter \"%s\" not found",
- CONF_LOG_FILE);
+ "config parameter 'log_file' not found");
return false;
#endif
#ifdef HAVE_SYSLOG
diff --git a/src/log.h b/src/Log.hxx
index 683ff3e9..cfcece10 100644
--- a/src/log.h
+++ b/src/Log.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_LOG_H
-#define MPD_LOG_H
+#ifndef MPD_LOG_HXX
+#define MPD_LOG_HXX
#include <glib.h>
#include <stdbool.h>
diff --git a/src/main.c b/src/Main.cxx
index 12f8d86f..9b6630d6 100644
--- a/src/main.c
+++ b/src/Main.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,55 +18,59 @@
*/
#include "config.h"
-#include "main.h"
-#include "daemon.h"
-#include "io_thread.h"
-#include "client.h"
-#include "client_idle.h"
-#include "idle.h"
-#include "command.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-#include "database.h"
-#include "update.h"
-#include "player_thread.h"
-#include "listen.h"
-#include "cmdline.h"
+#include "Main.hxx"
+#include "CommandLine.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistGlobal.hxx"
+#include "UpdateGlue.hxx"
+#include "MusicChunk.hxx"
+#include "StateFile.hxx"
+#include "PlayerThread.hxx"
+#include "Mapper.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "Permission.hxx"
+#include "Listen.hxx"
+#include "Client.hxx"
+#include "ClientList.hxx"
+#include "AllCommands.hxx"
+#include "Partition.hxx"
+#include "Volume.hxx"
+#include "OutputAll.hxx"
+#include "tag.h"
#include "conf.h"
-#include "path.h"
-#include "mapper.h"
-#include "chunk.h"
-#include "player_control.h"
+#include "replay_gain_config.h"
+#include "Idle.hxx"
+#include "SignalHandlers.hxx"
+#include "Log.hxx"
+#include "GlobalEvents.hxx"
+#include "InputInit.hxx"
+#include "event/Loop.hxx"
+#include "IOThread.hxx"
+#include "fs/Path.hxx"
+#include "PlaylistRegistry.hxx"
+#include "ZeroconfGlue.hxx"
+#include "DecoderList.hxx"
+#include "AudioConfig.hxx"
+
+extern "C" {
+#include "daemon.h"
#include "stats.h"
-#include "sig_handlers.h"
-#include "audio_config.h"
-#include "output_all.h"
-#include "volume.h"
-#include "log.h"
-#include "permission.h"
#include "pcm_resample.h"
-#include "replay_gain_config.h"
-#include "decoder_list.h"
-#include "input_init.h"
-#include "playlist_list.h"
-#include "state_file.h"
-#include "tag.h"
-#include "dbUtils.h"
-#include "zeroconf.h"
-#include "event_pipe.h"
-#include "tag_pool.h"
+}
+
#include "mpd_error.h"
#ifdef ENABLE_INOTIFY
-#include "inotify_update.h"
+#include "InotifyUpdate.hxx"
#endif
#ifdef ENABLE_SQLITE
-#include "sticker.h"
+#include "StickerDatabase.hxx"
#endif
#ifdef ENABLE_ARCHIVE
-#include "archive_list.h"
+#include "ArchiveList.hxx"
#endif
#include <glib.h>
@@ -91,11 +95,18 @@ enum {
};
GThread *main_task;
-GMainLoop *main_loop;
+EventLoop *main_loop;
+
+ClientList *client_list;
-GCond *main_cond;
+Partition *global_partition;
-struct player_control *global_player_control;
+static StateFile *state_file;
+
+static inline GQuark main_quark()
+{
+ return g_quark_from_static_string ("main");
+}
static bool
glue_daemonize_init(const struct options *options, GError **error_r)
@@ -138,7 +149,10 @@ glue_mapper_init(GError **error_r)
if (music_dir == NULL)
music_dir = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
- mapper_init(music_dir, playlist_dir);
+ if (!mapper_init(music_dir, playlist_dir, &error)) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
g_free(music_dir);
g_free(playlist_dir);
@@ -153,31 +167,44 @@ glue_mapper_init(GError **error_r)
static bool
glue_db_init_and_load(void)
{
+ const struct config_param *param = config_get_param(CONF_DATABASE);
const struct config_param *path = config_get_param(CONF_DB_FILE);
+ if (param != NULL && path != NULL)
+ g_message("Found both 'database' and 'db_file' setting - ignoring the latter");
+
GError *error = NULL;
bool ret;
if (!mapper_has_music_directory()) {
+ if (param != NULL)
+ g_message("Found database setting without "
+ "music_directory - disabling database");
if (path != NULL)
- g_message("Found " CONF_DB_FILE " setting without "
- CONF_MUSIC_DIR " - disabling database");
- db_init(NULL, NULL);
+ g_message("Found db_file setting without "
+ "music_directory - disabling database");
return true;
}
- if (path == NULL)
- MPD_ERROR(CONF_DB_FILE " setting missing");
+ struct config_param *allocated = NULL;
+
+ if (param == NULL && path != NULL) {
+ allocated = new config_param("database", path->line);
+ allocated->AddBlockParam("path", path->value, path->line);
+ param = allocated;
+ }
- if (!db_init(path, &error))
+ if (!DatabaseGlobalInit(param, &error))
MPD_ERROR("%s", error->message);
- ret = db_load(&error);
+ delete allocated;
+
+ ret = DatabaseGlobalOpen(&error);
if (!ret)
MPD_ERROR("%s", error->message);
/* run database update after daemonization? */
- return db_exists();
+ return !db_is_simple() || db_exists();
}
/**
@@ -205,14 +232,29 @@ glue_state_file_init(GError **error_r)
GError *error = NULL;
char *path = config_dup_path(CONF_STATE_FILE, &error);
- if (path == NULL && error != NULL) {
- g_propagate_error(error_r, error);
+ if (path == nullptr) {
+ if (error != nullptr) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ return true;
+ }
+
+ Path path_fs = Path::FromUTF8(path);
+
+ if (path_fs.IsNull()) {
+ g_free(path);
+ g_set_error(error_r, main_quark(), 0,
+ "Failed to convert state file path to FS encoding");
return false;
}
- state_file_init(path, global_player_control);
+ state_file = new StateFile(std::move(path_fs), path,
+ *global_partition, *main_loop);
g_free(path);
+ state_file->Read();
return true;
}
@@ -285,29 +327,35 @@ initialize_decoder_and_player(void)
if (buffered_before_play > buffered_chunks)
buffered_before_play = buffered_chunks;
- global_player_control = pc_new(buffered_chunks, buffered_before_play);
+ const unsigned max_length =
+ config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
+ DEFAULT_PLAYLIST_MAX_LENGTH);
+
+ global_partition = new Partition(max_length,
+ buffered_chunks,
+ buffered_before_play);
}
/**
- * event_pipe callback function for PIPE_EVENT_IDLE
+ * Handler for GlobalEvents::IDLE.
*/
static void
idle_event_emitted(void)
{
- /* send "idle" notificaions to all subscribed
+ /* send "idle" notifications to all subscribed
clients */
unsigned flags = idle_get();
if (flags != 0)
- client_manager_idle_add(flags);
+ client_list->IdleAdd(flags);
}
/**
- * event_pipe callback function for PIPE_EVENT_SHUTDOWN
+ * Handler for GlobalEvents::SHUTDOWN.
*/
static void
shutdown_event_emitted(void)
{
- g_main_loop_quit(main_loop);
+ main_loop->Break();
}
int main(int argc, char *argv[])
@@ -341,8 +389,6 @@ int mpd_main(int argc, char *argv[])
io_thread_init();
winsock_init();
- idle_init();
- tag_pool_init();
config_global_init();
success = parse_cmdline(argc, argv, &options, &error);
@@ -367,6 +413,12 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
+ main_task = g_thread_self();
+ main_loop = new EventLoop(EventLoop::Default());
+
+ const unsigned max_clients = config_get_positive(CONF_MAX_CONN, 10);
+ client_list = new ClientList(max_clients);
+
success = listen_global_init(&error);
if (!success) {
g_warning("%s", error->message);
@@ -376,15 +428,11 @@ int mpd_main(int argc, char *argv[])
daemonize_set_user();
- main_task = g_thread_self();
- main_loop = g_main_loop_new(NULL, FALSE);
- main_cond = g_cond_new();
-
- event_pipe_init();
- event_pipe_register(PIPE_EVENT_IDLE, idle_event_emitted);
- event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
+ GlobalEvents::Initialize();
+ GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted);
+ GlobalEvents::Register(GlobalEvents::SHUTDOWN, shutdown_event_emitted);
- path_global_init();
+ Path::GlobalInit();
if (!glue_mapper_init(&error)) {
g_warning("%s", error->message);
@@ -416,7 +464,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player();
volume_init();
initAudioConfig();
- audio_output_all_init(global_player_control);
+ audio_output_all_init(&global_partition->pc);
client_manager_init();
replay_gain_global_init();
@@ -440,9 +488,9 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
- initZeroconf();
+ ZeroconfInit(*main_loop);
- player_create(global_player_control);
+ player_create(&global_partition->pc);
if (create_db) {
/* the database failed to load: recreate the
@@ -458,6 +506,8 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(global_partition->playlist.queue.random));
+
success = config_get_bool(CONF_AUTO_UPDATE, false);
#ifdef ENABLE_INOTIFY
if (success && mapper_has_music_directory())
@@ -472,14 +522,14 @@ int mpd_main(int argc, char *argv[])
/* enable all audio outputs (if not already done by
playlist_state_restore() */
- pc_update_audio(global_player_control);
+ global_partition->pc.UpdateAudio();
#ifdef WIN32
win32_app_started();
#endif
/* run the main loop */
- g_main_loop_run(main_loop);
+ main_loop->Run();
#ifdef WIN32
win32_app_stopping();
@@ -487,21 +537,22 @@ int mpd_main(int argc, char *argv[])
/* cleanup */
- g_main_loop_unref(main_loop);
-
#ifdef ENABLE_INOTIFY
mpd_inotify_finish();
#endif
- state_file_finish(global_player_control);
- pc_kill(global_player_control);
- finishZeroconf();
- client_manager_deinit();
+ if (state_file != nullptr) {
+ state_file->Write();
+ delete state_file;
+ }
+
+ global_partition->pc.Kill();
+ ZeroconfDeinit();
listen_global_finish();
- playlist_global_finish();
+ delete client_list;
start = clock();
- db_finish();
+ DatabaseGlobalDeinit();
g_debug("db_finish took %f seconds",
((float)(clock()-start))/CLOCKS_PER_SEC);
@@ -509,17 +560,14 @@ int mpd_main(int argc, char *argv[])
sticker_global_finish();
#endif
- g_cond_free(main_cond);
- event_pipe_deinit();
+ GlobalEvents::Deinitialize();
playlist_list_global_finish();
input_stream_global_finish();
audio_output_all_finish();
volume_finish();
mapper_finish();
- path_global_finish();
- finishPermissions();
- pc_free(global_player_control);
+ delete global_partition;
command_finish();
update_global_finish();
decoder_plugin_deinit_all();
@@ -527,10 +575,9 @@ int mpd_main(int argc, char *argv[])
archive_plugin_deinit_all();
#endif
config_global_finish();
- tag_pool_deinit();
- idle_deinit();
stats_global_finish();
io_thread_deinit();
+ delete main_loop;
daemonize_finish();
#ifdef WIN32
WSACleanup();
diff --git a/src/main.h b/src/Main.hxx
index 2a7d7591..b2768600 100644
--- a/src/main.h
+++ b/src/Main.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,18 +17,20 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MAIN_H
-#define MAIN_H
+#ifndef MPD_MAIN_HXX
+#define MPD_MAIN_HXX
#include <glib.h>
+class EventLoop;
+
extern GThread *main_task;
-extern GMainLoop *main_loop;
+extern EventLoop *main_loop;
-extern GCond *main_cond;
+extern class ClientList *client_list;
-extern struct player_control *global_player_control;
+extern struct Partition *global_partition;
/**
* A entry point for application.
@@ -52,7 +54,7 @@ win32_main(int argc, char *argv[]);
* When running as a service reports to service control manager
* that our service is started.
* When running as a console application enables console handler that will
- * trigger PIPE_EVENT_SHUTDOWN when user closes console window
+ * trigger GlobalEvents::SHUTDOWN when user closes console window
* or presses Ctrl+C.
* This function should be called just before entering main loop.
*/
diff --git a/src/Mapper.cxx b/src/Mapper.cxx
new file mode 100644
index 00000000..559df0d2
--- /dev/null
+++ b/src/Mapper.cxx
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Maps directory and song objects to file system paths.
+ */
+
+#include "config.h"
+#include "Mapper.hxx"
+#include "Directory.hxx"
+#include "song.h"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+#include "fs/DirectoryReader.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+
+/**
+ * The absolute path of the music directory encoded in UTF-8.
+ */
+static char *music_dir_utf8;
+static size_t music_dir_utf8_length;
+
+/**
+ * The absolute path of the music directory encoded in the filesystem
+ * character set.
+ */
+static Path music_dir_fs = Path::Null();
+static size_t music_dir_fs_length;
+
+/**
+ * The absolute path of the playlist directory encoded in the
+ * filesystem character set.
+ */
+static Path playlist_dir_fs = Path::Null();
+
+static inline GQuark
+mapper_quark()
+{
+ return g_quark_from_static_string ("mapper");
+}
+
+/**
+ * Duplicate a string, chop all trailing slashes.
+ */
+static char *
+strdup_chop_slash(const char *path_fs)
+{
+ size_t length = strlen(path_fs);
+
+ while (length > 0 && path_fs[length - 1] == G_DIR_SEPARATOR)
+ --length;
+
+ return g_strndup(path_fs, length);
+}
+
+static void
+check_directory(const char *path_utf8, const Path &path_fs)
+{
+ struct stat st;
+ if (!StatFile(path_fs, st)) {
+ g_warning("Failed to stat directory \"%s\": %s",
+ path_utf8, g_strerror(errno));
+ return;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ g_warning("Not a directory: %s", path_utf8);
+ return;
+ }
+
+#ifndef WIN32
+ const Path x = Path::Build(path_fs, ".");
+ if (!StatFile(x, st) && errno == EACCES)
+ g_warning("No permission to traverse (\"execute\") directory: %s",
+ path_utf8);
+#endif
+
+ const DirectoryReader reader(path_fs);
+ if (reader.Failed() && errno == EACCES)
+ g_warning("No permission to read directory: %s", path_utf8);
+}
+
+static bool
+mapper_set_music_dir(const char *path_utf8, GError **error_r)
+{
+ music_dir_fs = Path::FromUTF8(path_utf8);
+ if (music_dir_fs.IsNull()) {
+ g_set_error(error_r, mapper_quark(), 0,
+ "Failed to convert music path to FS encoding");
+ return false;
+ }
+
+ music_dir_fs_length = music_dir_fs.length();
+
+ music_dir_utf8 = strdup_chop_slash(path_utf8);
+ music_dir_utf8_length = strlen(music_dir_utf8);
+
+ check_directory(path_utf8, music_dir_fs);
+
+ return true;
+}
+
+static bool
+mapper_set_playlist_dir(const char *path_utf8, GError **error_r)
+{
+ playlist_dir_fs = Path::FromUTF8(path_utf8);
+ if (playlist_dir_fs.IsNull()) {
+ g_set_error(error_r, mapper_quark(), 0,
+ "Failed to convert playlist path to FS encoding");
+ return false;
+ }
+
+ check_directory(path_utf8, playlist_dir_fs);
+ return true;
+}
+
+bool mapper_init(const char *_music_dir, const char *_playlist_dir,
+ GError **error_r)
+{
+ if (_music_dir != NULL)
+ if (!mapper_set_music_dir(_music_dir, error_r))
+ return false;
+
+ if (_playlist_dir != NULL)
+ if (!mapper_set_playlist_dir(_playlist_dir, error_r))
+ return false;
+
+ return true;
+}
+
+void mapper_finish(void)
+{
+ g_free(music_dir_utf8);
+}
+
+const char *
+mapper_get_music_directory_utf8(void)
+{
+ return music_dir_utf8;
+}
+
+const Path &
+mapper_get_music_directory_fs(void)
+{
+ return music_dir_fs;
+}
+
+const char *
+map_to_relative_path(const char *path_utf8)
+{
+ return music_dir_utf8 != NULL &&
+ memcmp(path_utf8, music_dir_utf8,
+ music_dir_utf8_length) == 0 &&
+ G_IS_DIR_SEPARATOR(path_utf8[music_dir_utf8_length])
+ ? path_utf8 + music_dir_utf8_length + 1
+ : path_utf8;
+}
+
+Path
+map_uri_fs(const char *uri)
+{
+ assert(uri != NULL);
+ assert(*uri != '/');
+
+ if (music_dir_fs.IsNull())
+ return Path::Null();
+
+ const Path uri_fs = Path::FromUTF8(uri);
+ if (uri_fs.IsNull())
+ return Path::Null();
+
+ return Path::Build(music_dir_fs, uri_fs);
+}
+
+Path
+map_directory_fs(const Directory *directory)
+{
+ assert(music_dir_utf8 != NULL);
+ assert(!music_dir_fs.IsNull());
+
+ if (directory->IsRoot())
+ return music_dir_fs;
+
+ return map_uri_fs(directory->GetPath());
+}
+
+Path
+map_directory_child_fs(const Directory *directory, const char *name)
+{
+ assert(music_dir_utf8 != NULL);
+ assert(!music_dir_fs.IsNull());
+
+ /* check for invalid or unauthorized base names */
+ if (*name == 0 || strchr(name, '/') != NULL ||
+ strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ return Path::Null();
+
+ const Path parent_fs = map_directory_fs(directory);
+ if (parent_fs.IsNull())
+ return Path::Null();
+
+ const Path name_fs = Path::FromUTF8(name);
+ if (name_fs.IsNull())
+ return Path::Null();
+
+ return Path::Build(parent_fs, name_fs);
+}
+
+/**
+ * Map a song object that was created by song_dup_detached(). It does
+ * not have a real parent directory, only the dummy object
+ * #detached_root.
+ */
+static Path
+map_detached_song_fs(const char *uri_utf8)
+{
+ Path uri_fs = Path::FromUTF8(uri_utf8);
+ if (uri_fs.IsNull())
+ return Path::Null();
+
+ return Path::Build(music_dir_fs, uri_fs);
+}
+
+Path
+map_song_fs(const struct song *song)
+{
+ assert(song_is_file(song));
+
+ if (song_in_database(song))
+ return song_is_detached(song)
+ ? map_detached_song_fs(song->uri)
+ : map_directory_child_fs(song->parent, song->uri);
+ else
+ return Path::FromUTF8(song->uri);
+}
+
+char *
+map_fs_to_utf8(const char *path_fs)
+{
+ if (!music_dir_fs.IsNull() &&
+ strncmp(path_fs, music_dir_fs.c_str(), music_dir_fs_length) == 0 &&
+ G_IS_DIR_SEPARATOR(path_fs[music_dir_fs_length]))
+ /* remove musicDir prefix */
+ path_fs += music_dir_fs_length + 1;
+ else if (G_IS_DIR_SEPARATOR(path_fs[0]))
+ /* not within musicDir */
+ return NULL;
+
+ while (path_fs[0] == G_DIR_SEPARATOR)
+ ++path_fs;
+
+ const std::string path_utf8 = Path::ToUTF8(path_fs);
+ if (path_utf8.empty())
+ return nullptr;
+
+ return g_strdup(path_utf8.c_str());
+}
+
+const Path &
+map_spl_path(void)
+{
+ return playlist_dir_fs;
+}
+
+Path
+map_spl_utf8_to_fs(const char *name)
+{
+ if (playlist_dir_fs.IsNull())
+ return Path::Null();
+
+ char *filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL);
+ const Path filename_fs = Path::FromUTF8(filename_utf8);
+ g_free(filename_utf8);
+ if (filename_fs.IsNull())
+ return Path::Null();
+
+ return Path::Build(playlist_dir_fs, filename_fs);
+}
diff --git a/src/mapper.h b/src/Mapper.hxx
index d6184a17..af6c84cc 100644
--- a/src/mapper.h
+++ b/src/Mapper.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,25 +21,27 @@
* Maps directory and song objects to file system paths.
*/
-#ifndef MPD_MAPPER_H
-#define MPD_MAPPER_H
+#ifndef MPD_MAPPER_HXX
+#define MPD_MAPPER_HXX
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
+#include "gerror.h"
#define PLAYLIST_FILE_SUFFIX ".m3u"
-struct directory;
+class Path;
+struct Directory;
struct song;
-void mapper_init(const char *_music_dir, const char *_playlist_dir);
+bool mapper_init(const char *_music_dir, const char *_playlist_dir,
+ GError **error_r);
void mapper_finish(void);
/**
* Return the absolute path of the music directory encoded in UTF-8.
*/
-G_GNUC_CONST
+gcc_const
const char *
mapper_get_music_directory_utf8(void);
@@ -47,18 +49,18 @@ mapper_get_music_directory_utf8(void);
* Return the absolute path of the music directory encoded in the
* filesystem character set.
*/
-G_GNUC_CONST
-const char *
+gcc_const
+const Path &
mapper_get_music_directory_fs(void);
/**
* Returns true if a music directory was configured.
*/
-G_GNUC_CONST
+gcc_const
static inline bool
mapper_has_music_directory(void)
{
- return mapper_get_music_directory_utf8() != NULL;
+ return mapper_get_music_directory_utf8() != nullptr;
}
/**
@@ -66,7 +68,7 @@ mapper_has_music_directory(void)
* this function converts it to a relative path. If not, it returns
* the unmodified string pointer.
*/
-G_GNUC_PURE
+gcc_pure
const char *
map_to_relative_path(const char *path_utf8);
@@ -75,19 +77,19 @@ map_to_relative_path(const char *path_utf8);
* is basically done by converting the URI to the file system charset
* and prepending the music directory.
*/
-G_GNUC_MALLOC
-char *
+gcc_pure
+Path
map_uri_fs(const char *uri);
/**
* Determines the file system path of a directory object.
*
* @param directory the directory object
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
-char *
-map_directory_fs(const struct directory *directory);
+gcc_pure
+Path
+map_directory_fs(const Directory *directory);
/**
* Determines the file system path of a directory's child (may be a
@@ -95,21 +97,21 @@ map_directory_fs(const struct directory *directory);
*
* @param directory the parent directory object
* @param name the child's name in UTF-8
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
-char *
-map_directory_child_fs(const struct directory *directory, const char *name);
+gcc_pure
+Path
+map_directory_child_fs(const Directory *directory, const char *name);
/**
* Determines the file system path of a song. This must not be a
* remote song.
*
* @param song the song object
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
-char *
+gcc_pure
+Path
map_song_fs(const struct song *song);
/**
@@ -117,17 +119,17 @@ map_song_fs(const struct song *song);
* absolute) to a relative path in UTF-8 encoding.
*
* @param path_fs a path in file system encoding
- * @return the relative path in UTF-8, or NULL if mapping failed
+ * @return the relative path in UTF-8, or nullptr if mapping failed
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
map_fs_to_utf8(const char *path_fs);
/**
* Returns the playlist directory.
*/
-G_GNUC_CONST
-const char *
+gcc_const
+const Path &
map_spl_path(void);
/**
@@ -135,10 +137,10 @@ map_spl_path(void);
* path. The return value is allocated on the heap and must be freed
* with g_free().
*
- * @return the path in file system encoding, or NULL if mapping failed
+ * @return the path in file system encoding, or nullptr if mapping failed
*/
-G_GNUC_PURE
-char *
+gcc_pure
+Path
map_spl_utf8_to_fs(const char *name);
#endif
diff --git a/src/MessageCommands.cxx b/src/MessageCommands.cxx
new file mode 100644
index 00000000..f19a1b5d
--- /dev/null
+++ b/src/MessageCommands.cxx
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MessageCommands.hxx"
+#include "ClientSubscribe.hxx"
+#include "ClientInternal.hxx"
+#include "ClientList.hxx"
+#include "Main.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+#include <set>
+#include <string>
+
+#include <assert.h>
+
+enum command_return
+handle_subscribe(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ switch (client_subscribe(client, argv[1])) {
+ case CLIENT_SUBSCRIBE_OK:
+ return COMMAND_RETURN_OK;
+
+ case CLIENT_SUBSCRIBE_INVALID:
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_ALREADY:
+ command_error(client, ACK_ERROR_EXIST,
+ "already subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_FULL:
+ command_error(client, ACK_ERROR_EXIST,
+ "subscription list is full");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* unreachable */
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_unsubscribe(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ if (client_unsubscribe(client, argv[1]))
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "not subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_channels(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ std::set<std::string> channels;
+ for (const auto &c : *client_list)
+ channels.insert(c->subscriptions.begin(),
+ c->subscriptions.end());
+
+ for (const auto &channel : channels)
+ client_printf(client, "channel: %s\n", channel.c_str());
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_read_messages(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ while (!client->messages.empty()) {
+ const ClientMessage &msg = client->messages.front();
+
+ client_printf(client, "channel: %s\nmessage: %s\n",
+ msg.GetChannel(), msg.GetMessage());
+ client->messages.pop_front();
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_send_message(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 3);
+
+ if (!client_message_valid_channel_name(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ bool sent = false;
+ const ClientMessage msg(argv[1], argv[2]);
+ for (const auto &c : *client_list)
+ if (client_push_message(c, msg))
+ sent = true;
+
+ if (sent)
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "nobody is subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
diff --git a/src/MessageCommands.hxx b/src/MessageCommands.hxx
new file mode 100644
index 00000000..b10f3d8e
--- /dev/null
+++ b/src/MessageCommands.hxx
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_MESSAGE_COMMANDS_HXX
+#define MPD_MESSAGE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_subscribe(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_unsubscribe(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_channels(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_read_messages(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_send_message(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/mixer_all.c b/src/MixerAll.cxx
index 95ba9079..00343a1a 100644
--- a/src/mixer_all.c
+++ b/src/MixerAll.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,16 @@
*/
#include "config.h"
-#include "mixer_all.h"
-#include "mixer_control.h"
-#include "output_all.h"
-#include "output_plugin.h"
+#include "MixerAll.hxx"
+#include "MixerControl.hxx"
+#include "MixerInternal.hxx"
+#include "MixerList.hxx"
+#include "OutputAll.hxx"
+#include "PcmVolume.hxx"
+
+extern "C" {
#include "output_internal.h"
-#include "pcm_volume.h"
-#include "mixer_api.h"
-#include "mixer_list.h"
+}
#include <glib.h>
diff --git a/src/mixer_all.h b/src/MixerAll.hxx
index fe873e71..23350a84 100644
--- a/src/mixer_all.h
+++ b/src/MixerAll.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,8 @@
* Functions which affect the mixers of all audio outputs.
*/
-#ifndef MPD_MIXER_ALL_H
-#define MPD_MIXER_ALL_H
-
-#include <stdbool.h>
+#ifndef MPD_MIXER_ALL_HXX
+#define MPD_MIXER_ALL_HXX
/**
* Returns the average volume of all available mixers (range 0..100).
diff --git a/src/mixer_control.c b/src/MixerControl.cxx
index 3e984dd0..4ca1c76e 100644
--- a/src/mixer_control.c
+++ b/src/MixerControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "mixer_control.h"
-#include "mixer_api.h"
+#include "MixerControl.hxx"
+#include "MixerInternal.hxx"
#include <assert.h>
#include <stddef.h>
diff --git a/src/mixer_control.h b/src/MixerControl.hxx
index 6c3468ac..ee1e959d 100644
--- a/src/mixer_control.h
+++ b/src/MixerControl.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,17 +22,19 @@
* Functions which manipulate a #mixer object.
*/
-#ifndef MPD_MIXER_CONTROL_H
-#define MPD_MIXER_CONTROL_H
+#ifndef MPD_MIXER_CONTROL_HXX
+#define MPD_MIXER_CONTROL_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "gerror.h"
struct mixer;
struct mixer_plugin;
struct config_param;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct mixer *
mixer_new(const struct mixer_plugin *plugin, void *ao,
const struct config_param *param,
@@ -60,4 +62,8 @@ mixer_get_volume(struct mixer *mixer, GError **error_r);
bool
mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/mixer_api.c b/src/MixerInternal.cxx
index c85916c9..b8729fc7 100644
--- a/src/mixer_api.c
+++ b/src/MixerInternal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "mixer_api.h"
+#include "MixerInternal.hxx"
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "mixer"
diff --git a/src/mixer_api.h b/src/MixerInternal.hxx
index 29c1e00c..9c39e3e3 100644
--- a/src/mixer_api.h
+++ b/src/MixerInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_MIXER_H
-#define MPD_MIXER_H
+#ifndef MPD_MIXER_INTERNAL_HXX
+#define MPD_MIXER_INTERNAL_HXX
-#include "mixer_plugin.h"
-#include "mixer_list.h"
+#include "MixerPlugin.hxx"
+#include "MixerList.hxx"
#include <glib.h>
diff --git a/src/mixer_list.h b/src/MixerList.hxx
index 078358ec..440f442b 100644
--- a/src/mixer_list.h
+++ b/src/MixerList.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,8 +22,8 @@
* This header provides "extern" declarations for all mixer plugins.
*/
-#ifndef MPD_MIXER_LIST_H
-#define MPD_MIXER_LIST_H
+#ifndef MPD_MIXER_LIST_HXX
+#define MPD_MIXER_LIST_HXX
extern const struct mixer_plugin software_mixer_plugin;
extern const struct mixer_plugin alsa_mixer_plugin;
diff --git a/src/mixer_plugin.h b/src/MixerPlugin.hxx
index 9532b95c..1fbdfbbc 100644
--- a/src/mixer_plugin.h
+++ b/src/MixerPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,14 +20,14 @@
/** \file
*
* This header declares the mixer_plugin class. It should not be
- * included directly; use mixer_api.h instead in mixer
+ * included directly; use MixerInternal.hxx instead in mixer
* implementations.
*/
-#ifndef MPD_MIXER_PLUGIN_H
-#define MPD_MIXER_PLUGIN_H
+#ifndef MPD_MIXER_PLUGIN_HXX
+#define MPD_MIXER_PLUGIN_HXX
-#include <glib.h>
+#include "gerror.h"
#include <stdbool.h>
diff --git a/src/mixer_type.c b/src/MixerType.cxx
index a479caf1..43507979 100644
--- a/src/mixer_type.c
+++ b/src/MixerType.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "mixer_type.h"
+#include "MixerType.hxx"
#include <assert.h>
#include <string.h>
diff --git a/src/mixer_type.h b/src/MixerType.hxx
index 15d136b5..320a36c0 100644
--- a/src/mixer_type.h
+++ b/src/MixerType.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_MIXER_TYPE_H
-#define MPD_MIXER_TYPE_H
+#ifndef MPD_MIXER_TYPE_HXX
+#define MPD_MIXER_TYPE_HXX
enum mixer_type {
/** parser error */
diff --git a/src/MusicBuffer.cxx b/src/MusicBuffer.cxx
new file mode 100644
index 00000000..ea03fc0b
--- /dev/null
+++ b/src/MusicBuffer.cxx
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "thread/Mutex.hxx"
+#include "util/SliceBuffer.hxx"
+#include "mpd_error.h"
+
+#include <assert.h>
+
+struct music_buffer : public SliceBuffer<music_chunk> {
+ /** a mutex which protects #available */
+ Mutex mutex;
+
+ music_buffer(unsigned num_chunks)
+ :SliceBuffer(num_chunks) {
+ if (IsOOM())
+ MPD_ERROR("Failed to allocate buffer");
+ }
+};
+
+struct music_buffer *
+music_buffer_new(unsigned num_chunks)
+{
+ return new music_buffer(num_chunks);
+}
+
+void
+music_buffer_free(struct music_buffer *buffer)
+{
+ delete buffer;
+}
+
+unsigned
+music_buffer_size(const struct music_buffer *buffer)
+{
+ return buffer->GetCapacity();
+}
+
+struct music_chunk *
+music_buffer_allocate(struct music_buffer *buffer)
+{
+ const ScopeLock protect(buffer->mutex);
+ return buffer->Allocate();
+}
+
+void
+music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
+{
+ assert(buffer != NULL);
+ assert(chunk != NULL);
+
+ const ScopeLock protect(buffer->mutex);
+
+ if (chunk->other != nullptr) {
+ assert(chunk->other->other == nullptr);
+ buffer->Free(chunk->other);
+ }
+
+ buffer->Free(chunk);
+}
diff --git a/src/buffer.h b/src/MusicBuffer.hxx
index f860231e..cc03dfcb 100644
--- a/src/buffer.h
+++ b/src/MusicBuffer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_MUSIC_BUFFER_H
-#define MPD_MUSIC_BUFFER_H
+#ifndef MPD_MUSIC_BUFFER_HXX
+#define MPD_MUSIC_BUFFER_HXX
/**
* An allocator for #music_chunk objects.
diff --git a/src/MusicChunk.cxx b/src/MusicChunk.cxx
new file mode 100644
index 00000000..eefda24b
--- /dev/null
+++ b/src/MusicChunk.cxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MusicChunk.hxx"
+#include "audio_format.h"
+#include "tag.h"
+
+#include <assert.h>
+
+music_chunk::~music_chunk()
+{
+ if (tag != NULL)
+ tag_free(tag);
+}
+
+#ifndef NDEBUG
+bool
+music_chunk::CheckFormat(const struct audio_format &other_format) const
+{
+ assert(audio_format_valid(&other_format));
+
+ return length == 0 ||
+ audio_format_equals(&audio_format, &other_format);
+}
+#endif
+
+void *
+music_chunk::Write(const struct audio_format &af,
+ float data_time, uint16_t _bit_rate,
+ size_t *max_length_r)
+{
+ assert(CheckFormat(af));
+ assert(length == 0 || audio_format_valid(&audio_format));
+
+ if (length == 0) {
+ /* if the chunk is empty, nobody has set bitRate and
+ times yet */
+
+ bit_rate = _bit_rate;
+ times = data_time;
+ }
+
+ const size_t frame_size = audio_format_frame_size(&af);
+ size_t num_frames = (sizeof(data) - length) / frame_size;
+ if (num_frames == 0)
+ return NULL;
+
+#ifndef NDEBUG
+ audio_format = af;
+#endif
+
+ *max_length_r = num_frames * frame_size;
+ return data + length;
+}
+
+bool
+music_chunk::Expand(const struct audio_format &af, size_t _length)
+{
+ const size_t frame_size = audio_format_frame_size(&af);
+
+ assert(length + _length <= sizeof(data));
+ assert(audio_format_equals(&audio_format, &af));
+
+ length += _length;
+
+ return length + frame_size > sizeof(data);
+}
diff --git a/src/chunk.h b/src/MusicChunk.hxx
index a06a203e..c03e4551 100644
--- a/src/chunk.h
+++ b/src/MusicChunk.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CHUNK_H
-#define MPD_CHUNK_H
+#ifndef MPD_MUSIC_CHUNK_HXX
+#define MPD_MUSIC_CHUNK_HXX
#include "replay_gain_info.h"
@@ -26,7 +26,6 @@
#include "audio_format.h"
#endif
-#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
@@ -92,60 +91,63 @@ struct music_chunk {
#ifndef NDEBUG
struct audio_format audio_format;
#endif
-};
-void
-music_chunk_init(struct music_chunk *chunk);
+ music_chunk()
+ :other(nullptr),
+ length(0),
+ tag(nullptr),
+ replay_gain_serial(0) {}
-void
-music_chunk_free(struct music_chunk *chunk);
+ ~music_chunk();
-static inline bool
-music_chunk_is_empty(const struct music_chunk *chunk)
-{
- return chunk->length == 0 && chunk->tag == NULL;
-}
+ bool IsEmpty() const {
+ return length == 0 && tag == nullptr;
+ }
#ifndef NDEBUG
-/**
- * Checks if the audio format if the chunk is equal to the specified
- * audio_format.
- */
-bool
-music_chunk_check_format(const struct music_chunk *chunk,
- const struct audio_format *audio_format);
+ /**
+ * Checks if the audio format if the chunk is equal to the
+ * specified audio_format.
+ */
+ gcc_pure
+ bool CheckFormat(const struct audio_format &audio_format) const;
#endif
-/**
- * Prepares appending to the music chunk. Returns a buffer where you
- * may write into. After you are finished, call music_chunk_expand().
- *
- * @param chunk the music_chunk object
- * @param audio_format the audio format for the appended data; must
- * stay the same for the life cycle of this chunk
- * @param data_time the time within the song
- * @param bit_rate the current bit rate of the source file
- * @param max_length_r the maximum write length is returned here
- * @return a writable buffer, or NULL if the chunk is full
- */
-void *
-music_chunk_write(struct music_chunk *chunk,
- const struct audio_format *audio_format,
- float data_time, uint16_t bit_rate,
- size_t *max_length_r);
+ /**
+ * Prepares appending to the music chunk. Returns a buffer
+ * where you may write into. After you are finished, call
+ * music_chunk_expand().
+ *
+ * @param chunk the music_chunk object
+ * @param audio_format the audio format for the appended data;
+ * must stay the same for the life cycle of this chunk
+ * @param data_time the time within the song
+ * @param bit_rate the current bit rate of the source file
+ * @param max_length_r the maximum write length is returned
+ * here
+ * @return a writable buffer, or NULL if the chunk is full
+ */
+ void *Write(const struct audio_format &af,
+ float data_time, uint16_t bit_rate,
+ size_t *max_length_r);
-/**
- * Increases the length of the chunk after the caller has written to
- * the buffer returned by music_chunk_write().
- *
- * @param chunk the music_chunk object
- * @param audio_format the audio format for the appended data; must
- * stay the same for the life cycle of this chunk
- * @param length the number of bytes which were appended
- * @return true if the chunk is full
- */
-bool
-music_chunk_expand(struct music_chunk *chunk,
- const struct audio_format *audio_format, size_t length);
+ /**
+ * Increases the length of the chunk after the caller has written to
+ * the buffer returned by music_chunk_write().
+ *
+ * @param chunk the music_chunk object
+ * @param audio_format the audio format for the appended data; must
+ * stay the same for the life cycle of this chunk
+ * @param length the number of bytes which were appended
+ * @return true if the chunk is full
+ */
+ bool Expand(const struct audio_format &af, size_t length);
+};
+
+void
+music_chunk_init(struct music_chunk *chunk);
+
+void
+music_chunk_free(struct music_chunk *chunk);
#endif
diff --git a/src/pipe.c b/src/MusicPipe.cxx
index d8131432..6f25eff8 100644
--- a/src/pipe.c
+++ b/src/MusicPipe.cxx
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "pipe.h"
-#include "buffer.h"
-#include "chunk.h"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
+#include "thread/Mutex.hxx"
#include <glib.h>
@@ -37,38 +38,35 @@ struct music_pipe {
unsigned size;
/** a mutex which protects #head and #tail_r */
- GMutex *mutex;
+ mutable Mutex mutex;
#ifndef NDEBUG
struct audio_format audio_format;
#endif
+
+ music_pipe()
+ :head(nullptr), tail_r(&head), size(0) {
+#ifndef NDEBUG
+ audio_format_clear(&audio_format);
+#endif
+ }
+
+ ~music_pipe() {
+ assert(head == nullptr);
+ assert(tail_r == &head);
+ }
};
struct music_pipe *
music_pipe_new(void)
{
- struct music_pipe *mp = g_new(struct music_pipe, 1);
-
- mp->head = NULL;
- mp->tail_r = &mp->head;
- mp->size = 0;
- mp->mutex = g_mutex_new();
-
-#ifndef NDEBUG
- audio_format_clear(&mp->audio_format);
-#endif
-
- return mp;
+ return new music_pipe();
}
void
music_pipe_free(struct music_pipe *mp)
{
- assert(mp->head == NULL);
- assert(mp->tail_r == &mp->head);
-
- g_mutex_free(mp->mutex);
- g_free(mp);
+ delete mp;
}
#ifndef NDEBUG
@@ -88,17 +86,12 @@ bool
music_pipe_contains(const struct music_pipe *mp,
const struct music_chunk *chunk)
{
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
for (const struct music_chunk *i = mp->head;
- i != NULL; i = i->next) {
- if (i == chunk) {
- g_mutex_unlock(mp->mutex);
+ i != NULL; i = i->next)
+ if (i == chunk)
return true;
- }
- }
-
- g_mutex_unlock(mp->mutex);
return false;
}
@@ -114,13 +107,11 @@ music_pipe_peek(const struct music_pipe *mp)
struct music_chunk *
music_pipe_shift(struct music_pipe *mp)
{
- struct music_chunk *chunk;
-
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
- chunk = mp->head;
+ struct music_chunk *chunk = mp->head;
if (chunk != NULL) {
- assert(!music_chunk_is_empty(chunk));
+ assert(!chunk->IsEmpty());
mp->head = chunk->next;
--mp->size;
@@ -137,15 +128,13 @@ music_pipe_shift(struct music_pipe *mp)
#ifndef NDEBUG
/* poison the "next" reference */
- chunk->next = (void*)0x01010101;
+ chunk->next = (struct music_chunk *)(void *)0x01010101;
if (mp->size == 0)
audio_format_clear(&mp->audio_format);
#endif
}
- g_mutex_unlock(mp->mutex);
-
return chunk;
}
@@ -161,14 +150,14 @@ music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer)
void
music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
{
- assert(!music_chunk_is_empty(chunk));
+ assert(!chunk->IsEmpty());
assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format));
- g_mutex_lock(mp->mutex);
+ const ScopeLock protect(mp->mutex);
assert(mp->size > 0 || !audio_format_defined(&mp->audio_format));
assert(!audio_format_defined(&mp->audio_format) ||
- music_chunk_check_format(chunk, &mp->audio_format));
+ chunk->CheckFormat(mp->audio_format));
#ifndef NDEBUG
if (!audio_format_defined(&mp->audio_format) && chunk->length > 0)
@@ -180,15 +169,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
mp->tail_r = &chunk->next;
++mp->size;
-
- g_mutex_unlock(mp->mutex);
}
unsigned
music_pipe_size(const struct music_pipe *mp)
{
- g_mutex_lock(mp->mutex);
- unsigned size = mp->size;
- g_mutex_unlock(mp->mutex);
- return size;
+ const ScopeLock protect(mp->mutex);
+ return mp->size;
}
diff --git a/src/pipe.h b/src/MusicPipe.hxx
index 84b9869e..99561ca6 100644
--- a/src/pipe.h
+++ b/src/MusicPipe.hxx
@@ -20,8 +20,7 @@
#ifndef MPD_PIPE_H
#define MPD_PIPE_H
-#include <glib.h>
-#include <stdbool.h>
+#include "gcc.h"
#ifndef NDEBUG
struct audio_format;
@@ -39,7 +38,7 @@ struct music_pipe;
/**
* Creates a new #music_pipe object. It is empty.
*/
-G_GNUC_MALLOC
+gcc_malloc
struct music_pipe *
music_pipe_new(void);
@@ -72,7 +71,7 @@ music_pipe_contains(const struct music_pipe *mp,
* Returns the first #music_chunk from the pipe. Returns NULL if the
* pipe is empty.
*/
-G_GNUC_PURE
+gcc_pure
const struct music_chunk *
music_pipe_peek(const struct music_pipe *mp);
@@ -99,11 +98,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
/**
* Returns the number of chunks currently in this pipe.
*/
-G_GNUC_PURE
+gcc_pure
unsigned
music_pipe_size(const struct music_pipe *mp);
-G_GNUC_PURE
+gcc_pure
static inline bool
music_pipe_empty(const struct music_pipe *mp)
{
diff --git a/src/OtherCommands.cxx b/src/OtherCommands.cxx
new file mode 100644
index 00000000..4909f37f
--- /dev/null
+++ b/src/OtherCommands.cxx
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OtherCommands.hxx"
+#include "DatabaseCommands.hxx"
+#include "CommandError.hxx"
+#include "UpdateGlue.hxx"
+#include "Directory.hxx"
+#include "song.h"
+#include "SongPrint.hxx"
+#include "TagPrint.hxx"
+#include "TimePrint.hxx"
+#include "Mapper.hxx"
+#include "DecoderPrint.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+#include "Volume.hxx"
+
+extern "C" {
+#include "uri.h"
+#include "stats.h"
+}
+
+#include "Permission.hxx"
+#include "PlaylistFile.hxx"
+#include "ClientFile.hxx"
+#include "ClientInternal.hxx"
+#include "Idle.hxx"
+
+#ifdef ENABLE_SQLITE
+#include "StickerDatabase.hxx"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+static void
+print_spl_list(Client *client, const PlaylistVector &list)
+{
+ for (const auto &i : list) {
+ client_printf(client, "playlist: %s\n", i.name.c_str());
+
+ if (i.mtime > 0)
+ time_print(client, "Last-Modified", i.mtime);
+ }
+}
+
+enum command_return
+handle_urlhandlers(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ if (client_is_local(client))
+ client_puts(client, "handler: file://\n");
+ print_supported_uri_schemes(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_decoders(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ decoder_list_print(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_tagtypes(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ tag_print_types(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_kill(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_KILL;
+}
+
+enum command_return
+handle_close(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_CLOSE;
+}
+
+enum command_return
+handle_lsinfo(Client *client, int argc, char *argv[])
+{
+ const char *uri;
+
+ if (argc == 2)
+ uri = argv[1];
+ else
+ /* default is root directory */
+ uri = "";
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ /* print information about an arbitrary local file */
+ const char *path_utf8 = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path_utf8, &error))
+ return print_error(client, error);
+
+ struct song *song = song_file_load(path_utf8, NULL);
+ if (song == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such file");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ song_print_info(client, song);
+ song_free(song);
+ return COMMAND_RETURN_OK;
+ }
+
+ enum command_return result = handle_lsinfo2(client, argc, argv);
+ if (result != COMMAND_RETURN_OK)
+ return result;
+
+ if (isRootDirectory(uri)) {
+ const auto &list = ListPlaylistFiles(NULL);
+ print_spl_list(client, list);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_update(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *path = NULL;
+ unsigned ret;
+
+ assert(argc <= 2);
+ if (argc == 2) {
+ path = argv[1];
+
+ if (*path == 0 || strcmp(path, "/") == 0)
+ /* backwards compatibility with MPD 0.15 */
+ path = NULL;
+ else if (!uri_safe_local(path)) {
+ command_error(client, ACK_ERROR_ARG,
+ "Malformed path");
+ return COMMAND_RETURN_ERROR;
+ }
+ }
+
+ ret = update_enqueue(path, false);
+ if (ret > 0) {
+ client_printf(client, "updating_db: %i\n", ret);
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_UPDATE_ALREADY,
+ "already updating");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_rescan(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *path = NULL;
+ unsigned ret;
+
+ assert(argc <= 2);
+ if (argc == 2) {
+ path = argv[1];
+
+ if (!uri_safe_local(path)) {
+ command_error(client, ACK_ERROR_ARG,
+ "Malformed path");
+ return COMMAND_RETURN_ERROR;
+ }
+ }
+
+ ret = update_enqueue(path, true);
+ if (ret > 0) {
+ client_printf(client, "updating_db: %i\n", ret);
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_UPDATE_ALREADY,
+ "already updating");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_setvol(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned level;
+ bool success;
+
+ if (!check_unsigned(client, &level, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (level > 100) {
+ command_error(client, ACK_ERROR_ARG, "Invalid volume value");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = volume_level_change(level);
+ if (!success) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "problems setting volume");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_stats(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ stats_print(client);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_ping(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_password(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned permission = 0;
+
+ if (getPermissionFromPassword(argv[1], &permission) < 0) {
+ command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ client_set_permission(client, permission);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_config(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ if (!client_is_local(client)) {
+ command_error(client, ACK_ERROR_PERMISSION,
+ "Command only permitted to local clients");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ const char *path = mapper_get_music_directory_utf8();
+ if (path != NULL)
+ client_printf(client, "music_directory: %s\n", path);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_idle(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ unsigned flags = 0, j;
+ int i;
+ const char *const* idle_names;
+
+ idle_names = idle_get_names();
+ for (i = 1; i < argc; ++i) {
+ if (!argv[i])
+ continue;
+
+ for (j = 0; idle_names[j]; ++j) {
+ if (!g_ascii_strcasecmp(argv[i], idle_names[j])) {
+ flags |= (1 << j);
+ }
+ }
+ }
+
+ /* No argument means that the client wants to receive everything */
+ if (flags == 0)
+ flags = ~0;
+
+ /* enable "idle" mode on this client */
+ client->IdleWait(flags);
+
+ return COMMAND_RETURN_IDLE;
+}
diff --git a/src/OtherCommands.hxx b/src/OtherCommands.hxx
new file mode 100644
index 00000000..564ad38e
--- /dev/null
+++ b/src/OtherCommands.hxx
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OTHER_COMMANDS_HXX
+#define MPD_OTHER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_urlhandlers(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_decoders(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_tagtypes(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_kill(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_close(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_lsinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_update(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rescan(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_setvol(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_stats(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_ping(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_password(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_config(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_idle(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/output_all.c b/src/OutputAll.cxx
index f56cd04e..4cdcc84e 100644
--- a/src/output_all.c
+++ b/src/OutputAll.cxx
@@ -18,20 +18,21 @@
*/
#include "config.h"
-#include "output_all.h"
+#include "OutputAll.hxx"
+
+extern "C" {
#include "output_internal.h"
-#include "output_control.h"
-#include "chunk.h"
-#include "conf.h"
-#include "pipe.h"
-#include "buffer.h"
-#include "player_control.h"
-#include "mpd_error.h"
-#include "notify.h"
+}
-#ifndef NDEBUG
-#include "chunk.h"
-#endif
+#include "PlayerControl.hxx"
+#include "OutputControl.hxx"
+#include "OutputError.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
+#include "mpd_error.h"
+#include "conf.h"
+#include "notify.hxx"
#include <assert.h>
#include <string.h>
@@ -109,8 +110,6 @@ audio_output_all_init(struct player_control *pc)
unsigned int i;
GError *error = NULL;
- notify_init(&audio_output_client_notify);
-
num_audio_outputs = audio_output_config_count();
audio_outputs = g_new(struct audio_output *, num_audio_outputs);
@@ -157,8 +156,6 @@ audio_output_all_finish(void)
g_free(audio_outputs);
audio_outputs = NULL;
num_audio_outputs = 0;
-
- notify_deinit(&audio_output_client_notify);
}
void
@@ -207,7 +204,7 @@ audio_output_all_finished(void)
static void audio_output_wait_all(void)
{
while (!audio_output_all_finished())
- notify_wait(&audio_output_client_notify);
+ audio_output_client_notify.Wait();
}
/**
@@ -269,8 +266,15 @@ audio_output_all_update(void)
return ret;
}
+void
+audio_output_all_set_replay_gain_mode(enum replay_gain_mode mode)
+{
+ for (unsigned i = 0; i < num_audio_outputs; ++i)
+ audio_output_set_replay_gain_mode(audio_outputs[i], mode);
+}
+
bool
-audio_output_all_play(struct music_chunk *chunk)
+audio_output_all_play(struct music_chunk *chunk, GError **error_r)
{
bool ret;
unsigned int i;
@@ -278,11 +282,15 @@ audio_output_all_play(struct music_chunk *chunk)
assert(g_music_buffer != NULL);
assert(g_mp != NULL);
assert(chunk != NULL);
- assert(music_chunk_check_format(chunk, &input_audio_format));
+ assert(chunk->CheckFormat(input_audio_format));
ret = audio_output_all_update();
- if (!ret)
+ if (!ret) {
+ /* TODO: obtain real error */
+ g_set_error(error_r, output_quark(), 0,
+ "Failed to open audio output");
return false;
+ }
music_pipe_push(g_mp, chunk);
@@ -294,7 +302,8 @@ audio_output_all_play(struct music_chunk *chunk)
bool
audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer)
+ struct music_buffer *buffer,
+ GError **error_r)
{
bool ret = false, enabled = false;
unsigned int i;
@@ -334,7 +343,12 @@ audio_output_all_open(const struct audio_format *audio_format,
}
if (!enabled)
- g_warning("All audio outputs are disabled");
+ g_set_error(error_r, output_quark(), 0,
+ "All audio outputs are disabled");
+ else if (!ret)
+ /* TODO: obtain real error */
+ g_set_error(error_r, output_quark(), 0,
+ "Failed to open audio output");
if (!ret)
/* close all devices if there was an error */
@@ -467,15 +481,15 @@ audio_output_all_check(void)
bool
audio_output_all_wait(struct player_control *pc, unsigned threshold)
{
- player_lock(pc);
+ pc->Lock();
if (audio_output_all_check() < threshold) {
- player_unlock(pc);
+ pc->Unlock();
return true;
}
- player_wait(pc);
- player_unlock(pc);
+ pc->Wait();
+ pc->Unlock();
return audio_output_all_check() < threshold;
}
diff --git a/src/output_all.h b/src/OutputAll.hxx
index 4eeb94f1..becf4b69 100644
--- a/src/output_all.h
+++ b/src/OutputAll.hxx
@@ -26,6 +26,9 @@
#ifndef OUTPUT_ALL_H
#define OUTPUT_ALL_H
+#include "replay_gain_info.h"
+#include "gerror.h"
+
#include <stdbool.h>
#include <stddef.h>
@@ -84,7 +87,8 @@ audio_output_all_enable_disable(void);
*/
bool
audio_output_all_open(const struct audio_format *audio_format,
- struct music_buffer *buffer);
+ struct music_buffer *buffer,
+ GError **error_r);
/**
* Closes all audio outputs.
@@ -99,6 +103,9 @@ audio_output_all_close(void);
void
audio_output_all_release(void);
+void
+audio_output_all_set_replay_gain_mode(enum replay_gain_mode mode);
+
/**
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
* #music_pipe.
@@ -108,7 +115,7 @@ audio_output_all_release(void);
* (all closed then)
*/
bool
-audio_output_all_play(struct music_chunk *chunk);
+audio_output_all_play(struct music_chunk *chunk, GError **error_r);
/**
* Checks if the output devices have drained their music pipe, and
diff --git a/src/output_command.c b/src/OutputCommand.cxx
index 3988f350..beb44f0e 100644
--- a/src/output_command.c
+++ b/src/OutputCommand.cxx
@@ -25,13 +25,16 @@
*/
#include "config.h"
-#include "output_command.h"
-#include "output_all.h"
+#include "OutputCommand.hxx"
+#include "OutputAll.hxx"
+#include "PlayerControl.hxx"
+#include "MixerControl.hxx"
+#include "Idle.hxx"
+
+extern "C" {
#include "output_internal.h"
#include "output_plugin.h"
-#include "mixer_control.h"
-#include "player_control.h"
-#include "idle.h"
+}
extern unsigned audio_output_state_version;
@@ -50,7 +53,7 @@ audio_output_enable_index(unsigned idx)
ao->enabled = true;
idle_add(IDLE_OUTPUT);
- pc_update_audio(ao->player_control);
+ ao->player_control->UpdateAudio();
++audio_output_state_version;
@@ -79,7 +82,7 @@ audio_output_disable_index(unsigned idx)
idle_add(IDLE_MIXER);
}
- pc_update_audio(ao->player_control);
+ ao->player_control->UpdateAudio();
++audio_output_state_version;
diff --git a/src/output_command.h b/src/OutputCommand.hxx
index eda30acc..74eaf8f1 100644
--- a/src/output_command.h
+++ b/src/OutputCommand.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,10 +24,8 @@
*
*/
-#ifndef OUTPUT_COMMAND_H
-#define OUTPUT_COMMAND_H
-
-#include <stdbool.h>
+#ifndef MPD_OUTPUT_COMMAND_HXX
+#define MPD_OUTPUT_COMMAND_HXX
/**
* Enables an audio output. Returns false if the specified output
diff --git a/src/OutputCommands.cxx b/src/OutputCommands.cxx
new file mode 100644
index 00000000..7d626477
--- /dev/null
+++ b/src/OutputCommands.cxx
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OutputCommands.hxx"
+#include "OutputPrint.hxx"
+#include "OutputCommand.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+#include <string.h>
+
+enum command_return
+handle_enableoutput(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned device;
+ bool ret;
+
+ if (!check_unsigned(client, &device, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = audio_output_enable_index(device);
+ if (!ret) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such audio output");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_disableoutput(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned device;
+ bool ret;
+
+ if (!check_unsigned(client, &device, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = audio_output_disable_index(device);
+ if (!ret) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "No such audio output");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_devices(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ printAudioDevices(client);
+
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/OutputCommands.hxx b/src/OutputCommands.hxx
new file mode 100644
index 00000000..4f7082bf
--- /dev/null
+++ b/src/OutputCommands.hxx
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OUTPUT_COMMANDS_HXX
+#define MPD_OUTPUT_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_enableoutput(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_disableoutput(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_devices(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/output_control.c b/src/OutputControl.cxx
index 7b95be49..c451938f 100644
--- a/src/output_control.c
+++ b/src/OutputControl.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,19 @@
*/
#include "config.h"
-#include "output_control.h"
+#include "OutputControl.hxx"
+#include "OutputThread.hxx"
#include "output_api.h"
+
+extern "C" {
#include "output_internal.h"
-#include "output_thread.h"
-#include "mixer_control.h"
-#include "mixer_plugin.h"
-#include "filter_plugin.h"
-#include "notify.h"
+}
+
+#include "MixerPlugin.hxx"
+#include "MixerControl.hxx"
+#include "notify.hxx"
+#include "filter/ReplayGainFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
#include <assert.h>
#include <stdlib.h>
@@ -47,7 +52,7 @@ static void ao_command_wait(struct audio_output *ao)
{
while (ao->command != AO_COMMAND_NONE) {
g_mutex_unlock(ao->mutex);
- notify_wait(&audio_output_client_notify);
+ audio_output_client_notify.Wait();
g_mutex_lock(ao->mutex);
}
}
@@ -92,6 +97,14 @@ ao_lock_command(struct audio_output *ao, enum audio_output_command cmd)
}
void
+audio_output_set_replay_gain_mode(struct audio_output *ao,
+ enum replay_gain_mode mode)
+{
+ if (ao->replay_gain_filter != NULL)
+ replay_gain_filter_set_mode(ao->replay_gain_filter, mode);
+}
+
+void
audio_output_enable(struct audio_output *ao)
{
if (ao->thread == NULL) {
diff --git a/src/output_control.h b/src/OutputControl.hxx
index 874a5351..2ff09539 100644
--- a/src/output_control.h
+++ b/src/OutputControl.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_CONTROL_H
-#define MPD_OUTPUT_CONTROL_H
+#ifndef MPD_OUTPUT_CONTROL_HXX
+#define MPD_OUTPUT_CONTROL_HXX
-#include <glib.h>
+#include "replay_gain_info.h"
#include <stddef.h>
-#include <stdbool.h>
struct audio_output;
struct audio_format;
@@ -31,11 +30,9 @@ struct config_param;
struct music_pipe;
struct player_control;
-static inline GQuark
-audio_output_quark(void)
-{
- return g_quark_from_static_string("audio_output");
-}
+void
+audio_output_set_replay_gain_mode(struct audio_output *ao,
+ enum replay_gain_mode mode);
/**
* Enables the device.
diff --git a/src/OutputError.hxx b/src/OutputError.hxx
new file mode 100644
index 00000000..451df985
--- /dev/null
+++ b/src/OutputError.hxx
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OUTPUT_ERROR_HXX
+#define MPD_OUTPUT_ERROR_HXX
+
+#include <glib.h>
+
+/**
+ * Quark for GError.domain.
+ */
+G_GNUC_CONST
+static inline GQuark
+output_quark(void)
+{
+ return g_quark_from_static_string("output");
+}
+
+#endif
diff --git a/src/output_finish.c b/src/OutputFinish.cxx
index e11b4367..559a5cf2 100644
--- a/src/output_finish.c
+++ b/src/OutputFinish.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,14 @@
*/
#include "config.h"
+
+extern "C" {
#include "output_internal.h"
#include "output_plugin.h"
-#include "mixer_control.h"
-#include "filter_plugin.h"
+}
+
+#include "MixerControl.hxx"
+#include "FilterInternal.hxx"
#include <assert.h>
@@ -38,13 +42,9 @@ ao_base_finish(struct audio_output *ao)
g_cond_free(ao->cond);
g_mutex_free(ao->mutex);
- if (ao->replay_gain_filter != NULL)
- filter_free(ao->replay_gain_filter);
-
- if (ao->other_replay_gain_filter != NULL)
- filter_free(ao->other_replay_gain_filter);
-
- filter_free(ao->filter);
+ delete ao->replay_gain_filter;
+ delete ao->other_replay_gain_filter;
+ delete ao->filter;
pcm_buffer_deinit(&ao->cross_fade_buffer);
}
diff --git a/src/output_init.c b/src/OutputInit.cxx
index c3b808e9..de686925 100644
--- a/src/output_init.c
+++ b/src/OutputInit.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,26 @@
*/
#include "config.h"
-#include "output_control.h"
+#include "OutputControl.hxx"
+#include "OutputList.hxx"
+#include "OutputError.hxx"
+#include "FilterConfig.hxx"
#include "output_api.h"
+#include "AudioParser.hxx"
+
+extern "C" {
#include "output_internal.h"
-#include "output_list.h"
-#include "audio_parser.h"
-#include "mixer_control.h"
-#include "mixer_type.h"
-#include "mixer_list.h"
-#include "mixer/software_mixer_plugin.h"
-#include "filter_plugin.h"
-#include "filter_registry.h"
-#include "filter_config.h"
-#include "filter/chain_filter_plugin.h"
-#include "filter/autoconvert_filter_plugin.h"
-#include "filter/replay_gain_filter_plugin.h"
+}
+
+#include "MixerList.hxx"
+#include "MixerType.hxx"
+#include "MixerControl.hxx"
+#include "mixer/SoftwareMixerPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterRegistry.hxx"
+#include "filter/AutoConvertFilterPlugin.hxx"
+#include "filter/ReplayGainFilterPlugin.hxx"
+#include "filter/ChainFilterPlugin.hxx"
#include <glib.h>
@@ -61,7 +66,7 @@ audio_output_detect(GError **error)
return plugin;
}
- g_set_error(error, audio_output_quark(), 0,
+ g_set_error(error, output_quark(), 0,
"Unable to detect an audio device");
return NULL;
}
@@ -87,14 +92,15 @@ audio_output_mixer_type(const struct config_param *param)
/* fall back to the global "mixer_type" setting (also
deprecated) */
- return mixer_type_parse(config_get_string("mixer_type", "hardware"));
+ return mixer_type_parse(config_get_string(CONF_MIXER_TYPE,
+ "hardware"));
}
static struct mixer *
audio_output_load_mixer(struct audio_output *ao,
const struct config_param *param,
const struct mixer_plugin *plugin,
- struct filter *filter_chain,
+ Filter &filter_chain,
GError **error_r)
{
struct mixer *mixer;
@@ -114,7 +120,7 @@ audio_output_load_mixer(struct audio_output *ao,
mixer = mixer_new(&software_mixer_plugin, NULL, NULL, NULL);
assert(mixer != NULL);
- filter_chain_append(filter_chain,
+ filter_chain_append(filter_chain, "software_mixer",
software_mixer_get_filter(mixer));
return mixer;
}
@@ -143,7 +149,7 @@ ao_base_init(struct audio_output *ao,
ao->name = config_get_block_string(param, AUDIO_OUTPUT_NAME,
NULL);
if (ao->name == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
+ g_set_error(error_r, output_quark(), 0,
"Missing \"name\" configuration");
return false;
}
@@ -165,6 +171,7 @@ ao_base_init(struct audio_output *ao,
}
ao->plugin = plugin;
+ ao->tags = config_get_block_bool(param, "tags", true);
ao->always_on = config_get_block_bool(param, "always_on", false);
ao->enabled = config_get_block_bool(param, "enabled", true);
ao->really_enabled = false;
@@ -183,15 +190,15 @@ ao_base_init(struct audio_output *ao,
/* create the normalization filter (if configured) */
if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) {
- struct filter *normalize_filter =
+ Filter *normalize_filter =
filter_new(&normalize_filter_plugin, NULL, NULL);
assert(normalize_filter != NULL);
- filter_chain_append(ao->filter,
+ filter_chain_append(*ao->filter, "normalize",
autoconvert_filter_new(normalize_filter));
}
- filter_chain_parse(ao->filter,
+ filter_chain_parse(*ao->filter,
config_get_block_string(param, AUDIO_FILTERS, ""),
&error
);
@@ -251,7 +258,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param,
GError *error = NULL;
ao->mixer = audio_output_load_mixer(ao, param,
ao->plugin->mixer_plugin,
- ao->filter, &error);
+ *ao->filter, &error);
if (ao->mixer == NULL && error != NULL) {
g_warning("Failed to initialize hardware mixer for '%s': %s",
ao->name, error->message);
@@ -268,7 +275,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param,
g_warning("No such mixer for output '%s'", ao->name);
} else if (strcmp(replay_gain_handler, "software") != 0 &&
ao->replay_gain_filter != NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
+ g_set_error(error_r, output_quark(), 0,
"Invalid \"replay_gain_handler\" value");
return false;
}
@@ -278,7 +285,7 @@ audio_output_setup(struct audio_output *ao, const struct config_param *param,
ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL);
assert(ao->convert_filter != NULL);
- filter_chain_append(ao->filter, ao->convert_filter);
+ filter_chain_append(*ao->filter, "convert", ao->convert_filter);
return true;
}
@@ -295,24 +302,23 @@ audio_output_new(const struct config_param *param,
p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL);
if (p == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
+ g_set_error(error_r, output_quark(), 0,
"Missing \"type\" configuration");
- return false;
+ return nullptr;
}
plugin = audio_output_plugin_get(p);
if (plugin == NULL) {
- g_set_error(error_r, audio_output_quark(), 0,
+ g_set_error(error_r, output_quark(), 0,
"No such audio output plugin: %s", p);
- return false;
+ return nullptr;
}
} else {
- g_warning("No \"%s\" defined in config file\n",
- CONF_AUDIO_OUTPUT);
+ g_warning("No 'audio_output' defined in config file\n");
plugin = audio_output_detect(error_r);
if (plugin == NULL)
- return false;
+ return nullptr;
g_message("Successfully detected a %s audio device",
plugin->name);
diff --git a/src/output_list.c b/src/OutputList.cxx
index 835c02bb..dc4e2319 100644
--- a/src/output_list.c
+++ b/src/OutputList.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,26 +18,26 @@
*/
#include "config.h"
-#include "output_list.h"
+#include "OutputList.hxx"
#include "output_api.h"
-#include "output/alsa_output_plugin.h"
+#include "output/AlsaOutputPlugin.hxx"
#include "output/ao_output_plugin.h"
#include "output/ffado_output_plugin.h"
#include "output/fifo_output_plugin.h"
-#include "output/httpd_output_plugin.h"
+#include "output/HttpdOutputPlugin.hxx"
#include "output/jack_output_plugin.h"
#include "output/mvp_output_plugin.h"
-#include "output/null_output_plugin.h"
+#include "output/NullOutputPlugin.hxx"
#include "output/openal_output_plugin.h"
-#include "output/oss_output_plugin.h"
-#include "output/osx_output_plugin.h"
+#include "output/OssOutputPlugin.hxx"
+#include "output/OSXOutputPlugin.hxx"
#include "output/pipe_output_plugin.h"
#include "output/pulse_output_plugin.h"
#include "output/recorder_output_plugin.h"
-#include "output/roar_output_plugin.h"
+#include "output/RoarOutputPlugin.hxx"
#include "output/shout_output_plugin.h"
#include "output/solaris_output_plugin.h"
-#include "output/winmm_output_plugin.h"
+#include "output/WinmmOutputPlugin.hxx"
const struct audio_output_plugin *const audio_output_plugins[] = {
#ifdef HAVE_SHOUT
diff --git a/src/output_list.h b/src/OutputList.hxx
index 185ada71..b7716c67 100644
--- a/src/output_list.h
+++ b/src/OutputList.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_LIST_H
-#define MPD_OUTPUT_LIST_H
+#ifndef MPD_OUTPUT_LIST_HXX
+#define MPD_OUTPUT_LIST_HXX
extern const struct audio_output_plugin *const audio_output_plugins[];
diff --git a/src/output_plugin.c b/src/OutputPlugin.cxx
index 221570c1..9aa0f779 100644
--- a/src/output_plugin.c
+++ b/src/OutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,11 @@
*/
#include "config.h"
+
+extern "C" {
#include "output_plugin.h"
+}
+
#include "output_internal.h"
struct audio_output *
diff --git a/src/output_print.c b/src/OutputPrint.cxx
index 483648ca..240ea967 100644
--- a/src/output_print.c
+++ b/src/OutputPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,15 +23,15 @@
*/
#include "config.h"
-#include "output_print.h"
+#include "OutputPrint.hxx"
+#include "OutputAll.hxx"
#include "output_internal.h"
-#include "output_all.h"
-#include "client.h"
+#include "Client.hxx"
void
-printAudioDevices(struct client *client)
+printAudioDevices(Client *client)
{
- unsigned n = audio_output_count();
+ const unsigned n = audio_output_count();
for (unsigned i = 0; i < n; ++i) {
const struct audio_output *ao = audio_output_get(i);
diff --git a/src/output_print.h b/src/OutputPrint.hxx
index e02f4e9f..78717d0a 100644
--- a/src/output_print.h
+++ b/src/OutputPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,12 +22,12 @@
*
*/
-#ifndef OUTPUT_PRINT_H
-#define OUTPUT_PRINT_H
+#ifndef MPD_OUTPUT_PRINT_HXX
+#define MPD_OUTPUT_PRINT_HXX
-struct client;
+class Client;
void
-printAudioDevices(struct client *client);
+printAudioDevices(Client *client);
#endif
diff --git a/src/output_state.c b/src/OutputState.cxx
index 7bcafb36..27fa34f8 100644
--- a/src/output_state.c
+++ b/src/OutputState.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,9 +23,9 @@
*/
#include "config.h"
-#include "output_state.h"
+#include "OutputState.hxx"
+#include "OutputAll.hxx"
#include "output_internal.h"
-#include "output_all.h"
#include <glib.h>
diff --git a/src/output_state.h b/src/OutputState.hxx
index 320a3520..5ab765ba 100644
--- a/src/output_state.h
+++ b/src/OutputState.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,9 @@
*
*/
-#ifndef OUTPUT_STATE_H
-#define OUTPUT_STATE_H
+#ifndef MPD_OUTPUT_STATE_HXX
+#define MPD_OUTPUT_STATE_HXX
-#include <stdbool.h>
#include <stdio.h>
bool
diff --git a/src/output_thread.c b/src/OutputThread.cxx
index 4eef2ccd..f1ffe876 100644
--- a/src/output_thread.c
+++ b/src/OutputThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,23 @@
*/
#include "config.h"
-#include "output_thread.h"
+#include "OutputThread.hxx"
#include "output_api.h"
+#include "PcmMix.hxx"
+
+extern "C" {
#include "output_internal.h"
-#include "chunk.h"
-#include "pipe.h"
-#include "player_control.h"
-#include "pcm_mix.h"
-#include "filter_plugin.h"
-#include "filter/convert_filter_plugin.h"
-#include "filter/replay_gain_filter_plugin.h"
+}
+
+#include "notify.hxx"
+#include "FilterInternal.hxx"
+#include "filter/ConvertFilterPlugin.hxx"
+#include "filter/ReplayGainFilterPlugin.hxx"
+#include "PlayerControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicChunk.hxx"
+
#include "mpd_error.h"
-#include "notify.h"
#include "gcc.h"
#include <glib.h>
@@ -47,7 +52,7 @@ static void ao_command_finished(struct audio_output *ao)
ao->command = AO_COMMAND_NONE;
g_mutex_unlock(ao->mutex);
- notify_signal(&audio_output_client_notify);
+ audio_output_client_notify.Signal();
g_mutex_lock(ao->mutex);
}
@@ -93,26 +98,24 @@ ao_disable(struct audio_output *ao)
}
static const struct audio_format *
-ao_filter_open(struct audio_output *ao,
- struct audio_format *audio_format,
+ao_filter_open(struct audio_output *ao, audio_format &format,
GError **error_r)
{
- assert(audio_format_valid(audio_format));
+ assert(audio_format_valid(&format));
/* the replay_gain filter cannot fail here */
if (ao->replay_gain_filter != NULL)
- filter_open(ao->replay_gain_filter, audio_format, error_r);
+ ao->replay_gain_filter->Open(format, error_r);
if (ao->other_replay_gain_filter != NULL)
- filter_open(ao->other_replay_gain_filter, audio_format,
- error_r);
+ ao->other_replay_gain_filter->Open(format, error_r);
const struct audio_format *af
- = filter_open(ao->filter, audio_format, error_r);
+ = ao->filter->Open(format, error_r);
if (af == NULL) {
if (ao->replay_gain_filter != NULL)
- filter_close(ao->replay_gain_filter);
+ ao->replay_gain_filter->Close();
if (ao->other_replay_gain_filter != NULL)
- filter_close(ao->other_replay_gain_filter);
+ ao->other_replay_gain_filter->Close();
}
return af;
@@ -122,11 +125,11 @@ static void
ao_filter_close(struct audio_output *ao)
{
if (ao->replay_gain_filter != NULL)
- filter_close(ao->replay_gain_filter);
+ ao->replay_gain_filter->Close();
if (ao->other_replay_gain_filter != NULL)
- filter_close(ao->other_replay_gain_filter);
+ ao->other_replay_gain_filter->Close();
- filter_close(ao->filter);
+ ao->filter->Close();
}
static void
@@ -134,7 +137,6 @@ ao_open(struct audio_output *ao)
{
bool success;
GError *error = NULL;
- const struct audio_format *filter_audio_format;
struct audio_format_string af_string;
assert(!ao->open);
@@ -159,7 +161,8 @@ ao_open(struct audio_output *ao)
/* open the filter */
- filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error);
+ const audio_format *filter_audio_format =
+ ao_filter_open(ao, ao->in_audio_format, &error);
if (filter_audio_format == NULL) {
g_warning("Failed to open filter for \"%s\" [%s]: %s",
ao->name, ao->plugin->name, error->message);
@@ -191,7 +194,7 @@ ao_open(struct audio_output *ao)
return;
}
- convert_filter_set(ao->convert_filter, &ao->out_audio_format);
+ convert_filter_set(ao->convert_filter, ao->out_audio_format);
ao->open = true;
@@ -239,7 +242,7 @@ ao_reopen_filter(struct audio_output *ao)
GError *error = NULL;
ao_filter_close(ao);
- filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error);
+ filter_audio_format = ao_filter_open(ao, ao->in_audio_format, &error);
if (filter_audio_format == NULL) {
g_warning("Failed to open filter for \"%s\" [%s]: %s",
ao->name, ao->plugin->name, error->message);
@@ -262,7 +265,7 @@ ao_reopen_filter(struct audio_output *ao)
return;
}
- convert_filter_set(ao->convert_filter, &ao->out_audio_format);
+ convert_filter_set(ao->convert_filter, ao->out_audio_format);
}
static void
@@ -315,17 +318,17 @@ ao_wait(struct audio_output *ao)
}
}
-static const char *
+static const void *
ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
- struct filter *replay_gain_filter,
+ Filter *replay_gain_filter,
unsigned *replay_gain_serial_p,
size_t *length_r)
{
assert(chunk != NULL);
- assert(!music_chunk_is_empty(chunk));
- assert(music_chunk_check_format(chunk, &ao->in_audio_format));
+ assert(!chunk->IsEmpty());
+ assert(chunk->CheckFormat(ao->in_audio_format));
- const char *data = chunk->data;
+ const void *data = chunk->data;
size_t length = chunk->length;
(void)ao;
@@ -342,8 +345,8 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
}
GError *error = NULL;
- data = filter_filter(replay_gain_filter, data, length,
- &length, &error);
+ data = replay_gain_filter->FilterPCM(data, length,
+ &length, &error);
if (data == NULL) {
g_warning("\"%s\" [%s] failed to filter: %s",
ao->name, ao->plugin->name, error->message);
@@ -356,14 +359,14 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk,
return data;
}
-static const char *
+static const void *
ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
size_t *length_r)
{
GError *error = NULL;
size_t length;
- const char *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
+ const void *data = ao_chunk_data(ao, chunk, ao->replay_gain_filter,
&ao->replay_gain_serial, &length);
if (data == NULL)
return NULL;
@@ -378,7 +381,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
if (chunk->other != NULL) {
size_t other_length;
- const char *other_data =
+ const void *other_data =
ao_chunk_data(ao, chunk->other,
ao->other_replay_gain_filter,
&ao->other_replay_gain_serial,
@@ -399,13 +402,14 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
if (length > other_length)
length = other_length;
- char *dest = pcm_buffer_get(&ao->cross_fade_buffer,
+ void *dest = pcm_buffer_get(&ao->cross_fade_buffer,
other_length);
memcpy(dest, other_data, other_length);
- if (!pcm_mix(dest, data, length, ao->in_audio_format.format,
+ if (!pcm_mix(dest, data, length,
+ sample_format(ao->in_audio_format.format),
1.0 - chunk->mix_ratio)) {
g_warning("Cannot cross-fade format %s",
- sample_format_to_string(ao->in_audio_format.format));
+ sample_format_to_string(sample_format(ao->in_audio_format.format)));
return NULL;
}
@@ -415,7 +419,7 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
/* apply filter chain */
- data = filter_filter(ao->filter, data, length, &length, &error);
+ data = ao->filter->FilterPCM(data, length, &length, &error);
if (data == NULL) {
g_warning("\"%s\" [%s] failed to filter: %s",
ao->name, ao->plugin->name, error->message);
@@ -435,7 +439,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
assert(ao != NULL);
assert(ao->filter != NULL);
- if (chunk->tag != NULL) {
+ if (ao->tags && gcc_unlikely(chunk->tag != NULL)) {
g_mutex_unlock(ao->mutex);
ao_plugin_send_tag(ao, chunk->tag);
g_mutex_lock(ao->mutex);
@@ -446,7 +450,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
/* workaround -Wmaybe-uninitialized false positive */
size = 0;
#endif
- const char *data = ao_filter_chunk(ao, chunk, &size);
+ const char *data = (const char *)ao_filter_chunk(ao, chunk, &size);
if (data == NULL) {
ao_close(ao, false);
@@ -542,7 +546,7 @@ ao_play(struct audio_output *ao)
ao->chunk_finished = true;
g_mutex_unlock(ao->mutex);
- player_lock_signal(ao->player_control);
+ ao->player_control->LockSignal();
g_mutex_lock(ao->mutex);
return true;
@@ -578,7 +582,7 @@ static void ao_pause(struct audio_output *ao)
static gpointer audio_output_task(gpointer arg)
{
- struct audio_output *ao = arg;
+ struct audio_output *ao = (struct audio_output *)arg;
g_mutex_lock(ao->mutex);
diff --git a/src/output_thread.h b/src/OutputThread.hxx
index 5ad9a752..1a793216 100644
--- a/src/output_thread.h
+++ b/src/OutputThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OUTPUT_THREAD_H
-#define MPD_OUTPUT_THREAD_H
+#ifndef MPD_OUTPUT_THREAD_HXX
+#define MPD_OUTPUT_THREAD_HXX
struct audio_output;
diff --git a/src/client_list.c b/src/Page.cxx
index 2c7f37af..bf30376e 100644
--- a/src/client_list.c
+++ b/src/Page.cxx
@@ -18,52 +18,53 @@
*/
#include "config.h"
-#include "client_internal.h"
+#include "Page.hxx"
-#include <assert.h>
+#include <glib.h>
-static GList *clients;
-static unsigned num_clients;
+#include <new>
-bool
-client_list_is_empty(void)
-{
- return num_clients == 0;
-}
+#include <assert.h>
+#include <string.h>
-bool
-client_list_is_full(void)
+Page *
+Page::Create(size_t size)
{
- return num_clients >= client_max_connections;
+ void *p = g_malloc(sizeof(Page) + size -
+ sizeof(Page::data));
+ return ::new(p) Page(size);
}
-struct client *
-client_list_get_first(void)
+Page *
+Page::Copy(const void *data, size_t size)
{
- assert(clients != NULL);
+ assert(data != nullptr);
- return clients->data;
+ Page *page = Create(size);
+ memcpy(page->data, data, size);
+ return page;
}
-void
-client_list_add(struct client *client)
+Page *
+Page::Concat(const Page &a, const Page &b)
{
- clients = g_list_prepend(clients, client);
- ++num_clients;
-}
+ Page *page = Create(a.size + b.size);
-void
-client_list_foreach(GFunc func, gpointer user_data)
-{
- g_list_foreach(clients, func, user_data);
+ memcpy(page->data, a.data, a.size);
+ memcpy(page->data + a.size, b.data, b.size);
+
+ return page;
}
-void
-client_list_remove(struct client *client)
+bool
+Page::Unref()
{
- assert(num_clients > 0);
- assert(clients != NULL);
+ bool unused = ref.Decrement();
+
+ if (unused) {
+ this->Page::~Page();
+ g_free(this);
+ }
- clients = g_list_remove(clients, client);
- --num_clients;
+ return unused;
}
diff --git a/src/page.h b/src/Page.hxx
index 8a3aaf39..2bc9a6ac 100644
--- a/src/page.h
+++ b/src/Page.hxx
@@ -22,73 +22,83 @@
* This is a library which manages reference counted buffers.
*/
-#ifndef MPD_PAGE_H
-#define MPD_PAGE_H
+#ifndef MPD_PAGE_HXX
+#define MPD_PAGE_HXX
+
+#include "util/RefCount.hxx"
+
+#include <algorithm>
#include <stddef.h>
-#include <stdbool.h>
/**
* A dynamically allocated buffer which keeps track of its reference
* count. This is useful for passing buffers around, when several
* instances hold references to one buffer.
*/
-struct page {
+class Page {
/**
* The number of references to this buffer. This library uses
* atomic functions to access it, i.e. no locks are required.
* As soon as this attribute reaches zero, the buffer is
* freed.
*/
- int ref;
+ RefCount ref;
+public:
/**
* The size of this buffer in bytes.
*/
- size_t size;
+ const size_t size;
/**
* Dynamic array containing the buffer data.
*/
unsigned char data[sizeof(long)];
-};
-/**
- * Creates a new #page object, and copies data from the specified
- * buffer. It is initialized with a reference count of 1.
- *
- * @param data the source buffer
- * @param size the size of the source buffer
- * @return the new #page object
- */
-struct page *
-page_new_copy(const void *data, size_t size);
+protected:
+ Page(size_t _size):size(_size) {}
+ ~Page() = default;
-/**
- * Concatenates two pages to a new page.
- *
- * @param a the first page
- * @param b the second page, which is appended
- */
-struct page *
-page_new_concat(const struct page *a, const struct page *b);
+ /**
+ * Allocates a new #Page object, without filling the data
+ * element.
+ */
+ static Page *Create(size_t size);
-/**
- * Increases the reference counter.
- *
- * @param page the #page object
- */
-void
-page_ref(struct page *page);
+public:
+ /**
+ * Creates a new #page object, and copies data from the
+ * specified buffer. It is initialized with a reference count
+ * of 1.
+ *
+ * @param data the source buffer
+ * @param size the size of the source buffer
+ */
+ static Page *Copy(const void *data, size_t size);
-/**
- * Decreases the reference counter. If it reaches zero, the #page is
- * freed.
- *
- * @param page the #page object
- * @return true if the #page has been freed
- */
-bool
-page_unref(struct page *page);
+ /**
+ * Concatenates two pages to a new page.
+ *
+ * @param a the first page
+ * @param b the second page, which is appended
+ */
+ static Page *Concat(const Page &a, const Page &b);
+
+ /**
+ * Increases the reference counter.
+ */
+ void Ref() {
+ ref.Increment();
+ }
+
+ /**
+ * Decreases the reference counter. If it reaches zero, the #page is
+ * freed.
+ *
+ * @return true if the #page has been freed
+ */
+ bool Unref();
+};
#endif
diff --git a/src/Partition.hxx b/src/Partition.hxx
new file mode 100644
index 00000000..776f74e2
--- /dev/null
+++ b/src/Partition.hxx
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PARTITION_HXX
+#define MPD_PARTITION_HXX
+
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+
+/**
+ * A partition of the Music Player Daemon. It is a separate unit with
+ * a playlist, a player, outputs etc.
+ */
+struct Partition {
+ struct playlist playlist;
+
+ player_control pc;
+
+ Partition(unsigned max_length,
+ unsigned buffer_chunks,
+ unsigned buffered_before_play)
+ :playlist(max_length),
+ pc(buffer_chunks, buffered_before_play) {
+ }
+
+ void ClearQueue() {
+ playlist.Clear(pc);
+ }
+
+ enum playlist_result AppendFile(const char *path_utf8,
+ unsigned *added_id=nullptr) {
+ return playlist.AppendFile(pc, path_utf8, added_id);
+ }
+
+ enum playlist_result AppendURI(const char *uri_utf8,
+ unsigned *added_id=nullptr) {
+ return playlist.AppendURI(pc, uri_utf8, added_id);
+ }
+
+ enum playlist_result DeletePosition(unsigned position) {
+ return playlist.DeletePosition(pc, position);
+ }
+
+ enum playlist_result DeleteId(unsigned id) {
+ return playlist.DeleteId(pc, id);
+ }
+
+ /**
+ * Deletes a range of songs from the playlist.
+ *
+ * @param start the position of the first song to delete
+ * @param end the position after the last song to delete
+ */
+ enum playlist_result DeleteRange(unsigned start, unsigned end) {
+ return playlist.DeleteRange(pc, start, end);
+ }
+
+ void DeleteSong(const song &song) {
+ playlist.DeleteSong(pc, song);
+ }
+
+ void Shuffle(unsigned start, unsigned end) {
+ playlist.Shuffle(pc, start, end);
+ }
+
+ enum playlist_result MoveRange(unsigned start, unsigned end, int to) {
+ return playlist.MoveRange(pc, start, end, to);
+ }
+
+ enum playlist_result MoveId(unsigned id, int to) {
+ return playlist.MoveId(pc, id, to);
+ }
+
+ enum playlist_result SwapPositions(unsigned song1, unsigned song2) {
+ return playlist.SwapPositions(pc, song1, song2);
+ }
+
+ enum playlist_result SwapIds(unsigned id1, unsigned id2) {
+ return playlist.SwapIds(pc, id1, id2);
+ }
+
+ enum playlist_result SetPriorityRange(unsigned start_position,
+ unsigned end_position,
+ uint8_t priority) {
+ return playlist.SetPriorityRange(pc,
+ start_position, end_position,
+ priority);
+ }
+
+ enum playlist_result SetPriorityId(unsigned song_id,
+ uint8_t priority) {
+ return playlist.SetPriorityId(pc, song_id, priority);
+ }
+
+ void Stop() {
+ playlist.Stop(pc);
+ }
+
+ enum playlist_result PlayPosition(int position) {
+ return playlist.PlayPosition(pc, position);
+ }
+
+ enum playlist_result PlayId(int id) {
+ return playlist.PlayId(pc, id);
+ }
+
+ void PlayNext() {
+ return playlist.PlayNext(pc);
+ }
+
+ void PlayPrevious() {
+ return playlist.PlayPrevious(pc);
+ }
+
+ enum playlist_result SeekSongPosition(unsigned song_position,
+ float seek_time) {
+ return playlist.SeekSongPosition(pc, song_position, seek_time);
+ }
+
+ enum playlist_result SeekSongId(unsigned song_id, float seek_time) {
+ return playlist.SeekSongId(pc, song_id, seek_time);
+ }
+
+ enum playlist_result SeekCurrent(float seek_time, bool relative) {
+ return playlist.SeekCurrent(pc, seek_time, relative);
+ }
+
+ void SetRepeat(bool new_value) {
+ playlist.SetRepeat(pc, new_value);
+ }
+
+ bool GetRandom() const {
+ return playlist.GetRandom();
+ }
+
+ void SetRandom(bool new_value) {
+ playlist.SetRandom(pc, new_value);
+ }
+
+ void SetSingle(bool new_value) {
+ playlist.SetSingle(pc, new_value);
+ }
+
+ void SetConsume(bool new_value) {
+ playlist.SetConsume(new_value);
+ }
+};
+
+#endif
diff --git a/src/pcm_channels.c b/src/PcmChannels.cxx
index ec2bd69a..eca6b250 100644
--- a/src/pcm_channels.c
+++ b/src/PcmChannels.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,23 +18,23 @@
*/
#include "config.h"
-#include "pcm_channels.h"
+#include "PcmChannels.hxx"
#include "pcm_buffer.h"
-#include "pcm_utils.h"
+#include "PcmUtils.hxx"
#include <assert.h>
+template<typename D, typename S>
static void
-pcm_convert_channels_16_1_to_2(int16_t *restrict dest,
- const int16_t *restrict src,
- const int16_t *restrict src_end)
+MonoToStereo(D dest, S src, S end)
{
- while (src < src_end) {
- int16_t value = *src++;
+ while (src != end) {
+ const auto value = *src++;
*dest++ = value;
*dest++ = value;
}
+
}
static void
@@ -84,11 +84,11 @@ pcm_convert_channels_16(struct pcm_buffer *buffer,
size_t dest_size = src_size / src_channels * dest_channels;
*dest_size_r = dest_size;
- int16_t *dest = pcm_buffer_get(buffer, dest_size);
+ int16_t *dest = (int16_t *)pcm_buffer_get(buffer, dest_size);
const int16_t *src_end = pcm_end_pointer(src, src_size);
if (src_channels == 1 && dest_channels == 2)
- pcm_convert_channels_16_1_to_2(dest, src, src_end);
+ MonoToStereo(dest, src, src_end);
else if (src_channels == 2 && dest_channels == 1)
pcm_convert_channels_16_2_to_1(dest, src, src_end);
else if (dest_channels == 2)
@@ -101,19 +101,6 @@ pcm_convert_channels_16(struct pcm_buffer *buffer,
}
static void
-pcm_convert_channels_24_1_to_2(int32_t *restrict dest,
- const int32_t *restrict src,
- const int32_t *restrict src_end)
-{
- while (src < src_end) {
- int32_t value = *src++;
-
- *dest++ = value;
- *dest++ = value;
- }
-}
-
-static void
pcm_convert_channels_24_2_to_1(int32_t *restrict dest,
const int32_t *restrict src,
const int32_t *restrict src_end)
@@ -160,11 +147,12 @@ pcm_convert_channels_24(struct pcm_buffer *buffer,
size_t dest_size = src_size / src_channels * dest_channels;
*dest_size_r = dest_size;
- int32_t *dest = pcm_buffer_get(buffer, dest_size);
- const int32_t *src_end = pcm_end_pointer(src, src_size);
+ int32_t *dest = (int32_t *)pcm_buffer_get(buffer, dest_size);
+ const int32_t *src_end = (const int32_t *)
+ pcm_end_pointer(src, src_size);
if (src_channels == 1 && dest_channels == 2)
- pcm_convert_channels_24_1_to_2(dest, src, src_end);
+ MonoToStereo(dest, src, src_end);
else if (src_channels == 2 && dest_channels == 1)
pcm_convert_channels_24_2_to_1(dest, src, src_end);
else if (dest_channels == 2)
@@ -177,13 +165,6 @@ pcm_convert_channels_24(struct pcm_buffer *buffer,
}
static void
-pcm_convert_channels_32_1_to_2(int32_t *dest, const int32_t *src,
- const int32_t *src_end)
-{
- pcm_convert_channels_24_1_to_2(dest, src, src_end);
-}
-
-static void
pcm_convert_channels_32_2_to_1(int32_t *restrict dest,
const int32_t *restrict src,
const int32_t *restrict src_end)
@@ -229,11 +210,12 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
size_t dest_size = src_size / src_channels * dest_channels;
*dest_size_r = dest_size;
- int32_t *dest = pcm_buffer_get(buffer, dest_size);
- const int32_t *src_end = pcm_end_pointer(src, src_size);
+ int32_t *dest = (int32_t *)pcm_buffer_get(buffer, dest_size);
+ const int32_t *src_end = (const int32_t *)
+ pcm_end_pointer(src, src_size);
if (src_channels == 1 && dest_channels == 2)
- pcm_convert_channels_32_1_to_2(dest, src, src_end);
+ MonoToStereo(dest, src, src_end);
else if (src_channels == 2 && dest_channels == 1)
pcm_convert_channels_32_2_to_1(dest, src, src_end);
else if (dest_channels == 2)
@@ -244,3 +226,65 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
return dest;
}
+
+static void
+pcm_convert_channels_float_2_to_1(float *restrict dest,
+ const float *restrict src,
+ const float *restrict src_end)
+{
+ while (src < src_end) {
+ double a = *src++, b = *src++;
+
+ *dest++ = (a + b) / 2;
+ }
+}
+
+static void
+pcm_convert_channels_float_n_to_2(float *dest,
+ unsigned src_channels, const float *src,
+ const float *src_end)
+{
+ unsigned c;
+
+ assert(src_channels > 0);
+
+ while (src < src_end) {
+ double sum = 0;
+ float value;
+
+ for (c = 0; c < src_channels; ++c)
+ sum += *src++;
+ value = sum / (double)src_channels;
+
+ /* XXX this is actually only mono ... */
+ *dest++ = value;
+ *dest++ = value;
+ }
+}
+
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r)
+{
+ assert(src_size % (sizeof(*src) * src_channels) == 0);
+
+ size_t dest_size = src_size / src_channels * dest_channels;
+ *dest_size_r = dest_size;
+
+ float *dest = (float *)pcm_buffer_get(buffer, dest_size);
+ const float *src_end = (const float *)pcm_end_pointer(src, src_size);
+
+ if (src_channels == 1 && dest_channels == 2)
+ MonoToStereo(dest, src, src_end);
+ else if (src_channels == 2 && dest_channels == 1)
+ pcm_convert_channels_float_2_to_1(dest, src, src_end);
+ else if (dest_channels == 2)
+ pcm_convert_channels_float_n_to_2(dest, src_channels, src,
+ src_end);
+ else
+ return NULL;
+
+ return dest;
+}
diff --git a/src/pcm_channels.h b/src/PcmChannels.hxx
index 1e4a0991..ede49cd8 100644
--- a/src/pcm_channels.h
+++ b/src/PcmChannels.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PCM_CHANNELS_H
-#define MPD_PCM_CHANNELS_H
+#ifndef MPD_PCM_CHANNELS_HXX
+#define MPD_PCM_CHANNELS_HXX
#include <stdint.h>
#include <stddef.h>
@@ -77,4 +77,21 @@ pcm_convert_channels_32(struct pcm_buffer *buffer,
unsigned src_channels, const int32_t *src,
size_t src_size, size_t *dest_size_r);
+/**
+ * Changes the number of channels in 32 bit float PCM data.
+ *
+ * @param buffer the destination pcm_buffer object
+ * @param dest_channels the number of channels requested
+ * @param src_channels the number of channels in the source buffer
+ * @param src the source PCM buffer
+ * @param src_size the number of bytes in #src
+ * @param dest_size_r returns the number of bytes of the destination buffer
+ * @return the destination buffer
+ */
+const float *
+pcm_convert_channels_float(struct pcm_buffer *buffer,
+ unsigned dest_channels,
+ unsigned src_channels, const float *src,
+ size_t src_size, size_t *dest_size_r);
+
#endif
diff --git a/src/PcmConvert.cxx b/src/PcmConvert.cxx
new file mode 100644
index 00000000..9618b964
--- /dev/null
+++ b/src/PcmConvert.cxx
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PcmConvert.hxx"
+#include "PcmChannels.hxx"
+#include "PcmFormat.hxx"
+#include "pcm_pack.h"
+#include "audio_format.h"
+
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "pcm"
+
+PcmConvert::PcmConvert()
+{
+ memset(this, 0, sizeof(*this));
+
+ pcm_dsd_init(&dsd);
+ pcm_resample_init(&resample);
+
+ pcm_buffer_init(&format_buffer);
+ pcm_buffer_init(&channels_buffer);
+}
+
+PcmConvert::~PcmConvert()
+{
+ pcm_dsd_deinit(&dsd);
+ pcm_resample_deinit(&resample);
+
+ pcm_buffer_deinit(&format_buffer);
+ pcm_buffer_deinit(&channels_buffer);
+}
+
+void
+PcmConvert::Reset()
+{
+ pcm_dsd_reset(&dsd);
+ pcm_resample_reset(&resample);
+}
+
+inline const int16_t *
+PcmConvert::Convert16(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format, size_t *dest_size_r,
+ GError **error_r)
+{
+ const int16_t *buf;
+ size_t len;
+
+ assert(dest_format->format == SAMPLE_FORMAT_S16);
+
+ buf = pcm_convert_to_16(&format_buffer, dither,
+ sample_format(src_format->format),
+ src_buffer, src_size,
+ &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %s to 16 bit is not implemented",
+ sample_format_to_string(sample_format(src_format->format)));
+ return NULL;
+ }
+
+ if (src_format->channels != dest_format->channels) {
+ buf = pcm_convert_channels_16(&channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buf, len, &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
+ if (src_format->sample_rate != dest_format->sample_rate) {
+ buf = pcm_resample_16(&resample,
+ dest_format->channels,
+ src_format->sample_rate, buf, len,
+ dest_format->sample_rate, &len,
+ error_r);
+ if (buf == NULL)
+ return NULL;
+ }
+
+ *dest_size_r = len;
+ return buf;
+}
+
+inline const int32_t *
+PcmConvert::Convert24(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format, size_t *dest_size_r,
+ GError **error_r)
+{
+ const int32_t *buf;
+ size_t len;
+
+ assert(dest_format->format == SAMPLE_FORMAT_S24_P32);
+
+ buf = pcm_convert_to_24(&format_buffer,
+ sample_format(src_format->format),
+ src_buffer, src_size, &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %s to 24 bit is not implemented",
+ sample_format_to_string(sample_format(src_format->format)));
+ return NULL;
+ }
+
+ if (src_format->channels != dest_format->channels) {
+ buf = pcm_convert_channels_24(&channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buf, len, &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
+ if (src_format->sample_rate != dest_format->sample_rate) {
+ buf = pcm_resample_24(&resample,
+ dest_format->channels,
+ src_format->sample_rate, buf, len,
+ dest_format->sample_rate, &len,
+ error_r);
+ if (buf == NULL)
+ return NULL;
+ }
+
+ *dest_size_r = len;
+ return buf;
+}
+
+inline const int32_t *
+PcmConvert::Convert32(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format, size_t *dest_size_r,
+ GError **error_r)
+{
+ const int32_t *buf;
+ size_t len;
+
+ assert(dest_format->format == SAMPLE_FORMAT_S32);
+
+ buf = pcm_convert_to_32(&format_buffer,
+ sample_format(src_format->format),
+ src_buffer, src_size, &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %s to 32 bit is not implemented",
+ sample_format_to_string(sample_format(src_format->format)));
+ return NULL;
+ }
+
+ if (src_format->channels != dest_format->channels) {
+ buf = pcm_convert_channels_32(&channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buf, len, &len);
+ if (buf == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
+ if (src_format->sample_rate != dest_format->sample_rate) {
+ buf = pcm_resample_32(&resample,
+ dest_format->channels,
+ src_format->sample_rate, buf, len,
+ dest_format->sample_rate, &len,
+ error_r);
+ if (buf == NULL)
+ return buf;
+ }
+
+ *dest_size_r = len;
+ return buf;
+}
+
+inline const float *
+PcmConvert::ConvertFloat(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format, size_t *dest_size_r,
+ GError **error_r)
+{
+ const float *buffer = (const float *)src_buffer;
+ size_t size = src_size;
+
+ assert(dest_format->format == SAMPLE_FORMAT_FLOAT);
+
+ /* convert to float now */
+
+ buffer = pcm_convert_to_float(&format_buffer,
+ sample_format(src_format->format),
+ buffer, size, &size);
+ if (buffer == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %s to float is not implemented",
+ sample_format_to_string(sample_format(src_format->format)));
+ return NULL;
+ }
+
+ /* convert channels */
+
+ if (src_format->channels != dest_format->channels) {
+ buffer = pcm_convert_channels_float(&channels_buffer,
+ dest_format->channels,
+ src_format->channels,
+ buffer, size, &size);
+ if (buffer == NULL) {
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_format->channels,
+ dest_format->channels);
+ return NULL;
+ }
+ }
+
+ /* resample with float, because this is the best format for
+ libsamplerate */
+
+ if (src_format->sample_rate != dest_format->sample_rate) {
+ buffer = pcm_resample_float(&resample,
+ dest_format->channels,
+ src_format->sample_rate,
+ buffer, size,
+ dest_format->sample_rate, &size,
+ error_r);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ *dest_size_r = size;
+ return buffer;
+}
+
+const void *
+PcmConvert::Convert(const audio_format *src_format,
+ const void *src, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r)
+{
+ struct audio_format float_format;
+ if (src_format->format == SAMPLE_FORMAT_DSD) {
+ size_t f_size;
+ const float *f = pcm_dsd_to_float(&dsd,
+ src_format->channels,
+ false, (const uint8_t *)src,
+ src_size, &f_size);
+ if (f == NULL) {
+ g_set_error_literal(error_r, pcm_convert_quark(), 0,
+ "DSD to PCM conversion failed");
+ return NULL;
+ }
+
+ float_format = *src_format;
+ float_format.format = SAMPLE_FORMAT_FLOAT;
+
+ src_format = &float_format;
+ src = f;
+ src_size = f_size;
+ }
+
+ switch (sample_format(dest_format->format)) {
+ case SAMPLE_FORMAT_S16:
+ return Convert16(src_format, src, src_size,
+ dest_format, dest_size_r,
+ error_r);
+
+ case SAMPLE_FORMAT_S24_P32:
+ return Convert24(src_format, src, src_size,
+ dest_format, dest_size_r,
+ error_r);
+
+ case SAMPLE_FORMAT_S32:
+ return Convert32(src_format, src, src_size,
+ dest_format, dest_size_r,
+ error_r);
+
+ case SAMPLE_FORMAT_FLOAT:
+ return ConvertFloat(src_format, src, src_size,
+ dest_format, dest_size_r,
+ error_r);
+
+ default:
+ g_set_error(error_r, pcm_convert_quark(), 0,
+ "PCM conversion to %s is not implemented",
+ sample_format_to_string(sample_format(dest_format->format)));
+ return NULL;
+ }
+}
diff --git a/src/PcmConvert.hxx b/src/PcmConvert.hxx
new file mode 100644
index 00000000..f08188a9
--- /dev/null
+++ b/src/PcmConvert.hxx
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PCM_CONVERT_HXX
+#define PCM_CONVERT_HXX
+
+#include "PcmDither.hxx"
+
+extern "C" {
+#include "pcm_dsd.h"
+#include "pcm_resample.h"
+#include "pcm_buffer.h"
+}
+
+#include <glib.h>
+
+struct audio_format;
+
+/**
+ * This object is statically allocated (within another struct), and
+ * holds buffer allocations and the state for all kinds of PCM
+ * conversions.
+ */
+class PcmConvert {
+ struct pcm_dsd dsd;
+
+ struct pcm_resample_state resample;
+
+ PcmDither dither;
+
+ /** the buffer for converting the sample format */
+ struct pcm_buffer format_buffer;
+
+ /** the buffer for converting the channel count */
+ struct pcm_buffer channels_buffer;
+
+public:
+ PcmConvert();
+ ~PcmConvert();
+
+
+ /**
+ * Reset the pcm_convert_state object. Use this at the
+ * boundary between two distinct songs and each time the
+ * format changes.
+ */
+ void Reset();
+
+ /**
+ * Converts PCM data between two audio formats.
+ *
+ * @param src_format the source audio format
+ * @param src the source PCM buffer
+ * @param src_size the size of #src in bytes
+ * @param dest_format the requested destination audio format
+ * @param dest_size_r returns the number of bytes of the destination buffer
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return the destination buffer, or NULL on error
+ */
+ const void *Convert(const audio_format *src_format,
+ const void *src, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r);
+
+private:
+ const int16_t *Convert16(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r);
+
+ const int32_t *Convert24(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r);
+
+ const int32_t *Convert32(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r);
+
+ const float *ConvertFloat(const audio_format *src_format,
+ const void *src_buffer, size_t src_size,
+ const audio_format *dest_format,
+ size_t *dest_size_r,
+ GError **error_r);
+};
+
+static inline GQuark
+pcm_convert_quark(void)
+{
+ return g_quark_from_static_string("pcm_convert");
+}
+
+#endif
diff --git a/src/PcmDither.cxx b/src/PcmDither.cxx
new file mode 100644
index 00000000..98d0d443
--- /dev/null
+++ b/src/PcmDither.cxx
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PcmDither.hxx"
+#include "PcmPrng.hxx"
+
+inline int16_t
+PcmDither::Dither24To16(int_fast32_t sample)
+{
+ constexpr unsigned from_bits = 24;
+ constexpr unsigned to_bits = 16;
+ constexpr unsigned scale_bits = from_bits - to_bits;
+ constexpr int_fast32_t round = 1 << (scale_bits - 1);
+ constexpr int_fast32_t mask = (1 << scale_bits) - 1;
+ constexpr int_fast32_t ONE = 1 << (from_bits - 1);
+ constexpr int_fast32_t MIN = -ONE;
+ constexpr int_fast32_t MAX = ONE - 1;
+
+ sample += error[0] - error[1] + error[2];
+
+ error[2] = error[1];
+ error[1] = error[0] / 2;
+
+ /* round */
+ int_fast32_t output = sample + round;
+
+ int_fast32_t rnd = pcm_prng(random);
+ output += (rnd & mask) - (random & mask);
+
+ random = rnd;
+
+ /* clip */
+ if (output > MAX) {
+ output = MAX;
+
+ if (sample > MAX)
+ sample = MAX;
+ } else if (output < MIN) {
+ output = MIN;
+
+ if (sample < MIN)
+ sample = MIN;
+ }
+
+ output &= ~mask;
+
+ error[0] = sample - output;
+
+ return (int16_t)(output >> scale_bits);
+}
+
+void
+PcmDither::Dither24To16(int16_t *dest, const int32_t *src,
+ const int32_t *src_end)
+{
+ while (src < src_end)
+ *dest++ = Dither24To16(*src++);
+}
+
+inline int16_t
+PcmDither::Dither32To16(int_fast32_t sample)
+{
+ return Dither24To16(sample >> 8);
+}
+
+void
+PcmDither::Dither32To16(int16_t *dest, const int32_t *src,
+ const int32_t *src_end)
+{
+ while (src < src_end)
+ *dest++ = Dither32To16(*src++);
+}
diff --git a/src/pcm_dither.h b/src/PcmDither.hxx
index 046dea21..10638230 100644
--- a/src/pcm_dither.h
+++ b/src/PcmDither.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,29 +17,28 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PCM_DITHER_H
-#define MPD_PCM_DITHER_H
+#ifndef MPD_PCM_DITHER_HXX
+#define MPD_PCM_DITHER_HXX
#include <stdint.h>
-struct pcm_dither {
+class PcmDither {
int32_t error[3];
int32_t random;
-};
-static inline void
-pcm_dither_24_init(struct pcm_dither *dither)
-{
- dither->error[0] = dither->error[1] = dither->error[2] = 0;
- dither->random = 0;
-}
+public:
+ constexpr PcmDither()
+ :error{0, 0, 0}, random(0) {}
+
+ void Dither24To16(int16_t *dest, const int32_t *src,
+ const int32_t *src_end);
-void
-pcm_dither_24_to_16(struct pcm_dither *dither,
- int16_t *dest, const int32_t *src, const int32_t *src_end);
+ void Dither32To16(int16_t *dest, const int32_t *src,
+ const int32_t *src_end);
-void
-pcm_dither_32_to_16(struct pcm_dither *dither,
- int16_t *dest, const int32_t *src, const int32_t *src_end);
+private:
+ int16_t Dither24To16(int_fast32_t sample);
+ int16_t Dither32To16(int_fast32_t sample);
+};
#endif
diff --git a/src/pcm_format.c b/src/PcmFormat.cxx
index d3ea3acb..1385d161 100644
--- a/src/pcm_format.c
+++ b/src/PcmFormat.cxx
@@ -18,11 +18,21 @@
*/
#include "config.h"
-#include "pcm_format.h"
-#include "pcm_dither.h"
+#include "PcmFormat.hxx"
+#include "PcmDither.hxx"
#include "pcm_buffer.h"
#include "pcm_pack.h"
-#include "pcm_utils.h"
+#include "PcmUtils.hxx"
+
+#include <type_traits>
+
+template<typename S>
+struct DefaultSampleBits {
+ typedef decltype(*S()) T;
+ typedef typename std::remove_reference<T>::type U;
+
+ static constexpr auto value = sizeof(U) * 8;
+};
static void
pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end)
@@ -33,65 +43,90 @@ pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end)
}
static void
-pcm_convert_24_to_16(struct pcm_dither *dither,
+pcm_convert_24_to_16(PcmDither &dither,
int16_t *out, const int32_t *in, const int32_t *in_end)
{
- pcm_dither_24_to_16(dither, out, in, in_end);
+ dither.Dither24To16(out, in, in_end);
}
static void
-pcm_convert_32_to_16(struct pcm_dither *dither,
+pcm_convert_32_to_16(PcmDither &dither,
int16_t *out, const int32_t *in, const int32_t *in_end)
{
- pcm_dither_32_to_16(dither, out, in, in_end);
+ dither.Dither32To16(out, in, in_end);
}
+template<typename S, unsigned bits=DefaultSampleBits<S>::value>
static void
-pcm_convert_float_to_16(int16_t *out, const float *in, const float *in_end)
+ConvertFromFloat(S dest, const float *src, const float *end)
{
- const unsigned OUT_BITS = 16;
- const float factor = 1 << (OUT_BITS - 1);
+ typedef decltype(*S()) T;
+ typedef typename std::remove_reference<T>::type U;
- while (in < in_end) {
- int sample = *in++ * factor;
- *out++ = pcm_clamp_16(sample);
+ const float factor = 1 << (bits - 1);
+
+ while (src != end) {
+ int sample(*src++ * factor);
+ *dest++ = PcmClamp<U, int, bits>(sample);
}
}
+template<typename S, unsigned bits=DefaultSampleBits<S>::value>
+static void
+ConvertFromFloat(S dest, const float *src, size_t size)
+{
+ ConvertFromFloat<S, bits>(dest, src, pcm_end_pointer(src, size));
+}
+
+template<typename S, unsigned bits=sizeof(S)*8>
+static S *
+AllocateFromFloat(pcm_buffer &buffer, const float *src, size_t src_size,
+ size_t *dest_size_r)
+{
+ constexpr size_t src_sample_size = sizeof(*src);
+ assert(src_size % src_sample_size == 0);
+
+ const size_t num_samples = src_size / src_sample_size;
+ *dest_size_r = num_samples * sizeof(S);
+ S *dest = (S *)pcm_buffer_get(&buffer, *dest_size_r);
+ ConvertFromFloat<S *, bits>(dest, src, src_size);
+ return dest;
+}
+
static int16_t *
pcm_allocate_8_to_16(struct pcm_buffer *buffer,
const int8_t *src, size_t src_size, size_t *dest_size_r)
{
int16_t *dest;
*dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_8_to_16(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
static int16_t *
-pcm_allocate_24p32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
+pcm_allocate_24p32_to_16(struct pcm_buffer *buffer, PcmDither &dither,
const int32_t *src, size_t src_size,
size_t *dest_size_r)
{
int16_t *dest;
*dest_size_r = src_size / 2;
assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_24_to_16(dither, dest, src,
pcm_end_pointer(src, src_size));
return dest;
}
static int16_t *
-pcm_allocate_32_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
+pcm_allocate_32_to_16(struct pcm_buffer *buffer, PcmDither &dither,
const int32_t *src, size_t src_size,
size_t *dest_size_r)
{
int16_t *dest;
*dest_size_r = src_size / 2;
assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int16_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_32_to_16(dither, dest, src,
pcm_end_pointer(src, src_size));
return dest;
@@ -102,17 +137,11 @@ pcm_allocate_float_to_16(struct pcm_buffer *buffer,
const float *src, size_t src_size,
size_t *dest_size_r)
{
- int16_t *dest;
- *dest_size_r = src_size / 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_float_to_16(dest, src,
- pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateFromFloat<int16_t>(*buffer, src, src_size, dest_size_r);
}
const int16_t *
-pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
+pcm_convert_to_16(struct pcm_buffer *buffer, PcmDither &dither,
enum sample_format src_format, const void *src,
size_t src_size, size_t *dest_size_r)
{
@@ -125,22 +154,26 @@ pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
case SAMPLE_FORMAT_S8:
return pcm_allocate_8_to_16(buffer,
- src, src_size, dest_size_r);
+ (const int8_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S16:
*dest_size_r = src_size;
- return src;
+ return (const int16_t *)src;
case SAMPLE_FORMAT_S24_P32:
- return pcm_allocate_24p32_to_16(buffer, dither, src, src_size,
+ return pcm_allocate_24p32_to_16(buffer, dither,
+ (const int32_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_S32:
- return pcm_allocate_32_to_16(buffer, dither, src, src_size,
+ return pcm_allocate_32_to_16(buffer, dither,
+ (const int32_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_FLOAT:
- return pcm_allocate_float_to_16(buffer, src, src_size,
+ return pcm_allocate_float_to_16(buffer,
+ (const float *)src, src_size,
dest_size_r);
}
@@ -170,25 +203,13 @@ pcm_convert_32_to_24(int32_t *restrict out,
*out++ = *in++ >> 8;
}
-static void
-pcm_convert_float_to_24(int32_t *out, const float *in, const float *in_end)
-{
- const unsigned OUT_BITS = 24;
- const float factor = 1 << (OUT_BITS - 1);
-
- while (in < in_end) {
- int sample = *in++ * factor;
- *out++ = pcm_clamp_24(sample);
- }
-}
-
static int32_t *
pcm_allocate_8_to_24(struct pcm_buffer *buffer,
const int8_t *src, size_t src_size, size_t *dest_size_r)
{
int32_t *dest;
*dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_8_to_24(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -200,7 +221,7 @@ pcm_allocate_16_to_24(struct pcm_buffer *buffer,
int32_t *dest;
*dest_size_r = src_size * 2;
assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_16_to_24(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -210,7 +231,7 @@ pcm_allocate_32_to_24(struct pcm_buffer *buffer,
const int32_t *src, size_t src_size, size_t *dest_size_r)
{
*dest_size_r = src_size;
- int32_t *dest = pcm_buffer_get(buffer, *dest_size_r);
+ int32_t *dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_32_to_24(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -220,10 +241,8 @@ pcm_allocate_float_to_24(struct pcm_buffer *buffer,
const float *src, size_t src_size,
size_t *dest_size_r)
{
- *dest_size_r = src_size;
- int32_t *dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_float_to_24(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateFromFloat<int32_t, 24>(*buffer, src, src_size,
+ dest_size_r);
}
const int32_t *
@@ -240,22 +259,26 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
case SAMPLE_FORMAT_S8:
return pcm_allocate_8_to_24(buffer,
- src, src_size, dest_size_r);
+ (const int8_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S16:
return pcm_allocate_16_to_24(buffer,
- src, src_size, dest_size_r);
+ (const int16_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S24_P32:
*dest_size_r = src_size;
- return src;
+ return (const int32_t *)src;
case SAMPLE_FORMAT_S32:
- return pcm_allocate_32_to_24(buffer, src, src_size,
+ return pcm_allocate_32_to_24(buffer,
+ (const int32_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_FLOAT:
- return pcm_allocate_float_to_24(buffer, src, src_size,
+ return pcm_allocate_float_to_24(buffer,
+ (const float *)src, src_size,
dest_size_r);
}
@@ -291,7 +314,7 @@ pcm_allocate_8_to_32(struct pcm_buffer *buffer,
{
int32_t *dest;
*dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_8_to_32(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -303,7 +326,7 @@ pcm_allocate_16_to_32(struct pcm_buffer *buffer,
int32_t *dest;
*dest_size_r = src_size * 2;
assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
+ dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_16_to_32(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -314,7 +337,7 @@ pcm_allocate_24p32_to_32(struct pcm_buffer *buffer,
size_t *dest_size_r)
{
*dest_size_r = src_size;
- int32_t *dest = pcm_buffer_get(buffer, *dest_size_r);
+ int32_t *dest = (int32_t *)pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_24_to_32(dest, src, pcm_end_pointer(src, src_size));
return dest;
}
@@ -346,63 +369,63 @@ pcm_convert_to_32(struct pcm_buffer *buffer,
break;
case SAMPLE_FORMAT_S8:
- return pcm_allocate_8_to_32(buffer, src, src_size,
+ return pcm_allocate_8_to_32(buffer,
+ (const int8_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_S16:
- return pcm_allocate_16_to_32(buffer, src, src_size,
+ return pcm_allocate_16_to_32(buffer,
+ (const int16_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_S24_P32:
- return pcm_allocate_24p32_to_32(buffer, src, src_size,
+ return pcm_allocate_24p32_to_32(buffer,
+ (const int32_t *)src, src_size,
dest_size_r);
case SAMPLE_FORMAT_S32:
*dest_size_r = src_size;
- return src;
+ return (const int32_t *)src;
case SAMPLE_FORMAT_FLOAT:
- return pcm_allocate_float_to_32(buffer, src, src_size,
+ return pcm_allocate_float_to_32(buffer,
+ (const float *)src, src_size,
dest_size_r);
}
return NULL;
}
+template<typename S, unsigned bits=DefaultSampleBits<S>::value>
static void
-pcm_convert_8_to_float(float *out, const int8_t *in, const int8_t *in_end)
+ConvertToFloat(float *dest, S src, S end)
{
- enum { in_bits = sizeof(*in) * 8 };
- static const float factor = 2.0f / (1 << in_bits);
- while (in < in_end)
- *out++ = (float)*in++ * factor;
-}
+ constexpr float factor = 0.5 / (1 << (bits - 2));
+ while (src != end)
+ *dest++ = float(*src++) * factor;
-static void
-pcm_convert_16_to_float(float *out, const int16_t *in, const int16_t *in_end)
-{
- enum { in_bits = sizeof(*in) * 8 };
- static const float factor = 2.0f / (1 << in_bits);
- while (in < in_end)
- *out++ = (float)*in++ * factor;
}
+template<typename S, unsigned bits=DefaultSampleBits<S>::value>
static void
-pcm_convert_24_to_float(float *out, const int32_t *in, const int32_t *in_end)
+ConvertToFloat(float *dest, S src, size_t size)
{
- enum { in_bits = 24 };
- static const float factor = 2.0f / (1 << in_bits);
- while (in < in_end)
- *out++ = (float)*in++ * factor;
+ ConvertToFloat<S, bits>(dest, src, pcm_end_pointer(src, size));
}
-static void
-pcm_convert_32_to_float(float *out, const int32_t *in, const int32_t *in_end)
+template<typename S, unsigned bits=DefaultSampleBits<S>::value>
+static float *
+AllocateToFloat(pcm_buffer &buffer, S src, size_t src_size,
+ size_t *dest_size_r)
{
- enum { in_bits = sizeof(*in) * 8 };
- static const float factor = 0.5f / (1 << (in_bits - 2));
- while (in < in_end)
- *out++ = (float)*in++ * factor;
+ constexpr size_t src_sample_size = sizeof(*S());
+ assert(src_size % src_sample_size == 0);
+
+ const size_t num_samples = src_size / src_sample_size;
+ *dest_size_r = num_samples * sizeof(float);
+ float *dest = (float *)pcm_buffer_get(&buffer, *dest_size_r);
+ ConvertToFloat<S, bits>(dest, src, src_size);
+ return dest;
}
static float *
@@ -410,11 +433,7 @@ pcm_allocate_8_to_float(struct pcm_buffer *buffer,
const int8_t *src, size_t src_size,
size_t *dest_size_r)
{
- float *dest;
- *dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_8_to_float(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateToFloat(*buffer, src, src_size, dest_size_r);
}
static float *
@@ -422,23 +441,16 @@ pcm_allocate_16_to_float(struct pcm_buffer *buffer,
const int16_t *src, size_t src_size,
size_t *dest_size_r)
{
- float *dest;
- *dest_size_r = src_size * 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_16_to_float(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateToFloat(*buffer, src, src_size, dest_size_r);
}
static float *
pcm_allocate_24p32_to_float(struct pcm_buffer *buffer,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+ const int32_t *src, size_t src_size,
+ size_t *dest_size_r)
{
- *dest_size_r = src_size;
- float *dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_24_to_float(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateToFloat<decltype(src), 24>
+ (*buffer, src, src_size, dest_size_r);
}
static float *
@@ -446,10 +458,7 @@ pcm_allocate_32_to_float(struct pcm_buffer *buffer,
const int32_t *src, size_t src_size,
size_t *dest_size_r)
{
- *dest_size_r = src_size;
- float *dest = pcm_buffer_get(buffer, *dest_size_r);
- pcm_convert_32_to_float(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateToFloat(*buffer, src, src_size, dest_size_r);
}
const float *
@@ -464,23 +473,27 @@ pcm_convert_to_float(struct pcm_buffer *buffer,
case SAMPLE_FORMAT_S8:
return pcm_allocate_8_to_float(buffer,
- src, src_size, dest_size_r);
+ (const int8_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S16:
return pcm_allocate_16_to_float(buffer,
- src, src_size, dest_size_r);
+ (const int16_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S24_P32:
return pcm_allocate_24p32_to_float(buffer,
- src, src_size, dest_size_r);
+ (const int32_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_S32:
return pcm_allocate_32_to_float(buffer,
- src, src_size, dest_size_r);
+ (const int32_t *)src, src_size,
+ dest_size_r);
case SAMPLE_FORMAT_FLOAT:
*dest_size_r = src_size;
- return src;
+ return (const float *)src;
}
return NULL;
diff --git a/src/pcm_format.h b/src/PcmFormat.hxx
index 48bcd066..a5970b2d 100644
--- a/src/pcm_format.h
+++ b/src/PcmFormat.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PCM_FORMAT_H
-#define PCM_FORMAT_H
+#ifndef MPD_PCM_FORMAT_HXX
+#define MPD_PCM_FORMAT_HXX
#include "audio_format.h"
@@ -26,7 +26,7 @@
#include <stddef.h>
struct pcm_buffer;
-struct pcm_dither;
+class PcmDither;
/**
* Converts PCM samples to 16 bit. If the source format is 24 bit,
@@ -41,7 +41,7 @@ struct pcm_dither;
* @return the destination buffer
*/
const int16_t *
-pcm_convert_to_16(struct pcm_buffer *buffer, struct pcm_dither *dither,
+pcm_convert_to_16(struct pcm_buffer *buffer, PcmDither &dither,
enum sample_format src_format, const void *src,
size_t src_size, size_t *dest_size_r);
diff --git a/src/PcmMix.cxx b/src/PcmMix.cxx
new file mode 100644
index 00000000..8435c0c2
--- /dev/null
+++ b/src/PcmMix.cxx
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PcmMix.hxx"
+#include "PcmVolume.hxx"
+#include "PcmUtils.hxx"
+#include "audio_format.h"
+
+#include <math.h>
+
+template<typename T, typename U, unsigned bits>
+static T
+PcmAddVolume(T _a, T _b, int volume1, int volume2)
+{
+ U a(_a), b(_b);
+
+ U c = ((a * volume1 + b * volume2) +
+ pcm_volume_dither() + PCM_VOLUME_1 / 2)
+ / PCM_VOLUME_1;
+
+ return PcmClamp<T, U, bits>(c);
+}
+
+template<typename T, typename U, unsigned bits>
+static void
+PcmAddVolume(T *a, const T *b, unsigned n, int volume1, int volume2)
+{
+ for (size_t i = 0; i != n; ++i)
+ a[i] = PcmAddVolume<T, U, bits>(a[i], b[i], volume1, volume2);
+}
+
+template<typename T, typename U, unsigned bits>
+static void
+PcmAddVolumeVoid(void *a, const void *b, size_t size, int volume1, int volume2)
+{
+ constexpr size_t sample_size = sizeof(T);
+ assert(size % sample_size == 0);
+
+ PcmAddVolume<T, U, bits>((T *)a, (const T *)b, size / sample_size,
+ volume1, volume2);
+}
+
+static void
+pcm_add_vol_float(float *buffer1, const float *buffer2,
+ unsigned num_samples, float volume1, float volume2)
+{
+ while (num_samples > 0) {
+ float sample1 = *buffer1;
+ float sample2 = *buffer2++;
+
+ sample1 = (sample1 * volume1 + sample2 * volume2);
+ *buffer1++ = sample1;
+ --num_samples;
+ }
+}
+
+static bool
+pcm_add_vol(void *buffer1, const void *buffer2, size_t size,
+ int vol1, int vol2,
+ enum sample_format format)
+{
+ switch (format) {
+ case SAMPLE_FORMAT_UNDEFINED:
+ case SAMPLE_FORMAT_DSD:
+ /* not implemented */
+ return false;
+
+ case SAMPLE_FORMAT_S8:
+ PcmAddVolumeVoid<int8_t, int32_t, 8>(buffer1, buffer2, size,
+ vol1, vol2);
+ return true;
+
+ case SAMPLE_FORMAT_S16:
+ PcmAddVolumeVoid<int16_t, int32_t, 16>(buffer1, buffer2, size,
+ vol1, vol2);
+ return true;
+
+ case SAMPLE_FORMAT_S24_P32:
+ PcmAddVolumeVoid<int32_t, int64_t, 24>(buffer1, buffer2, size,
+ vol1, vol2);
+ return true;
+
+ case SAMPLE_FORMAT_S32:
+ PcmAddVolumeVoid<int32_t, int64_t, 32>(buffer1, buffer2, size,
+ vol1, vol2);
+ return true;
+
+ case SAMPLE_FORMAT_FLOAT:
+ pcm_add_vol_float((float *)buffer1, (const float *)buffer2,
+ size / 4,
+ pcm_volume_to_float(vol1),
+ pcm_volume_to_float(vol2));
+ return true;
+ }
+
+ /* unreachable */
+ assert(false);
+ return false;
+}
+
+template<typename T, typename U, unsigned bits>
+static T
+PcmAdd(T _a, T _b)
+{
+ U a(_a), b(_b);
+ return PcmClamp<T, U, bits>(a + b);
+}
+
+template<typename T, typename U, unsigned bits>
+static void
+PcmAdd(T *a, const T *b, unsigned n)
+{
+ for (size_t i = 0; i != n; ++i)
+ a[i] = PcmAdd<T, U, bits>(a[i], b[i]);
+}
+
+template<typename T, typename U, unsigned bits>
+static void
+PcmAddVoid(void *a, const void *b, size_t size)
+{
+ constexpr size_t sample_size = sizeof(T);
+ assert(size % sample_size == 0);
+
+ PcmAdd<T, U, bits>((T *)a, (const T *)b, size / sample_size);
+}
+
+static void
+pcm_add_float(float *buffer1, const float *buffer2, unsigned num_samples)
+{
+ while (num_samples > 0) {
+ float sample1 = *buffer1;
+ float sample2 = *buffer2++;
+ *buffer1++ = sample1 + sample2;
+ --num_samples;
+ }
+}
+
+static bool
+pcm_add(void *buffer1, const void *buffer2, size_t size,
+ enum sample_format format)
+{
+ switch (format) {
+ case SAMPLE_FORMAT_UNDEFINED:
+ case SAMPLE_FORMAT_DSD:
+ /* not implemented */
+ return false;
+
+ case SAMPLE_FORMAT_S8:
+ PcmAddVoid<int8_t, int32_t, 8>(buffer1, buffer2, size);
+ return true;
+
+ case SAMPLE_FORMAT_S16:
+ PcmAddVoid<int16_t, int32_t, 16>(buffer1, buffer2, size);
+ return true;
+
+ case SAMPLE_FORMAT_S24_P32:
+ PcmAddVoid<int32_t, int64_t, 24>(buffer1, buffer2, size);
+ return true;
+
+ case SAMPLE_FORMAT_S32:
+ PcmAddVoid<int32_t, int64_t, 32>(buffer1, buffer2, size);
+ return true;
+
+ case SAMPLE_FORMAT_FLOAT:
+ pcm_add_float((float *)buffer1, (const float *)buffer2,
+ size / 4);
+ return true;
+ }
+
+ /* unreachable */
+ assert(false);
+ return false;
+}
+
+bool
+pcm_mix(void *buffer1, const void *buffer2, size_t size,
+ enum sample_format format, float portion1)
+{
+ int vol1;
+ float s;
+
+ /* portion1 is between 0.0 and 1.0 for crossfading, MixRamp uses NaN
+ * to signal mixing rather than fading */
+ if (isnan(portion1))
+ return pcm_add(buffer1, buffer2, size, format);
+
+ s = sin(M_PI_2 * portion1);
+ s *= s;
+
+ vol1 = s * PCM_VOLUME_1 + 0.5;
+ vol1 = vol1 > PCM_VOLUME_1 ? PCM_VOLUME_1 : (vol1 < 0 ? 0 : vol1);
+
+ return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1 - vol1, format);
+}
diff --git a/src/pcm_mix.h b/src/PcmMix.hxx
index 0e58d01e..bb7110d0 100644
--- a/src/pcm_mix.h
+++ b/src/PcmMix.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,12 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PCM_MIX_H
-#define PCM_MIX_H
+#ifndef MPD_PCM_MIX_HXX
+#define MPD_PCM_MIX_HXX
#include "audio_format.h"
+#include "gcc.h"
-#include <stdbool.h>
#include <stddef.h>
/*
@@ -41,7 +41,7 @@
*
* @return true on success, false if the format is not supported
*/
-G_GNUC_WARN_UNUSED_RESULT
+gcc_warn_unused_result
bool
pcm_mix(void *buffer1, const void *buffer2, size_t size,
enum sample_format format, float portion1);
diff --git a/src/pcm_prng.h b/src/PcmPrng.hxx
index 457ba4b6..0c823250 100644
--- a/src/pcm_prng.h
+++ b/src/PcmPrng.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PCM_PRNG_H
-#define PCM_PRNG_H
+#ifndef MPD_PCM_PRNG_HXX
+#define MPD_PCM_PRNG_HXX
/**
* A very simple linear congruential PRNG. It's good enough for PCM
diff --git a/src/PcmUtils.hxx b/src/PcmUtils.hxx
new file mode 100644
index 00000000..d77c4194
--- /dev/null
+++ b/src/PcmUtils.hxx
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_UTILS_H
+#define MPD_PCM_UTILS_H
+
+#include "gcc.h"
+
+#include <limits>
+
+#include <stdint.h>
+
+/**
+ * Add a byte count to the specified pointer. This is a utility
+ * function to convert a source pointer and a byte count to an "end"
+ * pointer for use in loops.
+ */
+template<typename T>
+static inline const T *
+pcm_end_pointer(const T *p, size_t size)
+{
+ return (const T *)((const uint8_t *)p + size);
+}
+
+/**
+ * Check if the value is within the range of the provided bit size,
+ * and caps it if necessary.
+ */
+template<typename T, typename U, unsigned bits>
+gcc_const
+static inline T
+PcmClamp(U x)
+{
+ constexpr U MIN_VALUE = -(U(1) << (bits - 1));
+ constexpr U MAX_VALUE = (U(1) << (bits - 1)) - 1;
+
+ typedef std::numeric_limits<T> limits;
+ static_assert(MIN_VALUE >= limits::min(), "out of range");
+ static_assert(MAX_VALUE <= limits::max(), "out of range");
+
+ if (gcc_unlikely(x < MIN_VALUE))
+ return T(MIN_VALUE);
+
+ if (gcc_unlikely(x > MAX_VALUE))
+ return T(MAX_VALUE);
+
+ return T(x);
+}
+
+#endif
diff --git a/src/pcm_volume.c b/src/PcmVolume.cxx
index 49c86026..556ab992 100644
--- a/src/pcm_volume.c
+++ b/src/PcmVolume.cxx
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "pcm_volume.h"
-#include "pcm_utils.h"
+#include "PcmVolume.hxx"
+#include "PcmUtils.hxx"
#include "audio_format.h"
#include <glib.h>
@@ -40,7 +40,7 @@ pcm_volume_change_8(int8_t *buffer, const int8_t *end, int volume)
PCM_VOLUME_1 / 2)
/ PCM_VOLUME_1;
- *buffer++ = pcm_range(sample, 8);
+ *buffer++ = PcmClamp<int8_t, int16_t, 8>(sample);
}
}
@@ -54,7 +54,7 @@ pcm_volume_change_16(int16_t *buffer, const int16_t *end, int volume)
PCM_VOLUME_1 / 2)
/ PCM_VOLUME_1;
- *buffer++ = pcm_range(sample, 16);
+ *buffer++ = PcmClamp<int16_t, int32_t, 16>(sample);
}
}
@@ -107,7 +107,7 @@ pcm_volume_change_24(int32_t *buffer, const int32_t *end, int volume)
PCM_VOLUME_1 / 2)
/ PCM_VOLUME_1;
#endif
- *buffer++ = pcm_range(sample, 24);
+ *buffer++ = PcmClamp<int32_t, int32_t, 24>(sample);
}
}
@@ -127,7 +127,7 @@ pcm_volume_change_32(int32_t *buffer, const int32_t *end, int volume)
sample = (sample * volume + pcm_volume_dither() +
PCM_VOLUME_1 / 2)
/ PCM_VOLUME_1;
- *buffer++ = pcm_range_64(sample, 32);
+ *buffer++ = PcmClamp<int32_t, int64_t, 32>(sample);
#endif
}
}
@@ -163,23 +163,27 @@ pcm_volume(void *buffer, size_t length,
return false;
case SAMPLE_FORMAT_S8:
- pcm_volume_change_8(buffer, end, volume);
+ pcm_volume_change_8((int8_t *)buffer, (const int8_t *)end,
+ volume);
return true;
case SAMPLE_FORMAT_S16:
- pcm_volume_change_16(buffer, end, volume);
+ pcm_volume_change_16((int16_t *)buffer, (const int16_t *)end,
+ volume);
return true;
case SAMPLE_FORMAT_S24_P32:
- pcm_volume_change_24(buffer, end, volume);
+ pcm_volume_change_24((int32_t *)buffer, (const int32_t *)end,
+ volume);
return true;
case SAMPLE_FORMAT_S32:
- pcm_volume_change_32(buffer, end, volume);
+ pcm_volume_change_32((int32_t *)buffer, (const int32_t *)end,
+ volume);
return true;
case SAMPLE_FORMAT_FLOAT:
- pcm_volume_change_float(buffer, end,
+ pcm_volume_change_float((float *)buffer, (const float *)end,
pcm_volume_to_float(volume));
return true;
}
diff --git a/src/pcm_volume.h b/src/PcmVolume.hxx
index 64e3c764..d3e6a553 100644
--- a/src/pcm_volume.h
+++ b/src/PcmVolume.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PCM_VOLUME_H
-#define PCM_VOLUME_H
+#ifndef MPD_PCM_VOLUME_HXX
+#define MPD_PCM_VOLUME_HXX
-#include "pcm_prng.h"
+#include "PcmPrng.hxx"
#include "audio_format.h"
#include <stdint.h>
#include <stdbool.h>
+#include <stddef.h>
enum {
/** this value means "100% volume" */
diff --git a/src/permission.c b/src/Permission.cxx
index cd52b9c8..e6cf5cfb 100644
--- a/src/permission.c
+++ b/src/Permission.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,12 @@
*/
#include "config.h"
-#include "permission.h"
-#include "conf.h"
+#include "Permission.hxx"
#include "mpd_error.h"
+#include "conf.h"
+
+#include <map>
+#include <string>
#include <glib.h>
@@ -35,7 +38,7 @@
#define PERMISSION_CONTROL_STRING "control"
#define PERMISSION_ADMIN_STRING "admin"
-static GHashTable *permission_passwords;
+static std::map<std::string, unsigned> permission_passwords;
static unsigned permission_default;
@@ -75,9 +78,6 @@ void initPermissions(void)
unsigned permission;
const struct config_param *param;
- permission_passwords = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
-
permission_default = PERMISSION_READ | PERMISSION_ADD |
PERMISSION_CONTROL | PERMISSION_ADMIN;
@@ -101,9 +101,8 @@ void initPermissions(void)
permission = parsePermissions(separator + 1);
- g_hash_table_replace(permission_passwords,
- password,
- GINT_TO_POINTER(permission));
+ permission_passwords.insert(std::make_pair(password,
+ permission));
} while ((param = config_get_next_param(CONF_PASSWORD, param)));
}
@@ -115,23 +114,14 @@ void initPermissions(void)
int getPermissionFromPassword(char const* password, unsigned* permission)
{
- bool found;
- gpointer key, value;
-
- found = g_hash_table_lookup_extended(permission_passwords,
- password, &key, &value);
- if (!found)
+ auto i = permission_passwords.find(password);
+ if (i == permission_passwords.end())
return -1;
- *permission = GPOINTER_TO_INT(value);
+ *permission = i->second;
return 0;
}
-void finishPermissions(void)
-{
- g_hash_table_destroy(permission_passwords);
-}
-
unsigned getDefaultPermissions(void)
{
return permission_default;
diff --git a/src/permission.h b/src/Permission.hxx
index 6c377136..4ff3850e 100644
--- a/src/permission.h
+++ b/src/Permission.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PERMISSION_H
-#define MPD_PERMISSION_H
+#ifndef MPD_PERMISSION_HXX
+#define MPD_PERMISSION_HXX
#define PERMISSION_NONE 0
#define PERMISSION_READ 1
@@ -29,8 +29,6 @@
int getPermissionFromPassword(char const* password, unsigned* permission);
-void finishPermissions(void);
-
unsigned getDefaultPermissions(void);
void initPermissions(void);
diff --git a/src/PlayerCommands.cxx b/src/PlayerCommands.cxx
new file mode 100644
index 00000000..32cd16d9
--- /dev/null
+++ b/src/PlayerCommands.cxx
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlayerCommands.hxx"
+#include "CommandError.hxx"
+#include "Playlist.hxx"
+#include "PlaylistPrint.hxx"
+#include "UpdateGlue.hxx"
+#include "ClientInternal.hxx"
+#include "Volume.hxx"
+#include "OutputAll.hxx"
+#include "Partition.hxx"
+#include "protocol/Result.hxx"
+#include "protocol/ArgParser.hxx"
+
+extern "C" {
+#include "audio_format.h"
+}
+
+#include "replay_gain_config.h"
+
+#include <errno.h>
+
+#define COMMAND_STATUS_STATE "state"
+#define COMMAND_STATUS_REPEAT "repeat"
+#define COMMAND_STATUS_SINGLE "single"
+#define COMMAND_STATUS_CONSUME "consume"
+#define COMMAND_STATUS_RANDOM "random"
+#define COMMAND_STATUS_PLAYLIST "playlist"
+#define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
+#define COMMAND_STATUS_SONG "song"
+#define COMMAND_STATUS_SONGID "songid"
+#define COMMAND_STATUS_NEXTSONG "nextsong"
+#define COMMAND_STATUS_NEXTSONGID "nextsongid"
+#define COMMAND_STATUS_TIME "time"
+#define COMMAND_STATUS_BITRATE "bitrate"
+#define COMMAND_STATUS_ERROR "error"
+#define COMMAND_STATUS_CROSSFADE "xfade"
+#define COMMAND_STATUS_MIXRAMPDB "mixrampdb"
+#define COMMAND_STATUS_MIXRAMPDELAY "mixrampdelay"
+#define COMMAND_STATUS_AUDIO "audio"
+#define COMMAND_STATUS_UPDATING_DB "updating_db"
+
+enum command_return
+handle_play(Client *client, int argc, char *argv[])
+{
+ int song = -1;
+
+ if (argc == 2 && !check_int(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ enum playlist_result result = client->partition.PlayPosition(song);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_playid(Client *client, int argc, char *argv[])
+{
+ int id = -1;
+
+ if (argc == 2 && !check_int(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.PlayId(id);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_stop(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client->partition.Stop();
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_currentsong(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_print_current(client, &client->playlist);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_pause(Client *client,
+ int argc, char *argv[])
+{
+ if (argc == 2) {
+ bool pause_flag;
+ if (!check_bool(client, &pause_flag, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->player_control->SetPause(pause_flag);
+ } else
+ client->player_control->Pause();
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_status(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ const char *state = NULL;
+ int updateJobId;
+ int song;
+
+ const auto player_status = client->player_control->GetStatus();
+
+ switch (player_status.state) {
+ case PLAYER_STATE_STOP:
+ state = "stop";
+ break;
+ case PLAYER_STATE_PAUSE:
+ state = "pause";
+ break;
+ case PLAYER_STATE_PLAY:
+ state = "play";
+ break;
+ }
+
+ const playlist &playlist = client->playlist;
+ client_printf(client,
+ "volume: %i\n"
+ COMMAND_STATUS_REPEAT ": %i\n"
+ COMMAND_STATUS_RANDOM ": %i\n"
+ COMMAND_STATUS_SINGLE ": %i\n"
+ COMMAND_STATUS_CONSUME ": %i\n"
+ COMMAND_STATUS_PLAYLIST ": %li\n"
+ COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
+ COMMAND_STATUS_CROSSFADE ": %i\n"
+ COMMAND_STATUS_MIXRAMPDB ": %f\n"
+ COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
+ COMMAND_STATUS_STATE ": %s\n",
+ volume_level_get(),
+ playlist.GetRepeat(),
+ playlist.GetRandom(),
+ playlist.GetSingle(),
+ playlist.GetConsume(),
+ (unsigned long)playlist.GetVersion(),
+ playlist.GetLength(),
+ (int)(client->player_control->GetCrossFade() + 0.5),
+ client->player_control->GetMixRampDb(),
+ client->player_control->GetMixRampDelay(),
+ state);
+
+ song = playlist.GetCurrentPosition();
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_SONG ": %i\n"
+ COMMAND_STATUS_SONGID ": %u\n",
+ song, playlist.PositionToId(song));
+ }
+
+ if (player_status.state != PLAYER_STATE_STOP) {
+ struct audio_format_string af_string;
+
+ client_printf(client,
+ COMMAND_STATUS_TIME ": %i:%i\n"
+ "elapsed: %1.3f\n"
+ COMMAND_STATUS_BITRATE ": %u\n"
+ COMMAND_STATUS_AUDIO ": %s\n",
+ (int)(player_status.elapsed_time + 0.5),
+ (int)(player_status.total_time + 0.5),
+ player_status.elapsed_time,
+ player_status.bit_rate,
+ audio_format_to_string(&player_status.audio_format,
+ &af_string));
+ }
+
+ if ((updateJobId = isUpdatingDB())) {
+ client_printf(client,
+ COMMAND_STATUS_UPDATING_DB ": %i\n",
+ updateJobId);
+ }
+
+ char *error = client->player_control->GetErrorMessage();
+ if (error != NULL) {
+ client_printf(client,
+ COMMAND_STATUS_ERROR ": %s\n",
+ error);
+ g_free(error);
+ }
+
+ song = playlist.GetNextPosition();
+ if (song >= 0) {
+ client_printf(client,
+ COMMAND_STATUS_NEXTSONG ": %i\n"
+ COMMAND_STATUS_NEXTSONGID ": %u\n",
+ song, playlist.PositionToId(song));
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_next(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist &playlist = client->playlist;
+
+ /* single mode is not considered when this is user who
+ * wants to change song. */
+ const bool single = playlist.queue.single;
+ playlist.queue.single = false;
+
+ client->partition.PlayNext();
+
+ playlist.queue.single = single;
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_previous(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client->partition.PlayPrevious();
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_repeat(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->partition.SetRepeat(status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_single(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->partition.SetSingle(status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_consume(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->partition.SetConsume(status);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_random(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ bool status;
+ if (!check_bool(client, &status, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->partition.SetRandom(status);
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->partition.GetRandom()));
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_clearerror(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client->player_control->ClearError();
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_seek(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned song, seek_time;
+
+ if (!check_unsigned(client, &song, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SeekSongPosition(song, seek_time);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_seekid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id, seek_time;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &seek_time, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SeekSongId(id, seek_time);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_seekcur(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ const char *p = argv[1];
+ bool relative = *p == '+' || *p == '-';
+ int seek_time;
+ if (!check_int(client, &seek_time, p))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SeekCurrent(seek_time, relative);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_crossfade(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned xfade_time;
+
+ if (!check_unsigned(client, &xfade_time, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ client->player_control->SetCrossFade(xfade_time);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_mixrampdb(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ float db;
+
+ if (!check_float(client, &db, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ client->player_control->SetMixRampDb(db);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_mixrampdelay(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ float delay_secs;
+
+ if (!check_float(client, &delay_secs, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ client->player_control->SetMixRampDelay(delay_secs);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_replay_gain_mode(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (!replay_gain_set_mode_string(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "Unrecognized replay gain mode");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ audio_output_all_set_replay_gain_mode(replay_gain_get_real_mode(client->playlist.queue.random));
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_replay_gain_status(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client_printf(client, "replay_gain_mode: %s\n",
+ replay_gain_get_mode_string());
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/PlayerCommands.hxx b/src/PlayerCommands.hxx
new file mode 100644
index 00000000..a2fed585
--- /dev/null
+++ b/src/PlayerCommands.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYER_COMMANDS_HXX
+#define MPD_PLAYER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_play(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_stop(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_currentsong(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_pause(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_status(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_next(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_previous(Client *client, int argc, char *avg[]);
+
+enum command_return
+handle_repeat(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_single(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_consume(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_random(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_clearerror(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seek(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seekid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_seekcur(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_crossfade(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_mixrampdb(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_mixrampdelay(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_replay_gain_mode(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_replay_gain_status(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/PlayerControl.cxx b/src/PlayerControl.cxx
new file mode 100644
index 00000000..d3e8c7d0
--- /dev/null
+++ b/src/PlayerControl.cxx
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlayerControl.hxx"
+#include "Idle.hxx"
+#include "song.h"
+#include "DecoderControl.hxx"
+#include "Main.hxx"
+
+#include <cmath>
+
+#include <assert.h>
+#include <stdio.h>
+
+static void
+pc_enqueue_song_locked(struct player_control *pc, struct song *song);
+
+player_control::player_control(unsigned _buffer_chunks,
+ unsigned _buffered_before_play)
+ :buffer_chunks(_buffer_chunks),
+ buffered_before_play(_buffered_before_play),
+ thread(nullptr),
+ command(PLAYER_COMMAND_NONE),
+ state(PLAYER_STATE_STOP),
+ error_type(PLAYER_ERROR_NONE),
+ error(nullptr),
+ next_song(nullptr),
+ cross_fade_seconds(0),
+ mixramp_db(0),
+#if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_C99_MATH_TR1)
+ /* workaround: on MacPorts, this option is disabled on gcc47,
+ and therefore std::nanf() is not available */
+ mixramp_delay_seconds(nanf("")),
+#else
+ mixramp_delay_seconds(std::nanf("")),
+#endif
+ total_play_time(0),
+ border_pause(false)
+{
+}
+
+player_control::~player_control()
+{
+ if (next_song != nullptr)
+ song_free(next_song);
+}
+
+static void
+player_command_wait_locked(struct player_control *pc)
+{
+ while (pc->command != PLAYER_COMMAND_NONE)
+ pc->ClientWait();
+}
+
+static void
+player_command_locked(struct player_control *pc, enum player_command cmd)
+{
+ assert(pc->command == PLAYER_COMMAND_NONE);
+
+ pc->command = cmd;
+ pc->Signal();
+ player_command_wait_locked(pc);
+}
+
+static void
+player_command(struct player_control *pc, enum player_command cmd)
+{
+ pc->Lock();
+ player_command_locked(pc, cmd);
+ pc->Unlock();
+}
+
+void
+player_control::Play(struct song *song)
+{
+ assert(song != NULL);
+
+ Lock();
+
+ if (state != PLAYER_STATE_STOP)
+ player_command_locked(this, PLAYER_COMMAND_STOP);
+
+ assert(next_song == nullptr);
+
+ pc_enqueue_song_locked(this, song);
+
+ assert(next_song == nullptr);
+
+ Unlock();
+
+ idle_add(IDLE_PLAYER);
+}
+
+void
+player_control::Cancel()
+{
+ player_command(this, PLAYER_COMMAND_CANCEL);
+ assert(next_song == NULL);
+}
+
+void
+player_control::Stop()
+{
+ player_command(this, PLAYER_COMMAND_CLOSE_AUDIO);
+ assert(next_song == nullptr);
+
+ idle_add(IDLE_PLAYER);
+}
+
+void
+player_control::UpdateAudio()
+{
+ player_command(this, PLAYER_COMMAND_UPDATE_AUDIO);
+}
+
+void
+player_control::Kill()
+{
+ assert(thread != NULL);
+
+ player_command(this, PLAYER_COMMAND_EXIT);
+ g_thread_join(thread);
+ thread = NULL;
+
+ idle_add(IDLE_PLAYER);
+}
+
+void
+player_control::Pause()
+{
+ Lock();
+
+ if (state != PLAYER_STATE_STOP) {
+ player_command_locked(this, PLAYER_COMMAND_PAUSE);
+ idle_add(IDLE_PLAYER);
+ }
+
+ Unlock();
+}
+
+static void
+pc_pause_locked(struct player_control *pc)
+{
+ if (pc->state != PLAYER_STATE_STOP) {
+ player_command_locked(pc, PLAYER_COMMAND_PAUSE);
+ idle_add(IDLE_PLAYER);
+ }
+}
+
+void
+player_control::SetPause(bool pause_flag)
+{
+ Lock();
+
+ switch (state) {
+ case PLAYER_STATE_STOP:
+ break;
+
+ case PLAYER_STATE_PLAY:
+ if (pause_flag)
+ pc_pause_locked(this);
+ break;
+
+ case PLAYER_STATE_PAUSE:
+ if (!pause_flag)
+ pc_pause_locked(this);
+ break;
+ }
+
+ Unlock();
+}
+
+void
+player_control::SetBorderPause(bool _border_pause)
+{
+ Lock();
+ border_pause = _border_pause;
+ Unlock();
+}
+
+player_status
+player_control::GetStatus()
+{
+ player_status status;
+
+ Lock();
+ player_command_locked(this, PLAYER_COMMAND_REFRESH);
+
+ status.state = state;
+
+ if (state != PLAYER_STATE_STOP) {
+ status.bit_rate = bit_rate;
+ status.audio_format = audio_format;
+ status.total_time = total_time;
+ status.elapsed_time = elapsed_time;
+ }
+
+ Unlock();
+
+ return status;
+}
+
+void
+player_control::SetError(player_error type, GError *_error)
+{
+ assert(type != PLAYER_ERROR_NONE);
+ assert(_error != NULL);
+
+ if (error_type != PLAYER_ERROR_NONE)
+ g_error_free(error);
+
+ error_type = type;
+ error = _error;
+}
+
+void
+player_control::ClearError()
+{
+ Lock();
+
+ if (error_type != PLAYER_ERROR_NONE) {
+ error_type = PLAYER_ERROR_NONE;
+ g_error_free(error);
+ }
+
+ Unlock();
+}
+
+char *
+player_control::GetErrorMessage() const
+{
+ Lock();
+ char *message = error_type != PLAYER_ERROR_NONE
+ ? g_strdup(error->message)
+ : NULL;
+ Unlock();
+ return message;
+}
+
+static void
+pc_enqueue_song_locked(struct player_control *pc, struct song *song)
+{
+ assert(song != NULL);
+ assert(pc->next_song == NULL);
+
+ pc->next_song = song;
+ player_command_locked(pc, PLAYER_COMMAND_QUEUE);
+}
+
+void
+player_control::EnqueueSong(struct song *song)
+{
+ assert(song != NULL);
+
+ Lock();
+ pc_enqueue_song_locked(this, song);
+ Unlock();
+}
+
+bool
+player_control::Seek(struct song *song, float seek_time)
+{
+ assert(song != NULL);
+
+ Lock();
+
+ if (next_song != nullptr)
+ song_free(next_song);
+
+ next_song = song;
+ seek_where = seek_time;
+ player_command_locked(this, PLAYER_COMMAND_SEEK);
+ Unlock();
+
+ assert(next_song == nullptr);
+
+ idle_add(IDLE_PLAYER);
+
+ return true;
+}
+
+void
+player_control::SetCrossFade(float _cross_fade_seconds)
+{
+ if (_cross_fade_seconds < 0)
+ _cross_fade_seconds = 0;
+ cross_fade_seconds = _cross_fade_seconds;
+
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+player_control::SetMixRampDb(float _mixramp_db)
+{
+ mixramp_db = _mixramp_db;
+
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+player_control::SetMixRampDelay(float _mixramp_delay_seconds)
+{
+ mixramp_delay_seconds = _mixramp_delay_seconds;
+
+ idle_add(IDLE_OPTIONS);
+}
diff --git a/src/PlayerControl.hxx b/src/PlayerControl.hxx
new file mode 100644
index 00000000..de05e17a
--- /dev/null
+++ b/src/PlayerControl.hxx
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYER_H
+#define MPD_PLAYER_H
+
+#include "audio_format.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+#include <glib.h>
+
+#include <stdint.h>
+
+struct decoder_control;
+
+enum player_state {
+ PLAYER_STATE_STOP = 0,
+ PLAYER_STATE_PAUSE,
+ PLAYER_STATE_PLAY
+};
+
+enum player_command {
+ PLAYER_COMMAND_NONE = 0,
+ PLAYER_COMMAND_EXIT,
+ PLAYER_COMMAND_STOP,
+ PLAYER_COMMAND_PAUSE,
+ PLAYER_COMMAND_SEEK,
+ PLAYER_COMMAND_CLOSE_AUDIO,
+
+ /**
+ * At least one audio_output.enabled flag has been modified;
+ * commit those changes to the output threads.
+ */
+ PLAYER_COMMAND_UPDATE_AUDIO,
+
+ /** player_control.next_song has been updated */
+ PLAYER_COMMAND_QUEUE,
+
+ /**
+ * cancel pre-decoding player_control.next_song; if the player
+ * has already started playing this song, it will completely
+ * stop
+ */
+ PLAYER_COMMAND_CANCEL,
+
+ /**
+ * Refresh status information in the #player_control struct,
+ * e.g. elapsed_time.
+ */
+ PLAYER_COMMAND_REFRESH,
+};
+
+enum player_error {
+ PLAYER_ERROR_NONE = 0,
+
+ /**
+ * The decoder has failed to decode the song.
+ */
+ PLAYER_ERROR_DECODER,
+
+ /**
+ * The audio output has failed.
+ */
+ PLAYER_ERROR_OUTPUT,
+};
+
+struct player_status {
+ enum player_state state;
+ uint16_t bit_rate;
+ struct audio_format audio_format;
+ float total_time;
+ float elapsed_time;
+};
+
+struct player_control {
+ unsigned buffer_chunks;
+
+ unsigned int buffered_before_play;
+
+ /** the handle of the player thread, or NULL if the player
+ thread isn't running */
+ GThread *thread;
+
+ /**
+ * This lock protects #command, #state, #error.
+ */
+ mutable Mutex mutex;
+
+ /**
+ * Trigger this object after you have modified #command.
+ */
+ Cond cond;
+
+ /**
+ * This object gets signalled when the player thread has
+ * finished the #command. It wakes up the client that waits
+ * (i.e. the main thread).
+ */
+ Cond client_cond;
+
+ enum player_command command;
+ enum player_state state;
+
+ enum player_error error_type;
+
+ /**
+ * The error that occurred in the player thread. This
+ * attribute is only valid if #error is not
+ * #PLAYER_ERROR_NONE. The object must be freed when this
+ * object transitions back to #PLAYER_ERROR_NONE.
+ */
+ GError *error;
+
+ uint16_t bit_rate;
+ struct audio_format audio_format;
+ float total_time;
+ float elapsed_time;
+
+ /**
+ * The next queued song.
+ *
+ * This is a duplicate, and must be freed when this attribute
+ * is cleared.
+ */
+ struct song *next_song;
+
+ double seek_where;
+ float cross_fade_seconds;
+ float mixramp_db;
+ float mixramp_delay_seconds;
+ double total_play_time;
+
+ /**
+ * If this flag is set, then the player will be auto-paused at
+ * the end of the song, before the next song starts to play.
+ *
+ * This is a copy of the queue's "single" flag most of the
+ * time.
+ */
+ bool border_pause;
+
+ player_control(unsigned buffer_chunks,
+ unsigned buffered_before_play);
+ ~player_control();
+
+ /**
+ * Locks the object.
+ */
+ void Lock() const {
+ mutex.lock();
+ }
+
+ /**
+ * Unlocks the object.
+ */
+ void Unlock() const {
+ mutex.unlock();
+ }
+
+ /**
+ * Signals the object. The object should be locked prior to
+ * calling this function.
+ */
+ void Signal() {
+ cond.signal();
+ }
+
+ /**
+ * Signals the object. The object is temporarily locked by
+ * this function.
+ */
+ void LockSignal() {
+ Lock();
+ Signal();
+ Unlock();
+ }
+
+ /**
+ * Waits for a signal on the object. This function is only
+ * valid in the player thread. The object must be locked
+ * prior to calling this function.
+ */
+ void Wait() {
+ assert(thread == g_thread_self());
+
+ cond.wait(mutex);
+ }
+
+ /**
+ * Wake up the client waiting for command completion.
+ *
+ * Caller must lock the object.
+ */
+ void ClientSignal() {
+ assert(thread == g_thread_self());
+
+ client_cond.signal();
+ }
+
+ /**
+ * The client calls this method to wait for command
+ * completion.
+ *
+ * Caller must lock the object.
+ */
+ void ClientWait() {
+ assert(thread != g_thread_self());
+
+ client_cond.wait(mutex);
+ }
+
+ /**
+ * @param song the song to be queued; the given instance will
+ * be owned and freed by the player
+ */
+ void Play(struct song *song);
+
+ /**
+ * see PLAYER_COMMAND_CANCEL
+ */
+ void Cancel();
+
+ void SetPause(bool pause_flag);
+
+ void Pause();
+
+ /**
+ * Set the player's #border_pause flag.
+ */
+ void SetBorderPause(bool border_pause);
+
+ void Kill();
+
+ gcc_pure
+ player_status GetStatus();
+
+ player_state GetState() const {
+ return state;
+ }
+
+ /**
+ * Set the error. Discards any previous error condition.
+ *
+ * Caller must lock the object.
+ *
+ * @param type the error type; must not be #PLAYER_ERROR_NONE
+ * @param error detailed error information; must not be NULL; the
+ * #player_control takes over ownership of this #GError instance
+ */
+ void SetError(player_error type, GError *error);
+
+ void ClearError();
+
+ /**
+ * Returns the human-readable message describing the last
+ * error during playback, NULL if no error occurred. The
+ * caller has to free the returned string.
+ */
+ char *GetErrorMessage() const;
+
+ player_error GetErrorType() const {
+ return error_type;
+ }
+
+ void Stop();
+
+ void UpdateAudio();
+
+ /**
+ * @param song the song to be queued; the given instance will be owned
+ * and freed by the player
+ */
+ void EnqueueSong(struct song *song);
+
+ /**
+ * Makes the player thread seek the specified song to a position.
+ *
+ * @param song the song to be queued; the given instance will be owned
+ * and freed by the player
+ * @return true on success, false on failure (e.g. if MPD isn't
+ * playing currently)
+ */
+ bool Seek(struct song *song, float seek_time);
+
+ void SetCrossFade(float cross_fade_seconds);
+
+ float GetCrossFade() const {
+ return cross_fade_seconds;
+ }
+
+ void SetMixRampDb(float mixramp_db);
+
+ float GetMixRampDb() const {
+ return mixramp_db;
+ }
+
+ void SetMixRampDelay(float mixramp_delay_seconds);
+
+ float GetMixRampDelay() const {
+ return mixramp_delay_seconds;
+ }
+
+ double GetTotalPlayTime() const {
+ return total_play_time;
+ }
+};
+
+#endif
diff --git a/src/player_thread.c b/src/PlayerThread.cxx
index 593788ca..599df833 100644
--- a/src/player_thread.c
+++ b/src/PlayerThread.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,23 +18,23 @@
*/
#include "config.h"
-#include "player_thread.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "decoder_thread.h"
-#include "output_all.h"
-#include "pcm_volume.h"
-#include "path.h"
-#include "event_pipe.h"
-#include "crossfade.h"
+#include "PlayerThread.hxx"
+#include "DecoderThread.hxx"
+#include "DecoderControl.hxx"
+#include "MusicPipe.hxx"
+#include "MusicBuffer.hxx"
+#include "MusicChunk.hxx"
#include "song.h"
-#include "tag.h"
-#include "pipe.h"
-#include "chunk.h"
-#include "idle.h"
-#include "main.h"
-#include "buffer.h"
+#include "Main.hxx"
#include "mpd_error.h"
+#include "CrossFade.hxx"
+#include "PlayerControl.hxx"
+#include "OutputAll.hxx"
+#include "tag.h"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+
+#include <cmath>
#include <glib.h>
@@ -123,6 +123,20 @@ struct player {
* precisely.
*/
float elapsed_time;
+
+ player(player_control *_pc, decoder_control *_dc)
+ :pc(_pc), dc(_dc),
+ buffering(false),
+ decoder_starting(false),
+ paused(false),
+ queued(true),
+ output_open(false),
+ song(NULL),
+ xfade(XFADE_UNKNOWN),
+ cross_fading(false),
+ cross_fade_chunks(0),
+ cross_fade_tag(NULL),
+ elapsed_time(0.0) {}
};
static struct music_buffer *player_buffer;
@@ -133,15 +147,15 @@ player_command_finished_locked(struct player_control *pc)
assert(pc->command != PLAYER_COMMAND_NONE);
pc->command = PLAYER_COMMAND_NONE;
- g_cond_signal(main_cond);
+ pc->ClientSignal();
}
static void
player_command_finished(struct player_control *pc)
{
- player_lock(pc);
+ pc->Lock();
player_command_finished_locked(pc);
- player_unlock(pc);
+ pc->Unlock();
}
/**
@@ -162,9 +176,9 @@ player_dc_start(struct player *player, struct music_pipe *pipe)
if (pc->command == PLAYER_COMMAND_SEEK)
start_ms += (unsigned)(pc->seek_where * 1000);
- dc_start(dc, pc->next_song,
- start_ms, pc->next_song->end_ms,
- player_buffer, pipe);
+ dc->Start(song_dup_detached(pc->next_song),
+ start_ms, pc->next_song->end_ms,
+ player_buffer, pipe);
}
/**
@@ -203,7 +217,7 @@ player_dc_stop(struct player *player)
{
struct decoder_control *dc = player->dc;
- dc_stop(dc);
+ dc->Stop();
if (dc->pipe != NULL) {
/* clear and free the decoder pipe */
@@ -235,16 +249,22 @@ player_wait_for_decoder(struct player *player)
player->queued = false;
- if (decoder_lock_has_failed(dc)) {
- player_lock(pc);
- pc->errored_song = dc->song;
- pc->error = PLAYER_ERROR_FILE;
+ GError *error = dc->LockGetError();
+ if (error != NULL) {
+ pc->Lock();
+ pc->SetError(PLAYER_ERROR_DECODER, error);
+
+ song_free(pc->next_song);
pc->next_song = NULL;
- player_unlock(pc);
+
+ pc->Unlock();
return false;
}
+ if (player->song != NULL)
+ song_free(player->song);
+
player->song = pc->next_song;
player->elapsed_time = 0.0;
@@ -252,7 +272,7 @@ player_wait_for_decoder(struct player *player)
player_check_decoder_startup() */
player->decoder_starting = true;
- player_lock(pc);
+ pc->Lock();
/* update player_control's song information */
pc->total_time = song_get_duration(pc->next_song);
@@ -262,10 +282,10 @@ player_wait_for_decoder(struct player *player)
/* clear the queued song */
pc->next_song = NULL;
- player_unlock(pc);
+ pc->Unlock();
/* call syncPlaylistWithQueue() in the main thread */
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
return true;
}
@@ -305,26 +325,30 @@ player_open_output(struct player *player)
assert(pc->state == PLAYER_STATE_PLAY ||
pc->state == PLAYER_STATE_PAUSE);
- if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
+ GError *error = NULL;
+ if (audio_output_all_open(&player->play_audio_format, player_buffer,
+ &error)) {
player->output_open = true;
player->paused = false;
- player_lock(pc);
+ pc->Lock();
pc->state = PLAYER_STATE_PLAY;
- player_unlock(pc);
+ pc->Unlock();
return true;
} else {
+ g_warning("%s", error->message);
+
player->output_open = false;
/* pause: the user may resume playback as soon as an
audio output becomes available */
player->paused = true;
- player_lock(pc);
- pc->error = PLAYER_ERROR_AUDIO;
+ pc->Lock();
+ pc->SetError(PLAYER_ERROR_OUTPUT, error);
pc->state = PLAYER_STATE_PAUSE;
- player_unlock(pc);
+ pc->Unlock();
idle_add(IDLE_PLAYER);
@@ -347,22 +371,22 @@ player_check_decoder_startup(struct player *player)
assert(player->decoder_starting);
- decoder_lock(dc);
+ dc->Lock();
- if (decoder_has_failed(dc)) {
+ GError *error = dc->GetError();
+ if (error != NULL) {
/* the decoder failed */
- decoder_unlock(dc);
+ dc->Unlock();
- player_lock(pc);
- pc->errored_song = dc->song;
- pc->error = PLAYER_ERROR_FILE;
- player_unlock(pc);
+ pc->Lock();
+ pc->SetError(PLAYER_ERROR_DECODER, error);
+ pc->Unlock();
return false;
- } else if (!decoder_is_starting(dc)) {
+ } else if (!dc->IsStarting()) {
/* the decoder is ready and ok */
- decoder_unlock(dc);
+ dc->Unlock();
if (player->output_open &&
!audio_output_all_wait(pc, 1))
@@ -370,10 +394,10 @@ player_check_decoder_startup(struct player *player)
all chunks yet - wait for that */
return true;
- player_lock(pc);
+ pc->Lock();
pc->total_time = real_song_duration(dc->song, dc->total_time);
pc->audio_format = dc->in_audio_format;
- player_unlock(pc);
+ pc->Unlock();
player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false;
@@ -391,8 +415,8 @@ player_check_decoder_startup(struct player *player)
} else {
/* the decoder is not yet ready; wait
some more */
- player_wait_decoder(pc, dc);
- decoder_unlock(dc);
+ dc->WaitForDecoder();
+ dc->Unlock();
return true;
}
@@ -431,7 +455,11 @@ player_send_silence(struct player *player)
chunk->length = num_frames * frame_size;
memset(chunk->data, 0, chunk->length);
- if (!audio_output_all_play(chunk)) {
+ GError *error = NULL;
+ if (!audio_output_all_play(chunk, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+
music_buffer_return(player_buffer, chunk);
return false;
}
@@ -454,7 +482,7 @@ static bool player_seek_decoder(struct player *player)
const unsigned start_ms = song->start_ms;
- if (decoder_current_song(dc) != song) {
+ if (!dc->LockIsCurrentSong(song)) {
/* the decoder is already decoding the "next" song -
stop it and start the previous song again */
@@ -480,6 +508,7 @@ static bool player_seek_decoder(struct player *player)
player->pipe = dc->pipe;
}
+ song_free(pc->next_song);
pc->next_song = NULL;
player->queued = false;
}
@@ -502,7 +531,7 @@ static bool player_seek_decoder(struct player *player)
if (where < 0.0)
where = 0.0;
- if (!dc_seek(dc, where + start_ms / 1000.0)) {
+ if (!dc->Seek(where + start_ms / 1000.0)) {
/* decoder failure */
player_command_finished(pc);
return false;
@@ -538,9 +567,9 @@ static void player_process_command(struct player *player)
break;
case PLAYER_COMMAND_UPDATE_AUDIO:
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_enable_disable();
- player_lock(pc);
+ pc->Lock();
player_command_finished_locked(pc);
break;
@@ -554,33 +583,33 @@ static void player_process_command(struct player *player)
break;
case PLAYER_COMMAND_PAUSE:
- player_unlock(pc);
+ pc->Unlock();
player->paused = !player->paused;
if (player->paused) {
audio_output_all_pause();
- player_lock(pc);
+ pc->Lock();
pc->state = PLAYER_STATE_PAUSE;
} else if (!audio_format_defined(&player->play_audio_format)) {
/* the decoder hasn't provided an audio format
yet - don't open the audio device yet */
- player_lock(pc);
+ pc->Lock();
pc->state = PLAYER_STATE_PLAY;
} else {
player_open_output(player);
- player_lock(pc);
+ pc->Lock();
}
player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_SEEK:
- player_unlock(pc);
+ pc->Unlock();
player_seek_decoder(player);
- player_lock(pc);
+ pc->Lock();
break;
case PLAYER_COMMAND_CANCEL:
@@ -595,11 +624,12 @@ static void player_process_command(struct player *player)
if (player_dc_at_next_song(player)) {
/* the decoder is already decoding the song -
stop it and reset the position */
- player_unlock(pc);
+ pc->Unlock();
player_dc_stop(player);
- player_lock(pc);
+ pc->Lock();
}
+ song_free(pc->next_song);
pc->next_song = NULL;
player->queued = false;
player_command_finished_locked(pc);
@@ -607,9 +637,9 @@ static void player_process_command(struct player *player)
case PLAYER_COMMAND_REFRESH:
if (player->output_open && !player->paused) {
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_check();
- player_lock(pc);
+ pc->Lock();
}
pc->elapsed_time = audio_output_all_get_elapsed_time();
@@ -637,7 +667,7 @@ update_song_tag(struct song *song, const struct tag *new_tag)
/* the main thread will update the playlist version when he
receives this event */
- event_pipe_emit(PIPE_EVENT_TAG);
+ GlobalEvents::Emit(GlobalEvents::TAG);
/* notify all clients that the tag of the current song has
changed */
@@ -654,9 +684,10 @@ update_song_tag(struct song *song, const struct tag *new_tag)
static bool
play_chunk(struct player_control *pc,
struct song *song, struct music_chunk *chunk,
- const struct audio_format *format)
+ const struct audio_format *format,
+ GError **error_r)
{
- assert(music_chunk_check_format(chunk, format));
+ assert(chunk->CheckFormat(*format));
if (chunk->tag != NULL)
update_song_tag(song, chunk->tag);
@@ -666,13 +697,13 @@ play_chunk(struct player_control *pc,
return true;
}
- player_lock(pc);
+ pc->Lock();
pc->bit_rate = chunk->bit_rate;
- player_unlock(pc);
+ pc->Unlock();
/* send the chunk to the audio outputs */
- if (!audio_output_all_play(chunk))
+ if (!audio_output_all_play(chunk, error_r))
return false;
pc->total_play_time += (double)chunk->length /
@@ -729,14 +760,14 @@ play_next_chunk(struct player *player)
other_chunk->tag);
other_chunk->tag = NULL;
- if (isnan(pc->mixramp_delay_seconds)) {
+ if (std::isnan(pc->mixramp_delay_seconds)) {
chunk->mix_ratio = ((float)cross_fade_position)
/ player->cross_fade_chunks;
} else {
chunk->mix_ratio = nan("");
}
- if (music_chunk_is_empty(other_chunk)) {
+ if (other_chunk->IsEmpty()) {
/* the "other" chunk was a music_chunk
which had only a tag, but no music
data - we cannot cross-fade that;
@@ -753,19 +784,19 @@ play_next_chunk(struct player *player)
} else {
/* there are not enough decoded chunks yet */
- decoder_lock(dc);
+ dc->Lock();
- if (decoder_is_idle(dc)) {
+ if (dc->IsIdle()) {
/* the decoder isn't running, abort
cross fading */
- decoder_unlock(dc);
+ dc->Unlock();
player->xfade = XFADE_DISABLED;
} else {
/* wait for the decoder */
- decoder_signal(dc);
- player_wait_decoder(pc, dc);
- decoder_unlock(dc);
+ dc->Signal();
+ dc->WaitForDecoder();
+ dc->Unlock();
return true;
}
@@ -787,20 +818,23 @@ play_next_chunk(struct player *player)
/* play the current chunk */
+ GError *error = NULL;
if (!play_chunk(player->pc, player->song, chunk,
- &player->play_audio_format)) {
+ &player->play_audio_format, &error)) {
+ g_warning("%s", error->message);
+
music_buffer_return(player_buffer, chunk);
- player_lock(pc);
+ pc->Lock();
- pc->error = PLAYER_ERROR_AUDIO;
+ pc->SetError(PLAYER_ERROR_OUTPUT, error);
/* pause: the user may resume playback as soon as an
audio output becomes available */
pc->state = PLAYER_STATE_PAUSE;
player->paused = true;
- player_unlock(pc);
+ pc->Unlock();
idle_add(IDLE_PLAYER);
@@ -810,12 +844,12 @@ play_next_chunk(struct player *player)
/* this formula should prevent that the decoder gets woken up
with each chunk; it is more efficient to make it decode a
larger block at a time */
- decoder_lock(dc);
- if (!decoder_is_idle(dc) &&
+ dc->Lock();
+ if (!dc->IsIdle() &&
music_pipe_size(dc->pipe) <= (pc->buffered_before_play +
music_buffer_size(player_buffer) * 3) / 4)
- decoder_signal(dc);
- decoder_unlock(dc);
+ dc->Signal();
+ dc->Unlock();
return true;
}
@@ -847,7 +881,7 @@ player_song_border(struct player *player)
return false;
struct player_control *const pc = player->pc;
- player_lock(pc);
+ pc->Lock();
const bool border_pause = pc->border_pause;
if (border_pause) {
@@ -855,7 +889,7 @@ player_song_border(struct player *player)
pc->state = PLAYER_STATE_PAUSE;
}
- player_unlock(pc);
+ pc->Unlock();
if (border_pause)
idle_add(IDLE_PLAYER);
@@ -870,37 +904,25 @@ player_song_border(struct player *player)
*/
static void do_play(struct player_control *pc, struct decoder_control *dc)
{
- struct player player = {
- .pc = pc,
- .dc = dc,
- .buffering = true,
- .decoder_starting = false,
- .paused = false,
- .queued = true,
- .output_open = false,
- .song = NULL,
- .xfade = XFADE_UNKNOWN,
- .cross_fading = false,
- .cross_fade_chunks = 0,
- .cross_fade_tag = NULL,
- .elapsed_time = 0.0,
- };
-
- player_unlock(pc);
+ player player(pc, dc);
+
+ pc->Unlock();
player.pipe = music_pipe_new();
player_dc_start(&player, player.pipe);
if (!player_wait_for_decoder(&player)) {
+ assert(player.song == NULL);
+
player_dc_stop(&player);
player_command_finished(pc);
music_pipe_free(player.pipe);
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
- player_lock(pc);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
+ pc->Lock();
return;
}
- player_lock(pc);
+ pc->Lock();
pc->state = PLAYER_STATE_PLAY;
if (pc->command == PLAYER_COMMAND_SEEK)
@@ -913,12 +935,12 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
if (pc->command == PLAYER_COMMAND_STOP ||
pc->command == PLAYER_COMMAND_EXIT ||
pc->command == PLAYER_COMMAND_CLOSE_AUDIO) {
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_cancel();
break;
}
- player_unlock(pc);
+ pc->Unlock();
if (player.buffering) {
/* buffering at the start of the song - wait
@@ -926,7 +948,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
prevent stuttering on slow machines */
if (music_pipe_size(player.pipe) < pc->buffered_before_play &&
- !decoder_lock_is_idle(dc)) {
+ !dc->LockIsIdle()) {
/* not enough decoded buffer space yet */
if (!player.paused &&
@@ -935,11 +957,11 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
!player_send_silence(&player))
break;
- decoder_lock(dc);
+ dc->Lock();
/* XXX race condition: check decoder again */
- player_wait_decoder(pc, dc);
- decoder_unlock(dc);
- player_lock(pc);
+ dc->WaitForDecoder();
+ dc->Unlock();
+ pc->Lock();
continue;
} else {
/* buffering is complete */
@@ -953,7 +975,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
if (!player_check_decoder_startup(&player))
break;
- player_lock(pc);
+ pc->Lock();
continue;
}
@@ -965,7 +987,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
*/
#endif
- if (decoder_lock_is_idle(dc) && player.queued &&
+ if (dc->LockIsIdle() && player.queued &&
dc->pipe == player.pipe) {
/* the decoder has finished the current song;
make it decode the next song */
@@ -980,7 +1002,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
!pc->border_pause &&
player_dc_at_next_song(&player) &&
player.xfade == XFADE_UNKNOWN &&
- !decoder_lock_is_starting(dc)) {
+ !dc->LockIsStarting()) {
/* enable cross fading in this song? if yes,
calculate how many chunks will be required
for it */
@@ -1006,10 +1028,10 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
}
if (player.paused) {
- player_lock(pc);
+ pc->Lock();
if (pc->command == PLAYER_COMMAND_NONE)
- player_wait(pc);
+ pc->Wait();
continue;
} else if (!music_pipe_empty(player.pipe)) {
/* at least one music chunk is ready - send it
@@ -1028,7 +1050,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
if (!player_song_border(&player))
break;
- } else if (decoder_lock_is_idle(dc)) {
+ } else if (dc->LockIsIdle()) {
/* check the size of the pipe again, because
the decoder thread may have added something
since we last checked */
@@ -1046,7 +1068,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
break;
}
- player_lock(pc);
+ pc->Lock();
}
player_dc_stop(&player);
@@ -1057,33 +1079,37 @@ static void do_play(struct player_control *pc, struct decoder_control *dc)
if (player.cross_fade_tag != NULL)
tag_free(player.cross_fade_tag);
- player_lock(pc);
+ if (player.song != NULL)
+ song_free(player.song);
+
+ pc->Lock();
if (player.queued) {
assert(pc->next_song != NULL);
+ song_free(pc->next_song);
pc->next_song = NULL;
}
pc->state = PLAYER_STATE_STOP;
- player_unlock(pc);
+ pc->Unlock();
- event_pipe_emit(PIPE_EVENT_PLAYLIST);
+ GlobalEvents::Emit(GlobalEvents::PLAYLIST);
- player_lock(pc);
+ pc->Lock();
}
static gpointer
player_task(gpointer arg)
{
- struct player_control *pc = arg;
+ struct player_control *pc = (struct player_control *)arg;
- struct decoder_control *dc = dc_new(pc->cond);
+ struct decoder_control *dc = new decoder_control();
decoder_thread_start(dc);
player_buffer = music_buffer_new(pc->buffer_chunks);
- player_lock(pc);
+ pc->Lock();
while (1) {
switch (pc->command) {
@@ -1095,23 +1121,27 @@ player_task(gpointer arg)
break;
case PLAYER_COMMAND_STOP:
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_cancel();
- player_lock(pc);
+ pc->Lock();
/* fall through */
case PLAYER_COMMAND_PAUSE:
- pc->next_song = NULL;
+ if (pc->next_song != NULL) {
+ song_free(pc->next_song);
+ pc->next_song = NULL;
+ }
+
player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_CLOSE_AUDIO:
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_release();
- player_lock(pc);
+ pc->Lock();
player_command_finished_locked(pc);
#ifndef NDEBUG
@@ -1125,17 +1155,17 @@ player_task(gpointer arg)
break;
case PLAYER_COMMAND_UPDATE_AUDIO:
- player_unlock(pc);
+ pc->Unlock();
audio_output_all_enable_disable();
- player_lock(pc);
+ pc->Lock();
player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_EXIT:
- player_unlock(pc);
+ pc->Unlock();
- dc_quit(dc);
- dc_free(dc);
+ dc->Quit();
+ delete dc;
audio_output_all_close();
music_buffer_free(player_buffer);
@@ -1143,7 +1173,11 @@ player_task(gpointer arg)
return NULL;
case PLAYER_COMMAND_CANCEL:
- pc->next_song = NULL;
+ if (pc->next_song != NULL) {
+ song_free(pc->next_song);
+ pc->next_song = NULL;
+ }
+
player_command_finished_locked(pc);
break;
@@ -1153,7 +1187,7 @@ player_task(gpointer arg)
break;
case PLAYER_COMMAND_NONE:
- player_wait(pc);
+ pc->Wait();
break;
}
}
diff --git a/src/player_thread.h b/src/PlayerThread.hxx
index 7373eb43..197669cd 100644
--- a/src/player_thread.h
+++ b/src/PlayerThread.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -34,8 +34,8 @@
* #music_chunk instances around in #music_pipe objects.
*/
-#ifndef MPD_PLAYER_THREAD_H
-#define MPD_PLAYER_THREAD_H
+#ifndef MPD_PLAYER_THREAD_HXX
+#define MPD_PLAYER_THREAD_HXX
struct player_control;
diff --git a/src/Playlist.cxx b/src/Playlist.cxx
new file mode 100644
index 00000000..89bdac63
--- /dev/null
+++ b/src/Playlist.cxx
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+#include "song.h"
+#include "Idle.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "playlist"
+
+void
+playlist::FullIncrementVersions()
+{
+ queue.ModifyAll();
+ idle_add(IDLE_PLAYLIST);
+}
+
+void
+playlist::TagChanged()
+{
+ if (!playing)
+ return;
+
+ assert(current >= 0);
+
+ queue.ModifyAtOrder(current);
+ idle_add(IDLE_PLAYLIST);
+}
+
+/**
+ * Queue a song, addressed by its order number.
+ */
+static void
+playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
+ unsigned order)
+{
+ char *uri;
+
+ assert(playlist->queue.IsValidOrder(order));
+
+ playlist->queued = order;
+
+ struct song *song =
+ song_dup_detached(playlist->queue.GetOrder(order));
+
+ uri = song_get_uri(song);
+ g_debug("queue song %i:\"%s\"", playlist->queued, uri);
+ g_free(uri);
+
+ pc->EnqueueSong(song);
+}
+
+/**
+ * Called if the player thread has started playing the "queued" song.
+ */
+static void
+playlist_song_started(struct playlist *playlist, struct player_control *pc)
+{
+ assert(pc->next_song == NULL);
+ assert(playlist->queued >= -1);
+
+ /* queued song has started: copy queued to current,
+ and notify the clients */
+
+ int current = playlist->current;
+ playlist->current = playlist->queued;
+ playlist->queued = -1;
+
+ if(playlist->queue.consume)
+ playlist->DeleteOrder(*pc, current);
+
+ idle_add(IDLE_PLAYER);
+}
+
+const struct song *
+playlist::GetQueuedSong() const
+{
+ return playing && queued >= 0
+ ? queue.GetOrder(queued)
+ : nullptr;
+}
+
+void
+playlist::UpdateQueuedSong(player_control &pc, const song *prev)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+ assert((queued < 0) == (prev == NULL));
+
+ const int next_order = current >= 0
+ ? queue.GetNextOrder(current)
+ : 0;
+
+ if (next_order == 0 && queue.random && !queue.single) {
+ /* shuffle the song order again, so we get a different
+ order each time the playlist is played
+ completely */
+ const unsigned current_position =
+ queue.OrderToPosition(current);
+
+ queue.ShuffleOrder();
+
+ /* make sure that the current still points to
+ the current song, after the song order has been
+ shuffled */
+ current = queue.PositionToOrder(current_position);
+ }
+
+ const struct song *const next_song = next_order >= 0
+ ? queue.GetOrder(next_order)
+ : nullptr;
+
+ if (prev != NULL && next_song != prev) {
+ /* clear the currently queued song */
+ pc.Cancel();
+ queued = -1;
+ }
+
+ if (next_order >= 0) {
+ if (next_song != prev)
+ playlist_queue_song_order(this, &pc, next_order);
+ else
+ queued = next_order;
+ }
+}
+
+void
+playlist::PlayOrder(player_control &pc, int order)
+{
+ playing = true;
+ queued = -1;
+
+ struct song *song = song_dup_detached(queue.GetOrder(order));
+
+ char *uri = song_get_uri(song);
+ g_debug("play %i:\"%s\"", order, uri);
+ g_free(uri);
+
+ pc.Play(song);
+ current = order;
+}
+
+static void
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
+
+void
+playlist::SyncWithPlayer(player_control &pc)
+{
+ if (!playing)
+ /* this event has reached us out of sync: we aren't
+ playing anymore; ignore the event */
+ return;
+
+ pc.Lock();
+ const player_state pc_state = pc.GetState();
+ const song *pc_next_song = pc.next_song;
+ pc.Unlock();
+
+ if (pc_state == PLAYER_STATE_STOP)
+ /* the player thread has stopped: check if playback
+ should be restarted with the next song. That can
+ happen if the playlist isn't filling the queue fast
+ enough */
+ playlist_resume_playback(this, &pc);
+ else {
+ /* check if the player thread has already started
+ playing the queued song */
+ if (pc_next_song == nullptr && queued != -1)
+ playlist_song_started(this, &pc);
+
+ pc.Lock();
+ pc_next_song = pc.next_song;
+ pc.Unlock();
+
+ /* make sure the queued song is always set (if
+ possible) */
+ if (pc_next_song == nullptr && queued < 0)
+ UpdateQueuedSong(pc, nullptr);
+ }
+}
+
+/**
+ * The player has stopped for some reason. Check the error, and
+ * decide whether to re-start playback
+ */
+static void
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
+{
+ assert(playlist->playing);
+ assert(pc->GetState() == PLAYER_STATE_STOP);
+
+ const auto error = pc->GetErrorType();
+ if (error == PLAYER_ERROR_NONE)
+ playlist->error_count = 0;
+ else
+ ++playlist->error_count;
+
+ if ((playlist->stop_on_error && error != PLAYER_ERROR_NONE) ||
+ error == PLAYER_ERROR_OUTPUT ||
+ playlist->error_count >= playlist->queue.GetLength())
+ /* too many errors, or critical error: stop
+ playback */
+ playlist->Stop(*pc);
+ else
+ /* continue playback at the next song */
+ playlist->PlayNext(*pc);
+}
+
+void
+playlist::SetRepeat(player_control &pc, bool status)
+{
+ if (status == queue.repeat)
+ return;
+
+ queue.repeat = status;
+
+ pc.SetBorderPause(queue.single && !queue.repeat);
+
+ /* if the last song is currently being played, the "next song"
+ might change when repeat mode is toggled */
+ UpdateQueuedSong(pc, GetQueuedSong());
+
+ idle_add(IDLE_OPTIONS);
+}
+
+static void
+playlist_order(struct playlist *playlist)
+{
+ if (playlist->current >= 0)
+ /* update playlist.current, order==position now */
+ playlist->current = playlist->queue.OrderToPosition(playlist->current);
+
+ playlist->queue.RestoreOrder();
+}
+
+void
+playlist::SetSingle(player_control &pc, bool status)
+{
+ if (status == queue.single)
+ return;
+
+ queue.single = status;
+
+ pc.SetBorderPause(queue.single && !queue.repeat);
+
+ /* if the last song is currently being played, the "next song"
+ might change when single mode is toggled */
+ UpdateQueuedSong(pc, GetQueuedSong());
+
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+playlist::SetConsume(bool status)
+{
+ if (status == queue.consume)
+ return;
+
+ queue.consume = status;
+ idle_add(IDLE_OPTIONS);
+}
+
+void
+playlist::SetRandom(player_control &pc, bool status)
+{
+ if (status == queue.random)
+ return;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ queue.random = status;
+
+ if (queue.random) {
+ /* shuffle the queue order, but preserve current */
+
+ const int current_position = GetCurrentPosition();
+
+ queue.ShuffleOrder();
+
+ if (current_position >= 0) {
+ /* make sure the current song is the first in
+ the order list, so the whole rest of the
+ playlist is played after that */
+ unsigned current_order =
+ queue.PositionToOrder(current_position);
+ queue.SwapOrders(0, current_order);
+ current = 0;
+ } else
+ current = -1;
+ } else
+ playlist_order(this);
+
+ UpdateQueuedSong(pc, queued_song);
+
+ idle_add(IDLE_OPTIONS);
+}
+
+int
+playlist::GetCurrentPosition() const
+{
+ return current >= 0
+ ? queue.OrderToPosition(current)
+ : -1;
+}
+
+int
+playlist::GetNextPosition() const
+{
+ if (current < 0)
+ return -1;
+
+ if (queue.single && queue.repeat)
+ return queue.OrderToPosition(current);
+ else if (queue.IsValidOrder(current + 1))
+ return queue.OrderToPosition(current + 1);
+ else if (queue.repeat)
+ return queue.OrderToPosition(0);
+
+ return -1;
+}
diff --git a/src/Playlist.hxx b/src/Playlist.hxx
new file mode 100644
index 00000000..c0181332
--- /dev/null
+++ b/src/Playlist.hxx
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_HXX
+#define MPD_PLAYLIST_HXX
+
+#include "Queue.hxx"
+#include "playlist_error.h"
+
+#include <stdbool.h>
+
+struct player_control;
+
+struct playlist {
+ /**
+ * The song queue - it contains the "real" playlist.
+ */
+ struct queue queue;
+
+ /**
+ * This value is true if the player is currently playing (or
+ * should be playing).
+ */
+ bool playing;
+
+ /**
+ * If true, then any error is fatal; if false, MPD will
+ * attempt to play the next song on non-fatal errors. During
+ * seeking, this flag is set.
+ */
+ bool stop_on_error;
+
+ /**
+ * Number of errors since playback was started. If this
+ * number exceeds the length of the playlist, MPD gives up,
+ * because all songs have been tried.
+ */
+ unsigned error_count;
+
+ /**
+ * The "current song pointer". This is the song which is
+ * played when we get the "play" command. It is also the song
+ * which is currently being played.
+ */
+ int current;
+
+ /**
+ * The "next" song to be played, when the current one
+ * finishes. The decoder thread may start decoding and
+ * buffering it, while the "current" song is still playing.
+ *
+ * This variable is only valid if #playing is true.
+ */
+ int queued;
+
+ playlist(unsigned max_length)
+ :queue(max_length), playing(false), current(-1), queued(-1) {
+ }
+
+ ~playlist() {
+ }
+
+ uint32_t GetVersion() const {
+ return queue.version;
+ }
+
+ unsigned GetLength() const {
+ return queue.GetLength();
+ }
+
+ unsigned PositionToId(unsigned position) const {
+ return queue.PositionToId(position);
+ }
+
+ gcc_pure
+ int GetCurrentPosition() const;
+
+ gcc_pure
+ int GetNextPosition() const;
+
+ /**
+ * Returns the song object which is currently queued. Returns
+ * none if there is none (yet?) or if MPD isn't playing.
+ */
+ gcc_pure
+ const struct song *GetQueuedSong() const;
+
+ /**
+ * This is the "PLAYLIST" event handler. It is invoked by the
+ * player thread whenever it requests a new queued song, or
+ * when it exits.
+ */
+ void SyncWithPlayer(player_control &pc);
+
+protected:
+ /**
+ * Called by all editing methods after a modification.
+ * Updates the queue version and emits #IDLE_PLAYLIST.
+ */
+ void OnModified();
+
+ /**
+ * Updates the "queued song". Calculates the next song
+ * according to the current one (if MPD isn't playing, it
+ * takes the first song), and queues this song. Clears the
+ * old queued song if there was one.
+ *
+ * @param prev the song which was previously queued, as
+ * determined by playlist_get_queued_song()
+ */
+ void UpdateQueuedSong(player_control &pc, const song *prev);
+
+public:
+ void Clear(player_control &pc);
+
+ void TagChanged();
+
+ void FullIncrementVersions();
+
+ enum playlist_result AppendSong(player_control &pc,
+ struct song *song,
+ unsigned *added_id=nullptr);
+
+ /**
+ * Appends a local file (outside the music database) to the
+ * playlist.
+ *
+ * Note: the caller is responsible for checking permissions.
+ */
+ enum playlist_result AppendFile(player_control &pc,
+ const char *path_utf8,
+ unsigned *added_id=nullptr);
+
+ enum playlist_result AppendURI(player_control &pc,
+ const char *uri_utf8,
+ unsigned *added_id=nullptr);
+
+protected:
+ void DeleteInternal(player_control &pc,
+ unsigned song, const struct song **queued_p);
+
+public:
+ enum playlist_result DeletePosition(player_control &pc,
+ unsigned position);
+
+ enum playlist_result DeleteOrder(player_control &pc,
+ unsigned order) {
+ return DeletePosition(pc, queue.OrderToPosition(order));
+ }
+
+ enum playlist_result DeleteId(player_control &pc, unsigned id);
+
+ /**
+ * Deletes a range of songs from the playlist.
+ *
+ * @param start the position of the first song to delete
+ * @param end the position after the last song to delete
+ */
+ enum playlist_result DeleteRange(player_control &pc,
+ unsigned start, unsigned end);
+
+ void DeleteSong(player_control &pc, const song &song);
+
+ void Shuffle(player_control &pc, unsigned start, unsigned end);
+
+ enum playlist_result MoveRange(player_control &pc,
+ unsigned start, unsigned end, int to);
+
+ enum playlist_result MoveId(player_control &pc, unsigned id, int to);
+
+ enum playlist_result SwapPositions(player_control &pc,
+ unsigned song1, unsigned song2);
+
+ enum playlist_result SwapIds(player_control &pc,
+ unsigned id1, unsigned id2);
+
+ enum playlist_result SetPriorityRange(player_control &pc,
+ unsigned start_position,
+ unsigned end_position,
+ uint8_t priority);
+
+ enum playlist_result SetPriorityId(player_control &pc,
+ unsigned song_id, uint8_t priority);
+
+ void Stop(player_control &pc);
+
+ enum playlist_result PlayPosition(player_control &pc, int position);
+
+ void PlayOrder(player_control &pc, int order);
+
+ enum playlist_result PlayId(player_control &pc, int id);
+
+ void PlayNext(player_control &pc);
+
+ void PlayPrevious(player_control &pc);
+
+ enum playlist_result SeekSongPosition(player_control &pc,
+ unsigned song_position,
+ float seek_time);
+
+ enum playlist_result SeekSongId(player_control &pc,
+ unsigned song_id, float seek_time);
+
+ /**
+ * Seek within the current song. Fails if MPD is not currently
+ * playing.
+ *
+ * @param time the time in seconds
+ * @param relative if true, then the specified time is relative to the
+ * current position
+ */
+ enum playlist_result SeekCurrent(player_control &pc,
+ float seek_time, bool relative);
+
+ bool GetRepeat() const {
+ return queue.repeat;
+ }
+
+ void SetRepeat(player_control &pc, bool new_value);
+
+ bool GetRandom() const {
+ return queue.random;
+ }
+
+ void SetRandom(player_control &pc, bool new_value);
+
+ bool GetSingle() const {
+ return queue.single;
+ }
+
+ void SetSingle(player_control &pc, bool new_value);
+
+ bool GetConsume() const {
+ return queue.consume;
+ }
+
+ void SetConsume(bool new_value);
+};
+
+#endif
diff --git a/src/playlist_any.c b/src/PlaylistAny.cxx
index 450ca593..3f6733f0 100644
--- a/src/playlist_any.c
+++ b/src/PlaylistAny.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,19 @@
*/
#include "config.h"
-#include "playlist_any.h"
-#include "playlist_list.h"
-#include "playlist_mapper.h"
-#include "uri.h"
+#include "PlaylistAny.hxx"
+#include "PlaylistMapper.hxx"
+#include "PlaylistRegistry.hxx"
#include "input_stream.h"
+extern "C" {
+#include "uri.h"
+}
+
#include <assert.h>
static struct playlist_provider *
-playlist_open_remote(const char *uri, GMutex *mutex, GCond *cond,
+playlist_open_remote(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
assert(uri_has_scheme(uri));
@@ -62,7 +65,7 @@ playlist_open_remote(const char *uri, GMutex *mutex, GCond *cond,
}
struct playlist_provider *
-playlist_open_any(const char *uri, GMutex *mutex, GCond *cond,
+playlist_open_any(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
return uri_has_scheme(uri)
diff --git a/src/playlist_any.h b/src/PlaylistAny.hxx
index 310913de..d69087b3 100644
--- a/src/playlist_any.h
+++ b/src/PlaylistAny.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_ANY_H
-#define MPD_PLAYLIST_ANY_H
+#ifndef MPD_PLAYLIST_ANY_HXX
+#define MPD_PLAYLIST_ANY_HXX
-#include <glib.h>
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
struct playlist_provider;
struct input_stream;
@@ -35,7 +36,7 @@ struct input_stream;
* freed
*/
struct playlist_provider *
-playlist_open_any(const char *uri, GMutex *mutex, GCond *cond,
+playlist_open_any(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r);
#endif
diff --git a/src/PlaylistCommands.cxx b/src/PlaylistCommands.cxx
new file mode 100644
index 00000000..dc3b3e0d
--- /dev/null
+++ b/src/PlaylistCommands.cxx
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlaylistCommands.hxx"
+#include "DatabasePlaylist.hxx"
+#include "CommandError.hxx"
+#include "PlaylistPrint.hxx"
+#include "PlaylistSave.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistVector.hxx"
+#include "PlaylistQueue.hxx"
+#include "TimePrint.hxx"
+#include "ClientInternal.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+#include "Playlist.hxx"
+
+extern "C" {
+#include "uri.h"
+}
+
+#include <assert.h>
+#include <stdlib.h>
+
+static void
+print_spl_list(Client *client, const PlaylistVector &list)
+{
+ for (const auto &i : list) {
+ client_printf(client, "playlist: %s\n", i.name.c_str());
+
+ if (i.mtime > 0)
+ time_print(client, "Last-Modified", i.mtime);
+ }
+}
+
+enum command_return
+handle_save(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ enum playlist_result result;
+
+ result = spl_save_playlist(argv[1], &client->playlist);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_load(Client *client, int argc, char *argv[])
+{
+ unsigned start_index, end_index;
+
+ if (argc < 3) {
+ start_index = 0;
+ end_index = G_MAXUINT;
+ } else if (!check_range(client, &start_index, &end_index, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result;
+
+ result = playlist_open_into_queue(argv[1],
+ start_index, end_index,
+ &client->playlist,
+ client->player_control, true);
+ if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
+ return print_playlist_result(client, result);
+
+ GError *error = NULL;
+ if (playlist_load_spl(&client->playlist, client->player_control,
+ argv[1], start_index, end_index,
+ &error))
+ return COMMAND_RETURN_OK;
+
+ if (error->domain == playlist_quark() &&
+ error->code == PLAYLIST_RESULT_BAD_NAME)
+ /* the message for BAD_NAME is confusing when the
+ client wants to load a playlist file from the music
+ directory; patch the GError object to show "no such
+ playlist" instead */
+ error->code = PLAYLIST_RESULT_NO_SUCH_LIST;
+
+ return print_error(client, error);
+}
+
+enum command_return
+handle_listplaylist(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (playlist_file_print(client, argv[1], false))
+ return COMMAND_RETURN_OK;
+
+ GError *error = NULL;
+ return spl_print(client, argv[1], false, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_listplaylistinfo(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[])
+{
+ if (playlist_file_print(client, argv[1], true))
+ return COMMAND_RETURN_OK;
+
+ GError *error = NULL;
+ return spl_print(client, argv[1], true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_rm(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_delete(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_rename(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_rename(argv[1], argv[2], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistdelete(Client *client,
+ G_GNUC_UNUSED int argc, char *argv[]) {
+ char *playlist = argv[1];
+ unsigned from;
+
+ if (!check_unsigned(client, &from, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ GError *error = NULL;
+ return spl_remove_index(playlist, from, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistmove(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *playlist = argv[1];
+ unsigned from, to;
+
+ if (!check_unsigned(client, &from, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &to, argv[3]))
+ return COMMAND_RETURN_ERROR;
+
+ GError *error = NULL;
+ return spl_move_index(playlist, from, to, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistclear(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ GError *error = NULL;
+ return spl_clear(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_playlistadd(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *playlist = argv[1];
+ char *uri = argv[2];
+
+ bool success;
+ GError *error = NULL;
+ if (uri_has_scheme(uri)) {
+ if (!uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = spl_append_uri(argv[1], playlist, &error);
+ } else
+ success = search_add_to_playlist(uri, playlist, nullptr,
+ &error);
+
+ if (!success && error == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "directory or file not found");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return success ? COMMAND_RETURN_OK : print_error(client, error);
+}
+
+enum command_return
+handle_listplaylists(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ GError *error = NULL;
+ const auto list = ListPlaylistFiles(&error);
+ if (list.empty() && error != NULL)
+ return print_error(client, error);
+
+ print_spl_list(client, list);
+ return COMMAND_RETURN_OK;
+}
diff --git a/src/PlaylistCommands.hxx b/src/PlaylistCommands.hxx
new file mode 100644
index 00000000..067f428b
--- /dev/null
+++ b/src/PlaylistCommands.hxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_COMMANDS_HXX
+#define MPD_PLAYLIST_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_save(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_load(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylist(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylistinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rm(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_rename(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistdelete(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistmove(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistclear(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistadd(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_listplaylists(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/PlaylistControl.cxx b/src/PlaylistControl.cxx
new file mode 100644
index 00000000..3db61cc7
--- /dev/null
+++ b/src/PlaylistControl.cxx
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Functions for controlling playback on the playlist level.
+ *
+ */
+
+#include "config.h"
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+#include "song.h"
+
+#include <glib.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "playlist"
+
+void
+playlist::Stop(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(current >= 0);
+
+ g_debug("stop");
+ pc.Stop();
+ queued = -1;
+ playing = false;
+
+ if (queue.random) {
+ /* shuffle the playlist, so the next playback will
+ result in a new random order */
+
+ unsigned current_position = queue.OrderToPosition(current);
+
+ queue.ShuffleOrder();
+
+ /* make sure that "current" stays valid, and the next
+ "play" command plays the same song again */
+ current = queue.PositionToOrder(current_position);
+ }
+}
+
+enum playlist_result
+playlist::PlayPosition(player_control &pc, int song)
+{
+ pc.ClearError();
+
+ unsigned i = song;
+ if (song == -1) {
+ /* play any song ("current" song, or the first song */
+
+ if (queue.IsEmpty())
+ return PLAYLIST_RESULT_SUCCESS;
+
+ if (playing) {
+ /* already playing: unpause playback, just in
+ case it was paused, and return */
+ pc.SetPause(false);
+ return PLAYLIST_RESULT_SUCCESS;
+ }
+
+ /* select a song: "current" song, or the first one */
+ i = current >= 0
+ ? current
+ : 0;
+ } else if (!queue.IsValidPosition(song))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (queue.random) {
+ if (song >= 0)
+ /* "i" is currently the song position (which
+ would be equal to the order number in
+ no-random mode); convert it to a order
+ number, because random mode is enabled */
+ i = queue.PositionToOrder(song);
+
+ if (!playing)
+ current = 0;
+
+ /* swap the new song with the previous "current" one,
+ so playback continues as planned */
+ queue.SwapOrders(i, current);
+ i = current;
+ }
+
+ stop_on_error = false;
+ error_count = 0;
+
+ PlayOrder(pc, i);
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::PlayId(player_control &pc, int id)
+{
+ if (id == -1)
+ return PlayPosition(pc, id);
+
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return PlayPosition(pc, song);
+}
+
+void
+playlist::PlayNext(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+ assert(queue.IsValidOrder(current));
+
+ const int old_current = current;
+ stop_on_error = false;
+
+ /* determine the next song from the queue's order list */
+
+ const int next_order = queue.GetNextOrder(current);
+ if (next_order < 0) {
+ /* no song after this one: stop playback */
+ Stop(pc);
+
+ /* reset "current song" */
+ current = -1;
+ }
+ else
+ {
+ if (next_order == 0 && queue.random) {
+ /* The queue told us that the next song is the first
+ song. This means we are in repeat mode. Shuffle
+ the queue order, so this time, the user hears the
+ songs in a different than before */
+ assert(queue.repeat);
+
+ queue.ShuffleOrder();
+
+ /* note that current and queued are
+ now invalid, but playlist_play_order() will
+ discard them anyway */
+ }
+
+ PlayOrder(pc, next_order);
+ }
+
+ /* Consume mode removes each played songs. */
+ if (queue.consume)
+ DeleteOrder(pc, old_current);
+}
+
+void
+playlist::PlayPrevious(player_control &pc)
+{
+ if (!playing)
+ return;
+
+ assert(!queue.IsEmpty());
+
+ int order;
+ if (current > 0) {
+ /* play the preceding song */
+ order = current - 1;
+ } else if (queue.repeat) {
+ /* play the last song in "repeat" mode */
+ order = queue.GetLength() - 1;
+ } else {
+ /* re-start playing the current song if it's
+ the first one */
+ order = current;
+ }
+
+ PlayOrder(pc, order);
+}
+
+enum playlist_result
+playlist::SeekSongPosition(player_control &pc, unsigned song, float seek_time)
+{
+ if (!queue.IsValidPosition(song))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ unsigned i = queue.random
+ ? queue.PositionToOrder(song)
+ : song;
+
+ pc.ClearError();
+ stop_on_error = true;
+ error_count = 0;
+
+ if (!playing || (unsigned)current != i) {
+ /* seeking is not within the current song - prepare
+ song change */
+
+ playing = true;
+ current = i;
+
+ queued_song = nullptr;
+ }
+
+ struct song *the_song = song_dup_detached(queue.GetOrder(i));
+ if (!pc.Seek(the_song, seek_time)) {
+ UpdateQueuedSong(pc, queued_song);
+
+ return PLAYLIST_RESULT_NOT_PLAYING;
+ }
+
+ queued = -1;
+ UpdateQueuedSong(pc, NULL);
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SeekSongId(player_control &pc, unsigned id, float seek_time)
+{
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SeekSongPosition(pc, song, seek_time);
+}
+
+enum playlist_result
+playlist::SeekCurrent(player_control &pc, float seek_time, bool relative)
+{
+ if (!playing)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ if (relative) {
+ const auto status = pc.GetStatus();
+
+ if (status.state != PLAYER_STATE_PLAY &&
+ status.state != PLAYER_STATE_PAUSE)
+ return PLAYLIST_RESULT_NOT_PLAYING;
+
+ seek_time += (int)status.elapsed_time;
+ }
+
+ if (seek_time < 0)
+ seek_time = 0;
+
+ return SeekSongPosition(pc, current, seek_time);
+}
diff --git a/src/playlist_database.c b/src/PlaylistDatabase.cxx
index 6b9d8715..edc6a281 100644
--- a/src/playlist_database.c
+++ b/src/PlaylistDatabase.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,13 @@
*/
#include "config.h"
-#include "playlist_database.h"
-#include "playlist_vector.h"
-#include "text_file.h"
+#include "PlaylistDatabase.hxx"
+#include "PlaylistVector.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "string_util.h"
+}
#include <string.h>
#include <stdlib.h>
@@ -33,27 +36,25 @@ playlist_database_quark(void)
}
void
-playlist_vector_save(FILE *fp, const struct list_head *pv)
+playlist_vector_save(FILE *fp, const PlaylistVector &pv)
{
- struct playlist_metadata *pm;
- playlist_vector_for_each(pm, pv)
+ for (const PlaylistInfo &pi : pv)
fprintf(fp, PLAYLIST_META_BEGIN "%s\n"
"mtime: %li\n"
"playlist_end\n",
- pm->name, (long)pm->mtime);
+ pi.name.c_str(), (long)pi.mtime);
}
bool
-playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
- GString *buffer, GError **error_r)
+playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name,
+ GError **error_r)
{
- struct playlist_metadata pm = {
- .mtime = 0,
- };
+ PlaylistInfo pm(name, 0);
+
char *line, *colon;
const char *value;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, "playlist_end") != 0) {
colon = strchr(line, ':');
if (colon == NULL || colon == line) {
@@ -74,6 +75,6 @@ playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
}
}
- playlist_vector_update_or_add(pv, name, pm.mtime);
+ pv.UpdateOrInsert(std::move(pm));
return true;
}
diff --git a/src/playlist_database.h b/src/PlaylistDatabase.hxx
index 3238fa06..a08d623f 100644
--- a/src/playlist_database.h
+++ b/src/PlaylistDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,24 +17,24 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_DATABASE_H
-#define MPD_PLAYLIST_DATABASE_H
+#ifndef MPD_PLAYLIST_DATABASE_HXX
+#define MPD_PLAYLIST_DATABASE_HXX
#include "check.h"
+#include "gerror.h"
-#include <stdbool.h>
#include <stdio.h>
-#include <glib.h>
#define PLAYLIST_META_BEGIN "playlist_begin: "
-struct list_head;
+class PlaylistVector;
+class TextFile;
void
-playlist_vector_save(FILE *fp, const struct list_head *pv);
+playlist_vector_save(FILE *fp, const PlaylistVector &pv);
bool
-playlist_metadata_load(FILE *fp, struct list_head *pv, const char *name,
- GString *buffer, GError **error_r);
+playlist_metadata_load(TextFile &file, PlaylistVector &pv, const char *name,
+ GError **error_r);
#endif
diff --git a/src/PlaylistEdit.cxx b/src/PlaylistEdit.cxx
new file mode 100644
index 00000000..df38c3da
--- /dev/null
+++ b/src/PlaylistEdit.cxx
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Functions for editing the playlist (adding, removing, reordering
+ * songs in the queue).
+ *
+ */
+
+#include "config.h"
+#include "Playlist.hxx"
+#include "PlayerControl.hxx"
+
+extern "C" {
+#include "uri.h"
+#include "song.h"
+}
+
+#include "Idle.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+
+#include <stdlib.h>
+
+void
+playlist::OnModified()
+{
+ queue.IncrementVersion();
+
+ idle_add(IDLE_PLAYLIST);
+}
+
+void
+playlist::Clear(player_control &pc)
+{
+ Stop(pc);
+
+ queue.Clear();
+ current = -1;
+
+ OnModified();
+}
+
+enum playlist_result
+playlist::AppendFile(struct player_control &pc,
+ const char *path_utf8, unsigned *added_id)
+{
+ struct song *song = song_file_load(path_utf8, NULL);
+ if (song == NULL)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return AppendSong(pc, song, added_id);
+}
+
+enum playlist_result
+playlist::AppendSong(struct player_control &pc,
+ struct song *song, unsigned *added_id)
+{
+ unsigned id;
+
+ if (queue.IsFull())
+ return PLAYLIST_RESULT_TOO_LARGE;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ id = queue.Append(song, 0);
+
+ if (queue.random) {
+ /* shuffle the new song into the list of remaining
+ songs to play */
+
+ unsigned start;
+ if (queued >= 0)
+ start = queued + 1;
+ else
+ start = current + 1;
+ if (start < queue.GetLength())
+ queue.ShuffleOrderLast(start, queue.GetLength());
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ if (added_id)
+ *added_id = id;
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::AppendURI(struct player_control &pc,
+ const char *uri, unsigned *added_id)
+{
+ g_debug("add to playlist: %s", uri);
+
+ const Database *db = nullptr;
+ struct song *song;
+ if (uri_has_scheme(uri)) {
+ song = song_remote_new(uri);
+ } else {
+ db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ song = db->GetSong(uri, nullptr);
+ if (song == nullptr)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+ }
+
+ enum playlist_result result = AppendSong(pc, song, added_id);
+ if (db != nullptr)
+ db->ReturnSong(song);
+
+ return result;
+}
+
+enum playlist_result
+playlist::SwapPositions(player_control &pc, unsigned song1, unsigned song2)
+{
+ if (!queue.IsValidPosition(song1) || !queue.IsValidPosition(song2))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ queue.SwapPositions(song1, song2);
+
+ if (queue.random) {
+ /* update the queue order, so that current
+ still points to the current song order */
+
+ queue.SwapOrders(queue.PositionToOrder(song1),
+ queue.PositionToOrder(song2));
+ } else {
+ /* correct the "current" song order */
+
+ if (current == (int)song1)
+ current = song2;
+ else if (current == (int)song2)
+ current = song1;
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SwapIds(player_control &pc, unsigned id1, unsigned id2)
+{
+ int song1 = queue.IdToPosition(id1);
+ int song2 = queue.IdToPosition(id2);
+
+ if (song1 < 0 || song2 < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SwapPositions(pc, song1, song2);
+}
+
+enum playlist_result
+playlist::SetPriorityRange(player_control &pc,
+ unsigned start, unsigned end,
+ uint8_t priority)
+{
+ if (start >= GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > GetLength())
+ end = GetLength();
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ /* remember "current" and "queued" */
+
+ const int current_position = GetCurrentPosition();
+ const struct song *const queued_song = GetQueuedSong();
+
+ /* apply the priority changes */
+
+ queue.SetPriorityRange(start, end, priority, current);
+
+ /* restore "current" and choose a new "queued" */
+
+ if (current_position >= 0)
+ current = queue.PositionToOrder(current_position);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::SetPriorityId(struct player_control &pc,
+ unsigned song_id, uint8_t priority)
+{
+ int song_position = queue.IdToPosition(song_id);
+ if (song_position < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return SetPriorityRange(pc, song_position, song_position + 1,
+ priority);
+
+}
+
+void
+playlist::DeleteInternal(player_control &pc,
+ unsigned song, const struct song **queued_p)
+{
+ assert(song < GetLength());
+
+ unsigned songOrder = queue.PositionToOrder(song);
+
+ if (playing && current == (int)songOrder) {
+ const bool paused = pc.GetState() == PLAYER_STATE_PAUSE;
+
+ /* the current song is going to be deleted: stop the player */
+
+ pc.Stop();
+ playing = false;
+
+ /* see which song is going to be played instead */
+
+ current = queue.GetNextOrder(current);
+ if (current == (int)songOrder)
+ current = -1;
+
+ if (current >= 0 && !paused)
+ /* play the song after the deleted one */
+ PlayOrder(pc, current);
+ else
+ /* no songs left to play, stop playback
+ completely */
+ Stop(pc);
+
+ *queued_p = NULL;
+ } else if (current == (int)songOrder)
+ /* there's a "current song" but we're not playing
+ currently - clear "current" */
+ current = -1;
+
+ /* now do it: remove the song */
+
+ queue.DeletePosition(song);
+
+ /* update the "current" and "queued" variables */
+
+ if (current > (int)songOrder)
+ current--;
+}
+
+enum playlist_result
+playlist::DeletePosition(struct player_control &pc, unsigned song)
+{
+ if (song >= queue.GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ DeleteInternal(pc, song, &queued_song);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::DeleteRange(struct player_control &pc, unsigned start, unsigned end)
+{
+ if (start >= queue.GetLength())
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > queue.GetLength())
+ end = queue.GetLength();
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ const struct song *queued_song = GetQueuedSong();
+
+ do {
+ DeleteInternal(pc, --end, &queued_song);
+ } while (end != start);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::DeleteId(struct player_control &pc, unsigned id)
+{
+ int song = queue.IdToPosition(id);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return DeletePosition(pc, song);
+}
+
+void
+playlist::DeleteSong(struct player_control &pc, const struct song &song)
+{
+ for (int i = queue.GetLength() - 1; i >= 0; --i)
+ // TODO: compare URI instead of pointer
+ if (&song == queue.Get(i))
+ DeletePosition(pc, i);
+}
+
+enum playlist_result
+playlist::MoveRange(player_control &pc, unsigned start, unsigned end, int to)
+{
+ if (!queue.IsValidPosition(start) || !queue.IsValidPosition(end - 1))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if ((to >= 0 && to + end - start - 1 >= GetLength()) ||
+ (to < 0 && unsigned(abs(to)) > GetLength()))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if ((int)start == to)
+ /* nothing happens */
+ return PLAYLIST_RESULT_SUCCESS;
+
+ const struct song *const queued_song = GetQueuedSong();
+
+ /*
+ * (to < 0) => move to offset from current song
+ * (-playlist.length == to) => move to position BEFORE current song
+ */
+ const int currentSong = GetCurrentPosition();
+ if (to < 0 && currentSong >= 0) {
+ if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
+ /* no-op, can't be moved to offset of itself */
+ return PLAYLIST_RESULT_SUCCESS;
+ to = (currentSong + abs(to)) % GetLength();
+ if (start < (unsigned)to)
+ to--;
+ }
+
+ queue.MoveRange(start, end, to);
+
+ if (!queue.random) {
+ /* update current/queued */
+ if ((int)start <= current && (unsigned)current < end)
+ current += to - start;
+ else if (current >= (int)end && current <= to)
+ current -= end - start;
+ else if (current >= to && current < (int)start)
+ current += end - start;
+ }
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist::MoveId(player_control &pc, unsigned id1, int to)
+{
+ int song = queue.IdToPosition(id1);
+ if (song < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return MoveRange(pc, song, song + 1, to);
+}
+
+void
+playlist::Shuffle(player_control &pc, unsigned start, unsigned end)
+{
+ if (end > GetLength())
+ /* correct the "end" offset */
+ end = GetLength();
+
+ if (start + 1 >= end)
+ /* needs at least two entries. */
+ return;
+
+ const struct song *const queued_song = GetQueuedSong();
+ if (playing && current >= 0) {
+ unsigned current_position = queue.OrderToPosition(current);
+
+ if (current_position >= start && current_position < end) {
+ /* put current playing song first */
+ queue.SwapPositions(start, current_position);
+
+ if (queue.random) {
+ current = queue.PositionToOrder(start);
+ } else
+ current = start;
+
+ /* start shuffle after the current song */
+ start++;
+ }
+ } else {
+ /* no playback currently: reset current */
+
+ current = -1;
+ }
+
+ queue.ShuffleRange(start, end);
+
+ UpdateQueuedSong(pc, queued_song);
+ OnModified();
+}
diff --git a/src/stored_playlist.c b/src/PlaylistFile.cxx
index 39ba2bac..a879f70a 100644
--- a/src/stored_playlist.c
+++ b/src/PlaylistFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,24 @@
*/
#include "config.h"
-#include "stored_playlist.h"
-#include "playlist_save.h"
-#include "text_file.h"
+#include "PlaylistFile.hxx"
+#include "PlaylistSave.hxx"
+#include "PlaylistInfo.hxx"
+#include "PlaylistVector.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
#include "song.h"
-#include "mapper.h"
-#include "path.h"
-#include "uri.h"
-#include "database.h"
-#include "idle.h"
+#include "io_error.h"
+#include "Mapper.hxx"
+#include "TextFile.hxx"
#include "conf.h"
-#include "glib_compat.h"
+#include "Idle.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+extern "C" {
+#include "uri.h"
+}
#include <assert.h>
#include <sys/types.h>
@@ -76,13 +83,13 @@ spl_valid_name(const char *name_utf8)
static const char *
spl_map(GError **error_r)
{
- const char *path_fs = map_spl_path();
- if (path_fs == NULL)
+ const Path &path_fs = map_spl_path();
+ if (path_fs.IsNull())
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_DISABLED,
"Stored playlists are disabled");
- return path_fs;
+ return path_fs.c_str();
}
static bool
@@ -98,15 +105,15 @@ spl_check_name(const char *name_utf8, GError **error_r)
return true;
}
-static char *
+static Path
spl_map_to_fs(const char *name_utf8, GError **error_r)
{
if (spl_map(error_r) == NULL ||
!spl_check_name(name_utf8, error_r))
- return NULL;
+ return Path::Null();
- char *path_fs = map_spl_utf8_to_fs(name_utf8);
- if (path_fs == NULL)
+ Path path_fs = map_spl_utf8_to_fs(name_utf8);
+ if (path_fs.IsNull())
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_NAME,
"Bad playlist name");
@@ -128,147 +135,115 @@ playlist_errno(GError **error_r)
break;
default:
- g_set_error_literal(error_r, g_file_error_quark(), errno,
- g_strerror(errno));
+ set_error_errno(error_r);
break;
}
}
-static struct stored_playlist_info *
-load_playlist_info(const char *parent_path_fs, const char *name_fs)
+static bool
+LoadPlaylistFileInfo(PlaylistInfo &info,
+ const char *parent_path_fs, const char *name_fs)
{
size_t name_length = strlen(name_fs);
- char *path_fs, *name, *name_utf8;
- int ret;
- struct stat st;
- struct stored_playlist_info *playlist;
if (name_length < sizeof(PLAYLIST_FILE_SUFFIX) ||
memchr(name_fs, '\n', name_length) != NULL)
- return NULL;
+ return false;
if (!g_str_has_suffix(name_fs, PLAYLIST_FILE_SUFFIX))
- return NULL;
+ return false;
- path_fs = g_build_filename(parent_path_fs, name_fs, NULL);
- ret = stat(path_fs, &st);
+ char *path_fs = g_build_filename(parent_path_fs, name_fs, NULL);
+ struct stat st;
+ int ret = stat(path_fs, &st);
g_free(path_fs);
if (ret < 0 || !S_ISREG(st.st_mode))
- return NULL;
+ return false;
- name = g_strndup(name_fs,
- name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX));
- name_utf8 = fs_charset_to_utf8(name);
+ char *name = g_strndup(name_fs,
+ name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX));
+ std::string name_utf8 = Path::ToUTF8(name);
g_free(name);
- if (name_utf8 == NULL)
- return NULL;
+ if (name_utf8.empty())
+ return false;
- playlist = g_new(struct stored_playlist_info, 1);
- playlist->name = name_utf8;
- playlist->mtime = st.st_mtime;
- return playlist;
+ info.name = std::move(name_utf8);
+ info.mtime = st.st_mtime;
+ return true;
}
-GPtrArray *
-spl_list(GError **error_r)
+PlaylistVector
+ListPlaylistFiles(GError **error_r)
{
- const char *parent_path_fs = spl_map(error_r);
- DIR *dir;
- struct dirent *ent;
- GPtrArray *list;
- struct stored_playlist_info *playlist;
+ PlaylistVector list;
+ const char *parent_path_fs = spl_map(error_r);
if (parent_path_fs == NULL)
- return NULL;
+ return list;
- dir = opendir(parent_path_fs);
+ DIR *dir = opendir(parent_path_fs);
if (dir == NULL) {
- g_set_error_literal(error_r, g_file_error_quark(), errno,
- g_strerror(errno));
- return NULL;
+ set_error_errno(error_r);
+ return list;
}
- list = g_ptr_array_new();
-
+ PlaylistInfo info;
+ struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
- playlist = load_playlist_info(parent_path_fs, ent->d_name);
- if (playlist != NULL)
- g_ptr_array_add(list, playlist);
+ if (LoadPlaylistFileInfo(info, parent_path_fs, ent->d_name))
+ list.push_back(std::move(info));
}
closedir(dir);
return list;
}
-void
-spl_list_free(GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- struct stored_playlist_info *playlist =
- g_ptr_array_index(list, i);
- g_free(playlist->name);
- g_free(playlist);
- }
-
- g_ptr_array_free(list, true);
-}
-
static bool
-spl_save(GPtrArray *list, const char *utf8path, GError **error_r)
+SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path,
+ GError **error_r)
{
- FILE *file;
-
assert(utf8path != NULL);
if (spl_map(error_r) == NULL)
return false;
- char *path_fs = spl_map_to_fs(utf8path, error_r);
- if (path_fs == NULL)
+ const Path path_fs = spl_map_to_fs(utf8path, error_r);
+ if (path_fs.IsNull())
return false;
- file = fopen(path_fs, "w");
- g_free(path_fs);
+ FILE *file = FOpen(path_fs, FOpenMode::WriteText);
if (file == NULL) {
playlist_errno(error_r);
return false;
}
- for (unsigned i = 0; i < list->len; ++i) {
- const char *uri = g_ptr_array_index(list, i);
- playlist_print_uri(file, uri);
- }
+ for (const auto &uri_utf8 : contents)
+ playlist_print_uri(file, uri_utf8.c_str());
fclose(file);
return true;
}
-GPtrArray *
-spl_load(const char *utf8path, GError **error_r)
+PlaylistFileContents
+LoadPlaylistFile(const char *utf8path, GError **error_r)
{
- FILE *file;
- GPtrArray *list;
- char *path_fs;
+ PlaylistFileContents contents;
if (spl_map(error_r) == NULL)
- return NULL;
+ return contents;
- path_fs = spl_map_to_fs(utf8path, error_r);
- if (path_fs == NULL)
- return NULL;
+ const Path path_fs = spl_map_to_fs(utf8path, error_r);
+ if (path_fs.IsNull())
+ return contents;
- file = fopen(path_fs, "r");
- g_free(path_fs);
- if (file == NULL) {
+ TextFile file(path_fs);
+ if (file.HasFailed()) {
playlist_errno(error_r);
- return NULL;
+ return contents;
}
- list = g_ptr_array_new();
-
- GString *buffer = g_string_sized_new(1024);
char *s;
- while ((s = read_text_line(file, buffer)) != NULL) {
+ while ((s = file.ReadLine()) != NULL) {
if (*s == 0 || *s == PLAYLIST_COMMENT)
continue;
@@ -283,80 +258,45 @@ spl_load(const char *utf8path, GError **error_r)
} else
s = g_strdup(s);
- g_ptr_array_add(list, s);
-
- if (list->len >= playlist_max_length)
+ contents.emplace_back(s);
+ if (contents.size() >= playlist_max_length)
break;
}
- fclose(file);
- return list;
-}
-
-void
-spl_free(GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- char *uri = g_ptr_array_index(list, i);
- g_free(uri);
- }
-
- g_ptr_array_free(list, true);
-}
-
-static char *
-spl_remove_index_internal(GPtrArray *list, unsigned idx)
-{
- char *uri;
-
- assert(idx < list->len);
-
- uri = g_ptr_array_remove_index(list, idx);
- assert(uri != NULL);
- return uri;
-}
-
-static void
-spl_insert_index_internal(GPtrArray *list, unsigned idx, char *uri)
-{
- assert(idx <= list->len);
-
- g_ptr_array_add(list, uri);
-
- memmove(list->pdata + idx + 1, list->pdata + idx,
- (list->len - idx - 1) * sizeof(list->pdata[0]));
- g_ptr_array_index(list, idx) = uri;
+ return contents;
}
bool
spl_move_index(const char *utf8path, unsigned src, unsigned dest,
GError **error_r)
{
- char *uri;
-
if (src == dest)
/* this doesn't check whether the playlist exists, but
what the hell.. */
return true;
- GPtrArray *list = spl_load(utf8path, error_r);
- if (list == NULL)
+ GError *error = nullptr;
+ auto contents = LoadPlaylistFile(utf8path, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (src >= list->len || dest >= list->len) {
- spl_free(list);
+ if (src >= contents.size() || dest >= contents.size()) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_RANGE,
"Bad range");
return false;
}
- uri = spl_remove_index_internal(list, src);
- spl_insert_index_internal(list, dest, uri);
+ const auto src_i = std::next(contents.begin(), src);
+ auto value = std::move(*src_i);
+ contents.erase(src_i);
- bool result = spl_save(list, utf8path, error_r);
+ const auto dest_i = std::next(contents.begin(), dest);
+ contents.insert(dest_i, std::move(value));
- spl_free(list);
+ bool result = SavePlaylistFile(contents, utf8path, error_r);
idle_add(IDLE_STORED_PLAYLIST);
return result;
@@ -365,17 +305,14 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest,
bool
spl_clear(const char *utf8path, GError **error_r)
{
- FILE *file;
-
if (spl_map(error_r) == NULL)
return false;
- char *path_fs = spl_map_to_fs(utf8path, error_r);
- if (path_fs == NULL)
+ const Path path_fs = spl_map_to_fs(utf8path, error_r);
+ if (path_fs.IsNull())
return false;
- file = fopen(path_fs, "w");
- g_free(path_fs);
+ FILE *file = FOpen(path_fs, FOpenMode::WriteText);
if (file == NULL) {
playlist_errno(error_r);
return false;
@@ -390,16 +327,11 @@ spl_clear(const char *utf8path, GError **error_r)
bool
spl_delete(const char *name_utf8, GError **error_r)
{
- char *path_fs;
- int ret;
-
- path_fs = spl_map_to_fs(name_utf8, error_r);
- if (path_fs == NULL)
+ const Path path_fs = spl_map_to_fs(name_utf8, error_r);
+ if (path_fs.IsNull())
return false;
- ret = unlink(path_fs);
- g_free(path_fs);
- if (ret < 0) {
+ if (!RemoveFile(path_fs)) {
playlist_errno(error_r);
return false;
}
@@ -411,25 +343,23 @@ spl_delete(const char *name_utf8, GError **error_r)
bool
spl_remove_index(const char *utf8path, unsigned pos, GError **error_r)
{
- char *uri;
-
- GPtrArray *list = spl_load(utf8path, error_r);
- if (list == NULL)
+ GError *error = nullptr;
+ auto contents = LoadPlaylistFile(utf8path, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (pos >= list->len) {
- spl_free(list);
+ if (pos >= contents.size()) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_RANGE,
"Bad range");
return false;
}
- uri = spl_remove_index_internal(list, pos);
- g_free(uri);
- bool result = spl_save(list, utf8path, error_r);
+ contents.erase(std::next(contents.begin(), pos));
- spl_free(list);
+ bool result = SavePlaylistFile(contents, utf8path, error_r);
idle_add(IDLE_STORED_PLAYLIST);
return result;
@@ -438,23 +368,20 @@ spl_remove_index(const char *utf8path, unsigned pos, GError **error_r)
bool
spl_append_song(const char *utf8path, struct song *song, GError **error_r)
{
- FILE *file;
- struct stat st;
-
if (spl_map(error_r) == NULL)
return false;
- char *path_fs = spl_map_to_fs(utf8path, error_r);
- if (path_fs == NULL)
+ const Path path_fs = spl_map_to_fs(utf8path, error_r);
+ if (path_fs.IsNull())
return false;
- file = fopen(path_fs, "a");
- g_free(path_fs);
+ FILE *file = FOpen(path_fs, FOpenMode::AppendText);
if (file == NULL) {
playlist_errno(error_r);
return false;
}
+ struct stat st;
if (fstat(fileno(file), &st) < 0) {
playlist_errno(error_r);
fclose(file);
@@ -480,45 +407,45 @@ spl_append_song(const char *utf8path, struct song *song, GError **error_r)
bool
spl_append_uri(const char *url, const char *utf8file, GError **error_r)
{
- struct song *song;
-
if (uri_has_scheme(url)) {
- song = song_remote_new(url);
+ struct song *song = song_remote_new(url);
bool success = spl_append_song(utf8file, song, error_r);
song_free(song);
return success;
} else {
- song = db_get_song(url);
- if (song == NULL) {
- g_set_error_literal(error_r, playlist_quark(),
- PLAYLIST_RESULT_NO_SUCH_SONG,
- "No such song");
+ const Database *db = GetDatabase(error_r);
+ if (db == nullptr)
+ return false;
+
+ song *song = db->GetSong(url, error_r);
+ if (song == nullptr)
return false;
- }
- return spl_append_song(utf8file, song, error_r);
+ bool success = spl_append_song(utf8file, song, error_r);
+ db->ReturnSong(song);
+ return success;
}
}
static bool
-spl_rename_internal(const char *from_path_fs, const char *to_path_fs,
+spl_rename_internal(const Path &from_path_fs, const Path &to_path_fs,
GError **error_r)
{
- if (!g_file_test(from_path_fs, G_FILE_TEST_IS_REGULAR)) {
+ if (!FileExists(from_path_fs)) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_NO_SUCH_LIST,
"No such playlist");
return false;
}
- if (g_file_test(to_path_fs, G_FILE_TEST_EXISTS)) {
+ if (FileExists(to_path_fs)) {
g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_LIST_EXISTS,
"Playlist exists already");
return false;
}
- if (rename(from_path_fs, to_path_fs) < 0) {
+ if (!RenameFile(from_path_fs, to_path_fs)) {
playlist_errno(error_r);
return false;
}
@@ -533,20 +460,13 @@ spl_rename(const char *utf8from, const char *utf8to, GError **error_r)
if (spl_map(error_r) == NULL)
return false;
- char *from_path_fs = spl_map_to_fs(utf8from, error_r);
- if (from_path_fs == NULL)
+ Path from_path_fs = spl_map_to_fs(utf8from, error_r);
+ if (from_path_fs.IsNull())
return false;
- char *to_path_fs = spl_map_to_fs(utf8to, error_r);
- if (to_path_fs == NULL) {
- g_free(from_path_fs);
+ Path to_path_fs = spl_map_to_fs(utf8to, error_r);
+ if (to_path_fs.IsNull())
return false;
- }
-
- bool success = spl_rename_internal(from_path_fs, to_path_fs, error_r);
-
- g_free(from_path_fs);
- g_free(to_path_fs);
- return success;
+ return spl_rename_internal(from_path_fs, to_path_fs, error_r);
}
diff --git a/src/stored_playlist.h b/src/PlaylistFile.hxx
index cfe49633..a9aeaa23 100644
--- a/src/stored_playlist.h
+++ b/src/PlaylistFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STORED_PLAYLIST_H
-#define MPD_STORED_PLAYLIST_H
+#ifndef MPD_PLAYLIST_FILE_HXX
+#define MPD_PLAYLIST_FILE_HXX
-#include <glib.h>
-#include <stdbool.h>
-#include <time.h>
+#include "gerror.h"
-struct song;
+#include <vector>
+#include <string>
-struct stored_playlist_info {
- char *name;
+struct song;
+struct PlaylistInfo;
+class PlaylistVector;
- time_t mtime;
-};
+typedef std::vector<std::string> PlaylistFileContents;
extern bool playlist_saveAbsolutePaths;
@@ -40,6 +39,8 @@ extern bool playlist_saveAbsolutePaths;
void
spl_global_init(void);
+#ifdef __cplusplus
+
/**
* Determines whether the specified string is a valid name for a
* stored playlist.
@@ -51,17 +52,11 @@ spl_valid_name(const char *name_utf8);
* Returns a list of stored_playlist_info struct pointers. Returns
* NULL if an error occurred.
*/
-GPtrArray *
-spl_list(GError **error_r);
-
-void
-spl_list_free(GPtrArray *list);
+PlaylistVector
+ListPlaylistFiles(GError **error_r);
-GPtrArray *
-spl_load(const char *utf8path, GError **error_r);
-
-void
-spl_free(GPtrArray *list);
+PlaylistFileContents
+LoadPlaylistFile(const char *utf8path, GError **error_r);
bool
spl_move_index(const char *utf8path, unsigned src, unsigned dest,
@@ -86,3 +81,5 @@ bool
spl_rename(const char *utf8from, const char *utf8to, GError **error_r);
#endif
+
+#endif
diff --git a/src/playlist_global.c b/src/PlaylistGlobal.cxx
index 650b88bb..9dd971d3 100644
--- a/src/playlist_global.c
+++ b/src/PlaylistGlobal.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,36 +23,27 @@
*/
#include "config.h"
-#include "playlist.h"
-#include "playlist_state.h"
-#include "event_pipe.h"
-#include "main.h"
-
-struct playlist g_playlist;
+#include "PlaylistGlobal.hxx"
+#include "Playlist.hxx"
+#include "Main.hxx"
+#include "Partition.hxx"
+#include "GlobalEvents.hxx"
static void
playlist_tag_event(void)
{
- playlist_tag_changed(&g_playlist);
+ global_partition->playlist.TagChanged();
}
static void
playlist_event(void)
{
- playlist_sync(&g_playlist, global_player_control);
-}
-
-void
-playlist_global_init(void)
-{
- playlist_init(&g_playlist);
-
- event_pipe_register(PIPE_EVENT_TAG, playlist_tag_event);
- event_pipe_register(PIPE_EVENT_PLAYLIST, playlist_event);
+ global_partition->playlist.SyncWithPlayer(global_partition->pc);
}
void
-playlist_global_finish(void)
+playlist_global_init()
{
- playlist_finish(&g_playlist);
+ GlobalEvents::Register(GlobalEvents::TAG, playlist_tag_event);
+ GlobalEvents::Register(GlobalEvents::PLAYLIST, playlist_event);
}
diff --git a/src/PlaylistGlobal.hxx b/src/PlaylistGlobal.hxx
new file mode 100644
index 00000000..4397292d
--- /dev/null
+++ b/src/PlaylistGlobal.hxx
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_GLOBAL_HXX
+#define MPD_PLAYLIST_GLOBAL_HXX
+
+void
+playlist_global_init();
+
+#endif
diff --git a/src/PlaylistInfo.hxx b/src/PlaylistInfo.hxx
new file mode 100644
index 00000000..96e4f6db
--- /dev/null
+++ b/src/PlaylistInfo.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_INFO_HXX
+#define MPD_PLAYLIST_INFO_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <string>
+
+#include <sys/time.h>
+
+/**
+ * A directory entry pointing to a playlist file.
+ */
+struct PlaylistInfo {
+ /**
+ * The UTF-8 encoded name of the playlist file.
+ */
+ std::string name;
+
+ time_t mtime;
+
+ class CompareName {
+ const char *const name;
+
+ public:
+ constexpr CompareName(const char *_name):name(_name) {}
+
+ gcc_pure
+ bool operator()(const PlaylistInfo &pi) const {
+ return pi.name.compare(name) == 0;
+ }
+ };
+
+ PlaylistInfo() = default;
+
+ template<typename N>
+ PlaylistInfo(N &&_name, time_t _mtime)
+ :name(std::forward<N>(_name)), mtime(_mtime) {}
+
+ PlaylistInfo(const PlaylistInfo &other) = delete;
+ PlaylistInfo(PlaylistInfo &&) = default;
+};
+
+#endif
diff --git a/src/playlist_mapper.c b/src/PlaylistMapper.cxx
index 13adb80d..85f47e44 100644
--- a/src/playlist_mapper.c
+++ b/src/PlaylistMapper.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,20 @@
*/
#include "config.h"
-#include "playlist_mapper.h"
-#include "playlist_list.h"
-#include "stored_playlist.h"
-#include "mapper.h"
+#include "PlaylistMapper.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistRegistry.hxx"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+
+extern "C" {
#include "uri.h"
+}
#include <assert.h>
static struct playlist_provider *
-playlist_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+playlist_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
struct playlist_provider *playlist;
@@ -45,18 +49,18 @@ playlist_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
* Load a playlist from the configured playlist directory.
*/
static struct playlist_provider *
-playlist_open_in_playlist_dir(const char *uri, GMutex *mutex, GCond *cond,
+playlist_open_in_playlist_dir(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
char *path_fs;
assert(spl_valid_name(uri));
- const char *playlist_directory_fs = map_spl_path();
- if (playlist_directory_fs == NULL)
+ const Path &playlist_directory_fs = map_spl_path();
+ if (playlist_directory_fs.IsNull())
return NULL;
- path_fs = g_build_filename(playlist_directory_fs, uri, NULL);
+ path_fs = g_build_filename(playlist_directory_fs.c_str(), uri, NULL);
struct playlist_provider *playlist =
playlist_open_path(path_fs, mutex, cond, is_r);
@@ -69,26 +73,20 @@ playlist_open_in_playlist_dir(const char *uri, GMutex *mutex, GCond *cond,
* Load a playlist from the configured music directory.
*/
static struct playlist_provider *
-playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond,
+playlist_open_in_music_dir(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
- char *path_fs;
-
assert(uri_safe_local(uri));
- path_fs = map_uri_fs(uri);
- if (path_fs == NULL)
+ Path path = map_uri_fs(uri);
+ if (path.IsNull())
return NULL;
- struct playlist_provider *playlist =
- playlist_open_path(path_fs, mutex, cond, is_r);
- g_free(path_fs);
-
- return playlist;
+ return playlist_open_path(path.c_str(), mutex, cond, is_r);
}
struct playlist_provider *
-playlist_mapper_open(const char *uri, GMutex *mutex, GCond *cond,
+playlist_mapper_open(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
struct playlist_provider *playlist;
diff --git a/src/playlist_mapper.h b/src/PlaylistMapper.hxx
index 9a7187d9..abfdb548 100644
--- a/src/playlist_mapper.h
+++ b/src/PlaylistMapper.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_MAPPER_H
-#define MPD_PLAYLIST_MAPPER_H
+#ifndef MPD_PLAYLIST_MAPPER_HXX
+#define MPD_PLAYLIST_MAPPER_HXX
-#include <glib.h>
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
struct input_stream;
@@ -33,7 +34,7 @@ struct input_stream;
* freed
*/
struct playlist_provider *
-playlist_mapper_open(const char *uri, GMutex *mutex, GCond *cond,
+playlist_mapper_open(const char *uri, Mutex &mutex, Cond &cond,
struct input_stream **is_r);
#endif
diff --git a/src/playlist_plugin.h b/src/PlaylistPlugin.hxx
index a27f651c..d422106b 100644
--- a/src/playlist_plugin.h
+++ b/src/PlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,12 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_PLUGIN_H
+#ifndef MPD_PLAYLIST_PLUGIN_HXX
+#define MPD_PLAYLIST_PLUGIN_HXX
-#include <glib.h>
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
-#include <stdbool.h>
#include <stddef.h>
struct config_param;
@@ -67,7 +67,7 @@ struct playlist_plugin {
* either matched one of the schemes or one of the suffixes.
*/
struct playlist_provider *(*open_uri)(const char *uri,
- GMutex *mutex, GCond *cond);
+ Mutex &mutex, Cond &cond);
/**
* Opens the playlist in the specified input stream. It has
@@ -114,7 +114,7 @@ playlist_plugin_finish(const struct playlist_plugin *plugin)
static inline struct playlist_provider *
playlist_plugin_open_uri(const struct playlist_plugin *plugin, const char *uri,
- GMutex *mutex, GCond *cond)
+ Mutex &mutex, Cond &cond)
{
return plugin->open_uri(uri, mutex, cond);
}
diff --git a/src/playlist_print.c b/src/PlaylistPrint.cxx
index 59c42f96..e79e8773 100644
--- a/src/playlist_print.c
+++ b/src/PlaylistPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,37 +18,41 @@
*/
#include "config.h"
-#include "playlist_print.h"
-#include "playlist_list.h"
-#include "playlist_plugin.h"
-#include "playlist_any.h"
-#include "playlist_song.h"
-#include "playlist.h"
-#include "queue_print.h"
-#include "stored_playlist.h"
-#include "song_print.h"
-#include "song.h"
-#include "database.h"
-#include "client.h"
+#include "PlaylistPrint.hxx"
+#include "PlaylistFile.hxx"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
+#include "PlaylistRegistry.hxx"
+#include "PlaylistPlugin.hxx"
+#include "QueuePrint.hxx"
+#include "SongPrint.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "Client.hxx"
#include "input_stream.h"
+extern "C" {
+#include "song.h"
+}
+
void
-playlist_print_uris(struct client *client, const struct playlist *playlist)
+playlist_print_uris(Client *client, const struct playlist *playlist)
{
const struct queue *queue = &playlist->queue;
- queue_print_uris(client, queue, 0, queue_length(queue));
+ queue_print_uris(client, queue, 0, queue->GetLength());
}
bool
-playlist_print_info(struct client *client, const struct playlist *playlist,
+playlist_print_info(Client *client, const struct playlist *playlist,
unsigned start, unsigned end)
{
const struct queue *queue = &playlist->queue;
- if (end > queue_length(queue))
+ if (end > queue->GetLength())
/* correct the "end" offset */
- end = queue_length(queue);
+ end = queue->GetLength();
if (start > end)
/* an invalid "start" offset is fatal */
@@ -59,13 +63,13 @@ playlist_print_info(struct client *client, const struct playlist *playlist,
}
bool
-playlist_print_id(struct client *client, const struct playlist *playlist,
+playlist_print_id(Client *client, const struct playlist *playlist,
unsigned id)
{
const struct queue *queue = &playlist->queue;
int position;
- position = queue_id_to_position(queue, id);
+ position = queue->IdToPosition(id);
if (position < 0)
/* no such song */
return false;
@@ -74,10 +78,9 @@ playlist_print_id(struct client *client, const struct playlist *playlist,
}
bool
-playlist_print_current(struct client *client, const struct playlist *playlist)
+playlist_print_current(Client *client, const struct playlist *playlist)
{
- int current_position = playlist_get_current_song(playlist);
-
+ int current_position = playlist->GetCurrentPosition();
if (current_position < 0)
return false;
@@ -87,21 +90,14 @@ playlist_print_current(struct client *client, const struct playlist *playlist)
}
void
-playlist_print_find(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list)
+playlist_print_find(Client *client, const struct playlist *playlist,
+ const SongFilter &filter)
{
- queue_find(client, &playlist->queue, list);
+ queue_find(client, &playlist->queue, filter);
}
void
-playlist_print_search(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list)
-{
- queue_search(client, &playlist->queue, list);
-}
-
-void
-playlist_print_changes_info(struct client *client,
+playlist_print_changes_info(Client *client,
const struct playlist *playlist,
uint32_t version)
{
@@ -109,46 +105,51 @@ playlist_print_changes_info(struct client *client,
}
void
-playlist_print_changes_position(struct client *client,
+playlist_print_changes_position(Client *client,
const struct playlist *playlist,
uint32_t version)
{
queue_print_changes_position(client, &playlist->queue, version);
}
+static bool
+PrintSongDetails(Client *client, const char *uri_utf8)
+{
+ const Database *db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return false;
+
+ song *song = db->GetSong(uri_utf8, nullptr);
+ if (song == nullptr)
+ return false;
+
+ song_print_info(client, song);
+ db->ReturnSong(song);
+ return true;
+}
+
bool
-spl_print(struct client *client, const char *name_utf8, bool detail,
+spl_print(Client *client, const char *name_utf8, bool detail,
GError **error_r)
{
- GPtrArray *list;
-
- list = spl_load(name_utf8, error_r);
- if (list == NULL)
+ GError *error = NULL;
+ PlaylistFileContents contents = LoadPlaylistFile(name_utf8, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- for (unsigned i = 0; i < list->len; ++i) {
- const char *temp = g_ptr_array_index(list, i);
- bool wrote = false;
-
- if (detail) {
- struct song *song = db_get_song(temp);
- if (song) {
- song_print_info(client, song);
- wrote = true;
- }
- }
-
- if (!wrote) {
- client_printf(client, SONG_FILE "%s\n", temp);
- }
+ for (const auto &uri_utf8 : contents) {
+ if (!detail || !PrintSongDetails(client, uri_utf8.c_str()))
+ client_printf(client, SONG_FILE "%s\n",
+ uri_utf8.c_str());
}
- spl_free(list);
return true;
}
static void
-playlist_provider_print(struct client *client, const char *uri,
+playlist_provider_print(Client *client, const char *uri,
struct playlist_provider *playlist, bool detail)
{
struct song *song;
@@ -164,27 +165,23 @@ playlist_provider_print(struct client *client, const char *uri,
else
song_print_uri(client, song);
- if (!song_in_database(song))
- song_free(song);
+ song_free(song);
}
g_free(base_uri);
}
bool
-playlist_file_print(struct client *client, const char *uri, bool detail)
+playlist_file_print(Client *client, const char *uri, bool detail)
{
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
struct input_stream *is;
struct playlist_provider *playlist =
playlist_open_any(uri, mutex, cond, &is);
- if (playlist == NULL) {
- g_cond_free(cond);
- g_mutex_free(mutex);
+ if (playlist == NULL)
return false;
- }
playlist_provider_print(client, uri, playlist, detail);
playlist_plugin_close(playlist);
@@ -192,8 +189,5 @@ playlist_file_print(struct client *client, const char *uri, bool detail)
if (is != NULL)
input_stream_close(is);
- g_cond_free(cond);
- g_mutex_free(mutex);
-
return true;
}
diff --git a/src/playlist_print.h b/src/PlaylistPrint.hxx
index d4f1911d..16bee9b8 100644
--- a/src/playlist_print.h
+++ b/src/PlaylistPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,22 +17,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef PLAYLIST_PRINT_H
-#define PLAYLIST_PRINT_H
+#ifndef MPD_PLAYLIST_PRINT_HXX
+#define MPD_PLAYLIST_PRINT_HXX
+
+#include "gerror.h"
-#include <glib.h>
-#include <stdbool.h>
#include <stdint.h>
-struct client;
struct playlist;
-struct locate_item_list;
+class SongFilter;
+class Client;
/**
* Sends the whole playlist to the client, song URIs only.
*/
void
-playlist_print_uris(struct client *client, const struct playlist *playlist);
+playlist_print_uris(Client *client, const struct playlist *playlist);
/**
* Sends a range of the playlist to the client, including all known
@@ -41,7 +41,7 @@ playlist_print_uris(struct client *client, const struct playlist *playlist);
* This function however fails when the start offset is invalid.
*/
bool
-playlist_print_info(struct client *client, const struct playlist *playlist,
+playlist_print_info(Client *client, const struct playlist *playlist,
unsigned start, unsigned end);
/**
@@ -50,7 +50,7 @@ playlist_print_info(struct client *client, const struct playlist *playlist,
* @return true on suite, false if there is no such song
*/
bool
-playlist_print_id(struct client *client, const struct playlist *playlist,
+playlist_print_id(Client *client, const struct playlist *playlist,
unsigned id);
/**
@@ -59,27 +59,20 @@ playlist_print_id(struct client *client, const struct playlist *playlist,
* @return true on success, false if there is no current song
*/
bool
-playlist_print_current(struct client *client, const struct playlist *playlist);
+playlist_print_current(Client *client, const struct playlist *playlist);
/**
* Find songs in the playlist.
*/
void
-playlist_print_find(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list);
-
-/**
- * Search for songs in the playlist.
- */
-void
-playlist_print_search(struct client *client, const struct playlist *playlist,
- const struct locate_item_list *list);
+playlist_print_find(Client *client, const struct playlist *playlist,
+ const SongFilter &filter);
/**
* Print detailed changes since the specified playlist version.
*/
void
-playlist_print_changes_info(struct client *client,
+playlist_print_changes_info(Client *client,
const struct playlist *playlist,
uint32_t version);
@@ -87,7 +80,7 @@ playlist_print_changes_info(struct client *client,
* Print changes since the specified playlist version, position only.
*/
void
-playlist_print_changes_position(struct client *client,
+playlist_print_changes_position(Client *client,
const struct playlist *playlist,
uint32_t version);
@@ -100,7 +93,7 @@ playlist_print_changes_position(struct client *client,
* @return true on success, false if the playlist does not exist
*/
bool
-spl_print(struct client *client, const char *name_utf8, bool detail,
+spl_print(Client *client, const char *name_utf8, bool detail,
GError **error_r);
/**
@@ -112,6 +105,6 @@ spl_print(struct client *client, const char *name_utf8, bool detail,
* @return true on success, false if the playlist does not exist
*/
bool
-playlist_file_print(struct client *client, const char *uri, bool detail);
+playlist_file_print(Client *client, const char *uri, bool detail);
#endif
diff --git a/src/playlist_queue.c b/src/PlaylistQueue.cxx
index aada9498..c52f49a9 100644
--- a/src/playlist_queue.c
+++ b/src/PlaylistQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,17 @@
*/
#include "config.h"
-#include "playlist_queue.h"
-#include "playlist_plugin.h"
-#include "playlist_any.h"
-#include "playlist_song.h"
-#include "playlist.h"
-#include "song.h"
+#include "PlaylistQueue.hxx"
+#include "PlaylistPlugin.hxx"
+#include "PlaylistAny.hxx"
+#include "PlaylistSong.hxx"
+#include "Playlist.hxx"
#include "input_stream.h"
+extern "C" {
+#include "song.h"
+}
+
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
unsigned start_index, unsigned end_index,
@@ -41,8 +44,7 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
++i) {
if (i < start_index) {
/* skip songs before the start index */
- if (!song_in_database(song))
- song_free(song);
+ song_free(song);
continue;
}
@@ -50,10 +52,9 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
if (song == NULL)
continue;
- result = playlist_append_song(dest, pc, song, NULL);
+ result = dest->AppendSong(*pc, song);
+ song_free(song);
if (result != PLAYLIST_RESULT_SUCCESS) {
- if (!song_in_database(song))
- song_free(song);
g_free(base_uri);
return result;
}
@@ -70,17 +71,14 @@ playlist_open_into_queue(const char *uri,
struct playlist *dest, struct player_control *pc,
bool secure)
{
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
struct input_stream *is;
struct playlist_provider *playlist =
playlist_open_any(uri, mutex, cond, &is);
- if (playlist == NULL) {
- g_cond_free(cond);
- g_mutex_free(mutex);
+ if (playlist == NULL)
return PLAYLIST_RESULT_NO_SUCH_LIST;
- }
enum playlist_result result =
playlist_load_into_queue(uri, playlist, start_index, end_index,
@@ -90,8 +88,5 @@ playlist_open_into_queue(const char *uri,
if (is != NULL)
input_stream_close(is);
- g_cond_free(cond);
- g_mutex_free(mutex);
-
return result;
}
diff --git a/src/playlist_queue.h b/src/PlaylistQueue.hxx
index 24a851aa..cda77c81 100644
--- a/src/playlist_queue.h
+++ b/src/PlaylistQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,11 @@
* \brief Glue between playlist plugin and the play queue
*/
-#ifndef MPD_PLAYLIST_QUEUE_H
-#define MPD_PLAYLIST_QUEUE_H
+#ifndef MPD_PLAYLIST_QUEUE_HXX
+#define MPD_PLAYLIST_QUEUE_HXX
#include "playlist_error.h"
-#include <stdbool.h>
-
struct playlist_provider;
struct playlist;
struct player_control;
diff --git a/src/playlist_list.c b/src/PlaylistRegistry.cxx
index 68d24fe4..6dc94034 100644
--- a/src/playlist_list.c
+++ b/src/PlaylistRegistry.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,25 @@
*/
#include "config.h"
-#include "playlist_list.h"
-#include "playlist_plugin.h"
-#include "playlist/extm3u_playlist_plugin.h"
-#include "playlist/m3u_playlist_plugin.h"
-#include "playlist/xspf_playlist_plugin.h"
-#include "playlist/lastfm_playlist_plugin.h"
-#include "playlist/despotify_playlist_plugin.h"
-#include "playlist/soundcloud_playlist_plugin.h"
-#include "playlist/pls_playlist_plugin.h"
-#include "playlist/asx_playlist_plugin.h"
-#include "playlist/rss_playlist_plugin.h"
-#include "playlist/cue_playlist_plugin.h"
-#include "playlist/embcue_playlist_plugin.h"
+#include "PlaylistRegistry.hxx"
+#include "PlaylistPlugin.hxx"
+#include "playlist/ExtM3uPlaylistPlugin.hxx"
+#include "playlist/M3uPlaylistPlugin.hxx"
+#include "playlist/XspfPlaylistPlugin.hxx"
+#include "playlist/LastFMPlaylistPlugin.hxx"
+#include "playlist/DespotifyPlaylistPlugin.hxx"
+#include "playlist/SoundCloudPlaylistPlugin.hxx"
+#include "playlist/PlsPlaylistPlugin.hxx"
+#include "playlist/AsxPlaylistPlugin.hxx"
+#include "playlist/RssPlaylistPlugin.hxx"
+#include "playlist/CuePlaylistPlugin.hxx"
+#include "playlist/EmbeddedCuePlaylistPlugin.hxx"
#include "input_stream.h"
+
+extern "C" {
#include "uri.h"
+}
+
#include "string_util.h"
#include "conf.h"
#include "mpd_error.h"
@@ -121,7 +125,7 @@ playlist_list_global_finish(void)
}
static struct playlist_provider *
-playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond,
+playlist_list_open_uri_scheme(const char *uri, Mutex &mutex, Cond &cond,
bool *tried)
{
char *scheme;
@@ -155,7 +159,7 @@ playlist_list_open_uri_scheme(const char *uri, GMutex *mutex, GCond *cond,
}
static struct playlist_provider *
-playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond,
+playlist_list_open_uri_suffix(const char *uri, Mutex &mutex, Cond &cond,
const bool *tried)
{
const char *suffix;
@@ -184,7 +188,7 @@ playlist_list_open_uri_suffix(const char *uri, GMutex *mutex, GCond *cond,
}
struct playlist_provider *
-playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond)
+playlist_list_open_uri(const char *uri, Mutex &mutex, Cond &cond)
{
struct playlist_provider *playlist;
/** this array tracks which plugins have already been tried by
@@ -229,19 +233,19 @@ playlist_list_open_stream_mime2(struct input_stream *is, const char *mime)
}
static struct playlist_provider *
-playlist_list_open_stream_mime(struct input_stream *is)
+playlist_list_open_stream_mime(struct input_stream *is, const char *full_mime)
{
- assert(is->mime != NULL);
+ assert(full_mime != NULL);
- const char *semicolon = strchr(is->mime, ';');
+ const char *semicolon = strchr(full_mime, ';');
if (semicolon == NULL)
- return playlist_list_open_stream_mime2(is, is->mime);
+ return playlist_list_open_stream_mime2(is, full_mime);
- if (semicolon == is->mime)
+ if (semicolon == full_mime)
return NULL;
/* probe only the portion before the semicolon*/
- char *mime = g_strndup(is->mime, semicolon - is->mime);
+ char *mime = g_strndup(full_mime, semicolon - full_mime);
struct playlist_provider *playlist =
playlist_list_open_stream_mime2(is, mime);
g_free(mime);
@@ -281,8 +285,9 @@ playlist_list_open_stream(struct input_stream *is, const char *uri)
input_stream_lock_wait_ready(is);
- if (is->mime != NULL) {
- playlist = playlist_list_open_stream_mime(is);
+ const char *const mime = input_stream_get_mime_type(is);
+ if (mime != NULL) {
+ playlist = playlist_list_open_stream_mime(is, mime);
if (playlist != NULL)
return playlist;
}
@@ -312,7 +317,7 @@ playlist_suffix_supported(const char *suffix)
}
struct playlist_provider *
-playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+playlist_list_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
struct input_stream **is_r)
{
GError *error = NULL;
diff --git a/src/playlist_list.h b/src/PlaylistRegistry.hxx
index c3967d5a..7c34c156 100644
--- a/src/playlist_list.h
+++ b/src/PlaylistRegistry.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,12 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_LIST_H
-#define MPD_PLAYLIST_LIST_H
+#ifndef MPD_PLAYLIST_REGISTRY_HXX
+#define MPD_PLAYLIST_REGISTRY_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
struct playlist_provider;
struct input_stream;
@@ -51,7 +50,7 @@ playlist_list_global_finish(void);
* Opens a playlist by its URI.
*/
struct playlist_provider *
-playlist_list_open_uri(const char *uri, GMutex *mutex, GCond *cond);
+playlist_list_open_uri(const char *uri, Mutex &mutex, Cond &cond);
/**
* Opens a playlist from an input stream.
@@ -79,7 +78,7 @@ playlist_suffix_supported(const char *suffix);
* @return a playlist, or NULL on error
*/
struct playlist_provider *
-playlist_list_open_path(const char *path_fs, GMutex *mutex, GCond *cond,
+playlist_list_open_path(const char *path_fs, Mutex &mutex, Cond &cond,
struct input_stream **is_r);
#endif
diff --git a/src/playlist_save.c b/src/PlaylistSave.cxx
index 334159e0..6ca6740f 100644
--- a/src/playlist_save.c
+++ b/src/PlaylistSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,18 @@
*/
#include "config.h"
-#include "playlist_save.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-#include "queue.h"
+#include "PlaylistSave.hxx"
+#include "PlaylistFile.hxx"
+#include "Playlist.hxx"
#include "song.h"
-#include "mapper.h"
-#include "path.h"
+#include "Mapper.hxx"
+#include "Idle.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+extern "C" {
#include "uri.h"
-#include "database.h"
-#include "idle.h"
-#include "glib_compat.h"
+}
#include <glib.h>
@@ -36,68 +37,54 @@ void
playlist_print_song(FILE *file, const struct song *song)
{
if (playlist_saveAbsolutePaths && song_in_database(song)) {
- char *path = map_song_fs(song);
- if (path != NULL) {
- fprintf(file, "%s\n", path);
- g_free(path);
- }
+ const Path path = map_song_fs(song);
+ if (!path.IsNull())
+ fprintf(file, "%s\n", path.c_str());
} else {
- char *uri = song_get_uri(song), *uri_fs;
-
- uri_fs = utf8_to_fs_charset(uri);
+ char *uri = song_get_uri(song);
+ const Path uri_fs = Path::FromUTF8(uri);
g_free(uri);
- fprintf(file, "%s\n", uri_fs);
- g_free(uri_fs);
+ if (!uri_fs.IsNull())
+ fprintf(file, "%s\n", uri_fs.c_str());
}
}
void
playlist_print_uri(FILE *file, const char *uri)
{
- char *s;
+ Path path = playlist_saveAbsolutePaths && !uri_has_scheme(uri) &&
+ !g_path_is_absolute(uri)
+ ? map_uri_fs(uri)
+ : Path::FromUTF8(uri);
- if (playlist_saveAbsolutePaths && !uri_has_scheme(uri) &&
- !g_path_is_absolute(uri))
- s = map_uri_fs(uri);
- else
- s = utf8_to_fs_charset(uri);
-
- if (s != NULL) {
- fprintf(file, "%s\n", s);
- g_free(s);
- }
+ if (!path.IsNull())
+ fprintf(file, "%s\n", path.c_str());
}
enum playlist_result
spl_save_queue(const char *name_utf8, const struct queue *queue)
{
- char *path_fs;
- FILE *file;
-
- if (map_spl_path() == NULL)
+ if (map_spl_path().IsNull())
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(name_utf8))
return PLAYLIST_RESULT_BAD_NAME;
- path_fs = map_spl_utf8_to_fs(name_utf8);
- if (path_fs == NULL)
+ const Path path_fs = map_spl_utf8_to_fs(name_utf8);
+ if (path_fs.IsNull())
return PLAYLIST_RESULT_BAD_NAME;
- if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) {
- g_free(path_fs);
+ if (FileExists(path_fs))
return PLAYLIST_RESULT_LIST_EXISTS;
- }
- file = fopen(path_fs, "w");
- g_free(path_fs);
+ FILE *file = FOpen(path_fs, FOpenMode::WriteText);
if (file == NULL)
return PLAYLIST_RESULT_ERRNO;
- for (unsigned i = 0; i < queue_length(queue); i++)
- playlist_print_song(file, queue_get(queue, i));
+ for (unsigned i = 0; i < queue->GetLength(); i++)
+ playlist_print_song(file, queue->Get(i));
fclose(file);
@@ -117,34 +104,35 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
unsigned start_index, unsigned end_index,
GError **error_r)
{
- GPtrArray *list;
-
- list = spl_load(name_utf8, error_r);
- if (list == NULL)
+ GError *error = NULL;
+ PlaylistFileContents contents = LoadPlaylistFile(name_utf8, &error);
+ if (contents.empty() && error != nullptr) {
+ g_propagate_error(error_r, error);
return false;
+ }
- if (list->len < end_index)
- end_index = list->len;
+ if (end_index > contents.size())
+ end_index = contents.size();
for (unsigned i = start_index; i < end_index; ++i) {
- const char *temp = g_ptr_array_index(list, i);
- if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ const auto &uri_utf8 = contents[i];
+
+ if ((playlist->AppendURI(*pc, uri_utf8.c_str())) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */
- char *temp2 = g_strdup(temp);
+ char *temp2 = g_strdup(uri_utf8.c_str());
char *p = temp2;
while (*p) {
if (*p == '\\')
*p = '/';
p++;
}
- if ((playlist_append_uri(playlist, pc, temp2,
- NULL)) != PLAYLIST_RESULT_SUCCESS) {
+
+ if (playlist->AppendURI(*pc, temp2) != PLAYLIST_RESULT_SUCCESS)
g_warning("can't add file \"%s\"", temp2);
- }
+
g_free(temp2);
}
}
- spl_free(list);
return true;
}
diff --git a/src/playlist_save.h b/src/PlaylistSave.hxx
index a6c31a9a..ff5f0c49 100644
--- a/src/playlist_save.h
+++ b/src/PlaylistSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,7 +22,6 @@
#include "playlist_error.h"
-#include <stdbool.h>
#include <stdio.h>
struct song;
diff --git a/src/playlist_song.c b/src/PlaylistSong.cxx
index a3d9ab4d..4df4d22d 100644
--- a/src/playlist_song.c
+++ b/src/PlaylistSong.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,20 @@
*/
#include "config.h"
-#include "playlist_song.h"
-#include "database.h"
-#include "mapper.h"
+#include "PlaylistSong.hxx"
+#include "Mapper.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "ls.hxx"
+#include "tag.h"
+#include "fs/Path.hxx"
+
+extern "C" {
#include "song.h"
#include "uri.h"
-#include "path.h"
-#include "ls.h"
-#include "tag.h"
+}
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -59,18 +65,15 @@ apply_song_metadata(struct song *dest, const struct song *src)
return dest;
if (song_in_database(dest)) {
- char *path_fs = map_song_fs(dest);
- if (path_fs == NULL)
+ const Path &path_fs = map_song_fs(dest);
+ if (path_fs.IsNull())
return dest;
- char *path_utf8 = fs_charset_to_utf8(path_fs);
- if (path_utf8 != NULL)
- g_free(path_fs);
- else
- path_utf8 = path_fs;
+ std::string path_utf8 = path_fs.ToUTF8();
+ if (path_utf8.empty())
+ path_utf8 = path_fs.c_str();
- tmp = song_file_new(path_utf8, NULL);
- g_free(path_utf8);
+ tmp = song_file_new(path_utf8.c_str(), NULL);
merge_song_metadata(tmp, dest, src);
} else {
@@ -86,9 +89,7 @@ apply_song_metadata(struct song *dest, const struct song *src)
(e.g. last track on a CUE file); fix it up here */
tmp->tag->time = dest->tag->time - src->start_ms / 1000;
- if (!song_in_database(dest))
- song_free(dest);
-
+ song_free(dest);
return tmp;
}
@@ -104,10 +105,17 @@ playlist_check_load_song(const struct song *song, const char *uri, bool secure)
if (dest == NULL)
return NULL;
} else {
- dest = db_get_song(uri);
- if (dest == NULL)
+ const Database *db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return nullptr;
+
+ struct song *tmp = db->GetSong(uri, nullptr);
+ if (tmp == NULL)
/* not found in database */
return NULL;
+
+ dest = song_dup_detached(tmp);
+ db->ReturnSong(tmp);
}
return apply_song_metadata(dest, song);
diff --git a/src/playlist_song.h b/src/PlaylistSong.hxx
index ea878691..117ee133 100644
--- a/src/playlist_song.h
+++ b/src/PlaylistSong.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_SONG_H
-#define MPD_PLAYLIST_SONG_H
-
-#include <stdbool.h>
+#ifndef MPD_PLAYLIST_SONG_HXX
+#define MPD_PLAYLIST_SONG_HXX
/**
* Verifies the song, returns NULL if it is unsafe. Translate the
diff --git a/src/playlist_state.c b/src/PlaylistState.cxx
index 4aa2c2c9..d03de0a1 100644
--- a/src/playlist_state.c
+++ b/src/PlaylistState.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,12 +23,11 @@
*/
#include "config.h"
-#include "playlist_state.h"
-#include "playlist.h"
-#include "player_control.h"
-#include "queue_save.h"
-#include "path.h"
-#include "text_file.h"
+#include "PlaylistState.hxx"
+#include "Playlist.hxx"
+#include "QueueSave.hxx"
+#include "TextFile.hxx"
+#include "PlayerControl.hxx"
#include "conf.h"
#include <string.h>
@@ -57,9 +56,7 @@ void
playlist_state_save(FILE *fp, const struct playlist *playlist,
struct player_control *pc)
{
- struct player_status player_status;
-
- pc_get_status(pc, &player_status);
+ const auto player_status = pc->GetStatus();
fputs(PLAYLIST_STATE_FILE_STATE, fp);
@@ -72,8 +69,7 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
fputs(PLAYLIST_STATE_FILE_STATE_PLAY "\n", fp);
}
fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
- queue_order_to_position(&playlist->queue,
- playlist->current));
+ playlist->queue.OrderToPosition(playlist->current));
fprintf(fp, PLAYLIST_STATE_FILE_TIME "%i\n",
(int)player_status.elapsed_time);
} else {
@@ -81,8 +77,7 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
if (playlist->current >= 0)
fprintf(fp, PLAYLIST_STATE_FILE_CURRENT "%i\n",
- queue_order_to_position(&playlist->queue,
- playlist->current));
+ playlist->queue.OrderToPosition(playlist->current));
}
fprintf(fp, PLAYLIST_STATE_FILE_RANDOM "%i\n", playlist->queue.random);
@@ -91,29 +86,29 @@ playlist_state_save(FILE *fp, const struct playlist *playlist,
fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n",
playlist->queue.consume);
fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
- (int)(pc_get_cross_fade(pc)));
+ (int)pc->GetCrossFade());
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n",
- pc_get_mixramp_db(pc));
+ pc->GetMixRampDb());
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
- pc_get_mixramp_delay(pc));
+ pc->GetMixRampDelay());
fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp);
queue_save(fp, &playlist->queue);
fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp);
}
static void
-playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
+playlist_state_load(TextFile &file, struct playlist *playlist)
{
- const char *line = read_text_line(fp, buffer);
+ const char *line = file.ReadLine();
if (line == NULL) {
g_warning("No playlist in state file");
return;
}
while (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_PLAYLIST_END)) {
- queue_load_song(fp, buffer, line, &playlist->queue);
+ queue_load_song(file, line, &playlist->queue);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL) {
g_warning("'" PLAYLIST_STATE_FILE_PLAYLIST_END
"' not found in state file");
@@ -121,11 +116,11 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
}
}
- queue_increment_version(&playlist->queue);
+ playlist->queue.IncrementVersion();
}
bool
-playlist_state_restore(const char *line, FILE *fp, GString *buffer,
+playlist_state_restore(const char *line, TextFile &file,
struct playlist *playlist, struct player_control *pc)
{
int current = -1;
@@ -143,40 +138,27 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
else if (strcmp(line, PLAYLIST_STATE_FILE_STATE_PAUSE) == 0)
state = PLAYER_STATE_PAUSE;
- while ((line = read_text_line(fp, buffer)) != NULL) {
+ while ((line = file.ReadLine()) != NULL) {
if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_TIME)) {
seek_time =
atoi(&(line[strlen(PLAYLIST_STATE_FILE_TIME)]));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_REPEAT)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
- "1") == 0) {
- playlist_set_repeat(playlist, pc, true);
- } else
- playlist_set_repeat(playlist, pc, false);
+ playlist->SetRepeat(*pc,
+ strcmp(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
- "1") == 0) {
- playlist_set_single(playlist, pc, true);
- } else
- playlist_set_single(playlist, pc, false);
+ playlist->SetSingle(*pc,
+ strcmp(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
- if (strcmp
- (&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
- "1") == 0) {
- playlist_set_consume(playlist, true);
- } else
- playlist_set_consume(playlist, false);
+ playlist->SetConsume(strcmp(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
+ "1") == 0);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
- pc_set_cross_fade(pc,
- atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
+ pc->SetCrossFade(atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB)) {
- pc_set_mixramp_db(pc,
- atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
+ pc->SetMixRampDb(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY)) {
- pc_set_mixramp_delay(pc,
- atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
+ pc->SetMixRampDelay(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_RANDOM)) {
random_mode =
strcmp(line + strlen(PLAYLIST_STATE_FILE_RANDOM),
@@ -187,18 +169,18 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
(PLAYLIST_STATE_FILE_CURRENT)]));
} else if (g_str_has_prefix(line,
PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)) {
- playlist_state_load(fp, buffer, playlist);
+ playlist_state_load(file, playlist);
}
}
- playlist_set_random(playlist, pc, random_mode);
+ playlist->SetRandom(*pc, random_mode);
- if (!queue_is_empty(&playlist->queue)) {
- if (!queue_valid_position(&playlist->queue, current))
+ if (!playlist->queue.IsEmpty()) {
+ if (!playlist->queue.IsValidPosition(current))
current = 0;
if (state == PLAYER_STATE_PLAY &&
- config_get_bool("restore_paused", false))
+ config_get_bool(CONF_RESTORE_PAUSED, false))
/* the user doesn't want MPD to auto-start
playback after startup; fall back to
"pause" */
@@ -208,17 +190,17 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
called here, after the audio output states were
restored, before playback begins */
if (state != PLAYER_STATE_STOP)
- pc_update_audio(pc);
+ pc->UpdateAudio();
if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current;
else if (seek_time == 0)
- playlist_play(playlist, pc, current);
+ playlist->PlayPosition(*pc, current);
else
- playlist_seek_song(playlist, pc, current, seek_time);
+ playlist->SeekSongPosition(*pc, current, seek_time);
if (state == PLAYER_STATE_PAUSE)
- pc_pause(pc);
+ pc->Pause();
}
return true;
@@ -228,19 +210,16 @@ unsigned
playlist_state_get_hash(const struct playlist *playlist,
struct player_control *pc)
{
- struct player_status player_status;
-
- pc_get_status(pc, &player_status);
+ const auto player_status = pc->GetStatus();
return playlist->queue.version ^
(player_status.state != PLAYER_STATE_STOP
? ((int)player_status.elapsed_time << 8)
: 0) ^
(playlist->current >= 0
- ? (queue_order_to_position(&playlist->queue,
- playlist->current) << 16)
+ ? (playlist->queue.OrderToPosition(playlist->current) << 16)
: 0) ^
- ((int)pc_get_cross_fade(pc) << 20) ^
+ ((int)pc->GetCrossFade() << 20) ^
(player_status.state << 24) ^
(playlist->queue.random << 27) ^
(playlist->queue.repeat << 28) ^
diff --git a/src/playlist_state.h b/src/PlaylistState.hxx
index f67d01d2..7e994478 100644
--- a/src/playlist_state.h
+++ b/src/PlaylistState.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,22 +22,21 @@
*
*/
-#ifndef PLAYLIST_STATE_H
-#define PLAYLIST_STATE_H
+#ifndef MPD_PLAYLIST_STATE_HXX
+#define MPD_PLAYLIST_STATE_HXX
-#include <glib.h>
-#include <stdbool.h>
#include <stdio.h>
struct playlist;
struct player_control;
+class TextFile;
void
playlist_state_save(FILE *fp, const struct playlist *playlist,
struct player_control *pc);
bool
-playlist_state_restore(const char *line, FILE *fp, GString *buffer,
+playlist_state_restore(const char *line, TextFile &file,
struct playlist *playlist, struct player_control *pc);
/**
diff --git a/src/PlaylistVector.cxx b/src/PlaylistVector.cxx
new file mode 100644
index 00000000..06c7b9ff
--- /dev/null
+++ b/src/PlaylistVector.cxx
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "PlaylistVector.hxx"
+#include "DatabaseLock.hxx"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+PlaylistVector::iterator
+PlaylistVector::find(const char *name)
+{
+ assert(holding_db_lock());
+ assert(name != NULL);
+
+ return std::find_if(begin(), end(),
+ PlaylistInfo::CompareName(name));
+}
+
+bool
+PlaylistVector::UpdateOrInsert(PlaylistInfo &&pi)
+{
+ assert(holding_db_lock());
+
+ auto i = find(pi.name.c_str());
+ if (i != end()) {
+ if (pi.mtime == i->mtime)
+ return false;
+
+ i->mtime = pi.mtime;
+ } else
+ push_back(std::move(pi));
+
+ return true;
+}
+
+bool
+PlaylistVector::erase(const char *name)
+{
+ assert(holding_db_lock());
+
+ auto i = find(name);
+ if (i == end())
+ return false;
+
+ erase(i);
+ return true;
+}
diff --git a/src/PlaylistVector.hxx b/src/PlaylistVector.hxx
new file mode 100644
index 00000000..d10c90fd
--- /dev/null
+++ b/src/PlaylistVector.hxx
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PLAYLIST_VECTOR_HXX
+#define MPD_PLAYLIST_VECTOR_HXX
+
+#include "PlaylistInfo.hxx"
+#include "gcc.h"
+
+#include <list>
+
+class PlaylistVector : protected std::list<PlaylistInfo> {
+protected:
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ gcc_pure
+ iterator find(const char *name);
+
+public:
+ using std::list<PlaylistInfo>::empty;
+ using std::list<PlaylistInfo>::begin;
+ using std::list<PlaylistInfo>::end;
+ using std::list<PlaylistInfo>::push_back;
+ using std::list<PlaylistInfo>::erase;
+
+ /**
+ * Caller must lock the #db_mutex.
+ *
+ * @return true if the vector or one of its items was modified
+ */
+ bool UpdateOrInsert(PlaylistInfo &&pi);
+
+ /**
+ * Caller must lock the #db_mutex.
+ */
+ bool erase(const char *name);
+};
+
+#endif /* SONGVEC_H */
diff --git a/src/Queue.cxx b/src/Queue.cxx
new file mode 100644
index 00000000..3fdb9ed1
--- /dev/null
+++ b/src/Queue.cxx
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "Queue.hxx"
+#include "song.h"
+
+#include <stdlib.h>
+
+queue::queue(unsigned _max_length)
+ :max_length(_max_length), length(0),
+ version(1),
+ items(new Item[max_length]),
+ order(new unsigned[max_length]),
+ id_table(max_length * HASH_MULT),
+ repeat(false),
+ single(false),
+ consume(false),
+ random(false)
+{
+}
+
+queue::~queue()
+{
+ Clear();
+
+ delete[] items;
+ delete[] order;
+}
+
+int
+queue::GetNextOrder(unsigned _order) const
+{
+ assert(_order < length);
+
+ if (single && repeat && !consume)
+ return _order;
+ else if (_order + 1 < length)
+ return _order + 1;
+ else if (repeat && (_order > 0 || !consume))
+ /* restart at first song */
+ return 0;
+ else
+ /* end of queue */
+ return -1;
+}
+
+void
+queue::IncrementVersion()
+{
+ static unsigned long max = ((uint32_t) 1 << 31) - 1;
+
+ version++;
+
+ if (version >= max) {
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = 0;
+
+ version = 1;
+ }
+}
+
+void
+queue::ModifyAtOrder(unsigned _order)
+{
+ assert(_order < length);
+
+ unsigned position = order[_order];
+ items[position].version = version;
+
+ IncrementVersion();
+}
+
+void
+queue::ModifyAll()
+{
+ for (unsigned i = 0; i < length; i++)
+ items[i].version = version;
+
+ IncrementVersion();
+}
+
+unsigned
+queue::Append(struct song *song, uint8_t priority)
+{
+ assert(!IsFull());
+
+ const unsigned position = length++;
+ const unsigned id = id_table.Insert(position);
+
+ auto &item = items[position];
+ item.song = song_dup_detached(song);
+ item.id = id;
+ item.version = version;
+ item.priority = priority;
+
+ order[position] = position;
+
+ return id;
+}
+
+void
+queue::SwapPositions(unsigned position1, unsigned position2)
+{
+ unsigned id1 = items[position1].id;
+ unsigned id2 = items[position2].id;
+
+ std::swap(items[position1], items[position2]);
+
+ items[position1].version = version;
+ items[position2].version = version;
+
+ id_table.Move(id1, position2);
+ id_table.Move(id2, position1);
+}
+
+void
+queue::MovePostion(unsigned from, unsigned to)
+{
+ const Item tmp = items[from];
+
+ /* move songs to one less in from->to */
+
+ for (unsigned i = from; i < to; i++)
+ MoveItemTo(i + 1, i);
+
+ /* move songs to one more in to->from */
+
+ for (unsigned i = from; i > to; i--)
+ MoveItemTo(i - 1, i);
+
+ /* put song at _to_ */
+
+ id_table.Move(tmp.id, to);
+ items[to] = tmp;
+ items[to].version = version;
+
+ /* now deal with order */
+
+ if (random) {
+ for (unsigned i = 0; i < length; i++) {
+ if (order[i] > from && order[i] <= to)
+ order[i]--;
+ else if (order[i] < from &&
+ order[i] >= to)
+ order[i]++;
+ else if (from == order[i])
+ order[i] = to;
+ }
+ }
+}
+
+void
+queue::MoveRange(unsigned start, unsigned end, unsigned to)
+{
+ Item tmp[end - start];
+ // Copy the original block [start,end-1]
+ for (unsigned i = start; i < end; i++)
+ tmp[i - start] = items[i];
+
+ // If to > start, we need to move to-start items to start, starting from end
+ for (unsigned i = end; i < end + to - start; i++)
+ MoveItemTo(i, start + i - end);
+
+ // If to < start, we need to move start-to items to newend (= end + to - start), starting from to
+ // This is the same as moving items from start-1 to to (decreasing), with start-1 going to end-1
+ // We have to iterate in this order to avoid writing over something we haven't yet moved
+ for (int i = start - 1; i >= int(to); i--)
+ MoveItemTo(i, i + end - start);
+
+ // Copy the original block back in, starting at to.
+ for (unsigned i = start; i< end; i++)
+ {
+ id_table.Move(tmp[i - start].id, to + i - start);
+ items[to + i - start] = tmp[i-start];
+ items[to + i - start].version = version;
+ }
+
+ if (random) {
+ // Update the positions in the queue.
+ // Note that the ranges for these cases are the same as the ranges of
+ // the loops above.
+ for (unsigned i = 0; i < length; i++) {
+ if (order[i] >= end && order[i] < to + end - start)
+ order[i] -= end - start;
+ else if (order[i] < start &&
+ order[i] >= to)
+ order[i] += end - start;
+ else if (start <= order[i] && order[i] < end)
+ order[i] += to - start;
+ }
+ }
+}
+
+void
+queue::MoveOrder(unsigned from_order, unsigned to_order)
+{
+ assert(from_order < length);
+ assert(to_order <= length);
+
+ const unsigned from_position = OrderToPosition(from_order);
+
+ if (from_order < to_order) {
+ for (unsigned i = from_order; i < to_order; ++i)
+ order[i] = order[i + 1];
+ } else {
+ for (unsigned i = from_order; i > to_order; --i)
+ order[i] = order[i - 1];
+ }
+
+ order[to_order] = from_position;
+}
+
+void
+queue::DeletePosition(unsigned position)
+{
+ assert(position < length);
+
+ struct song *song = Get(position);
+ assert(!song_in_database(song) || song_is_detached(song));
+ song_free(song);
+
+ const unsigned id = PositionToId(position);
+ const unsigned _order = PositionToOrder(position);
+
+ --length;
+
+ /* release the song id */
+
+ id_table.Erase(id);
+
+ /* delete song from songs array */
+
+ for (unsigned i = position; i < length; i++)
+ MoveItemTo(i + 1, i);
+
+ /* delete the entry from the order array */
+
+ for (unsigned i = _order; i < length; i++)
+ order[i] = order[i + 1];
+
+ /* readjust values in the order array */
+
+ for (unsigned i = 0; i < length; i++)
+ if (order[i] > position)
+ --order[i];
+}
+
+void
+queue::Clear()
+{
+ for (unsigned i = 0; i < length; i++) {
+ Item *item = &items[i];
+
+ assert(!song_in_database(item->song) ||
+ song_is_detached(item->song));
+ song_free(item->song);
+
+ id_table.Erase(item->id);
+ }
+
+ length = 0;
+}
+
+static void
+queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start <= end);
+ assert(end <= queue->length);
+
+ auto cmp = [queue](unsigned a_pos, unsigned b_pos){
+ const queue::Item &a = queue->items[a_pos];
+ const queue::Item &b = queue->items[b_pos];
+
+ return a.priority > b.priority;
+ };
+
+ std::stable_sort(queue->order + start, queue->order + end, cmp);
+}
+
+void
+queue::ShuffleOrderRange(unsigned start, unsigned end)
+{
+ assert(random);
+ assert(start <= end);
+ assert(end <= length);
+
+ rand.AutoCreate();
+ std::shuffle(order + start, order + end, rand);
+}
+
+/**
+ * Sort the "order" of items by priority, and then shuffle each
+ * priority group.
+ */
+void
+queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
+{
+ assert(random);
+ assert(start <= end);
+ assert(end <= length);
+
+ if (start == end)
+ return;
+
+ /* first group the range by priority */
+ queue_sort_order_by_priority(this, start, end);
+
+ /* now shuffle each priority group */
+ unsigned group_start = start;
+ uint8_t group_priority = GetOrderPriority(start);
+
+ for (unsigned i = start + 1; i < end; ++i) {
+ const uint8_t priority = GetOrderPriority(i);
+ assert(priority <= group_priority);
+
+ if (priority != group_priority) {
+ /* start of a new group - shuffle the one that
+ has just ended */
+ ShuffleOrderRange(group_start, i);
+ group_start = i;
+ group_priority = priority;
+ }
+ }
+
+ /* shuffle the last group */
+ ShuffleOrderRange(group_start, end);
+}
+
+void
+queue::ShuffleOrder()
+{
+ ShuffleOrderRangeWithPriority(0, length);
+}
+
+void
+queue::ShuffleOrderFirst(unsigned start, unsigned end)
+{
+ rand.AutoCreate();
+
+ std::uniform_int_distribution<unsigned> distribution(start, end - 1);
+ SwapOrders(start, distribution(rand));
+}
+
+void
+queue::ShuffleOrderLast(unsigned start, unsigned end)
+{
+ rand.AutoCreate();
+
+ std::uniform_int_distribution<unsigned> distribution(start, end - 1);
+ SwapOrders(end - 1, distribution(rand));
+}
+
+void
+queue::ShuffleRange(unsigned start, unsigned end)
+{
+ assert(start <= end);
+ assert(end <= length);
+
+ rand.AutoCreate();
+
+ for (unsigned i = start; i < end; i++) {
+ std::uniform_int_distribution<unsigned> distribution(start,
+ end - 1);
+ unsigned ri = distribution(rand);
+ SwapPositions(i, ri);
+ }
+}
+
+unsigned
+queue::FindPriorityOrder(unsigned start_order, uint8_t priority,
+ unsigned exclude_order) const
+{
+ assert(random);
+ assert(start_order <= length);
+
+ for (unsigned i = start_order; i < length; ++i) {
+ const unsigned position = OrderToPosition(i);
+ const Item *item = &items[position];
+ if (item->priority <= priority && i != exclude_order)
+ return i;
+ }
+
+ return length;
+}
+
+unsigned
+queue::CountSamePriority(unsigned start_order, uint8_t priority) const
+{
+ assert(random);
+ assert(start_order <= length);
+
+ for (unsigned i = start_order; i < length; ++i) {
+ const unsigned position = OrderToPosition(i);
+ const Item *item = &items[position];
+ if (item->priority != priority)
+ return i - start_order;
+ }
+
+ return length - start_order;
+}
+
+bool
+queue::SetPriority(unsigned position, uint8_t priority, int after_order)
+{
+ assert(position < length);
+
+ Item *item = &items[position];
+ uint8_t old_priority = item->priority;
+ if (old_priority == priority)
+ return false;
+
+ item->version = version;
+ item->priority = priority;
+
+ if (!random)
+ /* don't reorder if not in random mode */
+ return true;
+
+ unsigned _order = PositionToOrder(position);
+ if (after_order >= 0) {
+ if (_order == (unsigned)after_order)
+ /* don't reorder the current song */
+ return true;
+
+ if (_order < (unsigned)after_order) {
+ /* the specified song has been played already
+ - enqueue it only if its priority has just
+ become bigger than the current one's */
+
+ const unsigned after_position =
+ OrderToPosition(after_order);
+ const Item *after_item =
+ &items[after_position];
+ if (old_priority > after_item->priority ||
+ priority <= after_item->priority)
+ /* priority hasn't become bigger */
+ return true;
+ }
+ }
+
+ /* move the item to the beginning of the priority group (or
+ create a new priority group) */
+
+ const unsigned before_order =
+ FindPriorityOrder(after_order + 1, priority, _order);
+ const unsigned new_order = before_order > _order
+ ? before_order - 1
+ : before_order;
+ MoveOrder(_order, new_order);
+
+ /* shuffle the song within that priority group */
+
+ const unsigned priority_count = CountSamePriority(new_order, priority);
+ assert(priority_count >= 1);
+ ShuffleOrderFirst(new_order, new_order + priority_count);
+
+ return true;
+}
+
+bool
+queue::SetPriorityRange(unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order)
+{
+ assert(start_position <= end_position);
+ assert(end_position <= length);
+
+ bool modified = false;
+ int after_position = after_order >= 0
+ ? (int)OrderToPosition(after_order)
+ : -1;
+ for (unsigned i = start_position; i < end_position; ++i) {
+ after_order = after_position >= 0
+ ? (int)PositionToOrder(after_position)
+ : -1;
+
+ modified |= SetPriority(i, priority, after_order);
+ }
+
+ return modified;
+}
diff --git a/src/Queue.hxx b/src/Queue.hxx
new file mode 100644
index 00000000..6e7786bc
--- /dev/null
+++ b/src/Queue.hxx
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_QUEUE_HXX
+#define MPD_QUEUE_HXX
+
+#include "gcc.h"
+#include "IdTable.hxx"
+#include "util/LazyRandomEngine.hxx"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <stdint.h>
+
+/**
+ * A queue of songs. This is the backend of the playlist: it contains
+ * an ordered list of songs.
+ *
+ * Songs can be addressed in three possible ways:
+ *
+ * - the position in the queue
+ * - the unique id (which stays the same, regardless of moves)
+ * - the order number (which only differs from "position" in random mode)
+ */
+struct queue {
+ /**
+ * reserve max_length * HASH_MULT elements in the id
+ * number space
+ */
+ static constexpr unsigned HASH_MULT = 4;
+
+ /**
+ * One element of the queue: basically a song plus some queue specific
+ * information attached.
+ */
+ struct Item {
+ struct song *song;
+
+ /** the unique id of this item in the queue */
+ unsigned id;
+
+ /** when was this item last changed? */
+ uint32_t version;
+
+ /**
+ * The priority of this item, between 0 and 255. High
+ * priority value means that this song gets played first in
+ * "random" mode.
+ */
+ uint8_t priority;
+ };
+
+ /** configured maximum length of the queue */
+ unsigned max_length;
+
+ /** number of songs in the queue */
+ unsigned length;
+
+ /** the current version number */
+ uint32_t version;
+
+ /** all songs in "position" order */
+ Item *items;
+
+ /** map order numbers to positions */
+ unsigned *order;
+
+ /** map song ids to positions */
+ IdTable id_table;
+
+ /** repeat playback when the end of the queue has been
+ reached? */
+ bool repeat;
+
+ /** play only current song. */
+ bool single;
+
+ /** remove each played files. */
+ bool consume;
+
+ /** play back songs in random order? */
+ bool random;
+
+ /** random number generator for shuffle and random mode */
+ LazyRandomEngine rand;
+
+ queue(unsigned max_length);
+
+ /**
+ * Deinitializes a queue object. It does not free the queue
+ * pointer itself.
+ */
+ ~queue();
+
+ queue(const queue &other) = delete;
+ queue &operator=(const queue &other) = delete;
+
+ unsigned GetLength() const {
+ assert(length <= max_length);
+
+ return length;
+ }
+
+ /**
+ * Determine if the queue is empty, i.e. there are no songs.
+ */
+ bool IsEmpty() const {
+ return length == 0;
+ }
+
+ /**
+ * Determine if the maximum number of songs has been reached.
+ */
+ bool IsFull() const {
+ assert(length <= max_length);
+
+ return length >= max_length;
+ }
+
+ /**
+ * Is that a valid position number?
+ */
+ bool IsValidPosition(unsigned position) const {
+ return position < length;
+ }
+
+ /**
+ * Is that a valid order number?
+ */
+ bool IsValidOrder(unsigned _order) const {
+ return _order < length;
+ }
+
+ int IdToPosition(unsigned id) const {
+ return id_table.IdToPosition(id);
+ }
+
+ int PositionToId(unsigned position) const
+ {
+ assert(position < length);
+
+ return items[position].id;
+ }
+
+ gcc_pure
+ unsigned OrderToPosition(unsigned _order) const {
+ assert(_order < length);
+
+ return order[_order];
+ }
+
+ gcc_pure
+ unsigned PositionToOrder(unsigned position) const {
+ assert(position < length);
+
+ for (unsigned i = 0;; ++i) {
+ assert(i < length);
+
+ if (order[i] == position)
+ return i;
+ }
+ }
+
+ gcc_pure
+ uint8_t GetPriorityAtPosition(unsigned position) const {
+ assert(position < length);
+
+ return items[position].priority;
+ }
+
+ const Item &GetOrderItem(unsigned i) const {
+ assert(IsValidOrder(i));
+
+ return items[OrderToPosition(i)];
+ }
+
+ uint8_t GetOrderPriority(unsigned i) const {
+ return GetOrderItem(i).priority;
+ }
+
+ /**
+ * Returns the song at the specified position.
+ */
+ struct song *Get(unsigned position) const {
+ assert(position < length);
+
+ return items[position].song;
+ }
+
+ /**
+ * Returns the song at the specified order number.
+ */
+ struct song *GetOrder(unsigned _order) const {
+ return Get(OrderToPosition(_order));
+ }
+
+ /**
+ * Is the song at the specified position newer than the specified
+ * version?
+ */
+ bool IsNewerAtPosition(unsigned position, uint32_t _version) const {
+ assert(position < length);
+
+ return _version > version ||
+ items[position].version >= _version ||
+ items[position].version == 0;
+ }
+
+ /**
+ * Returns the order number following the specified one. This takes
+ * end of queue and "repeat" mode into account.
+ *
+ * @return the next order number, or -1 to stop playback
+ */
+ gcc_pure
+ int GetNextOrder(unsigned order) const;
+
+ /**
+ * Increments the queue's version number. This handles integer
+ * overflow well.
+ */
+ void IncrementVersion();
+
+ /**
+ * Marks the specified song as "modified" and increments the version
+ * number.
+ */
+ void ModifyAtOrder(unsigned order);
+
+ /**
+ * Marks all songs as "modified" and increments the version number.
+ */
+ void ModifyAll();
+
+ /**
+ * Appends a song to the queue and returns its position. Prior to
+ * that, the caller must check if the queue is already full.
+ *
+ * If a song is not in the database (determined by
+ * song_in_database()), it is freed when removed from the queue.
+ *
+ * @param priority the priority of this new queue item
+ */
+ unsigned Append(struct song *song, uint8_t priority);
+
+ /**
+ * Swaps two songs, addressed by their position.
+ */
+ void SwapPositions(unsigned position1, unsigned position2);
+
+ /**
+ * Swaps two songs, addressed by their order number.
+ */
+ void SwapOrders(unsigned order1, unsigned order2) {
+ std::swap(order[order1], order[order2]);
+ }
+
+ /**
+ * Moves a song to a new position.
+ */
+ void MovePostion(unsigned from, unsigned to);
+
+ /**
+ * Moves a range of songs to a new position.
+ */
+ void MoveRange(unsigned start, unsigned end, unsigned to);
+
+ /**
+ * Removes a song from the playlist.
+ */
+ void DeletePosition(unsigned position);
+
+ /**
+ * Removes all songs from the playlist.
+ */
+ void Clear();
+
+ /**
+ * Initializes the "order" array, and restores "normal" order.
+ */
+ void RestoreOrder() {
+ for (unsigned i = 0; i < length; ++i)
+ order[i] = i;
+ }
+
+ /**
+ * Shuffle the order of items in the specified range, ignoring
+ * their priorities.
+ */
+ void ShuffleOrderRange(unsigned start, unsigned end);
+
+ /**
+ * Shuffle the order of items in the specified range, taking their
+ * priorities into account.
+ */
+ void ShuffleOrderRangeWithPriority(unsigned start, unsigned end);
+
+ /**
+ * Shuffles the virtual order of songs, but does not move them
+ * physically. This is used in random mode.
+ */
+ void ShuffleOrder();
+
+ void ShuffleOrderFirst(unsigned start, unsigned end);
+
+ /**
+ * Shuffles the virtual order of the last song in the specified
+ * (order) range. This is used in random mode after a song has been
+ * appended by queue_append().
+ */
+ void ShuffleOrderLast(unsigned start, unsigned end);
+
+ /**
+ * Shuffles a (position) range in the queue. The songs are physically
+ * shuffled, not by using the "order" mapping.
+ */
+ void ShuffleRange(unsigned start, unsigned end);
+
+ bool SetPriority(unsigned position, uint8_t priority, int after_order);
+
+ bool SetPriorityRange(unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order);
+
+private:
+ /**
+ * Moves a song to a new position in the "order" list.
+ */
+ void MoveOrder(unsigned from_order, unsigned to_order);
+
+ void MoveItemTo(unsigned from, unsigned to) {
+ unsigned from_id = items[from].id;
+
+ items[to] = items[from];
+ items[to].version = version;
+ id_table.Move(from_id, to);
+ }
+
+ /**
+ * Find the first item that has this specified priority or
+ * higher.
+ */
+ gcc_pure
+ unsigned FindPriorityOrder(unsigned start_order, uint8_t priority,
+ unsigned exclude_order) const;
+
+ gcc_pure
+ unsigned CountSamePriority(unsigned start_order,
+ uint8_t priority) const;
+};
+
+#endif
diff --git a/src/QueueCommands.cxx b/src/QueueCommands.cxx
new file mode 100644
index 00000000..4a3e1312
--- /dev/null
+++ b/src/QueueCommands.cxx
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "QueueCommands.hxx"
+#include "CommandError.hxx"
+#include "DatabaseQueue.hxx"
+#include "SongFilter.hxx"
+#include "DatabaseSelection.hxx"
+#include "Playlist.hxx"
+#include "PlaylistPrint.hxx"
+#include "ClientFile.hxx"
+#include "ClientInternal.hxx"
+#include "Partition.hxx"
+#include "protocol/ArgParser.hxx"
+#include "protocol/Result.hxx"
+#include "ls.hxx"
+
+extern "C" {
+#include "uri.h"
+}
+
+#include <string.h>
+
+enum command_return
+handle_add(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ char *uri = argv[1];
+ enum playlist_result result;
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ result = client->partition.AppendFile(path);
+ return print_playlist_result(client, result);
+ }
+
+ if (uri_has_scheme(uri)) {
+ if (!uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ result = client->partition.AppendURI(uri);
+ return print_playlist_result(client, result);
+ }
+
+ const DatabaseSelection selection(uri, true);
+ GError *error = NULL;
+ return AddFromDatabase(client->partition, selection, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
+}
+
+enum command_return
+handle_addid(Client *client, int argc, char *argv[])
+{
+ char *uri = argv[1];
+ unsigned added_id;
+ enum playlist_result result;
+
+ if (strncmp(uri, "file:///", 8) == 0) {
+ const char *path = uri + 7;
+
+ GError *error = NULL;
+ if (!client_allow_file(client, path, &error))
+ return print_error(client, error);
+
+ result = client->partition.AppendFile(path, &added_id);
+ } else {
+ if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "unsupported URI scheme");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ result = client->partition.AppendURI(uri, &added_id);
+ }
+
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+
+ if (argc == 3) {
+ unsigned to;
+ if (!check_unsigned(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ result = client->partition.MoveId(added_id, to);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ enum command_return ret =
+ print_playlist_result(client, result);
+ client->partition.DeleteId(added_id);
+ return ret;
+ }
+ }
+
+ client_printf(client, "Id: %u\n", added_id);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_delete(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned start, end;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.DeleteRange(start, end);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_deleteid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.DeleteId(id);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_playlist(Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ playlist_print_uris(client, &client->playlist);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_shuffle(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ unsigned start = 0, end = client->playlist.queue.GetLength();
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ client->partition.Shuffle(start, end);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_clear(G_GNUC_UNUSED Client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ client->partition.ClearQueue();
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_plchanges(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ uint32_t version;
+
+ if (!check_uint32(client, &version, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_print_changes_info(client, &client->playlist, version);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_plchangesposid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ uint32_t version;
+
+ if (!check_uint32(client, &version, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ playlist_print_changes_position(client, &client->playlist, version);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistinfo(Client *client, int argc, char *argv[])
+{
+ unsigned start = 0, end = G_MAXUINT;
+ bool ret;
+
+ if (argc == 2 && !check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ ret = playlist_print_info(client, &client->playlist, start, end);
+ if (!ret)
+ return print_playlist_result(client,
+ PLAYLIST_RESULT_BAD_RANGE);
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistid(Client *client, int argc, char *argv[])
+{
+ if (argc >= 2) {
+ unsigned id;
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ bool ret = playlist_print_id(client, &client->playlist, id);
+ if (!ret)
+ return print_playlist_result(client,
+ PLAYLIST_RESULT_NO_SUCH_SONG);
+ } else {
+ playlist_print_info(client, &client->playlist, 0, G_MAXUINT);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_playlist_match(Client *client, int argc, char *argv[],
+ bool fold_case)
+{
+ SongFilter filter;
+ if (!filter.Parse(argc - 1, argv + 1, fold_case)) {
+ command_error(client, ACK_ERROR_ARG, "incorrect arguments");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ playlist_print_find(client, &client->playlist, filter);
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_playlistfind(Client *client, int argc, char *argv[])
+{
+ return handle_playlist_match(client, argc, argv, false);
+}
+
+enum command_return
+handle_playlistsearch(Client *client, int argc, char *argv[])
+{
+ return handle_playlist_match(client, argc, argv, true);
+}
+
+enum command_return
+handle_prio(Client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned start_position, end_position;
+ if (!check_range(client, &start_position, &end_position,
+ argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SetPriorityRange(start_position,
+ end_position,
+ priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_prioid(Client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned song_id;
+ if (!check_unsigned(client, &song_id, argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SetPriorityId(song_id, priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+enum command_return
+handle_move(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned start, end;
+ int to;
+
+ if (!check_range(client, &start, &end, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.MoveRange(start, end, to);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_moveid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id;
+ int to;
+
+ if (!check_unsigned(client, &id, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_int(client, &to, argv[2]))
+ return COMMAND_RETURN_ERROR;
+ enum playlist_result result = client->partition.MoveId(id, to);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_swap(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned song1, song2;
+
+ if (!check_unsigned(client, &song1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &song2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ client->partition.SwapPositions(song1, song2);
+ return print_playlist_result(client, result);
+}
+
+enum command_return
+handle_swapid(Client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ unsigned id1, id2;
+
+ if (!check_unsigned(client, &id1, argv[1]))
+ return COMMAND_RETURN_ERROR;
+ if (!check_unsigned(client, &id2, argv[2]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result = client->partition.SwapIds(id1, id2);
+ return print_playlist_result(client, result);
+}
diff --git a/src/QueueCommands.hxx b/src/QueueCommands.hxx
new file mode 100644
index 00000000..97b61e21
--- /dev/null
+++ b/src/QueueCommands.hxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_QUEUE_COMMANDS_HXX
+#define MPD_QUEUE_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_add(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_addid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_delete(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_deleteid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlist(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_shuffle(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_clear(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_plchanges(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_plchangesposid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistinfo(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistfind(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_playlistsearch(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_prio(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_prioid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_move(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_moveid(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_swap(Client *client, int argc, char *argv[]);
+
+enum command_return
+handle_swapid(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/QueuePrint.cxx b/src/QueuePrint.cxx
new file mode 100644
index 00000000..28abc5a8
--- /dev/null
+++ b/src/QueuePrint.cxx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "QueuePrint.hxx"
+#include "Queue.hxx"
+#include "SongFilter.hxx"
+#include "SongPrint.hxx"
+#include "Mapper.hxx"
+#include "Client.hxx"
+
+extern "C" {
+#include "song.h"
+}
+
+/**
+ * Send detailed information about a range of songs in the queue to a
+ * client.
+ *
+ * @param client the client which has requested information
+ * @param start the index of the first song (including)
+ * @param end the index of the last song (excluding)
+ */
+static void
+queue_print_song_info(Client *client, const struct queue *queue,
+ unsigned position)
+{
+ song_print_info(client, queue->Get(position));
+ client_printf(client, "Pos: %u\nId: %u\n",
+ position, queue->PositionToId(position));
+
+ uint8_t priority = queue->GetPriorityAtPosition(position);
+ if (priority != 0)
+ client_printf(client, "Prio: %u\n", priority);
+}
+
+void
+queue_print_info(Client *client, const struct queue *queue,
+ unsigned start, unsigned end)
+{
+ assert(start <= end);
+ assert(end <= queue->GetLength());
+
+ for (unsigned i = start; i < end; ++i)
+ queue_print_song_info(client, queue, i);
+}
+
+void
+queue_print_uris(Client *client, const struct queue *queue,
+ unsigned start, unsigned end)
+{
+ assert(start <= end);
+ assert(end <= queue->GetLength());
+
+ for (unsigned i = start; i < end; ++i) {
+ client_printf(client, "%i:", i);
+ song_print_uri(client, queue->Get(i));
+ }
+}
+
+void
+queue_print_changes_info(Client *client, const struct queue *queue,
+ uint32_t version)
+{
+ for (unsigned i = 0; i < queue->GetLength(); i++) {
+ if (queue->IsNewerAtPosition(i, version))
+ queue_print_song_info(client, queue, i);
+ }
+}
+
+void
+queue_print_changes_position(Client *client, const struct queue *queue,
+ uint32_t version)
+{
+ for (unsigned i = 0; i < queue->GetLength(); i++)
+ if (queue->IsNewerAtPosition(i, version))
+ client_printf(client, "cpos: %i\nId: %i\n",
+ i, queue->PositionToId(i));
+}
+
+void
+queue_find(Client *client, const struct queue *queue,
+ const SongFilter &filter)
+{
+ for (unsigned i = 0; i < queue->GetLength(); i++) {
+ const struct song *song = queue->Get(i);
+
+ if (filter.Match(*song))
+ queue_print_song_info(client, queue, i);
+ }
+}
diff --git a/src/queue_print.h b/src/QueuePrint.hxx
index 371e2041..6b3a29fb 100644
--- a/src/queue_print.h
+++ b/src/QueuePrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,37 +22,33 @@
* client.
*/
-#ifndef QUEUE_PRINT_H
-#define QUEUE_PRINT_H
+#ifndef MPD_QUEUE_PRINT_HXX
+#define MPD_QUEUE_PRINT_HXX
#include <stdint.h>
-struct client;
struct queue;
-struct locate_item_list;
+class SongFilter;
+class Client;
void
-queue_print_info(struct client *client, const struct queue *queue,
+queue_print_info(Client *client, const struct queue *queue,
unsigned start, unsigned end);
void
-queue_print_uris(struct client *client, const struct queue *queue,
+queue_print_uris(Client *client, const struct queue *queue,
unsigned start, unsigned end);
void
-queue_print_changes_info(struct client *client, const struct queue *queue,
+queue_print_changes_info(Client *client, const struct queue *queue,
uint32_t version);
void
-queue_print_changes_position(struct client *client, const struct queue *queue,
+queue_print_changes_position(Client *client, const struct queue *queue,
uint32_t version);
void
-queue_search(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria);
-
-void
-queue_find(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria);
+queue_find(Client *client, const struct queue *queue,
+ const SongFilter &filter);
#endif
diff --git a/src/queue_save.c b/src/QueueSave.cxx
index 16852d3c..09b0645f 100644
--- a/src/queue_save.c
+++ b/src/QueueSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,17 @@
*/
#include "config.h"
-#include "queue_save.h"
-#include "queue.h"
+#include "QueueSave.hxx"
+#include "Playlist.hxx"
#include "song.h"
+#include "SongSave.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "TextFile.hxx"
+
+extern "C" {
#include "uri.h"
-#include "database.h"
-#include "song_save.h"
-#include "text_file.h"
+}
#include <stdlib.h>
@@ -57,48 +61,40 @@ queue_save_song(FILE *fp, int idx, const struct song *song)
void
queue_save(FILE *fp, const struct queue *queue)
{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- uint8_t prio = queue_get_priority_at_position(queue, i);
+ for (unsigned i = 0; i < queue->GetLength(); i++) {
+ uint8_t prio = queue->GetPriorityAtPosition(i);
if (prio != 0)
fprintf(fp, PRIO_LABEL "%u\n", prio);
- queue_save_song(fp, i, queue_get(queue, i));
+ queue_save_song(fp, i, queue->Get(i));
}
}
-static struct song *
-get_song(const char *uri)
-{
- return uri_has_scheme(uri)
- ? song_remote_new(uri)
- : db_get_song(uri);
-}
-
void
-queue_load_song(FILE *fp, GString *buffer, const char *line,
- struct queue *queue)
+queue_load_song(TextFile &file, const char *line, queue *queue)
{
- struct song *song;
-
- if (queue_is_full(queue))
+ if (queue->IsFull())
return;
uint8_t priority = 0;
if (g_str_has_prefix(line, PRIO_LABEL)) {
priority = strtoul(line + sizeof(PRIO_LABEL) - 1, NULL, 10);
- line = read_text_line(fp, buffer);
+ line = file.ReadLine();
if (line == NULL)
return;
}
+ const Database *db = nullptr;
+ struct song *song;
+
if (g_str_has_prefix(line, SONG_BEGIN)) {
const char *uri = line + sizeof(SONG_BEGIN) - 1;
if (!uri_has_scheme(uri) && !g_path_is_absolute(uri))
return;
GError *error = NULL;
- song = song_load(fp, NULL, uri, buffer, &error);
+ song = song_load(file, NULL, uri, &error);
if (song == NULL) {
g_warning("%s", error->message);
g_error_free(error);
@@ -112,12 +108,23 @@ queue_load_song(FILE *fp, GString *buffer, const char *line,
return;
}
- line = endptr + 1;
+ const char *uri = endptr + 1;
- song = get_song(line);
- if (song == NULL)
- return;
+ if (uri_has_scheme(uri)) {
+ song = song_remote_new(uri);
+ } else {
+ db = GetDatabase(nullptr);
+ if (db == nullptr)
+ return;
+
+ song = db->GetSong(uri, nullptr);
+ if (song == nullptr)
+ return;
+ }
}
- queue_append(queue, song, priority);
+ queue->Append(song, priority);
+
+ if (db != nullptr)
+ db->ReturnSong(song);
}
diff --git a/src/queue_save.h b/src/QueueSave.hxx
index 5526d615..25d7804f 100644
--- a/src/queue_save.h
+++ b/src/QueueSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,13 +22,13 @@
* back into memory.
*/
-#ifndef QUEUE_SAVE_H
-#define QUEUE_SAVE_H
+#ifndef MPD_QUEUE_SAVE_HXX
+#define MPD_QUEUE_SAVE_HXX
-#include <glib.h>
#include <stdio.h>
struct queue;
+class TextFile;
void
queue_save(FILE *fp, const struct queue *queue);
@@ -37,7 +37,6 @@ queue_save(FILE *fp, const struct queue *queue);
* Loads one song from the state file and appends it to the queue.
*/
void
-queue_load_song(FILE *fp, GString *buffer, const char *line,
- struct queue *queue);
+queue_load_song(TextFile &file, const char *line, queue *queue);
#endif
diff --git a/src/replay_gain_config.c b/src/ReplayGainConfig.cxx
index 2181387b..d86c7005 100644
--- a/src/replay_gain_config.c
+++ b/src/ReplayGainConfig.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,9 +19,9 @@
#include "config.h"
#include "replay_gain_config.h"
-#include "playlist.h"
+#include "Idle.hxx"
#include "conf.h"
-#include "idle.h"
+#include "Playlist.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -29,20 +29,14 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
-#include <math.h>
-
-static const char *const replay_gain_mode_names[] = {
- [REPLAY_GAIN_ALBUM] = "album",
- [REPLAY_GAIN_TRACK] = "track",
-};
enum replay_gain_mode replay_gain_mode = REPLAY_GAIN_OFF;
-const bool DEFAULT_REPLAYGAIN_LIMIT = true;
+static constexpr bool DEFAULT_REPLAYGAIN_LIMIT = true;
float replay_gain_preamp = 1.0;
float replay_gain_missing_preamp = 1.0;
-bool replay_gain_limit;
+bool replay_gain_limit = DEFAULT_REPLAYGAIN_LIMIT;
const char *
replay_gain_get_mode_string(void)
@@ -137,14 +131,15 @@ void replay_gain_global_init(void)
replay_gain_limit = config_get_bool(CONF_REPLAYGAIN_LIMIT, DEFAULT_REPLAYGAIN_LIMIT);
}
-enum replay_gain_mode replay_gain_get_real_mode(void)
+enum replay_gain_mode
+replay_gain_get_real_mode(bool random_mode)
{
enum replay_gain_mode rgm;
rgm = replay_gain_mode;
if (rgm == REPLAY_GAIN_AUTO)
- rgm = g_playlist.queue.random ? REPLAY_GAIN_TRACK : REPLAY_GAIN_ALBUM;
+ rgm = random_mode ? REPLAY_GAIN_TRACK : REPLAY_GAIN_ALBUM;
return rgm;
}
diff --git a/src/replay_gain_info.c b/src/ReplayGainInfo.cxx
index 1f09e7a1..b9d1b82c 100644
--- a/src/replay_gain_info.c
+++ b/src/ReplayGainInfo.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/sig_handlers.c b/src/SignalHandlers.cxx
index b23f9e77..d438eb70 100644
--- a/src/sig_handlers.c
+++ b/src/SignalHandlers.cxx
@@ -18,13 +18,14 @@
*/
#include "config.h"
-#include "sig_handlers.h"
+#include "SignalHandlers.hxx"
#ifndef WIN32
-#include "log.h"
-#include "main.h"
-#include "event_pipe.h"
+#include "Log.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
+#include "GlobalEvents.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -35,12 +36,12 @@
static void exit_signal_handler(G_GNUC_UNUSED int signum)
{
- g_main_loop_quit(main_loop);
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
}
static void reload_signal_handler(G_GNUC_UNUSED int signum)
{
- event_pipe_emit_fast(PIPE_EVENT_RELOAD);
+ GlobalEvents::Emit(GlobalEvents::RELOAD);
}
static void
@@ -73,7 +74,7 @@ void initSigHandlers(void)
x_sigaction(SIGINT, &sa);
x_sigaction(SIGTERM, &sa);
- event_pipe_register(PIPE_EVENT_RELOAD, handle_reload_event);
+ GlobalEvents::Register(GlobalEvents::RELOAD, handle_reload_event);
sa.sa_handler = reload_signal_handler;
x_sigaction(SIGHUP, &sa);
#endif
diff --git a/src/SignalHandlers.hxx b/src/SignalHandlers.hxx
new file mode 100644
index 00000000..99f347fb
--- /dev/null
+++ b/src/SignalHandlers.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SIGNAL_HANDLERS_HXX
+#define MPD_SIGNAL_HANDLERS_HXX
+
+void initSigHandlers(void);
+
+#endif
diff --git a/src/SocketError.hxx b/src/SocketError.hxx
new file mode 100644
index 00000000..53a0c1d8
--- /dev/null
+++ b/src/SocketError.hxx
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SOCKET_ERROR_HXX
+#define MPD_SOCKET_ERROR_HXX
+
+#include "gcc.h"
+
+#include <glib.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+typedef DWORD socket_error_t;
+#else
+#include <errno.h>
+typedef int socket_error_t;
+#endif
+
+/**
+ * A GQuark for GError for socket I/O errors. The code is an errno
+ * value (or WSAGetLastError() on Windows).
+ */
+gcc_const
+static inline GQuark
+SocketErrorQuark(void)
+{
+ return g_quark_from_static_string("socket");
+}
+
+gcc_pure
+static inline socket_error_t
+GetSocketError()
+{
+#ifdef WIN32
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorAgain(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAEINPROGRESS;
+#else
+ return code == EAGAIN;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorInterruped(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAEINTR;
+#else
+ return code == EINTR;
+#endif
+}
+
+gcc_const
+static inline bool
+IsSocketErrorClosed(socket_error_t code)
+{
+#ifdef WIN32
+ return code == WSAECONNRESET;
+#else
+ return code == EPIPE || code == ECONNRESET;
+#endif
+}
+
+/**
+ * Helper class that formats a socket error message into a
+ * human-readable string. On Windows, a buffer is necessary for this,
+ * and this class hosts the buffer.
+ */
+class SocketErrorMessage {
+#ifdef WIN32
+ char msg[256];
+#else
+ const char *const msg;
+#endif
+
+public:
+#ifdef WIN32
+ explicit SocketErrorMessage(socket_error_t code=GetSocketError()) {
+ DWORD nbytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, code, 0,
+ (LPSTR)msg, sizeof(msg), NULL);
+ if (nbytes == 0)
+ strcpy(msg, "Unknown error");
+ }
+#else
+ explicit SocketErrorMessage(socket_error_t code=GetSocketError())
+ :msg(g_strerror(code)) {}
+#endif
+
+ operator const char *() const {
+ return msg;
+ }
+};
+
+static inline void
+SetSocketError(GError **error_r, socket_error_t code)
+{
+#ifdef WIN32
+ if (error_r == NULL)
+ return;
+#endif
+
+ const SocketErrorMessage msg(code);
+ g_set_error_literal(error_r, SocketErrorQuark(), code, msg);
+}
+
+static inline void
+SetSocketError(GError **error_r)
+{
+ SetSocketError(error_r, GetSocketError());
+}
+
+gcc_malloc
+static inline GError *
+NewSocketError(socket_error_t code)
+{
+ const SocketErrorMessage msg(code);
+ return g_error_new_literal(SocketErrorQuark(), code, msg);
+}
+
+gcc_malloc
+static inline GError *
+NewSocketError()
+{
+ return NewSocketError(GetSocketError());
+}
+
+#endif
diff --git a/src/socket_util.c b/src/SocketUtil.cxx
index a06a0cbd..dd7eb7dd 100644
--- a/src/socket_util.c
+++ b/src/SocketUtil.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,12 @@
*/
#include "config.h"
-#include "socket_util.h"
+#include "SocketUtil.hxx"
+#include "SocketError.hxx"
#include "fd_util.h"
-#include <errno.h>
+#include <glib.h>
+
#include <unistd.h>
#ifndef G_OS_WIN32
@@ -35,49 +37,42 @@
#include <string.h>
#endif
-static GQuark
-listen_quark(void)
-{
- return g_quark_from_static_string("listen");
-}
-
int
socket_bind_listen(int domain, int type, int protocol,
const struct sockaddr *address, size_t address_length,
int backlog,
- GError **error)
+ GError **error_r)
{
int fd, ret;
const int reuse = 1;
fd = socket_cloexec_nonblock(domain, type, protocol);
if (fd < 0) {
- g_set_error(error, listen_quark(), errno,
- "Failed to create socket: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "Failed to create socket: ");
return -1;
}
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(const char *) &reuse, sizeof(reuse));
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "setsockopt() failed: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "setsockopt() failed: ");
close_socket(fd);
return -1;
}
ret = bind(fd, address, address_length);
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "%s", g_strerror(errno));
+ SetSocketError(error_r);
close_socket(fd);
return -1;
}
ret = listen(fd, backlog);
if (ret < 0) {
- g_set_error(error, listen_quark(), errno,
- "listen() failed: %s", g_strerror(errno));
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "listen() failed: ");
close_socket(fd);
return -1;
}
diff --git a/src/socket_util.h b/src/SocketUtil.hxx
index 93bd2736..3d0be78c 100644
--- a/src/socket_util.h
+++ b/src/SocketUtil.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,10 +23,12 @@
*
*/
-#ifndef SOCKET_UTIL_H
-#define SOCKET_UTIL_H
+#ifndef MPD_SOCKET_UTIL_HXX
+#define MPD_SOCKET_UTIL_HXX
-#include <glib.h>
+#include "gerror.h"
+
+#include <stddef.h>
struct sockaddr;
diff --git a/src/song.c b/src/Song.cxx
index f5cc7c35..4c820c3f 100644
--- a/src/song.c
+++ b/src/Song.cxx
@@ -19,26 +19,28 @@
#include "config.h"
#include "song.h"
-#include "uri.h"
-#include "directory.h"
+#include "Directory.hxx"
#include "tag.h"
#include <glib.h>
#include <assert.h>
+Directory detached_root;
+
static struct song *
-song_alloc(const char *uri, struct directory *parent)
+song_alloc(const char *uri, Directory *parent)
{
size_t uri_length;
- struct song *song;
assert(uri);
uri_length = strlen(uri);
assert(uri_length);
- song = g_malloc(sizeof(*song) - sizeof(song->uri) + uri_length + 1);
- song->tag = NULL;
+ struct song *song = (struct song *)
+ g_malloc(sizeof(*song) - sizeof(song->uri) + uri_length + 1);
+
+ song->tag = nullptr;
memcpy(song->uri, uri, uri_length + 1);
song->parent = parent;
song->mtime = 0;
@@ -50,13 +52,13 @@ song_alloc(const char *uri, struct directory *parent)
struct song *
song_remote_new(const char *uri)
{
- return song_alloc(uri, NULL);
+ return song_alloc(uri, nullptr);
}
struct song *
-song_file_new(const char *path, struct directory *parent)
+song_file_new(const char *path, Directory *parent)
{
- assert((parent == NULL) == (*path == '/'));
+ assert((parent == nullptr) == (*path == '/'));
return song_alloc(path, parent);
}
@@ -73,6 +75,35 @@ song_replace_uri(struct song *old_song, const char *uri)
return new_song;
}
+struct song *
+song_detached_new(const char *uri)
+{
+ assert(uri != nullptr);
+
+ return song_alloc(uri, &detached_root);
+}
+
+struct song *
+song_dup_detached(const struct song *src)
+{
+ assert(src != nullptr);
+
+ struct song *song;
+ if (song_in_database(src)) {
+ char *uri = song_get_uri(src);
+ song = song_detached_new(uri);
+ g_free(uri);
+ } else
+ song = song_alloc(src->uri, nullptr);
+
+ song->tag = tag_dup(src->tag);
+ song->mtime = src->mtime;
+ song->start_ms = src->start_ms;
+ song->end_ms = src->end_ms;
+
+ return song;
+}
+
void
song_free(struct song *song)
{
@@ -81,17 +112,57 @@ song_free(struct song *song)
g_free(song);
}
+gcc_pure
+static inline bool
+directory_equals(const Directory &a, const Directory &b)
+{
+ return strcmp(a.path, b.path) == 0;
+}
+
+gcc_pure
+static inline bool
+directory_is_same(const Directory *a, const Directory *b)
+{
+ return a == b ||
+ (a != nullptr && b != nullptr &&
+ directory_equals(*a, *b));
+
+}
+
+bool
+song_equals(const struct song *a, const struct song *b)
+{
+ assert(a != nullptr);
+ assert(b != nullptr);
+
+ if (a->parent != nullptr && b->parent != nullptr &&
+ !directory_equals(*a->parent, *b->parent) &&
+ (a->parent == &detached_root || b->parent == &detached_root)) {
+ /* must compare the full URI if one of the objects is
+ "detached" */
+ char *au = song_get_uri(a);
+ char *bu = song_get_uri(b);
+ const bool result = strcmp(au, bu) == 0;
+ g_free(bu);
+ g_free(au);
+ return result;
+ }
+
+ return directory_is_same(a->parent, b->parent) &&
+ strcmp(a->uri, b->uri) == 0;
+}
+
char *
song_get_uri(const struct song *song)
{
- assert(song != NULL);
+ assert(song != nullptr);
assert(*song->uri);
- if (!song_in_database(song) || directory_is_root(song->parent))
+ if (!song_in_database(song) || song->parent->IsRoot())
return g_strdup(song->uri);
else
- return g_strconcat(directory_get_path(song->parent),
- "/", song->uri, NULL);
+ return g_strconcat(song->parent->GetPath(),
+ "/", song->uri, nullptr);
}
double
@@ -100,7 +171,7 @@ song_get_duration(const struct song *song)
if (song->end_ms > 0)
return (song->end_ms - song->start_ms) / 1000.0;
- if (song->tag == NULL)
+ if (song->tag == nullptr)
return 0;
return song->tag->time - song->start_ms / 1000.0;
diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx
new file mode 100644
index 00000000..0e138386
--- /dev/null
+++ b/src/SongFilter.cxx
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "SongFilter.hxx"
+#include "song.h"
+#include "tag.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#define LOCATE_TAG_FILE_KEY "file"
+#define LOCATE_TAG_FILE_KEY_OLD "filename"
+#define LOCATE_TAG_ANY_KEY "any"
+
+unsigned
+locate_parse_type(const char *str)
+{
+ if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY) ||
+ 0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD))
+ return LOCATE_TAG_FILE_TYPE;
+
+ if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_ANY_KEY))
+ return LOCATE_TAG_ANY_TYPE;
+
+ return tag_name_parse_i(str);
+}
+
+SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
+ :tag(_tag), fold_case(_fold_case),
+ value(fold_case
+ ? g_utf8_casefold(_value, -1)
+ : g_strdup(_value))
+{
+}
+
+SongFilter::Item::~Item()
+{
+ g_free(value);
+}
+
+bool
+SongFilter::Item::StringMatch(const char *s) const
+{
+ assert(value != nullptr);
+ assert(s != nullptr);
+
+ if (fold_case) {
+ char *p = g_utf8_casefold(s, -1);
+ const bool result = strstr(p, value) != NULL;
+ g_free(p);
+ return result;
+ } else {
+ return strcmp(s, value) == 0;
+ }
+}
+
+bool
+SongFilter::Item::Match(const tag_item &item) const
+{
+ return (tag == LOCATE_TAG_ANY_TYPE || (unsigned)item.type == tag) &&
+ StringMatch(item.value);
+}
+
+bool
+SongFilter::Item::Match(const struct tag &_tag) const
+{
+ bool visited_types[TAG_NUM_OF_ITEM_TYPES];
+ std::fill(visited_types, visited_types + TAG_NUM_OF_ITEM_TYPES, false);
+
+ for (unsigned i = 0; i < _tag.num_items; i++) {
+ visited_types[_tag.items[i]->type] = true;
+
+ if (Match(*_tag.items[i]))
+ return true;
+ }
+
+ /** If the search critieron was not visited during the sweep
+ * through the song's tag, it means this field is absent from
+ * the tag or empty. Thus, if the searched string is also
+ * empty (first char is a \0), then it's a match as well and
+ * we should return true.
+ */
+ if (*value == 0 && tag < TAG_NUM_OF_ITEM_TYPES &&
+ !visited_types[tag])
+ return true;
+
+ return false;
+}
+
+bool
+SongFilter::Item::Match(const song &song) const
+{
+ if (tag == LOCATE_TAG_FILE_TYPE || tag == LOCATE_TAG_ANY_TYPE) {
+ char *uri = song_get_uri(&song);
+ const bool result = StringMatch(uri);
+ g_free(uri);
+
+ if (result || tag == LOCATE_TAG_FILE_TYPE)
+ return result;
+ }
+
+ return song.tag != NULL && Match(*song.tag);
+}
+
+SongFilter::SongFilter(unsigned tag, const char *value, bool fold_case)
+{
+ items.push_back(Item(tag, value, fold_case));
+}
+
+SongFilter::~SongFilter()
+{
+ /* this destructor exists here just so it won't get inlined */
+}
+
+bool
+SongFilter::Parse(const char *tag_string, const char *value, bool fold_case)
+{
+ unsigned tag = locate_parse_type(tag_string);
+ if (tag == TAG_NUM_OF_ITEM_TYPES)
+ return false;
+
+ items.push_back(Item(tag, value, fold_case));
+ return true;
+}
+
+bool
+SongFilter::Parse(unsigned argc, char *argv[], bool fold_case)
+{
+ if (argc == 0 || argc % 2 != 0)
+ return false;
+
+ for (unsigned i = 0; i < argc; i += 2)
+ if (!Parse(argv[i], argv[i + 1], fold_case))
+ return false;
+
+ return true;
+}
+
+bool
+SongFilter::Match(const song &song) const
+{
+ for (const auto &i : items)
+ if (!i.Match(song))
+ return false;
+
+ return true;
+}
diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx
new file mode 100644
index 00000000..afec8130
--- /dev/null
+++ b/src/SongFilter.hxx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SONG_FILTER_HXX
+#define MPD_SONG_FILTER_HXX
+
+#include "gcc.h"
+
+#include <list>
+
+#include <stdint.h>
+
+#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
+#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
+
+struct tag;
+struct tag_item;
+struct song;
+
+class SongFilter {
+ class Item {
+ uint8_t tag;
+
+ bool fold_case;
+
+ char *value;
+
+ public:
+ gcc_nonnull(3)
+ Item(unsigned tag, const char *value, bool fold_case=false);
+
+ Item(const Item &other) = delete;
+
+ Item(Item &&other)
+ :tag(other.tag), fold_case(other.fold_case),
+ value(other.value) {
+ other.value = nullptr;
+ }
+
+ ~Item();
+
+ Item &operator=(const Item &other) = delete;
+
+ unsigned GetTag() const {
+ return tag;
+ }
+
+ gcc_pure gcc_nonnull(2)
+ bool StringMatch(const char *s) const;
+
+ gcc_pure
+ bool Match(const tag_item &tag_item) const;
+
+ gcc_pure
+ bool Match(const struct tag &tag) const;
+
+ gcc_pure
+ bool Match(const song &song) const;
+ };
+
+ std::list<Item> items;
+
+public:
+ SongFilter() = default;
+
+ gcc_nonnull(3)
+ SongFilter(unsigned tag, const char *value, bool fold_case=false);
+
+ ~SongFilter();
+
+ gcc_nonnull(2,3)
+ bool Parse(const char *tag, const char *value, bool fold_case=false);
+
+ gcc_nonnull(3)
+ bool Parse(unsigned argc, char *argv[], bool fold_case=false);
+
+ gcc_pure
+ bool Match(const tag &tag) const;
+
+ gcc_pure
+ bool Match(const song &song) const;
+};
+
+/**
+ * @return #TAG_NUM_OF_ITEM_TYPES on error
+ */
+gcc_pure
+unsigned
+locate_parse_type(const char *str);
+
+#endif
diff --git a/src/notify.h b/src/SongPointer.hxx
index 40821690..c80f96f4 100644
--- a/src/notify.h
+++ b/src/SongPointer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,37 +17,47 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_NOTIFY_H
-#define MPD_NOTIFY_H
+#ifndef MPD_SONG_POINTER_HXX
+#define MPD_SONG_POINTER_HXX
-#include <glib.h>
+#include "song.h"
-#include <stdbool.h>
+#include <utility>
-struct notify {
- GMutex *mutex;
- GCond *cond;
- bool pending;
-};
+class SongPointer {
+ struct song *song;
-void notify_init(struct notify *notify);
+public:
+ explicit SongPointer(struct song *_song)
+ :song(_song) {}
-void notify_deinit(struct notify *notify);
+ SongPointer(const SongPointer &) = delete;
-/**
- * Wait for a notification. Return immediately if we have already
- * been notified since we last returned from notify_wait().
- */
-void notify_wait(struct notify *notify);
+ SongPointer(SongPointer &&other):song(other.song) {
+ other.song = nullptr;
+ }
-/**
- * Notify the thread. This function never blocks.
- */
-void notify_signal(struct notify *notify);
+ ~SongPointer() {
+ if (song != nullptr)
+ song_free(song);
+ }
-/**
- * Clears a pending notification.
- */
-void notify_clear(struct notify *notify);
+ SongPointer &operator=(const SongPointer &) = delete;
+
+ SongPointer &operator=(SongPointer &&other) {
+ std::swap(song, other.song);
+ return *this;
+ }
+
+ operator const struct song *() const {
+ return song;
+ }
+
+ struct song *Steal() {
+ auto result = song;
+ song = nullptr;
+ return result;
+ }
+};
#endif
diff --git a/src/song_print.c b/src/SongPrint.cxx
index fb608a8b..126e5b9f 100644
--- a/src/song_print.c
+++ b/src/SongPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,26 @@
*/
#include "config.h"
-#include "song_print.h"
+#include "SongPrint.hxx"
#include "song.h"
-#include "directory.h"
-#include "tag_print.h"
-#include "client.h"
+#include "Directory.hxx"
+#include "TimePrint.hxx"
+#include "TagPrint.hxx"
+#include "Mapper.hxx"
+#include "Client.hxx"
+
+extern "C" {
#include "uri.h"
-#include "mapper.h"
+}
+
+#include <glib.h>
void
-song_print_uri(struct client *client, struct song *song)
+song_print_uri(Client *client, struct song *song)
{
- if (song_in_database(song) && !directory_is_root(song->parent)) {
+ if (song_in_database(song) && !song->parent->IsRoot()) {
client_printf(client, "%s%s/%s\n", SONG_FILE,
- directory_get_path(song->parent), song->uri);
+ song->parent->GetPath(), song->uri);
} else {
char *allocated;
const char *uri;
@@ -48,7 +54,7 @@ song_print_uri(struct client *client, struct song *song)
}
void
-song_print_info(struct client *client, struct song *song)
+song_print_info(Client *client, struct song *song)
{
song_print_uri(client, song);
@@ -63,32 +69,8 @@ song_print_info(struct client *client, struct song *song)
song->start_ms / 1000,
song->start_ms % 1000);
- if (song->mtime > 0) {
-#ifndef G_OS_WIN32
- struct tm tm;
-#endif
- const struct tm *tm2;
-
-#ifdef G_OS_WIN32
- tm2 = gmtime(&song->mtime);
-#else
- tm2 = gmtime_r(&song->mtime, &tm);
-#endif
-
- if (tm2 != NULL) {
- char timestamp[32];
-
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
-#else
- "%FT%TZ",
-#endif
- tm2);
- client_printf(client, "Last-Modified: %s\n",
- timestamp);
- }
- }
+ if (song->mtime > 0)
+ time_print(client, "Last-Modified", song->mtime);
if (song->tag)
tag_print(client, song->tag);
diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx
new file mode 100644
index 00000000..49f9478b
--- /dev/null
+++ b/src/SongPrint.hxx
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SONG_PRINT_HXX
+#define MPD_SONG_PRINT_HXX
+
+struct song;
+class Client;
+
+void
+song_print_info(Client *client, struct song *song);
+
+void
+song_print_uri(Client *client, struct song *song);
+
+#endif
diff --git a/src/song_save.c b/src/SongSave.cxx
index 4fcb46e2..2b74d935 100644
--- a/src/song_save.c
+++ b/src/SongSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,16 @@
*/
#include "config.h"
-#include "song_save.h"
+#include "SongSave.hxx"
#include "song.h"
-#include "tag_save.h"
-#include "directory.h"
+#include "TagSave.hxx"
+#include "Directory.hxx"
+#include "TextFile.hxx"
#include "tag.h"
-#include "text_file.h"
+
+extern "C" {
#include "string_util.h"
+}
#include <glib.h>
@@ -60,8 +63,8 @@ song_save(FILE *fp, const struct song *song)
}
struct song *
-song_load(FILE *fp, struct directory *parent, const char *uri,
- GString *buffer, GError **error_r)
+song_load(TextFile &file, Directory *parent, const char *uri,
+ GError **error_r)
{
struct song *song = parent != NULL
? song_file_new(uri, parent)
@@ -70,7 +73,7 @@ song_load(FILE *fp, struct directory *parent, const char *uri,
enum tag_type type;
const char *value;
- while ((line = read_text_line(fp, buffer)) != NULL &&
+ while ((line = file.ReadLine()) != NULL &&
strcmp(line, SONG_END) != 0) {
colon = strchr(line, ':');
if (colon == NULL || colon == line) {
diff --git a/src/song_save.h b/src/SongSave.hxx
index f6ecbbfe..3b0c3319 100644
--- a/src/song_save.h
+++ b/src/SongSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,17 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SONG_SAVE_H
-#define MPD_SONG_SAVE_H
+#ifndef MPD_SONG_SAVE_HXX
+#define MPD_SONG_SAVE_HXX
-#include <glib.h>
+#include "gerror.h"
#include <stdio.h>
#define SONG_BEGIN "song_begin: "
struct song;
-struct directory;
+struct Directory;
+class TextFile;
void
song_save(FILE *fp, const struct song *song);
@@ -41,7 +42,7 @@ song_save(FILE *fp, const struct song *song);
* @return true on success, false on error
*/
struct song *
-song_load(FILE *fp, struct directory *parent, const char *uri,
- GString *buffer, GError **error_r);
+song_load(TextFile &file, Directory *parent, const char *uri,
+ GError **error_r);
#endif
diff --git a/src/song_sticker.c b/src/SongSticker.cxx
index 78025906..86385fc3 100644
--- a/src/song_sticker.c
+++ b/src/SongSticker.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,10 @@
*/
#include "config.h"
-#include "song_sticker.h"
+#include "SongSticker.hxx"
+#include "StickerDatabase.hxx"
#include "song.h"
-#include "directory.h"
-#include "sticker.h"
+#include "Directory.hxx"
#include <glib.h>
@@ -109,46 +109,43 @@ sticker_song_get(const struct song *song)
}
struct sticker_song_find_data {
- struct directory *directory;
+ Directory *directory;
const char *base_uri;
size_t base_uri_length;
void (*func)(struct song *song, const char *value,
- gpointer user_data);
- gpointer user_data;
+ void *user_data);
+ void *user_data;
};
static void
-sticker_song_find_cb(const char *uri, const char *value, gpointer user_data)
+sticker_song_find_cb(const char *uri, const char *value, void *user_data)
{
- struct sticker_song_find_data *data = user_data;
- struct song *song;
+ struct sticker_song_find_data *data =
+ (struct sticker_song_find_data *)user_data;
if (memcmp(uri, data->base_uri, data->base_uri_length) != 0)
/* should not happen, ignore silently */
return;
- song = directory_lookup_song(data->directory,
- uri + data->base_uri_length);
+ song *song = data->directory->LookupSong(uri + data->base_uri_length);
if (song != NULL)
data->func(song, value, data->user_data);
}
bool
-sticker_song_find(struct directory *directory, const char *name,
+sticker_song_find(Directory *directory, const char *name,
void (*func)(struct song *song, const char *value,
- gpointer user_data),
- gpointer user_data)
+ void *user_data),
+ void *user_data)
{
- struct sticker_song_find_data data = {
- .directory = directory,
- .func = func,
- .user_data = user_data,
- };
- char *allocated;
- bool success;
+ struct sticker_song_find_data data;
+ data.directory = directory;
+ data.func = func;
+ data.user_data = user_data;
- data.base_uri = directory_get_path(directory);
+ char *allocated;
+ data.base_uri = directory->GetPath();
if (*data.base_uri != 0)
/* append slash to base_uri */
data.base_uri = allocated =
@@ -159,8 +156,8 @@ sticker_song_find(struct directory *directory, const char *name,
data.base_uri_length = strlen(data.base_uri);
- success = sticker_find("song", data.base_uri, name,
- sticker_song_find_cb, &data);
+ bool success = sticker_find("song", data.base_uri, name,
+ sticker_song_find_cb, &data);
g_free(allocated);
return success;
diff --git a/src/song_sticker.h b/src/SongSticker.hxx
index 20ae68ce..07539b40 100644
--- a/src/song_sticker.h
+++ b/src/SongSticker.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef SONG_STICKER_H
-#define SONG_STICKER_H
+#ifndef MPD_SONG_STICKER_HXX
+#define MPD_SONG_STICKER_HXX
-#include <stdbool.h>
-#include <glib.h>
+#include "gerror.h"
struct song;
-struct directory;
+struct Directory;
struct sticker;
/**
@@ -76,9 +75,9 @@ sticker_song_get(const struct song *song);
* failure
*/
bool
-sticker_song_find(struct directory *directory, const char *name,
+sticker_song_find(Directory *directory, const char *name,
void (*func)(struct song *song, const char *value,
- gpointer user_data),
- gpointer user_data);
+ void *user_data),
+ void *user_data);
#endif
diff --git a/src/song_update.c b/src/SongUpdate.cxx
index 37f502a2..186512ff 100644
--- a/src/song_update.c
+++ b/src/SongUpdate.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,26 @@
*/
#include "config.h" /* must be first for large file support */
+
+extern "C" {
#include "song.h"
#include "uri.h"
-#include "directory.h"
-#include "mapper.h"
-#include "decoder_list.h"
+}
+
+#include "Directory.hxx"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+#include "tag.h"
+#include "input_stream.h"
#include "decoder_plugin.h"
+#include "DecoderList.hxx"
+
+extern "C" {
#include "tag_ape.h"
#include "tag_id3.h"
-#include "tag.h"
#include "tag_handler.h"
-#include "input_stream.h"
+}
#include <glib.h>
@@ -38,16 +47,16 @@
#include <stdio.h>
struct song *
-song_file_load(const char *path, struct directory *parent)
+song_file_load(const char *path_utf8, Directory *parent)
{
struct song *song;
bool ret;
- assert((parent == NULL) == g_path_is_absolute(path));
- assert(!uri_has_scheme(path));
- assert(strchr(path, '\n') == NULL);
+ assert((parent == NULL) == g_path_is_absolute(path_utf8));
+ assert(!uri_has_scheme(path_utf8));
+ assert(strchr(path_utf8, '\n') == NULL);
- song = song_file_new(path, parent);
+ song = song_file_new(path_utf8, parent);
//in archive ?
if (parent != NULL && parent->device == DEVICE_INARCHIVE) {
@@ -78,7 +87,6 @@ bool
song_file_update(struct song *song)
{
const char *suffix;
- char *path_fs;
const struct decoder_plugin *plugin;
struct stat st;
struct input_stream *is = NULL;
@@ -95,8 +103,8 @@ song_file_update(struct song *song)
if (plugin == NULL)
return false;
- path_fs = map_song_fs(song);
- if (path_fs == NULL)
+ const Path path_fs = map_song_fs(song);
+ if (path_fs.IsNull())
return false;
if (song->tag != NULL) {
@@ -104,25 +112,19 @@ song_file_update(struct song *song)
song->tag = NULL;
}
- if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) {
- g_free(path_fs);
+ if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode)) {
return false;
}
song->mtime = st.st_mtime;
- GMutex *mutex = NULL;
- GCond *cond;
-#if !GCC_CHECK_VERSION(4, 2)
- /* work around "may be used uninitialized in this function"
- false positive */
- cond = NULL;
-#endif
+ Mutex mutex;
+ Cond cond;
do {
/* load file tag */
song->tag = tag_new();
- if (decoder_plugin_scan_file(plugin, path_fs,
+ if (decoder_plugin_scan_file(plugin, path_fs.c_str(),
&full_tag_handler, song->tag))
break;
@@ -134,9 +136,8 @@ song_file_update(struct song *song)
/* open the input_stream (if not already
open) */
if (is == NULL) {
- mutex = g_mutex_new();
- cond = g_cond_new();
- is = input_stream_open(path_fs, mutex, cond,
+ is = input_stream_open(path_fs.c_str(),
+ mutex, cond,
NULL);
}
@@ -161,15 +162,10 @@ song_file_update(struct song *song)
if (is != NULL)
input_stream_close(is);
- if (mutex != NULL) {
- g_cond_free(cond);
- g_mutex_free(mutex);
- }
-
if (song->tag != NULL && tag_is_empty(song->tag))
- tag_scan_fallback(path_fs, &full_tag_handler, song->tag);
+ tag_scan_fallback(path_fs.c_str(), &full_tag_handler,
+ song->tag);
- g_free(path_fs);
return song->tag != NULL;
}
@@ -187,7 +183,7 @@ song_file_update_inarchive(struct song *song)
if (suffix == NULL)
return false;
- plugin = decoder_plugin_from_suffix(suffix, false);
+ plugin = decoder_plugin_from_suffix(suffix, nullptr);
if (plugin == NULL)
return false;
diff --git a/src/StateFile.cxx b/src/StateFile.cxx
new file mode 100644
index 00000000..a15eb722
--- /dev/null
+++ b/src/StateFile.cxx
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "StateFile.hxx"
+#include "OutputState.hxx"
+#include "PlaylistState.hxx"
+#include "TextFile.hxx"
+#include "Partition.hxx"
+#include "Volume.hxx"
+#include "event/Loop.hxx"
+
+#include <glib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "state_file"
+
+StateFile::StateFile(Path &&_path, const char *_path_utf8,
+ Partition &_partition, EventLoop &_loop)
+ :TimeoutMonitor(_loop), path(std::move(_path)), path_utf8(_path_utf8),
+ partition(_partition),
+ prev_volume_version(0), prev_output_version(0),
+ prev_playlist_version(0)
+{
+ ScheduleSeconds(5 * 60);
+}
+
+void
+StateFile::Write()
+{
+ g_debug("Saving state file %s", path_utf8.c_str());
+
+ FILE *fp = FOpen(path, FOpenMode::WriteText);
+ if (G_UNLIKELY(!fp)) {
+ g_warning("failed to create %s: %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return;
+ }
+
+ save_sw_volume_state(fp);
+ audio_output_state_save(fp);
+ playlist_state_save(fp, &partition.playlist, &partition.pc);
+
+ fclose(fp);
+
+ prev_volume_version = sw_volume_state_get_hash();
+ prev_output_version = audio_output_state_get_version();
+ prev_playlist_version = playlist_state_get_hash(&partition.playlist,
+ &partition.pc);
+}
+
+void
+StateFile::Read()
+{
+ bool success;
+
+ g_debug("Loading state file %s", path_utf8.c_str());
+
+ TextFile file(path);
+ if (file.HasFailed()) {
+ g_warning("failed to open %s: %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return;
+ }
+
+ const char *line;
+ while ((line = file.ReadLine()) != NULL) {
+ success = read_sw_volume_state(line) ||
+ audio_output_state_read(line) ||
+ playlist_state_restore(line, file, &partition.playlist,
+ &partition.pc);
+ if (!success)
+ g_warning("Unrecognized line in state file: %s", line);
+ }
+
+ prev_volume_version = sw_volume_state_get_hash();
+ prev_output_version = audio_output_state_get_version();
+ prev_playlist_version = playlist_state_get_hash(&partition.playlist,
+ &partition.pc);
+}
+
+inline void
+StateFile::AutoWrite()
+{
+ if (prev_volume_version == sw_volume_state_get_hash() &&
+ prev_output_version == audio_output_state_get_version() &&
+ prev_playlist_version == playlist_state_get_hash(&partition.playlist,
+ &partition.pc))
+ /* nothing has changed - don't save the state file,
+ don't spin up the hard disk */
+ return;
+
+ Write();
+}
+
+/**
+ * This function is called every 5 minutes by the GLib main loop, and
+ * saves the state file.
+ */
+bool
+StateFile::OnTimeout()
+{
+ AutoWrite();
+ return true;
+}
diff --git a/src/state_file.h b/src/StateFile.hxx
index 4c4f881c..9ec97d51 100644
--- a/src/state_file.h
+++ b/src/StateFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,17 +17,40 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STATE_FILE_H
-#define MPD_STATE_FILE_H
+#ifndef MPD_STATE_FILE_HXX
+#define MPD_STATE_FILE_HXX
-struct player_control;
+#include "event/TimeoutMonitor.hxx"
+#include "fs/Path.hxx"
+#include "gcc.h"
-void
-state_file_init(const char *path, struct player_control *pc);
+#include <string>
-void
-state_file_finish(struct player_control *pc);
+struct Partition;
-void write_state_file(void);
+class StateFile final : private TimeoutMonitor {
+ Path path;
+ std::string path_utf8;
+
+ Partition &partition;
+
+ /**
+ * These version numbers determine whether we need to save the state
+ * file. If nothing has changed, we won't let the hard drive spin up.
+ */
+ unsigned prev_volume_version, prev_output_version,
+ prev_playlist_version;
+
+public:
+ StateFile(Path &&path, const char *path_utf8,
+ Partition &partition, EventLoop &loop);
+
+ void Read();
+ void Write();
+ void AutoWrite();
+
+private:
+ virtual bool OnTimeout() override;
+};
#endif /* STATE_FILE_H */
diff --git a/src/Stats.cxx b/src/Stats.cxx
new file mode 100644
index 00000000..354d26c5
--- /dev/null
+++ b/src/Stats.cxx
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+extern "C" {
+#include "stats.h"
+}
+
+#include "PlayerControl.hxx"
+#include "ClientInternal.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSimple.hxx"
+
+struct stats stats;
+
+void stats_global_init(void)
+{
+ stats.timer = g_timer_new();
+}
+
+void stats_global_finish(void)
+{
+ g_timer_destroy(stats.timer);
+}
+
+void stats_update(void)
+{
+ GError *error = nullptr;
+
+ DatabaseStats stats2;
+
+ const DatabaseSelection selection("", true);
+ if (GetDatabase()->GetStats(selection, stats2, &error)) {
+ stats.song_count = stats2.song_count;
+ stats.song_duration = stats2.total_duration;
+ stats.artist_count = stats2.artist_count;
+ stats.album_count = stats2.album_count;
+ } else {
+ g_warning("%s", error->message);
+ g_error_free(error);
+
+ stats.song_count = 0;
+ stats.song_duration = 0;
+ stats.artist_count = 0;
+ stats.album_count = 0;
+ }
+}
+
+void
+stats_print(Client *client)
+{
+ client_printf(client,
+ "artists: %u\n"
+ "albums: %u\n"
+ "songs: %i\n"
+ "uptime: %li\n"
+ "playtime: %li\n"
+ "db_playtime: %li\n",
+ stats.artist_count,
+ stats.album_count,
+ stats.song_count,
+ (long)g_timer_elapsed(stats.timer, NULL),
+ (long)(client->player_control->GetTotalPlayTime() + 0.5),
+ stats.song_duration);
+
+ if (db_is_simple())
+ client_printf(client,
+ "db_update: %li\n",
+ (long)db_get_mtime());
+}
diff --git a/src/StickerCommands.cxx b/src/StickerCommands.cxx
new file mode 100644
index 00000000..d13647c3
--- /dev/null
+++ b/src/StickerCommands.cxx
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "StickerCommands.hxx"
+#include "SongPrint.hxx"
+#include "DatabaseLock.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseGlue.hxx"
+#include "DatabaseSimple.hxx"
+#include "SongSticker.hxx"
+#include "StickerPrint.hxx"
+#include "StickerDatabase.hxx"
+#include "CommandError.hxx"
+#include "protocol/Result.hxx"
+
+#include <string.h>
+
+struct sticker_song_find_data {
+ Client *client;
+ const char *name;
+};
+
+static void
+sticker_song_find_print_cb(struct song *song, const char *value,
+ gpointer user_data)
+{
+ struct sticker_song_find_data *data =
+ (struct sticker_song_find_data *)user_data;
+
+ song_print_uri(data->client, song);
+ sticker_print_value(data->client, data->name, value);
+}
+
+static enum command_return
+handle_sticker_song(Client *client, int argc, char *argv[])
+{
+ GError *error = nullptr;
+ const Database *db = GetDatabase(&error);
+ if (db == nullptr)
+ return print_error(client, error);
+
+ /* get song song_id key */
+ if (argc == 5 && strcmp(argv[1], "get") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ char *value = sticker_song_get_value(song, argv[4]);
+ db->ReturnSong(song);
+ if (value == NULL) {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "no such sticker");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ sticker_print_value(client, argv[4], value);
+ g_free(value);
+
+ return COMMAND_RETURN_OK;
+ /* list song song_id */
+ } else if (argc == 4 && strcmp(argv[1], "list") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ sticker *sticker = sticker_song_get(song);
+ db->ReturnSong(song);
+ if (sticker) {
+ sticker_print(client, sticker);
+ sticker_free(sticker);
+ }
+
+ return COMMAND_RETURN_OK;
+ /* set song song_id id key */
+ } else if (argc == 6 && strcmp(argv[1], "set") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ bool ret = sticker_song_set_value(song, argv[4], argv[5]);
+ db->ReturnSong(song);
+ if (!ret) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "failed to set sticker value");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ /* delete song song_id [key] */
+ } else if ((argc == 4 || argc == 5) &&
+ strcmp(argv[1], "delete") == 0) {
+ song *song = db->GetSong(argv[3], &error);
+ if (song == nullptr)
+ return print_error(client, error);
+
+ bool ret = argc == 4
+ ? sticker_song_delete(song)
+ : sticker_song_delete_value(song, argv[4]);
+ db->ReturnSong(song);
+ if (!ret) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "no such sticker");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ /* find song dir key */
+ } else if (argc == 5 && strcmp(argv[1], "find") == 0) {
+ /* "sticker find song a/directory name" */
+ bool success;
+ struct sticker_song_find_data data = {
+ client,
+ argv[4],
+ };
+
+ db_lock();
+ Directory *directory = db_get_directory(argv[3]);
+ if (directory == NULL) {
+ db_unlock();
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "no such directory");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ success = sticker_song_find(directory, data.name,
+ sticker_song_find_print_cb, &data);
+ db_unlock();
+ if (!success) {
+ command_error(client, ACK_ERROR_SYSTEM,
+ "failed to set search sticker database");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ return COMMAND_RETURN_OK;
+ } else {
+ command_error(client, ACK_ERROR_ARG, "bad request");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+enum command_return
+handle_sticker(Client *client, int argc, char *argv[])
+{
+ assert(argc >= 4);
+
+ if (!sticker_enabled()) {
+ command_error(client, ACK_ERROR_UNKNOWN,
+ "sticker database is disabled");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ if (strcmp(argv[2], "song") == 0)
+ return handle_sticker_song(client, argc, argv);
+ else {
+ command_error(client, ACK_ERROR_ARG,
+ "unknown sticker domain");
+ return COMMAND_RETURN_ERROR;
+ }
+}
diff --git a/src/StickerCommands.hxx b/src/StickerCommands.hxx
new file mode 100644
index 00000000..840bd33d
--- /dev/null
+++ b/src/StickerCommands.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_STICKER_COMMANDS_HXX
+#define MPD_STICKER_COMMANDS_HXX
+
+#include "command.h"
+
+class Client;
+
+enum command_return
+handle_sticker(Client *client, int argc, char *argv[]);
+
+#endif
diff --git a/src/sticker.c b/src/StickerDatabase.cxx
index 346a827a..2d77e4b6 100644
--- a/src/sticker.c
+++ b/src/StickerDatabase.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,11 @@
*/
#include "config.h"
-#include "sticker.h"
-#include "idle.h"
+#include "StickerDatabase.hxx"
+#include "Idle.hxx"
+
+#include <string>
+#include <map>
#include <glib.h>
#include <sqlite3.h>
@@ -33,7 +36,7 @@
#endif
struct sticker {
- GHashTable *table;
+ std::map<std::string, std::string> table;
};
enum sticker_sql {
@@ -47,19 +50,19 @@ enum sticker_sql {
};
static const char *const sticker_sql[] = {
- [STICKER_SQL_GET] =
+ //[STICKER_SQL_GET] =
"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_LIST] =
+ //[STICKER_SQL_LIST] =
"SELECT name,value FROM sticker WHERE type=? AND uri=?",
- [STICKER_SQL_UPDATE] =
+ //[STICKER_SQL_UPDATE] =
"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_INSERT] =
+ //[STICKER_SQL_INSERT] =
"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)",
- [STICKER_SQL_DELETE] =
+ //[STICKER_SQL_DELETE] =
"DELETE FROM sticker WHERE type=? AND uri=?",
- [STICKER_SQL_DELETE_VALUE] =
+ //[STICKER_SQL_DELETE_VALUE] =
"DELETE FROM sticker WHERE type=? AND uri=? AND name=?",
- [STICKER_SQL_FIND] =
+ //[STICKER_SQL_FIND] =
"SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?",
};
@@ -226,13 +229,12 @@ sticker_load_value(const char *type, const char *uri, const char *name)
}
static bool
-sticker_list_values(GHashTable *hash, const char *type, const char *uri)
+sticker_list_values(std::map<std::string, std::string> &table,
+ const char *type, const char *uri)
{
sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST];
int ret;
- char *name, *value;
- assert(hash != NULL);
assert(type != NULL);
assert(uri != NULL);
assert(sticker_enabled());
@@ -256,10 +258,13 @@ sticker_list_values(GHashTable *hash, const char *type, const char *uri)
do {
ret = sqlite3_step(stmt);
switch (ret) {
+ const char *name, *value;
+
case SQLITE_ROW:
- name = g_strdup((const char*)sqlite3_column_text(stmt, 0));
- value = g_strdup((const char*)sqlite3_column_text(stmt, 1));
- g_hash_table_insert(hash, name, value);
+ name = (const char*)sqlite3_column_text(stmt, 0);
+ value = (const char*)sqlite3_column_text(stmt, 1);
+
+ table.insert(std::make_pair(name, value));
break;
case SQLITE_DONE:
break;
@@ -518,44 +523,20 @@ sticker_delete_value(const char *type, const char *uri, const char *name)
return ret > 0;
}
-static struct sticker *
-sticker_new(void)
-{
- struct sticker *sticker = g_new(struct sticker, 1);
-
- sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- return sticker;
-}
-
void
sticker_free(struct sticker *sticker)
{
- assert(sticker != NULL);
- assert(sticker->table != NULL);
-
- g_hash_table_destroy(sticker->table);
- g_free(sticker);
+ delete sticker;
}
const char *
sticker_get_value(const struct sticker *sticker, const char *name)
{
- return g_hash_table_lookup(sticker->table, name);
-}
-
-struct sticker_foreach_data {
- void (*func)(const char *name, const char *value,
- gpointer user_data);
- gpointer user_data;
-};
-
-static void
-sticker_foreach_func(gpointer key, gpointer value, gpointer user_data)
-{
- struct sticker_foreach_data *data = user_data;
+ auto i = sticker->table.find(name);
+ if (i == sticker->table.end())
+ return nullptr;
- data->func(key, value, data->user_data);
+ return i->second.c_str();
}
void
@@ -564,33 +545,23 @@ sticker_foreach(const struct sticker *sticker,
gpointer user_data),
gpointer user_data)
{
- struct sticker_foreach_data data = {
- .func = func,
- .user_data = user_data,
- };
-
- g_hash_table_foreach(sticker->table, sticker_foreach_func, &data);
+ for (const auto &i : sticker->table)
+ func(i.first.c_str(), i.second.c_str(), user_data);
}
struct sticker *
sticker_load(const char *type, const char *uri)
{
- struct sticker *sticker = sticker_new();
- bool success;
+ sticker s;
- success = sticker_list_values(sticker->table, type, uri);
- if (!success) {
- sticker_free(sticker);
+ if (!sticker_list_values(s.table, type, uri))
return NULL;
- }
- if (g_hash_table_size(sticker->table) == 0) {
+ if (s.table.empty())
/* don't return empty sticker objects */
- sticker_free(sticker);
return NULL;
- }
- return sticker;
+ return new sticker(std::move(s));
}
bool
diff --git a/src/sticker.h b/src/StickerDatabase.hxx
index 5545206a..90ff9b06 100644
--- a/src/sticker.h
+++ b/src/StickerDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -39,12 +39,10 @@
*
*/
-#ifndef STICKER_H
-#define STICKER_H
+#ifndef MPD_STICKER_DATABASE_HXX
+#define MPD_STICKER_DATABASE_HXX
-#include <glib.h>
-
-#include <stdbool.h>
+#include "gerror.h"
struct sticker;
@@ -127,8 +125,8 @@ sticker_get_value(const struct sticker *sticker, const char *name);
void
sticker_foreach(const struct sticker *sticker,
void (*func)(const char *name, const char *value,
- gpointer user_data),
- gpointer user_data);
+ void *user_data),
+ void *user_data);
/**
* Loads the sticker for the specified resource.
@@ -153,7 +151,7 @@ sticker_load(const char *type, const char *uri);
bool
sticker_find(const char *type, const char *base_uri, const char *name,
void (*func)(const char *uri, const char *value,
- gpointer user_data),
- gpointer user_data);
+ void *user_data),
+ void *user_data);
#endif
diff --git a/src/sticker_print.c b/src/StickerPrint.cxx
index 65e79513..1708ee4f 100644
--- a/src/sticker_print.c
+++ b/src/StickerPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,27 +18,27 @@
*/
#include "config.h"
-#include "sticker_print.h"
-#include "sticker.h"
-#include "client.h"
+#include "StickerPrint.hxx"
+#include "StickerDatabase.hxx"
+#include "Client.hxx"
void
-sticker_print_value(struct client *client,
+sticker_print_value(Client *client,
const char *name, const char *value)
{
client_printf(client, "sticker: %s=%s\n", name, value);
}
static void
-print_sticker_cb(const char *name, const char *value, gpointer data)
+print_sticker_cb(const char *name, const char *value, void *data)
{
- struct client *client = data;
+ Client *client = (Client *)data;
sticker_print_value(client, name, value);
}
void
-sticker_print(struct client *client, const struct sticker *sticker)
+sticker_print(Client *client, const struct sticker *sticker)
{
sticker_foreach(sticker, print_sticker_cb, client);
}
diff --git a/src/sticker_print.h b/src/StickerPrint.hxx
index 7398c808..27225a49 100644
--- a/src/sticker_print.h
+++ b/src/StickerPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,23 +17,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_STICKER_PRINT_H
-#define MPD_STICKER_PRINT_H
+#ifndef MPD_STICKER_PRINT_HXX
+#define MPD_STICKER_PRINT_HXX
struct sticker;
-struct client;
+class Client;
/**
* Sends one sticker value to the client.
*/
void
-sticker_print_value(struct client *client,
- const char *name, const char *value);
+sticker_print_value(Client *client, const char *name, const char *value);
/**
* Sends all sticker values to the client.
*/
void
-sticker_print(struct client *client, const struct sticker *sticker);
+sticker_print(Client *client, const struct sticker *sticker);
#endif
diff --git a/src/tag.c b/src/Tag.cxx
index c0faa7ab..afdeb055 100644
--- a/src/tag.c
+++ b/src/Tag.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,8 +19,8 @@
#include "config.h"
#include "tag.h"
-#include "tag_internal.h"
-#include "tag_pool.h"
+#include "TagInternal.hxx"
+#include "TagPool.hxx"
#include "conf.h"
#include "song.h"
#include "mpd_error.h"
@@ -43,38 +43,15 @@ static struct {
struct tag_item *items[BULK_MAX];
} bulk;
-const char *tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
- [TAG_ARTIST] = "Artist",
- [TAG_ARTIST_SORT] = "ArtistSort",
- [TAG_ALBUM] = "Album",
- [TAG_ALBUM_ARTIST] = "AlbumArtist",
- [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort",
- [TAG_TITLE] = "Title",
- [TAG_TRACK] = "Track",
- [TAG_NAME] = "Name",
- [TAG_GENRE] = "Genre",
- [TAG_DATE] = "Date",
- [TAG_COMPOSER] = "Composer",
- [TAG_PERFORMER] = "Performer",
- [TAG_COMMENT] = "Comment",
- [TAG_DISC] = "Disc",
-
- /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */
- [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID",
- [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID",
- [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID",
- [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID",
-};
-
bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
enum tag_type
tag_name_parse(const char *name)
{
- assert(name != NULL);
+ assert(name != nullptr);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
- assert(tag_item_names[i] != NULL);
+ assert(tag_item_names[i] != nullptr);
if (strcmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
@@ -86,10 +63,10 @@ tag_name_parse(const char *name)
enum tag_type
tag_name_parse_i(const char *name)
{
- assert(name != NULL);
+ assert(name != nullptr);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
- assert(tag_item_names[i] != NULL);
+ assert(tag_item_names[i] != nullptr);
if (g_ascii_strcasecmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
@@ -117,8 +94,8 @@ void tag_lib_init(void)
/* ignore comments by default */
ignore_tag_items[TAG_COMMENT] = true;
- value = config_get_string(CONF_METADATA_TO_USE, NULL);
- if (value == NULL)
+ value = config_get_string(CONF_METADATA_TO_USE, nullptr);
+ if (value == nullptr)
return;
memset(ignore_tag_items, true, TAG_NUM_OF_ITEM_TYPES);
@@ -156,7 +133,7 @@ void tag_lib_init(void)
struct tag *tag_new(void)
{
struct tag *ret = g_new(struct tag, 1);
- ret->items = NULL;
+ ret->items = nullptr;
ret->time = -1;
ret->has_playlist = false;
ret->num_items = 0;
@@ -168,9 +145,9 @@ static void tag_delete_item(struct tag *tag, unsigned idx)
assert(idx < tag->num_items);
tag->num_items--;
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
tag_pool_put_item(tag->items[idx]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
if (tag->num_items - idx > 0) {
memmove(tag->items + idx, tag->items + idx + 1,
@@ -178,10 +155,11 @@ static void tag_delete_item(struct tag *tag, unsigned idx)
}
if (tag->num_items > 0) {
- tag->items = g_realloc(tag->items, items_size(tag));
+ tag->items = (struct tag_item **)
+ g_realloc(tag->items, items_size(tag));
} else {
g_free(tag->items);
- tag->items = NULL;
+ tag->items = nullptr;
}
}
@@ -200,12 +178,12 @@ void tag_free(struct tag *tag)
{
int i;
- assert(tag != NULL);
+ assert(tag != nullptr);
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
for (i = tag->num_items; --i >= 0; )
tag_pool_put_item(tag->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
if (tag->items == bulk.items) {
#ifndef NDEBUG
@@ -223,18 +201,20 @@ struct tag *tag_dup(const struct tag *tag)
struct tag *ret;
if (!tag)
- return NULL;
+ return nullptr;
ret = tag_new();
ret->time = tag->time;
ret->has_playlist = tag->has_playlist;
ret->num_items = tag->num_items;
- ret->items = ret->num_items > 0 ? g_malloc(items_size(tag)) : NULL;
+ ret->items = ret->num_items > 0
+ ? (struct tag_item **)g_malloc(items_size(tag))
+ : nullptr;
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
for (unsigned i = 0; i < tag->num_items; i++)
ret->items[i] = tag_pool_dup_item(tag->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
return ret;
}
@@ -245,17 +225,19 @@ tag_merge(const struct tag *base, const struct tag *add)
struct tag *ret;
unsigned n;
- assert(base != NULL);
- assert(add != NULL);
+ assert(base != nullptr);
+ assert(add != nullptr);
/* allocate new tag object */
ret = tag_new();
ret->time = add->time > 0 ? add->time : base->time;
ret->num_items = base->num_items + add->num_items;
- ret->items = ret->num_items > 0 ? g_malloc(items_size(ret)) : NULL;
+ ret->items = ret->num_items > 0
+ ? (struct tag_item **)g_malloc(items_size(ret))
+ : nullptr;
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
/* copy all items from "add" */
@@ -270,7 +252,7 @@ tag_merge(const struct tag *base, const struct tag *add)
if (!tag_has_type(add, base->items[i]->type))
ret->items[n++] = tag_pool_dup_item(base->items[i]);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
assert(n <= ret->num_items);
@@ -279,7 +261,8 @@ tag_merge(const struct tag *base, const struct tag *add)
assert(n > 0);
ret->num_items = n;
- ret->items = g_realloc(ret->items, items_size(ret));
+ ret->items = (struct tag_item **)
+ g_realloc(ret->items, items_size(ret));
}
return ret;
@@ -288,10 +271,10 @@ tag_merge(const struct tag *base, const struct tag *add)
struct tag *
tag_merge_replace(struct tag *base, struct tag *add)
{
- if (add == NULL)
+ if (add == nullptr)
return base;
- if (base == NULL)
+ if (base == nullptr)
return add;
struct tag *tag = tag_merge(base, add);
@@ -304,24 +287,24 @@ tag_merge_replace(struct tag *base, struct tag *add)
const char *
tag_get_value(const struct tag *tag, enum tag_type type)
{
- assert(tag != NULL);
+ assert(tag != nullptr);
assert(type < TAG_NUM_OF_ITEM_TYPES);
for (unsigned i = 0; i < tag->num_items; i++)
if (tag->items[i]->type == type)
return tag->items[i]->value;
- return NULL;
+ return nullptr;
}
bool tag_has_type(const struct tag *tag, enum tag_type type)
{
- return tag_get_value(tag, type) != NULL;
+ return tag_get_value(tag, type) != nullptr;
}
bool tag_equal(const struct tag *tag1, const struct tag *tag2)
{
- if (tag1 == NULL && tag2 == NULL)
+ if (tag1 == nullptr && tag2 == nullptr)
return true;
else if (!tag1 || !tag2)
return false;
@@ -367,16 +350,16 @@ fix_utf8(const char *str, size_t length)
char *temp;
gsize written;
- assert(str != NULL);
+ assert(str != nullptr);
/* check if the string is already valid UTF-8 */
if (g_utf8_validate(str, length, &end))
- return NULL;
+ return nullptr;
/* no, it's not - try to import it from ISO-Latin-1 */
temp = g_convert(str, length, "utf-8", "iso-8859-1",
- NULL, &written, NULL);
- if (temp != NULL)
+ nullptr, &written, nullptr);
+ if (temp != nullptr)
/* success! */
return temp;
@@ -388,8 +371,8 @@ fix_utf8(const char *str, size_t length)
void tag_begin_add(struct tag *tag)
{
assert(!bulk.busy);
- assert(tag != NULL);
- assert(tag->items == NULL);
+ assert(tag != nullptr);
+ assert(tag->items == nullptr);
assert(tag->num_items == 0);
#ifndef NDEBUG
@@ -406,10 +389,11 @@ void tag_end_add(struct tag *tag)
if (tag->num_items > 0) {
/* copy the tag items from the bulk list over
to a new list (which fits exactly) */
- tag->items = g_malloc(items_size(tag));
+ tag->items = (struct tag_item **)
+ g_malloc(items_size(tag));
memcpy(tag->items, bulk.items, items_size(tag));
} else
- tag->items = NULL;
+ tag->items = nullptr;
}
#ifndef NDEBUG
@@ -430,12 +414,12 @@ find_non_printable(const char *p, size_t length)
if (char_is_non_printable(p[i]))
return p + i;
- return NULL;
+ return nullptr;
}
/**
* Clears all non-printable characters, convert them to space.
- * Returns NULL if nothing needs to be cleared.
+ * Returns nullptr if nothing needs to be cleared.
*/
static char *
clear_non_printable(const char *p, size_t length)
@@ -443,8 +427,8 @@ clear_non_printable(const char *p, size_t length)
const char *first = find_non_printable(p, length);
char *dest;
- if (first == NULL)
- return NULL;
+ if (first == nullptr)
+ return nullptr;
dest = g_strndup(p, length);
@@ -461,13 +445,13 @@ fix_tag_value(const char *p, size_t length)
char *utf8, *cleared;
utf8 = fix_utf8(p, length);
- if (utf8 != NULL) {
+ if (utf8 != nullptr) {
p = utf8;
length = strlen(p);
}
cleared = clear_non_printable(p, length);
- if (cleared == NULL)
+ if (cleared == nullptr)
cleared = utf8;
else
g_free(utf8);
@@ -483,7 +467,7 @@ tag_add_item_internal(struct tag *tag, enum tag_type type,
char *p;
p = fix_tag_value(value, len);
- if (p != NULL) {
+ if (p != nullptr) {
value = p;
len = strlen(value);
}
@@ -492,19 +476,20 @@ tag_add_item_internal(struct tag *tag, enum tag_type type,
if (tag->items != bulk.items)
/* bulk mode disabled */
- tag->items = g_realloc(tag->items, items_size(tag));
+ tag->items = (struct tag_item **)
+ g_realloc(tag->items, items_size(tag));
else if (tag->num_items >= BULK_MAX) {
/* bulk list already full - switch back to non-bulk */
assert(bulk.busy);
- tag->items = g_malloc(items_size(tag));
+ tag->items = (struct tag_item **)g_malloc(items_size(tag));
memcpy(tag->items, bulk.items,
items_size(tag) - sizeof(struct tag_item *));
}
- g_mutex_lock(tag_pool_lock);
+ tag_pool_lock.lock();
tag->items[i] = tag_pool_get_item(type, value, len);
- g_mutex_unlock(tag_pool_lock);
+ tag_pool_lock.unlock();
g_free(p);
}
diff --git a/src/tag_file.c b/src/TagFile.cxx
index 8d8a0f5f..046778bd 100644
--- a/src/tag_file.c
+++ b/src/TagFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,13 @@
*/
#include "config.h"
-#include "tag_file.h"
+#include "TagFile.hxx"
+
+extern "C" {
#include "uri.h"
-#include "decoder_list.h"
+}
+
+#include "DecoderList.hxx"
#include "decoder_plugin.h"
#include "input_stream.h"
@@ -46,8 +50,8 @@ tag_file_scan(const char *path_fs,
return false;
struct input_stream *is = NULL;
- GMutex *mutex = NULL;
- GCond *cond = NULL;
+ Mutex mutex;
+ Cond cond;
do {
/* load file tag */
@@ -59,12 +63,9 @@ tag_file_scan(const char *path_fs,
if (plugin->scan_stream != NULL) {
/* open the input_stream (if not already
open) */
- if (is == NULL) {
- mutex = g_mutex_new();
- cond = g_cond_new();
+ if (is == nullptr)
is = input_stream_open(path_fs, mutex, cond,
NULL);
- }
/* now try the stream_tag() method */
if (is != NULL) {
@@ -80,11 +81,8 @@ tag_file_scan(const char *path_fs,
plugin = decoder_plugin_from_suffix(suffix, plugin);
} while (plugin != NULL);
- if (is != NULL) {
+ if (is != NULL)
input_stream_close(is);
- g_cond_free(cond);
- g_mutex_free(mutex);
- }
return plugin != NULL;
}
diff --git a/src/tag_file.h b/src/TagFile.hxx
index 8cf1af3c..61f49185 100644
--- a/src/tag_file.h
+++ b/src/TagFile.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_FILE_H
-#define MPD_TAG_FILE_H
+#ifndef MPD_TAG_FILE_HXX
+#define MPD_TAG_FILE_HXX
#include "check.h"
diff --git a/src/tag_internal.h b/src/TagInternal.hxx
index af05cc6b..afce0427 100644
--- a/src/tag_internal.h
+++ b/src/TagInternal.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_INTERNAL_H
-#define MPD_TAG_INTERNAL_H
+#ifndef MPD_TAG_INTERNAL_HXX
+#define MPD_TAG_INTERNAL_HXX
#include "tag.h"
-#include <stdbool.h>
-
extern bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
#endif
diff --git a/src/TagNames.c b/src/TagNames.c
new file mode 100644
index 00000000..eca320af
--- /dev/null
+++ b/src/TagNames.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "tag.h"
+
+const char *tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
+ [TAG_ARTIST] = "Artist",
+ [TAG_ARTIST_SORT] = "ArtistSort",
+ [TAG_ALBUM] = "Album",
+ [TAG_ALBUM_ARTIST] = "AlbumArtist",
+ [TAG_ALBUM_ARTIST_SORT] = "AlbumArtistSort",
+ [TAG_TITLE] = "Title",
+ [TAG_TRACK] = "Track",
+ [TAG_NAME] = "Name",
+ [TAG_GENRE] = "Genre",
+ [TAG_DATE] = "Date",
+ [TAG_COMPOSER] = "Composer",
+ [TAG_PERFORMER] = "Performer",
+ [TAG_COMMENT] = "Comment",
+ [TAG_DISC] = "Disc",
+
+ /* MusicBrainz tags from http://musicbrainz.org/doc/MusicBrainzTag */
+ [TAG_MUSICBRAINZ_ARTISTID] = "MUSICBRAINZ_ARTISTID",
+ [TAG_MUSICBRAINZ_ALBUMID] = "MUSICBRAINZ_ALBUMID",
+ [TAG_MUSICBRAINZ_ALBUMARTISTID] = "MUSICBRAINZ_ALBUMARTISTID",
+ [TAG_MUSICBRAINZ_TRACKID] = "MUSICBRAINZ_TRACKID",
+};
diff --git a/src/tag_pool.c b/src/TagPool.cxx
index eabf3e36..a9b89284 100644
--- a/src/tag_pool.c
+++ b/src/TagPool.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,13 @@
*/
#include "config.h"
-#include "tag_pool.h"
+#include "TagPool.hxx"
+
+#include <glib.h>
#include <assert.h>
-GMutex *tag_pool_lock = NULL;
+Mutex tag_pool_lock;
#define NUM_SLOTS 4096
@@ -39,7 +41,7 @@ calc_hash_n(enum tag_type type, const char *p, size_t length)
{
unsigned hash = 5381;
- assert(p != NULL);
+ assert(p != nullptr);
while (length-- > 0)
hash = (hash << 5) + hash + *p++;
@@ -52,7 +54,7 @@ calc_hash(enum tag_type type, const char *p)
{
unsigned hash = 5381;
- assert(p != NULL);
+ assert(p != nullptr);
while (*p != 0)
hash = (hash << 5) + hash + *p++;
@@ -72,7 +74,8 @@ static struct slot *slot_alloc(struct slot *next,
{
struct slot *slot;
- slot = g_malloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
+ slot = (struct slot *)
+ g_malloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
slot->next = next;
slot->ref = 1;
slot->item.type = type;
@@ -81,26 +84,13 @@ static struct slot *slot_alloc(struct slot *next,
return slot;
}
-void tag_pool_init(void)
-{
- g_assert(tag_pool_lock == NULL);
- tag_pool_lock = g_mutex_new();
-}
-
-void tag_pool_deinit(void)
-{
- g_assert(tag_pool_lock != NULL);
- g_mutex_free(tag_pool_lock);
- tag_pool_lock = NULL;
-}
-
struct tag_item *
tag_pool_get_item(enum tag_type type, const char *value, size_t length)
{
struct slot **slot_p, *slot;
slot_p = &slots[calc_hash_n(type, value, length) % NUM_SLOTS];
- for (slot = *slot_p; slot != NULL; slot = slot->next) {
+ for (slot = *slot_p; slot != nullptr; slot = slot->next) {
if (slot->item.type == type &&
length == strlen(slot->item.value) &&
memcmp(value, slot->item.value, length) == 0 &&
@@ -153,7 +143,7 @@ void tag_pool_put_item(struct tag_item *item)
for (slot_p = &slots[calc_hash(item->type, item->value) % NUM_SLOTS];
*slot_p != slot;
slot_p = &(*slot_p)->next) {
- assert(*slot_p != NULL);
+ assert(*slot_p != nullptr);
}
*slot_p = slot->next;
diff --git a/src/tag_pool.h b/src/TagPool.hxx
index a96c00d8..649cef6e 100644
--- a/src/tag_pool.h
+++ b/src/TagPool.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,21 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_POOL_H
-#define MPD_TAG_POOL_H
+#ifndef MPD_TAG_POOL_HXX
+#define MPD_TAG_POOL_HXX
#include "tag.h"
+#include "thread/Mutex.hxx"
-#include <glib.h>
-
-extern GMutex *tag_pool_lock;
+extern Mutex tag_pool_lock;
struct tag_item;
-void tag_pool_init(void);
-
-void tag_pool_deinit(void);
-
struct tag_item *
tag_pool_get_item(enum tag_type type, const char *value, size_t length);
diff --git a/src/tag_print.c b/src/TagPrint.cxx
index 9a46b247..b3ad07df 100644
--- a/src/tag_print.c
+++ b/src/TagPrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,13 @@
*/
#include "config.h"
-#include "tag_print.h"
+#include "TagPrint.hxx"
#include "tag.h"
-#include "tag_internal.h"
-#include "client.h"
+#include "TagInternal.hxx"
#include "song.h"
+#include "Client.hxx"
-void tag_print_types(struct client *client)
+void tag_print_types(Client *client)
{
int i;
@@ -35,7 +35,7 @@ void tag_print_types(struct client *client)
}
}
-void tag_print(struct client *client, const struct tag *tag)
+void tag_print(Client *client, const struct tag *tag)
{
if (tag->time >= 0)
client_printf(client, SONG_TIME "%i\n", tag->time);
diff --git a/src/tag_print.h b/src/TagPrint.hxx
index b9eeeaec..99e1d085 100644
--- a/src/tag_print.h
+++ b/src/TagPrint.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,14 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_PRINT_H
-#define MPD_TAG_PRINT_H
+#ifndef MPD_TAG_PRINT_HXX
+#define MPD_TAG_PRINT_HXX
struct tag;
-struct client;
+class Client;
-void tag_print_types(struct client *client);
+void tag_print_types(Client *client);
-void tag_print(struct client *client, const struct tag *tag);
+void tag_print(Client *client, const struct tag *tag);
#endif
diff --git a/src/tag_save.c b/src/TagSave.cxx
index 2fdaef56..15da9fc4 100644
--- a/src/tag_save.c
+++ b/src/TagSave.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,9 @@
*/
#include "config.h"
-#include "tag_save.h"
+#include "TagSave.hxx"
#include "tag.h"
-#include "tag_internal.h"
+#include "TagInternal.hxx"
#include "song.h"
void tag_save(FILE *file, const struct tag *tag)
diff --git a/src/tag_save.h b/src/TagSave.hxx
index 9f6a580c..a7ccfa61 100644
--- a/src/tag_save.h
+++ b/src/TagSave.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TAG_SAVE_H
-#define MPD_TAG_SAVE_H
+#ifndef MPD_TAG_SAVE_HXX
+#define MPD_TAG_SAVE_HXX
#include <stdio.h>
diff --git a/src/text_file.c b/src/TextFile.cxx
index 3674e5ce..4ad59ee4 100644
--- a/src/text_file.c
+++ b/src/TextFile.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,27 +18,20 @@
*/
#include "config.h"
-#include "text_file.h"
+#include "TextFile.hxx"
#include <assert.h>
#include <string.h>
char *
-read_text_line(FILE *file, GString *buffer)
+TextFile::ReadLine()
{
- enum {
- max_length = 512 * 1024,
- step = 1024,
- };
-
gsize length = 0, i;
char *p;
assert(file != NULL);
assert(buffer != NULL);
-
- if (buffer->allocated_len < step)
- g_string_set_size(buffer, step);
+ assert(buffer->allocated_len >= step);
while (buffer->len < max_length) {
p = fgets(buffer->str + length,
diff --git a/src/TextFile.hxx b/src/TextFile.hxx
new file mode 100644
index 00000000..d593e096
--- /dev/null
+++ b/src/TextFile.hxx
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_TEXT_FILE_HXX
+#define MPD_TEXT_FILE_HXX
+
+#include "gcc.h"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+#include <glib.h>
+
+class TextFile {
+ static constexpr size_t max_length = 512 * 1024;
+ static constexpr size_t step = 1024;
+
+ FILE *const file;
+
+ GString *const buffer;
+
+public:
+ TextFile(const Path &path_fs)
+ :file(FOpen(path_fs, FOpenMode::ReadText)),
+ buffer(g_string_sized_new(step)) {}
+
+ TextFile(const TextFile &other) = delete;
+
+ ~TextFile() {
+ if (file != nullptr)
+ fclose(file);
+
+ g_string_free(buffer, true);
+ }
+
+ bool HasFailed() const {
+ return gcc_unlikely(file == nullptr);
+ }
+
+ /**
+ * Reads a line from the input file, and strips trailing
+ * space. There is a reasonable maximum line length, only to
+ * prevent denial of service.
+ *
+ * @param file the source file, opened in text mode
+ * @param buffer an allocator for the buffer
+ * @return a pointer to the line, or NULL on end-of-file or error
+ */
+ char *ReadLine();
+};
+
+#endif
diff --git a/src/glib_socket.h b/src/TimePrint.cxx
index 46fab37d..c5247dd9 100644
--- a/src/glib_socket.h
+++ b/src/TimePrint.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,24 +17,31 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_GLIB_SOCKET_H
-#define MPD_GLIB_SOCKET_H
+#include "config.h"
+#include "TimePrint.hxx"
+#include "Client.hxx"
#include <glib.h>
-/**
- * Portable wrapper for g_io_channel_unix_new() or
- * g_io_channel_win32_new_socket().
- */
-G_GNUC_MALLOC
-static inline GIOChannel *
-g_io_channel_new_socket(int fd)
+void
+time_print(Client *client, const char *name, time_t t)
{
#ifdef G_OS_WIN32
- return g_io_channel_win32_new_socket(fd);
+ const struct tm *tm2 = gmtime(&t);
#else
- return g_io_channel_unix_new(fd);
+ struct tm tm;
+ const struct tm *tm2 = gmtime_r(&t, &tm);
#endif
-}
+ if (tm2 == NULL)
+ return;
+ char buffer[32];
+ strftime(buffer, sizeof(buffer),
+#ifdef G_OS_WIN32
+ "%Y-%m-%dT%H:%M:%SZ",
+#else
+ "%FT%TZ",
#endif
+ tm2);
+ client_printf(client, "%s: %s\n", name, buffer);
+}
diff --git a/src/TimePrint.hxx b/src/TimePrint.hxx
new file mode 100644
index 00000000..f4510125
--- /dev/null
+++ b/src/TimePrint.hxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_TIME_PRINT_HXX
+#define MPD_TIME_PRINT_HXX
+
+#include <time.h>
+
+class Client;
+
+/**
+ * Write a line with a time stamp to the client.
+ */
+void
+time_print(Client *client, const char *name, time_t t);
+
+#endif
diff --git a/src/update_archive.c b/src/UpdateArchive.cxx
index 3fb2bc18..133dfd47 100644
--- a/src/update_archive.c
+++ b/src/UpdateArchive.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,31 +18,36 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_archive.h"
-#include "update_internal.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateArchive.hxx"
+#include "UpdateInternal.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "mapper.h"
-#include "archive_list.h"
-#include "archive_plugin.h"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+#include "ArchiveList.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "ArchiveVisitor.hxx"
#include <glib.h>
#include <string.h>
static void
-update_archive_tree(struct directory *directory, char *name)
+update_archive_tree(Directory *directory, const char *name)
{
- char *tmp = strchr(name, '/');
+ const char *tmp = strchr(name, '/');
if (tmp) {
- *tmp = 0;
+ char *child_name = g_strndup(name, tmp - name);
//add dir is not there already
db_lock();
- struct directory *subdir =
- directory_make_child(directory, name);
+ Directory *subdir =
+ directory->MakeChild(child_name);
subdir->device = DEVICE_INARCHIVE;
db_unlock();
+ g_free(child_name);
+
//create directories first
update_archive_tree(subdir, tmp+1);
} else {
@@ -53,18 +58,18 @@ update_archive_tree(struct directory *directory, char *name)
//add file
db_lock();
- struct song *song = directory_get_song(directory, name);
+ struct song *song = directory->FindSong(name);
db_unlock();
if (song == NULL) {
song = song_file_load(name, directory);
if (song != NULL) {
db_lock();
- directory_add_song(directory, song);
+ directory->AddSong(song);
db_unlock();
modified = true;
g_message("added %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
}
}
}
@@ -79,12 +84,12 @@ update_archive_tree(struct directory *directory, char *name)
* @param plugin the archive plugin which fits this archive type
*/
static void
-update_archive_file2(struct directory *parent, const char *name,
+update_archive_file2(Directory *parent, const char *name,
const struct stat *st,
const struct archive_plugin *plugin)
{
db_lock();
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
db_unlock();
if (directory != NULL && directory->mtime == st->st_mtime &&
@@ -93,25 +98,23 @@ update_archive_file2(struct directory *parent, const char *name,
changed since - don't consider updating it */
return;
- char *path_fs = map_directory_child_fs(parent, name);
+ const Path path_fs = map_directory_child_fs(parent, name);
/* open archive */
GError *error = NULL;
- struct archive_file *file = archive_file_open(plugin, path_fs, &error);
+ ArchiveFile *file = archive_file_open(plugin, path_fs.c_str(), &error);
if (file == NULL) {
- g_free(path_fs);
g_warning("%s", error->message);
g_error_free(error);
return;
}
- g_debug("archive %s opened", path_fs);
- g_free(path_fs);
+ g_debug("archive %s opened", path_fs.c_str());
if (directory == NULL) {
g_debug("creating archive directory: %s", name);
db_lock();
- directory = directory_new_child(parent, name);
+ directory = parent->CreateChild(name);
/* mark this directory as archive (we use device for
this) */
directory->device = DEVICE_INARCHIVE;
@@ -120,20 +123,26 @@ update_archive_file2(struct directory *parent, const char *name,
directory->mtime = st->st_mtime;
- archive_file_scan_reset(file);
+ class UpdateArchiveVisitor final : public ArchiveVisitor {
+ Directory *directory;
- char *filepath;
- while ((filepath = archive_file_scan_next(file)) != NULL) {
- /* split name into directory and file */
- g_debug("adding archive file: %s", filepath);
- update_archive_tree(directory, filepath);
- }
+ public:
+ UpdateArchiveVisitor(Directory *_directory)
+ :directory(_directory) {}
+
+ virtual void VisitArchiveEntry(const char *path_utf8) override {
+ g_debug("adding archive file: %s", path_utf8);
+ update_archive_tree(directory, path_utf8);
+ }
+ };
- archive_file_close(file);
+ UpdateArchiveVisitor visitor(directory);
+ file->Visit(visitor);
+ file->Close();
}
bool
-update_archive_file(struct directory *directory,
+update_archive_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
diff --git a/src/update_archive.h b/src/UpdateArchive.hxx
index 838697dd..73b363d2 100644
--- a/src/update_archive.h
+++ b/src/UpdateArchive.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,33 +17,31 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_ARCHIVE_H
-#define MPD_UPDATE_ARCHIVE_H
+#ifndef MPD_UPDATE_ARCHIVE_HXX
+#define MPD_UPDATE_ARCHIVE_HXX
#include "check.h"
+#include "gcc.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
struct archive_plugin;
#ifdef ENABLE_ARCHIVE
bool
-update_archive_file(struct directory *directory,
+update_archive_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st);
#else
-#include <glib.h>
-
static inline bool
-update_archive_file(G_GNUC_UNUSED struct directory *directory,
- G_GNUC_UNUSED const char *name,
- G_GNUC_UNUSED const char *suffix,
- G_GNUC_UNUSED const struct stat *st)
+update_archive_file(gcc_unused Directory *directory,
+ gcc_unused const char *name,
+ gcc_unused const char *suffix,
+ gcc_unused const struct stat *st)
{
return false;
}
diff --git a/src/update_container.c b/src/UpdateContainer.cxx
index bda95dab..27ee89ba 100644
--- a/src/update_container.c
+++ b/src/UpdateContainer.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,19 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_container.h"
-#include "update_internal.h"
-#include "update_db.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateContainer.hxx"
+#include "UpdateInternal.hxx"
+#include "UpdateDatabase.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "mapper.h"
#include "decoder_plugin.h"
-#include "tag.h"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+
+extern "C" {
#include "tag_handler.h"
+}
#include <glib.h>
@@ -39,17 +42,16 @@
*
* The caller must lock the database.
*/
-static struct directory *
-make_directory_if_modified(struct directory *parent, const char *name,
+static Directory *
+make_directory_if_modified(Directory *parent, const char *name,
const struct stat *st)
{
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
// directory exists already
if (directory != NULL) {
if (directory->mtime == st->st_mtime && !walk_discard) {
/* not modified */
- db_unlock();
return NULL;
}
@@ -57,13 +59,13 @@ make_directory_if_modified(struct directory *parent, const char *name,
modified = true;
}
- directory = directory_make_child(parent, name);
+ directory = parent->MakeChild(name);
directory->mtime = st->st_mtime;
return directory;
}
bool
-update_container_file(struct directory *directory,
+update_container_file(Directory *directory,
const char *name,
const struct stat *st,
const struct decoder_plugin *plugin)
@@ -72,8 +74,7 @@ update_container_file(struct directory *directory,
return false;
db_lock();
- struct directory *contdir =
- make_directory_if_modified(directory, name, st);
+ Directory *contdir = make_directory_if_modified(directory, name, st);
if (contdir == NULL) {
/* not modified */
db_unlock();
@@ -83,36 +84,33 @@ update_container_file(struct directory *directory,
contdir->device = DEVICE_CONTAINER;
db_unlock();
- char *const pathname = map_directory_child_fs(directory, name);
+ const Path pathname = map_directory_child_fs(directory, name);
char *vtrack;
unsigned int tnum = 0;
- while ((vtrack = plugin->container_scan(pathname, ++tnum)) != NULL) {
+ while ((vtrack = plugin->container_scan(pathname.c_str(), ++tnum)) != NULL) {
struct song *song = song_file_new(vtrack, contdir);
// shouldn't be necessary but it's there..
song->mtime = st->st_mtime;
- char *child_path_fs = map_directory_child_fs(contdir, vtrack);
+ const Path child_path_fs =
+ map_directory_child_fs(contdir, vtrack);
song->tag = tag_new();
- decoder_plugin_scan_file(plugin, child_path_fs,
+ decoder_plugin_scan_file(plugin, child_path_fs.c_str(),
&add_tag_handler, song->tag);
- g_free(child_path_fs);
db_lock();
- directory_add_song(contdir, song);
+ contdir->AddSong(song);
db_unlock();
modified = true;
- g_message("added %s/%s",
- directory_get_path(directory), vtrack);
+ g_message("added %s/%s", directory->GetPath(), vtrack);
g_free(vtrack);
}
- g_free(pathname);
-
if (tnum == 1) {
db_lock();
delete_directory(contdir);
diff --git a/src/update_container.h b/src/UpdateContainer.hxx
index 7f42c80c..0078730d 100644
--- a/src/update_container.h
+++ b/src/UpdateContainer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,19 +17,18 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_CONTAINER_H
-#define MPD_UPDATE_CONTAINER_H
+#ifndef MPD_UPDATE_CONTAINER_HXX
+#define MPD_UPDATE_CONTAINER_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
struct decoder_plugin;
bool
-update_container_file(struct directory *directory,
+update_container_file(Directory *directory,
const char *name,
const struct stat *st,
const struct decoder_plugin *plugin);
diff --git a/src/update_db.c b/src/UpdateDatabase.cxx
index 8982a53e..984fb1be 100644
--- a/src/update_db.c
+++ b/src/UpdateDatabase.cxx
@@ -18,23 +18,23 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_db.h"
-#include "update_remove.h"
-#include "directory.h"
+#include "UpdateDatabase.hxx"
+#include "UpdateRemove.hxx"
+#include "PlaylistVector.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "playlist_vector.h"
-#include "db_lock.h"
+#include "DatabaseLock.hxx"
#include <glib.h>
#include <assert.h>
void
-delete_song(struct directory *dir, struct song *del)
+delete_song(Directory *dir, struct song *del)
{
assert(del->parent == dir);
/* first, prevent traversers in main task from getting this */
- directory_remove_song(dir, del);
+ dir->RemoveSong(del);
db_unlock(); /* temporary unlock, because update_remove_song() blocks */
@@ -54,9 +54,9 @@ delete_song(struct directory *dir, struct song *del)
* Caller must lock the #db_mutex.
*/
static void
-clear_directory(struct directory *directory)
+clear_directory(Directory *directory)
{
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory)
delete_directory(child);
@@ -68,35 +68,35 @@ clear_directory(struct directory *directory)
}
void
-delete_directory(struct directory *directory)
+delete_directory(Directory *directory)
{
assert(directory->parent != NULL);
clear_directory(directory);
- directory_delete(directory);
+ directory->Delete();
}
bool
-delete_name_in(struct directory *parent, const char *name)
+delete_name_in(Directory *parent, const char *name)
{
bool modified = false;
db_lock();
- struct directory *directory = directory_get_child(parent, name);
+ Directory *directory = parent->FindChild(name);
if (directory != NULL) {
delete_directory(directory);
modified = true;
}
- struct song *song = directory_get_song(parent, name);
+ struct song *song = parent->FindSong(name);
if (song != NULL) {
delete_song(parent, song);
modified = true;
}
- playlist_vector_remove(&parent->playlists, name);
+ parent->playlists.erase(name);
db_unlock();
diff --git a/src/update_db.h b/src/UpdateDatabase.hxx
index 0a9e46b0..7b55ce95 100644
--- a/src/update_db.h
+++ b/src/UpdateDatabase.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,21 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_DB_H
-#define MPD_UPDATE_DB_H
+#ifndef MPD_UPDATE_DATABASE_HXX
+#define MPD_UPDATE_DATABASE_HXX
#include "check.h"
-#include <stdbool.h>
-
-struct directory;
+struct Directory;
struct song;
/**
* Caller must lock the #db_mutex.
*/
void
-delete_song(struct directory *parent, struct song *song);
+delete_song(Directory *parent, struct song *song);
/**
* Recursively free a directory and all its contents.
@@ -39,7 +37,7 @@ delete_song(struct directory *parent, struct song *song);
* Caller must lock the #db_mutex.
*/
void
-delete_directory(struct directory *directory);
+delete_directory(Directory *directory);
/**
* Caller must NOT lock the #db_mutex.
@@ -47,6 +45,6 @@ delete_directory(struct directory *directory);
* @return true if the database was modified
*/
bool
-delete_name_in(struct directory *parent, const char *name);
+delete_name_in(Directory *parent, const char *name);
#endif
diff --git a/src/update.c b/src/UpdateGlue.cxx
index 12eec40c..66463878 100644
--- a/src/update.c
+++ b/src/UpdateGlue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,19 +18,21 @@
*/
#include "config.h"
-#include "update.h"
-#include "update_queue.h"
-#include "update_walk.h"
-#include "update_remove.h"
-#include "update.h"
-#include "database.h"
-#include "mapper.h"
-#include "playlist.h"
-#include "event_pipe.h"
-#include "update.h"
-#include "idle.h"
+#include "UpdateGlue.hxx"
+#include "UpdateQueue.hxx"
+#include "UpdateWalk.hxx"
+#include "UpdateRemove.hxx"
+#include "Mapper.hxx"
+#include "DatabaseSimple.hxx"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
+
+extern "C" {
#include "stats.h"
-#include "main.h"
+}
+
+#include "Main.hxx"
+#include "Partition.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -65,7 +67,7 @@ isUpdatingDB(void)
static void * update_task(void *_path)
{
- const char *path = _path;
+ const char *path = (const char *)_path;
if (path != NULL && *path != 0)
g_debug("starting: %s", path);
@@ -90,7 +92,7 @@ static void * update_task(void *_path)
g_free(_path);
progress = UPDATE_PROGRESS_DONE;
- event_pipe_emit(PIPE_EVENT_UPDATE);
+ GlobalEvents::Emit(GlobalEvents::UPDATE);
return NULL;
}
@@ -118,7 +120,7 @@ update_enqueue(const char *path, bool _discard)
{
assert(g_thread_self() == main_task);
- if (!mapper_has_music_directory())
+ if (!db_is_simple() || !mapper_has_music_directory())
return 0;
if (progress != UPDATE_PROGRESS_IDLE) {
@@ -153,7 +155,7 @@ static void update_finished_event(void)
if (modified) {
/* send "idle" events */
- playlist_increment_version_all(&g_playlist);
+ global_partition->playlist.FullIncrementVersions();
idle_add(IDLE_DATABASE);
}
@@ -171,7 +173,7 @@ static void update_finished_event(void)
void update_global_init(void)
{
- event_pipe_register(PIPE_EVENT_UPDATE, update_finished_event);
+ GlobalEvents::Register(GlobalEvents::UPDATE, update_finished_event);
update_remove_global_init();
update_walk_global_init();
@@ -180,5 +182,4 @@ void update_global_init(void)
void update_global_finish(void)
{
update_walk_global_finish();
- update_remove_global_finish();
}
diff --git a/src/update.h b/src/UpdateGlue.hxx
index 3d586b69..9d546a2a 100644
--- a/src/update.h
+++ b/src/UpdateGlue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_H
-#define MPD_UPDATE_H
-
-#include <stdbool.h>
+#ifndef MPD_UPDATE_GLUE_HXX
+#define MPD_UPDATE_GLUE_HXX
void update_global_init(void);
diff --git a/src/UpdateIO.cxx b/src/UpdateIO.cxx
new file mode 100644
index 00000000..7e262555
--- /dev/null
+++ b/src/UpdateIO.cxx
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h" /* must be first for large file support */
+#include "UpdateIO.hxx"
+#include "Directory.hxx"
+#include "Mapper.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+#include <glib.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+int
+stat_directory(const Directory *directory, struct stat *st)
+{
+ const Path path_fs = map_directory_fs(directory);
+ if (path_fs.IsNull())
+ return -1;
+
+ if (!StatFile(path_fs, *st)) {
+ int error = errno;
+ const std::string path_utf8 = path_fs.ToUTF8();
+ g_warning("Failed to stat %s: %s",
+ path_utf8.c_str(), g_strerror(error));
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+stat_directory_child(const Directory *parent, const char *name,
+ struct stat *st)
+{
+ const Path path_fs = map_directory_child_fs(parent, name);
+ if (path_fs.IsNull())
+ return -1;
+
+ if (!StatFile(path_fs, *st)) {
+ int error = errno;
+ const std::string path_utf8 = path_fs.ToUTF8();
+ g_warning("Failed to stat %s: %s",
+ path_utf8.c_str(), g_strerror(error));
+ return -1;
+ }
+
+ return 0;
+}
+
+bool
+directory_exists(const Directory *directory)
+{
+ const Path path_fs = map_directory_fs(directory);
+ if (path_fs.IsNull())
+ /* invalid path: cannot exist */
+ return false;
+
+ return directory->device == DEVICE_INARCHIVE ||
+ directory->device == DEVICE_CONTAINER
+ ? FileExists(path_fs)
+ : DirectoryExists(path_fs);
+}
+
+bool
+directory_child_is_regular(const Directory *directory,
+ const char *name_utf8)
+{
+ const Path path_fs = map_directory_child_fs(directory, name_utf8);
+ if (path_fs.IsNull())
+ return false;
+
+ return FileExists(path_fs);
+}
+
+bool
+directory_child_access(const Directory *directory,
+ const char *name, int mode)
+{
+#ifdef WIN32
+ /* CheckAccess() is useless on WIN32 */
+ (void)directory;
+ (void)name;
+ (void)mode;
+ return true;
+#else
+ const Path path = map_directory_child_fs(directory, name);
+ if (path.IsNull())
+ /* something went wrong, but that isn't a permission
+ problem */
+ return true;
+
+ return CheckAccess(path, mode) || errno != EACCES;
+#endif
+}
diff --git a/src/update_io.h b/src/UpdateIO.hxx
index 6ff1cceb..ee47b268 100644
--- a/src/update_io.h
+++ b/src/UpdateIO.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,35 +17,34 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_IO_H
-#define MPD_UPDATE_IO_H
+#ifndef MPD_UPDATE_IO_HXX
+#define MPD_UPDATE_IO_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
int
-stat_directory(const struct directory *directory, struct stat *st);
+stat_directory(const Directory *directory, struct stat *st);
int
-stat_directory_child(const struct directory *parent, const char *name,
+stat_directory_child(const Directory *parent, const char *name,
struct stat *st);
bool
-directory_exists(const struct directory *directory);
+directory_exists(const Directory *directory);
bool
-directory_child_is_regular(const struct directory *directory,
+directory_child_is_regular(const Directory *directory,
const char *name_utf8);
/**
* Checks if the given permissions on the mapped file are given.
*/
bool
-directory_child_access(const struct directory *directory,
+directory_child_access(const Directory *directory,
const char *name, int mode);
#endif
diff --git a/src/update_internal.h b/src/UpdateInternal.hxx
index 76ab7bed..50443c08 100644
--- a/src/update_internal.h
+++ b/src/UpdateInternal.hxx
@@ -22,8 +22,6 @@
#include "check.h"
-#include <stdbool.h>
-
extern bool walk_discard;
extern bool modified;
diff --git a/src/update_queue.c b/src/UpdateQueue.cxx
index 2150fa4e..85eb2235 100644
--- a/src/update_queue.c
+++ b/src/UpdateQueue.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "update_queue.h"
+#include "UpdateQueue.hxx"
#include <glib.h>
diff --git a/src/update_queue.h b/src/UpdateQueue.hxx
index 84ba474b..7de06964 100644
--- a/src/update_queue.h
+++ b/src/UpdateQueue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_QUEUE_H
-#define MPD_UPDATE_QUEUE_H
+#ifndef MPD_UPDATE_QUEUE_HXX
+#define MPD_UPDATE_QUEUE_HXX
#include "check.h"
-#include <stdbool.h>
-
unsigned
update_queue_push(const char *path, bool discard, unsigned base);
diff --git a/src/update_remove.c b/src/UpdateRemove.cxx
index f443f5eb..acf8bf46 100644
--- a/src/update_remove.c
+++ b/src/UpdateRemove.cxx
@@ -18,15 +18,19 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_remove.h"
-#include "event_pipe.h"
+#include "UpdateRemove.hxx"
+#include "Playlist.hxx"
+#include "Partition.hxx"
+#include "GlobalEvents.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
#include "song.h"
-#include "playlist.h"
-#include "main.h"
+#include "Main.hxx"
#ifdef ENABLE_SQLITE
-#include "sticker.h"
-#include "song_sticker.h"
+#include "StickerDatabase.hxx"
+#include "SongSticker.hxx"
#endif
#include <glib.h>
@@ -35,8 +39,8 @@
static const struct song *removed_song;
-static GMutex *remove_mutex;
-static GCond *remove_cond;
+static Mutex remove_mutex;
+static Cond remove_cond;
/**
* Safely remove a song from the database. This must be done in the
@@ -59,29 +63,19 @@ song_remove_event(void)
sticker_song_delete(removed_song);
#endif
- playlist_delete_song(&g_playlist, global_player_control, removed_song);
+ global_partition->DeleteSong(*removed_song);
/* clear "removed_song" and send signal to update thread */
- g_mutex_lock(remove_mutex);
+ remove_mutex.lock();
removed_song = NULL;
- g_cond_signal(remove_cond);
- g_mutex_unlock(remove_mutex);
+ remove_cond.signal();
+ remove_mutex.unlock();
}
void
update_remove_global_init(void)
{
- remove_mutex = g_mutex_new();
- remove_cond = g_cond_new();
-
- event_pipe_register(PIPE_EVENT_DELETE, song_remove_event);
-}
-
-void
-update_remove_global_finish(void)
-{
- g_mutex_free(remove_mutex);
- g_cond_free(remove_cond);
+ GlobalEvents::Register(GlobalEvents::DELETE, song_remove_event);
}
void
@@ -91,12 +85,12 @@ update_remove_song(const struct song *song)
removed_song = song;
- event_pipe_emit(PIPE_EVENT_DELETE);
+ GlobalEvents::Emit(GlobalEvents::DELETE);
- g_mutex_lock(remove_mutex);
+ remove_mutex.lock();
while (removed_song != NULL)
- g_cond_wait(remove_cond, remove_mutex);
+ remove_cond.wait(remove_mutex);
- g_mutex_unlock(remove_mutex);
+ remove_mutex.unlock();
}
diff --git a/src/update_remove.h b/src/UpdateRemove.hxx
index 479ef83f..a83e14ae 100644
--- a/src/update_remove.h
+++ b/src/UpdateRemove.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_REMOVE_H
-#define MPD_UPDATE_REMOVE_H
+#ifndef MPD_UPDATE_REMOVE_HXX
+#define MPD_UPDATE_REMOVE_HXX
#include "check.h"
@@ -27,9 +27,6 @@ struct song;
void
update_remove_global_init(void);
-void
-update_remove_global_finish(void);
-
/**
* Sends a signal to the main thread which will in turn remove the
* song: from the sticker database and from the playlist. This
diff --git a/src/update_song.c b/src/UpdateSong.cxx
index 1126ad11..676ba48e 100644
--- a/src/update_song.c
+++ b/src/UpdateSong.cxx
@@ -18,33 +18,33 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_song.h"
-#include "update_internal.h"
-#include "update_io.h"
-#include "update_db.h"
-#include "update_container.h"
-#include "db_lock.h"
-#include "directory.h"
+#include "UpdateSong.hxx"
+#include "UpdateInternal.hxx"
+#include "UpdateIO.hxx"
+#include "UpdateDatabase.hxx"
+#include "UpdateContainer.hxx"
+#include "DatabaseLock.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "decoder_list.h"
#include "decoder_plugin.h"
+#include "DecoderList.hxx"
#include <glib.h>
#include <unistd.h>
static void
-update_song_file2(struct directory *directory,
+update_song_file2(Directory *directory,
const char *name, const struct stat *st,
const struct decoder_plugin *plugin)
{
db_lock();
- struct song *song = directory_get_song(directory, name);
+ struct song *song = directory->FindSong(name);
db_unlock();
if (!directory_child_access(directory, name, R_OK)) {
g_warning("no read permissions on %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
if (song != NULL) {
db_lock();
delete_song(directory, song);
@@ -67,28 +67,27 @@ update_song_file2(struct directory *directory,
}
if (song == NULL) {
- g_debug("reading %s/%s",
- directory_get_path(directory), name);
+ g_debug("reading %s/%s", directory->GetPath(), name);
song = song_file_load(name, directory);
if (song == NULL) {
g_debug("ignoring unrecognized file %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
return;
}
db_lock();
- directory_add_song(directory, song);
+ directory->AddSong(song);
db_unlock();
modified = true;
g_message("added %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
} else if (st->st_mtime != song->mtime || walk_discard) {
g_message("updating %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
if (!song_file_update(song)) {
g_debug("deleting unrecognized file %s/%s",
- directory_get_path(directory), name);
+ directory->GetPath(), name);
db_lock();
delete_song(directory, song);
db_unlock();
@@ -99,12 +98,12 @@ update_song_file2(struct directory *directory,
}
bool
-update_song_file(struct directory *directory,
+update_song_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
const struct decoder_plugin *plugin =
- decoder_plugin_from_suffix(suffix, false);
+ decoder_plugin_from_suffix(suffix, nullptr);
if (plugin == NULL)
return false;
diff --git a/src/update_song.h b/src/UpdateSong.hxx
index cff63f57..60a532e3 100644
--- a/src/update_song.h
+++ b/src/UpdateSong.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,18 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_SONG_H
-#define MPD_UPDATE_SONG_H
+#ifndef MPD_UPDATE_SONG_HXX
+#define MPD_UPDATE_SONG_HXX
#include "check.h"
-#include <stdbool.h>
#include <sys/stat.h>
-struct directory;
+struct Directory;
bool
-update_song_file(struct directory *directory,
+update_song_file(Directory *directory,
const char *name, const char *suffix,
const struct stat *st);
diff --git a/src/update_walk.c b/src/UpdateWalk.cxx
index 8554e8f3..9a2e87d5 100644
--- a/src/update_walk.c
+++ b/src/UpdateWalk.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,22 +18,26 @@
*/
#include "config.h" /* must be first for large file support */
-#include "update_walk.h"
-#include "update_io.h"
-#include "update_db.h"
-#include "update_song.h"
-#include "update_archive.h"
-#include "database.h"
-#include "db_lock.h"
-#include "exclude.h"
-#include "directory.h"
+#include "UpdateWalk.hxx"
+#include "UpdateIO.hxx"
+#include "UpdateDatabase.hxx"
+#include "UpdateSong.hxx"
+#include "UpdateArchive.hxx"
+#include "DatabaseLock.hxx"
+#include "DatabaseSimple.hxx"
+#include "Directory.hxx"
#include "song.h"
-#include "playlist_vector.h"
-#include "uri.h"
-#include "mapper.h"
-#include "path.h"
-#include "playlist_list.h"
+#include "PlaylistVector.hxx"
+#include "PlaylistRegistry.hxx"
+#include "Mapper.hxx"
+#include "ExcludeList.hxx"
#include "conf.h"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+extern "C" {
+#include "uri.h"
+}
#include <glib.h>
@@ -84,7 +88,7 @@ update_walk_global_finish(void)
}
static void
-directory_set_stat(struct directory *dir, const struct stat *st)
+directory_set_stat(Directory *dir, const struct stat *st)
{
dir->inode = st->st_ino;
dir->device = st->st_dev;
@@ -92,43 +96,39 @@ directory_set_stat(struct directory *dir, const struct stat *st)
}
static void
-remove_excluded_from_directory(struct directory *directory,
- GSList *exclude_list)
+remove_excluded_from_directory(Directory *directory,
+ const ExcludeList &exclude_list)
{
db_lock();
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory) {
- char *name_fs = utf8_to_fs_charset(directory_get_name(child));
+ const Path name_fs = Path::FromUTF8(child->GetName());
- if (exclude_list_check(exclude_list, name_fs)) {
+ if (name_fs.IsNull() || exclude_list.Check(name_fs.c_str())) {
delete_directory(child);
modified = true;
}
-
- g_free(name_fs);
}
struct song *song, *ns;
directory_for_each_song_safe(song, ns, directory) {
assert(song->parent == directory);
- char *name_fs = utf8_to_fs_charset(song->uri);
- if (exclude_list_check(exclude_list, name_fs)) {
+ const Path name_fs = Path::FromUTF8(song->uri);
+ if (name_fs.IsNull() || exclude_list.Check(name_fs.c_str())) {
delete_song(directory, song);
modified = true;
}
-
- g_free(name_fs);
}
db_unlock();
}
static void
-purge_deleted_from_directory(struct directory *directory)
+purge_deleted_from_directory(Directory *directory)
{
- struct directory *child, *n;
+ Directory *child, *n;
directory_for_each_child_safe(child, n, directory) {
if (directory_exists(child))
continue;
@@ -142,33 +142,31 @@ purge_deleted_from_directory(struct directory *directory)
struct song *song, *ns;
directory_for_each_song_safe(song, ns, directory) {
- char *path;
- struct stat st;
- if ((path = map_song_fs(song)) == NULL ||
- stat(path, &st) < 0 || !S_ISREG(st.st_mode)) {
+ const Path path = map_song_fs(song);
+ if (path.IsNull() || !FileExists(path)) {
db_lock();
delete_song(directory, song);
db_unlock();
modified = true;
}
-
- g_free(path);
}
- struct playlist_metadata *pm, *np;
- directory_for_each_playlist_safe(pm, np, directory) {
- if (!directory_child_is_regular(directory, pm->name)) {
+ for (auto i = directory->playlists.begin(),
+ end = directory->playlists.end();
+ i != end;) {
+ if (!directory_child_is_regular(directory, i->name.c_str())) {
db_lock();
- playlist_vector_remove(&directory->playlists, pm->name);
+ i = directory->playlists.erase(i);
db_unlock();
- }
+ } else
+ ++i;
}
}
#ifndef G_OS_WIN32
static int
-update_directory_stat(struct directory *directory)
+update_directory_stat(Directory *directory)
{
struct stat st;
if (stat_directory(directory, &st) < 0)
@@ -180,7 +178,7 @@ update_directory_stat(struct directory *directory)
#endif
static int
-find_inode_ancestor(struct directory *parent, ino_t inode, dev_t device)
+find_inode_ancestor(Directory *parent, ino_t inode, dev_t device)
{
#ifndef G_OS_WIN32
while (parent) {
@@ -204,23 +202,24 @@ find_inode_ancestor(struct directory *parent, ino_t inode, dev_t device)
}
static bool
-update_playlist_file2(struct directory *directory,
+update_playlist_file2(Directory *directory,
const char *name, const char *suffix,
const struct stat *st)
{
if (!playlist_suffix_supported(suffix))
return false;
+ PlaylistInfo pi(name, st->st_mtime);
+
db_lock();
- if (playlist_vector_update_or_add(&directory->playlists, name,
- st->st_mtime))
+ if (directory->playlists.UpdateOrInsert(std::move(pi)))
modified = true;
db_unlock();
return true;
}
static bool
-update_regular_file(struct directory *directory,
+update_regular_file(Directory *directory,
const char *name, const struct stat *st)
{
const char *suffix = uri_get_suffix(name);
@@ -233,10 +232,10 @@ update_regular_file(struct directory *directory,
}
static bool
-update_directory(struct directory *directory, const struct stat *st);
+update_directory(Directory *directory, const struct stat *st);
static void
-update_directory_child(struct directory *directory,
+update_directory_child(Directory *directory,
const char *name, const struct stat *st)
{
assert(strchr(name, '/') == NULL);
@@ -248,8 +247,7 @@ update_directory_child(struct directory *directory,
return;
db_lock();
- struct directory *subdir =
- directory_make_child(directory, name);
+ Directory *subdir = directory->MakeChild(name);
db_unlock();
assert(directory == subdir->parent);
@@ -275,16 +273,15 @@ static bool skip_path(const char *path)
G_GNUC_PURE
static bool
-skip_symlink(const struct directory *directory, const char *utf8_name)
+skip_symlink(const Directory *directory, const char *utf8_name)
{
#ifndef WIN32
- char *path_fs = map_directory_child_fs(directory, utf8_name);
- if (path_fs == NULL)
+ const Path path_fs = map_directory_child_fs(directory, utf8_name);
+ if (path_fs.IsNull())
return true;
char buffer[MPD_PATH_MAX];
- ssize_t length = readlink(path_fs, buffer, sizeof(buffer));
- g_free(path_fs);
+ ssize_t length = readlink(path_fs.c_str(), buffer, sizeof(buffer));
if (length < 0)
/* don't skip if this is not a symlink */
return errno != EINVAL;
@@ -348,64 +345,54 @@ skip_symlink(const struct directory *directory, const char *utf8_name)
}
static bool
-update_directory(struct directory *directory, const struct stat *st)
+update_directory(Directory *directory, const struct stat *st)
{
assert(S_ISDIR(st->st_mode));
directory_set_stat(directory, st);
- char *path_fs = map_directory_fs(directory);
- if (path_fs == NULL)
+ const Path path_fs = map_directory_fs(directory);
+ if (path_fs.IsNull())
return false;
- DIR *dir = opendir(path_fs);
+ DIR *dir = opendir(path_fs.c_str());
if (!dir) {
g_warning("Failed to open directory %s: %s",
- path_fs, g_strerror(errno));
- g_free(path_fs);
+ path_fs.c_str(), g_strerror(errno));
return false;
}
- char *exclude_path_fs = g_build_filename(path_fs, ".mpdignore", NULL);
- GSList *exclude_list = exclude_list_load(exclude_path_fs);
- g_free(exclude_path_fs);
+ ExcludeList exclude_list;
+ exclude_list.LoadFile(Path::Build(path_fs, ".mpdignore"));
- g_free(path_fs);
-
- if (exclude_list != NULL)
+ if (!exclude_list.IsEmpty())
remove_excluded_from_directory(directory, exclude_list);
purge_deleted_from_directory(directory);
struct dirent *ent;
while ((ent = readdir(dir))) {
- char *utf8;
+ std::string utf8;
struct stat st2;
- if (skip_path(ent->d_name) ||
- exclude_list_check(exclude_list, ent->d_name))
+ if (skip_path(ent->d_name) || exclude_list.Check(ent->d_name))
continue;
- utf8 = fs_charset_to_utf8(ent->d_name);
- if (utf8 == NULL)
+ utf8 = Path::ToUTF8(ent->d_name);
+ if (utf8.empty())
continue;
- if (skip_symlink(directory, utf8)) {
- modified |= delete_name_in(directory, utf8);
- g_free(utf8);
+ if (skip_symlink(directory, utf8.c_str())) {
+ modified |= delete_name_in(directory, utf8.c_str());
continue;
}
- if (stat_directory_child(directory, utf8, &st2) == 0)
- update_directory_child(directory, utf8, &st2);
+ if (stat_directory_child(directory, utf8.c_str(), &st2) == 0)
+ update_directory_child(directory, utf8.c_str(), &st2);
else
- modified |= delete_name_in(directory, utf8);
-
- g_free(utf8);
+ modified |= delete_name_in(directory, utf8.c_str());
}
- exclude_list_free(exclude_list);
-
closedir(dir);
directory->mtime = st->st_mtime;
@@ -413,11 +400,11 @@ update_directory(struct directory *directory, const struct stat *st)
return true;
}
-static struct directory *
-directory_make_child_checked(struct directory *parent, const char *name_utf8)
+static Directory *
+directory_make_child_checked(Directory *parent, const char *name_utf8)
{
db_lock();
- struct directory *directory = directory_get_child(parent, name_utf8);
+ Directory *directory = parent->FindChild(name_utf8);
db_unlock();
if (directory != NULL)
@@ -434,21 +421,21 @@ directory_make_child_checked(struct directory *parent, const char *name_utf8)
/* if we're adding directory paths, make sure to delete filenames
with potentially the same name */
db_lock();
- struct song *conflicting = directory_get_song(parent, name_utf8);
+ struct song *conflicting = parent->FindSong(name_utf8);
if (conflicting)
delete_song(parent, conflicting);
- directory = directory_new_child(parent, name_utf8);
+ directory = parent->CreateChild(name_utf8);
db_unlock();
directory_set_stat(directory, &st);
return directory;
}
-static struct directory *
+static Directory *
directory_make_uri_parent_checked(const char *uri)
{
- struct directory *directory = db_get_root();
+ Directory *directory = db_get_root();
char *duplicated = g_strdup(uri);
char *name_utf8 = duplicated, *slash;
@@ -472,7 +459,7 @@ directory_make_uri_parent_checked(const char *uri)
static void
update_uri(const char *uri)
{
- struct directory *parent = directory_make_uri_parent_checked(uri);
+ Directory *parent = directory_make_uri_parent_checked(uri);
if (parent == NULL)
return;
@@ -497,7 +484,7 @@ update_walk(const char *path, bool discard)
if (path != NULL && !isRootDirectory(path)) {
update_uri(path);
} else {
- struct directory *directory = db_get_root();
+ Directory *directory = db_get_root();
struct stat st;
if (stat_directory(directory, &st) == 0)
diff --git a/src/update_walk.h b/src/UpdateWalk.hxx
index ab1e41fb..62c0d0a8 100644
--- a/src/update_walk.h
+++ b/src/UpdateWalk.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_UPDATE_WALK_H
-#define MPD_UPDATE_WALK_H
+#ifndef MPD_UPDATE_WALK_HXX
+#define MPD_UPDATE_WALK_HXX
#include "check.h"
-#include <stdbool.h>
-
void
update_walk_global_init(void);
diff --git a/src/volume.c b/src/Volume.cxx
index d3ce47dd..116f4aa1 100644
--- a/src/volume.c
+++ b/src/Volume.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,14 @@
*/
#include "config.h"
-#include "volume.h"
-#include "conf.h"
-#include "idle.h"
-#include "pcm_volume.h"
-#include "output_all.h"
-#include "mixer_control.h"
-#include "mixer_all.h"
-#include "mixer_type.h"
-#include "event_pipe.h"
+#include "Volume.hxx"
+#include "MixerAll.hxx"
+#include "Idle.hxx"
+#include "GlobalEvents.hxx"
#include <glib.h>
#include <assert.h>
-#include <math.h>
-#include <string.h>
#include <stdlib.h>
#undef G_LOG_DOMAIN
@@ -48,7 +41,7 @@ static int last_hardware_volume = -1;
static GTimer *hardware_volume_timer;
/**
- * Handler for #PIPE_EVENT_MIXER.
+ * Handler for #GlobalEvents::MIXER.
*/
static void
mixer_event_callback(void)
@@ -69,7 +62,7 @@ void volume_init(void)
{
hardware_volume_timer = g_timer_new();
- event_pipe_register(PIPE_EVENT_MIXER, mixer_event_callback);
+ GlobalEvents::Register(GlobalEvents::MIXER, mixer_event_callback);
}
int volume_level_get(void)
diff --git a/src/volume.h b/src/Volume.hxx
index b08899a8..024b2840 100644
--- a/src/volume.h
+++ b/src/Volume.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_VOLUME_H
-#define MPD_VOLUME_H
+#ifndef MPD_VOLUME_HXX
+#define MPD_VOLUME_HXX
-#include <stdbool.h>
#include <stdio.h>
void volume_init(void);
diff --git a/src/main_win32.c b/src/Win32Main.cxx
index aac7ad88..0cd5e445 100644
--- a/src/main_win32.c
+++ b/src/Win32Main.cxx
@@ -18,12 +18,15 @@
*/
#include "config.h"
-#include "main.h"
+#include "Main.hxx"
#ifdef WIN32
#include "mpd_error.h"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
+
+#include <cstdlib>
+#include <atomic>
#include <glib.h>
@@ -32,7 +35,7 @@
static int service_argc;
static char **service_argv;
static char service_name[] = "";
-static BOOL ignore_console_events;
+static std::atomic_bool running;
static SERVICE_STATUS_HANDLE service_handle;
static void WINAPI
@@ -68,7 +71,7 @@ service_dispatcher(G_GNUC_UNUSED DWORD control, G_GNUC_UNUSED DWORD event_type,
switch (control) {
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
- event_pipe_emit(PIPE_EVENT_SHUTDOWN);
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
return NO_ERROR;
default:
return NO_ERROR;
@@ -103,8 +106,22 @@ console_handler(DWORD event)
switch (event) {
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
- if (!ignore_console_events)
- event_pipe_emit(PIPE_EVENT_SHUTDOWN);
+ if (running.load()) {
+ // Recent msdn docs that process is terminated
+ // if this function returns TRUE.
+ // We initiate correct shutdown sequence (if possible).
+ // Once main() returns CRT will terminate our process
+ // regardless our thread is still active.
+ // If this did not happen within 3 seconds
+ // let's shutdown anyway.
+ GlobalEvents::Emit(GlobalEvents::SHUTDOWN);
+ // Under debugger it's better to wait indefinitely
+ // to allow debugging of shutdown code.
+ Sleep(IsDebuggerPresent() ? INFINITE : 3000);
+ }
+ // If we're not running main loop there is no chance for
+ // clean shutdown.
+ std::exit(EXIT_FAILURE);
return TRUE;
default:
return FALSE;
@@ -125,8 +142,8 @@ int win32_main(int argc, char *argv[])
error_code = GetLastError();
if (error_code == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
/* running as console app */
+ running.store(false);
SetConsoleTitle("Music Player Daemon");
- ignore_console_events = TRUE;
SetConsoleCtrlHandler(console_handler, TRUE);
return mpd_main(argc, argv);
}
@@ -140,7 +157,7 @@ void win32_app_started()
if (service_handle != 0)
service_notify_status(SERVICE_RUNNING);
else
- ignore_console_events = FALSE;
+ running.store(true);
}
void win32_app_stopping()
@@ -148,7 +165,7 @@ void win32_app_stopping()
if (service_handle != 0)
service_notify_status(SERVICE_STOP_PENDING);
else
- ignore_console_events = TRUE;
+ running.store(false);
}
#endif
diff --git a/src/zeroconf-avahi.c b/src/ZeroconfAvahi.cxx
index f2cc5359..4764ad75 100644
--- a/src/zeroconf-avahi.c
+++ b/src/ZeroconfAvahi.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,10 @@
*/
#include "config.h"
-#include "zeroconf-internal.h"
-#include "listen.h"
+#include "ZeroconfAvahi.hxx"
+#include "ZeroconfInternal.hxx"
+#include "Listen.hxx"
+#include "event/Loop.hxx"
#include "mpd_error.h"
#include <glib.h>
@@ -116,7 +118,8 @@ static void avahiRegisterService(AvahiClient * c)
* if that's better. */
ret = avahi_entry_group_add_service(avahiGroup,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
- 0, avahiName, SERVICE_TYPE, NULL,
+ AvahiPublishFlags(0),
+ avahiName, SERVICE_TYPE, NULL,
NULL, listen_port, NULL);
if (ret < 0) {
g_warning("Failed to add service %s: %s", SERVICE_TYPE,
@@ -213,7 +216,8 @@ static void avahiClientCallback(AvahiClient * c, AvahiClientState state,
}
}
-void init_avahi(const char *serviceName)
+void
+AvahiInit(EventLoop &loop, const char *serviceName)
{
int error;
g_debug("Initializing interface");
@@ -225,7 +229,8 @@ void init_avahi(const char *serviceName)
avahiRunning = 1;
- avahi_glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
+ avahi_glib_poll = avahi_glib_poll_new(loop.GetContext(),
+ G_PRIORITY_DEFAULT);
avahi_poll = avahi_glib_poll_get(avahi_glib_poll);
avahiClient = avahi_client_new(avahi_poll, AVAHI_CLIENT_NO_FAIL,
@@ -234,16 +239,12 @@ void init_avahi(const char *serviceName)
if (!avahiClient) {
g_warning("Failed to create client: %s",
avahi_strerror(error));
- goto fail;
+ AvahiDeinit();
}
-
- return;
-
-fail:
- avahi_finish();
}
-void avahi_finish(void)
+void
+AvahiDeinit(void)
{
g_debug("Shutting down interface");
diff --git a/src/ZeroconfAvahi.hxx b/src/ZeroconfAvahi.hxx
new file mode 100644
index 00000000..bb046350
--- /dev/null
+++ b/src/ZeroconfAvahi.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ZEROCONF_AVAHI_HXX
+#define MPD_ZEROCONF_AVAHI_HXX
+
+class EventLoop;
+
+void
+AvahiInit(EventLoop &loop, const char *service_name);
+
+void
+AvahiDeinit();
+
+#endif
diff --git a/src/zeroconf-bonjour.c b/src/ZeroconfBonjour.cxx
index 0f216aad..959c9024 100644
--- a/src/zeroconf-bonjour.c
+++ b/src/ZeroconfBonjour.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,11 @@
*/
#include "config.h"
-#include "zeroconf-internal.h"
-#include "listen.h"
+#include "ZeroconfBonjour.hxx"
+#include "ZeroconfInternal.hxx"
+#include "Listen.hxx"
+#include "event/SocketMonitor.hxx"
+#include "gcc.h"
#include <glib.h>
@@ -28,8 +31,29 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "bonjour"
-static DNSServiceRef dnsReference;
-static GIOChannel *bonjour_channel;
+class BonjourMonitor final : public SocketMonitor {
+ DNSServiceRef service_ref;
+
+public:
+ BonjourMonitor(EventLoop &_loop, DNSServiceRef _service_ref)
+ :SocketMonitor(DNSServiceRefSockFD(_service_ref), _loop),
+ service_ref(_service_ref) {
+ ScheduleRead();
+ }
+
+ ~BonjourMonitor() {
+ Steal();
+ DNSServiceRefDeallocate(service_ref);
+ }
+
+protected:
+ virtual bool OnSocketReady(gcc_unused unsigned flags) override {
+ DNSServiceProcessResult(service_ref);
+ return false;
+ }
+};
+
+static BonjourMonitor *bonjour_monitor;
static void
dnsRegisterCallback(G_GNUC_UNUSED DNSServiceRef sdRef,
@@ -42,26 +66,18 @@ dnsRegisterCallback(G_GNUC_UNUSED DNSServiceRef sdRef,
if (errorCode != kDNSServiceErr_NoError) {
g_warning("Failed to register zeroconf service.");
- bonjour_finish();
+ bonjour_monitor->Cancel();
} else {
g_debug("Registered zeroconf service with name '%s'", name);
}
}
-static gboolean
-bonjour_channel_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- G_GNUC_UNUSED gpointer data)
-{
- DNSServiceProcessResult(dnsReference);
-
- return dnsReference != NULL;
-}
-
-void init_zeroconf_osx(const char *serviceName)
+void
+BonjourInit(EventLoop &loop, const char *service_name)
{
+ DNSServiceRef dnsReference;
DNSServiceErrorType error = DNSServiceRegister(&dnsReference,
- 0, 0, serviceName,
+ 0, 0, service_name,
SERVICE_TYPE, NULL, NULL,
g_htons(listen_port), 0,
NULL,
@@ -78,20 +94,11 @@ void init_zeroconf_osx(const char *serviceName)
return;
}
- bonjour_channel = g_io_channel_unix_new(DNSServiceRefSockFD(dnsReference));
- g_io_add_watch(bonjour_channel, G_IO_IN, bonjour_channel_event, NULL);
+ bonjour_monitor = new BonjourMonitor(loop, dnsReference);
}
-void bonjour_finish(void)
+void
+BonjourDeinit()
{
- if (bonjour_channel != NULL) {
- g_io_channel_unref(bonjour_channel);
- bonjour_channel = NULL;
- }
-
- if (dnsReference != NULL) {
- DNSServiceRefDeallocate(dnsReference);
- dnsReference = NULL;
- g_debug("Deregistered Zeroconf service.");
- }
+ delete bonjour_monitor;
}
diff --git a/src/ZeroconfBonjour.hxx b/src/ZeroconfBonjour.hxx
new file mode 100644
index 00000000..d91fe9a0
--- /dev/null
+++ b/src/ZeroconfBonjour.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ZEROCONF_BONJOUR_HXX
+#define MPD_ZEROCONF_BONJOUR_HXX
+
+class EventLoop;
+
+void
+BonjourInit(EventLoop &loop, const char *service_name);
+
+void
+BonjourDeinit();
+
+#endif
diff --git a/src/zeroconf.c b/src/ZeroconfGlue.cxx
index 4a399e4a..14e1d086 100644
--- a/src/zeroconf.c
+++ b/src/ZeroconfGlue.cxx
@@ -18,10 +18,12 @@
*/
#include "config.h"
-#include "zeroconf.h"
-#include "zeroconf-internal.h"
+#include "ZeroconfGlue.hxx"
+#include "ZeroconfAvahi.hxx"
+#include "ZeroconfBonjour.hxx"
#include "conf.h"
-#include "listen.h"
+#include "Listen.hxx"
+#include "gcc.h"
#include <glib.h>
@@ -34,7 +36,8 @@
static int zeroconfEnabled;
-void initZeroconf(void)
+void
+ZeroconfInit(gcc_unused EventLoop &loop)
{
const char *serviceName;
@@ -52,24 +55,25 @@ void initZeroconf(void)
serviceName = config_get_string(CONF_ZEROCONF_NAME, SERVICE_NAME);
#ifdef HAVE_AVAHI
- init_avahi(serviceName);
+ AvahiInit(loop, serviceName);
#endif
#ifdef HAVE_BONJOUR
- init_zeroconf_osx(serviceName);
+ BonjourInit(loop, serviceName);
#endif
}
-void finishZeroconf(void)
+void
+ZeroconfDeinit()
{
if (!zeroconfEnabled)
return;
#ifdef HAVE_AVAHI
- avahi_finish();
+ AvahiDeinit();
#endif /* HAVE_AVAHI */
#ifdef HAVE_BONJOUR
- bonjour_finish();
+ BonjourDeinit();
#endif
}
diff --git a/src/zeroconf.h b/src/ZeroconfGlue.hxx
index 8e33a3d8..2a291ce2 100644
--- a/src/zeroconf.h
+++ b/src/ZeroconfGlue.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,30 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ZEROCONF_H
-#define MPD_ZEROCONF_H
+#ifndef MPD_ZEROCONF_GLUE_HXX
+#define MPD_ZEROCONF_GLUE_HXX
#include "check.h"
+class EventLoop;
+
#ifdef HAVE_ZEROCONF
-void initZeroconf(void);
-void finishZeroconf(void);
+void
+ZeroconfInit(EventLoop &loop);
+
+void
+ZeroconfDeinit();
#else /* ! HAVE_ZEROCONF */
-static void initZeroconf(void) { }
-static void finishZeroconf(void) { }
+static inline void
+ZeroconfInit(EventLoop &)
+{}
+
+static inline void
+ZeroconfDeinit()
+{}
#endif /* ! HAVE_ZEROCONF */
diff --git a/src/zeroconf-internal.h b/src/ZeroconfInternal.hxx
index 983e5c55..2eadcff6 100644
--- a/src/zeroconf-internal.h
+++ b/src/ZeroconfInternal.hxx
@@ -23,12 +23,4 @@
/* The dns-sd service type qualifier to publish */
#define SERVICE_TYPE "_mpd._tcp"
-void init_avahi(const char *service_name);
-
-void avahi_finish(void);
-
-void init_zeroconf_osx(const char *service_name);
-
-void bonjour_finish(void);
-
#endif
diff --git a/src/archive/Bzip2ArchivePlugin.cxx b/src/archive/Bzip2ArchivePlugin.cxx
new file mode 100644
index 00000000..182b9ccd
--- /dev/null
+++ b/src/archive/Bzip2ArchivePlugin.cxx
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * single bz2 archive handling (requires libbz2)
+ */
+
+#include "config.h"
+#include "Bzip2ArchivePlugin.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "ArchiveVisitor.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
+#include "util/RefCount.hxx"
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <glib.h>
+#include <bzlib.h>
+
+#ifdef HAVE_OLDER_BZIP2
+#define BZ2_bzDecompressInit bzDecompressInit
+#define BZ2_bzDecompress bzDecompress
+#endif
+
+class Bzip2ArchiveFile final : public ArchiveFile {
+public:
+ RefCount ref;
+
+ char *const name;
+ struct input_stream *const istream;
+
+ Bzip2ArchiveFile(const char *path, input_stream *_is)
+ :ArchiveFile(bz2_archive_plugin),
+ name(g_path_get_basename(path)),
+ istream(_is) {
+ // remove .bz2 suffix
+ size_t len = strlen(name);
+ if (len > 4)
+ name[len - 4] = 0;
+ }
+
+ ~Bzip2ArchiveFile() {
+ input_stream_close(istream);
+ }
+
+ void Ref() {
+ ref.Increment();
+ }
+
+ void Unref() {
+ if (!ref.Decrement())
+ return;
+
+ g_free(name);
+ delete this;
+ }
+
+ virtual void Close() override {
+ Unref();
+ }
+
+ virtual void Visit(ArchiveVisitor &visitor) override {
+ visitor.VisitArchiveEntry(name);
+ }
+
+ virtual input_stream *OpenStream(const char *path,
+ Mutex &mutex, Cond &cond,
+ GError **error_r) override;
+};
+
+struct Bzip2InputStream {
+ struct input_stream base;
+
+ Bzip2ArchiveFile *archive;
+
+ bool eof;
+
+ bz_stream bzstream;
+
+ char buffer[5000];
+
+ Bzip2InputStream(Bzip2ArchiveFile &context, const char *uri,
+ Mutex &mutex, Cond &cond);
+ ~Bzip2InputStream();
+
+ bool Open(GError **error_r);
+ void Close();
+};
+
+extern const struct input_plugin bz2_inputplugin;
+
+static inline GQuark
+bz2_quark(void)
+{
+ return g_quark_from_static_string("bz2");
+}
+
+/* single archive handling allocation helpers */
+
+inline bool
+Bzip2InputStream::Open(GError **error_r)
+{
+ bzstream.bzalloc = nullptr;
+ bzstream.bzfree = nullptr;
+ bzstream.opaque = nullptr;
+
+ bzstream.next_in = (char *)buffer;
+ bzstream.avail_in = 0;
+
+ int ret = BZ2_bzDecompressInit(&bzstream, 0, 0);
+ if (ret != BZ_OK) {
+ g_set_error(error_r, bz2_quark(), ret,
+ "BZ2_bzDecompressInit() has failed");
+ return false;
+ }
+
+ base.ready = true;
+ return true;
+}
+
+inline void
+Bzip2InputStream::Close()
+{
+ BZ2_bzDecompressEnd(&bzstream);
+}
+
+/* archive open && listing routine */
+
+static ArchiveFile *
+bz2_open(const char *pathname, GError **error_r)
+{
+ static Mutex mutex;
+ static Cond cond;
+ input_stream *is = input_stream_open(pathname, mutex, cond, error_r);
+ if (is == nullptr)
+ return nullptr;
+
+ return new Bzip2ArchiveFile(pathname, is);
+}
+
+/* single archive handling */
+
+Bzip2InputStream::Bzip2InputStream(Bzip2ArchiveFile &_context, const char *uri,
+ Mutex &mutex, Cond &cond)
+ :base(bz2_inputplugin, uri, mutex, cond),
+ archive(&_context), eof(false)
+{
+ archive->Ref();
+}
+
+Bzip2InputStream::~Bzip2InputStream()
+{
+ archive->Unref();
+}
+
+input_stream *
+Bzip2ArchiveFile::OpenStream(const char *path,
+ Mutex &mutex, Cond &cond,
+ GError **error_r)
+{
+ Bzip2InputStream *bis = new Bzip2InputStream(*this, path, mutex, cond);
+ if (!bis->Open(error_r)) {
+ delete bis;
+ return NULL;
+ }
+
+ return &bis->base;
+}
+
+static void
+bz2_is_close(struct input_stream *is)
+{
+ Bzip2InputStream *bis = (Bzip2InputStream *)is;
+
+ bis->Close();
+ delete bis;
+}
+
+static bool
+bz2_fillbuffer(Bzip2InputStream *bis, GError **error_r)
+{
+ size_t count;
+ bz_stream *bzstream;
+
+ bzstream = &bis->bzstream;
+
+ if (bzstream->avail_in > 0)
+ return true;
+
+ count = input_stream_read(bis->archive->istream,
+ bis->buffer, sizeof(bis->buffer),
+ error_r);
+ if (count == 0)
+ return false;
+
+ bzstream->next_in = bis->buffer;
+ bzstream->avail_in = count;
+ return true;
+}
+
+static size_t
+bz2_is_read(struct input_stream *is, void *ptr, size_t length,
+ GError **error_r)
+{
+ Bzip2InputStream *bis = (Bzip2InputStream *)is;
+ bz_stream *bzstream;
+ int bz_result;
+ size_t nbytes = 0;
+
+ if (bis->eof)
+ return 0;
+
+ bzstream = &bis->bzstream;
+ bzstream->next_out = (char *)ptr;
+ bzstream->avail_out = length;
+
+ do {
+ if (!bz2_fillbuffer(bis, error_r))
+ return 0;
+
+ bz_result = BZ2_bzDecompress(bzstream);
+
+ if (bz_result == BZ_STREAM_END) {
+ bis->eof = true;
+ break;
+ }
+
+ if (bz_result != BZ_OK) {
+ g_set_error(error_r, bz2_quark(), bz_result,
+ "BZ2_bzDecompress() has failed");
+ return 0;
+ }
+ } while (bzstream->avail_out == length);
+
+ nbytes = length - bzstream->avail_out;
+ is->offset += nbytes;
+
+ return nbytes;
+}
+
+static bool
+bz2_is_eof(struct input_stream *is)
+{
+ Bzip2InputStream *bis = (Bzip2InputStream *)is;
+
+ return bis->eof;
+}
+
+/* exported structures */
+
+static const char *const bz2_extensions[] = {
+ "bz2",
+ NULL
+};
+
+const struct input_plugin bz2_inputplugin = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ bz2_is_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ bz2_is_read,
+ bz2_is_eof,
+ nullptr,
+};
+
+const struct archive_plugin bz2_archive_plugin = {
+ "bz2",
+ nullptr,
+ nullptr,
+ bz2_open,
+ bz2_extensions,
+};
+
diff --git a/src/archive/bz2_archive_plugin.h b/src/archive/Bzip2ArchivePlugin.hxx
index 46c69a66..a7933a7a 100644
--- a/src/archive/bz2_archive_plugin.h
+++ b/src/archive/Bzip2ArchivePlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_BZ2_H
-#define MPD_ARCHIVE_BZ2_H
+#ifndef MPD_ARCHIVE_BZ2_HXX
+#define MPD_ARCHIVE_BZ2_HXX
extern const struct archive_plugin bz2_archive_plugin;
diff --git a/src/archive/Iso9660ArchivePlugin.cxx b/src/archive/Iso9660ArchivePlugin.cxx
new file mode 100644
index 00000000..97fd8bd5
--- /dev/null
+++ b/src/archive/Iso9660ArchivePlugin.cxx
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * iso archive handling (requires cdio, and iso9660)
+ */
+
+#include "config.h"
+#include "Iso9660ArchivePlugin.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "ArchiveVisitor.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
+#include "util/RefCount.hxx"
+
+#include <cdio/cdio.h>
+#include <cdio/iso9660.h>
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#define CEILING(x, y) ((x+(y-1))/y)
+
+class Iso9660ArchiveFile final : public ArchiveFile {
+public:
+ RefCount ref;
+
+ iso9660_t *iso;
+
+ Iso9660ArchiveFile(iso9660_t *_iso)
+ :ArchiveFile(iso9660_archive_plugin), iso(_iso) {}
+
+ ~Iso9660ArchiveFile() {
+ iso9660_close(iso);
+ }
+
+ void Unref() {
+ if (ref.Decrement())
+ delete this;
+ }
+
+ void Visit(const char *path, ArchiveVisitor &visitor);
+
+ virtual void Close() override {
+ Unref();
+ }
+
+ virtual void Visit(ArchiveVisitor &visitor) override;
+
+ virtual input_stream *OpenStream(const char *path,
+ Mutex &mutex, Cond &cond,
+ GError **error_r) override;
+};
+
+extern const struct input_plugin iso9660_input_plugin;
+
+static inline GQuark
+iso9660_quark(void)
+{
+ return g_quark_from_static_string("iso9660");
+}
+
+/* archive open && listing routine */
+
+inline void
+Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor)
+{
+ CdioList_t *entlist;
+ CdioListNode_t *entnode;
+ iso9660_stat_t *statbuf;
+ char pathname[4096];
+
+ entlist = iso9660_ifs_readdir (iso, psz_path);
+ if (!entlist) {
+ return;
+ }
+ /* Iterate over the list of nodes that iso9660_ifs_readdir gives */
+ _CDIO_LIST_FOREACH (entnode, entlist) {
+ statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
+
+ strcpy(pathname, psz_path);
+ strcat(pathname, statbuf->filename);
+
+ if (iso9660_stat_s::_STAT_DIR == statbuf->type ) {
+ if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
+ strcat(pathname, "/");
+ Visit(pathname, visitor);
+ }
+ } else {
+ //remove leading /
+ visitor.VisitArchiveEntry(pathname + 1);
+ }
+ }
+ _cdio_list_free (entlist, true);
+}
+
+static ArchiveFile *
+iso9660_archive_open(const char *pathname, GError **error_r)
+{
+ /* open archive */
+ auto iso = iso9660_open(pathname);
+ if (iso == nullptr) {
+ g_set_error(error_r, iso9660_quark(), 0,
+ "Failed to open ISO9660 file %s", pathname);
+ return NULL;
+ }
+
+ return new Iso9660ArchiveFile(iso);
+}
+
+void
+Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
+{
+ Visit("/", visitor);
+}
+
+/* single archive handling */
+
+struct Iso9660InputStream {
+ struct input_stream base;
+
+ Iso9660ArchiveFile *archive;
+
+ iso9660_stat_t *statbuf;
+ size_t max_blocks;
+
+ Iso9660InputStream(Iso9660ArchiveFile &_archive, const char *uri,
+ Mutex &mutex, Cond &cond,
+ iso9660_stat_t *_statbuf)
+ :base(iso9660_input_plugin, uri, mutex, cond),
+ archive(&_archive), statbuf(_statbuf),
+ max_blocks(CEILING(statbuf->size, ISO_BLOCKSIZE)) {
+
+ base.ready = true;
+ base.size = statbuf->size;
+
+ archive->ref.Increment();
+ }
+
+ ~Iso9660InputStream() {
+ free(statbuf);
+ archive->Unref();
+ }
+};
+
+input_stream *
+Iso9660ArchiveFile::OpenStream(const char *pathname,
+ Mutex &mutex, Cond &cond,
+ GError **error_r)
+{
+ auto statbuf = iso9660_ifs_stat_translate(iso, pathname);
+ if (statbuf == nullptr) {
+ g_set_error(error_r, iso9660_quark(), 0,
+ "not found in the ISO file: %s", pathname);
+ return NULL;
+ }
+
+ Iso9660InputStream *iis =
+ new Iso9660InputStream(*this, pathname, mutex, cond,
+ statbuf);
+ return &iis->base;
+}
+
+static void
+iso9660_input_close(struct input_stream *is)
+{
+ Iso9660InputStream *iis = (Iso9660InputStream *)is;
+
+ delete iis;
+}
+
+
+static size_t
+iso9660_input_read(struct input_stream *is, void *ptr, size_t size, GError **error_r)
+{
+ Iso9660InputStream *iis = (Iso9660InputStream *)is;
+ int toread, readed = 0;
+ int no_blocks, cur_block;
+ size_t left_bytes = iis->statbuf->size - is->offset;
+
+ size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
+
+ if (left_bytes < size) {
+ toread = left_bytes;
+ no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
+ } else {
+ toread = size;
+ no_blocks = toread / ISO_BLOCKSIZE;
+ }
+ if (no_blocks > 0) {
+
+ cur_block = is->offset / ISO_BLOCKSIZE;
+
+ readed = iso9660_iso_seek_read (iis->archive->iso, ptr,
+ iis->statbuf->lsn + cur_block, no_blocks);
+
+ if (readed != no_blocks * ISO_BLOCKSIZE) {
+ g_set_error(error_r, iso9660_quark(), 0,
+ "error reading ISO file at lsn %lu",
+ (long unsigned int) cur_block);
+ return 0;
+ }
+ if (left_bytes < size) {
+ readed = left_bytes;
+ }
+
+ is->offset += readed;
+ }
+ return readed;
+}
+
+static bool
+iso9660_input_eof(struct input_stream *is)
+{
+ return is->offset == is->size;
+}
+
+/* exported structures */
+
+static const char *const iso9660_archive_extensions[] = {
+ "iso",
+ NULL
+};
+
+const struct input_plugin iso9660_input_plugin = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ iso9660_input_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ iso9660_input_read,
+ iso9660_input_eof,
+ nullptr,
+};
+
+const struct archive_plugin iso9660_archive_plugin = {
+ "iso",
+ nullptr,
+ nullptr,
+ iso9660_archive_open,
+ iso9660_archive_extensions,
+};
diff --git a/src/archive/iso9660_archive_plugin.h b/src/archive/Iso9660ArchivePlugin.hxx
index 47dc6e47..6fbab615 100644
--- a/src/archive/iso9660_archive_plugin.h
+++ b/src/archive/Iso9660ArchivePlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_ISO9660_H
-#define MPD_ARCHIVE_ISO9660_H
+#ifndef MPD_ARCHIVE_ISO9660_HXX
+#define MPD_ARCHIVE_ISO9660_HXX
extern const struct archive_plugin iso9660_archive_plugin;
diff --git a/src/archive/ZzipArchivePlugin.cxx b/src/archive/ZzipArchivePlugin.cxx
new file mode 100644
index 00000000..d0db7aa3
--- /dev/null
+++ b/src/archive/ZzipArchivePlugin.cxx
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * zip archive handling (requires zziplib)
+ */
+
+#include "config.h"
+#include "ZzipArchivePlugin.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "ArchiveVisitor.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
+#include "util/RefCount.hxx"
+
+#include <zzip/zzip.h>
+#include <glib.h>
+#include <string.h>
+
+class ZzipArchiveFile final : public ArchiveFile {
+public:
+ RefCount ref;
+
+ ZZIP_DIR *const dir;
+
+ ZzipArchiveFile(ZZIP_DIR *_dir)
+ :ArchiveFile(zzip_archive_plugin), dir(_dir) {}
+
+ ~ZzipArchiveFile() {
+ zzip_dir_close(dir);
+ }
+
+ void Unref() {
+ if (ref.Decrement())
+ delete this;
+ }
+
+ virtual void Close() override {
+ Unref();
+ }
+
+ virtual void Visit(ArchiveVisitor &visitor) override;
+
+ virtual input_stream *OpenStream(const char *path,
+ Mutex &mutex, Cond &cond,
+ GError **error_r) override;
+};
+
+extern const struct input_plugin zzip_input_plugin;
+
+static inline GQuark
+zzip_quark(void)
+{
+ return g_quark_from_static_string("zzip");
+}
+
+/* archive open && listing routine */
+
+static ArchiveFile *
+zzip_archive_open(const char *pathname, GError **error_r)
+{
+ ZZIP_DIR *dir = zzip_dir_open(pathname, NULL);
+ if (dir == nullptr) {
+ g_set_error(error_r, zzip_quark(), 0,
+ "Failed to open ZIP file %s", pathname);
+ return NULL;
+ }
+
+ return new ZzipArchiveFile(dir);
+}
+
+inline void
+ZzipArchiveFile::Visit(ArchiveVisitor &visitor)
+{
+ zzip_rewinddir(dir);
+
+ ZZIP_DIRENT dirent;
+ while (zzip_dir_read(dir, &dirent))
+ //add only files
+ if (dirent.st_size > 0)
+ visitor.VisitArchiveEntry(dirent.d_name);
+}
+
+/* single archive handling */
+
+struct ZzipInputStream {
+ struct input_stream base;
+
+ ZzipArchiveFile *archive;
+
+ ZZIP_FILE *file;
+
+ ZzipInputStream(ZzipArchiveFile &_archive, const char *uri,
+ Mutex &mutex, Cond &cond,
+ ZZIP_FILE *_file)
+ :base(zzip_input_plugin, uri, mutex, cond),
+ archive(&_archive), file(_file) {
+ base.ready = true;
+ //we are seekable (but its not recommendent to do so)
+ base.seekable = true;
+
+ ZZIP_STAT z_stat;
+ zzip_file_stat(file, &z_stat);
+ base.size = z_stat.st_size;
+
+ archive->ref.Increment();
+ }
+
+ ~ZzipInputStream() {
+ zzip_file_close(file);
+ archive->Unref();
+ }
+};
+
+input_stream *
+ZzipArchiveFile::OpenStream(const char *pathname,
+ Mutex &mutex, Cond &cond,
+ GError **error_r)
+{
+ ZZIP_FILE *_file = zzip_file_open(dir, pathname, 0);
+ if (_file == nullptr) {
+ g_set_error(error_r, zzip_quark(), 0,
+ "not found in the ZIP file: %s", pathname);
+ return NULL;
+ }
+
+ ZzipInputStream *zis =
+ new ZzipInputStream(*this, pathname,
+ mutex, cond,
+ _file);
+ return &zis->base;
+}
+
+static void
+zzip_input_close(struct input_stream *is)
+{
+ ZzipInputStream *zis = (ZzipInputStream *)is;
+
+ delete zis;
+}
+
+static size_t
+zzip_input_read(struct input_stream *is, void *ptr, size_t size,
+ GError **error_r)
+{
+ ZzipInputStream *zis = (ZzipInputStream *)is;
+ int ret;
+
+ ret = zzip_file_read(zis->file, ptr, size);
+ if (ret < 0) {
+ g_set_error(error_r, zzip_quark(), ret,
+ "zzip_file_read() has failed");
+ return 0;
+ }
+
+ is->offset = zzip_tell(zis->file);
+
+ return ret;
+}
+
+static bool
+zzip_input_eof(struct input_stream *is)
+{
+ ZzipInputStream *zis = (ZzipInputStream *)is;
+
+ return (goffset)zzip_tell(zis->file) == is->size;
+}
+
+static bool
+zzip_input_seek(struct input_stream *is,
+ goffset offset, int whence, GError **error_r)
+{
+ ZzipInputStream *zis = (ZzipInputStream *)is;
+ zzip_off_t ofs = zzip_seek(zis->file, offset, whence);
+ if (ofs != -1) {
+ g_set_error(error_r, zzip_quark(), ofs,
+ "zzip_seek() has failed");
+ is->offset = ofs;
+ return true;
+ }
+ return false;
+}
+
+/* exported structures */
+
+static const char *const zzip_archive_extensions[] = {
+ "zip",
+ NULL
+};
+
+const struct input_plugin zzip_input_plugin = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ zzip_input_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ zzip_input_read,
+ zzip_input_eof,
+ zzip_input_seek,
+};
+
+const struct archive_plugin zzip_archive_plugin = {
+ "zzip",
+ nullptr,
+ nullptr,
+ zzip_archive_open,
+ zzip_archive_extensions,
+};
diff --git a/src/archive/zzip_archive_plugin.h b/src/archive/ZzipArchivePlugin.hxx
index 2b2c01e5..4ba16849 100644
--- a/src/archive/zzip_archive_plugin.h
+++ b/src/archive/ZzipArchivePlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ARCHIVE_ZZIP_H
-#define MPD_ARCHIVE_ZZIP_H
+#ifndef MPD_ARCHIVE_ZZIP_HXX
+#define MPD_ARCHIVE_ZZIP_HXX
extern const struct archive_plugin zzip_archive_plugin;
diff --git a/src/archive/bz2_archive_plugin.c b/src/archive/bz2_archive_plugin.c
deleted file mode 100644
index e2420048..00000000
--- a/src/archive/bz2_archive_plugin.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/**
- * single bz2 archive handling (requires libbz2)
- */
-
-#include "config.h"
-#include "archive/bz2_archive_plugin.h"
-#include "archive_api.h"
-#include "input_internal.h"
-#include "input_plugin.h"
-#include "refcount.h"
-
-#include <stdint.h>
-#include <stddef.h>
-#include <string.h>
-#include <glib.h>
-#include <bzlib.h>
-
-#ifdef HAVE_OLDER_BZIP2
-#define BZ2_bzDecompressInit bzDecompressInit
-#define BZ2_bzDecompress bzDecompress
-#endif
-
-struct bz2_archive_file {
- struct archive_file base;
-
- struct refcount ref;
-
- char *name;
- bool reset;
- struct input_stream *istream;
-};
-
-struct bz2_input_stream {
- struct input_stream base;
-
- struct bz2_archive_file *archive;
-
- bool eof;
-
- bz_stream bzstream;
-
- char buffer[5000];
-};
-
-static const struct input_plugin bz2_inputplugin;
-
-static inline GQuark
-bz2_quark(void)
-{
- return g_quark_from_static_string("bz2");
-}
-
-/* single archive handling allocation helpers */
-
-static bool
-bz2_alloc(struct bz2_input_stream *data, GError **error_r)
-{
- int ret;
-
- data->bzstream.bzalloc = NULL;
- data->bzstream.bzfree = NULL;
- data->bzstream.opaque = NULL;
-
- data->bzstream.next_in = (void *) data->buffer;
- data->bzstream.avail_in = 0;
-
- ret = BZ2_bzDecompressInit(&data->bzstream, 0, 0);
- if (ret != BZ_OK) {
- g_free(data);
-
- g_set_error(error_r, bz2_quark(), ret,
- "BZ2_bzDecompressInit() has failed");
- return false;
- }
-
- return true;
-}
-
-static void
-bz2_destroy(struct bz2_input_stream *data)
-{
- BZ2_bzDecompressEnd(&data->bzstream);
-}
-
-/* archive open && listing routine */
-
-#if GCC_CHECK_VERSION(4, 2)
-/* workaround for a warning caused by G_STATIC_MUTEX_INIT */
-#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
-
-static struct archive_file *
-bz2_open(const char *pathname, GError **error_r)
-{
- struct bz2_archive_file *context;
- int len;
-
- context = g_malloc(sizeof(*context));
- archive_file_init(&context->base, &bz2_archive_plugin);
- refcount_init(&context->ref);
-
- //open archive
- static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
- context->istream = input_stream_open(pathname,
- g_static_mutex_get_mutex(&mutex),
- NULL,
- error_r);
- if (context->istream == NULL) {
- g_free(context);
- return NULL;
- }
-
- context->name = g_path_get_basename(pathname);
-
- //remove suffix
- len = strlen(context->name);
- if (len > 4) {
- context->name[len - 4] = 0; //remove .bz2 suffix
- }
-
- return &context->base;
-}
-
-static void
-bz2_scan_reset(struct archive_file *file)
-{
- struct bz2_archive_file *context = (struct bz2_archive_file *) file;
- context->reset = true;
-}
-
-static char *
-bz2_scan_next(struct archive_file *file)
-{
- struct bz2_archive_file *context = (struct bz2_archive_file *) file;
- char *name = NULL;
-
- if (context->reset) {
- name = context->name;
- context->reset = false;
- }
-
- return name;
-}
-
-static void
-bz2_close(struct archive_file *file)
-{
- struct bz2_archive_file *context = (struct bz2_archive_file *) file;
-
- if (!refcount_dec(&context->ref))
- return;
-
- g_free(context->name);
-
- input_stream_close(context->istream);
- g_free(context);
-}
-
-/* single archive handling */
-
-static struct input_stream *
-bz2_open_stream(struct archive_file *file, const char *path,
- GMutex *mutex, GCond *cond,
- GError **error_r)
-{
- struct bz2_archive_file *context = (struct bz2_archive_file *) file;
- struct bz2_input_stream *bis = g_new(struct bz2_input_stream, 1);
-
- input_stream_init(&bis->base, &bz2_inputplugin, path,
- mutex, cond);
-
- bis->archive = context;
-
- bis->base.ready = true;
- bis->base.seekable = false;
-
- if (!bz2_alloc(bis, error_r)) {
- input_stream_deinit(&bis->base);
- g_free(bis);
- return NULL;
- }
-
- bis->eof = false;
-
- refcount_inc(&context->ref);
-
- return &bis->base;
-}
-
-static void
-bz2_is_close(struct input_stream *is)
-{
- struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
-
- bz2_destroy(bis);
-
- bz2_close(&bis->archive->base);
-
- input_stream_deinit(&bis->base);
- g_free(bis);
-}
-
-static bool
-bz2_fillbuffer(struct bz2_input_stream *bis, GError **error_r)
-{
- size_t count;
- bz_stream *bzstream;
-
- bzstream = &bis->bzstream;
-
- if (bzstream->avail_in > 0)
- return true;
-
- count = input_stream_read(bis->archive->istream,
- bis->buffer, sizeof(bis->buffer),
- error_r);
- if (count == 0)
- return false;
-
- bzstream->next_in = bis->buffer;
- bzstream->avail_in = count;
- return true;
-}
-
-static size_t
-bz2_is_read(struct input_stream *is, void *ptr, size_t length,
- GError **error_r)
-{
- struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
- bz_stream *bzstream;
- int bz_result;
- size_t nbytes = 0;
-
- if (bis->eof)
- return 0;
-
- bzstream = &bis->bzstream;
- bzstream->next_out = ptr;
- bzstream->avail_out = length;
-
- do {
- if (!bz2_fillbuffer(bis, error_r))
- return 0;
-
- bz_result = BZ2_bzDecompress(bzstream);
-
- if (bz_result == BZ_STREAM_END) {
- bis->eof = true;
- break;
- }
-
- if (bz_result != BZ_OK) {
- g_set_error(error_r, bz2_quark(), bz_result,
- "BZ2_bzDecompress() has failed");
- return 0;
- }
- } while (bzstream->avail_out == length);
-
- nbytes = length - bzstream->avail_out;
- is->offset += nbytes;
-
- return nbytes;
-}
-
-static bool
-bz2_is_eof(struct input_stream *is)
-{
- struct bz2_input_stream *bis = (struct bz2_input_stream *)is;
-
- return bis->eof;
-}
-
-/* exported structures */
-
-static const char *const bz2_extensions[] = {
- "bz2",
- NULL
-};
-
-static const struct input_plugin bz2_inputplugin = {
- .close = bz2_is_close,
- .read = bz2_is_read,
- .eof = bz2_is_eof,
-};
-
-const struct archive_plugin bz2_archive_plugin = {
- .name = "bz2",
- .open = bz2_open,
- .scan_reset = bz2_scan_reset,
- .scan_next = bz2_scan_next,
- .open_stream = bz2_open_stream,
- .close = bz2_close,
- .suffixes = bz2_extensions
-};
-
diff --git a/src/archive/iso9660_archive_plugin.c b/src/archive/iso9660_archive_plugin.c
deleted file mode 100644
index bb6cb958..00000000
--- a/src/archive/iso9660_archive_plugin.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/**
- * iso archive handling (requires cdio, and iso9660)
- */
-
-#include "config.h"
-#include "archive/iso9660_archive_plugin.h"
-#include "archive_api.h"
-#include "input_internal.h"
-#include "input_plugin.h"
-#include "refcount.h"
-
-#include <cdio/cdio.h>
-#include <cdio/iso9660.h>
-
-#include <glib.h>
-#include <string.h>
-
-#define CEILING(x, y) ((x+(y-1))/y)
-
-struct iso9660_archive_file {
- struct archive_file base;
-
- struct refcount ref;
-
- iso9660_t *iso;
- GSList *list;
- GSList *iter;
-};
-
-static const struct input_plugin iso9660_input_plugin;
-
-static inline GQuark
-iso9660_quark(void)
-{
- return g_quark_from_static_string("iso9660");
-}
-
-/* archive open && listing routine */
-
-static void
-listdir_recur(const char *psz_path, struct iso9660_archive_file *context)
-{
- iso9660_t *iso = context->iso;
- CdioList_t *entlist;
- CdioListNode_t *entnode;
- iso9660_stat_t *statbuf;
- char pathname[4096];
-
- entlist = iso9660_ifs_readdir (iso, psz_path);
- if (!entlist) {
- return;
- }
- /* Iterate over the list of nodes that iso9660_ifs_readdir gives */
- _CDIO_LIST_FOREACH (entnode, entlist) {
- statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
-
- strcpy(pathname, psz_path);
- strcat(pathname, statbuf->filename);
-
- if (_STAT_DIR == statbuf->type ) {
- if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
- strcat(pathname, "/");
- listdir_recur(pathname, context);
- }
- } else {
- //remove leading /
- context->list = g_slist_prepend( context->list,
- g_strdup(pathname + 1));
- }
- }
- _cdio_list_free (entlist, true);
-}
-
-static struct archive_file *
-iso9660_archive_open(const char *pathname, GError **error_r)
-{
- struct iso9660_archive_file *context =
- g_new(struct iso9660_archive_file, 1);
-
- archive_file_init(&context->base, &iso9660_archive_plugin);
- refcount_init(&context->ref);
-
- context->list = NULL;
-
- /* open archive */
- context->iso = iso9660_open (pathname);
- if (context->iso == NULL) {
- g_set_error(error_r, iso9660_quark(), 0,
- "Failed to open ISO9660 file %s", pathname);
- return NULL;
- }
-
- listdir_recur("/", context);
-
- return &context->base;
-}
-
-static void
-iso9660_archive_scan_reset(struct archive_file *file)
-{
- struct iso9660_archive_file *context =
- (struct iso9660_archive_file *)file;
-
- //reset iterator
- context->iter = context->list;
-}
-
-static char *
-iso9660_archive_scan_next(struct archive_file *file)
-{
- struct iso9660_archive_file *context =
- (struct iso9660_archive_file *)file;
-
- char *data = NULL;
- if (context->iter != NULL) {
- ///fetch data and goto next
- data = context->iter->data;
- context->iter = g_slist_next(context->iter);
- }
- return data;
-}
-
-static void
-iso9660_archive_close(struct archive_file *file)
-{
- struct iso9660_archive_file *context =
- (struct iso9660_archive_file *)file;
- GSList *tmp;
-
- if (!refcount_dec(&context->ref))
- return;
-
- if (context->list) {
- //free list
- for (tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
- g_free(tmp->data);
- g_slist_free(context->list);
- }
- //close archive
- iso9660_close(context->iso);
-
- g_free(context);
-}
-
-/* single archive handling */
-
-struct iso9660_input_stream {
- struct input_stream base;
-
- struct iso9660_archive_file *archive;
-
- iso9660_stat_t *statbuf;
- size_t max_blocks;
-};
-
-static struct input_stream *
-iso9660_archive_open_stream(struct archive_file *file, const char *pathname,
- GMutex *mutex, GCond *cond,
- GError **error_r)
-{
- struct iso9660_archive_file *context =
- (struct iso9660_archive_file *)file;
- struct iso9660_input_stream *iis;
-
- iis = g_new(struct iso9660_input_stream, 1);
- input_stream_init(&iis->base, &iso9660_input_plugin, pathname,
- mutex, cond);
-
- iis->archive = context;
- iis->statbuf = iso9660_ifs_stat_translate(context->iso, pathname);
- if (iis->statbuf == NULL) {
- g_free(iis);
- g_set_error(error_r, iso9660_quark(), 0,
- "not found in the ISO file: %s", pathname);
- return NULL;
- }
-
- iis->base.ready = true;
- //we are not seekable
- iis->base.seekable = false;
-
- iis->base.size = iis->statbuf->size;
-
- iis->max_blocks = CEILING(iis->statbuf->size, ISO_BLOCKSIZE);
-
- refcount_inc(&context->ref);
-
- return &iis->base;
-}
-
-static void
-iso9660_input_close(struct input_stream *is)
-{
- struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
-
- g_free(iis->statbuf);
-
- iso9660_archive_close(&iis->archive->base);
-
- input_stream_deinit(&iis->base);
- g_free(iis);
-}
-
-
-static size_t
-iso9660_input_read(struct input_stream *is, void *ptr, size_t size, GError **error_r)
-{
- struct iso9660_input_stream *iis = (struct iso9660_input_stream *)is;
- int toread, readed = 0;
- int no_blocks, cur_block;
- size_t left_bytes = iis->statbuf->size - is->offset;
-
- size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE;
-
- if (left_bytes < size) {
- toread = left_bytes;
- no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE);
- } else {
- toread = size;
- no_blocks = toread / ISO_BLOCKSIZE;
- }
- if (no_blocks > 0) {
-
- cur_block = is->offset / ISO_BLOCKSIZE;
-
- readed = iso9660_iso_seek_read (iis->archive->iso, ptr,
- iis->statbuf->lsn + cur_block, no_blocks);
-
- if (readed != no_blocks * ISO_BLOCKSIZE) {
- g_set_error(error_r, iso9660_quark(), 0,
- "error reading ISO file at lsn %lu",
- (long unsigned int) cur_block);
- return 0;
- }
- if (left_bytes < size) {
- readed = left_bytes;
- }
-
- is->offset += readed;
- }
- return readed;
-}
-
-static bool
-iso9660_input_eof(struct input_stream *is)
-{
- return is->offset == is->size;
-}
-
-/* exported structures */
-
-static const char *const iso9660_archive_extensions[] = {
- "iso",
- NULL
-};
-
-static const struct input_plugin iso9660_input_plugin = {
- .close = iso9660_input_close,
- .read = iso9660_input_read,
- .eof = iso9660_input_eof,
-};
-
-const struct archive_plugin iso9660_archive_plugin = {
- .name = "iso",
- .open = iso9660_archive_open,
- .scan_reset = iso9660_archive_scan_reset,
- .scan_next = iso9660_archive_scan_next,
- .open_stream = iso9660_archive_open_stream,
- .close = iso9660_archive_close,
- .suffixes = iso9660_archive_extensions
-};
diff --git a/src/archive/zzip_archive_plugin.c b/src/archive/zzip_archive_plugin.c
deleted file mode 100644
index ad96b5f8..00000000
--- a/src/archive/zzip_archive_plugin.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/**
- * zip archive handling (requires zziplib)
- */
-
-#include "config.h"
-#include "archive/zzip_archive_plugin.h"
-#include "archive_api.h"
-#include "archive_api.h"
-#include "input_internal.h"
-#include "input_plugin.h"
-#include "refcount.h"
-
-#include <zzip/zzip.h>
-#include <glib.h>
-#include <string.h>
-
-struct zzip_archive {
- struct archive_file base;
-
- struct refcount ref;
-
- ZZIP_DIR *dir;
- GSList *list;
- GSList *iter;
-};
-
-static const struct input_plugin zzip_input_plugin;
-
-static inline GQuark
-zzip_quark(void)
-{
- return g_quark_from_static_string("zzip");
-}
-
-/* archive open && listing routine */
-
-static struct archive_file *
-zzip_archive_open(const char *pathname, GError **error_r)
-{
- struct zzip_archive *context = g_malloc(sizeof(*context));
- ZZIP_DIRENT dirent;
-
- archive_file_init(&context->base, &zzip_archive_plugin);
- refcount_init(&context->ref);
-
- // open archive
- context->list = NULL;
- context->dir = zzip_dir_open(pathname, NULL);
- if (context->dir == NULL) {
- g_set_error(error_r, zzip_quark(), 0,
- "Failed to open ZIP file %s", pathname);
- return NULL;
- }
-
- while (zzip_dir_read(context->dir, &dirent)) {
- //add only files
- if (dirent.st_size > 0) {
- context->list = g_slist_prepend(context->list,
- g_strdup(dirent.d_name));
- }
- }
-
- return &context->base;
-}
-
-static void
-zzip_archive_scan_reset(struct archive_file *file)
-{
- struct zzip_archive *context = (struct zzip_archive *) file;
- //reset iterator
- context->iter = context->list;
-}
-
-static char *
-zzip_archive_scan_next(struct archive_file *file)
-{
- struct zzip_archive *context = (struct zzip_archive *) file;
- char *data = NULL;
- if (context->iter != NULL) {
- ///fetch data and goto next
- data = context->iter->data;
- context->iter = g_slist_next(context->iter);
- }
- return data;
-}
-
-static void
-zzip_archive_close(struct archive_file *file)
-{
- struct zzip_archive *context = (struct zzip_archive *) file;
-
- if (!refcount_dec(&context->ref))
- return;
-
- if (context->list) {
- //free list
- for (GSList *tmp = context->list; tmp != NULL; tmp = g_slist_next(tmp))
- g_free(tmp->data);
- g_slist_free(context->list);
- }
- //close archive
- zzip_dir_close (context->dir);
-
- g_free(context);
-}
-
-/* single archive handling */
-
-struct zzip_input_stream {
- struct input_stream base;
-
- struct zzip_archive *archive;
-
- ZZIP_FILE *file;
-};
-
-static struct input_stream *
-zzip_archive_open_stream(struct archive_file *file,
- const char *pathname,
- GMutex *mutex, GCond *cond,
- GError **error_r)
-{
- struct zzip_archive *context = (struct zzip_archive *) file;
- struct zzip_input_stream *zis;
- ZZIP_STAT z_stat;
-
- zis = g_new(struct zzip_input_stream, 1);
- input_stream_init(&zis->base, &zzip_input_plugin, pathname,
- mutex, cond);
-
- zis->archive = context;
- zis->file = zzip_file_open(context->dir, pathname, 0);
- if (zis->file == NULL) {
- g_free(zis);
- g_set_error(error_r, zzip_quark(), 0,
- "not found in the ZIP file: %s", pathname);
- return NULL;
- }
-
- zis->base.ready = true;
- //we are seekable (but its not recommendent to do so)
- zis->base.seekable = true;
-
- zzip_file_stat(zis->file, &z_stat);
- zis->base.size = z_stat.st_size;
-
- refcount_inc(&context->ref);
-
- return &zis->base;
-}
-
-static void
-zzip_input_close(struct input_stream *is)
-{
- struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
-
- zzip_file_close(zis->file);
- zzip_archive_close(&zis->archive->base);
- input_stream_deinit(&zis->base);
- g_free(zis);
-}
-
-static size_t
-zzip_input_read(struct input_stream *is, void *ptr, size_t size,
- GError **error_r)
-{
- struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
- int ret;
-
- ret = zzip_file_read(zis->file, ptr, size);
- if (ret < 0) {
- g_set_error(error_r, zzip_quark(), ret,
- "zzip_file_read() has failed");
- return 0;
- }
-
- is->offset = zzip_tell(zis->file);
-
- return ret;
-}
-
-static bool
-zzip_input_eof(struct input_stream *is)
-{
- struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
-
- return (goffset)zzip_tell(zis->file) == is->size;
-}
-
-static bool
-zzip_input_seek(struct input_stream *is,
- goffset offset, int whence, GError **error_r)
-{
- struct zzip_input_stream *zis = (struct zzip_input_stream *)is;
- zzip_off_t ofs = zzip_seek(zis->file, offset, whence);
- if (ofs != -1) {
- g_set_error(error_r, zzip_quark(), ofs,
- "zzip_seek() has failed");
- is->offset = ofs;
- return true;
- }
- return false;
-}
-
-/* exported structures */
-
-static const char *const zzip_archive_extensions[] = {
- "zip",
- NULL
-};
-
-static const struct input_plugin zzip_input_plugin = {
- .close = zzip_input_close,
- .read = zzip_input_read,
- .eof = zzip_input_eof,
- .seek = zzip_input_seek,
-};
-
-const struct archive_plugin zzip_archive_plugin = {
- .name = "zzip",
- .open = zzip_archive_open,
- .scan_reset = zzip_archive_scan_reset,
- .scan_next = zzip_archive_scan_next,
- .open_stream = zzip_archive_open_stream,
- .close = zzip_archive_close,
- .suffixes = zzip_archive_extensions
-};
diff --git a/src/archive_internal.h b/src/archive_internal.h
deleted file mode 100644
index 0d885e91..00000000
--- a/src/archive_internal.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_ARCHIVE_INTERNAL_H
-#define MPD_ARCHIVE_INTERNAL_H
-
-struct archive_file {
- const struct archive_plugin *plugin;
-};
-
-static inline void
-archive_file_init(struct archive_file *archive_file,
- const struct archive_plugin *plugin)
-{
- archive_file->plugin = plugin;
-}
-
-#endif
diff --git a/src/archive_plugin.c b/src/archive_plugin.c
deleted file mode 100644
index cf23e639..00000000
--- a/src/archive_plugin.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "archive_plugin.h"
-#include "archive_internal.h"
-
-#include <assert.h>
-
-struct archive_file *
-archive_file_open(const struct archive_plugin *plugin, const char *path,
- GError **error_r)
-{
- struct archive_file *file;
-
- assert(plugin != NULL);
- assert(plugin->open != NULL);
- assert(path != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- file = plugin->open(path, error_r);
-
- if (file != NULL) {
- assert(file->plugin != NULL);
- assert(file->plugin->close != NULL);
- assert(file->plugin->scan_reset != NULL);
- assert(file->plugin->scan_next != NULL);
- assert(file->plugin->open_stream != NULL);
- assert(error_r == NULL || *error_r == NULL);
- } else {
- assert(error_r == NULL || *error_r != NULL);
- }
-
- return file;
-}
-
-void
-archive_file_close(struct archive_file *file)
-{
- assert(file != NULL);
- assert(file->plugin != NULL);
- assert(file->plugin->close != NULL);
-
- file->plugin->close(file);
-}
-
-void
-archive_file_scan_reset(struct archive_file *file)
-{
- assert(file != NULL);
- assert(file->plugin != NULL);
- assert(file->plugin->scan_reset != NULL);
- assert(file->plugin->scan_next != NULL);
-
- file->plugin->scan_reset(file);
-}
-
-char *
-archive_file_scan_next(struct archive_file *file)
-{
- assert(file != NULL);
- assert(file->plugin != NULL);
- assert(file->plugin->scan_next != NULL);
-
- return file->plugin->scan_next(file);
-}
-
-struct input_stream *
-archive_file_open_stream(struct archive_file *file, const char *path,
- GMutex *mutex, GCond *cond,
- GError **error_r)
-{
- assert(file != NULL);
- assert(file->plugin != NULL);
- assert(file->plugin->open_stream != NULL);
-
- return file->plugin->open_stream(file, path, mutex, cond,
- error_r);
-}
diff --git a/src/audio_check.h b/src/audio_check.h
index 9f71cf9c..d4d3f13f 100644
--- a/src/audio_check.h
+++ b/src/audio_check.h
@@ -28,13 +28,17 @@
/**
* The GLib quark used for errors reported by this library.
*/
-G_GNUC_CONST
+gcc_const
static inline GQuark
audio_format_quark(void)
{
return g_quark_from_static_string("audio_format");
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
bool
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
@@ -52,4 +56,8 @@ audio_format_init_checked(struct audio_format *af, unsigned long sample_rate,
enum sample_format sample_format, unsigned channels,
GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/audio_format.h b/src/audio_format.h
index bf77add3..f9b176bc 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -20,7 +20,8 @@
#ifndef MPD_AUDIO_FORMAT_H
#define MPD_AUDIO_FORMAT_H
-#include <glib.h>
+#include "gcc.h"
+
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
@@ -189,7 +190,7 @@ audio_valid_channel_count(unsigned channels)
* Returns false if the format is not valid for playback with MPD.
* This function performs some basic validity checks.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool audio_format_valid(const struct audio_format *af)
{
return audio_valid_sample_rate(af->sample_rate) &&
@@ -201,7 +202,7 @@ static inline bool audio_format_valid(const struct audio_format *af)
* Returns false if the format mask is not valid for playback with
* MPD. This function performs some basic validity checks.
*/
-G_GNUC_PURE
+gcc_pure
static inline bool audio_format_mask_valid(const struct audio_format *af)
{
return (af->sample_rate == 0 ||
@@ -219,11 +220,15 @@ static inline bool audio_format_equals(const struct audio_format *a,
a->channels == b->channels;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
audio_format_mask_apply(struct audio_format *af,
const struct audio_format *mask);
-G_GNUC_CONST
+gcc_const
static inline unsigned
sample_format_size(enum sample_format format)
{
@@ -254,7 +259,7 @@ sample_format_size(enum sample_format format)
/**
* Returns the size of each (mono) sample in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline unsigned audio_format_sample_size(const struct audio_format *af)
{
return sample_format_size((enum sample_format)af->format);
@@ -263,7 +268,7 @@ static inline unsigned audio_format_sample_size(const struct audio_format *af)
/**
* Returns the size of each full frame in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline unsigned
audio_format_frame_size(const struct audio_format *af)
{
@@ -274,7 +279,7 @@ audio_format_frame_size(const struct audio_format *af)
* Returns the floating point factor which converts a time span to a
* storage size in bytes.
*/
-G_GNUC_PURE
+gcc_pure
static inline double audio_format_time_to_size(const struct audio_format *af)
{
return af->sample_rate * audio_format_frame_size(af);
@@ -287,7 +292,7 @@ static inline double audio_format_time_to_size(const struct audio_format *af)
* @param format a #sample_format enum value
* @return the string
*/
-G_GNUC_PURE G_GNUC_MALLOC
+gcc_pure gcc_malloc
const char *
sample_format_to_string(enum sample_format format);
@@ -299,9 +304,13 @@ sample_format_to_string(enum sample_format format);
* @param s a buffer to print into
* @return the string, or NULL if the #audio_format object is invalid
*/
-G_GNUC_PURE G_GNUC_MALLOC
+gcc_pure gcc_malloc
const char *
audio_format_to_string(const struct audio_format *af,
struct audio_format_string *s);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/buffer.c b/src/buffer.c
deleted file mode 100644
index 559f39a9..00000000
--- a/src/buffer.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "buffer.h"
-#include "chunk.h"
-#include "poison.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-struct music_buffer {
- struct music_chunk *chunks;
- unsigned num_chunks;
-
- struct music_chunk *available;
-
- /** a mutex which protects #available */
- GMutex *mutex;
-
-#ifndef NDEBUG
- unsigned num_allocated;
-#endif
-};
-
-struct music_buffer *
-music_buffer_new(unsigned num_chunks)
-{
- struct music_buffer *buffer;
- struct music_chunk *chunk;
-
- assert(num_chunks > 0);
-
- buffer = g_new(struct music_buffer, 1);
-
- buffer->chunks = g_new(struct music_chunk, num_chunks);
- buffer->num_chunks = num_chunks;
-
- chunk = buffer->available = buffer->chunks;
- poison_undefined(chunk, sizeof(*chunk));
-
- for (unsigned i = 1; i < num_chunks; ++i) {
- chunk->next = &buffer->chunks[i];
- chunk = chunk->next;
- poison_undefined(chunk, sizeof(*chunk));
- }
-
- chunk->next = NULL;
-
- buffer->mutex = g_mutex_new();
-
-#ifndef NDEBUG
- buffer->num_allocated = 0;
-#endif
-
- return buffer;
-}
-
-void
-music_buffer_free(struct music_buffer *buffer)
-{
- assert(buffer->chunks != NULL);
- assert(buffer->num_chunks > 0);
- assert(buffer->num_allocated == 0);
-
- g_mutex_free(buffer->mutex);
- g_free(buffer->chunks);
- g_free(buffer);
-}
-
-unsigned
-music_buffer_size(const struct music_buffer *buffer)
-{
- return buffer->num_chunks;
-}
-
-struct music_chunk *
-music_buffer_allocate(struct music_buffer *buffer)
-{
- struct music_chunk *chunk;
-
- g_mutex_lock(buffer->mutex);
-
- chunk = buffer->available;
- if (chunk != NULL) {
- buffer->available = chunk->next;
- music_chunk_init(chunk);
-
-#ifndef NDEBUG
- ++buffer->num_allocated;
-#endif
- }
-
- g_mutex_unlock(buffer->mutex);
- return chunk;
-}
-
-void
-music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk)
-{
- assert(buffer != NULL);
- assert(chunk != NULL);
-
- if (chunk->other != NULL)
- music_buffer_return(buffer, chunk->other);
-
- g_mutex_lock(buffer->mutex);
-
- music_chunk_free(chunk);
- poison_undefined(chunk, sizeof(*chunk));
-
- chunk->next = buffer->available;
- buffer->available = chunk;
-
-#ifndef NDEBUG
- --buffer->num_allocated;
-#endif
-
- g_mutex_unlock(buffer->mutex);
-}
diff --git a/src/chunk.c b/src/chunk.c
deleted file mode 100644
index 1eb96f4b..00000000
--- a/src/chunk.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "chunk.h"
-#include "audio_format.h"
-#include "tag.h"
-
-#include <assert.h>
-
-void
-music_chunk_init(struct music_chunk *chunk)
-{
- chunk->other = NULL;
- chunk->length = 0;
- chunk->tag = NULL;
- chunk->replay_gain_serial = 0;
-}
-
-void
-music_chunk_free(struct music_chunk *chunk)
-{
- if (chunk->tag != NULL)
- tag_free(chunk->tag);
-}
-
-#ifndef NDEBUG
-bool
-music_chunk_check_format(const struct music_chunk *chunk,
- const struct audio_format *audio_format)
-{
- assert(chunk != NULL);
- assert(audio_format != NULL);
- assert(audio_format_valid(audio_format));
-
- return chunk->length == 0 ||
- audio_format_equals(&chunk->audio_format, audio_format);
-}
-#endif
-
-void *
-music_chunk_write(struct music_chunk *chunk,
- const struct audio_format *audio_format,
- float data_time, uint16_t bit_rate,
- size_t *max_length_r)
-{
- const size_t frame_size = audio_format_frame_size(audio_format);
- size_t num_frames;
-
- assert(music_chunk_check_format(chunk, audio_format));
- assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format));
-
- if (chunk->length == 0) {
- /* if the chunk is empty, nobody has set bitRate and
- times yet */
-
- chunk->bit_rate = bit_rate;
- chunk->times = data_time;
- }
-
- num_frames = (sizeof(chunk->data) - chunk->length) / frame_size;
- if (num_frames == 0)
- return NULL;
-
-#ifndef NDEBUG
- chunk->audio_format = *audio_format;
-#endif
-
- *max_length_r = num_frames * frame_size;
- return chunk->data + chunk->length;
-}
-
-bool
-music_chunk_expand(struct music_chunk *chunk,
- const struct audio_format *audio_format, size_t length)
-{
- const size_t frame_size = audio_format_frame_size(audio_format);
-
- assert(chunk != NULL);
- assert(chunk->length + length <= sizeof(chunk->data));
- assert(audio_format_equals(&chunk->audio_format, audio_format));
-
- chunk->length += length;
-
- return chunk->length + frame_size > sizeof(chunk->data);
-}
diff --git a/src/client_event.c b/src/client_event.c
deleted file mode 100644
index 4f54ae0a..00000000
--- a/src/client_event.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "client_internal.h"
-#include "main.h"
-
-#include <assert.h>
-
-static gboolean
-client_out_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct client *client = data;
-
- assert(!client_is_expired(client));
-
- if (condition != G_IO_OUT) {
- client_set_expired(client);
- return false;
- }
-
- client_write_deferred(client);
-
- if (client_is_expired(client)) {
- client_close(client);
- return false;
- }
-
- g_timer_start(client->last_activity);
-
- if (g_queue_is_empty(client->deferred_send)) {
- /* done sending deferred buffers exist: schedule
- read */
- client->source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- client_in_event, client);
- return false;
- }
-
- /* write more */
- return true;
-}
-
-gboolean
-client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct client *client = data;
- enum command_return ret;
-
- assert(!client_is_expired(client));
-
- if (condition != G_IO_IN) {
- client_set_expired(client);
- return false;
- }
-
- g_timer_start(client->last_activity);
-
- ret = client_read(client);
- switch (ret) {
- case COMMAND_RETURN_OK:
- case COMMAND_RETURN_ERROR:
- break;
-
- case COMMAND_RETURN_KILL:
- client_close(client);
- g_main_loop_quit(main_loop);
- return false;
-
- case COMMAND_RETURN_CLOSE:
- client_close(client);
- return false;
- }
-
- if (client_is_expired(client)) {
- client_close(client);
- return false;
- }
-
- if (!g_queue_is_empty(client->deferred_send)) {
- /* deferred buffers exist: schedule write */
- client->source_id = g_io_add_watch(client->channel,
- G_IO_OUT|G_IO_ERR|G_IO_HUP,
- client_out_event, client);
- return false;
- }
-
- /* read more */
- return true;
-}
diff --git a/src/client_expire.c b/src/client_expire.c
deleted file mode 100644
index 1ca32ebc..00000000
--- a/src/client_expire.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "client_internal.h"
-
-static guint expire_source_id;
-
-void
-client_set_expired(struct client *client)
-{
- if (!client_is_expired(client))
- client_schedule_expire();
-
- if (client->source_id != 0) {
- g_source_remove(client->source_id);
- client->source_id = 0;
- }
-
- if (client->channel != NULL) {
- g_io_channel_unref(client->channel);
- client->channel = NULL;
- }
-}
-
-static void
-client_check_expired_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct client *client = data;
-
- if (client_is_expired(client)) {
- g_debug("[%u] expired", client->num);
- client_close(client);
- } else if (!client->idle_waiting && /* idle clients
- never expire */
- (int)g_timer_elapsed(client->last_activity, NULL) >
- client_timeout) {
- g_debug("[%u] timeout", client->num);
- client_close(client);
- }
-}
-
-static void
-client_manager_expire(void)
-{
- client_list_foreach(client_check_expired_callback, NULL);
-}
-
-/**
- * An idle event which calls client_manager_expire().
- */
-static gboolean
-client_manager_expire_event(G_GNUC_UNUSED gpointer data)
-{
- expire_source_id = 0;
- client_manager_expire();
- return false;
-}
-
-void
-client_schedule_expire(void)
-{
- if (expire_source_id == 0)
- /* delayed deletion */
- expire_source_id = g_idle_add(client_manager_expire_event,
- NULL);
-}
-
-void
-client_deinit_expire(void)
-{
- if (expire_source_id != 0)
- g_source_remove(expire_source_id);
-}
diff --git a/src/client_idle.c b/src/client_idle.c
deleted file mode 100644
index 930911d6..00000000
--- a/src/client_idle.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "client_idle.h"
-#include "client_internal.h"
-#include "idle.h"
-
-#include <assert.h>
-
-/**
- * Send "idle" response to this client.
- */
-static void
-client_idle_notify(struct client *client)
-{
- unsigned flags, i;
- const char *const* idle_names;
-
- assert(client->idle_waiting);
- assert(client->idle_flags != 0);
-
- flags = client->idle_flags;
- client->idle_flags = 0;
- client->idle_waiting = false;
-
- idle_names = idle_get_names();
- for (i = 0; idle_names[i]; ++i) {
- if (flags & (1 << i) & client->idle_subscriptions)
- client_printf(client, "changed: %s\n",
- idle_names[i]);
- }
-
- client_puts(client, "OK\n");
- g_timer_start(client->last_activity);
-}
-
-void
-client_idle_add(struct client *client, unsigned flags)
-{
- if (client_is_expired(client))
- return;
-
- client->idle_flags |= flags;
- if (client->idle_waiting
- && (client->idle_flags & client->idle_subscriptions)) {
- client_idle_notify(client);
- client_write_output(client);
- }
-}
-
-static void
-client_idle_callback(gpointer data, gpointer user_data)
-{
- struct client *client = data;
- unsigned flags = GPOINTER_TO_UINT(user_data);
-
- client_idle_add(client, flags);
-}
-
-void client_manager_idle_add(unsigned flags)
-{
- assert(flags != 0);
-
- client_list_foreach(client_idle_callback, GUINT_TO_POINTER(flags));
-}
-
-bool client_idle_wait(struct client *client, unsigned flags)
-{
- assert(!client->idle_waiting);
-
- client->idle_waiting = true;
- client->idle_subscriptions = flags;
-
- if (client->idle_flags & client->idle_subscriptions) {
- client_idle_notify(client);
- return true;
- } else
- return false;
-}
diff --git a/src/client_idle.h b/src/client_idle.h
deleted file mode 100644
index c56fd014..00000000
--- a/src/client_idle.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_CLIENT_IDLE_H
-#define MPD_CLIENT_IDLE_H
-
-#include <stdbool.h>
-
-struct client;
-
-void
-client_idle_add(struct client *client, unsigned flags);
-
-/**
- * Adds the specified idle flags to all clients and immediately sends
- * notifications to all waiting clients.
- */
-void
-client_manager_idle_add(unsigned flags);
-
-/**
- * Checks whether the client has pending idle flags. If yes, they are
- * sent immediately and "true" is returned". If no, it puts the
- * client into waiting mode and returns false.
- */
-bool
-client_idle_wait(struct client *client, unsigned flags);
-
-#endif
diff --git a/src/client_internal.h b/src/client_internal.h
deleted file mode 100644
index ba97e4b8..00000000
--- a/src/client_internal.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_CLIENT_INTERNAL_H
-#define MPD_CLIENT_INTERNAL_H
-
-#include "client.h"
-#include "client_message.h"
-#include "command.h"
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "client"
-
-enum {
- CLIENT_MAX_SUBSCRIPTIONS = 16,
- CLIENT_MAX_MESSAGES = 64,
-};
-
-struct deferred_buffer {
- size_t size;
- char data[sizeof(long)];
-};
-
-struct client {
- struct player_control *player_control;
-
- GIOChannel *channel;
- guint source_id;
-
- /** the buffer for reading lines from the #channel */
- struct fifo_buffer *input;
-
- unsigned permission;
-
- /** the uid of the client process, or -1 if unknown */
- int uid;
-
- /**
- * How long since the last activity from this client?
- */
- GTimer *last_activity;
-
- GSList *cmd_list; /* for when in list mode */
- int cmd_list_OK; /* print OK after each command execution */
- size_t cmd_list_size; /* mem cmd_list consumes */
- GQueue *deferred_send; /* for output if client is slow */
- size_t deferred_bytes; /* mem deferred_send consumes */
- unsigned int num; /* client number */
-
- char send_buf[16384];
- size_t send_buf_used; /* bytes used this instance */
-
- /** is this client waiting for an "idle" response? */
- bool idle_waiting;
-
- /** idle flags pending on this client, to be sent as soon as
- the client enters "idle" */
- unsigned idle_flags;
-
- /** idle flags that the client wants to receive */
- unsigned idle_subscriptions;
-
- /**
- * A list of channel names this client is subscribed to.
- */
- GSList *subscriptions;
-
- /**
- * The number of subscriptions in #subscriptions. Used to
- * limit the number of subscriptions.
- */
- unsigned num_subscriptions;
-
- /**
- * A list of messages this client has received in reverse
- * order (latest first).
- */
- GSList *messages;
-
- /**
- * The number of messages in #messages.
- */
- unsigned num_messages;
-};
-
-extern unsigned int client_max_connections;
-extern int client_timeout;
-extern size_t client_max_command_list_size;
-extern size_t client_max_output_buffer_size;
-
-bool
-client_list_is_empty(void);
-
-bool
-client_list_is_full(void);
-
-struct client *
-client_list_get_first(void);
-
-void
-client_list_add(struct client *client);
-
-void
-client_list_foreach(GFunc func, gpointer user_data);
-
-void
-client_list_remove(struct client *client);
-
-void
-client_close(struct client *client);
-
-static inline void
-new_cmd_list_ptr(struct client *client, const char *s)
-{
- client->cmd_list = g_slist_prepend(client->cmd_list, g_strdup(s));
-}
-
-static inline void
-free_cmd_list(GSList *list)
-{
- for (GSList *tmp = list; tmp != NULL; tmp = g_slist_next(tmp))
- g_free(tmp->data);
-
- g_slist_free(list);
-}
-
-void
-client_set_expired(struct client *client);
-
-/**
- * Schedule an "expired" check for all clients: permanently delete
- * clients which have been set "expired" with client_set_expired().
- */
-void
-client_schedule_expire(void);
-
-/**
- * Removes a scheduled "expired" check.
- */
-void
-client_deinit_expire(void);
-
-enum command_return
-client_read(struct client *client);
-
-enum command_return
-client_process_line(struct client *client, char *line);
-
-void
-client_write_deferred(struct client *client);
-
-void
-client_write_output(struct client *client);
-
-gboolean
-client_in_event(GIOChannel *source, GIOCondition condition,
- gpointer data);
-
-#endif
diff --git a/src/client_message.c b/src/client_message.c
deleted file mode 100644
index b681b4e7..00000000
--- a/src/client_message.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "client_message.h"
-
-#include <assert.h>
-#include <glib.h>
-
-G_GNUC_PURE
-static bool
-valid_channel_char(const char ch)
-{
- return g_ascii_isalnum(ch) ||
- ch == '_' || ch == '-' || ch == '.' || ch == ':';
-}
-
-bool
-client_message_valid_channel_name(const char *name)
-{
- do {
- if (!valid_channel_char(*name))
- return false;
- } while (*++name != 0);
-
- return true;
-}
-
-void
-client_message_init_null(struct client_message *msg)
-{
- assert(msg != NULL);
-
- msg->channel = NULL;
- msg->message = NULL;
-}
-
-void
-client_message_init(struct client_message *msg,
- const char *channel, const char *message)
-{
- assert(msg != NULL);
-
- msg->channel = g_strdup(channel);
- msg->message = g_strdup(message);
-}
-
-void
-client_message_copy(struct client_message *dest,
- const struct client_message *src)
-{
- assert(dest != NULL);
- assert(src != NULL);
- assert(client_message_defined(src));
-
- client_message_init(dest, src->channel, src->message);
-}
-
-struct client_message *
-client_message_dup(const struct client_message *src)
-{
- struct client_message *dest = g_slice_new(struct client_message);
- client_message_copy(dest, src);
- return dest;
-}
-
-void
-client_message_deinit(struct client_message *msg)
-{
- assert(msg != NULL);
-
- g_free(msg->channel);
- g_free(msg->message);
-}
-
-void
-client_message_free(struct client_message *msg)
-{
- client_message_deinit(msg);
- g_slice_free(struct client_message, msg);
-}
diff --git a/src/client_message.h b/src/client_message.h
deleted file mode 100644
index 38c2e761..00000000
--- a/src/client_message.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_CLIENT_MESSAGE_H
-#define MPD_CLIENT_MESSAGE_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <glib.h>
-
-/**
- * A client-to-client message.
- */
-struct client_message {
- char *channel;
-
- char *message;
-};
-
-G_GNUC_PURE
-bool
-client_message_valid_channel_name(const char *name);
-
-G_GNUC_PURE
-static inline bool
-client_message_defined(const struct client_message *msg)
-{
- assert(msg != NULL);
- assert((msg->channel == NULL) == (msg->message == NULL));
-
- return msg->channel != NULL;
-}
-
-void
-client_message_init_null(struct client_message *msg);
-
-void
-client_message_init(struct client_message *msg,
- const char *channel, const char *message);
-
-void
-client_message_copy(struct client_message *dest,
- const struct client_message *src);
-
-G_GNUC_MALLOC
-struct client_message *
-client_message_dup(const struct client_message *src);
-
-void
-client_message_deinit(struct client_message *msg);
-
-void
-client_message_free(struct client_message *msg);
-
-#endif
diff --git a/src/client_read.c b/src/client_read.c
deleted file mode 100644
index 26ade264..00000000
--- a/src/client_read.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "client_internal.h"
-#include "fifo_buffer.h"
-
-#include <assert.h>
-#include <string.h>
-
-static char *
-client_read_line(struct client *client)
-{
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- return NULL;
-
- line = g_strndup(p, newline - p);
- fifo_buffer_consume(client->input, newline - p + 1);
-
- return g_strchomp(line);
-}
-
-static enum command_return
-client_input_received(struct client *client, size_t bytesRead)
-{
- char *line;
-
- fifo_buffer_append(client->input, bytesRead);
-
- /* process all lines */
-
- while ((line = client_read_line(client)) != NULL) {
- enum command_return ret = client_process_line(client, line);
- g_free(line);
-
- if (ret == COMMAND_RETURN_KILL ||
- ret == COMMAND_RETURN_CLOSE)
- return ret;
- if (client_is_expired(client))
- return COMMAND_RETURN_CLOSE;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-enum command_return
-client_read(struct client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- assert(client != NULL);
- assert(client->channel != NULL);
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("[%u] buffer overflow", client->num);
- return COMMAND_RETURN_CLOSE;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- return client_input_received(client, bytes_read);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return COMMAND_RETURN_OK;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return COMMAND_RETURN_CLOSE;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client %d: %s",
- client->num, error->message);
- g_error_free(error);
- return COMMAND_RETURN_CLOSE;
- }
-
- /* unreachable */
- return COMMAND_RETURN_CLOSE;
-}
diff --git a/src/client_subscribe.c b/src/client_subscribe.c
deleted file mode 100644
index c65a7ed3..00000000
--- a/src/client_subscribe.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "client_subscribe.h"
-#include "client_internal.h"
-#include "client_idle.h"
-#include "idle.h"
-
-#include <string.h>
-
-G_GNUC_PURE
-static GSList *
-client_find_subscription(const struct client *client, const char *channel)
-{
- for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
- if (strcmp((const char *)i->data, channel) == 0)
- return i;
-
- return NULL;
-}
-
-enum client_subscribe_result
-client_subscribe(struct client *client, const char *channel)
-{
- assert(client != NULL);
- assert(channel != NULL);
-
- if (!client_message_valid_channel_name(channel))
- return CLIENT_SUBSCRIBE_INVALID;
-
- if (client_find_subscription(client, channel) != NULL)
- return CLIENT_SUBSCRIBE_ALREADY;
-
- if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
- return CLIENT_SUBSCRIBE_FULL;
-
- client->subscriptions = g_slist_prepend(client->subscriptions,
- g_strdup(channel));
- ++client->num_subscriptions;
-
- idle_add(IDLE_SUBSCRIPTION);
-
- return CLIENT_SUBSCRIBE_OK;
-}
-
-bool
-client_unsubscribe(struct client *client, const char *channel)
-{
- GSList *i = client_find_subscription(client, channel);
- if (i == NULL)
- return false;
-
- assert(client->num_subscriptions > 0);
-
- client->subscriptions = g_slist_remove(client->subscriptions, i->data);
- --client->num_subscriptions;
-
- idle_add(IDLE_SUBSCRIPTION);
-
- assert((client->num_subscriptions == 0) ==
- (client->subscriptions == NULL));
-
- return true;
-}
-
-void
-client_unsubscribe_all(struct client *client)
-{
- for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
- g_free(i->data);
-
- g_slist_free(client->subscriptions);
- client->subscriptions = NULL;
- client->num_subscriptions = 0;
-}
-
-bool
-client_push_message(struct client *client, const struct client_message *msg)
-{
- assert(client != NULL);
- assert(msg != NULL);
- assert(client_message_defined(msg));
-
- if (client->num_messages >= CLIENT_MAX_MESSAGES ||
- client_find_subscription(client, msg->channel) == NULL)
- return false;
-
- if (client->messages == NULL)
- client_idle_add(client, IDLE_MESSAGE);
-
- client->messages = g_slist_prepend(client->messages,
- client_message_dup(msg));
- ++client->num_messages;
-
- return true;
-}
-
-GSList *
-client_read_messages(struct client *client)
-{
- GSList *messages = g_slist_reverse(client->messages);
-
- client->messages = NULL;
- client->num_messages = 0;
-
- return messages;
-}
diff --git a/src/client_write.c b/src/client_write.c
deleted file mode 100644
index 78cfca8a..00000000
--- a/src/client_write.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "client_internal.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-
-static size_t
-client_write_deferred_buffer(struct client *client,
- const struct deferred_buffer *buffer)
-{
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->channel != NULL);
- assert(buffer != NULL);
-
- status = g_io_channel_write_chars
- (client->channel, buffer->data, buffer->size,
- &bytes_written, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- return bytes_written;
-
- case G_IO_STATUS_AGAIN:
- return 0;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- client_set_expired(client);
- return 0;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- client_set_expired(client);
- g_warning("failed to flush buffer for %i: %s",
- client->num, error->message);
- g_error_free(error);
- return 0;
- }
-
- /* unreachable */
- return 0;
-}
-
-void
-client_write_deferred(struct client *client)
-{
- size_t ret;
-
- while (!g_queue_is_empty(client->deferred_send)) {
- struct deferred_buffer *buf =
- g_queue_peek_head(client->deferred_send);
-
- assert(buf->size > 0);
- assert(buf->size <= client->deferred_bytes);
-
- ret = client_write_deferred_buffer(client, buf);
- if (ret == 0)
- break;
-
- if (ret < buf->size) {
- assert(client->deferred_bytes >= (size_t)ret);
- client->deferred_bytes -= ret;
- buf->size -= ret;
- memmove(buf->data, buf->data + ret, buf->size);
- break;
- } else {
- size_t decr = sizeof(*buf) -
- sizeof(buf->data) + buf->size;
-
- assert(client->deferred_bytes >= decr);
- client->deferred_bytes -= decr;
- g_free(buf);
- g_queue_pop_head(client->deferred_send);
- }
-
- g_timer_start(client->last_activity);
- }
-
- if (g_queue_is_empty(client->deferred_send)) {
- g_debug("[%u] buffer empty %lu", client->num,
- (unsigned long)client->deferred_bytes);
- assert(client->deferred_bytes == 0);
- }
-}
-
-static void client_defer_output(struct client *client,
- const void *data, size_t length)
-{
- size_t alloc;
- struct deferred_buffer *buf;
-
- assert(length > 0);
-
- alloc = sizeof(*buf) - sizeof(buf->data) + length;
- client->deferred_bytes += alloc;
- if (client->deferred_bytes > client_max_output_buffer_size) {
- g_warning("[%u] output buffer size (%lu) is "
- "larger than the max (%lu)",
- client->num,
- (unsigned long)client->deferred_bytes,
- (unsigned long)client_max_output_buffer_size);
- /* cause client to close */
- client_set_expired(client);
- return;
- }
-
- buf = g_malloc(alloc);
- buf->size = length;
- memcpy(buf->data, data, length);
-
- g_queue_push_tail(client->deferred_send, buf);
-}
-
-static void client_write_direct(struct client *client,
- const char *data, size_t length)
-{
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->channel != NULL);
- assert(data != NULL);
- assert(length > 0);
- assert(g_queue_is_empty(client->deferred_send));
-
- status = g_io_channel_write_chars(client->channel, data, length,
- &bytes_written, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- break;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- client_set_expired(client);
- return;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- client_set_expired(client);
- g_warning("failed to write to %i: %s",
- client->num, error->message);
- g_error_free(error);
- return;
- }
-
- if (bytes_written < length)
- client_defer_output(client, data + bytes_written,
- length - bytes_written);
-
- if (!g_queue_is_empty(client->deferred_send))
- g_debug("[%u] buffer created", client->num);
-}
-
-void
-client_write_output(struct client *client)
-{
- if (client_is_expired(client) || !client->send_buf_used)
- return;
-
- if (!g_queue_is_empty(client->deferred_send)) {
- client_defer_output(client, client->send_buf,
- client->send_buf_used);
-
- if (client_is_expired(client))
- return;
-
- /* try to flush the deferred buffers now; the current
- server command may take too long to finish, and
- meanwhile try to feed output to the client,
- otherwise it will time out. One reason why
- deferring is slow might be that currently each
- client_write() allocates a new deferred buffer.
- This should be optimized after MPD 0.14. */
- client_write_deferred(client);
- } else
- client_write_direct(client, client->send_buf,
- client->send_buf_used);
-
- client->send_buf_used = 0;
-}
-
-/**
- * Write a block of data to the client.
- */
-static void client_write(struct client *client, const char *buffer, size_t buflen)
-{
- /* if the client is going to be closed, do nothing */
- if (client_is_expired(client))
- return;
-
- while (buflen > 0 && !client_is_expired(client)) {
- size_t copylen;
-
- assert(client->send_buf_used < sizeof(client->send_buf));
-
- copylen = sizeof(client->send_buf) - client->send_buf_used;
- if (copylen > buflen)
- copylen = buflen;
-
- memcpy(client->send_buf + client->send_buf_used, buffer,
- copylen);
- buflen -= copylen;
- client->send_buf_used += copylen;
- buffer += copylen;
- if (client->send_buf_used >= sizeof(client->send_buf))
- client_write_output(client);
- }
-}
-
-void client_puts(struct client *client, const char *s)
-{
- client_write(client, s, strlen(s));
-}
-
-void client_vprintf(struct client *client, const char *fmt, va_list args)
-{
-#ifndef G_OS_WIN32
- va_list tmp;
- int length;
- char *buffer;
-
- va_copy(tmp, args);
- length = vsnprintf(NULL, 0, fmt, tmp);
- va_end(tmp);
-
- if (length <= 0)
- /* wtf.. */
- return;
-
- buffer = g_malloc(length + 1);
- vsnprintf(buffer, length + 1, fmt, args);
- client_write(client, buffer, length);
- g_free(buffer);
-#else
- /* On mingw32, snprintf() expects a 64 bit integer instead of
- a "long int" for "%li". This is not consistent with our
- expectation, so we're using plain sprintf() here, hoping
- the static buffer is large enough. Sorry for this hack,
- but WIN32 development is so painful, I'm not in the mood to
- do it properly now. */
-
- static char buffer[4096];
- vsprintf(buffer, fmt, args);
- client_write(client, buffer, strlen(buffer));
-#endif
-}
-
-G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- client_vprintf(client, fmt, args);
- va_end(args);
-}
diff --git a/src/clock.h b/src/clock.h
index 4ece35ab..f1338938 100644
--- a/src/clock.h
+++ b/src/clock.h
@@ -20,21 +20,21 @@
#ifndef MPD_CLOCK_H
#define MPD_CLOCK_H
-#include <glib.h>
+#include "gcc.h"
#include <stdint.h>
/**
* Returns the value of a monotonic clock in milliseconds.
*/
-G_GNUC_PURE
+gcc_pure
unsigned
monotonic_clock_ms(void);
/**
* Returns the value of a monotonic clock in microseconds.
*/
-G_GNUC_PURE
+gcc_pure
uint64_t
monotonic_clock_us(void);
diff --git a/src/command.c b/src/command.c
deleted file mode 100644
index c405925f..00000000
--- a/src/command.c
+++ /dev/null
@@ -1,2298 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "command.h"
-#include "protocol/argparser.h"
-#include "protocol/result.h"
-#include "player_control.h"
-#include "playlist.h"
-#include "playlist_print.h"
-#include "playlist_save.h"
-#include "playlist_queue.h"
-#include "playlist_error.h"
-#include "queue_print.h"
-#include "ls.h"
-#include "uri.h"
-#include "decoder_print.h"
-#include "directory.h"
-#include "database.h"
-#include "update.h"
-#include "volume.h"
-#include "stats.h"
-#include "permission.h"
-#include "tokenizer.h"
-#include "stored_playlist.h"
-#include "ack.h"
-#include "output_command.h"
-#include "output_print.h"
-#include "locate.h"
-#include "dbUtils.h"
-#include "db_error.h"
-#include "db_print.h"
-#include "db_selection.h"
-#include "db_lock.h"
-#include "tag.h"
-#include "client.h"
-#include "client_idle.h"
-#include "client_internal.h"
-#include "client_subscribe.h"
-#include "client_file.h"
-#include "tag_print.h"
-#include "path.h"
-#include "replay_gain_config.h"
-#include "idle.h"
-#include "mapper.h"
-#include "song.h"
-#include "song_print.h"
-
-#ifdef ENABLE_SQLITE
-#include "sticker.h"
-#include "sticker_print.h"
-#include "song_sticker.h"
-#endif
-
-#include <assert.h>
-#include <time.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#define COMMAND_STATUS_STATE "state"
-#define COMMAND_STATUS_REPEAT "repeat"
-#define COMMAND_STATUS_SINGLE "single"
-#define COMMAND_STATUS_CONSUME "consume"
-#define COMMAND_STATUS_RANDOM "random"
-#define COMMAND_STATUS_PLAYLIST "playlist"
-#define COMMAND_STATUS_PLAYLIST_LENGTH "playlistlength"
-#define COMMAND_STATUS_SONG "song"
-#define COMMAND_STATUS_SONGID "songid"
-#define COMMAND_STATUS_NEXTSONG "nextsong"
-#define COMMAND_STATUS_NEXTSONGID "nextsongid"
-#define COMMAND_STATUS_TIME "time"
-#define COMMAND_STATUS_BITRATE "bitrate"
-#define COMMAND_STATUS_ERROR "error"
-#define COMMAND_STATUS_CROSSFADE "xfade"
-#define COMMAND_STATUS_MIXRAMPDB "mixrampdb"
-#define COMMAND_STATUS_MIXRAMPDELAY "mixrampdelay"
-#define COMMAND_STATUS_AUDIO "audio"
-#define COMMAND_STATUS_UPDATING_DB "updating_db"
-
-/*
- * The most we ever use is for search/find, and that limits it to the
- * number of tags we can have. Add one for the command, and one extra
- * to catch errors clients may send us
- */
-#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
-
-/* if min: -1 don't check args *
- * if max: -1 no max args */
-struct command {
- const char *cmd;
- unsigned permission;
- int min;
- int max;
- enum command_return (*handler)(struct client *client, int argc, char **argv);
-};
-
-static enum command_return
-print_playlist_result(struct client *client,
- enum playlist_result result)
-{
- switch (result) {
- case PLAYLIST_RESULT_SUCCESS:
- return COMMAND_RETURN_OK;
-
- case PLAYLIST_RESULT_ERRNO:
- command_error(client, ACK_ERROR_SYSTEM, "%s",
- g_strerror(errno));
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_DENIED:
- command_error(client, ACK_ERROR_PERMISSION, "Access denied");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NO_SUCH_SONG:
- command_error(client, ACK_ERROR_NO_EXIST, "No such song");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NO_SUCH_LIST:
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_LIST_EXISTS:
- command_error(client, ACK_ERROR_EXIST,
- "Playlist already exists");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_BAD_NAME:
- command_error(client, ACK_ERROR_ARG,
- "playlist name is invalid: "
- "playlist names may not contain slashes,"
- " newlines or carriage returns");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_BAD_RANGE:
- command_error(client, ACK_ERROR_ARG, "Bad song index");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_NOT_PLAYING:
- command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_TOO_LARGE:
- command_error(client, ACK_ERROR_PLAYLIST_MAX,
- "playlist is at the max size");
- return COMMAND_RETURN_ERROR;
-
- case PLAYLIST_RESULT_DISABLED:
- command_error(client, ACK_ERROR_UNKNOWN,
- "stored playlist support is disabled");
- return COMMAND_RETURN_ERROR;
- }
-
- assert(0);
- return COMMAND_RETURN_ERROR;
-}
-
-/**
- * Send the GError to the client and free the GError.
- */
-static enum command_return
-print_error(struct client *client, GError *error)
-{
- assert(client != NULL);
- assert(error != NULL);
-
- g_warning("%s", error->message);
-
- if (error->domain == playlist_quark()) {
- enum playlist_result result = error->code;
- g_error_free(error);
- return print_playlist_result(client, result);
- } else if (error->domain == ack_quark()) {
- command_error(client, error->code, "%s", error->message);
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- } else if (error->domain == db_quark()) {
- switch ((enum db_error)error->code) {
- case DB_DISABLED:
- command_error(client, ACK_ERROR_NO_EXIST, "%s",
- error->message);
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
-
- case DB_NOT_FOUND:
- g_error_free(error);
- command_error(client, ACK_ERROR_NO_EXIST, "Not found");
- return COMMAND_RETURN_ERROR;
- }
- } else if (error->domain == g_file_error_quark()) {
- command_error(client, ACK_ERROR_SYSTEM, "%s",
- g_strerror(error->code));
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- }
-
- g_error_free(error);
- command_error(client, ACK_ERROR_UNKNOWN, "error");
- return COMMAND_RETURN_ERROR;
-}
-
-static void
-print_spl_list(struct client *client, GPtrArray *list)
-{
- for (unsigned i = 0; i < list->len; ++i) {
- struct stored_playlist_info *playlist =
- g_ptr_array_index(list, i);
- time_t t;
-#ifndef WIN32
- struct tm tm;
-#endif
- char timestamp[32];
-
- client_printf(client, "playlist: %s\n", playlist->name);
-
- t = playlist->mtime;
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
- gmtime(&t)
-#else
- "%FT%TZ",
- gmtime_r(&t, &tm)
-#endif
- );
- client_printf(client, "Last-Modified: %s\n", timestamp);
- }
-}
-
-static enum command_return
-handle_urlhandlers(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- if (client_is_local(client))
- client_puts(client, "handler: file://\n");
- print_supported_uri_schemes(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_decoders(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- decoder_list_print(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_tagtypes(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- tag_print_types(client);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_play(struct client *client, int argc, char *argv[])
-{
- int song = -1;
- enum playlist_result result;
-
- if (argc == 2 && !check_int(client, &song, argv[1]))
- return COMMAND_RETURN_ERROR;
- result = playlist_play(&g_playlist, client->player_control, song);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_playid(struct client *client, int argc, char *argv[])
-{
- int id = -1;
- enum playlist_result result;
-
- if (argc == 2 && !check_int(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_play_id(&g_playlist, client->player_control, id);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_stop(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_stop(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_currentsong(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_print_current(client, &g_playlist);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_pause(struct client *client,
- int argc, char *argv[])
-{
- if (argc == 2) {
- bool pause_flag;
- if (!check_bool(client, &pause_flag, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- pc_set_pause(client->player_control, pause_flag);
- } else
- pc_pause(client->player_control);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_status(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const char *state = NULL;
- struct player_status player_status;
- int updateJobId;
- char *error;
- int song;
-
- pc_get_status(client->player_control, &player_status);
-
- switch (player_status.state) {
- case PLAYER_STATE_STOP:
- state = "stop";
- break;
- case PLAYER_STATE_PAUSE:
- state = "pause";
- break;
- case PLAYER_STATE_PLAY:
- state = "play";
- break;
- }
-
- client_printf(client,
- "volume: %i\n"
- COMMAND_STATUS_REPEAT ": %i\n"
- COMMAND_STATUS_RANDOM ": %i\n"
- COMMAND_STATUS_SINGLE ": %i\n"
- COMMAND_STATUS_CONSUME ": %i\n"
- COMMAND_STATUS_PLAYLIST ": %li\n"
- COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n"
- COMMAND_STATUS_CROSSFADE ": %i\n"
- COMMAND_STATUS_MIXRAMPDB ": %f\n"
- COMMAND_STATUS_MIXRAMPDELAY ": %f\n"
- COMMAND_STATUS_STATE ": %s\n",
- volume_level_get(),
- playlist_get_repeat(&g_playlist),
- playlist_get_random(&g_playlist),
- playlist_get_single(&g_playlist),
- playlist_get_consume(&g_playlist),
- playlist_get_version(&g_playlist),
- playlist_get_length(&g_playlist),
- (int)(pc_get_cross_fade(client->player_control) + 0.5),
- pc_get_mixramp_db(client->player_control),
- pc_get_mixramp_delay(client->player_control),
- state);
-
- song = playlist_get_current_song(&g_playlist);
- if (song >= 0) {
- client_printf(client,
- COMMAND_STATUS_SONG ": %i\n"
- COMMAND_STATUS_SONGID ": %u\n",
- song, playlist_get_song_id(&g_playlist, song));
- }
-
- if (player_status.state != PLAYER_STATE_STOP) {
- struct audio_format_string af_string;
-
- client_printf(client,
- COMMAND_STATUS_TIME ": %i:%i\n"
- "elapsed: %1.3f\n"
- COMMAND_STATUS_BITRATE ": %u\n"
- COMMAND_STATUS_AUDIO ": %s\n",
- (int)(player_status.elapsed_time + 0.5),
- (int)(player_status.total_time + 0.5),
- player_status.elapsed_time,
- player_status.bit_rate,
- audio_format_to_string(&player_status.audio_format,
- &af_string));
- }
-
- if ((updateJobId = isUpdatingDB())) {
- client_printf(client,
- COMMAND_STATUS_UPDATING_DB ": %i\n",
- updateJobId);
- }
-
- error = pc_get_error_message(client->player_control);
- if (error != NULL) {
- client_printf(client,
- COMMAND_STATUS_ERROR ": %s\n",
- error);
- g_free(error);
- }
-
- song = playlist_get_next_song(&g_playlist);
- if (song >= 0) {
- client_printf(client,
- COMMAND_STATUS_NEXTSONG ": %i\n"
- COMMAND_STATUS_NEXTSONGID ": %u\n",
- song, playlist_get_song_id(&g_playlist, song));
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_kill(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_KILL;
-}
-
-static enum command_return
-handle_close(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_CLOSE;
-}
-
-static enum command_return
-handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *uri = argv[1];
- enum playlist_result result;
-
- if (strncmp(uri, "file:///", 8) == 0) {
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- result = playlist_append_file(&g_playlist,
- client->player_control,
- path,
- NULL);
- return print_playlist_result(client, result);
- }
-
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- result = playlist_append_uri(&g_playlist,
- client->player_control,
- uri, NULL);
- return print_playlist_result(client, result);
- }
-
- GError *error = NULL;
- return addAllIn(client->player_control, uri, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_addid(struct client *client, int argc, char *argv[])
-{
- char *uri = argv[1];
- unsigned added_id;
- enum playlist_result result;
-
- if (strncmp(uri, "file:///", 8) == 0) {
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- result = playlist_append_file(&g_playlist,
- client->player_control,
- path,
- &added_id);
- } else {
- if (uri_has_scheme(uri) && !uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- result = playlist_append_uri(&g_playlist,
- client->player_control,
- uri, &added_id);
- }
-
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
-
- if (argc == 3) {
- unsigned to;
- if (!check_unsigned(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, client->player_control,
- added_id, to);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- enum command_return ret =
- print_playlist_result(client, result);
- playlist_delete_id(&g_playlist, client->player_control,
- added_id);
- return ret;
- }
- }
-
- client_printf(client, "Id: %u\n", added_id);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned start, end;
- enum playlist_result result;
-
- if (!check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_delete_range(&g_playlist, client->player_control,
- start, end);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_delete_id(&g_playlist, client->player_control, id);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_playlist(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_print_uris(client, &g_playlist);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_shuffle(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- unsigned start = 0, end = queue_length(&g_playlist.queue);
- if (argc == 2 && !check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_shuffle(&g_playlist, client->player_control, start, end);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_clear(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_clear(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_save(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- enum playlist_result result;
-
- result = spl_save_playlist(argv[1], &g_playlist);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_load(struct client *client, int argc, char *argv[])
-{
- unsigned start_index, end_index;
-
- if (argc < 3) {
- start_index = 0;
- end_index = G_MAXUINT;
- } else if (!check_range(client, &start_index, &end_index, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result;
-
- result = playlist_open_into_queue(argv[1],
- start_index, end_index,
- &g_playlist,
- client->player_control, true);
- if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
- return print_playlist_result(client, result);
-
- GError *error = NULL;
- if (playlist_load_spl(&g_playlist, client->player_control,
- argv[1], start_index, end_index,
- &error))
- return COMMAND_RETURN_OK;
-
- if (error->domain == playlist_quark() &&
- error->code == PLAYLIST_RESULT_BAD_NAME)
- /* the message for BAD_NAME is confusing when the
- client wants to load a playlist file from the music
- directory; patch the GError object to show "no such
- playlist" instead */
- error->code = PLAYLIST_RESULT_NO_SUCH_LIST;
-
- return print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- if (playlist_file_print(client, argv[1], false))
- return COMMAND_RETURN_OK;
-
- GError *error = NULL;
- return spl_print(client, argv[1], false, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylistinfo(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- if (playlist_file_print(client, argv[1], true))
- return COMMAND_RETURN_OK;
-
- GError *error = NULL;
- return spl_print(client, argv[1], true, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_lsinfo(struct client *client, int argc, char *argv[])
-{
- const char *uri;
-
- if (argc == 2)
- uri = argv[1];
- else
- /* default is root directory */
- uri = "";
-
- if (strncmp(uri, "file:///", 8) == 0) {
- /* print information about an arbitrary local file */
- const char *path = uri + 7;
-
- GError *error = NULL;
- if (!client_allow_file(client, path, &error))
- return print_error(client, error);
-
- struct song *song = song_file_load(path, NULL);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such file");
- return COMMAND_RETURN_ERROR;
- }
-
- song_print_info(client, song);
- song_free(song);
- return COMMAND_RETURN_OK;
- }
-
- struct db_selection selection;
- db_selection_init(&selection, uri, false);
-
- GError *error = NULL;
- if (!db_selection_print(client, &selection, true, &error))
- return print_error(client, error);
-
- if (isRootDirectory(uri)) {
- GPtrArray *list = spl_list(NULL);
- if (list != NULL) {
- print_spl_list(client, list);
- spl_list_free(list);
- }
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_rm(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_delete(argv[1], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_rename(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_rename(argv[1], argv[2], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_plchanges(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- uint32_t version;
-
- if (!check_uint32(client, &version, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_print_changes_info(client, &g_playlist, version);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_plchangesposid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- uint32_t version;
-
- if (!check_uint32(client, &version, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_print_changes_position(client, &g_playlist, version);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistinfo(struct client *client, int argc, char *argv[])
-{
- unsigned start = 0, end = G_MAXUINT;
- bool ret;
-
- if (argc == 2 && !check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = playlist_print_info(client, &g_playlist, start, end);
- if (!ret)
- return print_playlist_result(client,
- PLAYLIST_RESULT_BAD_RANGE);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistid(struct client *client, int argc, char *argv[])
-{
- if (argc >= 2) {
- unsigned id;
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- bool ret = playlist_print_id(client, &g_playlist, id);
- if (!ret)
- return print_playlist_result(client,
- PLAYLIST_RESULT_NO_SUCH_SONG);
- } else {
- playlist_print_info(client, &g_playlist, 0, G_MAXUINT);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_find(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = findSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_findadd(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- findAddIn(client->player_control, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_search(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = searchForSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_searchadd(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret = search_add_songs(client->player_control,
- "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_searchaddpl(struct client *client, int argc, char *argv[])
-{
- const char *playlist = argv[1];
-
- struct locate_item_list *list =
- locate_item_list_parse(argv + 2, argc - 2);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- search_add_to_playlist("", playlist, list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_count(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- GError *error = NULL;
- enum command_return ret =
- searchStatsForSongsIn(client, "", list, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(list);
-
- return ret;
-}
-
-static enum command_return
-handle_playlistfind(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_print_find(client, &g_playlist, list);
-
- locate_item_list_free(list);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistsearch(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *list =
- locate_item_list_parse(argv + 1, argc - 1);
-
- if (list == NULL || list->length == 0) {
- if (list != NULL)
- locate_item_list_free(list);
-
- command_error(client, ACK_ERROR_ARG, "incorrect arguments");
- return COMMAND_RETURN_ERROR;
- }
-
- playlist_print_search(client, &g_playlist, list);
-
- locate_item_list_free(list);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistdelete(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[]) {
- char *playlist = argv[1];
- unsigned from;
-
- if (!check_unsigned(client, &from, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- GError *error = NULL;
- return spl_remove_index(playlist, from, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *playlist = argv[1];
- unsigned from, to;
-
- if (!check_unsigned(client, &from, argv[2]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &to, argv[3]))
- return COMMAND_RETURN_ERROR;
-
- GError *error = NULL;
- return spl_move_index(playlist, from, to, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_update(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *path = NULL;
- unsigned ret;
-
- assert(argc <= 2);
- if (argc == 2) {
- path = argv[1];
-
- if (*path == 0 || strcmp(path, "/") == 0)
- /* backwards compatibility with MPD 0.15 */
- path = NULL;
- else if (!uri_safe_local(path)) {
- command_error(client, ACK_ERROR_ARG,
- "Malformed path");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- ret = update_enqueue(path, false);
- if (ret > 0) {
- client_printf(client, "updating_db: %i\n", ret);
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_rescan(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *path = NULL;
- unsigned ret;
-
- assert(argc <= 2);
- if (argc == 2) {
- path = argv[1];
-
- if (!uri_safe_local(path)) {
- command_error(client, ACK_ERROR_ARG,
- "Malformed path");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- ret = update_enqueue(path, true);
- if (ret > 0) {
- client_printf(client, "updating_db: %i\n", ret);
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_UPDATE_ALREADY,
- "already updating");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_next(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- /* single mode is not considered when this is user who
- * wants to change song. */
- const bool single = g_playlist.queue.single;
- g_playlist.queue.single = false;
-
- playlist_next(&g_playlist, client->player_control);
-
- g_playlist.queue.single = single;
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_previous(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- playlist_previous(&g_playlist, client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_prio(struct client *client, int argc, char *argv[])
-{
- unsigned priority;
-
- if (!check_unsigned(client, &priority, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (priority > 0xff) {
- command_error(client, ACK_ERROR_ARG,
- "Priority out of range: %s", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- for (int i = 2; i < argc; ++i) {
- unsigned start_position, end_position;
- if (!check_range(client, &start_position, &end_position,
- argv[i]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_set_priority(&g_playlist,
- client->player_control,
- start_position, end_position,
- priority);
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_prioid(struct client *client, int argc, char *argv[])
-{
- unsigned priority;
-
- if (!check_unsigned(client, &priority, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (priority > 0xff) {
- command_error(client, ACK_ERROR_ARG,
- "Priority out of range: %s", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- for (int i = 2; i < argc; ++i) {
- unsigned song_id;
- if (!check_unsigned(client, &song_id, argv[i]))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_set_priority_id(&g_playlist,
- client->player_control,
- song_id, priority);
- if (result != PLAYLIST_RESULT_SUCCESS)
- return print_playlist_result(client, result);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *directory = "";
-
- if (argc == 2)
- directory = argv[1];
-
- GError *error = NULL;
- return printAllIn(client, directory, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_setvol(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned level;
- bool success;
-
- if (!check_unsigned(client, &level, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- if (level > 100) {
- command_error(client, ACK_ERROR_ARG, "Invalid volume value");
- return COMMAND_RETURN_ERROR;
- }
-
- success = volume_level_change(level);
- if (!success) {
- command_error(client, ACK_ERROR_SYSTEM,
- "problems setting volume");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_repeat(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_single(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_consume(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_consume(&g_playlist, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- bool status;
- if (!check_bool(client, &status, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- playlist_set_random(&g_playlist, client->player_control, status);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_stats(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return stats_print(client);
-}
-
-static enum command_return
-handle_clearerror(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- pc_clear_error(client->player_control);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_list(struct client *client, int argc, char *argv[])
-{
- struct locate_item_list *conditionals;
- int tagType = locate_parse_type(argv[1]);
-
- if (tagType < 0) {
- command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
- return COMMAND_RETURN_ERROR;
- }
-
- if (tagType == LOCATE_TAG_ANY_TYPE) {
- command_error(client, ACK_ERROR_ARG,
- "\"any\" is not a valid return tag type");
- return COMMAND_RETURN_ERROR;
- }
-
- /* for compatibility with < 0.12.0 */
- if (argc == 3) {
- if (tagType != TAG_ALBUM) {
- command_error(client, ACK_ERROR_ARG,
- "should be \"%s\" for 3 arguments",
- tag_item_names[TAG_ALBUM]);
- return COMMAND_RETURN_ERROR;
- }
-
- locate_item_list_parse(argv + 1, argc - 1);
-
- conditionals = locate_item_list_new(1);
- conditionals->items[0].tag = TAG_ARTIST;
- conditionals->items[0].needle = g_strdup(argv[2]);
- } else {
- conditionals =
- locate_item_list_parse(argv + 2, argc - 2);
- if (conditionals == NULL) {
- command_error(client, ACK_ERROR_ARG,
- "not able to parse args");
- return COMMAND_RETURN_ERROR;
- }
- }
-
- GError *error = NULL;
- enum command_return ret =
- listAllUniqueTags(client, tagType, conditionals, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-
- locate_item_list_free(conditionals);
-
- return ret;
-}
-
-static enum command_return
-handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned start, end;
- int to;
- enum playlist_result result;
-
- if (!check_range(client, &start, &end, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_range(&g_playlist, client->player_control,
- start, end, to);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id;
- int to;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_int(client, &to, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, client->player_control,
- id, to);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned song1, song2;
- enum playlist_result result;
-
- if (!check_unsigned(client, &song1, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &song2, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs(&g_playlist, client->player_control,
- song1, song2);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id1, id2;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id1, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &id2, argv[2]))
- return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs_id(&g_playlist, client->player_control,
- id1, id2);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned song, seek_time;
- enum playlist_result result;
-
- if (!check_unsigned(client, &song, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &seek_time, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_seek_song(&g_playlist, client->player_control,
- song, seek_time);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned id, seek_time;
- enum playlist_result result;
-
- if (!check_unsigned(client, &id, argv[1]))
- return COMMAND_RETURN_ERROR;
- if (!check_unsigned(client, &seek_time, argv[2]))
- return COMMAND_RETURN_ERROR;
-
- result = playlist_seek_song_id(&g_playlist, client->player_control,
- id, seek_time);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_seekcur(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *p = argv[1];
- bool relative = *p == '+' || *p == '-';
- int seek_time;
- if (!check_int(client, &seek_time, p))
- return COMMAND_RETURN_ERROR;
-
- enum playlist_result result =
- playlist_seek_current(&g_playlist, client->player_control,
- seek_time, relative);
- return print_playlist_result(client, result);
-}
-
-static enum command_return
-handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- const char *directory = "";
-
- if (argc == 2)
- directory = argv[1];
-
- GError *error = NULL;
- return printInfoForAllIn(client, directory, &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_ping(G_GNUC_UNUSED struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_password(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned permission = 0;
-
- if (getPermissionFromPassword(argv[1], &permission) < 0) {
- command_error(client, ACK_ERROR_PASSWORD, "incorrect password");
- return COMMAND_RETURN_ERROR;
- }
-
- client_set_permission(client, permission);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned xfade_time;
-
- if (!check_unsigned(client, &xfade_time, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_cross_fade(client->player_control, xfade_time);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- float db;
-
- if (!check_float(client, &db, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_mixramp_db(client->player_control, db);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- float delay_secs;
-
- if (!check_float(client, &delay_secs, argv[1]))
- return COMMAND_RETURN_ERROR;
- pc_set_mixramp_delay(client->player_control, delay_secs);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_enableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned device;
- bool ret;
-
- if (!check_unsigned(client, &device, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = audio_output_enable_index(device);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such audio output");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_disableoutput(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- unsigned device;
- bool ret;
-
- if (!check_unsigned(client, &device, argv[1]))
- return COMMAND_RETURN_ERROR;
-
- ret = audio_output_disable_index(device);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "No such audio output");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_devices(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- printAudioDevices(client);
-
- return COMMAND_RETURN_OK;
-}
-
-/* don't be fooled, this is the command handler for "commands" command */
-static enum command_return
-handle_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
-
-static enum command_return
-handle_not_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[]);
-
-static enum command_return
-handle_config(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- if (!client_is_local(client)) {
- command_error(client, ACK_ERROR_PERMISSION,
- "Command only permitted to local clients");
- return COMMAND_RETURN_ERROR;
- }
-
- const char *path = mapper_get_music_directory_utf8();
- if (path != NULL)
- client_printf(client, "music_directory: %s\n", path);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- GError *error = NULL;
- return spl_clear(argv[1], &error)
- ? COMMAND_RETURN_OK
- : print_error(client, error);
-}
-
-static enum command_return
-handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- char *playlist = argv[1];
- char *uri = argv[2];
-
- bool success;
- GError *error = NULL;
- if (uri_has_scheme(uri)) {
- if (!uri_supported_scheme(uri)) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "unsupported URI scheme");
- return COMMAND_RETURN_ERROR;
- }
-
- success = spl_append_uri(argv[1], playlist, &error);
- } else
- success = addAllInToStoredPlaylist(uri, playlist, &error);
-
- if (!success && error == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
- return COMMAND_RETURN_ERROR;
- }
-
- return success ? COMMAND_RETURN_OK : print_error(client, error);
-}
-
-static enum command_return
-handle_listplaylists(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- GError *error = NULL;
- GPtrArray *list = spl_list(&error);
- if (list == NULL)
- return print_error(client, error);
-
- print_spl_list(client, list);
- spl_list_free(list);
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_replay_gain_mode(struct client *client,
- G_GNUC_UNUSED int argc, char *argv[])
-{
- if (!replay_gain_set_mode_string(argv[1])) {
- command_error(client, ACK_ERROR_ARG,
- "Unrecognized replay gain mode");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_replay_gain_status(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- client_printf(client, "replay_gain_mode: %s\n",
- replay_gain_get_mode_string());
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_idle(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- unsigned flags = 0, j;
- int i;
- const char *const* idle_names;
-
- idle_names = idle_get_names();
- for (i = 1; i < argc; ++i) {
- if (!argv[i])
- continue;
-
- for (j = 0; idle_names[j]; ++j) {
- if (!g_ascii_strcasecmp(argv[i], idle_names[j])) {
- flags |= (1 << j);
- }
- }
- }
-
- /* No argument means that the client wants to receive everything */
- if (flags == 0)
- flags = ~0;
-
- /* enable "idle" mode on this client */
- client_idle_wait(client, flags);
-
- /* return value is "1" so the caller won't print "OK" */
- return 1;
-}
-
-#ifdef ENABLE_SQLITE
-struct sticker_song_find_data {
- struct client *client;
- const char *name;
-};
-
-static void
-sticker_song_find_print_cb(struct song *song, const char *value,
- gpointer user_data)
-{
- struct sticker_song_find_data *data = user_data;
-
- song_print_uri(data->client, song);
- sticker_print_value(data->client, data->name, value);
-}
-
-static enum command_return
-handle_sticker_song(struct client *client, int argc, char *argv[])
-{
- /* get song song_id key */
- if (argc == 5 && strcmp(argv[1], "get") == 0) {
- struct song *song;
- char *value;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- value = sticker_song_get_value(song, argv[4]);
- if (value == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such sticker");
- return COMMAND_RETURN_ERROR;
- }
-
- sticker_print_value(client, argv[4], value);
- g_free(value);
-
- return COMMAND_RETURN_OK;
- /* list song song_id */
- } else if (argc == 4 && strcmp(argv[1], "list") == 0) {
- struct song *song;
- struct sticker *sticker;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- sticker = sticker_song_get(song);
- if (sticker) {
- sticker_print(client, sticker);
- sticker_free(sticker);
- }
-
- return COMMAND_RETURN_OK;
- /* set song song_id id key */
- } else if (argc == 6 && strcmp(argv[1], "set") == 0) {
- struct song *song;
- bool ret;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- ret = sticker_song_set_value(song, argv[4], argv[5]);
- if (!ret) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to set sticker value");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- /* delete song song_id [key] */
- } else if ((argc == 4 || argc == 5) &&
- strcmp(argv[1], "delete") == 0) {
- struct song *song;
- bool ret;
-
- song = db_get_song(argv[3]);
- if (song == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such song");
- return COMMAND_RETURN_ERROR;
- }
-
- ret = argc == 4
- ? sticker_song_delete(song)
- : sticker_song_delete_value(song, argv[4]);
- if (!ret) {
- command_error(client, ACK_ERROR_SYSTEM,
- "no such sticker");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- /* find song dir key */
- } else if (argc == 5 && strcmp(argv[1], "find") == 0) {
- /* "sticker find song a/directory name" */
- struct directory *directory;
- bool success;
- struct sticker_song_find_data data = {
- .client = client,
- .name = argv[4],
- };
-
- db_lock();
- directory = db_get_directory(argv[3]);
- if (directory == NULL) {
- db_unlock();
- command_error(client, ACK_ERROR_NO_EXIST,
- "no such directory");
- return COMMAND_RETURN_ERROR;
- }
-
- success = sticker_song_find(directory, data.name,
- sticker_song_find_print_cb, &data);
- db_unlock();
- if (!success) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to set search sticker database");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
- } else {
- command_error(client, ACK_ERROR_ARG, "bad request");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-static enum command_return
-handle_sticker(struct client *client, int argc, char *argv[])
-{
- assert(argc >= 4);
-
- if (!sticker_enabled()) {
- command_error(client, ACK_ERROR_UNKNOWN,
- "sticker database is disabled");
- return COMMAND_RETURN_ERROR;
- }
-
- if (strcmp(argv[2], "song") == 0)
- return handle_sticker_song(client, argc, argv);
- else {
- command_error(client, ACK_ERROR_ARG,
- "unknown sticker domain");
- return COMMAND_RETURN_ERROR;
- }
-}
-#endif
-
-static enum command_return
-handle_subscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- assert(argc == 2);
-
- switch (client_subscribe(client, argv[1])) {
- case CLIENT_SUBSCRIBE_OK:
- return COMMAND_RETURN_OK;
-
- case CLIENT_SUBSCRIBE_INVALID:
- command_error(client, ACK_ERROR_ARG,
- "invalid channel name");
- return COMMAND_RETURN_ERROR;
-
- case CLIENT_SUBSCRIBE_ALREADY:
- command_error(client, ACK_ERROR_EXIST,
- "already subscribed to this channel");
- return COMMAND_RETURN_ERROR;
-
- case CLIENT_SUBSCRIBE_FULL:
- command_error(client, ACK_ERROR_EXIST,
- "subscription list is full");
- return COMMAND_RETURN_ERROR;
- }
-
- /* unreachable */
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_unsubscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
-{
- assert(argc == 2);
-
- if (client_unsubscribe(client, argv[1]))
- return COMMAND_RETURN_OK;
- else {
- command_error(client, ACK_ERROR_NO_EXIST,
- "not subscribed to this channel");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-struct channels_context {
- GStringChunk *chunk;
-
- GHashTable *channels;
-};
-
-static void
-collect_channels(gpointer data, gpointer user_data)
-{
- struct channels_context *context = user_data;
- const struct client *client = data;
-
- for (GSList *i = client->subscriptions; i != NULL;
- i = g_slist_next(i)) {
- const char *channel = i->data;
-
- if (g_hash_table_lookup(context->channels, channel) == NULL) {
- char *channel2 = g_string_chunk_insert(context->chunk,
- channel);
- g_hash_table_insert(context->channels, channel2,
- context);
- }
- }
-}
-
-static void
-print_channel(gpointer key, G_GNUC_UNUSED gpointer value, gpointer user_data)
-{
- struct client *client = user_data;
- const char *channel = key;
-
- client_printf(client, "channel: %s\n", channel);
-}
-
-static enum command_return
-handle_channels(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 1);
-
- struct channels_context context = {
- .chunk = g_string_chunk_new(1024),
- .channels = g_hash_table_new(g_str_hash, g_str_equal),
- };
-
- client_list_foreach(collect_channels, &context);
-
- g_hash_table_foreach(context.channels, print_channel, client);
-
- g_hash_table_destroy(context.channels);
- g_string_chunk_free(context.chunk);
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_read_messages(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 1);
-
- GSList *messages = client_read_messages(client);
-
- for (GSList *i = messages; i != NULL; i = g_slist_next(i)) {
- struct client_message *msg = i->data;
-
- client_printf(client, "channel: %s\nmessage: %s\n",
- msg->channel, msg->message);
- client_message_free(msg);
- }
-
- g_slist_free(messages);
-
- return COMMAND_RETURN_OK;
-}
-
-struct send_message_context {
- struct client_message msg;
-
- bool sent;
-};
-
-static void
-send_message(gpointer data, gpointer user_data)
-{
- struct send_message_context *context = user_data;
- struct client *client = data;
-
- if (client_push_message(client, &context->msg))
- context->sent = true;
-}
-
-static enum command_return
-handle_send_message(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- assert(argc == 3);
-
- if (!client_message_valid_channel_name(argv[1])) {
- command_error(client, ACK_ERROR_ARG,
- "invalid channel name");
- return COMMAND_RETURN_ERROR;
- }
-
- struct send_message_context context = {
- .sent = false,
- };
-
- client_message_init(&context.msg, argv[1], argv[2]);
-
- client_list_foreach(send_message, &context);
-
- client_message_deinit(&context.msg);
-
- if (context.sent)
- return COMMAND_RETURN_OK;
- else {
- command_error(client, ACK_ERROR_NO_EXIST,
- "nobody is subscribed to this channel");
- return COMMAND_RETURN_ERROR;
- }
-}
-
-/**
- * The command registry.
- *
- * This array must be sorted!
- */
-static const struct command commands[] = {
- { "add", PERMISSION_ADD, 1, 1, handle_add },
- { "addid", PERMISSION_ADD, 1, 2, handle_addid },
- { "channels", PERMISSION_READ, 0, 0, handle_channels },
- { "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
- { "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
- { "close", PERMISSION_NONE, -1, -1, handle_close },
- { "commands", PERMISSION_NONE, 0, 0, handle_commands },
- { "config", PERMISSION_ADMIN, 0, 0, handle_config },
- { "consume", PERMISSION_CONTROL, 1, 1, handle_consume },
- { "count", PERMISSION_READ, 2, -1, handle_count },
- { "crossfade", PERMISSION_CONTROL, 1, 1, handle_crossfade },
- { "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },
- { "decoders", PERMISSION_READ, 0, 0, handle_decoders },
- { "delete", PERMISSION_CONTROL, 1, 1, handle_delete },
- { "deleteid", PERMISSION_CONTROL, 1, 1, handle_deleteid },
- { "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },
- { "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },
- { "find", PERMISSION_READ, 2, -1, handle_find },
- { "findadd", PERMISSION_READ, 2, -1, handle_findadd},
- { "idle", PERMISSION_READ, 0, -1, handle_idle },
- { "kill", PERMISSION_ADMIN, -1, -1, handle_kill },
- { "list", PERMISSION_READ, 1, -1, handle_list },
- { "listall", PERMISSION_READ, 0, 1, handle_listall },
- { "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },
- { "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },
- { "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },
- { "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },
- { "load", PERMISSION_ADD, 1, 2, handle_load },
- { "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },
- { "mixrampdb", PERMISSION_CONTROL, 1, 1, handle_mixrampdb },
- { "mixrampdelay", PERMISSION_CONTROL, 1, 1, handle_mixrampdelay },
- { "move", PERMISSION_CONTROL, 2, 2, handle_move },
- { "moveid", PERMISSION_CONTROL, 2, 2, handle_moveid },
- { "next", PERMISSION_CONTROL, 0, 0, handle_next },
- { "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
- { "outputs", PERMISSION_READ, 0, 0, handle_devices },
- { "password", PERMISSION_NONE, 1, 1, handle_password },
- { "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
- { "ping", PERMISSION_NONE, 0, 0, handle_ping },
- { "play", PERMISSION_CONTROL, 0, 1, handle_play },
- { "playid", PERMISSION_CONTROL, 0, 1, handle_playid },
- { "playlist", PERMISSION_READ, 0, 0, handle_playlist },
- { "playlistadd", PERMISSION_CONTROL, 2, 2, handle_playlistadd },
- { "playlistclear", PERMISSION_CONTROL, 1, 1, handle_playlistclear },
- { "playlistdelete", PERMISSION_CONTROL, 2, 2, handle_playlistdelete },
- { "playlistfind", PERMISSION_READ, 2, -1, handle_playlistfind },
- { "playlistid", PERMISSION_READ, 0, 1, handle_playlistid },
- { "playlistinfo", PERMISSION_READ, 0, 1, handle_playlistinfo },
- { "playlistmove", PERMISSION_CONTROL, 3, 3, handle_playlistmove },
- { "playlistsearch", PERMISSION_READ, 2, -1, handle_playlistsearch },
- { "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
- { "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
- { "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
- { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
- { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
- { "random", PERMISSION_CONTROL, 1, 1, handle_random },
- { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
- { "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
- { "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
- { "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
- handle_replay_gain_mode },
- { "replay_gain_status", PERMISSION_READ, 0, 0,
- handle_replay_gain_status },
- { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
- { "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
- { "save", PERMISSION_CONTROL, 1, 1, handle_save },
- { "search", PERMISSION_READ, 2, -1, handle_search },
- { "searchadd", PERMISSION_ADD, 2, -1, handle_searchadd },
- { "searchaddpl", PERMISSION_CONTROL, 3, -1, handle_searchaddpl },
- { "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
- { "seekcur", PERMISSION_CONTROL, 1, 1, handle_seekcur },
- { "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
- { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
- { "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
- { "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
- { "single", PERMISSION_CONTROL, 1, 1, handle_single },
- { "stats", PERMISSION_READ, 0, 0, handle_stats },
- { "status", PERMISSION_READ, 0, 0, handle_status },
-#ifdef ENABLE_SQLITE
- { "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
-#endif
- { "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
- { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
- { "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
- { "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
- { "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
- { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
- { "update", PERMISSION_CONTROL, 0, 1, handle_update },
- { "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
-};
-
-static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]);
-
-static bool
-command_available(G_GNUC_UNUSED const struct command *cmd)
-{
-#ifdef ENABLE_SQLITE
- if (strcmp(cmd->cmd, "sticker") == 0)
- return sticker_enabled();
-#endif
-
- return true;
-}
-
-/* don't be fooled, this is the command handler for "commands" command */
-static enum command_return
-handle_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const unsigned permission = client_get_permission(client);
- const struct command *cmd;
-
- for (unsigned i = 0; i < num_commands; ++i) {
- cmd = &commands[i];
-
- if (cmd->permission == (permission & cmd->permission) &&
- command_available(cmd))
- client_printf(client, "command: %s\n", cmd->cmd);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-static enum command_return
-handle_not_commands(struct client *client,
- G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
-{
- const unsigned permission = client_get_permission(client);
- const struct command *cmd;
-
- for (unsigned i = 0; i < num_commands; ++i) {
- cmd = &commands[i];
-
- if (cmd->permission != (permission & cmd->permission))
- client_printf(client, "command: %s\n", cmd->cmd);
- }
-
- return COMMAND_RETURN_OK;
-}
-
-void command_init(void)
-{
-#ifndef NDEBUG
- /* ensure that the command list is sorted */
- for (unsigned i = 0; i < num_commands - 1; ++i)
- assert(strcmp(commands[i].cmd, commands[i + 1].cmd) < 0);
-#endif
-}
-
-void command_finish(void)
-{
-}
-
-static const struct command *
-command_lookup(const char *name)
-{
- unsigned a = 0, b = num_commands, i;
- int cmp;
-
- /* binary search */
- do {
- i = (a + b) / 2;
-
- cmp = strcmp(name, commands[i].cmd);
- if (cmp == 0)
- return &commands[i];
- else if (cmp < 0)
- b = i;
- else if (cmp > 0)
- a = i + 1;
- } while (a < b);
-
- return NULL;
-}
-
-static bool
-command_check_request(const struct command *cmd, struct client *client,
- unsigned permission, int argc, char *argv[])
-{
- int min = cmd->min + 1;
- int max = cmd->max + 1;
-
- if (cmd->permission != (permission & cmd->permission)) {
- if (client != NULL)
- command_error(client, ACK_ERROR_PERMISSION,
- "you don't have permission for \"%s\"",
- cmd->cmd);
- return false;
- }
-
- if (min == 0)
- return true;
-
- if (min == max && max != argc) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "wrong number of arguments for \"%s\"",
- argv[0]);
- return false;
- } else if (argc < min) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "too few arguments for \"%s\"", argv[0]);
- return false;
- } else if (argc > max && max /* != 0 */ ) {
- if (client != NULL)
- command_error(client, ACK_ERROR_ARG,
- "too many arguments for \"%s\"", argv[0]);
- return false;
- } else
- return true;
-}
-
-static const struct command *
-command_checked_lookup(struct client *client, unsigned permission,
- int argc, char *argv[])
-{
- const struct command *cmd;
-
- current_command = "";
-
- if (argc == 0)
- return NULL;
-
- cmd = command_lookup(argv[0]);
- if (cmd == NULL) {
- if (client != NULL)
- command_error(client, ACK_ERROR_UNKNOWN,
- "unknown command \"%s\"", argv[0]);
- return NULL;
- }
-
- current_command = cmd->cmd;
-
- if (!command_check_request(cmd, client, permission, argc, argv))
- return NULL;
-
- return cmd;
-}
-
-enum command_return
-command_process(struct client *client, unsigned num, char *line)
-{
- GError *error = NULL;
- int argc;
- char *argv[COMMAND_ARGV_MAX] = { NULL };
- const struct command *cmd;
- enum command_return ret = COMMAND_RETURN_ERROR;
-
- command_list_num = num;
-
- /* get the command name (first word on the line) */
-
- argv[0] = tokenizer_next_word(&line, &error);
- if (argv[0] == NULL) {
- current_command = "";
- if (*line == 0)
- command_error(client, ACK_ERROR_UNKNOWN,
- "No command given");
- else {
- command_error(client, ACK_ERROR_UNKNOWN,
- "%s", error->message);
- g_error_free(error);
- }
- current_command = NULL;
-
- return COMMAND_RETURN_ERROR;
- }
-
- argc = 1;
-
- /* now parse the arguments (quoted or unquoted) */
-
- while (argc < (int)G_N_ELEMENTS(argv) &&
- (argv[argc] =
- tokenizer_next_param(&line, &error)) != NULL)
- ++argc;
-
- /* some error checks; we have to set current_command because
- command_error() expects it to be set */
-
- current_command = argv[0];
-
- if (argc >= (int)G_N_ELEMENTS(argv)) {
- command_error(client, ACK_ERROR_ARG, "Too many arguments");
- current_command = NULL;
- return COMMAND_RETURN_ERROR;
- }
-
- if (*line != 0) {
- command_error(client, ACK_ERROR_ARG,
- "%s", error->message);
- current_command = NULL;
- g_error_free(error);
- return COMMAND_RETURN_ERROR;
- }
-
- /* look up and invoke the command handler */
-
- cmd = command_checked_lookup(client, client_get_permission(client),
- argc, argv);
- if (cmd)
- ret = cmd->handler(client, argc, argv);
-
- current_command = NULL;
- command_list_num = 0;
-
- return ret;
-}
diff --git a/src/command.h b/src/command.h
index 68d1f95e..9ea5bb52 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,27 +20,34 @@
#ifndef MPD_COMMAND_H
#define MPD_COMMAND_H
-#include "ack.h"
-
-#include <glib.h>
-#include <stdbool.h>
-
enum command_return {
- COMMAND_RETURN_ERROR = -1,
- COMMAND_RETURN_OK = 0,
- COMMAND_RETURN_KILL = 10,
- COMMAND_RETURN_CLOSE = 20,
+ /**
+ * The command has succeeded, but the "OK" response was not
+ * yet sent to the client.
+ */
+ COMMAND_RETURN_OK,
+
+ /**
+ * The connection is now in "idle" mode, and no response shall
+ * be generated.
+ */
+ COMMAND_RETURN_IDLE,
+
+ /**
+ * There was an error. The "ACK" response was sent to the
+ * client.
+ */
+ COMMAND_RETURN_ERROR,
+
+ /**
+ * The connection to this client shall be closed.
+ */
+ COMMAND_RETURN_CLOSE,
+
+ /**
+ * The MPD process shall be shut down.
+ */
+ COMMAND_RETURN_KILL,
};
-struct client;
-
-void command_init(void);
-
-void command_finish(void);
-
-enum command_return
-command_process(struct client *client, unsigned num, char *line);
-
-void command_success(struct client *client);
-
#endif
diff --git a/src/conf.c b/src/conf.c
deleted file mode 100644
index 167f2da9..00000000
--- a/src/conf.c
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "conf.h"
-#include "utils.h"
-#include "string_util.h"
-#include "tokenizer.h"
-#include "path.h"
-#include "mpd_error.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "config"
-
-#define MAX_STRING_SIZE MPD_PATH_MAX+80
-
-#define CONF_COMMENT '#'
-
-struct config_entry {
- const char *const name;
- const bool repeatable;
- const bool block;
-
- GSList *params;
-};
-
-static struct config_entry config_entries[] = {
- { .name = CONF_MUSIC_DIR, false, false },
- { .name = CONF_PLAYLIST_DIR, false, false },
- { .name = CONF_FOLLOW_INSIDE_SYMLINKS, false, false },
- { .name = CONF_FOLLOW_OUTSIDE_SYMLINKS, false, false },
- { .name = CONF_DB_FILE, false, false },
- { .name = CONF_STICKER_FILE, false, false },
- { .name = CONF_LOG_FILE, false, false },
- { .name = CONF_PID_FILE, false, false },
- { .name = CONF_STATE_FILE, false, false },
- { .name = "restore_paused", false, false },
- { .name = CONF_USER, false, false },
- { .name = CONF_GROUP, false, false },
- { .name = CONF_BIND_TO_ADDRESS, true, false },
- { .name = CONF_PORT, false, false },
- { .name = CONF_LOG_LEVEL, false, false },
- { .name = CONF_ZEROCONF_NAME, false, false },
- { .name = CONF_ZEROCONF_ENABLED, false, false },
- { .name = CONF_PASSWORD, true, false },
- { .name = CONF_DEFAULT_PERMS, false, false },
- { .name = CONF_AUDIO_OUTPUT, true, true },
- { .name = CONF_AUDIO_OUTPUT_FORMAT, false, false },
- { .name = CONF_MIXER_TYPE, false, false },
- { .name = CONF_REPLAYGAIN, false, false },
- { .name = CONF_REPLAYGAIN_PREAMP, false, false },
- { .name = CONF_REPLAYGAIN_MISSING_PREAMP, false, false },
- { .name = CONF_REPLAYGAIN_LIMIT, false, false },
- { .name = CONF_VOLUME_NORMALIZATION, false, false },
- { .name = CONF_SAMPLERATE_CONVERTER, false, false },
- { .name = CONF_AUDIO_BUFFER_SIZE, false, false },
- { .name = CONF_BUFFER_BEFORE_PLAY, false, false },
- { .name = CONF_HTTP_PROXY_HOST, false, false },
- { .name = CONF_HTTP_PROXY_PORT, false, false },
- { .name = CONF_HTTP_PROXY_USER, false, false },
- { .name = CONF_HTTP_PROXY_PASSWORD, false, false },
- { .name = CONF_CONN_TIMEOUT, false, false },
- { .name = CONF_MAX_CONN, false, false },
- { .name = CONF_MAX_PLAYLIST_LENGTH, false, false },
- { .name = CONF_MAX_COMMAND_LIST_SIZE, false, false },
- { .name = CONF_MAX_OUTPUT_BUFFER_SIZE, false, false },
- { .name = CONF_FS_CHARSET, false, false },
- { .name = CONF_ID3V1_ENCODING, false, false },
- { .name = CONF_METADATA_TO_USE, false, false },
- { .name = CONF_SAVE_ABSOLUTE_PATHS, false, false },
- { .name = CONF_DECODER, true, true },
- { .name = CONF_INPUT, true, true },
- { .name = CONF_GAPLESS_MP3_PLAYBACK, false, false },
- { .name = CONF_PLAYLIST_PLUGIN, true, true },
- { .name = CONF_AUTO_UPDATE, false, false },
- { .name = CONF_AUTO_UPDATE_DEPTH, false, false },
- { .name = CONF_DESPOTIFY_USER, false, false },
- { .name = CONF_DESPOTIFY_PASSWORD, false, false},
- { .name = CONF_DESPOTIFY_HIGH_BITRATE, false, false },
- { .name = "filter", true, true },
-};
-
-static bool
-get_bool(const char *value, bool *value_r)
-{
- static const char *t[] = { "yes", "true", "1", NULL };
- static const char *f[] = { "no", "false", "0", NULL };
-
- if (string_array_contains(t, value)) {
- *value_r = true;
- return true;
- }
-
- if (string_array_contains(f, value)) {
- *value_r = false;
- return true;
- }
-
- return false;
-}
-
-struct config_param *
-config_new_param(const char *value, int line)
-{
- struct config_param *ret = g_new(struct config_param, 1);
-
- if (!value)
- ret->value = NULL;
- else
- ret->value = g_strdup(value);
-
- ret->line = line;
-
- ret->num_block_params = 0;
- ret->block_params = NULL;
- ret->used = false;
-
- return ret;
-}
-
-void
-config_param_free(struct config_param *param)
-{
- g_free(param->value);
-
- for (unsigned i = 0; i < param->num_block_params; i++) {
- g_free(param->block_params[i].name);
- g_free(param->block_params[i].value);
- }
-
- if (param->num_block_params)
- g_free(param->block_params);
-
- g_free(param);
-}
-
-static void
-config_param_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct config_param *param = data;
-
- config_param_free(param);
-}
-
-static struct config_entry *
-config_entry_get(const char *name)
-{
- for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
- struct config_entry *entry = &config_entries[i];
- if (strcmp(entry->name, name) == 0)
- return entry;
- }
-
- return NULL;
-}
-
-void config_global_finish(void)
-{
- for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
- struct config_entry *entry = &config_entries[i];
-
- g_slist_foreach(entry->params,
- config_param_free_callback, NULL);
- g_slist_free(entry->params);
- }
-}
-
-void config_global_init(void)
-{
-}
-
-static void
-config_param_check(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct config_param *param = data;
-
- if (!param->used)
- /* this whole config_param was not queried at all -
- the feature might be disabled at compile time?
- Silently ignore it here. */
- return;
-
- for (unsigned i = 0; i < param->num_block_params; i++) {
- struct block_param *bp = &param->block_params[i];
-
- if (!bp->used)
- g_warning("option '%s' on line %i was not recognized",
- bp->name, bp->line);
- }
-}
-
-void config_global_check(void)
-{
- for (unsigned i = 0; i < G_N_ELEMENTS(config_entries); ++i) {
- struct config_entry *entry = &config_entries[i];
-
- g_slist_foreach(entry->params, config_param_check, NULL);
- }
-}
-
-void
-config_add_block_param(struct config_param * param, const char *name,
- const char *value, int line)
-{
- struct block_param *bp;
-
- assert(config_get_block_param(param, name) == NULL);
-
- param->num_block_params++;
-
- param->block_params = g_realloc(param->block_params,
- param->num_block_params *
- sizeof(param->block_params[0]));
-
- bp = &param->block_params[param->num_block_params - 1];
-
- bp->name = g_strdup(name);
- bp->value = g_strdup(value);
- bp->line = line;
- bp->used = false;
-}
-
-static bool
-config_read_name_value(struct config_param *param, char *input, unsigned line,
- GError **error_r)
-{
- const char *name = tokenizer_next_word(&input, error_r);
- if (name == NULL) {
- assert(*input != 0);
- return false;
- }
-
- const char *value = tokenizer_next_string(&input, error_r);
- if (value == NULL) {
- if (*input == 0) {
- assert(error_r == NULL || *error_r == NULL);
- g_set_error(error_r, config_quark(), 0,
- "Value missing");
- } else {
- assert(error_r == NULL || *error_r != NULL);
- }
-
- return false;
- }
-
- if (*input != 0 && *input != CONF_COMMENT) {
- g_set_error(error_r, config_quark(), 0,
- "Unknown tokens after value");
- return false;
- }
-
- const struct block_param *bp = config_get_block_param(param, name);
- if (bp != NULL) {
- g_set_error(error_r, config_quark(), 0,
- "\"%s\" is duplicate, first defined on line %i",
- name, bp->line);
- return false;
- }
-
- config_add_block_param(param, name, value, line);
- return true;
-}
-
-static struct config_param *
-config_read_block(FILE *fp, int *count, char *string, GError **error_r)
-{
- struct config_param *ret = config_new_param(NULL, *count);
- GError *error = NULL;
-
- while (true) {
- char *line;
-
- line = fgets(string, MAX_STRING_SIZE, fp);
- if (line == NULL) {
- config_param_free(ret);
- g_set_error(error_r, config_quark(), 0,
- "Expected '}' before end-of-file");
- return NULL;
- }
-
- (*count)++;
- line = strchug_fast(line);
- if (*line == 0 || *line == CONF_COMMENT)
- continue;
-
- if (*line == '}') {
- /* end of this block; return from the function
- (and from this "while" loop) */
-
- line = strchug_fast(line + 1);
- if (*line != 0 && *line != CONF_COMMENT) {
- config_param_free(ret);
- g_set_error(error_r, config_quark(), 0,
- "line %i: Unknown tokens after '}'",
- *count);
- return false;
- }
-
- return ret;
- }
-
- /* parse name and value */
-
- if (!config_read_name_value(ret, line, *count, &error)) {
- assert(*line != 0);
- config_param_free(ret);
- g_propagate_prefixed_error(error_r, error,
- "line %i: ", *count);
- return NULL;
- }
- }
-}
-
-bool
-config_read_file(const char *file, GError **error_r)
-{
- FILE *fp;
- char string[MAX_STRING_SIZE + 1];
- int count = 0;
- struct config_entry *entry;
- struct config_param *param;
-
- g_debug("loading file %s", file);
-
- if (!(fp = fopen(file, "r"))) {
- g_set_error(error_r, config_quark(), errno,
- "Failed to open %s: %s",
- file, g_strerror(errno));
- return false;
- }
-
- while (fgets(string, MAX_STRING_SIZE, fp)) {
- char *line;
- const char *name, *value;
- GError *error = NULL;
-
- count++;
-
- line = strchug_fast(string);
- if (*line == 0 || *line == CONF_COMMENT)
- continue;
-
- /* the first token in each line is the name, followed
- by either the value or '{' */
-
- name = tokenizer_next_word(&line, &error);
- if (name == NULL) {
- assert(*line != 0);
- g_propagate_prefixed_error(error_r, error,
- "line %i: ", count);
- fclose(fp);
- return false;
- }
-
- /* get the definition of that option, and check the
- "repeatable" flag */
-
- entry = config_entry_get(name);
- if (entry == NULL) {
- g_set_error(error_r, config_quark(), 0,
- "unrecognized parameter in config file at "
- "line %i: %s\n", count, name);
- fclose(fp);
- return false;
- }
-
- if (entry->params != NULL && !entry->repeatable) {
- param = entry->params->data;
- g_set_error(error_r, config_quark(), 0,
- "config parameter \"%s\" is first defined "
- "on line %i and redefined on line %i\n",
- name, param->line, count);
- fclose(fp);
- return false;
- }
-
- /* now parse the block or the value */
-
- if (entry->block) {
- /* it's a block, call config_read_block() */
-
- if (*line != '{') {
- g_set_error(error_r, config_quark(), 0,
- "line %i: '{' expected", count);
- fclose(fp);
- return false;
- }
-
- line = strchug_fast(line + 1);
- if (*line != 0 && *line != CONF_COMMENT) {
- g_set_error(error_r, config_quark(), 0,
- "line %i: Unknown tokens after '{'",
- count);
- fclose(fp);
- return false;
- }
-
- param = config_read_block(fp, &count, string, error_r);
- if (param == NULL) {
- fclose(fp);
- return false;
- }
- } else {
- /* a string value */
-
- value = tokenizer_next_string(&line, &error);
- if (value == NULL) {
- if (*line == 0)
- g_set_error(error_r, config_quark(), 0,
- "line %i: Value missing",
- count);
- else {
- g_set_error(error_r, config_quark(), 0,
- "line %i: %s", count,
- error->message);
- g_error_free(error);
- }
-
- fclose(fp);
- return false;
- }
-
- if (*line != 0 && *line != CONF_COMMENT) {
- g_set_error(error_r, config_quark(), 0,
- "line %i: Unknown tokens after value",
- count);
- fclose(fp);
- return false;
- }
-
- param = config_new_param(value, count);
- }
-
- entry->params = g_slist_append(entry->params, param);
- }
- fclose(fp);
-
- return true;
-}
-
-const struct config_param *
-config_get_next_param(const char *name, const struct config_param * last)
-{
- struct config_entry *entry;
- GSList *node;
- struct config_param *param;
-
- entry = config_entry_get(name);
- if (entry == NULL)
- return NULL;
-
- node = entry->params;
-
- if (last) {
- node = g_slist_find(node, last);
- if (node == NULL)
- return NULL;
-
- node = g_slist_next(node);
- }
-
- if (node == NULL)
- return NULL;
-
- param = node->data;
- param->used = true;
- return param;
-}
-
-const char *
-config_get_string(const char *name, const char *default_value)
-{
- const struct config_param *param = config_get_param(name);
-
- if (param == NULL)
- return default_value;
-
- return param->value;
-}
-
-char *
-config_dup_path(const char *name, GError **error_r)
-{
- assert(error_r != NULL);
- assert(*error_r == NULL);
-
- const struct config_param *param = config_get_param(name);
- if (param == NULL)
- return NULL;
-
- char *path = parsePath(param->value, error_r);
- if (G_UNLIKELY(path == NULL))
- g_prefix_error(error_r,
- "Invalid path in \"%s\" at line %i: ",
- name, param->line);
-
- return path;
-}
-
-unsigned
-config_get_unsigned(const char *name, unsigned default_value)
-{
- const struct config_param *param = config_get_param(name);
- long value;
- char *endptr;
-
- if (param == NULL)
- return default_value;
-
- value = strtol(param->value, &endptr, 0);
- if (*endptr != 0 || value < 0)
- MPD_ERROR("Not a valid non-negative number in line %i",
- param->line);
-
- return (unsigned)value;
-}
-
-unsigned
-config_get_positive(const char *name, unsigned default_value)
-{
- const struct config_param *param = config_get_param(name);
- long value;
- char *endptr;
-
- if (param == NULL)
- return default_value;
-
- value = strtol(param->value, &endptr, 0);
- if (*endptr != 0)
- MPD_ERROR("Not a valid number in line %i", param->line);
-
- if (value <= 0)
- MPD_ERROR("Not a positive number in line %i", param->line);
-
- return (unsigned)value;
-}
-
-const struct block_param *
-config_get_block_param(const struct config_param * param, const char *name)
-{
- if (param == NULL)
- return NULL;
-
- for (unsigned i = 0; i < param->num_block_params; i++) {
- if (0 == strcmp(name, param->block_params[i].name)) {
- struct block_param *bp = &param->block_params[i];
- bp->used = true;
- return bp;
- }
- }
-
- return NULL;
-}
-
-bool config_get_bool(const char *name, bool default_value)
-{
- const struct config_param *param = config_get_param(name);
- bool success, value;
-
- if (param == NULL)
- return default_value;
-
- success = get_bool(param->value, &value);
- if (!success)
- MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
- "(no, false, 0) on line %i\n",
- name, param->line);
-
- return value;
-}
-
-const char *
-config_get_block_string(const struct config_param *param, const char *name,
- const char *default_value)
-{
- const struct block_param *bp = config_get_block_param(param, name);
-
- if (bp == NULL)
- return default_value;
-
- return bp->value;
-}
-
-char *
-config_dup_block_path(const struct config_param *param, const char *name,
- GError **error_r)
-{
- assert(error_r != NULL);
- assert(*error_r == NULL);
-
- const struct block_param *bp = config_get_block_param(param, name);
- if (bp == NULL)
- return NULL;
-
- char *path = parsePath(bp->value, error_r);
- if (G_UNLIKELY(path == NULL))
- g_prefix_error(error_r,
- "Invalid path in \"%s\" at line %i: ",
- name, bp->line);
-
- return path;
-}
-
-unsigned
-config_get_block_unsigned(const struct config_param *param, const char *name,
- unsigned default_value)
-{
- const struct block_param *bp = config_get_block_param(param, name);
- long value;
- char *endptr;
-
- if (bp == NULL)
- return default_value;
-
- value = strtol(bp->value, &endptr, 0);
- if (*endptr != 0)
- MPD_ERROR("Not a valid number in line %i", bp->line);
-
- if (value < 0)
- MPD_ERROR("Not a positive number in line %i", bp->line);
-
- return (unsigned)value;
-}
-
-bool
-config_get_block_bool(const struct config_param *param, const char *name,
- bool default_value)
-{
- const struct block_param *bp = config_get_block_param(param, name);
- bool success, value;
-
- if (bp == NULL)
- return default_value;
-
- success = get_bool(bp->value, &value);
- if (!success)
- MPD_ERROR("%s is not a boolean value (yes, true, 1) or "
- "(no, false, 0) on line %i\n",
- name, bp->line);
-
- return value;
-}
diff --git a/src/conf.h b/src/conf.h
index 815c739b..8eb185fa 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -20,208 +20,14 @@
#ifndef MPD_CONF_H
#define MPD_CONF_H
-#include <stdbool.h>
-#include <glib.h>
-
-#define CONF_MUSIC_DIR "music_directory"
-#define CONF_PLAYLIST_DIR "playlist_directory"
-#define CONF_FOLLOW_INSIDE_SYMLINKS "follow_inside_symlinks"
-#define CONF_FOLLOW_OUTSIDE_SYMLINKS "follow_outside_symlinks"
-#define CONF_DB_FILE "db_file"
-#define CONF_STICKER_FILE "sticker_file"
-#define CONF_LOG_FILE "log_file"
-#define CONF_PID_FILE "pid_file"
-#define CONF_STATE_FILE "state_file"
-#define CONF_USER "user"
-#define CONF_GROUP "group"
-#define CONF_BIND_TO_ADDRESS "bind_to_address"
-#define CONF_PORT "port"
-#define CONF_LOG_LEVEL "log_level"
-#define CONF_ZEROCONF_NAME "zeroconf_name"
-#define CONF_ZEROCONF_ENABLED "zeroconf_enabled"
-#define CONF_PASSWORD "password"
-#define CONF_DEFAULT_PERMS "default_permissions"
-#define CONF_AUDIO_OUTPUT "audio_output"
-#define CONF_AUDIO_FILTER "filter"
-#define CONF_AUDIO_OUTPUT_FORMAT "audio_output_format"
-#define CONF_MIXER_TYPE "mixer_type"
-#define CONF_REPLAYGAIN "replaygain"
-#define CONF_REPLAYGAIN_PREAMP "replaygain_preamp"
-#define CONF_REPLAYGAIN_MISSING_PREAMP "replaygain_missing_preamp"
-#define CONF_REPLAYGAIN_LIMIT "replaygain_limit"
-#define CONF_VOLUME_NORMALIZATION "volume_normalization"
-#define CONF_SAMPLERATE_CONVERTER "samplerate_converter"
-#define CONF_AUDIO_BUFFER_SIZE "audio_buffer_size"
-#define CONF_BUFFER_BEFORE_PLAY "buffer_before_play"
-#define CONF_HTTP_PROXY_HOST "http_proxy_host"
-#define CONF_HTTP_PROXY_PORT "http_proxy_port"
-#define CONF_HTTP_PROXY_USER "http_proxy_user"
-#define CONF_HTTP_PROXY_PASSWORD "http_proxy_password"
-#define CONF_CONN_TIMEOUT "connection_timeout"
-#define CONF_MAX_CONN "max_connections"
-#define CONF_MAX_PLAYLIST_LENGTH "max_playlist_length"
-#define CONF_MAX_COMMAND_LIST_SIZE "max_command_list_size"
-#define CONF_MAX_OUTPUT_BUFFER_SIZE "max_output_buffer_size"
-#define CONF_FS_CHARSET "filesystem_charset"
-#define CONF_ID3V1_ENCODING "id3v1_encoding"
-#define CONF_METADATA_TO_USE "metadata_to_use"
-#define CONF_SAVE_ABSOLUTE_PATHS "save_absolute_paths_in_playlists"
-#define CONF_DECODER "decoder"
-#define CONF_INPUT "input"
-#define CONF_GAPLESS_MP3_PLAYBACK "gapless_mp3_playback"
-#define CONF_PLAYLIST_PLUGIN "playlist_plugin"
-#define CONF_AUTO_UPDATE "auto_update"
-#define CONF_AUTO_UPDATE_DEPTH "auto_update_depth"
-#define CONF_DESPOTIFY_USER "despotify_user"
-#define CONF_DESPOTIFY_PASSWORD "despotify_password"
-#define CONF_DESPOTIFY_HIGH_BITRATE "despotify_high_bitrate"
+#include "ConfigGlobal.hxx"
+#include "ConfigOption.hxx"
+#include "ConfigData.hxx"
+#include "gcc.h"
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
#define MAX_FILTER_CHAIN_LENGTH 255
-struct block_param {
- char *name;
- char *value;
- int line;
-
- /**
- * This flag is false when nobody has queried the value of
- * this option yet.
- */
- bool used;
-};
-
-struct config_param {
- char *value;
- unsigned int line;
-
- struct block_param *block_params;
- unsigned num_block_params;
-
- /**
- * This flag is false when nobody has queried the value of
- * this option yet.
- */
- bool used;
-};
-
-/**
- * A GQuark for GError instances, resulting from malformed
- * configuration.
- */
-G_GNUC_CONST
-static inline GQuark
-config_quark(void)
-{
- return g_quark_from_static_string("config");
-}
-
-void config_global_init(void);
-void config_global_finish(void);
-
-/**
- * Call this function after all configuration has been evaluated. It
- * checks for unused parameters, and logs warnings.
- */
-void config_global_check(void);
-
-bool
-config_read_file(const char *file, GError **error_r);
-
-/* don't free the returned value
- set _last_ to NULL to get first entry */
-G_GNUC_PURE
-const struct config_param *
-config_get_next_param(const char *name, const struct config_param *last);
-
-G_GNUC_PURE
-static inline const struct config_param *
-config_get_param(const char *name)
-{
- return config_get_next_param(name, NULL);
-}
-
-/* Note on G_GNUC_PURE: Some of the functions declared pure are not
- really pure in strict sense. They have side effect such that they
- validate parameter's value and signal an error if it's invalid.
- However, if the argument was already validated or we don't care
- about the argument at all, this may be ignored so in the end, we
- should be fine with calling those functions pure. */
-
-G_GNUC_PURE
-const char *
-config_get_string(const char *name, const char *default_value);
-
-/**
- * Returns an optional configuration variable which contains an
- * absolute path. If there is a tilde prefix, it is expanded.
- * Returns NULL if the value is not present. If the path could not be
- * parsed, returns NULL and sets the error.
- *
- * The return value must be freed with g_free().
- */
-G_GNUC_MALLOC
-char *
-config_dup_path(const char *name, GError **error_r);
-
-G_GNUC_PURE
-unsigned
-config_get_unsigned(const char *name, unsigned default_value);
-
-G_GNUC_PURE
-unsigned
-config_get_positive(const char *name, unsigned default_value);
-
-G_GNUC_PURE
-const struct block_param *
-config_get_block_param(const struct config_param *param, const char *name);
-
-G_GNUC_PURE
-bool config_get_bool(const char *name, bool default_value);
-
-G_GNUC_PURE
-const char *
-config_get_block_string(const struct config_param *param, const char *name,
- const char *default_value);
-
-G_GNUC_MALLOC
-static inline char *
-config_dup_block_string(const struct config_param *param, const char *name,
- const char *default_value)
-{
- return g_strdup(config_get_block_string(param, name, default_value));
-}
-
-/**
- * Same as config_dup_path(), but looks up the setting in the
- * specified block.
- */
-G_GNUC_MALLOC
-char *
-config_dup_block_path(const struct config_param *param, const char *name,
- GError **error_r);
-
-G_GNUC_PURE
-unsigned
-config_get_block_unsigned(const struct config_param *param, const char *name,
- unsigned default_value);
-
-G_GNUC_PURE
-bool
-config_get_block_bool(const struct config_param *param, const char *name,
- bool default_value);
-
-G_GNUC_MALLOC
-struct config_param *
-config_new_param(const char *value, int line);
-
-void
-config_param_free(struct config_param *param);
-
-void
-config_add_block_param(struct config_param * param, const char *name,
- const char *value, int line);
-
#endif
diff --git a/src/cue/cue_parser.c b/src/cue/cue_parser.c
index 9ccc3bcd..bee757c9 100644
--- a/src/cue/cue_parser.c
+++ b/src/cue/cue_parser.c
@@ -23,6 +23,8 @@
#include "song.h"
#include "tag.h"
+#include <glib.h>
+
#include <assert.h>
#include <stdlib.h>
diff --git a/src/db/ProxyDatabasePlugin.cxx b/src/db/ProxyDatabasePlugin.cxx
new file mode 100644
index 00000000..efaaffeb
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.cxx
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ProxyDatabasePlugin.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "PlaylistVector.hxx"
+#include "Directory.hxx"
+#include "gcc.h"
+#include "conf.h"
+
+extern "C" {
+#include "db_error.h"
+#include "song.h"
+}
+
+#undef MPD_DIRECTORY_H
+#undef MPD_SONG_H
+#include <mpd/client.h>
+
+#include <cassert>
+#include <string>
+#include <list>
+
+class ProxyDatabase : public Database {
+ std::string host;
+ unsigned port;
+
+ struct mpd_connection *connection;
+ Directory *root;
+
+public:
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ virtual bool Open(GError **error_r) override;
+ virtual void Close() override;
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const override;
+ virtual void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const override;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const override;
+
+protected:
+ bool Configure(const struct config_param *param, GError **error_r);
+};
+
+G_GNUC_CONST
+static inline GQuark
+libmpdclient_quark(void)
+{
+ return g_quark_from_static_string("libmpdclient");
+}
+
+static constexpr struct {
+ enum tag_type d;
+ enum mpd_tag_type s;
+} tag_table[] = {
+ { TAG_ARTIST, MPD_TAG_ARTIST },
+ { TAG_ALBUM, MPD_TAG_ALBUM },
+ { TAG_ALBUM_ARTIST, MPD_TAG_ALBUM_ARTIST },
+ { TAG_TITLE, MPD_TAG_TITLE },
+ { TAG_TRACK, MPD_TAG_TRACK },
+ { TAG_NAME, MPD_TAG_NAME },
+ { TAG_GENRE, MPD_TAG_GENRE },
+ { TAG_DATE, MPD_TAG_DATE },
+ { TAG_COMPOSER, MPD_TAG_COMPOSER },
+ { TAG_PERFORMER, MPD_TAG_PERFORMER },
+ { TAG_COMMENT, MPD_TAG_COMMENT },
+ { TAG_DISC, MPD_TAG_DISC },
+ { TAG_MUSICBRAINZ_ARTISTID, MPD_TAG_MUSICBRAINZ_ARTISTID },
+ { TAG_MUSICBRAINZ_ALBUMID, MPD_TAG_MUSICBRAINZ_ALBUMID },
+ { TAG_MUSICBRAINZ_ALBUMARTISTID,
+ MPD_TAG_MUSICBRAINZ_ALBUMARTISTID },
+ { TAG_MUSICBRAINZ_TRACKID, MPD_TAG_MUSICBRAINZ_TRACKID },
+ { TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
+};
+
+G_GNUC_CONST
+static enum mpd_tag_type
+Convert(enum tag_type tag_type)
+{
+ for (auto i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ if (i->d == tag_type)
+ return i->s;
+
+ return MPD_TAG_COUNT;
+}
+
+static bool
+CheckError(struct mpd_connection *connection, GError **error_r)
+{
+ const auto error = mpd_connection_get_error(connection);
+ if (error == MPD_ERROR_SUCCESS)
+ return true;
+
+ g_set_error_literal(error_r, libmpdclient_quark(), (int)error,
+ mpd_connection_get_error_message(connection));
+ mpd_connection_clear_error(connection);
+ return false;
+}
+
+Database *
+ProxyDatabase::Create(const struct config_param *param, GError **error_r)
+{
+ ProxyDatabase *db = new ProxyDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
+
+ return db;
+}
+
+bool
+ProxyDatabase::Configure(const struct config_param *param, GError **)
+{
+ host = config_get_block_string(param, "host", "");
+ port = config_get_block_unsigned(param, "port", 0);
+
+ return true;
+}
+
+bool
+ProxyDatabase::Open(GError **error_r)
+{
+ connection = mpd_connection_new(host.empty() ? NULL : host.c_str(),
+ port, 0);
+ if (connection == NULL) {
+ g_set_error_literal(error_r, libmpdclient_quark(),
+ (int)MPD_ERROR_OOM, "Out of memory");
+ return false;
+ }
+
+ if (!CheckError(connection, error_r)) {
+ mpd_connection_free(connection);
+ return false;
+ }
+
+ root = Directory::NewRoot();
+
+ return true;
+}
+
+void
+ProxyDatabase::Close()
+{
+ assert(connection != nullptr);
+
+ root->Free();
+ mpd_connection_free(connection);
+}
+
+static song *
+Convert(const struct mpd_song *song);
+
+struct song *
+ProxyDatabase::GetSong(const char *uri, GError **error_r) const
+{
+ // TODO: implement
+ // TODO: auto-reconnect
+
+ if (!mpd_send_list_meta(connection, uri)) {
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ struct mpd_song *song = mpd_recv_song(connection);
+ struct song *song2 = song != nullptr
+ ? Convert(song)
+ : nullptr;
+ mpd_song_free(song);
+ if (!mpd_response_finish(connection)) {
+ if (song2 != nullptr)
+ song_free(song2);
+
+ CheckError(connection, error_r);
+ return nullptr;
+ }
+
+ if (song2 == nullptr)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+
+ return song2;
+}
+
+void
+ProxyDatabase::ReturnSong(struct song *song) const
+{
+ assert(song != nullptr);
+ assert(song_in_database(song));
+ assert(song_is_detached(song));
+
+ song_free(song);
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r);
+
+static bool
+Visit(struct mpd_connection *connection,
+ bool recursive, const struct mpd_directory *directory,
+ VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ const char *path = mpd_directory_get_path(directory);
+
+ if (visit_directory) {
+ Directory *d = Directory::NewGeneric(path, &detached_root);
+ bool success = visit_directory(*d, error_r);
+ d->Free();
+ if (!success)
+ return false;
+ }
+
+ if (recursive &&
+ !Visit(connection, path, recursive,
+ visit_directory, visit_song, visit_playlist, error_r))
+ return false;
+
+ return true;
+}
+
+static void
+Copy(struct tag *tag, enum tag_type d_tag,
+ const struct mpd_song *song, enum mpd_tag_type s_tag)
+{
+
+ for (unsigned i = 0;; ++i) {
+ const char *value = mpd_song_get_tag(song, s_tag, i);
+ if (value == NULL)
+ break;
+
+ tag_add_item(tag, d_tag, value);
+ }
+}
+
+static song *
+Convert(const struct mpd_song *song)
+{
+ struct song *s = song_detached_new(mpd_song_get_uri(song));
+
+ s->mtime = mpd_song_get_last_modified(song);
+ s->start_ms = mpd_song_get_start(song) * 1000;
+ s->end_ms = mpd_song_get_end(song) * 1000;
+
+ struct tag *tag = tag_new();
+ tag->time = mpd_song_get_duration(song);
+
+ tag_begin_add(tag);
+ for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
+ Copy(tag, i->d, song, i->s);
+ tag_end_add(tag);
+
+ s->tag = tag;
+
+ return s;
+}
+
+static bool
+Visit(const struct mpd_song *song,
+ VisitSong visit_song, GError **error_r)
+{
+ if (!visit_song)
+ return true;
+
+ struct song *s = Convert(song);
+ bool success = visit_song(*s, error_r);
+ song_free(s);
+
+ return success;
+}
+
+static bool
+Visit(const struct mpd_playlist *playlist,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!visit_playlist)
+ return true;
+
+ PlaylistInfo p(mpd_playlist_get_path(playlist),
+ mpd_playlist_get_last_modified(playlist));
+
+ return visit_playlist(p, detached_root, error_r);
+}
+
+class ProxyEntity {
+ struct mpd_entity *entity;
+
+public:
+ explicit ProxyEntity(struct mpd_entity *_entity)
+ :entity(_entity) {}
+
+ ProxyEntity(const ProxyEntity &other) = delete;
+
+ ProxyEntity(ProxyEntity &&other)
+ :entity(other.entity) {
+ other.entity = nullptr;
+ }
+
+ ~ProxyEntity() {
+ if (entity != nullptr)
+ mpd_entity_free(entity);
+ }
+
+ ProxyEntity &operator=(const ProxyEntity &other) = delete;
+
+ operator const struct mpd_entity *() const {
+ return entity;
+ }
+};
+
+static std::list<ProxyEntity>
+ReceiveEntities(struct mpd_connection *connection)
+{
+ std::list<ProxyEntity> entities;
+ struct mpd_entity *entity;
+ while ((entity = mpd_recv_entity(connection)) != NULL)
+ entities.push_back(ProxyEntity(entity));
+
+ mpd_response_finish(connection);
+ return entities;
+}
+
+static bool
+Visit(struct mpd_connection *connection, const char *uri,
+ bool recursive, VisitDirectory visit_directory, VisitSong visit_song,
+ VisitPlaylist visit_playlist, GError **error_r)
+{
+ if (!mpd_send_list_meta(connection, uri))
+ return CheckError(connection, error_r);
+
+ std::list<ProxyEntity> entities(ReceiveEntities(connection));
+ if (!CheckError(connection, error_r))
+ return false;
+
+ for (const auto &entity : entities) {
+ switch (mpd_entity_get_type(entity)) {
+ case MPD_ENTITY_TYPE_UNKNOWN:
+ break;
+
+ case MPD_ENTITY_TYPE_DIRECTORY:
+ if (!Visit(connection, recursive,
+ mpd_entity_get_directory(entity),
+ visit_directory, visit_song, visit_playlist,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_SONG:
+ if (!Visit(mpd_entity_get_song(entity), visit_song,
+ error_r))
+ return false;
+ break;
+
+ case MPD_ENTITY_TYPE_PLAYLIST:
+ if (!Visit(mpd_entity_get_playlist(entity),
+ visit_playlist, error_r))
+ return false;
+ break;
+ }
+ }
+
+ return CheckError(connection, error_r);
+}
+
+bool
+ProxyDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ // TODO: match
+ // TODO: auto-reconnect
+
+ return ::Visit(connection, selection.uri, selection.recursive,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
+}
+
+bool
+ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
+{
+ enum mpd_tag_type tag_type2 = Convert(tag_type);
+ if (tag_type2 == MPD_TAG_COUNT) {
+ g_set_error_literal(error_r, libmpdclient_quark(), 0,
+ "Unsupported tag");
+ return false;
+ }
+
+ if (!mpd_search_db_tags(connection, tag_type2))
+ return CheckError(connection, error_r);
+
+ // TODO: match
+ (void)selection;
+
+ if (!mpd_search_commit(connection))
+ return CheckError(connection, error_r);
+
+ bool result = true;
+
+ struct mpd_pair *pair;
+ while (result &&
+ (pair = mpd_recv_pair_tag(connection, tag_type2)) != nullptr) {
+ result = visit_string(pair->value, error_r);
+ mpd_return_pair(connection, pair);
+ }
+
+ return mpd_response_finish(connection) &&
+ CheckError(connection, error_r) &&
+ result;
+}
+
+bool
+ProxyDatabase::GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r) const
+{
+ // TODO: match
+ (void)selection;
+
+ struct mpd_stats *stats2 =
+ mpd_run_stats(connection);
+ if (stats2 == nullptr)
+ return CheckError(connection, error_r);
+
+ stats.song_count = mpd_stats_get_number_of_songs(stats2);
+ stats.total_duration = mpd_stats_get_db_play_time(stats2);
+ stats.artist_count = mpd_stats_get_number_of_artists(stats2);
+ stats.album_count = mpd_stats_get_number_of_albums(stats2);
+ mpd_stats_free(stats2);
+
+ return true;
+}
+
+const DatabasePlugin proxy_db_plugin = {
+ "proxy",
+ ProxyDatabase::Create,
+};
diff --git a/src/db/ProxyDatabasePlugin.hxx b/src/db/ProxyDatabasePlugin.hxx
new file mode 100644
index 00000000..8e878bac
--- /dev/null
+++ b/src/db/ProxyDatabasePlugin.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PROXY_DATABASE_PLUGIN_HXX
+#define MPD_PROXY_DATABASE_PLUGIN_HXX
+
+struct DatabasePlugin;
+
+extern const DatabasePlugin proxy_db_plugin;
+
+#endif
diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx
new file mode 100644
index 00000000..2b720c41
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.cxx
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "SimpleDatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "DatabaseHelpers.hxx"
+#include "Directory.hxx"
+#include "SongFilter.hxx"
+#include "DatabaseSave.hxx"
+#include "DatabaseLock.hxx"
+#include "db_error.h"
+#include "TextFile.hxx"
+#include "conf.h"
+#include "fs/FileSystem.hxx"
+
+#include <sys/types.h>
+#include <errno.h>
+
+G_GNUC_CONST
+static inline GQuark
+simple_db_quark(void)
+{
+ return g_quark_from_static_string("simple_db");
+}
+
+Database *
+SimpleDatabase::Create(const struct config_param *param, GError **error_r)
+{
+ SimpleDatabase *db = new SimpleDatabase();
+ if (!db->Configure(param, error_r)) {
+ delete db;
+ db = NULL;
+ }
+
+ return db;
+}
+
+bool
+SimpleDatabase::Configure(const struct config_param *param, GError **error_r)
+{
+ GError *error = NULL;
+
+ char *_path = config_dup_block_path(param, "path", &error);
+ if (_path == NULL) {
+ if (error != NULL)
+ g_propagate_error(error_r, error);
+ else
+ g_set_error(error_r, simple_db_quark(), 0,
+ "No \"path\" parameter specified");
+ return false;
+ }
+
+ path = Path::FromUTF8(_path);
+ path_utf8 = _path;
+
+ free(_path);
+
+ if (path.IsNull()) {
+ g_set_error(error_r, simple_db_quark(), 0,
+ "Failed to convert database path to FS encoding");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+SimpleDatabase::Check(GError **error_r) const
+{
+ assert(!path.IsNull());
+ assert(!path.empty());
+
+ /* Check if the file exists */
+ if (!CheckAccess(path, F_OK)) {
+ /* If the file doesn't exist, we can't check if we can write
+ * it, so we are going to try to get the directory path, and
+ * see if we can write a file in that */
+ const Path dirPath = path.GetDirectoryName();
+
+ /* Check that the parent part of the path is a directory */
+ struct stat st;
+ if (!StatFile(dirPath, st)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat parent directory of db file "
+ "\"%s\": %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ g_set_error(error_r, simple_db_quark(), 0,
+ "Couldn't create db file \"%s\" because the "
+ "parent path is not a directory",
+ path_utf8.c_str());
+ return false;
+ }
+
+ /* Check if we can write to the directory */
+ if (!CheckAccess(dirPath, X_OK | W_OK)) {
+ int error = errno;
+ const std::string dirPath_utf8 = dirPath.ToUTF8();
+ g_set_error(error_r, simple_db_quark(), error,
+ "Can't create db file in \"%s\": %s",
+ dirPath_utf8.c_str(), g_strerror(error));
+ return false;
+ }
+
+ return true;
+ }
+
+ /* Path exists, now check if it's a regular file */
+ struct stat st;
+ if (!StatFile(path, st)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat db file \"%s\": %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ g_set_error(error_r, simple_db_quark(), 0,
+ "db file \"%s\" is not a regular file",
+ path_utf8.c_str());
+ return false;
+ }
+
+ /* And check that we can write to it */
+ if (!CheckAccess(path, R_OK | W_OK)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Can't open db file \"%s\" for reading/writing: %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool
+SimpleDatabase::Load(GError **error_r)
+{
+ assert(!path.empty());
+ assert(root != NULL);
+
+ TextFile file(path);
+ if (file.HasFailed()) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to open database file \"%s\": %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ if (!db_load_internal(file, root, error_r))
+ return false;
+
+ struct stat st;
+ if (StatFile(path, st))
+ mtime = st.st_mtime;
+
+ return true;
+}
+
+bool
+SimpleDatabase::Open(GError **error_r)
+{
+ root = Directory::NewRoot();
+ mtime = 0;
+
+#ifndef NDEBUG
+ borrowed_song_count = 0;
+#endif
+
+ GError *error = NULL;
+ if (!Load(&error)) {
+ root->Free();
+
+ g_warning("Failed to load database: %s", error->message);
+ g_error_free(error);
+
+ if (!Check(error_r))
+ return false;
+
+ root = Directory::NewRoot();
+ }
+
+ return true;
+}
+
+void
+SimpleDatabase::Close()
+{
+ assert(root != NULL);
+ assert(borrowed_song_count == 0);
+
+ root->Free();
+}
+
+struct song *
+SimpleDatabase::GetSong(const char *uri, GError **error_r) const
+{
+ assert(root != NULL);
+
+ db_lock();
+ song *song = root->LookupSong(uri);
+ db_unlock();
+ if (song == NULL)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+#ifndef NDEBUG
+ else
+ ++const_cast<unsigned &>(borrowed_song_count);
+#endif
+
+ return song;
+}
+
+void
+SimpleDatabase::ReturnSong(gcc_unused struct song *song) const
+{
+ assert(song != nullptr);
+
+#ifndef NDEBUG
+ assert(borrowed_song_count > 0);
+ --const_cast<unsigned &>(borrowed_song_count);
+#endif
+}
+
+G_GNUC_PURE
+const Directory *
+SimpleDatabase::LookupDirectory(const char *uri) const
+{
+ assert(root != NULL);
+ assert(uri != NULL);
+
+ ScopeDatabaseLock protect;
+ return root->LookupDirectory(uri);
+}
+
+bool
+SimpleDatabase::Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const
+{
+ ScopeDatabaseLock protect;
+
+ const Directory *directory = root->LookupDirectory(selection.uri);
+ if (directory == NULL) {
+ if (visit_song) {
+ song *song = root->LookupSong(selection.uri);
+ if (song != nullptr)
+ return !selection.Match(*song) ||
+ visit_song(*song, error_r);
+ }
+
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such directory");
+ return false;
+ }
+
+ if (selection.recursive && visit_directory &&
+ !visit_directory(*directory, error_r))
+ return false;
+
+ return directory->Walk(selection.recursive, selection.filter,
+ visit_directory, visit_song, visit_playlist,
+ error_r);
+}
+
+bool
+SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const
+{
+ return ::VisitUniqueTags(*this, selection, tag_type, visit_string,
+ error_r);
+}
+
+bool
+SimpleDatabase::GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats, GError **error_r) const
+{
+ return ::GetStats(*this, selection, stats, error_r);
+}
+
+bool
+SimpleDatabase::Save(GError **error_r)
+{
+ db_lock();
+
+ g_debug("removing empty directories from DB");
+ root->PruneEmpty();
+
+ g_debug("sorting DB");
+ root->Sort();
+
+ db_unlock();
+
+ g_debug("writing DB");
+
+ FILE *fp = FOpen(path, FOpenMode::WriteText);
+ if (!fp) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "unable to write to db file \"%s\": %s",
+ path_utf8.c_str(), g_strerror(errno));
+ return false;
+ }
+
+ db_save_internal(fp, root);
+
+ if (ferror(fp)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to write to database file: %s",
+ g_strerror(errno));
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ struct stat st;
+ if (StatFile(path, st))
+ mtime = st.st_mtime;
+
+ return true;
+}
+
+const DatabasePlugin simple_db_plugin = {
+ "simple",
+ SimpleDatabase::Create,
+};
diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx
new file mode 100644
index 00000000..525e854d
--- /dev/null
+++ b/src/db/SimpleDatabasePlugin.hxx
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SIMPLE_DATABASE_PLUGIN_HXX
+#define MPD_SIMPLE_DATABASE_PLUGIN_HXX
+
+#include "DatabasePlugin.hxx"
+#include "fs/Path.hxx"
+#include "gcc.h"
+
+#include <cassert>
+
+#include <time.h>
+
+struct Directory;
+
+class SimpleDatabase : public Database {
+ Path path;
+ std::string path_utf8;
+
+ Directory *root;
+
+ time_t mtime;
+
+#ifndef NDEBUG
+ unsigned borrowed_song_count;
+#endif
+
+ SimpleDatabase()
+ :path(Path::Null()) {}
+
+public:
+ gcc_pure
+ Directory *GetRoot() {
+ assert(root != NULL);
+
+ return root;
+ }
+
+ bool Save(GError **error_r);
+
+ gcc_pure
+ time_t GetLastModified() const {
+ return mtime;
+ }
+
+ static Database *Create(const struct config_param *param,
+ GError **error_r);
+
+ virtual bool Open(GError **error_r) override;
+ virtual void Close() override;
+
+ virtual struct song *GetSong(const char *uri_utf8,
+ GError **error_r) const override;
+ virtual void ReturnSong(struct song *song) const;
+
+ virtual bool Visit(const DatabaseSelection &selection,
+ VisitDirectory visit_directory,
+ VisitSong visit_song,
+ VisitPlaylist visit_playlist,
+ GError **error_r) const override;
+
+ virtual bool VisitUniqueTags(const DatabaseSelection &selection,
+ enum tag_type tag_type,
+ VisitString visit_string,
+ GError **error_r) const override;
+
+ virtual bool GetStats(const DatabaseSelection &selection,
+ DatabaseStats &stats,
+ GError **error_r) const override;
+
+protected:
+ bool Configure(const struct config_param *param, GError **error_r);
+
+ gcc_pure
+ bool Check(GError **error_r) const;
+
+ bool Load(GError **error_r);
+
+ gcc_pure
+ const Directory *LookupDirectory(const char *uri) const;
+};
+
+extern const DatabasePlugin simple_db_plugin;
+
+#endif
diff --git a/src/db/simple_db_plugin.c b/src/db/simple_db_plugin.c
deleted file mode 100644
index 697e8da5..00000000
--- a/src/db/simple_db_plugin.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "simple_db_plugin.h"
-#include "db_internal.h"
-#include "db_error.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "db_save.h"
-#include "db_lock.h"
-#include "conf.h"
-#include "directory.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-
-struct simple_db {
- struct db base;
-
- char *path;
-
- struct directory *root;
-
- time_t mtime;
-};
-
-G_GNUC_CONST
-static inline GQuark
-simple_db_quark(void)
-{
- return g_quark_from_static_string("simple_db");
-}
-
-G_GNUC_PURE
-static const struct directory *
-simple_db_lookup_directory(const struct simple_db *db, const char *uri)
-{
- assert(db != NULL);
- assert(db->root != NULL);
- assert(uri != NULL);
-
- db_lock();
- struct directory *directory =
- directory_lookup_directory(db->root, uri);
- db_unlock();
- return directory;
-}
-
-static struct db *
-simple_db_init(const struct config_param *param, GError **error_r)
-{
- struct simple_db *db = g_malloc(sizeof(*db));
- db_base_init(&db->base, &simple_db_plugin);
-
- GError *error = NULL;
- db->path = config_dup_block_path(param, "path", &error);
- if (db->path == NULL) {
- g_free(db);
- if (error != NULL)
- g_propagate_error(error_r, error);
- else
- g_set_error(error_r, simple_db_quark(), 0,
- "No \"path\" parameter specified");
- return NULL;
- }
-
- return &db->base;
-}
-
-static void
-simple_db_finish(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- g_free(db->path);
- g_free(db);
-}
-
-static bool
-simple_db_check(struct simple_db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->path != NULL);
-
- /* Check if the file exists */
- if (access(db->path, F_OK)) {
- /* If the file doesn't exist, we can't check if we can write
- * it, so we are going to try to get the directory path, and
- * see if we can write a file in that */
- char *dirPath = g_path_get_dirname(db->path);
-
- /* Check that the parent part of the path is a directory */
- struct stat st;
- if (stat(dirPath, &st) < 0) {
- g_free(dirPath);
- g_set_error(error_r, simple_db_quark(), errno,
- "Couldn't stat parent directory of db file "
- "\"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- g_free(dirPath);
- g_set_error(error_r, simple_db_quark(), 0,
- "Couldn't create db file \"%s\" because the "
- "parent path is not a directory",
- db->path);
- return false;
- }
-
- /* Check if we can write to the directory */
- if (access(dirPath, X_OK | W_OK)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Can't create db file in \"%s\": %s",
- dirPath, g_strerror(errno));
- g_free(dirPath);
- return false;
- }
-
- g_free(dirPath);
-
- return true;
- }
-
- /* Path exists, now check if it's a regular file */
- struct stat st;
- if (stat(db->path, &st) < 0) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Couldn't stat db file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!S_ISREG(st.st_mode)) {
- g_set_error(error_r, simple_db_quark(), 0,
- "db file \"%s\" is not a regular file",
- db->path);
- return false;
- }
-
- /* And check that we can write to it */
- if (access(db->path, R_OK | W_OK)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Can't open db file \"%s\" for reading/writing: %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static bool
-simple_db_load(struct simple_db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->path != NULL);
- assert(db->root != NULL);
-
- FILE *fp = fopen(db->path, "r");
- if (fp == NULL) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Failed to open database file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- if (!db_load_internal(fp, db->root, error_r)) {
- fclose(fp);
- return false;
- }
-
- fclose(fp);
-
- struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
-
- return true;
-}
-
-static bool
-simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- db->root = directory_new_root();
- db->mtime = 0;
-
- GError *error = NULL;
- if (!simple_db_load(db, &error)) {
- directory_free(db->root);
-
- g_warning("Failed to load database: %s", error->message);
- g_error_free(error);
-
- if (!simple_db_check(db, error_r))
- return false;
-
- db->root = directory_new_root();
- }
-
- return true;
-}
-
-static void
-simple_db_close(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db->root != NULL);
-
- directory_free(db->root);
-}
-
-static struct song *
-simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db->root != NULL);
-
- db_lock();
- struct song *song = directory_lookup_song(db->root, uri);
- db_unlock();
- if (song == NULL)
- g_set_error(error_r, db_quark(), DB_NOT_FOUND,
- "No such song: %s", uri);
-
- return song;
-}
-
-static bool
-simple_db_visit(struct db *_db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- const struct simple_db *db = (const struct simple_db *)_db;
- const struct directory *directory =
- simple_db_lookup_directory(db, selection->uri);
- if (directory == NULL) {
- struct song *song;
- if (visitor->song != NULL &&
- (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL)
- return visitor->song(song, ctx, error_r);
-
- g_set_error(error_r, db_quark(), DB_NOT_FOUND,
- "No such directory");
- return false;
- }
-
- if (selection->recursive && visitor->directory != NULL &&
- !visitor->directory(directory, ctx, error_r))
- return false;
-
- db_lock();
- bool ret = directory_walk(directory, selection->recursive,
- visitor, ctx, error_r);
- db_unlock();
- return ret;
-}
-
-const struct db_plugin simple_db_plugin = {
- .name = "simple",
- .init = simple_db_init,
- .finish = simple_db_finish,
- .open = simple_db_open,
- .close = simple_db_close,
- .get_song = simple_db_get_song,
- .visit = simple_db_visit,
-};
-
-struct directory *
-simple_db_get_root(struct db *_db)
-{
- struct simple_db *db = (struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->root;
-}
-
-bool
-simple_db_save(struct db *_db, GError **error_r)
-{
- struct simple_db *db = (struct simple_db *)_db;
- struct directory *music_root = db->root;
-
- db_lock();
-
- g_debug("removing empty directories from DB");
- directory_prune_empty(music_root);
-
- g_debug("sorting DB");
- directory_sort(music_root);
-
- db_unlock();
-
- g_debug("writing DB");
-
- FILE *fp = fopen(db->path, "w");
- if (!fp) {
- g_set_error(error_r, simple_db_quark(), errno,
- "unable to write to db file \"%s\": %s",
- db->path, g_strerror(errno));
- return false;
- }
-
- db_save_internal(fp, music_root);
-
- if (ferror(fp)) {
- g_set_error(error_r, simple_db_quark(), errno,
- "Failed to write to database file: %s",
- g_strerror(errno));
- fclose(fp);
- return false;
- }
-
- fclose(fp);
-
- struct stat st;
- if (stat(db->path, &st) == 0)
- db->mtime = st.st_mtime;
-
- return true;
-}
-
-time_t
-simple_db_get_mtime(const struct db *_db)
-{
- const struct simple_db *db = (const struct simple_db *)_db;
-
- assert(db != NULL);
- assert(db->root != NULL);
-
- return db->mtime;
-}
diff --git a/src/dbUtils.c b/src/dbUtils.c
deleted file mode 100644
index c212d9f9..00000000
--- a/src/dbUtils.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "dbUtils.h"
-#include "locate.h"
-#include "database.h"
-#include "db_visitor.h"
-#include "playlist.h"
-#include "stored_playlist.h"
-
-#include <glib.h>
-
-static bool
-add_to_queue_song(struct song *song, void *ctx, GError **error_r)
-{
- struct player_control *pc = ctx;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, pc, song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor add_to_queue_visitor = {
- .song = add_to_queue_song,
-};
-
-bool
-addAllIn(struct player_control *pc, const char *uri, GError **error_r)
-{
- return db_walk(uri, &add_to_queue_visitor, pc, error_r);
-}
-
-struct add_data {
- const char *path;
-};
-
-static bool
-add_to_spl_song(struct song *song, void *ctx, GError **error_r)
-{
- struct add_data *data = ctx;
-
- if (!spl_append_song(data->path, song, error_r))
- return false;
-
- return true;
-}
-
-static const struct db_visitor add_to_spl_visitor = {
- .song = add_to_spl_song,
-};
-
-bool
-addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
- GError **error_r)
-{
- struct add_data data = {
- .path = path_utf8,
- };
-
- return db_walk(uri_utf8, &add_to_spl_visitor, &data, error_r);
-}
-
-struct find_add_data {
- struct player_control *pc;
- const struct locate_item_list *criteria;
-};
-
-static bool
-find_add_song(struct song *song, void *ctx, GError **error_r)
-{
- struct find_add_data *data = ctx;
-
- if (!locate_song_match(song, data->criteria))
- return true;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, data->pc,
- song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor find_add_visitor = {
- .song = find_add_song,
-};
-
-bool
-findAddIn(struct player_control *pc, const char *name,
- const struct locate_item_list *criteria, GError **error_r)
-{
- struct find_add_data data;
- data.pc = pc;
- data.criteria = criteria;
-
- return db_walk(name, &find_add_visitor, &data, error_r);
-}
-
-static bool
-searchadd_visitor_song(struct song *song, void *_data, GError **error_r)
-{
- struct find_add_data *data = _data;
-
- if (!locate_song_search(song, data->criteria))
- return true;
-
- enum playlist_result result =
- playlist_append_song(&g_playlist, data->pc, song, NULL);
- if (result != PLAYLIST_RESULT_SUCCESS) {
- g_set_error(error_r, playlist_quark(), result,
- "Playlist error");
- return false;
- }
-
- return true;
-}
-
-static const struct db_visitor searchadd_visitor = {
- .song = searchadd_visitor_song,
-};
-
-bool
-search_add_songs(struct player_control *pc, const char *uri,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list =
- locate_item_list_casefold(criteria);
- struct find_add_data data = {
- .pc = pc,
- .criteria = new_list,
- };
-
- bool success = db_walk(uri, &searchadd_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
-
-struct search_add_playlist_data {
- const char *playlist;
- const struct locate_item_list *criteria;
-};
-
-static bool
-searchaddpl_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_add_playlist_data *data = _data;
-
- if (!locate_song_search(song, data->criteria))
- return true;
-
- if (!spl_append_song(data->playlist, song, error_r))
- return false;
-
- return true;
-}
-
-static const struct db_visitor searchaddpl_visitor = {
- .song = searchaddpl_visitor_song,
-};
-
-bool
-search_add_to_playlist(const char *uri, const char *path_utf8,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list
- = locate_item_list_casefold(criteria);
- struct search_add_playlist_data data = {
- .playlist = path_utf8,
- .criteria = new_list,
- };
-
- bool success = db_walk(uri, &searchaddpl_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
diff --git a/src/dbUtils.h b/src/dbUtils.h
deleted file mode 100644
index 706c807f..00000000
--- a/src/dbUtils.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_DB_UTILS_H
-#define MPD_DB_UTILS_H
-
-#include "gcc.h"
-
-#include <glib.h>
-#include <stdbool.h>
-
-struct locate_item_list;
-struct player_control;
-
-gcc_nonnull(1,2)
-bool
-addAllIn(struct player_control *pc, const char *uri, GError **error_r);
-
-gcc_nonnull(1,2)
-bool
-addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
- GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-findAddIn(struct player_control *pc, const char *name,
- const struct locate_item_list *criteria, GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-search_add_songs(struct player_control *pc, const char *uri,
- const struct locate_item_list *criteria, GError **error_r);
-
-gcc_nonnull(1,2,3)
-bool
-search_add_to_playlist(const char *uri, const char *path_utf8,
- const struct locate_item_list *criteria,
- GError **error_r);
-
-#endif
diff --git a/src/db_plugin.h b/src/db_plugin.h
deleted file mode 100644
index 1c7e14ed..00000000
--- a/src/db_plugin.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/** \file
- *
- * This header declares the db_plugin class. It describes a
- * plugin API for databases of song metadata.
- */
-
-#ifndef MPD_DB_PLUGIN_H
-#define MPD_DB_PLUGIN_H
-
-#include <glib.h>
-#include <assert.h>
-#include <stdbool.h>
-
-struct config_param;
-struct db_selection;
-struct db_visitor;
-
-struct db {
- const struct db_plugin *plugin;
-};
-
-struct db_plugin {
- const char *name;
-
- /**
- * Allocates and configures a database.
- */
- struct db *(*init)(const struct config_param *param, GError **error_r);
-
- /**
- * Free instance data.
- */
- void (*finish)(struct db *db);
-
- /**
- * Open the database. Read it into memory if applicable.
- */
- bool (*open)(struct db *db, GError **error_r);
-
- /**
- * Close the database, free allocated memory.
- */
- void (*close)(struct db *db);
-
- /**
- * Look up a song (including tag data) in the database.
- *
- * @param the URI of the song within the music directory
- * (UTF-8)
- */
- struct song *(*get_song)(struct db *db, const char *uri,
- GError **error_r);
-
- /**
- * Visit the selected entities.
- */
- bool (*visit)(struct db *db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-};
-
-G_GNUC_MALLOC
-static inline struct db *
-db_plugin_new(const struct db_plugin *plugin, const struct config_param *param,
- GError **error_r)
-{
- assert(plugin != NULL);
- assert(plugin->init != NULL);
- assert(plugin->finish != NULL);
- assert(plugin->get_song != NULL);
- assert(plugin->visit != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- struct db *db = plugin->init(param, error_r);
- assert(db == NULL || db->plugin == plugin);
- assert(db != NULL || error_r == NULL || *error_r != NULL);
-
- return db;
-}
-
-static inline void
-db_plugin_free(struct db *db)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(db->plugin->finish != NULL);
-
- db->plugin->finish(db);
-}
-
-static inline bool
-db_plugin_open(struct db *db, GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
-
- return db->plugin->open != NULL
- ? db->plugin->open(db, error_r)
- : true;
-}
-
-static inline void
-db_plugin_close(struct db *db)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
-
- if (db->plugin->close != NULL)
- db->plugin->close(db);
-}
-
-static inline struct song *
-db_plugin_get_song(struct db *db, const char *uri, GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(db->plugin->get_song != NULL);
- assert(uri != NULL);
-
- return db->plugin->get_song(db, uri, error_r);
-}
-
-static inline bool
-db_plugin_visit(struct db *db, const struct db_selection *selection,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- assert(db != NULL);
- assert(db->plugin != NULL);
- assert(selection != NULL);
- assert(visitor != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- return db->plugin->visit(db, selection, visitor, ctx, error_r);
-}
-
-#endif
diff --git a/src/db_print.c b/src/db_print.c
deleted file mode 100644
index 4d7e3f5e..00000000
--- a/src/db_print.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "db_print.h"
-#include "db_selection.h"
-#include "db_visitor.h"
-#include "locate.h"
-#include "directory.h"
-#include "database.h"
-#include "client.h"
-#include "song.h"
-#include "song_print.h"
-#include "playlist_vector.h"
-#include "tag.h"
-#include "strset.h"
-
-#include <glib.h>
-
-typedef struct _ListCommandItem {
- int8_t tagType;
- const struct locate_item_list *criteria;
-} ListCommandItem;
-
-typedef struct _SearchStats {
- const struct locate_item_list *criteria;
- int numberOfSongs;
- unsigned long playTime;
-} SearchStats;
-
-static bool
-print_visitor_directory(const struct directory *directory, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = data;
-
- if (!directory_is_root(directory))
- client_printf(client, "directory: %s\n", directory_get_path(directory));
-
- return true;
-}
-
-static void
-print_playlist_in_directory(struct client *client,
- const struct directory *directory,
- const char *name_utf8)
-{
- if (directory_is_root(directory))
- client_printf(client, "playlist: %s\n", name_utf8);
- else
- client_printf(client, "playlist: %s/%s\n",
- directory_get_path(directory), name_utf8);
-}
-
-static bool
-print_visitor_song(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- assert(song != NULL);
- assert(song->parent != NULL);
-
- struct client *client = data;
- song_print_uri(client, song);
-
- if (song->tag != NULL && song->tag->has_playlist)
- /* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, song->parent,
- song->uri);
-
- return true;
-}
-
-static bool
-print_visitor_song_info(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- assert(song != NULL);
- assert(song->parent != NULL);
-
- struct client *client = data;
- song_print_info(client, song);
-
- if (song->tag != NULL && song->tag->has_playlist)
- /* this song file has an embedded CUE sheet */
- print_playlist_in_directory(client, song->parent,
- song->uri);
-
- return true;
-}
-
-static bool
-print_visitor_playlist(const struct playlist_metadata *playlist,
- const struct directory *directory, void *ctx,
- G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = ctx;
- print_playlist_in_directory(client, directory, playlist->name);
- return true;
-}
-
-static bool
-print_visitor_playlist_info(const struct playlist_metadata *playlist,
- const struct directory *directory,
- void *ctx, G_GNUC_UNUSED GError **error_r)
-{
- struct client *client = ctx;
- print_playlist_in_directory(client, directory, playlist->name);
-
-#ifndef G_OS_WIN32
- struct tm tm;
-#endif
- char timestamp[32];
- time_t t = playlist->mtime;
- strftime(timestamp, sizeof(timestamp),
-#ifdef G_OS_WIN32
- "%Y-%m-%dT%H:%M:%SZ",
- gmtime(&t)
-#else
- "%FT%TZ",
- gmtime_r(&t, &tm)
-#endif
- );
- client_printf(client, "Last-Modified: %s\n", timestamp);
-
- return true;
-}
-
-static const struct db_visitor print_visitor = {
- .directory = print_visitor_directory,
- .song = print_visitor_song,
- .playlist = print_visitor_playlist,
-};
-
-static const struct db_visitor print_info_visitor = {
- .directory = print_visitor_directory,
- .song = print_visitor_song_info,
- .playlist = print_visitor_playlist_info,
-};
-
-bool
-db_selection_print(struct client *client, const struct db_selection *selection,
- bool full, GError **error_r)
-{
- return db_visit(selection, full ? &print_info_visitor : &print_visitor,
- client, error_r);
-}
-
-struct search_data {
- struct client *client;
- const struct locate_item_list *criteria;
-};
-
-static bool
-search_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_data *data = _data;
-
- if (locate_song_search(song, data->criteria))
- song_print_info(data->client, song);
-
- return true;
-}
-
-static const struct db_visitor search_visitor = {
- .song = search_visitor_song,
-};
-
-bool
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct locate_item_list *new_list
- = locate_item_list_casefold(criteria);
- struct search_data data;
-
- data.client = client;
- data.criteria = new_list;
-
- bool success = db_walk(name, &search_visitor, &data, error_r);
-
- locate_item_list_free(new_list);
-
- return success;
-}
-
-static bool
-find_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct search_data *data = _data;
-
- if (locate_song_match(song, data->criteria))
- song_print_info(data->client, song);
-
- return true;
-}
-
-static const struct db_visitor find_visitor = {
- .song = find_visitor_song,
-};
-
-bool
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- struct search_data data;
-
- data.client = client;
- data.criteria = criteria;
-
- return db_walk(name, &find_visitor, &data, error_r);
-}
-
-static void printSearchStats(struct client *client, SearchStats *stats)
-{
- client_printf(client, "songs: %i\n", stats->numberOfSongs);
- client_printf(client, "playtime: %li\n", stats->playTime);
-}
-
-static bool
-stats_visitor_song(struct song *song, void *data,
- G_GNUC_UNUSED GError **error_r)
-{
- SearchStats *stats = data;
-
- if (locate_song_match(song, stats->criteria)) {
- stats->numberOfSongs++;
- stats->playTime += song_get_duration(song);
- }
-
- return true;
-}
-
-static const struct db_visitor stats_visitor = {
- .song = stats_visitor_song,
-};
-
-bool
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- SearchStats stats;
-
- stats.criteria = criteria;
- stats.numberOfSongs = 0;
- stats.playTime = 0;
-
- if (!db_walk(name, &stats_visitor, &stats, error_r))
- return false;
-
- printSearchStats(client, &stats);
- return true;
-}
-
-bool
-printAllIn(struct client *client, const char *uri_utf8, GError **error_r)
-{
- struct db_selection selection;
- db_selection_init(&selection, uri_utf8, true);
- return db_selection_print(client, &selection, false, error_r);
-}
-
-bool
-printInfoForAllIn(struct client *client, const char *uri_utf8,
- GError **error_r)
-{
- struct db_selection selection;
- db_selection_init(&selection, uri_utf8, true);
- return db_selection_print(client, &selection, true, error_r);
-}
-
-static ListCommandItem *
-newListCommandItem(int tagType, const struct locate_item_list *criteria)
-{
- ListCommandItem *item = g_new(ListCommandItem, 1);
-
- item->tagType = tagType;
- item->criteria = criteria;
-
- return item;
-}
-
-static void freeListCommandItem(ListCommandItem * item)
-{
- g_free(item);
-}
-
-static void
-visitTag(struct client *client, struct strset *set,
- struct song *song, enum tag_type tagType)
-{
- struct tag *tag = song->tag;
- bool found = false;
-
- if (tagType == LOCATE_TAG_FILE_TYPE) {
- song_print_uri(client, song);
- return;
- }
-
- if (!tag)
- return;
-
- for (unsigned i = 0; i < tag->num_items; i++) {
- if (tag->items[i]->type == tagType) {
- strset_add(set, tag->items[i]->value);
- found = true;
- }
- }
-
- if (!found)
- strset_add(set, "");
-}
-
-struct list_tags_data {
- struct client *client;
- ListCommandItem *item;
- struct strset *set;
-};
-
-static bool
-unique_tags_visitor_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct list_tags_data *data = _data;
- ListCommandItem *item = data->item;
-
- if (locate_song_match(song, item->criteria))
- visitTag(data->client, data->set, song, item->tagType);
-
- return true;
-}
-
-static const struct db_visitor unique_tags_visitor = {
- .song = unique_tags_visitor_song,
-};
-
-bool
-listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria,
- GError **error_r)
-{
- ListCommandItem *item = newListCommandItem(type, criteria);
- struct list_tags_data data = {
- .client = client,
- .item = item,
- };
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- data.set = strset_new();
- }
-
- if (!db_walk("", &unique_tags_visitor, &data, error_r)) {
- freeListCommandItem(item);
- return false;
- }
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- const char *value;
-
- strset_rewind(data.set);
-
- while ((value = strset_next(data.set)) != NULL)
- client_printf(client, "%s: %s\n",
- tag_item_names[type],
- value);
-
- strset_free(data.set);
- }
-
- freeListCommandItem(item);
-
- return true;
-}
diff --git a/src/db_visitor.h b/src/db_visitor.h
deleted file mode 100644
index 6b90c186..00000000
--- a/src/db_visitor.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_DB_VISITOR_H
-#define MPD_DB_VISITOR_H
-
-struct directory;
-struct song;
-struct playlist_metadata;
-
-struct db_visitor {
- /**
- * Visit a directory. Optional method.
- *
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*directory)(const struct directory *directory, void *ctx,
- GError **error_r);
-
- /**
- * Visit a song. Optional method.
- *
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*song)(struct song *song, void *ctx, GError **error_r);
-
- /**
- * Visit a playlist. Optional method.
- *
- * @param directory the directory the playlist resides in
- * @return true to continue the operation, false on error (set error_r)
- */
- bool (*playlist)(const struct playlist_metadata *playlist,
- const struct directory *directory, void *ctx,
- GError **error_r);
-};
-
-#endif
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
new file mode 100644
index 00000000..6d08fab5
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "AdPlugDecoderPlugin.h"
+#include "tag_handler.h"
+#include "decoder_api.h"
+
+extern "C" {
+#include "audio_check.h"
+}
+
+#include <adplug/adplug.h>
+#include <adplug/emuopl.h>
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "adplug"
+
+static unsigned sample_rate;
+
+static bool
+adplug_init(const struct config_param *param)
+{
+ GError *error = NULL;
+
+ sample_rate = config_get_block_unsigned(param, "sample_rate", 48000);
+ if (!audio_check_sample_rate(sample_rate, &error)) {
+ g_warning("%s\n", error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+adplug_file_decode(struct decoder *decoder, const char *path_fs)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return;
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
+ assert(audio_format_valid(&audio_format));
+
+ decoder_initialized(decoder, &audio_format, false,
+ player->songlength() / 1000.);
+
+ int16_t buffer[2048];
+ const unsigned frames_per_buffer = G_N_ELEMENTS(buffer) / 2;
+ enum decoder_command cmd;
+
+ do {
+ if (!player->update())
+ break;
+
+ opl.update(buffer, frames_per_buffer);
+ cmd = decoder_data(decoder, NULL,
+ buffer, sizeof(buffer),
+ 0);
+ } while (cmd == DECODE_COMMAND_NONE);
+
+ delete player;
+}
+
+static void
+adplug_scan_tag(enum tag_type type, const std::string &value,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ if (!value.empty())
+ tag_handler_invoke_tag(handler, handler_ctx,
+ type, value.c_str());
+}
+
+static bool
+adplug_scan_file(const char *path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return false;
+
+ tag_handler_invoke_duration(handler, handler_ctx,
+ player->songlength() / 1000);
+
+ if (handler->tag != nullptr) {
+ adplug_scan_tag(TAG_TITLE, player->gettitle(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_ARTIST, player->getauthor(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_COMMENT, player->getdesc(),
+ handler, handler_ctx);
+ }
+
+ delete player;
+ return true;
+}
+
+static const char *const adplug_suffixes[] = {
+ "amd",
+ "d00",
+ "hsc",
+ "laa",
+ "rad",
+ "raw",
+ "sa2",
+ nullptr
+};
+
+const struct decoder_plugin adplug_decoder_plugin = {
+ "adplug",
+ adplug_init,
+ nullptr,
+ nullptr,
+ adplug_file_decode,
+ adplug_scan_file,
+ nullptr,
+ nullptr,
+ adplug_suffixes,
+ nullptr,
+};
diff --git a/src/decoder/AdPlugDecoderPlugin.h b/src/decoder/AdPlugDecoderPlugin.h
new file mode 100644
index 00000000..9fdf438a
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_ADPLUG_H
+#define MPD_DECODER_ADPLUG_H
+
+extern const struct decoder_plugin adplug_decoder_plugin;
+
+#endif
diff --git a/src/decoder/_flac_common.c b/src/decoder/FLACCommon.cxx
index d7f0c4a8..25fd1f96 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/FLACCommon.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,40 +22,35 @@
*/
#include "config.h"
-#include "_flac_common.h"
-#include "flac_metadata.h"
-#include "flac_pcm.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "FLAC_PCM.hxx"
+
+extern "C" {
#include "audio_check.h"
+}
#include <glib.h>
#include <assert.h>
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream)
+flac_data::flac_data(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :FLACInput(_input_stream, _decoder),
+ initialized(false), unsupported(false),
+ total_frames(0), first_frame(0), next_frame(0), position(0),
+ decoder(_decoder), input_stream(_input_stream),
+ tag(nullptr)
{
- pcm_buffer_init(&data->buffer);
-
- data->unsupported = false;
- data->initialized = false;
- data->total_frames = 0;
- data->first_frame = 0;
- data->next_frame = 0;
-
- data->position = 0;
- data->decoder = decoder;
- data->input_stream = input_stream;
- data->tag = NULL;
+ pcm_buffer_init(&buffer);
}
-void
-flac_data_deinit(struct flac_data *data)
+flac_data::~flac_data()
{
- pcm_buffer_deinit(&data->buffer);
+ pcm_buffer_deinit(&buffer);
- if (data->tag != NULL)
- tag_free(data->tag);
+ if (tag != nullptr)
+ tag_free(tag);
}
static enum sample_format
@@ -86,7 +81,7 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
@@ -114,7 +109,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
@@ -123,14 +117,14 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
- if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL,
+ if (data->tag != nullptr)
+ flac_vorbis_comments_to_tag(data->tag,
&block->data.vorbis_comment);
default:
@@ -138,15 +132,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
}
}
-void flac_error_common_cb(const FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data)
-{
- if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
- return;
-
- g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
-}
-
/**
* This function attempts to call decoder_initialized() in case there
* was no STREAMINFO block. This is allowed for nonseekable streams,
@@ -160,7 +145,7 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
@@ -199,7 +184,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
buffer = pcm_buffer_get(&data->buffer, buffer_size);
flac_convert(buffer, frame->header.channels,
- data->audio_format.format, buf,
+ (enum sample_format)data->audio_format.format, buf,
0, frame->header.blocksize);
if (nbytes > 0)
diff --git a/src/decoder/_flac_common.h b/src/decoder/FLACCommon.hxx
index 0d90ba65..b80372bb 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/FLACCommon.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,15 @@
* Common data structures and functions used by FLAC and OggFLAC
*/
-#ifndef MPD_FLAC_COMMON_H
-#define MPD_FLAC_COMMON_H
+#ifndef MPD_FLAC_COMMON_HXX
+#define MPD_FLAC_COMMON_HXX
+#include "FLACInput.hxx"
#include "decoder_api.h"
-#include "pcm_buffer.h"
-#include <glib.h>
+extern "C" {
+#include "pcm_buffer.h"
+}
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
@@ -35,7 +37,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "flac"
-struct flac_data {
+struct flac_data : public FLACInput {
struct pcm_buffer buffer;
/**
@@ -78,25 +80,19 @@ struct flac_data {
FLAC__uint64 next_frame;
FLAC__uint64 position;
+
struct decoder *decoder;
struct input_stream *input_stream;
- struct tag *tag;
-};
-/* initializes a given FlacData struct */
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream);
+ struct tag *tag;
-void
-flac_data_deinit(struct flac_data *data);
+ flac_data(struct decoder *decoder, struct input_stream *input_stream);
+ ~flac_data();
+};
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
-void flac_error_common_cb(FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data);
-
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[],
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/FLACDecoderPlugin.cxx
index fb0b3502..43b60409 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/FLACDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,10 @@
*/
#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "flac_compat.h"
-#include "flac_metadata.h"
-
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+#include "FLACDecoderPlugin.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "OggCodec.hxx"
#include <glib.h>
@@ -34,114 +31,10 @@
#include <sys/stat.h>
#include <sys/types.h>
-/* this code was based on flac123, from flac-tools */
-
-static FLAC__StreamDecoderReadStatus
-flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__byte buf[], flac_read_status_size_t *bytes,
- void *fdata)
-{
- struct flac_data *data = fdata;
- size_t r;
-
- r = decoder_read(data->decoder, data->input_stream,
- (void *)buf, *bytes);
- *bytes = r;
-
- if (r == 0) {
- if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
- input_stream_lock_eof(data->input_stream))
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- else
- return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
- }
-
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderSeekStatus
-flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
-
- if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET,
- NULL))
- return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
-
- return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-static FLAC__StreamDecoderTellStatus
-flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
-
- *offset = (long)(data->input_stream->offset);
-
- return FLAC__STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static FLAC__StreamDecoderLengthStatus
-flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * length, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (data->input_stream->size < 0)
- return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
-
- *length = (size_t) (data->input_stream->size);
-
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static FLAC__bool
-flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
- decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
- input_stream_lock_eof(data->input_stream);
-}
-
-static void
-flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__StreamDecoderErrorStatus status, void *fdata)
-{
- flac_error_common_cb(status, (struct flac_data *) fdata);
-}
-
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
-{
- switch (state) {
- case FLAC__SEEKABLE_STREAM_DECODER_OK:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
- case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
- return;
-
- case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
- case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
- case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
- break;
- }
+#error libFLAC is too old
+#endif
- g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]);
-}
-#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
switch (state) {
@@ -162,7 +55,6 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
g_warning("%s\n", FLAC__StreamDecoderStateString[state]);
}
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
@@ -195,7 +87,30 @@ static bool
flac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- return flac_scan_file2(file, NULL, handler, handler_ctx);
+ FLACMetadataChain chain;
+ if (!chain.Read(file)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
+
+static bool
+flac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.Read(is)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
}
/**
@@ -205,15 +120,13 @@ static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
+ if (sd == nullptr) {
g_warning("FLAC__stream_decoder_new() failed");
- return NULL;
+ return nullptr;
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT))
g_debug("FLAC__stream_decoder_set_metadata_respond() has failed");
-#endif
return sd;
}
@@ -259,7 +172,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
data->first_frame = t_start;
while (true) {
- if (data->tag != NULL && !tag_is_empty(data->tag)) {
+ if (data->tag != nullptr && !tag_is_empty(data->tag)) {
cmd = decoder_tag(data->decoder, data->input_stream,
data->tag);
tag_free(data->tag);
@@ -300,34 +213,30 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
static FLAC__StreamDecoderInitStatus
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return FLAC__stream_decoder_init_ogg_stream(flac_dec,
- flac_read_cb,
- flac_seek_cb,
- flac_tell_cb,
- flac_length_cb,
- flac_eof_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
-#else
- (void)flac_dec;
- (void)data;
-
- return FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-#endif
}
static FLAC__StreamDecoderInitStatus
stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
return FLAC__stream_decoder_init_stream(flac_dec,
- flac_read_cb, flac_seek_cb,
- flac_tell_cb, flac_length_cb,
- flac_eof_cb, flac_write_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
+ flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
}
@@ -345,28 +254,23 @@ flac_decode_internal(struct decoder * decoder,
bool is_ogg)
{
FLAC__StreamDecoder *flac_dec;
- struct flac_data data;
flac_dec = flac_decoder_new();
- if (flac_dec == NULL)
+ if (flac_dec == nullptr)
return;
- flac_data_init(&data, decoder, input_stream);
+ struct flac_data data(decoder, input_stream);
data.tag = tag_new();
FLAC__StreamDecoderInitStatus status =
stream_init(flac_dec, &data, is_ogg);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
g_warning("%s", FLAC__StreamDecoderInitStatusString[status]);
-#endif
return;
}
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
- flac_data_deinit(&data);
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
return;
@@ -374,8 +278,6 @@ flac_decode_internal(struct decoder * decoder,
flac_decoder_loop(&data, flac_dec, 0, 0);
- flac_data_deinit(&data);
-
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
}
@@ -386,101 +288,96 @@ flac_decode(struct decoder * decoder, struct input_stream *input_stream)
flac_decode_internal(decoder, input_stream, false);
}
-#ifndef HAVE_OGGFLAC
-
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
-#else
- /* disable oggflac when libflac is too old */
- return false;
-#endif
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-
static bool
oggflac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_Iterator *it;
- FLAC__StreamMetadata *block;
- FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
-
- if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
- FLAC__metadata_chain_delete(chain);
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(file)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
return false;
}
- it = FLAC__metadata_iterator_new();
- FLAC__metadata_iterator_init(it, chain);
-
- do {
- if (!(block = FLAC__metadata_iterator_get_block(it)))
- break;
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
- flac_scan_metadata(NULL, block,
- handler, handler_ctx);
- } while (FLAC__metadata_iterator_next(it));
- FLAC__metadata_iterator_delete(it);
+static bool
+oggflac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(is)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
- FLAC__metadata_chain_delete(chain);
+ chain.Scan(handler, handler_ctx);
return true;
}
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
- if (ogg_stream_type_detect(input_stream) != FLAC)
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
- input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
flac_decode_internal(decoder, input_stream, true);
}
-static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
+static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
- NULL
+ nullptr
};
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
const struct decoder_plugin oggflac_decoder_plugin = {
- .name = "oggflac",
- .init = oggflac_init,
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
- .stream_decode = oggflac_decode,
- .scan_file = oggflac_scan_file,
- .suffixes = oggflac_suffixes,
- .mime_types = oggflac_mime_types
-#endif
+ "oggflac",
+ oggflac_init,
+ nullptr,
+ oggflac_decode,
+ nullptr,
+ oggflac_scan_file,
+ oggflac_scan_stream,
+ nullptr,
+ oggflac_suffixes,
+ oggflac_mime_types,
};
-#endif /* HAVE_OGGFLAC */
-
-static const char *const flac_suffixes[] = { "flac", NULL };
+static const char *const flac_suffixes[] = { "flac", nullptr };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
- NULL
+ nullptr
};
const struct decoder_plugin flac_decoder_plugin = {
- .name = "flac",
- .stream_decode = flac_decode,
- .scan_file = flac_scan_file,
- .suffixes = flac_suffixes,
- .mime_types = flac_mime_types,
+ "flac",
+ nullptr,
+ nullptr,
+ flac_decode,
+ nullptr,
+ flac_scan_file,
+ flac_scan_stream,
+ nullptr,
+ flac_suffixes,
+ flac_mime_types,
};
diff --git a/src/decoder/FLACDecoderPlugin.h b/src/decoder/FLACDecoderPlugin.h
new file mode 100644
index 00000000..c99deeef
--- /dev/null
+++ b/src/decoder/FLACDecoderPlugin.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_FLAC_H
+#define MPD_DECODER_FLAC_H
+
+extern const struct decoder_plugin flac_decoder_plugin;
+extern const struct decoder_plugin oggflac_decoder_plugin;
+
+#endif
diff --git a/src/decoder/FLACIOHandle.cxx b/src/decoder/FLACIOHandle.cxx
new file mode 100644
index 00000000..08ec36e4
--- /dev/null
+++ b/src/decoder/FLACIOHandle.cxx
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FLACIOHandle.hxx"
+#include "io_error.h"
+#include "gcc.h"
+
+#include <errno.h>
+
+static size_t
+FLACIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ uint8_t *const p0 = (uint8_t *)ptr, *p = p0,
+ *const end = p0 + size * nmemb;
+
+ /* libFLAC is very picky about short reads, and expects the IO
+ callback to fill the whole buffer (undocumented!) */
+
+ GError *error = nullptr;
+ while (p < end) {
+ size_t nbytes = input_stream_lock_read(is, p, end - p, &error);
+ if (nbytes == 0) {
+ if (error == nullptr)
+ /* end of file */
+ break;
+
+ if (error->domain == errno_quark())
+ errno = error->code;
+ else
+ /* just some random non-zero
+ errno value */
+ errno = EINVAL;
+ g_error_free(error);
+ return 0;
+ }
+
+ p += nbytes;
+ }
+
+ /* libFLAC expects a clean errno after returning from the IO
+ callbacks (undocumented!) */
+ errno = 0;
+ return (p - p0) / size;
+}
+
+static int
+FLACIOSeek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_seek(is, offset, whence, nullptr) ? 0 : -1;
+}
+
+static FLAC__int64
+FLACIOTell(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return is->offset;
+}
+
+static int
+FLACIOEof(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_eof(is);
+}
+
+static int
+FLACIOClose(gcc_unused FLAC__IOHandle handle)
+{
+ /* no-op because the libFLAC caller is repsonsible for closing
+ the #input_stream */
+
+ return 0;
+}
+
+const FLAC__IOCallbacks flac_io_callbacks = {
+ FLACIORead,
+ nullptr,
+ nullptr,
+ nullptr,
+ FLACIOEof,
+ FLACIOClose,
+};
+
+const FLAC__IOCallbacks flac_io_callbacks_seekable = {
+ FLACIORead,
+ nullptr,
+ FLACIOSeek,
+ FLACIOTell,
+ FLACIOEof,
+ FLACIOClose,
+};
diff --git a/src/decoder/FLACIOHandle.hxx b/src/decoder/FLACIOHandle.hxx
new file mode 100644
index 00000000..505d2db1
--- /dev/null
+++ b/src/decoder/FLACIOHandle.hxx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FLAC_IO_HANDLE_HXX
+#define MPD_FLAC_IO_HANDLE_HXX
+
+#include "gcc.h"
+#include "InputStream.hxx"
+
+#include <FLAC/callback.h>
+
+extern const FLAC__IOCallbacks flac_io_callbacks;
+extern const FLAC__IOCallbacks flac_io_callbacks_seekable;
+
+static inline FLAC__IOHandle
+ToFLACIOHandle(input_stream *is)
+{
+ return (FLAC__IOHandle)is;
+}
+
+static inline const FLAC__IOCallbacks &
+GetFLACIOCallbacks(const input_stream *is)
+{
+ return is->seekable
+ ? flac_io_callbacks_seekable
+ : flac_io_callbacks;
+}
+
+#endif
diff --git a/src/decoder/FLACInput.cxx b/src/decoder/FLACInput.cxx
new file mode 100644
index 00000000..ba0a86ce
--- /dev/null
+++ b/src/decoder/FLACInput.cxx
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FLACInput.hxx"
+#include "decoder_api.h"
+#include "gcc.h"
+#include "InputStream.hxx"
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(FLAC__byte buffer[], size_t *bytes)
+{
+ size_t r = decoder_read(decoder, input_stream, (void *)buffer, *bytes);
+ *bytes = r;
+
+ if (r == 0) {
+ if (input_stream_lock_eof(input_stream) ||
+ (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE))
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(FLAC__uint64 absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+
+ if (!input_stream_lock_seek(input_stream,
+ absolute_byte_offset, SEEK_SET,
+ nullptr))
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(FLAC__uint64 *absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+
+ *absolute_byte_offset = (FLAC__uint64)input_stream->offset;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(FLAC__uint64 *stream_length)
+{
+ if (input_stream->size < 0)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+
+ *stream_length = (FLAC__uint64)input_stream->size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+FLAC__bool
+FLACInput::Eof()
+{
+ return (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE &&
+ decoder_get_command(decoder) != DECODE_COMMAND_SEEK) ||
+ input_stream_lock_eof(input_stream);
+}
+
+void
+FLACInput::Error(FLAC__StreamDecoderErrorStatus status)
+{
+ if (decoder == nullptr ||
+ decoder_get_command(decoder) != DECODE_COMMAND_STOP)
+ g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
+}
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Read(buffer, bytes);
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Seek(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Tell(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Length(stream_length);
+}
+
+FLAC__bool
+FLACInput::Eof(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Eof();
+}
+
+void
+FLACInput::Error(gcc_unused const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ i->Error(status);
+}
+
diff --git a/src/decoder/FLACInput.hxx b/src/decoder/FLACInput.hxx
new file mode 100644
index 00000000..7661567d
--- /dev/null
+++ b/src/decoder/FLACInput.hxx
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FLAC_INPUT_HXX
+#define MPD_FLAC_INPUT_HXX
+
+#include <FLAC/stream_decoder.h>
+
+/**
+ * This class wraps an #input_stream in libFLAC stream decoder
+ * callbacks.
+ */
+class FLACInput {
+ struct decoder *decoder;
+
+ struct input_stream *input_stream;
+
+public:
+ FLACInput(struct input_stream *_input_stream,
+ struct decoder *_decoder=nullptr)
+ :decoder(_decoder), input_stream(_input_stream) {}
+
+protected:
+ FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset);
+ FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset);
+ FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length);
+ FLAC__bool Eof();
+ void Error(FLAC__StreamDecoderErrorStatus status);
+
+public:
+ static FLAC__StreamDecoderReadStatus
+ Read(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+ static FLAC__StreamDecoderSeekStatus
+ Seek(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderTellStatus
+ Tell(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderLengthStatus
+ Length(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data);
+
+ static FLAC__bool
+ Eof(const FLAC__StreamDecoder *flac_decoder, void *client_data);
+
+ static void
+ Error(const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data);
+};
+
+#endif
diff --git a/src/decoder/flac_metadata.c b/src/decoder/FLACMetaData.cxx
index bd1eaf32..8273a230 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/FLACMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,20 @@
*/
#include "config.h"
-#include "flac_metadata.h"
-#include "replay_gain_info.h"
+#include "FLACMetaData.hxx"
+
+extern "C" {
+#include "XiphTags.h"
+}
+
#include "tag.h"
#include "tag_handler.h"
#include "tag_table.h"
+#include "replay_gain_info.h"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
#include <stdlib.h>
static bool
@@ -91,7 +95,7 @@ flac_find_string_comment(const FLAC__StreamMetadata *block,
int len;
const unsigned char *p;
- *str = NULL;
+ *str = nullptr;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
@@ -128,36 +132,21 @@ flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
- const char *name, const char *char_tnum, size_t *length_r)
+ const char *name, size_t *length_r)
{
size_t name_length = strlen(name);
- size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
- return NULL;
-
- if (char_tnum != NULL) {
- char_tnum_length = strlen(char_tnum);
- if (entry->length > name_length + char_tnum_length + 2 &&
- comment[name_length] == '[' &&
- g_ascii_strncasecmp(comment + name_length + 1,
- char_tnum, char_tnum_length) == 0 &&
- comment[name_length + char_tnum_length + 1] == ']')
- name_length = name_length + char_tnum_length + 2;
- else if (entry->length > name_length + char_tnum_length &&
- g_ascii_strncasecmp(comment + name_length,
- char_tnum, char_tnum_length) == 0)
- name_length = name_length + char_tnum_length;
- }
+ return nullptr;
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
- return NULL;
+ return nullptr;
}
/**
@@ -167,14 +156,13 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static bool
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
- const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx)
{
const char *value;
size_t value_length;
- value = flac_comment_value(entry, name, char_tnum, &value_length);
- if (value != NULL) {
+ value = flac_comment_value(entry, name, &value_length);
+ if (value != nullptr) {
char *p = g_strndup(value, value_length);
tag_handler_invoke_tag(handler, handler_ctx, tag_type, p);
g_free(p);
@@ -184,23 +172,15 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
return false;
}
-static const struct tag_table flac_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
-flac_scan_comment(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment_Entry *entry,
+flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const struct tag_handler *handler, void *handler_ctx)
{
- if (handler->pair != NULL) {
+ if (handler->pair != nullptr) {
char *name = g_strdup((const char*)entry->entry);
char *value = strchr(name, '=');
- if (value != NULL && value > name) {
+ if (value != nullptr && value > name) {
*value++ = 0;
tag_handler_invoke_pair(handler, handler_ctx,
name, value);
@@ -209,36 +189,34 @@ flac_scan_comment(const char *char_tnum,
g_free(name);
}
- for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
- if (flac_copy_comment(entry, i->name, i->type, char_tnum,
+ for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
+ if (flac_copy_comment(entry, i->name, i->type,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(entry,
- tag_item_names[i], i, char_tnum,
+ tag_item_names[i], (enum tag_type)i,
handler, handler_ctx))
return;
}
static void
-flac_scan_comments(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment,
+flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < comment->num_comments; ++i)
- flac_scan_comment(char_tnum, &comment->comments[i],
+ flac_scan_comment(&comment->comments[i],
handler, handler_ctx);
}
void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
+flac_scan_metadata(const FLAC__StreamMetadata *block,
const struct tag_handler *handler, void *handler_ctx)
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- flac_scan_comments(track, &block->data.vorbis_comment,
+ flac_scan_comments(&block->data.vorbis_comment,
handler, handler_ctx);
break;
@@ -254,70 +232,22 @@ flac_scan_metadata(const char *track,
}
void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
+flac_vorbis_comments_to_tag(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment *comment)
{
- flac_scan_comments(char_tnum, comment,
- &add_tag_handler, tag);
+ flac_scan_comments(comment, &add_tag_handler, tag);
}
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx)
+void
+FLACMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_SimpleIterator *it;
- FLAC__StreamMetadata *block = NULL;
-
- it = FLAC__metadata_simple_iterator_new();
- if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
- const char *err;
- FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
-
- s = FLAC__metadata_simple_iterator_status(it);
-
- switch (s) { /* slightly more human-friendly messages: */
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
- err = "illegal input";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
- err = "error opening file";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
- err = "not a FLAC file";
- break;
- default:
- err = FLAC__Metadata_SimpleIteratorStatusString[s];
- }
- g_debug("Reading '%s' metadata gave the following error: %s\n",
- file, err);
- FLAC__metadata_simple_iterator_delete(it);
- return false;
- }
+ FLACMetadataIterator iterator(*this);
do {
- block = FLAC__metadata_simple_iterator_get_block(it);
- if (!block)
+ FLAC__StreamMetadata *block = iterator.GetBlock();
+ if (block == nullptr)
break;
- flac_scan_metadata(char_tnum, block, handler, handler_ctx);
- FLAC__metadata_object_delete(block);
- } while (FLAC__metadata_simple_iterator_next(it));
-
- FLAC__metadata_simple_iterator_delete(it);
-
- return true;
-}
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum)
-{
- struct tag *tag = tag_new();
-
- if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) ||
- tag_is_empty(tag)) {
- tag_free(tag);
- tag = NULL;
- }
-
- return tag;
+ flac_scan_metadata(block, handler, handler_ctx);
+ } while (iterator.Next());
}
diff --git a/src/decoder/FLACMetaData.hxx b/src/decoder/FLACMetaData.hxx
new file mode 100644
index 00000000..0eceec23
--- /dev/null
+++ b/src/decoder/FLACMetaData.hxx
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FLAC_METADATA_H
+#define MPD_FLAC_METADATA_H
+
+#include "gcc.h"
+#include "FLACIOHandle.hxx"
+
+#include <FLAC/metadata.h>
+
+#include <assert.h>
+
+class FLACMetadataChain {
+ FLAC__Metadata_Chain *chain;
+
+public:
+ FLACMetadataChain():chain(::FLAC__metadata_chain_new()) {}
+
+ ~FLACMetadataChain() {
+ ::FLAC__metadata_chain_delete(chain);
+ }
+
+ explicit operator FLAC__Metadata_Chain *() {
+ return chain;
+ }
+
+ bool Read(const char *path) {
+ return ::FLAC__metadata_chain_read(chain, path);
+ }
+
+ bool Read(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool Read(input_stream *is) {
+ return Read(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ bool ReadOgg(const char *path) {
+ return ::FLAC__metadata_chain_read_ogg(chain, path);
+ }
+
+ bool ReadOgg(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool ReadOgg(input_stream *is) {
+ return ReadOgg(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ gcc_pure
+ FLAC__Metadata_ChainStatus GetStatus() const {
+ return ::FLAC__metadata_chain_status(chain);
+ }
+
+ gcc_pure
+ const char *GetStatusString() const {
+ return FLAC__Metadata_ChainStatusString[GetStatus()];
+ }
+
+ void Scan(const struct tag_handler *handler, void *handler_ctx);
+};
+
+class FLACMetadataIterator {
+ FLAC__Metadata_Iterator *iterator;
+
+public:
+ FLACMetadataIterator():iterator(::FLAC__metadata_iterator_new()) {}
+
+ FLACMetadataIterator(FLACMetadataChain &chain)
+ :iterator(::FLAC__metadata_iterator_new()) {
+ ::FLAC__metadata_iterator_init(iterator,
+ (FLAC__Metadata_Chain *)chain);
+ }
+
+ ~FLACMetadataIterator() {
+ ::FLAC__metadata_iterator_delete(iterator);
+ }
+
+ bool Next() {
+ return ::FLAC__metadata_iterator_next(iterator);
+ }
+
+ gcc_pure
+ FLAC__StreamMetadata *GetBlock() {
+ return ::FLAC__metadata_iterator_get_block(iterator);
+ }
+};
+
+struct tag_handler;
+struct tag;
+struct replay_gain_info;
+
+static inline unsigned
+flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
+{
+ assert(stream_info->sample_rate > 0);
+
+ return (stream_info->total_samples + stream_info->sample_rate - 1) /
+ stream_info->sample_rate;
+}
+
+bool
+flac_parse_replay_gain(struct replay_gain_info *rgi,
+ const FLAC__StreamMetadata *block);
+
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block);
+
+void
+flac_vorbis_comments_to_tag(struct tag *tag,
+ const FLAC__StreamMetadata_VorbisComment *comment);
+
+void
+flac_scan_metadata(const FLAC__StreamMetadata *block,
+ const struct tag_handler *handler, void *handler_ctx);
+
+#endif
diff --git a/src/decoder/flac_pcm.c b/src/decoder/FLAC_PCM.cxx
index 6964d8ac..303530aa 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/FLAC_PCM.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "flac_pcm.h"
+#include "FLAC_PCM.hxx"
#include <assert.h>
diff --git a/src/decoder/flac_pcm.h b/src/decoder/FLAC_PCM.hxx
index a931998c..97d214c1 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/FLAC_PCM.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FLAC_PCM_H
-#define MPD_FLAC_PCM_H
+#ifndef MPD_FLAC_PCM_HXX
+#define MPD_FLAC_PCM_HXX
#include "audio_format.h"
diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/FfmpegDecoderPlugin.cxx
index 4c4cb2b8..dd98b968 100644
--- a/src/decoder/ffmpeg_decoder_plugin.c
+++ b/src/decoder/FfmpegDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
+#include "FfmpegDecoderPlugin.hxx"
#include "decoder_api.h"
-#include "audio_check.h"
-#include "ffmpeg_metadata.h"
+#include "FfmpegMetaData.hxx"
#include "tag_handler.h"
+#include "InputStream.hxx"
+
+extern "C" {
+#include "audio_check.h"
+}
#include <glib.h>
@@ -34,15 +42,15 @@
#include <sys/stat.h>
#include <unistd.h>
+extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
+}
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"
@@ -82,18 +90,15 @@ struct mpd_ffmpeg_stream {
struct decoder *decoder;
struct input_stream *input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *io;
-#else
- ByteIOContext *io;
-#endif
+
unsigned char buffer[8192];
};
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ struct mpd_ffmpeg_stream *stream = (struct mpd_ffmpeg_stream *)opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
@@ -102,7 +107,7 @@ mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ struct mpd_ffmpeg_stream *stream = (struct mpd_ffmpeg_stream *)opaque;
if (whence == AVSEEK_SIZE)
return stream->input->size;
@@ -119,19 +124,11 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
-#else
- stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
- false, stream,
- mpd_ffmpeg_stream_read, NULL,
- input->seekable
- ? mpd_ffmpeg_stream_seek : NULL);
-#endif
if (stream->io == NULL) {
g_free(stream);
return NULL;
@@ -146,15 +143,10 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
*/
static int
mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *pb,
-#else
- ByteIOContext *pb,
-#endif
const char *filename,
AVInputFormat *fmt)
{
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,1,3)
AVFormatContext *context = avformat_alloc_context();
if (context == NULL)
return AVERROR(ENOMEM);
@@ -162,9 +154,6 @@ mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
context->pb = pb;
*ic_ptr = context;
return avformat_open_input(ic_ptr, filename, fmt, NULL);
-#else
- return av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
-#endif
}
static void
@@ -188,11 +177,7 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
{
for (unsigned i = 0; i < format_context->nb_streams; ++i)
if (format_context->streams[i]->codec->codec_type ==
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
AVMEDIA_TYPE_AUDIO)
-#else
- CODEC_TYPE_AUDIO)
-#endif
return i;
return -1;
@@ -291,12 +276,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts, *time_base));
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
AVPacket packet2 = *packet;
-#else
- const uint8_t *packet_data = packet->data;
- int packet_size = packet->size;
-#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
uint8_t aligned_buffer[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
@@ -305,16 +285,11 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
/* libavcodec < 0.8 needs an aligned buffer */
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
size_t buffer_size = sizeof(audio_buf);
- int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
+ int16_t *aligned_buffer = (int16_t *)align16(audio_buf, &buffer_size);
#endif
enum decoder_command cmd = DECODE_COMMAND_NONE;
- while (
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
- packet2.size > 0 &&
-#else
- packet_size > 0 &&
-#endif
+ while (packet2.size > 0 &&
cmd == DECODE_COMMAND_NONE) {
int audio_size = buffer_size;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
@@ -332,14 +307,10 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
len = audio_size;
} else if (len >= 0)
len = -1;
-#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
+#else
int len = avcodec_decode_audio3(codec_context,
aligned_buffer, &audio_size,
&packet2);
-#else
- int len = avcodec_decode_audio2(codec_context,
- aligned_buffer, &audio_size,
- packet_data, packet_size);
#endif
if (len < 0) {
@@ -348,13 +319,8 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
packet2.data += len;
packet2.size -= len;
-#else
- packet_data += len;
- packet_size -= len;
-#endif
if (audio_size <= 0)
continue;
@@ -366,33 +332,21 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
return cmd;
}
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1)
-#define AVSampleFormat SampleFormat
-#endif
-
G_GNUC_CONST
static enum sample_format
ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
{
switch (sample_fmt) {
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S16:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S16P:
#endif
-#else
- case SAMPLE_FMT_S16:
-#endif
return SAMPLE_FORMAT_S16;
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S32:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S32P:
#endif
-#else
- case SAMPLE_FMT_S32:
-#endif
return SAMPLE_FORMAT_S32;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
@@ -404,7 +358,6 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
char buffer[64];
const char *name = av_get_sample_fmt_string(buffer, sizeof(buffer),
sample_fmt);
@@ -412,7 +365,6 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
g_warning("Unsupported libavcodec SampleFormat value: %s (%d)",
name, sample_fmt);
else
-#endif
g_warning("Unsupported libavcodec SampleFormat value: %d",
sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
@@ -426,7 +378,7 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
PADDING = 16,
};
- unsigned char *buffer = g_malloc(BUFFER_SIZE);
+ unsigned char *buffer = (unsigned char *)g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
if (nbytes <= PADDING ||
!input_stream_lock_seek(is, 0, SEEK_SET, NULL)) {
@@ -440,11 +392,10 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
size */
nbytes -= PADDING;
- AVProbeData avpd = {
- .buf = buffer,
- .buf_size = nbytes,
- .filename = is->uri,
- };
+ AVProbeData avpd;
+ avpd.buf = buffer;
+ avpd.buf_size = nbytes;
+ avpd.filename = is->uri.c_str();
AVInputFormat *format = av_probe_input_format(&avpd, true);
g_free(buffer);
@@ -471,7 +422,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
//ffmpeg works with ours "fileops" helper
AVFormatContext *format_context = NULL;
- if (mpd_ffmpeg_open_input(&format_context, stream->io, input->uri,
+ if (mpd_ffmpeg_open_input(&format_context, stream->io,
+ input->uri.c_str(),
input_format) != 0) {
g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
@@ -630,7 +582,7 @@ ffmpeg_scan_stream(struct input_stream *is,
return false;
AVFormatContext *f = NULL;
- if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
+ if (mpd_ffmpeg_open_input(&f, stream->io, is->uri.c_str(),
input_format) != 0) {
mpd_ffmpeg_stream_close(stream);
return false;
@@ -656,10 +608,6 @@ ffmpeg_scan_stream(struct input_stream *is,
tag_handler_invoke_duration(handler, handler_ctx,
f->duration / AV_TIME_BASE);
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
- av_metadata_conv(f, NULL, f->iformat->metadata_conv);
-#endif
-
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
int idx = ffmpeg_find_audio_stream(f);
if (idx >= 0)
@@ -791,10 +739,14 @@ static const char *const ffmpeg_mime_types[] = {
};
const struct decoder_plugin ffmpeg_decoder_plugin = {
- .name = "ffmpeg",
- .init = ffmpeg_init,
- .stream_decode = ffmpeg_decode,
- .scan_stream = ffmpeg_scan_stream,
- .suffixes = ffmpeg_suffixes,
- .mime_types = ffmpeg_mime_types
+ "ffmpeg",
+ ffmpeg_init,
+ nullptr,
+ ffmpeg_decode,
+ nullptr,
+ nullptr,
+ ffmpeg_scan_stream,
+ nullptr,
+ ffmpeg_suffixes,
+ ffmpeg_mime_types
};
diff --git a/src/decoder/FfmpegDecoderPlugin.hxx b/src/decoder/FfmpegDecoderPlugin.hxx
new file mode 100644
index 00000000..9a637fff
--- /dev/null
+++ b/src/decoder/FfmpegDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_FFMPEG_HXX
+#define MPD_DECODER_FFMPEG_HXX
+
+extern const struct decoder_plugin ffmpeg_decoder_plugin;
+
+#endif
diff --git a/src/decoder/ffmpeg_metadata.c b/src/decoder/FfmpegMetaData.cxx
index 3ef774f6..2d7ebbca 100644
--- a/src/decoder/ffmpeg_metadata.c
+++ b/src/decoder/FfmpegMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
-#include "ffmpeg_metadata.h"
+#include "FfmpegMetaData.hxx"
#include "tag_table.h"
#include "tag_handler.h"
@@ -26,9 +29,6 @@
#define G_LOG_DOMAIN "ffmpeg"
static const struct tag_table ffmpeg_tags[] = {
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,50,0)
- { "author", TAG_ARTIST },
-#endif
{ "year", TAG_DATE },
{ "author-sort", TAG_ARTIST_SORT },
{ "album_artist", TAG_ALBUM_ARTIST },
@@ -50,8 +50,6 @@ ffmpeg_copy_metadata(enum tag_type type,
type, mt->value);
}
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
-
static void
ffmpeg_scan_pairs(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
@@ -63,14 +61,12 @@ ffmpeg_scan_pairs(AVDictionary *dict,
i->key, i->value);
}
-#endif
-
void
ffmpeg_scan_dictionary(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
- ffmpeg_copy_metadata(i, dict, tag_item_names[i],
+ ffmpeg_copy_metadata(tag_type(i), dict, tag_item_names[i],
handler, handler_ctx);
for (const struct tag_table *i = ffmpeg_tags;
@@ -78,8 +74,6 @@ ffmpeg_scan_dictionary(AVDictionary *dict,
ffmpeg_copy_metadata(i->type, dict, i->name,
handler, handler_ctx);
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
if (handler->pair != NULL)
ffmpeg_scan_pairs(dict, handler, handler_ctx);
-#endif
}
diff --git a/src/decoder/ffmpeg_metadata.h b/src/decoder/FfmpegMetaData.hxx
index 60658f47..466d2cb1 100644
--- a/src/decoder/ffmpeg_metadata.h
+++ b/src/decoder/FfmpegMetaData.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FFMPEG_METADATA_H
-#define MPD_FFMPEG_METADATA_H
+#ifndef MPD_FFMPEG_METADATA_HXX
+#define MPD_FFMPEG_METADATA_HXX
+extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
-
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,1,0)
-#define AVDictionary AVMetadata
-#define AVDictionaryEntry AVMetadataTag
-#define av_dict_get av_metadata_get
-#endif
+}
struct tag_handler;
diff --git a/src/decoder/_ogg_common.c b/src/decoder/OggCodec.cxx
index 09d2712d..5ad9c69d 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/OggCodec.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,25 +22,27 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "OggCodec.hxx"
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
{
/* oggflac detection based on code in ogg123 and this post
* http://lists.xiph.org/pipermail/flac/2004-December/000393.html
* ogg123 trunk still doesn't have this patch as of June 2005 */
unsigned char buf[41];
- size_t r;
-
- r = decoder_read(NULL, inStream, buf, sizeof(buf));
+ size_t r = decoder_read(decoder, is, buf, sizeof(buf));
if (r < sizeof(buf) || memcmp(buf, "OggS", 4) != 0)
- return VORBIS;
+ return OGG_CODEC_UNKNOWN;
if ((memcmp(buf + 29, "FLAC", 4) == 0 &&
memcmp(buf + 37, "fLaC", 4) == 0) ||
memcmp(buf + 28, "FLAC", 4) == 0 ||
memcmp(buf + 28, "fLaC", 4) == 0)
- return FLAC;
+ return OGG_CODEC_FLAC;
+
+ if (memcmp(buf + 28, "Opus", 4) == 0)
+ return OGG_CODEC_OPUS;
- return VORBIS;
+ return OGG_CODEC_VORBIS;
}
diff --git a/src/decoder/_ogg_common.h b/src/decoder/OggCodec.hxx
index 85e4ebba..e241560f 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/OggCodec.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,19 @@
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
*/
-#ifndef MPD_OGG_COMMON_H
-#define MPD_OGG_COMMON_H
+#ifndef MPD_OGG_CODEC_HXX
+#define MPD_OGG_CODEC_HXX
#include "decoder_api.h"
-typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;
+enum ogg_codec {
+ OGG_CODEC_UNKNOWN,
+ OGG_CODEC_VORBIS,
+ OGG_CODEC_FLAC,
+ OGG_CODEC_OPUS,
+};
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream);
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is);
#endif /* _OGG_COMMON_H */
diff --git a/src/decoder/OggFind.cxx b/src/decoder/OggFind.cxx
new file mode 100644
index 00000000..9df4c645
--- /dev/null
+++ b/src/decoder/OggFind.cxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OggFind.hxx"
+#include "OggSyncState.hxx"
+
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
+{
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r == 0) {
+ if (!oy.ExpectPageIn(os))
+ return false;
+
+ continue;
+ } else if (r > 0 && packet.e_o_s)
+ return true;
+ }
+}
diff --git a/src/decoder/OggFind.hxx b/src/decoder/OggFind.hxx
new file mode 100644
index 00000000..7d18d206
--- /dev/null
+++ b/src/decoder/OggFind.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OGG_FIND_HXX
+#define MPD_OGG_FIND_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+class OggSyncState;
+
+/**
+ * Skip all pages/packets until an end-of-stream (EOS) packet for the
+ * specified stream is found.
+ *
+ * @return true if the EOS packet was found
+ */
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet);
+
+#endif
diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/OggSyncState.hxx
new file mode 100644
index 00000000..eaeb9bd8
--- /dev/null
+++ b/src/decoder/OggSyncState.hxx
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OGG_SYNC_STATE_HXX
+#define MPD_OGG_SYNC_STATE_HXX
+
+#include "check.h"
+#include "OggUtil.hxx"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Wrapper for an ogg_sync_state.
+ */
+class OggSyncState {
+ ogg_sync_state oy;
+
+ input_stream &is;
+ struct decoder *const decoder;
+
+public:
+ OggSyncState(input_stream &_is, struct decoder *const _decoder=nullptr)
+ :is(_is), decoder(_decoder) {
+ ogg_sync_init(&oy);
+ }
+
+ ~OggSyncState() {
+ ogg_sync_clear(&oy);
+ }
+
+ void Reset() {
+ ogg_sync_reset(&oy);
+ }
+
+ bool Feed(size_t size) {
+ return OggFeed(oy, decoder, &is, size);
+ }
+
+ bool ExpectPage(ogg_page &page) {
+ return OggExpectPage(oy, page, decoder, &is);
+ }
+
+ bool ExpectFirstPage(ogg_stream_state &os) {
+ return OggExpectFirstPage(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageIn(ogg_stream_state &os) {
+ return OggExpectPageIn(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageSeek(ogg_page &page) {
+ return OggExpectPageSeek(oy, page, decoder, &is);
+ }
+
+ bool ExpectPageSeekIn(ogg_stream_state &os) {
+ return OggExpectPageSeekIn(oy, os, decoder, &is);
+ }
+};
+
+#endif
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 00000000..a1125a2c
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OggUtil.hxx"
+#include "decoder_api.h"
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ input_stream *input_stream, size_t size)
+{
+ char *buffer = ogg_sync_buffer(&oy, size);
+ if (buffer == nullptr)
+ return false;
+
+ size_t nbytes = decoder_read(decoder, input_stream,
+ buffer, size);
+ if (nbytes == 0)
+ return false;
+
+ ogg_sync_wrote(&oy, nbytes);
+ return true;
+}
+
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ while (true) {
+ int r = ogg_sync_pageout(&oy, &page);
+ if (r != 0)
+ return r > 0;
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ size_t remaining_skipped = 16384;
+
+ while (true) {
+ int r = ogg_sync_pageseek(&oy, &page);
+ if (r > 0)
+ return true;
+
+ if (r < 0) {
+ /* skipped -r bytes */
+ size_t nbytes = -r;
+ if (nbytes > remaining_skipped)
+ /* still no ogg page - we lost our
+ patience, abort */
+ return false;
+
+ remaining_skipped -= nbytes;
+ continue;
+ }
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPageSeek(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 00000000..32479781
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OGG_UTIL_HXX
+#define MPD_OGG_UTIL_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+struct input_stream;
+struct decoder;
+
+/**
+ * Feed data from the #input_stream into the #ogg_sync_state.
+ *
+ * @return false on error or end-of-file
+ */
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder, input_stream *is,
+ size_t size);
+
+/**
+ * Feed into the #ogg_sync_state until a page gets available. Garbage
+ * data at the beginning is considered a fatal error.
+ *
+ * @return true if a page is available
+ */
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPage(), ogg_stream_init() and
+ * ogg_stream_pagein().
+ *
+ * @return true if the stream was initialized and the first page was
+ * delivered to it
+ */
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Combines OggExpectPage() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Like OggExpectPage(), but allow skipping garbage (after seeking).
+ */
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPageSeek() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 00000000..3e3a1e4e
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h" /* must be first for large file support */
+#include "OpusDecoderPlugin.h"
+#include "OpusHead.hxx"
+#include "OpusTags.hxx"
+#include "OggUtil.hxx"
+#include "OggFind.hxx"
+#include "OggSyncState.hxx"
+#include "decoder_api.h"
+#include "OggCodec.hxx"
+#include "audio_check.h"
+#include "tag_handler.h"
+#include "InputStream.hxx"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <glib.h>
+
+#include <stdio.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus"
+
+static const opus_int32 opus_sample_rate = 48000;
+
+gcc_pure
+static bool
+IsOpusHead(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
+}
+
+gcc_pure
+static bool
+IsOpusTags(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
+}
+
+static bool
+mpd_opus_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ g_debug("%s", opus_get_version_string());
+
+ return true;
+}
+
+class MPDOpusDecoder {
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+
+ ogg_stream_state os;
+
+ OpusDecoder *opus_decoder;
+ opus_int16 *output_buffer;
+ unsigned output_size;
+
+ bool os_initialized;
+ bool found_opus;
+
+ int opus_serialno;
+
+ size_t frame_size;
+
+public:
+ MPDOpusDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :decoder(_decoder), input_stream(_input_stream),
+ opus_decoder(nullptr),
+ output_buffer(nullptr), output_size(0),
+ os_initialized(false), found_opus(false) {}
+ ~MPDOpusDecoder();
+
+ bool ReadFirstPage(OggSyncState &oy);
+ bool ReadNextPage(OggSyncState &oy);
+
+ enum decoder_command HandlePackets();
+ enum decoder_command HandlePacket(const ogg_packet &packet);
+ enum decoder_command HandleBOS(const ogg_packet &packet);
+ enum decoder_command HandleTags(const ogg_packet &packet);
+ enum decoder_command HandleAudio(const ogg_packet &packet);
+};
+
+MPDOpusDecoder::~MPDOpusDecoder()
+{
+ g_free(output_buffer);
+
+ if (opus_decoder != nullptr)
+ opus_decoder_destroy(opus_decoder);
+
+ if (os_initialized)
+ ogg_stream_clear(&os);
+}
+
+inline bool
+MPDOpusDecoder::ReadFirstPage(OggSyncState &oy)
+{
+ assert(!os_initialized);
+
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ os_initialized = true;
+ return true;
+}
+
+inline bool
+MPDOpusDecoder::ReadNextPage(OggSyncState &oy)
+{
+ assert(os_initialized);
+
+ ogg_page page;
+ if (!oy.ExpectPage(page))
+ return false;
+
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandlePackets()
+{
+ ogg_packet packet;
+ while (ogg_stream_packetout(&os, &packet) == 1) {
+ enum decoder_command cmd = HandlePacket(packet);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
+{
+ if (packet.e_o_s)
+ return DECODE_COMMAND_STOP;
+
+ if (packet.b_o_s)
+ return HandleBOS(packet);
+ else if (!found_opus)
+ return DECODE_COMMAND_STOP;
+
+ if (IsOpusTags(packet))
+ return HandleTags(packet);
+
+ return HandleAudio(packet);
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
+{
+ assert(packet.b_o_s);
+
+ if (found_opus || !IsOpusHead(packet))
+ return DECODE_COMMAND_STOP;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels))
+ return DECODE_COMMAND_STOP;
+
+ assert(opus_decoder == nullptr);
+ assert(output_buffer == nullptr);
+
+ opus_serialno = os.serialno;
+ found_opus = true;
+
+ /* TODO: parse attributes from the OpusHead (sample rate,
+ channels, ...) */
+
+ int opus_error;
+ opus_decoder = opus_decoder_create(opus_sample_rate, channels,
+ &opus_error);
+ if (opus_decoder == nullptr) {
+ g_warning("libopus error: %s",
+ opus_strerror(opus_error));
+ return DECODE_COMMAND_STOP;
+ }
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, opus_sample_rate,
+ SAMPLE_FORMAT_S16, channels);
+ decoder_initialized(decoder, &audio_format, false, -1);
+ frame_size = audio_format_frame_size(&audio_format);
+
+ /* allocate an output buffer for 16 bit PCM samples big enough
+ to hold a quarter second, larger than 120ms required by
+ libopus */
+ output_size = audio_format.sample_rate / 4;
+ output_buffer = (opus_int16 *)
+ g_malloc(sizeof(*output_buffer) * output_size *
+ audio_format.channels);
+
+ return decoder_get_command(decoder);
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleTags(const ogg_packet &packet)
+{
+ struct tag *tag = tag_new();
+
+ enum decoder_command cmd;
+ if (ScanOpusTags(packet.packet, packet.bytes, &add_tag_handler, tag) &&
+ !tag_is_empty(tag))
+ cmd = decoder_tag(decoder, input_stream, tag);
+ else
+ cmd = decoder_get_command(decoder);
+
+ tag_free(tag);
+ return cmd;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
+{
+ assert(opus_decoder != nullptr);
+
+ int nframes = opus_decode(opus_decoder,
+ (const unsigned char*)packet.packet,
+ packet.bytes,
+ output_buffer, output_size,
+ 0);
+ if (nframes < 0) {
+ g_warning("%s", opus_strerror(nframes));
+ return DECODE_COMMAND_STOP;
+ }
+
+ if (nframes > 0) {
+ const size_t nbytes = nframes * frame_size;
+ enum decoder_command cmd =
+ decoder_data(decoder, input_stream,
+ output_buffer, nbytes,
+ 0);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+static void
+mpd_opus_stream_decode(struct decoder *decoder,
+ struct input_stream *input_stream)
+{
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
+ return;
+
+ /* rewind the stream, because ogg_codec_detect() has
+ moved it */
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
+
+ MPDOpusDecoder d(decoder, input_stream);
+ OggSyncState oy(*input_stream, decoder);
+
+ if (!d.ReadFirstPage(oy))
+ return;
+
+ while (true) {
+ enum decoder_command cmd = d.HandlePackets();
+ if (cmd != DECODE_COMMAND_NONE)
+ break;
+
+ if (!d.ReadNextPage(oy))
+ break;
+
+ }
+}
+
+static bool
+SeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
+ input_stream *is)
+{
+ if (is->size > 0 && is->size - is->offset < 65536)
+ return OggFindEOS(oy, os, packet);
+
+ if (!input_stream_cheap_seeking(is))
+ return false;
+
+ oy.Reset();
+
+ return input_stream_lock_seek(is, -65536, SEEK_END, nullptr) &&
+ oy.ExpectPageSeekIn(os) &&
+ OggFindEOS(oy, os, packet);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ OggSyncState oy(*is);
+
+ ogg_stream_state os;
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ ogg_packet packet;
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r < 0) {
+ result = false;
+ break;
+ }
+
+ if (r == 0) {
+ if (remaining_pages-- == 0)
+ break;
+
+ if (!oy.ExpectPageIn(os)) {
+ result = false;
+ break;
+ }
+
+ continue;
+ }
+
+ if (packet.b_o_s) {
+ if (!IsOpusHead(packet))
+ break;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels)) {
+ result = false;
+ break;
+ }
+
+ result = true;
+ } else if (!result)
+ break;
+ else if (IsOpusTags(packet)) {
+ if (!ScanOpusTags(packet.packet, packet.bytes,
+ handler, handler_ctx))
+ result = false;
+
+ break;
+ }
+ }
+
+ if (packet.e_o_s || SeekFindEOS(oy, os, packet, is))
+ tag_handler_invoke_duration(handler, handler_ctx,
+ packet.granulepos / opus_sample_rate);
+
+ ogg_stream_clear(&os);
+
+ return result;
+}
+
+static const char *const opus_suffixes[] = {
+ "opus",
+ "ogg",
+ "oga",
+ nullptr
+};
+
+static const char *const opus_mime_types[] = {
+ "audio/opus",
+ nullptr
+};
+
+const struct decoder_plugin opus_decoder_plugin = {
+ "opus",
+ mpd_opus_init,
+ nullptr,
+ mpd_opus_stream_decode,
+ nullptr,
+ nullptr,
+ mpd_opus_scan_stream,
+ nullptr,
+ opus_suffixes,
+ opus_mime_types,
+};
diff --git a/src/decoder/OpusDecoderPlugin.h b/src/decoder/OpusDecoderPlugin.h
new file mode 100644
index 00000000..c95d6ded
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_OPUS_H
+#define MPD_DECODER_OPUS_H
+
+extern const struct decoder_plugin opus_decoder_plugin;
+
+#endif
diff --git a/src/decoder/OpusHead.cxx b/src/decoder/OpusHead.cxx
new file mode 100644
index 00000000..c57e08e1
--- /dev/null
+++ b/src/decoder/OpusHead.cxx
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OpusHead.hxx"
+
+#include <stdint.h>
+#include <string.h>
+
+struct OpusHead {
+ char signature[8];
+ uint8_t version, channels;
+ uint16_t pre_skip;
+ uint32_t sample_rate;
+ uint16_t output_gain;
+ uint8_t channel_mapping;
+};
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
+{
+ const OpusHead *h = (const OpusHead *)data;
+ if (size < 19 || (h->version & 0xf0) != 0)
+ return false;
+
+ channels_r = h->channels;
+ return true;
+}
diff --git a/src/decoder/OpusHead.hxx b/src/decoder/OpusHead.hxx
new file mode 100644
index 00000000..9f75c4f7
--- /dev/null
+++ b/src/decoder/OpusHead.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OPUS_HEAD_HXX
+#define MPD_OPUS_HEAD_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
+
+#endif
diff --git a/src/decoder/OpusReader.hxx b/src/decoder/OpusReader.hxx
new file mode 100644
index 00000000..1fd07b55
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OPUS_READER_HXX
+#define MPD_OPUS_READER_HXX
+
+#include "check.h"
+#include "string_util.h"
+
+#include <stdint.h>
+#include <string.h>
+
+class OpusReader {
+ const uint8_t *p, *const end;
+
+public:
+ OpusReader(const void *_p, size_t size)
+ :p((const uint8_t *)_p), end(p + size) {}
+
+ bool Skip(size_t length) {
+ p += length;
+ return p <= end;
+ }
+
+ const void *Read(size_t length) {
+ const uint8_t *result = p;
+ return Skip(length)
+ ? result
+ : nullptr;
+ }
+
+ bool Expect(const void *value, size_t length) {
+ const void *data = Read(length);
+ return data != nullptr && memcmp(value, data, length) == 0;
+ }
+
+ bool ReadByte(uint8_t &value_r) {
+ if (p >= end)
+ return false;
+
+ value_r = *p++;
+ return true;
+ }
+
+ bool ReadShort(uint16_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8);
+ return true;
+ }
+
+ bool ReadWord(uint32_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8)
+ | (value[2] << 16) | (value[3] << 24);
+ return true;
+ }
+
+ bool SkipString() {
+ uint32_t length;
+ return ReadWord(length) && Skip(length);
+ }
+
+ char *ReadString() {
+ uint32_t length;
+ if (!ReadWord(length))
+ return nullptr;
+
+ const char *src = (const char *)Read(length);
+ if (src == nullptr)
+ return nullptr;
+
+ return strndup(src, length);
+ }
+};
+
+#endif
diff --git a/src/decoder/OpusTags.cxx b/src/decoder/OpusTags.cxx
new file mode 100644
index 00000000..cb35a624
--- /dev/null
+++ b/src/decoder/OpusTags.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OpusTags.hxx"
+#include "OpusReader.hxx"
+#include "XiphTags.h"
+#include "tag_handler.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void
+ScanOneOpusTag(const char *name, const char *value,
+ const struct tag_handler *handler, void *ctx)
+{
+ tag_handler_invoke_pair(handler, ctx, name, value);
+
+ if (handler->tag != nullptr) {
+ enum tag_type t = tag_table_lookup_i(xiph_tags, name);
+ if (t != TAG_NUM_OF_ITEM_TYPES)
+ tag_handler_invoke_tag(handler, ctx, t, value);
+ }
+}
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx)
+{
+ OpusReader r(data, size);
+ if (!r.Expect("OpusTags", 8))
+ return false;
+
+ if (handler->pair == nullptr && handler->tag == nullptr)
+ return true;
+
+ if (!r.SkipString())
+ return false;
+
+ uint32_t n;
+ if (!r.ReadWord(n))
+ return false;
+
+ while (n-- > 0) {
+ char *p = r.ReadString();
+ if (p == nullptr)
+ return false;
+
+ char *eq = strchr(p, '=');
+ if (eq != nullptr && eq > p) {
+ *eq = 0;
+
+ ScanOneOpusTag(p, eq + 1, handler, ctx);
+ }
+
+ free(p);
+ }
+
+ return true;
+}
diff --git a/src/decoder/OpusTags.hxx b/src/decoder/OpusTags.hxx
new file mode 100644
index 00000000..2f3eec84
--- /dev/null
+++ b/src/decoder/OpusTags.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OPUS_TAGS_HXX
+#define MPD_OPUS_TAGS_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx);
+
+#endif
diff --git a/src/decoder/vorbis_comments.c b/src/decoder/VorbisComments.cxx
index 6c2d57b7..10fe2236 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/VorbisComments.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,8 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
+#include "VorbisComments.hxx"
+#include "XiphTags.h"
#include "tag.h"
#include "tag_table.h"
#include "tag_handler.h"
@@ -95,13 +96,6 @@ vorbis_copy_comment(const char *comment,
return false;
}
-static const struct tag_table vorbis_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
vorbis_scan_comment(const char *comment,
const struct tag_handler *handler, void *handler_ctx)
@@ -119,14 +113,14 @@ vorbis_scan_comment(const char *comment,
g_free(name);
}
- for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
+ for (const struct tag_table *i = xiph_tags; i->name != NULL; ++i)
if (vorbis_copy_comment(comment, i->name, i->type,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (vorbis_copy_comment(comment,
- tag_item_names[i], i,
+ tag_item_names[i], tag_type(i),
handler, handler_ctx))
return;
}
diff --git a/src/decoder/vorbis_comments.h b/src/decoder/VorbisComments.hxx
index c1509693..8212cac4 100644
--- a/src/decoder/vorbis_comments.h
+++ b/src/decoder/VorbisComments.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_VORBIS_COMMENTS_H
-#define MPD_VORBIS_COMMENTS_H
+#ifndef MPD_VORBIS_COMMENTS_HXX
+#define MPD_VORBIS_COMMENTS_HXX
#include "check.h"
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/VorbisDecoderPlugin.cxx
index 15cdc0ca..488786ed 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/VorbisDecoderPlugin.cxx
@@ -18,10 +18,17 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
-#include "_ogg_common.h"
+#include "VorbisDecoderPlugin.h"
+#include "VorbisComments.hxx"
+#include "decoder_api.h"
+#include "InputStream.hxx"
+#include "OggCodec.hxx"
+
+extern "C" {
#include "audio_check.h"
#include "uri.h"
+}
+
#include "tag_handler.h"
#ifndef HAVE_TREMOR
@@ -48,12 +55,11 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "vorbis"
-#define OGG_CHUNK_SIZE 4096
#if G_BYTE_ORDER == G_BIG_ENDIAN
-#define OGG_DECODE_USE_BIGENDIAN 1
+#define VORBIS_BIG_ENDIAN true
#else
-#define OGG_DECODE_USE_BIGENDIAN 0
+#define VORBIS_BIG_ENDIAN false
#endif
struct vorbis_input_stream {
@@ -65,10 +71,9 @@ struct vorbis_input_stream {
static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
- struct vorbis_input_stream *vis = data;
- size_t ret;
-
- ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb);
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -77,7 +82,7 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence)
{
- struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return vis->seekable &&
(!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) &&
@@ -93,16 +98,16 @@ static int ogg_close_cb(G_GNUC_UNUSED void *data)
static long ogg_tell_cb(void *data)
{
- const struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return (long)vis->input_stream->offset;
}
static const ov_callbacks vorbis_is_callbacks = {
- .read_func = ogg_read_cb,
- .seek_func = ogg_seek_cb,
- .close_func = ogg_close_cb,
- .tell_func = ogg_tell_cb,
+ ogg_read_cb,
+ ogg_seek_cb,
+ ogg_close_cb,
+ ogg_tell_cb,
};
static const char *
@@ -135,9 +140,7 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
{
vis->decoder = decoder;
vis->input_stream = input_stream;
- vis->seekable = input_stream->seekable &&
- (input_stream->uri == NULL ||
- !uri_has_scheme(input_stream->uri));
+ vis->seekable = input_stream_cheap_seeking(input_stream);
int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks);
if (ret < 0) {
@@ -155,9 +158,7 @@ static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
char **comments)
{
- struct tag *tag;
-
- tag = vorbis_comments_to_tag(comments);
+ struct tag *tag = vorbis_comments_to_tag(comments);
if (!tag)
return;
@@ -165,55 +166,79 @@ vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
tag_free(tag);
}
+#ifndef HAVE_TREMOR
+static void
+vorbis_interleave(float *dest, const float *const*src,
+ unsigned nframes, unsigned channels)
+{
+ for (const float *const*src_end = src + channels;
+ src != src_end; ++src, ++dest) {
+ float *d = dest;
+ for (const float *s = *src, *s_end = s + nframes;
+ s != s_end; ++s, d += channels)
+ *d = *s;
+ }
+}
+#endif
+
/* public */
static void
vorbis_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
GError *error = NULL;
- OggVorbis_File vf;
- struct vorbis_input_stream vis;
- struct audio_format audio_format;
- float total_time;
- int current_section;
- int prev_section = -1;
- long ret;
- char chunk[OGG_CHUNK_SIZE];
- long bitRate = 0;
- long test;
- const vorbis_info *vi;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- if (ogg_stream_type_detect(input_stream) != VORBIS)
+
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_VORBIS)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ struct vorbis_input_stream vis;
+ OggVorbis_File vf;
if (!vorbis_is_open(&vis, &vf, decoder, input_stream))
return;
- vi = ov_info(&vf, -1);
+ const vorbis_info *vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
return;
}
+ struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format, vi->rate,
+#ifdef HAVE_TREMOR
SAMPLE_FORMAT_S16,
+#else
+ SAMPLE_FORMAT_FLOAT,
+#endif
vi->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
- total_time = ov_time_total(&vf, -1);
+ float total_time = ov_time_total(&vf, -1);
if (total_time < 0)
total_time = 0;
decoder_initialized(decoder, &audio_format, vis.seekable, total_time);
+ enum decoder_command cmd = decoder_get_command(decoder);
+
+#ifdef HAVE_TREMOR
+ char buffer[4096];
+#else
+ float buffer[2048];
+ const int frames_per_buffer =
+ G_N_ELEMENTS(buffer) / audio_format.channels;
+ const unsigned frame_size = sizeof(buffer[0]) * audio_format.channels;
+#endif
+
+ int prev_section = -1;
+ unsigned kbit_rate = 0;
+
do {
if (cmd == DECODE_COMMAND_SEEK) {
double seek_where = decoder_seek_where(decoder);
@@ -223,17 +248,33 @@ vorbis_stream_decode(struct decoder *decoder,
decoder_seek_error(decoder);
}
- ret = ov_read(&vf, chunk, sizeof(chunk),
- OGG_DECODE_USE_BIGENDIAN, 2, 1, &current_section);
- if (ret == OV_HOLE) /* bad packet */
- ret = 0;
- else if (ret <= 0)
+ int current_section;
+
+#ifdef HAVE_TREMOR
+ long nbytes = ov_read(&vf, buffer, sizeof(buffer),
+ VORBIS_BIG_ENDIAN, 2, 1,
+ &current_section);
+#else
+ float **per_channel;
+ long nframes = ov_read_float(&vf, &per_channel,
+ frames_per_buffer,
+ &current_section);
+ long nbytes = nframes;
+ if (nframes > 0) {
+ vorbis_interleave(buffer,
+ (const float*const*)per_channel,
+ nframes, audio_format.channels);
+ nbytes *= frame_size;
+ }
+#endif
+
+ if (nbytes == OV_HOLE) /* bad packet */
+ nbytes = 0;
+ else if (nbytes <= 0)
/* break on EOF or other error */
break;
if (current_section != prev_section) {
- char **comments;
-
vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
@@ -248,7 +289,7 @@ vorbis_stream_decode(struct decoder *decoder,
break;
}
- comments = ov_comment(&vf, -1)->user_comments;
+ char **comments = ov_comment(&vf, -1)->user_comments;
vorbis_send_comments(decoder, input_stream, comments);
struct replay_gain_info rgi;
@@ -258,12 +299,13 @@ vorbis_stream_decode(struct decoder *decoder,
prev_section = current_section;
}
- if ((test = ov_bitrate_instant(&vf)) > 0)
- bitRate = test / 1000;
+ long test = ov_bitrate_instant(&vf);
+ if (test > 0)
+ kbit_rate = test / 1000;
cmd = decoder_data(decoder, input_stream,
- chunk, ret,
- bitRate);
+ buffer, nbytes,
+ kbit_rate);
} while (cmd != DECODE_COMMAND_STOP);
ov_clear(&vf);
@@ -306,9 +348,14 @@ static const char *const vorbis_mime_types[] = {
};
const struct decoder_plugin vorbis_decoder_plugin = {
- .name = "vorbis",
- .stream_decode = vorbis_stream_decode,
- .scan_stream = vorbis_scan_stream,
- .suffixes = vorbis_suffixes,
- .mime_types = vorbis_mime_types
+ "vorbis",
+ nullptr,
+ nullptr,
+ vorbis_stream_decode,
+ nullptr,
+ nullptr,
+ vorbis_scan_stream,
+ nullptr,
+ vorbis_suffixes,
+ vorbis_mime_types
};
diff --git a/src/decoder/VorbisDecoderPlugin.h b/src/decoder/VorbisDecoderPlugin.h
new file mode 100644
index 00000000..618c9ffd
--- /dev/null
+++ b/src/decoder/VorbisDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_VORBIS_H
+#define MPD_DECODER_VORBIS_H
+
+extern const struct decoder_plugin vorbis_decoder_plugin;
+
+#endif
diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/WavpackDecoderPlugin.cxx
index 9ebd0fcc..bac62d42 100644
--- a/src/decoder/wavpack_decoder_plugin.c
+++ b/src/decoder/WavpackDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,14 @@
*/
#include "config.h"
+#include "WavpackDecoderPlugin.hxx"
#include "decoder_api.h"
+#include "InputStream.hxx"
+
+extern "C" {
#include "audio_check.h"
-#include "path.h"
-#include "utils.h"
-#include "tag_table.h"
+}
+
#include "tag_handler.h"
#include "tag_ape.h"
@@ -30,7 +33,6 @@
#include <glib.h>
#include <assert.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,18 +55,18 @@ typedef void (*format_samples_t)(
static void
format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
{
- int32_t *src = buffer;
+ int32_t *src = (int32_t *)buffer;
switch (bytes_per_sample) {
case 1: {
- int8_t *dst = buffer;
+ int8_t *dst = (int8_t *)buffer;
/*
* The asserts like the following one are because we do the
* formatting of samples within a single buffer. The size
* of the output samples never can be greater than the size
* of the input ones. Otherwise we would have an overflow.
*/
- assert_static(sizeof(*dst) <= sizeof(*src));
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 8-bit samples */
while (count--) {
@@ -73,8 +75,8 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
break;
}
case 2: {
- uint16_t *dst = buffer;
- assert_static(sizeof(*dst) <= sizeof(*src));
+ uint16_t *dst = (uint16_t *)buffer;
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 16-bit samples */
while (count--) {
@@ -97,7 +99,7 @@ static void
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
uint32_t count)
{
- float *p = buffer;
+ float *p = (float *)buffer;
while (count--) {
*p /= (1 << 23);
@@ -359,7 +361,7 @@ static struct wavpack_input *
wpin(void *id)
{
assert(id);
- return id;
+ return (struct wavpack_input *)id;
}
static int32_t
@@ -441,14 +443,14 @@ wavpack_input_can_seek(void *id)
}
static WavpackStreamReader mpd_is_reader = {
- .read_bytes = wavpack_input_read_bytes,
- .get_pos = wavpack_input_get_pos,
- .set_pos_abs = wavpack_input_set_pos_abs,
- .set_pos_rel = wavpack_input_set_pos_rel,
- .push_back_byte = wavpack_input_push_back_byte,
- .get_length = wavpack_input_get_length,
- .can_seek = wavpack_input_can_seek,
- .write_bytes = NULL /* no need to write edited tags */
+ wavpack_input_read_bytes,
+ wavpack_input_get_pos,
+ wavpack_input_set_pos_abs,
+ wavpack_input_set_pos_rel,
+ wavpack_input_push_back_byte,
+ wavpack_input_get_length,
+ wavpack_input_can_seek,
+ nullptr /* no need to write edited tags */
};
static void
@@ -462,7 +464,7 @@ wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder,
static struct input_stream *
wavpack_open_wvc(struct decoder *decoder, const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
struct wavpack_input *wpi)
{
struct input_stream *is_wvc;
@@ -475,7 +477,7 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
* single files. utf8url is not absolute file path :/
*/
if (uri == NULL)
- return false;
+ return nullptr;
wvc_url = g_strconcat(uri, "c", NULL);
is_wvc = input_stream_open(wvc_url, mutex, cond, NULL);
@@ -515,7 +517,8 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
struct wavpack_input isp, isp_wvc;
bool can_seek = is->seekable;
- is_wvc = wavpack_open_wvc(decoder, is->uri, is->mutex, is->cond,
+ is_wvc = wavpack_open_wvc(decoder, is->uri.c_str(),
+ is->mutex, is->cond,
&isp_wvc);
if (is_wvc != NULL) {
open_flags |= OPEN_WVC;
@@ -587,10 +590,14 @@ static char const *const wavpack_mime_types[] = {
};
const struct decoder_plugin wavpack_decoder_plugin = {
- .name = "wavpack",
- .stream_decode = wavpack_streamdecode,
- .file_decode = wavpack_filedecode,
- .scan_file = wavpack_scan_file,
- .suffixes = wavpack_suffixes,
- .mime_types = wavpack_mime_types
+ "wavpack",
+ nullptr,
+ nullptr,
+ wavpack_streamdecode,
+ wavpack_filedecode,
+ wavpack_scan_file,
+ nullptr,
+ nullptr,
+ wavpack_suffixes,
+ wavpack_mime_types
};
diff --git a/src/decoder/WavpackDecoderPlugin.hxx b/src/decoder/WavpackDecoderPlugin.hxx
new file mode 100644
index 00000000..9ebe6354
--- /dev/null
+++ b/src/decoder/WavpackDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_WAVPACK_HXX
+#define MPD_DECODER_WAVPACK_HXX
+
+extern const struct decoder_plugin wavpack_decoder_plugin;
+
+#endif
diff --git a/src/decoder/XiphTags.c b/src/decoder/XiphTags.c
new file mode 100644
index 00000000..d55787b9
--- /dev/null
+++ b/src/decoder/XiphTags.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "XiphTags.h"
+
+const struct tag_table xiph_tags[] = {
+ { "tracknumber", TAG_TRACK },
+ { "discnumber", TAG_DISC },
+ { "album artist", TAG_ALBUM_ARTIST },
+ { NULL, TAG_NUM_OF_ITEM_TYPES }
+};
diff --git a/src/decoder/XiphTags.h b/src/decoder/XiphTags.h
new file mode 100644
index 00000000..22a4e220
--- /dev/null
+++ b/src/decoder/XiphTags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_XIPH_TAGS_H
+#define MPD_XIPH_TAGS_H
+
+#include "check.h"
+#include "tag_table.h"
+
+extern const struct tag_table xiph_tags[];
+
+#endif
diff --git a/src/decoder/audiofile_decoder_plugin.c b/src/decoder/audiofile_decoder_plugin.c
index b344795e..d2ceee8a 100644
--- a/src/decoder/audiofile_decoder_plugin.c
+++ b/src/decoder/audiofile_decoder_plugin.c
@@ -69,14 +69,14 @@ static AFfileoffset
audiofile_file_length(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->size;
+ return input_stream_get_size(is);
}
static AFfileoffset
audiofile_file_tell(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->offset;
+ return input_stream_get_offset(is);
}
static void
@@ -93,7 +93,7 @@ audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
if (input_stream_lock_seek(is, offset, whence, NULL)) {
- return is->offset;
+ return input_stream_get_offset(is);
} else {
return -1;
}
@@ -166,7 +166,7 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
char chunk[CHUNK_SIZE];
enum decoder_command cmd;
- if (!is->seekable) {
+ if (!input_stream_is_seekable(is)) {
g_warning("not seekable");
return;
}
@@ -194,7 +194,7 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
total_time = ((float)frame_count / (float)audio_format.sample_rate);
- bit_rate = (uint16_t)(is->size * 8.0 / total_time / 1000.0 + 0.5);
+ bit_rate = (uint16_t)(input_stream_get_size(is) * 8.0 / total_time / 1000.0 + 0.5);
fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1);
diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c
index 84471fb3..44d12d89 100644
--- a/src/decoder/dsdiff_decoder_plugin.c
+++ b/src/decoder/dsdiff_decoder_plugin.c
@@ -52,10 +52,23 @@ struct dsdiff_chunk_header {
uint32_t size_high, size_low;
};
+/** struct for DSDIFF native Artist and Title tags */
+struct dsdiff_native_tag {
+ uint32_t size;
+};
+
struct dsdiff_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
+ /** offset for artist tag */
+ goffset diar_offset;
+ /** offset for title tag */
+ goffset diti_offset;
};
static bool lsbitfirst;
@@ -115,12 +128,12 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
goffset end_offset)
{
struct dsdiff_chunk_header header;
- while ((goffset)(is->offset + sizeof(header)) <= end_offset) {
+ while ((goffset)(input_stream_get_offset(is) + sizeof(header)) <= end_offset) {
if (!dsdiff_read_chunk_header(decoder, is, &header))
return false;
- goffset chunk_end_offset =
- is->offset + dsdiff_chunk_size(&header);
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + dsdiff_chunk_size(&header);
if (chunk_end_offset > end_offset)
return false;
@@ -161,7 +174,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
}
}
- return is->offset == end_offset;
+ return input_stream_get_offset(is) == end_offset;
}
/**
@@ -173,7 +186,7 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
const struct dsdiff_chunk_header *prop_header)
{
uint64_t prop_size = dsdiff_chunk_size(prop_header);
- goffset end_offset = is->offset + prop_size;
+ goffset end_offset = input_stream_get_offset(is) + prop_size;
struct dsdlib_id prop_id;
if (prop_size < sizeof(prop_id) ||
@@ -187,6 +200,127 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
return dsdlib_skip_to(decoder, is, end_offset);
}
+static void
+dsdiff_handle_native_tag(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset,
+ enum tag_type type)
+{
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct dsdiff_native_tag metatag;
+
+ if (!dsdlib_read(NULL, is, &metatag, sizeof(metatag)))
+ return;
+
+ uint32_t length = GUINT32_FROM_BE(metatag.size);
+
+ /* Check and limit size of the tag to prevent a stack overflow */
+ if (length == 0 || length > 60)
+ return;
+
+ char string[length];
+ char *label;
+ label = string;
+
+ if (!dsdlib_read(NULL, is, label, (size_t)length))
+ return;
+
+ string[length] = '\0';
+ tag_handler_invoke_tag(handler, handler_ctx, type, label);
+ return;
+}
+
+/**
+ * Read and parse additional metadata chunks for tagging purposes. By default
+ * dsdiff files only support equivalents for artist and title but some of the
+ * extract tools add an id3 tag to provide more tags. If such id3 is found
+ * this will be used for tagging otherwise the native tags (if any) will be
+ * used
+ */
+
+static bool
+dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+ struct dsdiff_metadata *metadata,
+ struct dsdiff_chunk_header *chunk_header,
+ const struct tag_handler *handler,
+ void *handler_ctx)
+{
+
+ /* skip from DSD data to next chunk header */
+ if (!dsdlib_skip(decoder, is, metadata->chunk_size))
+ return false;
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+
+#ifdef HAVE_ID3TAG
+ metadata->id3_size = 0;
+#endif
+
+ /* Now process all the remaining chunk headers in the stream
+ and record their position and size */
+
+ const goffset size = input_stream_get_size(is);
+ while (input_stream_get_offset(is) < size) {
+ uint64_t chunk_size = dsdiff_chunk_size(chunk_header);
+
+ /* DIIN chunk, is directly followed by other chunks */
+ if (dsdlib_id_equals(&chunk_header->id, "DIIN"))
+ chunk_size = 0;
+
+ /* DIAR chunk - DSDIFF native tag for Artist */
+ if (dsdlib_id_equals(&chunk_header->id, "DIAR")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diar_offset = input_stream_get_offset(is);
+ }
+
+ /* DITI chunk - DSDIFF native tag for Title */
+ if (dsdlib_id_equals(&chunk_header->id, "DITI")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diti_offset = input_stream_get_offset(is);
+ }
+#ifdef HAVE_ID3TAG
+ /* 'ID3 ' chunk, offspec. Used by sacdextract */
+ if (dsdlib_id_equals(&chunk_header->id, "ID3 ")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->id3_offset = input_stream_get_offset(is);
+ metadata->id3_size = chunk_size;
+ }
+#endif
+ if (chunk_size != 0) {
+ if (!dsdlib_skip(decoder, is, chunk_size))
+ break;
+ }
+
+ if (input_stream_get_offset(is) < size) {
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+ }
+ chunk_size = 0;
+ }
+ /* done processing chunk headers, process tags if any */
+
+#ifdef HAVE_ID3TAG
+ if (metadata->id3_offset != 0)
+ {
+ /* a ID3 tag has preference over the other tags, do not process
+ other tags if we have one */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset);
+ return true;
+ }
+#endif
+
+ if (metadata->diar_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diar_offset, TAG_ARTIST);
+
+ if (metadata->diti_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diti_offset, TAG_TITLE);
+ return true;
+}
+
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
@@ -221,7 +355,8 @@ dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
/* ignore unknown chunk */
uint64_t chunk_size;
chunk_size = dsdiff_chunk_size(chunk_header);
- goffset chunk_end_offset = is->offset + chunk_size;
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + chunk_size;
if (!dsdlib_skip_to(decoder, is, chunk_end_offset))
return false;
@@ -374,6 +509,10 @@ dsdiff_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+ /* Read additional metadata and created tags if available */
+ dsdiff_read_metadata_extra(NULL, is, &metadata, &chunk_header,
+ handler, handler_ctx);
+
return true;
}
diff --git a/src/decoder/dsdlib.c b/src/decoder/dsdlib.c
index 3df9497c..d3043fb0 100644
--- a/src/decoder/dsdlib.c
+++ b/src/decoder/dsdlib.c
@@ -27,12 +27,18 @@
#include "dsf_decoder_plugin.h"
#include "decoder_api.h"
#include "util/bit_reverse.h"
+#include "tag_handler.h"
+#include "tag_id3.h"
#include "dsdlib.h"
#include "dsdiff_decoder_plugin.h"
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
bool
dsdlib_id_equals(const struct dsdlib_id *id, const char *s)
{
@@ -58,24 +64,24 @@ bool
dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
goffset offset)
{
- if (is->seekable)
+ if (input_stream_is_seekable(is))
return input_stream_seek(is, offset, SEEK_SET, NULL);
- if (is->offset > offset)
+ if (input_stream_get_offset(is) > offset)
return false;
char buffer[8192];
- while (is->offset < offset) {
+ while (input_stream_get_offset(is) < offset) {
size_t length = sizeof(buffer);
- if (offset - is->offset < (goffset)length)
- length = offset - is->offset;
+ if (offset - input_stream_get_offset(is) < (goffset)length)
+ length = offset - input_stream_get_offset(is);
size_t nbytes = decoder_read(decoder, is, buffer, length);
if (nbytes == 0)
return false;
}
- assert(is->offset == offset);
+ assert(input_stream_get_offset(is) == offset);
return true;
}
@@ -91,7 +97,7 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
if (delta == 0)
return true;
- if (is->seekable)
+ if (input_stream_is_seekable(is))
return input_stream_seek(is, delta, SEEK_CUR, NULL);
char buffer[8192];
@@ -110,3 +116,55 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
return true;
}
+/**
+ * Add tags from ID3 tag. All tags commonly found in the ID3 tags of
+ * DSF and DSDIFF files are imported
+ */
+
+#ifdef HAVE_ID3TAG
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset)
+{
+ assert(tagoffset >= 0);
+
+ if (tagoffset == 0)
+ return;
+
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct id3_tag *id3_tag = NULL;
+ id3_length_t count;
+
+ /* Prevent broken files causing problems */
+ const goffset size = input_stream_get_size(is);
+ const goffset offset = input_stream_get_offset(is);
+ if (offset >= size)
+ return;
+
+ count = size - offset;
+
+ /* Check and limit id3 tag size to prevent a stack overflow */
+ if (count == 0 || count > 4096)
+ return;
+
+ id3_byte_t dsdid3[count];
+ id3_byte_t *dsdid3data;
+ dsdid3data = dsdid3;
+
+ if (!dsdlib_read(NULL, is, dsdid3data, count))
+ return;
+
+ id3_tag = id3_tag_parse(dsdid3data, count);
+ if (id3_tag == NULL)
+ return;
+
+ scan_id3_tag(id3_tag, handler, handler_ctx);
+
+ id3_tag_delete(id3_tag);
+
+ return;
+}
+#endif
diff --git a/src/decoder/dsdlib.h b/src/decoder/dsdlib.h
index d9675f5f..0912740c 100644
--- a/src/decoder/dsdlib.h
+++ b/src/decoder/dsdlib.h
@@ -39,4 +39,9 @@ bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta);
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset);
+
#endif
diff --git a/src/decoder/dsf_decoder_plugin.c b/src/decoder/dsf_decoder_plugin.c
index c0107eb3..23576a62 100644
--- a/src/decoder/dsf_decoder_plugin.c
+++ b/src/decoder/dsf_decoder_plugin.c
@@ -45,6 +45,10 @@ struct dsf_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
};
struct dsf_header {
@@ -57,6 +61,7 @@ struct dsf_header {
/** pointer to id3v2 metadata, should be at the end of the file */
uint32_t pmeta_low, pmeta_high;
};
+
/** DSF file fmt chunk */
struct dsf_fmt_chunk {
@@ -109,6 +114,12 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
if (sizeof(dsf_header) != chunk_size)
return false;
+#ifdef HAVE_ID3TAG
+ uint64_t metadata_offset;
+ metadata_offset = (((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_high)) << 32) |
+ ((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_low));
+#endif
+
/* read the 'fmt ' chunk of the DSF file */
struct dsf_fmt_chunk dsf_fmt_chunk;
if (!dsdlib_read(decoder, is, &dsf_fmt_chunk, sizeof(dsf_fmt_chunk)) ||
@@ -153,9 +164,20 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
data_size -= sizeof(data_chunk);
metadata->chunk_size = data_size;
+ /* data_size cannot be bigger or equal to total file size */
+ const uint64_t size = (uint64_t)input_stream_get_size(is);
+ if (data_size >= size)
+ return false;
+
metadata->channels = (unsigned) dsf_fmt_chunk.channelnum;
metadata->sample_rate = samplefreq;
-
+#ifdef HAVE_ID3TAG
+ /* metada_offset cannot be bigger then or equal to total file size */
+ if (metadata_offset >= size)
+ metadata->id3_offset = 0;
+ else
+ metadata->id3_offset = (goffset) metadata_offset;
+#endif
/* check bits per sample format, determine if bitreverse is needed */
metadata->bitreverse = dsf_fmt_chunk.bitssample == 1;
return true;
@@ -285,7 +307,7 @@ dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
decoder_initialized(decoder, &audio_format, false, songtime);
if (!dsf_decode_chunk(decoder, is, metadata.channels,
- metadata.chunk_size,
+ chunk_size,
metadata.bitreverse))
return;
}
@@ -316,6 +338,10 @@ dsf_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+#ifdef HAVE_ID3TAG
+ /* Add available tags from the ID3 tag */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
+#endif
return true;
}
diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/faad_decoder_plugin.c
index 911f033b..a7ece93f 100644
--- a/src/decoder/faad_decoder_plugin.c
+++ b/src/decoder/faad_decoder_plugin.c
@@ -23,16 +23,18 @@
#include "audio_check.h"
#include "tag_handler.h"
-#define AAC_MAX_CHANNELS 6
+#include <neaacdec.h>
+
+#include <glib.h>
#include <assert.h>
#include <unistd.h>
-#include <faad.h>
-#include <glib.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "faad"
+#define AAC_MAX_CHANNELS 6
+
static const unsigned adts_sample_rates[] =
{ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0
@@ -175,7 +177,8 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
size_t length;
bool success;
- fileread = is->size >= 0 ? is->size : 0;
+ const goffset size = input_stream_get_size(is);
+ fileread = size >= 0 ? size : 0;
decoder_buffer_fill(buffer);
data = decoder_buffer_read(buffer, &length);
@@ -201,7 +204,7 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
return -1;
}
- if (is->seekable && length >= 2 &&
+ if (input_stream_is_seekable(is) && length >= 2 &&
data[0] == 0xFF && ((data[1] & 0xF6) == 0xF0)) {
/* obtain the duration from the ADTS header */
float song_length = adts_song_duration(buffer);
@@ -238,11 +241,11 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
}
/**
- * Wrapper for faacDecInit() which works around some API
+ * Wrapper for NeAACDecInit() which works around some API
* inconsistencies in libfaad.
*/
static bool
-faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
+faad_decoder_init(NeAACDecHandle decoder, struct decoder_buffer *buffer,
struct audio_format *audio_format, GError **error_r)
{
union {
@@ -270,10 +273,8 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
return false;
}
- nbytes = faacDecInit(decoder, u.out,
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
+ nbytes = NeAACDecInit(decoder, u.out,
length,
-#endif
sample_rate_p, &channels);
if (nbytes < 0) {
g_set_error(error_r, faad_decoder_quark(), 0,
@@ -288,12 +289,12 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
}
/**
- * Wrapper for faacDecDecode() which works around some API
+ * Wrapper for NeAACDecDecode() which works around some API
* inconsistencies in libfaad.
*/
static const void *
-faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
- faacDecFrameInfo *frame_info)
+faad_decoder_decode(NeAACDecHandle decoder, struct decoder_buffer *buffer,
+ NeAACDecFrameInfo *frame_info)
{
union {
/* deconst hack for libfaad */
@@ -301,20 +302,13 @@ faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
void *out;
} u;
size_t length;
- void *result;
u.in = decoder_buffer_read(buffer, &length);
if (u.in == NULL)
return NULL;
- result = faacDecDecode(decoder, frame_info,
- u.out
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- , length
-#endif
- );
-
- return result;
+ return NeAACDecDecode(decoder, frame_info,
+ u.out, length);
}
/**
@@ -327,8 +321,6 @@ faad_get_file_time_float(struct input_stream *is)
{
struct decoder_buffer *buffer;
float length;
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
buffer = decoder_buffer_new(NULL, is,
FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
@@ -338,11 +330,12 @@ faad_get_file_time_float(struct input_stream *is)
bool ret;
struct audio_format audio_format;
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
decoder_buffer_fill(buffer);
@@ -350,7 +343,7 @@ faad_get_file_time_float(struct input_stream *is)
if (ret)
length = 0;
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
decoder_buffer_free(buffer);
@@ -380,9 +373,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
{
GError *error = NULL;
float total_time = 0;
- faacDecHandle decoder;
struct audio_format audio_format;
- faacDecConfigurationPtr config;
bool ret;
uint16_t bit_rate = 0;
struct decoder_buffer *buffer;
@@ -394,17 +385,14 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* create the libfaad decoder */
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
while (!decoder_buffer_is_full(buffer) &&
!input_stream_lock_eof(is) &&
@@ -419,7 +407,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
if (!ret) {
g_warning("%s", error->message);
g_error_free(error);
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
return;
}
@@ -432,7 +420,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
do {
size_t frame_size;
const void *decoded;
- faacDecFrameInfo frame_info;
+ NeAACDecFrameInfo frame_info;
/* find the next frame */
@@ -447,7 +435,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
if (frame_info.error > 0) {
g_warning("error decoding AAC stream: %s\n",
- faacDecGetErrorMessage(frame_info.error));
+ NeAACDecGetErrorMessage(frame_info.error));
break;
}
@@ -457,14 +445,12 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
break;
}
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
if (frame_info.samplerate != audio_format.sample_rate) {
g_warning("sample rate changed from %u to %lu",
audio_format.sample_rate,
(unsigned long)frame_info.samplerate);
break;
}
-#endif
decoder_buffer_consume(buffer, frame_info.bytesconsumed);
@@ -485,7 +471,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* cleanup */
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
static bool
diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h
deleted file mode 100644
index 9a30acc2..00000000
--- a/src/decoder/flac_compat.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Common data structures and functions used by FLAC and OggFLAC
- */
-
-#ifndef MPD_FLAC_COMPAT_H
-#define MPD_FLAC_COMPAT_H
-
-#include <FLAC/export.h>
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-# include <FLAC/seekable_stream_decoder.h>
-
-/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been
- merged into the StreamDecoder. The following macros try to emulate
- the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls
- to the old SeekableStreamDecoder API. */
-
-#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
-#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
-#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position
-#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
-#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
-#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
-#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
-#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
-#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
-
-#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
-
-typedef unsigned flac_read_status_size_t;
-
-#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
-#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
-
-#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
-#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
-#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-
-#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
-#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
-#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-
-#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-
-typedef enum {
- FLAC__STREAM_DECODER_INIT_STATUS_OK,
- FLAC__STREAM_DECODER_INIT_STATUS_ERROR,
-} FLAC__StreamDecoderInitStatus;
-
-static inline FLAC__StreamDecoderInitStatus
-FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder,
- FLAC__SeekableStreamDecoderReadCallback read_cb,
- FLAC__SeekableStreamDecoderSeekCallback seek_cb,
- FLAC__SeekableStreamDecoderTellCallback tell_cb,
- FLAC__SeekableStreamDecoderLengthCallback length_cb,
- FLAC__SeekableStreamDecoderEofCallback eof_cb,
- FLAC__SeekableStreamDecoderWriteCallback write_cb,
- FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
- FLAC__SeekableStreamDecoderErrorCallback error_cb,
- void *data)
-{
- return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) &&
- FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) &&
- FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) &&
- FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) &&
- FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) &&
- FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
- FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) &&
- FLAC__seekable_stream_decoder_set_client_data(decoder, data) &&
- FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK
- ? FLAC__STREAM_DECODER_INIT_STATUS_OK
- : FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-}
-
-#else /* FLAC_API_VERSION_CURRENT > 7 */
-
-# include <FLAC/stream_decoder.h>
-
-# define flac_init(a,b,c,d,e,f,g,h,i,j) \
- (FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
- == FLAC__STREAM_DECODER_INIT_STATUS_OK)
-
-typedef size_t flac_read_status_size_t;
-
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
-#endif /* _FLAC_COMMON_H */
diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h
deleted file mode 100644
index 3c463d5d..00000000
--- a/src/decoder/flac_metadata.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_FLAC_METADATA_H
-#define MPD_FLAC_METADATA_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <FLAC/metadata.h>
-
-struct tag_handler;
-struct tag;
-struct replay_gain_info;
-
-static inline unsigned
-flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
-{
- assert(stream_info->sample_rate > 0);
-
- return (stream_info->total_samples + stream_info->sample_rate - 1) /
- stream_info->sample_rate;
-}
-
-bool
-flac_parse_replay_gain(struct replay_gain_info *rgi,
- const FLAC__StreamMetadata *block);
-
-bool
-flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
- const FLAC__StreamMetadata *block);
-
-void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment);
-
-void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
- const struct tag_handler *handler, void *handler_ctx);
-
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx);
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum);
-
-#endif
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 62c37164..4e2e0331 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -76,9 +76,9 @@ mad_fixed_to_24_sample(mad_fixed_t sample)
sample = sample + (1L << (MAD_F_FRACBITS - bits));
/* clip */
- if (sample > MAX)
+ if (gcc_unlikely(sample > MAX))
sample = MAX;
- else if (sample < MIN)
+ else if (gcc_unlikely(sample < MIN))
sample = MIN;
/* quantize */
@@ -359,15 +359,14 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
data->found_replay_gain = true;
}
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
}
@@ -756,7 +755,7 @@ mp3_frame_duration(const struct mad_frame *frame)
static goffset
mp3_this_frame_offset(const struct mp3_data *data)
{
- goffset offset = data->input_stream->offset;
+ goffset offset = input_stream_get_offset(data->input_stream);
if (data->stream.this_frame != NULL)
offset -= data->stream.bufend - data->stream.this_frame;
@@ -769,7 +768,8 @@ mp3_this_frame_offset(const struct mp3_data *data)
static goffset
mp3_rest_including_this_frame(const struct mp3_data *data)
{
- return data->input_stream->size - mp3_this_frame_offset(data);
+ return input_stream_get_size(data->input_stream)
+ - mp3_this_frame_offset(data);
}
/**
@@ -842,7 +842,7 @@ mp3_decode_first_frame(struct mp3_data *data, struct tag **tag)
if (parse_lame(&lame, &ptr, &bitlen)) {
if (gapless_playback &&
- data->input_stream->seekable) {
+ input_stream_is_seekable(data->input_stream)) {
data->drop_start_samples = lame.encoder_delay +
DECODERDELAY;
data->drop_end_samples = lame.encoder_padding;
@@ -1082,7 +1082,7 @@ mp3_read(struct mp3_data *data)
if (cmd == DECODE_COMMAND_SEEK) {
unsigned long j;
- assert(data->input_stream->seekable);
+ assert(input_stream_is_seekable(data->input_stream));
j = mp3_time_to_frame(data,
decoder_seek_where(decoder));
@@ -1164,7 +1164,8 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
}
decoder_initialized(decoder, &audio_format,
- data.input_stream->seekable, data.total_time);
+ input_stream_is_seekable(input_stream),
+ data.total_time);
if (tag != NULL) {
decoder_tag(decoder, input_stream, tag);
diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/modplug_decoder_plugin.c
index 21ee79e7..bc3d8176 100644
--- a/src/decoder/modplug_decoder_plugin.c
+++ b/src/decoder/modplug_decoder_plugin.c
@@ -41,19 +41,21 @@ static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is
GByteArray *bdatas;
size_t ret;
- if (is->size == 0) {
+ const goffset size = input_stream_get_size(is);
+
+ if (size == 0) {
g_warning("file is empty");
return NULL;
}
- if (is->size > MODPLUG_FILE_LIMIT) {
+ if (size > MODPLUG_FILE_LIMIT) {
g_warning("file too large");
return NULL;
}
//known/unknown size, preallocate array, lets read in chunks
- if (is->size > 0) {
- bdatas = g_byte_array_sized_new(is->size);
+ if (size > 0) {
+ bdatas = g_byte_array_sized_new(size);
} else {
bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK);
}
@@ -126,7 +128,8 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
assert(audio_format_valid(&audio_format));
decoder_initialized(decoder, &audio_format,
- is->seekable, ModPlug_GetLength(f) / 1000.0);
+ input_stream_is_seekable(is),
+ ModPlug_GetLength(f) / 1000.0);
do {
ret = ModPlug_Read(f, audio_buffer, MODPLUG_FRAME_SIZE);
diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c
deleted file mode 100644
index ca78a22d..00000000
--- a/src/decoder/mp4ff_decoder_plugin.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "decoder_api.h"
-#include "audio_check.h"
-#include "tag_table.h"
-#include "tag_handler.h"
-
-#include <glib.h>
-
-#include <mp4ff.h>
-#include <faad.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mp4ff"
-
-/* all code here is either based on or copied from FAAD2's frontend code */
-
-struct mp4ff_input_stream {
- mp4ff_callback_t callback;
-
- struct decoder *decoder;
- struct input_stream *input_stream;
-};
-
-static int
-mp4_get_aac_track(mp4ff_t * infile, faacDecHandle decoder,
- uint32_t *sample_rate, unsigned char *channels_r)
-{
-#ifdef HAVE_FAAD_LONG
- /* neaacdec.h declares all arguments as "unsigned long", but
- internally expects uint32_t pointers. To avoid gcc
- warnings, use this workaround. */
- unsigned long *sample_rate_r = (unsigned long*)sample_rate;
-#else
- uint32_t *sample_rate_r = sample_rate;
-#endif
- int i, rc;
- int num_tracks = mp4ff_total_tracks(infile);
-
- for (i = 0; i < num_tracks; i++) {
- unsigned char *buff = NULL;
- unsigned int buff_size = 0;
-
- if (mp4ff_get_track_type(infile, i) != 1)
- /* not an audio track */
- continue;
-
- if (decoder == NULL)
- /* have don't have a decoder to initialize -
- we're done now, because we found an audio
- track */
- return i;
-
- mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
- if (buff == NULL)
- continue;
-
- rc = faacDecInit2(decoder, buff, buff_size,
- sample_rate_r, channels_r);
- free(buff);
-
- if (rc >= 0)
- /* found a valid AAC track */
- return i;
- }
-
- /* can't decode this */
- return -1;
-}
-
-static uint32_t
-mp4_read(void *user_data, void *buffer, uint32_t length)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- if (length == 0)
- /* libmp4ff is known to attempt to read 0 bytes - make
- this a special case, because the input_stream API
- would not allow this */
- return 0;
-
- return decoder_read(mis->decoder, mis->input_stream, buffer, length);
-}
-
-static uint32_t
-mp4_seek(void *user_data, uint64_t position)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- return input_stream_lock_seek(mis->input_stream, position, SEEK_SET,
- NULL)
- ? 0 : -1;
-}
-
-static const mp4ff_callback_t mpd_mp4ff_callback = {
- .read = mp4_read,
- .seek = mp4_seek,
-};
-
-static mp4ff_t *
-mp4ff_input_stream_open(struct mp4ff_input_stream *mis,
- struct decoder *decoder,
- struct input_stream *input_stream)
-{
- mis->callback = mpd_mp4ff_callback;
- mis->callback.user_data = mis;
- mis->decoder = decoder;
- mis->input_stream = input_stream;
-
- return mp4ff_open_read(&mis->callback);
-}
-
-static faacDecHandle
-mp4_faad_new(mp4ff_t *mp4fh, int *track_r, struct audio_format *audio_format)
-{
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
- int track;
- uint32_t sample_rate;
- unsigned char channels;
- GError *error = NULL;
-
- decoder = faacDecOpen();
-
- config = faacDecGetCurrentConfiguration(decoder);
- config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
- config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
- config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
-
- track = mp4_get_aac_track(mp4fh, decoder, &sample_rate, &channels);
- if (track < 0) {
- g_warning("No AAC track found");
- faacDecClose(decoder);
- return NULL;
- }
-
- if (!audio_format_init_checked(audio_format, sample_rate,
- SAMPLE_FORMAT_S16, channels,
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
- faacDecClose(decoder);
- return NULL;
- }
-
- *track_r = track;
-
- return decoder;
-}
-
-static void
-mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
-{
- struct mp4ff_input_stream mis;
- mp4ff_t *mp4fh;
- int32_t track;
- float file_time, total_time;
- int32_t scale;
- faacDecHandle decoder;
- struct audio_format audio_format;
- faacDecFrameInfo frame_info;
- unsigned char *mp4_buffer;
- unsigned int mp4_buffer_size;
- long sample_id;
- long num_samples;
- long dur;
- unsigned int sample_count;
- char *sample_buffer;
- size_t sample_buffer_length;
- unsigned int initial = 1;
- float *seek_table;
- long seek_table_end = -1;
- bool seek_position_found = false;
- long offset;
- uint16_t bit_rate = 0;
- bool seeking = false;
- double seek_where = 0;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- mp4fh = mp4ff_input_stream_open(&mis, mpd_decoder, input_stream);
- if (!mp4fh) {
- g_warning("Input does not appear to be a mp4 stream.\n");
- return;
- }
-
- decoder = mp4_faad_new(mp4fh, &track, &audio_format);
- if (decoder == NULL) {
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
-
- if (scale < 0) {
- g_warning("Error getting audio format of mp4 AAC track.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
- total_time = ((float)file_time) / scale;
-
- num_samples = mp4ff_num_samples(mp4fh, track);
- if (num_samples > (long)(G_MAXINT / sizeof(float))) {
- g_warning("Integer overflow.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = 0.0;
-
- seek_table = input_stream->seekable
- ? g_malloc(sizeof(float) * num_samples)
- : NULL;
-
- decoder_initialized(mpd_decoder, &audio_format,
- input_stream->seekable,
- total_time);
-
- for (sample_id = 0;
- sample_id < num_samples && cmd != DECODE_COMMAND_STOP;
- sample_id++) {
- if (cmd == DECODE_COMMAND_SEEK) {
- assert(seek_table != NULL);
-
- seeking = true;
- seek_where = decoder_seek_where(mpd_decoder);
- }
-
- if (seeking && seek_table_end > 1 &&
- seek_table[seek_table_end] >= seek_where) {
- int i = 2;
-
- assert(seek_table != NULL);
-
- while (seek_table[i] < seek_where)
- i++;
- sample_id = i - 1;
- file_time = seek_table[sample_id];
- }
-
- dur = mp4ff_get_sample_duration(mp4fh, track, sample_id);
- offset = mp4ff_get_sample_offset(mp4fh, track, sample_id);
-
- if (seek_table != NULL && sample_id > seek_table_end) {
- seek_table[sample_id] = file_time;
- seek_table_end = sample_id;
- }
-
- if (sample_id == 0)
- dur = 0;
- if (offset > dur)
- dur = 0;
- else
- dur -= offset;
- file_time += ((float)dur) / scale;
-
- if (seeking && file_time >= seek_where)
- seek_position_found = true;
-
- if (seeking && seek_position_found) {
- seek_position_found = false;
- seeking = 0;
- decoder_command_finished(mpd_decoder);
- }
-
- if (seeking)
- continue;
-
- if (mp4ff_read_sample(mp4fh, track, sample_id, &mp4_buffer,
- &mp4_buffer_size) == 0)
- break;
-
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer,
- mp4_buffer_size);
-#else
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer);
-#endif
-
- free(mp4_buffer);
-
- if (frame_info.error > 0) {
- g_warning("faad2 error: %s\n",
- faacDecGetErrorMessage(frame_info.error));
- break;
- }
-
- if (frame_info.channels != audio_format.channels) {
- g_warning("channel count changed from %u to %u",
- audio_format.channels, frame_info.channels);
- break;
- }
-
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
- if (frame_info.samplerate != audio_format.sample_rate) {
- g_warning("sample rate changed from %u to %lu",
- audio_format.sample_rate,
- (unsigned long)frame_info.samplerate);
- break;
- }
-#endif
-
- if (audio_format.channels * (unsigned long)(dur + offset) > frame_info.samples) {
- dur = frame_info.samples / audio_format.channels;
- offset = 0;
- }
-
- sample_count = (unsigned long)(dur * audio_format.channels);
-
- if (sample_count > 0) {
- initial = 0;
- bit_rate = frame_info.bytesconsumed * 8.0 *
- frame_info.channels * scale /
- frame_info.samples / 1000 + 0.5;
- }
-
- sample_buffer_length = sample_count * 2;
-
- sample_buffer += offset * audio_format.channels * 2;
-
- cmd = decoder_data(mpd_decoder, input_stream,
- sample_buffer, sample_buffer_length,
- bit_rate);
- }
-
- g_free(seek_table);
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
-}
-
-static const struct tag_table mp4ff_tags[] = {
- { "album artist", TAG_ALBUM_ARTIST },
- { "writer", TAG_COMPOSER },
- { "band", TAG_PERFORMER },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
-static enum tag_type
-mp4ff_tag_name_parse(const char *name)
-{
- enum tag_type type = tag_table_lookup_i(mp4ff_tags, name);
- if (type == TAG_NUM_OF_ITEM_TYPES)
- type = tag_name_parse_i(name);
-
- if (g_ascii_strcasecmp(name, "albumartist") == 0 ||
- g_ascii_strcasecmp(name, "album_artist") == 0)
- return TAG_ALBUM_ARTIST;
-
- return type;
-}
-
-static bool
-mp4ff_scan_stream(struct input_stream *is,
- const struct tag_handler *handler, void *handler_ctx)
-{
- struct mp4ff_input_stream mis;
- int32_t track;
- int32_t file_time;
- int32_t scale;
- int i;
-
- mp4ff_t *mp4fh = mp4ff_input_stream_open(&mis, NULL, is);
- if (mp4fh == NULL)
- return false;
-
- track = mp4_get_aac_track(mp4fh, NULL, NULL, NULL);
- if (track < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
- if (scale < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- tag_handler_invoke_duration(handler, handler_ctx,
- ((float)file_time) / scale + 0.5);
-
- for (i = 0; i < mp4ff_meta_get_num_items(mp4fh); i++) {
- char *item;
- char *value;
-
- mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
-
- tag_handler_invoke_pair(handler, handler_ctx, item, value);
-
- enum tag_type type = mp4ff_tag_name_parse(item);
- if (type != TAG_NUM_OF_ITEM_TYPES)
- tag_handler_invoke_tag(handler, handler_ctx,
- type, value);
-
- free(item);
- free(value);
- }
-
- mp4ff_close(mp4fh);
-
- return true;
-}
-
-static const char *const mp4_suffixes[] = {
- "m4a",
- "m4b",
- "mp4",
- NULL
-};
-
-static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
-
-const struct decoder_plugin mp4ff_decoder_plugin = {
- .name = "mp4ff",
- .stream_decode = mp4_decode,
- .scan_stream = mp4ff_scan_stream,
- .suffixes = mp4_suffixes,
- .mime_types = mp4_mime_types,
-};
diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/mpcdec_decoder_plugin.c
index d4768b35..77db2416 100644
--- a/src/decoder/mpcdec_decoder_plugin.c
+++ b/src/decoder/mpcdec_decoder_plugin.c
@@ -70,7 +70,7 @@ mpc_tell_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return (long)(data->is->offset);
+ return (long)input_stream_get_offset(data->is);
}
static mpc_bool_t
@@ -78,7 +78,7 @@ mpc_canseek_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return data->is->seekable;
+ return input_stream_is_seekable(data->is);
}
static mpc_int32_t
@@ -86,7 +86,7 @@ mpc_getsize_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return data->is->size;
+ return input_stream_get_size(data->is);
}
/* this _looks_ performance-critical, don't de-inline -- eric */
@@ -222,7 +222,7 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
decoder_replay_gain(mpd_decoder, &replay_gain_info);
decoder_initialized(mpd_decoder, &audio_format,
- is->seekable,
+ input_stream_is_seekable(is),
mpc_streaminfo_get_length(&info));
do {
diff --git a/src/decoder/pcm_decoder_plugin.c b/src/decoder/pcm_decoder_plugin.c
index fc7dffc0..d529cef5 100644
--- a/src/decoder/pcm_decoder_plugin.c
+++ b/src/decoder/pcm_decoder_plugin.c
@@ -38,8 +38,9 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
.channels = 2,
};
- const bool reverse_endian = is->mime != NULL &&
- strcmp(is->mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
+ const char *const mime = input_stream_get_mime_type(is);
+ const bool reverse_endian = mime != NULL &&
+ strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
GError *error = NULL;
enum decoder_command cmd;
@@ -47,10 +48,12 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
double time_to_size = audio_format_time_to_size(&audio_format);
float total_time = -1;
- if (is->size >= 0)
- total_time = is->size / time_to_size;
+ const goffset size = input_stream_get_size(is);
+ if (size >= 0)
+ total_time = size / time_to_size;
- decoder_initialized(decoder, &audio_format, is->seekable, total_time);
+ decoder_initialized(decoder, &audio_format,
+ input_stream_is_seekable(is), total_time);
do {
char buffer[4096];
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f17..175d2ee7 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -18,9 +18,9 @@
*/
#include "config.h"
+#include "../decoder_api.h"
extern "C" {
-#include "../decoder_api.h"
#include "tag_handler.h"
}
@@ -104,7 +104,7 @@ sidplay_init(const struct config_param *param)
return true;
}
-void
+static void
sidplay_finish()
{
g_pattern_spec_free(path_with_subtune);
@@ -136,7 +136,7 @@ get_container_name(const char *path_fs)
* returns tune number from file.sid/tune_xxx.sid style path or 1 if
* no subtune is appended
*/
-static int
+static unsigned
get_song_num(const char *path_fs)
{
if(g_pattern_match(path_with_subtune,
@@ -172,7 +172,7 @@ get_song_length(const char *path_fs)
char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum);
- int song_num=get_song_num(path_fs);
+ const unsigned song_num = get_song_num(path_fs);
gsize num_items;
gchar **values=g_key_file_get_string_list(songlength_database,
@@ -330,7 +330,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
decoder_command_finished(decoder);
}
- if (song_len > 0 && player.time() >= song_len)
+ if (song_len > 0 && player.time() >= (unsigned)song_len)
break;
} while (cmd != DECODE_COMMAND_STOP);
diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c
index 8dd98236..e70a2dc2 100644
--- a/src/decoder/sndfile_decoder_plugin.c
+++ b/src/decoder/sndfile_decoder_plugin.c
@@ -32,7 +32,7 @@ sndfile_vio_get_filelen(void *user_data)
{
const struct input_stream *is = user_data;
- return is->size;
+ return input_stream_get_size(is);
}
static sf_count_t
@@ -45,7 +45,7 @@ sndfile_vio_seek(sf_count_t offset, int whence, void *user_data)
if (!success)
return -1;
- return is->offset;
+ return input_stream_get_offset(is);
}
static sf_count_t
@@ -79,7 +79,7 @@ sndfile_vio_tell(void *user_data)
{
const struct input_stream *is = user_data;
- return is->offset;
+ return input_stream_get_offset(is);
}
/**
diff --git a/src/decoder_api.h b/src/decoder_api.h
index 6e011c39..3f84ca8b 100644
--- a/src/decoder_api.h
+++ b/src/decoder_api.h
@@ -38,6 +38,10 @@
#include <stdbool.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Notify the player thread that it has finished initialization and
* that it has read the song's meta data.
@@ -152,9 +156,8 @@ decoder_tag(struct decoder *decoder, struct input_stream *is,
* @param decoder the decoder object
* @param rgi the replay_gain_info object; may be NULL to invalidate
* the previous replay gain values
- * @return the replay gain adjustment used
*/
-float
+void
decoder_replay_gain(struct decoder *decoder,
const struct replay_gain_info *replay_gain_info);
@@ -162,12 +165,15 @@ decoder_replay_gain(struct decoder *decoder,
* Store MixRamp tags.
*
* @param decoder the decoder object
- * @param replay_gain_db the ReplayGain adjustment used for this song
* @param mixramp_start the mixramp_start tag; may be NULL to invalidate
* @param mixramp_end the mixramp_end tag; may be NULL to invalidate
*/
void
-decoder_mixramp(struct decoder *decoder, float replay_gain_db,
+decoder_mixramp(struct decoder *decoder,
char *mixramp_start, char *mixramp_end);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/decoder_control.c b/src/decoder_control.c
deleted file mode 100644
index 2ce03b66..00000000
--- a/src/decoder_control.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "decoder_control.h"
-#include "pipe.h"
-
-#include <assert.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "decoder_control"
-
-struct decoder_control *
-dc_new(GCond *client_cond)
-{
- struct decoder_control *dc = g_new(struct decoder_control, 1);
-
- dc->thread = NULL;
-
- dc->mutex = g_mutex_new();
- dc->cond = g_cond_new();
- dc->client_cond = client_cond;
-
- dc->state = DECODE_STATE_STOP;
- dc->command = DECODE_COMMAND_NONE;
-
- dc->replay_gain_db = 0;
- dc->replay_gain_prev_db = 0;
- dc->mixramp_start = NULL;
- dc->mixramp_end = NULL;
- dc->mixramp_prev_end = NULL;
-
- return dc;
-}
-
-void
-dc_free(struct decoder_control *dc)
-{
- g_cond_free(dc->cond);
- g_mutex_free(dc->mutex);
- g_free(dc->mixramp_start);
- g_free(dc->mixramp_end);
- g_free(dc->mixramp_prev_end);
- g_free(dc);
-}
-
-static void
-dc_command_wait_locked(struct decoder_control *dc)
-{
- while (dc->command != DECODE_COMMAND_NONE)
- g_cond_wait(dc->client_cond, dc->mutex);
-}
-
-static void
-dc_command_locked(struct decoder_control *dc, enum decoder_command cmd)
-{
- dc->command = cmd;
- decoder_signal(dc);
- dc_command_wait_locked(dc);
-}
-
-static void
-dc_command(struct decoder_control *dc, enum decoder_command cmd)
-{
- decoder_lock(dc);
- dc_command_locked(dc, cmd);
- decoder_unlock(dc);
-}
-
-static void
-dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
-{
- decoder_lock(dc);
-
- dc->command = cmd;
- decoder_signal(dc);
-
- decoder_unlock(dc);
-}
-
-void
-dc_start(struct decoder_control *dc, struct song *song,
- unsigned start_ms, unsigned end_ms,
- struct music_buffer *buffer, struct music_pipe *pipe)
-{
- assert(song != NULL);
- assert(buffer != NULL);
- assert(pipe != NULL);
- assert(music_pipe_empty(pipe));
-
- dc->song = song;
- dc->start_ms = start_ms;
- dc->end_ms = end_ms;
- dc->buffer = buffer;
- dc->pipe = pipe;
- dc_command(dc, DECODE_COMMAND_START);
-}
-
-void
-dc_stop(struct decoder_control *dc)
-{
- decoder_lock(dc);
-
- if (dc->command != DECODE_COMMAND_NONE)
- /* Attempt to cancel the current command. If it's too
- late and the decoder thread is already executing
- the old command, we'll call STOP again in this
- function (see below). */
- dc_command_locked(dc, DECODE_COMMAND_STOP);
-
- if (dc->state != DECODE_STATE_STOP && dc->state != DECODE_STATE_ERROR)
- dc_command_locked(dc, DECODE_COMMAND_STOP);
-
- decoder_unlock(dc);
-}
-
-bool
-dc_seek(struct decoder_control *dc, double where)
-{
- assert(dc->state != DECODE_STATE_START);
- assert(where >= 0.0);
-
- if (dc->state == DECODE_STATE_STOP ||
- dc->state == DECODE_STATE_ERROR || !dc->seekable)
- return false;
-
- dc->seek_where = where;
- dc->seek_error = false;
- dc_command(dc, DECODE_COMMAND_SEEK);
-
- if (dc->seek_error)
- return false;
-
- return true;
-}
-
-void
-dc_quit(struct decoder_control *dc)
-{
- assert(dc->thread != NULL);
-
- dc->quit = true;
- dc_command_async(dc, DECODE_COMMAND_STOP);
-
- g_thread_join(dc->thread);
- dc->thread = NULL;
-}
-
-void
-dc_mixramp_start(struct decoder_control *dc, char *mixramp_start)
-{
- assert(dc != NULL);
-
- g_free(dc->mixramp_start);
- dc->mixramp_start = mixramp_start;
-}
-
-void
-dc_mixramp_end(struct decoder_control *dc, char *mixramp_end)
-{
- assert(dc != NULL);
-
- g_free(dc->mixramp_end);
- dc->mixramp_end = mixramp_end;
-}
-
-void
-dc_mixramp_prev_end(struct decoder_control *dc, char *mixramp_prev_end)
-{
- assert(dc != NULL);
-
- g_free(dc->mixramp_prev_end);
- dc->mixramp_prev_end = mixramp_prev_end;
-}
diff --git a/src/decoder_control.h b/src/decoder_control.h
deleted file mode 100644
index 566b153e..00000000
--- a/src/decoder_control.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_DECODER_CONTROL_H
-#define MPD_DECODER_CONTROL_H
-
-#include "decoder_command.h"
-#include "audio_format.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-enum decoder_state {
- DECODE_STATE_STOP = 0,
- DECODE_STATE_START,
- DECODE_STATE_DECODE,
-
- /**
- * The last "START" command failed, because there was an I/O
- * error or because no decoder was able to decode the file.
- * This state will only come after START; once the state has
- * turned to DECODE, by definition no such error can occur.
- */
- DECODE_STATE_ERROR,
-};
-
-struct decoder_control {
- /** the handle of the decoder thread, or NULL if the decoder
- thread isn't running */
- GThread *thread;
-
- /**
- * This lock protects #state and #command.
- */
- GMutex *mutex;
-
- /**
- * Trigger this object after you have modified #command. This
- * is also used by the decoder thread to notify the caller
- * when it has finished a command.
- */
- GCond *cond;
-
- /**
- * The trigger of this object's client. It is signalled
- * whenever an event occurs.
- */
- GCond *client_cond;
-
- enum decoder_state state;
- enum decoder_command command;
-
- bool quit;
- bool seek_error;
- bool seekable;
- double seek_where;
-
- /** the format of the song file */
- struct audio_format in_audio_format;
-
- /** the format being sent to the music pipe */
- struct audio_format out_audio_format;
-
- /**
- * The song currently being decoded. This attribute is set by
- * the player thread, when it sends the #DECODE_COMMAND_START
- * command.
- */
- const struct song *song;
-
- /**
- * The initial seek position (in milliseconds), e.g. to the
- * start of a sub-track described by a CUE file.
- *
- * This attribute is set by dc_start().
- */
- unsigned start_ms;
-
- /**
- * The decoder will stop when it reaches this position (in
- * milliseconds). 0 means don't stop before the end of the
- * file.
- *
- * This attribute is set by dc_start().
- */
- unsigned end_ms;
-
- float total_time;
-
- /** the #music_chunk allocator */
- struct music_buffer *buffer;
-
- /**
- * The destination pipe for decoded chunks. The caller thread
- * owns this object, and is responsible for freeing it.
- */
- struct music_pipe *pipe;
-
- float replay_gain_db;
- float replay_gain_prev_db;
- char *mixramp_start;
- char *mixramp_end;
- char *mixramp_prev_end;
-};
-
-G_GNUC_MALLOC
-struct decoder_control *
-dc_new(GCond *client_cond);
-
-void
-dc_free(struct decoder_control *dc);
-
-/**
- * Locks the #decoder_control object.
- */
-static inline void
-decoder_lock(struct decoder_control *dc)
-{
- g_mutex_lock(dc->mutex);
-}
-
-/**
- * Unlocks the #decoder_control object.
- */
-static inline void
-decoder_unlock(struct decoder_control *dc)
-{
- g_mutex_unlock(dc->mutex);
-}
-
-/**
- * Waits for a signal on the #decoder_control object. This function
- * is only valid in the decoder thread. The object must be locked
- * prior to calling this function.
- */
-static inline void
-decoder_wait(struct decoder_control *dc)
-{
- g_cond_wait(dc->cond, dc->mutex);
-}
-
-/**
- * Signals the #decoder_control object. This function is only valid
- * in the player thread. The object should be locked prior to calling
- * this function.
- */
-static inline void
-decoder_signal(struct decoder_control *dc)
-{
- g_cond_signal(dc->cond);
-}
-
-static inline bool
-decoder_is_idle(const struct decoder_control *dc)
-{
- return dc->state == DECODE_STATE_STOP ||
- dc->state == DECODE_STATE_ERROR;
-}
-
-static inline bool
-decoder_is_starting(const struct decoder_control *dc)
-{
- return dc->state == DECODE_STATE_START;
-}
-
-static inline bool
-decoder_has_failed(const struct decoder_control *dc)
-{
- assert(dc->command == DECODE_COMMAND_NONE);
-
- return dc->state == DECODE_STATE_ERROR;
-}
-
-static inline bool
-decoder_lock_is_idle(struct decoder_control *dc)
-{
- bool ret;
-
- decoder_lock(dc);
- ret = decoder_is_idle(dc);
- decoder_unlock(dc);
-
- return ret;
-}
-
-static inline bool
-decoder_lock_is_starting(struct decoder_control *dc)
-{
- bool ret;
-
- decoder_lock(dc);
- ret = decoder_is_starting(dc);
- decoder_unlock(dc);
-
- return ret;
-}
-
-static inline bool
-decoder_lock_has_failed(struct decoder_control *dc)
-{
- bool ret;
-
- decoder_lock(dc);
- ret = decoder_has_failed(dc);
- decoder_unlock(dc);
-
- return ret;
-}
-
-static inline const struct song *
-decoder_current_song(const struct decoder_control *dc)
-{
- switch (dc->state) {
- case DECODE_STATE_STOP:
- case DECODE_STATE_ERROR:
- return NULL;
-
- case DECODE_STATE_START:
- case DECODE_STATE_DECODE:
- return dc->song;
- }
-
- assert(false);
- return NULL;
-}
-
-/**
- * Start the decoder.
- *
- * @param the decoder
- * @param song the song to be decoded
- * @param start_ms see #decoder_control
- * @param end_ms see #decoder_control
- * @param pipe the pipe which receives the decoded chunks (owned by
- * the caller)
- */
-void
-dc_start(struct decoder_control *dc, struct song *song,
- unsigned start_ms, unsigned end_ms,
- struct music_buffer *buffer, struct music_pipe *pipe);
-
-void
-dc_stop(struct decoder_control *dc);
-
-bool
-dc_seek(struct decoder_control *dc, double where);
-
-void
-dc_quit(struct decoder_control *dc);
-
-void
-dc_mixramp_start(struct decoder_control *dc, char *mixramp_start);
-
-void
-dc_mixramp_end(struct decoder_control *dc, char *mixramp_end);
-
-void
-dc_mixramp_prev_end(struct decoder_control *dc, char *mixramp_prev_end);
-
-#endif
diff --git a/src/decoder_error.h b/src/decoder_error.h
new file mode 100644
index 00000000..a12a3193
--- /dev/null
+++ b/src/decoder_error.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_DECODER_ERROR_H
+#define MPD_DECODER_ERROR_H
+
+#include <glib.h>
+
+/**
+ * Quark for GError.domain.
+ */
+G_GNUC_CONST
+static inline GQuark
+decoder_quark(void)
+{
+ return g_quark_from_static_string("decoder");
+}
+
+#endif
diff --git a/src/decoder_plugin.h b/src/decoder_plugin.h
index 933ba675..b7ab738b 100644
--- a/src/decoder_plugin.h
+++ b/src/decoder_plugin.h
@@ -190,6 +190,10 @@ decoder_plugin_container_scan( const struct decoder_plugin *plugin,
return plugin->container_scan(pathname, tnum);
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Does the plugin announce the specified file name suffix?
*/
@@ -204,4 +208,8 @@ bool
decoder_plugin_supports_mime_type(const struct decoder_plugin *plugin,
const char *mime_type);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/directory.c b/src/directory.c
deleted file mode 100644
index e886698d..00000000
--- a/src/directory.c
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "directory.h"
-#include "song.h"
-#include "song_sort.h"
-#include "playlist_vector.h"
-#include "path.h"
-#include "util/list_sort.h"
-#include "db_visitor.h"
-#include "db_lock.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-struct directory *
-directory_new(const char *path, struct directory *parent)
-{
- struct directory *directory;
- size_t pathlen = strlen(path);
-
- assert(path != NULL);
- assert((*path == 0) == (parent == NULL));
-
- directory = g_malloc0(sizeof(*directory) -
- sizeof(directory->path) + pathlen + 1);
- INIT_LIST_HEAD(&directory->children);
- INIT_LIST_HEAD(&directory->songs);
- INIT_LIST_HEAD(&directory->playlists);
-
- directory->parent = parent;
- memcpy(directory->path, path, pathlen + 1);
-
- return directory;
-}
-
-void
-directory_free(struct directory *directory)
-{
- playlist_vector_deinit(&directory->playlists);
-
- struct song *song, *ns;
- directory_for_each_song_safe(song, ns, directory)
- song_free(song);
-
- struct directory *child, *n;
- directory_for_each_child_safe(child, n, directory)
- directory_free(child);
-
- g_free(directory);
- /* this resets last dir returned */
- /*directory_get_path(NULL); */
-}
-
-void
-directory_delete(struct directory *directory)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(directory->parent != NULL);
-
- list_del(&directory->siblings);
- directory_free(directory);
-}
-
-const char *
-directory_get_name(const struct directory *directory)
-{
- assert(!directory_is_root(directory));
- assert(directory->path != NULL);
-
- const char *slash = strrchr(directory->path, '/');
- assert((slash == NULL) == directory_is_root(directory->parent));
-
- return slash != NULL
- ? slash + 1
- : directory->path;
-}
-
-struct directory *
-directory_new_child(struct directory *parent, const char *name_utf8)
-{
- assert(holding_db_lock());
- assert(parent != NULL);
- assert(name_utf8 != NULL);
- assert(*name_utf8 != 0);
-
- char *allocated;
- const char *path_utf8;
- if (directory_is_root(parent)) {
- allocated = NULL;
- path_utf8 = name_utf8;
- } else {
- allocated = g_strconcat(directory_get_path(parent),
- "/", name_utf8, NULL);
- path_utf8 = allocated;
- }
-
- struct directory *directory = directory_new(path_utf8, parent);
- g_free(allocated);
-
- list_add_tail(&directory->siblings, &parent->children);
- return directory;
-}
-
-struct directory *
-directory_get_child(const struct directory *directory, const char *name)
-{
- assert(holding_db_lock());
-
- struct directory *child;
- directory_for_each_child(child, directory)
- if (strcmp(directory_get_name(child), name) == 0)
- return child;
-
- return NULL;
-}
-
-void
-directory_prune_empty(struct directory *directory)
-{
- assert(holding_db_lock());
-
- struct directory *child, *n;
- directory_for_each_child_safe(child, n, directory) {
- directory_prune_empty(child);
-
- if (directory_is_empty(child))
- directory_delete(child);
- }
-}
-
-struct directory *
-directory_lookup_directory(struct directory *directory, const char *uri)
-{
- assert(holding_db_lock());
- assert(uri != NULL);
-
- if (isRootDirectory(uri))
- return directory;
-
- char *duplicated = g_strdup(uri), *name = duplicated;
-
- while (1) {
- char *slash = strchr(name, '/');
- if (slash == name) {
- directory = NULL;
- break;
- }
-
- if (slash != NULL)
- *slash = '\0';
-
- directory = directory_get_child(directory, name);
- if (directory == NULL || slash == NULL)
- break;
-
- name = slash + 1;
- }
-
- g_free(duplicated);
-
- return directory;
-}
-
-void
-directory_add_song(struct directory *directory, struct song *song)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(song != NULL);
- assert(song->parent == directory);
-
- list_add_tail(&song->siblings, &directory->songs);
-}
-
-void
-directory_remove_song(G_GNUC_UNUSED struct directory *directory,
- struct song *song)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(song != NULL);
- assert(song->parent == directory);
-
- list_del(&song->siblings);
-}
-
-struct song *
-directory_get_song(const struct directory *directory, const char *name_utf8)
-{
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(name_utf8 != NULL);
-
- struct song *song;
- directory_for_each_song(song, directory) {
- assert(song->parent == directory);
-
- if (strcmp(song->uri, name_utf8) == 0)
- return song;
- }
-
- return NULL;
-}
-
-struct song *
-directory_lookup_song(struct directory *directory, const char *uri)
-{
- char *duplicated, *base;
-
- assert(holding_db_lock());
- assert(directory != NULL);
- assert(uri != NULL);
-
- duplicated = g_strdup(uri);
- base = strrchr(duplicated, '/');
-
- if (base != NULL) {
- *base++ = 0;
- directory = directory_lookup_directory(directory, duplicated);
- if (directory == NULL) {
- g_free(duplicated);
- return NULL;
- }
- } else
- base = duplicated;
-
- struct song *song = directory_get_song(directory, base);
- assert(song == NULL || song->parent == directory);
-
- g_free(duplicated);
- return song;
-
-}
-
-static int
-directory_cmp(G_GNUC_UNUSED void *priv,
- struct list_head *_a, struct list_head *_b)
-{
- const struct directory *a = (const struct directory *)_a;
- const struct directory *b = (const struct directory *)_b;
- return g_utf8_collate(a->path, b->path);
-}
-
-void
-directory_sort(struct directory *directory)
-{
- assert(holding_db_lock());
-
- list_sort(NULL, &directory->children, directory_cmp);
- song_list_sort(&directory->songs);
-
- struct directory *child;
- directory_for_each_child(child, directory)
- directory_sort(child);
-}
-
-bool
-directory_walk(const struct directory *directory, bool recursive,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r)
-{
- assert(directory != NULL);
- assert(visitor != NULL);
- assert(error_r == NULL || *error_r == NULL);
-
- if (visitor->song != NULL) {
- struct song *song;
- directory_for_each_song(song, directory)
- if (!visitor->song(song, ctx, error_r))
- return false;
- }
-
- if (visitor->playlist != NULL) {
- struct playlist_metadata *i;
- directory_for_each_playlist(i, directory)
- if (!visitor->playlist(i, directory, ctx, error_r))
- return false;
- }
-
- struct directory *child;
- directory_for_each_child(child, directory) {
- if (visitor->directory != NULL &&
- !visitor->directory(child, ctx, error_r))
- return false;
-
- if (recursive &&
- !directory_walk(child, recursive, visitor, ctx, error_r))
- return false;
- }
-
- return true;
-}
diff --git a/src/directory.h b/src/directory.h
deleted file mode 100644
index b3cd9c8c..00000000
--- a/src/directory.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_DIRECTORY_H
-#define MPD_DIRECTORY_H
-
-#include "check.h"
-#include "util/list.h"
-
-#include <glib.h>
-#include <stdbool.h>
-#include <sys/types.h>
-
-#define DEVICE_INARCHIVE (dev_t)(-1)
-#define DEVICE_CONTAINER (dev_t)(-2)
-
-#define directory_for_each_child(pos, directory) \
- list_for_each_entry(pos, &directory->children, siblings)
-
-#define directory_for_each_child_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->children, siblings)
-
-#define directory_for_each_song(pos, directory) \
- list_for_each_entry(pos, &directory->songs, siblings)
-
-#define directory_for_each_song_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->songs, siblings)
-
-#define directory_for_each_playlist(pos, directory) \
- list_for_each_entry(pos, &directory->playlists, siblings)
-
-#define directory_for_each_playlist_safe(pos, n, directory) \
- list_for_each_entry_safe(pos, n, &directory->playlists, siblings)
-
-struct song;
-struct db_visitor;
-
-struct directory {
- /**
- * Pointers to the siblings of this directory within the
- * parent directory. It is unused (undefined) in the root
- * directory.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head siblings;
-
- /**
- * A doubly linked list of child directories.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head children;
-
- /**
- * A doubly linked list of songs within this directory.
- *
- * This attribute is protected with the global #db_mutex.
- * Read access in the update thread does not need protection.
- */
- struct list_head songs;
-
- struct list_head playlists;
-
- struct directory *parent;
- time_t mtime;
- ino_t inode;
- dev_t device;
- bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
- char path[sizeof(long)];
-};
-
-static inline bool
-isRootDirectory(const char *name)
-{
- return name[0] == 0 || (name[0] == '/' && name[1] == 0);
-}
-
-/**
- * Generic constructor for #directory object.
- */
-G_GNUC_MALLOC
-struct directory *
-directory_new(const char *dirname, struct directory *parent);
-
-/**
- * Create a new root #directory object.
- */
-G_GNUC_MALLOC
-static inline struct directory *
-directory_new_root(void)
-{
- return directory_new("", NULL);
-}
-
-/**
- * Free this #directory object (and the whole object tree within it),
- * assuming it was already removed from the parent.
- */
-void
-directory_free(struct directory *directory);
-
-/**
- * Remove this #directory object from its parent and free it. This
- * must not be called with the root directory.
- *
- * Caller must lock the #db_mutex.
- */
-void
-directory_delete(struct directory *directory);
-
-static inline bool
-directory_is_empty(const struct directory *directory)
-{
- return list_empty(&directory->children) &&
- list_empty(&directory->songs) &&
- list_empty(&directory->playlists);
-}
-
-static inline const char *
-directory_get_path(const struct directory *directory)
-{
- return directory->path;
-}
-
-/**
- * Is this the root directory of the music database?
- */
-static inline bool
-directory_is_root(const struct directory *directory)
-{
- return directory->parent == NULL;
-}
-
-/**
- * Returns the base name of the directory.
- */
-G_GNUC_PURE
-const char *
-directory_get_name(const struct directory *directory);
-
-/**
- * Caller must lock the #db_mutex.
- */
-G_GNUC_PURE
-struct directory *
-directory_get_child(const struct directory *directory, const char *name);
-
-/**
- * Create a new #directory object as a child of the given one.
- *
- * Caller must lock the #db_mutex.
- *
- * @param parent the parent directory the new one will be added to
- * @param name_utf8 the UTF-8 encoded name of the new sub directory
- */
-G_GNUC_MALLOC
-struct directory *
-directory_new_child(struct directory *parent, const char *name_utf8);
-
-/**
- * Look up a sub directory, and create the object if it does not
- * exist.
- *
- * Caller must lock the #db_mutex.
- */
-static inline struct directory *
-directory_make_child(struct directory *directory, const char *name_utf8)
-{
- struct directory *child = directory_get_child(directory, name_utf8);
- if (child == NULL)
- child = directory_new_child(directory, name_utf8);
- return child;
-}
-
-/**
- * Caller must lock the #db_mutex.
- */
-void
-directory_prune_empty(struct directory *directory);
-
-/**
- * Looks up a directory by its relative URI.
- *
- * @param directory the parent (or grandparent, ...) directory
- * @param uri the relative URI
- * @return the directory, or NULL if none was found
- */
-struct directory *
-directory_lookup_directory(struct directory *directory, const char *uri);
-
-/**
- * Add a song object to this directory. Its "parent" attribute must
- * be set already.
- */
-void
-directory_add_song(struct directory *directory, struct song *song);
-
-/**
- * Remove a song object from this directory (which effectively
- * invalidates the song object, because the "parent" attribute becomes
- * stale), but does not free it.
- */
-void
-directory_remove_song(struct directory *directory, struct song *song);
-
-/**
- * Look up a song in this directory by its name.
- *
- * Caller must lock the #db_mutex.
- */
-G_GNUC_PURE
-struct song *
-directory_get_song(const struct directory *directory, const char *name_utf8);
-
-/**
- * Looks up a song by its relative URI.
- *
- * Caller must lock the #db_mutex.
- *
- * @param directory the parent (or grandparent, ...) directory
- * @param uri the relative URI
- * @return the song, or NULL if none was found
- */
-struct song *
-directory_lookup_song(struct directory *directory, const char *uri);
-
-/**
- * Sort all directory entries recursively.
- *
- * Caller must lock the #db_mutex.
- */
-void
-directory_sort(struct directory *directory);
-
-/**
- * Caller must lock #db_mutex.
- */
-bool
-directory_walk(const struct directory *directory, bool recursive,
- const struct db_visitor *visitor, void *ctx,
- GError **error_r);
-
-#endif
diff --git a/src/dsd2pcm/dsd2pcm.hpp b/src/dsd2pcm/dsd2pcm.hpp
index b1b2ae1c..8f3f5519 100644
--- a/src/dsd2pcm/dsd2pcm.hpp
+++ b/src/dsd2pcm/dsd2pcm.hpp
@@ -13,11 +13,9 @@ class dxd
{
dsd2pcm_ctx *handle;
public:
- dxd() : handle(dsd2pcm_init())
- { if (!handle) throw std::runtime_error("wtf?!"); }
+ dxd() : handle(dsd2pcm_init()) {}
- dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle))
- { if (!handle) throw std::runtime_error("wtf?!"); }
+ dxd(dxd const& x) : handle(dsd2pcm_clone(x.handle)) {}
~dxd() { dsd2pcm_destroy(handle); }
diff --git a/src/dsd2pcm/noiseshape.hpp b/src/dsd2pcm/noiseshape.hpp
index 726272f9..1fc698b3 100644
--- a/src/dsd2pcm/noiseshape.hpp
+++ b/src/dsd2pcm/noiseshape.hpp
@@ -14,14 +14,12 @@ class noise_shaper
public:
noise_shaper(int sos_count, const float *bbaa)
{
- if (noise_shape_init(&ctx,sos_count,bbaa))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_init(&ctx, sos_count, bbaa);
}
noise_shaper(noise_shaper const& x)
{
- if (noise_shape_clone(&x.ctx,&ctx))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_clone(&x.ctx,&ctx);
}
~noise_shaper()
@@ -31,8 +29,7 @@ public:
{
if (this != &x) {
noise_shape_destroy(&ctx);
- if (noise_shape_clone(&x.ctx,&ctx))
- throw std::runtime_error("noise shaper initialization failed");
+ noise_shape_clone(&x.ctx,&ctx);
}
return *this;
}
diff --git a/src/dummy.cxx b/src/dummy.cxx
deleted file mode 100644
index b21555d0..00000000
--- a/src/dummy.cxx
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Just a dummy C++ file that is linked to work around an automake
- * bug: automake uses CXXLD when at least one source is C++, but when
- * you link a static library with a C++ source, it uses CCLD. This
- * causes linker problems (undefined reference to 'operator
- * delete(void*)'), because CCLD does not link with libstdc++.
- *
- * By linking with this empty C++ source, automake decides to use
- * CXXLD.
- *
- */
diff --git a/src/encoder/OggStream.hxx b/src/encoder/OggStream.hxx
new file mode 100644
index 00000000..ce847f49
--- /dev/null
+++ b/src/encoder/OggStream.hxx
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OGG_STREAM_HXX
+#define MPD_OGG_STREAM_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+
+class OggStream {
+ ogg_stream_state state;
+
+ bool flush;
+
+#ifndef NDEBUG
+ bool initialized;
+#endif
+
+public:
+#ifndef NDEBUG
+ OggStream():initialized(false) {}
+ ~OggStream() {
+ assert(!initialized);
+ }
+#endif
+
+ void Initialize(int serialno) {
+ assert(!initialized);
+
+ ogg_stream_init(&state, serialno);
+
+ /* set "flush" to true, so the caller gets the full
+ headers on the first read() */
+ flush = true;
+
+#ifndef NDEBUG
+ initialized = true;
+#endif
+ }
+
+ void Reinitialize(int serialno) {
+ assert(initialized);
+
+ ogg_stream_reset_serialno(&state, serialno);
+
+ /* set "flush" to true, so the caller gets the full
+ headers on the first read() */
+ flush = true;
+ }
+
+ void Deinitialize() {
+ assert(initialized);
+
+ ogg_stream_clear(&state);
+
+#ifndef NDEBUG
+ initialized = false;
+#endif
+ }
+
+ void Flush() {
+ assert(initialized);
+
+ flush = true;
+ }
+
+ void PacketIn(const ogg_packet &packet) {
+ assert(initialized);
+
+ ogg_stream_packetin(&state,
+ const_cast<ogg_packet *>(&packet));
+ }
+
+ bool PageOut(ogg_page &page) {
+ int result = ogg_stream_pageout(&state, &page);
+ if (result == 0 && flush) {
+ flush = false;
+ result = ogg_stream_flush(&state, &page);
+ }
+
+ return result != 0;
+ }
+
+ size_t PageOut(void *_buffer, size_t size) {
+ ogg_page page;
+ if (!PageOut(page))
+ return 0;
+
+ assert(page.header_len > 0 || page.body_len > 0);
+
+ size_t header_len = (size_t)page.header_len;
+ size_t body_len = (size_t)page.body_len;
+ assert(header_len <= size);
+
+ if (header_len + body_len > size)
+ /* TODO: better overflow handling */
+ body_len = size - header_len;
+
+ uint8_t *buffer = (uint8_t *)_buffer;
+ memcpy(buffer, page.header, header_len);
+ memcpy(buffer + header_len, page.body, body_len);
+
+ return header_len + body_len;
+ }
+};
+
+#endif
diff --git a/src/encoder/OpusEncoderPlugin.cxx b/src/encoder/OpusEncoderPlugin.cxx
new file mode 100644
index 00000000..8d2c0974
--- /dev/null
+++ b/src/encoder/OpusEncoderPlugin.cxx
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "OpusEncoderPlugin.hxx"
+#include "OggStream.hxx"
+#include "encoder_api.h"
+#include "encoder_plugin.h"
+#include "audio_format.h"
+#include "mpd_error.h"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus_encoder"
+
+struct opus_encoder {
+ /** the base class */
+ struct encoder encoder;
+
+ /* configuration */
+
+ opus_int32 bitrate;
+ int complexity;
+ int signal;
+
+ /* runtime information */
+
+ struct audio_format audio_format;
+
+ size_t frame_size;
+
+ size_t buffer_frames, buffer_size, buffer_position;
+ uint8_t *buffer;
+
+ OpusEncoder *enc;
+
+ unsigned char buffer2[1275 * 3 + 7];
+
+ OggStream stream;
+
+ int lookahead;
+
+ ogg_int64_t packetno;
+
+ ogg_int64_t granulepos;
+
+ opus_encoder() {
+ encoder_struct_init(&encoder, &opus_encoder_plugin);
+ }
+};
+
+gcc_const
+static inline GQuark
+opus_encoder_quark(void)
+{
+ return g_quark_from_static_string("opus_encoder");
+}
+
+static bool
+opus_encoder_configure(struct opus_encoder *encoder,
+ const struct config_param *param, GError **error_r)
+{
+ const char *value = config_get_block_string(param, "bitrate", "auto");
+ if (strcmp(value, "auto") == 0)
+ encoder->bitrate = OPUS_AUTO;
+ else if (strcmp(value, "max") == 0)
+ encoder->bitrate = OPUS_BITRATE_MAX;
+ else {
+ char *endptr;
+ encoder->bitrate = strtoul(value, &endptr, 10);
+ if (endptr == value || *endptr != 0 ||
+ encoder->bitrate < 500 || encoder->bitrate > 512000) {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid bit rate");
+ return false;
+ }
+ }
+
+ encoder->complexity = config_get_block_unsigned(param, "complexity",
+ 10);
+ if (encoder->complexity > 10) {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid complexity");
+ return false;
+ }
+
+ value = config_get_block_string(param, "signal", "auto");
+ if (strcmp(value, "auto") == 0)
+ encoder->bitrate = OPUS_AUTO;
+ else if (strcmp(value, "voice") == 0)
+ encoder->bitrate = OPUS_SIGNAL_VOICE;
+ else if (strcmp(value, "music") == 0)
+ encoder->bitrate = OPUS_SIGNAL_MUSIC;
+ else {
+ g_set_error(error_r, opus_encoder_quark(), 0,
+ "Invalid signal");
+ return false;
+ }
+
+ return true;
+}
+
+static struct encoder *
+opus_encoder_init(const struct config_param *param, GError **error)
+{
+ opus_encoder *encoder = new opus_encoder();
+
+ /* load configuration from "param" */
+ if (!opus_encoder_configure(encoder, param, error)) {
+ /* configuration has failed, roll back and return error */
+ delete encoder;
+ return NULL;
+ }
+
+ return &encoder->encoder;
+}
+
+static void
+opus_encoder_finish(struct encoder *_encoder)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ /* the real libopus cleanup was already performed by
+ opus_encoder_close(), so no real work here */
+ delete encoder;
+}
+
+static bool
+opus_encoder_open(struct encoder *_encoder,
+ struct audio_format *audio_format,
+ GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ /* libopus supports only 48 kHz */
+ audio_format->sample_rate = 48000;
+
+ if (audio_format->channels > 2)
+ audio_format->channels = 1;
+
+ switch ((enum sample_format)audio_format->format) {
+ case SAMPLE_FORMAT_S16:
+ case SAMPLE_FORMAT_FLOAT:
+ break;
+
+ case SAMPLE_FORMAT_S8:
+ audio_format->format = SAMPLE_FORMAT_S16;
+ break;
+
+ default:
+ audio_format->format = SAMPLE_FORMAT_FLOAT;
+ break;
+ }
+
+ encoder->audio_format = *audio_format;
+ encoder->frame_size = audio_format_frame_size(audio_format);
+
+ int error;
+ encoder->enc = opus_encoder_create(audio_format->sample_rate,
+ audio_format->channels,
+ OPUS_APPLICATION_AUDIO,
+ &error);
+ if (encoder->enc == nullptr) {
+ g_set_error_literal(error_r, opus_encoder_quark(), error,
+ opus_strerror(error));
+ return false;
+ }
+
+ opus_encoder_ctl(encoder->enc, OPUS_SET_BITRATE(encoder->bitrate));
+ opus_encoder_ctl(encoder->enc,
+ OPUS_SET_COMPLEXITY(encoder->complexity));
+ opus_encoder_ctl(encoder->enc, OPUS_SET_SIGNAL(encoder->signal));
+
+ opus_encoder_ctl(encoder->enc, OPUS_GET_LOOKAHEAD(&encoder->lookahead));
+
+ encoder->buffer_frames = audio_format->sample_rate / 50;
+ encoder->buffer_size = encoder->frame_size * encoder->buffer_frames;
+ encoder->buffer_position = 0;
+ encoder->buffer = (unsigned char *)g_malloc(encoder->buffer_size);
+
+ encoder->stream.Initialize(g_random_int());
+ encoder->packetno = 0;
+
+ return true;
+}
+
+static void
+opus_encoder_close(struct encoder *_encoder)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Deinitialize();
+ g_free(encoder->buffer);
+ opus_encoder_destroy(encoder->enc);
+}
+
+static bool
+opus_encoder_do_encode(struct opus_encoder *encoder, bool eos,
+ GError **error_r)
+{
+ assert(encoder->buffer_position == encoder->buffer_size);
+
+ opus_int32 result =
+ encoder->audio_format.format == SAMPLE_FORMAT_S16
+ ? opus_encode(encoder->enc,
+ (const opus_int16 *)encoder->buffer,
+ encoder->buffer_frames,
+ encoder->buffer2,
+ sizeof(encoder->buffer2))
+ : opus_encode_float(encoder->enc,
+ (const float *)encoder->buffer,
+ encoder->buffer_frames,
+ encoder->buffer2,
+ sizeof(encoder->buffer2));
+ if (result < 0) {
+ g_set_error_literal(error_r, opus_encoder_quark(), 0,
+ "Opus encoder error");
+ return false;
+ }
+
+ encoder->granulepos += encoder->buffer_frames;
+
+ ogg_packet packet;
+ packet.packet = encoder->buffer2;
+ packet.bytes = result;
+ packet.b_o_s = false;
+ packet.e_o_s = eos;
+ packet.granulepos = encoder->granulepos;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+
+ encoder->buffer_position = 0;
+
+ return true;
+}
+
+static bool
+opus_encoder_end(struct encoder *_encoder, GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Flush();
+
+ memset(encoder->buffer + encoder->buffer_position, 0,
+ encoder->buffer_size - encoder->buffer_position);
+ encoder->buffer_position = encoder->buffer_size;
+
+ return opus_encoder_do_encode(encoder, true, error_r);
+}
+
+static bool
+opus_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ encoder->stream.Flush();
+ return true;
+}
+
+static bool
+opus_encoder_write_silence(struct opus_encoder *encoder, unsigned fill_frames,
+ GError **error_r)
+{
+ size_t fill_bytes = fill_frames * encoder->frame_size;
+
+ while (fill_bytes > 0) {
+ size_t nbytes =
+ encoder->buffer_size - encoder->buffer_position;
+ if (nbytes > fill_bytes)
+ nbytes = fill_bytes;
+
+ memset(encoder->buffer + encoder->buffer_position,
+ 0, nbytes);
+ encoder->buffer_position += nbytes;
+ fill_bytes -= nbytes;
+
+ if (encoder->buffer_position == encoder->buffer_size &&
+ !opus_encoder_do_encode(encoder, false, error_r))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+opus_encoder_write(struct encoder *_encoder,
+ const void *_data, size_t length,
+ GError **error_r)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+ const uint8_t *data = (const uint8_t *)_data;
+
+ if (encoder->lookahead > 0) {
+ /* generate some silence at the beginning of the
+ stream */
+
+ assert(encoder->buffer_position == 0);
+
+ if (!opus_encoder_write_silence(encoder, encoder->lookahead,
+ error_r))
+ return false;
+
+ encoder->lookahead = 0;
+ }
+
+ while (length > 0) {
+ size_t nbytes =
+ encoder->buffer_size - encoder->buffer_position;
+ if (nbytes > length)
+ nbytes = length;
+
+ memcpy(encoder->buffer + encoder->buffer_position,
+ data, nbytes);
+ data += nbytes;
+ length -= nbytes;
+ encoder->buffer_position += nbytes;
+
+ if (encoder->buffer_position == encoder->buffer_size &&
+ !opus_encoder_do_encode(encoder, false, error_r))
+ return false;
+ }
+
+ return true;
+}
+
+static void
+opus_encoder_generate_head(struct opus_encoder *encoder)
+{
+ unsigned char header[19];
+ memcpy(header, "OpusHead", 8);
+ header[8] = 1;
+ header[9] = encoder->audio_format.channels;
+ *(uint16_t *)(header + 10) = GUINT16_TO_LE(encoder->lookahead);
+ *(uint32_t *)(header + 12) =
+ GUINT32_TO_LE(encoder->audio_format.sample_rate);
+ header[16] = 0;
+ header[17] = 0;
+ header[18] = 0;
+
+ ogg_packet packet;
+ packet.packet = header;
+ packet.bytes = 19;
+ packet.b_o_s = true;
+ packet.e_o_s = false;
+ packet.granulepos = 0;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+ encoder->stream.Flush();
+}
+
+static void
+opus_encoder_generate_tags(struct opus_encoder *encoder)
+{
+ const char *version = opus_get_version_string();
+ size_t version_length = strlen(version);
+
+ size_t comments_size = 8 + 4 + version_length + 4;
+ unsigned char *comments = (unsigned char *)g_malloc(comments_size);
+ memcpy(comments, "OpusTags", 8);
+ *(uint32_t *)(comments + 8) = GUINT32_TO_LE(version_length);
+ memcpy(comments + 12, version, version_length);
+ *(uint32_t *)(comments + 12 + version_length) = GUINT32_TO_LE(0);
+
+ ogg_packet packet;
+ packet.packet = comments;
+ packet.bytes = comments_size;
+ packet.b_o_s = false;
+ packet.e_o_s = false;
+ packet.granulepos = 0;
+ packet.packetno = encoder->packetno++;
+ encoder->stream.PacketIn(packet);
+ encoder->stream.Flush();
+
+ g_free(comments);
+}
+
+static size_t
+opus_encoder_read(struct encoder *_encoder, void *dest, size_t length)
+{
+ struct opus_encoder *encoder = (struct opus_encoder *)_encoder;
+
+ if (encoder->packetno == 0)
+ opus_encoder_generate_head(encoder);
+ else if (encoder->packetno == 1)
+ opus_encoder_generate_tags(encoder);
+
+ return encoder->stream.PageOut(dest, length);
+}
+
+static const char *
+opus_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
+{
+ return "audio/ogg";
+}
+
+const struct encoder_plugin opus_encoder_plugin = {
+ "opus",
+ opus_encoder_init,
+ opus_encoder_finish,
+ opus_encoder_open,
+ opus_encoder_close,
+ opus_encoder_end,
+ opus_encoder_flush,
+ nullptr,
+ nullptr,
+ opus_encoder_write,
+ opus_encoder_read,
+ opus_encoder_get_mime_type,
+};
diff --git a/src/encoder/OpusEncoderPlugin.hxx b/src/encoder/OpusEncoderPlugin.hxx
new file mode 100644
index 00000000..f5437720
--- /dev/null
+++ b/src/encoder/OpusEncoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ENCODER_OPUS_H
+#define MPD_ENCODER_OPUS_H
+
+extern const struct encoder_plugin opus_encoder_plugin;
+
+#endif
diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/VorbisEncoderPlugin.cxx
index 468cf38e..dc7ef0d5 100644
--- a/src/encoder/vorbis_encoder.c
+++ b/src/encoder/VorbisEncoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,8 @@
*/
#include "config.h"
+#include "VorbisEncoderPlugin.hxx"
+#include "OggStream.hxx"
#include "encoder_api.h"
#include "encoder_plugin.h"
#include "tag.h"
@@ -44,16 +46,16 @@ struct vorbis_encoder {
struct audio_format audio_format;
- ogg_stream_state os;
-
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_info vi;
- bool flush;
-};
+ OggStream stream;
-extern const struct encoder_plugin vorbis_encoder_plugin;
+ vorbis_encoder() {
+ encoder_struct_init(&encoder, &vorbis_encoder_plugin);
+ }
+};
static inline GQuark
vorbis_encoder_quark(void)
@@ -65,8 +67,8 @@ static bool
vorbis_encoder_configure(struct vorbis_encoder *encoder,
const struct config_param *param, GError **error)
{
- const char *value = config_get_block_string(param, "quality", NULL);
- if (value != NULL) {
+ const char *value = config_get_block_string(param, "quality", nullptr);
+ if (value != nullptr) {
/* a quality was configured (VBR) */
char *endptr;
@@ -81,7 +83,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
return false;
}
- if (config_get_block_string(param, "bitrate", NULL) != NULL) {
+ if (config_get_block_string(param, "bitrate", nullptr) != nullptr) {
g_set_error(error, vorbis_encoder_quark(), 0,
"quality and bitrate are "
"both defined (line %i)",
@@ -91,8 +93,8 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
} else {
/* a bit rate was configured */
- value = config_get_block_string(param, "bitrate", NULL);
- if (value == NULL) {
+ value = config_get_block_string(param, "bitrate", nullptr);
+ if (value == nullptr) {
g_set_error(error, vorbis_encoder_quark(), 0,
"neither bitrate nor quality defined "
"at line %i",
@@ -118,14 +120,13 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
static struct encoder *
vorbis_encoder_init(const struct config_param *param, GError **error)
{
- struct vorbis_encoder *encoder = g_new(struct vorbis_encoder, 1);
- encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
+ vorbis_encoder *encoder = new vorbis_encoder();
/* load configuration from "param" */
if (!vorbis_encoder_configure(encoder, param, error)) {
/* configuration has failed, roll back and return error */
- g_free(encoder);
- return NULL;
+ delete encoder;
+ return nullptr;
}
return &encoder->encoder;
@@ -138,7 +139,7 @@ vorbis_encoder_finish(struct encoder *_encoder)
/* the real libvorbis/libogg cleanup was already performed by
vorbis_encoder_close(), so no real work here */
- g_free(encoder);
+ delete encoder;
}
static bool
@@ -174,7 +175,7 @@ vorbis_encoder_reinit(struct vorbis_encoder *encoder, GError **error)
vorbis_analysis_init(&encoder->vd, &encoder->vi);
vorbis_block_init(&encoder->vd, &encoder->vb);
- ogg_stream_init(&encoder->os, g_random_int());
+ encoder->stream.Initialize(g_random_int());
return true;
}
@@ -187,9 +188,9 @@ vorbis_encoder_headerout(struct vorbis_encoder *encoder, vorbis_comment *vc)
vorbis_analysis_headerout(&encoder->vd, vc,
&packet, &comments, &codebooks);
- ogg_stream_packetin(&encoder->os, &packet);
- ogg_stream_packetin(&encoder->os, &comments);
- ogg_stream_packetin(&encoder->os, &codebooks);
+ encoder->stream.PacketIn(packet);
+ encoder->stream.PacketIn(comments);
+ encoder->stream.PacketIn(codebooks);
}
static void
@@ -209,7 +210,7 @@ vorbis_encoder_open(struct encoder *_encoder,
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- audio_format->format = SAMPLE_FORMAT_S16;
+ audio_format->format = SAMPLE_FORMAT_FLOAT;
encoder->audio_format = *audio_format;
@@ -218,17 +219,13 @@ vorbis_encoder_open(struct encoder *_encoder,
vorbis_encoder_send_header(encoder);
- /* set "flush" to true, so the caller gets the full headers on
- the first read() */
- encoder->flush = true;
-
return true;
}
static void
vorbis_encoder_clear(struct vorbis_encoder *encoder)
{
- ogg_stream_clear(&encoder->os);
+ encoder->stream.Deinitialize();
vorbis_block_clear(&encoder->vb);
vorbis_dsp_clear(&encoder->vd);
vorbis_info_clear(&encoder->vi);
@@ -246,12 +243,12 @@ static void
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
{
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
- vorbis_analysis(&encoder->vb, NULL);
+ vorbis_analysis(&encoder->vb, nullptr);
vorbis_bitrate_addblock(&encoder->vb);
ogg_packet packet;
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
- ogg_stream_packetin(&encoder->os, &packet);
+ encoder->stream.PacketIn(packet);
}
}
@@ -260,7 +257,7 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- encoder->flush = true;
+ encoder->stream.Flush();
return true;
}
@@ -279,7 +276,7 @@ vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
vorbis_analysis_init(&encoder->vd, &encoder->vi);
vorbis_block_init(&encoder->vd, &encoder->vb);
- encoder->flush = true;
+ encoder->stream.Flush();
return true;
}
@@ -308,28 +305,23 @@ vorbis_encoder_tag(struct encoder *_encoder, const struct tag *tag,
/* reset ogg_stream_state and begin a new stream */
- ogg_stream_reset_serialno(&encoder->os, g_random_int());
+ encoder->stream.Reinitialize(g_random_int());
/* send that vorbis_comment to the ogg_stream_state */
vorbis_encoder_headerout(encoder, &comment);
vorbis_comment_clear(&comment);
- /* the next vorbis_encoder_read() call should flush the
- ogg_stream_state */
-
- encoder->flush = true;
-
return true;
}
static void
-pcm16_to_vorbis_buffer(float **dest, const int16_t *src,
- unsigned num_frames, unsigned num_channels)
+interleaved_to_vorbis_buffer(float **dest, const float *src,
+ unsigned num_frames, unsigned num_channels)
{
for (unsigned i = 0; i < num_frames; i++)
for (unsigned j = 0; j < num_channels; j++)
- dest[j][i] = *src++ / 32768.0;
+ dest[j][i] = *src++;
}
static bool
@@ -344,10 +336,11 @@ vorbis_encoder_write(struct encoder *_encoder,
/* this is for only 16-bit audio */
- pcm16_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd,
- num_frames),
- (const int16_t *)data,
- num_frames, encoder->audio_format.channels);
+ interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd,
+ num_frames),
+ (const float *)data,
+ num_frames,
+ encoder->audio_format.channels);
vorbis_analysis_wrote(&encoder->vd, num_frames);
vorbis_encoder_blockout(encoder);
@@ -355,34 +348,11 @@ vorbis_encoder_write(struct encoder *_encoder,
}
static size_t
-vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
+vorbis_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
- unsigned char *dest = _dest;
-
- ogg_page page;
- int ret = ogg_stream_pageout(&encoder->os, &page);
- if (ret == 0 && encoder->flush) {
- encoder->flush = false;
- ret = ogg_stream_flush(&encoder->os, &page);
-
- }
-
- if (ret == 0)
- return 0;
-
- assert(page.header_len > 0 || page.body_len > 0);
-
- size_t nbytes = (size_t)page.header_len + (size_t)page.body_len;
-
- if (nbytes > length)
- /* XXX better error handling */
- MPD_ERROR("buffer too small");
-
- memcpy(dest, page.header, page.header_len);
- memcpy(dest + page.header_len, page.body, page.body_len);
- return nbytes;
+ return encoder->stream.PageOut(dest, length);
}
static const char *
@@ -392,16 +362,16 @@ vorbis_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
}
const struct encoder_plugin vorbis_encoder_plugin = {
- .name = "vorbis",
- .init = vorbis_encoder_init,
- .finish = vorbis_encoder_finish,
- .open = vorbis_encoder_open,
- .close = vorbis_encoder_close,
- .end = vorbis_encoder_pre_tag,
- .flush = vorbis_encoder_flush,
- .pre_tag = vorbis_encoder_pre_tag,
- .tag = vorbis_encoder_tag,
- .write = vorbis_encoder_write,
- .read = vorbis_encoder_read,
- .get_mime_type = vorbis_encoder_get_mime_type,
+ "vorbis",
+ vorbis_encoder_init,
+ vorbis_encoder_finish,
+ vorbis_encoder_open,
+ vorbis_encoder_close,
+ vorbis_encoder_pre_tag,
+ vorbis_encoder_flush,
+ vorbis_encoder_pre_tag,
+ vorbis_encoder_tag,
+ vorbis_encoder_write,
+ vorbis_encoder_read,
+ vorbis_encoder_get_mime_type,
};
diff --git a/src/encoder/VorbisEncoderPlugin.hxx b/src/encoder/VorbisEncoderPlugin.hxx
new file mode 100644
index 00000000..4cddf1b1
--- /dev/null
+++ b/src/encoder/VorbisEncoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_ENCODER_VORBIS_H
+#define MPD_ENCODER_VORBIS_H
+
+extern const struct encoder_plugin vorbis_encoder_plugin;
+
+#endif
diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c
index e32588e2..db6503fb 100644
--- a/src/encoder/flac_encoder.c
+++ b/src/encoder/flac_encoder.c
@@ -22,14 +22,18 @@
#include "encoder_plugin.h"
#include "audio_format.h"
#include "pcm_buffer.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
#include <assert.h>
#include <string.h>
#include <FLAC/stream_encoder.h>
+#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
+#error libFLAC is too old
+#endif
+
struct flac_encoder {
struct encoder encoder;
@@ -98,8 +102,6 @@ static bool
flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
GError **error)
{
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-#else
if ( !FLAC__stream_encoder_set_compression_level(encoder->fse,
encoder->compression)) {
g_set_error(error, flac_encoder_quark(), 0,
@@ -107,7 +109,7 @@ flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
encoder->compression);
return false;
}
-#endif
+
if ( !FLAC__stream_encoder_set_channels(encoder->fse,
encoder->audio_format.channels)) {
g_set_error(error, flac_encoder_quark(), 0,
@@ -135,11 +137,7 @@ flac_encoder_setup(struct flac_encoder *encoder, unsigned bits_per_sample,
static FLAC__StreamEncoderWriteStatus
flac_write_callback(G_GNUC_UNUSED const FLAC__StreamEncoder *fse,
const FLAC__byte data[],
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
- unsigned bytes,
-#else
size_t bytes,
-#endif
G_GNUC_UNUSED unsigned samples,
G_GNUC_UNUSED unsigned current_frame, void *client_data)
{
@@ -209,24 +207,6 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
/* this immediately outputs data through callback */
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
- {
- FLAC__StreamEncoderState init_status;
-
- FLAC__stream_encoder_set_write_callback(encoder->fse,
- flac_write_callback);
-
- init_status = FLAC__stream_encoder_init(encoder->fse);
-
- if (init_status != FLAC__STREAM_ENCODER_OK) {
- g_set_error(error, flac_encoder_quark(), 0,
- "failed to initialize encoder: %s\n",
- FLAC__StreamEncoderStateString[init_status]);
- flac_encoder_close(_encoder);
- return false;
- }
- }
-#else
{
FLAC__StreamEncoderInitStatus init_status;
@@ -242,7 +222,6 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
return false;
}
}
-#endif
return true;
}
diff --git a/src/encoder/lame_encoder.c b/src/encoder/lame_encoder.c
index 3bb99ea2..53594d34 100644
--- a/src/encoder/lame_encoder.c
+++ b/src/encoder/lame_encoder.c
@@ -23,6 +23,9 @@
#include "audio_format.h"
#include <lame/lame.h>
+
+#include <glib.h>
+
#include <assert.h>
#include <string.h>
@@ -225,7 +228,7 @@ lame_encoder_close(struct encoder *_encoder)
static bool
lame_encoder_write(struct encoder *_encoder,
const void *data, size_t length,
- G_GNUC_UNUSED GError **error)
+ gcc_unused GError **error)
{
struct lame_encoder *encoder = (struct lame_encoder *)_encoder;
unsigned num_frames;
@@ -283,7 +286,7 @@ lame_encoder_read(struct encoder *_encoder, void *dest, size_t length)
}
static const char *
-lame_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
+lame_encoder_get_mime_type(gcc_unused struct encoder *_encoder)
{
return "audio/mpeg";
}
diff --git a/src/encoder/null_encoder.c b/src/encoder/null_encoder.c
index 48cdf139..b973adf7 100644
--- a/src/encoder/null_encoder.c
+++ b/src/encoder/null_encoder.c
@@ -20,8 +20,11 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
+#include "gcc.h"
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -34,15 +37,9 @@ struct null_encoder {
extern const struct encoder_plugin null_encoder_plugin;
-static inline GQuark
-null_encoder_quark(void)
-{
- return g_quark_from_static_string("null_encoder");
-}
-
static struct encoder *
-null_encoder_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error)
+null_encoder_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error)
{
struct null_encoder *encoder;
@@ -71,8 +68,8 @@ null_encoder_close(struct encoder *_encoder)
static bool
null_encoder_open(struct encoder *_encoder,
- G_GNUC_UNUSED struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error)
+ gcc_unused struct audio_format *audio_format,
+ gcc_unused GError **error)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
@@ -83,7 +80,7 @@ null_encoder_open(struct encoder *_encoder,
static bool
null_encoder_write(struct encoder *_encoder,
const void *data, size_t length,
- G_GNUC_UNUSED GError **error)
+ gcc_unused GError **error)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
diff --git a/src/encoder/twolame_encoder.c b/src/encoder/twolame_encoder.c
index 934b2ab2..ff184977 100644
--- a/src/encoder/twolame_encoder.c
+++ b/src/encoder/twolame_encoder.c
@@ -23,6 +23,9 @@
#include "audio_format.h"
#include <twolame.h>
+
+#include <glib.h>
+
#include <assert.h>
#include <string.h>
@@ -224,7 +227,7 @@ twolame_encoder_close(struct encoder *_encoder)
}
static bool
-twolame_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
+twolame_encoder_flush(struct encoder *_encoder, gcc_unused GError **error)
{
struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
@@ -235,7 +238,7 @@ twolame_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
static bool
twolame_encoder_write(struct encoder *_encoder,
const void *data, size_t length,
- G_GNUC_UNUSED GError **error)
+ gcc_unused GError **error)
{
struct twolame_encoder *encoder = (struct twolame_encoder *)_encoder;
unsigned num_frames;
@@ -289,7 +292,7 @@ twolame_encoder_read(struct encoder *_encoder, void *dest, size_t length)
}
static const char *
-twolame_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
+twolame_encoder_get_mime_type(gcc_unused struct encoder *_encoder)
{
return "audio/mpeg";
}
diff --git a/src/encoder/wave_encoder.c b/src/encoder/wave_encoder.c
index 9eeb4d51..9b3c21fc 100644
--- a/src/encoder/wave_encoder.c
+++ b/src/encoder/wave_encoder.c
@@ -20,8 +20,10 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
-#include "fifo_buffer.h"
-#include "growing_fifo.h"
+#include "util/fifo_buffer.h"
+#include "util/growing_fifo.h"
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -51,12 +53,6 @@ struct wave_header {
extern const struct encoder_plugin wave_encoder_plugin;
-static inline GQuark
-wave_encoder_quark(void)
-{
- return g_quark_from_static_string("wave_encoder");
-}
-
static void
fill_wave_header(struct wave_header *header, int channels, int bits,
int freq, int block_size)
@@ -85,8 +81,8 @@ fill_wave_header(struct wave_header *header, int channels, int bits,
}
static struct encoder *
-wave_encoder_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error)
+wave_encoder_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error)
{
struct wave_encoder *encoder;
@@ -106,8 +102,8 @@ wave_encoder_finish(struct encoder *_encoder)
static bool
wave_encoder_open(struct encoder *_encoder,
- G_GNUC_UNUSED struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error)
+ gcc_unused struct audio_format *audio_format,
+ gcc_unused GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
@@ -202,7 +198,7 @@ pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length)
static bool
wave_encoder_write(struct encoder *_encoder,
const void *src, size_t length,
- G_GNUC_UNUSED GError **error)
+ gcc_unused GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
@@ -261,7 +257,7 @@ wave_encoder_read(struct encoder *_encoder, void *dest, size_t length)
}
static const char *
-wave_encoder_get_mime_type(G_GNUC_UNUSED struct encoder *_encoder)
+wave_encoder_get_mime_type(gcc_unused struct encoder *_encoder)
{
return "audio/wav";
}
diff --git a/src/encoder_list.c b/src/encoder_list.c
index 2326c109..029b4be3 100644
--- a/src/encoder_list.c
+++ b/src/encoder_list.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,12 @@
#include "config.h"
#include "encoder_list.h"
#include "encoder_plugin.h"
+#include "encoder/VorbisEncoderPlugin.hxx"
+#include "encoder/OpusEncoderPlugin.hxx"
#include <string.h>
extern const struct encoder_plugin null_encoder_plugin;
-extern const struct encoder_plugin vorbis_encoder_plugin;
extern const struct encoder_plugin lame_encoder_plugin;
extern const struct encoder_plugin twolame_encoder_plugin;
extern const struct encoder_plugin wave_encoder_plugin;
@@ -35,6 +36,9 @@ const struct encoder_plugin *const encoder_plugins[] = {
#ifdef ENABLE_VORBIS_ENCODER
&vorbis_encoder_plugin,
#endif
+#ifdef HAVE_OPUS
+ &opus_encoder_plugin,
+#endif
#ifdef ENABLE_LAME_ENCODER
&lame_encoder_plugin,
#endif
diff --git a/src/encoder_list.h b/src/encoder_list.h
index fb1c9bf9..31663c75 100644
--- a/src/encoder_list.h
+++ b/src/encoder_list.h
@@ -30,6 +30,10 @@ extern const struct encoder_plugin *const encoder_plugins[];
(plugin = *encoder_plugin_iterator) != NULL; \
++encoder_plugin_iterator)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Looks up an encoder plugin by its name.
*
@@ -40,4 +44,8 @@ extern const struct encoder_plugin *const encoder_plugins[];
const struct encoder_plugin *
encoder_plugin_get(const char *name);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/encoder_plugin.h b/src/encoder_plugin.h
index 3a42d79f..e0748a13 100644
--- a/src/encoder_plugin.h
+++ b/src/encoder_plugin.h
@@ -20,7 +20,7 @@
#ifndef MPD_ENCODER_PLUGIN_H
#define MPD_ENCODER_PLUGIN_H
-#include <glib.h>
+#include "gerror.h"
#include <assert.h>
#include <stdbool.h>
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
new file mode 100644
index 00000000..05e70344
--- /dev/null
+++ b/src/event/BufferedSocket.cxx
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "BufferedSocket.hxx"
+#include "SocketError.hxx"
+#include "util/fifo_buffer.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+BufferedSocket::~BufferedSocket()
+{
+ if (input != nullptr)
+ fifo_buffer_free(input);
+}
+
+BufferedSocket::ssize_t
+BufferedSocket::DirectRead(void *data, size_t length)
+{
+ const auto nbytes = SocketMonitor::Read((char *)data, length);
+ if (gcc_likely(nbytes > 0))
+ return nbytes;
+
+ if (nbytes == 0) {
+ OnSocketClosed();
+ return -1;
+ }
+
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ return -1;
+}
+
+bool
+BufferedSocket::ReadToBuffer()
+{
+ assert(IsDefined());
+
+ if (input == nullptr)
+ input = fifo_buffer_new(8192);
+
+ size_t length;
+ void *buffer = fifo_buffer_write(input, &length);
+ assert(buffer != nullptr);
+
+ const auto nbytes = DirectRead(buffer, length);
+ if (nbytes > 0)
+ fifo_buffer_append(input, nbytes);
+
+ return nbytes >= 0;
+}
+
+bool
+BufferedSocket::ResumeInput()
+{
+ assert(IsDefined());
+
+ if (input == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ while (true) {
+ size_t length;
+ const void *data = fifo_buffer_read(input, &length);
+ if (data == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ const auto result = OnSocketInput(data, length);
+ switch (result) {
+ case InputResult::MORE:
+ if (fifo_buffer_is_full(input)) {
+ // TODO
+ OnSocketError(g_error_new_literal(g_quark_from_static_string("buffered_socket"),
+ 0, "Input buffer is full"));
+ return false;
+ }
+
+ ScheduleRead();
+ return true;
+
+ case InputResult::PAUSE:
+ CancelRead();
+ return true;
+
+ case InputResult::AGAIN:
+ continue;
+
+ case InputResult::CLOSED:
+ return false;
+ }
+ }
+}
+
+void
+BufferedSocket::ConsumeInput(size_t nbytes)
+{
+ assert(IsDefined());
+
+ fifo_buffer_consume(input, nbytes);
+}
+
+bool
+BufferedSocket::OnSocketReady(unsigned flags)
+{
+ assert(IsDefined());
+
+ if (gcc_unlikely(flags & (ERROR|HANGUP))) {
+ OnSocketClosed();
+ return false;
+ }
+
+ if (flags & READ) {
+ assert(input == nullptr || !fifo_buffer_is_full(input));
+
+ if (!ReadToBuffer() || !ResumeInput())
+ return false;
+
+ if (input == nullptr || !fifo_buffer_is_full(input))
+ ScheduleRead();
+ }
+
+ return true;
+}
diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx
new file mode 100644
index 00000000..86deb8d9
--- /dev/null
+++ b/src/event/BufferedSocket.hxx
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_BUFFERED_SOCKET_HXX
+#define MPD_BUFFERED_SOCKET_HXX
+
+#include "check.h"
+#include "SocketMonitor.hxx"
+#include "gcc.h"
+
+struct fifo_buffer;
+
+/**
+ * A #SocketMonitor specialization that adds an input buffer.
+ */
+class BufferedSocket : protected SocketMonitor {
+ fifo_buffer *input;
+
+public:
+ BufferedSocket(int _fd, EventLoop &_loop)
+ :SocketMonitor(_fd, _loop), input(nullptr) {
+ ScheduleRead();
+ }
+
+ ~BufferedSocket();
+
+ using SocketMonitor::IsDefined;
+ using SocketMonitor::Close;
+ using SocketMonitor::Write;
+
+private:
+ ssize_t DirectRead(void *data, size_t length);
+
+ /**
+ * Receive data from the socket to the input buffer.
+ *
+ * @return false if the socket has been closed
+ */
+ bool ReadToBuffer();
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ bool ResumeInput();
+
+ /**
+ * Mark a portion of the input buffer "consumed". Only
+ * allowed to be called from OnSocketInput(). This method
+ * does not invalidate the pointer passed to OnSocketInput()
+ * yet.
+ */
+ void ConsumeInput(size_t nbytes);
+
+ enum class InputResult {
+ /**
+ * The method was successful, and it is ready to
+ * read more data.
+ */
+ MORE,
+
+ /**
+ * The method does not want to get more data for now.
+ * It will call ResumeInput() when it's ready for
+ * more.
+ */
+ PAUSE,
+
+ /**
+ * The method wants to be called again immediately, if
+ * there's more data in the buffer.
+ */
+ AGAIN,
+
+ /**
+ * The method has closed the socket.
+ */
+ CLOSED,
+ };
+
+ virtual InputResult OnSocketInput(const void *data, size_t length) = 0;
+ virtual void OnSocketError(GError *error) = 0;
+ virtual void OnSocketClosed() = 0;
+
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx
new file mode 100644
index 00000000..a92cb68a
--- /dev/null
+++ b/src/event/FullyBufferedSocket.cxx
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FullyBufferedSocket.hxx"
+#include "SocketError.hxx"
+#include "util/fifo_buffer.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+FullyBufferedSocket::ssize_t
+FullyBufferedSocket::DirectWrite(const void *data, size_t length)
+{
+ const auto nbytes = SocketMonitor::Write((const char *)data, length);
+ if (gcc_unlikely(nbytes < 0)) {
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ Cancel();
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ }
+
+ return nbytes;
+}
+
+bool
+FullyBufferedSocket::WriteFromBuffer()
+{
+ assert(IsDefined());
+
+ size_t length;
+ const void *data = output.Read(&length);
+ if (data == nullptr) {
+ CancelWrite();
+ return true;
+ }
+
+ auto nbytes = DirectWrite(data, length);
+ if (gcc_unlikely(nbytes <= 0))
+ return nbytes == 0;
+
+ output.Consume(nbytes);
+
+ if (output.IsEmpty())
+ CancelWrite();
+
+ return true;
+}
+
+bool
+FullyBufferedSocket::Write(const void *data, size_t length)
+{
+ assert(IsDefined());
+
+#if 0
+ /* TODO: disabled because this would add overhead on some callers (the ones that often), but it may be useful */
+
+ if (output.IsEmpty()) {
+ /* try to write it directly first */
+ const auto nbytes = DirectWrite(data, length);
+ if (gcc_likely(nbytes > 0)) {
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ if (length == 0)
+ return true;
+ } else if (nbytes < 0)
+ return false;
+ }
+#endif
+
+ if (!output.Append(data, length)) {
+ // TODO
+ OnSocketError(g_error_new_literal(g_quark_from_static_string("buffered_socket"),
+ 0, "Output buffer is full"));
+ return false;
+ }
+
+ ScheduleWrite();
+ return true;
+}
+
+bool
+FullyBufferedSocket::OnSocketReady(unsigned flags)
+{
+ const bool was_empty = output.IsEmpty();
+ if (!BufferedSocket::OnSocketReady(flags))
+ return false;
+
+ if (was_empty && !output.IsEmpty())
+ /* just in case the OnSocketInput() method has added
+ data to the output buffer: try to send it now
+ instead of waiting for the next event loop
+ iteration */
+ flags |= WRITE;
+
+ if (flags & WRITE) {
+ assert(!output.IsEmpty());
+
+ if (!WriteFromBuffer())
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx
new file mode 100644
index 00000000..c67c2c78
--- /dev/null
+++ b/src/event/FullyBufferedSocket.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FULLY_BUFFERED_SOCKET_HXX
+#define MPD_FULLY_BUFFERED_SOCKET_HXX
+
+#include "check.h"
+#include "BufferedSocket.hxx"
+#include "util/PeakBuffer.hxx"
+#include "gcc.h"
+
+/**
+ * A #BufferedSocket specialization that adds an output buffer.
+ */
+class FullyBufferedSocket : protected BufferedSocket {
+ PeakBuffer output;
+
+public:
+ FullyBufferedSocket(int _fd, EventLoop &_loop,
+ size_t normal_size, size_t peak_size=0)
+ :BufferedSocket(_fd, _loop),
+ output(normal_size, peak_size) {
+ }
+
+ using BufferedSocket::IsDefined;
+ using BufferedSocket::Close;
+
+private:
+ ssize_t DirectWrite(const void *data, size_t length);
+
+ /**
+ * Send data from the output buffer to the socket.
+ *
+ * @return false if the socket has been closed
+ */
+ bool WriteFromBuffer();
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ bool Write(const void *data, size_t length);
+
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
new file mode 100644
index 00000000..72731ea2
--- /dev/null
+++ b/src/event/Loop.hxx
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_LOOP_HXX
+#define MPD_EVENT_LOOP_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <glib.h>
+
+class EventLoop {
+ GMainContext *context;
+ GMainLoop *loop;
+
+public:
+ EventLoop()
+ :context(g_main_context_new()),
+ loop(g_main_loop_new(context, false)) {}
+
+ struct Default {};
+ EventLoop(gcc_unused Default _dummy)
+ :context(g_main_context_ref(g_main_context_default())),
+ loop(g_main_loop_new(context, false)) {}
+
+ ~EventLoop() {
+ g_main_loop_unref(loop);
+ g_main_context_unref(context);
+ }
+
+ GMainContext *GetContext() {
+ return context;
+ }
+
+ void WakeUp() {
+ g_main_context_wakeup(context);
+ }
+
+ void Break() {
+ g_main_loop_quit(loop);
+ }
+
+ void Run() {
+ g_main_loop_run(loop);
+ }
+
+ guint AddIdle(GSourceFunc function, gpointer data) {
+ GSource *source = g_idle_source_new();
+ g_source_set_callback(source, function, data, NULL);
+ guint id = g_source_attach(source, GetContext());
+ g_source_unref(source);
+ return id;
+ }
+
+ GSource *AddTimeout(guint interval_ms,
+ GSourceFunc function, gpointer data) {
+ GSource *source = g_timeout_source_new(interval_ms);
+ g_source_set_callback(source, function, data, nullptr);
+ g_source_attach(source, GetContext());
+ return source;
+ }
+
+ GSource *AddTimeoutSeconds(guint interval_s,
+ GSourceFunc function, gpointer data) {
+ GSource *source = g_timeout_source_new_seconds(interval_s);
+ g_source_set_callback(source, function, data, nullptr);
+ g_source_attach(source, GetContext());
+ return source;
+ }
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
new file mode 100644
index 00000000..6f20b907
--- /dev/null
+++ b/src/event/MultiSocketMonitor.cxx
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MultiSocketMonitor.hxx"
+#include "Loop.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+/**
+ * The vtable for our GSource implementation. Unfortunately, we
+ * cannot declare it "const", because g_source_new() takes a non-const
+ * pointer, for whatever reason.
+ */
+static GSourceFuncs multi_socket_monitor_source_funcs = {
+ MultiSocketMonitor::Prepare,
+ MultiSocketMonitor::Check,
+ MultiSocketMonitor::Dispatch,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
+ :loop(_loop),
+ source((Source *)g_source_new(&multi_socket_monitor_source_funcs,
+ sizeof(*source))) {
+ source->monitor = this;
+
+ g_source_attach(&source->base, loop.GetContext());
+}
+
+MultiSocketMonitor::~MultiSocketMonitor()
+{
+ g_source_destroy(&source->base);
+ g_source_unref(&source->base);
+ source = nullptr;
+}
+
+bool
+MultiSocketMonitor::Check() const
+{
+ if (CheckSockets())
+ return true;
+
+ for (const auto &i : fds)
+ if (i.revents != 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * GSource methods
+ *
+ */
+
+gboolean
+MultiSocketMonitor::Prepare(GSource *_source, gint *timeout_r)
+{
+ Source &source = *(Source *)_source;
+ MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Prepare(timeout_r);
+}
+
+gboolean
+MultiSocketMonitor::Check(GSource *_source)
+{
+ const Source &source = *(const Source *)_source;
+ const MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Check();
+}
+
+gboolean
+MultiSocketMonitor::Dispatch(GSource *_source,
+ gcc_unused GSourceFunc callback,
+ gcc_unused gpointer user_data)
+{
+ Source &source = *(Source *)_source;
+ MultiSocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ monitor.Dispatch();
+ return true;
+}
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
new file mode 100644
index 00000000..bf0a221a
--- /dev/null
+++ b/src/event/MultiSocketMonitor.hxx
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_MULTI_SOCKET_MONITOR_HXX
+#define MPD_MULTI_SOCKET_MONITOR_HXX
+
+#include "check.h"
+#include "gcc.h"
+#include "glib_compat.h"
+
+#include <glib.h>
+
+#include <forward_list>
+
+#include <assert.h>
+
+#ifdef WIN32
+/* ERRORis a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+/**
+ * Monitor multiple sockets.
+ */
+class MultiSocketMonitor {
+ struct Source {
+ GSource base;
+
+ MultiSocketMonitor *monitor;
+ };
+
+ EventLoop &loop;
+ Source *source;
+ std::forward_list<GPollFD> fds;
+
+public:
+ static constexpr unsigned READ = G_IO_IN;
+ static constexpr unsigned WRITE = G_IO_OUT;
+ static constexpr unsigned ERROR = G_IO_ERR;
+ static constexpr unsigned HANGUP = G_IO_HUP;
+
+ MultiSocketMonitor(EventLoop &_loop);
+ ~MultiSocketMonitor();
+
+public:
+ gcc_pure
+ gint64 GetTime() const {
+ return g_source_get_time(&source->base);
+ }
+
+ void InvalidateSockets() {
+ /* no-op because GLib always calls the GSource's
+ "prepare" method before each poll() anyway */
+ }
+
+ void AddSocket(int fd, unsigned events) {
+ fds.push_front({fd, gushort(events), 0});
+ g_source_add_poll(&source->base, &fds.front());
+ }
+
+ template<typename E>
+ void UpdateSocketList(E &&e) {
+ for (auto prev = fds.before_begin(), end = fds.end(),
+ i = std::next(prev);
+ i != end; i = std::next(prev)) {
+ assert(i->events != 0);
+
+ unsigned events = e(i->fd);
+ if (events != 0) {
+ i->events = events;
+ prev = i;
+ } else {
+ g_source_remove_poll(&source->base, &*i);
+ fds.erase_after(prev);
+ }
+ }
+ }
+
+protected:
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) {}
+ virtual bool CheckSockets() const { return false; }
+ virtual void DispatchSockets() = 0;
+
+public:
+ /* GSource callbacks */
+ static gboolean Prepare(GSource *source, gint *timeout_r);
+ static gboolean Check(GSource *source);
+ static gboolean Dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data);
+
+private:
+ bool Prepare(gint *timeout_r) {
+ PrepareSockets(timeout_r);
+ return false;
+ }
+
+ bool Check() const;
+
+ void Dispatch() {
+ DispatchSockets();
+ }
+};
+
+#endif
diff --git a/src/event/ServerSocket.cxx b/src/event/ServerSocket.cxx
new file mode 100644
index 00000000..119bfe1d
--- /dev/null
+++ b/src/event/ServerSocket.cxx
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_STRUCT_UCRED
+#define _GNU_SOURCE 1
+#endif
+
+#include "ServerSocket.hxx"
+#include "SocketUtil.hxx"
+#include "SocketError.hxx"
+#include "event/SocketMonitor.hxx"
+#include "resolver.h"
+#include "fd_util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef WIN32
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "listen"
+
+#define DEFAULT_PORT 6600
+
+class OneServerSocket final : private SocketMonitor {
+ ServerSocket &parent;
+
+ const unsigned serial;
+
+ char *path;
+
+ size_t address_length;
+ struct sockaddr *address;
+
+public:
+ OneServerSocket(EventLoop &_loop, ServerSocket &_parent,
+ unsigned _serial,
+ const struct sockaddr *_address,
+ size_t _address_length)
+ :SocketMonitor(_loop),
+ parent(_parent), serial(_serial),
+ path(nullptr),
+ address_length(_address_length),
+ address((sockaddr *)g_memdup(_address, _address_length))
+ {
+ assert(_address != nullptr);
+ assert(_address_length > 0);
+ }
+
+ OneServerSocket(const OneServerSocket &other) = delete;
+ OneServerSocket &operator=(const OneServerSocket &other) = delete;
+
+ ~OneServerSocket() {
+ g_free(path);
+ g_free(address);
+ }
+
+ unsigned GetSerial() const {
+ return serial;
+ }
+
+ void SetPath(const char *_path) {
+ assert(path == nullptr);
+
+ path = g_strdup(_path);
+ }
+
+ bool Open(GError **error_r);
+
+ using SocketMonitor::IsDefined;
+ using SocketMonitor::Close;
+
+ char *ToString() const;
+
+ void SetFD(int _fd) {
+ SocketMonitor::Open(_fd);
+ SocketMonitor::ScheduleRead();
+ }
+
+ void Accept();
+
+private:
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+static GQuark
+server_socket_quark(void)
+{
+ return g_quark_from_static_string("server_socket");
+}
+
+/**
+ * Wraper for sockaddr_to_string() which never fails.
+ */
+char *
+OneServerSocket::ToString() const
+{
+ char *p = sockaddr_to_string(address, address_length, nullptr);
+ if (p == nullptr)
+ p = g_strdup("[unknown]");
+ return p;
+}
+
+static int
+get_remote_uid(int fd)
+{
+#ifdef HAVE_STRUCT_UCRED
+ struct ucred cred;
+ socklen_t len = sizeof (cred);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0)
+ return 0;
+
+ return cred.uid;
+#else
+#ifdef HAVE_GETPEEREID
+ uid_t euid;
+ gid_t egid;
+
+ if (getpeereid(fd, &euid, &egid) == 0)
+ return euid;
+#else
+ (void)fd;
+#endif
+ return -1;
+#endif
+}
+
+inline void
+OneServerSocket::Accept()
+{
+ struct sockaddr_storage peer_address;
+ size_t peer_address_length = sizeof(peer_address);
+ int peer_fd =
+ accept_cloexec_nonblock(Get(), (struct sockaddr*)&peer_address,
+ &peer_address_length);
+ if (peer_fd < 0) {
+ const SocketErrorMessage msg;
+ g_warning("accept() failed: %s", (const char *)msg);
+ return;
+ }
+
+ if (socket_keepalive(peer_fd)) {
+ const SocketErrorMessage msg;
+ g_warning("Could not set TCP keepalive option: %s",
+ (const char *)msg);
+ }
+
+ parent.OnAccept(peer_fd,
+ (const sockaddr &)peer_address,
+ peer_address_length, get_remote_uid(peer_fd));
+}
+
+bool
+OneServerSocket::OnSocketReady(gcc_unused unsigned flags)
+{
+ Accept();
+ return true;
+}
+
+inline bool
+OneServerSocket::Open(GError **error_r)
+{
+ assert(!IsDefined());
+
+ int _fd = socket_bind_listen(address->sa_family,
+ SOCK_STREAM, 0,
+ address, address_length, 5,
+ error_r);
+ if (_fd < 0)
+ return false;
+
+ /* allow everybody to connect */
+
+ if (path != nullptr)
+ chmod(path, 0666);
+
+ /* register in the GLib main loop */
+
+ SetFD(_fd);
+
+ return true;
+}
+
+ServerSocket::ServerSocket(EventLoop &_loop)
+ :loop(_loop), next_serial(1) {}
+
+/* this is just here to allow the OneServerSocket forward
+ declaration */
+ServerSocket::~ServerSocket() {}
+
+bool
+ServerSocket::Open(GError **error_r)
+{
+ OneServerSocket *good = nullptr, *bad = nullptr;
+ GError *last_error = nullptr;
+
+ for (auto &i : sockets) {
+ assert(i.GetSerial() > 0);
+ assert(good == nullptr || i.GetSerial() <= good->GetSerial());
+
+ if (bad != nullptr && i.GetSerial() != bad->GetSerial()) {
+ Close();
+ g_propagate_error(error_r, last_error);
+ return false;
+ }
+
+ GError *error = nullptr;
+ if (!i.Open(&error)) {
+ if (good != nullptr && good->GetSerial() == i.GetSerial()) {
+ char *address_string = i.ToString();
+ char *good_string = good->ToString();
+ g_warning("bind to '%s' failed: %s "
+ "(continuing anyway, because "
+ "binding to '%s' succeeded)",
+ address_string, error->message,
+ good_string);
+ g_free(address_string);
+ g_free(good_string);
+ g_error_free(error);
+ } else if (bad == nullptr) {
+ bad = &i;
+
+ char *address_string = i.ToString();
+ g_propagate_prefixed_error(&last_error, error,
+ "Failed to bind to '%s': ",
+ address_string);
+ g_free(address_string);
+ } else
+ g_error_free(error);
+ continue;
+ }
+
+ /* mark this socket as "good", and clear previous
+ errors */
+
+ good = &i;
+
+ if (bad != nullptr) {
+ bad = nullptr;
+ g_error_free(last_error);
+ last_error = nullptr;
+ }
+ }
+
+ if (bad != nullptr) {
+ Close();
+ g_propagate_error(error_r, last_error);
+ return false;
+ }
+
+ return true;
+}
+
+void
+ServerSocket::Close()
+{
+ for (auto &i : sockets)
+ if (i.IsDefined())
+ i.Close();
+}
+
+OneServerSocket &
+ServerSocket::AddAddress(const sockaddr &address, size_t address_length)
+{
+ sockets.emplace_front(loop, *this, next_serial,
+ &address, address_length);
+
+ return sockets.front();
+}
+
+bool
+ServerSocket::AddFD(int fd, GError **error_r)
+{
+ assert(fd >= 0);
+
+ struct sockaddr_storage address;
+ socklen_t address_length = sizeof(address);
+ if (getsockname(fd, (struct sockaddr *)&address,
+ &address_length) < 0) {
+ SetSocketError(error_r);
+ g_prefix_error(error_r, "Failed to get socket address");
+ return false;
+ }
+
+ OneServerSocket &s = AddAddress((const sockaddr &)address,
+ address_length);
+ s.SetFD(fd);
+
+ return true;
+}
+
+#ifdef HAVE_TCP
+
+inline void
+ServerSocket::AddPortIPv4(unsigned port)
+{
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ AddAddress((const sockaddr &)sin, sizeof(sin));
+}
+
+#ifdef HAVE_IPV6
+inline void
+ServerSocket::AddPortIPv6(unsigned port)
+{
+ struct sockaddr_in6 sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_port = htons(port);
+ sin.sin6_family = AF_INET6;
+
+ AddAddress((const sockaddr &)sin, sizeof(sin));
+}
+#endif /* HAVE_IPV6 */
+
+#endif /* HAVE_TCP */
+
+bool
+ServerSocket::AddPort(unsigned port, GError **error_r)
+{
+#ifdef HAVE_TCP
+ if (port == 0 || port > 0xffff) {
+ g_set_error(error_r, server_socket_quark(), 0,
+ "Invalid TCP port");
+ return false;
+ }
+
+#ifdef HAVE_IPV6
+ AddPortIPv6(port);
+#endif
+ AddPortIPv4(port);
+
+ ++next_serial;
+
+ return true;
+#else /* HAVE_TCP */
+ (void)port;
+
+ g_set_error(error_r, server_socket_quark(), 0,
+ "TCP support is disabled");
+ return false;
+#endif /* HAVE_TCP */
+}
+
+bool
+ServerSocket::AddHost(const char *hostname, unsigned port, GError **error_r)
+{
+#ifdef HAVE_TCP
+ struct addrinfo *ai = resolve_host_port(hostname, port,
+ AI_PASSIVE, SOCK_STREAM,
+ error_r);
+ if (ai == nullptr)
+ return false;
+
+ for (const struct addrinfo *i = ai; i != nullptr; i = i->ai_next)
+ AddAddress(*i->ai_addr, i->ai_addrlen);
+
+ freeaddrinfo(ai);
+
+ ++next_serial;
+
+ return true;
+#else /* HAVE_TCP */
+ (void)hostname;
+ (void)port;
+
+ g_set_error(error_r, server_socket_quark(), 0,
+ "TCP support is disabled");
+ return false;
+#endif /* HAVE_TCP */
+}
+
+bool
+ServerSocket::AddPath(const char *path, GError **error_r)
+{
+#ifdef HAVE_UN
+ struct sockaddr_un s_un;
+
+ size_t path_length = strlen(path);
+ if (path_length >= sizeof(s_un.sun_path)) {
+ g_set_error(error_r, server_socket_quark(), 0,
+ "UNIX socket path is too long");
+ return false;
+ }
+
+ unlink(path);
+
+ s_un.sun_family = AF_UNIX;
+ memcpy(s_un.sun_path, path, path_length + 1);
+
+ OneServerSocket &s = AddAddress((const sockaddr &)s_un, sizeof(s_un));
+ s.SetPath(path);
+
+ return true;
+#else /* !HAVE_UN */
+ (void)path;
+
+ g_set_error(error_r, server_socket_quark(), 0,
+ "UNIX domain socket support is disabled");
+ return false;
+#endif /* !HAVE_UN */
+}
+
diff --git a/src/event/ServerSocket.hxx b/src/event/ServerSocket.hxx
new file mode 100644
index 00000000..600cdf8a
--- /dev/null
+++ b/src/event/ServerSocket.hxx
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SERVER_SOCKET_HXX
+#define MPD_SERVER_SOCKET_HXX
+
+#include "gerror.h"
+
+#include <forward_list>
+
+#include <stddef.h>
+
+struct sockaddr;
+class EventLoop;
+
+typedef void (*server_socket_callback_t)(int fd,
+ const struct sockaddr *address,
+ size_t address_length, int uid,
+ void *ctx);
+
+class OneServerSocket;
+
+class ServerSocket {
+ friend class OneServerSocket;
+
+ EventLoop &loop;
+
+ std::forward_list<OneServerSocket> sockets;
+
+ unsigned next_serial;
+
+public:
+ ServerSocket(EventLoop &_loop);
+ ~ServerSocket();
+
+ EventLoop &GetEventLoop() {
+ return loop;
+ }
+
+private:
+ OneServerSocket &AddAddress(const sockaddr &address, size_t length);
+
+ /**
+ * Add a listener on a port on all IPv4 interfaces.
+ *
+ * @param port the TCP port
+ */
+ void AddPortIPv4(unsigned port);
+
+ /**
+ * Add a listener on a port on all IPv6 interfaces.
+ *
+ * @param port the TCP port
+ */
+ void AddPortIPv6(unsigned port);
+
+public:
+ /**
+ * Add a listener on a port on all interfaces.
+ *
+ * @param port the TCP port
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddPort(unsigned port, GError **error_r);
+
+ /**
+ * Resolves a host name, and adds listeners on all addresses in the
+ * result set.
+ *
+ * @param hostname the host name to be resolved
+ * @param port the TCP port
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddHost(const char *hostname, unsigned port, GError **error_r);
+
+ /**
+ * Add a listener on a Unix domain socket.
+ *
+ * @param path the absolute socket path
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddPath(const char *path, GError **error_r);
+
+ /**
+ * Add a socket descriptor that is accepting connections. After this
+ * has been called, don't call server_socket_open(), because the
+ * socket is already open.
+ */
+ bool AddFD(int fd, GError **error_r);
+
+ bool Open(GError **error_r);
+ void Close();
+
+protected:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) = 0;
+};
+
+#endif
diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx
new file mode 100644
index 00000000..6efa6964
--- /dev/null
+++ b/src/event/SocketMonitor.cxx
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "SocketMonitor.hxx"
+#include "Loop.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+/*
+ * GSource methods
+ *
+ */
+
+gboolean
+SocketMonitor::Prepare(gcc_unused GSource *source, gcc_unused gint *timeout_r)
+{
+ return false;
+}
+
+gboolean
+SocketMonitor::Check(GSource *_source)
+{
+ const Source &source = *(const Source *)_source;
+ const SocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ return monitor.Check();
+}
+
+gboolean
+SocketMonitor::Dispatch(GSource *_source,
+ gcc_unused GSourceFunc callback,
+ gcc_unused gpointer user_data)
+{
+ Source &source = *(Source *)_source;
+ SocketMonitor &monitor = *source.monitor;
+ assert(_source == &monitor.source->base);
+
+ monitor.Dispatch();
+ return true;
+}
+
+/**
+ * The vtable for our GSource implementation. Unfortunately, we
+ * cannot declare it "const", because g_source_new() takes a non-const
+ * pointer, for whatever reason.
+ */
+static GSourceFuncs socket_monitor_source_funcs = {
+ SocketMonitor::Prepare,
+ SocketMonitor::Check,
+ SocketMonitor::Dispatch,
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+SocketMonitor::SocketMonitor(int _fd, EventLoop &_loop)
+ :fd(-1), loop(_loop),
+ source(nullptr) {
+ assert(_fd >= 0);
+
+ Open(_fd);
+}
+
+SocketMonitor::~SocketMonitor()
+{
+ if (IsDefined())
+ Close();
+}
+
+void
+SocketMonitor::Open(int _fd)
+{
+ assert(fd < 0);
+ assert(source == nullptr);
+ assert(_fd >= 0);
+
+ fd = _fd;
+ poll = {fd, 0, 0};
+
+ source = (Source *)g_source_new(&socket_monitor_source_funcs,
+ sizeof(*source));
+ source->monitor = this;
+
+ g_source_attach(&source->base, loop.GetContext());
+ g_source_add_poll(&source->base, &poll);
+}
+
+int
+SocketMonitor::Steal()
+{
+ assert(IsDefined());
+
+ Cancel();
+
+ int result = fd;
+ fd = -1;
+
+ g_source_destroy(&source->base);
+ g_source_unref(&source->base);
+ source = nullptr;
+
+ return result;
+}
+
+void
+SocketMonitor::Close()
+{
+ close_socket(Steal());
+}
+
+SocketMonitor::ssize_t
+SocketMonitor::Read(void *data, size_t length)
+{
+ int flags = 0;
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ return recv(Get(), (char *)data, length, flags);
+}
+
+SocketMonitor::ssize_t
+SocketMonitor::Write(const void *data, size_t length)
+{
+ int flags = 0;
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ return send(Get(), (const char *)data, length, flags);
+}
+
+void
+SocketMonitor::CommitEventFlags()
+{
+ loop.WakeUp();
+}
diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx
new file mode 100644
index 00000000..c60b8efd
--- /dev/null
+++ b/src/event/SocketMonitor.hxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SOCKET_MONITOR_HXX
+#define MPD_SOCKET_MONITOR_HXX
+
+#include "check.h"
+
+#include <glib.h>
+
+#include <type_traits>
+
+#include <assert.h>
+#include <stddef.h>
+
+#ifdef WIN32
+/* ERRORis a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+class SocketMonitor {
+ struct Source {
+ GSource base;
+
+ SocketMonitor *monitor;
+ };
+
+ int fd;
+ EventLoop &loop;
+ Source *source;
+ GPollFD poll;
+
+public:
+ static constexpr unsigned READ = G_IO_IN;
+ static constexpr unsigned WRITE = G_IO_OUT;
+ static constexpr unsigned ERROR = G_IO_ERR;
+ static constexpr unsigned HANGUP = G_IO_HUP;
+
+ typedef std::make_signed<size_t>::type ssize_t;
+
+ SocketMonitor(EventLoop &_loop)
+ :fd(-1), loop(_loop), source(nullptr) {}
+
+ SocketMonitor(int _fd, EventLoop &_loop);
+
+ ~SocketMonitor();
+
+ bool IsDefined() const {
+ return fd >= 0;
+ }
+
+ int Get() const {
+ assert(IsDefined());
+
+ return fd;
+ }
+
+ void Open(int _fd);
+
+ /**
+ * "Steal" the socket descriptor. This abandons the socket
+ * and puts the responsibility for closing it to the caller.
+ */
+ int Steal();
+
+ void Close();
+
+ void Schedule(unsigned flags) {
+ poll.events = flags;
+ poll.revents &= flags;
+ CommitEventFlags();
+ }
+
+ void Cancel() {
+ poll.events = 0;
+ CommitEventFlags();
+ }
+
+ void ScheduleRead() {
+ poll.events |= READ|HANGUP|ERROR;
+ CommitEventFlags();
+ }
+
+ void ScheduleWrite() {
+ poll.events |= WRITE;
+ CommitEventFlags();
+ }
+
+ void CancelRead() {
+ poll.events &= ~(READ|HANGUP|ERROR);
+ CommitEventFlags();
+ }
+
+ void CancelWrite() {
+ poll.events &= ~WRITE;
+ CommitEventFlags();
+ }
+
+ ssize_t Read(void *data, size_t length);
+ ssize_t Write(const void *data, size_t length);
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ virtual bool OnSocketReady(unsigned flags) = 0;
+
+public:
+ /* GSource callbacks */
+ static gboolean Prepare(GSource *source, gint *timeout_r);
+ static gboolean Check(GSource *source);
+ static gboolean Dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data);
+
+private:
+ void CommitEventFlags();
+
+ bool Check() const {
+ return (poll.revents & poll.events) != 0;
+ }
+
+ void Dispatch() {
+ OnSocketReady(poll.revents & poll.events);
+ }
+};
+
+#endif
diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx
new file mode 100644
index 00000000..e0bf997a
--- /dev/null
+++ b/src/event/TimeoutMonitor.cxx
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "TimeoutMonitor.hxx"
+#include "Loop.hxx"
+
+void
+TimeoutMonitor::Cancel()
+{
+ if (source != nullptr) {
+ g_source_destroy(source);
+ g_source_unref(source);
+ source = nullptr;
+ }
+}
+
+void
+TimeoutMonitor::Schedule(unsigned ms)
+{
+ Cancel();
+ source = loop.AddTimeout(ms, Callback, this);
+}
+
+void
+TimeoutMonitor::ScheduleSeconds(unsigned s)
+{
+ Cancel();
+ source = loop.AddTimeoutSeconds(s, Callback, this);
+}
+
+bool
+TimeoutMonitor::Run()
+{
+ bool result = OnTimeout();
+ if (!result && source != nullptr) {
+ g_source_unref(source);
+ source = nullptr;
+ }
+
+ return result;
+}
+
+gboolean
+TimeoutMonitor::Callback(gpointer data)
+{
+ TimeoutMonitor &monitor = *(TimeoutMonitor *)data;
+ return monitor.Run();
+}
diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx
new file mode 100644
index 00000000..6914bcb6
--- /dev/null
+++ b/src/event/TimeoutMonitor.hxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SOCKET_TIMEOUT_MONITOR_HXX
+#define MPD_SOCKET_TIMEOUT_MONITOR_HXX
+
+#include "check.h"
+
+#include <glib.h>
+
+class EventLoop;
+
+class TimeoutMonitor {
+ EventLoop &loop;
+ GSource *source;
+
+public:
+ TimeoutMonitor(EventLoop &_loop)
+ :loop(_loop), source(nullptr) {}
+
+ ~TimeoutMonitor() {
+ Cancel();
+ }
+
+ bool IsActive() const {
+ return source != nullptr;
+ }
+
+ void Schedule(unsigned ms);
+ void ScheduleSeconds(unsigned s);
+ void Cancel();
+
+protected:
+ /**
+ * @return true reschedules the timeout again
+ */
+ virtual bool OnTimeout() = 0;
+
+private:
+ bool Run();
+ static gboolean Callback(gpointer data);
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/WakeFD.cxx b/src/event/WakeFD.cxx
new file mode 100644
index 00000000..1a84f564
--- /dev/null
+++ b/src/event/WakeFD.cxx
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "WakeFD.hxx"
+#include "fd_util.h"
+#include "gcc.h"
+
+#include <unistd.h>
+
+#ifdef WIN32
+#include <ws2tcpip.h>
+#include <winsock2.h>
+#include <cstring> /* for memset() */
+#endif
+
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+#ifdef WIN32
+static bool PoorSocketPair(int fd[2]);
+#endif
+
+bool
+WakeFD::Create()
+{
+ assert(fds[0] == -1);
+ assert(fds[1] == -1);
+
+#ifdef WIN32
+ return PoorSocketPair(fds);
+#else
+#ifdef HAVE_EVENTFD
+ fds[0] = eventfd_cloexec_nonblock(0, 0);
+ if (fds[0] >= 0) {
+ fds[1] = -2;
+ return true;
+ }
+#endif
+ return pipe_cloexec_nonblock(fds) >= 0;
+#endif
+}
+
+void
+WakeFD::Destroy()
+{
+#ifdef WIN32
+ closesocket(fds[0]);
+ closesocket(fds[1]);
+#else
+ close(fds[0]);
+#ifdef HAVE_EVENTFD
+ if (!IsEventFD())
+#endif
+ close(fds[1]);
+#endif
+
+#ifndef NDEBUG
+ fds[0] = -1;
+ fds[1] = -1;
+#endif
+}
+
+bool
+WakeFD::Read()
+{
+ assert(fds[0] >= 0);
+
+#ifdef WIN32
+ assert(fds[1] >= 0);
+ char buffer[256];
+ return recv(fds[0], buffer, sizeof(buffer), 0) > 0;
+#else
+
+#ifdef HAVE_EVENTFD
+ if (IsEventFD()) {
+ eventfd_t value;
+ return read(fds[0], &value,
+ sizeof(value)) == (ssize_t)sizeof(value);
+ }
+#endif
+
+ assert(fds[1] >= 0);
+
+ char buffer[256];
+ return read(fds[0], buffer, sizeof(buffer)) > 0;
+#endif
+}
+
+void
+WakeFD::Write()
+{
+ assert(fds[0] >= 0);
+
+#ifdef WIN32
+ assert(fds[1] >= 0);
+
+ send(fds[1], "", 1, 0);
+#else
+
+#ifdef HAVE_EVENTFD
+ if (IsEventFD()) {
+ static constexpr eventfd_t value = 1;
+ gcc_unused ssize_t nbytes =
+ write(fds[0], &value, sizeof(value));
+ return;
+ }
+#endif
+
+ assert(fds[1] >= 0);
+
+ gcc_unused ssize_t nbytes = write(fds[1], "", 1);
+#endif
+}
+
+#ifdef WIN32
+
+static void SafeCloseSocket(SOCKET s)
+{
+ int error = WSAGetLastError();
+ closesocket(s);
+ WSASetLastError(error);
+}
+
+/* Our poor man's socketpair() implementation
+ * Due to limited protocol/address family support and primitive error handling
+ * it's better to keep this as a private implementation detail of WakeFD
+ * rather than wide-available API.
+ */
+static bool PoorSocketPair(int fd[2])
+{
+ assert (fd != nullptr);
+
+ SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listen_socket == INVALID_SOCKET)
+ return false;
+
+ sockaddr_in address;
+ std::memset(&address, 0, sizeof(address));
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ int ret = bind(listen_socket,
+ reinterpret_cast<sockaddr*>(&address),
+ sizeof(address));
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ ret = listen(listen_socket, 1);
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ int address_len = sizeof(address);
+ ret = getsockname(listen_socket,
+ reinterpret_cast<sockaddr*>(&address),
+ &address_len);
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ SOCKET socket0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (socket0 == INVALID_SOCKET) {
+ SafeCloseSocket(listen_socket);
+ return false;
+ }
+
+ ret = connect(socket0,
+ reinterpret_cast<sockaddr*>(&address),
+ sizeof(address));
+
+ if (ret < 0) {
+ SafeCloseSocket(listen_socket);
+ SafeCloseSocket(socket0);
+ return false;
+ }
+
+ SOCKET socket1 = accept(listen_socket, nullptr, nullptr);
+ if (socket1 == INVALID_SOCKET) {
+ SafeCloseSocket(listen_socket);
+ SafeCloseSocket(socket0);
+ return false;
+ }
+
+ SafeCloseSocket(listen_socket);
+
+ u_long non_block = 1;
+ if (ioctlsocket(socket0, FIONBIO, &non_block) < 0
+ || ioctlsocket(socket1, FIONBIO, &non_block) < 0) {
+ SafeCloseSocket(socket0);
+ SafeCloseSocket(socket1);
+ return false;
+ }
+
+ fd[0] = static_cast<int>(socket0);
+ fd[1] = static_cast<int>(socket1);
+
+ return true;
+}
+
+#endif
diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx
new file mode 100644
index 00000000..15b66b4c
--- /dev/null
+++ b/src/event/WakeFD.hxx
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_WAKE_FD_HXX
+#define MPD_WAKE_FD_HXX
+
+#include "check.h"
+
+#include <assert.h>
+
+/**
+ * This class can be used to wake up an I/O event loop.
+ *
+ * For optimization purposes, this class does not have a constructor
+ * or a destructor.
+ */
+class WakeFD {
+ int fds[2];
+
+public:
+#ifdef NDEBUG
+ WakeFD() = default;
+#else
+ WakeFD():fds{-1, -1} {};
+#endif
+
+ WakeFD(const WakeFD &other) = delete;
+ WakeFD &operator=(const WakeFD &other) = delete;
+
+ bool Create();
+ void Destroy();
+
+ int Get() const {
+ assert(fds[0] >= 0);
+#ifndef HAVE_EVENTFD
+ assert(fds[1] >= 0);
+#endif
+
+ return fds[0];
+ }
+
+ /**
+ * Checks if Write() was called at least once since the last
+ * Read() call.
+ */
+ bool Read();
+
+ /**
+ * Wakes up the reader. Multiple calls to this function will
+ * be combined to one wakeup.
+ */
+ void Write();
+
+private:
+#ifdef HAVE_EVENTFD
+ bool IsEventFD() {
+ assert(fds[0] >= 0);
+
+ return fds[1] == -2;
+ }
+#endif
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event_pipe.c b/src/event_pipe.c
deleted file mode 100644
index d5c3b956..00000000
--- a/src/event_pipe.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "event_pipe.h"
-#include "fd_util.h"
-#include "mpd_error.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <glib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef WIN32
-/* for _O_BINARY */
-#include <fcntl.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "event_pipe"
-
-static int event_pipe[2];
-static GIOChannel *event_channel;
-static guint event_pipe_source_id;
-static GMutex *event_pipe_mutex;
-static bool pipe_events[PIPE_EVENT_MAX];
-static event_pipe_callback_t event_pipe_callbacks[PIPE_EVENT_MAX];
-
-/**
- * Invoke the callback for a certain event.
- */
-static void
-event_pipe_invoke(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] != NULL);
-
- event_pipe_callbacks[event]();
-}
-
-static gboolean
-main_notify_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- G_GNUC_UNUSED gpointer data)
-{
- char buffer[256];
- gsize bytes_read;
- GError *error = NULL;
- GIOStatus status = g_io_channel_read_chars(event_channel,
- buffer, sizeof(buffer),
- &bytes_read, &error);
- if (status == G_IO_STATUS_ERROR)
- MPD_ERROR("error reading from pipe: %s", error->message);
-
- bool events[PIPE_EVENT_MAX];
- g_mutex_lock(event_pipe_mutex);
- memcpy(events, pipe_events, sizeof(events));
- memset(pipe_events, 0, sizeof(pipe_events));
- g_mutex_unlock(event_pipe_mutex);
-
- for (unsigned i = 0; i < PIPE_EVENT_MAX; ++i)
- if (events[i])
- /* invoke the event handler */
- event_pipe_invoke(i);
-
- return true;
-}
-
-void event_pipe_init(void)
-{
- GIOChannel *channel;
- int ret;
-
- ret = pipe_cloexec_nonblock(event_pipe);
- if (ret < 0)
- MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
-
-#ifndef G_OS_WIN32
- channel = g_io_channel_unix_new(event_pipe[0]);
-#else
- channel = g_io_channel_win32_new_fd(event_pipe[0]);
-#endif
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, false);
-
- event_pipe_source_id = g_io_add_watch(channel, G_IO_IN,
- main_notify_event, NULL);
-
- event_channel = channel;
-
- event_pipe_mutex = g_mutex_new();
-}
-
-void event_pipe_deinit(void)
-{
- g_mutex_free(event_pipe_mutex);
-
- g_source_remove(event_pipe_source_id);
- g_io_channel_unref(event_channel);
-
-#ifndef WIN32
- /* By some strange reason this call hangs on Win32 */
- close(event_pipe[0]);
-#endif
- close(event_pipe[1]);
-}
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] == NULL);
-
- event_pipe_callbacks[event] = callback;
-}
-
-void event_pipe_emit(enum pipe_event event)
-{
- ssize_t w;
-
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- g_mutex_lock(event_pipe_mutex);
- if (pipe_events[event]) {
- /* already set: don't write */
- g_mutex_unlock(event_pipe_mutex);
- return;
- }
-
- pipe_events[event] = true;
- g_mutex_unlock(event_pipe_mutex);
-
- w = write(event_pipe[1], "", 1);
- if (w < 0 && errno != EAGAIN && errno != EINTR)
- MPD_ERROR("error writing to pipe: %s", strerror(errno));
-}
-
-void event_pipe_emit_fast(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- pipe_events[event] = true;
-
- G_GNUC_UNUSED ssize_t nbytes = write(event_pipe[1], "", 1);
-}
diff --git a/src/event_pipe.h b/src/event_pipe.h
deleted file mode 100644
index 3734bb86..00000000
--- a/src/event_pipe.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef EVENT_PIPE_H
-#define EVENT_PIPE_H
-
-#include <glib.h>
-
-enum pipe_event {
- /** database update was finished */
- PIPE_EVENT_UPDATE,
-
- /** during database update, a song was deleted */
- PIPE_EVENT_DELETE,
-
- /** an idle event was emitted */
- PIPE_EVENT_IDLE,
-
- /** must call playlist_sync() */
- PIPE_EVENT_PLAYLIST,
-
- /** the current song's tag has changed */
- PIPE_EVENT_TAG,
-
- /** SIGHUP received: reload configuration, roll log file */
- PIPE_EVENT_RELOAD,
-
- /** a hardware mixer plugin has detected a change */
- PIPE_EVENT_MIXER,
-
- /** shutdown requested */
- PIPE_EVENT_SHUTDOWN,
-
- PIPE_EVENT_MAX
-};
-
-typedef void (*event_pipe_callback_t)(void);
-
-void event_pipe_init(void);
-
-void event_pipe_deinit(void);
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback);
-
-void event_pipe_emit(enum pipe_event event);
-
-/**
- * Similar to event_pipe_emit(), but aimed for use in signal handlers:
- * it doesn't lock the mutex, and doesn't log on error. That makes it
- * potentially lossy, but for its intended use, that does not matter.
- */
-void event_pipe_emit_fast(enum pipe_event event);
-
-#endif /* MAIN_NOTIFY_H */
diff --git a/src/fd_util.c b/src/fd_util.c
index 882b4c7d..ea29d6ea 100644
--- a/src/fd_util.c
+++ b/src/fd_util.c
@@ -49,6 +49,10 @@
#include <sys/inotify.h>
#endif
+#ifdef HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
#ifndef WIN32
static int
@@ -328,6 +332,16 @@ inotify_init_cloexec(void)
#endif
+#ifdef HAVE_EVENTFD
+
+int
+eventfd_cloexec_nonblock(unsigned initval, int flags)
+{
+ return eventfd(initval, flags | EFD_CLOEXEC | EFD_NONBLOCK);
+}
+
+#endif
+
int
close_socket(int fd)
{
diff --git a/src/fd_util.h b/src/fd_util.h
index dd4df7a1..e65c6a69 100644
--- a/src/fd_util.h
+++ b/src/fd_util.h
@@ -51,6 +51,10 @@
struct sockaddr;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Wrapper for dup(), which sets the CLOEXEC flag on the new
* descriptor.
@@ -140,10 +144,25 @@ inotify_init_cloexec(void);
#endif
+#ifdef HAVE_EVENTFD
+
+/**
+ * Wrapper for eventfd() which sets the flags CLOEXEC and NONBLOCK
+ * flag (atomically if supported by the OS).
+ */
+int
+eventfd_cloexec_nonblock(unsigned initval, int flags);
+
+#endif
+
/**
* Portable wrapper for close(); use closesocket() on WIN32/WinSock.
*/
int
close_socket(int fd);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif
diff --git a/src/filter/AutoConvertFilterPlugin.cxx b/src/filter/AutoConvertFilterPlugin.cxx
new file mode 100644
index 00000000..55ee4694
--- /dev/null
+++ b/src/filter/AutoConvertFilterPlugin.cxx
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "AutoConvertFilterPlugin.hxx"
+#include "ConvertFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "audio_format.h"
+
+#include <assert.h>
+
+class AutoConvertFilter final : public Filter {
+ /**
+ * The audio format being fed to the underlying filter. This
+ * plugin actually doesn't need this variable, we have it here
+ * just so our open() method doesn't return a stack pointer.
+ */
+ audio_format child_audio_format;
+
+ /**
+ * The underlying filter.
+ */
+ Filter *filter;
+
+ /**
+ * A convert_filter, just in case conversion is needed. nullptr
+ * if unused.
+ */
+ Filter *convert;
+
+public:
+ AutoConvertFilter(Filter *_filter):filter(_filter) {}
+ ~AutoConvertFilter() {
+ delete filter;
+ }
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+};
+
+const struct audio_format *
+AutoConvertFilter::Open(audio_format &in_audio_format, GError **error_r)
+{
+ assert(audio_format_valid(&in_audio_format));
+
+ /* open the "real" filter */
+
+ child_audio_format = in_audio_format;
+ const audio_format *out_audio_format =
+ filter->Open(child_audio_format, error_r);
+ if (out_audio_format == nullptr)
+ return nullptr;
+
+ /* need to convert? */
+
+ if (!audio_format_equals(&child_audio_format, &in_audio_format)) {
+ /* yes - create a convert_filter */
+
+ convert = filter_new(&convert_filter_plugin, nullptr, error_r);
+ if (convert == nullptr) {
+ filter->Close();
+ return nullptr;
+ }
+
+ audio_format audio_format2 = in_audio_format;
+ const audio_format *audio_format3 =
+ convert->Open(audio_format2, error_r);
+ if (audio_format3 == nullptr) {
+ delete convert;
+ filter->Close();
+ return nullptr;
+ }
+
+ assert(audio_format_equals(&audio_format2, &in_audio_format));
+
+ convert_filter_set(convert, child_audio_format);
+ } else
+ /* no */
+ convert = nullptr;
+
+ return out_audio_format;
+}
+
+void
+AutoConvertFilter::Close()
+{
+ if (convert != nullptr) {
+ convert->Close();
+ delete convert;
+ }
+
+ filter->Close();
+}
+
+const void *
+AutoConvertFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r)
+{
+ if (convert != nullptr) {
+ src = convert->FilterPCM(src, src_size, &src_size, error_r);
+ if (src == nullptr)
+ return nullptr;
+ }
+
+ return filter->FilterPCM(src, src_size, dest_size_r, error_r);
+}
+
+Filter *
+autoconvert_filter_new(Filter *filter)
+{
+ return new AutoConvertFilter(filter);
+}
diff --git a/src/filter/autoconvert_filter_plugin.h b/src/filter/AutoConvertFilterPlugin.hxx
index def08ab7..7db72a34 100644
--- a/src/filter/autoconvert_filter_plugin.h
+++ b/src/filter/AutoConvertFilterPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef AUTOCONVERT_FILTER_PLUGIN_H
-#define AUTOCONVERT_FILTER_PLUGIN_H
+#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
+#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
-struct filter;
+class Filter;
/**
* Creates a new "autoconvert" filter. When opened, it ensures that
@@ -28,7 +28,7 @@ struct filter;
* requests a different format, it automatically creates a
* convert_filter.
*/
-struct filter *
-autoconvert_filter_new(struct filter *filter);
+Filter *
+autoconvert_filter_new(Filter *filter);
#endif
diff --git a/src/filter/ChainFilterPlugin.cxx b/src/filter/ChainFilterPlugin.cxx
new file mode 100644
index 00000000..c8666615
--- /dev/null
+++ b/src/filter/ChainFilterPlugin.cxx
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ChainFilterPlugin.hxx"
+#include "conf.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "audio_format.h"
+
+#include <glib.h>
+
+#include <list>
+
+#include <assert.h>
+
+class ChainFilter final : public Filter {
+ struct Child {
+ const char *name;
+ Filter *filter;
+
+ Child(const char *_name, Filter *_filter)
+ :name(_name), filter(_filter) {}
+ ~Child() {
+ delete filter;
+ }
+
+ Child(const Child &) = delete;
+ Child &operator=(const Child &) = delete;
+ };
+
+ std::list<Child> children;
+
+public:
+ void Append(const char *name, Filter *filter) {
+ children.emplace_back(name, filter);
+ }
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+
+private:
+ /**
+ * Close all filters in the chain until #until is reached.
+ * #until itself is not closed.
+ */
+ void CloseUntil(const Filter *until);
+};
+
+static inline GQuark
+filter_quark(void)
+{
+ return g_quark_from_static_string("filter");
+}
+
+static Filter *
+chain_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new ChainFilter();
+}
+
+void
+ChainFilter::CloseUntil(const Filter *until)
+{
+ for (auto &child : children) {
+ if (child.filter == until)
+ /* don't close this filter */
+ return;
+
+ /* close this filter */
+ child.filter->Close();
+ }
+
+ /* this assertion fails if #until does not exist (anymore) */
+ assert(false);
+}
+
+static const struct audio_format *
+chain_open_child(const char *name, Filter *filter,
+ const audio_format &prev_audio_format,
+ GError **error_r)
+{
+ audio_format conv_audio_format = prev_audio_format;
+ const audio_format *next_audio_format =
+ filter->Open(conv_audio_format, error_r);
+ if (next_audio_format == NULL)
+ return NULL;
+
+ if (!audio_format_equals(&conv_audio_format, &prev_audio_format)) {
+ struct audio_format_string s;
+
+ filter->Close();
+ g_set_error(error_r, filter_quark(), 0,
+ "Audio format not supported by filter '%s': %s",
+ name,
+ audio_format_to_string(&prev_audio_format, &s));
+ return NULL;
+ }
+
+ return next_audio_format;
+}
+
+const audio_format *
+ChainFilter::Open(audio_format &in_audio_format, GError **error_r)
+{
+ const audio_format *audio_format = &in_audio_format;
+
+ for (auto &child : children) {
+ audio_format = chain_open_child(child.name, child.filter,
+ *audio_format, error_r);
+ if (audio_format == NULL) {
+ /* rollback, close all children */
+ CloseUntil(child.filter);
+ return NULL;
+ }
+ }
+
+ /* return the output format of the last filter */
+ return audio_format;
+}
+
+void
+ChainFilter::Close()
+{
+ for (auto &child : children)
+ child.filter->Close();
+}
+
+const void *
+ChainFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r)
+{
+ for (auto &child : children) {
+ /* feed the output of the previous filter as input
+ into the current one */
+ src = child.filter->FilterPCM(src, src_size, &src_size,
+ error_r);
+ if (src == NULL)
+ return NULL;
+ }
+
+ /* return the output of the last filter */
+ *dest_size_r = src_size;
+ return src;
+}
+
+const struct filter_plugin chain_filter_plugin = {
+ "chain",
+ chain_filter_init,
+};
+
+Filter *
+filter_chain_new(void)
+{
+ return new ChainFilter();
+}
+
+void
+filter_chain_append(Filter &_chain, const char *name, Filter *filter)
+{
+ ChainFilter &chain = (ChainFilter &)_chain;
+
+ chain.Append(name, filter);
+}
diff --git a/src/filter/chain_filter_plugin.h b/src/filter/ChainFilterPlugin.hxx
index 1dba4666..884c7ca1 100644
--- a/src/filter/chain_filter_plugin.h
+++ b/src/filter/ChainFilterPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,15 +24,15 @@
* output to the next one.
*/
-#ifndef MPD_FILTER_CHAIN_H
-#define MPD_FILTER_CHAIN_H
+#ifndef MPD_FILTER_CHAIN_HXX
+#define MPD_FILTER_CHAIN_HXX
-struct filter;
+class Filter;
/**
* Creates a new filter chain.
*/
-struct filter *
+Filter *
filter_chain_new(void);
/**
@@ -43,6 +43,6 @@ filter_chain_new(void);
* @param filter the filter to be appended to #chain
*/
void
-filter_chain_append(struct filter *chain, struct filter *filter);
+filter_chain_append(Filter &chain, const char *name, Filter *filter);
#endif
diff --git a/src/filter/ConvertFilterPlugin.cxx b/src/filter/ConvertFilterPlugin.cxx
new file mode 100644
index 00000000..2c690765
--- /dev/null
+++ b/src/filter/ConvertFilterPlugin.cxx
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConvertFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "conf.h"
+#include "PcmConvert.hxx"
+#include "util/Manual.hxx"
+#include "audio_format.h"
+#include "poison.h"
+
+#include <assert.h>
+#include <string.h>
+
+class ConvertFilter final : public Filter {
+ /**
+ * The input audio format; PCM data is passed to the filter()
+ * method in this format.
+ */
+ audio_format in_audio_format;
+
+ /**
+ * The output audio format; the consumer of this plugin
+ * expects PCM data in this format. This defaults to
+ * #in_audio_format, and can be set with convert_filter_set().
+ */
+ audio_format out_audio_format;
+
+ Manual<PcmConvert> state;
+
+public:
+ void Set(const audio_format &_out_audio_format) {
+ assert(audio_format_valid(&in_audio_format));
+ assert(audio_format_valid(&out_audio_format));
+ assert(audio_format_valid(&_out_audio_format));
+
+ out_audio_format = _out_audio_format;
+ }
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+};
+
+static Filter *
+convert_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new ConvertFilter();
+}
+
+const struct audio_format *
+ConvertFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
+{
+ assert(audio_format_valid(&audio_format));
+
+ in_audio_format = out_audio_format = audio_format;
+ state.Construct();
+
+ return &in_audio_format;
+}
+
+void
+ConvertFilter::Close()
+{
+ state.Destruct();
+
+ poison_undefined(&in_audio_format, sizeof(in_audio_format));
+ poison_undefined(&out_audio_format, sizeof(out_audio_format));
+}
+
+const void *
+ConvertFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r)
+{
+ if (audio_format_equals(&in_audio_format, &out_audio_format)) {
+ /* optimized special case: no-op */
+ *dest_size_r = src_size;
+ return src;
+ }
+
+ return state->Convert(&in_audio_format,
+ src, src_size,
+ &out_audio_format, dest_size_r,
+ error_r);
+}
+
+const struct filter_plugin convert_filter_plugin = {
+ "convert",
+ convert_filter_init,
+};
+
+void
+convert_filter_set(Filter *_filter, const audio_format &out_audio_format)
+{
+ ConvertFilter *filter = (ConvertFilter *)_filter;
+
+ filter->Set(out_audio_format);
+}
diff --git a/src/filter/convert_filter_plugin.h b/src/filter/ConvertFilterPlugin.hxx
index 156adf8e..840bf496 100644
--- a/src/filter/convert_filter_plugin.h
+++ b/src/filter/ConvertFilterPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,10 +17,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CONVERT_FILTER_PLUGIN_H
-#define CONVERT_FILTER_PLUGIN_H
+#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
+#define MPD_CONVERT_FILTER_PLUGIN_HXX
-struct filter;
+class Filter;
struct audio_format;
/**
@@ -30,7 +30,6 @@ struct audio_format;
* the last in a chain.
*/
void
-convert_filter_set(struct filter *filter,
- const struct audio_format *out_audio_format);
+convert_filter_set(Filter *filter, const audio_format &out_audio_format);
#endif
diff --git a/src/filter/NormalizeFilterPlugin.cxx b/src/filter/NormalizeFilterPlugin.cxx
new file mode 100644
index 00000000..e18c5cdf
--- /dev/null
+++ b/src/filter/NormalizeFilterPlugin.cxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "pcm_buffer.h"
+#include "audio_format.h"
+#include "AudioCompress/compress.h"
+
+#include <assert.h>
+#include <string.h>
+
+class NormalizeFilter final : public Filter {
+ struct Compressor *compressor;
+
+ struct pcm_buffer buffer;
+
+public:
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+};
+
+static Filter *
+normalize_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new NormalizeFilter();
+}
+
+const struct audio_format *
+NormalizeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
+{
+ audio_format.format = SAMPLE_FORMAT_S16;
+
+ compressor = Compressor_new(0);
+ pcm_buffer_init(&buffer);
+
+ return &audio_format;
+}
+
+void
+NormalizeFilter::Close()
+{
+ pcm_buffer_deinit(&buffer);
+ Compressor_delete(compressor);
+}
+
+const void *
+NormalizeFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, gcc_unused GError **error_r)
+{
+ int16_t *dest = (int16_t *)pcm_buffer_get(&buffer, src_size);
+ memcpy(dest, src, src_size);
+
+ Compressor_Process_int16(compressor, dest, src_size / 2);
+
+ *dest_size_r = src_size;
+ return dest;
+}
+
+const struct filter_plugin normalize_filter_plugin = {
+ "normalize",
+ normalize_filter_init,
+};
diff --git a/src/filter/NullFilterPlugin.cxx b/src/filter/NullFilterPlugin.cxx
new file mode 100644
index 00000000..d68065a3
--- /dev/null
+++ b/src/filter/NullFilterPlugin.cxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** \file
+ *
+ * This filter plugin does nothing. That is not quite useful, except
+ * for testing the filter core, or as a template for new filter
+ * plugins.
+ */
+
+#include "config.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "gcc.h"
+
+class NullFilter final : public Filter {
+public:
+ virtual const audio_format *Open(audio_format &af,
+ gcc_unused GError **error_r) {
+ return &af;
+ }
+
+ virtual void Close() {}
+
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r,
+ gcc_unused GError **error_r) {
+ *dest_size_r = src_size;
+ return src;
+ }
+};
+
+static Filter *
+null_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new NullFilter();
+}
+
+const struct filter_plugin null_filter_plugin = {
+ "null",
+ null_filter_init,
+};
diff --git a/src/filter/ReplayGainFilterPlugin.cxx b/src/filter/ReplayGainFilterPlugin.cxx
new file mode 100644
index 00000000..1fa2269b
--- /dev/null
+++ b/src/filter/ReplayGainFilterPlugin.cxx
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ReplayGainFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "audio_format.h"
+#include "replay_gain_info.h"
+#include "replay_gain_config.h"
+#include "MixerControl.hxx"
+#include "PcmVolume.hxx"
+
+extern "C" {
+#include "pcm_buffer.h"
+}
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "replay_gain"
+
+class ReplayGainFilter final : public Filter {
+ /**
+ * If set, then this hardware mixer is used for applying
+ * replay gain, instead of the software volume library.
+ */
+ struct mixer *mixer;
+
+ /**
+ * The base volume level for scale=1.0, between 1 and 100
+ * (including).
+ */
+ unsigned base;
+
+ enum replay_gain_mode mode;
+
+ struct replay_gain_info info;
+
+ /**
+ * The current volume, between 0 and a value that may or may not exceed
+ * #PCM_VOLUME_1.
+ *
+ * If the default value of true is used for replaygain_limit, the
+ * application of the volume to the signal will never cause clipping.
+ *
+ * On the other hand, if the user has set replaygain_limit to false,
+ * the chance of clipping is explicitly preferred if that's required to
+ * maintain a consistent audio level. Whether clipping will actually
+ * occur depends on what value the user is using for replaygain_preamp.
+ */
+ unsigned volume;
+
+ struct audio_format format;
+
+ struct pcm_buffer buffer;
+
+public:
+ ReplayGainFilter()
+ :mixer(nullptr), mode(REPLAY_GAIN_OFF),
+ volume(PCM_VOLUME_1) {
+ replay_gain_info_init(&info);
+ }
+
+ void SetMixer(struct mixer *_mixer, unsigned _base) {
+ assert(_mixer == NULL || (_base > 0 && _base <= 100));
+
+ mixer = _mixer;
+ base = _base;
+
+ Update();
+ }
+
+ void SetInfo(const struct replay_gain_info *_info) {
+ if (_info != NULL) {
+ info = *_info;
+ replay_gain_info_complete(&info);
+ } else
+ replay_gain_info_init(&info);
+
+ Update();
+ }
+
+ void SetMode(enum replay_gain_mode _mode) {
+ if (_mode == mode)
+ /* no change */
+ return;
+
+ g_debug("replay gain mode has changed %d->%d\n", mode, _mode);
+
+ mode = _mode;
+ Update();
+ }
+
+ /**
+ * Recalculates the new volume after a property was changed.
+ */
+ void Update();
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+};
+
+static inline GQuark
+replay_gain_quark(void)
+{
+ return g_quark_from_static_string("replay_gain");
+}
+
+void
+ReplayGainFilter::Update()
+{
+ if (mode != REPLAY_GAIN_OFF) {
+ float scale = replay_gain_tuple_scale(&info.tuples[mode],
+ replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit);
+ g_debug("scale=%f\n", (double)scale);
+
+ volume = pcm_float_to_volume(scale);
+ } else
+ volume = PCM_VOLUME_1;
+
+ if (mixer != NULL) {
+ /* update the hardware mixer volume */
+
+ unsigned _volume = (volume * base) / PCM_VOLUME_1;
+ if (_volume > 100)
+ _volume = 100;
+
+ GError *error = NULL;
+ if (!mixer_set_volume(mixer, _volume, &error)) {
+ g_warning("Failed to update hardware mixer: %s",
+ error->message);
+ g_error_free(error);
+ }
+ }
+}
+
+static Filter *
+replay_gain_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new ReplayGainFilter();
+}
+
+const audio_format *
+ReplayGainFilter::Open(audio_format &af, gcc_unused GError **error_r)
+{
+ format = af;
+ pcm_buffer_init(&buffer);
+
+ return &format;
+}
+
+void
+ReplayGainFilter::Close()
+{
+ pcm_buffer_deinit(&buffer);
+}
+
+const void *
+ReplayGainFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r)
+{
+
+ *dest_size_r = src_size;
+
+ if (volume == PCM_VOLUME_1)
+ /* optimized special case: 100% volume = no-op */
+ return src;
+
+ void *dest = pcm_buffer_get(&buffer, src_size);
+ if (volume <= 0) {
+ /* optimized special case: 0% volume = memset(0) */
+ /* XXX is this valid for all sample formats? What
+ about floating point? */
+ memset(dest, 0, src_size);
+ return dest;
+ }
+
+ memcpy(dest, src, src_size);
+
+ bool success = pcm_volume(dest, src_size,
+ sample_format(format.format),
+ volume);
+ if (!success) {
+ g_set_error(error_r, replay_gain_quark(), 0,
+ "pcm_volume() has failed");
+ return NULL;
+ }
+
+ return dest;
+}
+
+const struct filter_plugin replay_gain_filter_plugin = {
+ "replay_gain",
+ replay_gain_filter_init,
+};
+
+void
+replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
+ unsigned base)
+{
+ ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
+
+ filter->SetMixer(mixer, base);
+}
+
+void
+replay_gain_filter_set_info(Filter *_filter, const replay_gain_info *info)
+{
+ ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
+
+ filter->SetInfo(info);
+}
+
+void
+replay_gain_filter_set_mode(Filter *_filter, enum replay_gain_mode mode)
+{
+ ReplayGainFilter *filter = (ReplayGainFilter *)_filter;
+
+ filter->SetMode(mode);
+}
diff --git a/src/filter/replay_gain_filter_plugin.h b/src/filter/ReplayGainFilterPlugin.hxx
index 45b738e4..dd8ceb95 100644
--- a/src/filter/replay_gain_filter_plugin.h
+++ b/src/filter/ReplayGainFilterPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,12 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef REPLAY_GAIN_FILTER_PLUGIN_H
-#define REPLAY_GAIN_FILTER_PLUGIN_H
+#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
+#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
#include "replay_gain_info.h"
-struct filter;
+class Filter;
struct mixer;
/**
@@ -34,7 +34,7 @@ struct mixer;
* (including).
*/
void
-replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
+replay_gain_filter_set_mixer(Filter *_filter, struct mixer *mixer,
unsigned base);
/**
@@ -44,7 +44,9 @@ replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
* gain data is available for the current song
*/
void
-replay_gain_filter_set_info(struct filter *filter,
- const struct replay_gain_info *info);
+replay_gain_filter_set_info(Filter *filter, const replay_gain_info *info);
+
+void
+replay_gain_filter_set_mode(Filter *filter, enum replay_gain_mode mode);
#endif
diff --git a/src/filter/route_filter_plugin.c b/src/filter/RouteFilterPlugin.cxx
index 3bf8677e..55957893 100644
--- a/src/filter/route_filter_plugin.c
+++ b/src/filter/RouteFilterPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -41,25 +41,19 @@
#include "config.h"
#include "conf.h"
+#include "ConfigQuark.hxx"
#include "audio_format.h"
#include "audio_check.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
#include "pcm_buffer.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
-
-struct route_filter {
-
- /**
- * Inherit (and support cast to/from) filter
- */
- struct filter base;
-
+class RouteFilter final : public Filter {
/**
* The minimum number of channels we need for output
* to be able to perform all the copies the user has specified
@@ -109,21 +103,31 @@ struct route_filter {
*/
struct pcm_buffer output_buffer;
+public:
+ RouteFilter():sources(nullptr) {}
+ ~RouteFilter() {
+ g_free(sources);
+ }
+
+ /**
+ * Parse the "routes" section, a string on the form
+ * a>b, c>d, e>f, ...
+ * where a... are non-unique, non-negative integers
+ * and input channel a gets copied to output channel b, etc.
+ * @param param the configuration block to read
+ * @param filter a route_filter whose min_channels and sources[] to set
+ * @return true on success, false on error
+ */
+ bool Configure(const config_param *param, GError **error_r);
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
};
-/**
- * Parse the "routes" section, a string on the form
- * a>b, c>d, e>f, ...
- * where a... are non-unique, non-negative integers
- * and input channel a gets copied to output channel b, etc.
- * @param param the configuration block to read
- * @param filter a route_filter whose min_channels and sources[] to set
- * @return true on success, false on error
- */
-static bool
-route_filter_parse(const struct config_param *param,
- struct route_filter *filter,
- GError **error_r) {
+bool
+RouteFilter::Configure(const config_param *param, GError **error_r) {
/* TODO:
* With a more clever way of marking "don't copy to output N",
@@ -138,8 +142,8 @@ route_filter_parse(const struct config_param *param,
const char *routes =
config_get_block_string(param, "routes", "0>0, 1>1");
- filter->min_input_channels = 0;
- filter->min_output_channels = 0;
+ min_input_channels = 0;
+ min_output_channels = 0;
tokens = g_strsplit(routes, ",", 255);
number_of_copies = g_strv_length(tokens);
@@ -170,28 +174,28 @@ route_filter_parse(const struct config_param *param,
// Keep track of the highest channel numbers seen
// as either in- or outputs
- if (source >= filter->min_input_channels)
- filter->min_input_channels = source + 1;
- if (dest >= filter->min_output_channels)
- filter->min_output_channels = dest + 1;
+ if (source >= min_input_channels)
+ min_input_channels = source + 1;
+ if (dest >= min_output_channels)
+ min_output_channels = dest + 1;
g_strfreev(sd);
}
- if (!audio_valid_channel_count(filter->min_output_channels)) {
+ if (!audio_valid_channel_count(min_output_channels)) {
g_strfreev(tokens);
g_set_error(error_r, audio_format_quark(), 0,
"Invalid number of output channels requested: %d",
- filter->min_output_channels);
+ min_output_channels);
return false;
}
// Allocate a map of "copy nothing to me"
- filter->sources =
- g_malloc(filter->min_output_channels * sizeof(signed char));
+ sources = (signed char *)
+ g_malloc(min_output_channels * sizeof(signed char));
- for (int i=0; i<filter->min_output_channels; ++i)
- filter->sources[i] = -1;
+ for (int i=0; i<min_output_channels; ++i)
+ sources[i] = -1;
// Run through the spec again, and save the
// actual mapping output <- input
@@ -215,7 +219,7 @@ route_filter_parse(const struct config_param *param,
source = strtol(sd[0], NULL, 10);
dest = strtol(sd[1], NULL, 10);
- filter->sources[dest] = source;
+ sources[dest] = source;
g_strfreev(sd);
}
@@ -225,92 +229,73 @@ route_filter_parse(const struct config_param *param,
return true;
}
-static struct filter *
-route_filter_init(const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct route_filter *filter = g_new(struct route_filter, 1);
- filter_init(&filter->base, &route_filter_plugin);
-
- // Allocate and set the filter->sources[] array
- route_filter_parse(param, filter, error_r);
-
- return &filter->base;
-}
-
-static void
-route_filter_finish(struct filter *_filter)
+static Filter *
+route_filter_init(const config_param *param, GError **error_r)
{
- struct route_filter *filter = (struct route_filter *)_filter;
+ RouteFilter *filter = new RouteFilter();
+ if (!filter->Configure(param, error_r)) {
+ delete filter;
+ return nullptr;
+ }
- g_free(filter->sources);
- g_free(filter);
+ return filter;
}
-static const struct audio_format *
-route_filter_open(struct filter *_filter, struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
+const struct audio_format *
+RouteFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
{
- struct route_filter *filter = (struct route_filter *)_filter;
-
// Copy the input format for later reference
- filter->input_format = *audio_format;
- filter->input_frame_size =
- audio_format_frame_size(&filter->input_format);
+ input_format = audio_format;
+ input_frame_size = audio_format_frame_size(&input_format);
// Decide on an output format which has enough channels,
// and is otherwise identical
- filter->output_format = *audio_format;
- filter->output_format.channels = filter->min_output_channels;
+ output_format = audio_format;
+ output_format.channels = min_output_channels;
// Precalculate this simple value, to speed up allocation later
- filter->output_frame_size =
- audio_format_frame_size(&filter->output_format);
+ output_frame_size = audio_format_frame_size(&output_format);
// This buffer grows as needed
- pcm_buffer_init(&filter->output_buffer);
+ pcm_buffer_init(&output_buffer);
- return &filter->output_format;
+ return &output_format;
}
-static void
-route_filter_close(struct filter *_filter)
+void
+RouteFilter::Close()
{
- struct route_filter *filter = (struct route_filter *)_filter;
-
- pcm_buffer_deinit(&filter->output_buffer);
+ pcm_buffer_deinit(&output_buffer);
}
-static const void *
-route_filter_filter(struct filter *_filter,
- const void *src, size_t src_size,
- size_t *dest_size_r, G_GNUC_UNUSED GError **error_r)
+const void *
+RouteFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, gcc_unused GError **error_r)
{
- struct route_filter *filter = (struct route_filter *)_filter;
-
- size_t number_of_frames = src_size / filter->input_frame_size;
+ size_t number_of_frames = src_size / input_frame_size;
size_t bytes_per_frame_per_channel =
- audio_format_sample_size(&filter->input_format);
+ audio_format_sample_size(&input_format);
// A moving pointer that always refers to channel 0 in the input, at the currently handled frame
- const uint8_t *base_source = src;
+ const uint8_t *base_source = (const uint8_t *)src;
// A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output
uint8_t *chan_destination;
// Grow our reusable buffer, if needed, and set the moving pointer
- *dest_size_r = number_of_frames * filter->output_frame_size;
- chan_destination = pcm_buffer_get(&filter->output_buffer, *dest_size_r);
+ *dest_size_r = number_of_frames * output_frame_size;
+ chan_destination = (uint8_t *)
+ pcm_buffer_get(&output_buffer, *dest_size_r);
// Perform our copy operations, with N input channels and M output channels
for (unsigned int s=0; s<number_of_frames; ++s) {
// Need to perform one copy per output channel
- for (unsigned int c=0; c<filter->min_output_channels; ++c) {
- if (filter->sources[c] == -1 ||
- (unsigned)filter->sources[c] >= filter->input_format.channels) {
+ for (unsigned int c=0; c<min_output_channels; ++c) {
+ if (sources[c] == -1 ||
+ (unsigned)sources[c] >= input_format.channels) {
// No source for this destination output,
// give it zeroes as input
memset(chan_destination,
@@ -320,7 +305,7 @@ route_filter_filter(struct filter *_filter,
// Get the data from channel sources[c]
// and copy it to the output
const uint8_t *data = base_source +
- (filter->sources[c] * bytes_per_frame_per_channel);
+ (sources[c] * bytes_per_frame_per_channel);
memcpy(chan_destination,
data,
bytes_per_frame_per_channel);
@@ -331,18 +316,14 @@ route_filter_filter(struct filter *_filter,
// Go on to the next N input samples
- base_source += filter->input_frame_size;
+ base_source += input_frame_size;
}
// Here it is, ladies and gentlemen! Rerouted data!
- return (void *) filter->output_buffer.buffer;
+ return (void *) output_buffer.buffer;
}
const struct filter_plugin route_filter_plugin = {
- .name = "route",
- .init = route_filter_init,
- .finish = route_filter_finish,
- .open = route_filter_open,
- .close = route_filter_close,
- .filter = route_filter_filter,
+ "route",
+ route_filter_init,
};
diff --git a/src/filter/VolumeFilterPlugin.cxx b/src/filter/VolumeFilterPlugin.cxx
new file mode 100644
index 00000000..0689f5da
--- /dev/null
+++ b/src/filter/VolumeFilterPlugin.cxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "VolumeFilterPlugin.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "FilterRegistry.hxx"
+#include "conf.h"
+#include "pcm_buffer.h"
+#include "PcmVolume.hxx"
+#include "audio_format.h"
+
+#include <assert.h>
+#include <string.h>
+
+class VolumeFilter final : public Filter {
+ /**
+ * The current volume, from 0 to #PCM_VOLUME_1.
+ */
+ unsigned volume;
+
+ struct audio_format format;
+
+ struct pcm_buffer buffer;
+
+public:
+ VolumeFilter()
+ :volume(PCM_VOLUME_1) {}
+
+ unsigned GetVolume() const {
+ assert(volume <= PCM_VOLUME_1);
+
+ return volume;
+ }
+
+ void SetVolume(unsigned _volume) {
+ assert(_volume <= PCM_VOLUME_1);
+
+ volume = _volume;
+ }
+
+ virtual const audio_format *Open(audio_format &af, GError **error_r);
+ virtual void Close();
+ virtual const void *FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r);
+};
+
+static inline GQuark
+volume_quark(void)
+{
+ return g_quark_from_static_string("pcm_volume");
+}
+
+static Filter *
+volume_filter_init(gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
+{
+ return new VolumeFilter();
+}
+
+const struct audio_format *
+VolumeFilter::Open(audio_format &audio_format, gcc_unused GError **error_r)
+{
+ format = audio_format;
+ pcm_buffer_init(&buffer);
+
+ return &format;
+}
+
+void
+VolumeFilter::Close()
+{
+ pcm_buffer_deinit(&buffer);
+}
+
+const void *
+VolumeFilter::FilterPCM(const void *src, size_t src_size,
+ size_t *dest_size_r, GError **error_r)
+{
+ *dest_size_r = src_size;
+
+ if (volume >= PCM_VOLUME_1)
+ /* optimized special case: 100% volume = no-op */
+ return src;
+
+ void *dest = pcm_buffer_get(&buffer, src_size);
+
+ if (volume <= 0) {
+ /* optimized special case: 0% volume = memset(0) */
+ /* XXX is this valid for all sample formats? What
+ about floating point? */
+ memset(dest, 0, src_size);
+ return dest;
+ }
+
+ memcpy(dest, src, src_size);
+
+ bool success = pcm_volume(dest, src_size,
+ sample_format(format.format),
+ volume);
+ if (!success) {
+ g_set_error(error_r, volume_quark(), 0,
+ "pcm_volume() has failed");
+ return NULL;
+ }
+
+ return dest;
+}
+
+const struct filter_plugin volume_filter_plugin = {
+ "volume",
+ volume_filter_init,
+};
+
+unsigned
+volume_filter_get(const Filter *_filter)
+{
+ const VolumeFilter *filter =
+ (const VolumeFilter *)_filter;
+
+ return filter->GetVolume();
+}
+
+void
+volume_filter_set(Filter *_filter, unsigned volume)
+{
+ VolumeFilter *filter = (VolumeFilter *)_filter;
+
+ filter->SetVolume(volume);
+}
+
diff --git a/src/filter/volume_filter_plugin.h b/src/filter/VolumeFilterPlugin.hxx
index 5b16f7e5..822b7e93 100644
--- a/src/filter/volume_filter_plugin.h
+++ b/src/filter/VolumeFilterPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,15 +17,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef VOLUME_FILTER_PLUGIN_H
-#define VOLUME_FILTER_PLUGIN_H
+#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
+#define MPD_VOLUME_FILTER_PLUGIN_HXX
-struct filter;
+class Filter;
unsigned
-volume_filter_get(const struct filter *filter);
+volume_filter_get(const Filter *filter);
void
-volume_filter_set(struct filter *filter, unsigned volume);
+volume_filter_set(Filter *filter, unsigned volume);
#endif
diff --git a/src/filter/autoconvert_filter_plugin.c b/src/filter/autoconvert_filter_plugin.c
deleted file mode 100644
index 3826a0fb..00000000
--- a/src/filter/autoconvert_filter_plugin.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "filter/autoconvert_filter_plugin.h"
-#include "filter/convert_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "conf.h"
-#include "pcm_convert.h"
-#include "audio_format.h"
-#include "poison.h"
-
-#include <assert.h>
-#include <string.h>
-
-struct autoconvert_filter {
- struct filter base;
-
- /**
- * The audio format being fed to the underlying filter. This
- * plugin actually doesn't need this variable, we have it here
- * just so our open() method doesn't return a stack pointer.
- */
- struct audio_format in_audio_format;
-
- /**
- * The underlying filter.
- */
- struct filter *filter;
-
- /**
- * A convert_filter, just in case conversion is needed. NULL
- * if unused.
- */
- struct filter *convert;
-};
-
-static void
-autoconvert_filter_finish(struct filter *_filter)
-{
- struct autoconvert_filter *filter =
- (struct autoconvert_filter *)_filter;
-
- filter_free(filter->filter);
- g_free(filter);
-}
-
-static const struct audio_format *
-autoconvert_filter_open(struct filter *_filter,
- struct audio_format *in_audio_format,
- GError **error_r)
-{
- struct autoconvert_filter *filter =
- (struct autoconvert_filter *)_filter;
- const struct audio_format *out_audio_format;
-
- assert(audio_format_valid(in_audio_format));
-
- /* open the "real" filter */
-
- filter->in_audio_format = *in_audio_format;
-
- out_audio_format = filter_open(filter->filter,
- &filter->in_audio_format, error_r);
- if (out_audio_format == NULL)
- return NULL;
-
- /* need to convert? */
-
- if (!audio_format_equals(&filter->in_audio_format, in_audio_format)) {
- /* yes - create a convert_filter */
- struct audio_format audio_format2 = *in_audio_format;
- const struct audio_format *audio_format3;
-
- filter->convert = filter_new(&convert_filter_plugin, NULL,
- error_r);
- if (filter->convert == NULL) {
- filter_close(filter->filter);
- return NULL;
- }
-
- audio_format3 = filter_open(filter->convert, &audio_format2,
- error_r);
- if (audio_format3 == NULL) {
- filter_free(filter->convert);
- filter_close(filter->filter);
- return NULL;
- }
-
- assert(audio_format_equals(&audio_format2, in_audio_format));
-
- convert_filter_set(filter->convert, &filter->in_audio_format);
- } else
- /* no */
- filter->convert = NULL;
-
- return out_audio_format;
-}
-
-static void
-autoconvert_filter_close(struct filter *_filter)
-{
- struct autoconvert_filter *filter =
- (struct autoconvert_filter *)_filter;
-
- if (filter->convert != NULL) {
- filter_close(filter->convert);
- filter_free(filter->convert);
- }
-
- filter_close(filter->filter);
-}
-
-static const void *
-autoconvert_filter_filter(struct filter *_filter, const void *src,
- size_t src_size, size_t *dest_size_r,
- GError **error_r)
-{
- struct autoconvert_filter *filter =
- (struct autoconvert_filter *)_filter;
-
- if (filter->convert != NULL) {
- src = filter_filter(filter->convert, src, src_size, &src_size,
- error_r);
- if (src == NULL)
- return NULL;
- }
-
- return filter_filter(filter->filter, src, src_size, dest_size_r,
- error_r);
-}
-
-static const struct filter_plugin autoconvert_filter_plugin = {
- .name = "convert",
- .finish = autoconvert_filter_finish,
- .open = autoconvert_filter_open,
- .close = autoconvert_filter_close,
- .filter = autoconvert_filter_filter,
-};
-
-struct filter *
-autoconvert_filter_new(struct filter *_filter)
-{
- struct autoconvert_filter *filter =
- g_new(struct autoconvert_filter, 1);
-
- filter_init(&filter->base, &autoconvert_filter_plugin);
- filter->filter = _filter;
-
- return &filter->base;
-}
diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c
deleted file mode 100644
index 2c785a36..00000000
--- a/src/filter/chain_filter_plugin.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "conf.h"
-#include "filter/chain_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "audio_format.h"
-
-#include <assert.h>
-
-struct filter_chain {
- /** the base class */
- struct filter base;
-
- GSList *children;
-};
-
-static inline GQuark
-filter_quark(void)
-{
- return g_quark_from_static_string("filter");
-}
-
-static struct filter *
-chain_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct filter_chain *chain = g_new(struct filter_chain, 1);
-
- filter_init(&chain->base, &chain_filter_plugin);
- chain->children = NULL;
-
- return &chain->base;
-}
-
-static void
-chain_free_child(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct filter *filter = data;
-
- filter_free(filter);
-}
-
-static void
-chain_filter_finish(struct filter *_filter)
-{
- struct filter_chain *chain = (struct filter_chain *)_filter;
-
- g_slist_foreach(chain->children, chain_free_child, NULL);
- g_slist_free(chain->children);
-
- g_free(chain);
-}
-
-/**
- * Close all filters in the chain until #until is reached. #until
- * itself is not closed.
- */
-static void
-chain_close_until(struct filter_chain *chain, const struct filter *until)
-{
- GSList *i = chain->children;
- struct filter *filter;
-
- while (true) {
- /* this assertion fails if #until does not exist
- (anymore) */
- assert(i != NULL);
-
- if (i->data == until)
- /* don't close this filter */
- break;
-
- /* close this filter */
- filter = i->data;
- filter_close(filter);
-
- i = g_slist_next(i);
- }
-}
-
-static const struct audio_format *
-chain_open_child(struct filter *filter,
- const struct audio_format *prev_audio_format,
- GError **error_r)
-{
- struct audio_format conv_audio_format = *prev_audio_format;
- const struct audio_format *next_audio_format;
-
- next_audio_format = filter_open(filter, &conv_audio_format, error_r);
- if (next_audio_format == NULL)
- return NULL;
-
- if (!audio_format_equals(&conv_audio_format, prev_audio_format)) {
- struct audio_format_string s;
-
- filter_close(filter);
- g_set_error(error_r, filter_quark(), 0,
- "Audio format not supported by filter '%s': %s",
- filter->plugin->name,
- audio_format_to_string(prev_audio_format, &s));
- return NULL;
- }
-
- return next_audio_format;
-}
-
-static const struct audio_format *
-chain_filter_open(struct filter *_filter, struct audio_format *in_audio_format,
- GError **error_r)
-{
- struct filter_chain *chain = (struct filter_chain *)_filter;
- const struct audio_format *audio_format = in_audio_format;
-
- for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) {
- struct filter *filter = i->data;
-
- audio_format = chain_open_child(filter, audio_format, error_r);
- if (audio_format == NULL) {
- /* rollback, close all children */
- chain_close_until(chain, filter);
- return NULL;
- }
- }
-
- /* return the output format of the last filter */
- return audio_format;
-}
-
-static void
-chain_close_child(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct filter *filter = data;
-
- filter_close(filter);
-}
-
-static void
-chain_filter_close(struct filter *_filter)
-{
- struct filter_chain *chain = (struct filter_chain *)_filter;
-
- g_slist_foreach(chain->children, chain_close_child, NULL);
-}
-
-static const void *
-chain_filter_filter(struct filter *_filter,
- const void *src, size_t src_size,
- size_t *dest_size_r, GError **error_r)
-{
- struct filter_chain *chain = (struct filter_chain *)_filter;
-
- for (GSList *i = chain->children; i != NULL; i = g_slist_next(i)) {
- struct filter *filter = i->data;
-
- /* feed the output of the previous filter as input
- into the current one */
- src = filter_filter(filter, src, src_size, &src_size, error_r);
- if (src == NULL)
- return NULL;
- }
-
- /* return the output of the last filter */
- *dest_size_r = src_size;
- return src;
-}
-
-const struct filter_plugin chain_filter_plugin = {
- .name = "chain",
- .init = chain_filter_init,
- .finish = chain_filter_finish,
- .open = chain_filter_open,
- .close = chain_filter_close,
- .filter = chain_filter_filter,
-};
-
-struct filter *
-filter_chain_new(void)
-{
- struct filter *filter = filter_new(&chain_filter_plugin, NULL, NULL);
- /* chain_filter_init() never fails */
- assert(filter != NULL);
-
- return filter;
-}
-
-void
-filter_chain_append(struct filter *_chain, struct filter *filter)
-{
- struct filter_chain *chain = (struct filter_chain *)_chain;
-
- chain->children = g_slist_append(chain->children, filter);
-}
-
diff --git a/src/filter/convert_filter_plugin.c b/src/filter/convert_filter_plugin.c
deleted file mode 100644
index c55b69af..00000000
--- a/src/filter/convert_filter_plugin.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "filter/convert_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "conf.h"
-#include "pcm_convert.h"
-#include "audio_format.h"
-#include "poison.h"
-
-#include <assert.h>
-#include <string.h>
-
-struct convert_filter {
- struct filter base;
-
- /**
- * The current convert, from 0 to #PCM_CONVERT_1.
- */
- unsigned convert;
-
- /**
- * The input audio format; PCM data is passed to the filter()
- * method in this format.
- */
- struct audio_format in_audio_format;
-
- /**
- * The output audio format; the consumer of this plugin
- * expects PCM data in this format. This defaults to
- * #in_audio_format, and can be set with convert_filter_set().
- */
- struct audio_format out_audio_format;
-
- struct pcm_convert_state state;
-};
-
-static struct filter *
-convert_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct convert_filter *filter = g_new(struct convert_filter, 1);
-
- filter_init(&filter->base, &convert_filter_plugin);
- return &filter->base;
-}
-
-static void
-convert_filter_finish(struct filter *filter)
-{
- g_free(filter);
-}
-
-static const struct audio_format *
-convert_filter_open(struct filter *_filter, struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
-{
- struct convert_filter *filter = (struct convert_filter *)_filter;
-
- assert(audio_format_valid(audio_format));
-
- filter->in_audio_format = filter->out_audio_format = *audio_format;
- pcm_convert_init(&filter->state);
-
- return &filter->in_audio_format;
-}
-
-static void
-convert_filter_close(struct filter *_filter)
-{
- struct convert_filter *filter = (struct convert_filter *)_filter;
-
- pcm_convert_deinit(&filter->state);
-
- poison_undefined(&filter->in_audio_format,
- sizeof(filter->in_audio_format));
- poison_undefined(&filter->out_audio_format,
- sizeof(filter->out_audio_format));
-}
-
-static const void *
-convert_filter_filter(struct filter *_filter, const void *src, size_t src_size,
- size_t *dest_size_r, GError **error_r)
-{
- struct convert_filter *filter = (struct convert_filter *)_filter;
- const void *dest;
-
- if (audio_format_equals(&filter->in_audio_format,
- &filter->out_audio_format)) {
- /* optimized special case: no-op */
- *dest_size_r = src_size;
- return src;
- }
-
- dest = pcm_convert(&filter->state, &filter->in_audio_format,
- src, src_size,
- &filter->out_audio_format, dest_size_r,
- error_r);
- if (dest == NULL)
- return NULL;
-
- return dest;
-}
-
-const struct filter_plugin convert_filter_plugin = {
- .name = "convert",
- .init = convert_filter_init,
- .finish = convert_filter_finish,
- .open = convert_filter_open,
- .close = convert_filter_close,
- .filter = convert_filter_filter,
-};
-
-void
-convert_filter_set(struct filter *_filter,
- const struct audio_format *out_audio_format)
-{
- struct convert_filter *filter = (struct convert_filter *)_filter;
-
- assert(filter != NULL);
- assert(audio_format_valid(&filter->in_audio_format));
- assert(audio_format_valid(&filter->out_audio_format));
- assert(out_audio_format != NULL);
- assert(audio_format_valid(out_audio_format));
-
- filter->out_audio_format = *out_audio_format;
-}
diff --git a/src/filter/normalize_filter_plugin.c b/src/filter/normalize_filter_plugin.c
deleted file mode 100644
index 2151482e..00000000
--- a/src/filter/normalize_filter_plugin.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "pcm_buffer.h"
-#include "audio_format.h"
-#include "AudioCompress/compress.h"
-
-#include <assert.h>
-#include <string.h>
-
-struct normalize_filter {
- struct filter filter;
-
- struct Compressor *compressor;
-
- struct pcm_buffer buffer;
-};
-
-static inline GQuark
-normalize_quark(void)
-{
- return g_quark_from_static_string("normalize");
-}
-
-static struct filter *
-normalize_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct normalize_filter *filter = g_new(struct normalize_filter, 1);
-
- filter_init(&filter->filter, &normalize_filter_plugin);
-
- return &filter->filter;
-}
-
-static void
-normalize_filter_finish(struct filter *filter)
-{
- g_free(filter);
-}
-
-static const struct audio_format *
-normalize_filter_open(struct filter *_filter,
- struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
-{
- struct normalize_filter *filter = (struct normalize_filter *)_filter;
-
- audio_format->format = SAMPLE_FORMAT_S16;
-
- filter->compressor = Compressor_new(0);
-
- pcm_buffer_init(&filter->buffer);
-
- return audio_format;
-}
-
-static void
-normalize_filter_close(struct filter *_filter)
-{
- struct normalize_filter *filter = (struct normalize_filter *)_filter;
-
- pcm_buffer_deinit(&filter->buffer);
- Compressor_delete(filter->compressor);
-}
-
-static const void *
-normalize_filter_filter(struct filter *_filter,
- const void *src, size_t src_size, size_t *dest_size_r,
- G_GNUC_UNUSED GError **error_r)
-{
- struct normalize_filter *filter = (struct normalize_filter *)_filter;
- void *dest;
-
- dest = pcm_buffer_get(&filter->buffer, src_size);
-
- memcpy(dest, src, src_size);
-
- Compressor_Process_int16(filter->compressor, dest, src_size / 2);
-
- *dest_size_r = src_size;
- return dest;
-}
-
-const struct filter_plugin normalize_filter_plugin = {
- .name = "normalize",
- .init = normalize_filter_init,
- .finish = normalize_filter_finish,
- .open = normalize_filter_open,
- .close = normalize_filter_close,
- .filter = normalize_filter_filter,
-};
diff --git a/src/filter/null_filter_plugin.c b/src/filter/null_filter_plugin.c
deleted file mode 100644
index e7c99882..00000000
--- a/src/filter/null_filter_plugin.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/** \file
- *
- * This filter plugin does nothing. That is not quite useful, except
- * for testing the filter core, or as a template for new filter
- * plugins.
- */
-
-#include "config.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-
-#include <assert.h>
-
-struct null_filter {
- struct filter filter;
-};
-
-static struct filter *
-null_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct null_filter *filter = g_new(struct null_filter, 1);
-
- filter_init(&filter->filter, &null_filter_plugin);
- return &filter->filter;
-}
-
-static void
-null_filter_finish(struct filter *_filter)
-{
- struct null_filter *filter = (struct null_filter *)_filter;
-
- g_free(filter);
-}
-
-static const struct audio_format *
-null_filter_open(struct filter *_filter, struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
-{
- struct null_filter *filter = (struct null_filter *)_filter;
- (void)filter;
-
- return audio_format;
-}
-
-static void
-null_filter_close(struct filter *_filter)
-{
- struct null_filter *filter = (struct null_filter *)_filter;
- (void)filter;
-}
-
-static const void *
-null_filter_filter(struct filter *_filter,
- const void *src, size_t src_size,
- size_t *dest_size_r, G_GNUC_UNUSED GError **error_r)
-{
- struct null_filter *filter = (struct null_filter *)_filter;
- (void)filter;
-
- /* return the unmodified source buffer */
- *dest_size_r = src_size;
- return src;
-}
-
-const struct filter_plugin null_filter_plugin = {
- .name = "null",
- .init = null_filter_init,
- .finish = null_filter_finish,
- .open = null_filter_open,
- .close = null_filter_close,
- .filter = null_filter_filter,
-};
diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c
deleted file mode 100644
index 583a09f9..00000000
--- a/src/filter/replay_gain_filter_plugin.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "filter/replay_gain_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "audio_format.h"
-#include "pcm_buffer.h"
-#include "pcm_volume.h"
-#include "replay_gain_info.h"
-#include "replay_gain_config.h"
-#include "mixer_control.h"
-
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "replay_gain"
-
-struct replay_gain_filter {
- struct filter filter;
-
- /**
- * If set, then this hardware mixer is used for applying
- * replay gain, instead of the software volume library.
- */
- struct mixer *mixer;
-
- /**
- * The base volume level for scale=1.0, between 1 and 100
- * (including).
- */
- unsigned base;
-
- enum replay_gain_mode mode;
-
- struct replay_gain_info info;
-
- /**
- * The current volume, between 0 and a value that may or may not exceed
- * #PCM_VOLUME_1.
- *
- * If the default value of true is used for replaygain_limit, the
- * application of the volume to the signal will never cause clipping.
- *
- * On the other hand, if the user has set replaygain_limit to false,
- * the chance of clipping is explicitly preferred if that's required to
- * maintain a consistent audio level. Whether clipping will actually
- * occur depends on what value the user is using for replaygain_preamp.
- */
- unsigned volume;
-
- struct audio_format audio_format;
-
- struct pcm_buffer buffer;
-};
-
-static inline GQuark
-replay_gain_quark(void)
-{
- return g_quark_from_static_string("replay_gain");
-}
-
-/**
- * Recalculates the new volume after a property was changed.
- */
-static void
-replay_gain_filter_update(struct replay_gain_filter *filter)
-{
- if (filter->mode != REPLAY_GAIN_OFF) {
- float scale = replay_gain_tuple_scale(&filter->info.tuples[filter->mode],
- replay_gain_preamp, replay_gain_missing_preamp, replay_gain_limit);
- g_debug("scale=%f\n", (double)scale);
-
- filter->volume = pcm_float_to_volume(scale);
- } else
- filter->volume = PCM_VOLUME_1;
-
- if (filter->mixer != NULL) {
- /* update the hardware mixer volume */
-
- unsigned volume = (filter->volume * filter->base) / PCM_VOLUME_1;
- if (volume > 100)
- volume = 100;
-
- GError *error = NULL;
- if (!mixer_set_volume(filter->mixer, volume, &error)) {
- g_warning("Failed to update hardware mixer: %s",
- error->message);
- g_error_free(error);
- }
- }
-}
-
-static struct filter *
-replay_gain_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct replay_gain_filter *filter = g_new(struct replay_gain_filter, 1);
-
- filter_init(&filter->filter, &replay_gain_filter_plugin);
- filter->mixer = NULL;
-
- filter->mode = replay_gain_get_real_mode();
- replay_gain_info_init(&filter->info);
- filter->volume = PCM_VOLUME_1;
-
- return &filter->filter;
-}
-
-static void
-replay_gain_filter_finish(struct filter *filter)
-{
- g_free(filter);
-}
-
-static const struct audio_format *
-replay_gain_filter_open(struct filter *_filter,
- struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
-{
- struct replay_gain_filter *filter =
- (struct replay_gain_filter *)_filter;
-
- filter->audio_format = *audio_format;
- pcm_buffer_init(&filter->buffer);
-
- return &filter->audio_format;
-}
-
-static void
-replay_gain_filter_close(struct filter *_filter)
-{
- struct replay_gain_filter *filter =
- (struct replay_gain_filter *)_filter;
-
- pcm_buffer_deinit(&filter->buffer);
-}
-
-static const void *
-replay_gain_filter_filter(struct filter *_filter,
- const void *src, size_t src_size,
- size_t *dest_size_r, GError **error_r)
-{
- struct replay_gain_filter *filter =
- (struct replay_gain_filter *)_filter;
- bool success;
- void *dest;
- enum replay_gain_mode rg_mode;
-
- /* check if the mode has been changed since the last call */
- rg_mode = replay_gain_get_real_mode();
-
- if (filter->mode != rg_mode) {
- g_debug("replay gain mode has changed %d->%d\n", filter->mode, rg_mode);
- filter->mode = rg_mode;
- replay_gain_filter_update(filter);
- }
-
- *dest_size_r = src_size;
-
- if (filter->volume == PCM_VOLUME_1)
- /* optimized special case: 100% volume = no-op */
- return src;
-
- dest = pcm_buffer_get(&filter->buffer, src_size);
-
- if (filter->volume <= 0) {
- /* optimized special case: 0% volume = memset(0) */
- /* XXX is this valid for all sample formats? What
- about floating point? */
- memset(dest, 0, src_size);
- return dest;
- }
-
- memcpy(dest, src, src_size);
-
- success = pcm_volume(dest, src_size, filter->audio_format.format,
- filter->volume);
- if (!success) {
- g_set_error(error_r, replay_gain_quark(), 0,
- "pcm_volume() has failed");
- return NULL;
- }
-
- return dest;
-}
-
-const struct filter_plugin replay_gain_filter_plugin = {
- .name = "replay_gain",
- .init = replay_gain_filter_init,
- .finish = replay_gain_filter_finish,
- .open = replay_gain_filter_open,
- .close = replay_gain_filter_close,
- .filter = replay_gain_filter_filter,
-};
-
-void
-replay_gain_filter_set_mixer(struct filter *_filter, struct mixer *mixer,
- unsigned base)
-{
- struct replay_gain_filter *filter =
- (struct replay_gain_filter *)_filter;
-
- assert(mixer == NULL || (base > 0 && base <= 100));
-
- filter->mixer = mixer;
- filter->base = base;
-
- replay_gain_filter_update(filter);
-}
-
-void
-replay_gain_filter_set_info(struct filter *_filter,
- const struct replay_gain_info *info)
-{
- struct replay_gain_filter *filter =
- (struct replay_gain_filter *)_filter;
-
- if (info != NULL) {
- filter->info = *info;
- replay_gain_info_complete(&filter->info);
- } else
- replay_gain_info_init(&filter->info);
-
- replay_gain_filter_update(filter);
-}
diff --git a/src/filter/volume_filter_plugin.c b/src/filter/volume_filter_plugin.c
deleted file mode 100644
index 3260e898..00000000
--- a/src/filter/volume_filter_plugin.c
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "filter/volume_filter_plugin.h"
-#include "filter_plugin.h"
-#include "filter_internal.h"
-#include "filter_registry.h"
-#include "conf.h"
-#include "pcm_buffer.h"
-#include "pcm_volume.h"
-#include "audio_format.h"
-
-#include <assert.h>
-#include <string.h>
-
-struct volume_filter {
- struct filter filter;
-
- /**
- * The current volume, from 0 to #PCM_VOLUME_1.
- */
- unsigned volume;
-
- struct audio_format audio_format;
-
- struct pcm_buffer buffer;
-};
-
-static inline GQuark
-volume_quark(void)
-{
- return g_quark_from_static_string("pcm_volume");
-}
-
-static struct filter *
-volume_filter_init(G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
-{
- struct volume_filter *filter = g_new(struct volume_filter, 1);
-
- filter_init(&filter->filter, &volume_filter_plugin);
- filter->volume = PCM_VOLUME_1;
-
- return &filter->filter;
-}
-
-static void
-volume_filter_finish(struct filter *filter)
-{
- g_free(filter);
-}
-
-static const struct audio_format *
-volume_filter_open(struct filter *_filter, struct audio_format *audio_format,
- G_GNUC_UNUSED GError **error_r)
-{
- struct volume_filter *filter = (struct volume_filter *)_filter;
-
- filter->audio_format = *audio_format;
- pcm_buffer_init(&filter->buffer);
-
- return &filter->audio_format;
-}
-
-static void
-volume_filter_close(struct filter *_filter)
-{
- struct volume_filter *filter = (struct volume_filter *)_filter;
-
- pcm_buffer_deinit(&filter->buffer);
-}
-
-static const void *
-volume_filter_filter(struct filter *_filter, const void *src, size_t src_size,
- size_t *dest_size_r, GError **error_r)
-{
- struct volume_filter *filter = (struct volume_filter *)_filter;
- bool success;
- void *dest;
-
- *dest_size_r = src_size;
-
- if (filter->volume >= PCM_VOLUME_1)
- /* optimized special case: 100% volume = no-op */
- return src;
-
- dest = pcm_buffer_get(&filter->buffer, src_size);
-
- if (filter->volume <= 0) {
- /* optimized special case: 0% volume = memset(0) */
- /* XXX is this valid for all sample formats? What
- about floating point? */
- memset(dest, 0, src_size);
- return dest;
- }
-
- memcpy(dest, src, src_size);
-
- success = pcm_volume(dest, src_size, filter->audio_format.format,
- filter->volume);
- if (!success) {
- g_set_error(error_r, volume_quark(), 0,
- "pcm_volume() has failed");
- return NULL;
- }
-
- return dest;
-}
-
-const struct filter_plugin volume_filter_plugin = {
- .name = "volume",
- .init = volume_filter_init,
- .finish = volume_filter_finish,
- .open = volume_filter_open,
- .close = volume_filter_close,
- .filter = volume_filter_filter,
-};
-
-unsigned
-volume_filter_get(const struct filter *_filter)
-{
- const struct volume_filter *filter =
- (const struct volume_filter *)_filter;
-
- assert(filter->filter.plugin == &volume_filter_plugin);
- assert(filter->volume <= PCM_VOLUME_1);
-
- return filter->volume;
-}
-
-void
-volume_filter_set(struct filter *_filter, unsigned volume)
-{
- struct volume_filter *filter = (struct volume_filter *)_filter;
-
- assert(filter->filter.plugin == &volume_filter_plugin);
- assert(volume <= PCM_VOLUME_1);
-
- filter->volume = volume;
-}
-
diff --git a/src/filter_plugin.h b/src/filter_plugin.h
deleted file mode 100644
index 58e34dfb..00000000
--- a/src/filter_plugin.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/** \file
- *
- * This header declares the filter_plugin class. It describes a
- * plugin API for objects which filter raw PCM data.
- */
-
-#ifndef MPD_FILTER_PLUGIN_H
-#define MPD_FILTER_PLUGIN_H
-
-#include <glib.h>
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct config_param;
-struct filter;
-
-struct filter_plugin {
- const char *name;
-
- /**
- * Allocates and configures a filter.
- */
- struct filter *(*init)(const struct config_param *param,
- GError **error_r);
-
- /**
- * Free instance data.
- */
- void (*finish)(struct filter *filter);
-
- /**
- * Opens a filter.
- *
- * @param audio_format the audio format of incoming data; the
- * plugin may modify the object to enforce another input
- * format
- */
- const struct audio_format *
- (*open)(struct filter *filter,
- struct audio_format *audio_format,
- GError **error_r);
-
- /**
- * Closes a filter.
- */
- void (*close)(struct filter *filter);
-
- /**
- * Filters a block of PCM data.
- */
- const void *(*filter)(struct filter *filter,
- const void *src, size_t src_size,
- size_t *dest_buffer_r,
- GError **error_r);
-};
-
-/**
- * Creates a new instance of the specified filter plugin.
- *
- * @param plugin the filter plugin
- * @param param optional configuration section
- * @param error location to store the error occurring, or NULL to
- * ignore errors.
- * @return a new filter object, or NULL on error
- */
-struct filter *
-filter_new(const struct filter_plugin *plugin,
- const struct config_param *param, GError **error_r);
-
-/**
- * Creates a new filter, loads configuration and the plugin name from
- * the specified configuration section.
- *
- * @param param the configuration section
- * @param error location to store the error occurring, or NULL to
- * ignore errors.
- * @return a new filter object, or NULL on error
- */
-struct filter *
-filter_configured_new(const struct config_param *param, GError **error_r);
-
-/**
- * Deletes a filter. It must be closed prior to calling this
- * function, see filter_close().
- *
- * @param filter the filter object
- */
-void
-filter_free(struct filter *filter);
-
-/**
- * Opens the filter, preparing it for filter_filter().
- *
- * @param filter the filter object
- * @param audio_format the audio format of incoming data; the plugin
- * may modify the object to enforce another input format
- * @param error location to store the error occurring, or NULL to
- * ignore errors.
- * @return the format of outgoing data
- */
-const struct audio_format *
-filter_open(struct filter *filter, struct audio_format *audio_format,
- GError **error_r);
-
-/**
- * Closes the filter. After that, you may call filter_open() again.
- *
- * @param filter the filter object
- */
-void
-filter_close(struct filter *filter);
-
-/**
- * Filters a block of PCM data.
- *
- * @param filter the filter object
- * @param src the input buffer
- * @param src_size the size of #src_buffer in bytes
- * @param dest_size_r the size of the returned buffer
- * @param error location to store the error occurring, or NULL to
- * ignore errors.
- * @return the destination buffer on success (will be invalidated by
- * filter_close() or filter_filter()), NULL on error
- */
-const void *
-filter_filter(struct filter *filter, const void *src, size_t src_size,
- size_t *dest_size_r,
- GError **error_r);
-
-#endif
diff --git a/src/fs/DirectoryReader.hxx b/src/fs/DirectoryReader.hxx
new file mode 100644
index 00000000..caa1e90e
--- /dev/null
+++ b/src/fs/DirectoryReader.hxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FS_DIRECTORY_READER_HXX
+#define MPD_FS_DIRECTORY_READER_HXX
+
+#include "check.h"
+#include "Path.hxx"
+
+#include <dirent.h>
+
+/**
+ * Reader for directory entries.
+ */
+class DirectoryReader {
+ DIR *const dirp;
+ dirent *ent;
+public:
+ /**
+ * Creates new directory reader for the specified #dir.
+ */
+ explicit DirectoryReader(const Path &dir)
+ : dirp(opendir(dir.c_str())),
+ ent(nullptr) {
+ }
+
+ DirectoryReader(const DirectoryReader &other) = delete;
+ DirectoryReader &operator=(const DirectoryReader &other) = delete;
+
+ /**
+ * Destroys this instance.
+ */
+ ~DirectoryReader() {
+ if (!Failed())
+ closedir(dirp);
+ }
+
+ /**
+ * Checks if directory failed to open.
+ */
+ bool Failed() const {
+ return dirp == nullptr;
+ }
+
+ /**
+ * Checks if directory entry is available.
+ */
+ bool HasEntry() const {
+ assert(!Failed());
+ return ent != nullptr;
+ }
+
+ /**
+ * Reads next directory entry.
+ */
+ bool ReadEntry() {
+ assert(!Failed());
+ ent = readdir(dirp);
+ return HasEntry();
+ }
+
+ /**
+ * Extracts directory entry that was previously read by #ReadEntry.
+ */
+ Path GetEntry() const {
+ assert(HasEntry());
+ return Path::FromFS(ent->d_name);
+ }
+};
+
+#endif
diff --git a/src/fs/FileSystem.cxx b/src/fs/FileSystem.cxx
new file mode 100644
index 00000000..70ab01fb
--- /dev/null
+++ b/src/fs/FileSystem.cxx
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FileSystem.hxx"
+
+#include <errno.h>
+
+Path ReadLink(const Path &path)
+{
+#ifdef WIN32
+ (void)path;
+ errno = EINVAL;
+ return Path::Null();
+#else
+ char buffer[MPD_PATH_MAX];
+ ssize_t size = readlink(path.c_str(), buffer, MPD_PATH_MAX);
+ if (size < 0)
+ return Path::Null();
+ if (size >= MPD_PATH_MAX) {
+ errno = ENOMEM;
+ return Path::Null();
+ }
+ buffer[size] = '\0';
+ return Path::FromFS(buffer);
+#endif
+}
diff --git a/src/fs/FileSystem.hxx b/src/fs/FileSystem.hxx
new file mode 100644
index 00000000..2e1701c8
--- /dev/null
+++ b/src/fs/FileSystem.hxx
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FS_FILESYSTEM_HXX
+#define MPD_FS_FILESYSTEM_HXX
+
+#include "check.h"
+#include "fd_util.h"
+
+#include "Path.hxx"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+
+namespace FOpenMode {
+/**
+ * Open mode for reading text files.
+ */
+constexpr Path::const_pointer ReadText = "r";
+
+/**
+ * Open mode for reading binary files.
+ */
+constexpr Path::const_pointer ReadBinary = "rb";
+
+/**
+ * Open mode for writing text files.
+ */
+constexpr Path::const_pointer WriteText = "w";
+
+/**
+ * Open mode for writing binary files.
+ */
+constexpr Path::const_pointer WriteBinary = "wb";
+
+/**
+ * Open mode for appending text files.
+ */
+constexpr Path::const_pointer AppendText = "a";
+
+/**
+ * Open mode for appending binary files.
+ */
+constexpr Path::const_pointer AppendBinary = "ab";
+}
+
+/**
+ * Wrapper for fopen() that uses #Path names.
+ */
+static inline FILE *FOpen(const Path &file, Path::const_pointer mode)
+{
+ return fopen(file.c_str(), mode);
+}
+
+/**
+ * Wrapper for open_cloexec() that uses #Path names.
+ */
+static inline int OpenFile(const Path &file, int flags, int mode)
+{
+ return open_cloexec(file.c_str(), flags, mode);
+}
+
+/**
+ * Wrapper for rename() that uses #Path names.
+ */
+static inline bool RenameFile(const Path &oldpath, const Path &newpath)
+{
+ return rename(oldpath.c_str(), newpath.c_str()) == 0;
+}
+
+/**
+ * Wrapper for stat() that uses #Path names.
+ */
+static inline bool StatFile(const Path &file, struct stat &buf,
+ bool follow_symlinks = true)
+{
+#ifdef WIN32
+ (void)follow_symlinks;
+ return stat(file.c_str(), &buf) == 0;
+#else
+ int ret = follow_symlinks
+ ? stat(file.c_str(), &buf)
+ : lstat(file.c_str(), &buf);
+ return ret == 0;
+#endif
+}
+
+/**
+ * Wrapper for unlink() that uses #Path names.
+ */
+static inline bool RemoveFile(const Path &file)
+{
+ return unlink(file.c_str()) == 0;
+}
+
+/**
+ * Wrapper for readlink() that uses #Path names.
+ */
+Path ReadLink(const Path &path);
+
+/**
+ * Wrapper for access() that uses #Path names.
+ */
+static inline bool CheckAccess(const Path &path, int mode)
+{
+#ifdef WIN32
+ (void)path;
+ (void)mode;
+ return true;
+#else
+ return access(path.c_str(), mode) == 0;
+#endif
+}
+
+/**
+ * Checks if #Path exists and is a regular file.
+ */
+static inline bool FileExists(const Path &path,
+ bool follow_symlinks = true)
+{
+ struct stat buf;
+ return StatFile(path, buf, follow_symlinks) && S_ISREG(buf.st_mode);
+}
+
+/**
+ * Checks if #Path exists and is a directory.
+ */
+static inline bool DirectoryExists(const Path &path,
+ bool follow_symlinks = true)
+{
+ struct stat buf;
+ return StatFile(path, buf, follow_symlinks) && S_ISDIR(buf.st_mode);
+}
+
+/**
+ * Checks if #Path exists.
+ */
+static inline bool PathExists(const Path &path,
+ bool follow_symlinks = true)
+{
+ struct stat buf;
+ return StatFile(path, buf, follow_symlinks);
+}
+
+#endif
diff --git a/src/path.c b/src/fs/Path.cxx
index 59a91a0f..cb808b36 100644
--- a/src/path.c
+++ b/src/fs/Path.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "path.h"
+#include "fs/Path.hxx"
#include "conf.h"
#include "mpd_error.h"
+#include "gcc.h"
#include <glib.h>
@@ -35,56 +36,88 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "path"
-static char *fs_charset;
+/**
+ * Maximal number of bytes required to represent path name in UTF-8
+ * (including nul-terminator).
+ * This value is a rought estimate of upper bound.
+ * It's based on path name limit in bytes (MPD_PATH_MAX)
+ * and assumption that some weird encoding could represent some UTF-8 4 byte
+ * sequences with single byte.
+ */
+#define MPD_PATH_MAX_UTF8 ((MPD_PATH_MAX - 1) * 4 + 1)
+
+std::string fs_charset;
-char *
-fs_charset_to_utf8(const char *path_fs)
+std::string Path::ToUTF8(const_pointer path_fs)
{
- return g_convert(path_fs, -1,
- "utf-8", fs_charset,
- NULL, NULL, NULL);
+ if (path_fs == nullptr)
+ return std::string();
+
+ GIConv conv = g_iconv_open("utf-8", fs_charset.c_str());
+ if (conv == reinterpret_cast<GIConv>(-1))
+ return std::string();
+
+ // g_iconv() does not need nul-terminator,
+ // std::string could be created without it too.
+ char path_utf8[MPD_PATH_MAX_UTF8 - 1];
+ char *in = const_cast<char *>(path_fs);
+ char *out = path_utf8;
+ size_t in_left = strlen(path_fs);
+ size_t out_left = sizeof(path_utf8);
+
+ size_t ret = g_iconv(conv, &in, &in_left, &out, &out_left);
+
+ g_iconv_close(conv);
+
+ if (ret == static_cast<size_t>(-1) || in_left > 0)
+ return std::string();
+
+ return std::string(path_utf8, sizeof(path_utf8) - out_left);
}
-char *
-utf8_to_fs_charset(const char *path_utf8)
+Path Path::FromUTF8(const char *path_utf8)
{
gchar *p;
p = g_convert(path_utf8, -1,
- fs_charset, "utf-8",
+ fs_charset.c_str(), "utf-8",
NULL, NULL, NULL);
- if (p == NULL)
- /* fall back to UTF-8 */
- p = g_strdup(path_utf8);
- return p;
+ return Path(Donate(), p);
}
-static void
-path_set_fs_charset(const char *charset)
+gcc_pure
+static bool
+IsSupportedCharset(const char *charset)
{
- char *test;
+ /* convert a space to check if the charset is valid */
+ char *test = g_convert(" ", 1, charset, "UTF-8", NULL, NULL, NULL);
+ if (test == NULL)
+ return false;
+ g_free(test);
+ return true;
+}
+
+static void
+SetFSCharset(const char *charset)
+{
assert(charset != NULL);
- /* convert a space to ensure that the charset is valid */
- test = g_convert(" ", 1, charset, "UTF-8", NULL, NULL, NULL);
- if (test == NULL)
+ if (!IsSupportedCharset(charset))
MPD_ERROR("invalid filesystem charset: %s", charset);
- g_free(test);
- g_free(fs_charset);
- fs_charset = g_strdup(charset);
+ fs_charset = charset;
- g_debug("path_set_fs_charset: fs charset is: %s", fs_charset);
+ g_debug("SetFSCharset: fs charset is: %s", fs_charset.c_str());
}
-const char *path_get_fs_charset(void)
+const std::string &Path::GetFSCharset()
{
return fs_charset;
}
-void path_global_init(void)
+void Path::GlobalInit()
{
const char *charset = NULL;
@@ -102,21 +135,16 @@ void path_global_init(void)
* However this is true only if <gstdio.h> helpers are used.
* MPD uses regular <stdio.h> functions.
* Those functions use encoding determined by GetACP(). */
- char win_charset[13];
+ static char win_charset[13];
sprintf(win_charset, "cp%u", GetACP());
charset = win_charset;
#endif
}
if (charset) {
- path_set_fs_charset(charset);
+ SetFSCharset(charset);
} else {
g_message("setting filesystem charset to ISO-8859-1");
- path_set_fs_charset("ISO-8859-1");
+ SetFSCharset("ISO-8859-1");
}
}
-
-void path_global_finish(void)
-{
- g_free(fs_charset);
-}
diff --git a/src/fs/Path.hxx b/src/fs/Path.hxx
new file mode 100644
index 00000000..eaab2bde
--- /dev/null
+++ b/src/fs/Path.hxx
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FS_PATH_HXX
+#define MPD_FS_PATH_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#include <glib.h>
+
+#include <algorithm>
+#include <string>
+
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+
+#if !defined(MPD_PATH_MAX)
+# if defined(WIN32)
+# define MPD_PATH_MAX 260
+# elif defined(MAXPATHLEN)
+# define MPD_PATH_MAX MAXPATHLEN
+# elif defined(PATH_MAX)
+# define MPD_PATH_MAX PATH_MAX
+# else
+# define MPD_PATH_MAX 256
+# endif
+#endif
+
+/**
+ * A path name in the native file system character set.
+ */
+class Path {
+public:
+ typedef char value_type;
+ typedef value_type *pointer;
+ typedef const value_type *const_pointer;
+
+private:
+ pointer value;
+
+ struct Donate {};
+
+ /**
+ * Donate the allocated pointer to a new #Path object.
+ */
+ constexpr Path(Donate, pointer _value):value(_value) {}
+
+ /**
+ * Release memory allocated by the value, but do not clear the
+ * value pointer.
+ */
+ void Free() {
+ /* free() can be optimized by gcc, while g_free() can
+ not: when the compiler knows that the value is
+ nullptr, it will not emit a free() call in the
+ inlined destructor; however on Windows, we need to
+ call g_free(), because the value has been allocated
+ by GLib, and on Windows, this matters */
+#ifdef WIN32
+ g_free(value);
+#else
+ free(value);
+#endif
+ }
+
+public:
+ /**
+ * Copy a #Path object.
+ */
+ Path(const Path &other)
+ :value(g_strdup(other.value)) {}
+
+ /**
+ * Move a #Path object.
+ */
+ Path(Path &&other):value(other.value) {
+ other.value = nullptr;
+ }
+
+ ~Path() {
+ Free();
+ }
+
+ /**
+ * Return a "nulled" instance. Its IsNull() method will
+ * return true. Such an object must not be used.
+ *
+ * @see IsNull()
+ */
+ gcc_const
+ static Path Null() {
+ return Path(Donate(), nullptr);
+ }
+
+ /**
+ * Join two path components with the path separator.
+ */
+ gcc_pure gcc_nonnull_all
+ static Path Build(const_pointer a, const_pointer b) {
+ return Path(Donate(), g_build_filename(a, b, nullptr));
+ }
+
+ gcc_pure gcc_nonnull_all
+ static Path Build(const_pointer a, const Path &b) {
+ return Build(a, b.c_str());
+ }
+
+ gcc_pure gcc_nonnull_all
+ static Path Build(const Path &a, const_pointer b) {
+ return Build(a.c_str(), b);
+ }
+
+ gcc_pure
+ static Path Build(const Path &a, const Path &b) {
+ return Build(a.c_str(), b.c_str());
+ }
+
+ /**
+ * Convert a C string that is already in the filesystem
+ * character set to a #Path instance.
+ */
+ gcc_pure
+ static Path FromFS(const_pointer fs) {
+ return Path(Donate(), g_strdup(fs));
+ }
+
+ /**
+ * Convert a UTF-8 C string to a #Path instance.
+ * Returns return a "nulled" instance on error.
+ */
+ gcc_pure
+ static Path FromUTF8(const char *path_utf8);
+
+ /**
+ * Convert the path to UTF-8.
+ * Returns empty string on error or if #path_fs is null pointer.
+ */
+ gcc_pure
+ static std::string ToUTF8(const_pointer path_fs);
+
+ /**
+ * Performs global one-time initialization of this class.
+ */
+ static void GlobalInit();
+
+ /**
+ * Gets file system character set name.
+ */
+ static const std::string &GetFSCharset();
+
+ /**
+ * Copy a #Path object.
+ */
+ Path &operator=(const Path &other) {
+ if (this != &other) {
+ Free();
+ value = g_strdup(other.value);
+ }
+
+ return *this;
+ }
+
+ /**
+ * Move a #Path object.
+ */
+ Path &operator=(Path &&other) {
+ std::swap(value, other.value);
+ return *this;
+ }
+
+ /**
+ * Steal the allocated value. This object has an undefined
+ * value, and the caller is response for freeing this method's
+ * return value.
+ */
+ pointer Steal() {
+ pointer result = value;
+ value = nullptr;
+ return result;
+ }
+
+ /**
+ * Check if this is a "nulled" instance. A "nulled" instance
+ * must not be used.
+ */
+ bool IsNull() const {
+ return value == nullptr;
+ }
+
+ /**
+ * Clear this object's value, make it "nulled".
+ *
+ * @see IsNull()
+ */
+ void SetNull() {
+ Free();
+ value = nullptr;
+ }
+
+ gcc_pure
+ bool empty() const {
+ assert(value != nullptr);
+
+ return *value == 0;
+ }
+
+ /**
+ * @return the length of this string in number of "value_type"
+ * elements (which may not be the number of characters).
+ */
+ gcc_pure
+ size_t length() const {
+ assert(value != nullptr);
+
+ return strlen(value);
+ }
+
+ /**
+ * Returns the value as a const C string. The returned
+ * pointer is invalidated whenever the value of life of this
+ * instance ends.
+ */
+ gcc_pure
+ const_pointer c_str() const {
+ assert(value != nullptr);
+
+ return value;
+ }
+
+ /**
+ * Convert the path to UTF-8.
+ * Returns empty string on error or if this instance is "nulled"
+ * (#IsNull returns true).
+ */
+ std::string ToUTF8() const {
+ return ToUTF8(value);
+ }
+
+ /**
+ * Gets directory name of this path.
+ * Returns a "nulled" instance on error.
+ */
+ Path GetDirectoryName() const {
+ assert(value != nullptr);
+ return Path(Donate(), g_path_get_dirname(value));
+ }
+};
+
+#endif
diff --git a/src/gcc.h b/src/gcc.h
index 45f7101f..2282ff5e 100644
--- a/src/gcc.h
+++ b/src/gcc.h
@@ -32,6 +32,9 @@
*/
#if GCC_CHECK_VERSION(3,0)
+# define gcc_const __attribute__((const))
+# define gcc_pure __attribute__((pure))
+# define gcc_malloc __attribute__((malloc))
# define gcc_must_check __attribute__ ((warn_unused_result))
# define gcc_packed __attribute__ ((packed))
/* these are very useful for type checking */
@@ -41,11 +44,21 @@
# define gcc_fprintf__ __attribute__ ((format(printf,4,5)))
# define gcc_scanf __attribute__ ((format(scanf,1,2)))
# define gcc_used __attribute__ ((used))
+# define gcc_unused __attribute__((unused))
+# define gcc_warn_unused_result __attribute__((warn_unused_result))
/* # define inline inline __attribute__ ((always_inline)) */
# define gcc_noinline __attribute__ ((noinline))
# define gcc_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
# define gcc_nonnull_all __attribute__((nonnull))
+
+# define gcc_likely(x) __builtin_expect (!!(x), 1)
+# define gcc_unlikely(x) __builtin_expect (!!(x), 0)
+
#else
+# define gcc_unused
+# define gcc_const
+# define gcc_pure
+# define gcc_malloc
# define gcc_must_check
# define gcc_packed
# define gcc_printf
@@ -54,10 +67,38 @@
# define gcc_fprintf__
# define gcc_scanf
# define gcc_used
+# define gcc_unused
+# define gcc_warn_unused_result
/* # define inline */
# define gcc_noinline
# define gcc_nonnull(...)
# define gcc_nonnull_all
+
+# define gcc_likely(x) (x)
+# define gcc_unlikely(x) (x)
+
+#endif
+
+#ifdef __cplusplus
+
+#ifdef __GNUC__
+/* "__restrict__" is a GCC extension for C++ */
+#define restrict __restrict__
+#else
+/* disable it on other compilers */
+#define restrict
+#endif
+
+#if !defined(__clang__) && defined(__GNUC__) && !GCC_CHECK_VERSION(4,6)
+#error Your gcc version is too old. MPD requires gcc 4.6 or newer.
+#endif
+
+/* support for C++11 "override" was added in gcc 4.7 */
+#if !defined(__clang__) && defined(__GNUC__) && !GCC_CHECK_VERSION(4,7)
+#define override
+#define final
+#endif
+
#endif
#endif /* MPD_GCC_H */
diff --git a/src/sig_handlers.h b/src/gerror.h
index 32e9bad9..fe4c54da 100644
--- a/src/sig_handlers.h
+++ b/src/gerror.h
@@ -17,9 +17,9 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SIG_HANDLERS_H
-#define MPD_SIG_HANDLERS_H
+#ifndef MPD_GERROR_H
+#define MPD_GERROR_H
-void initSigHandlers(void);
+typedef struct _GError GError;
#endif
diff --git a/src/glib_compat.h b/src/glib_compat.h
index 97d1fdc0..a16b9c6e 100644
--- a/src/glib_compat.h
+++ b/src/glib_compat.h
@@ -28,17 +28,6 @@
#include <glib.h>
-#if !GLIB_CHECK_VERSION(2,18,0)
-
-static inline void
-g_set_error_literal(GError **err, GQuark domain, gint code,
- const gchar *message)
-{
- g_set_error(err, domain, code, "%s", message);
-}
-
-#endif
-
#if !GLIB_CHECK_VERSION(2,28,0)
static inline gint64
diff --git a/src/icy_metadata.h b/src/icy_metadata.h
deleted file mode 100644
index 9797122c..00000000
--- a/src/icy_metadata.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef ICY_METADATA_H
-#define ICY_METADATA_H
-
-#include <stdbool.h>
-#include <stddef.h>
-
-struct icy_metadata {
- size_t data_size, data_rest;
-
- size_t meta_size, meta_position;
- char *meta_data;
-
- struct tag *tag;
-};
-
-/**
- * Initialize a disabled icy_metadata object.
- */
-static inline void
-icy_clear(struct icy_metadata *im)
-{
- im->data_size = 0;
-}
-
-/**
- * Initialize an enabled icy_metadata object with the specified
- * data_size (from the icy-metaint HTTP response header).
- */
-static inline void
-icy_start(struct icy_metadata *im, size_t data_size)
-{
- im->data_size = im->data_rest = data_size;
- im->meta_size = 0;
- im->tag = NULL;
-}
-
-/**
- * Resets the icy_metadata. Call this after rewinding the stream.
- */
-void
-icy_reset(struct icy_metadata *im);
-
-void
-icy_deinit(struct icy_metadata *im);
-
-/**
- * Checks whether the icy_metadata object is enabled.
- */
-static inline bool
-icy_defined(const struct icy_metadata *im)
-{
- return im->data_size > 0;
-}
-
-/**
- * Evaluates data. Returns the number of bytes of normal data which
- * can be read by the caller, but not more than "length". If the
- * return value is smaller than "length", the caller should invoke
- * icy_meta().
- */
-size_t
-icy_data(struct icy_metadata *im, size_t length);
-
-/**
- * Reads metadata from the stream. Returns the number of bytes
- * consumed. If the return value is smaller than "length", the caller
- * should invoke icy_data().
- */
-size_t
-icy_meta(struct icy_metadata *im, const void *data, size_t length);
-
-static inline struct tag *
-icy_tag(struct icy_metadata *im)
-{
- struct tag *tag = im->tag;
- im->tag = NULL;
- return tag;
-}
-
-#endif
diff --git a/src/inotify_source.h b/src/inotify_source.h
deleted file mode 100644
index f92e18e3..00000000
--- a/src/inotify_source.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_INOTIFY_SOURCE_H
-#define MPD_INOTIFY_SOURCE_H
-
-#include <glib.h>
-
-typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
- const char *name, void *ctx);
-
-struct mpd_inotify_source;
-
-/**
- * Creates a new inotify source and registers it in the GLib main
- * loop.
- *
- * @param a callback invoked for events received from the kernel
- */
-struct mpd_inotify_source *
-mpd_inotify_source_new(mpd_inotify_callback_t callback, void *callback_ctx,
- GError **error_r);
-
-void
-mpd_inotify_source_free(struct mpd_inotify_source *source);
-
-/**
- * Adds a path to the notify list.
- *
- * @return a watch descriptor or -1 on error
- */
-int
-mpd_inotify_source_add(struct mpd_inotify_source *source,
- const char *path_fs, unsigned mask,
- GError **error_r);
-
-/**
- * Removes a path from the notify list.
- *
- * @param wd the watch descriptor returned by mpd_inotify_source_add()
- */
-void
-mpd_inotify_source_rm(struct mpd_inotify_source *source, unsigned wd);
-
-#endif
diff --git a/src/input/archive_input_plugin.c b/src/input/ArchiveInputPlugin.cxx
index 4a038b9e..0d856527 100644
--- a/src/input/archive_input_plugin.c
+++ b/src/input/ArchiveInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,12 @@
*/
#include "config.h"
-#include "input/archive_input_plugin.h"
-#include "archive_api.h"
-#include "archive_list.h"
-#include "input_plugin.h"
+#include "ArchiveInputPlugin.hxx"
+#include "ArchiveLookup.hxx"
+#include "ArchiveList.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "InputPlugin.hxx"
#include <glib.h>
@@ -35,11 +37,10 @@
*/
static struct input_stream *
input_archive_open(const char *pathname,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
const struct archive_plugin *arplug;
- struct archive_file *file;
char *archive, *filename, *suffix, *pname;
struct input_stream *is;
@@ -62,20 +63,31 @@ input_archive_open(const char *pathname,
return NULL;
}
- file = archive_file_open(arplug, archive, error_r);
- if (file == NULL)
+ auto file = archive_file_open(arplug, archive, error_r);
+ if (file == NULL) {
+ g_free(pname);
return NULL;
+ }
//setup fileops
- is = archive_file_open_stream(file, filename, mutex, cond,
- error_r);
- archive_file_close(file);
+ is = file->OpenStream(filename, mutex, cond, error_r);
g_free(pname);
+ file->Close();
return is;
}
const struct input_plugin input_plugin_archive = {
- .name = "archive",
- .open = input_archive_open,
+ "archive",
+ nullptr,
+ nullptr,
+ input_archive_open,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
};
diff --git a/src/input/archive_input_plugin.h b/src/input/ArchiveInputPlugin.hxx
index 51095f37..96fcd0dd 100644
--- a/src/input/archive_input_plugin.h
+++ b/src/input/ArchiveInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_ARCHIVE_H
-#define MPD_INPUT_ARCHIVE_H
+#ifndef MPD_INPUT_ARCHIVE_HXX
+#define MPD_INPUT_ARCHIVE_HXX
extern const struct input_plugin input_plugin_archive;
diff --git a/src/input/cdio_paranoia_input_plugin.c b/src/input/CdioParanoiaInputPlugin.cxx
index 1de7623a..f0fa835b 100644
--- a/src/input/cdio_paranoia_input_plugin.c
+++ b/src/input/CdioParanoiaInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,10 +22,10 @@
*/
#include "config.h"
-#include "input/cdio_paranoia_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
-#include "refcount.h"
+#include "CdioParanoiaInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include <stdio.h>
#include <stdint.h>
@@ -38,7 +38,7 @@
#include <cdio/paranoia.h>
#include <cdio/cd_types.h>
-struct input_cdio_paranoia {
+struct CdioParanoiaInputStream {
struct input_stream base;
cdrom_drive_t *drv;
@@ -52,6 +52,23 @@ struct input_cdio_paranoia {
char buffer[CDIO_CD_FRAMESIZE_RAW];
int buffer_lsn;
+
+ CdioParanoiaInputStream(const char *uri, Mutex &mutex, Cond &cond,
+ int _trackno)
+ :base(input_plugin_cdio_paranoia, uri, mutex, cond),
+ drv(nullptr), cdio(nullptr), para(nullptr),
+ trackno(_trackno)
+ {
+ }
+
+ ~CdioParanoiaInputStream() {
+ if (para != nullptr)
+ cdio_paranoia_free(para);
+ if (drv != nullptr)
+ cdio_cddap_close_no_free_cdio(drv);
+ if (cdio != nullptr)
+ cdio_destroy(cdio);
+ }
};
static inline GQuark
@@ -63,17 +80,9 @@ cdio_quark(void)
static void
input_cdio_close(struct input_stream *is)
{
- struct input_cdio_paranoia *i = (struct input_cdio_paranoia *)is;
+ CdioParanoiaInputStream *i = (CdioParanoiaInputStream *)is;
- if (i->para)
- cdio_paranoia_free(i->para);
- if (i->drv)
- cdio_cddap_close_no_free_cdio( i->drv);
- if (i->cdio)
- cdio_destroy( i->cdio );
-
- input_stream_deinit(&i->base);
- g_free(i);
+ delete i;
}
struct cdio_uri {
@@ -97,7 +106,7 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r)
}
const char *slash = strrchr(src, '/');
- if (slash == NULL) {
+ if (slash == nullptr) {
/* play the whole CD in the specified drive */
g_strlcpy(dest->device, src, sizeof(dest->device));
dest->track = -1;
@@ -131,9 +140,10 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r)
static char *
cdio_detect_device(void)
{
- char **devices = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
- if (devices == NULL)
- return NULL;
+ char **devices = cdio_get_devices_with_cap(nullptr, CDIO_FS_AUDIO,
+ false);
+ if (devices == nullptr)
+ return nullptr;
char *device = g_strdup(devices[0]);
cdio_free_device_list(devices);
@@ -143,55 +153,47 @@ cdio_detect_device(void)
static struct input_stream *
input_cdio_open(const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
- struct input_cdio_paranoia *i;
-
struct cdio_uri parsed_uri;
if (!parse_cdio_uri(&parsed_uri, uri, error_r))
- return NULL;
-
- i = g_new(struct input_cdio_paranoia, 1);
- input_stream_init(&i->base, &input_plugin_cdio_paranoia, uri,
- mutex, cond);
+ return nullptr;
- /* initialize everything (should be already) */
- i->drv = NULL;
- i->cdio = NULL;
- i->para = NULL;
- i->trackno = parsed_uri.track;
+ CdioParanoiaInputStream *i =
+ new CdioParanoiaInputStream(uri, mutex, cond,
+ parsed_uri.track);
/* get list of CD's supporting CD-DA */
char *device = parsed_uri.device[0] != 0
? g_strdup(parsed_uri.device)
: cdio_detect_device();
- if (device == NULL) {
+ if (device == nullptr) {
g_set_error(error_r, cdio_quark(), 0,
"Unable find or access a CD-ROM drive with an audio CD in it.");
- input_cdio_close(&i->base);
- return NULL;
+ delete i;
+ return nullptr;
}
/* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */
i->cdio = cdio_open(device, DRIVER_UNKNOWN);
g_free(device);
- i->drv = cdio_cddap_identify_cdio(i->cdio, 1, NULL);
+ i->drv = cdio_cddap_identify_cdio(i->cdio, 1, nullptr);
if ( !i->drv ) {
g_set_error(error_r, cdio_quark(), 0,
"Unable to identify audio CD disc.");
- input_cdio_close(&i->base);
- return NULL;
+ delete i;
+ return nullptr;
}
cdda_verbose_set(i->drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
if ( 0 != cdio_cddap_open(i->drv) ) {
g_set_error(error_r, cdio_quark(), 0, "Unable to open disc.");
- input_cdio_close(&i->base);
- return NULL;
+ delete i;
+ return nullptr;
}
bool reverse_endian;
@@ -212,8 +214,8 @@ input_cdio_open(const char *uri,
g_set_error(error_r, cdio_quark(), 0,
"Drive returns unknown data type %d",
data_bigendianp(i->drv));
- input_cdio_close(&i->base);
- return NULL;
+ delete i;
+ return nullptr;
}
i->lsn_relofs = 0;
@@ -250,7 +252,7 @@ static bool
input_cdio_seek(struct input_stream *is,
goffset offset, int whence, GError **error_r)
{
- struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+ CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is;
/* calculate absolute offset */
switch (whence) {
@@ -288,7 +290,7 @@ static size_t
input_cdio_read(struct input_stream *is, void *ptr, size_t length,
GError **error_r)
{
- struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+ CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is;
size_t nbytes = 0;
int diff;
size_t len, maxwrite;
@@ -305,7 +307,7 @@ input_cdio_read(struct input_stream *is, void *ptr, size_t length,
//current sector was changed ?
if (cis->lsn_relofs != cis->buffer_lsn) {
- rbuf = cdio_paranoia_read(cis->para, NULL);
+ rbuf = cdio_paranoia_read(cis->para, nullptr);
s_err = cdda_errors(cis->drv);
if (s_err) {
@@ -356,16 +358,22 @@ input_cdio_read(struct input_stream *is, void *ptr, size_t length,
static bool
input_cdio_eof(struct input_stream *is)
{
- struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+ CdioParanoiaInputStream *cis = (CdioParanoiaInputStream *)is;
return (cis->lsn_from + cis->lsn_relofs > cis->lsn_to);
}
const struct input_plugin input_plugin_cdio_paranoia = {
- .name = "cdio_paranoia",
- .open = input_cdio_open,
- .close = input_cdio_close,
- .seek = input_cdio_seek,
- .read = input_cdio_read,
- .eof = input_cdio_eof
+ "cdio_paranoia",
+ nullptr,
+ nullptr,
+ input_cdio_open,
+ input_cdio_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ input_cdio_read,
+ input_cdio_eof,
+ input_cdio_seek,
};
diff --git a/src/input/cdio_paranoia_input_plugin.h b/src/input/CdioParanoiaInputPlugin.hxx
index 71c5cbe8..80d98b4b 100644
--- a/src/input/cdio_paranoia_input_plugin.h
+++ b/src/input/CdioParanoiaInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_CDIO_PARANOIA_INPUT_PLUGIN_H
-#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_H
+#ifndef MPD_CDIO_PARANOIA_INPUT_PLUGIN_HXX
+#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_HXX
/**
* An input plugin based on libcdio_paranoia library.
diff --git a/src/input/curl_input_plugin.c b/src/input/CurlInputPlugin.cxx
index 3f191141..fe944b75 100644
--- a/src/input/curl_input_plugin.c
+++ b/src/input/CurlInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,16 @@
*/
#include "config.h"
-#include "input/curl_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "CurlInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include "conf.h"
#include "tag.h"
-#include "icy_metadata.h"
-#include "io_thread.h"
-#include "glib_compat.h"
+#include "IcyMetaDataParser.hxx"
+#include "event/MultiSocketMonitor.hxx"
+#include "event/Loop.hxx"
+#include "IOThread.hxx"
#include <assert.h>
@@ -38,9 +40,16 @@
#include <string.h>
#include <errno.h>
+#include <list>
+#include <forward_list>
+
#include <curl/curl.h>
#include <glib.h>
+#if LIBCURL_VERSION_NUM < 0x071200
+#error libcurl is too old
+#endif
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_curl"
@@ -59,7 +68,7 @@ static const size_t CURL_RESUME_AT = 384 * 1024;
/**
* Buffers created by input_curl_writefunction().
*/
-struct buffer {
+class CurlInputBuffer {
/** size of the payload */
size_t size;
@@ -67,7 +76,55 @@ struct buffer {
size_t consumed;
/** the payload */
- unsigned char data[sizeof(long)];
+ uint8_t *data;
+
+public:
+ CurlInputBuffer(const void *_data, size_t _size)
+ :size(_size), consumed(0), data(new uint8_t[size]) {
+ memcpy(data, _data, size);
+ }
+
+ ~CurlInputBuffer() {
+ delete[] data;
+ }
+
+ CurlInputBuffer(const CurlInputBuffer &) = delete;
+ CurlInputBuffer &operator=(const CurlInputBuffer &) = delete;
+
+ const void *Begin() const {
+ return data + consumed;
+ }
+
+ size_t TotalSize() const {
+ return size;
+ }
+
+ size_t Available() const {
+ return size - consumed;
+ }
+
+ /**
+ * Mark a part of the buffer as consumed.
+ *
+ * @return false if the buffer is now empty
+ */
+ bool Consume(size_t length) {
+ assert(consumed < size);
+
+ consumed += length;
+ if (consumed < size)
+ return true;
+
+ assert(consumed == size);
+ return false;
+ }
+
+ bool Read(void *dest, size_t length) {
+ assert(consumed + length <= size);
+
+ memcpy(dest, data + consumed, length);
+ return Consume(length);
+ }
};
struct input_curl {
@@ -75,40 +132,28 @@ struct input_curl {
/* some buffers which were passed to libcurl, which we have
too free */
- char *url, *range;
+ char *range;
struct curl_slist *request_headers;
/** the curl handles */
CURL *easy;
- /** the GMainLoop source used to poll all CURL file
- descriptors */
- GSource *source;
-
- /** the source id of #source */
- guint source_id;
-
- /** a linked list of all registered GPollFD objects */
- GSList *fds;
-
/** list of buffers, where input_curl_writefunction() appends
to, and input_curl_read() reads from them */
- GQueue *buffers;
+ std::list<CurlInputBuffer> buffers;
-#if LIBCURL_VERSION_NUM >= 0x071200
/**
* Is the connection currently paused? That happens when the
* buffer was getting too large. It will be unpaused when the
* buffer is below the threshold again.
*/
bool paused;
-#endif
/** error message provided by libcurl */
char error[CURL_ERROR_SIZE];
/** parser for icy-metadata */
- struct icy_metadata icy_metadata;
+ IcyMetaDataParser icy;
/** the stream name from the icy-name response header */
char *meta_name;
@@ -118,6 +163,50 @@ struct input_curl {
struct tag *tag;
GError *postponed_error;
+
+ input_curl(const char *url, Mutex &mutex, Cond &cond)
+ :base(input_plugin_curl, url, mutex, cond),
+ range(nullptr), request_headers(nullptr),
+ paused(false),
+ meta_name(nullptr),
+ tag(nullptr),
+ postponed_error(nullptr) {
+ }
+
+ ~input_curl();
+
+ input_curl(const input_curl &) = delete;
+ input_curl &operator=(const input_curl &) = delete;
+};
+
+/**
+ * This class monitors all CURL file descriptors.
+ */
+class CurlSockets final : private MultiSocketMonitor {
+ /**
+ * Did CURL give us a timeout? If yes, then we need to call
+ * curl_multi_perform(), even if there was no event on any
+ * file descriptor.
+ */
+ bool have_timeout;
+
+ /**
+ * The absolute time stamp when the timeout expires.
+ */
+ gint64 absolute_timeout;
+
+public:
+ CurlSockets(EventLoop &_loop)
+ :MultiSocketMonitor(_loop) {}
+
+ using MultiSocketMonitor::InvalidateSockets;
+
+private:
+ void UpdateSockets();
+
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) override;
+ virtual bool CheckSockets() const override;
+ virtual void DispatchSockets() override;
};
/** libcurl should accept "ICY 200 OK" */
@@ -134,35 +223,9 @@ static struct {
* A linked list of all active HTTP requests. An active
* request is one that doesn't have the "eof" flag set.
*/
- GSList *requests;
-
- /**
- * The GMainLoop source used to poll all CURL file
- * descriptors.
- */
- GSource *source;
+ std::forward_list<input_curl *> requests;
- /**
- * The source id of #source.
- */
- guint source_id;
-
- GSList *fds;
-
-#if LIBCURL_VERSION_NUM >= 0x070f04
- /**
- * Did CURL give us a timeout? If yes, then we need to call
- * curl_multi_perform(), even if there was no event on any
- * file descriptor.
- */
- bool timeout;
-
- /**
- * The absolute time stamp when the timeout expires. This is
- * used in the GSource method check().
- */
- gint64 absolute_timeout;
-#endif
+ CurlSockets *sockets;
} curl;
static inline GQuark
@@ -181,23 +244,19 @@ input_curl_find_request(CURL *easy)
{
assert(io_thread_inside());
- for (GSList *i = curl.requests; i != NULL; i = g_slist_next(i)) {
- struct input_curl *c = i->data;
+ for (auto c : curl.requests)
if (c->easy == easy)
return c;
- }
return NULL;
}
-#if LIBCURL_VERSION_NUM >= 0x071200
-
static gpointer
input_curl_resume(gpointer data)
{
assert(io_thread_inside());
- struct input_curl *c = data;
+ struct input_curl *c = (struct input_curl *)data;
if (c->paused) {
c->paused = false;
@@ -207,13 +266,11 @@ input_curl_resume(gpointer data)
return NULL;
}
-#endif
-
/**
* Calculates the GLib event bit mask for one file descriptor,
* obtained from three #fd_set objects filled by curl_multi_fdset().
*/
-static gushort
+static unsigned
input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)
{
gushort events = 0;
@@ -242,8 +299,8 @@ input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)
*
* Runs in the I/O thread. No lock needed.
*/
-static void
-curl_update_fds(void)
+void
+CurlSockets::UpdateSockets()
{
assert(io_thread_inside());
@@ -262,42 +319,15 @@ curl_update_fds(void)
return;
}
- GSList *fds = curl.fds;
- curl.fds = NULL;
-
- while (fds != NULL) {
- GPollFD *poll_fd = fds->data;
- gushort events = input_curl_fd_events(poll_fd->fd, &rfds,
- &wfds, &efds);
-
- assert(poll_fd->events != 0);
-
- fds = g_slist_remove(fds, poll_fd);
-
- if (events != poll_fd->events)
- g_source_remove_poll(curl.source, poll_fd);
-
- if (events != 0) {
- if (events != poll_fd->events) {
- poll_fd->events = events;
- g_source_add_poll(curl.source, poll_fd);
- }
-
- curl.fds = g_slist_prepend(curl.fds, poll_fd);
- } else {
- g_free(poll_fd);
- }
- }
+ UpdateSocketList([&rfds, &wfds, &efds](int fd){
+ return input_curl_fd_events(fd, &rfds,
+ &wfds, &efds);
+ });
for (int fd = 0; fd <= max_fd; ++fd) {
- gushort events = input_curl_fd_events(fd, &rfds, &wfds, &efds);
- if (events != 0) {
- GPollFD *poll_fd = g_new(GPollFD, 1);
- poll_fd->fd = fd;
- poll_fd->events = events;
- g_source_add_poll(curl.source, poll_fd);
- curl.fds = g_slist_prepend(curl.fds, poll_fd);
- }
+ unsigned events = input_curl_fd_events(fd, &rfds, &wfds, &efds);
+ if (events != 0)
+ AddSocket(fd, events);
}
}
@@ -312,7 +342,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r)
assert(c->easy != NULL);
assert(input_curl_find_request(c->easy) == NULL);
- curl.requests = g_slist_prepend(curl.requests, c);
+ curl.requests.push_front(c);
CURLMcode mcode = curl_multi_add_handle(curl.multi, c->easy);
if (mcode != CURLM_OK) {
@@ -322,7 +352,7 @@ input_curl_easy_add(struct input_curl *c, GError **error_r)
return false;
}
- curl_update_fds();
+ curl.sockets->InvalidateSockets();
return true;
}
@@ -335,7 +365,8 @@ struct easy_add_params {
static gpointer
input_curl_easy_add_callback(gpointer data)
{
- const struct easy_add_params *params = data;
+ const struct easy_add_params *params =
+ (const struct easy_add_params *)data;
bool success = input_curl_easy_add(params->c, params->error_r);
return GUINT_TO_POINTER(success);
@@ -352,8 +383,8 @@ input_curl_easy_add_indirect(struct input_curl *c, GError **error_r)
assert(c->easy != NULL);
struct easy_add_params params = {
- .c = c,
- .error_r = error_r,
+ c,
+ error_r,
};
gpointer result =
@@ -376,7 +407,7 @@ input_curl_easy_free(struct input_curl *c)
if (c->easy == NULL)
return;
- curl.requests = g_slist_remove(curl.requests, c);
+ curl.requests.remove(c);
curl_multi_remove_handle(curl.multi, c->easy);
curl_easy_cleanup(c->easy);
@@ -392,10 +423,10 @@ input_curl_easy_free(struct input_curl *c)
static gpointer
input_curl_easy_free_callback(gpointer data)
{
- struct input_curl *c = data;
+ struct input_curl *c = (struct input_curl *)data;
input_curl_easy_free(c);
- curl_update_fds();
+ curl.sockets->InvalidateSockets();
return NULL;
}
@@ -424,17 +455,18 @@ input_curl_abort_all_requests(GError *error)
assert(io_thread_inside());
assert(error != NULL);
- while (curl.requests != NULL) {
- struct input_curl *c = curl.requests->data;
+ while (!curl.requests.empty()) {
+ struct input_curl *c = curl.requests.front();
assert(c->postponed_error == NULL);
input_curl_easy_free(c);
- g_mutex_lock(c->base.mutex);
+ const ScopeLock protect(c->base.mutex);
+
c->postponed_error = g_error_copy(error);
c->base.ready = true;
- g_cond_broadcast(c->base.cond);
- g_mutex_unlock(c->base.mutex);
+
+ c->base.cond.broadcast();
}
g_error_free(error);
@@ -454,7 +486,7 @@ input_curl_request_done(struct input_curl *c, CURLcode result, long status)
assert(c->easy == NULL);
assert(c->postponed_error == NULL);
- g_mutex_lock(c->base.mutex);
+ const ScopeLock protect(c->base.mutex);
if (result != CURLE_OK) {
c->postponed_error = g_error_new(curl_quark(), result,
@@ -467,8 +499,8 @@ input_curl_request_done(struct input_curl *c, CURLcode result, long status)
}
c->base.ready = true;
- g_cond_broadcast(c->base.cond);
- g_mutex_unlock(c->base.mutex);
+
+ c->base.cond.broadcast();
}
static void
@@ -532,28 +564,18 @@ input_curl_perform(void)
return true;
}
-/*
- * GSource methods
- *
- */
-
-/**
- * The GSource prepare() method implementation.
- */
-static gboolean
-input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r)
+void
+CurlSockets::PrepareSockets(gint *timeout_r)
{
- curl_update_fds();
+ UpdateSockets();
-#if LIBCURL_VERSION_NUM >= 0x070f04
- curl.timeout = false;
+ have_timeout = false;
long timeout2;
CURLMcode mcode = curl_multi_timeout(curl.multi, &timeout2);
if (mcode == CURLM_OK) {
if (timeout2 >= 0)
- curl.absolute_timeout = g_source_get_time(source)
- + timeout2 * 1000;
+ absolute_timeout = GetTime() + timeout2 * 1000;
if (timeout2 >= 0 && timeout2 < 10)
/* CURL 7.21.1 likes to report "timeout=0",
@@ -564,69 +586,28 @@ input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r)
*timeout_r = timeout2;
- curl.timeout = timeout2 >= 0;
+ have_timeout = timeout2 >= 0;
} else
g_warning("curl_multi_timeout() failed: %s\n",
curl_multi_strerror(mcode));
-#else
- (void)timeout_r;
-#endif
-
- return false;
}
-/**
- * The GSource check() method implementation.
- */
-static gboolean
-input_curl_source_check(G_GNUC_UNUSED GSource *source)
+bool
+CurlSockets::CheckSockets() const
{
-#if LIBCURL_VERSION_NUM >= 0x070f04
- if (curl.timeout) {
- /* when a timeout has expired, we need to call
- curl_multi_perform(), even if there was no file
- descriptor event */
-
- if (g_source_get_time(source) >= curl.absolute_timeout)
- return true;
- }
-#endif
-
- for (GSList *i = curl.fds; i != NULL; i = i->next) {
- GPollFD *poll_fd = i->data;
- if (poll_fd->revents != 0)
- return true;
- }
-
- return false;
+ /* when a timeout has expired, we need to call
+ curl_multi_perform(), even if there was no file descriptor
+ event */
+ return have_timeout && GetTime() >= absolute_timeout;
}
-/**
- * The GSource dispatch() method implementation. The callback isn't
- * used, because we're handling all events directly.
- */
-static gboolean
-input_curl_source_dispatch(G_GNUC_UNUSED GSource *source,
- G_GNUC_UNUSED GSourceFunc callback,
- G_GNUC_UNUSED gpointer user_data)
+void
+CurlSockets::DispatchSockets()
{
if (input_curl_perform())
input_curl_info_read();
-
- return true;
}
-/**
- * The vtable for our GSource implementation. Unfortunately, we
- * cannot declare it "const", because g_source_new() takes a non-const
- * pointer, for whatever reason.
- */
-static GSourceFuncs curl_source_funcs = {
- .prepare = input_curl_source_prepare,
- .check = input_curl_source_check,
- .dispatch = input_curl_source_dispatch,
-};
-
/*
* input_plugin methods
*
@@ -668,8 +649,7 @@ input_curl_init(const struct config_param *param,
return false;
}
- curl.source = g_source_new(&curl_source_funcs, sizeof(*curl.source));
- curl.source_id = g_source_attach(curl.source, io_thread_context());
+ curl.sockets = new CurlSockets(io_thread_get());
return true;
}
@@ -677,7 +657,7 @@ input_curl_init(const struct config_param *param,
static gpointer
curl_destroy_sources(G_GNUC_UNUSED gpointer data)
{
- g_source_destroy(curl.source);
+ delete curl.sockets;
return NULL;
}
@@ -685,7 +665,7 @@ curl_destroy_sources(G_GNUC_UNUSED gpointer data)
static void
input_curl_finish(void)
{
- assert(curl.requests == NULL);
+ assert(curl.requests.empty());
io_thread_call(curl_destroy_sources, NULL);
@@ -696,8 +676,6 @@ input_curl_finish(void)
curl_global_cleanup();
}
-#if LIBCURL_VERSION_NUM >= 0x071200
-
/**
* Determine the total sizes of all buffers, including portions that
* have already been consumed.
@@ -710,55 +688,22 @@ curl_total_buffer_size(const struct input_curl *c)
{
size_t total = 0;
- for (GList *i = g_queue_peek_head_link(c->buffers);
- i != NULL; i = g_list_next(i)) {
- struct buffer *buffer = i->data;
- total += buffer->size;
- }
+ for (const auto &i : c->buffers)
+ total += i.TotalSize();
return total;
}
-#endif
-
-static void
-buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
+input_curl::~input_curl()
{
- struct buffer *buffer = data;
+ if (tag != NULL)
+ tag_free(tag);
+ g_free(meta_name);
- assert(buffer->consumed <= buffer->size);
+ input_curl_easy_free_indirect(this);
- g_free(buffer);
-}
-
-static void
-input_curl_flush_buffers(struct input_curl *c)
-{
- g_queue_foreach(c->buffers, buffer_free_callback, NULL);
- g_queue_clear(c->buffers);
-}
-
-/**
- * Frees this stream, including the input_stream struct.
- */
-static void
-input_curl_free(struct input_curl *c)
-{
- if (c->tag != NULL)
- tag_free(c->tag);
- g_free(c->meta_name);
-
- input_curl_easy_free_indirect(c);
- input_curl_flush_buffers(c);
-
- g_queue_free(c->buffers);
-
- if (c->postponed_error != NULL)
- g_error_free(c->postponed_error);
-
- g_free(c->url);
- input_stream_deinit(&c->base);
- g_free(c);
+ if (postponed_error != NULL)
+ g_error_free(postponed_error);
}
static bool
@@ -788,8 +733,8 @@ input_curl_tag(struct input_stream *is)
static bool
fill_buffer(struct input_curl *c, GError **error_r)
{
- while (c->easy != NULL && g_queue_is_empty(c->buffers))
- g_cond_wait(c->base.cond, c->base.mutex);
+ while (c->easy != NULL && c->buffers.empty())
+ c->base.cond.wait(c->base.mutex);
if (c->postponed_error != NULL) {
g_propagate_error(error_r, c->postponed_error);
@@ -797,86 +742,63 @@ fill_buffer(struct input_curl *c, GError **error_r)
return false;
}
- return !g_queue_is_empty(c->buffers);
-}
-
-/**
- * Mark a part of the buffer object as consumed.
- */
-static struct buffer *
-consume_buffer(struct buffer *buffer, size_t length)
-{
- assert(buffer != NULL);
- assert(buffer->consumed < buffer->size);
-
- buffer->consumed += length;
- if (buffer->consumed < buffer->size)
- return buffer;
-
- assert(buffer->consumed == buffer->size);
-
- g_free(buffer);
-
- return NULL;
+ return !c->buffers.empty();
}
static size_t
-read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
+read_from_buffer(IcyMetaDataParser &icy, std::list<CurlInputBuffer> &buffers,
void *dest0, size_t length)
{
- struct buffer *buffer = g_queue_pop_head(buffers);
- uint8_t *dest = dest0;
+ auto &buffer = buffers.front();
+ uint8_t *dest = (uint8_t *)dest0;
size_t nbytes = 0;
- assert(buffer->size > 0);
- assert(buffer->consumed < buffer->size);
-
- if (length > buffer->size - buffer->consumed)
- length = buffer->size - buffer->consumed;
+ if (length > buffer.Available())
+ length = buffer.Available();
while (true) {
size_t chunk;
- chunk = icy_data(icy_metadata, length);
+ chunk = icy.Data(length);
if (chunk > 0) {
- memcpy(dest, buffer->data + buffer->consumed,
- chunk);
- buffer = consume_buffer(buffer, chunk);
+ const bool empty = !buffer.Read(dest, chunk);
nbytes += chunk;
dest += chunk;
length -= chunk;
- if (length == 0)
+ if (empty) {
+ buffers.pop_front();
break;
+ }
- assert(buffer != NULL);
+ if (length == 0)
+ break;
}
- chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed,
- length);
+ chunk = icy.Meta(buffer.Begin(), length);
if (chunk > 0) {
- buffer = consume_buffer(buffer, chunk);
+ const bool empty = !buffer.Consume(chunk);
length -= chunk;
- if (length == 0)
+ if (empty) {
+ buffers.pop_front();
break;
+ }
- assert(buffer != NULL);
+ if (length == 0)
+ break;
}
}
- if (buffer != NULL)
- g_queue_push_head(buffers, buffer);
-
return nbytes;
}
static void
copy_icy_tag(struct input_curl *c)
{
- struct tag *tag = icy_tag(&c->icy_metadata);
+ struct tag *tag = c->icy.ReadTag();
if (tag == NULL)
return;
@@ -896,7 +818,7 @@ input_curl_available(struct input_stream *is)
struct input_curl *c = (struct input_curl *)is;
return c->postponed_error != NULL || c->easy == NULL ||
- !g_queue_is_empty(c->buffers);
+ !c->buffers.empty();
}
static size_t
@@ -906,7 +828,7 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
struct input_curl *c = (struct input_curl *)is;
bool success;
size_t nbytes = 0;
- char *dest = ptr;
+ char *dest = (char *)ptr;
do {
/* fill the buffer */
@@ -917,8 +839,8 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
/* send buffer contents */
- while (size > 0 && !g_queue_is_empty(c->buffers)) {
- size_t copy = read_from_buffer(&c->icy_metadata, c->buffers,
+ while (size > 0 && !c->buffers.empty()) {
+ size_t copy = read_from_buffer(c->icy, c->buffers,
dest + nbytes, size);
nbytes += copy;
@@ -926,18 +848,16 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
}
} while (nbytes == 0);
- if (icy_defined(&c->icy_metadata))
+ if (c->icy.IsDefined())
copy_icy_tag(c);
is->offset += (goffset)nbytes;
-#if LIBCURL_VERSION_NUM >= 0x071200
if (c->paused && curl_total_buffer_size(c) < CURL_RESUME_AT) {
- g_mutex_unlock(c->base.mutex);
+ c->base.mutex.unlock();
io_thread_call(input_curl_resume, c);
- g_mutex_lock(c->base.mutex);
+ c->base.mutex.lock();
}
-#endif
return nbytes;
}
@@ -947,7 +867,7 @@ input_curl_close(struct input_stream *is)
{
struct input_curl *c = (struct input_curl *)is;
- input_curl_free(c);
+ delete c;
}
static bool
@@ -955,7 +875,7 @@ input_curl_eof(G_GNUC_UNUSED struct input_stream *is)
{
struct input_curl *c = (struct input_curl *)is;
- return c->easy == NULL && g_queue_is_empty(c->buffers);
+ return c->easy == NULL && c->buffers.empty();
}
/** called by curl when new data is available */
@@ -963,13 +883,14 @@ static size_t
input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct input_curl *c = (struct input_curl *)stream;
- const char *header = ptr, *end, *value;
char name[64];
size *= nmemb;
- end = header + size;
- value = memchr(header, ':', size);
+ const char *header = (const char *)ptr;
+ const char *end = header + size;
+
+ const char *value = (const char *)memchr(header, ':', size);
if (value == NULL || (size_t)(value - header) >= sizeof(name))
return size;
@@ -990,7 +911,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
if (g_ascii_strcasecmp(name, "accept-ranges") == 0) {
/* a stream with icy-metadata is not seekable */
- if (!icy_defined(&c->icy_metadata))
+ if (!c->icy.IsDefined())
c->base.seekable = true;
} else if (g_ascii_strcasecmp(name, "content-length") == 0) {
char buffer[64];
@@ -1003,8 +924,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
c->base.size = c->base.offset + g_ascii_strtoull(buffer, NULL, 10);
} else if (g_ascii_strcasecmp(name, "content-type") == 0) {
- g_free(c->base.mime);
- c->base.mime = g_strndup(value, end - value);
+ c->base.mime.assign(value, end);
} else if (g_ascii_strcasecmp(name, "icy-name") == 0 ||
g_ascii_strcasecmp(name, "ice-name") == 0 ||
g_ascii_strcasecmp(name, "x-audiocast-name") == 0) {
@@ -1021,7 +941,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
size_t icy_metaint;
if ((size_t)(end - header) >= sizeof(buffer) ||
- icy_defined(&c->icy_metadata))
+ c->icy.IsDefined())
return size;
memcpy(buffer, value, end - value);
@@ -1031,7 +951,7 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
g_debug("icy-metaint=%zu", icy_metaint);
if (icy_metaint > 0) {
- icy_start(&c->icy_metadata, icy_metaint);
+ c->icy.Start(icy_metaint);
/* a stream with icy-metadata is not
seekable */
@@ -1047,33 +967,22 @@ static size_t
input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
struct input_curl *c = (struct input_curl *)stream;
- struct buffer *buffer;
size *= nmemb;
if (size == 0)
return 0;
- g_mutex_lock(c->base.mutex);
+ const ScopeLock protect(c->base.mutex);
-#if LIBCURL_VERSION_NUM >= 0x071200
if (curl_total_buffer_size(c) + size >= CURL_MAX_BUFFERED) {
c->paused = true;
- g_mutex_unlock(c->base.mutex);
return CURL_WRITEFUNC_PAUSE;
}
-#endif
-
- buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size);
- buffer->size = size;
- buffer->consumed = 0;
- memcpy(buffer->data, ptr, size);
- g_queue_push_tail(c->buffers, buffer);
+ c->buffers.emplace_back(ptr, size);
c->base.ready = true;
- g_cond_broadcast(c->base.cond);
- g_mutex_unlock(c->base.mutex);
-
+ c->base.cond.broadcast();
return size;
}
@@ -1120,7 +1029,7 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
g_free(proxy_auth_str);
}
- code = curl_easy_setopt(c->easy, CURLOPT_URL, c->url);
+ code = curl_easy_setopt(c->easy, CURLOPT_URL, c->base.uri.c_str());
if (code != CURLE_OK) {
g_set_error(error_r, curl_quark(), code,
"curl_easy_setopt() failed: %s",
@@ -1179,19 +1088,15 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
/* check if we can fast-forward the buffer */
- while (offset > is->offset && !g_queue_is_empty(c->buffers)) {
- struct buffer *buffer;
- size_t length;
-
- buffer = (struct buffer *)g_queue_pop_head(c->buffers);
-
- length = buffer->size - buffer->consumed;
+ while (offset > is->offset && !c->buffers.empty()) {
+ auto &buffer = c->buffers.front();
+ size_t length = buffer.Available();
if (offset - is->offset < (goffset)length)
length = offset - is->offset;
- buffer = consume_buffer(buffer, length);
- if (buffer != NULL)
- g_queue_push_head(c->buffers, buffer);
+ const bool empty = !buffer.Consume(length);
+ if (empty)
+ c->buffers.pop_front();
is->offset += length;
}
@@ -1201,10 +1106,10 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
/* close the old connection and open a new one */
- g_mutex_unlock(c->base.mutex);
+ c->base.mutex.unlock();
input_curl_easy_free_indirect(c);
- input_curl_flush_buffers(c);
+ c->buffers.clear();
is->offset = offset;
if (is->offset == is->size) {
@@ -1230,10 +1135,10 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
if (!input_curl_easy_add_indirect(c, error_r))
return false;
- g_mutex_lock(c->base.mutex);
+ c->base.mutex.lock();
while (!c->base.ready)
- g_cond_wait(c->base.cond, c->base.mutex);
+ c->base.cond.wait(c->base.mutex);
if (c->postponed_error != NULL) {
g_propagate_error(error_r, c->postponed_error);
@@ -1245,40 +1150,21 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
}
static struct input_stream *
-input_curl_open(const char *url, GMutex *mutex, GCond *cond,
+input_curl_open(const char *url, Mutex &mutex, Cond &cond,
GError **error_r)
{
- assert(mutex != NULL);
- assert(cond != NULL);
-
- struct input_curl *c;
-
if (strncmp(url, "http://", 7) != 0)
return NULL;
- c = g_new0(struct input_curl, 1);
- input_stream_init(&c->base, &input_plugin_curl, url,
- mutex, cond);
-
- c->url = g_strdup(url);
- c->buffers = g_queue_new();
-
- icy_clear(&c->icy_metadata);
- c->tag = NULL;
-
- c->postponed_error = NULL;
-
-#if LIBCURL_VERSION_NUM >= 0x071200
- c->paused = false;
-#endif
+ struct input_curl *c = new input_curl(url, mutex, cond);
if (!input_curl_easy_init(c, error_r)) {
- input_curl_free(c);
+ delete c;
return NULL;
}
if (!input_curl_easy_add_indirect(c, error_r)) {
- input_curl_free(c);
+ delete c;
return NULL;
}
@@ -1286,16 +1172,16 @@ input_curl_open(const char *url, GMutex *mutex, GCond *cond,
}
const struct input_plugin input_plugin_curl = {
- .name = "curl",
- .init = input_curl_init,
- .finish = input_curl_finish,
-
- .open = input_curl_open,
- .close = input_curl_close,
- .check = input_curl_check,
- .tag = input_curl_tag,
- .available = input_curl_available,
- .read = input_curl_read,
- .eof = input_curl_eof,
- .seek = input_curl_seek,
+ "curl",
+ input_curl_init,
+ input_curl_finish,
+ input_curl_open,
+ input_curl_close,
+ input_curl_check,
+ nullptr,
+ input_curl_tag,
+ input_curl_available,
+ input_curl_read,
+ input_curl_eof,
+ input_curl_seek,
};
diff --git a/src/input/curl_input_plugin.h b/src/input/CurlInputPlugin.hxx
index c6e71bf4..20d1309d 100644
--- a/src/input/curl_input_plugin.h
+++ b/src/input/CurlInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_CURL_H
-#define MPD_INPUT_CURL_H
+#ifndef MPD_INPUT_CURL_HXX
+#define MPD_INPUT_CURL_HXX
struct input_stream;
diff --git a/src/input/despotify_input_plugin.c b/src/input/DespotifyInputPlugin.cxx
index 200a0afd..1e5a8c60 100644
--- a/src/input/despotify_input_plugin.c
+++ b/src/input/DespotifyInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Music Player Daemon Project
+ * Copyright (C) 2011-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,22 +18,26 @@
*/
#include "config.h"
-#include "input/despotify_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "DespotifyInputPlugin.hxx"
+#include "DespotifyUtils.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include "tag.h"
-#include "despotify_utils.h"
+
+extern "C" {
+#include <despotify.h>
+}
#include <glib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
-#include <despotify.h>
#include <stdio.h>
-struct input_despotify {
+struct DespotifyInputStream {
struct input_stream base;
struct despotify_session *session;
@@ -42,11 +46,33 @@ struct input_despotify {
struct ds_pcm_data pcm;
size_t len_available;
bool eof;
-};
+ DespotifyInputStream(const char *uri,
+ Mutex &mutex, Cond &cond,
+ despotify_session *_session,
+ ds_track *_track)
+ :base(input_plugin_despotify, uri, mutex, cond),
+ session(_session), track(_track),
+ tag(mpd_despotify_tag_from_track(track)),
+ len_available(0), eof(false) {
+
+ memset(&pcm, 0, sizeof(pcm));
+
+ /* Despotify outputs pcm data */
+ base.mime = g_strdup("audio/x-mpd-cdda-pcm");
+ base.ready = true;
+ }
+
+ ~DespotifyInputStream() {
+ if (tag != NULL)
+ tag_free(tag);
+
+ despotify_free_track(track);
+ }
+};
static void
-refill_buffer(struct input_despotify *ctx)
+refill_buffer(DespotifyInputStream *ctx)
{
/* Wait until there is data */
while (1) {
@@ -73,7 +99,7 @@ refill_buffer(struct input_despotify *ctx)
static void callback(G_GNUC_UNUSED struct despotify_session* ds,
int sig, G_GNUC_UNUSED void* data, void* callback_data)
{
- struct input_despotify *ctx = (struct input_despotify *)callback_data;
+ DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data;
switch (sig) {
case DESPOTIFY_NEW_TRACK:
@@ -98,10 +124,9 @@ static void callback(G_GNUC_UNUSED struct despotify_session* ds,
static struct input_stream *
input_despotify_open(const char *url,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
G_GNUC_UNUSED GError **error_r)
{
- struct input_despotify *ctx;
struct despotify_session *session;
struct ds_link *ds_link;
struct ds_track *track;
@@ -123,35 +148,23 @@ input_despotify_open(const char *url,
return NULL;
}
- ctx = g_new(struct input_despotify, 1);
- memset(ctx, 0, sizeof(*ctx));
-
track = despotify_link_get_track(session, ds_link);
despotify_free_link(ds_link);
- if (!track) {
- g_free(ctx);
+ if (!track)
return NULL;
- }
- input_stream_init(&ctx->base, &input_plugin_despotify, url,
- mutex, cond);
- ctx->session = session;
- ctx->track = track;
- ctx->tag = mpd_despotify_tag_from_track(track);
- ctx->eof = false;
- /* Despotify outputs pcm data */
- ctx->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
- ctx->base.ready = true;
+ DespotifyInputStream *ctx =
+ new DespotifyInputStream(url, mutex, cond,
+ session, track);
if (!mpd_despotify_register_callback(callback, ctx)) {
- despotify_free_link(ds_link);
-
+ delete ctx;
return NULL;
}
if (despotify_play(ctx->session, ctx->track, false) == false) {
- despotify_free_track(ctx->track);
- g_free(ctx);
+ mpd_despotify_unregister_callback(callback);
+ delete ctx;
return NULL;
}
@@ -162,7 +175,7 @@ static size_t
input_despotify_read(struct input_stream *is, void *ptr, size_t size,
G_GNUC_UNUSED GError **error_r)
{
- struct input_despotify *ctx = (struct input_despotify *)is;
+ DespotifyInputStream *ctx = (DespotifyInputStream *)is;
size_t to_cpy = size;
if (ctx->len_available == 0)
@@ -181,21 +194,16 @@ input_despotify_read(struct input_stream *is, void *ptr, size_t size,
static void
input_despotify_close(struct input_stream *is)
{
- struct input_despotify *ctx = (struct input_despotify *)is;
-
- if (ctx->tag != NULL)
- tag_free(ctx->tag);
+ DespotifyInputStream *ctx = (DespotifyInputStream *)is;
mpd_despotify_unregister_callback(callback);
- despotify_free_track(ctx->track);
- input_stream_deinit(&ctx->base);
- g_free(ctx);
+ delete ctx;
}
static bool
input_despotify_eof(struct input_stream *is)
{
- struct input_despotify *ctx = (struct input_despotify *)is;
+ DespotifyInputStream *ctx = (DespotifyInputStream *)is;
return ctx->eof;
}
@@ -211,7 +219,7 @@ input_despotify_seek(G_GNUC_UNUSED struct input_stream *is,
static struct tag *
input_despotify_tag(struct input_stream *is)
{
- struct input_despotify *ctx = (struct input_despotify *)is;
+ DespotifyInputStream *ctx = (DespotifyInputStream *)is;
struct tag *tag = ctx->tag;
ctx->tag = NULL;
@@ -220,11 +228,16 @@ input_despotify_tag(struct input_stream *is)
}
const struct input_plugin input_plugin_despotify = {
- .name = "spt",
- .open = input_despotify_open,
- .close = input_despotify_close,
- .read = input_despotify_read,
- .eof = input_despotify_eof,
- .seek = input_despotify_seek,
+ "spt",
+ nullptr,
+ nullptr,
+ input_despotify_open,
+ input_despotify_close,
+ nullptr,
+ nullptr,
.tag = input_despotify_tag,
+ nullptr,
+ input_despotify_read,
+ input_despotify_eof,
+ input_despotify_seek,
};
diff --git a/src/input/despotify_input_plugin.h b/src/input/DespotifyInputPlugin.hxx
index 4c070d88..00d69940 100644
--- a/src/input/despotify_input_plugin.h
+++ b/src/input/DespotifyInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Music Player Daemon Project
+ * Copyright (C) 2011-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef INPUT_DESPOTIFY_H
-#define INPUT_DESPOTIFY_H
+#ifndef INPUT_DESPOTIFY_HXX
+#define INPUT_DESPOTIFY_HXX
extern const struct input_plugin input_plugin_despotify;
diff --git a/src/input/ffmpeg_input_plugin.c b/src/input/FfmpegInputPlugin.cxx
index 6d339a06..1660f177 100644
--- a/src/input/ffmpeg_input_plugin.c
+++ b/src/input/FfmpegInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,28 +17,49 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
-#include "input/ffmpeg_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "FfmpegInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
+extern "C" {
#include <libavutil/avutil.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
+}
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_ffmpeg"
-struct input_ffmpeg {
+struct FfmpegInputStream {
struct input_stream base;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
AVIOContext *h;
-#else
- URLContext *h;
-#endif
bool eof;
+
+ FfmpegInputStream(const char *uri, Mutex &mutex, Cond &cond,
+ AVIOContext *_h)
+ :base(input_plugin_ffmpeg, uri, mutex, cond),
+ h(_h), eof(false) {
+ base.ready = true;
+ base.seekable = (h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
+ base.size = avio_size(h);
+
+ /* hack to make MPD select the "ffmpeg" decoder plugin
+ - since avio.h doesn't tell us the MIME type of the
+ resource, we can't select a decoder plugin, but the
+ "ffmpeg" plugin is quite good at auto-detection */
+ base.mime = g_strdup("audio/x-mpd-ffmpeg");
+ }
+
+ ~FfmpegInputStream() {
+ avio_close(h);
+ }
};
static inline GQuark
@@ -50,12 +71,8 @@ ffmpeg_quark(void)
static inline bool
input_ffmpeg_supported(void)
{
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
- void *opaque = NULL;
- return avio_enum_protocols(&opaque, 0) != NULL;
-#else
- return av_protocol_next(NULL) != NULL;
-#endif
+ void *opaque = nullptr;
+ return avio_enum_protocols(&opaque, 0) != nullptr;
}
static bool
@@ -76,54 +93,26 @@ input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
static struct input_stream *
input_ffmpeg_open(const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
- struct input_ffmpeg *i;
-
if (!g_str_has_prefix(uri, "gopher://") &&
!g_str_has_prefix(uri, "rtp://") &&
!g_str_has_prefix(uri, "rtsp://") &&
!g_str_has_prefix(uri, "rtmp://") &&
!g_str_has_prefix(uri, "rtmpt://") &&
!g_str_has_prefix(uri, "rtmps://"))
- return NULL;
-
- i = g_new(struct input_ffmpeg, 1);
- input_stream_init(&i->base, &input_plugin_ffmpeg, uri,
- mutex, cond);
-
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
- int ret = avio_open(&i->h, uri, AVIO_FLAG_READ);
-#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
- int ret = avio_open(&i->h, uri, AVIO_RDONLY);
-#else
- int ret = url_open(&i->h, uri, URL_RDONLY);
-#endif
+ return nullptr;
+
+ AVIOContext *h;
+ int ret = avio_open(&h, uri, AVIO_FLAG_READ);
if (ret != 0) {
- g_free(i);
g_set_error(error_r, ffmpeg_quark(), ret,
"libavformat failed to open the URI");
- return NULL;
+ return nullptr;
}
- i->eof = false;
-
- i->base.ready = true;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
- i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
- i->base.size = avio_size(i->h);
-#else
- i->base.seekable = !i->h->is_streamed;
- i->base.size = url_filesize(i->h);
-#endif
-
- /* hack to make MPD select the "ffmpeg" decoder plugin - since
- avio.h doesn't tell us the MIME type of the resource, we
- can't select a decoder plugin, but the "ffmpeg" plugin is
- quite good at auto-detection */
- i->base.mime = g_strdup("audio/x-mpd-ffmpeg");
-
+ auto *i = new FfmpegInputStream(uri, mutex, cond, h);
return &i->base;
}
@@ -131,13 +120,9 @@ static size_t
input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
- struct input_ffmpeg *i = (struct input_ffmpeg *)is;
+ FfmpegInputStream *i = (FfmpegInputStream *)is;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
- int ret = avio_read(i->h, ptr, size);
-#else
- int ret = url_read(i->h, ptr, size);
-#endif
+ int ret = avio_read(i->h, (unsigned char *)ptr, size);
if (ret <= 0) {
if (ret < 0)
g_set_error(error_r, ffmpeg_quark(), 0,
@@ -154,21 +139,15 @@ input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size,
static void
input_ffmpeg_close(struct input_stream *is)
{
- struct input_ffmpeg *i = (struct input_ffmpeg *)is;
-
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
- avio_close(i->h);
-#else
- url_close(i->h);
-#endif
- input_stream_deinit(&i->base);
- g_free(i);
+ FfmpegInputStream *i = (FfmpegInputStream *)is;
+
+ delete i;
}
static bool
input_ffmpeg_eof(struct input_stream *is)
{
- struct input_ffmpeg *i = (struct input_ffmpeg *)is;
+ FfmpegInputStream *i = (FfmpegInputStream *)is;
return i->eof;
}
@@ -177,12 +156,8 @@ static bool
input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence,
G_GNUC_UNUSED GError **error_r)
{
- struct input_ffmpeg *i = (struct input_ffmpeg *)is;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ FfmpegInputStream *i = (FfmpegInputStream *)is;
int64_t ret = avio_seek(i->h, offset, whence);
-#else
- int64_t ret = url_seek(i->h, offset, whence);
-#endif
if (ret >= 0) {
i->eof = false;
@@ -194,11 +169,16 @@ input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence,
}
const struct input_plugin input_plugin_ffmpeg = {
- .name = "ffmpeg",
- .init = input_ffmpeg_init,
- .open = input_ffmpeg_open,
- .close = input_ffmpeg_close,
- .read = input_ffmpeg_read,
- .eof = input_ffmpeg_eof,
- .seek = input_ffmpeg_seek,
+ "ffmpeg",
+ input_ffmpeg_init,
+ nullptr,
+ input_ffmpeg_open,
+ input_ffmpeg_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ input_ffmpeg_read,
+ input_ffmpeg_eof,
+ input_ffmpeg_seek,
};
diff --git a/src/input/ffmpeg_input_plugin.h b/src/input/FfmpegInputPlugin.hxx
index 393836ca..d5e3a8d9 100644
--- a/src/input/ffmpeg_input_plugin.h
+++ b/src/input/FfmpegInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FFMPEG_INPUT_PLUGIN_H
-#define MPD_FFMPEG_INPUT_PLUGIN_H
+#ifndef MPD_FFMPEG_INPUT_PLUGIN_HXX
+#define MPD_FFMPEG_INPUT_PLUGIN_HXX
/**
* An input plugin based on libavformat's "avio" library.
diff --git a/src/input/file_input_plugin.c b/src/input/FileInputPlugin.cxx
index 5ee3f200..2eecf32b 100644
--- a/src/input/file_input_plugin.c
+++ b/src/input/FileInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,13 @@
*/
#include "config.h" /* must be first for large file support */
-#include "input/file_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "FileInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include "fd_util.h"
#include "open.h"
+#include "io_error.h"
#include <sys/stat.h>
#include <unistd.h>
@@ -33,69 +35,67 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_file"
-struct file_input_stream {
+struct FileInputStream {
struct input_stream base;
int fd;
-};
-static inline GQuark
-file_quark(void)
-{
- return g_quark_from_static_string("file");
-}
+ FileInputStream(const char *path, int _fd, off_t size,
+ Mutex &mutex, Cond &cond)
+ :base(input_plugin_file, path, mutex, cond),
+ fd(_fd) {
+ base.size = size;
+ base.seekable = true;
+ base.ready = true;
+ }
+
+ ~FileInputStream() {
+ close(fd);
+ }
+};
static struct input_stream *
input_file_open(const char *filename,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
int fd, ret;
struct stat st;
- struct file_input_stream *fis;
if (!g_path_is_absolute(filename))
- return NULL;
+ return nullptr;
fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0);
if (fd < 0) {
if (errno != ENOENT && errno != ENOTDIR)
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to open \"%s\": %s",
filename, g_strerror(errno));
- return NULL;
+ return nullptr;
}
ret = fstat(fd, &st);
if (ret < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to stat \"%s\": %s",
filename, g_strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
if (!S_ISREG(st.st_mode)) {
- g_set_error(error_r, file_quark(), 0,
+ g_set_error(error_r, errno_quark(), 0,
"Not a regular file: %s", filename);
close(fd);
- return NULL;
+ return nullptr;
}
#ifdef POSIX_FADV_SEQUENTIAL
posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif
- fis = g_new(struct file_input_stream, 1);
- input_stream_init(&fis->base, &input_plugin_file, filename,
- mutex, cond);
-
- fis->base.size = st.st_size;
- fis->base.seekable = true;
- fis->base.ready = true;
-
- fis->fd = fd;
-
+ FileInputStream *fis = new FileInputStream(filename, fd, st.st_size,
+ mutex, cond);
return &fis->base;
}
@@ -103,11 +103,11 @@ static bool
input_file_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
{
- struct file_input_stream *fis = (struct file_input_stream *)is;
+ FileInputStream *fis = (FileInputStream *)is;
offset = (goffset)lseek(fis->fd, (off_t)offset, whence);
if (offset < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to seek: %s", g_strerror(errno));
return false;
}
@@ -120,12 +120,12 @@ static size_t
input_file_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
- struct file_input_stream *fis = (struct file_input_stream *)is;
+ FileInputStream *fis = (FileInputStream *)is;
ssize_t nbytes;
nbytes = read(fis->fd, ptr, size);
if (nbytes < 0) {
- g_set_error(error_r, file_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to read: %s", g_strerror(errno));
return 0;
}
@@ -137,11 +137,9 @@ input_file_read(struct input_stream *is, void *ptr, size_t size,
static void
input_file_close(struct input_stream *is)
{
- struct file_input_stream *fis = (struct file_input_stream *)is;
+ FileInputStream *fis = (FileInputStream *)is;
- close(fis->fd);
- input_stream_deinit(&fis->base);
- g_free(fis);
+ delete fis;
}
static bool
@@ -151,10 +149,16 @@ input_file_eof(struct input_stream *is)
}
const struct input_plugin input_plugin_file = {
- .name = "file",
- .open = input_file_open,
- .close = input_file_close,
- .read = input_file_read,
- .eof = input_file_eof,
- .seek = input_file_seek,
+ "file",
+ nullptr,
+ nullptr,
+ input_file_open,
+ input_file_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ input_file_read,
+ input_file_eof,
+ input_file_seek,
};
diff --git a/src/input/file_input_plugin.h b/src/input/FileInputPlugin.hxx
index f24769d5..aacfd0b5 100644
--- a/src/input/file_input_plugin.h
+++ b/src/input/FileInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_FILE_H
-#define MPD_INPUT_FILE_H
+#ifndef MPD_INPUT_FILE_HXX
+#define MPD_INPUT_FILE_HXX
extern const struct input_plugin input_plugin_file;
diff --git a/src/input/mms_input_plugin.c b/src/input/MmsInputPlugin.cxx
index cff15125..b347eb92 100644
--- a/src/input/mms_input_plugin.c
+++ b/src/input/MmsInputPlugin.cxx
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "input/mms_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "MmsInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include <glib.h>
#include <libmms/mmsx.h>
@@ -31,12 +32,28 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_mms"
-struct input_mms {
+struct MmsInputStream {
struct input_stream base;
mmsx_t *mms;
bool eof;
+
+ MmsInputStream(const char *uri,
+ Mutex &mutex, Cond &cond,
+ mmsx_t *_mms)
+ :base(input_plugin_mms, uri, mutex, cond),
+ mms(_mms), eof(false) {
+ /* XX is this correct? at least this selects the ffmpeg
+ decoder, which seems to work fine*/
+ base.mime = g_strdup("audio/x-ms-wma");
+
+ base.ready = true;
+ }
+
+ ~MmsInputStream() {
+ mmsx_close(mms);
+ }
};
static inline GQuark
@@ -47,36 +64,22 @@ mms_quark(void)
static struct input_stream *
input_mms_open(const char *url,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r)
{
- struct input_mms *m;
-
if (!g_str_has_prefix(url, "mms://") &&
!g_str_has_prefix(url, "mmsh://") &&
!g_str_has_prefix(url, "mmst://") &&
!g_str_has_prefix(url, "mmsu://"))
- return NULL;
-
- m = g_new(struct input_mms, 1);
- input_stream_init(&m->base, &input_plugin_mms, url,
- mutex, cond);
+ return nullptr;
- m->mms = mmsx_connect(NULL, NULL, url, 128 * 1024);
- if (m->mms == NULL) {
- g_free(m);
+ const auto mms = mmsx_connect(nullptr, nullptr, url, 128 * 1024);
+ if (mms == nullptr) {
g_set_error(error_r, mms_quark(), 0, "mmsx_connect() failed");
- return NULL;
+ return nullptr;
}
- m->eof = false;
-
- /* XX is this correct? at least this selects the ffmpeg
- decoder, which seems to work fine*/
- m->base.mime = g_strdup("audio/x-ms-wma");
-
- m->base.ready = true;
-
+ auto m = new MmsInputStream(url, mutex, cond, mms);
return &m->base;
}
@@ -84,10 +87,10 @@ static size_t
input_mms_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
- struct input_mms *m = (struct input_mms *)is;
+ MmsInputStream *m = (MmsInputStream *)is;
int ret;
- ret = mmsx_read(NULL, m->mms, ptr, size);
+ ret = mmsx_read(nullptr, m->mms, (char *)ptr, size);
if (ret <= 0) {
if (ret < 0) {
g_set_error(error_r, mms_quark(), errno,
@@ -107,17 +110,15 @@ input_mms_read(struct input_stream *is, void *ptr, size_t size,
static void
input_mms_close(struct input_stream *is)
{
- struct input_mms *m = (struct input_mms *)is;
+ MmsInputStream *m = (MmsInputStream *)is;
- mmsx_close(m->mms);
- input_stream_deinit(&m->base);
- g_free(m);
+ delete m;
}
static bool
input_mms_eof(struct input_stream *is)
{
- struct input_mms *m = (struct input_mms *)is;
+ MmsInputStream *m = (MmsInputStream *)is;
return m->eof;
}
@@ -131,10 +132,16 @@ input_mms_seek(G_GNUC_UNUSED struct input_stream *is,
}
const struct input_plugin input_plugin_mms = {
- .name = "mms",
- .open = input_mms_open,
- .close = input_mms_close,
- .read = input_mms_read,
- .eof = input_mms_eof,
- .seek = input_mms_seek,
+ "mms",
+ nullptr,
+ nullptr,
+ input_mms_open,
+ input_mms_close,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ input_mms_read,
+ input_mms_eof,
+ input_mms_seek,
};
diff --git a/src/input/mms_input_plugin.h b/src/input/MmsInputPlugin.hxx
index d6aa593f..d6aa593f 100644
--- a/src/input/mms_input_plugin.h
+++ b/src/input/MmsInputPlugin.hxx
diff --git a/src/input/rewind_input_plugin.c b/src/input/RewindInputPlugin.cxx
index cf06fc57..d93d7d1c 100644
--- a/src/input/rewind_input_plugin.c
+++ b/src/input/RewindInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,10 @@
*/
#include "config.h"
-#include "input/rewind_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
+#include "RewindInputPlugin.hxx"
+#include "InputInternal.hxx"
+#include "InputStream.hxx"
+#include "InputPlugin.hxx"
#include "tag.h"
#include <glib.h>
@@ -31,14 +32,16 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_rewind"
-struct input_rewind {
+extern const struct input_plugin rewind_input_plugin;
+
+struct RewindInputStream {
struct input_stream base;
struct input_stream *input;
/**
* The read position within the buffer. Undefined as long as
- * reading_from_buffer() returns false.
+ * ReadingFromBuffer() returns false.
*/
size_t head;
@@ -56,61 +59,61 @@ struct input_rewind {
* stream (offset 0).
*/
char buffer[64 * 1024];
-};
-/**
- * Are we currently reading from the buffer, and does the buffer
- * contain more data for the next read operation?
- */
-static bool
-reading_from_buffer(const struct input_rewind *r)
-{
- return r->tail > 0 && r->base.offset < r->input->offset;
-}
+ RewindInputStream(input_stream *_input)
+ :base(rewind_input_plugin, _input->uri.c_str(),
+ _input->mutex, _input->cond),
+ input(_input), tail(0) {
+ }
-/**
- * Copy public attributes from the underlying input stream to the
- * "rewind" input stream. This function is called when a method of
- * the underlying stream has returned, which may have modified these
- * attributes.
- */
-static void
-copy_attributes(struct input_rewind *r)
-{
- struct input_stream *dest = &r->base;
- const struct input_stream *src = r->input;
+ ~RewindInputStream() {
+ input_stream_close(input);
+ }
- assert(dest != src);
- assert(src->mime == NULL || dest->mime != src->mime);
+ /**
+ * Are we currently reading from the buffer, and does the
+ * buffer contain more data for the next read operation?
+ */
+ bool ReadingFromBuffer() const {
+ return tail > 0 && base.offset < input->offset;
+ }
+
+ /**
+ * Copy public attributes from the underlying input stream to the
+ * "rewind" input stream. This function is called when a method of
+ * the underlying stream has returned, which may have modified these
+ * attributes.
+ */
+ void CopyAttributes() {
+ struct input_stream *dest = &base;
+ const struct input_stream *src = input;
+
+ assert(dest != src);
- bool dest_ready = dest->ready;
+ bool dest_ready = dest->ready;
- dest->ready = src->ready;
- dest->seekable = src->seekable;
- dest->size = src->size;
- dest->offset = src->offset;
+ dest->ready = src->ready;
+ dest->seekable = src->seekable;
+ dest->size = src->size;
+ dest->offset = src->offset;
- if (!dest_ready && src->ready) {
- g_free(dest->mime);
- dest->mime = g_strdup(src->mime);
+ if (!dest_ready && src->ready)
+ dest->mime = src->mime;
}
-}
+};
static void
input_rewind_close(struct input_stream *is)
{
- struct input_rewind *r = (struct input_rewind *)is;
-
- input_stream_close(r->input);
+ RewindInputStream *r = (RewindInputStream *)is;
- input_stream_deinit(&r->base);
- g_free(r);
+ delete r;
}
static bool
input_rewind_check(struct input_stream *is, GError **error_r)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
return input_stream_check(r->input, error_r);
}
@@ -118,16 +121,16 @@ input_rewind_check(struct input_stream *is, GError **error_r)
static void
input_rewind_update(struct input_stream *is)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
- if (!reading_from_buffer(r))
- copy_attributes(r);
+ if (!r->ReadingFromBuffer())
+ r->CopyAttributes();
}
static struct tag *
input_rewind_tag(struct input_stream *is)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
return input_stream_tag(r->input);
}
@@ -135,7 +138,7 @@ input_rewind_tag(struct input_stream *is)
static bool
input_rewind_available(struct input_stream *is)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
return input_stream_available(r->input);
}
@@ -144,9 +147,9 @@ static size_t
input_rewind_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
- if (reading_from_buffer(r)) {
+ if (r->ReadingFromBuffer()) {
/* buffered read */
assert(r->head == (size_t)is->offset);
@@ -177,7 +180,7 @@ input_rewind_read(struct input_stream *is, void *ptr, size_t size,
assert(r->tail == (size_t)r->input->offset);
}
- copy_attributes(r);
+ r->CopyAttributes();
return nbytes;
}
@@ -186,23 +189,23 @@ input_rewind_read(struct input_stream *is, void *ptr, size_t size,
static bool
input_rewind_eof(struct input_stream *is)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
- return !reading_from_buffer(r) && input_stream_eof(r->input);
+ return !r->ReadingFromBuffer() && input_stream_eof(r->input);
}
static bool
input_rewind_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
{
- struct input_rewind *r = (struct input_rewind *)is;
+ RewindInputStream *r = (RewindInputStream *)is;
assert(is->ready);
if (whence == SEEK_SET && r->tail > 0 && offset <= (goffset)r->tail) {
/* buffered seek */
- assert(!reading_from_buffer(r) ||
+ assert(!r->ReadingFromBuffer() ||
r->head == (size_t)is->offset);
assert(r->tail == (size_t)r->input->offset);
@@ -213,7 +216,7 @@ input_rewind_seek(struct input_stream *is, goffset offset, int whence,
} else {
bool success = input_stream_seek(r->input, offset, whence,
error_r);
- copy_attributes(r);
+ r->CopyAttributes();
/* disable the buffer, because r->input has left the
buffered range now */
@@ -223,22 +226,24 @@ input_rewind_seek(struct input_stream *is, goffset offset, int whence,
}
}
-static const struct input_plugin rewind_input_plugin = {
- .close = input_rewind_close,
- .check = input_rewind_check,
- .update = input_rewind_update,
- .tag = input_rewind_tag,
- .available = input_rewind_available,
- .read = input_rewind_read,
- .eof = input_rewind_eof,
- .seek = input_rewind_seek,
+const struct input_plugin rewind_input_plugin = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ input_rewind_close,
+ input_rewind_check,
+ input_rewind_update,
+ input_rewind_tag,
+ input_rewind_available,
+ input_rewind_read,
+ input_rewind_eof,
+ input_rewind_seek,
};
struct input_stream *
input_rewind_open(struct input_stream *is)
{
- struct input_rewind *c;
-
assert(is != NULL);
assert(is->offset == 0);
@@ -246,11 +251,6 @@ input_rewind_open(struct input_stream *is)
/* seekable resources don't need this plugin */
return is;
- c = g_new(struct input_rewind, 1);
- input_stream_init(&c->base, &rewind_input_plugin, is->uri,
- is->mutex, is->cond);
- c->tail = 0;
- c->input = is;
-
+ RewindInputStream *c = new RewindInputStream(is);
return &c->base;
}
diff --git a/src/input/rewind_input_plugin.h b/src/input/RewindInputPlugin.hxx
index 83abe257..cf21e92f 100644
--- a/src/input/rewind_input_plugin.h
+++ b/src/input/RewindInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,8 +24,8 @@
* each decoder plugin peek a portion from the stream).
*/
-#ifndef MPD_INPUT_REWIND_H
-#define MPD_INPUT_REWIND_H
+#ifndef MPD_INPUT_REWIND_HXX
+#define MPD_INPUT_REWIND_HXX
#include "check.h"
diff --git a/src/input/soup_input_plugin.c b/src/input/SoupInputPlugin.cxx
index fc903b48..e9767c20 100644
--- a/src/input/soup_input_plugin.c
+++ b/src/input/SoupInputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,18 @@
*/
#include "config.h"
-#include "input/soup_input_plugin.h"
-#include "input_internal.h"
-#include "input_plugin.h"
-#include "io_thread.h"
+#include "SoupInputPlugin.hxx"
+#include "InputPlugin.hxx"
+#include "InputStream.hxx"
+#include "InputInternal.hxx"
+#include "IOThread.hxx"
+#include "event/Loop.hxx"
#include "conf.h"
+extern "C" {
#include <libsoup/soup-uri.h>
#include <libsoup/soup-session-async.h>
+}
#include <assert.h>
#include <string.h>
@@ -48,7 +52,7 @@ static const size_t SOUP_RESUME_AT = 384 * 1024;
static SoupURI *soup_proxy;
static SoupSession *soup_session;
-struct input_soup {
+struct SoupInputStream {
struct input_stream base;
SoupMessage *msg;
@@ -68,6 +72,15 @@ struct input_soup {
bool completed;
GError *postponed_error;
+
+ SoupInputStream(const char *uri, Mutex &mutex, Cond &cond);
+ ~SoupInputStream();
+
+ bool CopyError(const SoupMessage *msg);
+
+ bool WaitData();
+
+ size_t Read(void *ptr, size_t size, GError **error_r);
};
static inline GQuark
@@ -99,7 +112,7 @@ input_soup_init(const struct config_param *param, GError **error_r)
soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI,
soup_proxy,
SOUP_SESSION_ASYNC_CONTEXT,
- io_thread_context(),
+ io_thread_get().GetContext(),
NULL);
return true;
@@ -123,31 +136,31 @@ input_soup_finish(void)
*
* @return true if there was no error
*/
-static bool
-input_soup_copy_error(struct input_soup *s, const SoupMessage *msg)
+bool
+SoupInputStream::CopyError(const SoupMessage *src)
{
- if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code))
+ if (SOUP_STATUS_IS_SUCCESSFUL(src->status_code))
return true;
- if (msg->status_code == SOUP_STATUS_CANCELLED)
+ if (src->status_code == SOUP_STATUS_CANCELLED)
/* failure, but don't generate a GError, because this
status was caused by _close() */
return false;
- if (s->postponed_error != NULL)
+ if (postponed_error != nullptr)
/* there's already a GError, don't overwrite it */
return false;
- if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code))
- s->postponed_error =
- g_error_new(soup_quark(), msg->status_code,
+ if (SOUP_STATUS_IS_TRANSPORT_ERROR(src->status_code))
+ postponed_error =
+ g_error_new(soup_quark(), src->status_code,
"HTTP client error: %s",
- msg->reason_phrase);
+ src->reason_phrase);
else
- s->postponed_error =
- g_error_new(soup_quark(), msg->status_code,
+ postponed_error =
+ g_error_new(soup_quark(), src->status_code,
"got HTTP status: %d %s",
- msg->status_code, msg->reason_phrase);
+ src->status_code, src->reason_phrase);
return false;
}
@@ -156,33 +169,32 @@ static void
input_soup_session_callback(G_GNUC_UNUSED SoupSession *session,
SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ SoupInputStream *s = (SoupInputStream *)user_data;
assert(msg == s->msg);
assert(!s->completed);
- g_mutex_lock(s->base.mutex);
+ const ScopeLock protect(s->base.mutex);
if (!s->base.ready)
- input_soup_copy_error(s, msg);
+ s->CopyError(msg);
s->base.ready = true;
s->alive = false;
s->completed = true;
- g_cond_broadcast(s->base.cond);
- g_mutex_unlock(s->base.mutex);
+ s->base.cond.broadcast();
}
static void
input_soup_got_headers(SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ SoupInputStream *s = (SoupInputStream *)user_data;
- g_mutex_lock(s->base.mutex);
+ s->base.mutex.lock();
- if (!input_soup_copy_error(s, msg)) {
- g_mutex_unlock(s->base.mutex);
+ if (!s->CopyError(msg)) {
+ s->base.mutex.unlock();
soup_session_cancel_message(soup_session, msg,
SOUP_STATUS_CANCELLED);
@@ -190,8 +202,8 @@ input_soup_got_headers(SoupMessage *msg, gpointer user_data)
}
s->base.ready = true;
- g_cond_broadcast(s->base.cond);
- g_mutex_unlock(s->base.mutex);
+ s->base.cond.broadcast();
+ s->base.mutex.unlock();
soup_message_body_set_accumulate(msg->response_body, false);
}
@@ -199,11 +211,11 @@ input_soup_got_headers(SoupMessage *msg, gpointer user_data)
static void
input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
{
- struct input_soup *s = user_data;
+ SoupInputStream *s = (SoupInputStream *)user_data;
assert(msg == s->msg);
- g_mutex_lock(s->base.mutex);
+ const ScopeLock protect(s->base.mutex);
g_queue_push_tail(s->buffers, soup_buffer_copy(chunk));
s->total_buffered += chunk->length;
@@ -213,50 +225,50 @@ input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
soup_session_pause_message(soup_session, msg);
}
- g_cond_broadcast(s->base.cond);
- g_mutex_unlock(s->base.mutex);
+ s->base.cond.broadcast();
+ s->base.mutex.unlock();
}
static void
input_soup_got_body(G_GNUC_UNUSED SoupMessage *msg, gpointer user_data)
{
- struct input_soup *s = user_data;
+ SoupInputStream *s = (SoupInputStream *)user_data;
assert(msg == s->msg);
- g_mutex_lock(s->base.mutex);
+ const ScopeLock protect(s->base.mutex);
s->base.ready = true;
s->eof = true;
s->alive = false;
- g_cond_broadcast(s->base.cond);
- g_mutex_unlock(s->base.mutex);
+ s->base.cond.broadcast();
+ s->base.mutex.unlock();
}
-static bool
-input_soup_wait_data(struct input_soup *s)
+inline bool
+SoupInputStream::WaitData()
{
while (true) {
- if (s->eof)
+ if (eof)
return true;
- if (!s->alive)
+ if (!alive)
return false;
- if (!g_queue_is_empty(s->buffers))
+ if (!g_queue_is_empty(buffers))
return true;
- assert(s->current_consumed == 0);
+ assert(current_consumed == 0);
- g_cond_wait(s->base.cond, s->base.mutex);
+ base.cond.wait(base.mutex);
}
}
static gpointer
input_soup_queue(gpointer data)
{
- struct input_soup *s = data;
+ SoupInputStream *s = (SoupInputStream *)data;
soup_session_queue_message(soup_session, s->msg,
input_soup_session_callback, s);
@@ -264,22 +276,14 @@ input_soup_queue(gpointer data)
return NULL;
}
-static struct input_stream *
-input_soup_open(const char *uri,
- GMutex *mutex, GCond *cond,
- G_GNUC_UNUSED GError **error_r)
+SoupInputStream::SoupInputStream(const char *uri,
+ Mutex &mutex, Cond &cond)
+ :base(input_plugin_soup, uri, mutex, cond),
+ buffers(g_queue_new()),
+ current_consumed(0), total_buffered(0),
+ alive(false), pause(false), eof(false), completed(false),
+ postponed_error(nullptr)
{
- if (strncmp(uri, "http://", 7) != 0)
- return NULL;
-
- struct input_soup *s = g_new(struct input_soup, 1);
- input_stream_init(&s->base, &input_plugin_soup, uri,
- mutex, cond);
-
- s->buffers = g_queue_new();
- s->current_consumed = 0;
- s->total_buffered = 0;
-
#if GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic push
/* the libsoup macro SOUP_METHOD_GET discards the "const"
@@ -288,39 +292,43 @@ input_soup_open(const char *uri,
#pragma GCC diagnostic ignored "-Wcast-qual"
#endif
- s->msg = soup_message_new(SOUP_METHOD_GET, uri);
+ msg = soup_message_new(SOUP_METHOD_GET, uri);
#if GCC_CHECK_VERSION(4,6)
#pragma GCC diagnostic pop
#endif
- soup_message_set_flags(s->msg, SOUP_MESSAGE_NO_REDIRECT);
+ soup_message_set_flags(msg, SOUP_MESSAGE_NO_REDIRECT);
- soup_message_headers_append(s->msg->request_headers, "User-Agent",
+ soup_message_headers_append(msg->request_headers, "User-Agent",
"Music Player Daemon " VERSION);
- g_signal_connect(s->msg, "got-headers",
- G_CALLBACK(input_soup_got_headers), s);
- g_signal_connect(s->msg, "got-chunk",
- G_CALLBACK(input_soup_got_chunk), s);
- g_signal_connect(s->msg, "got-body",
- G_CALLBACK(input_soup_got_body), s);
+ g_signal_connect(msg, "got-headers",
+ G_CALLBACK(input_soup_got_headers), this);
+ g_signal_connect(msg, "got-chunk",
+ G_CALLBACK(input_soup_got_chunk), this);
+ g_signal_connect(msg, "got-body",
+ G_CALLBACK(input_soup_got_body), this);
- s->alive = true;
- s->pause = false;
- s->eof = false;
- s->completed = false;
- s->postponed_error = NULL;
+ io_thread_call(input_soup_queue, this);
+}
- io_thread_call(input_soup_queue, s);
+static struct input_stream *
+input_soup_open(const char *uri,
+ Mutex &mutex, Cond &cond,
+ G_GNUC_UNUSED GError **error_r)
+{
+ if (strncmp(uri, "http://", 7) != 0)
+ return NULL;
+ SoupInputStream *s = new SoupInputStream(uri, mutex, cond);
return &s->base;
}
static gpointer
input_soup_cancel(gpointer data)
{
- struct input_soup *s = data;
+ SoupInputStream *s = (SoupInputStream *)data;
if (!s->completed)
soup_session_cancel_message(soup_session, s->msg,
@@ -329,44 +337,46 @@ input_soup_cancel(gpointer data)
return NULL;
}
-static void
-input_soup_close(struct input_stream *is)
+SoupInputStream::~SoupInputStream()
{
- struct input_soup *s = (struct input_soup *)is;
-
- g_mutex_lock(s->base.mutex);
+ base.mutex.lock();
- if (!s->completed) {
+ if (!completed) {
/* the messages's session callback hasn't been invoked
yet; cancel it and wait for completion */
- g_mutex_unlock(s->base.mutex);
+ base.mutex.unlock();
- io_thread_call(input_soup_cancel, s);
+ io_thread_call(input_soup_cancel, this);
- g_mutex_lock(s->base.mutex);
- while (!s->completed)
- g_cond_wait(s->base.cond, s->base.mutex);
+ base.mutex.lock();
+ while (!completed)
+ base.cond.wait(base.mutex);
}
- g_mutex_unlock(s->base.mutex);
+ base.mutex.unlock();
SoupBuffer *buffer;
- while ((buffer = g_queue_pop_head(s->buffers)) != NULL)
+ while ((buffer = (SoupBuffer *)g_queue_pop_head(buffers)) != NULL)
soup_buffer_free(buffer);
- g_queue_free(s->buffers);
+ g_queue_free(buffers);
- if (s->postponed_error != NULL)
- g_error_free(s->postponed_error);
+ if (postponed_error != NULL)
+ g_error_free(postponed_error);
+}
+
+static void
+input_soup_close(struct input_stream *is)
+{
+ SoupInputStream *s = (SoupInputStream *)is;
- input_stream_deinit(&s->base);
- g_free(s);
+ delete s;
}
static bool
input_soup_check(struct input_stream *is, GError **error_r)
{
- struct input_soup *s = (struct input_soup *)is;
+ SoupInputStream *s = (SoupInputStream *)is;
bool success = s->postponed_error == NULL;
if (!success) {
@@ -380,45 +390,43 @@ input_soup_check(struct input_stream *is, GError **error_r)
static bool
input_soup_available(struct input_stream *is)
{
- struct input_soup *s = (struct input_soup *)is;
+ SoupInputStream *s = (SoupInputStream *)is;
return s->eof || !s->alive || !g_queue_is_empty(s->buffers);
}
-static size_t
-input_soup_read(struct input_stream *is, void *ptr, size_t size,
- G_GNUC_UNUSED GError **error_r)
+inline size_t
+SoupInputStream::Read(void *ptr, size_t size, GError **error_r)
{
- struct input_soup *s = (struct input_soup *)is;
+ if (!WaitData()) {
+ assert(!alive);
- if (!input_soup_wait_data(s)) {
- assert(!s->alive);
-
- if (s->postponed_error != NULL) {
- g_propagate_error(error_r, s->postponed_error);
- s->postponed_error = NULL;
+ if (postponed_error != nullptr) {
+ g_propagate_error(error_r, postponed_error);
+ postponed_error = nullptr;
} else
g_set_error_literal(error_r, soup_quark(), 0,
"HTTP failure");
return 0;
}
- char *p0 = ptr, *p = p0, *p_end = p0 + size;
+ char *p0 = (char *)ptr, *p = p0, *p_end = p0 + size;
while (p < p_end) {
- SoupBuffer *buffer = g_queue_pop_head(s->buffers);
+ SoupBuffer *buffer = (SoupBuffer *)
+ g_queue_pop_head(buffers);
if (buffer == NULL) {
- assert(s->current_consumed == 0);
+ assert(current_consumed == 0);
break;
}
- assert(s->current_consumed < buffer->length);
- assert(s->total_buffered >= buffer->length);
+ assert(current_consumed < buffer->length);
+ assert(total_buffered >= buffer->length);
const char *q = buffer->data;
- q += s->current_consumed;
+ q += current_consumed;
- size_t remaining = buffer->length - s->current_consumed;
+ size_t remaining = buffer->length - current_consumed;
size_t nbytes = p_end - p;
if (nbytes > remaining)
nbytes = remaining;
@@ -426,48 +434,59 @@ input_soup_read(struct input_stream *is, void *ptr, size_t size,
memcpy(p, q, nbytes);
p += nbytes;
- s->current_consumed += remaining;
- if (s->current_consumed >= buffer->length) {
+ current_consumed += remaining;
+ if (current_consumed >= buffer->length) {
/* done with this buffer */
- s->total_buffered -= buffer->length;
+ total_buffered -= buffer->length;
soup_buffer_free(buffer);
- s->current_consumed = 0;
+ current_consumed = 0;
} else {
/* partial read */
assert(p == p_end);
- g_queue_push_head(s->buffers, buffer);
+ g_queue_push_head(buffers, buffer);
}
}
- if (s->pause && s->total_buffered < SOUP_RESUME_AT) {
- s->pause = false;
- soup_session_unpause_message(soup_session, s->msg);
+ if (pause && total_buffered < SOUP_RESUME_AT) {
+ pause = false;
+ soup_session_unpause_message(soup_session, msg);
}
size_t nbytes = p - p0;
- s->base.offset += nbytes;
+ base.offset += nbytes;
return nbytes;
}
+static size_t
+input_soup_read(struct input_stream *is, void *ptr, size_t size,
+ GError **error_r)
+{
+ SoupInputStream *s = (SoupInputStream *)is;
+
+ return s->Read(ptr, size, error_r);
+}
+
static bool
input_soup_eof(G_GNUC_UNUSED struct input_stream *is)
{
- struct input_soup *s = (struct input_soup *)is;
+ SoupInputStream *s = (SoupInputStream *)is;
return !s->alive && g_queue_is_empty(s->buffers);
}
const struct input_plugin input_plugin_soup = {
- .name = "soup",
- .init = input_soup_init,
- .finish = input_soup_finish,
-
- .open = input_soup_open,
- .close = input_soup_close,
- .check = input_soup_check,
- .available = input_soup_available,
- .read = input_soup_read,
- .eof = input_soup_eof,
+ "soup",
+ input_soup_init,
+ input_soup_finish,
+ input_soup_open,
+ input_soup_close,
+ input_soup_check,
+ nullptr,
+ nullptr,
+ input_soup_available,
+ input_soup_read,
+ input_soup_eof,
+ nullptr,
};
diff --git a/src/input/soup_input_plugin.h b/src/input/SoupInputPlugin.hxx
index 689b2d97..4c089b39 100644
--- a/src/input/soup_input_plugin.h
+++ b/src/input/SoupInputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_INPUT_SOUP_H
-#define MPD_INPUT_SOUP_H
+#ifndef MPD_INPUT_SOUP_HXX
+#define MPD_INPUT_SOUP_HXX
extern const struct input_plugin input_plugin_soup;
diff --git a/src/input_internal.c b/src/input_internal.c
deleted file mode 100644
index 92a71856..00000000
--- a/src/input_internal.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "input_internal.h"
-#include "input_stream.h"
-
-#include <assert.h>
-
-void
-input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
- const char *uri, GMutex *mutex, GCond *cond)
-{
- assert(is != NULL);
- assert(plugin != NULL);
- assert(uri != NULL);
-
- is->plugin = plugin;
- is->uri = g_strdup(uri);
- is->mutex = mutex;
- is->cond = cond;
- is->ready = false;
- is->seekable = false;
- is->size = -1;
- is->offset = 0;
- is->mime = NULL;
-}
-
-void
-input_stream_deinit(struct input_stream *is)
-{
- assert(is != NULL);
- assert(is->plugin != NULL);
-
- g_free(is->uri);
- g_free(is->mime);
-}
-
-void
-input_stream_signal_client(struct input_stream *is)
-{
- if (is->cond != NULL)
- g_cond_broadcast(is->cond);
-}
-
-void
-input_stream_set_ready(struct input_stream *is)
-{
- g_mutex_lock(is->mutex);
-
- if (!is->ready) {
- is->ready = true;
- input_stream_signal_client(is);
- }
-
- g_mutex_unlock(is->mutex);
-}
diff --git a/src/input_stream.h b/src/input_stream.h
index 10ad9716..24dda1ee 100644
--- a/src/input_stream.h
+++ b/src/input_stream.h
@@ -29,64 +29,13 @@
#include <stdbool.h>
#include <sys/types.h>
-struct input_stream {
- /**
- * the plugin which implements this input stream
- */
- const struct input_plugin *plugin;
+struct input_stream;
- /**
- * The absolute URI which was used to open this stream. May
- * be NULL if this is unknown.
- */
- char *uri;
+#ifdef __cplusplus
+extern "C" {
- /**
- * A mutex that protects the mutable attributes of this object
- * and its implementation. It must be locked before calling
- * any of the public methods.
- *
- * This object is allocated by the client, and the client is
- * responsible for freeing it.
- */
- GMutex *mutex;
-
- /**
- * A cond that gets signalled when the state of this object
- * changes from the I/O thread. The client of this object may
- * wait on it. Optional, may be NULL.
- *
- * This object is allocated by the client, and the client is
- * responsible for freeing it.
- */
- GCond *cond;
-
- /**
- * indicates whether the stream is ready for reading and
- * whether the other attributes in this struct are valid
- */
- bool ready;
-
- /**
- * if true, then the stream is fully seekable
- */
- bool seekable;
-
- /**
- * the size of the resource, or -1 if unknown
- */
- goffset size;
-
- /**
- * the current offset within the stream
- */
- goffset offset;
-
- /**
- * the MIME content type of the resource, or NULL if unknown
- */
- char *mime;
-};
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
/**
* Opens a new input stream. You may not access it until the "ready"
@@ -99,13 +48,15 @@ struct input_stream {
* notifications
* @return an #input_stream object on success, NULL on error
*/
-gcc_nonnull(1, 2)
+gcc_nonnull(1)
G_GNUC_MALLOC
struct input_stream *
input_stream_open(const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
GError **error_r);
+#endif
+
/**
* Close the input stream and free resources.
*
@@ -115,20 +66,6 @@ gcc_nonnull(1)
void
input_stream_close(struct input_stream *is);
-gcc_nonnull(1)
-static inline void
-input_stream_lock(struct input_stream *is)
-{
- g_mutex_lock(is->mutex);
-}
-
-gcc_nonnull(1)
-static inline void
-input_stream_unlock(struct input_stream *is)
-{
- g_mutex_unlock(is->mutex);
-}
-
/**
* Check for errors that may have occurred in the I/O thread.
*
@@ -163,6 +100,33 @@ gcc_nonnull(1)
void
input_stream_lock_wait_ready(struct input_stream *is);
+gcc_nonnull_all gcc_pure
+const char *
+input_stream_get_mime_type(const struct input_stream *is);
+
+gcc_nonnull_all
+void
+input_stream_override_mime_type(struct input_stream *is, const char *mime);
+
+gcc_nonnull_all gcc_pure
+goffset
+input_stream_get_size(const struct input_stream *is);
+
+gcc_nonnull_all gcc_pure
+goffset
+input_stream_get_offset(const struct input_stream *is);
+
+gcc_nonnull_all gcc_pure
+bool
+input_stream_is_seekable(const struct input_stream *is);
+
+/**
+ * Determines whether seeking is cheap. This is true for local files.
+ */
+gcc_pure gcc_nonnull(1)
+bool
+input_stream_cheap_seeking(const struct input_stream *is);
+
/**
* Seeks to the specified position in the stream. This will most
* likely fail if the "seekable" flag is false.
@@ -264,4 +228,8 @@ size_t
input_stream_lock_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/io_error.h b/src/io_error.h
new file mode 100644
index 00000000..930ced10
--- /dev/null
+++ b/src/io_error.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_IO_ERROR_H
+#define MPD_IO_ERROR_H
+
+#include <glib.h>
+
+#include <errno.h>
+
+/**
+ * A GQuark for GError for I/O errors. The code is an errno value.
+ */
+G_GNUC_CONST
+static inline GQuark
+errno_quark(void)
+{
+ return g_quark_from_static_string("errno");
+}
+
+static inline void
+set_error_errno(GError **error_r)
+{
+ g_set_error_literal(error_r, errno_quark(), errno,
+ g_strerror(errno));
+}
+
+static inline GError *
+new_error_errno(void)
+{
+ return g_error_new_literal(errno_quark(), errno,
+ g_strerror(errno));
+}
+
+#endif
diff --git a/src/locate.c b/src/locate.c
deleted file mode 100644
index c9684d2b..00000000
--- a/src/locate.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "locate.h"
-#include "path.h"
-#include "tag.h"
-#include "song.h"
-
-#include <glib.h>
-
-#include <stdlib.h>
-
-#define LOCATE_TAG_FILE_KEY "file"
-#define LOCATE_TAG_FILE_KEY_OLD "filename"
-#define LOCATE_TAG_ANY_KEY "any"
-
-int
-locate_parse_type(const char *str)
-{
- if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY) ||
- 0 == g_ascii_strcasecmp(str, LOCATE_TAG_FILE_KEY_OLD))
- return LOCATE_TAG_FILE_TYPE;
-
- if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_ANY_KEY))
- return LOCATE_TAG_ANY_TYPE;
-
- enum tag_type i = tag_name_parse_i(str);
- if (i != TAG_NUM_OF_ITEM_TYPES)
- return i;
-
- return -1;
-}
-
-static bool
-locate_item_init(struct locate_item *item,
- const char *type_string, const char *needle)
-{
- item->tag = locate_parse_type(type_string);
-
- if (item->tag < 0)
- return false;
-
- item->needle = g_strdup(needle);
-
- return true;
-}
-
-void
-locate_item_list_free(struct locate_item_list *list)
-{
- for (unsigned i = 0; i < list->length; ++i)
- g_free(list->items[i].needle);
-
- g_free(list);
-}
-
-struct locate_item_list *
-locate_item_list_new(unsigned length)
-{
- struct locate_item_list *list =
- g_malloc0(sizeof(*list) - sizeof(list->items[0]) +
- length * sizeof(list->items[0]));
- list->length = length;
-
- return list;
-}
-
-struct locate_item_list *
-locate_item_list_parse(char *argv[], int argc)
-{
- if (argc % 2 != 0)
- return NULL;
-
- struct locate_item_list *list = locate_item_list_new(argc / 2);
-
- for (unsigned i = 0; i < list->length; ++i) {
- if (!locate_item_init(&list->items[i], argv[i * 2],
- argv[i * 2 + 1])) {
- locate_item_list_free(list);
- return NULL;
- }
- }
-
- return list;
-}
-
-struct locate_item_list *
-locate_item_list_casefold(const struct locate_item_list *list)
-{
- struct locate_item_list *new_list = locate_item_list_new(list->length);
-
- for (unsigned i = 0; i < list->length; i++){
- new_list->items[i].needle =
- g_utf8_casefold(list->items[i].needle, -1);
- new_list->items[i].tag = list->items[i].tag;
- }
-
- return new_list;
-}
-
-void
-locate_item_free(struct locate_item *item)
-{
- g_free(item->needle);
- g_free(item);
-}
-
-static bool
-locate_tag_search(const struct song *song, enum tag_type type, const char *str)
-{
- bool ret = false;
-
- if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
- char *uri = song_get_uri(song);
- char *p = g_utf8_casefold(uri, -1);
- g_free(uri);
-
- if (strstr(p, str))
- ret = true;
- g_free(p);
- if (ret == 1 || type == LOCATE_TAG_FILE_TYPE)
- return ret;
- }
-
- if (!song->tag)
- return false;
-
- bool visited_types[TAG_NUM_OF_ITEM_TYPES];
- memset(visited_types, 0, sizeof(visited_types));
-
- for (unsigned i = 0; i < song->tag->num_items && !ret; i++) {
- visited_types[song->tag->items[i]->type] = true;
- if ((int)type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i]->type != type) {
- continue;
- }
-
- char *duplicate = g_utf8_casefold(song->tag->items[i]->value, -1);
- if (*str && strstr(duplicate, str))
- ret = true;
- g_free(duplicate);
- }
-
- /** If the search critieron was not visited during the sweep
- * through the song's tag, it means this field is absent from
- * the tag or empty. Thus, if the searched string is also
- * empty (first char is a \0), then it's a match as well and
- * we should return true.
- */
- if (!*str && !visited_types[type])
- return true;
-
- return ret;
-}
-
-bool
-locate_song_search(const struct song *song,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < criteria->length; i++)
- if (!locate_tag_search(song, criteria->items[i].tag,
- criteria->items[i].needle))
- return false;
-
- return true;
-}
-
-static bool
-locate_tag_match(const struct song *song, enum tag_type type, const char *str)
-{
- if (type == LOCATE_TAG_FILE_TYPE || (int)type == LOCATE_TAG_ANY_TYPE) {
- char *uri = song_get_uri(song);
- bool matches = strcmp(str, uri) == 0;
- g_free(uri);
-
- if (matches)
- return true;
-
- if (type == LOCATE_TAG_FILE_TYPE)
- return false;
- }
-
- if (!song->tag)
- return false;
-
- bool visited_types[TAG_NUM_OF_ITEM_TYPES];
- memset(visited_types, 0, sizeof(visited_types));
-
- for (unsigned i = 0; i < song->tag->num_items; i++) {
- visited_types[song->tag->items[i]->type] = true;
- if ((int)type != LOCATE_TAG_ANY_TYPE &&
- song->tag->items[i]->type != type) {
- continue;
- }
-
- if (0 == strcmp(str, song->tag->items[i]->value))
- return true;
- }
-
- /** If the search critieron was not visited during the sweep
- * through the song's tag, it means this field is absent from
- * the tag or empty. Thus, if the searched string is also
- * empty (first char is a \0), then it's a match as well and
- * we should return true.
- */
- if (!*str && !visited_types[type])
- return true;
-
- return false;
-}
-
-bool
-locate_song_match(const struct song *song,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < criteria->length; i++)
- if (!locate_tag_match(song, criteria->items[i].tag,
- criteria->items[i].needle))
- return false;
-
- return true;
-}
diff --git a/src/locate.h b/src/locate.h
deleted file mode 100644
index ec20ded2..00000000
--- a/src/locate.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_LOCATE_H
-#define MPD_LOCATE_H
-
-#include "gcc.h"
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#define LOCATE_TAG_FILE_TYPE TAG_NUM_OF_ITEM_TYPES+10
-#define LOCATE_TAG_ANY_TYPE TAG_NUM_OF_ITEM_TYPES+20
-
-struct song;
-
-/* struct used for search, find, list queries */
-struct locate_item {
- int8_t tag;
- /* what we are looking for */
- char *needle;
-};
-
-/**
- * An array of struct locate_item objects.
- */
-struct locate_item_list {
- /** number of items */
- unsigned length;
-
- /** this is a variable length array */
- struct locate_item items[1];
-};
-
-int
-locate_parse_type(const char *str);
-
-/**
- * Allocates a new struct locate_item_list, and initializes all
- * members with zero bytes.
- */
-struct locate_item_list *
-locate_item_list_new(unsigned length);
-
-/* return number of items or -1 on error */
-gcc_nonnull(1)
-struct locate_item_list *
-locate_item_list_parse(char *argv[], int argc);
-
-/**
- * Duplicate the struct locate_item_list object and convert all
- * needles with g_utf8_casefold().
- */
-gcc_nonnull(1)
-struct locate_item_list *
-locate_item_list_casefold(const struct locate_item_list *list);
-
-gcc_nonnull(1)
-void
-locate_item_list_free(struct locate_item_list *list);
-
-gcc_nonnull(1)
-void
-locate_item_free(struct locate_item *item);
-
-gcc_nonnull(1,2)
-bool
-locate_song_search(const struct song *song,
- const struct locate_item_list *criteria);
-
-gcc_nonnull(1,2)
-bool
-locate_song_match(const struct song *song,
- const struct locate_item_list *criteria);
-
-#endif
diff --git a/src/ls.c b/src/ls.cxx
index 310c2d7b..9bb56c89 100644
--- a/src/ls.c
+++ b/src/ls.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,15 @@
*/
#include "config.h"
-#include "ls.h"
+#include "ls.hxx"
+
+extern "C" {
#include "uri.h"
-#include "client.h"
+}
+
+#include "Client.hxx"
+
+#include <glib.h>
#include <assert.h>
#include <string.h>
@@ -72,7 +78,7 @@ void print_supported_uri_schemes_to_fp(FILE *fp)
fprintf(fp,"\n");
}
-void print_supported_uri_schemes(struct client *client)
+void print_supported_uri_schemes(Client *client)
{
const char **prefixes = remoteUrlPrefixes;
diff --git a/src/ls.h b/src/ls.hxx
index 15cb0116..8ae5a58f 100644
--- a/src/ls.h
+++ b/src/ls.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,12 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_LS_H
-#define MPD_LS_H
+#ifndef MPD_LS_HXX
+#define MPD_LS_HXX
-#include <stdbool.h>
#include <stdio.h>
-struct client;
+class Client;
/**
* Checks whether the scheme of the specified URI is supported by MPD.
@@ -36,7 +35,7 @@ bool uri_supported_scheme(const char *url);
* Send a list of supported URI schemes to the client. This is the
* response to the "urlhandlers" command.
*/
-void print_supported_uri_schemes(struct client *client);
+void print_supported_uri_schemes(Client *client);
/**
* Send a list of supported URI schemes to a file pointer.
diff --git a/src/mapper.c b/src/mapper.c
deleted file mode 100644
index 7db74b1a..00000000
--- a/src/mapper.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Maps directory and song objects to file system paths.
- */
-
-#include "config.h"
-#include "mapper.h"
-#include "directory.h"
-#include "song.h"
-#include "path.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dirent.h>
-
-/**
- * The absolute path of the music directory encoded in UTF-8.
- */
-static char *music_dir_utf8;
-static size_t music_dir_utf8_length;
-
-/**
- * The absolute path of the music directory encoded in the filesystem
- * character set.
- */
-static char *music_dir_fs;
-static size_t music_dir_fs_length;
-
-/**
- * The absolute path of the playlist directory encoded in the
- * filesystem character set.
- */
-static char *playlist_dir_fs;
-
-/**
- * Duplicate a string, chop all trailing slashes.
- */
-static char *
-strdup_chop_slash(const char *path_fs)
-{
- size_t length = strlen(path_fs);
-
- while (length > 0 && path_fs[length - 1] == G_DIR_SEPARATOR)
- --length;
-
- return g_strndup(path_fs, length);
-}
-
-static void
-check_directory(const char *path)
-{
- struct stat st;
- if (stat(path, &st) < 0) {
- g_warning("Failed to stat directory \"%s\": %s",
- path, g_strerror(errno));
- return;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- g_warning("Not a directory: %s", path);
- return;
- }
-
-#ifndef WIN32
- char *x = g_build_filename(path, ".", NULL);
- if (stat(x, &st) < 0 && errno == EACCES)
- g_warning("No permission to traverse (\"execute\") directory: %s",
- path);
- g_free(x);
-#endif
-
- DIR *dir = opendir(path);
- if (dir != NULL)
- closedir(dir);
- else if (errno == EACCES)
- g_warning("No permission to read directory: %s", path);
-}
-
-static void
-mapper_set_music_dir(const char *path_utf8)
-{
- music_dir_utf8 = strdup_chop_slash(path_utf8);
- music_dir_utf8_length = strlen(music_dir_utf8);
-
- music_dir_fs = utf8_to_fs_charset(music_dir_utf8);
- check_directory(music_dir_fs);
- music_dir_fs_length = strlen(music_dir_fs);
-}
-
-static void
-mapper_set_playlist_dir(const char *path_utf8)
-{
- playlist_dir_fs = utf8_to_fs_charset(path_utf8);
- check_directory(playlist_dir_fs);
-}
-
-void mapper_init(const char *_music_dir, const char *_playlist_dir)
-{
- if (_music_dir != NULL)
- mapper_set_music_dir(_music_dir);
-
- if (_playlist_dir != NULL)
- mapper_set_playlist_dir(_playlist_dir);
-}
-
-void mapper_finish(void)
-{
- g_free(music_dir_utf8);
- g_free(music_dir_fs);
- g_free(playlist_dir_fs);
-}
-
-const char *
-mapper_get_music_directory_utf8(void)
-{
- return music_dir_utf8;
-}
-
-const char *
-mapper_get_music_directory_fs(void)
-{
- return music_dir_fs;
-}
-
-const char *
-map_to_relative_path(const char *path_utf8)
-{
- return music_dir_utf8 != NULL &&
- memcmp(path_utf8, music_dir_utf8,
- music_dir_utf8_length) == 0 &&
- G_IS_DIR_SEPARATOR(path_utf8[music_dir_utf8_length])
- ? path_utf8 + music_dir_utf8_length + 1
- : path_utf8;
-}
-
-char *
-map_uri_fs(const char *uri)
-{
- char *uri_fs, *path_fs;
-
- assert(uri != NULL);
- assert(*uri != '/');
-
- if (music_dir_fs == NULL)
- return NULL;
-
- uri_fs = utf8_to_fs_charset(uri);
- if (uri_fs == NULL)
- return NULL;
-
- path_fs = g_build_filename(music_dir_fs, uri_fs, NULL);
- g_free(uri_fs);
-
- return path_fs;
-}
-
-char *
-map_directory_fs(const struct directory *directory)
-{
- assert(music_dir_utf8 != NULL);
- assert(music_dir_fs != NULL);
-
- if (directory_is_root(directory))
- return g_strdup(music_dir_fs);
-
- return map_uri_fs(directory_get_path(directory));
-}
-
-char *
-map_directory_child_fs(const struct directory *directory, const char *name)
-{
- assert(music_dir_utf8 != NULL);
- assert(music_dir_fs != NULL);
-
- char *name_fs, *parent_fs, *path;
-
- /* check for invalid or unauthorized base names */
- if (*name == 0 || strchr(name, '/') != NULL ||
- strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
- return NULL;
-
- parent_fs = map_directory_fs(directory);
- if (parent_fs == NULL)
- return NULL;
-
- name_fs = utf8_to_fs_charset(name);
- if (name_fs == NULL) {
- g_free(parent_fs);
- return NULL;
- }
-
- path = g_build_filename(parent_fs, name_fs, NULL);
- g_free(parent_fs);
- g_free(name_fs);
-
- return path;
-}
-
-char *
-map_song_fs(const struct song *song)
-{
- assert(song_is_file(song));
-
- if (song_in_database(song))
- return map_directory_child_fs(song->parent, song->uri);
- else
- return utf8_to_fs_charset(song->uri);
-}
-
-char *
-map_fs_to_utf8(const char *path_fs)
-{
- if (music_dir_fs != NULL &&
- strncmp(path_fs, music_dir_fs, music_dir_fs_length) == 0 &&
- G_IS_DIR_SEPARATOR(path_fs[music_dir_fs_length]))
- /* remove musicDir prefix */
- path_fs += music_dir_fs_length + 1;
- else if (G_IS_DIR_SEPARATOR(path_fs[0]))
- /* not within musicDir */
- return NULL;
-
- while (path_fs[0] == G_DIR_SEPARATOR)
- ++path_fs;
-
- return fs_charset_to_utf8(path_fs);
-}
-
-const char *
-map_spl_path(void)
-{
- return playlist_dir_fs;
-}
-
-char *
-map_spl_utf8_to_fs(const char *name)
-{
- char *filename_utf8, *filename_fs, *path;
-
- if (playlist_dir_fs == NULL)
- return NULL;
-
- filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL);
- filename_fs = utf8_to_fs_charset(filename_utf8);
- g_free(filename_utf8);
- if (filename_fs == NULL)
- return NULL;
-
- path = g_build_filename(playlist_dir_fs, filename_fs, NULL);
- g_free(filename_fs);
-
- return path;
-}
diff --git a/src/mixer/alsa_mixer_plugin.c b/src/mixer/AlsaMixerPlugin.cxx
index 22e4e22b..d75016c0 100644
--- a/src/mixer/alsa_mixer_plugin.c
+++ b/src/mixer/AlsaMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,13 @@
*/
#include "config.h"
-#include "mixer_api.h"
+#include "MixerInternal.hxx"
#include "output_api.h"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
+#include "Main.hxx"
+#include "event/MultiSocketMonitor.hxx"
+
+#include <algorithm>
#include <glib.h>
#include <alsa/asoundlib.h>
@@ -29,13 +33,16 @@
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
#define VOLUME_MIXER_ALSA_INDEX_DEFAULT 0
-struct alsa_mixer_source {
- GSource source;
+class AlsaMixerMonitor final : private MultiSocketMonitor {
+ snd_mixer_t *const mixer;
- snd_mixer_t *mixer;
+public:
+ AlsaMixerMonitor(EventLoop &_loop, snd_mixer_t *_mixer)
+ :MultiSocketMonitor(_loop), mixer(_mixer) {}
- /** a linked list of all registered GPollFD objects */
- GSList *fds;
+private:
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) override;
+ virtual void DispatchSockets() override;
};
struct alsa_mixer {
@@ -52,7 +59,7 @@ struct alsa_mixer {
long volume_max;
int volume_set;
- struct alsa_mixer_source *source;
+ AlsaMixerMonitor *monitor;
};
/**
@@ -64,142 +71,45 @@ alsa_mixer_quark(void)
return g_quark_from_static_string("alsa_mixer");
}
-/*
- * GSource helper functions
- *
- */
-
-static GSList **
-find_fd(GSList **list_r, int fd)
-{
- while (true) {
- GSList *list = *list_r;
- if (list == NULL)
- return NULL;
-
- GPollFD *p = list->data;
- if (p->fd == fd)
- return list_r;
-
- list_r = &list->next;
- }
-}
-
-static void
-alsa_mixer_update_fd(struct alsa_mixer_source *source, const struct pollfd *p,
- GSList **old_r)
-{
- GSList **found_r = find_fd(old_r, p->fd);
- if (found_r == NULL) {
- /* new fd */
- GPollFD *q = g_new(GPollFD, 1);
- q->fd = p->fd;
- q->events = p->events;
- g_source_add_poll(&source->source, q);
- source->fds = g_slist_prepend(source->fds, q);
- return;
- }
-
- GSList *found = *found_r;
- *found_r = found->next;
-
- GPollFD *q = found->data;
- if (q->events != p->events) {
- /* refresh events */
- g_source_remove_poll(&source->source, q);
- q->events = p->events;
- g_source_add_poll(&source->source, q);
- }
-
- found->next = source->fds;
- source->fds = found;
-}
-
-static void
-alsa_mixer_update_fds(struct alsa_mixer_source *source)
+void
+AlsaMixerMonitor::PrepareSockets(gcc_unused gint *timeout_r)
{
- int count = snd_mixer_poll_descriptors_count(source->mixer);
+ int count = snd_mixer_poll_descriptors_count(mixer);
if (count < 0)
count = 0;
struct pollfd *pfds = g_new(struct pollfd, count);
- count = snd_mixer_poll_descriptors(source->mixer, pfds, count);
+ count = snd_mixer_poll_descriptors(mixer, pfds, count);
if (count < 0)
count = 0;
- GSList *old = source->fds;
- source->fds = NULL;
+ struct pollfd *end = pfds + count;
- for (int i = 0; i < count; ++i)
- alsa_mixer_update_fd(source, &pfds[i], &old);
- g_free(pfds);
+ UpdateSocketList([pfds, end](int fd) -> unsigned {
+ auto i = std::find_if(pfds, end, [fd](const struct pollfd &pfd){
+ return pfd.fd == fd;
+ });
+ if (i == end)
+ return 0;
- for (; old != NULL; old = old->next) {
- GPollFD *q = old->data;
- g_source_remove_poll(&source->source, q);
- g_free(q);
- }
+ auto events = i->events;
+ i->events = 0;
+ return events;
+ });
- g_slist_free(old);
-}
+ for (auto i = pfds; i != end; ++i)
+ if (i->events != 0)
+ AddSocket(i->fd, i->events);
-/*
- * GSource methods
- *
- */
-
-static gboolean
-alsa_mixer_source_prepare(GSource *_source, G_GNUC_UNUSED gint *timeout_r)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
- alsa_mixer_update_fds(source);
-
- return false;
-}
-
-static gboolean
-alsa_mixer_source_check(GSource *_source)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- for (const GSList *i = source->fds; i != NULL; i = i->next) {
- const GPollFD *poll_fd = i->data;
- if (poll_fd->revents != 0)
- return true;
- }
-
- return false;
+ g_free(pfds);
}
-static gboolean
-alsa_mixer_source_dispatch(GSource *_source,
- G_GNUC_UNUSED GSourceFunc callback,
- G_GNUC_UNUSED gpointer user_data)
+void
+AlsaMixerMonitor::DispatchSockets()
{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- snd_mixer_handle_events(source->mixer);
- return true;
+ snd_mixer_handle_events(mixer);
}
-static void
-alsa_mixer_source_finalize(GSource *_source)
-{
- struct alsa_mixer_source *source = (struct alsa_mixer_source *)_source;
-
- for (GSList *i = source->fds; i != NULL; i = i->next)
- g_free(i->data);
-
- g_slist_free(source->fds);
-}
-
-static GSourceFuncs alsa_mixer_source_funcs = {
- .prepare = alsa_mixer_source_prepare,
- .check = alsa_mixer_source_check,
- .dispatch = alsa_mixer_source_dispatch,
- .finalize = alsa_mixer_source_finalize,
-};
-
/*
* libasound callbacks
*
@@ -209,7 +119,7 @@ static int
alsa_mixer_elem_callback(G_GNUC_UNUSED snd_mixer_elem_t *elem, unsigned mask)
{
if (mask & SND_CTL_EVENT_MASK_VALUE)
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
return 0;
}
@@ -304,11 +214,7 @@ alsa_mixer_setup(struct alsa_mixer *am, GError **error_r)
snd_mixer_elem_set_callback(am->elem, alsa_mixer_elem_callback);
- am->source = (struct alsa_mixer_source *)
- g_source_new(&alsa_mixer_source_funcs, sizeof(*am->source));
- am->source->mixer = am->handle;
- am->source->fds = NULL;
- g_source_attach(&am->source->source, g_main_context_default());
+ am->monitor = new AlsaMixerMonitor(*main_loop, am->handle);
return true;
}
@@ -343,8 +249,7 @@ alsa_mixer_close(struct mixer *data)
assert(am->handle != NULL);
- g_source_destroy(&am->source->source);
- g_source_unref(&am->source->source);
+ delete am->monitor;
snd_mixer_elem_set_callback(am->elem, NULL);
snd_mixer_close(am->handle);
@@ -421,11 +326,11 @@ alsa_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin alsa_mixer_plugin = {
- .init = alsa_mixer_init,
- .finish = alsa_mixer_finish,
- .open = alsa_mixer_open,
- .close = alsa_mixer_close,
- .get_volume = alsa_mixer_get_volume,
- .set_volume = alsa_mixer_set_volume,
- .global = true,
+ alsa_mixer_init,
+ alsa_mixer_finish,
+ alsa_mixer_open,
+ alsa_mixer_close,
+ alsa_mixer_get_volume,
+ alsa_mixer_set_volume,
+ true,
};
diff --git a/src/mixer/oss_mixer_plugin.c b/src/mixer/OssMixerPlugin.cxx
index 608f1f9b..8d266b40 100644
--- a/src/mixer/oss_mixer_plugin.c
+++ b/src/mixer/OssMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "mixer_api.h"
+#include "MixerInternal.hxx"
#include "output_api.h"
#include "fd_util.h"
@@ -206,11 +206,11 @@ oss_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin oss_mixer_plugin = {
- .init = oss_mixer_init,
- .finish = oss_mixer_finish,
- .open = oss_mixer_open,
- .close = oss_mixer_close,
- .get_volume = oss_mixer_get_volume,
- .set_volume = oss_mixer_set_volume,
- .global = true,
+ oss_mixer_init,
+ oss_mixer_finish,
+ oss_mixer_open,
+ oss_mixer_close,
+ oss_mixer_get_volume,
+ oss_mixer_set_volume,
+ true,
};
diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/PulseMixerPlugin.cxx
index a82c032b..d7c6c804 100644
--- a/src/mixer/pulse_mixer_plugin.c
+++ b/src/mixer/PulseMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,11 @@
*/
#include "config.h"
-#include "pulse_mixer_plugin.h"
-#include "mixer_api.h"
+#include "PulseMixerPlugin.h"
+#include "MixerInternal.hxx"
#include "output/pulse_output_plugin.h"
#include "conf.h"
-#include "event_pipe.h"
+#include "GlobalEvents.hxx"
#include <glib.h>
@@ -66,7 +66,7 @@ pulse_mixer_offline(struct pulse_mixer *pm)
pm->online = false;
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
}
/**
@@ -77,7 +77,7 @@ static void
pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_info *i,
int eol, void *userdata)
{
- struct pulse_mixer *pm = userdata;
+ struct pulse_mixer *pm = (struct pulse_mixer *)userdata;
if (eol)
return;
@@ -90,7 +90,7 @@ pulse_mixer_volume_cb(G_GNUC_UNUSED pa_context *context, const pa_sink_input_inf
pm->online = true;
pm->volume = i->volume;
- event_pipe_emit(PIPE_EVENT_MIXER);
+ GlobalEvents::Emit(GlobalEvents::MIXER);
}
static void
@@ -153,16 +153,15 @@ static struct mixer *
pulse_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
GError **error_r)
{
- struct pulse_mixer *pm;
- struct pulse_output *po = ao;
+ struct pulse_output *po = (struct pulse_output *)ao;
if (ao == NULL) {
g_set_error(error_r, pulse_mixer_quark(), 0,
"The pulse mixer cannot work without the audio output");
- return false;
+ return nullptr;
}
- pm = g_new(struct pulse_mixer,1);
+ struct pulse_mixer *pm = g_new(struct pulse_mixer,1);
mixer_init(&pm->base, &pulse_mixer_plugin);
pm->online = false;
@@ -229,8 +228,11 @@ pulse_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin pulse_mixer_plugin = {
- .init = pulse_mixer_init,
- .finish = pulse_mixer_finish,
- .get_volume = pulse_mixer_get_volume,
- .set_volume = pulse_mixer_set_volume,
+ pulse_mixer_init,
+ pulse_mixer_finish,
+ nullptr,
+ nullptr,
+ pulse_mixer_get_volume,
+ pulse_mixer_set_volume,
+ false,
};
diff --git a/src/mixer/pulse_mixer_plugin.h b/src/mixer/PulseMixerPlugin.h
index 461633d3..f432c44a 100644
--- a/src/mixer/pulse_mixer_plugin.h
+++ b/src/mixer/PulseMixerPlugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,10 @@ struct pulse_mixer;
struct pa_context;
struct pa_stream;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_mixer_on_connect(struct pulse_mixer *pm, struct pa_context *context);
@@ -36,4 +40,8 @@ void
pulse_mixer_on_change(struct pulse_mixer *pm,
struct pa_context *context, struct pa_stream *stream);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/mixer/roar_mixer_plugin.c b/src/mixer/RoarMixerPlugin.cxx
index 47d3c17f..a027f857 100644
--- a/src/mixer/roar_mixer_plugin.c
+++ b/src/mixer/RoarMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
* Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
*
@@ -20,85 +20,57 @@
#include "config.h"
-#include "mixer_api.h"
+#include "MixerInternal.hxx"
#include "output_api.h"
-#include "output/roar_output_plugin.h"
+#include "output/RoarOutputPlugin.hxx"
-#include <glib.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-typedef struct roar_mpd_mixer
-{
+struct RoarMixer {
/** the base mixer class */
struct mixer base;
- struct roar *self;
-} roar_mixer_t;
+ RoarOutput *self;
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-roar_mixer_quark(void)
-{
- return g_quark_from_static_string("roar_mixer");
-}
+ RoarMixer(RoarOutput *_output):self(_output) {
+ mixer_init(&base, &roar_mixer_plugin);
+ }
+};
static struct mixer *
-roar_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
- G_GNUC_UNUSED GError **error_r)
+roar_mixer_init(void *ao, gcc_unused const struct config_param *param,
+ gcc_unused GError **error_r)
{
- roar_mixer_t *self = g_new(roar_mixer_t, 1);
- self->self = ao;
-
- mixer_init(&self->base, &roar_mixer_plugin);
-
+ RoarMixer *self = new RoarMixer((RoarOutput *)ao);
return &self->base;
}
static void
roar_mixer_finish(struct mixer *data)
{
- roar_mixer_t *self = (roar_mixer_t *) data;
+ RoarMixer *self = (RoarMixer *) data;
- g_free(self);
-}
-
-static void
-roar_mixer_close(G_GNUC_UNUSED struct mixer *data)
-{
-}
-
-static bool
-roar_mixer_open(G_GNUC_UNUSED struct mixer *data,
- G_GNUC_UNUSED GError **error_r)
-{
- return true;
+ delete self;
}
static int
-roar_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r)
+roar_mixer_get_volume(struct mixer *mixer, gcc_unused GError **error_r)
{
- roar_mixer_t *self = (roar_mixer_t *)mixer;
+ RoarMixer *self = (RoarMixer *)mixer;
return roar_output_get_volume(self->self);
}
static bool
roar_mixer_set_volume(struct mixer *mixer, unsigned volume,
- G_GNUC_UNUSED GError **error_r)
+ gcc_unused GError **error_r)
{
- roar_mixer_t *self = (roar_mixer_t *)mixer;
+ RoarMixer *self = (RoarMixer *)mixer;
return roar_output_set_volume(self->self, volume);
}
const struct mixer_plugin roar_mixer_plugin = {
- .init = roar_mixer_init,
- .finish = roar_mixer_finish,
- .open = roar_mixer_open,
- .close = roar_mixer_close,
- .get_volume = roar_mixer_get_volume,
- .set_volume = roar_mixer_set_volume,
- .global = false,
+ roar_mixer_init,
+ roar_mixer_finish,
+ nullptr,
+ nullptr,
+ roar_mixer_get_volume,
+ roar_mixer_set_volume,
+ false,
};
diff --git a/src/mixer/software_mixer_plugin.c b/src/mixer/SoftwareMixerPlugin.cxx
index 0206c3b9..6c287ea0 100644
--- a/src/mixer/software_mixer_plugin.c
+++ b/src/mixer/SoftwareMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,12 @@
*/
#include "config.h"
-#include "software_mixer_plugin.h"
-#include "mixer_api.h"
-#include "filter_plugin.h"
-#include "filter_registry.h"
-#include "filter/volume_filter_plugin.h"
-#include "pcm_volume.h"
+#include "SoftwareMixerPlugin.hxx"
+#include "MixerInternal.hxx"
+#include "FilterPlugin.hxx"
+#include "FilterRegistry.hxx"
+#include "filter/VolumeFilterPlugin.hxx"
+#include "PcmVolume.hxx"
#include <assert.h>
#include <math.h>
@@ -32,7 +32,7 @@ struct software_mixer {
/** the base mixer class */
struct mixer base;
- struct filter *filter;
+ Filter *filter;
unsigned volume;
};
@@ -91,14 +91,16 @@ software_mixer_set_volume(struct mixer *mixer, unsigned volume,
}
const struct mixer_plugin software_mixer_plugin = {
- .init = software_mixer_init,
- .finish = software_mixer_finish,
- .get_volume = software_mixer_get_volume,
- .set_volume = software_mixer_set_volume,
- .global = true,
+ software_mixer_init,
+ software_mixer_finish,
+ nullptr,
+ nullptr,
+ software_mixer_get_volume,
+ software_mixer_set_volume,
+ true,
};
-struct filter *
+Filter *
software_mixer_get_filter(struct mixer *mixer)
{
struct software_mixer *sm = (struct software_mixer *)mixer;
diff --git a/src/mixer/software_mixer_plugin.h b/src/mixer/SoftwareMixerPlugin.hxx
index ee2b2023..33e9e6c6 100644
--- a/src/mixer/software_mixer_plugin.h
+++ b/src/mixer/SoftwareMixerPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,17 +17,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef SOFTWARE_MIXER_PLUGIN_H
-#define SOFTWARE_MIXER_PLUGIN_H
+#ifndef MPD_SOFTWARE_MIXER_PLUGIN_HXX
+#define MPD_SOFTWARE_MIXER_PLUGIN_HXX
struct mixer;
-struct filter;
+class Filter;
/**
* Returns the (volume) filter associated with this mixer. All users
* of this mixer plugin should install this filter.
*/
-struct filter *
+Filter *
software_mixer_get_filter(struct mixer *mixer);
#endif
diff --git a/src/mixer/winmm_mixer_plugin.c b/src/mixer/WinmmMixerPlugin.cxx
index ceddf6af..bf315586 100644
--- a/src/mixer/winmm_mixer_plugin.c
+++ b/src/mixer/WinmmMixerPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,11 @@
*/
#include "config.h"
-#include "mixer_api.h"
+#include "MixerInternal.hxx"
#include "output_api.h"
-#include "output/winmm_output_plugin.h"
+#include "output/WinmmOutputPlugin.hxx"
+
+#include <mmsystem.h>
#include <assert.h>
#include <math.h>
@@ -31,7 +33,7 @@
struct winmm_mixer {
struct mixer base;
- struct winmm_output *output;
+ WinmmOutput *output;
};
static inline GQuark
@@ -57,11 +59,11 @@ static struct mixer *
winmm_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
{
- assert(ao != NULL);
+ assert(ao != nullptr);
struct winmm_mixer *wm = g_new(struct winmm_mixer, 1);
mixer_init(&wm->base, &winmm_mixer_plugin);
- wm->output = (struct winmm_output *) ao;
+ wm->output = (WinmmOutput *) ao;
return &wm->base;
}
@@ -107,8 +109,11 @@ winmm_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
}
const struct mixer_plugin winmm_mixer_plugin = {
- .init = winmm_mixer_init,
- .finish = winmm_mixer_finish,
- .get_volume = winmm_mixer_get_volume,
- .set_volume = winmm_mixer_set_volume,
+ winmm_mixer_init,
+ winmm_mixer_finish,
+ nullptr,
+ nullptr,
+ winmm_mixer_get_volume,
+ winmm_mixer_set_volume,
+ false,
};
diff --git a/src/mpd_error.h b/src/mpd_error.h
index 219738ce..e0b7d29a 100644
--- a/src/mpd_error.h
+++ b/src/mpd_error.h
@@ -20,6 +20,7 @@
#ifndef MPD_ERROR_H
#define MPD_ERROR_H
+#include <glib.h>
#include <stdlib.h>
/* This macro is used as an intermediate step to a proper error handling
diff --git a/src/notify.c b/src/notify.c
deleted file mode 100644
index 3c0112c9..00000000
--- a/src/notify.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "notify.h"
-
-void notify_init(struct notify *notify)
-{
- notify->mutex = g_mutex_new();
- notify->cond = g_cond_new();
- notify->pending = false;
-}
-
-void notify_deinit(struct notify *notify)
-{
- g_mutex_free(notify->mutex);
- g_cond_free(notify->cond);
-}
-
-void notify_wait(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- while (!notify->pending)
- g_cond_wait(notify->cond, notify->mutex);
- notify->pending = false;
- g_mutex_unlock(notify->mutex);
-}
-
-void notify_signal(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- notify->pending = true;
- g_cond_signal(notify->cond);
- g_mutex_unlock(notify->mutex);
-}
-
-void notify_clear(struct notify *notify)
-{
- g_mutex_lock(notify->mutex);
- notify->pending = false;
- g_mutex_unlock(notify->mutex);
-}
diff --git a/src/notify.cxx b/src/notify.cxx
new file mode 100644
index 00000000..64018968
--- /dev/null
+++ b/src/notify.cxx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "notify.hxx"
+
+void
+notify::Wait()
+{
+ const ScopeLock protect(mutex);
+ while (!pending)
+ cond.wait(mutex);
+ pending = false;
+}
+
+void
+notify::Signal()
+{
+ const ScopeLock protect(mutex);
+ pending = true;
+ cond.signal();
+}
+
+void
+notify::Clear()
+{
+ const ScopeLock protect(mutex);
+ pending = false;
+}
diff --git a/src/notify.hxx b/src/notify.hxx
new file mode 100644
index 00000000..6b9e9536
--- /dev/null
+++ b/src/notify.hxx
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_NOTIFY_HXX
+#define MPD_NOTIFY_HXX
+
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+struct notify {
+ Mutex mutex;
+ Cond cond;
+ bool pending;
+
+#ifndef WIN32
+ constexpr
+#endif
+ notify():pending(false) {}
+
+ /**
+ * Wait for a notification. Return immediately if we have already
+ * been notified since we last returned from notify_wait().
+ */
+ void Wait();
+
+ /**
+ * Notify the thread. This function never blocks.
+ */
+ void Signal();
+
+ /**
+ * Clears a pending notification.
+ */
+ void Clear();
+};
+
+#endif
diff --git a/src/output/alsa_output_plugin.c b/src/output/AlsaOutputPlugin.cxx
index d8b18427..c87c4cb9 100644
--- a/src/output/alsa_output_plugin.c
+++ b/src/output/AlsaOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,16 @@
*/
#include "config.h"
-#include "alsa_output_plugin.h"
+#include "AlsaOutputPlugin.hxx"
#include "output_api.h"
-#include "mixer_list.h"
+#include "MixerList.hxx"
#include "pcm_export.h"
#include <glib.h>
#include <alsa/asoundlib.h>
+#include <string>
+
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "alsa"
@@ -43,14 +45,16 @@ enum {
typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer,
snd_pcm_uframes_t size);
-struct alsa_data {
+struct AlsaOutput {
struct audio_output base;
- struct pcm_export_state export;
+ struct pcm_export_state pcm_export;
- /** the configured name of the ALSA device; NULL for the
- default device */
- char *device;
+ /**
+ * The configured name of the ALSA device; empty for the
+ * default device
+ */
+ std::string device;
/** use memory mapped I/O? */
bool use_mmap;
@@ -101,6 +105,25 @@ struct alsa_data {
* The number of frames written in the current period.
*/
snd_pcm_uframes_t period_position;
+
+ /**
+ * This buffer gets allocated after opening the ALSA device.
+ * It contains silence samples, enough to fill one period (see
+ * #period_frames).
+ */
+ void *silence;
+
+ AlsaOutput():mode(0), writei(snd_pcm_writei) {
+ }
+
+ bool Init(const config_param *param, GError **error_r) {
+ return ao_base_init(&base, &alsa_output_plugin,
+ param, error_r);
+ }
+
+ void Deinit() {
+ ao_base_finish(&base);
+ }
};
/**
@@ -113,26 +136,15 @@ alsa_output_quark(void)
}
static const char *
-alsa_device(const struct alsa_data *ad)
+alsa_device(const AlsaOutput *ad)
{
- return ad->device != NULL ? ad->device : default_device;
-}
-
-static struct alsa_data *
-alsa_data_new(void)
-{
- struct alsa_data *ret = g_new(struct alsa_data, 1);
-
- ret->mode = 0;
- ret->writei = snd_pcm_writei;
-
- return ret;
+ return ad->device.empty() ? default_device : ad->device.c_str();
}
static void
-alsa_configure(struct alsa_data *ad, const struct config_param *param)
+alsa_configure(AlsaOutput *ad, const struct config_param *param)
{
- ad->device = config_dup_block_string(param, "device", NULL);
+ ad->device = config_get_block_string(param, "device", "");
ad->use_mmap = config_get_block_bool(param, "use_mmap", false);
@@ -161,10 +173,10 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param)
static struct audio_output *
alsa_init(const struct config_param *param, GError **error_r)
{
- struct alsa_data *ad = alsa_data_new();
+ AlsaOutput *ad = new AlsaOutput();
- if (!ao_base_init(&ad->base, &alsa_output_plugin, param, error_r)) {
- g_free(ad);
+ if (!ad->Init(param, error_r)) {
+ delete ad;
return NULL;
}
@@ -176,12 +188,10 @@ alsa_init(const struct config_param *param, GError **error_r)
static void
alsa_finish(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- ao_base_finish(&ad->base);
-
- g_free(ad->device);
- g_free(ad);
+ ad->Deinit();
+ delete ad;
/* free libasound's config cache */
snd_config_update_free_global();
@@ -190,18 +200,18 @@ alsa_finish(struct audio_output *ao)
static bool
alsa_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- pcm_export_init(&ad->export);
+ pcm_export_init(&ad->pcm_export);
return true;
}
static void
alsa_output_disable(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- pcm_export_deinit(&ad->export);
+ pcm_export_deinit(&ad->pcm_export);
}
static bool
@@ -349,7 +359,8 @@ alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
{
/* try the input format first */
- int err = alsa_output_try_format(pcm, hwparams, audio_format->format,
+ int err = alsa_output_try_format(pcm, hwparams,
+ sample_format(audio_format->format),
packed_r, reverse_endian_r);
/* if unsupported by the hardware, try other formats */
@@ -383,15 +394,11 @@ alsa_output_setup_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwparams,
* the configured settings and the audio format.
*/
static bool
-alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup(AlsaOutput *ad, struct audio_format *audio_format,
bool *packed_r, bool *reverse_endian_r, GError **error)
{
- snd_pcm_hw_params_t *hwparams;
- snd_pcm_sw_params_t *swparams;
unsigned int sample_rate = audio_format->sample_rate;
unsigned int channels = audio_format->channels;
- snd_pcm_uframes_t alsa_buffer_size;
- snd_pcm_uframes_t alsa_period_size;
int err;
const char *cmd = NULL;
int retry = MPD_ALSA_RETRY_NR;
@@ -401,6 +408,7 @@ alsa_setup(struct alsa_data *ad, struct audio_format *audio_format,
period_time_ro = period_time = ad->period_time;
configure_hw:
/* configure HW params */
+ snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
cmd = "snd_pcm_hw_params_any";
err = snd_pcm_hw_params_any(ad->pcm, hwparams);
@@ -434,7 +442,7 @@ configure_hw:
g_set_error(error, alsa_output_quark(), err,
"ALSA device \"%s\" does not support format %s: %s",
alsa_device(ad),
- sample_format_to_string(audio_format->format),
+ sample_format_to_string(sample_format(audio_format->format)),
snd_strerror(-err));
return false;
}
@@ -525,11 +533,13 @@ configure_hw:
if (retry != MPD_ALSA_RETRY_NR)
g_debug("ALSA period_time set to %d\n", period_time);
+ snd_pcm_uframes_t alsa_buffer_size;
cmd = "snd_pcm_hw_params_get_buffer_size";
err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size);
if (err < 0)
goto error;
+ snd_pcm_uframes_t alsa_period_size;
cmd = "snd_pcm_hw_params_get_period_size";
err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
NULL);
@@ -537,6 +547,7 @@ configure_hw:
goto error;
/* configure SW params */
+ snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
cmd = "snd_pcm_sw_params_current";
@@ -576,6 +587,11 @@ configure_hw:
ad->period_frames = alsa_period_size;
ad->period_position = 0;
+ ad->silence = g_malloc(snd_pcm_frames_to_bytes(ad->pcm,
+ alsa_period_size));
+ snd_pcm_format_set_silence(format, ad->silence,
+ alsa_period_size * channels);
+
return true;
error:
@@ -586,7 +602,7 @@ error:
}
static bool
-alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup_dsd(AlsaOutput *ad, struct audio_format *audio_format,
bool *shift8_r, bool *packed_r, bool *reverse_endian_r,
GError **error_r)
{
@@ -619,6 +635,7 @@ alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
g_set_error(error_r, alsa_output_quark(), 0,
"Failed to configure DSD-over-USB on ALSA device \"%s\"",
alsa_device(ad));
+ g_free(ad->silence);
return false;
}
@@ -626,7 +643,7 @@ alsa_setup_dsd(struct alsa_data *ad, struct audio_format *audio_format,
}
static bool
-alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
+alsa_setup_or_dsd(AlsaOutput *ad, struct audio_format *audio_format,
GError **error_r)
{
bool shift8 = false, packed, reverse_endian;
@@ -642,8 +659,9 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
if (!success)
return false;
- pcm_export_open(&ad->export,
- audio_format->format, audio_format->channels,
+ pcm_export_open(&ad->pcm_export,
+ sample_format(audio_format->format),
+ audio_format->channels,
dsd_usb, shift8, packed, reverse_endian);
return true;
}
@@ -651,12 +669,10 @@ alsa_setup_or_dsd(struct alsa_data *ad, struct audio_format *audio_format,
static bool
alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
- int err;
- bool success;
+ AlsaOutput *ad = (AlsaOutput *)ao;
- err = snd_pcm_open(&ad->pcm, alsa_device(ad),
- SND_PCM_STREAM_PLAYBACK, ad->mode);
+ int err = snd_pcm_open(&ad->pcm, alsa_device(ad),
+ SND_PCM_STREAM_PLAYBACK, ad->mode);
if (err < 0) {
g_set_error(error, alsa_output_quark(), err,
"Failed to open ALSA device \"%s\": %s",
@@ -667,20 +683,29 @@ alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **e
g_debug("opened %s type=%s", snd_pcm_name(ad->pcm),
snd_pcm_type_name(snd_pcm_type(ad->pcm)));
- success = alsa_setup_or_dsd(ad, audio_format, error);
- if (!success) {
+ if (!alsa_setup_or_dsd(ad, audio_format, error)) {
snd_pcm_close(ad->pcm);
return false;
}
ad->in_frame_size = audio_format_frame_size(audio_format);
- ad->out_frame_size = pcm_export_frame_size(&ad->export, audio_format);
+ ad->out_frame_size = pcm_export_frame_size(&ad->pcm_export,
+ audio_format);
return true;
}
+/**
+ * Write silence to the ALSA device.
+ */
+static void
+alsa_write_silence(AlsaOutput *ad, snd_pcm_uframes_t nframes)
+{
+ ad->writei(ad->pcm, ad->silence, nframes);
+}
+
static int
-alsa_recover(struct alsa_data *ad, int err)
+alsa_recover(AlsaOutput *ad, int err)
{
if (err == -EPIPE) {
g_debug("Underrun on ALSA device \"%s\"\n", alsa_device(ad));
@@ -701,6 +726,26 @@ alsa_recover(struct alsa_data *ad, int err)
case SND_PCM_STATE_XRUN:
ad->period_position = 0;
err = snd_pcm_prepare(ad->pcm);
+
+ if (err == 0) {
+ /* this works around a driver bug observed on
+ the Raspberry Pi: after snd_pcm_drop(), the
+ whole ring buffer must be invalidated, but
+ the snd_pcm_prepare() call above makes the
+ driver play random data that just happens
+ to be still in the buffer; by adding and
+ cancelling some silence, this bug does not
+ occur */
+ alsa_write_silence(ad, ad->period_frames);
+
+ /* cancel the silence data right away to avoid
+ increasing latency; even though this
+ function call invalidates the portion of
+ silence, the driver seems to avoid the
+ bug */
+ snd_pcm_reset(ad->pcm);
+ }
+
break;
case SND_PCM_STATE_DISCONNECTED:
break;
@@ -719,7 +764,7 @@ alsa_recover(struct alsa_data *ad, int err)
static void
alsa_drain(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING)
return;
@@ -729,20 +774,7 @@ alsa_drain(struct audio_output *ao)
period */
snd_pcm_uframes_t nframes =
ad->period_frames - ad->period_position;
- size_t nbytes = nframes * ad->out_frame_size;
- void *buffer = g_malloc(nbytes);
- snd_pcm_hw_params_t *params;
- snd_pcm_format_t format;
- unsigned channels;
-
- snd_pcm_hw_params_alloca(&params);
- snd_pcm_hw_params_current(ad->pcm, params);
- snd_pcm_hw_params_get_format(params, &format);
- snd_pcm_hw_params_get_channels(params, &channels);
-
- snd_pcm_format_set_silence(format, buffer, nframes * channels);
- ad->writei(ad->pcm, buffer, nframes);
- g_free(buffer);
+ alsa_write_silence(ad, nframes);
}
snd_pcm_drain(ad->pcm);
@@ -753,7 +785,7 @@ alsa_drain(struct audio_output *ao)
static void
alsa_cancel(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
ad->period_position = 0;
@@ -763,20 +795,21 @@ alsa_cancel(struct audio_output *ao)
static void
alsa_close(struct audio_output *ao)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
snd_pcm_close(ad->pcm);
+ g_free(ad->silence);
}
static size_t
alsa_play(struct audio_output *ao, const void *chunk, size_t size,
GError **error)
{
- struct alsa_data *ad = (struct alsa_data *)ao;
+ AlsaOutput *ad = (AlsaOutput *)ao;
assert(size % ad->in_frame_size == 0);
- chunk = pcm_export(&ad->export, chunk, size, &size);
+ chunk = pcm_export(&ad->pcm_export, chunk, size, &size);
assert(size % ad->out_frame_size == 0);
@@ -789,7 +822,7 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
% ad->period_frames;
size_t bytes_written = ret * ad->out_frame_size;
- return pcm_export_source_size(&ad->export,
+ return pcm_export_source_size(&ad->pcm_export,
bytes_written);
}
@@ -803,17 +836,20 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
}
const struct audio_output_plugin alsa_output_plugin = {
- .name = "alsa",
- .test_default_device = alsa_test_default_device,
- .init = alsa_init,
- .finish = alsa_finish,
- .enable = alsa_output_enable,
- .disable = alsa_output_disable,
- .open = alsa_open,
- .play = alsa_play,
- .drain = alsa_drain,
- .cancel = alsa_cancel,
- .close = alsa_close,
-
- .mixer_plugin = &alsa_mixer_plugin,
+ "alsa",
+ alsa_test_default_device,
+ alsa_init,
+ alsa_finish,
+ alsa_output_enable,
+ alsa_output_disable,
+ alsa_open,
+ alsa_close,
+ nullptr,
+ nullptr,
+ alsa_play,
+ alsa_drain,
+ alsa_cancel,
+ nullptr,
+
+ &alsa_mixer_plugin,
};
diff --git a/src/output/alsa_output_plugin.h b/src/output/AlsaOutputPlugin.hxx
index daa1f361..dc7e639a 100644
--- a/src/output/alsa_output_plugin.h
+++ b/src/output/AlsaOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_ALSA_OUTPUT_PLUGIN_H
-#define MPD_ALSA_OUTPUT_PLUGIN_H
+#ifndef MPD_ALSA_OUTPUT_PLUGIN_HXX
+#define MPD_ALSA_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin alsa_output_plugin;
diff --git a/src/output/HttpdClient.cxx b/src/output/HttpdClient.cxx
new file mode 100644
index 00000000..0a00ee2f
--- /dev/null
+++ b/src/output/HttpdClient.cxx
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "HttpdClient.hxx"
+#include "HttpdInternal.hxx"
+#include "util/fifo_buffer.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "SocketError.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+HttpdClient::~HttpdClient()
+{
+ if (state == RESPONSE) {
+ if (current_page != nullptr)
+ current_page->Unref();
+
+ for (auto page : pages)
+ page->Unref();
+ }
+
+ if (metadata)
+ metadata->Unref();
+}
+
+void
+HttpdClient::Close()
+{
+ httpd->RemoveClient(*this);
+}
+
+void
+HttpdClient::LockClose()
+{
+ const ScopeLock protect(httpd->mutex);
+ Close();
+}
+
+void
+HttpdClient::BeginResponse()
+{
+ assert(state != RESPONSE);
+
+ state = RESPONSE;
+ current_page = nullptr;
+
+ httpd->SendHeader(*this);
+}
+
+/**
+ * Handle a line of the HTTP request.
+ */
+bool
+HttpdClient::HandleLine(const char *line)
+{
+ assert(state != RESPONSE);
+
+ if (state == REQUEST) {
+ if (strncmp(line, "GET /", 5) != 0) {
+ /* only GET is supported */
+ g_warning("malformed request line from client");
+ return false;
+ }
+
+ line = strchr(line + 5, ' ');
+ if (line == nullptr || strncmp(line + 1, "HTTP/", 5) != 0) {
+ /* HTTP/0.9 without request headers */
+ BeginResponse();
+ return true;
+ }
+
+ /* after the request line, request headers follow */
+ state = HEADERS;
+ return true;
+ } else {
+ if (*line == 0) {
+ /* empty line: request is finished */
+ BeginResponse();
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
+ /* Send icy metadata */
+ metadata_requested = metadata_supported;
+ return true;
+ }
+
+ if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
+ /* Send as dlna */
+ dlna_streaming_requested = true;
+ /* metadata is not supported by dlna streaming, so disable it */
+ metadata_supported = false;
+ metadata_requested = false;
+ return true;
+ }
+
+ /* expect more request headers */
+ return true;
+ }
+}
+
+/**
+ * Sends the status line and response headers to the client.
+ */
+bool
+HttpdClient::SendResponse()
+{
+ char buffer[1024];
+ assert(state == RESPONSE);
+
+ if (dlna_streaming_requested) {
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 206 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: 10000\r\n"
+ "Content-RangeX: 0-1000000/1000000\r\n"
+ "transferMode.dlna.org: Streaming\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "Connection: close\r\n"
+ "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
+ "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
+ "\r\n",
+ httpd->content_type);
+
+ } else if (metadata_requested) {
+ gchar *metadata_header;
+
+ metadata_header =
+ icy_server_metadata_header(httpd->name, httpd->genre,
+ httpd->website,
+ httpd->content_type,
+ metaint);
+
+ g_strlcpy(buffer, metadata_header, sizeof(buffer));
+
+ g_free(metadata_header);
+
+ } else { /* revert to a normal HTTP request */
+ g_snprintf(buffer, sizeof(buffer),
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: close\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "\r\n",
+ httpd->content_type);
+ }
+
+ ssize_t nbytes = SocketMonitor::Write(buffer, strlen(buffer));
+ if (gcc_unlikely(nbytes < 0)) {
+ const SocketErrorMessage msg;
+ g_warning("failed to write to client: %s", (const char *)msg);
+ Close();
+ return false;
+ }
+
+ return true;
+}
+
+HttpdClient::HttpdClient(HttpdOutput *_httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported)
+ :BufferedSocket(_fd, _loop),
+ httpd(_httpd),
+ state(REQUEST),
+ dlna_streaming_requested(false),
+ metadata_supported(_metadata_supported),
+ metadata_requested(false), metadata_sent(true),
+ metaint(8192), /*TODO: just a std value */
+ metadata(nullptr),
+ metadata_current_position(0), metadata_fill(0)
+{
+}
+
+size_t
+HttpdClient::GetQueueSize() const
+{
+ if (state != RESPONSE)
+ return 0;
+
+ size_t size = 0;
+ for (auto page : pages)
+ size += page->size;
+ return size;
+}
+
+void
+HttpdClient::CancelQueue()
+{
+ if (state != RESPONSE)
+ return;
+
+ for (auto page : pages)
+ page->Unref();
+ pages.clear();
+
+ if (current_page == nullptr)
+ CancelWrite();
+}
+
+ssize_t
+HttpdClient::TryWritePage(const Page &page, size_t position)
+{
+ assert(position < page.size);
+
+ return Write(page.data + position, page.size - position);
+}
+
+ssize_t
+HttpdClient::TryWritePageN(const Page &page, size_t position, ssize_t n)
+{
+ return n >= 0
+ ? Write(page.data + position, n)
+ : TryWritePage(page, position);
+}
+
+ssize_t
+HttpdClient::GetBytesTillMetaData() const
+{
+ if (metadata_requested &&
+ current_page->size - current_position > metaint - metadata_fill)
+ return metaint - metadata_fill;
+
+ return -1;
+}
+
+inline bool
+HttpdClient::TryWrite()
+{
+ const ScopeLock protect(httpd->mutex);
+
+ assert(state == RESPONSE);
+
+ if (current_page == nullptr) {
+ if (pages.empty()) {
+ /* another thread has removed the event source
+ while this thread was waiting for
+ httpd->mutex */
+ CancelWrite();
+ return true;
+ }
+
+ current_page = pages.front();
+ pages.pop_front();
+ current_position = 0;
+ }
+
+ const ssize_t bytes_to_write = GetBytesTillMetaData();
+ if (bytes_to_write == 0) {
+ if (!metadata_sent) {
+ ssize_t nbytes = TryWritePage(*metadata,
+ metadata_current_position);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_current_position += nbytes;
+
+ if (metadata->size - metadata_current_position == 0) {
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ metadata_sent = true;
+ }
+ } else {
+ guchar empty_data = 0;
+
+ ssize_t nbytes = Write(&empty_data, 1);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ metadata_fill = 0;
+ metadata_current_position = 0;
+ }
+ } else {
+ ssize_t nbytes =
+ TryWritePageN(*current_page, current_position,
+ bytes_to_write);
+ if (nbytes < 0) {
+ auto e = GetSocketError();
+ if (IsSocketErrorAgain(e))
+ return true;
+
+ if (!IsSocketErrorClosed(e)) {
+ SocketErrorMessage msg(e);
+ g_warning("failed to write to client: %s",
+ (const char *)msg);
+ }
+
+ Close();
+ return false;
+ }
+
+ current_position += nbytes;
+ assert(current_position <= current_page->size);
+
+ if (metadata_requested)
+ metadata_fill += nbytes;
+
+ if (current_position >= current_page->size) {
+ current_page->Unref();
+ current_page = nullptr;
+
+ if (pages.empty())
+ /* all pages are sent: remove the
+ event source */
+ CancelWrite();
+ }
+ }
+
+ return true;
+}
+
+void
+HttpdClient::PushPage(Page *page)
+{
+ if (state != RESPONSE)
+ /* the client is still writing the HTTP request */
+ return;
+
+ page->Ref();
+ pages.push_back(page);
+
+ ScheduleWrite();
+}
+
+void
+HttpdClient::PushMetaData(Page *page)
+{
+ if (metadata) {
+ metadata->Unref();
+ metadata = nullptr;
+ }
+
+ g_return_if_fail (page);
+
+ page->Ref();
+ metadata = page;
+ metadata_sent = false;
+}
+
+bool
+HttpdClient::OnSocketReady(unsigned flags)
+{
+ if (!BufferedSocket::OnSocketReady(flags))
+ return false;
+
+ if (flags & WRITE)
+ if (!TryWrite())
+ return false;
+
+ return true;
+}
+
+BufferedSocket::InputResult
+HttpdClient::OnSocketInput(const void *data, size_t length)
+{
+ if (state == RESPONSE) {
+ g_warning("unexpected input from client");
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ const char *line = (const char *)data;
+ const char *newline = (const char *)memchr(line, '\n', length);
+ if (newline == nullptr)
+ return InputResult::MORE;
+
+ ConsumeInput(newline + 1 - line);
+
+ if (newline > line && newline[-1] == '\r')
+ --newline;
+
+ /* terminate the string at the end of the line; the const_cast
+ is a dirty hack */
+ *const_cast<char *>(newline) = 0;
+
+ if (!HandleLine(line)) {
+ assert(state == RESPONSE);
+ LockClose();
+ return InputResult::CLOSED;
+ }
+
+ if (state == RESPONSE && !SendResponse())
+ return InputResult::CLOSED;
+
+ return InputResult::AGAIN;
+}
+
+void
+HttpdClient::OnSocketError(GError *error)
+{
+ g_warning("error on HTTP client: %s", error->message);
+ g_error_free(error);
+}
+
+void
+HttpdClient::OnSocketClosed()
+{
+ LockClose();
+}
diff --git a/src/output/HttpdClient.hxx b/src/output/HttpdClient.hxx
new file mode 100644
index 00000000..46196d2e
--- /dev/null
+++ b/src/output/HttpdClient.hxx
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_OUTPUT_HTTPD_CLIENT_HXX
+#define MPD_OUTPUT_HTTPD_CLIENT_HXX
+
+#include "event/BufferedSocket.hxx"
+#include "gcc.h"
+
+#include <list>
+
+#include <stddef.h>
+
+struct HttpdOutput;
+class Page;
+
+class HttpdClient final : public BufferedSocket {
+ /**
+ * The httpd output object this client is connected to.
+ */
+ HttpdOutput *const httpd;
+
+ /**
+ * The current state of the client.
+ */
+ enum {
+ /** reading the request line */
+ REQUEST,
+
+ /** reading the request headers */
+ HEADERS,
+
+ /** sending the HTTP response */
+ RESPONSE,
+ } state;
+
+ /**
+ * A queue of #Page objects to be sent to the client.
+ */
+ std::list<Page *> pages;
+
+ /**
+ * The #page which is currently being sent to the client.
+ */
+ Page *current_page;
+
+ /**
+ * The amount of bytes which were already sent from
+ * #current_page.
+ */
+ size_t current_position;
+
+ /**
+ * If DLNA streaming was an option.
+ */
+ bool dlna_streaming_requested;
+
+ /* ICY */
+
+ /**
+ * Do we support sending Icy-Metadata to the client? This is
+ * disabled if the httpd audio output uses encoder tags.
+ */
+ bool metadata_supported;
+
+ /**
+ * If we should sent icy metadata.
+ */
+ bool metadata_requested;
+
+ /**
+ * If the current metadata was already sent to the client.
+ */
+ bool metadata_sent;
+
+ /**
+ * The amount of streaming data between each metadata block
+ */
+ guint metaint;
+
+ /**
+ * The metadata as #Page which is currently being sent to the client.
+ */
+ Page *metadata;
+
+ /*
+ * The amount of bytes which were already sent from the metadata.
+ */
+ size_t metadata_current_position;
+
+ /**
+ * The amount of streaming data sent to the client
+ * since the last icy information was sent.
+ */
+ guint metadata_fill;
+
+public:
+ /**
+ * @param httpd the HTTP output device
+ * @param fd the socket file descriptor
+ */
+ HttpdClient(HttpdOutput *httpd, int _fd, EventLoop &_loop,
+ bool _metadata_supported);
+
+ /**
+ * Note: this does not remove the client from the
+ * #HttpdOutput object.
+ */
+ ~HttpdClient();
+
+ /**
+ * Frees the client and removes it from the server's client list.
+ */
+ void Close();
+
+ void LockClose();
+
+ /**
+ * Returns the total size of this client's page queue.
+ */
+ gcc_pure
+ size_t GetQueueSize() const;
+
+ /**
+ * Clears the page queue.
+ */
+ void CancelQueue();
+
+ /**
+ * Handle a line of the HTTP request.
+ */
+ bool HandleLine(const char *line);
+
+ /**
+ * Switch the client to the "RESPONSE" state.
+ */
+ void BeginResponse();
+
+ /**
+ * Sends the status line and response headers to the client.
+ */
+ bool SendResponse();
+
+ gcc_pure
+ ssize_t GetBytesTillMetaData() const;
+
+ ssize_t TryWritePage(const Page &page, size_t position);
+ ssize_t TryWritePageN(const Page &page, size_t position, ssize_t n);
+
+ bool TryWrite();
+
+ /**
+ * Appends a page to the client's queue.
+ */
+ void PushPage(Page *page);
+
+ /**
+ * Sends the passed metadata.
+ */
+ void PushMetaData(Page *page);
+
+protected:
+ virtual bool OnSocketReady(unsigned flags) override;
+ virtual InputResult OnSocketInput(const void *data,
+ size_t length) override;
+ virtual void OnSocketError(GError *error) override;
+ virtual void OnSocketClosed() override;
+};
+
+#endif
diff --git a/src/output/httpd_internal.h b/src/output/HttpdInternal.hxx
index 5dcb8ab9..4b526bcd 100644
--- a/src/output/httpd_internal.h
+++ b/src/output/HttpdInternal.hxx
@@ -27,14 +27,18 @@
#include "output_internal.h"
#include "timer.h"
+#include "thread/Mutex.hxx"
+#include "event/ServerSocket.hxx"
-#include <glib.h>
+#include <forward_list>
-#include <stdbool.h>
+struct config_param;
+class EventLoop;
+class ServerSocket;
+class HttpdClient;
+class Page;
-struct httpd_client;
-
-struct httpd_output {
+struct HttpdOutput final : private ServerSocket {
struct audio_output base;
/**
@@ -65,7 +69,7 @@ struct httpd_output {
* This mutex protects the listener socket and the client
* list.
*/
- GMutex *mutex;
+ mutable Mutex mutex;
/**
* A #timer object to synchronize this output with the
@@ -74,19 +78,14 @@ struct httpd_output {
struct timer *timer;
/**
- * The listener socket.
- */
- struct server_socket *server_socket;
-
- /**
* The header page, which is sent to every client on connect.
*/
- struct page *header;
+ Page *header;
/**
* The metadata, which is sent to every client.
*/
- struct page *metadata;
+ Page *metadata;
/**
* The configured name.
@@ -105,7 +104,7 @@ struct httpd_output {
* A linked list containing all clients which are currently
* connected.
*/
- GList *clients;
+ std::forward_list<HttpdClient> clients;
/**
* A temporary buffer for the httpd_output_read_page()
@@ -118,21 +117,88 @@ struct httpd_output {
* at the same time.
*/
guint clients_max, clients_cnt;
-};
-/**
- * Removes a client from the httpd_output.clients linked list.
- */
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client);
+ HttpdOutput(EventLoop &_loop);
+ ~HttpdOutput();
-/**
- * Sends the encoder header to the client. This is called right after
- * the response headers have been sent.
- */
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client);
+ bool Configure(const config_param *param, GError **error_r);
+
+ bool Bind(GError **error_r);
+ void Unbind();
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool OpenEncoder(struct audio_format *audio_format,
+ GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ bool Open(struct audio_format *audio_format, GError **error_r);
+
+ /**
+ * Caller must lock the mutex.
+ */
+ void Close();
+
+ /**
+ * Check whether there is at least one client.
+ *
+ * Caller must lock the mutex.
+ */
+ gcc_pure
+ bool HasClients() const {
+ return !clients.empty();
+ }
+
+ /**
+ * Check whether there is at least one client.
+ */
+ gcc_pure
+ bool LockHasClients() const {
+ const ScopeLock protect(mutex);
+ return HasClients();
+ }
+
+ void AddClient(int fd);
+
+ /**
+ * Removes a client from the httpd_output.clients linked list.
+ */
+ void RemoveClient(HttpdClient &client);
+
+ /**
+ * Sends the encoder header to the client. This is called
+ * right after the response headers have been sent.
+ */
+ void SendHeader(HttpdClient &client) const;
+
+ /**
+ * Reads data from the encoder (as much as available) and
+ * returns it as a new #page object.
+ */
+ Page *ReadPage();
+
+ /**
+ * Broadcasts a page struct to all clients.
+ *
+ * Mutext must not be locked.
+ */
+ void BroadcastPage(Page *page);
+
+ /**
+ * Broadcasts data from the encoder to all clients.
+ */
+ void BroadcastFromEncoder();
+
+ bool EncodeAndPlay(const void *chunk, size_t size, GError **error_r);
+
+ void SendTag(const struct tag *tag);
+
+private:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) override;
+};
#endif
diff --git a/src/output/HttpdOutputPlugin.cxx b/src/output/HttpdOutputPlugin.cxx
new file mode 100644
index 00000000..cb515e65
--- /dev/null
+++ b/src/output/HttpdOutputPlugin.cxx
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "HttpdOutputPlugin.hxx"
+#include "HttpdInternal.hxx"
+#include "HttpdClient.hxx"
+#include "output_api.h"
+#include "encoder_plugin.h"
+#include "encoder_list.h"
+#include "resolver.h"
+#include "Page.hxx"
+#include "IcyMetaDataServer.hxx"
+#include "fd_util.h"
+#include "Main.hxx"
+
+#include <assert.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBWRAP
+#include <sys/socket.h> /* needed for AF_UNIX */
+#include <tcpd.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "httpd_output"
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+httpd_output_quark(void)
+{
+ return g_quark_from_static_string("httpd_output");
+}
+
+inline
+HttpdOutput::HttpdOutput(EventLoop &_loop)
+ :ServerSocket(_loop),
+ encoder(nullptr), unflushed_input(0),
+ metadata(nullptr)
+{
+}
+
+HttpdOutput::~HttpdOutput()
+{
+ if (metadata != nullptr)
+ metadata->Unref();
+
+ if (encoder != nullptr)
+ encoder_finish(encoder);
+
+}
+
+inline bool
+HttpdOutput::Bind(GError **error_r)
+{
+ open = false;
+
+ const ScopeLock protect(mutex);
+ return ServerSocket::Open(error_r);
+}
+
+inline void
+HttpdOutput::Unbind()
+{
+ assert(!open);
+
+ const ScopeLock protect(mutex);
+ ServerSocket::Close();
+}
+
+inline bool
+HttpdOutput::Configure(const config_param *param, GError **error_r)
+{
+ /* read configuration */
+ name = config_get_block_string(param, "name", "Set name in config");
+ genre = config_get_block_string(param, "genre", "Set genre in config");
+ website = config_get_block_string(param, "website",
+ "Set website in config");
+
+ guint port = config_get_block_unsigned(param, "port", 8000);
+
+ const char *encoder_name =
+ config_get_block_string(param, "encoder", "vorbis");
+ const struct encoder_plugin *encoder_plugin =
+ encoder_plugin_get(encoder_name);
+ if (encoder_plugin == NULL) {
+ g_set_error(error_r, httpd_output_quark(), 0,
+ "No such encoder: %s", encoder_name);
+ return false;
+ }
+
+ clients_max = config_get_block_unsigned(param,"max_clients", 0);
+
+ /* set up bind_to_address */
+
+ const char *bind_to_address =
+ config_get_block_string(param, "bind_to_address", NULL);
+ bool success = bind_to_address != NULL &&
+ strcmp(bind_to_address, "any") != 0
+ ? AddHost(bind_to_address, port, error_r)
+ : AddPort(port, error_r);
+ if (!success)
+ return false;
+
+ /* initialize encoder */
+
+ encoder = encoder_init(encoder_plugin, param, error_r);
+ if (encoder == nullptr)
+ return false;
+
+ /* determine content type */
+ content_type = encoder_get_mime_type(encoder);
+ if (content_type == nullptr)
+ content_type = "application/octet-stream";
+
+ return true;
+}
+
+static struct audio_output *
+httpd_output_init(const struct config_param *param,
+ GError **error_r)
+{
+ HttpdOutput *httpd = new HttpdOutput(*main_loop);
+
+ if (!ao_base_init(&httpd->base, &httpd_output_plugin, param,
+ error_r)) {
+ delete httpd;
+ return nullptr;
+ }
+
+ if (!httpd->Configure(param, error_r)) {
+ ao_base_finish(&httpd->base);
+ delete httpd;
+ return nullptr;
+ }
+
+ return &httpd->base;
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
+
+static inline constexpr HttpdOutput *
+Cast(audio_output *ao)
+{
+ return (HttpdOutput *)((char *)ao - offsetof(HttpdOutput, base));
+}
+
+#if GCC_CHECK_VERSION(4,6) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+static void
+httpd_output_finish(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ ao_base_finish(&httpd->base);
+ delete httpd;
+}
+
+/**
+ * Creates a new #HttpdClient object and adds it into the
+ * HttpdOutput.clients linked list.
+ */
+inline void
+HttpdOutput::AddClient(int fd)
+{
+ clients.emplace_front(this, fd, GetEventLoop(),
+ encoder->plugin->tag == NULL);
+ ++clients_cnt;
+
+ /* pass metadata to client */
+ if (metadata != nullptr)
+ clients.front().PushMetaData(metadata);
+}
+
+void
+HttpdOutput::OnAccept(int fd, const sockaddr &address,
+ size_t address_length, gcc_unused int uid)
+{
+ /* the listener socket has become readable - a client has
+ connected */
+
+#ifdef HAVE_LIBWRAP
+ if (address.sa_family != AF_UNIX) {
+ char *hostaddr = sockaddr_to_string(&address, address_length, NULL);
+ const char *progname = g_get_prgname();
+
+ struct request_info req;
+ request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
+
+ fromhost(&req);
+
+ if (!hosts_access(&req)) {
+ /* tcp wrappers says no */
+ g_warning("libwrap refused connection (libwrap=%s) from %s",
+ progname, hostaddr);
+ g_free(hostaddr);
+ close_socket(fd);
+ return;
+ }
+
+ g_free(hostaddr);
+ }
+#else
+ (void)address;
+ (void)address_length;
+#endif /* HAVE_WRAP */
+
+ const ScopeLock protect(mutex);
+
+ if (fd >= 0) {
+ /* can we allow additional client */
+ if (open && (clients_max == 0 || clients_cnt < clients_max))
+ AddClient(fd);
+ else
+ close_socket(fd);
+ } else if (fd < 0 && errno != EINTR) {
+ g_warning("accept() failed: %s", g_strerror(errno));
+ }
+}
+
+Page *
+HttpdOutput::ReadPage()
+{
+ if (unflushed_input >= 65536) {
+ /* we have fed a lot of input into the encoder, but it
+ didn't give anything back yet - flush now to avoid
+ buffer underruns */
+ encoder_flush(encoder, NULL);
+ unflushed_input = 0;
+ }
+
+ size_t size = 0;
+ do {
+ size_t nbytes = encoder_read(encoder,
+ buffer + size,
+ sizeof(buffer) - size);
+ if (nbytes == 0)
+ break;
+
+ unflushed_input = 0;
+
+ size += nbytes;
+ } while (size < sizeof(buffer));
+
+ if (size == 0)
+ return NULL;
+
+ return Page::Copy(buffer, size);
+}
+
+static bool
+httpd_output_enable(struct audio_output *ao, GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ return httpd->Bind(error_r);
+}
+
+static void
+httpd_output_disable(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->Unbind();
+}
+
+inline bool
+HttpdOutput::OpenEncoder(struct audio_format *audio_format, GError **error)
+{
+ if (!encoder_open(encoder, audio_format, error))
+ return false;
+
+ /* we have to remember the encoder header, i.e. the first
+ bytes of encoder output after opening it, because it has to
+ be sent to every new client */
+ header = ReadPage();
+
+ unflushed_input = 0;
+
+ return true;
+}
+
+inline bool
+HttpdOutput::Open(struct audio_format *audio_format, GError **error_r)
+{
+ assert(!open);
+ assert(clients.empty());
+
+ /* open the encoder */
+
+ if (!OpenEncoder(audio_format, error_r))
+ return false;
+
+ /* initialize other attributes */
+
+ clients_cnt = 0;
+ timer = timer_new(audio_format);
+
+ open = true;
+
+ return true;
+}
+
+static bool
+httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
+ GError **error)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ assert(httpd->clients.empty());
+
+ const ScopeLock protect(httpd->mutex);
+ return httpd->Open(audio_format, error);
+}
+
+inline void
+HttpdOutput::Close()
+{
+ assert(open);
+
+ open = false;
+
+ timer_free(timer);
+
+ clients.clear();
+
+ if (header != NULL)
+ header->Unref();
+
+ encoder_close(encoder);
+}
+
+static void
+httpd_output_close(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ httpd->Close();
+}
+
+void
+HttpdOutput::RemoveClient(HttpdClient &client)
+{
+ assert(clients_cnt > 0);
+
+ for (auto prev = clients.before_begin(), i = std::next(prev);;
+ prev = i, i = std::next(prev)) {
+ assert(i != clients.end());
+ if (&*i == &client) {
+ clients.erase_after(prev);
+ clients_cnt--;
+ break;
+ }
+ }
+}
+
+void
+HttpdOutput::SendHeader(HttpdClient &client) const
+{
+ if (header != NULL)
+ client.PushPage(header);
+}
+
+static unsigned
+httpd_output_delay(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (!httpd->LockHasClients() && httpd->base.pause) {
+ /* if there's no client and this output is paused,
+ then httpd_output_pause() will not do anything, it
+ will not fill the buffer and it will not update the
+ timer; therefore, we reset the timer here */
+ timer_reset(httpd->timer);
+
+ /* some arbitrary delay that is long enough to avoid
+ consuming too much CPU, and short enough to notice
+ new clients quickly enough */
+ return 1000;
+ }
+
+ return httpd->timer->started
+ ? timer_delay(httpd->timer)
+ : 0;
+}
+
+void
+HttpdOutput::BroadcastPage(Page *page)
+{
+ assert(page != NULL);
+
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushPage(page);
+}
+
+void
+HttpdOutput::BroadcastFromEncoder()
+{
+ mutex.lock();
+ for (auto &client : clients) {
+ if (client.GetQueueSize() > 256 * 1024) {
+ g_debug("client is too slow, flushing its queue");
+ client.CancelQueue();
+ }
+ }
+ mutex.unlock();
+
+ Page *page;
+ while ((page = ReadPage()) != nullptr) {
+ BroadcastPage(page);
+ page->Unref();
+ }
+}
+
+inline bool
+HttpdOutput::EncodeAndPlay(const void *chunk, size_t size, GError **error_r)
+{
+ if (!encoder_write(encoder, chunk, size, error_r))
+ return false;
+
+ unflushed_input += size;
+
+ BroadcastFromEncoder();
+ return true;
+}
+
+static size_t
+httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
+ GError **error_r)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ if (!httpd->EncodeAndPlay(chunk, size, error_r))
+ return 0;
+ }
+
+ if (!httpd->timer->started)
+ timer_start(httpd->timer);
+ timer_add(httpd->timer, size);
+
+ return size;
+}
+
+static bool
+httpd_output_pause(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ if (httpd->LockHasClients()) {
+ static const char silence[1020] = { 0 };
+ return httpd_output_play(ao, silence, sizeof(silence),
+ NULL) > 0;
+ } else {
+ return true;
+ }
+}
+
+inline void
+HttpdOutput::SendTag(const struct tag *tag)
+{
+ assert(tag != NULL);
+
+ if (encoder->plugin->tag != NULL) {
+ /* embed encoder tags */
+
+ /* flush the current stream, and end it */
+
+ encoder_pre_tag(encoder, NULL);
+ BroadcastFromEncoder();
+
+ /* send the tag to the encoder - which starts a new
+ stream now */
+
+ encoder_tag(encoder, tag, NULL);
+
+ /* the first page generated by the encoder will now be
+ used as the new "header" page, which is sent to all
+ new clients */
+
+ Page *page = ReadPage();
+ if (page != NULL) {
+ if (header != NULL)
+ header->Unref();
+ header = page;
+ BroadcastPage(page);
+ }
+ } else {
+ /* use Icy-Metadata */
+
+ if (metadata != NULL)
+ metadata->Unref();
+
+ static constexpr tag_type types[] = {
+ TAG_ALBUM, TAG_ARTIST, TAG_TITLE,
+ TAG_NUM_OF_ITEM_TYPES
+ };
+
+ metadata = icy_server_metadata_page(tag, &types[0]);
+ if (metadata != NULL) {
+ const ScopeLock protect(mutex);
+ for (auto &client : clients)
+ client.PushMetaData(metadata);
+ }
+ }
+}
+
+static void
+httpd_output_tag(struct audio_output *ao, const struct tag *tag)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ httpd->SendTag(tag);
+}
+
+static void
+httpd_output_cancel(struct audio_output *ao)
+{
+ HttpdOutput *httpd = Cast(ao);
+
+ const ScopeLock protect(httpd->mutex);
+ for (auto &client : httpd->clients)
+ client.CancelQueue();
+}
+
+const struct audio_output_plugin httpd_output_plugin = {
+ "httpd",
+ nullptr,
+ httpd_output_init,
+ httpd_output_finish,
+ httpd_output_enable,
+ httpd_output_disable,
+ httpd_output_open,
+ httpd_output_close,
+ httpd_output_delay,
+ httpd_output_tag,
+ httpd_output_play,
+ nullptr,
+ httpd_output_cancel,
+ httpd_output_pause,
+ nullptr,
+};
diff --git a/src/output/httpd_output_plugin.h b/src/output/HttpdOutputPlugin.hxx
index d0eb1533..c74d2bd4 100644
--- a/src/output/httpd_output_plugin.h
+++ b/src/output/HttpdOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_HTTPD_OUTPUT_PLUGIN_H
-#define MPD_HTTPD_OUTPUT_PLUGIN_H
+#ifndef MPD_HTTPD_OUTPUT_PLUGIN_HXX
+#define MPD_HTTPD_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin httpd_output_plugin;
diff --git a/src/output/null_output_plugin.c b/src/output/NullOutputPlugin.cxx
index 9d7588ff..3596cbdc 100644
--- a/src/output/null_output_plugin.c
+++ b/src/output/NullOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "null_output_plugin.h"
+#include "NullOutputPlugin.hxx"
#include "output_api.h"
#include "timer.h"
@@ -26,7 +26,7 @@
#include <assert.h>
-struct null_data {
+struct NullOutput {
struct audio_output base;
bool sync;
@@ -37,7 +37,7 @@ struct null_data {
static struct audio_output *
null_init(const struct config_param *param, GError **error_r)
{
- struct null_data *nd = g_new(struct null_data, 1);
+ NullOutput *nd = g_new(NullOutput, 1);
if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) {
g_free(nd);
@@ -52,7 +52,7 @@ null_init(const struct config_param *param, GError **error_r)
static void
null_finish(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
ao_base_finish(&nd->base);
g_free(nd);
@@ -62,7 +62,7 @@ static bool
null_open(struct audio_output *ao, struct audio_format *audio_format,
G_GNUC_UNUSED GError **error)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (nd->sync)
nd->timer = timer_new(audio_format);
@@ -73,7 +73,7 @@ null_open(struct audio_output *ao, struct audio_format *audio_format,
static void
null_close(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (nd->sync)
timer_free(nd->timer);
@@ -82,7 +82,7 @@ null_close(struct audio_output *ao)
static unsigned
null_delay(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
return nd->sync && nd->timer->started
? timer_delay(nd->timer)
@@ -93,7 +93,7 @@ static size_t
null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
struct timer *timer = nd->timer;
if (!nd->sync)
@@ -109,7 +109,7 @@ null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size,
static void
null_cancel(struct audio_output *ao)
{
- struct null_data *nd = (struct null_data *)ao;
+ NullOutput *nd = (NullOutput *)ao;
if (!nd->sync)
return;
@@ -118,12 +118,19 @@ null_cancel(struct audio_output *ao)
}
const struct audio_output_plugin null_output_plugin = {
- .name = "null",
- .init = null_init,
- .finish = null_finish,
- .open = null_open,
- .close = null_close,
- .delay = null_delay,
- .play = null_play,
- .cancel = null_cancel,
+ "null",
+ nullptr,
+ null_init,
+ null_finish,
+ nullptr,
+ nullptr,
+ null_open,
+ null_close,
+ null_delay,
+ nullptr,
+ null_play,
+ nullptr,
+ null_cancel,
+ nullptr,
+ nullptr,
};
diff --git a/src/output/null_output_plugin.h b/src/output/NullOutputPlugin.hxx
index 392bf0aa..a58f1cb1 100644
--- a/src/output/null_output_plugin.h
+++ b/src/output/NullOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_NULL_OUTPUT_PLUGIN_H
-#define MPD_NULL_OUTPUT_PLUGIN_H
+#ifndef MPD_NULL_OUTPUT_PLUGIN_HXX
+#define MPD_NULL_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin null_output_plugin;
diff --git a/src/output/osx_output_plugin.c b/src/output/OSXOutputPlugin.cxx
index cd51fca2..5a04fe1d 100644
--- a/src/output/osx_output_plugin.c
+++ b/src/output/OSXOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,11 @@
*/
#include "config.h"
-#include "osx_output_plugin.h"
+#include "OSXOutputPlugin.hxx"
#include "output_api.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
#include <glib.h>
#include <CoreAudio/AudioHardware.h>
@@ -30,7 +32,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "osx"
-struct osx_output {
+struct OSXOutput {
struct audio_output base;
/* configuration settings */
@@ -39,8 +41,8 @@ struct osx_output {
const char *device_name;
AudioUnit au;
- GMutex *mutex;
- GCond *condition;
+ Mutex mutex;
+ Cond condition;
struct fifo_buffer *buffer;
};
@@ -63,7 +65,7 @@ osx_output_test_default_device(void)
}
static void
-osx_output_configure(struct osx_output *oo, const struct config_param *param)
+osx_output_configure(OSXOutput *oo, const struct config_param *param)
{
const char *device = config_get_block_string(param, "device", NULL);
@@ -85,15 +87,13 @@ osx_output_configure(struct osx_output *oo, const struct config_param *param)
static struct audio_output *
osx_output_init(const struct config_param *param, GError **error_r)
{
- struct osx_output *oo = g_new(struct osx_output, 1);
+ OSXOutput *oo = new OSXOutput();
if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) {
- g_free(oo);
+ delete oo;
return NULL;
}
osx_output_configure(oo, param);
- oo->mutex = g_mutex_new();
- oo->condition = g_cond_new();
return &oo->base;
}
@@ -101,15 +101,13 @@ osx_output_init(const struct config_param *param, GError **error_r)
static void
osx_output_finish(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
- g_mutex_free(od->mutex);
- g_cond_free(od->condition);
- g_free(od);
+ delete oo;
}
static bool
-osx_output_set_device(struct osx_output *oo, GError **error)
+osx_output_set_device(OSXOutput *oo, GError **error)
{
bool ret = true;
OSStatus status;
@@ -135,7 +133,7 @@ osx_output_set_device(struct osx_output *oo, GError **error)
/* what are the available audio device IDs? */
numdevices = size / sizeof(AudioDeviceID);
- deviceids = g_malloc(size);
+ deviceids = new AudioDeviceID[numdevices];
status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
&size,
deviceids);
@@ -192,8 +190,7 @@ osx_output_set_device(struct osx_output *oo, GError **error)
(unsigned int) deviceids[i], name);
done:
- if (deviceids != NULL)
- g_free(deviceids);
+ delete[] deviceids;
return ret;
}
@@ -205,13 +202,13 @@ osx_render(void *vdata,
G_GNUC_UNUSED UInt32 in_number_frames,
AudioBufferList *buffer_list)
{
- struct osx_output *od = (struct osx_output *) vdata;
+ OSXOutput *od = (OSXOutput *) vdata;
AudioBuffer *buffer = &buffer_list->mBuffers[0];
size_t buffer_size = buffer->mDataByteSize;
assert(od->buffer != NULL);
- g_mutex_lock(od->mutex);
+ od->mutex.lock();
size_t nbytes;
const void *src = fifo_buffer_read(od->buffer, &nbytes);
@@ -225,8 +222,8 @@ osx_render(void *vdata,
} else
nbytes = 0;
- g_cond_signal(od->condition);
- g_mutex_unlock(od->mutex);
+ od->condition.signal();
+ od->mutex.unlock();
buffer->mDataByteSize = nbytes;
@@ -242,7 +239,7 @@ osx_render(void *vdata,
static bool
osx_output_enable(struct audio_output *ao, GError **error_r)
{
- struct osx_output *oo = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
@@ -293,7 +290,7 @@ osx_output_enable(struct audio_output *ao, GError **error_r)
static void
osx_output_disable(struct audio_output *ao)
{
- struct osx_output *oo = (struct osx_output *)ao;
+ OSXOutput *oo = (OSXOutput *)ao;
CloseComponent(oo->au);
}
@@ -301,17 +298,16 @@ osx_output_disable(struct audio_output *ao)
static void
osx_output_cancel(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
- g_mutex_lock(od->mutex);
+ const ScopeLock protect(od->mutex);
fifo_buffer_clear(od->buffer);
- g_mutex_unlock(od->mutex);
}
static void
osx_output_close(struct audio_output *ao)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
AudioOutputUnitStop(od->au);
AudioUnitUninitialize(od->au);
@@ -322,7 +318,7 @@ osx_output_close(struct audio_output *ao)
static bool
osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
AudioStreamBasicDescription stream_description;
stream_description.mSampleRate = audio_format->sample_rate;
@@ -397,9 +393,9 @@ static size_t
osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
- struct osx_output *od = (struct osx_output *)ao;
+ OSXOutput *od = (OSXOutput *)ao;
- g_mutex_lock(od->mutex);
+ const ScopeLock protect(od->mutex);
void *dest;
size_t max_length;
@@ -410,7 +406,7 @@ osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
break;
/* wait for some free space in the buffer */
- g_cond_wait(od->condition, od->mutex);
+ od->condition.wait(od->mutex);
}
if (size > max_length)
@@ -419,20 +415,23 @@ osx_output_play(struct audio_output *ao, const void *chunk, size_t size,
memcpy(dest, chunk, size);
fifo_buffer_append(od->buffer, size);
- g_mutex_unlock(od->mutex);
-
return size;
}
const struct audio_output_plugin osx_output_plugin = {
- .name = "osx",
- .test_default_device = osx_output_test_default_device,
- .init = osx_output_init,
- .finish = osx_output_finish,
- .enable = osx_output_enable,
- .disable = osx_output_disable,
- .open = osx_output_open,
- .close = osx_output_close,
- .play = osx_output_play,
- .cancel = osx_output_cancel,
+ "osx",
+ osx_output_test_default_device,
+ osx_output_init,
+ osx_output_finish,
+ osx_output_enable,
+ osx_output_disable,
+ osx_output_open,
+ osx_output_close,
+ nullptr,
+ nullptr,
+ osx_output_play,
+ nullptr,
+ osx_output_cancel,
+ nullptr,
+ nullptr,
};
diff --git a/src/output/osx_output_plugin.h b/src/output/OSXOutputPlugin.hxx
index 814702d4..2a417288 100644
--- a/src/output/osx_output_plugin.h
+++ b/src/output/OSXOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OSX_OUTPUT_PLUGIN_H
-#define MPD_OSX_OUTPUT_PLUGIN_H
+#ifndef MPD_OSX_OUTPUT_PLUGIN_HXX
+#define MPD_OSX_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin osx_output_plugin;
diff --git a/src/output/oss_output_plugin.c b/src/output/OssOutputPlugin.cxx
index e366a453..0111b13f 100644
--- a/src/output/oss_output_plugin.c
+++ b/src/output/OssOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,10 @@
*/
#include "config.h"
-#include "oss_output_plugin.h"
+#include "OssOutputPlugin.hxx"
#include "output_api.h"
-#include "mixer_list.h"
+#include "MixerList.hxx"
#include "fd_util.h"
-#include "glib_compat.h"
#include <glib.h>
@@ -60,7 +59,7 @@ struct oss_data {
struct audio_output base;
#ifdef AFMT_S24_PACKED
- struct pcm_export_state export;
+ struct pcm_export_state pcm_export;
#endif
int fd;
@@ -163,11 +162,10 @@ oss_output_test_default_device(void)
static struct audio_output *
oss_open_default(GError **error)
{
- int i;
int err[G_N_ELEMENTS(default_devices)];
enum oss_stat ret[G_N_ELEMENTS(default_devices)];
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
ret[i] = oss_stat_device(default_devices[i], &err[i]);
if (ret[i] == OSS_STAT_NO_ERROR) {
struct oss_data *od = oss_data_new();
@@ -182,7 +180,7 @@ oss_open_default(GError **error)
}
}
- for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
+ for (int i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
const char *dev = default_devices[i];
switch(ret[i]) {
case OSS_STAT_NO_ERROR:
@@ -243,7 +241,7 @@ oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r)
{
struct oss_data *od = (struct oss_data *)ao;
- pcm_export_init(&od->export);
+ pcm_export_init(&od->pcm_export);
return true;
}
@@ -252,7 +250,7 @@ oss_output_disable(struct audio_output *ao)
{
struct oss_data *od = (struct oss_data *)ao;
- pcm_export_deinit(&od->export);
+ pcm_export_deinit(&od->pcm_export);
}
#endif
@@ -504,7 +502,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
enum sample_format *sample_format_r,
int *oss_format_r,
#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
+ struct pcm_export_state *pcm_export,
#endif
GError **error_r)
{
@@ -539,7 +537,7 @@ oss_probe_sample_format(int fd, enum sample_format sample_format,
*oss_format_r = oss_format;
#ifdef AFMT_S24_PACKED
- pcm_export_open(export, sample_format, 0, false, false,
+ pcm_export_open(pcm_export, sample_format, 0, false, false,
oss_format == AFMT_S24_PACKED,
oss_format == AFMT_S24_PACKED &&
G_BYTE_ORDER != G_LITTLE_ENDIAN);
@@ -556,16 +554,16 @@ static bool
oss_setup_sample_format(int fd, struct audio_format *audio_format,
int *oss_format_r,
#ifdef AFMT_S24_PACKED
- struct pcm_export_state *export,
+ struct pcm_export_state *pcm_export,
#endif
GError **error_r)
{
enum sample_format mpd_format;
enum oss_setup_result result =
- oss_probe_sample_format(fd, audio_format->format,
+ oss_probe_sample_format(fd, sample_format(audio_format->format),
&mpd_format, oss_format_r,
#ifdef AFMT_S24_PACKED
- export,
+ pcm_export,
#endif
error_r);
switch (result) {
@@ -603,7 +601,7 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
result = oss_probe_sample_format(fd, mpd_format,
&mpd_format, oss_format_r,
#ifdef AFMT_S24_PACKED
- export,
+ pcm_export,
#endif
error_r);
switch (result) {
@@ -635,7 +633,7 @@ oss_setup(struct oss_data *od, struct audio_format *audio_format,
oss_setup_sample_rate(od->fd, audio_format, error_r) &&
oss_setup_sample_format(od->fd, audio_format, &od->oss_format,
#ifdef AFMT_S24_PACKED
- &od->export,
+ &od->pcm_export,
#endif
error_r);
}
@@ -749,14 +747,14 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
return 0;
#ifdef AFMT_S24_PACKED
- chunk = pcm_export(&od->export, chunk, size, &size);
+ chunk = pcm_export(&od->pcm_export, chunk, size, &size);
#endif
while (true) {
ret = write(od->fd, chunk, size);
if (ret > 0) {
#ifdef AFMT_S24_PACKED
- ret = pcm_export_source_size(&od->export, ret);
+ ret = pcm_export_source_size(&od->pcm_export, ret);
#endif
return ret;
}
@@ -771,18 +769,25 @@ oss_output_play(struct audio_output *ao, const void *chunk, size_t size,
}
const struct audio_output_plugin oss_output_plugin = {
- .name = "oss",
- .test_default_device = oss_output_test_default_device,
- .init = oss_output_init,
- .finish = oss_output_finish,
+ "oss",
+ oss_output_test_default_device,
+ oss_output_init,
+ oss_output_finish,
#ifdef AFMT_S24_PACKED
- .enable = oss_output_enable,
- .disable = oss_output_disable,
+ oss_output_enable,
+ oss_output_disable,
+#else
+ nullptr,
+ nullptr,
#endif
- .open = oss_output_open,
- .close = oss_output_close,
- .play = oss_output_play,
- .cancel = oss_output_cancel,
-
- .mixer_plugin = &oss_mixer_plugin,
+ oss_output_open,
+ oss_output_close,
+ nullptr,
+ nullptr,
+ oss_output_play,
+ nullptr,
+ oss_output_cancel,
+ nullptr,
+
+ &oss_mixer_plugin,
};
diff --git a/src/output/oss_output_plugin.h b/src/output/OssOutputPlugin.hxx
index 2aecc2b3..6c5c9530 100644
--- a/src/output/oss_output_plugin.h
+++ b/src/output/OssOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_OSS_OUTPUT_PLUGIN_H
-#define MPD_OSS_OUTPUT_PLUGIN_H
+#ifndef MPD_OSS_OUTPUT_PLUGIN_HXX
+#define MPD_OSS_OUTPUT_PLUGIN_HXX
extern const struct audio_output_plugin oss_output_plugin;
diff --git a/src/output/roar_output_plugin.c b/src/output/RoarOutputPlugin.cxx
index 1c2c4832..9d6c4591 100644
--- a/src/output/roar_output_plugin.c
+++ b/src/output/RoarOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
* Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
*
@@ -19,25 +19,19 @@
*/
#include "config.h"
-#include "roar_output_plugin.h"
+#include "RoarOutputPlugin.hxx"
#include "output_api.h"
-#include "mixer_list.h"
-#include "roar_output_plugin.h"
+#include "MixerList.hxx"
+#include "thread/Mutex.hxx"
#include <glib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
#include <roaraudio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "roaraudio"
-typedef struct roar
-{
+struct RoarOutput {
struct audio_output base;
roar_vs_t * vss;
@@ -47,9 +41,18 @@ typedef struct roar
int role;
struct roar_connection con;
struct roar_audio_info info;
- GMutex *lock;
+ Mutex mutex;
volatile bool alive;
-} roar_t;
+
+ RoarOutput()
+ :err(ROAR_ERROR_NONE),
+ host(nullptr), name(nullptr) {}
+
+ ~RoarOutput() {
+ g_free(host);
+ g_free(name);
+ }
+};
static inline GQuark
roar_output_quark(void)
@@ -58,9 +61,9 @@ roar_output_quark(void)
}
static int
-roar_output_get_volume_locked(struct roar *roar)
+roar_output_get_volume_locked(RoarOutput *roar)
{
- if (roar->vss == NULL || !roar->alive)
+ if (roar->vss == nullptr || !roar->alive)
return -1;
float l, r;
@@ -72,20 +75,18 @@ roar_output_get_volume_locked(struct roar *roar)
}
int
-roar_output_get_volume(struct roar *roar)
+roar_output_get_volume(RoarOutput *roar)
{
- g_mutex_lock(roar->lock);
- int volume = roar_output_get_volume_locked(roar);
- g_mutex_unlock(roar->lock);
- return volume;
+ const ScopeLock protect(roar->mutex);
+ return roar_output_get_volume_locked(roar);
}
static bool
-roar_output_set_volume_locked(struct roar *roar, unsigned volume)
+roar_output_set_volume_locked(RoarOutput *roar, unsigned volume)
{
assert(volume <= 100);
- if (roar->vss == NULL || !roar->alive)
+ if (roar->vss == nullptr || !roar->alive)
return false;
int error;
@@ -96,22 +97,20 @@ roar_output_set_volume_locked(struct roar *roar, unsigned volume)
}
bool
-roar_output_set_volume(struct roar *roar, unsigned volume)
+roar_output_set_volume(RoarOutput *roar, unsigned volume)
{
- g_mutex_lock(roar->lock);
- bool success = roar_output_set_volume_locked(roar, volume);
- g_mutex_unlock(roar->lock);
- return success;
+ const ScopeLock protect(roar->mutex);
+ return roar_output_set_volume_locked(roar, volume);
}
static void
-roar_configure(struct roar * self, const struct config_param *param)
+roar_configure(RoarOutput *self, const struct config_param *param)
{
- self->host = config_dup_block_string(param, "server", NULL);
+ self->host = config_dup_block_string(param, "server", nullptr);
self->name = config_dup_block_string(param, "name", "MPD");
const char *role = config_get_block_string(param, "role", "music");
- self->role = role != NULL
+ self->role = role != nullptr
? roar_str2role(role)
: ROAR_ROLE_MUSIC;
}
@@ -119,15 +118,13 @@ roar_configure(struct roar * self, const struct config_param *param)
static struct audio_output *
roar_init(const struct config_param *param, GError **error_r)
{
- struct roar *self = g_new0(struct roar, 1);
+ RoarOutput *self = new RoarOutput();
if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) {
- g_free(self);
- return NULL;
+ delete self;
+ return nullptr;
}
- self->lock = g_mutex_new();
- self->err = ROAR_ERROR_NONE;
roar_configure(self, param);
return &self->base;
}
@@ -135,14 +132,10 @@ roar_init(const struct config_param *param, GError **error_r)
static void
roar_finish(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
-
- g_free(self->host);
- g_free(self->name);
- g_mutex_free(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
ao_base_finish(&self->base);
- g_free(self);
+ delete self;
}
static void
@@ -181,24 +174,22 @@ roar_use_audio_format(struct roar_audio_info *info,
static bool
roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error)
{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
{
g_set_error(error, roar_output_quark(), 0,
"Failed to connect to Roar server");
- g_mutex_unlock(self->lock);
return false;
}
self->vss = roar_vs_new_from_con(&(self->con), &(self->err));
- if (self->vss == NULL || self->err != ROAR_ERROR_NONE)
+ if (self->vss == nullptr || self->err != ROAR_ERROR_NONE)
{
g_set_error(error, roar_output_quark(), 0,
"Failed to connect to server");
- g_mutex_unlock(self->lock);
return false;
}
@@ -208,43 +199,41 @@ roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **e
&(self->err)) < 0)
{
g_set_error(error, roar_output_quark(), 0, "Failed to start stream");
- g_mutex_unlock(self->lock);
return false;
}
roar_vs_role(self->vss, self->role, &(self->err));
self->alive = true;
- g_mutex_unlock(self->lock);
return true;
}
static void
roar_close(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
- g_mutex_lock(self->lock);
+ RoarOutput *self = (RoarOutput *)ao;
+ const ScopeLock protect(self->mutex);
+
self->alive = false;
- if (self->vss != NULL)
+ if (self->vss != nullptr)
roar_vs_close(self->vss, ROAR_VS_TRUE, &(self->err));
- self->vss = NULL;
+ self->vss = nullptr;
roar_disconnect(&(self->con));
- g_mutex_unlock(self->lock);
}
static void
-roar_cancel_locked(struct roar *self)
+roar_cancel_locked(RoarOutput *self)
{
- if (self->vss == NULL)
+ if (self->vss == nullptr)
return;
roar_vs_t *vss = self->vss;
- self->vss = NULL;
+ self->vss = nullptr;
roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
self->alive = false;
vss = roar_vs_new_from_con(&(self->con), &(self->err));
- if (vss == NULL)
+ if (vss == nullptr)
return;
if (roar_vs_stream(vss, &(self->info), ROAR_DIR_PLAY,
@@ -262,20 +251,19 @@ roar_cancel_locked(struct roar *self)
static void
roar_cancel(struct audio_output *ao)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
- g_mutex_lock(self->lock);
+ const ScopeLock protect(self->mutex);
roar_cancel_locked(self);
- g_mutex_unlock(self->lock);
}
static size_t
roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
ssize_t rc;
- if (self->vss == NULL)
+ if (self->vss == nullptr)
{
g_set_error(error, roar_output_quark(), 0, "Connection is invalid");
return 0;
@@ -332,19 +320,20 @@ roar_tag_convert(enum tag_type type, bool *is_uuid)
return "HASH";
default:
- return NULL;
+ return nullptr;
}
}
static void
roar_send_tag(struct audio_output *ao, const struct tag *meta)
{
- struct roar *self = (struct roar *)ao;
+ RoarOutput *self = (RoarOutput *)ao;
- if (self->vss == NULL)
+ if (self->vss == nullptr)
return;
- g_mutex_lock(self->lock);
+ const ScopeLock protect(self->mutex);
+
size_t cnt = 1;
struct roar_keyval vals[32];
memset(vals, 0, sizeof(vals));
@@ -361,7 +350,7 @@ roar_send_tag(struct audio_output *ao, const struct tag *meta)
{
bool is_uuid = false;
const char *key = roar_tag_convert(meta->items[i]->type, &is_uuid);
- if (key != NULL)
+ if (key != nullptr)
{
if (is_uuid)
{
@@ -383,19 +372,22 @@ roar_send_tag(struct audio_output *ao, const struct tag *meta)
for (unsigned i = 0; i < 32; i++)
g_free(vals[i].key);
-
- g_mutex_unlock(self->lock);
}
const struct audio_output_plugin roar_output_plugin = {
- .name = "roar",
- .init = roar_init,
- .finish = roar_finish,
- .open = roar_open,
- .play = roar_play,
- .cancel = roar_cancel,
- .close = roar_close,
- .send_tag = roar_send_tag,
-
- .mixer_plugin = &roar_mixer_plugin
+ "roar",
+ nullptr,
+ roar_init,
+ roar_finish,
+ nullptr,
+ nullptr,
+ roar_open,
+ roar_close,
+ nullptr,
+ roar_send_tag,
+ roar_play,
+ nullptr,
+ roar_cancel,
+ nullptr,
+ &roar_mixer_plugin,
};
diff --git a/src/output/roar_output_plugin.h b/src/output/RoarOutputPlugin.hxx
index 78b628cc..faa4b4d5 100644
--- a/src/output/roar_output_plugin.h
+++ b/src/output/RoarOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,16 +20,14 @@
#ifndef MPD_ROAR_OUTPUT_PLUGIN_H
#define MPD_ROAR_OUTPUT_PLUGIN_H
-#include <stdbool.h>
-
-struct roar;
+struct RoarOutput;
extern const struct audio_output_plugin roar_output_plugin;
int
-roar_output_get_volume(struct roar *roar);
+roar_output_get_volume(RoarOutput *roar);
bool
-roar_output_set_volume(struct roar *roar, unsigned volume);
+roar_output_set_volume(RoarOutput *roar, unsigned volume);
#endif
diff --git a/src/output/winmm_output_plugin.c b/src/output/WinmmOutputPlugin.cxx
index 4d95834b..b9652fc0 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/WinmmOutputPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,26 +18,24 @@
*/
#include "config.h"
-#include "winmm_output_plugin.h"
+#include "WinmmOutputPlugin.hxx"
#include "output_api.h"
#include "pcm_buffer.h"
-#include "mixer_list.h"
-#include "winmm_output_plugin.h"
+#include "MixerList.hxx"
#include <stdlib.h>
#include <string.h>
-#include <windows.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "winmm_output"
-struct winmm_buffer {
+struct WinmmBuffer {
struct pcm_buffer buffer;
WAVEHDR hdr;
};
-struct winmm_output {
+struct WinmmOutput {
struct audio_output base;
UINT device_id;
@@ -49,7 +47,7 @@ struct winmm_output {
*/
HANDLE event;
- struct winmm_buffer buffers[8];
+ WinmmBuffer buffers[8];
unsigned next_buffer;
};
@@ -63,7 +61,7 @@ winmm_output_quark(void)
}
HWAVEOUT
-winmm_output_get_handle(struct winmm_output* output)
+winmm_output_get_handle(WinmmOutput *output)
{
return output->handle;
}
@@ -78,7 +76,7 @@ static bool
get_device_id(const char *device_name, UINT *device_id, GError **error_r)
{
/* if device is not specified use wave mapper */
- if (device_name == NULL) {
+ if (device_name == nullptr) {
*device_id = WAVE_MAPPER;
return true;
}
@@ -118,17 +116,17 @@ fail:
static struct audio_output *
winmm_output_init(const struct config_param *param, GError **error_r)
{
- struct winmm_output *wo = g_new(struct winmm_output, 1);
+ WinmmOutput *wo = new WinmmOutput();
if (!ao_base_init(&wo->base, &winmm_output_plugin, param, error_r)) {
g_free(wo);
- return NULL;
+ return nullptr;
}
- const char *device = config_get_block_string(param, "device", NULL);
+ const char *device = config_get_block_string(param, "device", nullptr);
if (!get_device_id(device, &wo->device_id, error_r)) {
ao_base_finish(&wo->base);
g_free(wo);
- return NULL;
+ return nullptr;
}
return &wo->base;
@@ -137,20 +135,20 @@ winmm_output_init(const struct config_param *param, GError **error_r)
static void
winmm_output_finish(struct audio_output *ao)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
ao_base_finish(&wo->base);
- g_free(wo);
+ delete wo;
}
static bool
winmm_output_open(struct audio_output *ao, struct audio_format *audio_format,
GError **error_r)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
- wo->event = CreateEvent(NULL, false, false, NULL);
- if (wo->event == NULL) {
+ wo->event = CreateEvent(nullptr, false, false, nullptr);
+ if (wo->event == nullptr) {
g_set_error(error_r, winmm_output_quark(), 0,
"CreateEvent() failed");
return false;
@@ -204,7 +202,7 @@ winmm_output_open(struct audio_output *ao, struct audio_format *audio_format,
static void
winmm_output_close(struct audio_output *ao)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i)
pcm_buffer_deinit(&wo->buffers[i].buffer);
@@ -218,17 +216,17 @@ winmm_output_close(struct audio_output *ao)
* Copy data into a buffer, and prepare the wave header.
*/
static bool
-winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
+winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
const void *data, size_t size,
GError **error_r)
{
void *dest = pcm_buffer_get(&buffer->buffer, size);
- assert(dest != NULL);
+ assert(dest != nullptr);
memcpy(dest, data, size);
memset(&buffer->hdr, 0, sizeof(buffer->hdr));
- buffer->hdr.lpData = dest;
+ buffer->hdr.lpData = (LPSTR)dest;
buffer->hdr.dwBufferLength = size;
MMRESULT result = waveOutPrepareHeader(wo->handle, &buffer->hdr,
@@ -246,7 +244,7 @@ winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
* Wait until the buffer is finished.
*/
static bool
-winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
+winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer,
GError **error_r)
{
if ((buffer->hdr.dwFlags & WHDR_DONE) == WHDR_DONE)
@@ -273,10 +271,10 @@ winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
static size_t
winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
/* get the next buffer from the ring and prepare it */
- struct winmm_buffer *buffer = &wo->buffers[wo->next_buffer];
+ WinmmBuffer *buffer = &wo->buffers[wo->next_buffer];
if (!winmm_drain_buffer(wo, buffer, error_r) ||
!winmm_set_buffer(wo, buffer, chunk, size, error_r))
return 0;
@@ -300,7 +298,7 @@ winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GErro
}
static bool
-winmm_drain_all_buffers(struct winmm_output *wo, GError **error_r)
+winmm_drain_all_buffers(WinmmOutput *wo, GError **error_r)
{
for (unsigned i = wo->next_buffer; i < G_N_ELEMENTS(wo->buffers); ++i)
if (!winmm_drain_buffer(wo, &wo->buffers[i], error_r))
@@ -314,12 +312,12 @@ winmm_drain_all_buffers(struct winmm_output *wo, GError **error_r)
}
static void
-winmm_stop(struct winmm_output *wo)
+winmm_stop(WinmmOutput *wo)
{
waveOutReset(wo->handle);
for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) {
- struct winmm_buffer *buffer = &wo->buffers[i];
+ WinmmBuffer *buffer = &wo->buffers[i];
waveOutUnprepareHeader(wo->handle, &buffer->hdr,
sizeof(buffer->hdr));
}
@@ -328,29 +326,34 @@ winmm_stop(struct winmm_output *wo)
static void
winmm_output_drain(struct audio_output *ao)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
- if (!winmm_drain_all_buffers(wo, NULL))
+ if (!winmm_drain_all_buffers(wo, nullptr))
winmm_stop(wo);
}
static void
winmm_output_cancel(struct audio_output *ao)
{
- struct winmm_output *wo = (struct winmm_output *)ao;
+ WinmmOutput *wo = (WinmmOutput *)ao;
winmm_stop(wo);
}
const struct audio_output_plugin winmm_output_plugin = {
- .name = "winmm",
- .test_default_device = winmm_output_test_default_device,
- .init = winmm_output_init,
- .finish = winmm_output_finish,
- .open = winmm_output_open,
- .close = winmm_output_close,
- .play = winmm_output_play,
- .drain = winmm_output_drain,
- .cancel = winmm_output_cancel,
- .mixer_plugin = &winmm_mixer_plugin,
+ "winmm",
+ winmm_output_test_default_device,
+ winmm_output_init,
+ winmm_output_finish,
+ nullptr,
+ nullptr,
+ winmm_output_open,
+ winmm_output_close,
+ nullptr,
+ nullptr,
+ winmm_output_play,
+ winmm_output_drain,
+ winmm_output_cancel,
+ nullptr,
+ &winmm_mixer_plugin,
};
diff --git a/src/output/winmm_output_plugin.h b/src/output/WinmmOutputPlugin.hxx
index 0605530e..e8688782 100644
--- a/src/output/winmm_output_plugin.h
+++ b/src/output/WinmmOutputPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,25 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_WINMM_OUTPUT_PLUGIN_H
-#define MPD_WINMM_OUTPUT_PLUGIN_H
+#ifndef MPD_WINMM_OUTPUT_PLUGIN_HXX
+#define MPD_WINMM_OUTPUT_PLUGIN_HXX
#include "check.h"
#ifdef ENABLE_WINMM_OUTPUT
+#include "gcc.h"
+
#include <windows.h>
+#include <mmsystem.h>
-struct winmm_output;
+struct WinmmOutput;
extern const struct audio_output_plugin winmm_output_plugin;
-HWAVEOUT winmm_output_get_handle(struct winmm_output*);
+gcc_pure
+HWAVEOUT
+winmm_output_get_handle(WinmmOutput *);
#endif
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index 022be0b4..3d6171ae 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -20,7 +20,6 @@
#include "config.h"
#include "fifo_output_plugin.h"
#include "output_api.h"
-#include "utils.h"
#include "timer.h"
#include "fd_util.h"
#include "open.h"
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
deleted file mode 100644
index 72de9045..00000000
--- a/src/output/httpd_client.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "httpd_client.h"
-#include "httpd_internal.h"
-#include "fifo_buffer.h"
-#include "page.h"
-#include "icy_server.h"
-#include "glib_socket.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <string.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-struct httpd_client {
- /**
- * The httpd output object this client is connected to.
- */
- struct httpd_output *httpd;
-
- /**
- * The TCP socket.
- */
- GIOChannel *channel;
-
- /**
- * The GLib main loop source id for reading from the socket,
- * and to detect errors.
- */
- guint read_source_id;
-
- /**
- * The GLib main loop source id for writing to the socket. If
- * 0, then there is no event source currently (because there
- * are no queued pages).
- */
- guint write_source_id;
-
- /**
- * For buffered reading. This pointer is only valid while the
- * HTTP request is read.
- */
- struct fifo_buffer *input;
-
- /**
- * The current state of the client.
- */
- enum {
- /** reading the request line */
- REQUEST,
-
- /** reading the request headers */
- HEADERS,
-
- /** sending the HTTP response */
- RESPONSE,
- } state;
-
- /**
- * A queue of #page objects to be sent to the client.
- */
- GQueue *pages;
-
- /**
- * The #page which is currently being sent to the client.
- */
- struct page *current_page;
-
- /**
- * The amount of bytes which were already sent from
- * #current_page.
- */
- size_t current_position;
-
- /**
- * If DLNA streaming was an option.
- */
- bool dlna_streaming_requested;
-
- /* ICY */
-
- /**
- * Do we support sending Icy-Metadata to the client? This is
- * disabled if the httpd audio output uses encoder tags.
- */
- bool metadata_supported;
-
- /**
- * If we should sent icy metadata.
- */
- bool metadata_requested;
-
- /**
- * If the current metadata was already sent to the client.
- */
- bool metadata_sent;
-
- /**
- * The amount of streaming data between each metadata block
- */
- guint metaint;
-
- /**
- * The metadata as #page which is currently being sent to the client.
- */
- struct page *metadata;
-
- /*
- * The amount of bytes which were already sent from the metadata.
- */
- size_t metadata_current_position;
-
- /**
- * The amount of streaming data sent to the client
- * since the last icy information was sent.
- */
- guint metadata_fill;
-};
-
-static void
-httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct page *page = data;
-
- page_unref(page);
-}
-
-void
-httpd_client_free(struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->state == RESPONSE) {
- if (client->write_source_id != 0)
- g_source_remove(client->write_source_id);
-
- if (client->current_page != NULL)
- page_unref(client->current_page);
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_free(client->pages);
- } else
- fifo_buffer_free(client->input);
-
- if (client->metadata)
- page_unref (client->metadata);
-
- g_source_remove(client->read_source_id);
- g_io_channel_unref(client->channel);
- g_free(client);
-}
-
-/**
- * Frees the client and removes it from the server's client list.
- */
-static void
-httpd_client_close(struct httpd_client *client)
-{
- assert(client != NULL);
-
- httpd_output_remove_client(client->httpd, client);
- httpd_client_free(client);
-}
-
-/**
- * Switch the client to the "RESPONSE" state.
- */
-static void
-httpd_client_begin_response(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- client->state = RESPONSE;
- client->write_source_id = 0;
- client->pages = g_queue_new();
- client->current_page = NULL;
-
- httpd_output_send_header(client->httpd, client);
-}
-
-/**
- * Handle a line of the HTTP request.
- */
-static bool
-httpd_client_handle_line(struct httpd_client *client, const char *line)
-{
- assert(client->state != RESPONSE);
-
- if (client->state == REQUEST) {
- if (strncmp(line, "GET /", 5) != 0) {
- /* only GET is supported */
- g_warning("malformed request line from client");
- return false;
- }
-
- line = strchr(line + 5, ' ');
- if (line == NULL || strncmp(line + 1, "HTTP/", 5) != 0) {
- /* HTTP/0.9 without request headers */
- httpd_client_begin_response(client);
- return true;
- }
-
- /* after the request line, request headers follow */
- client->state = HEADERS;
- return true;
- } else {
- if (*line == 0) {
- /* empty line: request is finished */
- httpd_client_begin_response(client);
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "Icy-MetaData: 1", 15) == 0) {
- /* Send icy metadata */
- client->metadata_requested =
- client->metadata_supported;
- return true;
- }
-
- if (g_ascii_strncasecmp(line, "transferMode.dlna.org: Streaming", 32) == 0) {
- /* Send as dlna */
- client->dlna_streaming_requested = true;
- /* metadata is not supported by dlna streaming, so disable it */
- client->metadata_supported = false;
- client->metadata_requested = false;
- return true;
- }
-
- /* expect more request headers */
- return true;
- }
-}
-
-/**
- * Check if a complete line of input is present in the input buffer,
- * and duplicates it. It is removed from the input buffer. The
- * return value has to be freed with g_free().
- */
-static char *
-httpd_client_read_line(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- const char *p, *newline;
- size_t length;
- char *line;
-
- p = fifo_buffer_read(client->input, &length);
- if (p == NULL)
- /* empty input buffer */
- return NULL;
-
- newline = memchr(p, '\n', length);
- if (newline == NULL)
- /* incomplete line */
- return NULL;
-
- line = g_strndup(p, newline - p);
- fifo_buffer_consume(client->input, newline - p + 1);
-
- /* remove trailing whitespace (e.g. '\r') */
- return g_strchomp(line);
-}
-
-/**
- * Sends the status line and response headers to the client.
- */
-static bool
-httpd_client_send_response(struct httpd_client *client)
-{
- char buffer[1024];
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
-
- assert(client != NULL);
- assert(client->state == RESPONSE);
-
- if (client->dlna_streaming_requested) {
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 206 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: 10000\r\n"
- "Content-RangeX: 0-1000000/1000000\r\n"
- "transferMode.dlna.org: Streaming\r\n"
- "Accept-Ranges: bytes\r\n"
- "Connection: close\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0\r\n"
- "\r\n",
- client->httpd->content_type);
-
- } else if (client->metadata_requested) {
- gchar *metadata_header;
-
- metadata_header = icy_server_metadata_header(
- client->httpd->name,
- client->httpd->genre,
- client->httpd->website,
- client->httpd->content_type,
- client->metaint);
-
- g_strlcpy(buffer, metadata_header, sizeof(buffer));
-
- g_free(metadata_header);
-
- } else { /* revert to a normal HTTP request */
- g_snprintf(buffer, sizeof(buffer),
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Connection: close\r\n"
- "Pragma: no-cache\r\n"
- "Cache-Control: no-cache, no-store\r\n"
- "\r\n",
- client->httpd->content_type);
- }
-
- status = g_io_channel_write_chars(client->channel,
- buffer, strlen(buffer),
- &bytes_written, &error);
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_AGAIN:
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- return false;
-}
-
-/**
- * Data has been received from the client and it is appended to the
- * input buffer.
- */
-static bool
-httpd_client_received(struct httpd_client *client)
-{
- assert(client != NULL);
- assert(client->state != RESPONSE);
-
- char *line;
- bool success;
-
- while ((line = httpd_client_read_line(client)) != NULL) {
- success = httpd_client_handle_line(client, line);
- g_free(line);
- if (!success) {
- assert(client->state != RESPONSE);
- return false;
- }
-
- if (client->state == RESPONSE) {
- if (!fifo_buffer_is_empty(client->input)) {
- g_warning("unexpected input from client");
- return false;
- }
-
- fifo_buffer_free(client->input);
-
- return httpd_client_send_response(client);
- }
- }
-
- return true;
-}
-
-static bool
-httpd_client_read(struct httpd_client *client)
-{
- char *p;
- size_t max_length;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_read;
-
- if (client->state == RESPONSE) {
- /* the client has already sent the request, and he
- must not send more */
- char buffer[1];
-
- status = g_io_channel_read_chars(client->channel, buffer,
- sizeof(buffer), &bytes_read,
- NULL);
- if (status == G_IO_STATUS_NORMAL)
- g_warning("unexpected input from client");
-
- return false;
- }
-
- p = fifo_buffer_write(client->input, &max_length);
- if (p == NULL) {
- g_warning("buffer overflow");
- return false;
- }
-
- status = g_io_channel_read_chars(client->channel, p, max_length,
- &bytes_read, &error);
- switch (status) {
- case G_IO_STATUS_NORMAL:
- fifo_buffer_append(client->input, bytes_read);
- return httpd_client_received(client);
-
- case G_IO_STATUS_AGAIN:
- /* try again later, after select() */
- return true;
-
- case G_IO_STATUS_EOF:
- /* peer disconnected */
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
- g_warning("failed to read from client: %s",
- error->message);
- g_error_free(error);
- return false;
- }
-
- /* unreachable */
- return false;
-}
-
-static gboolean
-httpd_client_in_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
- gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- bool ret;
-
- g_mutex_lock(httpd->mutex);
-
- if (condition == G_IO_IN && httpd_client_read(client)) {
- ret = true;
- } else {
- httpd_client_close(client);
- ret = false;
- }
-
- g_mutex_unlock(httpd->mutex);
-
- return ret;
-}
-
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
-{
- struct httpd_client *client = g_new(struct httpd_client, 1);
-
- client->httpd = httpd;
-
- client->channel = g_io_channel_new_socket(fd);
-
- /* GLib is responsible for closing the file descriptor */
- g_io_channel_set_close_on_unref(client->channel, true);
- /* NULL encoding means the stream is binary safe */
- g_io_channel_set_encoding(client->channel, NULL, NULL);
- /* we prefer to do buffering */
- g_io_channel_set_buffered(client->channel, false);
-
- client->read_source_id = g_io_add_watch(client->channel,
- G_IO_IN|G_IO_ERR|G_IO_HUP,
- httpd_client_in_event, client);
-
- client->input = fifo_buffer_new(4096);
- client->state = REQUEST;
-
- client->dlna_streaming_requested = false;
- client->metadata_supported = metadata_supported;
- client->metadata_requested = false;
- client->metadata_sent = true;
- client->metaint = 8192; /*TODO: just a std value */
- client->metadata = NULL;
- client->metadata_current_position = 0;
- client->metadata_fill = 0;
-
- return client;
-}
-
-static void
-httpd_client_add_page_size(gpointer data, gpointer user_data)
-{
- struct page *page = data;
- size_t *size = user_data;
-
- *size += page->size;
-}
-
-size_t
-httpd_client_queue_size(const struct httpd_client *client)
-{
- size_t size = 0;
-
- if (client->state != RESPONSE)
- return 0;
-
- g_queue_foreach(client->pages, httpd_client_add_page_size, &size);
- return size;
-}
-
-void
-httpd_client_cancel(struct httpd_client *client)
-{
- if (client->state != RESPONSE)
- return;
-
- g_queue_foreach(client->pages, httpd_client_unref_page, NULL);
- g_queue_clear(client->pages);
-
- if (client->write_source_id != 0 && client->current_page == NULL) {
- g_source_remove(client->write_source_id);
- client->write_source_id = 0;
- }
-}
-
-static GIOStatus
-write_page_to_channel(GIOChannel *channel,
- const struct page *page, size_t position,
- gsize *bytes_written_r, GError **error)
-{
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- return g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- page->size - position,
- bytes_written_r, error);
-}
-
-static GIOStatus
-write_n_bytes_to_channel(GIOChannel *channel, const struct page *page,
- size_t position, gint n,
- gsize *bytes_written_r, GError **error)
-{
- GIOStatus status;
-
- assert(channel != NULL);
- assert(page != NULL);
- assert(position < page->size);
-
- if (n == -1) {
- status = write_page_to_channel (channel, page, position,
- bytes_written_r, error);
- } else {
- status = g_io_channel_write_chars(channel,
- (const gchar*)page->data + position,
- n, bytes_written_r, error);
- }
-
- return status;
-}
-
-static gint
-bytes_left_till_metadata (struct httpd_client *client)
-{
- assert(client != NULL);
-
- if (client->metadata_requested &&
- client->current_page->size - client->current_position
- > client->metaint - client->metadata_fill)
- return client->metaint - client->metadata_fill;
-
- return -1;
-}
-
-static gboolean
-httpd_client_out_event(GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition, gpointer data)
-{
- struct httpd_client *client = data;
- struct httpd_output *httpd = client->httpd;
- GError *error = NULL;
- GIOStatus status;
- gsize bytes_written;
- gint bytes_to_write;
-
- g_mutex_lock(httpd->mutex);
-
- assert(condition == G_IO_OUT);
- assert(client->state == RESPONSE);
-
- if (client->write_source_id == 0) {
- /* another thread has removed the event source while
- this thread was waiting for httpd->mutex */
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- if (client->current_page == NULL) {
- client->current_page = g_queue_pop_head(client->pages);
- client->current_position = 0;
- }
-
- bytes_to_write = bytes_left_till_metadata(client);
-
- if (bytes_to_write == 0) {
- gint metadata_to_write;
-
- metadata_to_write = client->metadata_current_position;
-
- if (!client->metadata_sent) {
- status = write_page_to_channel(source,
- client->metadata,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (client->metadata->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- client->metadata_sent = true;
- }
- } else {
- struct page *empty_meta;
- guchar empty_data = 0;
-
- empty_meta = page_new_copy(&empty_data, 1);
-
- status = write_page_to_channel(source,
- empty_meta,
- metadata_to_write,
- &bytes_written, &error);
-
- client->metadata_current_position += bytes_written;
-
- if (empty_meta->size
- - client->metadata_current_position == 0) {
- client->metadata_fill = 0;
- client->metadata_current_position = 0;
- }
- }
-
- bytes_written = 0;
- } else {
- status = write_n_bytes_to_channel(source, client->current_page,
- client->current_position, bytes_to_write,
- &bytes_written, &error);
- }
-
- switch (status) {
- case G_IO_STATUS_NORMAL:
- client->current_position += bytes_written;
- assert(client->current_position <= client->current_page->size);
-
- if (client->metadata_requested)
- client->metadata_fill += bytes_written;
-
- if (client->current_position >= client->current_page->size) {
- page_unref(client->current_page);
- client->current_page = NULL;
-
- if (g_queue_is_empty(client->pages)) {
- /* all pages are sent: remove the
- event source */
- client->write_source_id = 0;
-
- g_mutex_unlock(httpd->mutex);
- return false;
- }
- }
-
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_AGAIN:
- g_mutex_unlock(httpd->mutex);
- return true;
-
- case G_IO_STATUS_EOF:
- /* client has disconnected */
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-
- case G_IO_STATUS_ERROR:
- /* I/O error */
-
- g_warning("failed to write to client: %s", error->message);
- g_error_free(error);
-
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* unreachable */
- httpd_client_close(client);
- g_mutex_unlock(httpd->mutex);
- return false;
-}
-
-void
-httpd_client_send(struct httpd_client *client, struct page *page)
-{
- if (client->state != RESPONSE)
- /* the client is still writing the HTTP request */
- return;
-
- page_ref(page);
- g_queue_push_tail(client->pages, page);
-
- if (client->write_source_id == 0)
- client->write_source_id =
- g_io_add_watch(client->channel, G_IO_OUT,
- httpd_client_out_event, client);
-}
-
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page)
-{
- if (client->metadata) {
- page_unref(client->metadata);
- client->metadata = NULL;
- }
-
- g_return_if_fail (page);
-
- page_ref(page);
- client->metadata = page;
- client->metadata_sent = false;
-}
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
deleted file mode 100644
index 739163f4..00000000
--- a/src/output/httpd_client.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_OUTPUT_HTTPD_CLIENT_H
-#define MPD_OUTPUT_HTTPD_CLIENT_H
-
-#include <glib.h>
-
-#include <stdbool.h>
-
-struct httpd_client;
-struct httpd_output;
-struct page;
-
-/**
- * Creates a new #httpd_client object
- *
- * @param httpd the HTTP output device
- * @param fd the socket file descriptor
- */
-struct httpd_client *
-httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported);
-
-/**
- * Frees memory and resources allocated by the #httpd_client object.
- * This does not remove it from the #httpd_output object.
- */
-void
-httpd_client_free(struct httpd_client *client);
-
-/**
- * Returns the total size of this client's page queue.
- */
-size_t
-httpd_client_queue_size(const struct httpd_client *client);
-
-/**
- * Clears the page queue.
- */
-void
-httpd_client_cancel(struct httpd_client *client);
-
-/**
- * Appends a page to the client's queue.
- */
-void
-httpd_client_send(struct httpd_client *client, struct page *page);
-
-/**
- * Sends the passed metadata.
- */
-void
-httpd_client_send_metadata(struct httpd_client *client, struct page *page);
-
-#endif
diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c
deleted file mode 100644
index 1d730df7..00000000
--- a/src/output/httpd_output_plugin.c
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "httpd_output_plugin.h"
-#include "httpd_internal.h"
-#include "httpd_client.h"
-#include "output_api.h"
-#include "encoder_plugin.h"
-#include "encoder_list.h"
-#include "resolver.h"
-#include "page.h"
-#include "icy_server.h"
-#include "fd_util.h"
-#include "server_socket.h"
-
-#include <assert.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#ifdef HAVE_LIBWRAP
-#include <sys/socket.h> /* needed for AF_UNIX */
-#include <tcpd.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "httpd_output"
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-httpd_output_quark(void)
-{
- return g_quark_from_static_string("httpd_output");
-}
-
-/**
- * Check whether there is at least one client.
- *
- * Caller must lock the mutex.
- */
-G_GNUC_PURE
-static bool
-httpd_output_has_clients(const struct httpd_output *httpd)
-{
- return httpd->clients != NULL;
-}
-
-/**
- * Check whether there is at least one client.
- */
-G_GNUC_PURE
-static bool
-httpd_output_lock_has_clients(const struct httpd_output *httpd)
-{
- g_mutex_lock(httpd->mutex);
- bool result = httpd_output_has_clients(httpd);
- g_mutex_unlock(httpd->mutex);
- return result;
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, int uid, void *ctx);
-
-static bool
-httpd_output_bind(struct httpd_output *httpd, GError **error_r)
-{
- httpd->open = false;
-
- g_mutex_lock(httpd->mutex);
- bool success = server_socket_open(httpd->server_socket, error_r);
- g_mutex_unlock(httpd->mutex);
-
- return success;
-}
-
-static void
-httpd_output_unbind(struct httpd_output *httpd)
-{
- assert(!httpd->open);
-
- g_mutex_lock(httpd->mutex);
- server_socket_close(httpd->server_socket);
- g_mutex_unlock(httpd->mutex);
-}
-
-static struct audio_output *
-httpd_output_init(const struct config_param *param,
- GError **error)
-{
- struct httpd_output *httpd = g_new(struct httpd_output, 1);
- if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) {
- g_free(httpd);
- return NULL;
- }
-
- /* read configuration */
- httpd->name =
- config_get_block_string(param, "name", "Set name in config");
- httpd->genre =
- config_get_block_string(param, "genre", "Set genre in config");
- httpd->website =
- config_get_block_string(param, "website", "Set website in config");
-
- guint port = config_get_block_unsigned(param, "port", 8000);
-
- const char *encoder_name =
- config_get_block_string(param, "encoder", "vorbis");
- const struct encoder_plugin *encoder_plugin =
- encoder_plugin_get(encoder_name);
- if (encoder_plugin == NULL) {
- g_set_error(error, httpd_output_quark(), 0,
- "No such encoder: %s", encoder_name);
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- httpd->clients_max = config_get_block_unsigned(param,"max_clients", 0);
-
- /* set up bind_to_address */
-
- httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
-
- const char *bind_to_address =
- config_get_block_string(param, "bind_to_address", NULL);
- bool success = bind_to_address != NULL &&
- strcmp(bind_to_address, "any") != 0
- ? server_socket_add_host(httpd->server_socket, bind_to_address,
- port, error)
- : server_socket_add_port(httpd->server_socket, port, error);
- if (!success) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* initialize metadata */
- httpd->metadata = NULL;
- httpd->unflushed_input = 0;
-
- /* initialize encoder */
-
- httpd->encoder = encoder_init(encoder_plugin, param, error);
- if (httpd->encoder == NULL) {
- ao_base_finish(&httpd->base);
- g_free(httpd);
- return NULL;
- }
-
- /* determine content type */
- httpd->content_type = encoder_get_mime_type(httpd->encoder);
- if (httpd->content_type == NULL) {
- httpd->content_type = "application/octet-stream";
- }
-
- httpd->mutex = g_mutex_new();
-
- return &httpd->base;
-}
-
-static void
-httpd_output_finish(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd->metadata)
- page_unref(httpd->metadata);
-
- encoder_finish(httpd->encoder);
- server_socket_free(httpd->server_socket);
- g_mutex_free(httpd->mutex);
- ao_base_finish(&httpd->base);
- g_free(httpd);
-}
-
-/**
- * Creates a new #httpd_client object and adds it into the
- * httpd_output.clients linked list.
- */
-static void
-httpd_client_add(struct httpd_output *httpd, int fd)
-{
- struct httpd_client *client =
- httpd_client_new(httpd, fd,
- httpd->encoder->plugin->tag == NULL);
-
- httpd->clients = g_list_prepend(httpd->clients, client);
- httpd->clients_cnt++;
-
- /* pass metadata to client */
- if (httpd->metadata)
- httpd_client_send_metadata(client, httpd->metadata);
-}
-
-static void
-httpd_listen_in_event(int fd, const struct sockaddr *address,
- size_t address_length, G_GNUC_UNUSED int uid, void *ctx)
-{
- struct httpd_output *httpd = ctx;
-
- /* the listener socket has become readable - a client has
- connected */
-
-#ifdef HAVE_LIBWRAP
- if (address->sa_family != AF_UNIX) {
- char *hostaddr = sockaddr_to_string(address, address_length, NULL);
- const char *progname = g_get_prgname();
-
- struct request_info req;
- request_init(&req, RQ_FILE, fd, RQ_DAEMON, progname, 0);
-
- fromhost(&req);
-
- if (!hosts_access(&req)) {
- /* tcp wrappers says no */
- g_warning("libwrap refused connection (libwrap=%s) from %s",
- progname, hostaddr);
- g_free(hostaddr);
- close_socket(fd);
- g_mutex_unlock(httpd->mutex);
- return;
- }
-
- g_free(hostaddr);
- }
-#else
- (void)address;
- (void)address_length;
-#endif /* HAVE_WRAP */
-
- g_mutex_lock(httpd->mutex);
-
- if (fd >= 0) {
- /* can we allow additional client */
- if (httpd->open &&
- (httpd->clients_max == 0 ||
- httpd->clients_cnt < httpd->clients_max))
- httpd_client_add(httpd, fd);
- else
- close_socket(fd);
- } else if (fd < 0 && errno != EINTR) {
- g_warning("accept() failed: %s", g_strerror(errno));
- }
-
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Reads data from the encoder (as much as available) and returns it
- * as a new #page object.
- */
-static struct page *
-httpd_output_read_page(struct httpd_output *httpd)
-{
- if (httpd->unflushed_input >= 65536) {
- /* we have fed a lot of input into the encoder, but it
- didn't give anything back yet - flush now to avoid
- buffer underruns */
- encoder_flush(httpd->encoder, NULL);
- httpd->unflushed_input = 0;
- }
-
- size_t size = 0;
- do {
- size_t nbytes = encoder_read(httpd->encoder,
- httpd->buffer + size,
- sizeof(httpd->buffer) - size);
- if (nbytes == 0)
- break;
-
- httpd->unflushed_input = 0;
-
- size += nbytes;
- } while (size < sizeof(httpd->buffer));
-
- if (size == 0)
- return NULL;
-
- return page_new_copy(httpd->buffer, size);
-}
-
-static bool
-httpd_output_encoder_open(struct httpd_output *httpd,
- struct audio_format *audio_format,
- GError **error)
-{
- if (!encoder_open(httpd->encoder, audio_format, error))
- return false;
-
- /* we have to remember the encoder header, i.e. the first
- bytes of encoder output after opening it, because it has to
- be sent to every new client */
- httpd->header = httpd_output_read_page(httpd);
-
- httpd->unflushed_input = 0;
-
- return true;
-}
-
-static bool
-httpd_output_enable(struct audio_output *ao, GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- return httpd_output_bind(httpd, error_r);
-}
-
-static void
-httpd_output_disable(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- httpd_output_unbind(httpd);
-}
-
-static bool
-httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
- GError **error)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- /* open the encoder */
-
- if (!httpd_output_encoder_open(httpd, audio_format, error)) {
- g_mutex_unlock(httpd->mutex);
- return false;
- }
-
- /* initialize other attributes */
-
- httpd->clients = NULL;
- httpd->clients_cnt = 0;
- httpd->timer = timer_new(audio_format);
-
- httpd->open = true;
-
- g_mutex_unlock(httpd->mutex);
- return true;
-}
-
-static void
-httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_free(client);
-}
-
-static void
-httpd_output_close(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
-
- httpd->open = false;
-
- timer_free(httpd->timer);
-
- g_list_foreach(httpd->clients, httpd_client_delete, NULL);
- g_list_free(httpd->clients);
-
- if (httpd->header != NULL)
- page_unref(httpd->header);
-
- encoder_close(httpd->encoder);
-
- g_mutex_unlock(httpd->mutex);
-}
-
-void
-httpd_output_remove_client(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- assert(httpd != NULL);
- assert(client != NULL);
-
- httpd->clients = g_list_remove(httpd->clients, client);
- httpd->clients_cnt--;
-}
-
-void
-httpd_output_send_header(struct httpd_output *httpd,
- struct httpd_client *client)
-{
- if (httpd->header != NULL)
- httpd_client_send(client, httpd->header);
-}
-
-static unsigned
-httpd_output_delay(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) {
- /* if there's no client and this output is paused,
- then httpd_output_pause() will not do anything, it
- will not fill the buffer and it will not update the
- timer; therefore, we reset the timer here */
- timer_reset(httpd->timer);
-
- /* some arbitrary delay that is long enough to avoid
- consuming too much CPU, and short enough to notice
- new clients quickly enough */
- return 1000;
- }
-
- return httpd->timer->started
- ? timer_delay(httpd->timer)
- : 0;
-}
-
-static void
-httpd_client_check_queue(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- if (httpd_client_queue_size(client) > 256 * 1024) {
- g_debug("client is too slow, flushing its queue");
- httpd_client_cancel(client);
- }
-}
-
-static void
-httpd_client_send_page(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *page = user_data;
-
- httpd_client_send(client, page);
-}
-
-/**
- * Broadcasts a page struct to all clients.
- */
-static void
-httpd_output_broadcast_page(struct httpd_output *httpd, struct page *page)
-{
- assert(page != NULL);
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_send_page, page);
- g_mutex_unlock(httpd->mutex);
-}
-
-/**
- * Broadcasts data from the encoder to all clients.
- */
-static void
-httpd_output_encoder_to_clients(struct httpd_output *httpd)
-{
- struct page *page;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_check_queue, NULL);
- g_mutex_unlock(httpd->mutex);
-
- while ((page = httpd_output_read_page(httpd)) != NULL) {
- httpd_output_broadcast_page(httpd, page);
- page_unref(page);
- }
-}
-
-static bool
-httpd_output_encode_and_play(struct httpd_output *httpd,
- const void *chunk, size_t size, GError **error)
-{
- if (!encoder_write(httpd->encoder, chunk, size, error))
- return false;
-
- httpd->unflushed_input += size;
-
- httpd_output_encoder_to_clients(httpd);
-
- return true;
-}
-
-static size_t
-httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
- GError **error_r)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
- return 0;
- }
-
- if (!httpd->timer->started)
- timer_start(httpd->timer);
- timer_add(httpd->timer, size);
-
- return size;
-}
-
-static bool
-httpd_output_pause(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- if (httpd_output_lock_has_clients(httpd)) {
- static const char silence[1020];
- return httpd_output_play(ao, silence, sizeof(silence),
- NULL) > 0;
- } else {
- return true;
- }
-}
-
-static void
-httpd_send_metadata(gpointer data, gpointer user_data)
-{
- struct httpd_client *client = data;
- struct page *icy_metadata = user_data;
-
- httpd_client_send_metadata(client, icy_metadata);
-}
-
-static void
-httpd_output_tag(struct audio_output *ao, const struct tag *tag)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- assert(tag != NULL);
-
- if (httpd->encoder->plugin->tag != NULL) {
- /* embed encoder tags */
-
- /* flush the current stream, and end it */
-
- encoder_pre_tag(httpd->encoder, NULL);
- httpd_output_encoder_to_clients(httpd);
-
- /* send the tag to the encoder - which starts a new
- stream now */
-
- encoder_tag(httpd->encoder, tag, NULL);
-
- /* the first page generated by the encoder will now be
- used as the new "header" page, which is sent to all
- new clients */
-
- struct page *page = httpd_output_read_page(httpd);
- if (page != NULL) {
- if (httpd->header != NULL)
- page_unref(httpd->header);
- httpd->header = page;
- httpd_output_broadcast_page(httpd, page);
- }
- } else {
- /* use Icy-Metadata */
-
- if (httpd->metadata != NULL)
- page_unref (httpd->metadata);
-
- httpd->metadata =
- icy_server_metadata_page(tag, TAG_ALBUM,
- TAG_ARTIST, TAG_TITLE,
- TAG_NUM_OF_ITEM_TYPES);
- if (httpd->metadata != NULL) {
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients,
- httpd_send_metadata, httpd->metadata);
- g_mutex_unlock(httpd->mutex);
- }
- }
-}
-
-static void
-httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct httpd_client *client = data;
-
- httpd_client_cancel(client);
-}
-
-static void
-httpd_output_cancel(struct audio_output *ao)
-{
- struct httpd_output *httpd = (struct httpd_output *)ao;
-
- g_mutex_lock(httpd->mutex);
- g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL);
- g_mutex_unlock(httpd->mutex);
-}
-
-const struct audio_output_plugin httpd_output_plugin = {
- .name = "httpd",
- .init = httpd_output_init,
- .finish = httpd_output_finish,
- .enable = httpd_output_enable,
- .disable = httpd_output_disable,
- .open = httpd_output_open,
- .close = httpd_output_close,
- .delay = httpd_output_delay,
- .send_tag = httpd_output_tag,
- .play = httpd_output_play,
- .pause = httpd_output_pause,
- .cancel = httpd_output_cancel,
-};
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index e267427d..e82d3d3f 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -20,8 +20,8 @@
#include "config.h"
#include "pulse_output_plugin.h"
#include "output_api.h"
-#include "mixer_list.h"
-#include "mixer/pulse_mixer_plugin.h"
+#include "MixerList.hxx"
+#include "mixer/PulseMixerPlugin.h"
#include <glib.h>
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 02a51f27..bcc8004a 100644
--- a/src/output/pulse_output_plugin.h
+++ b/src/output/pulse_output_plugin.h
@@ -20,9 +20,9 @@
#ifndef MPD_PULSE_OUTPUT_PLUGIN_H
#define MPD_PULSE_OUTPUT_PLUGIN_H
-#include <stdbool.h>
+#include "gerror.h"
-#include <glib.h>
+#include <stdbool.h>
struct pulse_output;
struct pulse_mixer;
@@ -30,6 +30,10 @@ struct pa_cvolume;
extern const struct audio_output_plugin pulse_output_plugin;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void
pulse_output_lock(struct pulse_output *po);
@@ -46,4 +50,8 @@ bool
pulse_output_set_volume(struct pulse_output *po,
const struct pa_cvolume *volume, GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output/shout_output_plugin.c b/src/output/shout_output_plugin.c
index 56456a0e..56d7a88b 100644
--- a/src/output/shout_output_plugin.c
+++ b/src/output/shout_output_plugin.c
@@ -97,23 +97,22 @@ static void free_shout_data(struct shout_data *sd)
g_free(sd);
}
-#define check_block_param(name) { \
- block_param = config_get_block_param(param, name); \
- if (!block_param) { \
- MPD_ERROR("no \"%s\" defined for shout device defined at line " \
- "%i\n", name, param->line); \
- } \
- }
+gcc_pure
+static const char *
+require_block_string(const struct config_param *param, const char *name)
+{
+ const char *value = config_get_block_string(param, name, NULL);
+ if (value == NULL)
+ MPD_ERROR("no \"%s\" defined for shout device defined at line " \
+ "%i\n", name, param->line); \
-static struct audio_output *
-my_shout_init_driver(const struct config_param *param,
- GError **error)
+ return value;
+}
+
+static bool
+my_shout_configure(struct shout_data *sd, const struct config_param *param,
+ GError **error)
{
- struct shout_data *sd = new_shout_data();
- if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
- free_shout_data(sd);
- return NULL;
- }
const struct audio_format *audio_format =
&sd->base.config_audio_format;
@@ -125,30 +124,18 @@ my_shout_init_driver(const struct config_param *param,
return NULL;
}
- if (shout_init_count == 0)
- shout_init();
-
- shout_init_count++;
-
- const struct block_param *block_param;
- check_block_param("host");
- char *host = block_param->value;
-
- check_block_param("mount");
- char *mount = block_param->value;
+ const char *host = require_block_string(param, "host");
+ const char *mount = require_block_string(param, "mount");
unsigned port = config_get_block_unsigned(param, "port", 0);
if (port == 0) {
g_set_error(error, shout_output_quark(), 0,
"shout port must be configured");
- goto failure;
+ return false;
}
- check_block_param("password");
- const char *passwd = block_param->value;
-
- check_block_param("name");
- const char *name = block_param->value;
+ const char *passwd = require_block_string(param, "password");
+ const char *name = require_block_string(param, "name");
bool public = config_get_block_bool(param, "public", false);
@@ -164,21 +151,21 @@ my_shout_init_driver(const struct config_param *param,
"shout quality \"%s\" is not a number in the "
"range -1 to 10, line %i",
value, param->line);
- goto failure;
+ return false;
}
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
g_set_error(error, shout_output_quark(), 0,
"quality and bitrate are "
"both defined");
- goto failure;
+ return false;
}
} else {
value = config_get_block_string(param, "bitrate", NULL);
if (value == NULL) {
g_set_error(error, shout_output_quark(), 0,
"neither bitrate nor quality defined");
- goto failure;
+ return false;
}
char *test;
@@ -187,7 +174,7 @@ my_shout_init_driver(const struct config_param *param,
if (*test != '\0' || sd->bitrate <= 0) {
g_set_error(error, shout_output_quark(), 0,
"bitrate must be a positive integer");
- goto failure;
+ return false;
}
}
@@ -199,12 +186,12 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"couldn't find shout encoder plugin \"%s\"",
encoding);
- goto failure;
+ return false;
}
sd->encoder = encoder_init(encoder_plugin, param, error);
if (sd->encoder == NULL)
- goto failure;
+ return false;
unsigned shout_format;
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
@@ -220,7 +207,7 @@ my_shout_init_driver(const struct config_param *param,
g_set_error(error, shout_output_quark(), 0,
"you cannot stream \"%s\" to shoutcast, use mp3",
encoding);
- goto failure;
+ return false;
} else if (0 == strcmp(value, "shoutcast"))
protocol = SHOUT_PROTOCOL_ICY;
else if (0 == strcmp(value, "icecast1"))
@@ -232,7 +219,7 @@ my_shout_init_driver(const struct config_param *param,
"shout protocol \"%s\" is not \"shoutcast\" or "
"\"icecast1\"or \"icecast2\"",
value);
- goto failure;
+ return false;
}
} else {
protocol = SHOUT_PROTOCOL_HTTP;
@@ -251,7 +238,7 @@ my_shout_init_driver(const struct config_param *param,
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
/* optional paramters */
@@ -262,21 +249,21 @@ my_shout_init_driver(const struct config_param *param,
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "description", NULL);
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
value = config_get_block_string(param, "url", NULL);
if (value != NULL && shout_set_url(sd->shout_conn, value)) {
g_set_error(error, shout_output_quark(), 0,
"%s", shout_get_error(sd->shout_conn));
- goto failure;
+ return false;
}
{
@@ -301,12 +288,31 @@ my_shout_init_driver(const struct config_param *param,
}
}
- return &sd->base;
+ return true;
+}
-failure:
- ao_base_finish(&sd->base);
- free_shout_data(sd);
- return NULL;
+static struct audio_output *
+my_shout_init_driver(const struct config_param *param,
+ GError **error)
+{
+ struct shout_data *sd = new_shout_data();
+ if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (!my_shout_configure(sd, param, error)) {
+ ao_base_finish(&sd->base);
+ free_shout_data(sd);
+ return NULL;
+ }
+
+ if (shout_init_count == 0)
+ shout_init();
+
+ shout_init_count++;
+
+ return &sd->base;
}
static bool
diff --git a/src/output_internal.h b/src/output_internal.h
index 9d975d78..201962a7 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -27,6 +27,12 @@
#include <time.h>
+#ifdef __cplusplus
+class Filter;
+#else
+typedef void *Filter;
+#endif
+
struct config_param;
enum audio_output_command {
@@ -73,6 +79,13 @@ struct audio_output {
struct mixer *mixer;
/**
+ * Will this output receive tags from the decoder? The
+ * default is true, but it may be configured to false to
+ * suppress sending tags to the output.
+ */
+ bool tags;
+
+ /**
* Shall this output always play something (i.e. silence),
* even when playback is stopped?
*/
@@ -149,13 +162,13 @@ struct audio_output {
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
*/
- struct filter *filter;
+ Filter *filter;
/**
* The replay_gain_filter_plugin instance of this audio
* output.
*/
- struct filter *replay_gain_filter;
+ Filter *replay_gain_filter;
/**
* The serial number of the last replay gain info. 0 means no
@@ -168,7 +181,7 @@ struct audio_output {
* output, to be applied to the second chunk during
* cross-fading.
*/
- struct filter *other_replay_gain_filter;
+ Filter *other_replay_gain_filter;
/**
* The serial number of the last replay gain info by the
@@ -182,7 +195,7 @@ struct audio_output {
* for converting the input data into the appropriate format
* for this audio output.
*/
- struct filter *convert_filter;
+ Filter *convert_filter;
/**
* The thread handle, or NULL if the output thread isn't
@@ -250,6 +263,10 @@ audio_output_command_is_finished(const struct audio_output *ao)
return ao->command == AO_COMMAND_NONE;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct audio_output *
audio_output_new(const struct config_param *param,
struct player_control *pc,
@@ -266,4 +283,8 @@ ao_base_finish(struct audio_output *ao);
void
audio_output_free(struct audio_output *ao);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 209ca622..2b71ba6a 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -20,7 +20,8 @@
#ifndef MPD_OUTPUT_PLUGIN_H
#define MPD_OUTPUT_PLUGIN_H
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
#include <stdbool.h>
#include <stddef.h>
@@ -165,7 +166,11 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin)
: false;
}
-G_GNUC_MALLOC
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gcc_malloc
struct audio_output *
ao_plugin_init(const struct audio_output_plugin *plugin,
const struct config_param *param,
@@ -187,7 +192,7 @@ ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format,
void
ao_plugin_close(struct audio_output *ao);
-G_GNUC_PURE
+gcc_pure
unsigned
ao_plugin_delay(struct audio_output *ao);
@@ -207,4 +212,8 @@ ao_plugin_cancel(struct audio_output *ao);
bool
ao_plugin_pause(struct audio_output *ao);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/page.c b/src/page.c
deleted file mode 100644
index e2e22791..00000000
--- a/src/page.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "page.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-
-/**
- * Allocates a new #page object, without filling the data element.
- */
-static struct page *
-page_new(size_t size)
-{
- struct page *page = g_malloc(sizeof(*page) + size -
- sizeof(page->data));
-
- assert(size > 0);
-
- page->ref = 1;
- page->size = size;
- return page;
-}
-
-struct page *
-page_new_copy(const void *data, size_t size)
-{
- struct page *page = page_new(size);
-
- assert(data != NULL);
-
- memcpy(page->data, data, size);
- return page;
-}
-
-struct page *
-page_new_concat(const struct page *a, const struct page *b)
-{
- struct page *page = page_new(a->size + b->size);
-
- memcpy(page->data, a->data, a->size);
- memcpy(page->data + a->size, b->data, b->size);
-
- return page;
-}
-
-void
-page_ref(struct page *page)
-{
- g_atomic_int_inc(&page->ref);
-}
-
-static void
-page_free(struct page *page)
-{
- assert(page->ref == 0);
-
- g_free(page);
-}
-
-bool
-page_unref(struct page *page)
-{
- bool unused = g_atomic_int_dec_and_test(&page->ref);
-
- if (unused)
- page_free(page);
-
- return unused;
-}
diff --git a/src/path.h b/src/path.h
deleted file mode 100644
index 00c368e7..00000000
--- a/src/path.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PATH_H
-#define MPD_PATH_H
-
-#include <limits.h>
-
-#if !defined(MPD_PATH_MAX)
-# if defined(MAXPATHLEN)
-# define MPD_PATH_MAX MAXPATHLEN
-# elif defined(PATH_MAX)
-# define MPD_PATH_MAX PATH_MAX
-# else
-# define MPD_PATH_MAX 256
-# endif
-#endif
-
-void path_global_init(void);
-
-void path_global_finish(void);
-
-/**
- * Converts a file name in the filesystem charset to UTF-8. Returns
- * NULL on failure.
- */
-char *
-fs_charset_to_utf8(const char *path_fs);
-
-/**
- * Converts a file name in UTF-8 to the filesystem charset. Returns a
- * duplicate of the UTF-8 string on failure.
- */
-char *
-utf8_to_fs_charset(const char *path_utf8);
-
-const char *path_get_fs_charset(void);
-
-#endif
diff --git a/src/pcm_buffer.h b/src/pcm_buffer.h
index 4502976f..5d6382d5 100644
--- a/src/pcm_buffer.h
+++ b/src/pcm_buffer.h
@@ -62,6 +62,10 @@ pcm_buffer_deinit(struct pcm_buffer *buffer)
buffer->buffer = NULL;
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Get the buffer, and guarantee a minimum size. This buffer becomes
* invalid with the next pcm_buffer_get() call.
@@ -74,4 +78,8 @@ G_GNUC_MALLOC
void *
pcm_buffer_get(struct pcm_buffer *buffer, size_t size);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
deleted file mode 100644
index 63f9a1b9..00000000
--- a/src/pcm_convert.c
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "pcm_convert.h"
-#include "pcm_channels.h"
-#include "pcm_format.h"
-#include "pcm_pack.h"
-#include "audio_format.h"
-#include "glib_compat.h"
-
-#include <assert.h>
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "pcm"
-
-void pcm_convert_init(struct pcm_convert_state *state)
-{
- memset(state, 0, sizeof(*state));
-
- pcm_dsd_init(&state->dsd);
- pcm_resample_init(&state->resample);
- pcm_dither_24_init(&state->dither);
-
- pcm_buffer_init(&state->format_buffer);
- pcm_buffer_init(&state->channels_buffer);
-}
-
-void pcm_convert_deinit(struct pcm_convert_state *state)
-{
- pcm_dsd_deinit(&state->dsd);
- pcm_resample_deinit(&state->resample);
-
- pcm_buffer_deinit(&state->format_buffer);
- pcm_buffer_deinit(&state->channels_buffer);
-}
-
-void
-pcm_convert_reset(struct pcm_convert_state *state)
-{
- pcm_dsd_reset(&state->dsd);
- pcm_resample_reset(&state->resample);
-}
-
-static const void *
-pcm_convert_channels(struct pcm_buffer *buffer, enum sample_format format,
- uint8_t dest_channels,
- uint8_t src_channels, const void *src,
- size_t src_size, size_t *dest_size_r,
- GError **error_r)
-{
- const void *dest = NULL;
-
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_S8:
- case SAMPLE_FORMAT_FLOAT:
- case SAMPLE_FORMAT_DSD:
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Channel conversion not implemented for format '%s'",
- sample_format_to_string(format));
- return NULL;
-
- case SAMPLE_FORMAT_S16:
- dest = pcm_convert_channels_16(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S24_P32:
- dest = pcm_convert_channels_24(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
-
- case SAMPLE_FORMAT_S32:
- dest = pcm_convert_channels_32(buffer, dest_channels,
- src_channels, src,
- src_size, dest_size_r);
- break;
- }
-
- if (dest == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_channels, dest_channels);
- return NULL;
- }
-
- return dest;
-}
-
-static const int16_t *
-pcm_convert_16(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src_buffer, size_t src_size,
- const struct audio_format *dest_format, size_t *dest_size_r,
- GError **error_r)
-{
- const int16_t *buf;
- size_t len;
-
- assert(dest_format->format == SAMPLE_FORMAT_S16);
-
- buf = pcm_convert_to_16(&state->format_buffer, &state->dither,
- src_format->format, src_buffer, src_size,
- &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %s to 16 bit is not implemented",
- sample_format_to_string(src_format->format));
- return NULL;
- }
-
- if (src_format->channels != dest_format->channels) {
- buf = pcm_convert_channels_16(&state->channels_buffer,
- dest_format->channels,
- src_format->channels,
- buf, len, &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format->channels,
- dest_format->channels);
- return NULL;
- }
- }
-
- if (src_format->sample_rate != dest_format->sample_rate) {
- buf = pcm_resample_16(&state->resample,
- dest_format->channels,
- src_format->sample_rate, buf, len,
- dest_format->sample_rate, &len,
- error_r);
- if (buf == NULL)
- return NULL;
- }
-
- *dest_size_r = len;
- return buf;
-}
-
-static const int32_t *
-pcm_convert_24(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src_buffer, size_t src_size,
- const struct audio_format *dest_format, size_t *dest_size_r,
- GError **error_r)
-{
- const int32_t *buf;
- size_t len;
-
- assert(dest_format->format == SAMPLE_FORMAT_S24_P32);
-
- buf = pcm_convert_to_24(&state->format_buffer, src_format->format,
- src_buffer, src_size, &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %s to 24 bit is not implemented",
- sample_format_to_string(src_format->format));
- return NULL;
- }
-
- if (src_format->channels != dest_format->channels) {
- buf = pcm_convert_channels_24(&state->channels_buffer,
- dest_format->channels,
- src_format->channels,
- buf, len, &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format->channels,
- dest_format->channels);
- return NULL;
- }
- }
-
- if (src_format->sample_rate != dest_format->sample_rate) {
- buf = pcm_resample_24(&state->resample,
- dest_format->channels,
- src_format->sample_rate, buf, len,
- dest_format->sample_rate, &len,
- error_r);
- if (buf == NULL)
- return NULL;
- }
-
- *dest_size_r = len;
- return buf;
-}
-
-static const int32_t *
-pcm_convert_32(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src_buffer, size_t src_size,
- const struct audio_format *dest_format, size_t *dest_size_r,
- GError **error_r)
-{
- const int32_t *buf;
- size_t len;
-
- assert(dest_format->format == SAMPLE_FORMAT_S32);
-
- buf = pcm_convert_to_32(&state->format_buffer, src_format->format,
- src_buffer, src_size, &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %s to 32 bit is not implemented",
- sample_format_to_string(src_format->format));
- return NULL;
- }
-
- if (src_format->channels != dest_format->channels) {
- buf = pcm_convert_channels_32(&state->channels_buffer,
- dest_format->channels,
- src_format->channels,
- buf, len, &len);
- if (buf == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format->channels,
- dest_format->channels);
- return NULL;
- }
- }
-
- if (src_format->sample_rate != dest_format->sample_rate) {
- buf = pcm_resample_32(&state->resample,
- dest_format->channels,
- src_format->sample_rate, buf, len,
- dest_format->sample_rate, &len,
- error_r);
- if (buf == NULL)
- return buf;
- }
-
- *dest_size_r = len;
- return buf;
-}
-
-static const float *
-pcm_convert_float(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src_buffer, size_t src_size,
- const struct audio_format *dest_format, size_t *dest_size_r,
- GError **error_r)
-{
- const float *buffer = src_buffer;
- size_t size = src_size;
-
- assert(dest_format->format == SAMPLE_FORMAT_FLOAT);
-
- /* convert channels first, hoping the source format is
- supported (float is not) */
-
- if (dest_format->channels != src_format->channels) {
- buffer = pcm_convert_channels(&state->channels_buffer,
- src_format->format,
- dest_format->channels,
- src_format->channels,
- buffer, size, &size, error_r);
- if (buffer == NULL)
- return NULL;
- }
-
- /* convert to float now */
-
- buffer = pcm_convert_to_float(&state->format_buffer,
- src_format->format,
- buffer, size, &size);
- if (buffer == NULL) {
- g_set_error(error_r, pcm_convert_quark(), 0,
- "Conversion from %s to float is not implemented",
- sample_format_to_string(src_format->format));
- return NULL;
- }
-
- /* resample with float, because this is the best format for
- libsamplerate */
-
- if (src_format->sample_rate != dest_format->sample_rate) {
- buffer = pcm_resample_float(&state->resample,
- dest_format->channels,
- src_format->sample_rate,
- buffer, size,
- dest_format->sample_rate, &size,
- error_r);
- if (buffer == NULL)
- return NULL;
- }
-
- *dest_size_r = size;
- return buffer;
-}
-
-const void *
-pcm_convert(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src, size_t src_size,
- const struct audio_format *dest_format,
- size_t *dest_size_r,
- GError **error_r)
-{
- struct audio_format float_format;
- if (src_format->format == SAMPLE_FORMAT_DSD) {
- size_t f_size;
- const float *f = pcm_dsd_to_float(&state->dsd,
- src_format->channels,
- false, src, src_size,
- &f_size);
- if (f == NULL) {
- g_set_error_literal(error_r, pcm_convert_quark(), 0,
- "DSD to PCM conversion failed");
- return NULL;
- }
-
- float_format = *src_format;
- float_format.format = SAMPLE_FORMAT_FLOAT;
-
- src_format = &float_format;
- src = f;
- src_size = f_size;
- }
-
- switch (dest_format->format) {
- case SAMPLE_FORMAT_S16:
- return pcm_convert_16(state,
- src_format, src, src_size,
- dest_format, dest_size_r,
- error_r);
-
- case SAMPLE_FORMAT_S24_P32:
- return pcm_convert_24(state,
- src_format, src, src_size,
- dest_format, dest_size_r,
- error_r);
-
- case SAMPLE_FORMAT_S32:
- return pcm_convert_32(state,
- src_format, src, src_size,
- dest_format, dest_size_r,
- error_r);
-
- case SAMPLE_FORMAT_FLOAT:
- return pcm_convert_float(state,
- src_format, src, src_size,
- dest_format, dest_size_r,
- error_r);
-
- default:
- g_set_error(error_r, pcm_convert_quark(), 0,
- "PCM conversion to %s is not implemented",
- sample_format_to_string(dest_format->format));
- return NULL;
- }
-}
diff --git a/src/pcm_convert.h b/src/pcm_convert.h
deleted file mode 100644
index be11a6e4..00000000
--- a/src/pcm_convert.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef PCM_CONVERT_H
-#define PCM_CONVERT_H
-
-#include "pcm_dsd.h"
-#include "pcm_resample.h"
-#include "pcm_dither.h"
-#include "pcm_buffer.h"
-
-struct audio_format;
-
-/**
- * This object is statically allocated (within another struct), and
- * holds buffer allocations and the state for all kinds of PCM
- * conversions.
- */
-struct pcm_convert_state {
- struct pcm_dsd dsd;
-
- struct pcm_resample_state resample;
-
- struct pcm_dither dither;
-
- /** the buffer for converting the sample format */
- struct pcm_buffer format_buffer;
-
- /** the buffer for converting the channel count */
- struct pcm_buffer channels_buffer;
-};
-
-static inline GQuark
-pcm_convert_quark(void)
-{
- return g_quark_from_static_string("pcm_convert");
-}
-
-/**
- * Initializes a pcm_convert_state object.
- */
-void pcm_convert_init(struct pcm_convert_state *state);
-
-/**
- * Deinitializes a pcm_convert_state object and frees allocated
- * memory.
- */
-void pcm_convert_deinit(struct pcm_convert_state *state);
-
-/**
- * Reset the pcm_convert_state object. Use this at the boundary
- * between two distinct songs and each time the format changes.
- */
-void
-pcm_convert_reset(struct pcm_convert_state *state);
-
-/**
- * Converts PCM data between two audio formats.
- *
- * @param state an initialized pcm_convert_state object
- * @param src_format the source audio format
- * @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_format the requested destination audio format
- * @param dest_size_r returns the number of bytes of the destination buffer
- * @param error_r location to store the error occurring, or NULL to
- * ignore errors
- * @return the destination buffer, or NULL on error
- */
-const void *
-pcm_convert(struct pcm_convert_state *state,
- const struct audio_format *src_format,
- const void *src, size_t src_size,
- const struct audio_format *dest_format,
- size_t *dest_size_r,
- GError **error_r);
-
-#endif
diff --git a/src/pcm_dither.c b/src/pcm_dither.c
deleted file mode 100644
index 4811946c..00000000
--- a/src/pcm_dither.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "pcm_dither.h"
-#include "pcm_prng.h"
-
-static int16_t
-pcm_dither_sample_24_to_16(int32_t sample, struct pcm_dither *dither)
-{
- int32_t output, rnd;
-
- enum {
- from_bits = 24,
- to_bits = 16,
- scale_bits = from_bits - to_bits,
- round = 1 << (scale_bits - 1),
- mask = (1 << scale_bits) - 1,
- ONE = 1 << (from_bits - 1),
- MIN = -ONE,
- MAX = ONE - 1
- };
-
- sample += dither->error[0] - dither->error[1] + dither->error[2];
-
- dither->error[2] = dither->error[1];
- dither->error[1] = dither->error[0] / 2;
-
- /* round */
- output = sample + round;
-
- rnd = pcm_prng(dither->random);
- output += (rnd & mask) - (dither->random & mask);
-
- dither->random = rnd;
-
- /* clip */
- if (output > MAX) {
- output = MAX;
-
- if (sample > MAX)
- sample = MAX;
- } else if (output < MIN) {
- output = MIN;
-
- if (sample < MIN)
- sample = MIN;
- }
-
- output &= ~mask;
-
- dither->error[0] = sample - output;
-
- return (int16_t)(output >> scale_bits);
-}
-
-void
-pcm_dither_24_to_16(struct pcm_dither *dither,
- int16_t *dest, const int32_t *src, const int32_t *src_end)
-{
- while (src < src_end)
- *dest++ = pcm_dither_sample_24_to_16(*src++, dither);
-}
-
-static int16_t
-pcm_dither_sample_32_to_16(int32_t sample, struct pcm_dither *dither)
-{
- return pcm_dither_sample_24_to_16(sample >> 8, dither);
-}
-
-void
-pcm_dither_32_to_16(struct pcm_dither *dither,
- int16_t *dest, const int32_t *src, const int32_t *src_end)
-{
- while (src < src_end)
- *dest++ = pcm_dither_sample_32_to_16(*src++, dither);
-}
diff --git a/src/pcm_export.h b/src/pcm_export.h
index a7e7c3f6..005db48e 100644
--- a/src/pcm_export.h
+++ b/src/pcm_export.h
@@ -87,6 +87,10 @@ struct pcm_export_state {
uint8_t reverse_endian;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Initialize a #pcm_export_state object.
*/
@@ -144,4 +148,8 @@ G_GNUC_PURE
size_t
pcm_export_source_size(const struct pcm_export_state *state, size_t dest_size);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/pcm_mix.c b/src/pcm_mix.c
deleted file mode 100644
index 6c6d1b4a..00000000
--- a/src/pcm_mix.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "pcm_mix.h"
-#include "pcm_volume.h"
-#include "pcm_utils.h"
-#include "audio_format.h"
-
-#include <glib.h>
-
-#include <math.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "pcm"
-
-static void
-pcm_add_vol_8(int8_t *buffer1, const int8_t *buffer2,
- unsigned num_samples, int volume1, int volume2)
-{
- while (num_samples > 0) {
- int32_t sample1 = *buffer1;
- int32_t sample2 = *buffer2++;
-
- sample1 = ((sample1 * volume1 + sample2 * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer1++ = pcm_range(sample1, 8);
- --num_samples;
- }
-}
-
-static void
-pcm_add_vol_16(int16_t *buffer1, const int16_t *buffer2,
- unsigned num_samples, int volume1, int volume2)
-{
- while (num_samples > 0) {
- int32_t sample1 = *buffer1;
- int32_t sample2 = *buffer2++;
-
- sample1 = ((sample1 * volume1 + sample2 * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer1++ = pcm_range(sample1, 16);
- --num_samples;
- }
-}
-
-static void
-pcm_add_vol_24(int32_t *buffer1, const int32_t *buffer2,
- unsigned num_samples, unsigned volume1, unsigned volume2)
-{
- while (num_samples > 0) {
- int64_t sample1 = *buffer1;
- int64_t sample2 = *buffer2++;
-
- sample1 = ((sample1 * volume1 + sample2 * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer1++ = pcm_range(sample1, 24);
- --num_samples;
- }
-}
-
-static void
-pcm_add_vol_32(int32_t *buffer1, const int32_t *buffer2,
- unsigned num_samples, unsigned volume1, unsigned volume2)
-{
- while (num_samples > 0) {
- int64_t sample1 = *buffer1;
- int64_t sample2 = *buffer2++;
-
- sample1 = ((sample1 * volume1 + sample2 * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer1++ = pcm_range_64(sample1, 32);
- --num_samples;
- }
-}
-
-static void
-pcm_add_vol_float(float *buffer1, const float *buffer2,
- unsigned num_samples, float volume1, float volume2)
-{
- while (num_samples > 0) {
- float sample1 = *buffer1;
- float sample2 = *buffer2++;
-
- sample1 = (sample1 * volume1 + sample2 * volume2);
- *buffer1++ = sample1;
- --num_samples;
- }
-}
-
-static bool
-pcm_add_vol(void *buffer1, const void *buffer2, size_t size,
- int vol1, int vol2,
- enum sample_format format)
-{
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_DSD:
- /* not implemented */
- return false;
-
- case SAMPLE_FORMAT_S8:
- pcm_add_vol_8((int8_t *)buffer1, (const int8_t *)buffer2,
- size, vol1, vol2);
- return true;
-
- case SAMPLE_FORMAT_S16:
- pcm_add_vol_16((int16_t *)buffer1, (const int16_t *)buffer2,
- size / 2, vol1, vol2);
- return true;
-
- case SAMPLE_FORMAT_S24_P32:
- pcm_add_vol_24((int32_t *)buffer1, (const int32_t *)buffer2,
- size / 4, vol1, vol2);
- return true;
-
- case SAMPLE_FORMAT_S32:
- pcm_add_vol_32((int32_t *)buffer1, (const int32_t *)buffer2,
- size / 4, vol1, vol2);
- return true;
-
- case SAMPLE_FORMAT_FLOAT:
- pcm_add_vol_float(buffer1, buffer2, size / 4,
- pcm_volume_to_float(vol1),
- pcm_volume_to_float(vol2));
- return true;
- }
-
- /* unreachable */
- assert(false);
- return false;
-}
-
-static void
-pcm_add_8(int8_t *buffer1, const int8_t *buffer2, unsigned num_samples)
-{
- while (num_samples > 0) {
- int32_t sample1 = *buffer1;
- int32_t sample2 = *buffer2++;
-
- sample1 += sample2;
-
- *buffer1++ = pcm_range(sample1, 8);
- --num_samples;
- }
-}
-
-static void
-pcm_add_16(int16_t *buffer1, const int16_t *buffer2, unsigned num_samples)
-{
- while (num_samples > 0) {
- int32_t sample1 = *buffer1;
- int32_t sample2 = *buffer2++;
-
- sample1 += sample2;
-
- *buffer1++ = pcm_range(sample1, 16);
- --num_samples;
- }
-}
-
-static void
-pcm_add_24(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples)
-{
- while (num_samples > 0) {
- int64_t sample1 = *buffer1;
- int64_t sample2 = *buffer2++;
-
- sample1 += sample2;
-
- *buffer1++ = pcm_range(sample1, 24);
- --num_samples;
- }
-}
-
-static void
-pcm_add_32(int32_t *buffer1, const int32_t *buffer2, unsigned num_samples)
-{
- while (num_samples > 0) {
- int64_t sample1 = *buffer1;
- int64_t sample2 = *buffer2++;
-
- sample1 += sample2;
-
- *buffer1++ = pcm_range_64(sample1, 32);
- --num_samples;
- }
-}
-
-static void
-pcm_add_float(float *buffer1, const float *buffer2, unsigned num_samples)
-{
- while (num_samples > 0) {
- float sample1 = *buffer1;
- float sample2 = *buffer2++;
- *buffer1++ = sample1 + sample2;
- --num_samples;
- }
-}
-
-static bool
-pcm_add(void *buffer1, const void *buffer2, size_t size,
- enum sample_format format)
-{
- switch (format) {
- case SAMPLE_FORMAT_UNDEFINED:
- case SAMPLE_FORMAT_DSD:
- /* not implemented */
- return false;
-
- case SAMPLE_FORMAT_S8:
- pcm_add_8((int8_t *)buffer1, (const int8_t *)buffer2, size);
- return true;
-
- case SAMPLE_FORMAT_S16:
- pcm_add_16((int16_t *)buffer1, (const int16_t *)buffer2, size / 2);
- return true;
-
- case SAMPLE_FORMAT_S24_P32:
- pcm_add_24((int32_t *)buffer1, (const int32_t *)buffer2, size / 4);
- return true;
-
- case SAMPLE_FORMAT_S32:
- pcm_add_32((int32_t *)buffer1, (const int32_t *)buffer2, size / 4);
- return true;
-
- case SAMPLE_FORMAT_FLOAT:
- pcm_add_float(buffer1, buffer2, size / 4);
- return true;
- }
-
- /* unreachable */
- assert(false);
- return false;
-}
-
-bool
-pcm_mix(void *buffer1, const void *buffer2, size_t size,
- enum sample_format format, float portion1)
-{
- int vol1;
- float s;
-
- /* portion1 is between 0.0 and 1.0 for crossfading, MixRamp uses NaN
- * to signal mixing rather than fading */
- if (isnan(portion1))
- return pcm_add(buffer1, buffer2, size, format);
-
- s = sin(M_PI_2 * portion1);
- s *= s;
-
- vol1 = s * PCM_VOLUME_1 + 0.5;
- vol1 = vol1 > PCM_VOLUME_1 ? PCM_VOLUME_1 : (vol1 < 0 ? 0 : vol1);
-
- return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1 - vol1, format);
-}
diff --git a/src/pcm_utils.h b/src/pcm_utils.h
deleted file mode 100644
index 4ad89657..00000000
--- a/src/pcm_utils.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PCM_UTILS_H
-#define MPD_PCM_UTILS_H
-
-#include <glib.h>
-
-#include <stdint.h>
-
-/**
- * Add a byte count to the specified pointer. This is a utility
- * function to convert a source pointer and a byte count to an "end"
- * pointer for use in loops.
- */
-static inline const void *
-pcm_end_pointer(const void *p, size_t size)
-{
- return (const char *)p + size;
-}
-
-/**
- * Check if the value is within the range of the provided bit size,
- * and caps it if necessary.
- */
-static inline int32_t
-pcm_range(int32_t sample, unsigned bits)
-{
- if (G_UNLIKELY(sample < (-1 << (bits - 1))))
- return -1 << (bits - 1);
- if (G_UNLIKELY(sample >= (1 << (bits - 1))))
- return (1 << (bits - 1)) - 1;
- return sample;
-}
-
-/**
- * Check if the value is within the range of the provided bit size,
- * and caps it if necessary.
- */
-static inline int64_t
-pcm_range_64(int64_t sample, unsigned bits)
-{
- if (G_UNLIKELY(sample < ((int64_t)-1 << (bits - 1))))
- return (int64_t)-1 << (bits - 1);
- if (G_UNLIKELY(sample >= ((int64_t)1 << (bits - 1))))
- return ((int64_t)1 << (bits - 1)) - 1;
- return sample;
-}
-
-G_GNUC_CONST
-static inline int16_t
-pcm_clamp_16(int x)
-{
- static const int32_t MIN_VALUE = G_MININT16;
- static const int32_t MAX_VALUE = G_MAXINT16;
-
- if (G_UNLIKELY(x < MIN_VALUE))
- return MIN_VALUE;
- if (G_UNLIKELY(x > MAX_VALUE))
- return MAX_VALUE;
- return x;
-}
-
-G_GNUC_CONST
-static inline int32_t
-pcm_clamp_24(int x)
-{
- static const int32_t MIN_VALUE = -(1 << 23);
- static const int32_t MAX_VALUE = (1 << 23) - 1;
-
- if (G_UNLIKELY(x < MIN_VALUE))
- return MIN_VALUE;
- if (G_UNLIKELY(x > MAX_VALUE))
- return MAX_VALUE;
- return x;
-}
-
-#endif
diff --git a/src/player_control.c b/src/player_control.c
deleted file mode 100644
index 90f616d7..00000000
--- a/src/player_control.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "player_control.h"
-#include "decoder_control.h"
-#include "path.h"
-#include "log.h"
-#include "tag.h"
-#include "song.h"
-#include "idle.h"
-#include "pcm_volume.h"
-#include "main.h"
-
-#include <assert.h>
-#include <stdio.h>
-#include <math.h>
-
-static void
-pc_enqueue_song_locked(struct player_control *pc, struct song *song);
-
-struct player_control *
-pc_new(unsigned buffer_chunks, unsigned int buffered_before_play)
-{
- struct player_control *pc = g_new0(struct player_control, 1);
-
- pc->buffer_chunks = buffer_chunks;
- pc->buffered_before_play = buffered_before_play;
-
- pc->mutex = g_mutex_new();
- pc->cond = g_cond_new();
-
- pc->command = PLAYER_COMMAND_NONE;
- pc->error = PLAYER_ERROR_NOERROR;
- pc->state = PLAYER_STATE_STOP;
- pc->cross_fade_seconds = 0;
- pc->mixramp_db = 0;
- pc->mixramp_delay_seconds = nanf("");
-
- return pc;
-}
-
-void
-pc_free(struct player_control *pc)
-{
- g_cond_free(pc->cond);
- g_mutex_free(pc->mutex);
- g_free(pc);
-}
-
-void
-player_wait_decoder(struct player_control *pc, struct decoder_control *dc)
-{
- assert(pc != NULL);
- assert(dc != NULL);
- assert(dc->client_cond == pc->cond);
-
- /* during this function, the decoder lock is held, because
- we're waiting for the decoder thread */
- g_cond_wait(pc->cond, dc->mutex);
-}
-
-void
-pc_song_deleted(struct player_control *pc, const struct song *song)
-{
- if (pc->errored_song == song) {
- pc->error = PLAYER_ERROR_NOERROR;
- pc->errored_song = NULL;
- }
-}
-
-static void
-player_command_wait_locked(struct player_control *pc)
-{
- while (pc->command != PLAYER_COMMAND_NONE)
- g_cond_wait(main_cond, pc->mutex);
-}
-
-static void
-player_command_locked(struct player_control *pc, enum player_command cmd)
-{
- assert(pc->command == PLAYER_COMMAND_NONE);
-
- pc->command = cmd;
- player_signal(pc);
- player_command_wait_locked(pc);
-}
-
-static void
-player_command(struct player_control *pc, enum player_command cmd)
-{
- player_lock(pc);
- player_command_locked(pc, cmd);
- player_unlock(pc);
-}
-
-void
-pc_play(struct player_control *pc, struct song *song)
-{
- assert(song != NULL);
-
- player_lock(pc);
-
- if (pc->state != PLAYER_STATE_STOP)
- player_command_locked(pc, PLAYER_COMMAND_STOP);
-
- assert(pc->next_song == NULL);
-
- pc_enqueue_song_locked(pc, song);
-
- assert(pc->next_song == NULL);
-
- player_unlock(pc);
-
- idle_add(IDLE_PLAYER);
-}
-
-void
-pc_cancel(struct player_control *pc)
-{
- player_command(pc, PLAYER_COMMAND_CANCEL);
- assert(pc->next_song == NULL);
-}
-
-void
-pc_stop(struct player_control *pc)
-{
- player_command(pc, PLAYER_COMMAND_CLOSE_AUDIO);
- assert(pc->next_song == NULL);
-
- idle_add(IDLE_PLAYER);
-}
-
-void
-pc_update_audio(struct player_control *pc)
-{
- player_command(pc, PLAYER_COMMAND_UPDATE_AUDIO);
-}
-
-void
-pc_kill(struct player_control *pc)
-{
- assert(pc->thread != NULL);
-
- player_command(pc, PLAYER_COMMAND_EXIT);
- g_thread_join(pc->thread);
- pc->thread = NULL;
-
- idle_add(IDLE_PLAYER);
-}
-
-void
-pc_pause(struct player_control *pc)
-{
- player_lock(pc);
-
- if (pc->state != PLAYER_STATE_STOP) {
- player_command_locked(pc, PLAYER_COMMAND_PAUSE);
- idle_add(IDLE_PLAYER);
- }
-
- player_unlock(pc);
-}
-
-static void
-pc_pause_locked(struct player_control *pc)
-{
- if (pc->state != PLAYER_STATE_STOP) {
- player_command_locked(pc, PLAYER_COMMAND_PAUSE);
- idle_add(IDLE_PLAYER);
- }
-}
-
-void
-pc_set_pause(struct player_control *pc, bool pause_flag)
-{
- player_lock(pc);
-
- switch (pc->state) {
- case PLAYER_STATE_STOP:
- break;
-
- case PLAYER_STATE_PLAY:
- if (pause_flag)
- pc_pause_locked(pc);
- break;
-
- case PLAYER_STATE_PAUSE:
- if (!pause_flag)
- pc_pause_locked(pc);
- break;
- }
-
- player_unlock(pc);
-}
-
-void
-pc_set_border_pause(struct player_control *pc, bool border_pause)
-{
- player_lock(pc);
- pc->border_pause = border_pause;
- player_unlock(pc);
-}
-
-void
-pc_get_status(struct player_control *pc, struct player_status *status)
-{
- player_lock(pc);
- player_command_locked(pc, PLAYER_COMMAND_REFRESH);
-
- status->state = pc->state;
-
- if (pc->state != PLAYER_STATE_STOP) {
- status->bit_rate = pc->bit_rate;
- status->audio_format = pc->audio_format;
- status->total_time = pc->total_time;
- status->elapsed_time = pc->elapsed_time;
- }
-
- player_unlock(pc);
-}
-
-enum player_state
-pc_get_state(struct player_control *pc)
-{
- return pc->state;
-}
-
-void
-pc_clear_error(struct player_control *pc)
-{
- player_lock(pc);
- pc->error = PLAYER_ERROR_NOERROR;
- pc->errored_song = NULL;
- player_unlock(pc);
-}
-
-enum player_error
-pc_get_error(struct player_control *pc)
-{
- return pc->error;
-}
-
-static char *
-pc_errored_song_uri(struct player_control *pc)
-{
- return song_get_uri(pc->errored_song);
-}
-
-char *
-pc_get_error_message(struct player_control *pc)
-{
- char *error;
- char *uri;
-
- switch (pc->error) {
- case PLAYER_ERROR_NOERROR:
- return NULL;
-
- case PLAYER_ERROR_FILENOTFOUND:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("file \"%s\" does not exist or is inaccessible", uri);
- g_free(uri);
- return error;
-
- case PLAYER_ERROR_FILE:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("problems decoding \"%s\"", uri);
- g_free(uri);
- return error;
-
- case PLAYER_ERROR_AUDIO:
- return g_strdup("problems opening audio device");
-
- case PLAYER_ERROR_SYSTEM:
- return g_strdup("system error occurred");
-
- case PLAYER_ERROR_UNKTYPE:
- uri = pc_errored_song_uri(pc);
- error = g_strdup_printf("file type of \"%s\" is unknown", uri);
- g_free(uri);
- return error;
- }
-
- assert(false);
- return NULL;
-}
-
-static void
-pc_enqueue_song_locked(struct player_control *pc, struct song *song)
-{
- assert(song != NULL);
- assert(pc->next_song == NULL);
-
- pc->next_song = song;
- player_command_locked(pc, PLAYER_COMMAND_QUEUE);
-}
-
-void
-pc_enqueue_song(struct player_control *pc, struct song *song)
-{
- assert(song != NULL);
-
- player_lock(pc);
- pc_enqueue_song_locked(pc, song);
- player_unlock(pc);
-}
-
-bool
-pc_seek(struct player_control *pc, struct song *song, float seek_time)
-{
- assert(song != NULL);
-
- player_lock(pc);
- pc->next_song = song;
- pc->seek_where = seek_time;
- player_command_locked(pc, PLAYER_COMMAND_SEEK);
- player_unlock(pc);
-
- assert(pc->next_song == NULL);
-
- idle_add(IDLE_PLAYER);
-
- return true;
-}
-
-float
-pc_get_cross_fade(const struct player_control *pc)
-{
- return pc->cross_fade_seconds;
-}
-
-void
-pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds)
-{
- if (cross_fade_seconds < 0)
- cross_fade_seconds = 0;
- pc->cross_fade_seconds = cross_fade_seconds;
-
- idle_add(IDLE_OPTIONS);
-}
-
-float
-pc_get_mixramp_db(const struct player_control *pc)
-{
- return pc->mixramp_db;
-}
-
-void
-pc_set_mixramp_db(struct player_control *pc, float mixramp_db)
-{
- pc->mixramp_db = mixramp_db;
-
- idle_add(IDLE_OPTIONS);
-}
-
-float
-pc_get_mixramp_delay(const struct player_control *pc)
-{
- return pc->mixramp_delay_seconds;
-}
-
-void
-pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds)
-{
- pc->mixramp_delay_seconds = mixramp_delay_seconds;
-
- idle_add(IDLE_OPTIONS);
-}
-
-double
-pc_get_total_play_time(const struct player_control *pc)
-{
- return pc->total_play_time;
-}
diff --git a/src/player_control.h b/src/player_control.h
deleted file mode 100644
index a77d31ec..00000000
--- a/src/player_control.h
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PLAYER_H
-#define MPD_PLAYER_H
-
-#include "audio_format.h"
-
-#include <glib.h>
-
-#include <stdint.h>
-
-struct decoder_control;
-
-enum player_state {
- PLAYER_STATE_STOP = 0,
- PLAYER_STATE_PAUSE,
- PLAYER_STATE_PLAY
-};
-
-enum player_command {
- PLAYER_COMMAND_NONE = 0,
- PLAYER_COMMAND_EXIT,
- PLAYER_COMMAND_STOP,
- PLAYER_COMMAND_PAUSE,
- PLAYER_COMMAND_SEEK,
- PLAYER_COMMAND_CLOSE_AUDIO,
-
- /**
- * At least one audio_output.enabled flag has been modified;
- * commit those changes to the output threads.
- */
- PLAYER_COMMAND_UPDATE_AUDIO,
-
- /** player_control.next_song has been updated */
- PLAYER_COMMAND_QUEUE,
-
- /**
- * cancel pre-decoding player_control.next_song; if the player
- * has already started playing this song, it will completely
- * stop
- */
- PLAYER_COMMAND_CANCEL,
-
- /**
- * Refresh status information in the #player_control struct,
- * e.g. elapsed_time.
- */
- PLAYER_COMMAND_REFRESH,
-};
-
-enum player_error {
- PLAYER_ERROR_NOERROR = 0,
- PLAYER_ERROR_FILE,
- PLAYER_ERROR_AUDIO,
- PLAYER_ERROR_SYSTEM,
- PLAYER_ERROR_UNKTYPE,
- PLAYER_ERROR_FILENOTFOUND,
-};
-
-struct player_status {
- enum player_state state;
- uint16_t bit_rate;
- struct audio_format audio_format;
- float total_time;
- float elapsed_time;
-};
-
-struct player_control {
- unsigned buffer_chunks;
-
- unsigned int buffered_before_play;
-
- /** the handle of the player thread, or NULL if the player
- thread isn't running */
- GThread *thread;
-
- /**
- * This lock protects #command, #state, #error.
- */
- GMutex *mutex;
-
- /**
- * Trigger this object after you have modified #command.
- */
- GCond *cond;
-
- enum player_command command;
- enum player_state state;
- enum player_error error;
- uint16_t bit_rate;
- struct audio_format audio_format;
- float total_time;
- float elapsed_time;
- struct song *next_song;
- const struct song *errored_song;
- double seek_where;
- float cross_fade_seconds;
- float mixramp_db;
- float mixramp_delay_seconds;
- double total_play_time;
-
- /**
- * If this flag is set, then the player will be auto-paused at
- * the end of the song, before the next song starts to play.
- *
- * This is a copy of the queue's "single" flag most of the
- * time.
- */
- bool border_pause;
-};
-
-struct player_control *
-pc_new(unsigned buffer_chunks, unsigned buffered_before_play);
-
-void
-pc_free(struct player_control *pc);
-
-/**
- * Locks the #player_control object.
- */
-static inline void
-player_lock(struct player_control *pc)
-{
- g_mutex_lock(pc->mutex);
-}
-
-/**
- * Unlocks the #player_control object.
- */
-static inline void
-player_unlock(struct player_control *pc)
-{
- g_mutex_unlock(pc->mutex);
-}
-
-/**
- * Waits for a signal on the #player_control object. This function is
- * only valid in the player thread. The object must be locked prior
- * to calling this function.
- */
-static inline void
-player_wait(struct player_control *pc)
-{
- g_cond_wait(pc->cond, pc->mutex);
-}
-
-/**
- * Waits for a signal on the #player_control object. This function is
- * only valid in the player thread. The #decoder_control object must
- * be locked prior to calling this function.
- *
- * Note the small difference to the player_wait() function!
- */
-void
-player_wait_decoder(struct player_control *pc, struct decoder_control *dc);
-
-/**
- * Signals the #player_control object. The object should be locked
- * prior to calling this function.
- */
-static inline void
-player_signal(struct player_control *pc)
-{
- g_cond_signal(pc->cond);
-}
-
-/**
- * Signals the #player_control object. The object is temporarily
- * locked by this function.
- */
-static inline void
-player_lock_signal(struct player_control *pc)
-{
- player_lock(pc);
- player_signal(pc);
- player_unlock(pc);
-}
-
-/**
- * Call this function when the specified song pointer is about to be
- * invalidated. This makes sure that player_control.errored_song does
- * not point to an invalid pointer.
- */
-void
-pc_song_deleted(struct player_control *pc, const struct song *song);
-
-void
-pc_play(struct player_control *pc, struct song *song);
-
-/**
- * see PLAYER_COMMAND_CANCEL
- */
-void
-pc_cancel(struct player_control *pc);
-
-void
-pc_set_pause(struct player_control *pc, bool pause_flag);
-
-void
-pc_pause(struct player_control *pc);
-
-/**
- * Set the player's #border_pause flag.
- */
-void
-pc_set_border_pause(struct player_control *pc, bool border_pause);
-
-void
-pc_kill(struct player_control *pc);
-
-void
-pc_get_status(struct player_control *pc, struct player_status *status);
-
-enum player_state
-pc_get_state(struct player_control *pc);
-
-void
-pc_clear_error(struct player_control *pc);
-
-/**
- * Returns the human-readable message describing the last error during
- * playback, NULL if no error occurred. The caller has to free the
- * returned string.
- */
-char *
-pc_get_error_message(struct player_control *pc);
-
-enum player_error
-pc_get_error(struct player_control *pc);
-
-void
-pc_stop(struct player_control *pc);
-
-void
-pc_update_audio(struct player_control *pc);
-
-void
-pc_enqueue_song(struct player_control *pc, struct song *song);
-
-/**
- * Makes the player thread seek the specified song to a position.
- *
- * @return true on success, false on failure (e.g. if MPD isn't
- * playing currently)
- */
-bool
-pc_seek(struct player_control *pc, struct song *song, float seek_time);
-
-void
-pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds);
-
-float
-pc_get_cross_fade(const struct player_control *pc);
-
-void
-pc_set_mixramp_db(struct player_control *pc, float mixramp_db);
-
-float
-pc_get_mixramp_db(const struct player_control *pc);
-
-void
-pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds);
-
-float
-pc_get_mixramp_delay(const struct player_control *pc);
-
-double
-pc_get_total_play_time(const struct player_control *pc);
-
-#endif
diff --git a/src/playlist.c b/src/playlist.c
deleted file mode 100644
index dc6d8c34..00000000
--- a/src/playlist.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "playlist_internal.h"
-#include "playlist_save.h"
-#include "player_control.h"
-#include "command.h"
-#include "tag.h"
-#include "song.h"
-#include "conf.h"
-#include "stored_playlist.h"
-#include "idle.h"
-
-#include <glib.h>
-
-#include <assert.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "playlist"
-
-void
-playlist_increment_version_all(struct playlist *playlist)
-{
- queue_modify_all(&playlist->queue);
- idle_add(IDLE_PLAYLIST);
-}
-
-void
-playlist_tag_changed(struct playlist *playlist)
-{
- if (!playlist->playing)
- return;
-
- assert(playlist->current >= 0);
-
- queue_modify(&playlist->queue, playlist->current);
- idle_add(IDLE_PLAYLIST);
-}
-
-void
-playlist_init(struct playlist *playlist)
-{
- queue_init(&playlist->queue,
- config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
- DEFAULT_PLAYLIST_MAX_LENGTH));
-
- playlist->queued = -1;
- playlist->current = -1;
-}
-
-void
-playlist_finish(struct playlist *playlist)
-{
- queue_finish(&playlist->queue);
-}
-
-/**
- * Queue a song, addressed by its order number.
- */
-static void
-playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
- unsigned order)
-{
- struct song *song;
- char *uri;
-
- assert(queue_valid_order(&playlist->queue, order));
-
- playlist->queued = order;
-
- song = queue_get_order(&playlist->queue, order);
- uri = song_get_uri(song);
- g_debug("queue song %i:\"%s\"", playlist->queued, uri);
- g_free(uri);
-
- pc_enqueue_song(pc, song);
-}
-
-/**
- * Called if the player thread has started playing the "queued" song.
- */
-static void
-playlist_song_started(struct playlist *playlist, struct player_control *pc)
-{
- assert(pc->next_song == NULL);
- assert(playlist->queued >= -1);
-
- /* queued song has started: copy queued to current,
- and notify the clients */
-
- int current = playlist->current;
- playlist->current = playlist->queued;
- playlist->queued = -1;
-
- if(playlist->queue.consume)
- playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
-
- idle_add(IDLE_PLAYER);
-}
-
-const struct song *
-playlist_get_queued_song(struct playlist *playlist)
-{
- if (!playlist->playing || playlist->queued < 0)
- return NULL;
-
- return queue_get_order(&playlist->queue, playlist->queued);
-}
-
-void
-playlist_update_queued_song(struct playlist *playlist,
- struct player_control *pc,
- const struct song *prev)
-{
- int next_order;
- const struct song *next_song;
-
- if (!playlist->playing)
- return;
-
- assert(!queue_is_empty(&playlist->queue));
- assert((playlist->queued < 0) == (prev == NULL));
-
- next_order = playlist->current >= 0
- ? queue_next_order(&playlist->queue, playlist->current)
- : 0;
-
- if (next_order == 0 && playlist->queue.random &&
- !playlist->queue.single) {
- /* shuffle the song order again, so we get a different
- order each time the playlist is played
- completely */
- unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
-
- queue_shuffle_order(&playlist->queue);
-
- /* make sure that the playlist->current still points to
- the current song, after the song order has been
- shuffled */
- playlist->current =
- queue_position_to_order(&playlist->queue,
- current_position);
- }
-
- if (next_order >= 0)
- next_song = queue_get_order(&playlist->queue, next_order);
- else
- next_song = NULL;
-
- if (prev != NULL && next_song != prev) {
- /* clear the currently queued song */
- pc_cancel(pc);
- playlist->queued = -1;
- }
-
- if (next_order >= 0) {
- if (next_song != prev)
- playlist_queue_song_order(playlist, pc, next_order);
- else
- playlist->queued = next_order;
- }
-}
-
-void
-playlist_play_order(struct playlist *playlist, struct player_control *pc,
- int orderNum)
-{
- struct song *song;
- char *uri;
-
- playlist->playing = true;
- playlist->queued = -1;
-
- song = queue_get_order(&playlist->queue, orderNum);
-
- uri = song_get_uri(song);
- g_debug("play %i:\"%s\"", orderNum, uri);
- g_free(uri);
-
- pc_play(pc, song);
- playlist->current = orderNum;
-}
-
-static void
-playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
-
-/**
- * This is the "PLAYLIST" event handler. It is invoked by the player
- * thread whenever it requests a new queued song, or when it exits.
- */
-void
-playlist_sync(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- /* this event has reached us out of sync: we aren't
- playing anymore; ignore the event */
- return;
-
- player_lock(pc);
- enum player_state pc_state = pc_get_state(pc);
- const struct song *pc_next_song = pc->next_song;
- player_unlock(pc);
-
- if (pc_state == PLAYER_STATE_STOP)
- /* the player thread has stopped: check if playback
- should be restarted with the next song. That can
- happen if the playlist isn't filling the queue fast
- enough */
- playlist_resume_playback(playlist, pc);
- else {
- /* check if the player thread has already started
- playing the queued song */
- if (pc_next_song == NULL && playlist->queued != -1)
- playlist_song_started(playlist, pc);
-
- player_lock(pc);
- pc_next_song = pc->next_song;
- player_unlock(pc);
-
- /* make sure the queued song is always set (if
- possible) */
- if (pc_next_song == NULL && playlist->queued < 0)
- playlist_update_queued_song(playlist, pc, NULL);
- }
-}
-
-/**
- * The player has stopped for some reason. Check the error, and
- * decide whether to re-start playback
- */
-static void
-playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
-{
- enum player_error error;
-
- assert(playlist->playing);
- assert(pc_get_state(pc) == PLAYER_STATE_STOP);
-
- error = pc_get_error(pc);
- if (error == PLAYER_ERROR_NOERROR)
- playlist->error_count = 0;
- else
- ++playlist->error_count;
-
- if ((playlist->stop_on_error && error != PLAYER_ERROR_NOERROR) ||
- error == PLAYER_ERROR_AUDIO || error == PLAYER_ERROR_SYSTEM ||
- playlist->error_count >= queue_length(&playlist->queue))
- /* too many errors, or critical error: stop
- playback */
- playlist_stop(playlist, pc);
- else
- /* continue playback at the next song */
- playlist_next(playlist, pc);
-}
-
-bool
-playlist_get_repeat(const struct playlist *playlist)
-{
- return playlist->queue.repeat;
-}
-
-bool
-playlist_get_random(const struct playlist *playlist)
-{
- return playlist->queue.random;
-}
-
-bool
-playlist_get_single(const struct playlist *playlist)
-{
- return playlist->queue.single;
-}
-
-bool
-playlist_get_consume(const struct playlist *playlist)
-{
- return playlist->queue.consume;
-}
-
-void
-playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- if (status == playlist->queue.repeat)
- return;
-
- struct queue *queue = &playlist->queue;
-
- queue->repeat = status;
-
- pc_set_border_pause(pc, queue->single && !queue->repeat);
-
- /* if the last song is currently being played, the "next song"
- might change when repeat mode is toggled */
- playlist_update_queued_song(playlist, pc,
- playlist_get_queued_song(playlist));
-
- idle_add(IDLE_OPTIONS);
-}
-
-static void
-playlist_order(struct playlist *playlist)
-{
- if (playlist->current >= 0)
- /* update playlist.current, order==position now */
- playlist->current = queue_order_to_position(&playlist->queue,
- playlist->current);
-
- queue_restore_order(&playlist->queue);
-}
-
-void
-playlist_set_single(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- if (status == playlist->queue.single)
- return;
-
- struct queue *queue = &playlist->queue;
-
- queue->single = status;
-
- pc_set_border_pause(pc, queue->single && !queue->repeat);
-
- /* if the last song is currently being played, the "next song"
- might change when single mode is toggled */
- playlist_update_queued_song(playlist, pc,
- playlist_get_queued_song(playlist));
-
- idle_add(IDLE_OPTIONS);
-}
-
-void
-playlist_set_consume(struct playlist *playlist, bool status)
-{
- if (status == playlist->queue.consume)
- return;
-
- playlist->queue.consume = status;
- idle_add(IDLE_OPTIONS);
-}
-
-void
-playlist_set_random(struct playlist *playlist, struct player_control *pc,
- bool status)
-{
- const struct song *queued;
-
- if (status == playlist->queue.random)
- return;
-
- queued = playlist_get_queued_song(playlist);
-
- playlist->queue.random = status;
-
- if (playlist->queue.random) {
- /* shuffle the queue order, but preserve
- playlist->current */
-
- int current_position =
- playlist->playing && playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
- : -1;
-
- queue_shuffle_order(&playlist->queue);
-
- if (current_position >= 0) {
- /* make sure the current song is the first in
- the order list, so the whole rest of the
- playlist is played after that */
- unsigned current_order =
- queue_position_to_order(&playlist->queue,
- current_position);
- queue_swap_order(&playlist->queue, 0, current_order);
- playlist->current = 0;
- } else
- playlist->current = -1;
- } else
- playlist_order(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- idle_add(IDLE_OPTIONS);
-}
-
-int
-playlist_get_current_song(const struct playlist *playlist)
-{
- if (playlist->current >= 0)
- return queue_order_to_position(&playlist->queue,
- playlist->current);
-
- return -1;
-}
-
-int
-playlist_get_next_song(const struct playlist *playlist)
-{
- if (playlist->current >= 0)
- {
- if (playlist->queue.single == 1 && playlist->queue.repeat == 1)
- return queue_order_to_position(&playlist->queue,
- playlist->current);
- else if (playlist->current + 1 < (int)queue_length(&playlist->queue))
- return queue_order_to_position(&playlist->queue,
- playlist->current + 1);
- else if (playlist->queue.repeat == 1)
- return queue_order_to_position(&playlist->queue, 0);
- }
-
- return -1;
-}
-
-unsigned long
-playlist_get_version(const struct playlist *playlist)
-{
- return playlist->queue.version;
-}
-
-int
-playlist_get_length(const struct playlist *playlist)
-{
- return queue_length(&playlist->queue);
-}
-
-unsigned
-playlist_get_song_id(const struct playlist *playlist, unsigned song)
-{
- return queue_position_to_id(&playlist->queue, song);
-}
diff --git a/src/playlist.h b/src/playlist.h
deleted file mode 100644
index a21bdf24..00000000
--- a/src/playlist.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PLAYLIST_H
-#define MPD_PLAYLIST_H
-
-#include "queue.h"
-#include "playlist_error.h"
-
-#include <stdbool.h>
-
-struct player_control;
-
-struct playlist {
- /**
- * The song queue - it contains the "real" playlist.
- */
- struct queue queue;
-
- /**
- * This value is true if the player is currently playing (or
- * should be playing).
- */
- bool playing;
-
- /**
- * If true, then any error is fatal; if false, MPD will
- * attempt to play the next song on non-fatal errors. During
- * seeking, this flag is set.
- */
- bool stop_on_error;
-
- /**
- * Number of errors since playback was started. If this
- * number exceeds the length of the playlist, MPD gives up,
- * because all songs have been tried.
- */
- unsigned error_count;
-
- /**
- * The "current song pointer". This is the song which is
- * played when we get the "play" command. It is also the song
- * which is currently being played.
- */
- int current;
-
- /**
- * The "next" song to be played, when the current one
- * finishes. The decoder thread may start decoding and
- * buffering it, while the "current" song is still playing.
- *
- * This variable is only valid if #playing is true.
- */
- int queued;
-};
-
-/** the global playlist object */
-extern struct playlist g_playlist;
-
-void
-playlist_global_init(void);
-
-void
-playlist_global_finish(void);
-
-void
-playlist_init(struct playlist *playlist);
-
-void
-playlist_finish(struct playlist *playlist);
-
-void
-playlist_tag_changed(struct playlist *playlist);
-
-/**
- * Returns the "queue" object of the global playlist instance.
- */
-static inline const struct queue *
-playlist_get_queue(const struct playlist *playlist)
-{
- return &playlist->queue;
-}
-
-void
-playlist_clear(struct playlist *playlist, struct player_control *pc);
-
-/**
- * Appends a local file (outside the music database) to the playlist.
- *
- * Note: the caller is responsible for checking permissions.
- */
-enum playlist_result
-playlist_append_file(struct playlist *playlist, struct player_control *pc,
- const char *path_fs, unsigned *added_id);
-
-enum playlist_result
-playlist_append_uri(struct playlist *playlist, struct player_control *pc,
- const char *file, unsigned *added_id);
-
-enum playlist_result
-playlist_append_song(struct playlist *playlist, struct player_control *pc,
- struct song *song, unsigned *added_id);
-
-enum playlist_result
-playlist_delete(struct playlist *playlist, struct player_control *pc,
- unsigned song);
-
-/**
- * Deletes a range of songs from the playlist.
- *
- * @param start the position of the first song to delete
- * @param end the position after the last song to delete
- */
-enum playlist_result
-playlist_delete_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end);
-
-enum playlist_result
-playlist_delete_id(struct playlist *playlist, struct player_control *pc,
- unsigned song);
-
-void
-playlist_stop(struct playlist *playlist, struct player_control *pc);
-
-enum playlist_result
-playlist_play(struct playlist *playlist, struct player_control *pc,
- int song);
-
-enum playlist_result
-playlist_play_id(struct playlist *playlist, struct player_control *pc,
- int song);
-
-void
-playlist_next(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_sync(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_previous(struct playlist *playlist, struct player_control *pc);
-
-void
-playlist_shuffle(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end);
-
-void
-playlist_delete_song(struct playlist *playlist, struct player_control *pc,
- const struct song *song);
-
-enum playlist_result
-playlist_move_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end, int to);
-
-enum playlist_result
-playlist_move_id(struct playlist *playlist, struct player_control *pc,
- unsigned id, int to);
-
-enum playlist_result
-playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
- unsigned song1, unsigned song2);
-
-enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
- unsigned id1, unsigned id2);
-
-enum playlist_result
-playlist_set_priority(struct playlist *playlist, struct player_control *pc,
- unsigned start_position, unsigned end_position,
- uint8_t priority);
-
-enum playlist_result
-playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
- unsigned song_id, uint8_t priority);
-
-bool
-playlist_get_repeat(const struct playlist *playlist);
-
-void
-playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_random(const struct playlist *playlist);
-
-void
-playlist_set_random(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_single(const struct playlist *playlist);
-
-void
-playlist_set_single(struct playlist *playlist, struct player_control *pc,
- bool status);
-
-bool
-playlist_get_consume(const struct playlist *playlist);
-
-void
-playlist_set_consume(struct playlist *playlist, bool status);
-
-int
-playlist_get_current_song(const struct playlist *playlist);
-
-int
-playlist_get_next_song(const struct playlist *playlist);
-
-unsigned
-playlist_get_song_id(const struct playlist *playlist, unsigned song);
-
-int
-playlist_get_length(const struct playlist *playlist);
-
-unsigned long
-playlist_get_version(const struct playlist *playlist);
-
-enum playlist_result
-playlist_seek_song(struct playlist *playlist, struct player_control *pc,
- unsigned song, float seek_time);
-
-enum playlist_result
-playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
- unsigned id, float seek_time);
-
-/**
- * Seek within the current song. Fails if MPD is not currently
- * playing.
- *
- * @param time the time in seconds
- * @param relative if true, then the specified time is relative to the
- * current position
- */
-enum playlist_result
-playlist_seek_current(struct playlist *playlist, struct player_control *pc,
- float seek_time, bool relative);
-
-void
-playlist_increment_version_all(struct playlist *playlist);
-
-#endif
diff --git a/src/playlist/asx_playlist_plugin.c b/src/playlist/AsxPlaylistPlugin.cxx
index 29868785..25319ca6 100644
--- a/src/playlist/asx_playlist_plugin.c
+++ b/src/playlist/AsxPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "playlist/asx_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "AsxPlaylistPlugin.hxx"
+#include "MemoryPlaylistProvider.hxx"
#include "input_stream.h"
#include "song.h"
#include "tag.h"
@@ -35,12 +35,12 @@
/**
* This is the state object for the GLib XML parser.
*/
-struct asx_parser {
+struct AsxParser {
/**
* The list of songs (in reverse order because that's faster
* while adding).
*/
- GSList *songs;
+ std::forward_list<SongPointer> songs;
/**
* The current position in the XML file.
@@ -61,6 +61,10 @@ struct asx_parser {
* element.
*/
struct song *song;
+
+ AsxParser()
+ :state(ROOT) {}
+
};
static const gchar *
@@ -81,19 +85,19 @@ asx_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar **attribute_values,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct asx_parser *parser = user_data;
+ AsxParser *parser = (AsxParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case AsxParser::ROOT:
if (g_ascii_strcasecmp(element_name, "entry") == 0) {
- parser->state = ENTRY;
+ parser->state = AsxParser::ENTRY;
parser->song = song_remote_new("asx:");
parser->tag = TAG_NUM_OF_ITEM_TYPES;
}
break;
- case ENTRY:
+ case AsxParser::ENTRY:
if (g_ascii_strcasecmp(element_name, "ref") == 0) {
const gchar *href = get_attribute(attribute_names,
attribute_values,
@@ -130,21 +134,20 @@ asx_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct asx_parser *parser = user_data;
+ AsxParser *parser = (AsxParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case AsxParser::ROOT:
break;
- case ENTRY:
+ case AsxParser::ENTRY:
if (g_ascii_strcasecmp(element_name, "entry") == 0) {
if (strcmp(parser->song->uri, "asx:") != 0)
- parser->songs = g_slist_prepend(parser->songs,
- parser->song);
+ parser->songs.emplace_front(parser->song);
else
song_free(parser->song);
- parser->state = ROOT;
+ parser->state = AsxParser::ROOT;
} else
parser->tag = TAG_NUM_OF_ITEM_TYPES;
@@ -157,13 +160,13 @@ asx_text(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *text, gsize text_len,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct asx_parser *parser = user_data;
+ AsxParser *parser = (AsxParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case AsxParser::ROOT:
break;
- case ENTRY:
+ case AsxParser::ENTRY:
if (parser->tag != TAG_NUM_OF_ITEM_TYPES) {
if (parser->song->tag == NULL)
parser->song->tag = tag_new();
@@ -176,29 +179,20 @@ asx_text(G_GNUC_UNUSED GMarkupParseContext *context,
}
static const GMarkupParser asx_parser = {
- .start_element = asx_start_element,
- .end_element = asx_end_element,
- .text = asx_text,
+ asx_start_element,
+ asx_end_element,
+ asx_text,
+ nullptr,
+ nullptr,
};
static void
-song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct song *song = data;
-
- song_free(song);
-}
-
-static void
asx_parser_destroy(gpointer data)
{
- struct asx_parser *parser = data;
+ AsxParser *parser = (AsxParser *)data;
- if (parser->state >= ENTRY)
+ if (parser->state >= AsxParser::ENTRY)
song_free(parser->song);
-
- g_slist_foreach(parser->songs, song_free_callback, NULL);
- g_slist_free(parser->songs);
}
/*
@@ -206,20 +200,10 @@ asx_parser_destroy(gpointer data)
*
*/
-struct asx_playlist {
- struct playlist_provider base;
-
- GSList *songs;
-};
-
static struct playlist_provider *
asx_open_stream(struct input_stream *is)
{
- struct asx_parser parser = {
- .songs = NULL,
- .state = ROOT,
- };
- struct asx_playlist *playlist;
+ AsxParser parser;
GMarkupParseContext *context;
char buffer[1024];
size_t nbytes;
@@ -264,41 +248,13 @@ asx_open_stream(struct input_stream *is)
return NULL;
}
- /* create a #asx_playlist object from the parsed song list */
-
- playlist = g_new(struct asx_playlist, 1);
- playlist_provider_init(&playlist->base, &asx_playlist_plugin);
- playlist->songs = g_slist_reverse(parser.songs);
- parser.songs = NULL;
+ parser.songs.reverse();
+ MemoryPlaylistProvider *playlist =
+ new MemoryPlaylistProvider(std::move(parser.songs));
g_markup_parse_context_free(context);
- return &playlist->base;
-}
-
-static void
-asx_close(struct playlist_provider *_playlist)
-{
- struct asx_playlist *playlist = (struct asx_playlist *)_playlist;
-
- g_slist_foreach(playlist->songs, song_free_callback, NULL);
- g_slist_free(playlist->songs);
- g_free(playlist);
-}
-
-static struct song *
-asx_read(struct playlist_provider *_playlist)
-{
- struct asx_playlist *playlist = (struct asx_playlist *)_playlist;
- struct song *song;
-
- if (playlist->songs == NULL)
- return NULL;
-
- song = playlist->songs->data;
- playlist->songs = g_slist_remove(playlist->songs, song);
-
- return song;
+ return playlist;
}
static const char *const asx_suffixes[] = {
@@ -312,12 +268,16 @@ static const char *const asx_mime_types[] = {
};
const struct playlist_plugin asx_playlist_plugin = {
- .name = "asx",
+ "asx",
- .open_stream = asx_open_stream,
- .close = asx_close,
- .read = asx_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ asx_open_stream,
+ nullptr,
+ nullptr,
- .suffixes = asx_suffixes,
- .mime_types = asx_mime_types,
+ nullptr,
+ asx_suffixes,
+ asx_mime_types,
};
diff --git a/src/playlist/asx_playlist_plugin.h b/src/playlist/AsxPlaylistPlugin.hxx
index 6c01c120..240c1824 100644
--- a/src/playlist/asx_playlist_plugin.h
+++ b/src/playlist/AsxPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_ASX_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_ASX_PLAYLIST_PLUGIN_H
+#ifndef MPD_ASX_PLAYLIST_PLUGIN_HXX
+#define MPD_ASX_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin asx_playlist_plugin;
diff --git a/src/playlist/cue_playlist_plugin.c b/src/playlist/CuePlaylistPlugin.cxx
index b85de77d..07eb5e24 100644
--- a/src/playlist/cue_playlist_plugin.c
+++ b/src/playlist/CuePlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,16 @@
*/
#include "config.h"
-#include "playlist/cue_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "CuePlaylistPlugin.hxx"
+#include "PlaylistPlugin.hxx"
#include "tag.h"
#include "song.h"
-#include "cue/cue_parser.h"
#include "input_stream.h"
+
+extern "C" {
#include "text_input_stream.h"
+#include "cue/cue_parser.h"
+}
#include <glib.h>
#include <assert.h>
@@ -33,7 +36,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "cue"
-struct cue_playlist {
+struct CuePlaylist {
struct playlist_provider base;
struct input_stream *is;
@@ -44,21 +47,20 @@ struct cue_playlist {
static struct playlist_provider *
cue_playlist_open_stream(struct input_stream *is)
{
- struct cue_playlist *playlist = g_new(struct cue_playlist, 1);
+ CuePlaylist *playlist = g_new(CuePlaylist, 1);
playlist_provider_init(&playlist->base, &cue_playlist_plugin);
playlist->is = is;
playlist->tis = text_input_stream_new(is);
playlist->parser = cue_parser_new();
-
return &playlist->base;
}
static void
cue_playlist_close(struct playlist_provider *_playlist)
{
- struct cue_playlist *playlist = (struct cue_playlist *)_playlist;
+ CuePlaylist *playlist = (CuePlaylist *)_playlist;
cue_parser_free(playlist->parser);
text_input_stream_free(playlist->tis);
@@ -68,7 +70,7 @@ cue_playlist_close(struct playlist_provider *_playlist)
static struct song *
cue_playlist_read(struct playlist_provider *_playlist)
{
- struct cue_playlist *playlist = (struct cue_playlist *)_playlist;
+ CuePlaylist *playlist = (CuePlaylist *)_playlist;
struct song *song = cue_parser_get(playlist->parser);
if (song != NULL)
@@ -97,12 +99,16 @@ static const char *const cue_playlist_mime_types[] = {
};
const struct playlist_plugin cue_playlist_plugin = {
- .name = "cue",
+ "cue",
- .open_stream = cue_playlist_open_stream,
- .close = cue_playlist_close,
- .read = cue_playlist_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ cue_playlist_open_stream,
+ cue_playlist_close,
+ cue_playlist_read,
- .suffixes = cue_playlist_suffixes,
- .mime_types = cue_playlist_mime_types,
+ nullptr,
+ cue_playlist_suffixes,
+ cue_playlist_mime_types,
};
diff --git a/src/playlist/cue_playlist_plugin.h b/src/playlist/CuePlaylistPlugin.hxx
index c02e2235..cf5e3a8f 100644
--- a/src/playlist/cue_playlist_plugin.h
+++ b/src/playlist/CuePlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_CUE_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_CUE_PLAYLIST_PLUGIN_H
+#ifndef MPD_CUE_PLAYLIST_PLUGIN_HXX
+#define MPD_CUE_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin cue_playlist_plugin;
diff --git a/src/playlist/DespotifyPlaylistPlugin.cxx b/src/playlist/DespotifyPlaylistPlugin.cxx
new file mode 100644
index 00000000..25f12785
--- /dev/null
+++ b/src/playlist/DespotifyPlaylistPlugin.cxx
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DespotifyPlaylistPlugin.hxx"
+#include "DespotifyUtils.hxx"
+#include "MemoryPlaylistProvider.hxx"
+#include "tag.h"
+#include "song.h"
+
+extern "C" {
+#include <despotify.h>
+}
+
+#include <glib.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+static void
+add_song(std::forward_list<SongPointer> &songs, struct ds_track *track)
+{
+ const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
+ struct song *song;
+ char uri[128];
+ char *ds_uri;
+
+ /* Create a spt://... URI for MPD */
+ g_snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
+ ds_uri = uri + strlen(dsp_scheme) + 3;
+
+ if (despotify_track_to_uri(track, ds_uri) != ds_uri) {
+ /* Should never really fail, but let's be sure */
+ g_debug("Can't add track %s\n", track->title);
+ return;
+ }
+
+ song = song_remote_new(uri);
+ song->tag = mpd_despotify_tag_from_track(track);
+
+ songs.emplace_front(song);
+}
+
+static bool
+parse_track(struct despotify_session *session,
+ std::forward_list<SongPointer> &songs,
+ struct ds_link *link)
+{
+ struct ds_track *track = despotify_link_get_track(session, link);
+ if (track == nullptr)
+ return false;
+
+ add_song(songs, track);
+ return true;
+}
+
+static bool
+parse_playlist(struct despotify_session *session,
+ std::forward_list<SongPointer> &songs,
+ struct ds_link *link)
+{
+ ds_playlist *playlist = despotify_link_get_playlist(session, link);
+ if (playlist == nullptr)
+ return false;
+
+ for (ds_track *track = playlist->tracks; track != nullptr;
+ track = track->next)
+ add_song(songs, track);
+
+ return true;
+}
+
+static struct playlist_provider *
+despotify_playlist_open_uri(const char *url,
+ gcc_unused Mutex &mutex, gcc_unused Cond &cond)
+{
+ despotify_session *session = mpd_despotify_get_session();
+ if (session == nullptr)
+ return nullptr;
+
+ /* Get link without spt:// */
+ ds_link *link =
+ despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
+ if (link == nullptr) {
+ g_debug("Can't find %s\n", url);
+ return nullptr;
+ }
+
+ std::forward_list<SongPointer> songs;
+
+ bool parse_result;
+ switch (link->type) {
+ case LINK_TYPE_TRACK:
+ parse_result = parse_track(session, songs, link);
+ break;
+ case LINK_TYPE_PLAYLIST:
+ parse_result = parse_playlist(session, songs, link);
+ break;
+ default:
+ parse_result = false;
+ break;
+ }
+
+ despotify_free_link(link);
+ if (!parse_result)
+ return nullptr;
+
+ songs.reverse();
+ return new MemoryPlaylistProvider(std::move(songs));
+}
+
+static const char *const despotify_schemes[] = {
+ "spt",
+ nullptr
+};
+
+const struct playlist_plugin despotify_playlist_plugin = {
+ "despotify",
+
+ nullptr,
+ nullptr,
+ despotify_playlist_open_uri,
+ nullptr,
+ nullptr,
+ nullptr,
+
+ despotify_schemes,
+ nullptr,
+ nullptr,
+};
diff --git a/src/playlist/despotify_playlist_plugin.h b/src/playlist/DespotifyPlaylistPlugin.hxx
index f8ee20de..c1e5b7f3 100644
--- a/src/playlist/despotify_playlist_plugin.h
+++ b/src/playlist/DespotifyPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Music Player Daemon Project
+ * Copyright (C) 2011-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
+#ifndef MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
+#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin despotify_playlist_plugin;
diff --git a/src/playlist/embcue_playlist_plugin.c b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
index 6d9a957f..04cb12ec 100644
--- a/src/playlist/embcue_playlist_plugin.c
+++ b/src/playlist/EmbeddedCuePlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,15 +24,18 @@
*/
#include "config.h"
-#include "playlist/embcue_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "EmbeddedCuePlaylistPlugin.hxx"
+#include "PlaylistPlugin.hxx"
#include "tag.h"
#include "tag_handler.h"
-#include "tag_file.h"
+#include "song.h"
+#include "TagFile.hxx"
+
+extern "C" {
#include "tag_ape.h"
#include "tag_id3.h"
-#include "song.h"
#include "cue/cue_parser.h"
+}
#include <glib.h>
#include <assert.h>
@@ -67,7 +70,7 @@ struct embcue_playlist {
static void
embcue_tag_pair(const char *name, const char *value, void *ctx)
{
- struct embcue_playlist *playlist = ctx;
+ struct embcue_playlist *playlist = (struct embcue_playlist *)ctx;
if (playlist->cuesheet == NULL &&
g_ascii_strcasecmp(name, "cuesheet") == 0)
@@ -75,13 +78,15 @@ embcue_tag_pair(const char *name, const char *value, void *ctx)
}
static const struct tag_handler embcue_tag_handler = {
- .pair = embcue_tag_pair,
+ nullptr,
+ nullptr,
+ embcue_tag_pair,
};
static struct playlist_provider *
embcue_playlist_open_uri(const char *uri,
- G_GNUC_UNUSED GMutex *mutex,
- G_GNUC_UNUSED GCond *cond)
+ gcc_unused Mutex &mutex,
+ gcc_unused Cond &cond)
{
if (!g_path_is_absolute(uri))
/* only local files supported */
@@ -170,12 +175,16 @@ static const char *const embcue_playlist_suffixes[] = {
};
const struct playlist_plugin embcue_playlist_plugin = {
- .name = "cue",
-
- .open_uri = embcue_playlist_open_uri,
- .close = embcue_playlist_close,
- .read = embcue_playlist_read,
-
- .suffixes = embcue_playlist_suffixes,
- .mime_types = NULL,
+ "cue",
+
+ nullptr,
+ nullptr,
+ embcue_playlist_open_uri,
+ nullptr,
+ embcue_playlist_close,
+ embcue_playlist_read,
+
+ embcue_playlist_suffixes,
+ nullptr,
+ nullptr,
};
diff --git a/src/playlist/embcue_playlist_plugin.h b/src/playlist/EmbeddedCuePlaylistPlugin.hxx
index c5f21b27..e306730f 100644
--- a/src/playlist/embcue_playlist_plugin.h
+++ b/src/playlist/EmbeddedCuePlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_EMBCUE_PLAYLIST_PLUGIN_H
+#ifndef MPD_EMBCUE_PLAYLIST_PLUGIN_HXX
+#define MPD_EMBCUE_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin embcue_playlist_plugin;
diff --git a/src/playlist/extm3u_playlist_plugin.c b/src/playlist/ExtM3uPlaylistPlugin.cxx
index 19be8d1c..ce026dab 100644
--- a/src/playlist/extm3u_playlist_plugin.c
+++ b/src/playlist/ExtM3uPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,22 @@
*/
#include "config.h"
-#include "playlist/extm3u_playlist_plugin.h"
-#include "playlist_plugin.h"
-#include "text_input_stream.h"
-#include "uri.h"
+#include "ExtM3uPlaylistPlugin.hxx"
+#include "PlaylistPlugin.hxx"
#include "song.h"
#include "tag.h"
#include "string_util.h"
+extern "C" {
+#include "text_input_stream.h"
+}
+
#include <glib.h>
#include <string.h>
#include <stdlib.h>
-struct extm3u_playlist {
+struct ExtM3uPlaylist {
struct playlist_provider base;
struct text_input_stream *tis;
@@ -40,13 +42,10 @@ struct extm3u_playlist {
static struct playlist_provider *
extm3u_open_stream(struct input_stream *is)
{
- struct extm3u_playlist *playlist;
- const char *line;
-
- playlist = g_new(struct extm3u_playlist, 1);
+ ExtM3uPlaylist *playlist = g_new(ExtM3uPlaylist, 1);
playlist->tis = text_input_stream_new(is);
- line = text_input_stream_read(playlist->tis);
+ const char *line = text_input_stream_read(playlist->tis);
if (line == NULL || strcmp(line, "#EXTM3U") != 0) {
/* no EXTM3U header: fall back to the plain m3u
plugin */
@@ -62,7 +61,7 @@ extm3u_open_stream(struct input_stream *is)
static void
extm3u_close(struct playlist_provider *_playlist)
{
- struct extm3u_playlist *playlist = (struct extm3u_playlist *)_playlist;
+ ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_playlist;
text_input_stream_free(playlist->tis);
g_free(playlist);
@@ -111,7 +110,7 @@ extm3u_parse_tag(const char *line)
static struct song *
extm3u_read(struct playlist_provider *_playlist)
{
- struct extm3u_playlist *playlist = (struct extm3u_playlist *)_playlist;
+ ExtM3uPlaylist *playlist = (ExtM3uPlaylist *)_playlist;
struct tag *tag = NULL;
const char *line;
struct song *song;
@@ -151,12 +150,16 @@ static const char *const extm3u_mime_types[] = {
};
const struct playlist_plugin extm3u_playlist_plugin = {
- .name = "extm3u",
-
- .open_stream = extm3u_open_stream,
- .close = extm3u_close,
- .read = extm3u_read,
-
- .suffixes = extm3u_suffixes,
- .mime_types = extm3u_mime_types,
+ "extm3u",
+
+ nullptr,
+ nullptr,
+ nullptr,
+ extm3u_open_stream,
+ extm3u_close,
+ extm3u_read,
+
+ nullptr,
+ extm3u_suffixes,
+ extm3u_mime_types,
};
diff --git a/src/playlist/extm3u_playlist_plugin.h b/src/playlist/ExtM3uPlaylistPlugin.hxx
index 5f611ac9..844fba15 100644
--- a/src/playlist/extm3u_playlist_plugin.h
+++ b/src/playlist/ExtM3uPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_EXTM3U_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_EXTM3U_PLAYLIST_PLUGIN_H
+#ifndef MPD_EXTM3U_PLAYLIST_PLUGIN_HXX
+#define MPD_EXTM3U_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin extm3u_playlist_plugin;
diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/LastFMPlaylistPlugin.cxx
index ead14dea..49638840 100644
--- a/src/playlist/lastfm_playlist_plugin.c
+++ b/src/playlist/LastFMPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,10 @@
*/
#include "config.h"
-#include "playlist/lastfm_playlist_plugin.h"
-#include "playlist_plugin.h"
-#include "playlist_list.h"
+#include "LastFMPlaylistPlugin.hxx"
+#include "PlaylistPlugin.hxx"
+#include "PlaylistRegistry.hxx"
#include "conf.h"
-#include "uri.h"
#include "song.h"
#include "input_stream.h"
@@ -80,7 +79,7 @@ lastfm_finish(void)
* @return data fetched, or NULL on error. Must be freed with g_free.
*/
static char *
-lastfm_get(const char *url, GMutex *mutex, GCond *cond)
+lastfm_get(const char *url, Mutex &mutex, Cond &cond)
{
struct input_stream *input_stream;
GError *error = NULL;
@@ -97,7 +96,7 @@ lastfm_get(const char *url, GMutex *mutex, GCond *cond)
return NULL;
}
- g_mutex_lock(mutex);
+ mutex.lock();
input_stream_wait_ready(input_stream);
@@ -114,7 +113,7 @@ lastfm_get(const char *url, GMutex *mutex, GCond *cond)
break;
/* I/O error */
- g_mutex_unlock(mutex);
+ mutex.unlock();
input_stream_close(input_stream);
return NULL;
}
@@ -122,7 +121,7 @@ lastfm_get(const char *url, GMutex *mutex, GCond *cond)
length += nbytes;
} while (length < sizeof(buffer));
- g_mutex_unlock(mutex);
+ mutex.unlock();
input_stream_close(input_stream);
return g_strndup(buffer, length);
@@ -155,7 +154,7 @@ lastfm_find(const char *response, const char *name)
}
static struct playlist_provider *
-lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond)
+lastfm_open_uri(const char *uri, Mutex &mutex, Cond &cond)
{
struct lastfm_playlist *playlist;
GError *error = NULL;
@@ -236,16 +235,15 @@ lastfm_open_uri(const char *uri, GMutex *mutex, GCond *cond)
return NULL;
}
- g_mutex_lock(mutex);
+ mutex.lock();
input_stream_wait_ready(playlist->is);
/* last.fm does not send a MIME type, we have to fake it here
:-( */
- g_free(playlist->is->mime);
- playlist->is->mime = g_strdup("application/xspf+xml");
+ input_stream_override_mime_type(playlist->is, "application/xspf+xml");
- g_mutex_unlock(mutex);
+ mutex.unlock();
/* parse the XSPF playlist */
@@ -284,13 +282,16 @@ static const char *const lastfm_schemes[] = {
};
const struct playlist_plugin lastfm_playlist_plugin = {
- .name = "lastfm",
+ "lastfm",
- .init = lastfm_init,
- .finish = lastfm_finish,
- .open_uri = lastfm_open_uri,
- .close = lastfm_close,
- .read = lastfm_read,
+ lastfm_init,
+ lastfm_finish,
+ lastfm_open_uri,
+ nullptr,
+ lastfm_close,
+ lastfm_read,
- .schemes = lastfm_schemes,
+ lastfm_schemes,
+ nullptr,
+ nullptr,
};
diff --git a/src/playlist/lastfm_playlist_plugin.h b/src/playlist/LastFMPlaylistPlugin.hxx
index 46a8b0ca..fe0e206d 100644
--- a/src/playlist/lastfm_playlist_plugin.h
+++ b/src/playlist/LastFMPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_LASTFM_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_LASTFM_PLAYLIST_PLUGIN_H
+#ifndef MPD_LASTFM_PLAYLIST_PLUGIN_HXX
+#define MPD_LASTFM_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin lastfm_playlist_plugin;
diff --git a/src/playlist/m3u_playlist_plugin.c b/src/playlist/M3uPlaylistPlugin.cxx
index 45b70d2b..eeecd277 100644
--- a/src/playlist/m3u_playlist_plugin.c
+++ b/src/playlist/M3uPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,15 +18,17 @@
*/
#include "config.h"
-#include "playlist/m3u_playlist_plugin.h"
-#include "playlist_plugin.h"
-#include "text_input_stream.h"
-#include "uri.h"
+#include "M3uPlaylistPlugin.hxx"
+#include "PlaylistPlugin.hxx"
#include "song.h"
+extern "C" {
+#include "text_input_stream.h"
+}
+
#include <glib.h>
-struct m3u_playlist {
+struct M3uPlaylist {
struct playlist_provider base;
struct text_input_stream *tis;
@@ -35,7 +37,7 @@ struct m3u_playlist {
static struct playlist_provider *
m3u_open_stream(struct input_stream *is)
{
- struct m3u_playlist *playlist = g_new(struct m3u_playlist, 1);
+ M3uPlaylist *playlist = g_new(M3uPlaylist, 1);
playlist_provider_init(&playlist->base, &m3u_playlist_plugin);
playlist->tis = text_input_stream_new(is);
@@ -46,7 +48,7 @@ m3u_open_stream(struct input_stream *is)
static void
m3u_close(struct playlist_provider *_playlist)
{
- struct m3u_playlist *playlist = (struct m3u_playlist *)_playlist;
+ M3uPlaylist *playlist = (M3uPlaylist *)_playlist;
text_input_stream_free(playlist->tis);
g_free(playlist);
@@ -55,7 +57,7 @@ m3u_close(struct playlist_provider *_playlist)
static struct song *
m3u_read(struct playlist_provider *_playlist)
{
- struct m3u_playlist *playlist = (struct m3u_playlist *)_playlist;
+ M3uPlaylist *playlist = (M3uPlaylist *)_playlist;
const char *line;
do {
@@ -81,12 +83,16 @@ static const char *const m3u_mime_types[] = {
};
const struct playlist_plugin m3u_playlist_plugin = {
- .name = "m3u",
+ "m3u",
- .open_stream = m3u_open_stream,
- .close = m3u_close,
- .read = m3u_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ m3u_open_stream,
+ m3u_close,
+ m3u_read,
- .suffixes = m3u_suffixes,
- .mime_types = m3u_mime_types,
+ nullptr,
+ m3u_suffixes,
+ m3u_mime_types,
};
diff --git a/src/playlist/m3u_playlist_plugin.h b/src/playlist/M3uPlaylistPlugin.hxx
index 3890a5fc..a2058bb2 100644
--- a/src/playlist/m3u_playlist_plugin.h
+++ b/src/playlist/M3uPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_M3U_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_M3U_PLAYLIST_PLUGIN_H
+#ifndef MPD_M3U_PLAYLIST_PLUGIN_HXX
+#define MPD_M3U_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin m3u_playlist_plugin;
diff --git a/src/playlist/MemoryPlaylistProvider.cxx b/src/playlist/MemoryPlaylistProvider.cxx
new file mode 100644
index 00000000..4fe3d6ce
--- /dev/null
+++ b/src/playlist/MemoryPlaylistProvider.cxx
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "MemoryPlaylistProvider.hxx"
+#include "song.h"
+
+static void
+memory_playlist_close(struct playlist_provider *_playlist)
+{
+ MemoryPlaylistProvider *playlist = (MemoryPlaylistProvider *)_playlist;
+
+ delete playlist;
+}
+
+static struct song *
+memory_playlist_read(struct playlist_provider *_playlist)
+{
+ MemoryPlaylistProvider *playlist = (MemoryPlaylistProvider *)_playlist;
+
+ return playlist->Read();
+}
+
+static constexpr struct playlist_plugin memory_playlist_plugin = {
+ nullptr,
+
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ memory_playlist_close,
+ memory_playlist_read,
+
+ nullptr,
+ nullptr,
+ nullptr,
+};
+
+MemoryPlaylistProvider::MemoryPlaylistProvider(std::forward_list<SongPointer> &&_songs)
+ :songs(std::move(_songs)) {
+ playlist_provider_init(this, &memory_playlist_plugin);
+}
+
+inline song *
+MemoryPlaylistProvider::Read()
+{
+ if (songs.empty())
+ return NULL;
+
+ auto result = songs.front().Steal();
+ songs.pop_front();
+ return result;
+}
diff --git a/src/db/simple_db_plugin.h b/src/playlist/MemoryPlaylistProvider.hxx
index 51150584..246ffd10 100644
--- a/src/db/simple_db_plugin.h
+++ b/src/playlist/MemoryPlaylistProvider.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,26 +17,23 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_SIMPLE_DB_PLUGIN_H
-#define MPD_SIMPLE_DB_PLUGIN_H
+#ifndef MPD_MEMORY_PLAYLIST_PROVIDER_HXX
+#define MPD_MEMORY_PLAYLIST_PROVIDER_HXX
-#include <glib.h>
-#include <stdbool.h>
-#include <time.h>
+#include "PlaylistPlugin.hxx"
+#include "SongPointer.hxx"
-extern const struct db_plugin simple_db_plugin;
+#include <forward_list>
-struct db;
+struct song;
-G_GNUC_PURE
-struct directory *
-simple_db_get_root(struct db *db);
+class MemoryPlaylistProvider : public playlist_provider {
+ std::forward_list<SongPointer> songs;
-bool
-simple_db_save(struct db *db, GError **error_r);
+public:
+ MemoryPlaylistProvider(std::forward_list<SongPointer> &&_songs);
-G_GNUC_PURE
-time_t
-simple_db_get_mtime(const struct db *db);
+ song *Read();
+};
#endif
diff --git a/src/playlist/pls_playlist_plugin.c b/src/playlist/PlsPlaylistPlugin.cxx
index c4e5492a..3cf5f46e 100644
--- a/src/playlist/pls_playlist_plugin.c
+++ b/src/playlist/PlsPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,21 +18,17 @@
*/
#include "config.h"
-#include "playlist/pls_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "PlsPlaylistPlugin.hxx"
+#include "MemoryPlaylistProvider.hxx"
#include "input_stream.h"
#include "uri.h"
#include "song.h"
#include "tag.h"
-#include <glib.h>
-
-struct pls_playlist {
- struct playlist_provider base;
- GSList *songs;
-};
+#include <glib.h>
-static void pls_parser(GKeyFile *keyfile, struct pls_playlist *playlist)
+static void
+pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
{
gchar *key;
gchar *value;
@@ -97,7 +93,7 @@ static void pls_parser(GKeyFile *keyfile, struct pls_playlist *playlist)
if(error) g_error_free(error);
error = NULL;
- playlist->songs = g_slist_prepend(playlist->songs, song);
+ songs.emplace_front(song);
num_entries--;
}
@@ -111,7 +107,6 @@ pls_open_stream(struct input_stream *is)
char buffer[1024];
bool success;
GKeyFile *keyfile;
- struct pls_playlist *playlist;
GString *kf_data = g_string_new("");
do {
@@ -152,50 +147,12 @@ pls_open_stream(struct input_stream *is)
return NULL;
}
- playlist = g_new(struct pls_playlist, 1);
- playlist_provider_init(&playlist->base, &pls_playlist_plugin);
- playlist->songs = NULL;
-
- pls_parser(keyfile, playlist);
-
+ std::forward_list<SongPointer> songs;
+ pls_parser(keyfile, songs);
g_key_file_free(keyfile);
- return &playlist->base;
-}
-
-static void
-song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct song *song = data;
-
- song_free(song);
-}
-
-static void
-pls_close(struct playlist_provider *_playlist)
-{
- struct pls_playlist *playlist = (struct pls_playlist *)_playlist;
-
- g_slist_foreach(playlist->songs, song_free_callback, NULL);
- g_slist_free(playlist->songs);
-
- g_free(playlist);
-
-}
-
-static struct song *
-pls_read(struct playlist_provider *_playlist)
-{
- struct pls_playlist *playlist = (struct pls_playlist *)_playlist;
- struct song *song;
-
- if (playlist->songs == NULL)
- return NULL;
-
- song = playlist->songs->data;
- playlist->songs = g_slist_remove(playlist->songs, song);
-
- return song;
+ songs.reverse();
+ return new MemoryPlaylistProvider(std::move(songs));
}
static const char *const pls_suffixes[] = {
@@ -209,12 +166,16 @@ static const char *const pls_mime_types[] = {
};
const struct playlist_plugin pls_playlist_plugin = {
- .name = "pls",
+ "pls",
- .open_stream = pls_open_stream,
- .close = pls_close,
- .read = pls_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ pls_open_stream,
+ nullptr,
+ nullptr,
- .suffixes = pls_suffixes,
- .mime_types = pls_mime_types,
+ nullptr,
+ pls_suffixes,
+ pls_mime_types,
};
diff --git a/src/playlist/pls_playlist_plugin.h b/src/playlist/PlsPlaylistPlugin.hxx
index d03435f6..3fafd36d 100644
--- a/src/playlist/pls_playlist_plugin.h
+++ b/src/playlist/PlsPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_PLS_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_PLS_PLAYLIST_PLUGIN_H
+#ifndef MPD_PLS_PLAYLIST_PLUGIN_HXX
+#define MPD_PLS_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin pls_playlist_plugin;
diff --git a/src/playlist/rss_playlist_plugin.c b/src/playlist/RssPlaylistPlugin.cxx
index 6740cba7..3b69202e 100644
--- a/src/playlist/rss_playlist_plugin.c
+++ b/src/playlist/RssPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "playlist/rss_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "RssPlaylistPlugin.hxx"
+#include "MemoryPlaylistProvider.hxx"
#include "input_stream.h"
#include "song.h"
#include "tag.h"
@@ -35,12 +35,12 @@
/**
* This is the state object for the GLib XML parser.
*/
-struct rss_parser {
+struct RssParser {
/**
* The list of songs (in reverse order because that's faster
* while adding).
*/
- GSList *songs;
+ std::forward_list<SongPointer> songs;
/**
* The current position in the XML file.
@@ -61,6 +61,9 @@ struct rss_parser {
* element.
*/
struct song *song;
+
+ RssParser()
+ :state(ROOT) {}
};
static const gchar *
@@ -81,19 +84,19 @@ rss_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar **attribute_values,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct rss_parser *parser = user_data;
+ RssParser *parser = (RssParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case RssParser::ROOT:
if (g_ascii_strcasecmp(element_name, "item") == 0) {
- parser->state = ITEM;
+ parser->state = RssParser::ITEM;
parser->song = song_remote_new("rss:");
parser->tag = TAG_NUM_OF_ITEM_TYPES;
}
break;
- case ITEM:
+ case RssParser::ITEM:
if (g_ascii_strcasecmp(element_name, "enclosure") == 0) {
const gchar *href = get_attribute(attribute_names,
attribute_values,
@@ -128,21 +131,20 @@ rss_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct rss_parser *parser = user_data;
+ RssParser *parser = (RssParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case RssParser::ROOT:
break;
- case ITEM:
+ case RssParser::ITEM:
if (g_ascii_strcasecmp(element_name, "item") == 0) {
if (strcmp(parser->song->uri, "rss:") != 0)
- parser->songs = g_slist_prepend(parser->songs,
- parser->song);
+ parser->songs.emplace_front(parser->song);
else
song_free(parser->song);
- parser->state = ROOT;
+ parser->state = RssParser::ROOT;
} else
parser->tag = TAG_NUM_OF_ITEM_TYPES;
@@ -155,13 +157,13 @@ rss_text(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *text, gsize text_len,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct rss_parser *parser = user_data;
+ RssParser *parser = (RssParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case RssParser::ROOT:
break;
- case ITEM:
+ case RssParser::ITEM:
if (parser->tag != TAG_NUM_OF_ITEM_TYPES) {
if (parser->song->tag == NULL)
parser->song->tag = tag_new();
@@ -174,29 +176,20 @@ rss_text(G_GNUC_UNUSED GMarkupParseContext *context,
}
static const GMarkupParser rss_parser = {
- .start_element = rss_start_element,
- .end_element = rss_end_element,
- .text = rss_text,
+ rss_start_element,
+ rss_end_element,
+ rss_text,
+ nullptr,
+ nullptr,
};
static void
-song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct song *song = data;
-
- song_free(song);
-}
-
-static void
rss_parser_destroy(gpointer data)
{
- struct rss_parser *parser = data;
+ RssParser *parser = (RssParser *)data;
- if (parser->state >= ITEM)
+ if (parser->state >= RssParser::ITEM)
song_free(parser->song);
-
- g_slist_foreach(parser->songs, song_free_callback, NULL);
- g_slist_free(parser->songs);
}
/*
@@ -204,20 +197,10 @@ rss_parser_destroy(gpointer data)
*
*/
-struct rss_playlist {
- struct playlist_provider base;
-
- GSList *songs;
-};
-
static struct playlist_provider *
rss_open_stream(struct input_stream *is)
{
- struct rss_parser parser = {
- .songs = NULL,
- .state = ROOT,
- };
- struct rss_playlist *playlist;
+ RssParser parser;
GMarkupParseContext *context;
char buffer[1024];
size_t nbytes;
@@ -262,41 +245,13 @@ rss_open_stream(struct input_stream *is)
return NULL;
}
- /* create a #rss_playlist object from the parsed song list */
-
- playlist = g_new(struct rss_playlist, 1);
- playlist_provider_init(&playlist->base, &rss_playlist_plugin);
- playlist->songs = g_slist_reverse(parser.songs);
- parser.songs = NULL;
+ parser.songs.reverse();
+ MemoryPlaylistProvider *playlist =
+ new MemoryPlaylistProvider(std::move(parser.songs));
g_markup_parse_context_free(context);
- return &playlist->base;
-}
-
-static void
-rss_close(struct playlist_provider *_playlist)
-{
- struct rss_playlist *playlist = (struct rss_playlist *)_playlist;
-
- g_slist_foreach(playlist->songs, song_free_callback, NULL);
- g_slist_free(playlist->songs);
- g_free(playlist);
-}
-
-static struct song *
-rss_read(struct playlist_provider *_playlist)
-{
- struct rss_playlist *playlist = (struct rss_playlist *)_playlist;
- struct song *song;
-
- if (playlist->songs == NULL)
- return NULL;
-
- song = playlist->songs->data;
- playlist->songs = g_slist_remove(playlist->songs, song);
-
- return song;
+ return playlist;
}
static const char *const rss_suffixes[] = {
@@ -311,12 +266,16 @@ static const char *const rss_mime_types[] = {
};
const struct playlist_plugin rss_playlist_plugin = {
- .name = "rss",
+ "rss",
- .open_stream = rss_open_stream,
- .close = rss_close,
- .read = rss_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ rss_open_stream,
+ nullptr,
+ nullptr,
- .suffixes = rss_suffixes,
- .mime_types = rss_mime_types,
+ nullptr,
+ rss_suffixes,
+ rss_mime_types,
};
diff --git a/src/playlist/rss_playlist_plugin.h b/src/playlist/RssPlaylistPlugin.hxx
index 3b376de7..f49f7e9c 100644
--- a/src/playlist/rss_playlist_plugin.h
+++ b/src/playlist/RssPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_RSS_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_RSS_PLAYLIST_PLUGIN_H
+#ifndef MPD_RSS_PLAYLIST_PLUGIN_HXX
+#define MPD_RSS_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin rss_playlist_plugin;
diff --git a/src/playlist/soundcloud_playlist_plugin.c b/src/playlist/SoundCloudPlaylistPlugin.cxx
index 7c79f880..5a258865 100644
--- a/src/playlist/soundcloud_playlist_plugin.c
+++ b/src/playlist/SoundCloudPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,10 +18,10 @@
*/
#include "config.h"
-#include "playlist/soundcloud_playlist_plugin.h"
+#include "SoundCloudPlaylistPlugin.hxx"
+#include "MemoryPlaylistProvider.hxx"
#include "conf.h"
#include "input_stream.h"
-#include "playlist_plugin.h"
#include "song.h"
#include "tag.h"
@@ -30,12 +30,6 @@
#include <string.h>
-struct soundcloud_playlist {
- struct playlist_provider base;
-
- GSList *songs;
-};
-
static struct {
char *apikey;
} soundcloud_config;
@@ -107,7 +101,8 @@ struct parse_data {
long duration;
char* title;
int got_url; /* nesting level of last stream_url */
- GSList* songs;
+
+ std::forward_list<SongPointer> songs;
};
static int handle_integer(void *ctx,
@@ -221,7 +216,7 @@ static int handle_end_map(void *ctx)
tag_add_item(t, TAG_NAME, data->title);
s->tag = t;
- data->songs = g_slist_prepend(data->songs, s);
+ data->songs.emplace_front(s);
return 1;
}
@@ -247,7 +242,8 @@ static yajl_callbacks parse_callbacks = {
* @return -1 on error, 0 on success.
*/
static int
-soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* cond)
+soundcloud_parse_json(const char *url, yajl_handle hand,
+ Mutex &mutex, Cond &cond)
{
struct input_stream *input_stream;
GError *error = NULL;
@@ -264,7 +260,7 @@ soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* c
return -1;
}
- g_mutex_lock(mutex);
+ mutex.lock();
input_stream_wait_ready(input_stream);
yajl_status stat;
@@ -280,7 +276,7 @@ soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* c
if (input_stream_eof(input_stream)) {
done = true;
} else {
- g_mutex_unlock(mutex);
+ mutex.unlock();
input_stream_close(input_stream);
return -1;
}
@@ -308,7 +304,7 @@ soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* c
}
}
- g_mutex_unlock(mutex);
+ mutex.unlock();
input_stream_close(input_stream);
return 0;
@@ -323,10 +319,8 @@ soundcloud_parse_json(const char *url, yajl_handle hand, GMutex* mutex, GCond* c
*/
static struct playlist_provider *
-soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond)
+soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
{
- struct soundcloud_playlist *playlist = NULL;
-
char *s, *p;
char *scheme, *arg, *rest;
s = g_strdup(uri);
@@ -377,7 +371,6 @@ soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond)
struct parse_data data;
data.got_url = 0;
- data.songs = NULL;
data.title = NULL;
data.stream_url = NULL;
#ifdef HAVE_YAJL1
@@ -398,34 +391,8 @@ soundcloud_open_uri(const char *uri, GMutex *mutex, GCond *cond)
if (ret == -1)
return NULL;
- playlist = g_new(struct soundcloud_playlist, 1);
- playlist_provider_init(&playlist->base, &soundcloud_playlist_plugin);
- playlist->songs = g_slist_reverse(data.songs);
-
- return &playlist->base;
-}
-
-static void
-soundcloud_close(struct playlist_provider *_playlist)
-{
- struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist;
-
- g_free(playlist);
-}
-
-
-static struct song *
-soundcloud_read(struct playlist_provider *_playlist)
-{
- struct soundcloud_playlist *playlist = (struct soundcloud_playlist *)_playlist;
-
- if (playlist->songs == NULL)
- return NULL;
-
- struct song* s;
- s = (struct song *)playlist->songs->data;
- playlist->songs = g_slist_remove(playlist->songs, s);
- return s;
+ data.songs.reverse();
+ return new MemoryPlaylistProvider(std::move(data.songs));
}
static const char *const soundcloud_schemes[] = {
@@ -434,15 +401,18 @@ static const char *const soundcloud_schemes[] = {
};
const struct playlist_plugin soundcloud_playlist_plugin = {
- .name = "soundcloud",
+ "soundcloud",
- .init = soundcloud_init,
- .finish = soundcloud_finish,
- .open_uri = soundcloud_open_uri,
- .close = soundcloud_close,
- .read = soundcloud_read,
+ soundcloud_init,
+ soundcloud_finish,
+ soundcloud_open_uri,
+ nullptr,
+ nullptr,
+ nullptr,
- .schemes = soundcloud_schemes,
+ soundcloud_schemes,
+ nullptr,
+ nullptr,
};
diff --git a/src/playlist/soundcloud_playlist_plugin.h b/src/playlist/SoundCloudPlaylistPlugin.hxx
index e09e2dd4..7c121328 100644
--- a/src/playlist/soundcloud_playlist_plugin.h
+++ b/src/playlist/SoundCloudPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_SOUNDCLOUD_PLAYLIST_PLUGIN_H
+#ifndef MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX
+#define MPD_SOUNDCLOUD_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin soundcloud_playlist_plugin;
diff --git a/src/playlist/xspf_playlist_plugin.c b/src/playlist/XspfPlaylistPlugin.cxx
index 17d9040e..bd84d86b 100644
--- a/src/playlist/xspf_playlist_plugin.c
+++ b/src/playlist/XspfPlaylistPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,10 @@
*/
#include "config.h"
-#include "playlist/xspf_playlist_plugin.h"
-#include "playlist_plugin.h"
+#include "XspfPlaylistPlugin.hxx"
+#include "MemoryPlaylistProvider.hxx"
#include "input_stream.h"
#include "uri.h"
-#include "song.h"
#include "tag.h"
#include <glib.h>
@@ -36,12 +35,12 @@
/**
* This is the state object for the GLib XML parser.
*/
-struct xspf_parser {
+struct XspfParser {
/**
* The list of songs (in reverse order because that's faster
* while adding).
*/
- GSList *songs;
+ std::forward_list<SongPointer> songs;
/**
* The current position in the XML file.
@@ -63,6 +62,9 @@ struct xspf_parser {
* element.
*/
struct song *song;
+
+ XspfParser()
+ :state(ROOT) {}
};
static void
@@ -72,33 +74,33 @@ xspf_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
G_GNUC_UNUSED const gchar **attribute_values,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct xspf_parser *parser = user_data;
+ XspfParser *parser = (XspfParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case XspfParser::ROOT:
if (strcmp(element_name, "playlist") == 0)
- parser->state = PLAYLIST;
+ parser->state = XspfParser::PLAYLIST;
break;
- case PLAYLIST:
+ case XspfParser::PLAYLIST:
if (strcmp(element_name, "trackList") == 0)
- parser->state = TRACKLIST;
+ parser->state = XspfParser::TRACKLIST;
break;
- case TRACKLIST:
+ case XspfParser::TRACKLIST:
if (strcmp(element_name, "track") == 0) {
- parser->state = TRACK;
+ parser->state = XspfParser::TRACK;
parser->song = NULL;
parser->tag = TAG_NUM_OF_ITEM_TYPES;
}
break;
- case TRACK:
+ case XspfParser::TRACK:
if (strcmp(element_name, "location") == 0)
- parser->state = LOCATION;
+ parser->state = XspfParser::LOCATION;
else if (strcmp(element_name, "title") == 0)
parser->tag = TAG_TITLE;
else if (strcmp(element_name, "creator") == 0)
@@ -114,7 +116,7 @@ xspf_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
break;
- case LOCATION:
+ case XspfParser::LOCATION:
break;
}
}
@@ -124,38 +126,37 @@ xspf_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct xspf_parser *parser = user_data;
+ XspfParser *parser = (XspfParser *)user_data;
switch (parser->state) {
- case ROOT:
+ case XspfParser::ROOT:
break;
- case PLAYLIST:
+ case XspfParser::PLAYLIST:
if (strcmp(element_name, "playlist") == 0)
- parser->state = ROOT;
+ parser->state = XspfParser::ROOT;
break;
- case TRACKLIST:
+ case XspfParser::TRACKLIST:
if (strcmp(element_name, "tracklist") == 0)
- parser->state = PLAYLIST;
+ parser->state = XspfParser::PLAYLIST;
break;
- case TRACK:
+ case XspfParser::TRACK:
if (strcmp(element_name, "track") == 0) {
if (parser->song != NULL)
- parser->songs = g_slist_prepend(parser->songs,
- parser->song);
+ parser->songs.emplace_front(parser->song);
- parser->state = TRACKLIST;
+ parser->state = XspfParser::TRACKLIST;
} else
parser->tag = TAG_NUM_OF_ITEM_TYPES;
break;
- case LOCATION:
- parser->state = TRACK;
+ case XspfParser::LOCATION:
+ parser->state = XspfParser::TRACK;
break;
}
}
@@ -165,15 +166,15 @@ xspf_text(G_GNUC_UNUSED GMarkupParseContext *context,
const gchar *text, gsize text_len,
gpointer user_data, G_GNUC_UNUSED GError **error)
{
- struct xspf_parser *parser = user_data;
+ XspfParser *parser = (XspfParser *)user_data;
switch (parser->state) {
- case ROOT:
- case PLAYLIST:
- case TRACKLIST:
+ case XspfParser::ROOT:
+ case XspfParser::PLAYLIST:
+ case XspfParser::TRACKLIST:
break;
- case TRACK:
+ case XspfParser::TRACK:
if (parser->song != NULL &&
parser->tag != TAG_NUM_OF_ITEM_TYPES) {
if (parser->song->tag == NULL)
@@ -184,7 +185,7 @@ xspf_text(G_GNUC_UNUSED GMarkupParseContext *context,
break;
- case LOCATION:
+ case XspfParser::LOCATION:
if (parser->song == NULL) {
char *uri = g_strndup(text, text_len);
parser->song = song_remote_new(uri);
@@ -196,29 +197,20 @@ xspf_text(G_GNUC_UNUSED GMarkupParseContext *context,
}
static const GMarkupParser xspf_parser = {
- .start_element = xspf_start_element,
- .end_element = xspf_end_element,
- .text = xspf_text,
+ xspf_start_element,
+ xspf_end_element,
+ xspf_text,
+ nullptr,
+ nullptr,
};
static void
-song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct song *song = data;
-
- song_free(song);
-}
-
-static void
xspf_parser_destroy(gpointer data)
{
- struct xspf_parser *parser = data;
+ XspfParser *parser = (XspfParser *)data;
- if (parser->state >= TRACK && parser->song != NULL)
+ if (parser->state >= XspfParser::TRACK && parser->song != NULL)
song_free(parser->song);
-
- g_slist_foreach(parser->songs, song_free_callback, NULL);
- g_slist_free(parser->songs);
}
/*
@@ -226,20 +218,10 @@ xspf_parser_destroy(gpointer data)
*
*/
-struct xspf_playlist {
- struct playlist_provider base;
-
- GSList *songs;
-};
-
static struct playlist_provider *
xspf_open_stream(struct input_stream *is)
{
- struct xspf_parser parser = {
- .songs = NULL,
- .state = ROOT,
- };
- struct xspf_playlist *playlist;
+ XspfParser parser;
GMarkupParseContext *context;
char buffer[1024];
size_t nbytes;
@@ -284,41 +266,13 @@ xspf_open_stream(struct input_stream *is)
return NULL;
}
- /* create a #xspf_playlist object from the parsed song list */
-
- playlist = g_new(struct xspf_playlist, 1);
- playlist_provider_init(&playlist->base, &xspf_playlist_plugin);
- playlist->songs = g_slist_reverse(parser.songs);
- parser.songs = NULL;
+ parser.songs.reverse();
+ MemoryPlaylistProvider *playlist =
+ new MemoryPlaylistProvider(std::move(parser.songs));
g_markup_parse_context_free(context);
- return &playlist->base;
-}
-
-static void
-xspf_close(struct playlist_provider *_playlist)
-{
- struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist;
-
- g_slist_foreach(playlist->songs, song_free_callback, NULL);
- g_slist_free(playlist->songs);
- g_free(playlist);
-}
-
-static struct song *
-xspf_read(struct playlist_provider *_playlist)
-{
- struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist;
- struct song *song;
-
- if (playlist->songs == NULL)
- return NULL;
-
- song = playlist->songs->data;
- playlist->songs = g_slist_remove(playlist->songs, song);
-
- return song;
+ return playlist;
}
static const char *const xspf_suffixes[] = {
@@ -332,12 +286,16 @@ static const char *const xspf_mime_types[] = {
};
const struct playlist_plugin xspf_playlist_plugin = {
- .name = "xspf",
+ "xspf",
- .open_stream = xspf_open_stream,
- .close = xspf_close,
- .read = xspf_read,
+ nullptr,
+ nullptr,
+ nullptr,
+ xspf_open_stream,
+ nullptr,
+ nullptr,
- .suffixes = xspf_suffixes,
- .mime_types = xspf_mime_types,
+ nullptr,
+ xspf_suffixes,
+ xspf_mime_types,
};
diff --git a/src/playlist/xspf_playlist_plugin.h b/src/playlist/XspfPlaylistPlugin.hxx
index 4636d7e8..fc9bbd2c 100644
--- a/src/playlist/xspf_playlist_plugin.h
+++ b/src/playlist/XspfPlaylistPlugin.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PLAYLIST_XSPF_PLAYLIST_PLUGIN_H
-#define MPD_PLAYLIST_XSPF_PLAYLIST_PLUGIN_H
+#ifndef MPD_XSPF_PLAYLIST_PLUGIN_HXX
+#define MPD_XSPF_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin xspf_playlist_plugin;
diff --git a/src/playlist/despotify_playlist_plugin.c b/src/playlist/despotify_playlist_plugin.c
deleted file mode 100644
index 30b852c7..00000000
--- a/src/playlist/despotify_playlist_plugin.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "playlist/despotify_playlist_plugin.h"
-#include "playlist_plugin.h"
-#include "playlist_list.h"
-#include "conf.h"
-#include "uri.h"
-#include "tag.h"
-#include "song.h"
-#include "input_stream.h"
-#include "despotify_utils.h"
-
-#include <glib.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-#include <despotify.h>
-
-struct despotify_playlist {
- struct playlist_provider base;
-
- struct despotify_session *session;
- GSList *list;
-};
-
-static void
-add_song(struct despotify_playlist *ctx, struct ds_track *track)
-{
- const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
- struct song *song;
- char uri[128];
- char *ds_uri;
-
- /* Create a spt://... URI for MPD */
- g_snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
- ds_uri = uri + strlen(dsp_scheme) + 3;
-
- if (despotify_track_to_uri(track, ds_uri) != ds_uri) {
- /* Should never really fail, but let's be sure */
- g_debug("Can't add track %s\n", track->title);
- return;
- }
-
- song = song_remote_new(uri);
- song->tag = mpd_despotify_tag_from_track(track);
-
- ctx->list = g_slist_prepend(ctx->list, song);
-}
-
-static bool
-parse_track(struct despotify_playlist *ctx,
- struct ds_link *link)
-{
- struct ds_track *track;
-
- track = despotify_link_get_track(ctx->session, link);
- if (!track)
- return false;
- add_song(ctx, track);
-
- return true;
-}
-
-static bool
-parse_playlist(struct despotify_playlist *ctx,
- struct ds_link *link)
-{
- struct ds_playlist *playlist;
- struct ds_track *track;
-
- playlist = despotify_link_get_playlist(ctx->session, link);
- if (!playlist)
- return false;
-
- for (track = playlist->tracks; track; track = track->next)
- add_song(ctx, track);
-
- return true;
-}
-
-static bool
-despotify_playlist_init(G_GNUC_UNUSED const struct config_param *param)
-{
- return true;
-}
-
-static void
-despotify_playlist_finish(void)
-{
-}
-
-
-static struct playlist_provider *
-despotify_playlist_open_uri(const char *url, G_GNUC_UNUSED GMutex *mutex,
- G_GNUC_UNUSED GCond *cond)
-{
- struct despotify_playlist *ctx;
- struct despotify_session *session;
- struct ds_link *link;
- bool parse_result;
-
- session = mpd_despotify_get_session();
- if (!session)
- goto clean_none;
-
- /* Get link without spt:// */
- link = despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
- if (!link) {
- g_debug("Can't find %s\n", url);
- goto clean_none;
- }
-
- ctx = g_new(struct despotify_playlist, 1);
-
- ctx->list = NULL;
- ctx->session = session;
- playlist_provider_init(&ctx->base, &despotify_playlist_plugin);
-
- switch (link->type)
- {
- case LINK_TYPE_TRACK:
- parse_result = parse_track(ctx, link);
- break;
- case LINK_TYPE_PLAYLIST:
- parse_result = parse_playlist(ctx, link);
- break;
- default:
- parse_result = false;
- break;
- }
- despotify_free_link(link);
- if (!parse_result)
- goto clean_playlist;
-
- ctx->list = g_slist_reverse(ctx->list);
-
- return &ctx->base;
-
-clean_playlist:
- g_slist_free(ctx->list);
-clean_none:
-
- return NULL;
-}
-
-static void
-track_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
-{
- struct song *song = (struct song *)data;
-
- song_free(song);
-}
-
-static void
-despotify_playlist_close(struct playlist_provider *_playlist)
-{
- struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
-
- g_slist_foreach(ctx->list, track_free_callback, NULL);
- g_slist_free(ctx->list);
-
- g_free(ctx);
-}
-
-
-static struct song *
-despotify_playlist_read(struct playlist_provider *_playlist)
-{
- struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
- struct song *out;
-
- if (!ctx->list)
- return NULL;
-
- /* Remove the current track */
- out = ctx->list->data;
- ctx->list = g_slist_remove(ctx->list, out);
-
- return out;
-}
-
-
-static const char *const despotify_schemes[] = {
- "spt",
- NULL
-};
-
-const struct playlist_plugin despotify_playlist_plugin = {
- .name = "despotify",
-
- .init = despotify_playlist_init,
- .finish = despotify_playlist_finish,
- .open_uri = despotify_playlist_open_uri,
- .read = despotify_playlist_read,
- .close = despotify_playlist_close,
-
- .schemes = despotify_schemes,
-};
diff --git a/src/playlist_control.c b/src/playlist_control.c
deleted file mode 100644
index 0dea7676..00000000
--- a/src/playlist_control.c
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Functions for controlling playback on the playlist level.
- *
- */
-
-#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "idle.h"
-
-#include <glib.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "playlist"
-
-void
-playlist_stop(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- return;
-
- assert(playlist->current >= 0);
-
- g_debug("stop");
- pc_stop(pc);
- playlist->queued = -1;
- playlist->playing = false;
-
- if (playlist->queue.random) {
- /* shuffle the playlist, so the next playback will
- result in a new random order */
-
- unsigned current_position =
- queue_order_to_position(&playlist->queue,
- playlist->current);
-
- queue_shuffle_order(&playlist->queue);
-
- /* make sure that "current" stays valid, and the next
- "play" command plays the same song again */
- playlist->current =
- queue_position_to_order(&playlist->queue,
- current_position);
- }
-}
-
-enum playlist_result
-playlist_play(struct playlist *playlist, struct player_control *pc,
- int song)
-{
- unsigned i = song;
-
- pc_clear_error(pc);
-
- if (song == -1) {
- /* play any song ("current" song, or the first song */
-
- if (queue_is_empty(&playlist->queue))
- return PLAYLIST_RESULT_SUCCESS;
-
- if (playlist->playing) {
- /* already playing: unpause playback, just in
- case it was paused, and return */
- pc_set_pause(pc, false);
- return PLAYLIST_RESULT_SUCCESS;
- }
-
- /* select a song: "current" song, or the first one */
- i = playlist->current >= 0
- ? playlist->current
- : 0;
- } else if (!queue_valid_position(&playlist->queue, song))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (playlist->queue.random) {
- if (song >= 0)
- /* "i" is currently the song position (which
- would be equal to the order number in
- no-random mode); convert it to a order
- number, because random mode is enabled */
- i = queue_position_to_order(&playlist->queue, song);
-
- if (!playlist->playing)
- playlist->current = 0;
-
- /* swap the new song with the previous "current" one,
- so playback continues as planned */
- queue_swap_order(&playlist->queue,
- i, playlist->current);
- i = playlist->current;
- }
-
- playlist->stop_on_error = false;
- playlist->error_count = 0;
-
- playlist_play_order(playlist, pc, i);
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_play_id(struct playlist *playlist, struct player_control *pc,
- int id)
-{
- int song;
-
- if (id == -1) {
- return playlist_play(playlist, pc, id);
- }
-
- song = queue_id_to_position(&playlist->queue, id);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_play(playlist, pc, song);
-}
-
-void
-playlist_next(struct playlist *playlist, struct player_control *pc)
-{
- int next_order;
- int current;
-
- if (!playlist->playing)
- return;
-
- assert(!queue_is_empty(&playlist->queue));
- assert(queue_valid_order(&playlist->queue, playlist->current));
-
- current = playlist->current;
- playlist->stop_on_error = false;
-
- /* determine the next song from the queue's order list */
-
- next_order = queue_next_order(&playlist->queue, playlist->current);
- if (next_order < 0) {
- /* no song after this one: stop playback */
- playlist_stop(playlist, pc);
-
- /* reset "current song" */
- playlist->current = -1;
- }
- else
- {
- if (next_order == 0 && playlist->queue.random) {
- /* The queue told us that the next song is the first
- song. This means we are in repeat mode. Shuffle
- the queue order, so this time, the user hears the
- songs in a different than before */
- assert(playlist->queue.repeat);
-
- queue_shuffle_order(&playlist->queue);
-
- /* note that playlist->current and playlist->queued are
- now invalid, but playlist_play_order() will
- discard them anyway */
- }
-
- playlist_play_order(playlist, pc, next_order);
- }
-
- /* Consume mode removes each played songs. */
- if(playlist->queue.consume)
- playlist_delete(playlist, pc,
- queue_order_to_position(&playlist->queue,
- current));
-}
-
-void
-playlist_previous(struct playlist *playlist, struct player_control *pc)
-{
- if (!playlist->playing)
- return;
-
- assert(queue_length(&playlist->queue) > 0);
-
- if (playlist->current > 0) {
- /* play the preceding song */
- playlist_play_order(playlist, pc,
- playlist->current - 1);
- } else if (playlist->queue.repeat) {
- /* play the last song in "repeat" mode */
- playlist_play_order(playlist, pc,
- queue_length(&playlist->queue) - 1);
- } else {
- /* re-start playing the current song if it's
- the first one */
- playlist_play_order(playlist, pc, playlist->current);
- }
-}
-
-enum playlist_result
-playlist_seek_song(struct playlist *playlist, struct player_control *pc,
- unsigned song, float seek_time)
-{
- const struct song *queued;
- unsigned i;
- bool success;
-
- if (!queue_valid_position(&playlist->queue, song))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- if (playlist->queue.random)
- i = queue_position_to_order(&playlist->queue, song);
- else
- i = song;
-
- pc_clear_error(pc);
- playlist->stop_on_error = true;
- playlist->error_count = 0;
-
- if (!playlist->playing || (unsigned)playlist->current != i) {
- /* seeking is not within the current song - prepare
- song change */
-
- playlist->playing = true;
- playlist->current = i;
-
- queued = NULL;
- }
-
- success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
- if (!success) {
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_NOT_PLAYING;
- }
-
- playlist->queued = -1;
- playlist_update_queued_song(playlist, pc, NULL);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
- unsigned id, float seek_time)
-{
- int song = queue_id_to_position(&playlist->queue, id);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_seek_song(playlist, pc, song, seek_time);
-}
-
-enum playlist_result
-playlist_seek_current(struct playlist *playlist, struct player_control *pc,
- float seek_time, bool relative)
-{
- if (!playlist->playing)
- return PLAYLIST_RESULT_NOT_PLAYING;
-
- if (relative) {
- struct player_status status;
- pc_get_status(pc, &status);
-
- if (status.state != PLAYER_STATE_PLAY &&
- status.state != PLAYER_STATE_PAUSE)
- return PLAYLIST_RESULT_NOT_PLAYING;
-
- seek_time += (int)status.elapsed_time;
- }
-
- if (seek_time < 0)
- seek_time = 0;
-
- return playlist_seek_song(playlist, pc, playlist->current, seek_time);
-}
diff --git a/src/playlist_edit.c b/src/playlist_edit.c
deleted file mode 100644
index d10f4945..00000000
--- a/src/playlist_edit.c
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Functions for editing the playlist (adding, removing, reordering
- * songs in the queue).
- *
- */
-
-#include "config.h"
-#include "playlist_internal.h"
-#include "player_control.h"
-#include "database.h"
-#include "uri.h"
-#include "song.h"
-#include "idle.h"
-
-#include <stdlib.h>
-
-static void playlist_increment_version(struct playlist *playlist)
-{
- queue_increment_version(&playlist->queue);
-
- idle_add(IDLE_PLAYLIST);
-}
-
-void
-playlist_clear(struct playlist *playlist, struct player_control *pc)
-{
- playlist_stop(playlist, pc);
-
- /* make sure there are no references to allocated songs
- anymore */
- for (unsigned i = 0; i < queue_length(&playlist->queue); i++) {
- const struct song *song = queue_get(&playlist->queue, i);
- if (!song_in_database(song))
- pc_song_deleted(pc, song);
- }
-
- queue_clear(&playlist->queue);
-
- playlist->current = -1;
-
- playlist_increment_version(playlist);
-}
-
-enum playlist_result
-playlist_append_file(struct playlist *playlist, struct player_control *pc,
- const char *path_fs, unsigned *added_id)
-{
- struct song *song = song_file_load(path_fs, NULL);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_append_song(playlist, pc, song, added_id);
-}
-
-enum playlist_result
-playlist_append_song(struct playlist *playlist, struct player_control *pc,
- struct song *song, unsigned *added_id)
-{
- const struct song *queued;
- unsigned id;
-
- if (queue_is_full(&playlist->queue))
- return PLAYLIST_RESULT_TOO_LARGE;
-
- queued = playlist_get_queued_song(playlist);
-
- id = queue_append(&playlist->queue, song, 0);
-
- if (playlist->queue.random) {
- /* shuffle the new song into the list of remaining
- songs to play */
-
- unsigned start;
- if (playlist->queued >= 0)
- start = playlist->queued + 1;
- else
- start = playlist->current + 1;
- if (start < queue_length(&playlist->queue))
- queue_shuffle_order_last(&playlist->queue, start,
- queue_length(&playlist->queue));
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- if (added_id)
- *added_id = id;
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-static struct song *
-song_by_uri(const char *uri)
-{
- struct song *song;
-
- song = db_get_song(uri);
- if (song != NULL)
- return song;
-
- if (uri_has_scheme(uri))
- return song_remote_new(uri);
-
- return NULL;
-}
-
-enum playlist_result
-playlist_append_uri(struct playlist *playlist, struct player_control *pc,
- const char *uri, unsigned *added_id)
-{
- struct song *song;
-
- g_debug("add to playlist: %s", uri);
-
- song = song_by_uri(uri);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_append_song(playlist, pc, song, added_id);
-}
-
-enum playlist_result
-playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
- unsigned song1, unsigned song2)
-{
- const struct song *queued;
-
- if (!queue_valid_position(&playlist->queue, song1) ||
- !queue_valid_position(&playlist->queue, song2))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- queue_swap(&playlist->queue, song1, song2);
-
- if (playlist->queue.random) {
- /* update the queue order, so that playlist->current
- still points to the current song order */
-
- queue_swap_order(&playlist->queue,
- queue_position_to_order(&playlist->queue,
- song1),
- queue_position_to_order(&playlist->queue,
- song2));
- } else {
- /* correct the "current" song order */
-
- if (playlist->current == (int)song1)
- playlist->current = song2;
- else if (playlist->current == (int)song2)
- playlist->current = song1;
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
- unsigned id1, unsigned id2)
-{
- int song1 = queue_id_to_position(&playlist->queue, id1);
- int song2 = queue_id_to_position(&playlist->queue, id2);
-
- if (song1 < 0 || song2 < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_swap_songs(playlist, pc, song1, song2);
-}
-
-enum playlist_result
-playlist_set_priority(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end,
- uint8_t priority)
-{
- if (start >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
-
- if (start >= end)
- return PLAYLIST_RESULT_SUCCESS;
-
- /* remember "current" and "queued" */
-
- int current_position = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
- : -1;
-
- const struct song *queued = playlist_get_queued_song(playlist);
-
- /* apply the priority changes */
-
- queue_set_priority_range(&playlist->queue, start, end, priority,
- playlist->current);
-
- playlist_increment_version(playlist);
-
- /* restore "current" and choose a new "queued" */
-
- if (current_position >= 0)
- playlist->current = queue_position_to_order(&playlist->queue,
- current_position);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
- unsigned song_id, uint8_t priority)
-{
- int song_position = queue_id_to_position(&playlist->queue, song_id);
- if (song_position < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_set_priority(playlist, pc,
- song_position, song_position + 1,
- priority);
-
-}
-
-static void
-playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
- unsigned song, const struct song **queued_p)
-{
- unsigned songOrder;
-
- assert(song < queue_length(&playlist->queue));
-
- songOrder = queue_position_to_order(&playlist->queue, song);
-
- if (playlist->playing && playlist->current == (int)songOrder) {
- bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
-
- /* the current song is going to be deleted: stop the player */
-
- pc_stop(pc);
- playlist->playing = false;
-
- /* see which song is going to be played instead */
-
- playlist->current = queue_next_order(&playlist->queue,
- playlist->current);
- if (playlist->current == (int)songOrder)
- playlist->current = -1;
-
- if (playlist->current >= 0 && !paused)
- /* play the song after the deleted one */
- playlist_play_order(playlist, pc, playlist->current);
- else
- /* no songs left to play, stop playback
- completely */
- playlist_stop(playlist, pc);
-
- *queued_p = NULL;
- } else if (playlist->current == (int)songOrder)
- /* there's a "current song" but we're not playing
- currently - clear "current" */
- playlist->current = -1;
-
- /* now do it: remove the song */
-
- if (!song_in_database(queue_get(&playlist->queue, song)))
- pc_song_deleted(pc, queue_get(&playlist->queue, song));
-
- queue_delete(&playlist->queue, song);
-
- /* update the "current" and "queued" variables */
-
- if (playlist->current > (int)songOrder) {
- playlist->current--;
- }
-}
-
-enum playlist_result
-playlist_delete(struct playlist *playlist, struct player_control *pc,
- unsigned song)
-{
- const struct song *queued;
-
- if (song >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- queued = playlist_get_queued_song(playlist);
-
- playlist_delete_internal(playlist, pc, song, &queued);
-
- playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_delete_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end)
-{
- const struct song *queued;
-
- if (start >= queue_length(&playlist->queue))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if (end > queue_length(&playlist->queue))
- end = queue_length(&playlist->queue);
-
- if (start >= end)
- return PLAYLIST_RESULT_SUCCESS;
-
- queued = playlist_get_queued_song(playlist);
-
- do {
- playlist_delete_internal(playlist, pc, --end, &queued);
- } while (end != start);
-
- playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_delete_id(struct playlist *playlist, struct player_control *pc,
- unsigned id)
-{
- int song = queue_id_to_position(&playlist->queue, id);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_delete(playlist, pc, song);
-}
-
-void
-playlist_delete_song(struct playlist *playlist, struct player_control *pc,
- const struct song *song)
-{
- for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i)
- if (song == queue_get(&playlist->queue, i))
- playlist_delete(playlist, pc, i);
-
- pc_song_deleted(pc, song);
-}
-
-enum playlist_result
-playlist_move_range(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end, int to)
-{
- const struct song *queued;
- int currentSong;
-
- if (!queue_valid_position(&playlist->queue, start) ||
- !queue_valid_position(&playlist->queue, end - 1))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if ((to >= 0 && to + end - start - 1 >= queue_length(&playlist->queue)) ||
- (to < 0 && abs(to) > (int)queue_length(&playlist->queue)))
- return PLAYLIST_RESULT_BAD_RANGE;
-
- if ((int)start == to)
- /* nothing happens */
- return PLAYLIST_RESULT_SUCCESS;
-
- queued = playlist_get_queued_song(playlist);
-
- /*
- * (to < 0) => move to offset from current song
- * (-playlist.length == to) => move to position BEFORE current song
- */
- currentSong = playlist->current >= 0
- ? (int)queue_order_to_position(&playlist->queue,
- playlist->current)
- : -1;
- if (to < 0 && playlist->current >= 0) {
- if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
- /* no-op, can't be moved to offset of itself */
- return PLAYLIST_RESULT_SUCCESS;
- to = (currentSong + abs(to)) % queue_length(&playlist->queue);
- if (start < (unsigned)to)
- to--;
- }
-
- queue_move_range(&playlist->queue, start, end, to);
-
- if (!playlist->queue.random) {
- /* update current/queued */
- if ((int)start <= playlist->current &&
- (unsigned)playlist->current < end)
- playlist->current += to - start;
- else if (playlist->current >= (int)end &&
- playlist->current <= to) {
- playlist->current -= end - start;
- } else if (playlist->current >= to &&
- playlist->current < (int)start) {
- playlist->current += end - start;
- }
- }
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-
- return PLAYLIST_RESULT_SUCCESS;
-}
-
-enum playlist_result
-playlist_move_id(struct playlist *playlist, struct player_control *pc,
- unsigned id1, int to)
-{
- int song = queue_id_to_position(&playlist->queue, id1);
- if (song < 0)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return playlist_move_range(playlist, pc, song, song+1, to);
-}
-
-void
-playlist_shuffle(struct playlist *playlist, struct player_control *pc,
- unsigned start, unsigned end)
-{
- const struct song *queued;
-
- if (end > queue_length(&playlist->queue))
- /* correct the "end" offset */
- end = queue_length(&playlist->queue);
-
- if ((start+1) >= end)
- /* needs at least two entries. */
- return;
-
- queued = playlist_get_queued_song(playlist);
- if (playlist->playing && playlist->current >= 0) {
- unsigned current_position;
- current_position = queue_order_to_position(&playlist->queue,
- playlist->current);
-
- if (current_position >= start && current_position < end)
- {
- /* put current playing song first */
- queue_swap(&playlist->queue, start, current_position);
-
- if (playlist->queue.random) {
- playlist->current =
- queue_position_to_order(&playlist->queue, start);
- } else
- playlist->current = start;
-
- /* start shuffle after the current song */
- start++;
- }
- } else {
- /* no playback currently: reset playlist->current */
-
- playlist->current = -1;
- }
-
- queue_shuffle_range(&playlist->queue, start, end);
-
- playlist_increment_version(playlist);
-
- playlist_update_queued_song(playlist, pc, queued);
-}
diff --git a/src/playlist_internal.h b/src/playlist_internal.h
deleted file mode 100644
index 81b17517..00000000
--- a/src/playlist_internal.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/*
- * Internal header for the components of the playlist code.
- *
- */
-
-#ifndef PLAYLIST_INTERNAL_H
-#define PLAYLIST_INTERNAL_H
-
-#include "playlist.h"
-
-struct player_control;
-
-/**
- * Returns the song object which is currently queued. Returns none if
- * there is none (yet?) or if MPD isn't playing.
- */
-const struct song *
-playlist_get_queued_song(struct playlist *playlist);
-
-/**
- * Updates the "queued song". Calculates the next song according to
- * the current one (if MPD isn't playing, it takes the first song),
- * and queues this song. Clears the old queued song if there was one.
- *
- * @param prev the song which was previously queued, as determined by
- * playlist_get_queued_song()
- */
-void
-playlist_update_queued_song(struct playlist *playlist,
- struct player_control *pc,
- const struct song *prev);
-
-void
-playlist_play_order(struct playlist *playlist, struct player_control *pc,
- int orderNum);
-
-#endif
diff --git a/src/playlist_vector.c b/src/playlist_vector.c
deleted file mode 100644
index 74c7bf08..00000000
--- a/src/playlist_vector.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "playlist_vector.h"
-#include "db_lock.h"
-
-#include <assert.h>
-#include <string.h>
-#include <glib.h>
-
-static struct playlist_metadata *
-playlist_metadata_new(const char *name, time_t mtime)
-{
- assert(name != NULL);
-
- struct playlist_metadata *pm = g_slice_new(struct playlist_metadata);
- pm->name = g_strdup(name);
- pm->mtime = mtime;
- return pm;
-}
-
-static void
-playlist_metadata_free(struct playlist_metadata *pm)
-{
- assert(pm != NULL);
- assert(pm->name != NULL);
-
- g_free(pm->name);
- g_slice_free(struct playlist_metadata, pm);
-}
-
-void
-playlist_vector_deinit(struct list_head *pv)
-{
- assert(pv != NULL);
-
- struct playlist_metadata *pm, *n;
- playlist_vector_for_each_safe(pm, n, pv)
- playlist_metadata_free(pm);
-}
-
-struct playlist_metadata *
-playlist_vector_find(struct list_head *pv, const char *name)
-{
- assert(holding_db_lock());
- assert(pv != NULL);
- assert(name != NULL);
-
- struct playlist_metadata *pm;
- playlist_vector_for_each(pm, pv)
- if (strcmp(pm->name, name) == 0)
- return pm;
-
- return NULL;
-}
-
-void
-playlist_vector_add(struct list_head *pv,
- const char *name, time_t mtime)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_metadata_new(name, mtime);
- list_add_tail(&pm->siblings, pv);
-}
-
-bool
-playlist_vector_update_or_add(struct list_head *pv,
- const char *name, time_t mtime)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_vector_find(pv, name);
- if (pm != NULL) {
- if (mtime == pm->mtime)
- return false;
-
- pm->mtime = mtime;
- } else
- playlist_vector_add(pv, name, mtime);
-
- return true;
-}
-
-bool
-playlist_vector_remove(struct list_head *pv, const char *name)
-{
- assert(holding_db_lock());
-
- struct playlist_metadata *pm = playlist_vector_find(pv, name);
- if (pm == NULL)
- return false;
-
- list_del(&pm->siblings);
- playlist_metadata_free(pm);
- return true;
-}
diff --git a/src/playlist_vector.h b/src/playlist_vector.h
deleted file mode 100644
index 0af6df8b..00000000
--- a/src/playlist_vector.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PLAYLIST_VECTOR_H
-#define MPD_PLAYLIST_VECTOR_H
-
-#include "util/list.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <sys/time.h>
-
-#define playlist_vector_for_each(pos, head) \
- list_for_each_entry(pos, head, siblings)
-
-#define playlist_vector_for_each_safe(pos, n, head) \
- list_for_each_entry_safe(pos, n, head, siblings)
-
-/**
- * A directory entry pointing to a playlist file.
- */
-struct playlist_metadata {
- struct list_head siblings;
-
- /**
- * The UTF-8 encoded name of the playlist file.
- */
- char *name;
-
- time_t mtime;
-};
-
-void
-playlist_vector_deinit(struct list_head *pv);
-
-/**
- * Caller must lock the #db_mutex.
- */
-struct playlist_metadata *
-playlist_vector_find(struct list_head *pv, const char *name);
-
-/**
- * Caller must lock the #db_mutex.
- */
-void
-playlist_vector_add(struct list_head *pv,
- const char *name, time_t mtime);
-
-/**
- * Caller must lock the #db_mutex.
- *
- * @return true if the vector or one of its items was modified
- */
-bool
-playlist_vector_update_or_add(struct list_head *pv,
- const char *name, time_t mtime);
-
-/**
- * Caller must lock the #db_mutex.
- */
-bool
-playlist_vector_remove(struct list_head *pv, const char *name);
-
-#endif /* SONGVEC_H */
diff --git a/src/protocol/argparser.c b/src/protocol/ArgParser.cxx
index d20437cb..6bd53a35 100644
--- a/src/protocol/argparser.c
+++ b/src/protocol/ArgParser.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,14 +18,14 @@
*/
#include "config.h"
-#include "argparser.h"
-#include "result.h"
+#include "ArgParser.hxx"
+#include "Result.hxx"
#include <glib.h>
#include <stdlib.h>
bool
-check_uint32(struct client *client, uint32_t *dst, const char *s)
+check_uint32(Client *client, uint32_t *dst, const char *s)
{
char *test;
@@ -39,7 +39,7 @@ check_uint32(struct client *client, uint32_t *dst, const char *s)
}
bool
-check_int(struct client *client, int *value_r, const char *s)
+check_int(Client *client, int *value_r, const char *s)
{
char *test;
long value;
@@ -64,7 +64,7 @@ check_int(struct client *client, int *value_r, const char *s)
}
bool
-check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
+check_range(Client *client, unsigned *value_r1, unsigned *value_r2,
const char *s)
{
char *test, *test2;
@@ -134,7 +134,7 @@ check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
}
bool
-check_unsigned(struct client *client, unsigned *value_r, const char *s)
+check_unsigned(Client *client, unsigned *value_r, const char *s)
{
unsigned long value;
char *endptr;
@@ -157,7 +157,7 @@ check_unsigned(struct client *client, unsigned *value_r, const char *s)
}
bool
-check_bool(struct client *client, bool *value_r, const char *s)
+check_bool(Client *client, bool *value_r, const char *s)
{
long value;
char *endptr;
@@ -174,7 +174,7 @@ check_bool(struct client *client, bool *value_r, const char *s)
}
bool
-check_float(struct client *client, float *value_r, const char *s)
+check_float(Client *client, float *value_r, const char *s)
{
float value;
char *endptr;
diff --git a/src/protocol/argparser.h b/src/protocol/ArgParser.hxx
index e88aea47..b6feb3e6 100644
--- a/src/protocol/argparser.h
+++ b/src/protocol/ArgParser.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,33 +17,33 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PROTOCOL_ARGPARSER_H
-#define MPD_PROTOCOL_ARGPARSER_H
+#ifndef MPD_PROTOCOL_ARGPARSER_HXX
+#define MPD_PROTOCOL_ARGPARSER_HXX
#include "check.h"
#include <stdbool.h>
#include <stdint.h>
-struct client;
+class Client;
bool
-check_uint32(struct client *client, uint32_t *dst, const char *s);
+check_uint32(Client *client, uint32_t *dst, const char *s);
bool
-check_int(struct client *client, int *value_r, const char *s);
+check_int(Client *client, int *value_r, const char *s);
bool
-check_range(struct client *client, unsigned *value_r1, unsigned *value_r2,
+check_range(Client *client, unsigned *value_r1, unsigned *value_r2,
const char *s);
bool
-check_unsigned(struct client *client, unsigned *value_r, const char *s);
+check_unsigned(Client *client, unsigned *value_r, const char *s);
bool
-check_bool(struct client *client, bool *value_r, const char *s);
+check_bool(Client *client, bool *value_r, const char *s);
bool
-check_float(struct client *client, float *value_r, const char *s);
+check_float(Client *client, float *value_r, const char *s);
#endif
diff --git a/src/protocol/result.c b/src/protocol/Result.cxx
index 30cd0a26..e10a731c 100644
--- a/src/protocol/result.c
+++ b/src/protocol/Result.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,8 +18,8 @@
*/
#include "config.h"
-#include "result.h"
-#include "client.h"
+#include "Result.hxx"
+#include "Client.hxx"
#include <assert.h>
@@ -27,13 +27,13 @@ const char *current_command;
int command_list_num;
void
-command_success(struct client *client)
+command_success(Client *client)
{
client_puts(client, "OK\n");
}
void
-command_error_v(struct client *client, enum ack error,
+command_error_v(Client *client, enum ack error,
const char *fmt, va_list args)
{
assert(client != NULL);
@@ -48,7 +48,7 @@ command_error_v(struct client *client, enum ack error,
}
void
-command_error(struct client *client, enum ack error, const char *fmt, ...)
+command_error(Client *client, enum ack error, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
diff --git a/src/protocol/result.h b/src/protocol/Result.hxx
index 8b9e44bf..99d9a2fa 100644
--- a/src/protocol/result.h
+++ b/src/protocol/Result.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,28 +17,27 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_PROTOCOL_RESULT_H
-#define MPD_PROTOCOL_RESULT_H
+#ifndef MPD_PROTOCOL_RESULT_HXX
+#define MPD_PROTOCOL_RESULT_HXX
#include "check.h"
+#include "gcc.h"
#include "ack.h"
-#include <glib.h>
-
-struct client;
+class Client;
extern const char *current_command;
extern int command_list_num;
void
-command_success(struct client *client);
+command_success(Client *client);
void
-command_error_v(struct client *client, enum ack error,
+command_error_v(Client *client, enum ack error,
const char *fmt, va_list args);
-G_GNUC_PRINTF(3, 4)
+gcc_fprintf_
void
-command_error(struct client *client, enum ack error, const char *fmt, ...);
+command_error(Client *client, enum ack error, const char *fmt, ...);
#endif
diff --git a/src/queue.c b/src/queue.c
deleted file mode 100644
index 4fe564a3..00000000
--- a/src/queue.c
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "queue.h"
-#include "song.h"
-
-#include <stdlib.h>
-
-/**
- * Generate a non-existing id number.
- */
-static unsigned
-queue_generate_id(const struct queue *queue)
-{
- static unsigned cur = (unsigned)-1;
-
- do {
- cur++;
-
- if (cur >= queue->max_length * QUEUE_HASH_MULT)
- cur = 0;
- } while (queue->id_to_position[cur] != -1);
-
- return cur;
-}
-
-int
-queue_next_order(const struct queue *queue, unsigned order)
-{
- assert(order < queue->length);
-
- if (queue->single && queue->repeat && !queue->consume)
- return order;
- else if (order + 1 < queue->length)
- return order + 1;
- else if (queue->repeat && (order > 0 || !queue->consume))
- /* restart at first song */
- return 0;
- else
- /* end of queue */
- return -1;
-}
-
-void
-queue_increment_version(struct queue *queue)
-{
- static unsigned long max = ((uint32_t) 1 << 31) - 1;
-
- queue->version++;
-
- if (queue->version >= max) {
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = 0;
-
- queue->version = 1;
- }
-}
-
-void
-queue_modify(struct queue *queue, unsigned order)
-{
- unsigned position;
-
- assert(order < queue->length);
-
- position = queue->order[order];
- queue->items[position].version = queue->version;
-
- queue_increment_version(queue);
-}
-
-void
-queue_modify_all(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; i++)
- queue->items[i].version = queue->version;
-
- queue_increment_version(queue);
-}
-
-unsigned
-queue_append(struct queue *queue, struct song *song, uint8_t priority)
-{
- unsigned id = queue_generate_id(queue);
-
- assert(!queue_is_full(queue));
-
- queue->items[queue->length] = (struct queue_item){
- .song = song,
- .id = id,
- .version = queue->version,
- .priority = priority,
- };
-
- queue->order[queue->length] = queue->length;
- queue->id_to_position[id] = queue->length;
-
- ++queue->length;
-
- return id;
-}
-
-void
-queue_swap(struct queue *queue, unsigned position1, unsigned position2)
-{
- struct queue_item tmp;
- unsigned id1 = queue->items[position1].id;
- unsigned id2 = queue->items[position2].id;
-
- tmp = queue->items[position1];
- queue->items[position1] = queue->items[position2];
- queue->items[position2] = tmp;
-
- queue->items[position1].version = queue->version;
- queue->items[position2].version = queue->version;
-
- queue->id_to_position[id1] = position2;
- queue->id_to_position[id2] = position1;
-}
-
-static void
-queue_move_song_to(struct queue *queue, unsigned from, unsigned to)
-{
- unsigned from_id = queue->items[from].id;
-
- queue->items[to] = queue->items[from];
- queue->items[to].version = queue->version;
- queue->id_to_position[from_id] = to;
-}
-
-void
-queue_move(struct queue *queue, unsigned from, unsigned to)
-{
- struct queue_item item = queue->items[from];
-
- /* move songs to one less in from->to */
-
- for (unsigned i = from; i < to; i++)
- queue_move_song_to(queue, i + 1, i);
-
- /* move songs to one more in to->from */
-
- for (unsigned i = from; i > to; i--)
- queue_move_song_to(queue, i - 1, i);
-
- /* put song at _to_ */
-
- queue->id_to_position[item.id] = to;
- queue->items[to] = item;
- queue->items[to].version = queue->version;
-
- /* now deal with order */
-
- if (queue->random) {
- for (unsigned i = 0; i < queue->length; i++) {
- if (queue->order[i] > from && queue->order[i] <= to)
- queue->order[i]--;
- else if (queue->order[i] < from &&
- queue->order[i] >= to)
- queue->order[i]++;
- else if (from == queue->order[i])
- queue->order[i] = to;
- }
- }
-}
-
-void
-queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to)
-{
- struct queue_item items[end - start];
- // Copy the original block [start,end-1]
- for (unsigned i = start; i < end; i++)
- items[i - start] = queue->items[i];
-
- // If to > start, we need to move to-start items to start, starting from end
- for (unsigned i = end; i < end + to - start; i++)
- queue_move_song_to(queue, i, start + i - end);
-
- // If to < start, we need to move start-to items to newend (= end + to - start), starting from to
- // This is the same as moving items from start-1 to to (decreasing), with start-1 going to end-1
- // We have to iterate in this order to avoid writing over something we haven't yet moved
- for (unsigned i = start - 1; i >= to && i != G_MAXUINT; i--)
- queue_move_song_to(queue, i, i + end - start);
-
- // Copy the original block back in, starting at to.
- for (unsigned i = start; i< end; i++)
- {
- queue->id_to_position[items[i-start].id] = to + i - start;
- queue->items[to + i - start] = items[i-start];
- queue->items[to + i - start].version = queue->version;
- }
-
- if (queue->random) {
- // Update the positions in the queue.
- // Note that the ranges for these cases are the same as the ranges of
- // the loops above.
- for (unsigned i = 0; i < queue->length; i++) {
- if (queue->order[i] >= end && queue->order[i] < to + end - start)
- queue->order[i] -= end - start;
- else if (queue->order[i] < start &&
- queue->order[i] >= to)
- queue->order[i] += end - start;
- else if (start <= queue->order[i] && queue->order[i] < end)
- queue->order[i] += to - start;
- }
- }
-}
-
-/**
- * Moves a song to a new position in the "order" list.
- */
-static void
-queue_move_order(struct queue *queue, unsigned from_order, unsigned to_order)
-{
- assert(queue != NULL);
- assert(from_order < queue->length);
- assert(to_order <= queue->length);
-
- const unsigned from_position =
- queue_order_to_position(queue, from_order);
-
- if (from_order < to_order) {
- for (unsigned i = from_order; i < to_order; ++i)
- queue->order[i] = queue->order[i + 1];
- } else {
- for (unsigned i = from_order; i > to_order; --i)
- queue->order[i] = queue->order[i - 1];
- }
-
- queue->order[to_order] = from_position;
-}
-
-void
-queue_delete(struct queue *queue, unsigned position)
-{
- struct song *song;
- unsigned id, order;
-
- assert(position < queue->length);
-
- song = queue_get(queue, position);
- if (!song_in_database(song))
- song_free(song);
-
- id = queue_position_to_id(queue, position);
- order = queue_position_to_order(queue, position);
-
- --queue->length;
-
- /* release the song id */
-
- queue->id_to_position[id] = -1;
-
- /* delete song from songs array */
-
- for (unsigned i = position; i < queue->length; i++)
- queue_move_song_to(queue, i + 1, i);
-
- /* delete the entry from the order array */
-
- for (unsigned i = order; i < queue->length; i++)
- queue->order[i] = queue->order[i + 1];
-
- /* readjust values in the order array */
-
- for (unsigned i = 0; i < queue->length; i++)
- if (queue->order[i] > position)
- --queue->order[i];
-}
-
-void
-queue_clear(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; i++) {
- struct queue_item *item = &queue->items[i];
-
- if (!song_in_database(item->song))
- song_free(item->song);
-
- queue->id_to_position[item->id] = -1;
- }
-
- queue->length = 0;
-}
-
-void
-queue_init(struct queue *queue, unsigned max_length)
-{
- queue->max_length = max_length;
- queue->length = 0;
- queue->version = 1;
- queue->repeat = false;
- queue->random = false;
- queue->single = false;
- queue->consume = false;
-
- queue->items = g_new(struct queue_item, max_length);
- queue->order = g_malloc(sizeof(queue->order[0]) *
- max_length);
- queue->id_to_position = g_malloc(sizeof(queue->id_to_position[0]) *
- max_length * QUEUE_HASH_MULT);
-
- for (unsigned i = 0; i < max_length * QUEUE_HASH_MULT; ++i)
- queue->id_to_position[i] = -1;
-
- queue->rand = g_rand_new();
-}
-
-void
-queue_finish(struct queue *queue)
-{
- queue_clear(queue);
-
- g_free(queue->items);
- g_free(queue->order);
- g_free(queue->id_to_position);
-
- g_rand_free(queue->rand);
-}
-
-static const struct queue_item *
-queue_get_order_item_const(const struct queue *queue, unsigned order)
-{
- assert(queue != NULL);
- assert(order < queue->length);
-
- return &queue->items[queue->order[order]];
-}
-
-static uint8_t
-queue_get_order_priority(const struct queue *queue, unsigned order)
-{
- return queue_get_order_item_const(queue, order)->priority;
-}
-
-static gint
-queue_item_compare_order_priority(gconstpointer av, gconstpointer bv,
- gpointer user_data)
-{
- const struct queue *queue = user_data;
- const unsigned *const ap = av;
- const unsigned *const bp = bv;
- assert(ap >= queue->order && ap < queue->order + queue->length);
- assert(bp >= queue->order && bp < queue->order + queue->length);
- uint8_t a = queue->items[*ap].priority;
- uint8_t b = queue->items[*bp].priority;
-
- if (G_LIKELY(a == b))
- return 0;
- else if (a > b)
- return -1;
- else
- return 1;
-}
-
-static void
-queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= end);
- assert(end <= queue->length);
-
- g_qsort_with_data(&queue->order[start], end - start,
- sizeof(queue->order[0]),
- queue_item_compare_order_priority,
- queue);
-}
-
-/**
- * Shuffle the order of items in the specified range, ignoring their
- * priorities.
- */
-static void
-queue_shuffle_order_range(struct queue *queue, unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= end);
- assert(end <= queue->length);
-
- for (unsigned i = start; i < end; ++i)
- queue_swap_order(queue, i,
- g_rand_int_range(queue->rand, i, end));
-}
-
-/**
- * Sort the "order" of items by priority, and then shuffle each
- * priority group.
- */
-void
-queue_shuffle_order_range_with_priority(struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start <= end);
- assert(end <= queue->length);
-
- if (start == end)
- return;
-
- /* first group the range by priority */
- queue_sort_order_by_priority(queue, start, end);
-
- /* now shuffle each priority group */
- unsigned group_start = start;
- uint8_t group_priority = queue_get_order_priority(queue, start);
-
- for (unsigned i = start + 1; i < end; ++i) {
- uint8_t priority = queue_get_order_priority(queue, i);
- assert(priority <= group_priority);
-
- if (priority != group_priority) {
- /* start of a new group - shuffle the one that
- has just ended */
- queue_shuffle_order_range(queue, group_start, i);
- group_start = i;
- group_priority = priority;
- }
- }
-
- /* shuffle the last group */
- queue_shuffle_order_range(queue, group_start, end);
-}
-
-void
-queue_shuffle_order(struct queue *queue)
-{
- queue_shuffle_order_range_with_priority(queue, 0, queue->length);
-}
-
-static void
-queue_shuffle_order_first(struct queue *queue, unsigned start, unsigned end)
-{
- queue_swap_order(queue, start,
- g_rand_int_range(queue->rand, start, end));
-}
-
-void
-queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end)
-{
- queue_swap_order(queue, end - 1,
- g_rand_int_range(queue->rand, start, end));
-}
-
-void
-queue_shuffle_range(struct queue *queue, unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue->length);
-
- for (unsigned i = start; i < end; i++) {
- unsigned ri = g_rand_int_range(queue->rand, i, end);
- queue_swap(queue, i, ri);
- }
-}
-
-/**
- * Find the first item that has this specified priority or higher.
- */
-G_GNUC_PURE
-static unsigned
-queue_find_priority_order(const struct queue *queue, unsigned start_order,
- uint8_t priority, unsigned exclude_order)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start_order <= queue->length);
-
- for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
- const struct queue_item *item = &queue->items[position];
- if (item->priority <= priority && order != exclude_order)
- return order;
- }
-
- return queue->length;
-}
-
-G_GNUC_PURE
-static unsigned
-queue_count_same_priority(const struct queue *queue, unsigned start_order,
- uint8_t priority)
-{
- assert(queue != NULL);
- assert(queue->random);
- assert(start_order <= queue->length);
-
- for (unsigned order = start_order; order < queue->length; ++order) {
- const unsigned position = queue_order_to_position(queue, order);
- const struct queue_item *item = &queue->items[position];
- if (item->priority != priority)
- return order - start_order;
- }
-
- return queue->length - start_order;
-}
-
-bool
-queue_set_priority(struct queue *queue, unsigned position, uint8_t priority,
- int after_order)
-{
- assert(queue != NULL);
- assert(position < queue->length);
-
- struct queue_item *item = &queue->items[position];
- uint8_t old_priority = item->priority;
- if (old_priority == priority)
- return false;
-
- item->version = queue->version;
- item->priority = priority;
-
- if (!queue->random)
- /* don't reorder if not in random mode */
- return true;
-
- unsigned order = queue_position_to_order(queue, position);
- if (after_order >= 0) {
- if (order == (unsigned)after_order)
- /* don't reorder the current song */
- return true;
-
- if (order < (unsigned)after_order) {
- /* the specified song has been played already
- - enqueue it only if its priority has just
- become bigger than the current one's */
-
- const unsigned after_position =
- queue_order_to_position(queue, after_order);
- const struct queue_item *after_item =
- &queue->items[after_position];
- if (old_priority > after_item->priority ||
- priority <= after_item->priority)
- /* priority hasn't become bigger */
- return true;
- }
- }
-
- /* move the item to the beginning of the priority group (or
- create a new priority group) */
-
- const unsigned before_order =
- queue_find_priority_order(queue, after_order + 1, priority,
- order);
- const unsigned new_order = before_order > order
- ? before_order - 1
- : before_order;
- queue_move_order(queue, order, new_order);
-
- /* shuffle the song within that priority group */
-
- const unsigned priority_count =
- queue_count_same_priority(queue, new_order, priority);
- assert(priority_count >= 1);
- queue_shuffle_order_first(queue, new_order,
- new_order + priority_count);
-
- return true;
-}
-
-bool
-queue_set_priority_range(struct queue *queue,
- unsigned start_position, unsigned end_position,
- uint8_t priority, int after_order)
-{
- assert(queue != NULL);
- assert(start_position <= end_position);
- assert(end_position <= queue->length);
-
- bool modified = false;
- int after_position = after_order >= 0
- ? (int)queue_order_to_position(queue, after_order)
- : -1;
- for (unsigned i = start_position; i < end_position; ++i) {
- after_order = after_position >= 0
- ? (int)queue_position_to_order(queue, after_position)
- : -1;
-
- modified |= queue_set_priority(queue, i, priority,
- after_order);
- }
-
- return modified;
-}
diff --git a/src/queue.h b/src/queue.h
deleted file mode 100644
index e4bfcdff..00000000
--- a/src/queue.h
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef QUEUE_H
-#define QUEUE_H
-
-#include <glib.h>
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-enum {
- /**
- * reserve max_length * QUEUE_HASH_MULT elements in the id
- * number space
- */
- QUEUE_HASH_MULT = 4,
-};
-
-/**
- * One element of the queue: basically a song plus some queue specific
- * information attached.
- */
-struct queue_item {
- struct song *song;
-
- /** the unique id of this item in the queue */
- unsigned id;
-
- /** when was this item last changed? */
- uint32_t version;
-
- /**
- * The priority of this item, between 0 and 255. High
- * priority value means that this song gets played first in
- * "random" mode.
- */
- uint8_t priority;
-};
-
-/**
- * A queue of songs. This is the backend of the playlist: it contains
- * an ordered list of songs.
- *
- * Songs can be addressed in three possible ways:
- *
- * - the position in the queue
- * - the unique id (which stays the same, regardless of moves)
- * - the order number (which only differs from "position" in random mode)
- */
-struct queue {
- /** configured maximum length of the queue */
- unsigned max_length;
-
- /** number of songs in the queue */
- unsigned length;
-
- /** the current version number */
- uint32_t version;
-
- /** all songs in "position" order */
- struct queue_item *items;
-
- /** map order numbers to positions */
- unsigned *order;
-
- /** map song ids to positions */
- int *id_to_position;
-
- /** repeat playback when the end of the queue has been
- reached? */
- bool repeat;
-
- /** play only current song. */
- bool single;
-
- /** remove each played files. */
- bool consume;
-
- /** play back songs in random order? */
- bool random;
-
- /** random number generator for shuffle and random mode */
- GRand *rand;
-};
-
-static inline unsigned
-queue_length(const struct queue *queue)
-{
- assert(queue->length <= queue->max_length);
-
- return queue->length;
-}
-
-/**
- * Determine if the queue is empty, i.e. there are no songs.
- */
-static inline bool
-queue_is_empty(const struct queue *queue)
-{
- return queue->length == 0;
-}
-
-/**
- * Determine if the maximum number of songs has been reached.
- */
-static inline bool
-queue_is_full(const struct queue *queue)
-{
- assert(queue->length <= queue->max_length);
-
- return queue->length >= queue->max_length;
-}
-
-/**
- * Is that a valid position number?
- */
-static inline bool
-queue_valid_position(const struct queue *queue, unsigned position)
-{
- return position < queue->length;
-}
-
-/**
- * Is that a valid order number?
- */
-static inline bool
-queue_valid_order(const struct queue *queue, unsigned order)
-{
- return order < queue->length;
-}
-
-static inline int
-queue_id_to_position(const struct queue *queue, unsigned id)
-{
- if (id >= queue->max_length * QUEUE_HASH_MULT)
- return -1;
-
- assert(queue->id_to_position[id] >= -1);
- assert(queue->id_to_position[id] < (int)queue->length);
-
- return queue->id_to_position[id];
-}
-
-static inline int
-queue_position_to_id(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].id;
-}
-
-static inline unsigned
-queue_order_to_position(const struct queue *queue, unsigned order)
-{
- assert(order < queue->length);
-
- return queue->order[order];
-}
-
-static inline unsigned
-queue_position_to_order(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- for (unsigned i = 0;; ++i) {
- assert(i < queue->length);
-
- if (queue->order[i] == position)
- return i;
- }
-}
-
-G_GNUC_PURE
-static inline uint8_t
-queue_get_priority_at_position(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].priority;
-}
-
-/**
- * Returns the song at the specified position.
- */
-static inline struct song *
-queue_get(const struct queue *queue, unsigned position)
-{
- assert(position < queue->length);
-
- return queue->items[position].song;
-}
-
-/**
- * Returns the song at the specified order number.
- */
-static inline struct song *
-queue_get_order(const struct queue *queue, unsigned order)
-{
- return queue_get(queue, queue_order_to_position(queue, order));
-}
-
-/**
- * Is the song at the specified position newer than the specified
- * version?
- */
-static inline bool
-queue_song_newer(const struct queue *queue, unsigned position,
- uint32_t version)
-{
- assert(position < queue->length);
-
- return version > queue->version ||
- queue->items[position].version >= version ||
- queue->items[position].version == 0;
-}
-
-/**
- * Initialize a queue object.
- */
-void
-queue_init(struct queue *queue, unsigned max_length);
-
-/**
- * Deinitializes a queue object. It does not free the queue pointer
- * itself.
- */
-void
-queue_finish(struct queue *queue);
-
-/**
- * Returns the order number following the specified one. This takes
- * end of queue and "repeat" mode into account.
- *
- * @return the next order number, or -1 to stop playback
- */
-int
-queue_next_order(const struct queue *queue, unsigned order);
-
-/**
- * Increments the queue's version number. This handles integer
- * overflow well.
- */
-void
-queue_increment_version(struct queue *queue);
-
-/**
- * Marks the specified song as "modified" and increments the version
- * number.
- */
-void
-queue_modify(struct queue *queue, unsigned order);
-
-/**
- * Marks all songs as "modified" and increments the version number.
- */
-void
-queue_modify_all(struct queue *queue);
-
-/**
- * Appends a song to the queue and returns its position. Prior to
- * that, the caller must check if the queue is already full.
- *
- * If a song is not in the database (determined by
- * song_in_database()), it is freed when removed from the queue.
- *
- * @param priority the priority of this new queue item
- */
-unsigned
-queue_append(struct queue *queue, struct song *song, uint8_t priority);
-
-/**
- * Swaps two songs, addressed by their position.
- */
-void
-queue_swap(struct queue *queue, unsigned position1, unsigned position2);
-
-/**
- * Swaps two songs, addressed by their order number.
- */
-static inline void
-queue_swap_order(struct queue *queue, unsigned order1, unsigned order2)
-{
- unsigned tmp = queue->order[order1];
- queue->order[order1] = queue->order[order2];
- queue->order[order2] = tmp;
-}
-
-/**
- * Moves a song to a new position.
- */
-void
-queue_move(struct queue *queue, unsigned from, unsigned to);
-
-/**
- * Moves a range of songs to a new position.
- */
-void
-queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to);
-
-/**
- * Removes a song from the playlist.
- */
-void
-queue_delete(struct queue *queue, unsigned position);
-
-/**
- * Removes all songs from the playlist.
- */
-void
-queue_clear(struct queue *queue);
-
-/**
- * Initializes the "order" array, and restores "normal" order.
- */
-static inline void
-queue_restore_order(struct queue *queue)
-{
- for (unsigned i = 0; i < queue->length; ++i)
- queue->order[i] = i;
-}
-
-/**
- * Shuffle the order of items in the specified range, taking their
- * priorities into account.
- */
-void
-queue_shuffle_order_range_with_priority(struct queue *queue,
- unsigned start, unsigned end);
-
-/**
- * Shuffles the virtual order of songs, but does not move them
- * physically. This is used in random mode.
- */
-void
-queue_shuffle_order(struct queue *queue);
-
-/**
- * Shuffles the virtual order of the last song in the specified
- * (order) range. This is used in random mode after a song has been
- * appended by queue_append().
- */
-void
-queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end);
-
-/**
- * Shuffles a (position) range in the queue. The songs are physically
- * shuffled, not by using the "order" mapping.
- */
-void
-queue_shuffle_range(struct queue *queue, unsigned start, unsigned end);
-
-bool
-queue_set_priority(struct queue *queue, unsigned position,
- uint8_t priority, int after_order);
-
-bool
-queue_set_priority_range(struct queue *queue,
- unsigned start_position, unsigned end_position,
- uint8_t priority, int after_order);
-
-#endif
diff --git a/src/queue_print.c b/src/queue_print.c
deleted file mode 100644
index d149e8b6..00000000
--- a/src/queue_print.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "queue_print.h"
-#include "queue.h"
-#include "song.h"
-#include "song_print.h"
-#include "locate.h"
-#include "client.h"
-#include "mapper.h"
-
-/**
- * Send detailed information about a range of songs in the queue to a
- * client.
- *
- * @param client the client which has requested information
- * @param start the index of the first song (including)
- * @param end the index of the last song (excluding)
- */
-static void
-queue_print_song_info(struct client *client, const struct queue *queue,
- unsigned position)
-{
- song_print_info(client, queue_get(queue, position));
- client_printf(client, "Pos: %u\nId: %u\n",
- position, queue_position_to_id(queue, position));
-
- uint8_t priority = queue_get_priority_at_position(queue, position);
- if (priority != 0)
- client_printf(client, "Prio: %u\n", priority);
-}
-
-void
-queue_print_info(struct client *client, const struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue_length(queue));
-
- for (unsigned i = start; i < end; ++i)
- queue_print_song_info(client, queue, i);
-}
-
-void
-queue_print_uris(struct client *client, const struct queue *queue,
- unsigned start, unsigned end)
-{
- assert(start <= end);
- assert(end <= queue_length(queue));
-
- for (unsigned i = start; i < end; ++i) {
- client_printf(client, "%i:", i);
- song_print_uri(client, queue_get(queue, i));
- }
-}
-
-void
-queue_print_changes_info(struct client *client, const struct queue *queue,
- uint32_t version)
-{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- if (queue_song_newer(queue, i, version))
- queue_print_song_info(client, queue, i);
- }
-}
-
-void
-queue_print_changes_position(struct client *client, const struct queue *queue,
- uint32_t version)
-{
- for (unsigned i = 0; i < queue_length(queue); i++)
- if (queue_song_newer(queue, i, version))
- client_printf(client, "cpos: %i\nId: %i\n",
- i, queue_position_to_id(queue, i));
-}
-
-void
-queue_search(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria)
-{
- unsigned i;
- struct locate_item_list *new_list =
- locate_item_list_casefold(criteria);
-
- for (i = 0; i < queue_length(queue); i++) {
- const struct song *song = queue_get(queue, i);
-
- if (locate_song_search(song, new_list))
- queue_print_song_info(client, queue, i);
- }
-
- locate_item_list_free(new_list);
-}
-
-void
-queue_find(struct client *client, const struct queue *queue,
- const struct locate_item_list *criteria)
-{
- for (unsigned i = 0; i < queue_length(queue); i++) {
- const struct song *song = queue_get(queue, i);
-
- if (locate_song_match(song, criteria))
- queue_print_song_info(client, queue, i);
- }
-}
diff --git a/src/replay_gain_config.h b/src/replay_gain_config.h
index 18747cef..4b59334c 100644
--- a/src/replay_gain_config.h
+++ b/src/replay_gain_config.h
@@ -30,6 +30,10 @@ extern float replay_gain_preamp;
extern float replay_gain_missing_preamp;
extern bool replay_gain_limit;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void replay_gain_global_init(void);
/**
@@ -50,6 +54,10 @@ replay_gain_set_mode_string(const char *p);
* Returns the "real" mode according to the "auto" setting"
*/
enum replay_gain_mode
-replay_gain_get_real_mode(void);
+replay_gain_get_real_mode(bool random_mode);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/replay_gain_info.h b/src/replay_gain_info.h
index 9097c3e0..b06dc6cf 100644
--- a/src/replay_gain_info.h
+++ b/src/replay_gain_info.h
@@ -22,8 +22,12 @@
#include "check.h"
+#ifdef __cplusplus
+#include <cmath>
+#else
#include <stdbool.h>
#include <math.h>
+#endif
enum replay_gain_mode {
REPLAY_GAIN_AUTO = -2,
@@ -58,9 +62,17 @@ replay_gain_info_init(struct replay_gain_info *info)
static inline bool
replay_gain_tuple_defined(const struct replay_gain_tuple *tuple)
{
+#ifdef __cplusplus
+ return !std::isinf(tuple->gain);
+#else
return !isinf(tuple->gain);
+#endif
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
float
replay_gain_tuple_scale(const struct replay_gain_tuple *tuple, float preamp, float missing_preamp, bool peak_limit);
@@ -71,4 +83,8 @@ replay_gain_tuple_scale(const struct replay_gain_tuple *tuple, float preamp, flo
void
replay_gain_info_complete(struct replay_gain_info *info);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/resolver.h b/src/resolver.h
index e5ad0675..af14f5f2 100644
--- a/src/resolver.h
+++ b/src/resolver.h
@@ -20,18 +20,24 @@
#ifndef MPD_RESOLVER_H
#define MPD_RESOLVER_H
+#include "gcc.h"
+
#include <glib.h>
struct sockaddr;
struct addrinfo;
-G_GNUC_CONST
+gcc_const
static inline GQuark
resolver_quark(void)
{
return g_quark_from_static_string("resolver");
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Converts the specified socket address into a string in the form
* "IP:PORT". The return value must be freed with g_free() when you
@@ -42,7 +48,7 @@ resolver_quark(void)
* @param error location to store the error occurring, or NULL to
* ignore errors
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
sockaddr_to_string(const struct sockaddr *sa, size_t length, GError **error);
@@ -61,4 +67,8 @@ resolve_host_port(const char *host_port, unsigned default_port,
int flags, int socktype,
GError **error_r);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/server_socket.c b/src/server_socket.c
deleted file mode 100644
index 39639959..00000000
--- a/src/server_socket.c
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-
-#ifdef HAVE_STRUCT_UCRED
-#define _GNU_SOURCE 1
-#endif
-
-#include "server_socket.h"
-#include "socket_util.h"
-#include "resolver.h"
-#include "fd_util.h"
-#include "glib_socket.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#ifdef WIN32
-#include <ws2tcpip.h>
-#include <winsock.h>
-#else
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netdb.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "listen"
-
-#define DEFAULT_PORT 6600
-
-struct one_socket {
- struct one_socket *next;
- struct server_socket *parent;
-
- unsigned serial;
-
- int fd;
- guint source_id;
-
- char *path;
-
- size_t address_length;
- struct sockaddr address;
-};
-
-struct server_socket {
- server_socket_callback_t callback;
- void *callback_ctx;
-
- struct one_socket *sockets, **sockets_tail_r;
- unsigned next_serial;
-};
-
-static GQuark
-server_socket_quark(void)
-{
- return g_quark_from_static_string("server_socket");
-}
-
-struct server_socket *
-server_socket_new(server_socket_callback_t callback, void *callback_ctx)
-{
- struct server_socket *ss = g_new(struct server_socket, 1);
- ss->callback = callback;
- ss->callback_ctx = callback_ctx;
- ss->sockets = NULL;
- ss->sockets_tail_r = &ss->sockets;
- ss->next_serial = 1;
- return ss;
-}
-
-void
-server_socket_free(struct server_socket *ss)
-{
- server_socket_close(ss);
-
- while (ss->sockets != NULL) {
- struct one_socket *s = ss->sockets;
- ss->sockets = s->next;
-
- assert(s->fd < 0);
-
- g_free(s->path);
- g_free(s);
- }
-
- g_free(ss);
-}
-
-/**
- * Wraper for sockaddr_to_string() which never fails.
- */
-static char *
-one_socket_to_string(const struct one_socket *s)
-{
- char *p = sockaddr_to_string(&s->address, s->address_length, NULL);
- if (p == NULL)
- p = g_strdup("[unknown]");
- return p;
-}
-
-static int
-get_remote_uid(int fd)
-{
-#ifdef HAVE_STRUCT_UCRED
- struct ucred cred;
- socklen_t len = sizeof (cred);
-
- if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0)
- return 0;
-
- return cred.uid;
-#else
-#ifdef HAVE_GETPEEREID
- uid_t euid;
- gid_t egid;
-
- if (getpeereid(fd, &euid, &egid) == 0)
- return euid;
-#else
- (void)fd;
-#endif
- return -1;
-#endif
-}
-
-static gboolean
-server_socket_in_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- gpointer data)
-{
- struct one_socket *s = data;
-
- struct sockaddr_storage address;
- size_t address_length = sizeof(address);
- int fd = accept_cloexec_nonblock(s->fd, (struct sockaddr*)&address,
- &address_length);
- if (fd >= 0) {
- if (socket_keepalive(fd))
- g_warning("Could not set TCP keepalive option: %s",
- g_strerror(errno));
- s->parent->callback(fd, (const struct sockaddr*)&address,
- address_length, get_remote_uid(fd),
- s->parent->callback_ctx);
- } else {
- g_warning("accept() failed: %s", g_strerror(errno));
- }
-
- return true;
-}
-
-static void
-set_fd(struct one_socket *s, int fd)
-{
- assert(s != NULL);
- assert(s->fd < 0);
- assert(fd >= 0);
-
- s->fd = fd;
-
- GIOChannel *channel = g_io_channel_new_socket(s->fd);
- s->source_id = g_io_add_watch(channel, G_IO_IN,
- server_socket_in_event, s);
- g_io_channel_unref(channel);
-}
-
-bool
-server_socket_open(struct server_socket *ss, GError **error_r)
-{
- struct one_socket *good = NULL, *bad = NULL;
- GError *last_error = NULL;
-
- for (struct one_socket *s = ss->sockets; s != NULL; s = s->next) {
- assert(s->serial > 0);
- assert(good == NULL || s->serial >= good->serial);
- assert(s->fd < 0);
-
- if (bad != NULL && s->serial != bad->serial) {
- server_socket_close(ss);
- g_propagate_error(error_r, last_error);
- return false;
- }
-
- GError *error = NULL;
- int fd = socket_bind_listen(s->address.sa_family,
- SOCK_STREAM, 0,
- &s->address, s->address_length, 5,
- &error);
- if (fd < 0) {
- if (good != NULL && good->serial == s->serial) {
- char *address_string = one_socket_to_string(s);
- char *good_string = one_socket_to_string(good);
- g_warning("bind to '%s' failed: %s "
- "(continuing anyway, because "
- "binding to '%s' succeeded)",
- address_string, error->message,
- good_string);
- g_free(address_string);
- g_free(good_string);
- g_error_free(error);
- } else if (bad == NULL) {
- bad = s;
-
- char *address_string = one_socket_to_string(s);
- g_propagate_prefixed_error(&last_error, error,
- "Failed to bind to '%s': ",
- address_string);
- g_free(address_string);
- } else
- g_error_free(error);
- continue;
- }
-
- /* allow everybody to connect */
-
- if (s->path != NULL)
- chmod(s->path, 0666);
-
- /* register in the GLib main loop */
-
- set_fd(s, fd);
-
- /* mark this socket as "good", and clear previous
- errors */
-
- good = s;
-
- if (bad != NULL) {
- bad = NULL;
- g_error_free(last_error);
- last_error = NULL;
- }
- }
-
- if (bad != NULL) {
- server_socket_close(ss);
- g_propagate_error(error_r, last_error);
- return false;
- }
-
- return true;
-}
-
-void
-server_socket_close(struct server_socket *ss)
-{
- for (struct one_socket *s = ss->sockets; s != NULL; s = s->next) {
- if (s->fd < 0)
- continue;
-
- g_source_remove(s->source_id);
- close_socket(s->fd);
- s->fd = -1;
- }
-}
-
-static struct one_socket *
-one_socket_new(unsigned serial, const struct sockaddr *address,
- size_t address_length)
-{
- assert(address != NULL);
- assert(address_length > 0);
-
- struct one_socket *s = g_malloc(sizeof(*s) - sizeof(s->address) +
- address_length);
- s->next = NULL;
- s->serial = serial;
- s->fd = -1;
- s->path = NULL;
- s->address_length = address_length;
- memcpy(&s->address, address, address_length);
-
- return s;
-}
-
-bool
-server_socket_add_fd(struct server_socket *ss, int fd, GError **error_r)
-{
- assert(ss != NULL);
- assert(ss->sockets_tail_r != NULL);
- assert(*ss->sockets_tail_r == NULL);
- assert(fd >= 0);
-
- struct sockaddr_storage address;
- socklen_t address_length;
- if (getsockname(fd, (struct sockaddr *)&address,
- &address_length) < 0) {
- g_set_error(error_r, server_socket_quark(), errno,
- "Failed to get socket address: %s",
- g_strerror(errno));
- return false;
- }
-
- struct one_socket *s = one_socket_new(ss->next_serial,
- (struct sockaddr *)&address,
- address_length);
- s->parent = ss;
- *ss->sockets_tail_r = s;
- ss->sockets_tail_r = &s->next;
-
- set_fd(s, fd);
-
- return true;
-}
-
-static struct one_socket *
-server_socket_add_address(struct server_socket *ss,
- const struct sockaddr *address,
- size_t address_length)
-{
- assert(ss != NULL);
- assert(ss->sockets_tail_r != NULL);
- assert(*ss->sockets_tail_r == NULL);
-
- struct one_socket *s = one_socket_new(ss->next_serial,
- address, address_length);
- s->parent = ss;
- *ss->sockets_tail_r = s;
- ss->sockets_tail_r = &s->next;
-
- return s;
-}
-
-#ifdef HAVE_TCP
-
-/**
- * Add a listener on a port on all IPv4 interfaces.
- *
- * @param port the TCP port
- */
-static void
-server_socket_add_port_ipv4(struct server_socket *ss, unsigned port)
-{
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin_port = htons(port);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
-
- server_socket_add_address(ss, (const struct sockaddr *)&sin,
- sizeof(sin));
-}
-
-#ifdef HAVE_IPV6
-/**
- * Add a listener on a port on all IPv6 interfaces.
- *
- * @param port the TCP port
- */
-static void
-server_socket_add_port_ipv6(struct server_socket *ss, unsigned port)
-{
- struct sockaddr_in6 sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin6_port = htons(port);
- sin.sin6_family = AF_INET6;
-
- server_socket_add_address(ss, (const struct sockaddr *)&sin,
- sizeof(sin));
-}
-#endif /* HAVE_IPV6 */
-
-#endif /* HAVE_TCP */
-
-bool
-server_socket_add_port(struct server_socket *ss, unsigned port,
- GError **error_r)
-{
-#ifdef HAVE_TCP
- if (port == 0 || port > 0xffff) {
- g_set_error(error_r, server_socket_quark(), 0,
- "Invalid TCP port");
- return false;
- }
-
-#ifdef HAVE_IPV6
- server_socket_add_port_ipv6(ss, port);
-#endif
- server_socket_add_port_ipv4(ss, port);
-
- ++ss->next_serial;
-
- return true;
-#else /* HAVE_TCP */
- (void)ss;
- (void)port;
-
- g_set_error(error_r, server_socket_quark(), 0,
- "TCP support is disabled");
- return false;
-#endif /* HAVE_TCP */
-}
-
-bool
-server_socket_add_host(struct server_socket *ss, const char *hostname,
- unsigned port, GError **error_r)
-{
-#ifdef HAVE_TCP
- struct addrinfo *ai = resolve_host_port(hostname, port,
- AI_PASSIVE, SOCK_STREAM,
- error_r);
- if (ai == NULL)
- return false;
-
- for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next)
- server_socket_add_address(ss, i->ai_addr, i->ai_addrlen);
-
- freeaddrinfo(ai);
-
- ++ss->next_serial;
-
- return true;
-#else /* HAVE_TCP */
- (void)ss;
- (void)hostname;
- (void)port;
-
- g_set_error(error_r, server_socket_quark(), 0,
- "TCP support is disabled");
- return false;
-#endif /* HAVE_TCP */
-}
-
-bool
-server_socket_add_path(struct server_socket *ss, const char *path,
- GError **error_r)
-{
-#ifdef HAVE_UN
- struct sockaddr_un s_un;
-
- size_t path_length = strlen(path);
- if (path_length >= sizeof(s_un.sun_path)) {
- g_set_error(error_r, server_socket_quark(), 0,
- "UNIX socket path is too long");
- return false;
- }
-
- unlink(path);
-
- s_un.sun_family = AF_UNIX;
- memcpy(s_un.sun_path, path, path_length + 1);
-
- struct one_socket *s =
- server_socket_add_address(ss, (const struct sockaddr *)&s_un,
- sizeof(s_un));
- s->path = g_strdup(path);
-
- return true;
-#else /* !HAVE_UN */
- (void)ss;
- (void)path;
-
- g_set_error(error_r, server_socket_quark(), 0,
- "UNIX domain socket support is disabled");
- return false;
-#endif /* !HAVE_UN */
-}
-
diff --git a/src/server_socket.h b/src/server_socket.h
deleted file mode 100644
index 7caa4bbf..00000000
--- a/src/server_socket.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_SERVER_SOCKET_H
-#define MPD_SERVER_SOCKET_H
-
-#include <stdbool.h>
-
-#include <glib.h>
-
-struct sockaddr;
-
-typedef void (*server_socket_callback_t)(int fd,
- const struct sockaddr *address,
- size_t address_length, int uid,
- void *ctx);
-
-struct server_socket *
-server_socket_new(server_socket_callback_t callback, void *callback_ctx);
-
-void
-server_socket_free(struct server_socket *ss);
-
-bool
-server_socket_open(struct server_socket *ss, GError **error_r);
-
-void
-server_socket_close(struct server_socket *ss);
-
-/**
- * Add a socket descriptor that is accepting connections. After this
- * has been called, don't call server_socket_open(), because the
- * socket is already open.
- */
-bool
-server_socket_add_fd(struct server_socket *ss, int fd, GError **error_r);
-
-/**
- * Add a listener on a port on all interfaces.
- *
- * @param port the TCP port
- * @param error_r location to store the error occurring, or NULL to
- * ignore errors
- * @return true on success
- */
-bool
-server_socket_add_port(struct server_socket *ss, unsigned port,
- GError **error_r);
-
-/**
- * Resolves a host name, and adds listeners on all addresses in the
- * result set.
- *
- * @param hostname the host name to be resolved
- * @param port the TCP port
- * @param error_r location to store the error occurring, or NULL to
- * ignore errors
- * @return true on success
- */
-bool
-server_socket_add_host(struct server_socket *ss, const char *hostname,
- unsigned port, GError **error_r);
-
-/**
- * Add a listener on a Unix domain socket.
- *
- * @param path the absolute socket path
- * @param error_r location to store the error occurring, or NULL to
- * ignore errors
- * @return true on success
- */
-bool
-server_socket_add_path(struct server_socket *ss, const char *path,
- GError **error_r);
-
-#endif
diff --git a/src/song.h b/src/song.h
index 8b97d45d..4095317f 100644
--- a/src/song.h
+++ b/src/song.h
@@ -21,7 +21,9 @@
#define MPD_SONG_H
#include "util/list.h"
+#include "gcc.h"
+#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/time.h>
@@ -41,7 +43,7 @@ struct song {
struct list_head siblings;
struct tag *tag;
- struct directory *parent;
+ struct Directory *parent;
time_t mtime;
/**
@@ -58,13 +60,23 @@ struct song {
char uri[sizeof(int)];
};
+/**
+ * A dummy #directory instance that is used for "detached" song
+ * copies.
+ */
+extern struct Directory detached_root;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** allocate a new song with a remote URL */
struct song *
song_remote_new(const char *uri);
/** allocate a new song with a local file name */
struct song *
-song_file_new(const char *path, struct directory *parent);
+song_file_new(const char *path_utf8, struct Directory *parent);
/**
* allocate a new song structure with a local file name and attempt to
@@ -72,7 +84,7 @@ song_file_new(const char *path, struct directory *parent);
* data, NULL is returned.
*/
struct song *
-song_file_load(const char *path, struct directory *parent);
+song_file_load(const char *path_utf8, struct Directory *parent);
/**
* Replaces the URI of a song object. The given song object is
@@ -83,9 +95,52 @@ song_file_load(const char *path, struct directory *parent);
struct song *
song_replace_uri(struct song *song, const char *uri);
+/**
+ * Creates a "detached" song object.
+ */
+struct song *
+song_detached_new(const char *uri);
+
+/**
+ * Creates a duplicate of the song object. If the object is in the
+ * database, it creates a "detached" copy of this song, see
+ * song_is_detached().
+ */
+gcc_malloc
+struct song *
+song_dup_detached(const struct song *src);
+
void
song_free(struct song *song);
+static inline bool
+song_in_database(const struct song *song)
+{
+ return song->parent != NULL;
+}
+
+static inline bool
+song_is_file(const struct song *song)
+{
+ return song_in_database(song) || song->uri[0] == '/';
+}
+
+static inline bool
+song_is_detached(const struct song *song)
+{
+ assert(song != NULL);
+ assert(song_in_database(song));
+
+ return song->parent == &detached_root;
+}
+
+/**
+ * Returns true if both objects refer to the same physical song.
+ */
+gcc_pure
+bool
+song_equals(const struct song *a, const struct song *b);
+
bool
song_file_update(struct song *song);
@@ -105,16 +160,8 @@ song_get_uri(const struct song *song);
double
song_get_duration(const struct song *song);
-static inline bool
-song_in_database(const struct song *song)
-{
- return song->parent != NULL;
-}
-
-static inline bool
-song_is_file(const struct song *song)
-{
- return song_in_database(song) || song->uri[0] == '/';
+#ifdef __cplusplus
}
+#endif
#endif
diff --git a/src/state_file.c b/src/state_file.c
deleted file mode 100644
index de0e7053..00000000
--- a/src/state_file.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "state_file.h"
-#include "output_state.h"
-#include "playlist.h"
-#include "playlist_state.h"
-#include "volume.h"
-#include "text_file.h"
-
-#include <glib.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "state_file"
-
-static char *state_file_path;
-
-/** the GLib source id for the save timer */
-static guint save_state_source_id;
-
-/**
- * These version numbers determine whether we need to save the state
- * file. If nothing has changed, we won't let the hard drive spin up.
- */
-static unsigned prev_volume_version, prev_output_version,
- prev_playlist_version;
-
-static void
-state_file_write(struct player_control *pc)
-{
- FILE *fp;
-
- assert(state_file_path != NULL);
-
- g_debug("Saving state file %s", state_file_path);
-
- fp = fopen(state_file_path, "w");
- if (G_UNLIKELY(!fp)) {
- g_warning("failed to create %s: %s",
- state_file_path, g_strerror(errno));
- return;
- }
-
- save_sw_volume_state(fp);
- audio_output_state_save(fp);
- playlist_state_save(fp, &g_playlist, pc);
-
- fclose(fp);
-
- prev_volume_version = sw_volume_state_get_hash();
- prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
-}
-
-static void
-state_file_read(struct player_control *pc)
-{
- FILE *fp;
- bool success;
-
- assert(state_file_path != NULL);
-
- g_debug("Loading state file %s", state_file_path);
-
- fp = fopen(state_file_path, "r");
- if (G_UNLIKELY(!fp)) {
- g_warning("failed to open %s: %s",
- state_file_path, g_strerror(errno));
- return;
- }
-
- GString *buffer = g_string_sized_new(1024);
- const char *line;
- while ((line = read_text_line(fp, buffer)) != NULL) {
- success = read_sw_volume_state(line) ||
- audio_output_state_read(line) ||
- playlist_state_restore(line, fp, buffer,
- &g_playlist, pc);
- if (!success)
- g_warning("Unrecognized line in state file: %s", line);
- }
-
- fclose(fp);
-
- prev_volume_version = sw_volume_state_get_hash();
- prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
-
-
- g_string_free(buffer, true);
-}
-
-/**
- * This function is called every 5 minutes by the GLib main loop, and
- * saves the state file.
- */
-static gboolean
-timer_save_state_file(gpointer data)
-{
- struct player_control *pc = data;
-
- if (prev_volume_version == sw_volume_state_get_hash() &&
- prev_output_version == audio_output_state_get_version() &&
- prev_playlist_version == playlist_state_get_hash(&g_playlist, pc))
- /* nothing has changed - don't save the state file,
- don't spin up the hard disk */
- return true;
-
- state_file_write(pc);
- return true;
-}
-
-void
-state_file_init(const char *path, struct player_control *pc)
-{
- assert(state_file_path == NULL);
-
- if (path == NULL)
- return;
-
- state_file_path = g_strdup(path);
- state_file_read(pc);
-
- save_state_source_id = g_timeout_add_seconds(5 * 60,
- timer_save_state_file,
- pc);
-}
-
-void
-state_file_finish(struct player_control *pc)
-{
- if (state_file_path == NULL)
- /* no state file configured, no cleanup required */
- return;
-
- if (save_state_source_id != 0)
- g_source_remove(save_state_source_id);
-
- state_file_write(pc);
-
- g_free(state_file_path);
-}
diff --git a/src/stats.c b/src/stats.c
deleted file mode 100644
index fe6a064a..00000000
--- a/src/stats.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "stats.h"
-#include "database.h"
-#include "db_visitor.h"
-#include "tag.h"
-#include "song.h"
-#include "client.h"
-#include "player_control.h"
-#include "strset.h"
-#include "client_internal.h"
-
-struct stats stats;
-
-void stats_global_init(void)
-{
- stats.timer = g_timer_new();
-}
-
-void stats_global_finish(void)
-{
- g_timer_destroy(stats.timer);
-}
-
-struct visit_data {
- struct strset *artists;
- struct strset *albums;
-};
-
-static void
-visit_tag(struct visit_data *data, const struct tag *tag)
-{
- if (tag->time > 0)
- stats.song_duration += tag->time;
-
- for (unsigned i = 0; i < tag->num_items; ++i) {
- const struct tag_item *item = tag->items[i];
-
- switch (item->type) {
- case TAG_ARTIST:
- strset_add(data->artists, item->value);
- break;
-
- case TAG_ALBUM:
- strset_add(data->albums, item->value);
- break;
-
- default:
- break;
- }
- }
-}
-
-static bool
-collect_stats_song(struct song *song, void *_data,
- G_GNUC_UNUSED GError **error_r)
-{
- struct visit_data *data = _data;
-
- ++stats.song_count;
-
- if (song->tag != NULL)
- visit_tag(data, song->tag);
-
- return true;
-}
-
-static const struct db_visitor collect_stats_visitor = {
- .song = collect_stats_song,
-};
-
-void stats_update(void)
-{
- struct visit_data data;
-
- stats.song_count = 0;
- stats.song_duration = 0;
- stats.artist_count = 0;
-
- data.artists = strset_new();
- data.albums = strset_new();
-
- db_walk("", &collect_stats_visitor, &data, NULL);
-
- stats.artist_count = strset_size(data.artists);
- stats.album_count = strset_size(data.albums);
-
- strset_free(data.artists);
- strset_free(data.albums);
-}
-
-int stats_print(struct client *client)
-{
- client_printf(client,
- "artists: %u\n"
- "albums: %u\n"
- "songs: %i\n"
- "uptime: %li\n"
- "playtime: %li\n"
- "db_playtime: %li\n"
- "db_update: %li\n",
- stats.artist_count,
- stats.album_count,
- stats.song_count,
- (long)g_timer_elapsed(stats.timer, NULL),
- (long)(pc_get_total_play_time(client->player_control) + 0.5),
- stats.song_duration,
- (long)db_get_mtime());
- return 0;
-}
diff --git a/src/stats.h b/src/stats.h
index a686477d..eb723bcf 100644
--- a/src/stats.h
+++ b/src/stats.h
@@ -22,7 +22,7 @@
#include <glib.h>
-struct client;
+class Client;
struct stats {
GTimer *timer;
@@ -49,6 +49,7 @@ void stats_global_finish(void);
void stats_update(void);
-int stats_print(struct client *client);
+void
+stats_print(Client *client);
#endif
diff --git a/src/string_util.c b/src/string_util.c
index 6e542907..5d9feccf 100644
--- a/src/string_util.c
+++ b/src/string_util.c
@@ -20,6 +20,8 @@
#include "config.h"
#include "string_util.h"
+#include <stdlib.h> /* for malloc() */
+#include <string.h> /* for strnlen() */
#include <glib.h>
#include <assert.h>
@@ -45,3 +47,37 @@ string_array_contains(const char *const* haystack, const char *needle)
return false;
}
+
+#ifndef HAVE_STRNLEN
+
+size_t
+strnlen(const char *s, size_t max)
+{
+ assert(s != NULL);
+
+ const char *t = memchr(s, 0, max);
+ return t != NULL
+ ? (size_t)(t - s)
+ : max;
+}
+
+#endif
+
+#if !defined(HAVE_STRNDUP)
+
+char *
+strndup(const char *str, size_t n)
+{
+ assert(str != NULL);
+
+ size_t len = strnlen(str, n);
+ char* ret = (char *) malloc(len + 1);
+ if (ret == NULL)
+ return NULL;
+
+ memcpy(ret, str, len);
+ ret[len] = '\0';
+ return ret;
+}
+
+#endif
diff --git a/src/string_util.h b/src/string_util.h
index dc80a46e..62de5387 100644
--- a/src/string_util.h
+++ b/src/string_util.h
@@ -20,18 +20,26 @@
#ifndef MPD_STRING_UTIL_H
#define MPD_STRING_UTIL_H
-#include <glib.h>
+#include "gcc.h"
#include <stdbool.h>
+#include <stdlib.h> /* for size_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
/**
* Remove the "const" attribute from a string pointer. This is a
* dirty hack, don't use it unless you know what you're doing!
*/
-G_GNUC_CONST
+gcc_const
static inline char *
deconst_string(const char *p)
{
+#ifdef __cplusplus
+ return const_cast<char *>(p);
+#else
union {
const char *in;
char *out;
@@ -40,6 +48,7 @@ deconst_string(const char *p)
};
return u.out;
+#endif
}
/**
@@ -49,14 +58,14 @@ deconst_string(const char *p)
* This is a faster version of g_strchug(), because it does not move
* data.
*/
-G_GNUC_PURE
+gcc_pure
const char *
strchug_fast_c(const char *p);
/**
* Same as strchug_fast_c(), but works with a writable pointer.
*/
-G_GNUC_PURE
+gcc_pure
static inline char *
strchug_fast(char *p)
{
@@ -74,4 +83,33 @@ strchug_fast(char *p)
bool
string_array_contains(const char *const* haystack, const char *needle);
+#ifndef HAVE_STRNLEN
+
+gcc_pure
+size_t
+strnlen(const char *s, size_t max);
+
+#endif
+
+#if !defined(HAVE_STRNDUP)
+
+/**
+ * Duplicates the string to a newly allocated buffer
+ * copying at most n characters.
+ *
+ * @param str a string to duplicate
+ * @param n maximal number of characters to copy
+ * @return a pointer to the duplicated string,
+ * or NULL if memory allocation failed.
+ */
+gcc_malloc
+char *
+strndup(const char *str, size_t n);
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif
diff --git a/src/strset.c b/src/strset.c
deleted file mode 100644
index 5862e407..00000000
--- a/src/strset.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "strset.h"
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define NUM_SLOTS 16384
-
-struct strset_slot {
- struct strset_slot *next;
- const char *value;
-};
-
-struct strset {
- unsigned size;
-
- struct strset_slot *current_slot;
- unsigned next_slot;
-
- struct strset_slot slots[NUM_SLOTS];
-};
-
-static unsigned calc_hash(const char *p) {
- unsigned hash = 5381;
-
- assert(p != NULL);
-
- while (*p != 0)
- hash = (hash << 5) + hash + *p++;
-
- return hash;
-}
-
-G_GNUC_MALLOC struct strset *strset_new(void)
-{
- struct strset *set = g_new0(struct strset, 1);
- return set;
-}
-
-void strset_free(struct strset *set)
-{
- unsigned i;
-
- for (i = 0; i < NUM_SLOTS; ++i) {
- struct strset_slot *slot = set->slots[i].next, *next;
-
- while (slot != NULL) {
- next = slot->next;
- g_free(slot);
- slot = next;
- }
- }
-
- g_free(set);
-}
-
-void strset_add(struct strset *set, const char *value)
-{
- struct strset_slot *base_slot
- = &set->slots[calc_hash(value) % NUM_SLOTS];
- struct strset_slot *slot = base_slot;
-
- if (base_slot->value == NULL) {
- /* empty slot - put into base_slot */
- assert(base_slot->next == NULL);
-
- base_slot->value = value;
- ++set->size;
- return;
- }
-
- for (slot = base_slot; slot != NULL; slot = slot->next)
- if (strcmp(slot->value, value) == 0)
- /* found it - do nothing */
- return;
-
- /* insert it into the slot chain */
- slot = g_new(struct strset_slot, 1);
- slot->next = base_slot->next;
- slot->value = value;
- base_slot->next = slot;
- ++set->size;
-}
-
-int strset_get(const struct strset *set, const char *value)
-{
- const struct strset_slot *slot = &set->slots[calc_hash(value)];
-
- if (slot->value == NULL)
- return 0;
-
- for (slot = slot->next; slot != NULL; slot = slot->next)
- if (strcmp(slot->value, value) == 0)
- /* found it - do nothing */
- return 1;
-
- return 0;
-}
-
-unsigned strset_size(const struct strset *set)
-{
- return set->size;
-}
-
-void strset_rewind(struct strset *set)
-{
- set->current_slot = NULL;
- set->next_slot = 0;
-}
-
-const char *strset_next(struct strset *set)
-{
- if (set->current_slot != NULL && set->current_slot->next != NULL) {
- set->current_slot = set->current_slot->next;
- return set->current_slot->value;
- }
-
- while (set->next_slot < NUM_SLOTS &&
- set->slots[set->next_slot].value == NULL)
- ++set->next_slot;
-
- if (set->next_slot >= NUM_SLOTS)
- return NULL;
-
- set->current_slot = &set->slots[set->next_slot++];
- return set->current_slot->value;
-}
-
diff --git a/src/strset.h b/src/strset.h
deleted file mode 100644
index 5382e59b..00000000
--- a/src/strset.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/**
- * "struct strset" is a hashed string set: you can add strings to this
- * library, and it stores them as a set of unique strings. You can
- * get the size of the set, and you can enumerate through all values.
- *
- * It is important to note that the strset does not copy the string
- * values - it stores the exact pointers it was given in strset_add().
- */
-
-#ifndef MPD_STRSET_H
-#define MPD_STRSET_H
-
-#include <glib.h>
-
-struct strset;
-
-G_GNUC_MALLOC struct strset *strset_new(void);
-
-void strset_free(struct strset *set);
-
-void strset_add(struct strset *set, const char *value);
-
-int strset_get(const struct strset *set, const char *value);
-
-unsigned strset_size(const struct strset *set);
-
-void strset_rewind(struct strset *set);
-
-const char *strset_next(struct strset *set);
-
-#endif
diff --git a/src/tag.h b/src/tag.h
index 2556cf3a..3b47148a 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -101,6 +101,10 @@ struct tag {
unsigned num_items;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Parse the string, and convert it into a #tag_type. Returns
* #TAG_NUM_OF_ITEM_TYPES if the string could not be recognized.
@@ -236,4 +240,8 @@ bool tag_has_type(const struct tag *tag, enum tag_type type);
*/
bool tag_equal(const struct tag *tag1, const struct tag *tag2);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/tag_id3.c b/src/tag_id3.c
index 0971829f..5744e0e6 100644
--- a/src/tag_id3.c
+++ b/src/tag_id3.c
@@ -25,6 +25,7 @@
#include "riff.h"
#include "aiff.h"
#include "conf.h"
+#include "io_error.h"
#include <glib.h>
#include <id3tag.h>
@@ -343,7 +344,7 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
}
}
-static void
+void
scan_id3_tag(struct id3_tag *tag,
const struct tag_handler *handler, void *handler_ctx)
{
@@ -546,7 +547,7 @@ tag_id3_load(const char *path_fs, GError **error_r)
{
FILE *file = fopen(path_fs, "rb");
if (file == NULL) {
- g_set_error(error_r, g_file_error_quark(), errno,
+ g_set_error(error_r, errno_quark(), errno,
"Failed to open file %s: %s",
path_fs, g_strerror(errno));
return NULL;
diff --git a/src/tag_id3.h b/src/tag_id3.h
index 049c53ad..1907c13f 100644
--- a/src/tag_id3.h
+++ b/src/tag_id3.h
@@ -21,8 +21,8 @@
#define MPD_TAG_ID3_H
#include "check.h"
-
-#include <glib.h>
+#include "gcc.h"
+#include "gerror.h"
#include <stdbool.h>
@@ -48,12 +48,20 @@ struct tag *tag_id3_import(struct id3_tag *);
struct id3_tag *
tag_id3_load(const char *path_fs, GError **error_r);
+/**
+ * Import all tags from the provided id3_tag *tag
+ *
+ */
+void
+scan_id3_tag(struct id3_tag *tag,
+ const struct tag_handler *handler, void *handler_ctx);
+
#else
static inline bool
-tag_id3_scan(G_GNUC_UNUSED const char *path_fs,
- G_GNUC_UNUSED const struct tag_handler *handler,
- G_GNUC_UNUSED void *handler_ctx)
+tag_id3_scan(gcc_unused const char *path_fs,
+ gcc_unused const struct tag_handler *handler,
+ gcc_unused void *handler_ctx)
{
return false;
}
diff --git a/src/tag_table.h b/src/tag_table.h
index d87d4869..8e2172c1 100644
--- a/src/tag_table.h
+++ b/src/tag_table.h
@@ -21,6 +21,7 @@
#define MPD_TAG_TABLE_H
#include "tag.h"
+#include "gcc.h"
#include <glib.h>
@@ -35,7 +36,7 @@ struct tag_table {
* Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found
* in the table.
*/
-G_GNUC_PURE
+gcc_pure
static inline enum tag_type
tag_table_lookup(const struct tag_table *table, const char *name)
{
@@ -51,7 +52,7 @@ tag_table_lookup(const struct tag_table *table, const char *name)
* Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found
* in the table.
*/
-G_GNUC_PURE
+gcc_pure
static inline enum tag_type
tag_table_lookup_i(const struct tag_table *table, const char *name)
{
diff --git a/src/text_file.h b/src/text_file.h
deleted file mode 100644
index 9dd81094..00000000
--- a/src/text_file.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_TEXT_FILE_H
-#define MPD_TEXT_FILE_H
-
-#include <glib.h>
-
-#include <stdio.h>
-
-/**
- * Reads a line from the input file, and strips trailing space. There
- * is a reasonable maximum line length, only to prevent denial of
- * service.
- *
- * @param file the source file, opened in text mode
- * @param buffer an allocator for the buffer
- * @return a pointer to the line, or NULL on end-of-file or error
- */
-char *
-read_text_line(FILE *file, GString *buffer);
-
-#endif
diff --git a/src/text_input_stream.c b/src/text_input_stream.c
index 4a858fc8..4a2eeb81 100644
--- a/src/text_input_stream.c
+++ b/src/text_input_stream.c
@@ -20,7 +20,7 @@
#include "config.h"
#include "text_input_stream.h"
#include "input_stream.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
#include <glib.h>
diff --git a/src/thread/Cond.hxx b/src/thread/Cond.hxx
new file mode 100644
index 00000000..bbaabddc
--- /dev/null
+++ b/src/thread/Cond.hxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_THREAD_COND_HXX
+#define MPD_THREAD_COND_HXX
+
+#ifdef WIN32
+
+/* mingw-w64 4.6.3 lacks a std::cond implementation */
+
+#include "WindowsCond.hxx"
+typedef WindowsCond Cond;
+
+#else
+
+#include "PosixCond.hxx"
+typedef PosixCond Cond;
+
+#endif
+
+#endif
diff --git a/src/thread/CriticalSection.hxx b/src/thread/CriticalSection.hxx
new file mode 100644
index 00000000..8bc05b8f
--- /dev/null
+++ b/src/thread/CriticalSection.hxx
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_CRITICAL_SECTION_HXX
+#define MPD_THREAD_CRITICAL_SECTION_HXX
+
+#include <windows.h>
+
+/**
+ * Wrapper for a CRITICAL_SECTION, backend for the Mutex class.
+ */
+class CriticalSection {
+ friend class WindowsCond;
+
+ CRITICAL_SECTION critical_section;
+
+public:
+ CriticalSection() {
+ ::InitializeCriticalSection(&critical_section);
+ }
+
+ ~CriticalSection() {
+ ::DeleteCriticalSection(&critical_section);
+ }
+
+ CriticalSection(const CriticalSection &other) = delete;
+ CriticalSection &operator=(const CriticalSection &other) = delete;
+
+ void lock() {
+ ::EnterCriticalSection(&critical_section);
+ };
+
+ bool try_lock() {
+ return ::TryEnterCriticalSection(&critical_section) != 0;
+ };
+
+ void unlock() {
+ ::LeaveCriticalSection(&critical_section);
+ }
+};
+
+#endif
diff --git a/src/thread/GLibCond.hxx b/src/thread/GLibCond.hxx
new file mode 100644
index 00000000..9ab08e9f
--- /dev/null
+++ b/src/thread/GLibCond.hxx
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_GLIB_COND_HXX
+#define MPD_THREAD_GLIB_COND_HXX
+
+#include "GLibMutex.hxx"
+
+/**
+ * A wrapper for GCond.
+ */
+class GLibCond {
+#if GLIB_CHECK_VERSION(2,32,0)
+ GCond cond;
+#else
+ GCond *cond;
+#endif
+
+public:
+ GLibCond() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_cond_init(&cond);
+#else
+ cond = g_cond_new();
+#endif
+ }
+
+ ~GLibCond() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_cond_clear(&cond);
+#else
+ g_cond_free(cond);
+#endif
+ }
+
+ GLibCond(const GLibCond &other) = delete;
+ GLibCond &operator=(const GLibCond &other) = delete;
+
+private:
+ GCond *GetNative() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ return &cond;
+#else
+ return cond;
+#endif
+ }
+
+public:
+ void signal() {
+ g_cond_signal(GetNative());
+ }
+
+ void broadcast() {
+ g_cond_broadcast(GetNative());
+ }
+
+ void wait(GLibMutex &mutex) {
+ g_cond_wait(GetNative(), mutex.GetNative());
+ }
+};
+
+#endif
diff --git a/src/thread/GLibMutex.hxx b/src/thread/GLibMutex.hxx
new file mode 100644
index 00000000..2c666c1e
--- /dev/null
+++ b/src/thread/GLibMutex.hxx
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_GLIB_MUTEX_HXX
+#define MPD_THREAD_GLIB_MUTEX_HXX
+
+#include <glib.h>
+
+/**
+ * A wrapper for GMutex.
+ */
+class GLibMutex {
+ friend class GLibCond;
+
+#if GLIB_CHECK_VERSION(2,32,0)
+ GMutex mutex;
+#else
+ GMutex *mutex;
+#endif
+
+public:
+ GLibMutex() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_mutex_init(&mutex);
+#else
+ mutex = g_mutex_new();
+#endif
+ }
+
+ ~GLibMutex() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ g_mutex_clear(&mutex);
+#else
+ g_mutex_free(mutex);
+#endif
+ }
+
+ GLibMutex(const GLibMutex &other) = delete;
+ GLibMutex &operator=(const GLibMutex &other) = delete;
+
+private:
+ GMutex *GetNative() {
+#if GLIB_CHECK_VERSION(2,32,0)
+ return &mutex;
+#else
+ return mutex;
+#endif
+ }
+
+public:
+ void lock() {
+ g_mutex_lock(GetNative());
+ }
+
+ bool try_lock() {
+ return g_mutex_trylock(GetNative());
+ }
+
+ void unlock() {
+ g_mutex_lock(GetNative());
+ }
+};
+
+#endif
diff --git a/src/thread/Mutex.hxx b/src/thread/Mutex.hxx
new file mode 100644
index 00000000..675af74b
--- /dev/null
+++ b/src/thread/Mutex.hxx
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_THREAD_MUTEX_HXX
+#define MPD_THREAD_MUTEX_HXX
+
+#ifdef WIN32
+
+/* mingw-w64 4.6.3 lacks a std::mutex implementation */
+
+#include "CriticalSection.hxx"
+typedef CriticalSection Mutex;
+
+#else
+
+#include "PosixMutex.hxx"
+
+typedef PosixMutex Mutex;
+
+#endif
+
+class ScopeLock {
+ Mutex &mutex;
+
+public:
+ ScopeLock(Mutex &_mutex):mutex(_mutex) {
+ mutex.lock();
+ };
+
+ ~ScopeLock() {
+ mutex.unlock();
+ };
+
+ ScopeLock(const ScopeLock &other) = delete;
+ ScopeLock &operator=(const ScopeLock &other) = delete;
+};
+
+#endif
diff --git a/src/thread/PosixCond.hxx b/src/thread/PosixCond.hxx
new file mode 100644
index 00000000..acdc05ed
--- /dev/null
+++ b/src/thread/PosixCond.hxx
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_POSIX_COND_HXX
+#define MPD_THREAD_POSIX_COND_HXX
+
+#include "PosixMutex.hxx"
+
+/**
+ * Low-level wrapper for a pthread_cond_t.
+ */
+class PosixCond {
+ pthread_cond_t cond;
+
+public:
+ constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
+
+ PosixCond(const PosixCond &other) = delete;
+ PosixCond &operator=(const PosixCond &other) = delete;
+
+ void signal() {
+ pthread_cond_signal(&cond);
+ }
+
+ void broadcast() {
+ pthread_cond_broadcast(&cond);
+ }
+
+ void wait(PosixMutex &mutex) {
+ pthread_cond_wait(&cond, &mutex.mutex);
+ }
+};
+
+#endif
diff --git a/src/thread/PosixMutex.hxx b/src/thread/PosixMutex.hxx
new file mode 100644
index 00000000..d50764af
--- /dev/null
+++ b/src/thread/PosixMutex.hxx
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_POSIX_MUTEX_HXX
+#define MPD_THREAD_POSIX_MUTEX_HXX
+
+#include <pthread.h>
+
+/**
+ * Low-level wrapper for a pthread_mutex_t.
+ */
+class PosixMutex {
+ friend class PosixCond;
+
+ pthread_mutex_t mutex;
+
+public:
+ constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
+
+ PosixMutex(const PosixMutex &other) = delete;
+ PosixMutex &operator=(const PosixMutex &other) = delete;
+
+ void lock() {
+ pthread_mutex_lock(&mutex);
+ }
+
+ bool try_lock() {
+ return pthread_mutex_trylock(&mutex) == 0;
+ }
+
+ void unlock() {
+ pthread_mutex_unlock(&mutex);
+ }
+};
+
+#endif
diff --git a/src/thread/WindowsCond.hxx b/src/thread/WindowsCond.hxx
new file mode 100644
index 00000000..f4e909c7
--- /dev/null
+++ b/src/thread/WindowsCond.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_THREAD_WINDOWS_COND_HXX
+#define MPD_THREAD_WINDOWS_COND_HXX
+
+#include "CriticalSection.hxx"
+
+/**
+ * Wrapper for a CONDITION_VARIABLE, backend for the Cond class.
+ */
+class WindowsCond {
+ CONDITION_VARIABLE cond;
+
+public:
+ WindowsCond() {
+ InitializeConditionVariable(&cond);
+ }
+
+ WindowsCond(const WindowsCond &other) = delete;
+ WindowsCond &operator=(const WindowsCond &other) = delete;
+
+ void signal() {
+ WakeConditionVariable(&cond);
+ }
+
+ void broadcast() {
+ WakeAllConditionVariable(&cond);
+ }
+
+ void wait(CriticalSection &mutex) {
+ SleepConditionVariableCS(&cond, &mutex.critical_section,
+ INFINITE);
+ }
+};
+
+#endif
diff --git a/src/timer.h b/src/timer.h
index 18488124..1506c917 100644
--- a/src/timer.h
+++ b/src/timer.h
@@ -30,6 +30,10 @@ struct timer {
int rate;
};
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct timer *timer_new(const struct audio_format *af);
void timer_free(struct timer *timer);
@@ -48,4 +52,8 @@ timer_delay(const struct timer *timer);
void timer_sync(struct timer *timer);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/tokenizer.c b/src/tokenizer.c
index bbb34e10..4a98e882 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -21,6 +21,8 @@
#include "tokenizer.h"
#include "string_util.h"
+#include <glib.h>
+
#include <stdbool.h>
#include <assert.h>
#include <string.h>
diff --git a/src/tokenizer.h b/src/tokenizer.h
index d55eb3ca..2026e5ad 100644
--- a/src/tokenizer.h
+++ b/src/tokenizer.h
@@ -20,7 +20,7 @@
#ifndef MPD_TOKENIZER_H
#define MPD_TOKENIZER_H
-#include <glib.h>
+#include "gerror.h"
/**
* Reads the next word from the input string. This function modifies
diff --git a/src/update_io.c b/src/update_io.c
deleted file mode 100644
index c6a540a0..00000000
--- a/src/update_io.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h" /* must be first for large file support */
-#include "update_io.h"
-#include "mapper.h"
-#include "directory.h"
-#include "glib_compat.h"
-
-#include <glib.h>
-
-#include <errno.h>
-#include <unistd.h>
-
-int
-stat_directory(const struct directory *directory, struct stat *st)
-{
- char *path_fs = map_directory_fs(directory);
- if (path_fs == NULL)
- return -1;
-
- int ret = stat(path_fs, st);
- if (ret < 0)
- g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
-
- g_free(path_fs);
- return ret;
-}
-
-int
-stat_directory_child(const struct directory *parent, const char *name,
- struct stat *st)
-{
- char *path_fs = map_directory_child_fs(parent, name);
- if (path_fs == NULL)
- return -1;
-
- int ret = stat(path_fs, st);
- if (ret < 0)
- g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
-
- g_free(path_fs);
- return ret;
-}
-
-bool
-directory_exists(const struct directory *directory)
-{
- char *path_fs = map_directory_fs(directory);
- if (path_fs == NULL)
- /* invalid path: cannot exist */
- return false;
-
- GFileTest test = directory->device == DEVICE_INARCHIVE ||
- directory->device == DEVICE_CONTAINER
- ? G_FILE_TEST_IS_REGULAR
- : G_FILE_TEST_IS_DIR;
-
- bool exists = g_file_test(path_fs, test);
- g_free(path_fs);
-
- return exists;
-}
-
-bool
-directory_child_is_regular(const struct directory *directory,
- const char *name_utf8)
-{
- char *path_fs = map_directory_child_fs(directory, name_utf8);
- if (path_fs == NULL)
- return false;
-
- struct stat st;
- bool is_regular = stat(path_fs, &st) == 0 && S_ISREG(st.st_mode);
- g_free(path_fs);
-
- return is_regular;
-}
-
-bool
-directory_child_access(const struct directory *directory,
- const char *name, int mode)
-{
-#ifdef WIN32
- /* access() is useless on WIN32 */
- (void)directory;
- (void)name;
- (void)mode;
- return true;
-#else
- char *path = map_directory_child_fs(directory, name);
- if (path == NULL)
- /* something went wrong, but that isn't a permission
- problem */
- return true;
-
- bool success = access(path, mode) == 0 || errno != EACCES;
- g_free(path);
- return success;
-#endif
-}
diff --git a/src/uri.h b/src/uri.h
index 5a9b472f..6713f169 100644
--- a/src/uri.h
+++ b/src/uri.h
@@ -20,7 +20,7 @@
#ifndef MPD_URI_H
#define MPD_URI_H
-#include <glib.h>
+#include "gcc.h"
#include <stdbool.h>
@@ -28,10 +28,10 @@
* Checks whether the specified URI has a scheme in the form
* "scheme://".
*/
-G_GNUC_PURE
+gcc_pure
bool uri_has_scheme(const char *uri);
-G_GNUC_PURE
+gcc_pure
const char *
uri_get_suffix(const char *uri);
@@ -43,7 +43,7 @@ uri_get_suffix(const char *uri);
* - no double slashes
* - no path component begins with a dot
*/
-G_GNUC_PURE
+gcc_pure
bool
uri_safe_local(const char *uri);
@@ -53,7 +53,7 @@ uri_safe_local(const char *uri);
* NULL if nothing needs to be removed, or if the URI is not
* recognized.
*/
-G_GNUC_MALLOC
+gcc_malloc
char *
uri_remove_auth(const char *uri);
diff --git a/src/util/HugeAllocator.cxx b/src/util/HugeAllocator.cxx
new file mode 100644
index 00000000..d1c55c96
--- /dev/null
+++ b/src/util/HugeAllocator.cxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "HugeAllocator.hxx"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#include <unistd.h>
+#else
+#include <stdlib.h>
+#endif
+
+#ifdef __linux__
+
+/**
+ * Round up the parameter, make it page-aligned.
+ */
+gcc_const
+static size_t
+AlignToPageSize(size_t size)
+{
+ static const long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size > 0)
+ return size;
+
+ size_t ps(page_size);
+ return (size + ps - 1) / ps * ps;
+}
+
+void *
+HugeAllocate(size_t size)
+{
+ size = AlignToPageSize(size);
+
+ constexpr int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE;
+ void *p = mmap(nullptr, size,
+ PROT_READ|PROT_WRITE, flags,
+ -1, 0);
+ if (p == (void *)-1)
+ return nullptr;
+
+#ifdef MADV_HUGEPAGE
+ /* allow the Linux kernel to use "Huge Pages", which reduces page
+ table overhead for this big chunk of data */
+ madvise(p, size, MADV_HUGEPAGE);
+#endif
+
+#ifdef MADV_DONTFORK
+ /* just in case MPD needs to fork, don't copy this allocation
+ to the child process, to reduce overhead */
+ madvise(p, size, MADV_DONTFORK);
+#endif
+
+ return p;
+}
+
+void
+HugeFree(void *p, size_t size)
+{
+ munmap(p, AlignToPageSize(size));
+}
+
+void
+HugeDiscard(void *p, size_t size)
+{
+#ifdef MADV_DONTNEED
+ madvise(p, AlignToPageSize(size), MADV_DONTNEED);
+#endif
+}
+
+#endif
diff --git a/src/util/HugeAllocator.hxx b/src/util/HugeAllocator.hxx
new file mode 100644
index 00000000..01c92cd4
--- /dev/null
+++ b/src/util/HugeAllocator.hxx
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_HUGE_ALLOCATOR_HXX
+#define MPD_HUGE_ALLOCATOR_HXX
+
+#include "gcc.h"
+
+#include <stddef.h>
+
+#ifdef __linux__
+
+/**
+ * Allocate a huge amount of memory. This will be done in a way that
+ * allows giving the memory back to the kernel as soon as we don't
+ * need it anymore. On the downside, this call is expensive.
+ */
+gcc_malloc
+void *
+HugeAllocate(size_t size);
+
+/**
+ * @param p an allocation returned by HugeAllocate()
+ * @param size the allocation's size as passed to HugeAllocate()
+ */
+void
+HugeFree(void *p, size_t size);
+
+/**
+ * Discard any data stored in the allocation and give the memory back
+ * to the kernel. After returning, the allocation still exists and
+ * can be reused at any time, but its contents are undefined.
+ *
+ * @param p an allocation returned by HugeAllocate()
+ * @param size the allocation's size as passed to HugeAllocate()
+ */
+void
+HugeDiscard(void *p, size_t size);
+
+#else
+
+/* not Linux: fall back to standard C calls */
+
+#include <stdlib.h>
+
+gcc_malloc
+static inline void *
+HugeAllocate(size_t size)
+{
+ return malloc(size);
+}
+
+static inline void
+HugeFree(void *p, size_t)
+{
+ free(p);
+}
+
+static inline void
+HugeDiscard(void *, size_t)
+{
+}
+
+#endif
+
+#endif
diff --git a/src/util/LazyRandomEngine.cxx b/src/util/LazyRandomEngine.cxx
new file mode 100644
index 00000000..0f90ebb2
--- /dev/null
+++ b/src/util/LazyRandomEngine.cxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "LazyRandomEngine.hxx"
+
+void
+LazyRandomEngine::AutoCreate()
+{
+ if (engine != nullptr)
+ return;
+
+ std::random_device rd;
+ engine = new std::mt19937(rd());
+}
diff --git a/src/util/LazyRandomEngine.hxx b/src/util/LazyRandomEngine.hxx
new file mode 100644
index 00000000..8afe1d1c
--- /dev/null
+++ b/src/util/LazyRandomEngine.hxx
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_LAZY_RANDOM_ENGINE_HXX
+#define MPD_LAZY_RANDOM_ENGINE_HXX
+
+#include "check.h"
+
+#include <random>
+
+#include <assert.h>
+
+/**
+ * A random engine that will be created and seeded on demand.
+ */
+class LazyRandomEngine {
+ std::mt19937 *engine;
+
+public:
+ typedef std::mt19937::result_type result_type;
+
+ LazyRandomEngine():engine(nullptr) {}
+ ~LazyRandomEngine() {
+ delete engine;
+ }
+
+ LazyRandomEngine(const LazyRandomEngine &other) = delete;
+ LazyRandomEngine &operator=(const LazyRandomEngine &other) = delete;
+
+ /**
+ * Create and seed the real engine. Call this before any
+ * other method.
+ */
+ void AutoCreate();
+
+ result_type min() const {
+ return engine->min();
+ }
+
+ result_type max() const {
+ return engine->max();
+ }
+
+ result_type operator()() {
+ assert(engine != nullptr);
+
+ return engine->operator()();
+ }
+};
+
+#endif
diff --git a/src/util/Manual.hxx b/src/util/Manual.hxx
new file mode 100644
index 00000000..ecd2c52b
--- /dev/null
+++ b/src/util/Manual.hxx
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013 Max Kellermann <max@duempel.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MPD_MANUAL_HXX
+#define MPD_MANUAL_HXX
+
+#include "gcc.h"
+
+#include <new>
+
+#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8)
+#include <type_traits>
+#endif
+
+#include <assert.h>
+
+/**
+ * Container for an object that gets constructed and destructed
+ * manually. The object is constructed in-place, and therefore
+ * without allocation overhead. It can be constructed and destructed
+ * repeatedly.
+ */
+template<class T>
+class Manual {
+#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8)
+ /* no alignas() on gcc < 4.8: apply worst-case fallback */
+ __attribute__((aligned(8)))
+#else
+ alignas(T)
+#endif
+ char data[sizeof(T)];
+
+#ifndef NDEBUG
+ bool initialized;
+#endif
+
+public:
+#ifndef NDEBUG
+ Manual():initialized(false) {}
+ ~Manual() {
+ assert(!initialized);
+ }
+#endif
+
+ template<typename... Args>
+ void Construct(Args&&... args) {
+ assert(!initialized);
+
+ void *p = data;
+ new(p) T(std::forward<Args>(args)...);
+
+#ifndef NDEBUG
+ initialized = true;
+#endif
+ }
+
+ void Destruct() {
+ assert(initialized);
+
+ T *t = (T *)data;
+ t->T::~T();
+
+#ifndef NDEBUG
+ initialized = false;
+#endif
+ }
+
+ operator T &() {
+ return *(T *)data;
+ }
+
+ operator const T &() const {
+ return *(const T *)data;
+ }
+
+ T *operator->() {
+ return (T *)data;
+ }
+
+ const T *operator->() const {
+ return (T *)data;
+ }
+};
+
+#endif
diff --git a/src/util/PeakBuffer.cxx b/src/util/PeakBuffer.cxx
new file mode 100644
index 00000000..a3659b8f
--- /dev/null
+++ b/src/util/PeakBuffer.cxx
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "PeakBuffer.hxx"
+#include "HugeAllocator.hxx"
+#include "fifo_buffer.h"
+
+#include <algorithm>
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+PeakBuffer::~PeakBuffer()
+{
+ if (normal_buffer != nullptr)
+ fifo_buffer_free(normal_buffer);
+
+ if (peak_buffer != nullptr)
+ HugeFree(peak_buffer, peak_size);
+}
+
+bool
+PeakBuffer::IsEmpty() const
+{
+ return (normal_buffer == nullptr ||
+ fifo_buffer_is_empty(normal_buffer)) &&
+ (peak_buffer == nullptr ||
+ fifo_buffer_is_empty(peak_buffer));
+}
+
+const void *
+PeakBuffer::Read(size_t *length_r) const
+{
+ if (normal_buffer != nullptr) {
+ const void *p = fifo_buffer_read(normal_buffer, length_r);
+ if (p != nullptr)
+ return p;
+ }
+
+ if (peak_buffer != nullptr) {
+ const void *p = fifo_buffer_read(peak_buffer, length_r);
+ if (p != nullptr)
+ return p;
+ }
+
+ return nullptr;
+}
+
+void
+PeakBuffer::Consume(size_t length)
+{
+ if (normal_buffer != nullptr && !fifo_buffer_is_empty(normal_buffer)) {
+ fifo_buffer_consume(normal_buffer, length);
+ return;
+ }
+
+ if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) {
+ fifo_buffer_consume(peak_buffer, length);
+ if (fifo_buffer_is_empty(peak_buffer)) {
+ HugeFree(peak_buffer, peak_size);
+ peak_buffer = nullptr;
+ }
+
+ return;
+ }
+}
+
+static size_t
+AppendTo(fifo_buffer *buffer, const void *data, size_t length)
+{
+ assert(data != nullptr);
+ assert(length > 0);
+
+ size_t total = 0;
+
+ do {
+ size_t max_length;
+ void *p = fifo_buffer_write(buffer, &max_length);
+ if (p == nullptr)
+ break;
+
+ const size_t nbytes = std::min(length, max_length);
+ memcpy(p, data, nbytes);
+ fifo_buffer_append(buffer, nbytes);
+
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ total += nbytes;
+ } while (length > 0);
+
+ return total;
+}
+
+bool
+PeakBuffer::Append(const void *data, size_t length)
+{
+ if (length == 0)
+ return true;
+
+ if (peak_buffer != nullptr && !fifo_buffer_is_empty(peak_buffer)) {
+ size_t nbytes = AppendTo(peak_buffer, data, length);
+ return nbytes == length;
+ }
+
+ if (normal_buffer == nullptr)
+ normal_buffer = fifo_buffer_new(normal_size);
+
+ size_t nbytes = AppendTo(normal_buffer, data, length);
+ if (nbytes > 0) {
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ if (length == 0)
+ return true;
+ }
+
+ if (peak_buffer == nullptr && peak_size > 0) {
+ peak_buffer = (fifo_buffer *)HugeAllocate(peak_size);
+ if (peak_buffer == nullptr)
+ return false;
+
+ fifo_buffer_init(peak_buffer, peak_size);
+ }
+
+ nbytes = AppendTo(peak_buffer, data, length);
+ return nbytes == length;
+}
diff --git a/src/util/PeakBuffer.hxx b/src/util/PeakBuffer.hxx
new file mode 100644
index 00000000..0fbba8d7
--- /dev/null
+++ b/src/util/PeakBuffer.hxx
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PEAK_BUFFER_HXX
+#define MPD_PEAK_BUFFER_HXX
+
+#include "gcc.h"
+
+#include <stddef.h>
+
+struct fifo_buffer;
+
+/**
+ * A FIFO-like buffer that will allocate more memory on demand to
+ * allow large peaks. This second buffer will be given back to the
+ * kernel when it has been consumed.
+ */
+class PeakBuffer {
+ size_t normal_size, peak_size;
+
+ fifo_buffer *normal_buffer, *peak_buffer;
+
+public:
+ PeakBuffer(size_t _normal_size, size_t _peak_size)
+ :normal_size(_normal_size), peak_size(_peak_size),
+ normal_buffer(nullptr), peak_buffer(nullptr) {}
+
+ PeakBuffer(PeakBuffer &&other)
+ :normal_size(other.normal_size), peak_size(other.peak_size),
+ normal_buffer(other.normal_buffer),
+ peak_buffer(other.peak_buffer) {
+ other.normal_buffer = nullptr;
+ other.peak_buffer = nullptr;
+ }
+
+ ~PeakBuffer();
+
+ PeakBuffer(const PeakBuffer &) = delete;
+ PeakBuffer &operator=(const PeakBuffer &) = delete;
+
+ gcc_pure
+ bool IsEmpty() const;
+
+ const void *Read(size_t *length_r) const;
+ void Consume(size_t length);
+
+ bool Append(const void *data, size_t length);
+};
+
+#endif
diff --git a/src/refcount.h b/src/util/RefCount.hxx
index a882d76b..9a45a585 100644
--- a/src/refcount.h
+++ b/src/util/RefCount.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
@@ -33,35 +33,27 @@
* A very simple reference counting library.
*/
-#ifndef MPD_REFCOUNT_H
-#define MPD_REFCOUNT_H
+#ifndef MPD_REFCOUNT_HXX
+#define MPD_REFCOUNT_HXX
-#include <glib.h>
-#include <stdbool.h>
+#include <atomic>
-struct refcount {
- gint n;
-};
+class RefCount {
+ std::atomic_uint n;
-static inline void
-refcount_init(struct refcount *r)
-{
- r->n = 1;
-}
+public:
+ constexpr RefCount():n(1) {}
-static inline void
-refcount_inc(struct refcount *r)
-{
- g_atomic_int_inc(&r->n);
-}
+ void Increment() {
+ ++n;
+ }
-/**
- * @return true if the number of references has been dropped to 0
- */
-static inline bool
-refcount_dec(struct refcount *r)
-{
- return g_atomic_int_dec_and_test(&r->n);
-}
+ /**
+ * @return true if the number of references has been dropped to 0
+ */
+ bool Decrement() {
+ return --n == 0;
+ }
+};
#endif
diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx
new file mode 100644
index 00000000..c61f164f
--- /dev/null
+++ b/src/util/SliceBuffer.hxx
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_SLICE_BUFFER_HXX
+#define MPD_SLICE_BUFFER_HXX
+
+#include "HugeAllocator.hxx"
+#include "gcc.h"
+
+#include <utility>
+#include <new>
+
+#include <assert.h>
+#include <stddef.h>
+
+/**
+ * This class pre-allocates a certain number of objects, and allows
+ * callers to allocate and free these objects ("slices").
+ */
+template<typename T>
+class SliceBuffer {
+ union Slice {
+ Slice *next;
+
+ T value;
+ };
+
+ /**
+ * The maximum number of slices in this container.
+ */
+ const unsigned n_max;
+
+ /**
+ * The number of slices that are initialized. This is used to
+ * avoid page faulting on the new allocation, so the kernel
+ * does not need to reserve physical memory pages.
+ */
+ unsigned n_initialized;
+
+ /**
+ * The number of slices currently allocated.
+ */
+ unsigned n_allocated;
+
+ Slice *const data;
+
+ /**
+ * Pointer to the first free element in the chain.
+ */
+ Slice *available;
+
+ size_t CalcAllocationSize() const {
+ return n_max * sizeof(Slice);
+ }
+
+public:
+ SliceBuffer(unsigned _count)
+ :n_max(_count), n_initialized(0), n_allocated(0),
+ data((Slice *)HugeAllocate(CalcAllocationSize())),
+ available(nullptr) {
+ assert(n_max > 0);
+ }
+
+ ~SliceBuffer() {
+ /* all slices must be freed explicitly, and this
+ assertion checks for leaks */
+ assert(n_allocated == 0);
+
+ HugeFree(data, CalcAllocationSize());
+ }
+
+ SliceBuffer(const SliceBuffer &other) = delete;
+ SliceBuffer &operator=(const SliceBuffer &other) = delete;
+
+ /**
+ * @return true if buffer allocation (by the constructor) has failed
+ */
+ bool IsOOM() {
+ return data == nullptr;
+ }
+
+ unsigned GetCapacity() const {
+ return n_max;
+ }
+
+ bool IsEmpty() const {
+ return n_allocated == 0;
+ }
+
+ bool IsFull() const {
+ return n_allocated == n_max;
+ }
+
+ template<typename... Args>
+ T *Allocate(Args&&... args) {
+ assert(n_initialized <= n_max);
+ assert(n_allocated <= n_initialized);
+
+ if (available == nullptr) {
+ if (n_initialized == n_max) {
+ /* out of (internal) memory, buffer is full */
+ assert(n_allocated == n_max);
+ return nullptr;
+ }
+
+ available = &data[n_initialized++];
+ available->next = nullptr;
+ }
+
+ /* allocate a slice */
+ T *value = &available->value;
+ available = available->next;
+ ++n_allocated;
+
+ /* construct the object */
+ return ::new((void *)value) T(std::forward<Args>(args)...);
+ }
+
+ void Free(T *value) {
+ assert(n_initialized <= n_max);
+ assert(n_allocated > 0);
+ assert(n_allocated <= n_initialized);
+
+ Slice *slice = reinterpret_cast<Slice *>(value);
+ assert(slice >= data && slice < data + n_max);
+
+ /* destruct the object */
+ value->~T();
+
+ /* insert the slice in the "available" linked list */
+ slice->next = available;
+ available = slice;
+ --n_allocated;
+
+ /* give memory back to the kernel when the last slice
+ was freed */
+ if (n_allocated == 0) {
+ HugeDiscard(data, CalcAllocationSize());
+ n_initialized = 0;
+ available = nullptr;
+ }
+ }
+};
+
+#endif
diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h
index e44693b1..54cb789b 100644
--- a/src/util/bit_reverse.h
+++ b/src/util/bit_reverse.h
@@ -20,12 +20,13 @@
#ifndef MPD_BIT_REVERSE_H
#define MPD_BIT_REVERSE_H
-#include <glib.h>
+#include "gcc.h"
+
#include <stdint.h>
extern const uint8_t bit_reverse_table[256];
-G_GNUC_CONST
+gcc_const
static inline uint8_t
bit_reverse(uint8_t x)
{
diff --git a/src/fifo_buffer.c b/src/util/fifo_buffer.c
index 915fb057..162ddf94 100644
--- a/src/fifo_buffer.c
+++ b/src/util/fifo_buffer.c
@@ -58,6 +58,14 @@ fifo_buffer_new(size_t size)
return buffer;
}
+void
+fifo_buffer_init(struct fifo_buffer *buffer, size_t size)
+{
+ buffer->size = size - (sizeof(*buffer) - sizeof(buffer->buffer));
+ buffer->start = 0;
+ buffer->end = 0;
+}
+
static void
fifo_buffer_move(struct fifo_buffer *buffer);
diff --git a/src/fifo_buffer.h b/src/util/fifo_buffer.h
index 3bdb2393..ccea97d8 100644
--- a/src/fifo_buffer.h
+++ b/src/util/fifo_buffer.h
@@ -46,6 +46,10 @@
struct fifo_buffer;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Creates a new #fifo_buffer object. Free this object with
* fifo_buffer_free().
@@ -56,6 +60,9 @@ struct fifo_buffer;
struct fifo_buffer *
fifo_buffer_new(size_t size);
+void
+fifo_buffer_init(struct fifo_buffer *buffer, size_t size);
+
/**
* Change the capacity of the #fifo_buffer, while preserving existing
* data.
@@ -150,4 +157,8 @@ fifo_buffer_is_empty(struct fifo_buffer *buffer);
bool
fifo_buffer_is_full(struct fifo_buffer *buffer);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/src/growing_fifo.c b/src/util/growing_fifo.c
index 88431f60..88431f60 100644
--- a/src/growing_fifo.c
+++ b/src/util/growing_fifo.c
diff --git a/src/growing_fifo.h b/src/util/growing_fifo.h
index 723c3b3f..723c3b3f 100644
--- a/src/growing_fifo.h
+++ b/src/util/growing_fifo.h
diff --git a/src/util/list.h b/src/util/list.h
index fdab4767..73d99bef 100644
--- a/src/util/list.h
+++ b/src/util/list.h
@@ -25,8 +25,6 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
-#include <glib.h>
-
#ifdef __clang__
/* allow typeof() */
#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
@@ -40,15 +38,15 @@
*
*/
#define container_of(ptr, type, member) \
- (&G_STRUCT_MEMBER(type, ptr, -G_STRUCT_OFFSET(type, member)))
+ ((type *)((uint8_t *)ptr - offsetof(type, member)))
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
-#define LIST_POISON1 ((void *) 0x00100100)
-#define LIST_POISON2 ((void *) 0x00200200)
+#define LIST_POISON1 ((struct list_head *)(void *) 0x00100100)
+#define LIST_POISON2 ((struct list_head *)(void *) 0x00200200)
/*
* Simple doubly linked list implementation.
@@ -82,46 +80,47 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
* the prev/next entries already!
*/
#ifndef CONFIG_DEBUG_LIST
-static inline void __list_add(struct list_head *new,
+static inline void __list_add(struct list_head *new_item,
struct list_head *prev,
struct list_head *next)
{
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
+ next->prev = new_item;
+ new_item->next = next;
+ new_item->prev = prev;
+ prev->next = new_item;
}
#else
-extern void __list_add(struct list_head *new,
- struct list_head *prev,
- struct list_head *next);
+extern void __list_add(struct list_head *new_item,
+ struct list_head *prev,
+ struct list_head *next);
#endif
/**
* list_add - add a new entry
- * @new: new entry to be added
+ * @new_item: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
-static inline void list_add(struct list_head *new, struct list_head *head)
+static inline void list_add(struct list_head *new_item, struct list_head *head)
{
- __list_add(new, head, head->next);
+ __list_add(new_item, head, head->next);
}
/**
* list_add_tail - add a new entry
- * @new: new entry to be added
+ * @new_item: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
-static inline void list_add_tail(struct list_head *new, struct list_head *head)
+static inline void
+list_add_tail(struct list_head *new_item, struct list_head *head)
{
- __list_add(new, head->prev, head);
+ __list_add(new_item, head->prev, head);
}
/*
@@ -163,23 +162,23 @@ extern void list_del(struct list_head *entry);
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
- * @new : the new element to insert
+ * @new_item : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
- struct list_head *new)
+ struct list_head *new_item)
{
- new->next = old->next;
- new->next->prev = new;
- new->prev = old->prev;
- new->prev->next = new;
+ new_item->next = old->next;
+ new_item->next->prev = new_item;
+ new_item->prev = old->prev;
+ new_item->prev->next = new_item;
}
static inline void list_replace_init(struct list_head *old,
- struct list_head *new)
+ struct list_head *new_item)
{
- list_replace(old, new);
+ list_replace(old, new_item);
INIT_LIST_HEAD(old);
}
diff --git a/src/utils.c b/src/utils.c
index a2de3212..776813c4 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -19,7 +19,6 @@
#include "config.h"
#include "utils.h"
-#include "glib_compat.h"
#include "conf.h"
#include <glib.h>
diff --git a/src/utils.h b/src/utils.h
index f8d6657f..059d44fa 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -20,17 +20,7 @@
#ifndef MPD_UTILS_H
#define MPD_UTILS_H
-#include <glib.h>
-#include <stdbool.h>
-
-#ifndef assert_static
-/* Compile time assertion developed by Ralf Holly */
-/* http://pera-software.com/articles/compile-time-assertions.pdf */
-#define assert_static(e) \
- do { \
- enum { assert_static__ = 1/(e) }; \
- } while (0)
-#endif /* !assert_static */
+#include "gerror.h"
char *
parsePath(const char *path, GError **error_r);
diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx
new file mode 100644
index 00000000..ba0e74e4
--- /dev/null
+++ b/test/DumpDatabase.cxx
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "DatabaseRegistry.hxx"
+#include "DatabasePlugin.hxx"
+#include "DatabaseSelection.hxx"
+#include "Directory.hxx"
+#include "song.h"
+#include "PlaylistVector.hxx"
+#include "conf.h"
+#include "tag.h"
+#include "fs/Path.hxx"
+
+#include <iostream>
+using std::cout;
+using std::cerr;
+using std::endl;
+
+#include <stdlib.h>
+
+static void
+my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
+ const gchar *message, G_GNUC_UNUSED gpointer user_data)
+{
+ if (log_domain != NULL)
+ g_printerr("%s: %s\n", log_domain, message);
+ else
+ g_printerr("%s\n", message);
+}
+
+static bool
+DumpDirectory(const Directory &directory, GError **)
+{
+ cout << "D " << directory.path << endl;
+ return true;
+}
+
+static bool
+DumpSong(song &song, GError **)
+{
+ cout << "S " << song.parent->path << "/" << song.uri << endl;
+ return true;
+}
+
+static bool
+DumpPlaylist(const PlaylistInfo &playlist,
+ const Directory &directory, GError **)
+{
+ cout << "P " << directory.path << "/" << playlist.name.c_str() << endl;
+ return true;
+}
+
+int
+main(int argc, char **argv)
+{
+ GError *error = nullptr;
+
+ if (argc != 3) {
+ cerr << "Usage: DumpDatabase CONFIG PLUGIN" << endl;
+ return 1;
+ }
+
+ const Path config_path = Path::FromFS(argv[1]);
+ const char *const plugin_name = argv[2];
+
+ const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name);
+ if (plugin == NULL) {
+ cerr << "No such database plugin: " << plugin_name << endl;
+ return EXIT_FAILURE;
+ }
+
+ /* initialize GLib */
+
+ g_thread_init(nullptr);
+ g_log_set_default_handler(my_log_func, nullptr);
+
+ /* initialize MPD */
+
+ config_global_init();
+
+ if (!ReadConfigFile(config_path, &error)) {
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ tag_lib_init();
+
+ /* do it */
+
+ const struct config_param *path = config_get_param(CONF_DB_FILE);
+ config_param param("database", path->line);
+ if (path != nullptr)
+ param.AddBlockParam("path", path->value, path->line);
+
+ Database *db = plugin->create(&param, &error);
+
+ if (db == nullptr) {
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ if (!db->Open(&error)) {
+ delete db;
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ const DatabaseSelection selection("", true);
+
+ if (!db->Visit(selection, DumpDirectory, DumpSong, DumpPlaylist,
+ &error)) {
+ db->Close();
+ delete db;
+ cerr << error->message << endl;
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ db->Close();
+ delete db;
+
+ /* deinitialize everything */
+
+ config_global_finish();
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/FakeReplayGainConfig.cxx b/test/FakeReplayGainConfig.cxx
new file mode 100644
index 00000000..9c2431bf
--- /dev/null
+++ b/test/FakeReplayGainConfig.cxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "replay_gain_config.h"
+
+float replay_gain_preamp = 1.0;
+float replay_gain_missing_preamp = 1.0;
+bool replay_gain_limit = true;
diff --git a/test/FakeSong.cxx b/test/FakeSong.cxx
new file mode 100644
index 00000000..927a0765
--- /dev/null
+++ b/test/FakeSong.cxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "song.h"
+#include "directory.h"
+#include "gcc.h"
+
+#include <stdlib.h>
+
+struct directory detached_root;
+
+struct song *
+song_dup_detached(gcc_unused const struct song *src)
+{
+ abort();
+}
diff --git a/test/dump_playlist.c b/test/dump_playlist.cxx
index 84ac6904..6e1f4858 100644
--- a/test/dump_playlist.c
+++ b/test/dump_playlist.cxx
@@ -18,23 +18,27 @@
*/
#include "config.h"
-#include "io_thread.h"
-#include "input_init.h"
+#include "TagSave.hxx"
+#include "song.h"
+#include "Directory.hxx"
#include "input_stream.h"
-#include "tag_pool.h"
-#include "tag_save.h"
#include "conf.h"
-#include "song.h"
#include "decoder_api.h"
-#include "decoder_list.h"
-#include "playlist_list.h"
-#include "playlist_plugin.h"
+#include "DecoderList.hxx"
+#include "InputInit.hxx"
+#include "IOThread.hxx"
+#include "PlaylistRegistry.hxx"
+#include "PlaylistPlugin.hxx"
+#include "fs/Path.hxx"
#include <glib.h>
#include <unistd.h>
#include <stdlib.h>
+Directory::Directory() {}
+Directory::~Directory() {}
+
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
const gchar *message, G_GNUC_UNUSED gpointer user_data)
@@ -107,7 +111,7 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
@@ -121,13 +125,10 @@ decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
if (replay_gain_tuple_defined(tuple))
g_printerr("replay_gain[track]: gain=%f peak=%f\n",
tuple->gain, tuple->peak);
-
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -138,7 +139,6 @@ int main(int argc, char **argv)
{
const char *uri;
struct input_stream *is = NULL;
- bool success;
GError *error = NULL;
struct playlist_provider *playlist;
struct song *song;
@@ -148,6 +148,7 @@ int main(int argc, char **argv)
return 1;
}
+ const Path config_path = Path::FromFS(argv[1]);
uri = argv[2];
/* initialize GLib */
@@ -157,10 +158,8 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
- success = config_read_file(argv[1], &error);
- if (!success) {
+ if (!ReadConfigFile(config_path, &error)) {
g_printerr("%s\n", error->message);
g_error_free(error);
return 1;
@@ -184,8 +183,8 @@ int main(int argc, char **argv)
/* open the playlist */
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
playlist = playlist_list_open_uri(uri, mutex, cond);
if (playlist == NULL) {
@@ -241,15 +240,11 @@ int main(int argc, char **argv)
if (is != NULL)
input_stream_close(is);
- g_cond_free(cond);
- g_mutex_free(mutex);
-
decoder_plugin_deinit_all();
playlist_list_global_finish();
input_stream_global_finish();
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return 0;
}
diff --git a/test/dump_rva2.c b/test/dump_rva2.c
index 4a726518..6e978c42 100644
--- a/test/dump_rva2.c
+++ b/test/dump_rva2.c
@@ -26,6 +26,8 @@
#include <id3tag.h>
+#include <glib.h>
+
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
@@ -33,7 +35,8 @@
#include <stdlib.h>
const char *
-config_get_string(G_GNUC_UNUSED const char *name, const char *default_value)
+config_get_string(gcc_unused enum ConfigOption option,
+ const char *default_value)
{
return default_value;
}
@@ -45,8 +48,8 @@ tag_new(void)
}
void
-tag_add_item_n(G_GNUC_UNUSED struct tag *tag, G_GNUC_UNUSED enum tag_type type,
- G_GNUC_UNUSED const char *value, G_GNUC_UNUSED size_t len)
+tag_add_item_n(gcc_unused struct tag *tag, gcc_unused enum tag_type type,
+ gcc_unused const char *value, gcc_unused size_t len)
{
}
diff --git a/test/dump_text_file.c b/test/dump_text_file.cxx
index f1437144..93b0d018 100644
--- a/test/dump_text_file.c
+++ b/test/dump_text_file.cxx
@@ -18,16 +18,18 @@
*/
#include "config.h"
-#include "io_thread.h"
-#include "input_init.h"
-#include "input_stream.h"
-#include "text_input_stream.h"
-#include "tag_pool.h"
+#include "IOThread.hxx"
+#include "InputInit.hxx"
+#include "InputStream.hxx"
#include "conf.h"
#include "stdbin.h"
+extern "C" {
+#include "text_input_stream.h"
+}
+
#ifdef ENABLE_ARCHIVE
-#include "archive_list.h"
+#include "ArchiveList.hxx"
#endif
#include <glib.h>
@@ -112,7 +114,6 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
io_thread_init();
@@ -134,8 +135,8 @@ int main(int argc, char **argv)
/* open the stream and dump it */
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
is = input_stream_open(argv[1], mutex, cond, &error);
if (is != NULL) {
@@ -150,9 +151,6 @@ int main(int argc, char **argv)
ret = 2;
}
- g_cond_free(cond);
- g_mutex_free(mutex);
-
/* deinitialize everything */
input_stream_global_finish();
@@ -164,7 +162,6 @@ int main(int argc, char **argv)
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return ret;
}
diff --git a/test/read_conf.c b/test/read_conf.cxx
index 4f6005c6..8759398d 100644
--- a/test/read_conf.c
+++ b/test/read_conf.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h"
#include "conf.h"
+#include "fs/Path.hxx"
#include <glib.h>
@@ -42,7 +43,7 @@ int main(int argc, char **argv)
return 1;
}
- const char *path = argv[1];
+ const Path config_path = Path::FromFS(argv[1]);
const char *name = argv[2];
g_log_set_default_handler(my_log_func, NULL);
@@ -50,14 +51,16 @@ int main(int argc, char **argv)
config_global_init();
GError *error = NULL;
- bool success = config_read_file(path, &error);
- if (!success) {
+ if (!ReadConfigFile(config_path, &error)) {
g_printerr("%s:", error->message);
g_error_free(error);
return 1;
}
- const char *value = config_get_string(name, NULL);
+ ConfigOption option = ParseConfigOptionName(name);
+ const char *value = option != CONF_MAX
+ ? config_get_string(option, nullptr)
+ : nullptr;
int ret;
if (value != NULL) {
g_print("%s\n", value);
diff --git a/test/read_mixer.c b/test/read_mixer.cxx
index f6de8177..45344a2f 100644
--- a/test/read_mixer.c
+++ b/test/read_mixer.cxx
@@ -18,11 +18,13 @@
*/
#include "config.h"
-#include "mixer_control.h"
-#include "mixer_list.h"
-#include "filter_registry.h"
-#include "pcm_volume.h"
-#include "event_pipe.h"
+#include "MixerControl.hxx"
+#include "MixerList.hxx"
+#include "FilterRegistry.hxx"
+#include "PcmVolume.hxx"
+#include "GlobalEvents.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
#include <glib.h>
@@ -30,6 +32,8 @@
#include <string.h>
#include <unistd.h>
+EventLoop *main_loop;
+
#ifdef HAVE_PULSE
#include "output/pulse_output_plugin.h"
@@ -66,16 +70,16 @@ pulse_output_set_volume(G_GNUC_UNUSED struct pulse_output *po,
#endif
#ifdef HAVE_ROAR
-#include "output/roar_output_plugin.h"
+#include "output/RoarOutputPlugin.hxx"
int
-roar_output_get_volume(G_GNUC_UNUSED struct roar *roar)
+roar_output_get_volume(gcc_unused RoarOutput *roar)
{
return -1;
}
bool
-roar_output_set_volume(G_GNUC_UNUSED struct roar *roar,
+roar_output_set_volume(gcc_unused RoarOutput *roar,
G_GNUC_UNUSED unsigned volume)
{
return true;
@@ -84,7 +88,7 @@ roar_output_set_volume(G_GNUC_UNUSED struct roar *roar,
#endif
void
-event_pipe_emit(G_GNUC_UNUSED enum pipe_event event)
+GlobalEvents::Emit(gcc_unused Event event)
{
}
@@ -118,6 +122,8 @@ int main(int argc, G_GNUC_UNUSED char **argv)
g_thread_init(NULL);
+ main_loop = new EventLoop(EventLoop::Default());
+
mixer = mixer_new(&alsa_mixer_plugin, NULL, NULL, &error);
if (mixer == NULL) {
g_printerr("mixer_new() failed: %s\n", error->message);
@@ -137,6 +143,8 @@ int main(int argc, G_GNUC_UNUSED char **argv)
mixer_close(mixer);
mixer_free(mixer);
+ delete main_loop;
+
assert(volume >= -1 && volume <= 100);
if (volume < 0) {
diff --git a/test/read_tags.c b/test/read_tags.cxx
index faf9a45c..1ceddf1d 100644
--- a/test/read_tags.c
+++ b/test/read_tags.cxx
@@ -18,17 +18,17 @@
*/
#include "config.h"
-#include "io_thread.h"
-#include "decoder_list.h"
+#include "IOThread.hxx"
+#include "DecoderList.hxx"
#include "decoder_api.h"
-#include "input_init.h"
-#include "input_stream.h"
+#include "InputInit.hxx"
+#include "InputStream.hxx"
#include "audio_format.h"
-#include "pcm_volume.h"
+extern "C" {
#include "tag_ape.h"
#include "tag_id3.h"
+}
#include "tag_handler.h"
-#include "idle.h"
#include <glib.h>
@@ -40,25 +40,6 @@
#include <locale.h>
#endif
-/**
- * No-op dummy.
- */
-void
-idle_add(G_GNUC_UNUSED unsigned flags)
-{
-}
-
-/**
- * No-op dummy.
- */
-bool
-pcm_volume(G_GNUC_UNUSED void *buffer, G_GNUC_UNUSED size_t length,
- G_GNUC_UNUSED enum sample_format format,
- G_GNUC_UNUSED int volume)
-{
- return true;
-}
-
void
decoder_initialized(G_GNUC_UNUSED struct decoder *decoder,
G_GNUC_UNUSED const struct audio_format *audio_format,
@@ -118,16 +99,14 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
G_GNUC_UNUSED const struct replay_gain_info *replay_gain_info)
{
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -156,9 +135,9 @@ print_pair(const char *name, const char *value, G_GNUC_UNUSED void *ctx)
}
static const struct tag_handler print_handler = {
- .duration = print_duration,
- .tag = print_tag,
- .pair = print_pair,
+ print_duration,
+ print_tag,
+ print_pair,
};
int main(int argc, char **argv)
@@ -205,8 +184,8 @@ int main(int argc, char **argv)
bool success = decoder_plugin_scan_file(plugin, path,
&print_handler, NULL);
if (!success && plugin->scan_stream != NULL) {
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
struct input_stream *is =
input_stream_open(path, mutex, cond, &error);
@@ -218,12 +197,28 @@ int main(int argc, char **argv)
return 1;
}
+ mutex.lock();
+
+ while (!is->ready) {
+ cond.wait(mutex);
+ input_stream_update(is);
+ }
+
+ if (!input_stream_check(is, &error)) {
+ mutex.unlock();
+
+ g_printerr("Failed to read %s: %s\n",
+ path, error->message);
+ g_error_free(error);
+
+ return EXIT_FAILURE;
+ }
+
+ mutex.unlock();
+
success = decoder_plugin_scan_stream(plugin, is,
&print_handler, NULL);
input_stream_close(is);
-
- g_cond_free(cond);
- g_mutex_free(mutex);
}
decoder_plugin_deinit_all();
diff --git a/test/run_convert.c b/test/run_convert.cxx
index 4f57400f..e66d0be2 100644
--- a/test/run_convert.c
+++ b/test/run_convert.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,11 +24,11 @@
*/
#include "config.h"
-#include "audio_parser.h"
+#include "AudioParser.hxx"
#include "audio_format.h"
-#include "pcm_convert.h"
+#include "PcmConvert.hxx"
#include "conf.h"
-#include "fifo_buffer.h"
+#include "util/fifo_buffer.h"
#include "stdbin.h"
#include <glib.h>
@@ -48,7 +48,8 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
}
const char *
-config_get_string(G_GNUC_UNUSED const char *name, const char *default_value)
+config_get_string(gcc_unused enum ConfigOption option,
+ const char *default_value)
{
return default_value;
}
@@ -57,7 +58,6 @@ int main(int argc, char **argv)
{
GError *error = NULL;
struct audio_format in_audio_format, out_audio_format;
- struct pcm_convert_state state;
const void *output;
ssize_t nbytes;
size_t length;
@@ -89,7 +89,7 @@ int main(int argc, char **argv)
const size_t in_frame_size = audio_format_frame_size(&in_audio_format);
- pcm_convert_init(&state);
+ PcmConvert state;
struct fifo_buffer *buffer = fifo_buffer_new(4096);
@@ -112,8 +112,8 @@ int main(int argc, char **argv)
fifo_buffer_consume(buffer, length);
- output = pcm_convert(&state, &in_audio_format, src, length,
- &out_audio_format, &length, &error);
+ output = state.Convert(&in_audio_format, src, length,
+ &out_audio_format, &length, &error);
if (output == NULL) {
g_printerr("Failed to convert: %s\n", error->message);
return 2;
@@ -122,5 +122,5 @@ int main(int argc, char **argv)
G_GNUC_UNUSED ssize_t ignored = write(1, output, length);
}
- pcm_convert_deinit(&state);
+ return EXIT_SUCCESS;
}
diff --git a/test/run_decoder.c b/test/run_decoder.cxx
index e6712c75..d5c7c313 100644
--- a/test/run_decoder.c
+++ b/test/run_decoder.cxx
@@ -18,15 +18,12 @@
*/
#include "config.h"
-#include "io_thread.h"
-#include "decoder_list.h"
+#include "IOThread.hxx"
+#include "DecoderList.hxx"
#include "decoder_api.h"
-#include "tag_pool.h"
-#include "input_init.h"
+#include "InputInit.hxx"
#include "input_stream.h"
#include "audio_format.h"
-#include "pcm_volume.h"
-#include "idle.h"
#include "stdbin.h"
#include <glib.h>
@@ -45,25 +42,6 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
g_printerr("%s\n", message);
}
-/**
- * No-op dummy.
- */
-void
-idle_add(G_GNUC_UNUSED unsigned flags)
-{
-}
-
-/**
- * No-op dummy.
- */
-bool
-pcm_volume(G_GNUC_UNUSED void *buffer, G_GNUC_UNUSED size_t length,
- G_GNUC_UNUSED enum sample_format format,
- G_GNUC_UNUSED int volume)
-{
- return true;
-}
-
struct decoder {
const char *uri;
@@ -140,7 +118,7 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder,
return DECODE_COMMAND_NONE;
}
-float
+void
decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
const struct replay_gain_info *replay_gain_info)
{
@@ -154,13 +132,10 @@ decoder_replay_gain(G_GNUC_UNUSED struct decoder *decoder,
if (replay_gain_tuple_defined(tuple))
g_printerr("replay_gain[track]: gain=%f peak=%f\n",
tuple->gain, tuple->peak);
-
- return 0.0;
}
void
decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
- G_GNUC_UNUSED float replay_gain_db,
char *mixramp_start, char *mixramp_end)
{
g_free(mixramp_start);
@@ -191,8 +166,6 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- tag_pool_init();
-
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -213,8 +186,8 @@ int main(int argc, char **argv)
decoder_plugin_file_decode(decoder.plugin, &decoder,
decoder.uri);
} else if (decoder.plugin->stream_decode != NULL) {
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
struct input_stream *is =
input_stream_open(decoder.uri, mutex, cond, &error);
@@ -231,9 +204,6 @@ int main(int argc, char **argv)
decoder_plugin_stream_decode(decoder.plugin, &decoder, is);
input_stream_close(is);
-
- g_cond_free(cond);
- g_mutex_free(mutex);
} else {
g_printerr("Decoder plugin is not usable\n");
return 1;
@@ -248,7 +218,5 @@ int main(int argc, char **argv)
return 1;
}
- tag_pool_deinit();
-
return 0;
}
diff --git a/test/run_encoder.c b/test/run_encoder.cxx
index db4d3af9..9039f2db 100644
--- a/test/run_encoder.c
+++ b/test/run_encoder.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,7 @@
#include "encoder_list.h"
#include "encoder_plugin.h"
#include "audio_format.h"
-#include "audio_parser.h"
+#include "AudioParser.hxx"
#include "conf.h"
#include "stdbin.h"
@@ -49,9 +49,7 @@ int main(int argc, char **argv)
const char *encoder_name;
const struct encoder_plugin *plugin;
struct encoder *encoder;
- struct config_param *param;
static char buffer[32768];
- ssize_t nbytes;
/* parse command line */
@@ -75,10 +73,10 @@ int main(int argc, char **argv)
return 1;
}
- param = config_new_param(NULL, -1);
- config_add_block_param(param, "quality", "5.0", -1);
+ config_param param;
+ param.AddBlockParam("quality", "5.0", -1);
- encoder = encoder_init(plugin, param, &error);
+ encoder = encoder_init(plugin, &param, &error);
if (encoder == NULL) {
g_printerr("Failed to initialize encoder: %s\n",
error->message);
@@ -110,6 +108,7 @@ int main(int argc, char **argv)
/* do it */
+ ssize_t nbytes;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
ret = encoder_write(encoder, buffer, nbytes, &error);
if (!ret) {
diff --git a/test/run_filter.c b/test/run_filter.cxx
index 8e793b76..9ea50ff5 100644
--- a/test/run_filter.c
+++ b/test/run_filter.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,13 +19,13 @@
#include "config.h"
#include "conf.h"
-#include "audio_parser.h"
+#include "fs/Path.hxx"
+#include "AudioParser.hxx"
#include "audio_format.h"
-#include "filter_plugin.h"
-#include "pcm_volume.h"
-#include "idle.h"
-#include "mixer_control.h"
-#include "playlist.h"
+#include "FilterPlugin.hxx"
+#include "FilterInternal.hxx"
+#include "PcmVolume.hxx"
+#include "MixerControl.hxx"
#include "stdbin.h"
#include <glib.h>
@@ -35,13 +35,6 @@
#include <errno.h>
#include <unistd.h>
-struct playlist g_playlist;
-
-void
-idle_add(G_GNUC_UNUSED unsigned flags)
-{
-}
-
bool
mixer_set_volume(G_GNUC_UNUSED struct mixer *mixer,
G_GNUC_UNUSED unsigned volume, G_GNUC_UNUSED GError **error_r)
@@ -60,11 +53,11 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
}
static const struct config_param *
-find_named_config_block(const char *block, const char *name)
+find_named_config_block(ConfigOption option, const char *name)
{
const struct config_param *param = NULL;
- while ((param = config_get_next_param(block, param)) != NULL) {
+ while ((param = config_get_next_param(option, param)) != NULL) {
const char *current_name =
config_get_block_string(param, "name", NULL);
if (current_name != NULL && strcmp(current_name, name) == 0)
@@ -74,20 +67,19 @@ find_named_config_block(const char *block, const char *name)
return NULL;
}
-static struct filter *
+static Filter *
load_filter(const char *name)
{
const struct config_param *param;
- struct filter *filter;
GError *error = NULL;
- param = find_named_config_block("filter", name);
+ param = find_named_config_block(CONF_AUDIO_FILTER, name);
if (param == NULL) {
g_printerr("No such configured filter: %s\n", name);
- return false;
+ return nullptr;
}
- filter = filter_configured_new(param, &error);
+ Filter *filter = filter_configured_new(param, &error);
if (filter == NULL) {
g_printerr("Failed to load filter: %s\n", error->message);
g_error_free(error);
@@ -103,7 +95,6 @@ int main(int argc, char **argv)
struct audio_format_string af_string;
bool success;
GError *error = NULL;
- struct filter *filter;
const struct audio_format *out_audio_format;
char buffer[4096];
@@ -112,6 +103,8 @@ int main(int argc, char **argv)
return 1;
}
+ const Path config_path = Path::FromFS(argv[1]);
+
audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2);
/* initialize GLib */
@@ -122,8 +115,7 @@ int main(int argc, char **argv)
/* read configuration file (mpd.conf) */
config_global_init();
- success = config_read_file(argv[1], &error);
- if (!success) {
+ if (!ReadConfigFile(config_path, &error)) {
g_printerr("%s:", error->message);
g_error_free(error);
return 1;
@@ -144,17 +136,17 @@ int main(int argc, char **argv)
/* initialize the filter */
- filter = load_filter(argv[2]);
+ Filter *filter = load_filter(argv[2]);
if (filter == NULL)
return 1;
/* open the filter */
- out_audio_format = filter_open(filter, &audio_format, &error);
+ out_audio_format = filter->Open(audio_format, &error);
if (out_audio_format == NULL) {
g_printerr("Failed to open filter: %s\n", error->message);
g_error_free(error);
- filter_free(filter);
+ delete filter;
return 1;
}
@@ -172,28 +164,28 @@ int main(int argc, char **argv)
if (nbytes <= 0)
break;
- dest = filter_filter(filter, buffer, (size_t)nbytes,
- &length, &error);
+ dest = filter->FilterPCM(buffer, (size_t)nbytes,
+ &length, &error);
if (dest == NULL) {
g_printerr("Filter failed: %s\n", error->message);
- filter_close(filter);
- filter_free(filter);
+ filter->Close();
+ delete filter;
return 1;
}
nbytes = write(1, dest, length);
if (nbytes < 0) {
g_printerr("Failed to write: %s\n", g_strerror(errno));
- filter_close(filter);
- filter_free(filter);
+ filter->Close();
+ delete filter;
return 1;
}
}
/* cleanup and exit */
- filter_close(filter);
- filter_free(filter);
+ filter->Close();
+ delete filter;
config_global_finish();
diff --git a/test/run_inotify.c b/test/run_inotify.cxx
index 3e7c70db..29fcf70a 100644
--- a/test/run_inotify.c
+++ b/test/run_inotify.cxx
@@ -18,18 +18,20 @@
*/
#include "config.h"
-#include "inotify_source.h"
+#include "InotifySource.hxx"
+#include "event/Loop.hxx"
+
+#include <glib.h>
-#include <stdbool.h>
#include <sys/inotify.h>
#include <signal.h>
-static GMainLoop *main_loop;
+static EventLoop *event_loop;
static void
exit_signal_handler(G_GNUC_UNUSED int signum)
{
- g_main_loop_quit(main_loop);
+ event_loop->Break();
}
enum {
@@ -59,26 +61,25 @@ int main(int argc, char **argv)
path = argv[1];
- struct mpd_inotify_source *source =
- mpd_inotify_source_new(my_inotify_callback, NULL,
- &error);
+ event_loop = new EventLoop(EventLoop::Default());
+
+ InotifySource *source = InotifySource::Create(*event_loop,
+ my_inotify_callback,
+ nullptr, &error);
if (source == NULL) {
g_warning("%s", error->message);
g_error_free(error);
return 2;
}
- int descriptor = mpd_inotify_source_add(source, path,
- IN_MASK, &error);
+ int descriptor = source->Add(path, IN_MASK, &error);
if (descriptor < 0) {
- mpd_inotify_source_free(source);
+ delete source;
g_warning("%s", error->message);
g_error_free(error);
return 2;
}
- main_loop = g_main_loop_new(NULL, false);
-
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
@@ -86,8 +87,8 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
- g_main_loop_run(main_loop);
- g_main_loop_unref(main_loop);
+ event_loop->Run();
- mpd_inotify_source_free(source);
+ delete source;
+ delete event_loop;
}
diff --git a/test/run_input.c b/test/run_input.cxx
index 676e4e61..157613db 100644
--- a/test/run_input.c
+++ b/test/run_input.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,17 @@
*/
#include "config.h"
-#include "io_thread.h"
-#include "input_init.h"
-#include "input_stream.h"
-#include "tag_pool.h"
-#include "tag_save.h"
-#include "conf.h"
+#include "TagSave.hxx"
#include "stdbin.h"
+#include "tag.h"
+#include "conf.h"
+#include "input_stream.h"
+#include "InputStream.hxx"
+#include "InputInit.hxx"
+#include "IOThread.hxx"
#ifdef ENABLE_ARCHIVE
-#include "archive_list.h"
+#include "ArchiveList.hxx"
#endif
#include <glib.h>
@@ -68,8 +69,8 @@ dump_input_stream(struct input_stream *is)
/* print meta data */
- if (is->mime != NULL)
- g_printerr("MIME type: %s\n", is->mime);
+ if (!is->mime.empty())
+ g_printerr("MIME type: %s\n", is->mime.c_str());
/* read data and tags from the stream */
@@ -127,7 +128,6 @@ int main(int argc, char **argv)
/* initialize MPD */
- tag_pool_init();
config_global_init();
io_thread_init();
@@ -149,8 +149,8 @@ int main(int argc, char **argv)
/* open the stream and dump it */
- GMutex *mutex = g_mutex_new();
- GCond *cond = g_cond_new();
+ Mutex mutex;
+ Cond cond;
is = input_stream_open(argv[1], mutex, cond, &error);
if (is != NULL) {
@@ -165,9 +165,6 @@ int main(int argc, char **argv)
ret = 2;
}
- g_cond_free(cond);
- g_mutex_free(mutex);
-
/* deinitialize everything */
input_stream_global_finish();
@@ -179,7 +176,6 @@ int main(int argc, char **argv)
io_thread_deinit();
config_global_finish();
- tag_pool_deinit();
return ret;
}
diff --git a/test/run_normalize.c b/test/run_normalize.cxx
index fc26829e..070dbaf5 100644
--- a/test/run_normalize.c
+++ b/test/run_normalize.cxx
@@ -25,7 +25,7 @@
#include "config.h"
#include "AudioCompress/compress.h"
-#include "audio_parser.h"
+#include "AudioParser.hxx"
#include "audio_format.h"
#include "stdbin.h"
@@ -58,7 +58,7 @@ int main(int argc, char **argv)
return 1;
}
} else
- audio_format_init(&audio_format, 48000, 16, 2);
+ audio_format_init(&audio_format, 48000, SAMPLE_FORMAT_S16, 2);
compressor = Compressor_new(0);
diff --git a/test/run_output.c b/test/run_output.cxx
index bbb1be7d..5b6d54a1 100644
--- a/test/run_output.c
+++ b/test/run_output.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +18,24 @@
*/
#include "config.h"
-#include "io_thread.h"
+#include "OutputControl.hxx"
+#include "conf.h"
+#include "Idle.hxx"
+#include "Main.hxx"
+#include "event/Loop.hxx"
+#include "GlobalEvents.hxx"
+#include "IOThread.hxx"
+#include "fs/Path.hxx"
+#include "AudioParser.hxx"
+#include "PcmConvert.hxx"
+
+extern "C" {
#include "output_plugin.h"
#include "output_internal.h"
-#include "output_control.h"
-#include "conf.h"
-#include "audio_parser.h"
-#include "filter_registry.h"
-#include "pcm_convert.h"
-#include "event_pipe.h"
-#include "idle.h"
-#include "playlist.h"
-#include "player_control.h"
+}
+
+#include "FilterRegistry.hxx"
+#include "PlayerControl.hxx"
#include "stdbin.h"
#include <glib.h>
@@ -39,33 +45,22 @@
#include <unistd.h>
#include <stdlib.h>
-struct playlist g_playlist;
+EventLoop *main_loop;
void
-idle_add(G_GNUC_UNUSED unsigned flags)
-{
-}
-
-void
-event_pipe_emit(G_GNUC_UNUSED enum pipe_event event)
-{
-}
-
-void pcm_convert_init(G_GNUC_UNUSED struct pcm_convert_state *state)
+GlobalEvents::Emit(gcc_unused Event event)
{
}
-void pcm_convert_deinit(G_GNUC_UNUSED struct pcm_convert_state *state)
-{
-}
+PcmConvert::PcmConvert() {}
+PcmConvert::~PcmConvert() {}
const void *
-pcm_convert(G_GNUC_UNUSED struct pcm_convert_state *state,
- G_GNUC_UNUSED const struct audio_format *src_format,
- G_GNUC_UNUSED const void *src, G_GNUC_UNUSED size_t src_size,
- G_GNUC_UNUSED const struct audio_format *dest_format,
- G_GNUC_UNUSED size_t *dest_size_r,
- GError **error_r)
+PcmConvert::Convert(gcc_unused const audio_format *src_format,
+ gcc_unused const void *src, gcc_unused size_t src_size,
+ gcc_unused const audio_format *dest_format,
+ gcc_unused size_t *dest_size_r,
+ gcc_unused GError **error_r)
{
g_set_error(error_r, pcm_convert_quark(), 0,
"Not implemented");
@@ -80,11 +75,11 @@ filter_plugin_by_name(G_GNUC_UNUSED const char *name)
}
static const struct config_param *
-find_named_config_block(const char *block, const char *name)
+find_named_config_block(ConfigOption option, const char *name)
{
const struct config_param *param = NULL;
- while ((param = config_get_next_param(block, param)) != NULL) {
+ while ((param = config_get_next_param(option, param)) != NULL) {
const char *current_name =
config_get_block_string(param, "name", NULL);
if (current_name != NULL && strcmp(current_name, name) == 0)
@@ -94,6 +89,10 @@ find_named_config_block(const char *block, const char *name)
return NULL;
}
+player_control::player_control(gcc_unused unsigned _buffer_chunks,
+ gcc_unused unsigned _buffered_before_play) {}
+player_control::~player_control() {}
+
static struct audio_output *
load_audio_output(const char *name)
{
@@ -103,10 +102,10 @@ load_audio_output(const char *name)
param = find_named_config_block(CONF_AUDIO_OUTPUT, name);
if (param == NULL) {
g_printerr("No such configured audio output: %s\n", name);
- return false;
+ return nullptr;
}
- static struct player_control dummy_player_control;
+ static struct player_control dummy_player_control(32, 4);
struct audio_output *ao =
audio_output_new(param, &dummy_player_control, &error);
@@ -197,6 +196,8 @@ int main(int argc, char **argv)
return 1;
}
+ const Path config_path = Path::FromFS(argv[1]);
+
audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2);
g_thread_init(NULL);
@@ -204,13 +205,14 @@ int main(int argc, char **argv)
/* read configuration file (mpd.conf) */
config_global_init();
- success = config_read_file(argv[1], &error);
- if (!success) {
+ if (!ReadConfigFile(config_path, &error)) {
g_printerr("%s:", error->message);
g_error_free(error);
return 1;
}
+ main_loop = new EventLoop(EventLoop::Default());
+
io_thread_init();
if (!io_thread_start(&error)) {
g_warning("%s", error->message);
@@ -247,6 +249,8 @@ int main(int argc, char **argv)
io_thread_deinit();
+ delete main_loop;
+
config_global_finish();
return success ? EXIT_SUCCESS : EXIT_FAILURE;
diff --git a/test/run_tcp_connect.c b/test/run_tcp_connect.c
deleted file mode 100644
index bf8d9b82..00000000
--- a/test/run_tcp_connect.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "resolver.h"
-#include "io_thread.h"
-#include "tcp_connect.h"
-#include "fd_util.h"
-
-#include <assert.h>
-#include <stdlib.h>
-
-#ifdef WIN32
-#include <ws2tcpip.h>
-#include <winsock.h>
-#else
-#include <sys/socket.h>
-#include <netdb.h>
-#endif
-
-static struct tcp_connect *handle;
-static GMutex *mutex;
-static GCond *cond;
-static bool done, success;
-
-static void
-my_tcp_connect_success(int fd, G_GNUC_UNUSED void *ctx)
-{
- assert(!done);
- assert(!success);
-
- close_socket(fd);
- g_print("success\n");
-
- g_mutex_lock(mutex);
- done = success = true;
- g_cond_signal(cond);
- g_mutex_unlock(mutex);
-}
-
-static void
-my_tcp_connect_error(GError *error, G_GNUC_UNUSED void *ctx)
-{
- assert(!done);
- assert(!success);
-
- g_printerr("error: %s\n", error->message);
- g_error_free(error);
-
- g_mutex_lock(mutex);
- done = true;
- g_cond_signal(cond);
- g_mutex_unlock(mutex);
-}
-
-static void
-my_tcp_connect_timeout(G_GNUC_UNUSED void *ctx)
-{
- assert(!done);
- assert(!success);
-
- g_printerr("timeout\n");
-
- g_mutex_lock(mutex);
- done = true;
- g_cond_signal(cond);
- g_mutex_unlock(mutex);
-}
-
-static void
-my_tcp_connect_canceled(G_GNUC_UNUSED void *ctx)
-{
- assert(!done);
- assert(!success);
-
- g_printerr("canceled\n");
-
- g_mutex_lock(mutex);
- done = true;
- g_cond_signal(cond);
- g_mutex_unlock(mutex);
-}
-
-static const struct tcp_connect_handler my_tcp_connect_handler = {
- .success = my_tcp_connect_success,
- .error = my_tcp_connect_error,
- .timeout = my_tcp_connect_timeout,
- .canceled = my_tcp_connect_canceled,
-};
-
-int main(int argc, char **argv)
-{
- if (argc != 2) {
- g_printerr("Usage: run_tcp_connect IP:PORT\n");
- return 1;
- }
-
- GError *error = NULL;
- struct addrinfo *ai = resolve_host_port(argv[1], 80, 0, SOCK_STREAM,
- &error);
- if (ai == NULL) {
- g_printerr("%s\n", error->message);
- g_error_free(error);
- return EXIT_FAILURE;
- }
-
- /* initialize GLib */
-
- g_thread_init(NULL);
-
- /* initialize MPD */
-
- io_thread_init();
- if (!io_thread_start(&error)) {
- freeaddrinfo(ai);
- g_printerr("%s", error->message);
- g_error_free(error);
- return EXIT_FAILURE;
- }
-
- /* open the connection */
-
- mutex = g_mutex_new();
- cond = g_cond_new();
-
- tcp_connect_address(ai->ai_addr, ai->ai_addrlen, 5000,
- &my_tcp_connect_handler, NULL,
- &handle);
- freeaddrinfo(ai);
-
- if (handle != NULL) {
- g_mutex_lock(mutex);
- while (!done)
- g_cond_wait(cond, mutex);
- g_mutex_unlock(mutex);
-
- tcp_connect_free(handle);
- }
-
- g_cond_free(cond);
- g_mutex_free(mutex);
-
- /* deinitialize everything */
-
- io_thread_deinit();
-
- return EXIT_SUCCESS;
-}
diff --git a/test/software_volume.c b/test/software_volume.cxx
index 2357da67..92993239 100644
--- a/test/software_volume.c
+++ b/test/software_volume.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,8 +24,8 @@
*/
#include "config.h"
-#include "pcm_volume.h"
-#include "audio_parser.h"
+#include "PcmVolume.hxx"
+#include "AudioParser.hxx"
#include "audio_format.h"
#include "stdbin.h"
@@ -59,7 +59,8 @@ int main(int argc, char **argv)
audio_format_init(&audio_format, 48000, SAMPLE_FORMAT_S16, 2);
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
- if (!pcm_volume(buffer, nbytes, audio_format.format,
+ if (!pcm_volume(buffer, nbytes,
+ sample_format(audio_format.format),
PCM_VOLUME_1 / 2)) {
g_printerr("pcm_volume() has failed\n");
return 2;
diff --git a/test/test_pcm_all.h b/test/test_pcm_all.hxx
index 493619b5..18202454 100644
--- a/test/test_pcm_all.h
+++ b/test/test_pcm_all.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,40 +17,64 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_TEST_PCM_ALL_H
-#define MPD_TEST_PCM_ALL_H
+#ifndef MPD_TEST_PCM_ALL_HXX
+#define MPD_TEST_PCM_ALL_HXX
void
-test_pcm_dither_24(void);
+test_pcm_dither_24();
void
-test_pcm_dither_32(void);
+test_pcm_dither_32();
void
-test_pcm_pack_24(void);
+test_pcm_pack_24();
void
-test_pcm_unpack_24(void);
+test_pcm_unpack_24();
void
-test_pcm_channels_16(void);
+test_pcm_channels_16();
void
-test_pcm_channels_32(void);
+test_pcm_channels_32();
void
-test_pcm_volume_8(void);
+test_pcm_volume_8();
void
-test_pcm_volume_16(void);
+test_pcm_volume_16();
void
-test_pcm_volume_24(void);
+test_pcm_volume_24();
void
-test_pcm_volume_32(void);
+test_pcm_volume_32();
void
-test_pcm_volume_float(void);
+test_pcm_volume_float();
+
+void
+test_pcm_format_8_to_16();
+
+void
+test_pcm_format_16_to_24();
+
+void
+test_pcm_format_16_to_32();
+
+void
+test_pcm_format_float();
+
+void
+test_pcm_mix_8();
+
+void
+test_pcm_mix_16();
+
+void
+test_pcm_mix_24();
+
+void
+test_pcm_mix_32();
#endif
diff --git a/test/test_pcm_channels.c b/test/test_pcm_channels.cxx
index fb3ec5c3..8e013d5a 100644
--- a/test/test_pcm_channels.c
+++ b/test/test_pcm_channels.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,20 +18,18 @@
*/
#include "config.h"
-#include "test_pcm_all.h"
-#include "pcm_channels.h"
+#include "test_pcm_all.hxx"
+#include "test_pcm_util.hxx"
+#include "PcmChannels.hxx"
#include "pcm_buffer.h"
#include <glib.h>
void
-test_pcm_channels_16(void)
+test_pcm_channels_16()
{
- enum { N = 256 };
- int16_t src[N * 2];
-
- for (unsigned i = 0; i < G_N_ELEMENTS(src); ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int16_t, N * 2>();
struct pcm_buffer buffer;
pcm_buffer_init(&buffer);
@@ -63,13 +61,10 @@ test_pcm_channels_16(void)
}
void
-test_pcm_channels_32(void)
+test_pcm_channels_32()
{
- enum { N = 256 };
- int32_t src[N * 2];
-
- for (unsigned i = 0; i < G_N_ELEMENTS(src); ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int32_t, N * 2>();
struct pcm_buffer buffer;
pcm_buffer_init(&buffer);
diff --git a/test/test_pcm_dither.c b/test/test_pcm_dither.cxx
index 44d10520..2fb976db 100644
--- a/test/test_pcm_dither.c
+++ b/test/test_pcm_dither.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,38 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "test_pcm_all.h"
-#include "pcm_dither.h"
+#include "test_pcm_all.hxx"
+#include "test_pcm_util.hxx"
+#include "PcmDither.hxx"
#include <glib.h>
-/**
- * Generate a random 24 bit PCM sample.
- */
-static int32_t
-random24(void)
-{
- int32_t x = g_random_int() & 0xffffff;
- if (x & 0x800000)
- x |= 0xff000000;
- return x;
-}
-
void
-test_pcm_dither_24(void)
+test_pcm_dither_24()
{
- struct pcm_dither dither;
-
- pcm_dither_24_init(&dither);
-
- enum { N = 256 };
- int32_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = random24();
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int32_t, N>(GlibRandomInt24());
int16_t dest[N];
-
- pcm_dither_24_to_16(&dither, dest, src, src + N);
+ PcmDither dither;
+ dither.Dither24To16(dest, src.begin(), src.end());
for (unsigned i = 0; i < N; ++i) {
g_assert_cmpint(dest[i], >=, (src[i] >> 8) - 8);
@@ -57,20 +40,14 @@ test_pcm_dither_24(void)
}
void
-test_pcm_dither_32(void)
+test_pcm_dither_32()
{
- struct pcm_dither dither;
-
- pcm_dither_24_init(&dither);
-
- enum { N = 256 };
- int32_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int32_t, N>();
int16_t dest[N];
-
- pcm_dither_32_to_16(&dither, dest, src, src + N);
+ PcmDither dither;
+ dither.Dither32To16(dest, src.begin(), src.end());
for (unsigned i = 0; i < N; ++i) {
g_assert_cmpint(dest[i], >=, (src[i] >> 16) - 8);
diff --git a/test/test_pcm_format.cxx b/test/test_pcm_format.cxx
new file mode 100644
index 00000000..f175a850
--- /dev/null
+++ b/test/test_pcm_format.cxx
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "test_pcm_all.hxx"
+#include "test_pcm_util.hxx"
+#include "PcmFormat.hxx"
+#include "PcmDither.hxx"
+#include "PcmUtils.hxx"
+#include "pcm_buffer.h"
+#include "audio_format.h"
+
+#include <glib.h>
+
+void
+test_pcm_format_8_to_16()
+{
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int8_t, N>();
+
+ struct pcm_buffer buffer;
+ pcm_buffer_init(&buffer);
+
+ size_t d_size;
+ PcmDither dither;
+ auto d = pcm_convert_to_16(&buffer, dither, SAMPLE_FORMAT_S8,
+ src, sizeof(src), &d_size);
+ auto d_end = pcm_end_pointer(d, d_size);
+ g_assert_cmpint(d_end - d, ==, N);
+
+ for (size_t i = 0; i < N; ++i)
+ g_assert_cmpint(src[i], ==, d[i] >> 8);
+
+ pcm_buffer_deinit(&buffer);
+}
+
+void
+test_pcm_format_16_to_24()
+{
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int16_t, N>();
+
+ struct pcm_buffer buffer;
+ pcm_buffer_init(&buffer);
+
+ size_t d_size;
+ auto d = pcm_convert_to_24(&buffer, SAMPLE_FORMAT_S16,
+ src, sizeof(src), &d_size);
+ auto d_end = pcm_end_pointer(d, d_size);
+ g_assert_cmpint(d_end - d, ==, N);
+
+ for (size_t i = 0; i < N; ++i)
+ g_assert_cmpint(src[i], ==, d[i] >> 8);
+
+ pcm_buffer_deinit(&buffer);
+}
+
+void
+test_pcm_format_16_to_32()
+{
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int16_t, N>();
+
+ struct pcm_buffer buffer;
+ pcm_buffer_init(&buffer);
+
+ size_t d_size;
+ auto d = pcm_convert_to_32(&buffer, SAMPLE_FORMAT_S16,
+ src, sizeof(src), &d_size);
+ auto d_end = pcm_end_pointer(d, d_size);
+ g_assert_cmpint(d_end - d, ==, N);
+
+ for (size_t i = 0; i < N; ++i)
+ g_assert_cmpint(src[i], ==, d[i] >> 16);
+
+ pcm_buffer_deinit(&buffer);
+}
+
+void
+test_pcm_format_float()
+{
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int16_t, N>();
+
+ struct pcm_buffer buffer1, buffer2;
+ pcm_buffer_init(&buffer1);
+ pcm_buffer_init(&buffer2);
+
+ size_t f_size;
+ auto f = pcm_convert_to_float(&buffer1, SAMPLE_FORMAT_S16,
+ src, sizeof(src), &f_size);
+ auto f_end = pcm_end_pointer(f, f_size);
+ g_assert_cmpint(f_end - f, ==, N);
+
+ for (auto i = f; i != f_end; ++i) {
+ g_assert(*i >= -1.);
+ g_assert(*i <= 1.);
+ }
+
+ PcmDither dither;
+
+ size_t d_size;
+ auto d = pcm_convert_to_16(&buffer2, dither,
+ SAMPLE_FORMAT_FLOAT,
+ f, f_size, &d_size);
+ auto d_end = pcm_end_pointer(d, d_size);
+ g_assert_cmpint(d_end - d, ==, N);
+
+ for (size_t i = 0; i < N; ++i)
+ g_assert_cmpint(src[i], ==, d[i]);
+
+ pcm_buffer_deinit(&buffer1);
+ pcm_buffer_deinit(&buffer2);
+}
diff --git a/test/test_pcm_main.c b/test/test_pcm_main.cxx
index a483baaa..a221b26a 100644
--- a/test/test_pcm_main.c
+++ b/test/test_pcm_main.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "test_pcm_all.h"
+#include "test_pcm_all.hxx"
#include <glib.h>
@@ -38,5 +38,15 @@ main(int argc, char **argv)
g_test_add_func("/pcm/volume/32", test_pcm_volume_32);
g_test_add_func("/pcm/volume/float", test_pcm_volume_float);
+ g_test_add_func("/pcm/format/8_to_16", test_pcm_format_8_to_16);
+ g_test_add_func("/pcm/format/16_to_24", test_pcm_format_16_to_24);
+ g_test_add_func("/pcm/format/16_to_32", test_pcm_format_16_to_32);
+ g_test_add_func("/pcm/format/float", test_pcm_format_float);
+
+ g_test_add_func("/pcm/mix/8", test_pcm_mix_8);
+ g_test_add_func("/pcm/mix/16", test_pcm_mix_16);
+ g_test_add_func("/pcm/mix/24", test_pcm_mix_24);
+ g_test_add_func("/pcm/mix/32", test_pcm_mix_32);
+
g_test_run();
}
diff --git a/test/test_pcm_mix.cxx b/test/test_pcm_mix.cxx
new file mode 100644
index 00000000..a6e01d20
--- /dev/null
+++ b/test/test_pcm_mix.cxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "test_pcm_all.hxx"
+#include "test_pcm_util.hxx"
+#include "PcmMix.hxx"
+
+#include <glib.h>
+
+template<typename T, sample_format format, typename G=GlibRandomInt<T>>
+void
+TestPcmMix(G g=G())
+{
+ constexpr unsigned N = 256;
+ const auto src1 = TestDataBuffer<T, N>(g);
+ const auto src2 = TestDataBuffer<T, N>(g);
+
+ /* portion1=1.0: result must be equal to src1 */
+ auto result = src1;
+ bool success = pcm_mix(result.begin(), src2.begin(), sizeof(result),
+ format, 1.0);
+ g_assert(success);
+ AssertEqualWithTolerance(result, src1, 1);
+
+ /* portion1=0.0: result must be equal to src2 */
+ result = src1;
+ success = pcm_mix(result.begin(), src2.begin(), sizeof(result),
+ format, 0.0);
+ g_assert(success);
+ AssertEqualWithTolerance(result, src2, 1);
+
+ /* portion1=0.5 */
+ result = src1;
+ success = pcm_mix(result.begin(), src2.begin(), sizeof(result),
+ format, 0.5);
+ g_assert(success);
+
+ auto expected = src1;
+ for (unsigned i = 0; i < N; ++i)
+ expected[i] = (int64_t(src1[i]) + int64_t(src2[i])) / 2;
+
+ AssertEqualWithTolerance(result, expected, 1);
+}
+
+void
+test_pcm_mix_8()
+{
+ TestPcmMix<int8_t, SAMPLE_FORMAT_S8>();
+}
+
+void
+test_pcm_mix_16()
+{
+ TestPcmMix<int16_t, SAMPLE_FORMAT_S16>();
+}
+
+void
+test_pcm_mix_24()
+{
+ TestPcmMix<int32_t, SAMPLE_FORMAT_S24_P32>(GlibRandomInt24());
+}
+
+void
+test_pcm_mix_32()
+{
+ TestPcmMix<int32_t, SAMPLE_FORMAT_S32>();
+}
diff --git a/test/test_pcm_pack.c b/test/test_pcm_pack.cxx
index 5127536f..e23acad5 100644
--- a/test/test_pcm_pack.c
+++ b/test/test_pcm_pack.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,34 +17,23 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "test_pcm_all.h"
+#include "test_pcm_all.hxx"
+#include "test_pcm_util.hxx"
+
+extern "C" {
#include "pcm_pack.h"
+}
#include <glib.h>
-/**
- * Generate a random 24 bit PCM sample.
- */
-static int32_t
-random24(void)
-{
- int32_t x = g_random_int() & 0xffffff;
- if (x & 0x800000)
- x |= 0xff000000;
- return x;
-}
-
void
-test_pcm_pack_24(void)
+test_pcm_pack_24()
{
- enum { N = 256 };
- int32_t src[N * 3];
- for (unsigned i = 0; i < N; ++i)
- src[i] = random24();
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<int32_t, N>(GlibRandomInt24());
uint8_t dest[N * 3];
-
- pcm_pack_24(dest, src, src + N);
+ pcm_pack_24(dest, src.begin(), src.end());
for (unsigned i = 0; i < N; ++i) {
int32_t d;
@@ -62,16 +51,13 @@ test_pcm_pack_24(void)
}
void
-test_pcm_unpack_24(void)
+test_pcm_unpack_24()
{
- enum { N = 256 };
- uint8_t src[N * 3];
- for (unsigned i = 0; i < G_N_ELEMENTS(src); ++i)
- src[i] = g_random_int_range(0, 256);
+ constexpr unsigned N = 256;
+ const auto src = TestDataBuffer<uint8_t, N * 3>();
int32_t dest[N];
-
- pcm_unpack_24(dest, src, src + G_N_ELEMENTS(src));
+ pcm_unpack_24(dest, src.begin(), src.end());
for (unsigned i = 0; i < N; ++i) {
int32_t s;
diff --git a/test/test_pcm_util.hxx b/test/test_pcm_util.hxx
new file mode 100644
index 00000000..84ba074f
--- /dev/null
+++ b/test/test_pcm_util.hxx
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+
+#include <array>
+
+#include <stddef.h>
+#include <stdint.h>
+
+template<typename T>
+struct GlibRandomInt {
+ T operator()() const {
+ return T(g_random_int());
+ }
+};
+
+struct GlibRandomInt24 : GlibRandomInt<int32_t> {
+ int32_t operator()() const {
+ auto t = GlibRandomInt::operator()();
+ t &= 0xffffff;
+ if (t & 0x800000)
+ t |= 0xff000000;
+ return t;
+ }
+};
+
+struct GlibRandomFloat {
+ float operator()() const {
+ return g_random_double_range(-1.0, 1.0);
+ }
+};
+
+template<typename T, size_t N>
+class TestDataBuffer : std::array<T, N> {
+public:
+ using typename std::array<T, N>::const_pointer;
+ using std::array<T, N>::size;
+ using std::array<T, N>::begin;
+ using std::array<T, N>::end;
+ using std::array<T, N>::operator[];
+
+ template<typename G=GlibRandomInt<T>>
+ TestDataBuffer(G g=G()):std::array<T, N>() {
+ for (auto &i : *this)
+ i = g();
+
+ }
+
+ operator typename std::array<T, N>::const_pointer() const {
+ return begin();
+ }
+};
+
+template<typename T>
+bool
+AssertEqualWithTolerance(const T &a, const T &b, unsigned tolerance)
+{
+ g_assert_cmpint(a.size(), ==, b.size());
+
+ for (unsigned i = 0; i < a.size(); ++i) {
+ int64_t x = a[i], y = b[i];
+
+ g_assert_cmpint(x, >=, y - int64_t(tolerance));
+ g_assert_cmpint(x, <=, y + int64_t(tolerance));
+ }
+
+ return true;
+}
diff --git a/test/test_pcm_volume.c b/test/test_pcm_volume.cxx
index 713645cf..1ab59049 100644
--- a/test/test_pcm_volume.c
+++ b/test/test_pcm_volume.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,35 +17,36 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "test_pcm_all.h"
-#include "pcm_volume.h"
+#include "test_pcm_all.hxx"
+#include "PcmVolume.hxx"
+#include "test_pcm_util.hxx"
#include <glib.h>
+#include <algorithm>
+
#include <string.h>
void
-test_pcm_volume_8(void)
+test_pcm_volume_8()
{
- enum { N = 256 };
- static const int8_t zero[N];
- int8_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ static int8_t zero[N];
+ const auto src = TestDataBuffer<int8_t, N>();
int8_t dest[N];
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S8,
0), ==, true);
g_assert_cmpint(memcmp(dest, zero, sizeof(zero)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S8,
PCM_VOLUME_1), ==, true);
g_assert_cmpint(memcmp(dest, src, sizeof(src)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S8,
PCM_VOLUME_1 / 2), ==, true);
@@ -56,27 +57,25 @@ test_pcm_volume_8(void)
}
void
-test_pcm_volume_16(void)
+test_pcm_volume_16()
{
- enum { N = 256 };
- static const int16_t zero[N];
- int16_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ static int16_t zero[N];
+ const auto src = TestDataBuffer<int16_t, N>();
int16_t dest[N];
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S16,
0), ==, true);
g_assert_cmpint(memcmp(dest, zero, sizeof(zero)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S16,
PCM_VOLUME_1), ==, true);
g_assert_cmpint(memcmp(dest, src, sizeof(src)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S16,
PCM_VOLUME_1 / 2), ==, true);
@@ -86,40 +85,26 @@ test_pcm_volume_16(void)
}
}
-/**
- * Generate a random 24 bit PCM sample.
- */
-static int32_t
-random24(void)
-{
- int32_t x = g_random_int() & 0xffffff;
- if (x & 0x800000)
- x |= 0xff000000;
- return x;
-}
-
void
-test_pcm_volume_24(void)
+test_pcm_volume_24()
{
- enum { N = 256 };
- static const int32_t zero[N];
- int32_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = random24();
+ constexpr unsigned N = 256;
+ static int32_t zero[N];
+ const auto src = TestDataBuffer<int32_t, N>(GlibRandomInt24());
int32_t dest[N];
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S24_P32,
0), ==, true);
g_assert_cmpint(memcmp(dest, zero, sizeof(zero)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S24_P32,
PCM_VOLUME_1), ==, true);
g_assert_cmpint(memcmp(dest, src, sizeof(src)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S24_P32,
PCM_VOLUME_1 / 2), ==, true);
@@ -130,27 +115,25 @@ test_pcm_volume_24(void)
}
void
-test_pcm_volume_32(void)
+test_pcm_volume_32()
{
- enum { N = 256 };
- static const int32_t zero[N];
- int32_t src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = g_random_int();
+ constexpr unsigned N = 256;
+ static int32_t zero[N];
+ const auto src = TestDataBuffer<int32_t, N>();
int32_t dest[N];
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S32,
0), ==, true);
g_assert_cmpint(memcmp(dest, zero, sizeof(zero)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S32,
PCM_VOLUME_1), ==, true);
g_assert_cmpint(memcmp(dest, src, sizeof(src)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_S32,
PCM_VOLUME_1 / 2), ==, true);
@@ -161,27 +144,25 @@ test_pcm_volume_32(void)
}
void
-test_pcm_volume_float(void)
+test_pcm_volume_float()
{
- enum { N = 256 };
- static const float zero[N];
- float src[N];
- for (unsigned i = 0; i < N; ++i)
- src[i] = g_random_double_range(-1.0, 1.0);
+ constexpr unsigned N = 256;
+ static float zero[N];
+ const auto src = TestDataBuffer<float, N>(GlibRandomFloat());
float dest[N];
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_FLOAT,
0), ==, true);
g_assert_cmpint(memcmp(dest, zero, sizeof(zero)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_FLOAT,
PCM_VOLUME_1), ==, true);
g_assert_cmpint(memcmp(dest, src, sizeof(src)), ==, 0);
- memcpy(dest, src, sizeof(src));
+ std::copy(src.begin(), src.end(), dest);
g_assert_cmpint(pcm_volume(dest, sizeof(dest), SAMPLE_FORMAT_FLOAT,
PCM_VOLUME_1 / 2), ==, true);
diff --git a/test/test_queue_priority.c b/test/test_queue_priority.c
deleted file mode 100644
index 5543edbb..00000000
--- a/test/test_queue_priority.c
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "queue.h"
-#include "song.h"
-
-void
-song_free(G_GNUC_UNUSED struct song *song)
-{
-}
-
-G_GNUC_UNUSED
-static void
-dump_order(const struct queue *queue)
-{
- g_printerr("queue length=%u, order:\n", queue_length(queue));
- for (unsigned i = 0; i < queue_length(queue); ++i)
- g_printerr(" [%u] -> %u (prio=%u)\n", i, queue->order[i],
- queue->items[queue->order[i]].priority);
-}
-
-static void
-check_descending_priority(G_GNUC_UNUSED const struct queue *queue,
- unsigned start_order)
-{
- assert(start_order < queue_length(queue));
-
- uint8_t last_priority = 0xff;
- for (unsigned order = start_order; order < queue_length(queue); ++order) {
- unsigned position = queue_order_to_position(queue, order);
- uint8_t priority = queue->items[position].priority;
- assert(priority <= last_priority);
- (void)last_priority;
- last_priority = priority;
- }
-}
-
-int
-main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
-{
- struct song songs[16];
-
- struct queue queue;
- queue_init(&queue, 32);
-
- for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
- queue_append(&queue, &songs[i], 0);
-
- assert(queue_length(&queue) == G_N_ELEMENTS(songs));
-
- /* priority=10 for 4 items */
-
- queue_set_priority_range(&queue, 4, 8, 10, -1);
-
- queue.random = true;
- queue_shuffle_order(&queue);
- check_descending_priority(&queue, 0);
-
- for (unsigned i = 0; i < 4; ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) < 4);
- }
-
- for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- /* priority=50 one more item */
-
- queue_set_priority_range(&queue, 15, 16, 50, -1);
- check_descending_priority(&queue, 0);
-
- assert(queue_position_to_order(&queue, 15) == 0);
-
- for (unsigned i = 0; i < 4; ++i) {
- assert(queue_position_to_order(&queue, i) >= 4);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) >= 1 &&
- queue_position_to_order(&queue, i) < 5);
- }
-
- for (unsigned i = 8; i < 15; ++i) {
- assert(queue_position_to_order(&queue, i) >= 5);
- }
-
- /* priority=20 for one of the 4 priority=10 items */
-
- queue_set_priority_range(&queue, 3, 4, 20, -1);
- check_descending_priority(&queue, 0);
-
- assert(queue_position_to_order(&queue, 3) == 1);
- assert(queue_position_to_order(&queue, 15) == 0);
-
- for (unsigned i = 0; i < 3; ++i) {
- assert(queue_position_to_order(&queue, i) >= 5);
- }
-
- for (unsigned i = 4; i < 8; ++i) {
- assert(queue_position_to_order(&queue, i) >= 2 &&
- queue_position_to_order(&queue, i) < 6);
- }
-
- for (unsigned i = 8; i < 15; ++i) {
- assert(queue_position_to_order(&queue, i) >= 6);
- }
-
- /* priority=20 for another one of the 4 priority=10 items;
- pass "after_order" (with priority=10) and see if it's moved
- after that one */
-
- unsigned current_order = 4;
- unsigned current_position =
- queue_order_to_position(&queue, current_order);
-
- unsigned a_order = 3;
- unsigned a_position = queue_order_to_position(&queue, a_order);
- assert(queue.items[a_position].priority == 10);
- queue_set_priority(&queue, a_position, 20, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 4);
-
- check_descending_priority(&queue, current_order + 1);
-
- /* priority=70 for one of the last items; must be inserted
- right after the current song, before the priority=20 one we
- just created */
-
- unsigned b_order = 10;
- unsigned b_position = queue_order_to_position(&queue, b_order);
- assert(queue.items[b_position].priority == 0);
- queue_set_priority(&queue, b_position, 70, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- b_order = queue_position_to_order(&queue, b_position);
- assert(b_order == 4);
-
- check_descending_priority(&queue, current_order + 1);
-
- /* priority=60 for the old prio50 item; must not be moved,
- because it's before the current song, and it's status
- hasn't changed (it was already higher before) */
-
- unsigned c_order = 0;
- unsigned c_position = queue_order_to_position(&queue, c_order);
- assert(queue.items[c_position].priority == 50);
- queue_set_priority(&queue, c_position, 60, current_order);
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- c_order = queue_position_to_order(&queue, c_position);
- assert(c_order == 0);
-
- /* move the prio=20 item back */
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 5);
- assert(queue.items[a_position].priority == 20);
- queue_set_priority(&queue, a_position, 5, current_order);
-
-
- current_order = queue_position_to_order(&queue, current_position);
- assert(current_order == 3);
-
- a_order = queue_position_to_order(&queue, a_position);
- assert(a_order == 6);
-}
diff --git a/test/test_queue_priority.cxx b/test/test_queue_priority.cxx
new file mode 100644
index 00000000..8dd1c3f5
--- /dev/null
+++ b/test/test_queue_priority.cxx
@@ -0,0 +1,188 @@
+#include "config.h"
+#include "Queue.hxx"
+#include "song.h"
+#include "Directory.hxx"
+
+#include <glib.h>
+
+Directory detached_root;
+
+Directory::Directory() {}
+Directory::~Directory() {}
+
+struct song *
+song_dup_detached(const struct song *src)
+{
+ return const_cast<song *>(src);
+}
+
+void
+song_free(gcc_unused struct song *song)
+{
+}
+
+gcc_unused
+static void
+dump_order(const struct queue *queue)
+{
+ g_printerr("queue length=%u, order:\n", queue->GetLength());
+ for (unsigned i = 0; i < queue->GetLength(); ++i)
+ g_printerr(" [%u] -> %u (prio=%u)\n", i, queue->order[i],
+ queue->items[queue->order[i]].priority);
+}
+
+static void
+check_descending_priority(const struct queue *queue,
+ unsigned start_order)
+{
+ assert(start_order < queue->GetLength());
+
+ uint8_t last_priority = 0xff;
+ for (unsigned order = start_order; order < queue->GetLength(); ++order) {
+ unsigned position = queue->OrderToPosition(order);
+ uint8_t priority = queue->items[position].priority;
+ assert(priority <= last_priority);
+ (void)last_priority;
+ last_priority = priority;
+ }
+}
+
+int
+main(gcc_unused int argc, gcc_unused char **argv)
+{
+ static struct song songs[16];
+
+ struct queue queue(32);
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
+ queue.Append(&songs[i], 0);
+
+ assert(queue.GetLength() == G_N_ELEMENTS(songs));
+
+ /* priority=10 for 4 items */
+
+ queue.SetPriorityRange(4, 8, 10, -1);
+
+ queue.random = true;
+ queue.ShuffleOrder();
+ check_descending_priority(&queue, 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) < 4);
+ }
+
+ for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ /* priority=50 one more item */
+
+ queue.SetPriorityRange(15, 16, 50, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue.PositionToOrder(15) == 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue.PositionToOrder(i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) >= 1 &&
+ queue.PositionToOrder(i) < 5);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue.PositionToOrder(i) >= 5);
+ }
+
+ /* priority=20 for one of the 4 priority=10 items */
+
+ queue.SetPriorityRange(3, 4, 20, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue.PositionToOrder(3) == 1);
+ assert(queue.PositionToOrder(15) == 0);
+
+ for (unsigned i = 0; i < 3; ++i) {
+ assert(queue.PositionToOrder(i) >= 5);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue.PositionToOrder(i) >= 2 &&
+ queue.PositionToOrder(i) < 6);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue.PositionToOrder(i) >= 6);
+ }
+
+ /* priority=20 for another one of the 4 priority=10 items;
+ pass "after_order" (with priority=10) and see if it's moved
+ after that one */
+
+ unsigned current_order = 4;
+ unsigned current_position =
+ queue.OrderToPosition(current_order);
+
+ unsigned a_order = 3;
+ unsigned a_position = queue.OrderToPosition(a_order);
+ assert(queue.items[a_position].priority == 10);
+ queue.SetPriority(a_position, 20, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=70 for one of the last items; must be inserted
+ right after the current song, before the priority=20 one we
+ just created */
+
+ unsigned b_order = 10;
+ unsigned b_position = queue.OrderToPosition(b_order);
+ assert(queue.items[b_position].priority == 0);
+ queue.SetPriority(b_position, 70, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ b_order = queue.PositionToOrder(b_position);
+ assert(b_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=60 for the old prio50 item; must not be moved,
+ because it's before the current song, and it's status
+ hasn't changed (it was already higher before) */
+
+ unsigned c_order = 0;
+ unsigned c_position = queue.OrderToPosition(c_order);
+ assert(queue.items[c_position].priority == 50);
+ queue.SetPriority(c_position, 60, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ c_order = queue.PositionToOrder(c_position);
+ assert(c_order == 0);
+
+ /* move the prio=20 item back */
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 5);
+ assert(queue.items[a_position].priority == 20);
+ queue.SetPriority(a_position, 5, current_order);
+
+ current_order = queue.PositionToOrder(current_position);
+ assert(current_order == 3);
+
+ a_order = queue.PositionToOrder(a_position);
+ assert(a_order == 6);
+}
diff --git a/test/test_vorbis_encoder.c b/test/test_vorbis_encoder.cxx
index 61939915..0e502b15 100644
--- a/test/test_vorbis_encoder.c
+++ b/test/test_vorbis_encoder.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -53,10 +53,10 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
const struct encoder_plugin *plugin = encoder_plugin_get("vorbis");
assert(plugin != NULL);
- struct config_param *param = config_new_param(NULL, -1);
- config_add_block_param(param, "quality", "5.0", -1);
+ config_param param;
+ param.AddBlockParam("quality", "5.0", -1);
- struct encoder *encoder = encoder_init(plugin, param, NULL);
+ struct encoder *encoder = encoder_init(plugin, &param, NULL);
assert(encoder != NULL);
/* open the encoder */
diff --git a/test/visit_archive.cxx b/test/visit_archive.cxx
new file mode 100644
index 00000000..6faf4f3a
--- /dev/null
+++ b/test/visit_archive.cxx
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "stdbin.h"
+#include "tag.h"
+#include "conf.h"
+#include "IOThread.hxx"
+#include "InputInit.hxx"
+#include "ArchiveList.hxx"
+#include "ArchivePlugin.hxx"
+#include "ArchiveFile.hxx"
+#include "ArchiveVisitor.hxx"
+#include "fs/Path.hxx"
+
+#include <glib.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+static void
+my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
+ const gchar *message, G_GNUC_UNUSED gpointer user_data)
+{
+ if (log_domain != NULL)
+ g_printerr("%s: %s\n", log_domain, message);
+ else
+ g_printerr("%s\n", message);
+}
+
+class MyArchiveVisitor final : public ArchiveVisitor {
+ public:
+ virtual void VisitArchiveEntry(const char *path_utf8) override {
+ printf("%s\n", path_utf8);
+ }
+};
+
+int
+main(int argc, char **argv)
+{
+ GError *error = nullptr;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: visit_archive PLUGIN PATH\n");
+ return EXIT_FAILURE;
+ }
+
+ const char *plugin_name = argv[1];
+ const Path path = Path::FromFS(argv[2]);
+
+ /* initialize GLib */
+
+ g_thread_init(NULL);
+ g_log_set_default_handler(my_log_func, NULL);
+
+ /* initialize MPD */
+
+ config_global_init();
+
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ archive_plugin_init_all();
+
+ if (!input_stream_global_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return 2;
+ }
+
+ /* open the archive and dump it */
+
+ const archive_plugin *plugin = archive_plugin_from_name(plugin_name);
+ if (plugin == nullptr) {
+ fprintf(stderr, "No such plugin: %s\n", plugin_name);
+ return EXIT_FAILURE;
+ }
+
+ int result = EXIT_SUCCESS;
+
+ ArchiveFile *file = archive_file_open(plugin, path.c_str(), &error);
+ if (file != nullptr) {
+ MyArchiveVisitor visitor;
+ file->Visit(visitor);
+ file->Close();
+ } else {
+ fprintf(stderr, "%s\n", error->message);
+ g_error_free(error);
+ result = EXIT_FAILURE;
+ }
+
+ /* deinitialize everything */
+
+ input_stream_global_finish();
+
+ archive_plugin_deinit_all();
+
+ io_thread_deinit();
+
+ config_global_finish();
+
+ return result;
+}
diff --git a/valgrind.suppressions b/valgrind.suppressions
index 2cce3418..56bb8e62 100644
--- a/valgrind.suppressions
+++ b/valgrind.suppressions
@@ -10,6 +10,15 @@
...
fun:g_random_int
}
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ fun:g_mutex_impl_new
+ fun:g_mutex_get_impl
+ fun:g_mutex_lock
+ fun:g_main_context_new
+}
{
g_main_context_dispatch
@@ -98,7 +107,7 @@
}
{
- g_static_private_set
+ <insert_a_suppression_name_here>
Memcheck:Leak
fun:*alloc
...
@@ -106,6 +115,14 @@
}
{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_intern_string
+}
+
+{
g_get_language_names
Memcheck:Leak
fun:*alloc
@@ -309,6 +326,29 @@
}
{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:malloc
+ fun:strdup
+ ...
+ fun:ao_initialize
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:calloc
+ fun:ao_initialize
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Addr4
+ ...
+ fun:WildMidi_Init
+}
+
+{
g_quark_from_string
Memcheck:Leak
fun:*alloc