mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-06 12:39:54 +00:00
More scaffholding.
This commit is contained in:
parent
dee5150c2e
commit
beb95e55a5
@ -155,10 +155,8 @@ add_dependencies(musikcubed musikcube)
|
|||||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
add_subdirectory(src/plugins/alsaout)
|
add_subdirectory(src/plugins/alsaout)
|
||||||
add_subdirectory(src/plugins/pulseout)
|
add_subdirectory(src/plugins/pulseout)
|
||||||
if (${ENABLE_PIPEWIRE} MATCHES "true")
|
add_subdirectory(src/plugins/pipewireout)
|
||||||
add_subdirectory(src/plugins/pipewireout)
|
add_dependencies(musikcube pipewireout)
|
||||||
add_dependencies(musikcube pipewireout)
|
|
||||||
endif()
|
|
||||||
if (${ENABLE_MPRIS} MATCHES "true")
|
if (${ENABLE_MPRIS} MATCHES "true")
|
||||||
add_subdirectory(src/plugins/mpris)
|
add_subdirectory(src/plugins/mpris)
|
||||||
add_dependencies(musikcube mpris)
|
add_dependencies(musikcube mpris)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <musikcore/sdk/IPlugin.h>
|
#include <musikcore/sdk/IPlugin.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <systemd/sd-bus.h>
|
#include <systemd/sd-bus.h>
|
||||||
|
@ -5,5 +5,8 @@ set (pipewireout_SOURCES
|
|||||||
|
|
||||||
message(STATUS "[pipewireout] plugin enabled")
|
message(STATUS "[pipewireout] plugin enabled")
|
||||||
|
|
||||||
|
include_directories("/usr/include/spa-0.2")
|
||||||
|
include_directories("/usr/include/pipewire-0.3")
|
||||||
|
|
||||||
add_library(pipewireout SHARED ${pipewireout_SOURCES})
|
add_library(pipewireout SHARED ${pipewireout_SOURCES})
|
||||||
target_link_libraries(pipewireout ${musikcube_LINK_LIBS} pipewire-0.3)
|
target_link_libraries(pipewireout ${musikcube_LINK_LIBS} pipewire-0.3)
|
||||||
|
@ -42,6 +42,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
constexpr size_t MAX_BUFFERS = 16;
|
||||||
|
|
||||||
static IPreferences* prefs = nullptr;
|
static IPreferences* prefs = nullptr;
|
||||||
|
|
||||||
extern "C" void SetPreferences(IPreferences* prefs) {
|
extern "C" void SetPreferences(IPreferences* prefs) {
|
||||||
@ -53,26 +55,70 @@ extern "C" musik::core::sdk::ISchema* GetSchema() {
|
|||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onStreamStageChanged(
|
void PipeWireOut::OnStreamStateChanged(void* data, enum pw_stream_state old, enum pw_stream_state state, const char* error) {
|
||||||
void *data,
|
|
||||||
pw_stream_state old,
|
|
||||||
pw_stream_state state,
|
|
||||||
const char *error)
|
|
||||||
{
|
|
||||||
std::cerr << "[PipeWire] state changed from " << old << " to " << state << "\n";
|
std::cerr << "[PipeWire] state changed from " << old << " to " << state << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onStreamProcess(void *userdata) {
|
void PipeWireOut::OnStreamProcess(void* data) {
|
||||||
std::cerr << "clclcl onProcess\n";
|
PipeWireOut* output = static_cast<PipeWireOut*>(data);
|
||||||
|
|
||||||
|
struct pw_buffer* pwBuffer;
|
||||||
|
|
||||||
|
if ((pwBuffer = pw_stream_dequeue_buffer(output->pwStream)) == nullptr) {
|
||||||
|
std::cerr << "[PipeWire] no more output buffers available to fill\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferContext* outputBufferContext = nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(output->mutex);
|
||||||
|
if (output->buffers.empty()) {
|
||||||
|
std::cerr << "[PipeWire] no more input buffers available\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outputBufferContext = output->buffers.front();
|
||||||
|
output->buffers.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spa_buffer* spaBuffer = pwBuffer->buffer;
|
||||||
|
auto& outBufferData = spaBuffer->datas[0];
|
||||||
|
void* outBufferPtr = outBufferData.data;
|
||||||
|
uint32_t outBufferSize = outBufferData.maxsize;
|
||||||
|
void* inBufferPtr = outputBufferContext->buffer->BufferPointer() ;
|
||||||
|
uint32_t inBufferSize = (uint32_t) outputBufferContext->buffer->Bytes();
|
||||||
|
uint32_t frameSize = sizeof(float) * outputBufferContext->buffer->Channels();
|
||||||
|
|
||||||
|
std::cerr << "[PipeWire] onProcess " << inBufferSize << " vs " << outBufferSize << "\n";
|
||||||
|
|
||||||
|
/* TODO: assumes dest size >= src size. need to do additional book keeping in
|
||||||
|
BufferContext to store offset if target isn't large enough, then push the
|
||||||
|
struct back */
|
||||||
|
|
||||||
|
if (inBufferSize > outBufferSize) {
|
||||||
|
std::cerr << "[PipeWire] onProcess output buffer too small\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(outBufferPtr, inBufferPtr, inBufferSize);
|
||||||
|
outBufferData.chunk->offset = 0;
|
||||||
|
outBufferData.chunk->stride = frameSize;
|
||||||
|
outBufferData.chunk->size = inBufferSize;
|
||||||
|
pwBuffer->size = inBufferSize / frameSize;
|
||||||
|
|
||||||
|
outputBufferContext->provider->OnBufferProcessed(outputBufferContext->buffer);
|
||||||
|
|
||||||
|
delete outputBufferContext;
|
||||||
|
|
||||||
|
pw_stream_queue_buffer(output->pwStream, pwBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pw_stream_events streamEvents = {
|
|
||||||
PW_VERSION_STREAM_EVENTS,
|
|
||||||
.process = onStreamProcess,
|
|
||||||
.state_changed = onStreamStageChanged
|
|
||||||
};
|
|
||||||
|
|
||||||
PipeWireOut::PipeWireOut() {
|
PipeWireOut::PipeWireOut() {
|
||||||
|
this->pwStreamEvents = {
|
||||||
|
PW_VERSION_STREAM_EVENTS,
|
||||||
|
};
|
||||||
|
this->pwStreamEvents.state_changed = PipeWireOut::OnStreamStateChanged;
|
||||||
|
this->pwStreamEvents.process = OnStreamProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
PipeWireOut::~PipeWireOut() {
|
PipeWireOut::~PipeWireOut() {
|
||||||
@ -158,25 +204,32 @@ bool PipeWireOut::StartPipeWire(IBuffer* buffer) {
|
|||||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||||
PW_KEY_MEDIA_ROLE, "Music",
|
PW_KEY_MEDIA_ROLE, "Music",
|
||||||
NULL),
|
NULL),
|
||||||
&streamEvents,
|
&this->pwStreamEvents,
|
||||||
this);
|
this);
|
||||||
|
|
||||||
if (this->pwStream) {
|
if (this->pwStream) {
|
||||||
uint8_t intBuffer[1024];
|
uint8_t intBuffer[1024];
|
||||||
|
|
||||||
spa_pod_builder builder =
|
spa_pod_builder builder =
|
||||||
SPA_POD_BUILDER_INIT(intBuffer, sizeof(intBuffer));
|
SPA_POD_BUILDER_INIT(intBuffer, sizeof(intBuffer));
|
||||||
|
|
||||||
const spa_pod *params[1];
|
const spa_pod *params[1];
|
||||||
|
|
||||||
spa_audio_info_raw audioInfo = SPA_AUDIO_INFO_RAW_INIT(
|
spa_audio_info_raw audioInfo;
|
||||||
.format = SPA_AUDIO_FORMAT_F32,
|
spa_zero(audioInfo);
|
||||||
.channels = (uint32_t) buffer->Channels(),
|
audioInfo.flags = 0;
|
||||||
.rate = (uint32_t) buffer->SampleRate());
|
audioInfo.format = SPA_AUDIO_FORMAT_F32;
|
||||||
|
audioInfo.channels = (uint32_t) buffer->Channels();
|
||||||
|
audioInfo.rate = (uint32_t) buffer->SampleRate();
|
||||||
|
|
||||||
params[0] = spa_format_audio_raw_build(
|
params[0] = spa_format_audio_raw_build(
|
||||||
&builder, SPA_PARAM_EnumFormat, &audioInfo);
|
&builder, SPA_PARAM_EnumFormat, &audioInfo);
|
||||||
|
|
||||||
|
if (!params[0]) {
|
||||||
|
std::cerr << "[PipeWire] failed to create audio format\n";
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
result = pw_stream_connect(
|
result = pw_stream_connect(
|
||||||
this->pwStream,
|
this->pwStream,
|
||||||
PW_DIRECTION_OUTPUT,
|
PW_DIRECTION_OUTPUT,
|
||||||
@ -211,18 +264,18 @@ OutputState PipeWireOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cerr << "clclcl here\n";
|
if (this->state != State::Playing) {
|
||||||
|
|
||||||
/* TODO: if buffer format changes, drain, then update the params! */
|
|
||||||
|
|
||||||
if (this->state == State::Paused) {
|
|
||||||
return OutputState::InvalidState;
|
return OutputState::InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* order of operations matters, otherwise overflow. */
|
{
|
||||||
int micros = ((buffer->Samples() * 1000) / buffer->SampleRate() * 1000) / buffer->Channels();
|
std::unique_lock<std::recursive_mutex> lock(this->mutex);
|
||||||
usleep((long)((float) micros));
|
if (this->buffers.size() >= MAX_BUFFERS) {
|
||||||
provider->OnBufferProcessed(buffer);
|
return OutputState::BufferFull;
|
||||||
|
}
|
||||||
|
this->buffers.push_back(new BufferContext(buffer, provider));
|
||||||
|
}
|
||||||
|
|
||||||
return OutputState::BufferWritten;
|
return OutputState::BufferWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_set>
|
#include <deque>
|
||||||
|
|
||||||
using namespace musik::core::sdk;
|
using namespace musik::core::sdk;
|
||||||
|
|
||||||
@ -69,8 +69,19 @@ class PipeWireOut : public IOutput {
|
|||||||
bool StartPipeWire(IBuffer* buffer);
|
bool StartPipeWire(IBuffer* buffer);
|
||||||
void StopPipeWire();
|
void StopPipeWire();
|
||||||
|
|
||||||
struct BufferState {
|
static void OnStreamStateChanged(
|
||||||
BufferState(IBuffer* buffer, IBufferProvider* provider) {
|
void* userdata,
|
||||||
|
enum pw_stream_state old,
|
||||||
|
enum pw_stream_state state,
|
||||||
|
const char* error);
|
||||||
|
|
||||||
|
static void OnStreamProcess(void* userdata);
|
||||||
|
|
||||||
|
struct BufferContext {
|
||||||
|
BufferContext() {
|
||||||
|
this->buffer = nullptr; this->provider = nullptr;
|
||||||
|
}
|
||||||
|
BufferContext(IBuffer* buffer, IBufferProvider* provider) {
|
||||||
this->buffer = buffer; this->provider = provider;
|
this->buffer = buffer; this->provider = provider;
|
||||||
}
|
}
|
||||||
IBuffer* buffer;
|
IBuffer* buffer;
|
||||||
@ -81,12 +92,12 @@ class PipeWireOut : public IOutput {
|
|||||||
Stopped, Paused, Playing
|
Stopped, Paused, Playing
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_set<BufferState*> buffers;
|
std::deque<BufferContext*> buffers;
|
||||||
std::recursive_mutex mutex;
|
std::recursive_mutex mutex;
|
||||||
std::atomic<bool> initialized { false };
|
std::atomic<bool> initialized { false };
|
||||||
std::atomic<State> state { State::Stopped };
|
std::atomic<State> state { State::Stopped };
|
||||||
double volume { 1.0 };
|
double volume { 1.0 };
|
||||||
|
pw_stream_events pwStreamEvents;
|
||||||
pw_thread_loop* pwThreadLoop { nullptr };
|
pw_thread_loop* pwThreadLoop { nullptr };
|
||||||
pw_stream* pwStream { nullptr };
|
pw_stream* pwStream { nullptr };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user