Add autoconf stuff to git.
[rtaudio-cdist.git] / RtAudio.cpp
index 1b27de1fd5c60f883c1842d9d36272828195bb15..64b15774c8642423fffb6492eac264469b930d60 100644 (file)
@@ -47,6 +47,7 @@
 #include <climits>
 #include <cmath>
 #include <algorithm>
+#include <cmath>
 
 // Static variable definitions.
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
@@ -3778,7 +3779,7 @@ static const char* getAsioErrorString( ASIOError result )
 #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)
@@ -8435,6 +8436,7 @@ static void *alsaCallbackHandler( void *ptr )
 
 #include <pulse/error.h>
 #include <pulse/simple.h>
+#include <pulse/pulseaudio.h>
 #include <cstdio>
 
 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
@@ -8471,8 +8473,33 @@ unsigned int RtApiPulse::getDeviceCount( void )
   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";
@@ -8488,6 +8515,72 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
   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;
 }
 
@@ -8640,6 +8733,14 @@ void RtApiPulse::callbackEvent( void )
   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();
 }
@@ -8754,10 +8855,6 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
 
   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;
@@ -8877,7 +8974,38 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     }
     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;