Added the ability for the user to select their preferred output plugin!

This commit is contained in:
casey langen 2016-12-05 20:16:53 -08:00
parent 3e6f66d986
commit ae88f6ee31
34 changed files with 537 additions and 118 deletions

View File

@ -49,6 +49,12 @@ class AlsaOut : public musik::core::sdk::IOutput {
AlsaOut();
virtual ~AlsaOut();
/* IPlugin */
virtual const char* Name() { return "AlsaOut IOutput"; }
virtual const char* Version() { return "0.3"; }
virtual const char* Author() { return "Julian Cromarty, clangen"; }
/* IOutput */
virtual void Destroy();
virtual void Pause();
virtual void Resume();

View File

@ -37,15 +37,8 @@
#include <core/sdk/IOutput.h>
#include "AlsaOut.h"
class AlsaOutPlugin : public musik::core::sdk::IPlugin {
virtual void Destroy() { delete this; };
virtual const char* Name() { return "AlsaOut IOutput plugin"; }
virtual const char* Version() { return "0.3"; }
virtual const char* Author() { return "Julian Cromarty, clangen"; }
};
extern "C" musik::core::sdk::IPlugin* GetPlugin() {
return new AlsaOutPlugin();
return new AlsaOut();
}
extern "C" musik::core::sdk::IOutput* GetAudioOutput() {

View File

@ -54,6 +54,13 @@ class CoreAudioOut : public musik::core::sdk::IOutput {
CoreAudioOut();
virtual ~CoreAudioOut();
/* IPlugin */
void Destroy() { delete this; };
const char* Name() { return "CoreAudio IOutput"; };
const char* Version() { return "0.2"; };
const char* Author() { return "clangen"; };
/* IOutput */
virtual void Destroy();
virtual void Pause();
virtual void Resume();

View File

@ -42,21 +42,8 @@
#define DLLEXPORT
#endif
#ifdef WIN32
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return true;
}
#endif
class CoreAudioOutPlugin : public musik::core::sdk::IPlugin {
void Destroy() { delete this; };
const char* Name() { return "CoreAudio IOutput"; };
const char* Version() { return "0.2"; };
const char* Author() { return "clangen"; };
};
extern "C" DLLEXPORT musik::core::sdk::IPlugin* GetPlugin() {
return new CoreAudioOutPlugin;
return new CoreAudioOut();
}
extern "C" DLLEXPORT musik::core::sdk::IOutput* GetAudioOutput() {

View File

@ -44,6 +44,13 @@ class PulseOut : public musik::core::sdk::IOutput {
PulseOut();
virtual ~PulseOut();
/* IPlugin */
virtual void Destroy() { delete this; };
virtual const char* Name() { return "PulseAudio IOutput plugin"; }
virtual const char* Version() { return "0.1"; }
virtual const char* Author() { return "clangen"; }
/* IOutput */
virtual void Destroy();
virtual void Pause();
virtual void Resume();

View File

@ -37,15 +37,8 @@
#include <core/sdk/IOutput.h>
#include "PulseOut.h"
class PulseOutPlugin : public musik::core::sdk::IPlugin {
virtual void Destroy() { delete this; };
virtual const char* Name() { return "PulseAudio IOutput plugin"; }
virtual const char* Version() { return "0.1"; }
virtual const char* Author() { return "clangen"; }
};
extern "C" musik::core::sdk::IPlugin* GetPlugin() {
return new PulseOutPlugin();
return new PulseOut();
}
extern "C" musik::core::sdk::IOutput* GetAudioOutput() {

View File

@ -61,7 +61,8 @@ WasapiOut::WasapiOut()
, audioClock(nullptr)
, outputBufferFrames(0)
, state(StateStopped)
, latency(0) {
, latency(0)
, volume(1.0f) {
ZeroMemory(&waveFormat, sizeof(WAVEFORMATEXTENSIBLE));
}
@ -96,6 +97,7 @@ void WasapiOut::Resume() {
void WasapiOut::SetVolume(double volume) {
Lock lock(this->stateMutex);
this->volume = volume;
if (this->simpleAudioVolume) {
simpleAudioVolume->SetMasterVolume((float) volume, 0);
simpleAudioVolume->SetMute(false, 0);
@ -309,6 +311,7 @@ bool WasapiOut::Configure(IBuffer *buffer) {
}
this->state = StatePlaying;
this->SetVolume(this->volume);
return true;
}

View File

@ -52,7 +52,13 @@ class WasapiOut : public IOutput {
WasapiOut();
~WasapiOut();
/* IPlugin */
const char* Name() { return "Wasapi IOutput"; };
const char* Version() { return "0.1"; };
const char* Author() { return "clangen"; };
virtual void Destroy();
/* IOutput */
virtual void Pause();
virtual void Resume();
virtual void SetVolume(double volume);
@ -81,6 +87,7 @@ class WasapiOut : public IOutput {
UINT32 outputBufferFrames;
std::atomic<State> state;
WAVEFORMATEXTENSIBLE waveFormat;
double volume;
std::recursive_mutex stateMutex;
INT64 latency;

View File

@ -41,15 +41,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
return true;
}
class WasapiOutPlugin : public musik::core::sdk::IPlugin {
void Destroy() { delete this; };
const char* Name() { return "WASAPI IOutput"; };
const char* Version() { return "0.1"; };
const char* Author() { return "clangen"; };
};
extern "C" __declspec(dllexport) musik::core::sdk::IPlugin* GetPlugin() {
return new WasapiOutPlugin();
return new WasapiOut();
}
extern "C" __declspec(dllexport) musik::core::sdk::IOutput* GetAudioOutput() {

View File

@ -49,7 +49,13 @@ class WaveOut : public IOutput {
WaveOut();
~WaveOut();
/* IPlugin */
const char* Name() { return "WaveOut IOutput"; };
const char* Version() { return "0.4"; };
const char* Author() { return "Bj\xC3\xB6rn Olievier, clangen"; };
virtual void Destroy();
/* IOutput */
virtual void Pause();
virtual void Resume();
virtual void SetVolume(double volume);

View File

@ -41,15 +41,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
return true;
}
class WaveOutPlugin : public musik::core::sdk::IPlugin {
void Destroy() { delete this; };
const char* Name() { return "WaveOut IOutput"; };
const char* Version() { return "0.3"; };
const char* Author() { return "Björn Olievier, clangen"; };
};
extern "C" __declspec(dllexport) musik::core::sdk::IPlugin* GetPlugin() {
return new WaveOutPlugin();
return new WaveOut();
}
extern "C" __declspec(dllexport) musik::core::sdk::IOutput* GetAudioOutput() {

View File

@ -2,6 +2,7 @@ set(CORE_SOURCES
./debug.cpp
./audio/Buffer.cpp
./audio/GaplessTransport.cpp
./audio/Outputs.cpp
./audio/Player.cpp
./audio/Stream.cpp
./audio/Streams.cpp

View File

@ -37,6 +37,7 @@
#include <core/debug.h>
#include <core/audio/GaplessTransport.h>
#include <core/plugin/PluginFactory.h>
#include <core/audio/Outputs.h>
#include <algorithm>
#include <boost/thread.hpp>
@ -57,7 +58,7 @@ GaplessTransport::GaplessTransport()
, nextPlayer(nullptr)
, nextCanStart(false)
, muted(false) {
this->output = Player::CreateDefaultOutput();
this->output = outputs::SelectedOutput();
}
GaplessTransport::~GaplessTransport() {
@ -135,6 +136,12 @@ void GaplessTransport::StartWithPlayer(Player* newPlayer) {
}
}
void GaplessTransport::ReloadOutput() {
this->Stop();
this->output = outputs::SelectedOutput();
this->output->SetVolume(volume);
}
void GaplessTransport::Stop() {
this->StopInternal(false, true);
}

View File

@ -68,6 +68,8 @@ namespace musik { namespace core { namespace audio {
virtual bool IsMuted();
virtual void SetMuted(bool muted);
virtual void ReloadOutput();
virtual musik::core::sdk::PlaybackState GetPlaybackState();
private:
@ -95,7 +97,7 @@ namespace musik { namespace core { namespace audio {
musik::core::sdk::PlaybackState state;
std::recursive_mutex stateMutex;
musik::core::audio::OutputPtr output;
std::shared_ptr<musik::core::sdk::IOutput> output;
PlayerList active;
Player* nextPlayer;
double volume;

View File

@ -65,6 +65,8 @@ namespace musik { namespace core { namespace audio {
virtual bool IsMuted() = 0;
virtual void SetMuted(bool muted) = 0;
virtual void ReloadOutput() = 0;
virtual musik::core::sdk::PlaybackState GetPlaybackState() = 0;
};

134
src/core/audio/Outputs.cpp Normal file
View File

@ -0,0 +1,134 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 COPYRIGHT OWNER 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.
//
//////////////////////////////////////////////////////////////////////////////
#include "pch.hpp"
#include "Outputs.h"
#include <core/plugin/PluginFactory.h>
#include <core/support/Preferences.h>
#include <core/support/PreferenceKeys.h>
#include <atomic>
#include <algorithm>
using namespace musik::core;
using namespace musik::core::audio;
using namespace musik::core::sdk;
using namespace musik::core::prefs;
using Output = std::shared_ptr<IOutput>;
using OutputList = std::vector<Output>;
#if defined(WIN32)
static const std::string defaultOutput = "Wasapi IOutput";
#elif defined(__APPLE__)
static const std::string defaultOutput = "CoreAudio IOutput";
#else
static const std::string defaultOutput = "Alsa IOutput";
#endif
#define LOWER(x) std::transform(x.begin(), x.end(), x.begin(), tolower);
namespace musik {
namespace core {
namespace audio {
namespace outputs {
Output FindByName(const std::string& name, const OutputList& list) {
if (name.size()) {
auto it = list.begin();
while (it != list.end()) {
if ((*it)->Name() == name) {
return (*it);
}
++it;
}
}
return Output();
}
OutputList GetAllOutputs() {
using OutputDeleter = PluginFactory::DestroyDeleter<IOutput>;
OutputList result = PluginFactory::Instance()
.QueryInterface<IOutput, OutputDeleter>("GetAudioOutput");
std::sort(
result.begin(),
result.end(),
[](Output left, Output right) -> bool {
std::string l = left->Name();
LOWER(l);
std::string r = right->Name();
LOWER(r);
return l < r;
});
return result;
}
void SelectOutput(std::shared_ptr<musik::core::sdk::IOutput> output) {
std::shared_ptr<Preferences> prefs =
Preferences::ForComponent(components::Playback);
prefs->SetString(keys::OutputPlugin, output->Name());
}
Output SelectedOutput() {
std::shared_ptr<Preferences> prefs =
Preferences::ForComponent(components::Playback);
const OutputList plugins = GetAllOutputs();
if (plugins.size()) {
/* try to find the user selected plugin first */
Output result = FindByName(prefs->GetString(keys::OutputPlugin), plugins);
if (!result) {
/* fall back to the default */
result = FindByName(defaultOutput, plugins);
}
if (!result) {
/* no default? ugh, return the first one. */
result = plugins[0];
}
return result;
}
return Output();
}
}
}
}
}

46
src/core/audio/Outputs.h Normal file
View File

@ -0,0 +1,46 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 COPYRIGHT OWNER 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.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <core/config.h>
#include <core/sdk/IOutput.h>
namespace musik { namespace core { namespace audio { namespace outputs {
std::vector<std::shared_ptr<musik::core::sdk::IOutput> > GetAllOutputs();
void SelectOutput(std::shared_ptr<musik::core::sdk::IOutput> output);
std::shared_ptr<musik::core::sdk::IOutput> SelectedOutput();
} } } }

View File

@ -103,26 +103,11 @@ namespace musik {
}
}
Player* Player::Create(const std::string &url, OutputPtr output, PlayerEventListener *listener) {
Player* Player::Create(const std::string &url, std::shared_ptr<IOutput> output, PlayerEventListener *listener) {
return new Player(url, output, listener);
}
OutputPtr Player::CreateDefaultOutput() {
/* if no output is specified, find all output plugins, and select the first one. */
typedef std::vector<OutputPtr> OutputVector;
OutputVector outputs = musik::core::PluginFactory::Instance().QueryInterface<
IOutput, musik::core::PluginFactory::DestroyDeleter<IOutput> >("GetAudioOutput");
if (!outputs.empty()) {
musik::debug::info(TAG, "found an IOutput device!");
return outputs.front();
}
return OutputPtr();
}
Player::Player(const std::string &url, OutputPtr output, PlayerEventListener *listener)
Player::Player(const std::string &url, std::shared_ptr<IOutput> output, PlayerEventListener *listener)
: state(Player::Precache)
, url(url)
, currentPosition(0)

View File

@ -47,9 +47,6 @@
namespace musik { namespace core { namespace audio {
class Output;
typedef std::shared_ptr<musik::core::sdk::IOutput> OutputPtr;
struct FftContext;
class Player : public musik::core::sdk::IBufferProvider {
@ -62,11 +59,9 @@ namespace musik { namespace core { namespace audio {
virtual void OnPlaybackError(Player *player) = 0;
};
static OutputPtr CreateDefaultOutput();
static Player* Create(
const std::string &url,
OutputPtr output,
std::shared_ptr<musik::core::sdk::IOutput> output,
PlayerEventListener *listener);
virtual void OnBufferProcessed(musik::core::sdk::IBuffer *buffer);
@ -91,7 +86,7 @@ namespace musik { namespace core { namespace audio {
Player(
const std::string &url,
OutputPtr output,
std::shared_ptr<musik::core::sdk::IOutput> output,
PlayerEventListener *listener);
virtual ~Player();
@ -106,7 +101,7 @@ namespace musik { namespace core { namespace audio {
Quit = 2
} States;
OutputPtr output;
std::shared_ptr<musik::core::sdk::IOutput> output;
StreamPtr stream;
ThreadPtr thread;
BufferList lockedBuffers;

View File

@ -85,6 +85,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="audio\GaplessTransport.cpp" />
<ClCompile Include="audio\Outputs.cpp" />
<ClCompile Include="audio\Streams.cpp" />
<ClCompile Include="audio\Visualizer.cpp" />
<ClCompile Include="debug.cpp" />
@ -118,6 +119,7 @@
<ClInclude Include="audio\GaplessTransport.h" />
<ClInclude Include="audio\IStream.h" />
<ClInclude Include="audio\ITransport.h" />
<ClInclude Include="audio\Outputs.h" />
<ClInclude Include="audio\Streams.h" />
<ClInclude Include="audio\Visualizer.h" />
<ClInclude Include="debug.h" />

View File

@ -115,6 +115,9 @@
<ClCompile Include="audio\Streams.cpp">
<Filter>src\audio</Filter>
</ClCompile>
<ClCompile Include="audio\Outputs.cpp">
<Filter>src\audio</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.hpp">
@ -282,5 +285,8 @@
<ClInclude Include="audio\Streams.h">
<Filter>src\audio</Filter>
</ClInclude>
<ClInclude Include="audio\Outputs.h">
<Filter>src\audio</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -35,15 +35,15 @@
#pragma once
#include "config.h"
#include "IPlugin.h"
#include "IDataStream.h"
#include "IBuffer.h"
#include "IBufferProvider.h"
namespace musik { namespace core { namespace sdk {
class IOutput {
class IOutput : public IPlugin {
public:
virtual void Destroy() = 0;
virtual void Pause() = 0;
virtual void Resume() = 0;
virtual void SetVolume(double volume) = 0;

View File

@ -47,6 +47,7 @@ namespace musik { namespace core { namespace prefs {
const std::string keys::SyncOnStartup = "SyncOnStartup";
const std::string keys::Volume = "Volume";
const std::string keys::RepeatMode = "RepeatMode";
const std::string keys::OutputPlugin = "OutputPlugin";
} } }

View File

@ -51,6 +51,7 @@ namespace musik { namespace core { namespace prefs {
extern const std::string SyncOnStartup;
extern const std::string Volume;
extern const std::string RepeatMode;
extern const std::string OutputPlugin;
}
} } }

View File

@ -16,6 +16,7 @@ set (BOX_SRCS
./app/query/NowPlayingTrackListQuery.cpp
./app/query/SearchTrackListQuery.cpp
./app/service/PlaybackService.cpp
./app/overlay/OutputOverlay.cpp
./app/overlay/VisualizerOverlay.cpp
./app/util/Duration.cpp
./app/util/Hotkeys.cpp

View File

@ -126,7 +126,7 @@ int main(int argc, char* argv[])
Layout libraryLayout(new LibraryLayout(playback, library));
Layout consoleLayout(new ConsoleLayout(transport, library));
Layout settingsLayout(new SettingsLayout(library));
Layout settingsLayout(new SettingsLayout(library, playback.GetTransport()));
Main mainLayout(new MainLayout());
mainLayout->Layout();

View File

@ -43,18 +43,22 @@
#include <core/library/Indexer.h>
#include <core/library/LocalLibraryConstants.h>
#include <core/support/PreferenceKeys.h>
#include <core/audio/Outputs.h>
#include <app/query/SearchTrackListQuery.h>
#include <app/util/Hotkeys.h>
#include <app/util/PreferenceKeys.h>
#include <app/overlay/OutputOverlay.h>
#include <boost/format.hpp>
#include "SettingsLayout.h"
using namespace musik;
using namespace musik::core::library::constants;
using namespace musik::core;
using namespace musik::core::audio;
using namespace musik::core::library::constants;
using namespace musik::core::sdk;
using namespace musik::box;
using namespace cursespp;
using namespace std::placeholders;
@ -75,13 +79,17 @@ using namespace std::placeholders;
using EntryPtr = IScrollAdapter::EntryPtr;
static const std::string arrow = "\xe2\x96\xbc";
static bool showDotfiles = false;
SettingsLayout::SettingsLayout(musik::core::LibraryPtr library)
SettingsLayout::SettingsLayout(
musik::core::LibraryPtr library,
musik::core::audio::ITransport& transport)
: LayoutBase()
, library(library)
, indexer(library->Indexer()) {
this->prefs = Preferences::ForComponent(core::prefs::components::Settings);
, indexer(library->Indexer())
, transport(transport) {
this->libraryPrefs = Preferences::ForComponent(core::prefs::components::Settings);
this->indexer->PathsUpdated.connect(this, &SettingsLayout::RefreshAddedPaths);
this->InitializeWindows();
}
@ -91,8 +99,8 @@ SettingsLayout::~SettingsLayout() {
void SettingsLayout::OnCheckboxChanged(cursespp::Checkbox* cb, bool checked) {
if (cb == removeCheckbox.get()) {
this->prefs->SetBool(core::prefs::keys::RemoveMissingFiles, checked);
this->prefs->Save();
this->libraryPrefs->SetBool(core::prefs::keys::RemoveMissingFiles, checked);
this->libraryPrefs->Save();
}
else if (cb == dotfileCheckbox.get()) {
showDotfiles = !showDotfiles;
@ -100,15 +108,32 @@ void SettingsLayout::OnCheckboxChanged(cursespp::Checkbox* cb, bool checked) {
this->browseList->OnAdapterChanged();
}
else if (cb == focusShortcutsCheckbox.get()) {
this->prefs->SetBool(box::prefs::keys::EscFocusesShortcuts, checked);
this->prefs->Save();
this->libraryPrefs->SetBool(box::prefs::keys::EscFocusesShortcuts, checked);
this->libraryPrefs->Save();
}
else if (cb == customColorsCheckbox.get()) {
this->prefs->SetBool(box::prefs::keys::DisableCustomColors, checked);
this->prefs->Save();
this->libraryPrefs->SetBool(box::prefs::keys::DisableCustomColors, checked);
this->libraryPrefs->Save();
}
}
void SettingsLayout::OnOutputDropdownActivated(cursespp::TextLabel* label) {
std::string currentName;
std::shared_ptr<IOutput> currentPlugin = outputs::SelectedOutput();
currentName = currentPlugin ? currentPlugin->Name() : currentName;
OutputOverlay::Show([this, currentName] {
std::string newName;
std::shared_ptr<IOutput> newPlugin = outputs::SelectedOutput();
newName = newPlugin ? newPlugin->Name() : newName;
if (currentName != newName) {
this->LoadPreferences();
this->transport.ReloadOutput();
}
});
}
void SettingsLayout::Layout() {
int x = this->GetX(), y = this->GetY();
int cx = this->GetWidth(), cy = this->GetHeight();
@ -128,7 +153,9 @@ void SettingsLayout::Layout() {
this->browseList->MoveAndResize(leftX, pathListsY, leftWidth, pathsHeight);
this->addedPathsList->MoveAndResize(rightX, pathListsY, rightWidth, pathsHeight);
this->dotfileCheckbox->MoveAndResize(1, BOTTOM(this->browseList), cx - 1, LABEL_HEIGHT);
this->outputDropdown->MoveAndResize(1, BOTTOM(this->browseList), cx - 1, LABEL_HEIGHT);
this->dotfileCheckbox->MoveAndResize(1, BOTTOM(this->outputDropdown) + 1, cx - 1, LABEL_HEIGHT);
this->removeCheckbox->MoveAndResize(1, BOTTOM(this->dotfileCheckbox), cx - 1, LABEL_HEIGHT);
this->focusShortcutsCheckbox->MoveAndResize(1, BOTTOM(this->removeCheckbox), cx - 1, LABEL_HEIGHT);
this->customColorsCheckbox->MoveAndResize(1, BOTTOM(this->focusShortcutsCheckbox), cx - 1, LABEL_HEIGHT);
@ -202,6 +229,9 @@ void SettingsLayout::InitializeWindows() {
this->addedPathsAdapter.SetItemDecorator(decorator);
this->browseAdapter.SetItemDecorator(decorator);
this->outputDropdown.reset(new TextLabel());
this->outputDropdown->Activated.connect(this, &SettingsLayout::OnOutputDropdownActivated);
CREATE_CHECKBOX(this->dotfileCheckbox, "show dotfiles in directory browser");
CREATE_CHECKBOX(this->removeCheckbox, "remove missing files from library");
CREATE_CHECKBOX(this->focusShortcutsCheckbox, "esc key focuses shortcuts bar");
@ -213,16 +243,18 @@ void SettingsLayout::InitializeWindows() {
this->browseList->SetFocusOrder(0);
this->addedPathsList->SetFocusOrder(1);
this->dotfileCheckbox->SetFocusOrder(2);
this->removeCheckbox->SetFocusOrder(3);
this->focusShortcutsCheckbox->SetFocusOrder(4);
this->customColorsCheckbox->SetFocusOrder(5);
this->hotkeyInput->SetFocusOrder(6);
this->outputDropdown->SetFocusOrder(2);
this->dotfileCheckbox->SetFocusOrder(3);
this->removeCheckbox->SetFocusOrder(4);
this->focusShortcutsCheckbox->SetFocusOrder(5);
this->customColorsCheckbox->SetFocusOrder(6);
this->hotkeyInput->SetFocusOrder(7);
this->AddWindow(this->browseLabel);
this->AddWindow(this->addedPathsLabel);
this->AddWindow(this->browseList);
this->AddWindow(this->addedPathsList);
this->AddWindow(this->outputDropdown);
this->AddWindow(this->dotfileCheckbox);
this->AddWindow(this->removeCheckbox);
this->AddWindow(this->focusShortcutsCheckbox);
@ -252,7 +284,7 @@ void SettingsLayout::OnVisibilityChanged(bool visible) {
}
void SettingsLayout::CheckShowFirstRunDialog() {
if (!this->prefs->GetBool(box::prefs::keys::FirstRunSettingsDisplayed)) {
if (!this->libraryPrefs->GetBool(box::prefs::keys::FirstRunSettingsDisplayed)) {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
@ -271,7 +303,7 @@ void SettingsLayout::CheckShowFirstRunDialog() {
"ENTER",
"ok",
[this](std::string key) {
this->prefs->SetBool(box::prefs::keys::FirstRunSettingsDisplayed, true);
this->libraryPrefs->SetBool(box::prefs::keys::FirstRunSettingsDisplayed, true);
});
App::Overlays().Push(dialog);
@ -279,9 +311,14 @@ void SettingsLayout::CheckShowFirstRunDialog() {
}
void SettingsLayout::LoadPreferences() {
this->removeCheckbox->SetChecked(this->prefs->GetBool(core::prefs::keys::RemoveMissingFiles, true));
this->focusShortcutsCheckbox->SetChecked(this->prefs->GetBool(box::prefs::keys::EscFocusesShortcuts, true));
this->customColorsCheckbox->SetChecked(this->prefs->GetBool(box::prefs::keys::DisableCustomColors));
this->removeCheckbox->SetChecked(this->libraryPrefs->GetBool(core::prefs::keys::RemoveMissingFiles, true));
this->focusShortcutsCheckbox->SetChecked(this->libraryPrefs->GetBool(box::prefs::keys::EscFocusesShortcuts, true));
this->customColorsCheckbox->SetChecked(this->libraryPrefs->GetBool(box::prefs::keys::DisableCustomColors));
std::shared_ptr<IOutput> output = outputs::SelectedOutput();
if (output) {
this->outputDropdown->SetText(arrow + " output device: " + output->Name());
}
}
void SettingsLayout::AddSelectedDirectory() {

View File

@ -64,7 +64,9 @@ namespace musik {
public sigslot::has_slots<>
{
public:
SettingsLayout(musik::core::LibraryPtr library);
SettingsLayout(
musik::core::LibraryPtr library,
musik::core::audio::ITransport& transport);
virtual ~SettingsLayout();
@ -87,6 +89,8 @@ namespace musik {
void OnCheckboxChanged(
cursespp::Checkbox* checkbox, bool checked);
void OnOutputDropdownActivated(cursespp::TextLabel* label);
int64 ListItemDecorator(
cursespp::ScrollableWindow* w,
size_t index,
@ -95,8 +99,12 @@ namespace musik {
musik::core::LibraryPtr library;
musik::core::IIndexer* indexer;
musik::core::audio::ITransport& transport;
std::shared_ptr<musik::core::Preferences> prefs;
std::shared_ptr<musik::core::Preferences> libraryPrefs;
std::shared_ptr<musik::core::Preferences> playbackPrefs;
std::shared_ptr<cursespp::TextLabel> outputDropdown;
std::shared_ptr<cursespp::Checkbox> removeCheckbox;
std::shared_ptr<cursespp::Checkbox> dotfileCheckbox;

View File

@ -0,0 +1,103 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 COPYRIGHT OWNER 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.
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "OutputOverlay.h"
#include <core/audio/Outputs.h>
#include <cursespp/App.h>
#include <cursespp/SimpleScrollAdapter.h>
#include <cursespp/ListOverlay.h>
#include <cursespp/DialogOverlay.h>
#include <vector>
using namespace musik::box;
using namespace musik::core::audio;
using namespace musik::core::sdk;
using namespace cursespp;
static std::vector<std::shared_ptr<IOutput> > plugins;
static void showNoOutputPluginsMessage() {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
.SetTitle("musikbox")
.SetMessage("no output plugins found!")
.AddButton(
"KEY_ENTER",
"ENTER",
"ok");
App::Overlays().Push(dialog);
}
OutputOverlay::OutputOverlay() {
}
void OutputOverlay::Show(std::function<void()> callback) {
plugins = outputs::GetAllOutputs();
if (!plugins.size()) {
showNoOutputPluginsMessage();
return;
}
using Adapter = cursespp::SimpleScrollAdapter;
using ListOverlay = cursespp::ListOverlay;
std::shared_ptr<Adapter> adapter(new Adapter());
for (size_t i = 0; i < plugins.size(); i++) {
adapter->AddEntry(plugins[i]->Name());
}
adapter->SetSelectable(true);
std::shared_ptr<ListOverlay> dialog(new ListOverlay());
dialog->SetAdapter(adapter)
.SetTitle("output plugins")
.SetItemSelectedCallback(
[callback](cursespp::IScrollAdapterPtr adapter, size_t index) {
outputs::SelectOutput(plugins[index]);
if (callback) {
callback();
}
});
cursespp::App::Overlays().Push(dialog);
}

View File

@ -0,0 +1,49 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// 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 COPYRIGHT OWNER 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.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <functional>
namespace musik {
namespace box {
class OutputOverlay {
public:
static void Show(std::function<void()> callback);
private:
OutputOverlay();
};
}
}

View File

@ -44,21 +44,6 @@
using namespace cursespp;
inline static void redrawContents(
IWindow &window,
const text::TextAlign alignment,
const std::string& text)
{
std::string aligned = text::Align(
text, alignment, window.GetContentWidth());
WINDOW* c = window.GetContent();
werase(c);
wprintw(c, aligned.c_str());
window.Repaint();
}
TextLabel::TextLabel()
: Window()
, alignment(text::AlignLeft) {
@ -68,15 +53,54 @@ TextLabel::TextLabel()
TextLabel::~TextLabel() {
}
void TextLabel::Redraw()
{
std::string aligned = text::Align(
this->buffer, alignment, this->GetContentWidth());
WINDOW* c = this->GetContent();
werase(c);
int64 attrs = this->IsFocused() ? CURSESPP_TEXT_FOCUSED : -1LL;
if (attrs != -1) {
wattron(c, COLOR_PAIR(attrs));
}
wprintw(c, aligned.c_str());
if (attrs != -1) {
wattroff(c, COLOR_PAIR(attrs));
}
this->Repaint();
}
void TextLabel::Show() {
Window::Show();
redrawContents(*this, this->alignment, this->buffer);
this->Redraw();
}
void TextLabel::OnFocusChanged(bool focused) {
this->Redraw();
}
void TextLabel::SetText(const std::string& value, const text::TextAlign alignment) {
if (value != this->buffer || alignment != this->alignment) {
this->buffer = value;
this->alignment = alignment;
redrawContents(*this, alignment, buffer);
this->Redraw();
}
}
bool TextLabel::KeyPress(const std::string& key) {
if (this->IsFocused()) {
if (key == " " || key == "KEY_ENTER") {
this->Activated(this);
return true;
}
}
return false;
}

View File

@ -37,6 +37,7 @@
#include <cursespp/curses_config.h>
#include <cursespp/Window.h>
#include <cursespp/IInput.h>
#include <cursespp/IKeyHandler.h>
#include <cursespp/Text.h>
#include <sigslot/sigslot.h>
@ -44,11 +45,15 @@ namespace cursespp {
class TextLabel :
#if (__clang_major__ == 7 && __clang_minor__ == 3)
public cursespp::Window,
public cursespp::IKeyHandler,
public std::enable_shared_from_this<TextLabel> {
#else
public cursespp::Window {
public cursespp::Window,
public cursespp::IKeyHandler {
#endif
public:
sigslot::signal1<TextLabel*> Activated;
TextLabel();
virtual ~TextLabel();
@ -61,7 +66,14 @@ namespace cursespp {
virtual void Show();
virtual bool KeyPress(const std::string& key);
protected:
virtual void OnFocusChanged(bool focused);
private:
void Redraw();
std::string buffer;
text::TextAlign alignment;
};

View File

@ -129,6 +129,7 @@
<ClCompile Include="app\layout\TrackSearchLayout.cpp" />
<ClCompile Include="app\model\DirectoryAdapter.cpp" />
<ClCompile Include="app\model\TrackList.cpp" />
<ClCompile Include="app\overlay\OutputOverlay.cpp" />
<ClCompile Include="app\overlay\VisualizerOverlay.cpp" />
<ClCompile Include="app\query\CategoryListViewQuery.cpp" />
<ClCompile Include="app\query\NowPlayingTrackListQuery.cpp" />
@ -185,6 +186,7 @@
<ClInclude Include="app\layout\TrackSearchLayout.h" />
<ClInclude Include="app\model\DirectoryAdapter.h" />
<ClInclude Include="app\model\TrackList.h" />
<ClInclude Include="app\overlay\OutputOverlay.h" />
<ClInclude Include="app\overlay\VisualizerOverlay.h" />
<ClInclude Include="app\query\CategoryListViewQuery.h" />
<ClInclude Include="app\query\NowPlayingTrackListQuery.h" />

View File

@ -147,6 +147,9 @@
<ClCompile Include="app\overlay\VisualizerOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
<ClCompile Include="app\overlay\OutputOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -345,6 +348,9 @@
<ClInclude Include="app\overlay\VisualizerOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
<ClInclude Include="app\overlay\OutputOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">