Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/cubeb-sys/libcubeb/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 36 kB image not shown  

Quelle  cubeb_jack.cpp   Sprache: C

 
/*
 * Copyright © 2012 David Richards
 * Copyright © 2013 Sebastien Alaiwan
 * Copyright © 2016 Damien Zammit
 *
 * This program is made available under an ISC-style license.  See the
 * accompanying file LICENSE for details.
 */

#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
#define _POSIX_SOURCE
#endif
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_resampler.h"
#include "cubeb_utils.h"
#include <dlfcn.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <jack/jack.h>
#include <jack/statistics.h>

#ifdef DISABLE_LIBJACK_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*api_##x)
#define JACK_API_VISIT(X)                                                      \
  X(jack_activate)                                                             \
  X(jack_client_close)                                                         \
  X(jack_client_open)                                                          \
  X(jack_connect)                                                              \
  X(jack_free)                                                                 \
  X(jack_get_ports)                                                            \
  X(jack_get_sample_rate)                                                      \
  X(jack_get_xrun_delayed_usecs)                                               \
  X(jack_get_buffer_size)                                                      \
  X(jack_port_get_buffer)                                                      \
  X(jack_port_name)                                                            \
  X(jack_port_register)                                                        \
  X(jack_port_unregister)                                                      \
  X(jack_port_get_latency_range)                                               \
  X(jack_set_process_callback)                                                 \
  X(jack_set_xrun_callback)                                                    \
  X(jack_set_graph_order_callback)                                             \
  X(jack_set_error_function)                                                   \
  X(jack_set_info_function)

#define IMPORT_FUNC(x) static decltype(x) * api_##x;
JACK_API_VISIT(IMPORT_FUNC);
#undef IMPORT_FUNC
#endif

#define JACK_DEFAULT_IN "JACK capture"
#define JACK_DEFAULT_OUT "JACK playback"

static const int MAX_STREAMS = 16;
static const int MAX_CHANNELS = 8;
static const int FIFO_SIZE = 4096 * sizeof(float);

enum devstream {
  NONE = 0,
  IN_ONLY,
  OUT_ONLY,
  DUPLEX,
};

enum cbjack_connect_ports_options {
  CBJACK_CP_OPTIONS_NONE = 0x0,
  CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1,
  CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2,
};

static void
s16ne_to_float(float * dst, const int16_t * src, size_t n)
{
  for (size_t i = 0; i < n; i++)
    *(dst++) = (float)((float)*(src++) / 32767.0f);
}

static void
float_to_s16ne(int16_t * dst, float * src, size_t n)
{
  for (size_t i = 0; i < n; i++) {
    if (*src > 1.f)
      *src = 1.f;
    if (*src < -1.f)
      *src = -1.f;
    *(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
  }
}

extern "C" {
/*static*/ int
jack_init(cubeb ** context, char const * context_name);
}
static char const *
cbjack_get_backend_id(cubeb * context);
static int
cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
static int
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
                       uint32_t * latency_frames);
static int
cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
static int
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
static void
cbjack_destroy(cubeb * context);
static void
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
                          jack_nframes_t nframes, bool format_mismatch);
static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream,
                                          short ** bufs_in, float ** bufs_out,
                                          jack_nframes_t nframes);
static void
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream,
                                          float ** bufs_in, float ** bufs_out,
                                          jack_nframes_t nframes);
static int
cbjack_stream_device_destroy(cubeb_stream * stream, cubeb_device * device);
static int
cbjack_stream_get_current_device(cubeb_stream * stm,
                                 cubeb_device ** const device);
static int
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                         cubeb_device_collection * collection);
static int
cbjack_device_collection_destroy(cubeb * context,
                                 cubeb_device_collection * collection);
static int
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
                   char const * stream_name, cubeb_devid input_device,
                   cubeb_stream_params * input_stream_params,
                   cubeb_devid output_device,
                   cubeb_stream_params * output_stream_params,
                   unsigned int latency_frames,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback, void * user_ptr);
static void
cbjack_stream_destroy(cubeb_stream * stream);
static int
cbjack_stream_start(cubeb_stream * stream);
static int
cbjack_stream_stop(cubeb_stream * stream);
static int
cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
static int
cbjack_stream_set_volume(cubeb_stream * stm, float volume);

static struct cubeb_ops const cbjack_ops = {
    .init = jack_init,
    .get_backend_id = cbjack_get_backend_id,
    .get_max_channel_count = cbjack_get_max_channel_count,
    .get_min_latency = cbjack_get_min_latency,
    .get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
    .get_supported_input_processing_params = NULL,
    .enumerate_devices = cbjack_enumerate_devices,
    .device_collection_destroy = cbjack_device_collection_destroy,
    .destroy = cbjack_destroy,
    .stream_init = cbjack_stream_init,
    .stream_destroy = cbjack_stream_destroy,
    .stream_start = cbjack_stream_start,
    .stream_stop = cbjack_stream_stop,
    .stream_get_position = cbjack_stream_get_position,
    .stream_get_latency = cbjack_get_latency,
    .stream_get_input_latency = NULL,
    .stream_set_volume = cbjack_stream_set_volume,
    .stream_set_name = NULL,
    .stream_get_current_device = cbjack_stream_get_current_device,
    .stream_set_input_mute = NULL,
    .stream_set_input_processing_params = NULL,
    .stream_device_destroy = cbjack_stream_device_destroy,
    .stream_register_device_changed_callback = NULL,
    .register_device_collection_changed = NULL};

struct cubeb_stream {
  /* Note: Must match cubeb_stream layout in cubeb.c. */
  cubeb * context;
  void * user_ptr;
  /**/

  /**< Mutex for each stream */
  pthread_mutex_t mutex;

  bool in_use;      /**< Set to false iff the stream is free */
  bool ports_ready; /**< Set to true iff the JACK ports are ready */

  cubeb_data_callback data_callback;
  cubeb_state_callback state_callback;
  cubeb_stream_params in_params;
  cubeb_stream_params out_params;

  cubeb_resampler * resampler;

  uint64_t position;
  bool pause;
  float ratio;
  enum devstream devs;
  char stream_name[256];
  jack_port_t * output_ports[MAX_CHANNELS];
  jack_port_t * input_ports[MAX_CHANNELS];
  float volume;
};

struct cubeb {
  struct cubeb_ops const * ops;
  void * libjack;

  /**< Mutex for whole context */
  pthread_mutex_t mutex;

  /**< Audio buffers, converted to float */
  float in_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];
  float out_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS];

  /**< Audio buffer, at the sampling rate of the output */
  float in_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
  int16_t in_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];
  float out_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3];
  int16_t out_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3];

  cubeb_stream streams[MAX_STREAMS];
  unsigned int active_streams;

  cubeb_device_collection_changed_callback collection_changed_callback;

  bool active;
  unsigned int jack_sample_rate;
  unsigned int jack_latency;
  unsigned int jack_xruns;
  unsigned int jack_buffer_size;
  unsigned int fragment_size;
  unsigned int output_bytes_per_frame;
  jack_client_t * jack_client;
};

static int
load_jack_lib(cubeb * context)
{
#ifndef DISABLE_LIBJACK_DLOPEN
#ifdef __APPLE__
  context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
  context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
#elif defined(__WIN32__)
#ifdef _WIN64
  context->libjack = LoadLibrary("libjack64.dll");
#else
  context->libjack = LoadLibrary("libjack.dll");
#endif
#else
  context->libjack = dlopen("libjack.so.0", RTLD_LAZY);
  if (!context->libjack) {
    context->libjack = dlopen("libjack.so", RTLD_LAZY);
  }
#endif
  if (!context->libjack) {
    return CUBEB_ERROR;
  }

#define LOAD(x)                                                                \
  {                                                                            \
    api_##x = (decltype(x) *)dlsym(context->libjack, #x);                      \
    if (!api_##x) {                                                            \
      dlclose(context->libjack);                                               \
      return CUBEB_ERROR;                                                      \
    }                                                                          \
  }

  JACK_API_VISIT(LOAD);
#undef LOAD
#endif
  return CUBEB_OK;
}

static void
cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port,
                        const char * const phys_in_port)
{
  const char * src_port = WRAP(jack_port_name)(stream->output_ports[out_port]);

  WRAP(jack_connect)(stream->context->jack_client, src_port, phys_in_port);
}

static void
cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port,
                       size_t in_port)
{
  const char * src_port = WRAP(jack_port_name)(stream->input_ports[in_port]);

  WRAP(jack_connect)(stream->context->jack_client, phys_out_port, src_port);
}

static int
cbjack_connect_ports(cubeb_stream * stream,
                     enum cbjack_connect_ports_options options)
{
  int r = CUBEB_ERROR;
  const char ** phys_in_ports =
      WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
                           JackPortIsInput | JackPortIsPhysical);
  const char ** phys_out_ports =
      WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
                           JackPortIsOutput | JackPortIsPhysical);

  if (phys_in_ports == NULL || *phys_in_ports == NULL ||
      options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) {
    goto skipplayback;
  }

  // Connect outputs to playback
  for (unsigned int c = 0;
       c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
    cbjack_connect_port_out(stream, c, phys_in_ports[c]);
  }

  // Special case playing mono source in stereo
  if (stream->out_params.channels == 1 && phys_in_ports[1] != NULL) {
    cbjack_connect_port_out(stream, 0, phys_in_ports[1]);
  }

  r = CUBEB_OK;

skipplayback:
  if (phys_out_ports == NULL || *phys_out_ports == NULL ||
      options & CBJACK_CP_OPTIONS_SKIP_INPUT) {
    goto end;
  }
  // Connect inputs to capture
  for (unsigned int c = 0;
       c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
    cbjack_connect_port_in(stream, phys_out_ports[c], c);
  }
  r = CUBEB_OK;
end:
  if (phys_out_ports) {
    WRAP(jack_free)(phys_out_ports);
  }
  if (phys_in_ports) {
    WRAP(jack_free)(phys_in_ports);
  }
  return r;
}

static int
cbjack_xrun_callback(void * arg)
{
  cubeb * ctx = (cubeb *)arg;

  float delay = WRAP(jack_get_xrun_delayed_usecs)(ctx->jack_client);
  float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) /
                          ctx->jack_buffer_size);

  ctx->jack_xruns += (unsigned int)fragments;
  return 0;
}

static int
cbjack_graph_order_callback(void * arg)
{
  cubeb * ctx = (cubeb *)arg;
  int i;
  jack_latency_range_t latency_range;
  jack_nframes_t port_latency, max_latency = 0;

  for (int j = 0; j < MAX_STREAMS; j++) {
    cubeb_stream * stm = &ctx->streams[j];

    if (!stm->in_use)
      continue;
    if (!stm->ports_ready)
      continue;

    for (i = 0; i < (int)stm->out_params.channels; ++i) {
      WRAP(jack_port_get_latency_range)
      (stm->output_ports[i], JackPlaybackLatency, &latency_range);
      port_latency = latency_range.max;
      if (port_latency > max_latency)
        max_latency = port_latency;
    }
    /* Cap minimum latency to 128 frames */
    if (max_latency < 128)
      max_latency = 128;
  }

  ctx->jack_latency = max_latency;

  return 0;
}

static int
cbjack_process(jack_nframes_t nframes, void * arg)
{
  cubeb * ctx = (cubeb *)arg;
  unsigned int t_jack_xruns = ctx->jack_xruns;
  int i;

  ctx->jack_xruns = 0;

  for (int j = 0; j < MAX_STREAMS; j++) {
    cubeb_stream * stm = &ctx->streams[j];
    float * bufs_out[stm->out_params.channels];
    float * bufs_in[stm->in_params.channels];

    if (!stm->in_use)
      continue;

    // handle xruns by skipping audio that should have been played
    stm->position += t_jack_xruns * ctx->fragment_size * stm->ratio;

    if (!stm->ports_ready)
      continue;

    if (stm->devs & OUT_ONLY) {
      // get jack output buffers
      for (i = 0; i < (int)stm->out_params.channels; i++)
        bufs_out[i] =
            (float *)WRAP(jack_port_get_buffer)(stm->output_ports[i], nframes);
    }
    if (stm->devs & IN_ONLY) {
      // get jack input buffers
      for (i = 0; i < (int)stm->in_params.channels; i++)
        bufs_in[i] =
            (float *)WRAP(jack_port_get_buffer)(stm->input_ports[i], nframes);
    }
    if (stm->pause) {
      // paused, play silence on output
      if (stm->devs & OUT_ONLY) {
        for (unsigned int c = 0; c < stm->out_params.channels; c++) {
          float * buffer_out = bufs_out[c];
          if (buffer_out) {
            for (long f = 0; f < nframes; f++) {
              buffer_out[f] = 0.f;
            }
          }
        }
      }
      if (stm->devs & IN_ONLY) {
        // paused, capture silence
        for (unsigned int c = 0; c < stm->in_params.channels; c++) {
          float * buffer_in = bufs_in[c];
          if (buffer_in) {
            for (long f = 0; f < nframes; f++) {
              buffer_in[f] = 0.f;
            }
          }
        }
      }
    } else {

      // try to lock stream mutex
      if (pthread_mutex_trylock(&stm->mutex) == 0) {

        int16_t * in_s16ne =
            stm->context->in_resampled_interleaved_buffer_s16ne;
        float * in_float = stm->context->in_resampled_interleaved_buffer_float;

        // unpaused, play audio
        if (stm->devs == DUPLEX) {
          if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
            cbjack_interleave_capture(stm, bufs_in, nframes, true);
            cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out,
                                                      nframes);
          } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
            cbjack_interleave_capture(stm, bufs_in, nframes, false);
            cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out,
                                                      nframes);
          }
        } else if (stm->devs == IN_ONLY) {
          if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
            cbjack_interleave_capture(stm, bufs_in, nframes, true);
            cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr,
                                                      nframes);
          } else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
            cbjack_interleave_capture(stm, bufs_in, nframes, false);
            cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr,
                                                      nframes);
          }
        } else if (stm->devs == OUT_ONLY) {
          if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
            cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out,
                                                      nframes);
          } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
            cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out,
                                                      nframes);
          }
        }
        // unlock stream mutex
        pthread_mutex_unlock(&stm->mutex);

      } else {
        // could not lock mutex
        // output silence
        if (stm->devs & OUT_ONLY) {
          for (unsigned int c = 0; c < stm->out_params.channels; c++) {
            float * buffer_out = bufs_out[c];
            if (buffer_out) {
              for (long f = 0; f < nframes; f++) {
                buffer_out[f] = 0.f;
              }
            }
          }
        }
        if (stm->devs & IN_ONLY) {
          // capture silence
          for (unsigned int c = 0; c < stm->in_params.channels; c++) {
            float * buffer_in = bufs_in[c];
            if (buffer_in) {
              for (long f = 0; f < nframes; f++) {
                buffer_in[f] = 0.f;
              }
            }
          }
        }
      }
    }
  }
  return 0;
}

static void
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in,
                                          float ** bufs_out,
                                          jack_nframes_t nframes)
{
  float * out_interleaved_buffer = nullptr;

  float * inptr = (in != NULL) ? *in : nullptr;
  float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;

  long needed_frames = (bufs_out != NULL) ? nframes : 0;
  long done_frames = 0;
  long input_frames_count = (in != NULL) ? nframes : 0;

  done_frames = cubeb_resampler_fill(
      stream->resampler, inptr, &input_frames_count,
      (bufs_out != NULL)
          ? stream->context->out_resampled_interleaved_buffer_float
          : NULL,
      needed_frames);

  out_interleaved_buffer =
      stream->context->out_resampled_interleaved_buffer_float;

  if (outptr) {
    // convert interleaved output buffers to contiguous buffers
    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
      float * buffer = bufs_out[c];
      for (long f = 0; f < done_frames; f++) {
        if (buffer) {
          buffer[f] =
              out_interleaved_buffer[(f * stream->out_params.channels) + c] *
              stream->volume;
        }
      }
      if (done_frames < needed_frames) {
        // draining
        for (long f = done_frames; f < needed_frames; f++) {
          if (buffer) {
            buffer[f] = 0.f;
          }
        }
      }
      if (done_frames == 0) {
        // stop, but first zero out the existing buffer
        for (long f = 0; f < needed_frames; f++) {
          if (buffer) {
            buffer[f] = 0.f;
          }
        }
      }
    }
  }

  if (done_frames >= 0 && done_frames < needed_frames) {
    // set drained
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
    // stop stream
    cbjack_stream_stop(stream);
  }
  if (done_frames > 0 && done_frames <= needed_frames) {
    // advance stream position
    stream->position += done_frames * stream->ratio;
  }
  if (done_frames < 0 || done_frames > needed_frames) {
    // stream error
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
  }
}

static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in,
                                          float ** bufs_out,
                                          jack_nframes_t nframes)
{
  float * out_interleaved_buffer = nullptr;

  short * inptr = (in != NULL) ? *in : nullptr;
  float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr;

  long needed_frames = (bufs_out != NULL) ? nframes : 0;
  long done_frames = 0;
  long input_frames_count = (in != NULL) ? nframes : 0;

  done_frames = cubeb_resampler_fill(
      stream->resampler, inptr, &input_frames_count,
      (bufs_out != NULL)
          ? stream->context->out_resampled_interleaved_buffer_s16ne
          : NULL,
      needed_frames);

  s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float,
                 stream->context->out_resampled_interleaved_buffer_s16ne,
                 done_frames * stream->out_params.channels);

  out_interleaved_buffer =
      stream->context->out_resampled_interleaved_buffer_float;

  if (outptr) {
    // convert interleaved output buffers to contiguous buffers
    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
      float * buffer = bufs_out[c];
      for (long f = 0; f < done_frames; f++) {
        buffer[f] =
            out_interleaved_buffer[(f * stream->out_params.channels) + c] *
            stream->volume;
      }
      if (done_frames < needed_frames) {
        // draining
        for (long f = done_frames; f < needed_frames; f++) {
          buffer[f] = 0.f;
        }
      }
      if (done_frames == 0) {
        // stop, but first zero out the existing buffer
        for (long f = 0; f < needed_frames; f++) {
          buffer[f] = 0.f;
        }
      }
    }
  }

  if (done_frames >= 0 && done_frames < needed_frames) {
    // set drained
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
    // stop stream
    cbjack_stream_stop(stream);
  }
  if (done_frames > 0 && done_frames <= needed_frames) {
    // advance stream position
    stream->position += done_frames * stream->ratio;
  }
  if (done_frames < 0 || done_frames > needed_frames) {
    // stream error
    stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR);
  }
}

static void
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
                          jack_nframes_t nframes, bool format_mismatch)
{
  float * in_buffer = stream->context->in_float_interleaved_buffer;

  for (unsigned int c = 0; c < stream->in_params.channels; c++) {
    for (long f = 0; f < nframes; f++) {
      in_buffer[(f * stream->in_params.channels) + c] =
          in[c][f] * stream->volume;
    }
  }
  if (format_mismatch) {
    float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne,
                   in_buffer, nframes * stream->in_params.channels);
  } else {
    memset(stream->context->in_resampled_interleaved_buffer_float, 0,
           (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
    memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer,
           (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
  }
}

static void
silent_jack_error_callback(char const * /*msg*/)
{
}

/*static*/ int
jack_init(cubeb ** context, char const * context_name)
{
  int r;

  *context = NULL;

  cubeb * ctx = (cubeb *)calloc(1, sizeof(*ctx));
  if (ctx == NULL) {
    return CUBEB_ERROR;
  }

  r = load_jack_lib(ctx);
  if (r != 0) {
    cbjack_destroy(ctx);
    return CUBEB_ERROR;
  }

  WRAP(jack_set_error_function)(silent_jack_error_callback);
  WRAP(jack_set_info_function)(silent_jack_error_callback);

  ctx->ops = &cbjack_ops;

  ctx->mutex = PTHREAD_MUTEX_INITIALIZER;
  for (r = 0; r < MAX_STREAMS; r++) {
    ctx->streams[r].mutex = PTHREAD_MUTEX_INITIALIZER;
  }

  const char * jack_client_name = "cubeb";
  if (context_name)
    jack_client_name = context_name;

  ctx->jack_client =
      WRAP(jack_client_open)(jack_client_name, JackNoStartServer, NULL);

  if (ctx->jack_client == NULL) {
    cbjack_destroy(ctx);
    return CUBEB_ERROR;
  }

  ctx->jack_xruns = 0;

  WRAP(jack_set_process_callback)(ctx->jack_client, cbjack_process, ctx);
  WRAP(jack_set_xrun_callback)(ctx->jack_client, cbjack_xrun_callback, ctx);
  WRAP(jack_set_graph_order_callback)
  (ctx->jack_client, cbjack_graph_order_callback, ctx);

  if (WRAP(jack_activate)(ctx->jack_client)) {
    cbjack_destroy(ctx);
    return CUBEB_ERROR;
  }

  ctx->jack_sample_rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
  ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;

  ctx->active = true;
  *context = ctx;

  return CUBEB_OK;
}

static char const *
cbjack_get_backend_id(cubeb * /*context*/)
{
  return "jack";
}

static int
cbjack_get_max_channel_count(cubeb * /*ctx*/, uint32_t * max_channels)
{
  *max_channels = MAX_CHANNELS;
  return CUBEB_OK;
}

static int
cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms)
{
  *latency_ms = stm->context->jack_latency;
  return CUBEB_OK;
}

static int
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/,
                       uint32_t * latency_ms)
{
  *latency_ms = ctx->jack_latency;
  return CUBEB_OK;
}

static int
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
  if (!ctx->jack_client) {
    jack_client_t * testclient =
        WRAP(jack_client_open)("test-samplerate", JackNoStartServer, NULL);
    if (!testclient) {
      return CUBEB_ERROR;
    }

    *rate = WRAP(jack_get_sample_rate)(testclient);
    WRAP(jack_client_close)(testclient);

  } else {
    *rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
  }
  return CUBEB_OK;
}

static void
cbjack_destroy(cubeb * context)
{
  context->active = false;

  if (context->jack_client != NULL)
    WRAP(jack_client_close)(context->jack_client);
#ifndef DISABLE_LIBJACK_DLOPEN
  if (context->libjack)
    dlclose(context->libjack);
#endif
  free(context);
}

static cubeb_stream *
context_alloc_stream(cubeb * context, char const * stream_name)
{
  for (int i = 0; i < MAX_STREAMS; i++) {
    if (!context->streams[i].in_use) {
      cubeb_stream * stm = &context->streams[i];
      stm->in_use = true;
      snprintf(stm->stream_name, 255, "%s_%u", stream_name, i);
      return stm;
    }
  }
  return NULL;
}

static int
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
                   char const * stream_name, cubeb_devid input_device,
                   cubeb_stream_params * input_stream_params,
                   cubeb_devid output_device,
                   cubeb_stream_params * output_stream_params,
                   unsigned int /*latency_frames*/,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback, void * user_ptr)
{
  int stream_actual_rate = 0;
  int jack_rate = WRAP(jack_get_sample_rate)(context->jack_client);

  if (output_stream_params &&
      (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
       output_stream_params->format != CUBEB_SAMPLE_S16NE)) {
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  if (input_stream_params &&
      (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
       input_stream_params->format != CUBEB_SAMPLE_S16NE)) {
    return CUBEB_ERROR_INVALID_FORMAT;
  }

  if ((input_device && input_device != JACK_DEFAULT_IN) ||
      (output_device && output_device != JACK_DEFAULT_OUT)) {
    return CUBEB_ERROR_NOT_SUPPORTED;
  }

  // Loopback is unsupported
  if ((input_stream_params &&
       (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
      (output_stream_params &&
       (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
    return CUBEB_ERROR_NOT_SUPPORTED;
  }

  *stream = NULL;

  // Find a free stream.
  pthread_mutex_lock(&context->mutex);
  cubeb_stream * stm = context_alloc_stream(context, stream_name);

  // No free stream?
  if (stm == NULL) {
    pthread_mutex_unlock(&context->mutex);
    return CUBEB_ERROR;
  }

  // unlock context mutex
  pthread_mutex_unlock(&context->mutex);

  // Lock active stream
  pthread_mutex_lock(&stm->mutex);

  stm->ports_ready = false;
  stm->user_ptr = user_ptr;
  stm->context = context;
  stm->devs = NONE;
  if (output_stream_params && !input_stream_params) {
    stm->out_params = *output_stream_params;
    stream_actual_rate = stm->out_params.rate;
    stm->out_params.rate = jack_rate;
    stm->devs = OUT_ONLY;
    if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
      context->output_bytes_per_frame = sizeof(float);
    } else {
      context->output_bytes_per_frame = sizeof(short);
    }
  }
  if (input_stream_params && output_stream_params) {
    stm->in_params = *input_stream_params;
    stm->out_params = *output_stream_params;
    stream_actual_rate = stm->out_params.rate;
    stm->in_params.rate = jack_rate;
    stm->out_params.rate = jack_rate;
    stm->devs = DUPLEX;
    if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
      context->output_bytes_per_frame = sizeof(float);
      stm->in_params.format = CUBEB_SAMPLE_FLOAT32NE;
    } else {
      context->output_bytes_per_frame = sizeof(short);
      stm->in_params.format = CUBEB_SAMPLE_S16NE;
    }
  } else if (input_stream_params && !output_stream_params) {
    stm->in_params = *input_stream_params;
    stream_actual_rate = stm->in_params.rate;
    stm->in_params.rate = jack_rate;
    stm->devs = IN_ONLY;
    if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
      context->output_bytes_per_frame = sizeof(float);
    } else {
      context->output_bytes_per_frame = sizeof(short);
    }
  }

  stm->ratio = (float)stream_actual_rate / (float)jack_rate;

  stm->data_callback = data_callback;
  stm->state_callback = state_callback;
  stm->position = 0;
  stm->volume = 1.0f;
  context->jack_buffer_size = WRAP(jack_get_buffer_size)(context->jack_client);
  context->fragment_size = context->jack_buffer_size;

  if (stm->devs == NONE) {
    pthread_mutex_unlock(&stm->mutex);
    return CUBEB_ERROR;
  }

  stm->resampler = NULL;

  if (stm->devs == DUPLEX) {
    stm->resampler = cubeb_resampler_create(
        stm, &stm->in_params, &stm->out_params, stream_actual_rate,
        stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
        CUBEB_RESAMPLER_RECLOCK_NONE);
  } else if (stm->devs == IN_ONLY) {
    stm->resampler = cubeb_resampler_create(
        stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
        stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
        CUBEB_RESAMPLER_RECLOCK_NONE);
  } else if (stm->devs == OUT_ONLY) {
    stm->resampler = cubeb_resampler_create(
        stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
        stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
        CUBEB_RESAMPLER_RECLOCK_NONE);
  }

  if (!stm->resampler) {
    stm->in_use = false;
    pthread_mutex_unlock(&stm->mutex);
    return CUBEB_ERROR;
  }

  if (stm->devs == DUPLEX || stm->devs == OUT_ONLY) {
    for (unsigned int c = 0; c < stm->out_params.channels; c++) {
      char portname[256];
      snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
      stm->output_ports[c] = WRAP(jack_port_register)(
          stm->context->jack_client, portname, JACK_DEFAULT_AUDIO_TYPE,
          JackPortIsOutput, 0);
      if (!(output_stream_params->prefs &
            CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
        if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) !=
            CUBEB_OK) {
          pthread_mutex_unlock(&stm->mutex);
          cbjack_stream_destroy(stm);
          return CUBEB_ERROR;
        }
      }
    }
  }

  if (stm->devs == DUPLEX || stm->devs == IN_ONLY) {
    for (unsigned int c = 0; c < stm->in_params.channels; c++) {
      char portname[256];
      snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
      stm->input_ports[c] =
          WRAP(jack_port_register)(stm->context->jack_client, portname,
                                   JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
      if (!(input_stream_params->prefs &
            CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
        if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) !=
            CUBEB_OK) {
          pthread_mutex_unlock(&stm->mutex);
          cbjack_stream_destroy(stm);
          return CUBEB_ERROR;
        }
      }
    }
  }

  *stream = stm;

  stm->ports_ready = true;
  stm->pause = true;
  pthread_mutex_unlock(&stm->mutex);

  return CUBEB_OK;
}

static void
cbjack_stream_destroy(cubeb_stream * stream)
{
  pthread_mutex_lock(&stream->mutex);
  stream->ports_ready = false;

  if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
    for (unsigned int c = 0; c < stream->out_params.channels; c++) {
      if (stream->output_ports[c]) {
        WRAP(jack_port_unregister)
        (stream->context->jack_client, stream->output_ports[c]);
        stream->output_ports[c] = NULL;
      }
    }
  }

  if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
    for (unsigned int c = 0; c < stream->in_params.channels; c++) {
      if (stream->input_ports[c]) {
        WRAP(jack_port_unregister)
        (stream->context->jack_client, stream->input_ports[c]);
        stream->input_ports[c] = NULL;
      }
    }
  }

  if (stream->resampler) {
    cubeb_resampler_destroy(stream->resampler);
    stream->resampler = NULL;
  }
  stream->in_use = false;
  pthread_mutex_unlock(&stream->mutex);
}

static int
cbjack_stream_start(cubeb_stream * stream)
{
  stream->pause = false;
  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED);
  return CUBEB_OK;
}

static int
cbjack_stream_stop(cubeb_stream * stream)
{
  stream->pause = true;
  stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED);
  return CUBEB_OK;
}

static int
cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position)
{
  *position = stream->position;
  return CUBEB_OK;
}

static int
cbjack_stream_set_volume(cubeb_stream * stm, float volume)
{
  stm->volume = volume;
  return CUBEB_OK;
}

static int
cbjack_stream_get_current_device(cubeb_stream * stm,
                                 cubeb_device ** const device)
{
  *device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
  if (*device == NULL)
    return CUBEB_ERROR;

  const char * j_in = JACK_DEFAULT_IN;
  const char * j_out = JACK_DEFAULT_OUT;
  const char * empty = "";

  if (stm->devs == DUPLEX) {
    (*device)->input_name = strdup(j_in);
    (*device)->output_name = strdup(j_out);
  } else if (stm->devs == IN_ONLY) {
    (*device)->input_name = strdup(j_in);
    (*device)->output_name = strdup(empty);
  } else if (stm->devs == OUT_ONLY) {
    (*device)->input_name = strdup(empty);
    (*device)->output_name = strdup(j_out);
  }

  return CUBEB_OK;
}

static int
cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device)
{
  if (device->input_name)
    free(device->input_name);
  if (device->output_name)
    free(device->output_name);
  free(device);
  return CUBEB_OK;
}

static int
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
                         cubeb_device_collection * collection)
{
  if (!context)
    return CUBEB_ERROR;

  uint32_t rate;
  cbjack_get_preferred_sample_rate(context, &rate);

  cubeb_device_info * devices = new cubeb_device_info[2];
  if (!devices)
    return CUBEB_ERROR;
  PodZero(devices, 2);
  collection->count = 0;

  if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
    cubeb_device_info * cur = &devices[collection->count];
    cur->device_id = JACK_DEFAULT_OUT;
    cur->devid = (cubeb_devid)cur->device_id;
    cur->friendly_name = JACK_DEFAULT_OUT;
    cur->group_id = JACK_DEFAULT_OUT;
    cur->vendor_name = JACK_DEFAULT_OUT;
    cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
    cur->state = CUBEB_DEVICE_STATE_ENABLED;
    cur->preferred = CUBEB_DEVICE_PREF_ALL;
    cur->format = CUBEB_DEVICE_FMT_F32NE;
    cur->default_format = CUBEB_DEVICE_FMT_F32NE;
    cur->max_channels = MAX_CHANNELS;
    cur->min_rate = rate;
    cur->max_rate = rate;
    cur->default_rate = rate;
    cur->latency_lo = 0;
    cur->latency_hi = 0;
    collection->count += 1;
  }

  if (type & CUBEB_DEVICE_TYPE_INPUT) {
    cubeb_device_info * cur = &devices[collection->count];
    cur->device_id = JACK_DEFAULT_IN;
    cur->devid = (cubeb_devid)cur->device_id;
    cur->friendly_name = JACK_DEFAULT_IN;
    cur->group_id = JACK_DEFAULT_IN;
    cur->vendor_name = JACK_DEFAULT_IN;
    cur->type = CUBEB_DEVICE_TYPE_INPUT;
    cur->state = CUBEB_DEVICE_STATE_ENABLED;
    cur->preferred = CUBEB_DEVICE_PREF_ALL;
    cur->format = CUBEB_DEVICE_FMT_F32NE;
    cur->default_format = CUBEB_DEVICE_FMT_F32NE;
    cur->max_channels = MAX_CHANNELS;
    cur->min_rate = rate;
    cur->max_rate = rate;
    cur->default_rate = rate;
    cur->latency_lo = 0;
    cur->latency_hi = 0;
    collection->count += 1;
  }

  collection->device = devices;

  return CUBEB_OK;
}

static int
cbjack_device_collection_destroy(cubeb * /*ctx*/,
                                 cubeb_device_collection * collection)
{
  XASSERT(collection);
  delete[] collection->device;
  return CUBEB_OK;
}

Messung V0.5
C=97 H=92 G=94

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.