Moved some stream decoder and dsp loading logic out of the actual Stream implementation, and into a helper namespace so it can be re-used. Added back DynamicStream to test on Windows.

This commit is contained in:
Casey Langen 2016-12-03 14:52:37 -08:00
parent 6be3a5030a
commit 53429bdf92
11 changed files with 209 additions and 129 deletions

View File

@ -1,9 +1,11 @@
set(CORE_SOURCES set(CORE_SOURCES
./debug.cpp ./debug.cpp
./audio/Buffer.cpp ./audio/Buffer.cpp
./audio/Player.cpp ./audio/DynamicStream.cpp
./audio/GaplessTransport.cpp ./audio/GaplessTransport.cpp
./audio/Player.cpp
./audio/Stream.cpp ./audio/Stream.cpp
./audio/Streams.cpp
./audio/Visualizer.cpp ./audio/Visualizer.cpp
./db/Connection.cpp ./db/Connection.cpp
./db/ScopedTransaction.cpp ./db/ScopedTransaction.cpp

View File

@ -38,6 +38,7 @@
#include <core/audio/DynamicStream.h> #include <core/audio/DynamicStream.h>
#include <core/sdk/IDecoderFactory.h> #include <core/sdk/IDecoderFactory.h>
#include <core/plugin/PluginFactory.h> #include <core/plugin/PluginFactory.h>
#include <core/audio/Streams.h>
using namespace musik::core::audio; using namespace musik::core::audio;
using namespace musik::core::sdk; using namespace musik::core::sdk;
@ -53,10 +54,8 @@ DynamicStream::DynamicStream(unsigned int options)
, decoderSamplePosition(0) { , decoderSamplePosition(0) {
if ((this->options & NoDSP) == 0) { if ((this->options & NoDSP) == 0) {
typedef PluginFactory::DestroyDeleter<IDSP> Deleter; typedef PluginFactory::DestroyDeleter<IDSP> Deleter;
this->dsps = PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP"); this->dsps = streams::GetDspPlugins();
} }
this->LoadDecoderPlugins();
} }
DynamicStream::~DynamicStream() { DynamicStream::~DynamicStream() {
@ -91,42 +90,8 @@ bool DynamicStream::OpenStream(std::string uri) {
return false; return false;
} }
/* find a DecoderFactory we can use for this type of data*/ this->decoder = streams::GetDecoderForDataStream(this->dataStream);
DecoderFactoryList::iterator factories = this->decoderFactories.begin(); return !!this->decoder;
DecoderFactoryList::iterator end = this->decoderFactories.end();
DecoderFactoryPtr decoderFactory;
for (; factories != end && !decoderFactory; ++factories) {
if ((*factories)->CanHandle(this->dataStream->Type())) {
decoderFactory = (*factories);
}
}
if (!decoderFactory) {
/* nothing can decode this type of file */
musik::debug::err(TAG, "nothing could open " + uri);
return false;
}
IDecoder *decoder = decoderFactory->CreateDecoder();
if (!decoder) {
/* shouldn't ever happen, the factory said it can handle this file */
return false;
}
/* ask the decoder to open the data stream. if it returns true we're
good to start pulling data out of it! */
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
this->decoder.reset(decoder, Deleter());
if (!this->decoder->Open(this->dataStream.get())) {
musik::debug::err(TAG, "open ok, but decode failed " + uri);
return false;
}
musik::debug::info(TAG, "about ready to play: " + uri);
return true;
} }
void DynamicStream::OnBufferProcessedByPlayer(BufferPtr buffer) { void DynamicStream::OnBufferProcessedByPlayer(BufferPtr buffer) {
@ -140,13 +105,8 @@ BufferPtr DynamicStream::GetNextBufferFromDecoder() {
return BufferPtr(); /* return NULL */ return BufferPtr(); /* return NULL */
} }
/* remember the sample rate so we can calculate the current time-position */ this->decoderSampleRate = buffer->SampleRate();
if (!this->decoderSampleRate || !this->decoderChannels) { this->decoderChannels = buffer->Channels();
this->decoderSampleRate = buffer->SampleRate();
this->decoderChannels = buffer->Channels();
}
/* offset, in samples */
this->decoderSamplePosition += buffer->Samples(); this->decoderSamplePosition += buffer->Samples();
/* calculate the position (seconds) in the buffer */ /* calculate the position (seconds) in the buffer */
@ -163,8 +123,6 @@ BufferPtr DynamicStream::GetNextProcessedOutputBuffer() {
BufferPtr currentBuffer = this->GetNextBufferFromDecoder(); BufferPtr currentBuffer = this->GetNextBufferFromDecoder();
if (currentBuffer) { if (currentBuffer) {
void * f = &free;
/* try to fill the buffer to its optimal size; if the decoder didn't return /* try to fill the buffer to its optimal size; if the decoder didn't return
a full buffer, ask it for some more data. */ a full buffer, ask it for some more data. */
bool moreBuffers = true; bool moreBuffers = true;
@ -220,11 +178,4 @@ BufferPtr DynamicStream::GetEmptyBuffer() {
/* marks a used buffer as recycled so it can be re-used later. */ /* marks a used buffer as recycled so it can be re-used later. */
void DynamicStream::RecycleBuffer(BufferPtr oldBuffer) { void DynamicStream::RecycleBuffer(BufferPtr oldBuffer) {
this->recycledBuffers.push_back(oldBuffer); this->recycledBuffers.push_back(oldBuffer);
}
void DynamicStream::LoadDecoderPlugins() {
PluginFactory::DestroyDeleter<IDecoderFactory> typedef Deleter;
this->decoderFactories = PluginFactory::Instance()
.QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
} }

View File

@ -40,7 +40,6 @@
#include <core/audio/IStream.h> #include <core/audio/IStream.h>
#include <core/sdk/IDecoder.h> #include <core/sdk/IDecoder.h>
#include <core/sdk/IDSP.h> #include <core/sdk/IDSP.h>
#include <core/sdk/IDecoderFactory.h>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <list> #include <list>
@ -50,7 +49,6 @@ namespace musik { namespace core { namespace audio {
class DynamicStream : public IStream { class DynamicStream : public IStream {
using IDSP = musik::core::sdk::IDSP; using IDSP = musik::core::sdk::IDSP;
using IDecoder = musik::core::sdk::IDecoder; using IDecoder = musik::core::sdk::IDecoder;
using IDecoderFactory = musik::core::sdk::IDecoderFactory;
public: public:
virtual ~DynamicStream(); virtual ~DynamicStream();
@ -69,11 +67,8 @@ namespace musik { namespace core { namespace audio {
void RecycleBuffer(BufferPtr oldBuffer); void RecycleBuffer(BufferPtr oldBuffer);
BufferPtr GetNextBufferFromDecoder(); BufferPtr GetNextBufferFromDecoder();
BufferPtr GetEmptyBuffer(); BufferPtr GetEmptyBuffer();
void LoadDecoderPlugins();
typedef std::list<BufferPtr> BufferList; typedef std::list<BufferPtr> BufferList;
typedef std::shared_ptr<IDecoderFactory> DecoderFactoryPtr;
typedef std::vector<DecoderFactoryPtr> DecoderFactoryList;
typedef std::shared_ptr<IDecoder> DecoderPtr; typedef std::shared_ptr<IDecoder> DecoderPtr;
typedef std::shared_ptr<IDSP> DspPtr; typedef std::shared_ptr<IDSP> DspPtr;
typedef std::vector<DspPtr> Dsps; typedef std::vector<DspPtr> Dsps;
@ -87,7 +82,6 @@ namespace musik { namespace core { namespace audio {
musik::core::io::DataStreamFactory::DataStreamPtr dataStream; musik::core::io::DataStreamFactory::DataStreamPtr dataStream;
BufferList recycledBuffers; BufferList recycledBuffers;
DecoderFactoryList decoderFactories;
DecoderPtr decoder; DecoderPtr decoder;
Dsps dsps; Dsps dsps;
}; };

View File

@ -36,10 +36,12 @@
#include <kiss_fftr.h> #include <kiss_fftr.h>
#include <core/debug.h> #include <core/debug.h>
#include <core/audio/Player.h> #include <core/audio/DynamicStream.h>
#include <core/audio/Stream.h> #include <core/audio/Stream.h>
#include <core/audio/Player.h>
#include <core/audio/Visualizer.h> #include <core/audio/Visualizer.h>
#include <core/plugin/PluginFactory.h> #include <core/plugin/PluginFactory.h>
#include <algorithm> #include <algorithm>
#include <math.h> #include <math.h>
#include <future> #include <future>
@ -189,6 +191,7 @@ int Player::State() {
void musik::core::audio::playerThreadLoop(Player* player) { void musik::core::audio::playerThreadLoop(Player* player) {
player->stream = Stream::Create(); player->stream = Stream::Create();
// player->stream = DynamicStream::Create();
BufferPtr buffer; BufferPtr buffer;

View File

@ -35,13 +35,11 @@
#include "pch.hpp" #include "pch.hpp"
#include "Stream.h" #include "Stream.h"
#include "Streams.h"
#include <core/debug.h> #include <core/debug.h>
#include <core/sdk/IDecoderFactory.h>
#include <core/plugin/PluginFactory.h>
using namespace musik::core::audio; using namespace musik::core::audio;
using namespace musik::core::sdk; using namespace musik::core::sdk;
using musik::core::PluginFactory;
static std::string TAG = "Stream"; static std::string TAG = "Stream";
@ -63,15 +61,13 @@ Stream::Stream(int samplesPerChannel, int bufferCount, unsigned int options)
, decoderChannels(0) , decoderChannels(0)
, decoderSamplePosition(0) { , decoderSamplePosition(0) {
if ((this->options & NoDSP) == 0) { if ((this->options & NoDSP) == 0) {
typedef PluginFactory::DestroyDeleter<IDSP> Deleter; streams::GetDspPlugins();
this->dsps = PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP");
} }
for (int i = 0; i < bufferCount; i++) { for (int i = 0; i < bufferCount; i++) {
this->recycledBuffers.push_back(Buffer::Create()); this->recycledBuffers.push_back(Buffer::Create());
} }
this->LoadDecoderPlugins();
this->dspBuffer = Buffer::Create(); this->dspBuffer = Buffer::Create();
/* note that the decoder buffer needs to have a pre-allocated, non-resizable buffer /* note that the decoder buffer needs to have a pre-allocated, non-resizable buffer
@ -122,42 +118,8 @@ bool Stream::OpenStream(std::string uri) {
return false; return false;
} }
/* find a DecoderFactory we can use for this type of data*/ this->decoder = streams::GetDecoderForDataStream(this->dataStream);
DecoderFactoryList::iterator factories = this->decoderFactories.begin(); return !!this->decoder;
DecoderFactoryList::iterator end = this->decoderFactories.end();
DecoderFactoryPtr decoderFactory;
for ( ; factories != end && !decoderFactory; ++factories) {
if ((*factories)->CanHandle(this->dataStream->Type())) {
decoderFactory = (*factories);
}
}
if (!decoderFactory) {
/* nothing can decode this type of file */
musik::debug::err(TAG, "nothing could open " + uri);
return false;
}
IDecoder *decoder = decoderFactory->CreateDecoder();
if (!decoder) {
/* shouldn't ever happen, the factory said it can handle this file */
return false;
}
/* ask the decoder to open the data stream. if it returns true we're
good to start pulling data out of it! */
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
this->decoder.reset(decoder, Deleter());
if (!this->decoder->Open(this->dataStream.get())) {
musik::debug::err(TAG, "open ok, but decode failed " + uri);
return false;
}
musik::debug::info(TAG, "about ready to play: " + uri);
return true;
} }
void Stream::OnBufferProcessedByPlayer(BufferPtr buffer) { void Stream::OnBufferProcessedByPlayer(BufferPtr buffer) {
@ -172,13 +134,8 @@ bool Stream::GetNextBufferFromDecoder() {
return false; return false;
} }
/* remember the sample rate so we can calculate the current time-position */ this->decoderSampleRate = buffer->SampleRate();
if (!this->decoderSampleRate || !this->decoderChannels) { this->decoderChannels = buffer->Channels();
this->decoderSampleRate = buffer->SampleRate();
this->decoderChannels = buffer->Channels();
}
/* offset, in samples */
this->decoderSamplePosition += buffer->Samples(); this->decoderSamplePosition += buffer->Samples();
/* calculate the position (seconds) in the buffer */ /* calculate the position (seconds) in the buffer */
@ -297,10 +254,3 @@ BufferPtr Stream::GetNextProcessedOutputBuffer() {
void Stream::RecycleBuffer(BufferPtr oldBuffer) { void Stream::RecycleBuffer(BufferPtr oldBuffer) {
this->recycledBuffers.push_back(oldBuffer); this->recycledBuffers.push_back(oldBuffer);
} }
void Stream::LoadDecoderPlugins() {
PluginFactory::DestroyDeleter<IDecoderFactory> typedef Deleter;
this->decoderFactories = PluginFactory::Instance()
.QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
}

View File

@ -40,7 +40,6 @@
#include <core/audio/IStream.h> #include <core/audio/IStream.h>
#include <core/sdk/IDecoder.h> #include <core/sdk/IDecoder.h>
#include <core/sdk/IDSP.h> #include <core/sdk/IDSP.h>
#include <core/sdk/IDecoderFactory.h>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <list> #include <list>
@ -50,7 +49,6 @@ namespace musik { namespace core { namespace audio {
class Stream : public IStream { class Stream : public IStream {
using IDSP = musik::core::sdk::IDSP; using IDSP = musik::core::sdk::IDSP;
using IDecoder = musik::core::sdk::IDecoder; using IDecoder = musik::core::sdk::IDecoder;
using IDecoderFactory = musik::core::sdk::IDecoderFactory;
public: public:
static StreamPtr Create( static StreamPtr Create(
@ -73,11 +71,8 @@ namespace musik { namespace core { namespace audio {
void RecycleBuffer(BufferPtr oldBuffer); void RecycleBuffer(BufferPtr oldBuffer);
bool GetNextBufferFromDecoder(); bool GetNextBufferFromDecoder();
BufferPtr GetEmptyBuffer(); BufferPtr GetEmptyBuffer();
void LoadDecoderPlugins();
typedef std::list<BufferPtr> BufferList; typedef std::list<BufferPtr> BufferList;
typedef std::shared_ptr<IDecoderFactory> DecoderFactoryPtr;
typedef std::vector<DecoderFactoryPtr> DecoderFactoryList;
typedef std::shared_ptr<IDecoder> DecoderPtr; typedef std::shared_ptr<IDecoder> DecoderPtr;
typedef std::shared_ptr<IDSP> DspPtr; typedef std::shared_ptr<IDSP> DspPtr;
typedef std::vector<DspPtr> Dsps; typedef std::vector<DspPtr> Dsps;
@ -99,7 +94,6 @@ namespace musik { namespace core { namespace audio {
int samplesPerChannel; int samplesPerChannel;
int bufferCount; int bufferCount;
DecoderFactoryList decoderFactories;
DecoderPtr decoder; DecoderPtr decoder;
Dsps dsps; Dsps dsps;
}; };

124
src/core/audio/Streams.cpp Normal file
View File

@ -0,0 +1,124 @@
//////////////////////////////////////////////////////////////////////////////
//
// 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 "Streams.h"
#include <core/debug.h>
#include <core/sdk/IDecoderFactory.h>
#include <core/plugin/PluginFactory.h>
#include <mutex>
#define TAG "Streams"
using namespace musik::core::audio;
using namespace musik::core::sdk;
using musik::core::PluginFactory;
using DataStreamPtr = musik::core::io::DataStreamFactory::DataStreamPtr;
using IDSP = musik::core::sdk::IDSP;
using IDecoder = musik::core::sdk::IDecoder;
using IDecoderFactory = musik::core::sdk::IDecoderFactory;
using DecoderFactoryList = std::vector<std::shared_ptr<IDecoderFactory > >;
using DspList = std::vector<std::shared_ptr<IDSP > >;
static std::mutex initLock;
static DecoderFactoryList decoders;
static void init() {
std::unique_lock<std::mutex> lock(initLock);
if (!decoders.size()) {
typedef PluginFactory::DestroyDeleter<IDecoderFactory> Deleter;
decoders = PluginFactory::Instance()
.QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
}
}
namespace musik { namespace core { namespace audio {
namespace streams {
std::shared_ptr<IDecoder> GetDecoderForDataStream(DataStreamPtr dataStream) {
init();
std::shared_ptr<IDecoder> result;
/* find a DecoderFactory we can use for this type of data*/
DecoderFactoryList::iterator factories = decoders.begin();
DecoderFactoryList::iterator end = decoders.end();
std::shared_ptr<IDecoderFactory> factory;
for ( ; factories != end && !factory; ++factories) {
if ((*factories)->CanHandle(dataStream->Type())) {
factory = (*factories);
}
}
const std::string uri = dataStream->Uri();
if (!factory) {
/* nothing can decode this type of file */
musik::debug::err(TAG, "nothing could open " + uri);
return result;
}
IDecoder *decoder = factory->CreateDecoder();
if (!decoder) {
/* shouldn't ever happen, the factory said it can handle this file */
return result;
}
/* ask the decoder to open the data stream. if it returns true we're
good to start pulling data out of it! */
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
result.reset(decoder, Deleter());
if (!result->Open(dataStream.get())) {
musik::debug::err(TAG, "open ok, but decode failed " + uri);
result.reset();
return result;
}
musik::debug::info(TAG, "about ready to play: " + uri);
return result;
}
DspList GetDspPlugins() {
typedef PluginFactory::DestroyDeleter<IDSP> Deleter;
return PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP");
}
};
} } }

55
src/core/audio/Streams.h Normal file
View File

@ -0,0 +1,55 @@
//////////////////////////////////////////////////////////////////////////////
//
// 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/io/DataStreamFactory.h>
#include <core/sdk/IDecoder.h>
#include <core/sdk/IDSP.h>
#include <core/sdk/IDecoderFactory.h>
#include <memory>
#include <vector>
namespace musik { namespace core { namespace audio {
namespace streams {
std::shared_ptr<musik::core::sdk::IDecoder>
GetDecoderForDataStream(musik::core::io::DataStreamFactory::DataStreamPtr dataStream);
std::vector<std::shared_ptr<musik::core::sdk::IDSP > > GetDspPlugins();
};
} } }

View File

@ -61,25 +61,25 @@ LocalFileStream::~LocalFileStream() {
bool LocalFileStream::Open(const char *filename, unsigned int options) { bool LocalFileStream::Open(const char *filename, unsigned int options) {
try { try {
std::string fn(filename); this->uri = filename;
debug::info(TAG, "opening file: " + std::string(filename)); debug::info(TAG, "opening file: " + std::string(filename));
boost::filesystem::path file(filename); boost::filesystem::path file(filename);
if (!boost::filesystem::exists(file)) { if (!boost::filesystem::exists(file)) {
debug::err(TAG, "open failed " + fn); debug::err(TAG, "open failed " + this->uri);
return false; return false;
} }
if (!boost::filesystem::is_regular(file)) { if (!boost::filesystem::is_regular(file)) {
debug::err(TAG, "not a regular file" + fn); debug::err(TAG, "not a regular file" + this->uri);
return false; return false;
} }
this->filesize = (long)boost::filesystem::file_size(file); this->filesize = (long)boost::filesystem::file_size(file);
this->extension = file.extension().string(); this->extension = file.extension().string();
#ifdef WIN32 #ifdef WIN32
std::wstring u16fn = u8to16(fn); std::wstring u16fn = u8to16(this->uri);
this->file = _wfopen(u16fn.c_str(), L"rb"); this->file = _wfopen(u16fn.c_str(), L"rb");
#else #else
this->file = fopen(filename, "rb"); this->file = fopen(filename, "rb");
@ -135,3 +135,7 @@ long LocalFileStream::Length() {
const char* LocalFileStream::Type() { const char* LocalFileStream::Type() {
return this->extension.c_str(); return this->extension.c_str();
} }
const char* LocalFileStream::Uri() {
return this->uri.c_str();
}

View File

@ -62,9 +62,11 @@ namespace musik { namespace core { namespace io {
virtual bool Eof(); virtual bool Eof();
virtual long Length(); virtual long Length();
virtual const char* Type(); virtual const char* Type();
virtual const char* Uri();
private: private:
std::string extension; std::string extension;
std::string uri;
FILE *file; FILE *file;
long filesize; long filesize;
}; };

View File

@ -51,6 +51,7 @@ namespace musik { namespace core { namespace sdk {
virtual bool Eof() = 0; virtual bool Eof() = 0;
virtual long Length() = 0; virtual long Length() = 0;
virtual const char* Type() = 0; virtual const char* Type() = 0;
virtual const char* Uri() = 0;
}; };
} } } } } }