MuseScore/mscore/pulseaudio.cpp

233 lines
7 KiB
C++
Raw Normal View History

2012-05-26 14:49:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2012 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "libmscore/score.h"
#include "seq.h"
#include <pulse/pulseaudio.h>
#include "driver.h"
#include "preferences.h"
2012-05-26 14:49:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:49:10 +02:00
#define FRAMES 2048
//---------------------------------------------------------
// PulseAudio
//---------------------------------------------------------
class PulseAudio : public Driver {
Transport state;
2012-05-26 14:49:10 +02:00
int runState;
int _sampleRate;
pa_sample_spec ss;
pa_mainloop* pa_ml;
pa_buffer_attr bufattr;
float buffer[FRAMES * 2];
2012-05-26 14:49:10 +02:00
pthread_t thread;
static void paCallback(pa_stream* s, size_t len, void* data);
static void* paLoop(void*);
public:
PulseAudio(Seq*);
virtual ~PulseAudio();
2014-07-07 17:31:41 +02:00
virtual bool init(bool hot = false);
2012-05-26 14:49:10 +02:00
virtual bool start();
virtual bool stop();
virtual Transport getState() override { return state; }
2012-05-26 14:49:10 +02:00
virtual int sampleRate() const { return _sampleRate; }
virtual void stopTransport() { state = Transport::STOP; }
virtual void startTransport() { state = Transport::PLAY; }
2012-05-26 14:49:10 +02:00
};
//---------------------------------------------------------
// paCallback
//---------------------------------------------------------
void PulseAudio::paCallback(pa_stream* s, size_t len, void* data)
{
PulseAudio* pa = (PulseAudio*)data;
size_t n = FRAMES * 2 * sizeof(float);
if (len > n)
len = n;
float* p = pa->buffer;
2012-06-25 16:40:31 +02:00
pa->seq->process(len / (2 * sizeof(float)), p);
2012-05-26 14:49:10 +02:00
pa_stream_write(s, p, len, NULL, 0LL, PA_SEEK_RELATIVE);
}
//---------------------------------------------------------
// PulseAudio
//---------------------------------------------------------
PulseAudio::PulseAudio(Seq* s)
: Driver(s)
{
_sampleRate = preferences.alsaSampleRate;
state = Transport::STOP;
2012-05-26 14:49:10 +02:00
runState = 0;
}
//---------------------------------------------------------
// ~PulseAudio
//---------------------------------------------------------
PulseAudio::~PulseAudio()
{
stop();
}
//---------------------------------------------------------
// pa_state_cb
//---------------------------------------------------------
static void pa_state_cb(pa_context* c, void* data)
{
int* pa_ready = (int*)data;
switch (pa_context_get_state(c)) {
// These are just here for reference
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready = 2;
break;
case PA_CONTEXT_READY:
*pa_ready = 1;
break;
}
}
//---------------------------------------------------------
// init
// return false on error
//---------------------------------------------------------
2014-07-07 17:31:41 +02:00
bool PulseAudio::init(bool)
2012-05-26 14:49:10 +02:00
{
pa_ml = pa_mainloop_new();
pa_mainloop_api* pa_mlapi = pa_mainloop_get_api(pa_ml);
pa_context* pa_ctx = pa_context_new(pa_mlapi, "MuseScore");
if (pa_context_connect(pa_ctx, NULL, pa_context_flags_t(0), NULL) != 0)
qDebug("PulseAudio Context Connect Failed with Error: %s", pa_strerror(pa_context_errno(pa_ctx)));
2012-05-26 14:49:10 +02:00
int pa_ready = 0;
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
while (pa_ready == 0)
pa_mainloop_iterate(pa_ml, 1, NULL);
if (pa_ready == 2)
return false;
ss.rate = _sampleRate;
ss.channels = 2;
ss.format = PA_SAMPLE_FLOAT32LE;
pa_stream* playstream = pa_stream_new(pa_ctx, "Playback", &ss, NULL);
if (!playstream) {
2014-07-27 15:06:49 +02:00
qDebug("pa_stream_new failed");
2012-05-26 14:49:10 +02:00
return false;
}
pa_stream_set_write_callback(playstream, paCallback, this);
bufattr.fragsize = (uint32_t)-1;
bufattr.maxlength = FRAMES * 2 * sizeof(float);
bufattr.minreq = FRAMES * 1 * sizeof(float); // pa_usec_to_bytes(0, &ss);
bufattr.prebuf = (uint32_t)-1;
bufattr.tlength = bufattr.maxlength;
int r = pa_stream_connect_playback(playstream, NULL, &bufattr,
pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_ADJUST_LATENCY
| PA_STREAM_AUTO_TIMING_UPDATE),
NULL, NULL);
if (r < 0) {
// Old pulse audio servers don't like the ADJUST_LATENCY flag, so retry without that
r = pa_stream_connect_playback(playstream, NULL, &bufattr,
pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE),
NULL, NULL);
}
if (r < 0) {
2014-07-27 15:06:49 +02:00
qDebug("pa_stream_connect_playback failed");
2012-05-26 14:49:10 +02:00
pa_context_disconnect(pa_ctx);
pa_context_unref(pa_ctx);
pa_mainloop_free(pa_ml);
pa_ml = 0;
return false;
}
return true;
}
//---------------------------------------------------------
// paLoop
//---------------------------------------------------------
void* PulseAudio::paLoop(void* data)
{
PulseAudio* pa = (PulseAudio*)data;
pa->runState = 2;
while (pa->runState == 2)
pa_mainloop_iterate(pa->pa_ml, 1, NULL);
pa->runState = 0;
return 0;
}
//---------------------------------------------------------
// start
//---------------------------------------------------------
bool PulseAudio::start()
{
pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t));
pthread_attr_init(attributes);
if (pthread_create(&thread, attributes, paLoop, this))
perror("creating thread failed:");
pthread_attr_destroy(attributes);
return true;
}
//---------------------------------------------------------
// stop
//---------------------------------------------------------
bool PulseAudio::stop()
{
if (runState == 2) {
runState = 1;
int i = 0;
for (;i < 4; ++i) {
if (runState == 0)
break;
sleep(1);
}
pthread_cancel(thread);
pthread_join(thread, 0);
}
return true;
}
//---------------------------------------------------------
// getPulseaudioDriver
// driver factory
//---------------------------------------------------------
Driver* getPulseAudioDriver(Seq* seq)
{
return new PulseAudio(seq);
}
2013-05-13 18:49:17 +02:00
}
2012-05-26 14:49:10 +02:00