#include <climits>
#include <cmath>
#include <algorithm>
+#include <cmath>
// Static variable definitions.
const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
struct timeval then;
struct timeval now;
- if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
+ if ( stream_.state != STREAM_RUNNING || (stream_.lastTickTimestamp.tv_sec == 0 && stream_.lastTickTimestamp.tv_usec == 0) )
return stream_.streamTime;
gettimeofday( &now, NULL );
return stream_.sampleRate;
}
+void RtApi :: startStream( void )
+{
+#if defined( HAVE_GETTIMEOFDAY )
+ stream_.lastTickTimestamp.tv_sec = 0;
+ stream_.lastTickTimestamp.tv_usec = 0;
+#endif
+}
+
// *************************************************** //
//
void RtApiCore :: startStream( void )
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiCore::startStream(): the stream is already running!";
error( RtAudioError::WARNING );
void RtApiJack :: startStream( void )
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiJack::startStream(): the stream is already running!";
error( RtAudioError::WARNING );
void RtApiAsio :: startStream()
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiAsio::startStream(): the stream is already running!";
error( RtAudioError::WARNING );
#include <audioclient.h>
#include <avrt.h>
#include <mmdeviceapi.h>
-#include <functiondiscoverykeys_devpkey.h>
+#include <FunctionDiscoveryKeys_devpkey.h>
#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT
#define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72)
relOutIndex += bufferSize_;
}
- // "in" index can end on the "out" index but cannot begin at it
- if ( inIndex_ < relOutIndex && inIndexEnd > relOutIndex ) {
+ // the "IN" index CAN BEGIN at the "OUT" index
+ // the "IN" index CANNOT END at the "OUT" index
+ if ( inIndex_ < relOutIndex && inIndexEnd >= relOutIndex ) {
return false; // not enough space between "in" index and "out" index
}
relInIndex += bufferSize_;
}
- // "out" index can begin at and end on the "in" index
+ // the "OUT" index CANNOT BEGIN at the "IN" index
+ // the "OUT" index CAN END at the "IN" index
if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) {
return false; // not enough space between "out" index and "in" index
}
void RtApiWasapi::startStream( void )
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiWasapi::startStream: The stream is already running.";
captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,
stream_.callbackInfo.userData );
+ // tick stream time
+ RtApi::tickStreamTime();
+
// Handle return value from callback
if ( callbackResult == 1 ) {
// instantiate a thread to stop this thread
// unsetting the callbackPulled flag lets the stream know that
// the audio device is ready for another callback output buffer.
callbackPulled = false;
-
- // tick stream time
- RtApi::tickStreamTime();
}
}
void RtApiDs :: startStream()
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiDs::startStream(): the stream is already running!";
error( RtAudioError::WARNING );
void RtApiDs :: stopStream()
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_STOPPED ) {
errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
error( RtAudioError::WARNING );
// This method calls snd_pcm_prepare if the device isn't already in that state.
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
error( RtAudioError::WARNING );
#include <pulse/error.h>
#include <pulse/simple.h>
+#include <pulse/pulseaudio.h>
#include <cstdio>
static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
return 1;
}
+void RtApiPulse::sinkInfoCallback(pa_context*, const pa_sink_info* info, int, void* arg)
+{
+ RtApiPulse* api = (RtApiPulse *) arg;
+ if (info) {
+ api->channels_ = info->sample_spec.channels;
+ }
+ pa_threaded_mainloop_signal(api->mainloop_, 0);
+}
+
+void RtApiPulse::contextStateCallback(pa_context* c, void* arg)
+{
+ pa_threaded_mainloop* mainloop = (pa_threaded_mainloop*) arg;
+
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(mainloop, 0);
+ break;
+ default:
+ break;
+ }
+}
+
RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
{
+ /* Set up some defaults in case we crash and burn */
RtAudio::DeviceInfo info;
info.probed = true;
info.name = "PulseAudio";
info.preferredSampleRate = 48000;
info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
+ /* Get the number of output channels from pulseaudio. A simple task, you say?
+ "What is your mainloop?" */
+ mainloop_ = pa_threaded_mainloop_new();
+ if (!mainloop_) {
+ return info;
+ }
+
+ pa_threaded_mainloop_start(mainloop_);
+ pa_threaded_mainloop_lock(mainloop_);
+
+ /* "And what is your context?" */
+ pa_context* context = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), "RtAudio");
+ if (!context) {
+ pa_threaded_mainloop_unlock(mainloop_);
+ pa_threaded_mainloop_stop(mainloop_);
+ pa_threaded_mainloop_free(mainloop_);
+ mainloop_ = 0;
+ return info;
+ }
+
+ pa_context_set_state_callback(context, contextStateCallback, mainloop_);
+
+ pa_context_connect(context, 0, (pa_context_flags_t) 0, 0);
+
+ /* "And what is your favourite colour?" */
+ int connected = 0;
+ pa_context_state_t state = pa_context_get_state(context);
+ for (; !connected; state = pa_context_get_state(context)) {
+ switch (state) {
+ case PA_CONTEXT_READY:
+ connected = 1;
+ continue;
+ case PA_CONTEXT_FAILED:
+ case PA_CONTEXT_TERMINATED:
+ /* Blue! No, I mean red! */
+ pa_threaded_mainloop_unlock(mainloop_);
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+ pa_threaded_mainloop_stop(mainloop_);
+ pa_threaded_mainloop_free(mainloop_);
+ mainloop_ = 0;
+ return info;
+ default:
+ pa_threaded_mainloop_wait(mainloop_);
+ break;
+ }
+ }
+
+ pa_operation* op = pa_context_get_sink_info_by_index(context, 0, sinkInfoCallback, this);
+
+ if (op) {
+ pa_operation_unref(op);
+ }
+
+ pa_threaded_mainloop_wait(mainloop_);
+ pa_threaded_mainloop_unlock(mainloop_);
+
+ pa_context_disconnect(context);
+ pa_context_unref(context);
+
+ pa_threaded_mainloop_stop(mainloop_);
+ pa_threaded_mainloop_free(mainloop_);
+ mainloop_ = 0;
+
+ info.outputChannels = channels_;
+
return info;
}
MUTEX_UNLOCK( &stream_.mutex );
RtApi::tickStreamTime();
+ if (pah->s_play) {
+ int e = 0;
+ pa_usec_t const lat = pa_simple_get_latency(pah->s_play, &e);
+ if (e == 0) {
+ stream_.latency[0] = lat * stream_.sampleRate / 1000000;
+ }
+ }
+
if ( doStopStream == 1 )
stopStream();
}
void RtApiPulse::startStream( void )
{
+ RtApi::startStream();
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
if ( stream_.state == STREAM_CLOSED ) {
}
stream_.state = STREAM_STOPPED;
+ pah->runnable = false;
MUTEX_LOCK( &stream_.mutex );
if ( pah && pah->s_play ) {
}
stream_.state = STREAM_STOPPED;
+ pah->runnable = false;
MUTEX_LOCK( &stream_.mutex );
if ( pah && pah->s_play ) {
if ( device != 0 ) return false;
if ( mode != INPUT && mode != OUTPUT ) return false;
- if ( channels != 1 && channels != 2 ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";
- return false;
- }
ss.channels = channels;
if ( firstChannel != 0 ) return false;
}
break;
case OUTPUT:
- pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
+ /* XXX: hard-coded for DCP-o-matic */
+ pa_channel_map map;
+ pa_channel_map_init(&map);
+ /* XXX: need to check 7.1 */
+ map.channels = channels;
+
+ if (channels > 0) {
+ map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ }
+ if (channels > 1) {
+ map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ }
+ if (channels > 2) {
+ map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ }
+ if (channels > 3) {
+ map.map[3] = PA_CHANNEL_POSITION_LFE;
+ }
+ if (channels > 4) {
+ map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
+ }
+ if (channels > 5) {
+ map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ }
+ if (channels > 6) {
+ map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+ }
+ if (channels > 7) {
+ map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+ }
+
+ pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, &map, NULL, &error );
if ( !pah->s_play ) {
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
goto error;
void RtApiOss :: startStream()
{
verifyStream();
+ RtApi::startStream();
if ( stream_.state == STREAM_RUNNING ) {
errorText_ = "RtApiOss::startStream(): the stream is already running!";
error( RtAudioError::WARNING );