1
0
mirror of https://github.com/clangen/musikcube.git synced 2025-02-19 15:40:50 +00:00

Initial implementation of sndio output driver for BSD.

This commit is contained in:
casey langen 2017-12-21 12:46:36 +00:00
parent 402008e148
commit 650f349710
3 changed files with 163 additions and 9 deletions

@ -4,4 +4,4 @@ set (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 <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;
SndioOut::SndioOut() {
SndioOut::SndioOut() {
this->volume = 1.0f;
this->state = StateStopped;
this->handle = nullptr;
this->buffer = nullptr;
this->bufferSamples = 0;
this->latency = 0.0;
this->pars = { 0 };
}
SndioOut::~SndioOut() {
this->Stop();
delete[] this->buffer;
this->bufferSamples = 0;
}
void SndioOut::Release() {
@ -52,15 +68,41 @@ void SndioOut::Release() {
}
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() {
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;
}
void SndioOut::SetVolume(double volume) {
this->volume = volume;
if (this->handle) {
sio_setvol(this->handle, lround(volume * SIO_MAXVOL));
}
}
double SndioOut::GetVolume() {
@ -68,11 +110,24 @@ double SndioOut::GetVolume() {
}
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;
}
void SndioOut::Drain() {
INFO("Drain()")
LOCK()
if (this->handle) {
sio_stop(this->handle);
sio_start(this->handle);
}
}
IDeviceList* SndioOut::GetDeviceList() {
@ -87,14 +142,103 @@ IDevice* SndioOut::GetDefaultDevice() {
return nullptr;
}
int SndioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
if (this->state == StatePaused) {
return OutputInvalidState;
bool SndioOut::InitDevice(IBuffer *buffer) {
this->handle = sio_open(nullptr, SIO_PLAY, 0);
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;
}
double SndioOut::Latency() {
return 0.0;
return this->latency;
}

@ -34,6 +34,8 @@
#pragma once
#include <core/sdk/IOutput.h>
#include <sndio.h>
#include <mutex>
using namespace musik::core::sdk;
@ -60,6 +62,8 @@ class SndioOut : public IOutput {
virtual IDevice* GetDefaultDevice() override;
private:
bool InitDevice(IBuffer *buffer);
enum State {
StateStopped,
StatePaused,
@ -68,4 +72,10 @@ class SndioOut : public IOutput {
State state;
double volume;
sio_hdl* handle;
sio_par pars;
short* buffer;
long bufferSamples;
double latency;
std::recursive_mutex mutex;
};