A bunch of small changes in preparation for release:

1. Finalized the initial `IEncoder` interface with a new
`GetPreferences()` method so external parties can configure encoder
operation.

2. Updated `IEnvironment` interface with a new `GetPreferences()` call
so plugins (e.g. encoders) can be configured by other plugins.

3. Fixed sort order in plugin list to be alphabetical.

4. Ensure the main app depends on the "stockencoders" plugin in the
Visual Studio solution.
This commit is contained in:
casey langen 2017-12-03 14:14:47 -08:00
parent 51be0ccfb4
commit e9f924fef8
17 changed files with 162 additions and 38 deletions

View File

@ -1,10 +1,11 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2002
VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "musikcube", "src\musikcube\musikcube.vcxproj", "{C7102EB1-7311-4B36-A7FF-89DD7F077FF9}"
ProjectSection(ProjectDependencies) = postProject
{68AA481E-3CCE-440F-8CCE-69F1B371C89D} = {68AA481E-3CCE-440F-8CCE-69F1B371C89D}
{8AD92D25-0921-44AB-BBEF-5244F5CFC6DA} = {8AD92D25-0921-44AB-BBEF-5244F5CFC6DA}
{51C18730-DC48-411A-829D-F2B3B7AC4C97} = {51C18730-DC48-411A-829D-F2B3B7AC4C97}
{3E30064E-B9C4-4690-8AC2-2C694176A319} = {3E30064E-B9C4-4690-8AC2-2C694176A319}
{EBD2E652-AA1B-4B8B-8D03-CCECB9BF3304} = {EBD2E652-AA1B-4B8B-8D03-CCECB9BF3304}

View File

@ -42,7 +42,7 @@
#include <core/io/DataStreamFactory.h>
#include <core/audio/Buffer.h>
#include <core/audio/Streams.h>
#include <core/support/Preferences.h>
#include <core/library/LocalSimpleDataProvider.h>
#include <core/sdk/IIndexerNotifier.h>
@ -104,6 +104,10 @@ static class Environment : public IEnvironment {
return streams::GetEncoderForType(type);
}
virtual IPreferences* GetPreferences(const char* name) override {
return Preferences::Unmanaged(name ? name : std::string());
}
virtual IBuffer* GetBuffer(size_t samples, size_t rate = 44100, size_t channels = 2) override {
musik::core::audio::Buffer* buffer = new Buffer();
buffer->SetChannels(2);

View File

@ -35,6 +35,7 @@
#pragma once
#include "IBuffer.h"
#include "IPreferences.h"
#include <stddef.h>
namespace musik { namespace core { namespace sdk {
@ -46,6 +47,7 @@ namespace musik { namespace core { namespace sdk {
virtual int Encode(const IBuffer* pcm, char** data) = 0;
virtual int Flush(char** data) = 0;
virtual void Finalize(const char* uri) = 0;
};
virtual IPreferences* GetPreferences() = 0;
};
} } }

View File

@ -38,6 +38,7 @@
#include "IDataStream.h"
#include "IDecoder.h"
#include "IEncoder.h"
#include "IPreferences.h"
namespace musik { namespace core { namespace sdk {
@ -48,6 +49,7 @@ namespace musik { namespace core { namespace sdk {
virtual IDecoder* GetDecoder(IDataStream* stream) = 0;
virtual IEncoder* GetEncoder(const char* type) = 0;
virtual IBuffer* GetBuffer(size_t samples, size_t rate = 44100, size_t channels = 2) = 0;
virtual IPreferences* GetPreferences(const char* name) = 0;
};
} } }

View File

@ -38,6 +38,8 @@ namespace musik { namespace core { namespace sdk {
class IPreferences {
public:
virtual void Release() = 0;
virtual bool GetBool(const char* key, bool defaultValue = false) = 0;
virtual int GetInt(const char* key, int defaultValue = 0) = 0;
virtual double GetDouble(const char* key, double defaultValue = 0.0f) = 0;

View File

@ -63,26 +63,26 @@ static FILE* openFile(const std::string& fn, const std::string& mode) {
}
static std::string fileToString(const std::string& fn) {
FILE* f = openFile(fn, "rb");
std::string result;
if (!f) {
return result;
if (fn.size()) {
FILE* f = openFile(fn, "rb");
if (f) {
fseek(f, 0, SEEK_END);
long len = ftell(f);
rewind(f);
if (len > 0) {
char* bytes = new char[len];
fread(static_cast<void*>(bytes), len, 1, f);
result.assign(bytes, len);
delete[] bytes;
}
fclose(f);
}
}
fseek(f, 0, SEEK_END);
long len = ftell(f);
rewind(f);
if (len > 0) {
char* bytes = new char[len];
fread(static_cast<void*>(bytes), len, 1, f);
result.assign(bytes, len);
delete[] bytes;
}
fclose(f);
return result;
}
@ -131,6 +131,14 @@ std::shared_ptr<Preferences> Preferences::ForPlugin(const std::string& pluginNam
return pluginCache[name];
}
musik::core::sdk::IPreferences* Preferences::Unmanaged(const std::string& name) {
if (!name.size()) {
return new Preferences(name, ModeTransient);
}
return Preferences::ForPlugin("unmanaged_" + name).get();
}
std::shared_ptr<Preferences> Preferences::ForComponent(
const std::string& c, Preferences::Mode mode)
{
@ -169,6 +177,12 @@ Preferences::~Preferences() {
}
}
void Preferences::Release() {
if (this->mode == ModeTransient) {
delete this;
}
}
#define RETURN_VALUE(defaultValue) \
auto it = json.find(key); \
if (it == json.end()) { \
@ -242,8 +256,9 @@ void Preferences::Save() {
if (this->mode == ModeReadOnly) {
throw std::runtime_error("cannot save a ModeReadOnly Preference!");
}
stringToFile(FILENAME(this->component), this->json.dump(2));
else if (this->mode != ModeTransient) {
stringToFile(FILENAME(this->component), this->json.dump(2));
}
}
/* SDK IPreferences interface */

View File

@ -47,6 +47,7 @@ namespace musik { namespace core {
class Preferences : public musik::core::sdk::IPreferences {
public:
enum Mode {
ModeTransient,
ModeReadOnly,
ModeReadWrite,
ModeAutoSave
@ -55,6 +56,8 @@ namespace musik { namespace core {
static void LoadPluginPreferences();
static void SavePluginPreferences();
static musik::core::sdk::IPreferences* Unmanaged(const std::string& name);
static std::shared_ptr<Preferences>
ForPlugin(const std::string& pluginName);
@ -64,15 +67,17 @@ namespace musik { namespace core {
~Preferences();
/* IPreferences (for plugin use) */
virtual bool GetBool(const char* key, bool defaultValue = false);
virtual int GetInt(const char* key, int defaultValue = 0);
virtual double GetDouble(const char* key, double defaultValue = 0.0f);
virtual int GetString(const char* key, char* dst, size_t size, const char* defaultValue = "");
virtual void Release() override;
virtual void SetBool(const char* key, bool value);
virtual void SetInt(const char* key, int value);
virtual void SetDouble(const char* key, double value);
virtual void SetString(const char* key, const char* value);
virtual bool GetBool(const char* key, bool defaultValue = false) override;
virtual int GetInt(const char* key, int defaultValue = 0) override;
virtual double GetDouble(const char* key, double defaultValue = 0.0f) override;
virtual int GetString(const char* key, char* dst, size_t size, const char* defaultValue = "") override;
virtual void SetBool(const char* key, bool value) override;
virtual void SetInt(const char* key, int value) override;
virtual void SetDouble(const char* key, double value) override;
virtual void SetString(const char* key, const char* value) override;
virtual void Save();

View File

@ -105,23 +105,34 @@ class PluginAdapter : public ScrollAdapterBase {
}
private:
static std::string SortKey(const std::string& input) {
std::string name = input;
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
return name;
}
void Refresh() {
plugins.clear();
PluginList& plugins = this->plugins;
auto prefs = this->prefs;
typedef PluginFactory::NullDeleter<IPlugin> Deleter;
using Deleter = PluginFactory::NullDeleter<IPlugin>;
using Plugin = std::shared_ptr<IPlugin>;
PluginFactory::Instance().QueryInterface<IPlugin, Deleter>(
"GetPlugin",
[&plugins, prefs](std::shared_ptr<IPlugin> plugin, const std::string& fn) {
[&plugins, prefs](Plugin plugin, const std::string& fn) {
PluginInfoPtr info(new PluginInfo());
info->name = plugin->Name();
info->fn = boost::filesystem::path(fn).filename().string();
info->enabled = prefs->GetBool(info->fn, true);
plugins.push_back(info);
});
std::sort(plugins.begin(), plugins.end(), [](PluginInfoPtr p1, PluginInfoPtr p2) -> bool {
return SortKey(p1->name) < SortKey(p2->name);
});
}
std::shared_ptr<Preferences> prefs;

View File

@ -199,8 +199,8 @@ PositionType TranscodingDataStream::Read(void *buffer, PositionType bytesToRead)
}
this->encoder->Initialize(
this->pcmBuffer->SampleRate(),
this->pcmBuffer->Channels(),
this->pcmBuffer->SampleRate(),
this->pcmBuffer->Channels(),
this->bitrate);
}
}

View File

@ -33,8 +33,11 @@
//////////////////////////////////////////////////////////////////////////////
#include "LameEncoder.h"
#include "shared.h"
#include <string>
using namespace musik::core::sdk;
#ifdef WIN32
#include <windows.h>
static inline std::wstring utf8to16(const char* utf8) {
@ -48,10 +51,9 @@ static inline std::wstring utf8to16(const char* utf8) {
}
#endif
void LameEncoder::Release() {
lame_close(lame);
lame = nullptr;
delete this;
LameEncoder::LameEncoder() {
this->lame = nullptr;
this->prefs = env()->GetPreferences("LameEncoder");
}
void LameEncoder::Initialize(size_t rate, size_t channels, size_t bitrate) {
@ -66,6 +68,17 @@ void LameEncoder::Initialize(size_t rate, size_t channels, size_t bitrate) {
lame_init_params(lame);
}
void LameEncoder::Release() {
lame_close(lame);
lame = nullptr;
this->prefs->Release();
delete this;
}
IPreferences* LameEncoder::GetPreferences() {
return this->prefs;
}
int LameEncoder::Encode(const IBuffer* pcm, char** data) {
/* calculated according to lame.h */
size_t channels = pcm->Channels();

View File

@ -40,14 +40,18 @@ class LameEncoder : public musik::core::sdk::IEncoder {
using IBuffer = musik::core::sdk::IBuffer;
public:
LameEncoder();
virtual void Release() override;
virtual void Initialize(size_t rate, size_t channels, size_t bitrate) override;
virtual int Encode(const IBuffer* pcm, char** data) override;
virtual int Flush(char** data) override;
virtual void Finalize(const char* uri) override;
virtual musik::core::sdk::IPreferences* GetPreferences() override;
private:
DataBuffer<unsigned char> encodedBytes;
DataBuffer<float> downmix;
musik::core::sdk::IPreferences* prefs;
lame_t lame;
};

View File

@ -33,11 +33,14 @@
//////////////////////////////////////////////////////////////////////////////
#include "OggEncoder.h"
#include "shared.h"
#include <random>
/* fre:ac/BoCA has an excellent example of vorbis encoder usage, a lot of code
was adapted (stolen) from here: https://github.com/enzo1982/BoCA/blob/master/components/encoder/vorbis/vorbis.cpp */
using namespace musik::core::sdk;
void OggEncoder::Initialize(size_t rate, size_t channels, size_t bitrate) {
ogg_stream_state os = { 0 };
ogg_page og = { 0 };
@ -48,6 +51,7 @@ void OggEncoder::Initialize(size_t rate, size_t channels, size_t bitrate) {
vorbis_block vb = { 0 };
this->bitrate = bitrate * 1000;
this->headerWritten = false;
this->prefs = env()->GetPreferences("OggEncoder");
}
void OggEncoder::Release() {
@ -56,9 +60,14 @@ void OggEncoder::Release() {
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
this->prefs->Release();
delete this;
}
IPreferences* OggEncoder::GetPreferences() {
return this->prefs;
}
int OggEncoder::Encode(const IBuffer* pcm, char** data) {
encodedData.reset();

View File

@ -36,7 +36,7 @@
#include <core/sdk/DataBuffer.h>
#include <vorbis/vorbisenc.h>
/* fre:ac/BoCA has an excellent example of vorbis encoder usage, a lot of code
/* fre:ac/BoCA has an excellent example of vorbis encoder usage, a lot of code
was adapted (stolen) from here: https://github.com/enzo1982/BoCA/blob/master/components/encoder/vorbis/vorbis.cpp */
class OggEncoder : public musik::core::sdk::IEncoder {
@ -48,11 +48,13 @@ class OggEncoder : public musik::core::sdk::IEncoder {
virtual int Encode(const IBuffer* pcm, char** data) override;
virtual int Flush(char** data) override;
virtual void Finalize(const char* uri) override;
virtual musik::core::sdk::IPreferences* GetPreferences() override;
private:
int WritePackets(bool flush);
DataBuffer<char> encodedData;
musik::core::sdk::IPreferences* prefs;
long bitrate;
bool headerWritten;
ogg_stream_state os;

View File

@ -36,6 +36,7 @@
#include <core/sdk/IPlugin.h>
#include <core/sdk/IEncoderFactory.h>
#include "shared.h"
#include "LameEncoder.h"
#include "OggEncoder.h"
@ -50,6 +51,8 @@
using namespace musik::core::sdk;
static IEnvironment* environment = nullptr;
static class Plugin : public IPlugin {
public:
Plugin() {
@ -120,3 +123,11 @@ extern "C" DLL_EXPORT IPlugin* GetPlugin() {
extern "C" DLL_EXPORT IEncoderFactory* GetEncoderFactory() {
return &encoderFactory;
}
extern "C" DLL_EXPORT void SetEnvironment(musik::core::sdk::IEnvironment* env) {
environment = env;
}
IEnvironment* env() {
return environment;
}

View File

@ -0,0 +1,39 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 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/sdk/IEnvironment.h>
extern musik::core::sdk::IEnvironment* env();

View File

@ -152,6 +152,7 @@
<ItemGroup>
<ClInclude Include="LameEncoder.h" />
<ClInclude Include="OggEncoder.h" />
<ClInclude Include="shared.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -24,5 +24,8 @@
<ClInclude Include="OggEncoder.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="shared.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
</Project>