diff options
Diffstat (limited to 'src/output/RoarOutputPlugin.cxx')
-rw-r--r-- | src/output/RoarOutputPlugin.cxx | 428 |
1 files changed, 0 insertions, 428 deletions
diff --git a/src/output/RoarOutputPlugin.cxx b/src/output/RoarOutputPlugin.cxx deleted file mode 100644 index 9634379c5..000000000 --- a/src/output/RoarOutputPlugin.cxx +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2003-2014 The Music Player Daemon Project - * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft - * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "RoarOutputPlugin.hxx" -#include "OutputAPI.hxx" -#include "MixerList.hxx" -#include "thread/Mutex.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include <string> - -/* libroar/services.h declares roar_service_stream::new - work around - this C++ problem */ -#define new _new -#include <roaraudio.h> -#undef new - -class RoarOutput { - struct audio_output base; - - std::string host, name; - - roar_vs_t * vss; - int err; - int role; - struct roar_connection con; - struct roar_audio_info info; - mutable Mutex mutex; - volatile bool alive; - -public: - RoarOutput() - :err(ROAR_ERROR_NONE) {} - - operator audio_output *() { - return &base; - } - - bool Initialize(const config_param ¶m, Error &error) { - return ao_base_init(&base, &roar_output_plugin, param, - error); - } - - void Deinitialize() { - ao_base_finish(&base); - } - - void Configure(const config_param ¶m); - - bool Open(AudioFormat &audio_format, Error &error); - void Close(); - - void SendTag(const Tag &tag); - size_t Play(const void *chunk, size_t size, Error &error); - void Cancel(); - - int GetVolume() const; - bool SetVolume(unsigned volume); -}; - -static constexpr Domain roar_output_domain("roar_output"); - -inline int -RoarOutput::GetVolume() const -{ - const ScopeLock protect(mutex); - - if (vss == nullptr || !alive) - return -1; - - float l, r; - int error; - if (roar_vs_volume_get(vss, &l, &r, &error) < 0) - return -1; - - return (l + r) * 50; -} - -int -roar_output_get_volume(RoarOutput *roar) -{ - return roar->GetVolume(); -} - -bool -RoarOutput::SetVolume(unsigned volume) -{ - assert(volume <= 100); - - const ScopeLock protect(mutex); - if (vss == nullptr || !alive) - return false; - - int error; - float level = volume / 100.0; - - roar_vs_volume_mono(vss, level, &error); - return true; -} - -bool -roar_output_set_volume(RoarOutput *roar, unsigned volume) -{ - return roar->SetVolume(volume); -} - -inline void -RoarOutput::Configure(const config_param ¶m) -{ - host = param.GetBlockValue("server", ""); - name = param.GetBlockValue("name", "MPD"); - - const char *_role = param.GetBlockValue("role", "music"); - role = _role != nullptr - ? roar_str2role(_role) - : ROAR_ROLE_MUSIC; -} - -static struct audio_output * -roar_init(const config_param ¶m, Error &error) -{ - RoarOutput *self = new RoarOutput(); - - if (!self->Initialize(param, error)) { - delete self; - return nullptr; - } - - self->Configure(param); - return *self; -} - -static void -roar_finish(struct audio_output *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - - self->Deinitialize(); - delete self; -} - -static void -roar_use_audio_format(struct roar_audio_info *info, - AudioFormat &audio_format) -{ - info->rate = audio_format.sample_rate; - info->channels = audio_format.channels; - info->codec = ROAR_CODEC_PCM_S; - - switch (audio_format.format) { - case SampleFormat::UNDEFINED: - case SampleFormat::FLOAT: - case SampleFormat::DSD: - info->bits = 16; - audio_format.format = SampleFormat::S16; - break; - - case SampleFormat::S8: - info->bits = 8; - break; - - case SampleFormat::S16: - info->bits = 16; - break; - - case SampleFormat::S24_P32: - info->bits = 32; - audio_format.format = SampleFormat::S32; - break; - - case SampleFormat::S32: - info->bits = 32; - break; - } -} - -inline bool -RoarOutput::Open(AudioFormat &audio_format, Error &error) -{ - const ScopeLock protect(mutex); - - if (roar_simple_connect(&con, - host.empty() ? nullptr : host.c_str(), - name.c_str()) < 0) { - error.Set(roar_output_domain, - "Failed to connect to Roar server"); - return false; - } - - vss = roar_vs_new_from_con(&con, &err); - - if (vss == nullptr || err != ROAR_ERROR_NONE) { - error.Set(roar_output_domain, "Failed to connect to server"); - return false; - } - - roar_use_audio_format(&info, audio_format); - - if (roar_vs_stream(vss, &info, ROAR_DIR_PLAY, &err) < 0) { - error.Set(roar_output_domain, "Failed to start stream"); - return false; - } - - roar_vs_role(vss, role, &err); - alive = true; - return true; -} - -static bool -roar_open(struct audio_output *ao, AudioFormat &audio_format, Error &error) -{ - RoarOutput *self = (RoarOutput *)ao; - - return self->Open(audio_format, error); -} - -inline void -RoarOutput::Close() -{ - const ScopeLock protect(mutex); - - alive = false; - - if (vss != nullptr) - roar_vs_close(vss, ROAR_VS_TRUE, &err); - vss = nullptr; - roar_disconnect(&con); -} - -static void -roar_close(struct audio_output *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - self->Close(); -} - -inline void -RoarOutput::Cancel() -{ - const ScopeLock protect(mutex); - - if (vss == nullptr) - return; - - roar_vs_t *_vss = vss; - vss = nullptr; - roar_vs_close(_vss, ROAR_VS_TRUE, &err); - alive = false; - - _vss = roar_vs_new_from_con(&con, &err); - if (_vss == nullptr) - return; - - if (roar_vs_stream(_vss, &info, ROAR_DIR_PLAY, &err) < 0) { - roar_vs_close(_vss, ROAR_VS_TRUE, &err); - LogError(roar_output_domain, "Failed to start stream"); - return; - } - - roar_vs_role(_vss, role, &err); - vss = _vss; - alive = true; -} - -static void -roar_cancel(struct audio_output *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - - self->Cancel(); -} - -inline size_t -RoarOutput::Play(const void *chunk, size_t size, Error &error) -{ - if (vss == nullptr) { - error.Set(roar_output_domain, "Connection is invalid"); - return 0; - } - - ssize_t nbytes = roar_vs_write(vss, chunk, size, &err); - if (nbytes <= 0) { - error.Set(roar_output_domain, "Failed to play data"); - return 0; - } - - return nbytes; -} - -static size_t -roar_play(struct audio_output *ao, const void *chunk, size_t size, - Error &error) -{ - RoarOutput *self = (RoarOutput *)ao; - return self->Play(chunk, size, error); -} - -static const char* -roar_tag_convert(TagType type, bool *is_uuid) -{ - *is_uuid = false; - switch (type) - { - case TAG_ARTIST: - case TAG_ALBUM_ARTIST: - return "AUTHOR"; - case TAG_ALBUM: - return "ALBUM"; - case TAG_TITLE: - return "TITLE"; - case TAG_TRACK: - return "TRACK"; - case TAG_NAME: - return "NAME"; - case TAG_GENRE: - return "GENRE"; - case TAG_DATE: - return "DATE"; - case TAG_PERFORMER: - return "PERFORMER"; - case TAG_COMMENT: - return "COMMENT"; - case TAG_DISC: - return "DISCID"; - case TAG_COMPOSER: -#ifdef ROAR_META_TYPE_COMPOSER - return "COMPOSER"; -#else - return "AUTHOR"; -#endif - case TAG_MUSICBRAINZ_ARTISTID: - case TAG_MUSICBRAINZ_ALBUMID: - case TAG_MUSICBRAINZ_ALBUMARTISTID: - case TAG_MUSICBRAINZ_TRACKID: - *is_uuid = true; - return "HASH"; - - default: - return nullptr; - } -} - -inline void -RoarOutput::SendTag(const Tag &tag) -{ - if (vss == nullptr) - return; - - const ScopeLock protect(mutex); - - size_t cnt = 1; - struct roar_keyval vals[32]; - char uuid_buf[32][64]; - - char timebuf[16]; - snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", - tag.time / 3600, (tag.time % 3600) / 60, tag.time % 60); - - vals[0].key = const_cast<char *>("LENGTH"); - vals[0].value = timebuf; - - for (unsigned i = 0; i < tag.num_items && cnt < 32; i++) - { - bool is_uuid = false; - const char *key = roar_tag_convert(tag.items[i]->type, - &is_uuid); - if (key != nullptr) { - vals[cnt].key = const_cast<char *>(key); - - if (is_uuid) { - snprintf(uuid_buf[cnt], sizeof(uuid_buf[0]), "{UUID}%s", - tag.items[i]->value); - vals[cnt].value = uuid_buf[cnt]; - } else { - vals[cnt].value = tag.items[i]->value; - } - - cnt++; - } - } - - roar_vs_meta(vss, vals, cnt, &(err)); -} - -static void -roar_send_tag(struct audio_output *ao, const Tag *meta) -{ - RoarOutput *self = (RoarOutput *)ao; - self->SendTag(*meta); -} - -const struct audio_output_plugin roar_output_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, -}; |