Fixed WaveOut to actually use both channels, instead of just the left one. Also fixed a few bugs related to closing WaveOut.

This commit is contained in:
casey 2016-04-30 14:31:37 -07:00
parent fe1b2827e4
commit a0f7a7670c
16 changed files with 542 additions and 412 deletions

View File

@ -1,233 +0,0 @@
//////////////////////////////////////////////////////////////////////////////
// Copyright © 2007, mC2 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 "OGGDecoder.h"
OGGDecoder::OGGDecoder()
{
// Set the callbacks to handle all file io
this->oggCallbacks.read_func = &OggRead;
this->oggCallbacks.seek_func = &OggSeek;
this->oggCallbacks.tell_func = &OggTell;
this->oggCallbacks.close_func = &OggClose;
}
size_t OGGDecoder::OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource){
return (size_t)((OGGDecoder*)datasource)->fileStream->Read(buffer,(long)(nofParts*partSize));
}
int OGGDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence){
switch(whence){
case SEEK_CUR:
{
long currentPosition = ((OGGDecoder*)datasource)->fileStream->Position();
if( ((OGGDecoder*)datasource)->fileStream->SetPosition(currentPosition+(long)offset)){
return 0;
}
}
break;
case SEEK_END:
{
long fileSize = ((OGGDecoder*)datasource)->fileStream->Filesize();
if( ((OGGDecoder*)datasource)->fileStream->SetPosition(fileSize)){
return 0;
}
}
break;
default:
{
if( ((OGGDecoder*)datasource)->fileStream->SetPosition((long)offset)){
return 0;
}
}
}
// Unsuccessfull
return -1;
}
long OGGDecoder::OggTell(void *datasource){
return ((OGGDecoder*)datasource)->fileStream->Position();
}
int OGGDecoder::OggClose(void *datasource){
if( ((OGGDecoder*)datasource)->fileStream->Close() ){
return 0;
}
return -1;
}
OGGDecoder::~OGGDecoder()
{
}
bool OGGDecoder::Open(musik::core::filestreams::IFileStream *fileStream)
{
this->fileStream = fileStream;
if(ov_open_callbacks(this,&this->oggFile,NULL,0,this->oggCallbacks)!=0){
return false;
}
return true;
}
void OGGDecoder::Destroy(void){
ov_clear(&this->oggFile);
delete this;
}
/*
bool OGGDecoder::GetFormat(unsigned long * SampleRate, unsigned long * Channels){
vorbis_info *info = ov_info(&this->oggFile,-1);
if(info){
*SampleRate = info->rate;
*Channels = info->channels;
return true;
}
return false;
}
bool OGGDecoder::GetLength(unsigned long * MS){
double time = ov_time_total(&this->oggFile, 0);
if( time!=OV_EINVAL ){
*MS = (unsigned long)(time * 1000.0);
return true;
}
return false;
}
*/
double OGGDecoder::SetPosition(double second,double totalLength){
if(ov_seekable(&this->oggFile)){
if(!ov_time_seek(&this->oggFile, second)){
return ov_time_tell(&this->oggFile);
}
}
return -1;
}
bool OGGDecoder::GetBuffer(IBuffer *buffer){
vorbis_info *info = ov_info(&this->oggFile, -1);
long nofSamplesMax = 1024*2;
int currentSelection;
buffer->SetChannels(info->channels);
buffer->SetSampleRate(info->rate);
buffer->SetSamples(nofSamplesMax);
float ** pcm;
unsigned long samplesRead = ov_read_float(&this->oggFile, &pcm, nofSamplesMax, &currentSelection);
if(samplesRead == 0) {
return false;
}
buffer->SetSamples(samplesRead);
/* MUSIKCUBE EXPECTS
SPEAKER_FRONT_LEFT 0x1
SPEAKER_FRONT_RIGHT 0x2
SPEAKER_FRONT_CENTER 0x4
SPEAKER_LOW_FREQUENCY 0x8
SPEAKER_BACK_LEFT 0x10
SPEAKER_BACK_RIGHT 0x20
*/
/* OGG DOES
One channel: the stream is monophonic
Two channels: the stream is stereo. channel order: left, right.
Three channels: the stream is a 1d-surround encoding. channel order: left, center, right
Four channels: the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
Five channels: the stream is five-channel surround. channel order: front left, front center, front right, rear left, rear right
Six channels: (used in Dolby Digital/AC3) the stream is 5,1 surround. channel order: front left, front center, front right, rear left, rear right, LFE (the sixth channel is entirely bass).
*/
// so we need to REORDER
float *pDataBuffer = buffer->BufferPointer();
if(info->channels == 3){
float *pDataBuffer = buffer->BufferPointer();
for(unsigned long x=0; x<samplesRead; ++x){
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
}
}else if(info->channels == 5){
for(unsigned long x=0; x<samplesRead; ++x){
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
*pDataBuffer = pcm[3][x];
++pDataBuffer;
*pDataBuffer = pcm[4][x];
++pDataBuffer;
}
}else if(info->channels == 6){
for(unsigned long x=0; x<samplesRead; ++x){
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
*pDataBuffer = pcm[5][x];
++pDataBuffer;
*pDataBuffer = pcm[3][x];
++pDataBuffer;
*pDataBuffer = pcm[4][x];
++pDataBuffer;
}
}else{
for(unsigned long x=0; x<samplesRead; ++x){
for(int channel(0); channel<info->channels; ++channel){
*pDataBuffer = pcm[channel][x];
++pDataBuffer;
}
}
}
return true;
}

View File

@ -32,47 +32,41 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "stdafx.h"
#include <cctype> #include <cctype>
#include "OggDecoderFactory.h"
#include "VorbisDecoder.h"
#include "OggSourceSupplier.h" OggDecoderFactory::OggDecoderFactory() {
#include "OggDecoder.h"
OggSourceSupplier::OggSourceSupplier()
{
} }
OggSourceSupplier::~OggSourceSupplier() OggDecoderFactory::~OggDecoderFactory() {
{
} }
void OggSourceSupplier::Destroy() void OggDecoderFactory::Destroy() {
{
delete this; delete this;
} }
IDecoder* OggSourceSupplier::CreateDecoder() IDecoder* OggDecoderFactory::CreateDecoder() {
{ return new VorbisDecoder();
return new OGGDecoder();
} }
bool OggSourceSupplier::CanHandle(const utfchar* type) const bool OggDecoderFactory::CanHandle(const utfchar* type) const {
{ if (type) {
if(type){
utfstring typeString(type); utfstring typeString(type);
if(typeString.find(UTF("ogg"))!=utfstring::npos){
if (typeString.find(UTF("ogg")) != utfstring::npos) {
return true; return true;
} }
if(typeString.find(UTF("oga"))!=utfstring::npos){ if (typeString.find(UTF("oga")) != utfstring::npos) {
return true; return true;
} }
if(typeString.find(UTF("audio/ogg"))!=utfstring::npos){ if (typeString.find(UTF("audio/ogg")) != utfstring::npos) {
return true; return true;
} }
if(typeString.find(UTF("audio/vorbis"))!=utfstring::npos){ if (typeString.find(UTF("audio/vorbis")) != utfstring::npos) {
return true; return true;
} }
} }
return false; return false;
} }

View File

@ -37,10 +37,10 @@
using namespace musik::core::audio; using namespace musik::core::audio;
class OggSourceSupplier : public IDecoderFactory { class OggDecoderFactory : public IDecoderFactory {
public: public:
OggSourceSupplier(); OggDecoderFactory();
~OggSourceSupplier(); ~OggDecoderFactory();
IDecoder* CreateDecoder(); IDecoder* CreateDecoder();
void Destroy(); void Destroy();

View File

@ -0,0 +1,214 @@
//////////////////////////////////////////////////////////////////////////////
// Copyright © 2007, mC2 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 "VorbisDecoder.h"
VorbisDecoder::VorbisDecoder() {
this->oggCallbacks.read_func = &OggRead;
this->oggCallbacks.seek_func = &OggSeek;
this->oggCallbacks.tell_func = &OggTell;
this->oggCallbacks.close_func = &OggClose;
}
size_t VorbisDecoder::OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource) {
return (size_t)((VorbisDecoder*)datasource)->fileStream->Read(buffer,(long)(nofParts*partSize));
}
int VorbisDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence) {
switch(whence) {
case SEEK_CUR:
{
long currentPosition = ((VorbisDecoder*)datasource)->fileStream->Position();
if(((VorbisDecoder*)datasource)->fileStream->SetPosition(currentPosition+(long) offset)) {
return 0;
}
}
break;
case SEEK_END:
{
long fileSize = ((VorbisDecoder*) datasource)->fileStream->Filesize();
if(((VorbisDecoder*) datasource)->fileStream->SetPosition(fileSize)) {
return 0;
}
}
break;
default:
{
if(((VorbisDecoder*) datasource)->fileStream->SetPosition((long) offset)) {
return 0;
}
}
}
return -1;
}
long VorbisDecoder::OggTell(void *datasource) {
return ((VorbisDecoder*)datasource)->fileStream->Position();
}
int VorbisDecoder::OggClose(void *datasource) {
if(((VorbisDecoder*)datasource)->fileStream->Close()) {
return 0;
}
return -1;
}
VorbisDecoder::~VorbisDecoder() {
}
bool VorbisDecoder::Open(musik::core::filestreams::IFileStream *fileStream) {
this->fileStream = fileStream;
if (ov_open_callbacks(this, &this->oggFile, NULL, 0, this->oggCallbacks) != 0) {
return false;
}
return true;
}
void VorbisDecoder::Destroy() {
ov_clear(&this->oggFile);
delete this;
}
double VorbisDecoder::SetPosition(double second, double totalLength) {
if (ov_seekable(&this->oggFile)) {
if (!ov_time_seek(&this->oggFile, second)) {
return ov_time_tell(&this->oggFile);
}
}
return -1;
}
#define OGG_MAX_SAMPLES 1024
bool VorbisDecoder::GetBuffer(IBuffer *buffer) {
int bitstream;
float **pcm;
unsigned long samplesRead = ov_read_float(&this->oggFile, &pcm, OGG_MAX_SAMPLES, &bitstream);
if (samplesRead == 0) {
return false;
}
vorbis_info *info = ov_info(&this->oggFile, -1);
buffer->SetChannels(info->channels);
buffer->SetSampleRate(info->rate);
buffer->SetSamples(samplesRead);
/*
The musik audio engine expects:
SPEAKER_FRONT_LEFT 0x1
SPEAKER_FRONT_RIGHT 0x2
SPEAKER_FRONT_CENTER 0x4
SPEAKER_LOW_FREQUENCY 0x8
SPEAKER_BACK_LEFT 0x10
SPEAKER_BACK_RIGHT 0x20
OGG returns:
One channel: the stream is monophonic
Two channels: the stream is stereo. channel order: left, right.
Three channels: the stream is a 1d-surround encoding. channel order: left, center, right
Four channels: the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
Five channels: the stream is five-channel surround. channel order: front left, front center, front right, rear left, rear right
Six channels: (used in Dolby Digital/AC3) the stream is 5,1 surround. channel order: front left, front center, front right, rear left, rear right, LFE (the sixth channel is entirely bass).
... so let's re-order when writing to our output buffer ...
*/
float *pDataBuffer = buffer->BufferPointer();
if (info->channels == 2) {
for (unsigned long x = 0; x < samplesRead; ++x) {
pDataBuffer[0] = pcm[0][x];
pDataBuffer[1] = pcm[1][x];
pDataBuffer += 2;
}
}
else if (info->channels == 3) {
for (unsigned long x=0; x < samplesRead; ++x) {
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
}
}
else if (info->channels == 5) {
for (unsigned long x = 0; x < samplesRead; ++x) {
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
*pDataBuffer = pcm[3][x];
++pDataBuffer;
*pDataBuffer = pcm[4][x];
++pDataBuffer;
}
}
else if (info->channels == 6) {
for (unsigned long x = 0; x < samplesRead; ++x) {
*pDataBuffer = pcm[0][x];
++pDataBuffer;
*pDataBuffer = pcm[2][x];
++pDataBuffer;
*pDataBuffer = pcm[1][x];
++pDataBuffer;
*pDataBuffer = pcm[5][x];
++pDataBuffer;
*pDataBuffer = pcm[3][x];
++pDataBuffer;
*pDataBuffer = pcm[4][x];
++pDataBuffer;
}
}
else {
for (unsigned long x = 0; x < samplesRead; ++x) {
for (int channel(0); channel < info->channels; ++channel) {
*pDataBuffer = pcm[channel][x];
++pDataBuffer;
}
}
}
return true;
}

View File

@ -39,29 +39,28 @@
using namespace musik::core::audio; using namespace musik::core::audio;
class OGGDecoder : public IDecoder class VorbisDecoder : public IDecoder
{ {
public: public:
OGGDecoder(); VorbisDecoder();
~OGGDecoder(); ~VorbisDecoder();
public: public:
virtual void Destroy(); virtual void Destroy();
virtual double SetPosition(double second,double totalLength); virtual double SetPosition(double second, double totalLength);
virtual bool GetBuffer(IBuffer *buffer); virtual bool GetBuffer(IBuffer *buffer);
virtual bool Open(musik::core::filestreams::IFileStream *fileStream); virtual bool Open(musik::core::filestreams::IFileStream *fileStream);
public: public:
// OGG callbacks /* libvorbis callbacks */
static size_t OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource); static size_t OggRead(void *buffer, size_t nofParts, size_t partSize, void *datasource);
static int OggSeek(void *datasource, ogg_int64_t offset, int whence); static int OggSeek(void *datasource, ogg_int64_t offset, int whence);
static long OggTell(void *datasource); static long OggTell(void *datasource);
static int OggClose(void *datasource); static int OggClose(void *datasource);
protected: protected:
musik::core::filestreams::IFileStream *fileStream; musik::core::filestreams::IFileStream *fileStream;
OggVorbis_File oggFile; OggVorbis_File oggFile;
ov_callbacks oggCallbacks; ov_callbacks oggCallbacks;
}; };

View File

@ -93,14 +93,14 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="OGGDecoder.cpp" /> <ClCompile Include="VorbisDecoder.cpp" />
<ClCompile Include="oggdecoder_plugin.cpp" /> <ClCompile Include="oggdecoder_plugin.cpp" />
<ClCompile Include="OggSourceSupplier.cpp" /> <ClCompile Include="OggDecoderFactory.cpp" />
<ClCompile Include="stdafx.cpp" /> <ClCompile Include="stdafx.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="OGGDecoder.h" /> <ClInclude Include="VorbisDecoder.h" />
<ClInclude Include="OggSourceSupplier.h" /> <ClInclude Include="OggDecoderFactory.h" />
<ClInclude Include="stdafx.h" /> <ClInclude Include="stdafx.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -38,7 +38,7 @@
#include <core/sdk/IPlugin.h> #include <core/sdk/IPlugin.h>
#include "OggSourceSupplier.h" #include "OggDecoderFactory.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return true; return true;
@ -57,5 +57,5 @@ extern "C" __declspec(dllexport) musik::core::IPlugin* GetPlugin() {
} }
extern "C" __declspec(dllexport) IDecoderFactory* GetDecoderFactory() { extern "C" __declspec(dllexport) IDecoderFactory* GetDecoderFactory() {
return new OggSourceSupplier(); return new OggDecoderFactory();
} }

View File

@ -33,7 +33,7 @@
#include "WaveOut.h" #include "WaveOut.h"
#define MAX_VOLUME 65535.0 #define MAX_VOLUME 0xFFFF
WaveOut::WaveOut() WaveOut::WaveOut()
: waveHandle(NULL) : waveHandle(NULL)
@ -51,7 +51,7 @@ WaveOut::~WaveOut(){
} }
} }
void WaveOut::Destroy(){ void WaveOut::Destroy() {
delete this; delete this;
} }
@ -66,7 +66,8 @@ void WaveOut::Resume() {
void WaveOut::SetVolume(double volume) { void WaveOut::SetVolume(double volume) {
if (this->waveHandle) { if (this->waveHandle) {
DWORD newVolume = (DWORD)(volume * MAX_VOLUME); DWORD newVolume = (DWORD)(volume * MAX_VOLUME);
waveOutSetVolume(this->waveHandle,newVolume); DWORD leftAndRight = (newVolume << 16) | newVolume;
waveOutSetVolume(this->waveHandle, leftAndRight);
} }
this->currentVolume = volume; this->currentVolume = volume;
@ -148,7 +149,7 @@ void WaveOut::SetFormat(IBuffer *buffer) {
this->currentSampleRate = buffer->SampleRate(); this->currentSampleRate = buffer->SampleRate();
/* format changed, kill the old output device */ /* format changed, kill the old output device */
if(this->waveHandle!=NULL) { if (this->waveHandle != NULL) {
waveOutClose(this->waveHandle); waveOutClose(this->waveHandle);
this->waveHandle = NULL; this->waveHandle = NULL;
} }
@ -156,44 +157,41 @@ void WaveOut::SetFormat(IBuffer *buffer) {
/* reset, and configure speaker output */ /* reset, and configure speaker output */
ZeroMemory(&this->waveFormat, sizeof(this->waveFormat)); ZeroMemory(&this->waveFormat, sizeof(this->waveFormat));
DWORD speakerconfig = 0; DWORD speakerConfig = 0;
switch (buffer->Channels()) { switch (buffer->Channels()) {
case 1: case 1:
speakerconfig = KSAUDIO_SPEAKER_MONO; speakerConfig = KSAUDIO_SPEAKER_MONO;
break; break;
case 2: case 2:
speakerconfig = KSAUDIO_SPEAKER_STEREO; speakerConfig = KSAUDIO_SPEAKER_STEREO;
break; break;
case 4: case 4:
speakerconfig = KSAUDIO_SPEAKER_QUAD; speakerConfig = KSAUDIO_SPEAKER_QUAD;
break; break;
case 5: case 5:
speakerconfig = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT); speakerConfig = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT);
break; break;
case 6: case 6:
speakerconfig = KSAUDIO_SPEAKER_5POINT1; speakerConfig = KSAUDIO_SPEAKER_5POINT1;
break; break;
} }
this->waveFormat.Format.cbSize = 22; this->waveFormat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
this->waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; this->waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
this->waveFormat.Format.nChannels = (WORD) buffer->Channels(); this->waveFormat.Format.nChannels = (WORD) buffer->Channels();
this->waveFormat.Format.wBitsPerSample = sizeof(float) * 8;
this->waveFormat.Format.nSamplesPerSec = (DWORD) buffer->SampleRate(); this->waveFormat.Format.nSamplesPerSec = (DWORD) buffer->SampleRate();
this->waveFormat.Format.wBitsPerSample = 32;
this->waveFormat.Format.nBlockAlign = int bytesPerSample = this->waveFormat.Format.wBitsPerSample / 8;
(this->waveFormat.Format.wBitsPerSample / 8) * this->waveFormat.Format.nChannels; this->waveFormat.Format.nBlockAlign = bytesPerSample * this->waveFormat.Format.nChannels;
this->waveFormat.Format.nAvgBytesPerSec = this->waveFormat.Format.nAvgBytesPerSec =
((this->waveFormat.Format.wBitsPerSample/8) * this->waveFormat.Format.nBlockAlign * this->waveFormat.Format.nSamplesPerSec;
this->waveFormat.Format.nChannels) *
this->waveFormat.Format.nSamplesPerSec; /* Compute using nBlkAlign * nSamp/Sec */
/* IMPORTANT NOTE: wValidBitsPerSample/wReserved/wSamplesPerBlock are a union, /* NOTE: wValidBitsPerSample/wReserved/wSamplesPerBlock are a union */
so don't set wReserved or wSamplesPerBlock to 0 after assigning wValidBitsPerSample. */ this->waveFormat.Samples.wValidBitsPerSample = this->waveFormat.Format.wBitsPerSample;
this->waveFormat.Samples.wValidBitsPerSample = 32; this->waveFormat.dwChannelMask = speakerConfig;
this->waveFormat.dwChannelMask = speakerconfig;
this->waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; this->waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
/* create the output device */ /* create the output device */

View File

@ -34,6 +34,8 @@
#include "WaveOutBuffer.h" #include "WaveOutBuffer.h"
#include "WaveOut.h" #include "WaveOut.h"
#include <iostream>
WaveOutBuffer::WaveOutBuffer(WaveOut *waveOut, IBuffer *buffer, IPlayer *player) WaveOutBuffer::WaveOutBuffer(WaveOut *waveOut, IBuffer *buffer, IPlayer *player)
: waveOut(waveOut) : waveOut(waveOut)
, buffer(buffer) , buffer(buffer)
@ -44,9 +46,9 @@ WaveOutBuffer::WaveOutBuffer(WaveOut *waveOut, IBuffer *buffer, IPlayer *player)
} }
void WaveOutBuffer::Initialize() { void WaveOutBuffer::Initialize() {
this->header.dwBufferLength = this->buffer->Samples() * this->buffer->Channels()*sizeof(float); this->header.dwBufferLength = this->buffer->Samples() * this->buffer->Channels() * sizeof(float);
this->header.lpData = (LPSTR)this->buffer->BufferPointer(); this->header.lpData = (LPSTR) this->buffer->BufferPointer();
this->header.dwUser = (DWORD_PTR)this; this->header.dwUser = (DWORD_PTR) this;
this->header.dwBytesRecorded = 0; this->header.dwBytesRecorded = 0;
this->header.dwFlags = 0; this->header.dwFlags = 0;
this->header.dwLoops = 0; this->header.dwLoops = 0;
@ -57,14 +59,13 @@ void WaveOutBuffer::Initialize() {
if (result != MMSYSERR_NOERROR) { if (result != MMSYSERR_NOERROR) {
throw; throw;
} }
this->header.dwFlags |= WHDR_DONE;
} }
void WaveOutBuffer::Destroy() { void WaveOutBuffer::Destroy() {
if (!this->destroyed) { if (!this->destroyed) {
if (this->waveOut->waveHandle && this->header.dwFlags & WHDR_PREPARED) { if (this->waveOut->waveHandle && this->header.dwFlags & WHDR_PREPARED) {
waveOutUnprepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR)); waveOutUnprepareHeader(this->waveOut->waveHandle, &this->header, sizeof(WAVEHDR));
this->header.dwFlags = WHDR_DONE;
} }
this->player->Notify(); this->player->Notify();

View File

@ -186,7 +186,9 @@ void Player::ThreadLoop() {
} }
} }
/* TODO: why do we release buffers from the output here?? */ /* TODO: why do we release buffers from the output here?? this appears to be
an old hack to fix up some WaveOut issues where pending buffers need to be
released before the device can be closed. we can probably remove this now. */
this->output->ReleaseBuffers(); this->output->ReleaseBuffers();
BufferPtr buffer; BufferPtr buffer;
@ -207,6 +209,8 @@ void Player::ThreadLoop() {
} }
} }
/* TODO CLEAN UP AND DOCUMENT */
/* if we have a buffer available, let's try to send it to the output device */ /* if we have a buffer available, let's try to send it to the output device */
if (buffer) { if (buffer) {
{ {
@ -232,7 +236,7 @@ void Player::ThreadLoop() {
} }
} }
} }
else{ else {
// Buffer send to output // Buffer send to output
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
if(!this->bufferQueue.empty()) { if(!this->bufferQueue.empty()) {
@ -246,6 +250,8 @@ void Player::ThreadLoop() {
} }
} }
/* END TODO CLEAN UP AND DOCUMENT */
/* if we're unable to obtain a buffer, it means we're out of data and the /* if we're unable to obtain a buffer, it means we're out of data and the
player is finished. terminate the thread. */ player is finished. terminate the thread. */
else { else {

View File

@ -38,6 +38,7 @@
#include <core/PluginFactory.h> #include <core/PluginFactory.h>
using namespace musik::core::audio; using namespace musik::core::audio;
using musik::core::PluginFactory;
Stream::Stream(unsigned int options) Stream::Stream(unsigned int options)
:preferedBufferSampleSize(4096) :preferedBufferSampleSize(4096)
@ -45,12 +46,9 @@ Stream::Stream(unsigned int options)
,decoderSampleRate(0) ,decoderSampleRate(0)
,decoderSamplePosition(0) ,decoderSamplePosition(0)
{ {
// Get all DSPs if((this->options&NoDSP) == 0){
// TODO: fixing PluginFactory typedef PluginFactory::DestroyDeleter<IDSP> Deleter;
if( (this->options&NoDSP)==0){ this->dsps = PluginFactory::Instance().QueryInterface<IDSP, Deleter>("GetDSP");
this->dsps = musik::core::PluginFactory::Instance().QueryInterface<
IDSP,
musik::core::PluginFactory::DestroyDeleter<IDSP> >("GetDSP");
} }
} }
@ -62,9 +60,7 @@ StreamPtr Stream::Create(unsigned int options){
return StreamPtr(new Stream(options)); return StreamPtr(new Stream(options));
} }
BufferPtr Stream::NextBuffer(){ BufferPtr Stream::NextBuffer() {
// Decode a new buffer
return this->GetNextBuffer(); return this->GetNextBuffer();
} }
@ -82,110 +78,104 @@ double Stream::SetPosition(double seconds){
} }
return newPosition; return newPosition;
} }
/*
void Stream::SetMaxCacheLength(double seconds){
}
void Stream::SetPreferedBufferSampleSize(long samples){ bool Stream::OpenStream(utfstring uri) {
} /* use our file stream abstraction to open the data at the
*/ specified URI */
bool Stream::OpenStream(utfstring uri){ this->fileStream = musik::core::filestreams::Factory::OpenFile(uri.c_str());
if (!this->fileStream) {
// Open the filestream
this->fileStream = musik::core::filestreams::Factory::OpenFile(uri.c_str());
if(!this->fileStream){
return false; return false;
} }
// Look up what DecoderFactory to use /* find a DecoderFactory we can use for this type of data*/
StreamHelper::DecoderFactoryPtr decoderFactory; StreamHelper::DecoderFactoryPtr decoderFactory;
for(StreamHelper::DecoderFactories::iterator decoderFactoryIt=Stream::Helper()->decoderFactories.begin();decoderFactoryIt!=Stream::Helper()->decoderFactories.end() && !decoderFactory;++decoderFactoryIt){ StreamHelper::DecoderFactories::iterator factories = Stream::Helper()->decoderFactories.begin();
if( (*decoderFactoryIt)->CanHandle(this->fileStream->Type())){ StreamHelper::DecoderFactories::iterator end = Stream::Helper()->decoderFactories.end();
decoderFactory = (*decoderFactoryIt);
for( ; factories != end && !decoderFactory; ++factories) {
if((*factories)->CanHandle(this->fileStream->Type())){
decoderFactory = (*factories);
} }
} }
if(!decoderFactory){ if (!decoderFactory) {
// We have failed to get a working decoderFactory /* nothing can decode this type of file */
return false; return false;
} }
IDecoder *decoder = decoderFactory->CreateDecoder();
// Create the decoder if (!decoder) {
IDecoder *decoderPtr = decoderFactory->CreateDecoder(); /* shouldn't ever happen, the factory said it can handle this file */
if(!decoderPtr){
return false; return false;
} }
typedef PluginFactory::DestroyDeleter<IDecoder> Deleter;
// Open the decoder /* ask the decoder to open the data stream. if it returns true we're
typedef musik::core::PluginFactory::DestroyDeleter<IDecoder> IDecoderDeleter; good to start pulling data out of it! */
this->decoder.reset(decoderPtr,IDecoderDeleter()); this->decoder.reset(decoder, Deleter());
if( !this->decoder->Open(this->fileStream.get()) ){ if (!this->decoder->Open(this->fileStream.get())) {
return false; return false;
} }
return true; return true;
} }
BufferPtr Stream::GetNextDecoderBuffer(){ BufferPtr Stream::GetNextDecoderBuffer() {
// First get a buffer /* get a spare buffer, then ask the decoder for some data */
BufferPtr buffer = this->NewBuffer(); BufferPtr buffer = this->NewBuffer();
if(!this->decoder->GetBuffer(buffer.get())) {
// Get the buffer from the decoder
if(!this->decoder->GetBuffer(buffer.get())){
// Nothing to decode left, return a empty buffer
return BufferPtr(); return BufferPtr();
} }
// We need to save the decoders samplerate to be able to calculate the current time-position /* remember the samplerate so we can calculate the current time-position */
if(!this->decoderSampleRate){ if(!this->decoderSampleRate){
this->decoderSampleRate = buffer->SampleRate(); this->decoderSampleRate = buffer->SampleRate();
} }
// Calculate the current sample position /* calculate the current offset, in samples */
this->decoderSamplePosition += buffer->Samples(); this->decoderSamplePosition += buffer->Samples();
// Save the position (seconds) in the buffer /* calculate the position (seconds) in the buffer */
buffer->position = ((double)this->decoderSamplePosition)/((double)this->decoderSampleRate); buffer->position = ((double)this->decoderSamplePosition) / ((double)this->decoderSampleRate);
return buffer; return buffer;
} }
BufferPtr Stream::GetNextBuffer(){ BufferPtr Stream::GetNextBuffer() {
// First get the next decoded buffer /* ask the decoder for the next buffer */
BufferPtr currentBuffer = this->GetNextDecoderBuffer(); BufferPtr currentBuffer = this->GetNextDecoderBuffer();
if(currentBuffer){ if(currentBuffer) {
///////////////////////////////////////////// /* try to fill the buffer to its optimal size; if the decoder didn't return
// Lets check if the buffer is too small a full buffer, ask it for some more data. */
bool moreBuffers(true); bool moreBuffers = true;
while(currentBuffer->Samples()<this->preferedBufferSampleSize && moreBuffers){ while (currentBuffer->Samples() < this->preferedBufferSampleSize && moreBuffers) {
BufferPtr appendBuffer = this->GetNextDecoderBuffer(); BufferPtr bufferToAppend = this->GetNextDecoderBuffer();
if(appendBuffer){ if (bufferToAppend) {
currentBuffer->Append(appendBuffer); currentBuffer->Append(bufferToAppend);
this->DeleteBuffer(appendBuffer); this->DeleteBuffer(bufferToAppend);
}else{ }
else {
moreBuffers = false; moreBuffers = false;
} }
} }
/////////////////////////////////////////////
BufferPtr oldBuffer = this->NewBuffer(); /* let DSP plugins process the buffer */
if (this->dsps.size() > 0) {
BufferPtr oldBuffer = this->NewBuffer();
// Now lets loop through all DSP plugins for (Dsps::iterator dsp = this->dsps.begin(); dsp != this->dsps.end(); ++dsp) {
for(Dsps::iterator dsp=this->dsps.begin();dsp!=this->dsps.end();++dsp){ oldBuffer->CopyFormat(currentBuffer);
oldBuffer->CopyFormat(currentBuffer); oldBuffer->position = currentBuffer->position;
oldBuffer->position = currentBuffer->position;
if( (*dsp)->ProcessBuffers(currentBuffer.get(),oldBuffer.get()) ){ if ((*dsp)->ProcessBuffers(currentBuffer.get(), oldBuffer.get())) {
// Success in processing DSP, swap the buffers currentBuffer.swap(oldBuffer);
currentBuffer.swap(oldBuffer); }
} }
}
this->DeleteBuffer(oldBuffer); this->DeleteBuffer(oldBuffer);
}
} }
@ -203,7 +193,7 @@ BufferPtr Stream::NewBuffer(){
return buffer; return buffer;
} }
void Stream::DeleteBuffer(BufferPtr oldBuffer){ void Stream::DeleteBuffer(BufferPtr oldBuffer) {
this->availableBuffers.push_back(oldBuffer); this->availableBuffers.push_back(oldBuffer);
} }
@ -215,20 +205,22 @@ Stream::StreamHelperPtr Stream::Helper(){
return helper; return helper;
} }
Stream::StreamHelper::StreamHelper(){ Stream::StreamHelper::StreamHelper() {
// Look up all DecoderFactories PluginFactory::DestroyDeleter<IDecoderFactory> typedef Deleter;
this->decoderFactories = musik::core::PluginFactory::Instance().QueryInterface<
IDecoderFactory, this->decoderFactories = PluginFactory::Instance()
musik::core::PluginFactory::DestroyDeleter<IDecoderFactory> >("GetDecoderFactory"); .QueryInterface<IDecoderFactory, Deleter>("GetDecoderFactory");
} }
double Stream::DecoderProgress(){ double Stream::DecoderProgress() {
if(this->fileStream){ if (this->fileStream) {
long fileSize = this->fileStream->Filesize(); long fileSize = this->fileStream->Filesize();
long filePosition = this->fileStream->Position(); long filePosition = this->fileStream->Position();
if(fileSize && filePosition){
return ((double)filePosition)/((double)fileSize); if (fileSize && filePosition) {
return ((double) filePosition) / ((double) fileSize);
} }
} }
return 0; return 0;
} }

View File

@ -41,8 +41,6 @@
#include <core/Common.h> #include <core/Common.h>
#include <core/config_filesystem.h> #include <core/config_filesystem.h>
//////////////////////////////////////////////////////////////////////////////
#ifdef UTF_WIDECHAR #ifdef UTF_WIDECHAR
#define UTFFopen _wfopen #define UTFFopen _wfopen
typedef fpos_t stdioPositionType; typedef fpos_t stdioPositionType;
@ -51,11 +49,8 @@ typedef fpos_t stdioPositionType;
typedef fpos_t stdioPositionType; typedef fpos_t stdioPositionType;
#endif #endif
//////////////////////////////////////////////////////////////////////////////
using namespace musik::core::filestreams; using namespace musik::core::filestreams;
//////////////////////////////////////////////////////////////////////////////
LocalFileStream::LocalFileStream() LocalFileStream::LocalFileStream()
: file(NULL) : file(NULL)
, filesize(-1) , filesize(-1)
@ -82,10 +77,10 @@ bool LocalFileStream::Open(const utfchar *filename,unsigned int options){
std::cerr << "File not a regular file" << std::endl; std::cerr << "File not a regular file" << std::endl;
} }
this->filesize = (long)boost::filesystem::file_size(file); this->filesize = (long)boost::filesystem::file_size(file);
this->extension = file.extension().wstring(); this->extension = file.extension().wstring();
this->file = UTFFopen(filename,UTF("rb")); this->file = UTFFopen(filename,UTF("rb"));
this->fd = new boost::iostreams::file_descriptor(file); this->fd = new boost::iostreams::file_descriptor(file);
this->fileStream = new boost::iostreams::stream<boost::iostreams::file_descriptor>(*this->fd); this->fileStream = new boost::iostreams::stream<boost::iostreams::file_descriptor>(*this->fd);
this->fileStream->exceptions(std::ios_base::eofbit | std::ios_base::failbit | std::ios_base::badbit); this->fileStream->exceptions(std::ios_base::eofbit | std::ios_base::failbit | std::ios_base::badbit);
@ -128,8 +123,6 @@ PositionType LocalFileStream::Read(void* buffer,PositionType readBytes) {
} }
bool LocalFileStream::SetPosition(PositionType position) { bool LocalFileStream::SetPosition(PositionType position) {
/*stdioPositionType newPosition = (stdioPositionType)position;
return fsetpos(this->file,&newPosition)==0;*/
try { try {
this->fileStream->clear(); this->fileStream->clear();
this->fileStream->seekg(position); this->fileStream->seekg(position);
@ -142,16 +135,10 @@ bool LocalFileStream::SetPosition(PositionType position) {
} }
PositionType LocalFileStream::Position() { PositionType LocalFileStream::Position() {
/*stdioPositionType currentPosition(0);
if(fgetpos(this->file,&currentPosition)==0){
return (PositionType)currentPosition;
}
return -1;*/
return this->fileStream->tellg(); return this->fileStream->tellg();
} }
bool LocalFileStream::Eof() { bool LocalFileStream::Eof() {
//return feof(this->file)!=0;
return this->fileStream->eof(); return this->fileStream->eof();
} }

View File

@ -37,17 +37,12 @@
#pragma once #pragma once
namespace musik { namespace core { namespace musik { namespace core {
class IPlugin{
class IPlugin{
protected:
virtual ~IPlugin() {};
public: public:
virtual void Destroy() =0; virtual void Destroy() = 0;
virtual const utfchar* Name() = 0;
virtual const utfchar* Name()=0; virtual const utfchar* Version() = 0;
virtual const utfchar* Version()=0; virtual const utfchar* Author() = 0;
virtual const utfchar* Author()=0;
}; };
} } } }

View File

@ -0,0 +1,58 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace http{
//////////////////////////////////////////////////////////////////////////////
class IRequestParser{
public:
virtual const char* Attribute(const char* key)=0;
virtual const char* Path()=0;
virtual const char* SubPath(int position)=0;
};
//////////////////////////////////////////////////////////////////////////////
} } } // musik::core:http
//////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,61 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 "IResponder.h"
#include "IRequestParser.h"
#include "ITrack.h"
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace http{
//////////////////////////////////////////////////////////////////////////////
class IRequestPlugin{
public:
virtual void Destroy()=0;
virtual const char* WatchPath()=0;
virtual void Execute(musik::core::http::IResponder* responder,musik::core::http::IRequestParser* request,musik::core::ITrack* track)=0;
};
//////////////////////////////////////////////////////////////////////////////
} } }
//////////////////////////////////////////////////////////////////////////////

58
src/core/sdk/IResponder.h Normal file
View File

@ -0,0 +1,58 @@
//////////////////////////////////////////////////////////////////////////////
//
// License Agreement:
//
// The following are Copyright © 2008, Daniel Önnerby
//
// 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 <stddef.h>
//////////////////////////////////////////////////////////////////////////////
namespace musik{ namespace core{ namespace http{
//////////////////////////////////////////////////////////////////////////////
class IResponder{
public:
virtual void SendContent(const char* buffer,const std::size_t bufferSize)=0;
virtual bool Exited()=0;
};
//////////////////////////////////////////////////////////////////////////////
} } }
//////////////////////////////////////////////////////////////////////////////