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:
parent
402008e148
commit
650f349710
src/plugins/sndioout
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user