mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-21 03:41:10 +00:00
Initial implementation of sndio output driver for BSD.
This commit is contained in:
parent
402008e148
commit
650f349710
@ -4,4 +4,4 @@ set (sndioout_SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_library(sndioout SHARED ${sndioout_SOURCES})
|
add_library(sndioout SHARED ${sndioout_SOURCES})
|
||||||
target_link_libraries(sndioout ${musikcube_LINK_LIBS})
|
target_link_libraries(sndioout ${musikcube_LINK_LIBS} sndio)
|
||||||
|
@ -35,16 +35,32 @@
|
|||||||
#include "SndioOut.h"
|
#include "SndioOut.h"
|
||||||
|
|
||||||
#include <core/sdk/constants.h>
|
#include <core/sdk/constants.h>
|
||||||
#include <core/sdk/IPreferences.h>
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define DUMPSTATE() std::cerr << "handle=" << this->handle << " state=" << this->state << "\n";
|
||||||
|
#define ERROR(str) std::cerr << "SndioOut Error: " << str << "\n";
|
||||||
|
#define INFO(str) std::cerr << "SndioOut Info: " << str << "\n";
|
||||||
|
#define LOCK() std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||||
|
|
||||||
|
|
||||||
using namespace musik::core::sdk;
|
using namespace musik::core::sdk;
|
||||||
|
|
||||||
SndioOut::SndioOut() {
|
SndioOut::SndioOut() {
|
||||||
this->volume = 1.0f;
|
this->volume = 1.0f;
|
||||||
this->state = StateStopped;
|
this->state = StateStopped;
|
||||||
|
this->handle = nullptr;
|
||||||
|
this->buffer = nullptr;
|
||||||
|
this->bufferSamples = 0;
|
||||||
|
this->latency = 0.0;
|
||||||
|
this->pars = { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
SndioOut::~SndioOut() {
|
SndioOut::~SndioOut() {
|
||||||
|
this->Stop();
|
||||||
|
delete[] this->buffer;
|
||||||
|
this->bufferSamples = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::Release() {
|
void SndioOut::Release() {
|
||||||
@ -52,15 +68,41 @@ void SndioOut::Release() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::Pause() {
|
void SndioOut::Pause() {
|
||||||
this->state = StatePaused;
|
INFO("Pause()")
|
||||||
|
LOCK()
|
||||||
|
if (this->handle && this->state == StatePlaying) {
|
||||||
|
if (!sio_stop(this->handle)) {
|
||||||
|
ERROR("pause failed")
|
||||||
|
this->Stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
INFO("paused")
|
||||||
|
this->state = StatePaused;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::Resume() {
|
void SndioOut::Resume() {
|
||||||
|
INFO("Resume()")
|
||||||
|
LOCK()
|
||||||
|
if (this->handle && this->state == StatePaused) {
|
||||||
|
if (!sio_start(this->handle)) {
|
||||||
|
ERROR("resume failed")
|
||||||
|
this->Stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
INFO("playing")
|
||||||
|
}
|
||||||
|
}
|
||||||
this->state = StatePlaying;
|
this->state = StatePlaying;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::SetVolume(double volume) {
|
void SndioOut::SetVolume(double volume) {
|
||||||
this->volume = volume;
|
this->volume = volume;
|
||||||
|
|
||||||
|
if (this->handle) {
|
||||||
|
sio_setvol(this->handle, lround(volume * SIO_MAXVOL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double SndioOut::GetVolume() {
|
double SndioOut::GetVolume() {
|
||||||
@ -68,11 +110,24 @@ double SndioOut::GetVolume() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::Stop() {
|
void SndioOut::Stop() {
|
||||||
|
INFO("Stop()")
|
||||||
|
LOCK()
|
||||||
|
if (this->handle) {
|
||||||
|
sio_close(this->handle);
|
||||||
|
}
|
||||||
|
this->handle = nullptr;
|
||||||
|
this->pars = { 0 };
|
||||||
|
this->latency = 0;
|
||||||
this->state = StateStopped;
|
this->state = StateStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndioOut::Drain() {
|
void SndioOut::Drain() {
|
||||||
|
INFO("Drain()")
|
||||||
|
LOCK()
|
||||||
|
if (this->handle) {
|
||||||
|
sio_stop(this->handle);
|
||||||
|
sio_start(this->handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IDeviceList* SndioOut::GetDeviceList() {
|
IDeviceList* SndioOut::GetDeviceList() {
|
||||||
@ -87,14 +142,103 @@ IDevice* SndioOut::GetDefaultDevice() {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SndioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
bool SndioOut::InitDevice(IBuffer *buffer) {
|
||||||
if (this->state == StatePaused) {
|
this->handle = sio_open(nullptr, SIO_PLAY, 0);
|
||||||
return OutputInvalidState;
|
|
||||||
|
if (this->handle == nullptr) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int n = 1; bool littleEndian = *(char *) &n == 1;
|
||||||
|
|
||||||
|
sio_initpar(&this->pars);
|
||||||
|
this->pars.pchan = buffer->Channels();
|
||||||
|
this->pars.rate = buffer->SampleRate();
|
||||||
|
this->pars.sig = 1;
|
||||||
|
this->pars.le = !!littleEndian;
|
||||||
|
this->pars.bits = 16;
|
||||||
|
|
||||||
|
/* stolen from cmus; presumeably they've already iterated
|
||||||
|
this value and it should be a reasonable default */
|
||||||
|
this->pars.appbufsz = pars.rate * 300 / 1000;
|
||||||
|
|
||||||
|
if (!sio_setpar(this->handle, &this->pars)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sio_getpar(this->handle, &this->pars)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sio_start(this->handle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->latency = (double)
|
||||||
|
this->pars.bufsz /
|
||||||
|
this->pars.pchan /
|
||||||
|
this->pars.rate;
|
||||||
|
|
||||||
|
this->SetVolume(this->volume);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SndioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||||
|
//DUMPSTATE()
|
||||||
|
|
||||||
|
if (this->handle == nullptr) {
|
||||||
|
INFO("initializing device...");
|
||||||
|
if (!this->InitDevice(buffer)) {
|
||||||
|
ERROR("failed to initialize device")
|
||||||
|
return OutputInvalidState;
|
||||||
|
}
|
||||||
|
INFO("initialized");
|
||||||
|
this->state = StateStopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->handle || this->state == StatePaused) {
|
||||||
|
return OutputInvalidState;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->state = StatePlaying;
|
||||||
|
|
||||||
|
/* convert to 16-bit PCM */
|
||||||
|
long samples = buffer->Samples();
|
||||||
|
if (!this->buffer || samples > this->bufferSamples) {
|
||||||
|
delete[] this->buffer;
|
||||||
|
this->buffer = new short[samples];
|
||||||
|
this->bufferSamples = samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
float* src = buffer->BufferPointer();
|
||||||
|
short* dst = this->buffer;
|
||||||
|
for (long i = 0; i < samples; i++) {
|
||||||
|
*dst = (short)((*src) * SHRT_MAX);
|
||||||
|
++dst; ++src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the entire output buffer. this may require multiple passes;
|
||||||
|
that's ok, just loop until we're done */
|
||||||
|
char* data = (char*) this->buffer;
|
||||||
|
size_t dataLength = samples * sizeof(short);
|
||||||
|
size_t totalWritten = 0;
|
||||||
|
|
||||||
|
while (totalWritten < dataLength && this->state == StatePlaying) {
|
||||||
|
size_t remaining = dataLength - totalWritten;
|
||||||
|
size_t written = 0;
|
||||||
|
{
|
||||||
|
LOCK()
|
||||||
|
written = sio_write(this->handle, data, remaining);
|
||||||
|
}
|
||||||
|
totalWritten += written;
|
||||||
|
data += written;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider->OnBufferProcessed(buffer);
|
||||||
return OutputBufferWritten;
|
return OutputBufferWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
double SndioOut::Latency() {
|
double SndioOut::Latency() {
|
||||||
return 0.0;
|
return this->latency;
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/sdk/IOutput.h>
|
#include <core/sdk/IOutput.h>
|
||||||
|
#include <sndio.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
using namespace musik::core::sdk;
|
using namespace musik::core::sdk;
|
||||||
|
|
||||||
@ -60,6 +62,8 @@ class SndioOut : public IOutput {
|
|||||||
virtual IDevice* GetDefaultDevice() override;
|
virtual IDevice* GetDefaultDevice() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool InitDevice(IBuffer *buffer);
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
StateStopped,
|
StateStopped,
|
||||||
StatePaused,
|
StatePaused,
|
||||||
@ -68,4 +72,10 @@ class SndioOut : public IOutput {
|
|||||||
|
|
||||||
State state;
|
State state;
|
||||||
double volume;
|
double volume;
|
||||||
|
sio_hdl* handle;
|
||||||
|
sio_par pars;
|
||||||
|
short* buffer;
|
||||||
|
long bufferSamples;
|
||||||
|
double latency;
|
||||||
|
std::recursive_mutex mutex;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user