mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	more hacking
Move plugin API to separate directory for now Add ringbuffer and way to get ringbuffer from a port
This commit is contained in:
		
							parent
							
								
									b8f6e99537
								
							
						
					
					
						commit
						3c029cba53
					
				
					 56 changed files with 7055 additions and 1530 deletions
				
			
		
							
								
								
									
										587
									
								
								spa/plugins/alsa/alsa-sink.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										587
									
								
								spa/plugins/alsa/alsa-sink.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,587 @@
 | 
			
		|||
/* Spa ALSA Sink
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
#include <asoundlib.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/audio/raw.h>
 | 
			
		||||
 | 
			
		||||
typedef struct _SpaALSASink SpaALSASink;
 | 
			
		||||
 | 
			
		||||
static const char default_device[] = "default";
 | 
			
		||||
static const uint32_t default_buffer_time = 10000;
 | 
			
		||||
static const uint32_t default_period_time = 5000;
 | 
			
		||||
static const bool default_period_event = 0;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaProps props;
 | 
			
		||||
  char device[64];
 | 
			
		||||
  char device_name[128];
 | 
			
		||||
  char card_name[128];
 | 
			
		||||
  uint32_t buffer_time;
 | 
			
		||||
  uint32_t period_time;
 | 
			
		||||
  bool period_event;
 | 
			
		||||
} SpaALSASinkProps;
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
reset_alsa_sink_props (SpaALSASinkProps *props)
 | 
			
		||||
{
 | 
			
		||||
  strncpy (props->device, default_device, 64);
 | 
			
		||||
  props->buffer_time = default_buffer_time;
 | 
			
		||||
  props->period_time = default_period_time;
 | 
			
		||||
  props->period_event = default_period_event;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  snd_pcm_t *handle;
 | 
			
		||||
  snd_output_t *output;
 | 
			
		||||
  snd_pcm_sframes_t buffer_size;
 | 
			
		||||
  snd_pcm_sframes_t period_size;
 | 
			
		||||
  snd_pcm_channel_area_t areas[16];
 | 
			
		||||
  pthread_t thread;
 | 
			
		||||
  bool running;
 | 
			
		||||
} SpaALSAState;
 | 
			
		||||
 | 
			
		||||
typedef struct _ALSABuffer ALSABuffer;
 | 
			
		||||
 | 
			
		||||
struct _ALSABuffer {
 | 
			
		||||
  SpaBuffer buffer;
 | 
			
		||||
  SpaMeta meta[1];
 | 
			
		||||
  SpaMetaHeader header;
 | 
			
		||||
  SpaData data[1];
 | 
			
		||||
  ALSABuffer *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _SpaALSASink {
 | 
			
		||||
  SpaHandle handle;
 | 
			
		||||
 | 
			
		||||
  SpaALSASinkProps tmp_props;
 | 
			
		||||
  SpaALSASinkProps props;
 | 
			
		||||
 | 
			
		||||
  bool activated;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaAudioRawFormat query_format;
 | 
			
		||||
  SpaAudioRawFormat current_format;
 | 
			
		||||
 | 
			
		||||
  SpaALSAState state;
 | 
			
		||||
 | 
			
		||||
  SpaPortInfo info;
 | 
			
		||||
  SpaPortStatus status;
 | 
			
		||||
 | 
			
		||||
  SpaBuffer *input_buffer;
 | 
			
		||||
 | 
			
		||||
  ALSABuffer buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "alsa-utils.c"
 | 
			
		||||
 | 
			
		||||
static const uint32_t min_uint32 = 1;
 | 
			
		||||
static const uint32_t max_uint32 = UINT32_MAX;
 | 
			
		||||
 | 
			
		||||
static const SpaPropRangeInfo uint32_range[] = {
 | 
			
		||||
  { "min", "Minimum value", 4, &min_uint32 },
 | 
			
		||||
  { "max", "Maximum value", 4, &max_uint32 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  PROP_ID_DEVICE,
 | 
			
		||||
  PROP_ID_DEVICE_NAME,
 | 
			
		||||
  PROP_ID_CARD_NAME,
 | 
			
		||||
  PROP_ID_BUFFER_TIME,
 | 
			
		||||
  PROP_ID_PERIOD_TIME,
 | 
			
		||||
  PROP_ID_PERIOD_EVENT,
 | 
			
		||||
  PROP_ID_LAST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const SpaPropInfo prop_info[] =
 | 
			
		||||
{
 | 
			
		||||
  { PROP_ID_DEVICE,            "device", "ALSA device, as defined in an asound configuration file",
 | 
			
		||||
                                SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                SPA_PROP_TYPE_STRING, 63,
 | 
			
		||||
                                strlen (default_device)+1, default_device,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, device),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
  { PROP_ID_DEVICE_NAME,       "device-name", "Human-readable name of the sound device",
 | 
			
		||||
                                SPA_PROP_FLAG_READABLE,
 | 
			
		||||
                                SPA_PROP_TYPE_STRING, 127,
 | 
			
		||||
                                0, NULL,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, device_name),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
  { PROP_ID_CARD_NAME,         "card-name", "Human-readable name of the sound card",
 | 
			
		||||
                                SPA_PROP_FLAG_READABLE,
 | 
			
		||||
                                SPA_PROP_TYPE_STRING, 127,
 | 
			
		||||
                                0, NULL,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, card_name),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
  { PROP_ID_BUFFER_TIME,       "buffer-time", "The total size of the buffer in time",
 | 
			
		||||
                                SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                sizeof (uint32_t), &default_buffer_time,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, buffer_time),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
  { PROP_ID_PERIOD_TIME,       "period-time", "The size of a period in time",
 | 
			
		||||
                                SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                                sizeof (uint32_t), &default_period_time,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, period_time),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
  { PROP_ID_PERIOD_EVENT,      "period-event", "Generate an event each period",
 | 
			
		||||
                                SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                                SPA_PROP_TYPE_BOOL, sizeof (bool),
 | 
			
		||||
                                sizeof (bool), &default_period_event,
 | 
			
		||||
                                SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                offsetof (SpaALSASinkProps, period_event),
 | 
			
		||||
                                0, 0,
 | 
			
		||||
                                NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_props (SpaHandle     *handle,
 | 
			
		||||
                              SpaProps     **props)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || props == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  memcpy (&this->tmp_props, &this->props, sizeof (this->props));
 | 
			
		||||
  *props = &this->tmp_props.props;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_set_props (SpaHandle       *handle,
 | 
			
		||||
                              const SpaProps  *props)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
  SpaALSASinkProps *p = &this->props;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (props == NULL) {
 | 
			
		||||
    reset_alsa_sink_props (p);
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res = spa_props_copy (props, &p->props);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_send_command (SpaHandle     *handle,
 | 
			
		||||
                                 SpaCommand    *command)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || command == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (command->type) {
 | 
			
		||||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (!this->activated) {
 | 
			
		||||
        spa_alsa_open (this);
 | 
			
		||||
        this->activated = true;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->activated) {
 | 
			
		||||
        spa_alsa_close (this);
 | 
			
		||||
        this->activated = false;
 | 
			
		||||
      }
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
      spa_alsa_start (this);
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
      spa_alsa_stop (this);
 | 
			
		||||
      break;
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
      return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_set_event_callback (SpaHandle     *handle,
 | 
			
		||||
                                       SpaEventCallback event,
 | 
			
		||||
                                       void          *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  this->event_cb = event;
 | 
			
		||||
  this->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_n_ports (SpaHandle     *handle,
 | 
			
		||||
                                unsigned int  *n_input_ports,
 | 
			
		||||
                                unsigned int  *max_input_ports,
 | 
			
		||||
                                unsigned int  *n_output_ports,
 | 
			
		||||
                                unsigned int  *max_output_ports)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports)
 | 
			
		||||
    *n_input_ports = 1;
 | 
			
		||||
  if (n_output_ports)
 | 
			
		||||
    *n_output_ports = 0;
 | 
			
		||||
  if (max_input_ports)
 | 
			
		||||
    *max_input_ports = 1;
 | 
			
		||||
  if (max_output_ports)
 | 
			
		||||
    *max_output_ports = 0;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_port_ids (SpaHandle     *handle,
 | 
			
		||||
                                 unsigned int   n_input_ports,
 | 
			
		||||
                                 uint32_t      *input_ids,
 | 
			
		||||
                                 unsigned int   n_output_ports,
 | 
			
		||||
                                 uint32_t      *output_ids)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports > 0)
 | 
			
		||||
    input_ids[0] = 0;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_add_port (SpaHandle      *handle,
 | 
			
		||||
                             SpaDirection    direction,
 | 
			
		||||
                             uint32_t       *port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_remove_port (SpaHandle      *handle,
 | 
			
		||||
                                uint32_t        port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		||||
                                      uint32_t         port_id,
 | 
			
		||||
                                      unsigned int     index,
 | 
			
		||||
                                      SpaFormat      **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      spa_audio_raw_format_init (&this->query_format);
 | 
			
		||||
      break;
 | 
			
		||||
    case 1:
 | 
			
		||||
      spa_audio_raw_format_init (&this->query_format);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_ENUM_END;
 | 
			
		||||
  }
 | 
			
		||||
  *format = &this->query_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                    uint32_t         port_id,
 | 
			
		||||
                                    int              test_only,
 | 
			
		||||
                                    const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (format == NULL) {
 | 
			
		||||
    this->have_format = false;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  printf ("format %d\n", this->current_format.info.rate);
 | 
			
		||||
 | 
			
		||||
  this->have_format = true;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_port_format (SpaHandle        *handle,
 | 
			
		||||
                                    uint32_t          port_id,
 | 
			
		||||
                                    const SpaFormat **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *format = &this->current_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_port_info (SpaHandle          *handle,
 | 
			
		||||
                                  uint32_t            port_id,
 | 
			
		||||
                                  const SpaPortInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  *info = &this->info;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_port_props (SpaHandle  *handle,
 | 
			
		||||
                                   uint32_t    port_id,
 | 
			
		||||
                                   SpaProps  **props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_set_port_props (SpaHandle       *handle,
 | 
			
		||||
                                   uint32_t         port_id,
 | 
			
		||||
                                   const SpaProps  *props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_get_port_status (SpaHandle            *handle,
 | 
			
		||||
                                    uint32_t              port_id,
 | 
			
		||||
                                    const SpaPortStatus **status)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || status == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  *status = &this->status;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_push_port_input (SpaHandle      *handle,
 | 
			
		||||
                                    unsigned int    n_info,
 | 
			
		||||
                                    SpaInputInfo   *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = (SpaALSASink *) handle;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
  bool have_error = false, have_enough = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    if (info[i].port_id != 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_PORT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (info[i].buffer != NULL) {
 | 
			
		||||
      if (!this->have_format) {
 | 
			
		||||
        info[i].status = SPA_RESULT_NO_FORMAT;
 | 
			
		||||
        have_error = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this->input_buffer != NULL) {
 | 
			
		||||
        info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
        have_enough = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      this->input_buffer = spa_buffer_ref (info[i].buffer);
 | 
			
		||||
    }
 | 
			
		||||
    info[i].status = SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
  if (have_enough)
 | 
			
		||||
    return SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_node_pull_port_output (SpaHandle      *handle,
 | 
			
		||||
                                     unsigned int    n_info,
 | 
			
		||||
                                     SpaOutputInfo  *info)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaNode alsasink_node = {
 | 
			
		||||
  sizeof (SpaNode),
 | 
			
		||||
  spa_alsa_sink_node_get_props,
 | 
			
		||||
  spa_alsa_sink_node_set_props,
 | 
			
		||||
  spa_alsa_sink_node_send_command,
 | 
			
		||||
  spa_alsa_sink_node_set_event_callback,
 | 
			
		||||
  spa_alsa_sink_node_get_n_ports,
 | 
			
		||||
  spa_alsa_sink_node_get_port_ids,
 | 
			
		||||
  spa_alsa_sink_node_add_port,
 | 
			
		||||
  spa_alsa_sink_node_remove_port,
 | 
			
		||||
  spa_alsa_sink_node_enum_port_formats,
 | 
			
		||||
  spa_alsa_sink_node_set_port_format,
 | 
			
		||||
  spa_alsa_sink_node_get_port_format,
 | 
			
		||||
  spa_alsa_sink_node_get_port_info,
 | 
			
		||||
  spa_alsa_sink_node_get_port_props,
 | 
			
		||||
  spa_alsa_sink_node_set_port_props,
 | 
			
		||||
  spa_alsa_sink_node_get_port_status,
 | 
			
		||||
  spa_alsa_sink_node_push_port_input,
 | 
			
		||||
  spa_alsa_sink_node_pull_port_output,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_alsa_sink_get_interface (SpaHandle               *handle,
 | 
			
		||||
                             uint32_t                 interface_id,
 | 
			
		||||
                             const void             **interface)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL || interface == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (interface_id) {
 | 
			
		||||
    case SPA_INTERFACE_ID_NODE:
 | 
			
		||||
      *interface = &alsasink_node;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_UNKNOWN_INTERFACE;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SpaHandle *
 | 
			
		||||
spa_alsa_sink_new (void)
 | 
			
		||||
{
 | 
			
		||||
  SpaHandle *handle;
 | 
			
		||||
  SpaALSASink *this;
 | 
			
		||||
 | 
			
		||||
  handle = calloc (1, sizeof (SpaALSASink));
 | 
			
		||||
  handle->get_interface = spa_alsa_sink_get_interface;
 | 
			
		||||
 | 
			
		||||
  this = (SpaALSASink *) handle;
 | 
			
		||||
  this->props.props.n_prop_info = PROP_ID_LAST;
 | 
			
		||||
  this->props.props.prop_info = prop_info;
 | 
			
		||||
  this->props.props.set_prop = spa_props_generic_set_prop;
 | 
			
		||||
  this->props.props.get_prop = spa_props_generic_get_prop;
 | 
			
		||||
  reset_alsa_sink_props (&this->props);
 | 
			
		||||
 | 
			
		||||
  this->info.flags = SPA_PORT_INFO_FLAG_NONE;
 | 
			
		||||
  this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
  return handle;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										367
									
								
								spa/plugins/alsa/alsa-utils.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								spa/plugins/alsa/alsa-utils.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,367 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
static int verbose = 0;					/* verbose flag */
 | 
			
		||||
 | 
			
		||||
#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; }
 | 
			
		||||
 | 
			
		||||
static snd_pcm_format_t
 | 
			
		||||
spa_alsa_format_to_alsa (SpaAudioFormat format)
 | 
			
		||||
{
 | 
			
		||||
  switch (format) {
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S8:
 | 
			
		||||
      return SND_PCM_FORMAT_S8;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U8:
 | 
			
		||||
      return SND_PCM_FORMAT_U8;
 | 
			
		||||
    /* 16 bit */
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S16LE:
 | 
			
		||||
      return SND_PCM_FORMAT_S16_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S16BE:
 | 
			
		||||
      return SND_PCM_FORMAT_S16_BE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U16LE:
 | 
			
		||||
      return SND_PCM_FORMAT_U16_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U16BE:
 | 
			
		||||
      return SND_PCM_FORMAT_U16_BE;
 | 
			
		||||
    /* 24 bit in low 3 bytes of 32 bits */
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S24_32LE:
 | 
			
		||||
      return SND_PCM_FORMAT_S24_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S24_32BE:
 | 
			
		||||
      return SND_PCM_FORMAT_S24_BE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U24_32LE:
 | 
			
		||||
      return SND_PCM_FORMAT_U24_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U24_32BE:
 | 
			
		||||
      return SND_PCM_FORMAT_U24_BE;
 | 
			
		||||
    /* 24 bit in 3 bytes */
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S24LE:
 | 
			
		||||
      return SND_PCM_FORMAT_S24_3LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S24BE:
 | 
			
		||||
      return SND_PCM_FORMAT_S24_3BE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U24LE:
 | 
			
		||||
      return SND_PCM_FORMAT_U24_3LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U24BE:
 | 
			
		||||
      return SND_PCM_FORMAT_U24_3BE;
 | 
			
		||||
    /* 32 bit */
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S32LE:
 | 
			
		||||
      return SND_PCM_FORMAT_S32_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_S32BE:
 | 
			
		||||
      return SND_PCM_FORMAT_S32_BE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U32LE:
 | 
			
		||||
      return SND_PCM_FORMAT_U32_LE;
 | 
			
		||||
    case SPA_AUDIO_FORMAT_U32BE:
 | 
			
		||||
      return SND_PCM_FORMAT_U32_BE;
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return SND_PCM_FORMAT_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
set_hwparams (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  unsigned int rrate;
 | 
			
		||||
  snd_pcm_uframes_t size;
 | 
			
		||||
  int err, dir;
 | 
			
		||||
  snd_pcm_hw_params_t *params;
 | 
			
		||||
  snd_pcm_format_t format;
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  SpaAudioRawFormat *fmt = &this->current_format;
 | 
			
		||||
  SpaAudioRawInfo *info = &fmt->info;
 | 
			
		||||
  snd_pcm_t *handle = state->handle;
 | 
			
		||||
  unsigned int buffer_time;
 | 
			
		||||
  unsigned int period_time;
 | 
			
		||||
 | 
			
		||||
  snd_pcm_hw_params_alloca (¶ms);
 | 
			
		||||
  /* choose all parameters */
 | 
			
		||||
  CHECK (snd_pcm_hw_params_any (handle, params), "Broken configuration for playback: no configurations available");
 | 
			
		||||
  /* set hardware resampling */
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_rate_resample (handle, params, 0), "set_rate_resample");
 | 
			
		||||
  /* set the interleaved read/write format */
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access");
 | 
			
		||||
 | 
			
		||||
  /* set the sample format */
 | 
			
		||||
  format = spa_alsa_format_to_alsa (info->format);
 | 
			
		||||
  printf ("Stream parameters are %iHz, %s, %i channels\n", info->rate, snd_pcm_format_name(format), info->channels);
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_format (handle, params, format), "set_format");
 | 
			
		||||
  /* set the count of channels */
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_channels (handle, params, info->channels), "set_channels");
 | 
			
		||||
  /* set the stream rate */
 | 
			
		||||
  rrate = info->rate;
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_rate_near (handle, params, &rrate, 0), "set_rate_near");
 | 
			
		||||
  if (rrate != info->rate) {
 | 
			
		||||
    printf("Rate doesn't match (requested %iHz, get %iHz)\n", info->rate, rrate);
 | 
			
		||||
    return -EINVAL;
 | 
			
		||||
  }
 | 
			
		||||
  /* set the buffer time */
 | 
			
		||||
  buffer_time = this->props.buffer_time;
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_buffer_time_near (handle, params, &buffer_time, &dir), "set_buffer_time_near");
 | 
			
		||||
  CHECK (snd_pcm_hw_params_get_buffer_size (params, &size), "get_buffer_size");
 | 
			
		||||
  state->buffer_size = size;
 | 
			
		||||
 | 
			
		||||
  /* set the period time */
 | 
			
		||||
  period_time = this->props.period_time;
 | 
			
		||||
  CHECK (snd_pcm_hw_params_set_period_time_near (handle, params, &period_time, &dir), "set_period_time_near");
 | 
			
		||||
  CHECK (snd_pcm_hw_params_get_period_size (params, &size, &dir), "get_period_size");
 | 
			
		||||
  state->period_size = size;
 | 
			
		||||
 | 
			
		||||
  /* write the parameters to device */
 | 
			
		||||
  CHECK (snd_pcm_hw_params (handle, params), "set_hw_params");
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
set_swparams (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  snd_pcm_t *handle = state->handle;
 | 
			
		||||
  int err = 0;
 | 
			
		||||
  snd_pcm_sw_params_t *params;
 | 
			
		||||
 | 
			
		||||
  snd_pcm_sw_params_alloca (¶ms);
 | 
			
		||||
 | 
			
		||||
  /* get the current params */
 | 
			
		||||
  CHECK (snd_pcm_sw_params_current (handle, params), "sw_params_current");
 | 
			
		||||
  /* start the transfer when the buffer is almost full: */
 | 
			
		||||
  /* (buffer_size / avail_min) * avail_min */
 | 
			
		||||
  CHECK (snd_pcm_sw_params_set_start_threshold (handle, params,
 | 
			
		||||
        (state->buffer_size / state->period_size) * state->period_size), "set_start_threshold");
 | 
			
		||||
 | 
			
		||||
  /* allow the transfer when at least period_size samples can be processed */
 | 
			
		||||
  /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
 | 
			
		||||
  CHECK (snd_pcm_sw_params_set_avail_min (handle, params,
 | 
			
		||||
        this->props.period_event ? state->buffer_size : state->period_size), "set_avail_min");
 | 
			
		||||
  /* enable period events when requested */
 | 
			
		||||
  if (this->props.period_event) {
 | 
			
		||||
    CHECK (snd_pcm_sw_params_set_period_event (handle, params, 1), "set_period_event");
 | 
			
		||||
  }
 | 
			
		||||
  /* write the parameters to the playback device */
 | 
			
		||||
  CHECK (snd_pcm_sw_params (handle, params), "sw_params");
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *   Underrun and suspend recovery
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
xrun_recovery (snd_pcm_t *handle, int err)
 | 
			
		||||
{
 | 
			
		||||
  if (verbose)
 | 
			
		||||
    printf("stream recovery\n");
 | 
			
		||||
  if (err == -EPIPE) {	/* under-run */
 | 
			
		||||
    err = snd_pcm_prepare(handle);
 | 
			
		||||
    if (err < 0)
 | 
			
		||||
      printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
 | 
			
		||||
    return 0;
 | 
			
		||||
  } else if (err == -ESTRPIPE) {
 | 
			
		||||
    while ((err = snd_pcm_resume(handle)) == -EAGAIN)
 | 
			
		||||
      sleep(1);	/* wait until the suspend flag is released */
 | 
			
		||||
    if (err < 0) {
 | 
			
		||||
      err = snd_pcm_prepare(handle);
 | 
			
		||||
      if (err < 0)
 | 
			
		||||
        printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *   Transfer method - direct write only
 | 
			
		||||
 */
 | 
			
		||||
static void *
 | 
			
		||||
direct_loop (void *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSASink *this = user_data;
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  snd_pcm_t *handle = state->handle;
 | 
			
		||||
  const snd_pcm_channel_area_t *my_areas;
 | 
			
		||||
  snd_pcm_uframes_t offset, frames, size;
 | 
			
		||||
  snd_pcm_sframes_t avail, commitres;
 | 
			
		||||
  snd_pcm_state_t st;
 | 
			
		||||
  int err, first = 1;
 | 
			
		||||
 | 
			
		||||
  while (state->running) {
 | 
			
		||||
    st = snd_pcm_state(handle);
 | 
			
		||||
    if (st == SND_PCM_STATE_XRUN) {
 | 
			
		||||
      err = xrun_recovery(handle, -EPIPE);
 | 
			
		||||
      if (err < 0) {
 | 
			
		||||
        printf("XRUN recovery failed: %s\n", snd_strerror(err));
 | 
			
		||||
        return NULL;
 | 
			
		||||
      }
 | 
			
		||||
      first = 1;
 | 
			
		||||
    } else if (st == SND_PCM_STATE_SUSPENDED) {
 | 
			
		||||
      err = xrun_recovery(handle, -ESTRPIPE);
 | 
			
		||||
      if (err < 0) {
 | 
			
		||||
        printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
 | 
			
		||||
        return NULL;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    avail = snd_pcm_avail_update(handle);
 | 
			
		||||
    if (avail < 0) {
 | 
			
		||||
      err = xrun_recovery(handle, avail);
 | 
			
		||||
      if (err < 0) {
 | 
			
		||||
        printf("avail update failed: %s\n", snd_strerror(err));
 | 
			
		||||
        return NULL;
 | 
			
		||||
      }
 | 
			
		||||
      first = 1;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (avail < state->period_size) {
 | 
			
		||||
      if (first) {
 | 
			
		||||
        first = 0;
 | 
			
		||||
        err = snd_pcm_start(handle);
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
          printf("Start error: %s\n", snd_strerror(err));
 | 
			
		||||
          exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        err = snd_pcm_wait(handle, -1);
 | 
			
		||||
        if (err < 0) {
 | 
			
		||||
          if ((err = xrun_recovery(handle, err)) < 0) {
 | 
			
		||||
            printf("snd_pcm_wait error: %s\n", snd_strerror(err));
 | 
			
		||||
            exit(EXIT_FAILURE);
 | 
			
		||||
          }
 | 
			
		||||
          first = 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    size = state->period_size;
 | 
			
		||||
    while (size > 0) {
 | 
			
		||||
      frames = size;
 | 
			
		||||
      err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
 | 
			
		||||
      if (err < 0) {
 | 
			
		||||
        if ((err = xrun_recovery(handle, err)) < 0) {
 | 
			
		||||
          printf("MMAP begin avail error: %s\n", snd_strerror(err));
 | 
			
		||||
          exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
        first = 1;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
        ALSABuffer *buffer = &this->buffer;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_PULL_INPUT;
 | 
			
		||||
        event.port_id = 0;
 | 
			
		||||
        event.size = frames * sizeof (uint16_t) * 2;
 | 
			
		||||
        event.data = buffer;
 | 
			
		||||
 | 
			
		||||
        buffer->buffer.refcount = 1;
 | 
			
		||||
        buffer->buffer.notify = NULL;
 | 
			
		||||
        buffer->buffer.size = frames * sizeof (uint16_t) * 2;
 | 
			
		||||
        buffer->buffer.n_metas = 1;
 | 
			
		||||
        buffer->buffer.metas = buffer->meta;
 | 
			
		||||
        buffer->buffer.n_datas = 1;
 | 
			
		||||
        buffer->buffer.datas = buffer->data;
 | 
			
		||||
 | 
			
		||||
        buffer->header.flags = 0;
 | 
			
		||||
        buffer->header.seq = 0;
 | 
			
		||||
        buffer->header.pts = 0;
 | 
			
		||||
        buffer->header.dts_offset = 0;
 | 
			
		||||
 | 
			
		||||
        buffer->meta[0].type = SPA_META_TYPE_HEADER;
 | 
			
		||||
        buffer->meta[0].data = &buffer->header;
 | 
			
		||||
        buffer->meta[0].size = sizeof (buffer->header);
 | 
			
		||||
 | 
			
		||||
        buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
 | 
			
		||||
        buffer->data[0].data = (uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2);
 | 
			
		||||
        buffer->data[0].size = frames * sizeof (uint16_t) * 2;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (&this->handle, &event,this->user_data);
 | 
			
		||||
 | 
			
		||||
        spa_buffer_unref ((SpaBuffer *)event.data);
 | 
			
		||||
      }
 | 
			
		||||
      if (this->input_buffer) {
 | 
			
		||||
        if (this->input_buffer != &this->buffer.buffer) {
 | 
			
		||||
          /* FIXME, copy input */
 | 
			
		||||
        }
 | 
			
		||||
        spa_buffer_unref (this->input_buffer);
 | 
			
		||||
        this->input_buffer = NULL;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      commitres = snd_pcm_mmap_commit(handle, offset, frames);
 | 
			
		||||
      if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
 | 
			
		||||
        if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
 | 
			
		||||
          printf("MMAP commit error: %s\n", snd_strerror(err));
 | 
			
		||||
          exit(EXIT_FAILURE);
 | 
			
		||||
        }
 | 
			
		||||
        first = 1;
 | 
			
		||||
      }
 | 
			
		||||
      size -= frames;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_alsa_open (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed");
 | 
			
		||||
 | 
			
		||||
  printf ("Playback device is '%s'\n", this->props.device);
 | 
			
		||||
  CHECK (snd_pcm_open (&state->handle,
 | 
			
		||||
                       this->props.device,
 | 
			
		||||
                       SND_PCM_STREAM_PLAYBACK,
 | 
			
		||||
                       SND_PCM_NONBLOCK |
 | 
			
		||||
                       SND_PCM_NO_AUTO_RESAMPLE |
 | 
			
		||||
                       SND_PCM_NO_AUTO_CHANNELS |
 | 
			
		||||
                       SND_PCM_NO_AUTO_FORMAT), "open failed");
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_alsa_start (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err;
 | 
			
		||||
 | 
			
		||||
  CHECK (set_hwparams (this), "hwparams");
 | 
			
		||||
  CHECK (set_swparams (this), "swparams");
 | 
			
		||||
 | 
			
		||||
  snd_pcm_dump (this->state.handle, this->state.output);
 | 
			
		||||
 | 
			
		||||
  state->running = true;
 | 
			
		||||
  if ((err = pthread_create (&state->thread, NULL, direct_loop, this)) != 0) {
 | 
			
		||||
    printf ("can't create thread: %d", err);
 | 
			
		||||
    state->running = false;
 | 
			
		||||
  }
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_alsa_stop (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
 | 
			
		||||
  if (state->running) {
 | 
			
		||||
    state->running = false;
 | 
			
		||||
    pthread_join (state->thread, NULL);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
spa_alsa_close (SpaALSASink *this)
 | 
			
		||||
{
 | 
			
		||||
  SpaALSAState *state = &this->state;
 | 
			
		||||
  int err = 0;
 | 
			
		||||
 | 
			
		||||
  CHECK (snd_pcm_close (state->handle), "close failed");
 | 
			
		||||
 | 
			
		||||
  return err;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								spa/plugins/alsa/alsa.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								spa/plugins/alsa/alsa.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* Spa ALSA support
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <spa/plugin.h>
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
 | 
			
		||||
SpaHandle * spa_alsa_sink_new (void);
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
alsa_sink_instantiate (const SpaHandleFactory  *factory,
 | 
			
		||||
                       SpaHandle              **handle)
 | 
			
		||||
{
 | 
			
		||||
  if (factory == NULL || handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  *handle = spa_alsa_sink_new ();
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaInterfaceInfo alsa_sink_interfaces[] =
 | 
			
		||||
{
 | 
			
		||||
  { SPA_INTERFACE_ID_NODE,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_NAME,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_DESCRIPTION,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
alsa_sink_enum_interface_info (const SpaHandleFactory  *factory,
 | 
			
		||||
                               unsigned int             index,
 | 
			
		||||
                               const SpaInterfaceInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *info = &alsa_sink_interfaces[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaHandleFactory factories[] =
 | 
			
		||||
{
 | 
			
		||||
  { "alsa-sink",
 | 
			
		||||
    NULL,
 | 
			
		||||
    alsa_sink_instantiate,
 | 
			
		||||
    alsa_sink_enum_interface_info,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SpaResult
 | 
			
		||||
spa_enum_handle_factory (unsigned int             index,
 | 
			
		||||
                         const SpaHandleFactory **factory)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *factory = &factories[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								spa/plugins/alsa/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								spa/plugins/alsa/meson.build
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
alsa_sources = ['alsa.c',
 | 
			
		||||
                'alsa-sink.c']
 | 
			
		||||
 | 
			
		||||
alsalib = shared_library('spa-alsa',
 | 
			
		||||
                          alsa_sources,
 | 
			
		||||
                          include_directories : inc,
 | 
			
		||||
                          dependencies : alsa_dep,
 | 
			
		||||
                          link_with : spalib,
 | 
			
		||||
                          install : true)
 | 
			
		||||
							
								
								
									
										740
									
								
								spa/plugins/audiomixer/audiomixer.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										740
									
								
								spa/plugins/audiomixer/audiomixer.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,740 @@
 | 
			
		|||
/* Spa
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/audio/raw.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_PORTS       128
 | 
			
		||||
 | 
			
		||||
typedef struct _SpaAudioMixer SpaAudioMixer;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaProps props;
 | 
			
		||||
} SpaAudioMixerProps;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaProps prop;
 | 
			
		||||
} SpaAudioMixerPortProps;
 | 
			
		||||
 | 
			
		||||
typedef struct _MixerBuffer MixerBuffer;
 | 
			
		||||
 | 
			
		||||
struct _MixerBuffer {
 | 
			
		||||
  SpaBuffer buffer;
 | 
			
		||||
  SpaMeta meta[1];
 | 
			
		||||
  SpaMetaHeader header;
 | 
			
		||||
  SpaData data[1];
 | 
			
		||||
  uint16_t samples[4096];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  bool valid;
 | 
			
		||||
  SpaAudioMixerPortProps props;
 | 
			
		||||
  SpaPortInfo info;
 | 
			
		||||
  SpaPortStatus status;
 | 
			
		||||
  SpaBuffer *buffer;
 | 
			
		||||
  size_t buffer_index;
 | 
			
		||||
  size_t buffer_offset;
 | 
			
		||||
  size_t buffer_queued;
 | 
			
		||||
  MixerBuffer mix;
 | 
			
		||||
} SpaAudioMixerPort;
 | 
			
		||||
 | 
			
		||||
struct _SpaAudioMixer {
 | 
			
		||||
  SpaHandle  handle;
 | 
			
		||||
 | 
			
		||||
  SpaAudioMixerProps props;
 | 
			
		||||
  SpaAudioMixerProps tmp_props;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaAudioRawFormat query_format;
 | 
			
		||||
  SpaAudioRawFormat current_format;
 | 
			
		||||
 | 
			
		||||
  int port_count;
 | 
			
		||||
  int port_queued;
 | 
			
		||||
  SpaAudioMixerPort ports[MAX_PORTS];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  PROP_ID_LAST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const SpaPropInfo prop_info[] =
 | 
			
		||||
{
 | 
			
		||||
  { PROP_ID_LAST, },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
reset_audiomixer_props (SpaAudioMixerProps *props)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_props (SpaHandle     *handle,
 | 
			
		||||
                               SpaProps     **props)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || props == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
 | 
			
		||||
  *props = &this->tmp_props.props;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_set_props (SpaHandle       *handle,
 | 
			
		||||
                               const SpaProps  *props)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  SpaAudioMixerProps *p = &this->props;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (props == NULL) {
 | 
			
		||||
    reset_audiomixer_props (p);
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  res = spa_props_copy (props, &p->props);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_send_command (SpaHandle     *handle,
 | 
			
		||||
                                  SpaCommand    *command)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || command == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (command->type) {
 | 
			
		||||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
      return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_set_event_callback (SpaHandle        *handle,
 | 
			
		||||
                                        SpaEventCallback  event,
 | 
			
		||||
                                        void             *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  this->event_cb = event;
 | 
			
		||||
  this->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_n_ports (SpaHandle     *handle,
 | 
			
		||||
                                 unsigned int  *n_input_ports,
 | 
			
		||||
                                 unsigned int  *max_input_ports,
 | 
			
		||||
                                 unsigned int  *n_output_ports,
 | 
			
		||||
                                 unsigned int  *max_output_ports)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports)
 | 
			
		||||
    *n_input_ports = 0;
 | 
			
		||||
  if (n_output_ports)
 | 
			
		||||
    *n_output_ports = 1;
 | 
			
		||||
  if (max_input_ports)
 | 
			
		||||
    *max_input_ports = MAX_PORTS;
 | 
			
		||||
  if (max_output_ports)
 | 
			
		||||
    *max_output_ports = 1;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_port_ids (SpaHandle     *handle,
 | 
			
		||||
                                  unsigned int   n_input_ports,
 | 
			
		||||
                                  uint32_t      *input_ids,
 | 
			
		||||
                                  unsigned int   n_output_ports,
 | 
			
		||||
                                  uint32_t      *output_ids)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  int i, idx;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (input_ids) {
 | 
			
		||||
    for (i = 1, idx = 0; i < MAX_PORTS && idx < n_input_ports; i++) {
 | 
			
		||||
      if (this->ports[i].valid)
 | 
			
		||||
        input_ids[idx++] = i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (n_output_ports > 0 && output_ids)
 | 
			
		||||
    output_ids[0] = 0;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_add_port (SpaHandle      *handle,
 | 
			
		||||
                              SpaDirection    direction,
 | 
			
		||||
                              uint32_t       *port_id)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || port_id == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (direction != SPA_DIRECTION_INPUT)
 | 
			
		||||
    return SPA_RESULT_INVALID_DIRECTION;
 | 
			
		||||
 | 
			
		||||
  for (i = 1; i < MAX_PORTS; i++)
 | 
			
		||||
    if (!this->ports[i].valid)
 | 
			
		||||
      break;
 | 
			
		||||
  if (i == MAX_PORTS)
 | 
			
		||||
    return SPA_RESULT_TOO_MANY_PORTS;
 | 
			
		||||
 | 
			
		||||
  this->ports[i].valid = true;
 | 
			
		||||
  *port_id = i;
 | 
			
		||||
  this->port_count++;
 | 
			
		||||
 | 
			
		||||
  this->ports[i].info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_REMOVABLE |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_OPTIONAL |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_IN_PLACE;
 | 
			
		||||
  this->ports[i].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
 | 
			
		||||
  this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_remove_port (SpaHandle      *handle,
 | 
			
		||||
                                 uint32_t        port_id)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id == 0 || port_id >= MAX_PORTS || !this->ports[port_id].valid)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  this->ports[port_id].valid = false;
 | 
			
		||||
  this->port_count--;
 | 
			
		||||
  if (this->ports[port_id].buffer) {
 | 
			
		||||
    spa_buffer_unref (this->ports[port_id].buffer);
 | 
			
		||||
    this->ports[port_id].buffer = NULL;
 | 
			
		||||
    this->port_queued--;
 | 
			
		||||
  }
 | 
			
		||||
  if (this->port_count == this->port_queued)
 | 
			
		||||
    this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_enum_port_formats (SpaHandle       *handle,
 | 
			
		||||
                                       uint32_t         port_id,
 | 
			
		||||
                                       unsigned int     index,
 | 
			
		||||
                                       SpaFormat      **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id > MAX_PORTS)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      spa_audio_raw_format_init (&this->query_format);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_ENUM_END;
 | 
			
		||||
  }
 | 
			
		||||
  *format = &this->query_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                     uint32_t         port_id,
 | 
			
		||||
                                     int              test_only,
 | 
			
		||||
                                     const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id > MAX_PORTS)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (format == NULL) {
 | 
			
		||||
    this->have_format = false;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  this->have_format = true;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_port_format (SpaHandle        *handle,
 | 
			
		||||
                                     uint32_t          port_id,
 | 
			
		||||
                                     const SpaFormat **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *format = &this->current_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_port_info (SpaHandle          *handle,
 | 
			
		||||
                                   uint32_t            port_id,
 | 
			
		||||
                                   const SpaPortInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  *info = &this->ports[port_id].info;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_port_props (SpaHandle *handle,
 | 
			
		||||
                                    uint32_t   port_id,
 | 
			
		||||
                                    SpaProps **props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_set_port_props (SpaHandle      *handle,
 | 
			
		||||
                                    uint32_t        port_id,
 | 
			
		||||
                                    const SpaProps *props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_get_port_status (SpaHandle            *handle,
 | 
			
		||||
                                     uint32_t              port_id,
 | 
			
		||||
                                     const SpaPortStatus **status)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || status == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= MAX_PORTS || !this->ports[port_id].valid)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *status = &this->ports[port_id].status;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_push_port_input (SpaHandle      *handle,
 | 
			
		||||
                                     unsigned int    n_info,
 | 
			
		||||
                                     SpaInputInfo   *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  SpaBuffer *buffer;
 | 
			
		||||
  SpaEvent *event;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
  bool have_error = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (this->ports[0].status.flags & SPA_PORT_STATUS_FLAG_HAVE_OUTPUT)
 | 
			
		||||
    return SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    int idx = info[i].port_id;
 | 
			
		||||
 | 
			
		||||
    if (idx >= MAX_PORTS || !this->ports[idx].valid) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_PORT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event = info[i].event;
 | 
			
		||||
    buffer = info[i].buffer;
 | 
			
		||||
 | 
			
		||||
    if (buffer == NULL && event == NULL) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (buffer) {
 | 
			
		||||
      if (!this->have_format) {
 | 
			
		||||
        info[i].status = SPA_RESULT_NO_FORMAT;
 | 
			
		||||
        have_error = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this->ports[idx].buffer != NULL) {
 | 
			
		||||
        info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
        have_error = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      this->ports[idx].buffer = spa_buffer_ref (buffer);
 | 
			
		||||
      this->ports[idx].buffer_queued = buffer->size;
 | 
			
		||||
      this->ports[idx].buffer_index = 0;
 | 
			
		||||
      this->ports[idx].buffer_offset = 0;
 | 
			
		||||
      this->port_queued++;
 | 
			
		||||
 | 
			
		||||
      if (this->port_queued == this->port_count)
 | 
			
		||||
        this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
    }
 | 
			
		||||
    if (event) {
 | 
			
		||||
      info[i].status = SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    info[i].status = SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
pull_port (SpaAudioMixer *this, uint32_t port_id, SpaOutputInfo *info, size_t pull_size)
 | 
			
		||||
{
 | 
			
		||||
  SpaEvent event;
 | 
			
		||||
  MixerBuffer *buffer = &this->ports[port_id].mix;
 | 
			
		||||
 | 
			
		||||
  event.refcount = 1;
 | 
			
		||||
  event.notify = NULL;
 | 
			
		||||
  event.type = SPA_EVENT_TYPE_PULL_INPUT;
 | 
			
		||||
  event.port_id = port_id;
 | 
			
		||||
  event.data = buffer;
 | 
			
		||||
 | 
			
		||||
  buffer->buffer.refcount = 1;
 | 
			
		||||
  buffer->buffer.notify = NULL;
 | 
			
		||||
  buffer->buffer.size = pull_size;
 | 
			
		||||
  buffer->buffer.n_metas = 1;
 | 
			
		||||
  buffer->buffer.metas = buffer->meta;
 | 
			
		||||
  buffer->buffer.n_datas = 1;
 | 
			
		||||
  buffer->buffer.datas = buffer->data;
 | 
			
		||||
 | 
			
		||||
  buffer->header.flags = 0;
 | 
			
		||||
  buffer->header.seq = 0;
 | 
			
		||||
  buffer->header.pts = 0;
 | 
			
		||||
  buffer->header.dts_offset = 0;
 | 
			
		||||
 | 
			
		||||
  buffer->meta[0].type = SPA_META_TYPE_HEADER;
 | 
			
		||||
  buffer->meta[0].data = &buffer->header;
 | 
			
		||||
  buffer->meta[0].size = sizeof (buffer->header);
 | 
			
		||||
 | 
			
		||||
  buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
 | 
			
		||||
  buffer->data[0].data = buffer->samples;
 | 
			
		||||
  buffer->data[0].size = pull_size;
 | 
			
		||||
 | 
			
		||||
  this->event_cb (&this->handle, &event, this->user_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
add_port_data (SpaAudioMixer *this, SpaBuffer *out, SpaAudioMixerPort *port)
 | 
			
		||||
{
 | 
			
		||||
  int i, oi = 0;
 | 
			
		||||
  uint8_t *op, *ip;
 | 
			
		||||
  size_t os, is, chunk;
 | 
			
		||||
 | 
			
		||||
  op = ip = NULL;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (op == NULL) {
 | 
			
		||||
      op = out->datas[oi].data;
 | 
			
		||||
      os = out->datas[oi].size;
 | 
			
		||||
    }
 | 
			
		||||
    if (ip == NULL) {
 | 
			
		||||
      ip = port->buffer->datas[port->buffer_index].data;
 | 
			
		||||
      is = port->buffer->datas[port->buffer_index].size;
 | 
			
		||||
      ip += port->buffer_offset;
 | 
			
		||||
      is -= port->buffer_offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    chunk = os < is ? os : is;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < chunk; i++)
 | 
			
		||||
      op[i] += ip[i];
 | 
			
		||||
 | 
			
		||||
    if ((is -= chunk) == 0) {
 | 
			
		||||
      if (++port->buffer_index == port->buffer->n_datas) {
 | 
			
		||||
        spa_buffer_unref (port->buffer);
 | 
			
		||||
        port->buffer = NULL;
 | 
			
		||||
        port->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
        this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      port->buffer_offset = 0;
 | 
			
		||||
      ip = NULL;
 | 
			
		||||
    } else {
 | 
			
		||||
      port->buffer_offset += chunk;
 | 
			
		||||
    }
 | 
			
		||||
    port->buffer_queued -= chunk;
 | 
			
		||||
 | 
			
		||||
    if ((os -= chunk) == 0) {
 | 
			
		||||
      if (++oi == out->n_datas)
 | 
			
		||||
        break;
 | 
			
		||||
      op = NULL;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
mix_data (SpaAudioMixer *this, SpaOutputInfo *info)
 | 
			
		||||
{
 | 
			
		||||
  int i, min_size, min_port, pull_size;
 | 
			
		||||
 | 
			
		||||
  if (info->port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (info->buffer) {
 | 
			
		||||
    pull_size = info->buffer->size;
 | 
			
		||||
  } else {
 | 
			
		||||
    pull_size = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  min_size = 0;
 | 
			
		||||
  min_port = 0;
 | 
			
		||||
  for (i = 1; i < MAX_PORTS; i++) {
 | 
			
		||||
    if (!this->ports[i].valid)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    if (this->ports[i].buffer == NULL) {
 | 
			
		||||
      if (pull_size && info->flags & SPA_OUTPUT_FLAG_PULL) {
 | 
			
		||||
        pull_port (this, i, info, pull_size);
 | 
			
		||||
      }
 | 
			
		||||
      if (this->ports[i].buffer == NULL)
 | 
			
		||||
        return SPA_RESULT_NEED_MORE_INPUT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (min_size == 0 || this->ports[i].buffer_queued < min_size) {
 | 
			
		||||
      min_size = this->ports[i].buffer_queued;
 | 
			
		||||
      min_port = i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (min_port == 0)
 | 
			
		||||
    return SPA_RESULT_NEED_MORE_INPUT;
 | 
			
		||||
 | 
			
		||||
  if (info->buffer) {
 | 
			
		||||
    if (info->buffer->size < min_size)
 | 
			
		||||
      min_size = info->buffer->size;
 | 
			
		||||
    else
 | 
			
		||||
      info->buffer->size = min_size;
 | 
			
		||||
  } else {
 | 
			
		||||
    info->buffer = this->ports[min_port].buffer;
 | 
			
		||||
    this->ports[min_port].buffer = NULL;
 | 
			
		||||
    this->ports[min_port].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
    this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (i = 1; i < MAX_PORTS; i++) {
 | 
			
		||||
    if (!this->ports[i].valid || this->ports[i].buffer == NULL)
 | 
			
		||||
      continue;
 | 
			
		||||
 | 
			
		||||
    add_port_data (this, info->buffer, &this->ports[i]);
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_node_pull_port_output (SpaHandle      *handle,
 | 
			
		||||
                                      unsigned int    n_info,
 | 
			
		||||
                                      SpaOutputInfo  *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioMixer *this = (SpaAudioMixer *) handle;
 | 
			
		||||
  int i;
 | 
			
		||||
  bool have_error = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (info->port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
//  if (!(this->ports[0].status.flags & SPA_PORT_STATUS_FLAG_HAVE_OUTPUT))
 | 
			
		||||
//    return SPA_RESULT_NEED_MORE_INPUT;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    if ((info[i].status = mix_data (this, &info[i])) < 0) {
 | 
			
		||||
      printf ("error mixing: %d\n", info[i].status);
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaNode audiomixer_node = {
 | 
			
		||||
  sizeof (SpaNode),
 | 
			
		||||
  spa_audiomixer_node_get_props,
 | 
			
		||||
  spa_audiomixer_node_set_props,
 | 
			
		||||
  spa_audiomixer_node_send_command,
 | 
			
		||||
  spa_audiomixer_node_set_event_callback,
 | 
			
		||||
  spa_audiomixer_node_get_n_ports,
 | 
			
		||||
  spa_audiomixer_node_get_port_ids,
 | 
			
		||||
  spa_audiomixer_node_add_port,
 | 
			
		||||
  spa_audiomixer_node_remove_port,
 | 
			
		||||
  spa_audiomixer_node_enum_port_formats,
 | 
			
		||||
  spa_audiomixer_node_set_port_format,
 | 
			
		||||
  spa_audiomixer_node_get_port_format,
 | 
			
		||||
  spa_audiomixer_node_get_port_info,
 | 
			
		||||
  spa_audiomixer_node_get_port_props,
 | 
			
		||||
  spa_audiomixer_node_set_port_props,
 | 
			
		||||
  spa_audiomixer_node_get_port_status,
 | 
			
		||||
  spa_audiomixer_node_push_port_input,
 | 
			
		||||
  spa_audiomixer_node_pull_port_output,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiomixer_get_interface (SpaHandle   *handle,
 | 
			
		||||
                              uint32_t     interface_id,
 | 
			
		||||
                              const void **interface)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL || interface == 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (interface_id) {
 | 
			
		||||
    case SPA_INTERFACE_ID_NODE:
 | 
			
		||||
      *interface = &audiomixer_node;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_UNKNOWN_INTERFACE;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SpaHandle *
 | 
			
		||||
spa_audiomixer_new (void)
 | 
			
		||||
{
 | 
			
		||||
  SpaHandle *handle;
 | 
			
		||||
  SpaAudioMixer *this;
 | 
			
		||||
 | 
			
		||||
  handle = calloc (1, sizeof (SpaAudioMixer));
 | 
			
		||||
  handle->get_interface = spa_audiomixer_get_interface;
 | 
			
		||||
 | 
			
		||||
  this = (SpaAudioMixer *) handle;
 | 
			
		||||
  this->props.props.n_prop_info = PROP_ID_LAST;
 | 
			
		||||
  this->props.props.prop_info = prop_info;
 | 
			
		||||
  this->props.props.set_prop = spa_props_generic_set_prop;
 | 
			
		||||
  this->props.props.get_prop = spa_props_generic_get_prop;
 | 
			
		||||
  reset_audiomixer_props (&this->props);
 | 
			
		||||
 | 
			
		||||
  this->ports[0].valid = true;
 | 
			
		||||
  this->ports[0].info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_NO_REF;
 | 
			
		||||
  return handle;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								spa/plugins/audiomixer/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spa/plugins/audiomixer/meson.build
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
audiomixer_sources = ['audiomixer.c', 'plugin.c']
 | 
			
		||||
 | 
			
		||||
audiomixerlib = shared_library('spa-audiomixer',
 | 
			
		||||
                          audiomixer_sources,
 | 
			
		||||
                          include_directories : inc,
 | 
			
		||||
                          link_with : spalib,
 | 
			
		||||
                          install : true)
 | 
			
		||||
							
								
								
									
										78
									
								
								spa/plugins/audiomixer/plugin.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								spa/plugins/audiomixer/plugin.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* Spa Audiomixer plugin
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <spa/plugin.h>
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
 | 
			
		||||
SpaHandle * spa_audiomixer_new (void);
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
audiomixer_instantiate (const SpaHandleFactory  *factory,
 | 
			
		||||
                        SpaHandle              **handle)
 | 
			
		||||
{
 | 
			
		||||
  if (factory == NULL || handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  *handle = spa_audiomixer_new ();
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaInterfaceInfo audiomixer_interfaces[] =
 | 
			
		||||
{
 | 
			
		||||
  { SPA_INTERFACE_ID_NODE,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_NAME,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_DESCRIPTION,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
audiomixer_enum_interface_info (const SpaHandleFactory  *factory,
 | 
			
		||||
                                unsigned int             index,
 | 
			
		||||
                                const SpaInterfaceInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *info = &audiomixer_interfaces[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaHandleFactory factories[] =
 | 
			
		||||
{
 | 
			
		||||
  { "audiomixer",
 | 
			
		||||
    NULL,
 | 
			
		||||
    audiomixer_instantiate,
 | 
			
		||||
    audiomixer_enum_interface_info,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SpaResult
 | 
			
		||||
spa_enum_handle_factory (unsigned int             index,
 | 
			
		||||
                         const SpaHandleFactory **factory)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *factory = &factories[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										526
									
								
								spa/plugins/audiotestsrc/audiotestsrc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										526
									
								
								spa/plugins/audiotestsrc/audiotestsrc.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,526 @@
 | 
			
		|||
/* Spa
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/audio/raw.h>
 | 
			
		||||
 | 
			
		||||
typedef struct _SpaAudioTestSrc SpaAudioTestSrc;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaProps props;
 | 
			
		||||
  uint32_t wave;
 | 
			
		||||
  double freq;
 | 
			
		||||
  double volume;
 | 
			
		||||
} SpaAudioTestSrcProps;
 | 
			
		||||
 | 
			
		||||
struct _SpaAudioTestSrc {
 | 
			
		||||
  SpaHandle handle;
 | 
			
		||||
 | 
			
		||||
  SpaAudioTestSrcProps props;
 | 
			
		||||
  SpaAudioTestSrcProps tmp_props;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
  SpaPortInfo info;
 | 
			
		||||
  SpaPortStatus status;
 | 
			
		||||
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaAudioRawFormat query_format;
 | 
			
		||||
  SpaAudioRawFormat current_format;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint32_t default_wave = 1.0;
 | 
			
		||||
static const double default_volume = 1.0;
 | 
			
		||||
static const double min_volume = 0.0;
 | 
			
		||||
static const double max_volume = 10.0;
 | 
			
		||||
static const double default_freq = 440.0;
 | 
			
		||||
static const double min_freq = 0.0;
 | 
			
		||||
static const double max_freq = 50000000.0;
 | 
			
		||||
 | 
			
		||||
static const SpaPropRangeInfo volume_range[] = {
 | 
			
		||||
  { "min", "Minimum value", sizeof (double), &min_volume },
 | 
			
		||||
  { "max", "Maximum value", sizeof (double), &max_volume },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const uint32_t wave_val_sine = 0;
 | 
			
		||||
static const uint32_t wave_val_square = 1;
 | 
			
		||||
 | 
			
		||||
static const SpaPropRangeInfo wave_range[] = {
 | 
			
		||||
  { "sine", "Sine", sizeof (uint32_t), &wave_val_sine },
 | 
			
		||||
  { "square", "Square", sizeof (uint32_t), &wave_val_square },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const SpaPropRangeInfo freq_range[] = {
 | 
			
		||||
  { "min", "Minimum value", sizeof (double), &min_freq },
 | 
			
		||||
  { "max", "Maximum value", sizeof (double), &max_freq },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  PROP_ID_WAVE,
 | 
			
		||||
  PROP_ID_FREQ,
 | 
			
		||||
  PROP_ID_VOLUME,
 | 
			
		||||
  PROP_ID_LAST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const SpaPropInfo prop_info[] =
 | 
			
		||||
{
 | 
			
		||||
  { PROP_ID_WAVE,              "wave", "Oscillator waveform",
 | 
			
		||||
                               SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                               SPA_PROP_TYPE_UINT32, sizeof (uint32_t),
 | 
			
		||||
                               sizeof (uint32_t), &default_wave,
 | 
			
		||||
                               SPA_PROP_RANGE_TYPE_ENUM, SPA_N_ELEMENTS (wave_range), wave_range,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               offsetof (SpaAudioTestSrcProps, wave),
 | 
			
		||||
                               0, 0,
 | 
			
		||||
                               NULL },
 | 
			
		||||
  { PROP_ID_FREQ,              "freq", "Frequency of test signal. The sample rate needs to be at least 4 times higher",
 | 
			
		||||
                               SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                               SPA_PROP_TYPE_DOUBLE, sizeof (double),
 | 
			
		||||
                               sizeof (double), &default_freq,
 | 
			
		||||
                               SPA_PROP_RANGE_TYPE_MIN_MAX, 2, freq_range,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               offsetof (SpaAudioTestSrcProps, freq),
 | 
			
		||||
                               0, 0,
 | 
			
		||||
                               NULL },
 | 
			
		||||
  { PROP_ID_VOLUME,            "volume", "The Volume factor",
 | 
			
		||||
                               SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                               SPA_PROP_TYPE_DOUBLE, sizeof (double),
 | 
			
		||||
                               sizeof (double), &default_volume,
 | 
			
		||||
                               SPA_PROP_RANGE_TYPE_MIN_MAX, 2, volume_range,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               offsetof (SpaAudioTestSrcProps, volume),
 | 
			
		||||
                               0, 0,
 | 
			
		||||
                               NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
reset_audiotestsrc_props (SpaAudioTestSrcProps *props)
 | 
			
		||||
{
 | 
			
		||||
  props->wave = default_wave;
 | 
			
		||||
  props->freq = default_freq;
 | 
			
		||||
  props->volume = default_volume;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_props (SpaHandle     *handle,
 | 
			
		||||
                                 SpaProps     **props)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || props == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
 | 
			
		||||
  *props = &this->tmp_props.props;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_set_props (SpaHandle       *handle,
 | 
			
		||||
                                 const SpaProps  *props)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
  SpaAudioTestSrcProps *p = &this->props;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (props == NULL) {
 | 
			
		||||
    reset_audiotestsrc_props (p);
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  res = spa_props_copy (props, &p->props);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_send_command (SpaHandle     *handle,
 | 
			
		||||
                                    SpaCommand    *command)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || command == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (command->type) {
 | 
			
		||||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
      return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_set_event_callback (SpaHandle     *handle,
 | 
			
		||||
                                          SpaEventCallback event,
 | 
			
		||||
                                          void          *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  this->event_cb = event;
 | 
			
		||||
  this->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_n_ports (SpaHandle     *handle,
 | 
			
		||||
                                   unsigned int  *n_input_ports,
 | 
			
		||||
                                   unsigned int  *max_input_ports,
 | 
			
		||||
                                   unsigned int  *n_output_ports,
 | 
			
		||||
                                   unsigned int  *max_output_ports)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports)
 | 
			
		||||
    *n_input_ports = 0;
 | 
			
		||||
  if (n_output_ports)
 | 
			
		||||
    *n_output_ports = 1;
 | 
			
		||||
  if (max_input_ports)
 | 
			
		||||
    *max_input_ports = 0;
 | 
			
		||||
  if (max_output_ports)
 | 
			
		||||
    *max_output_ports = 1;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_port_ids (SpaHandle     *handle,
 | 
			
		||||
                                    unsigned int   n_input_ports,
 | 
			
		||||
                                    uint32_t      *input_ids,
 | 
			
		||||
                                    unsigned int   n_output_ports,
 | 
			
		||||
                                    uint32_t      *output_ids)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL || input_ids == NULL || output_ids == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_output_ports > 0)
 | 
			
		||||
    output_ids[0] = 0;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_add_port (SpaHandle      *handle,
 | 
			
		||||
                                SpaDirection    direction,
 | 
			
		||||
                                uint32_t       *port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_remove_port (SpaHandle      *handle,
 | 
			
		||||
                                   uint32_t        port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_enum_port_formats (SpaHandle        *handle,
 | 
			
		||||
                                         uint32_t          port_id,
 | 
			
		||||
                                         unsigned int      index,
 | 
			
		||||
                                         SpaFormat       **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      spa_audio_raw_format_init (&this->query_format);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_ENUM_END;
 | 
			
		||||
  }
 | 
			
		||||
  *format = &this->query_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                       uint32_t         port_id,
 | 
			
		||||
                                       int              test_only,
 | 
			
		||||
                                       const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (format == NULL) {
 | 
			
		||||
    this->have_format = false;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  this->have_format = true;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_port_format (SpaHandle        *handle,
 | 
			
		||||
                                       uint32_t          port_id,
 | 
			
		||||
                                       const SpaFormat **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *format = &this->current_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_port_info (SpaHandle          *handle,
 | 
			
		||||
                                     uint32_t            port_id,
 | 
			
		||||
                                     const SpaPortInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  *info = &this->info;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_port_props (SpaHandle *handle,
 | 
			
		||||
                                      uint32_t   port_id,
 | 
			
		||||
                                      SpaProps **props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_set_port_props (SpaHandle      *handle,
 | 
			
		||||
                                      uint32_t        port_id,
 | 
			
		||||
                                      const SpaProps *props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_get_port_status (SpaHandle            *handle,
 | 
			
		||||
                                       uint32_t              port_id,
 | 
			
		||||
                                       const SpaPortStatus **status)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || status == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *status = &this->status;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_push_port_input (SpaHandle     *handle,
 | 
			
		||||
                                       unsigned int   n_info,
 | 
			
		||||
                                       SpaInputInfo  *info)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_node_pull_port_output (SpaHandle     *handle,
 | 
			
		||||
                                        unsigned int   n_info,
 | 
			
		||||
                                        SpaOutputInfo *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaAudioTestSrc *this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
  size_t j, size;
 | 
			
		||||
  uint8_t *ptr;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
  bool have_error = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    if (info[i].port_id != 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_PORT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this->have_format) {
 | 
			
		||||
      info[i].status = SPA_RESULT_NO_FORMAT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (info[i].buffer == NULL || info[i].buffer->n_datas == 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ptr = info[i].buffer->datas[0].data;
 | 
			
		||||
    size = info[i].buffer->datas[0].size;
 | 
			
		||||
 | 
			
		||||
    for (j = 0; j < size; j++)
 | 
			
		||||
      ptr[j] = rand();
 | 
			
		||||
 | 
			
		||||
    info[i].status = SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaNode audiotestsrc_node = {
 | 
			
		||||
  sizeof (SpaNode),
 | 
			
		||||
  spa_audiotestsrc_node_get_props,
 | 
			
		||||
  spa_audiotestsrc_node_set_props,
 | 
			
		||||
  spa_audiotestsrc_node_send_command,
 | 
			
		||||
  spa_audiotestsrc_node_set_event_callback,
 | 
			
		||||
  spa_audiotestsrc_node_get_n_ports,
 | 
			
		||||
  spa_audiotestsrc_node_get_port_ids,
 | 
			
		||||
  spa_audiotestsrc_node_add_port,
 | 
			
		||||
  spa_audiotestsrc_node_remove_port,
 | 
			
		||||
  spa_audiotestsrc_node_enum_port_formats,
 | 
			
		||||
  spa_audiotestsrc_node_set_port_format,
 | 
			
		||||
  spa_audiotestsrc_node_get_port_format,
 | 
			
		||||
  spa_audiotestsrc_node_get_port_info,
 | 
			
		||||
  spa_audiotestsrc_node_get_port_props,
 | 
			
		||||
  spa_audiotestsrc_node_set_port_props,
 | 
			
		||||
  spa_audiotestsrc_node_get_port_status,
 | 
			
		||||
  spa_audiotestsrc_node_push_port_input,
 | 
			
		||||
  spa_audiotestsrc_node_pull_port_output,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_audiotestsrc_get_interface (SpaHandle               *handle,
 | 
			
		||||
                                uint32_t                 interface_id,
 | 
			
		||||
                                const void             **interface)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL || interface == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (interface_id) {
 | 
			
		||||
    case SPA_INTERFACE_ID_NODE:
 | 
			
		||||
      *interface = &audiotestsrc_node;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_UNKNOWN_INTERFACE;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SpaHandle *
 | 
			
		||||
spa_audiotestsrc_new (void)
 | 
			
		||||
{
 | 
			
		||||
  SpaHandle *handle;
 | 
			
		||||
  SpaAudioTestSrc *this;
 | 
			
		||||
 | 
			
		||||
  handle = calloc (1, sizeof (SpaAudioTestSrc));
 | 
			
		||||
  handle->get_interface = spa_audiotestsrc_get_interface;
 | 
			
		||||
 | 
			
		||||
  this = (SpaAudioTestSrc *) handle;
 | 
			
		||||
  this->props.props.n_prop_info = PROP_ID_LAST;
 | 
			
		||||
  this->props.props.prop_info = prop_info;
 | 
			
		||||
  this->props.props.set_prop = spa_props_generic_set_prop;
 | 
			
		||||
  this->props.props.get_prop = spa_props_generic_get_prop;
 | 
			
		||||
  reset_audiotestsrc_props (&this->props);
 | 
			
		||||
 | 
			
		||||
  this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
 | 
			
		||||
                     SPA_PORT_INFO_FLAG_NO_REF;
 | 
			
		||||
  this->status.flags = SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
 | 
			
		||||
  return handle;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								spa/plugins/audiotestsrc/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spa/plugins/audiotestsrc/meson.build
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
audiotestsrc_sources = ['audiotestsrc.c', 'plugin.c']
 | 
			
		||||
 | 
			
		||||
audiotestsrclib = shared_library('spa-audiotestsrc',
 | 
			
		||||
                          audiotestsrc_sources,
 | 
			
		||||
                          include_directories : inc,
 | 
			
		||||
                          link_with : spalib,
 | 
			
		||||
                          install : true)
 | 
			
		||||
							
								
								
									
										78
									
								
								spa/plugins/audiotestsrc/plugin.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								spa/plugins/audiotestsrc/plugin.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* Spa Volume plugin
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <spa/plugin.h>
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
 | 
			
		||||
SpaHandle * spa_audiotestsrc_new (void);
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
audiotestsrc_instantiate (const SpaHandleFactory  *factory,
 | 
			
		||||
                          SpaHandle              **handle)
 | 
			
		||||
{
 | 
			
		||||
  if (factory == NULL || handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  *handle = spa_audiotestsrc_new ();
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaInterfaceInfo audiotestsrc_interfaces[] =
 | 
			
		||||
{
 | 
			
		||||
  { SPA_INTERFACE_ID_NODE,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_NAME,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_DESCRIPTION,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
audiotestsrc_enum_interface_info (const SpaHandleFactory  *factory,
 | 
			
		||||
                                  unsigned int             index,
 | 
			
		||||
                                  const SpaInterfaceInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *info = &audiotestsrc_interfaces[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaHandleFactory factories[] =
 | 
			
		||||
{
 | 
			
		||||
  { "audiotestsrc",
 | 
			
		||||
    NULL,
 | 
			
		||||
    audiotestsrc_instantiate,
 | 
			
		||||
    audiotestsrc_enum_interface_info,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SpaResult
 | 
			
		||||
spa_enum_handle_factory (unsigned int             index,
 | 
			
		||||
                         const SpaHandleFactory **factory)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *factory = &factories[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								spa/plugins/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spa/plugins/meson.build
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
subdir('alsa')
 | 
			
		||||
subdir('audiomixer')
 | 
			
		||||
subdir('audiotestsrc')
 | 
			
		||||
subdir('volume')
 | 
			
		||||
							
								
								
									
										7
									
								
								spa/plugins/volume/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								spa/plugins/volume/meson.build
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
volume_sources = ['volume.c', 'plugin.c']
 | 
			
		||||
 | 
			
		||||
volumelib = shared_library('spa-volume',
 | 
			
		||||
                           volume_sources,
 | 
			
		||||
                           include_directories : inc,
 | 
			
		||||
                           link_with : spalib,
 | 
			
		||||
                           install : true)
 | 
			
		||||
							
								
								
									
										78
									
								
								spa/plugins/volume/plugin.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								spa/plugins/volume/plugin.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
/* Spa Volume plugin
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <spa/plugin.h>
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
 | 
			
		||||
SpaHandle * spa_volume_new (void);
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
volume_instantiate (const SpaHandleFactory  *factory,
 | 
			
		||||
                    SpaHandle              **handle)
 | 
			
		||||
{
 | 
			
		||||
  if (factory == NULL || handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  *handle = spa_volume_new ();
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaInterfaceInfo volume_interfaces[] =
 | 
			
		||||
{
 | 
			
		||||
  { SPA_INTERFACE_ID_NODE,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_NAME,
 | 
			
		||||
    SPA_INTERFACE_ID_NODE_DESCRIPTION,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
volume_enum_interface_info (const SpaHandleFactory  *factory,
 | 
			
		||||
                            unsigned int             index,
 | 
			
		||||
                            const SpaInterfaceInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *info = &volume_interfaces[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaHandleFactory factories[] =
 | 
			
		||||
{
 | 
			
		||||
  { "volume",
 | 
			
		||||
    NULL,
 | 
			
		||||
    volume_instantiate,
 | 
			
		||||
    volume_enum_interface_info,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SpaResult
 | 
			
		||||
spa_enum_handle_factory (unsigned int             index,
 | 
			
		||||
                         const SpaHandleFactory **factory)
 | 
			
		||||
{
 | 
			
		||||
  if (index >= 1)
 | 
			
		||||
    return SPA_RESULT_ENUM_END;
 | 
			
		||||
 | 
			
		||||
  *factory = &factories[index];
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										605
									
								
								spa/plugins/volume/volume.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								spa/plugins/volume/volume.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,605 @@
 | 
			
		|||
/* Spa
 | 
			
		||||
 * Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Library General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Library General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Library General Public
 | 
			
		||||
 * License along with this library; if not, write to the
 | 
			
		||||
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/node.h>
 | 
			
		||||
#include <spa/audio/raw.h>
 | 
			
		||||
 | 
			
		||||
typedef struct _SpaVolume SpaVolume;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  SpaProps props;
 | 
			
		||||
  double volume;
 | 
			
		||||
  bool mute;
 | 
			
		||||
} SpaVolumeProps;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaPortInfo info;
 | 
			
		||||
  SpaPortStatus status;
 | 
			
		||||
} SpaVolumePort;
 | 
			
		||||
 | 
			
		||||
struct _SpaVolume {
 | 
			
		||||
  SpaHandle  handle;
 | 
			
		||||
 | 
			
		||||
  SpaVolumeProps props;
 | 
			
		||||
  SpaVolumeProps tmp_props;
 | 
			
		||||
 | 
			
		||||
  SpaEventCallback event_cb;
 | 
			
		||||
  void *user_data;
 | 
			
		||||
 | 
			
		||||
  bool have_format;
 | 
			
		||||
  SpaAudioRawFormat query_format;
 | 
			
		||||
  SpaAudioRawFormat current_format;
 | 
			
		||||
 | 
			
		||||
  SpaVolumePort ports[2];
 | 
			
		||||
  SpaBuffer *input_buffer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const double default_volume = 1.0;
 | 
			
		||||
static const double min_volume = 0.0;
 | 
			
		||||
static const double max_volume = 10.0;
 | 
			
		||||
static const bool default_mute = false;
 | 
			
		||||
 | 
			
		||||
static const SpaPropRangeInfo volume_range[] = {
 | 
			
		||||
  { "min", "Minimum value", sizeof (double), &min_volume },
 | 
			
		||||
  { "max", "Maximum value", sizeof (double), &max_volume },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
  PROP_ID_VOLUME,
 | 
			
		||||
  PROP_ID_MUTE,
 | 
			
		||||
  PROP_ID_LAST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const SpaPropInfo prop_info[] =
 | 
			
		||||
{
 | 
			
		||||
  { PROP_ID_VOLUME,            "volume", "The Volume factor",
 | 
			
		||||
                               SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                               SPA_PROP_TYPE_DOUBLE, sizeof (double),
 | 
			
		||||
                               sizeof (double), &default_volume,
 | 
			
		||||
                               SPA_PROP_RANGE_TYPE_MIN_MAX, 2, volume_range,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               offsetof (SpaVolumeProps, volume),
 | 
			
		||||
                               0, 0,
 | 
			
		||||
                               NULL },
 | 
			
		||||
  { PROP_ID_MUTE,              "mute", "Mute",
 | 
			
		||||
                               SPA_PROP_FLAG_READWRITE,
 | 
			
		||||
                               SPA_PROP_TYPE_BOOL, sizeof (bool),
 | 
			
		||||
                               sizeof (bool), &default_mute,
 | 
			
		||||
                               SPA_PROP_RANGE_TYPE_NONE, 0, NULL,
 | 
			
		||||
                               NULL,
 | 
			
		||||
                               offsetof (SpaVolumeProps, mute),
 | 
			
		||||
                               0, 0,
 | 
			
		||||
                               NULL },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
reset_volume_props (SpaVolumeProps *props)
 | 
			
		||||
{
 | 
			
		||||
  props->volume = default_volume;
 | 
			
		||||
  props->mute = default_mute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_props (SpaHandle      *handle,
 | 
			
		||||
                            SpaProps     **props)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || props == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  memcpy (&this->tmp_props, &this->props, sizeof (this->tmp_props));
 | 
			
		||||
  *props = &this->tmp_props.props;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_set_props (SpaHandle        *handle,
 | 
			
		||||
                            const SpaProps  *props)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
  SpaVolumeProps *p = &this->props;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (props == NULL) {
 | 
			
		||||
    reset_volume_props (p);
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  res = spa_props_copy (props, &p->props);
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_send_command (SpaHandle     *handle,
 | 
			
		||||
                              SpaCommand    *command)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || command == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (command->type) {
 | 
			
		||||
    case SPA_COMMAND_INVALID:
 | 
			
		||||
      return SPA_RESULT_INVALID_COMMAND;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_ACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_ACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_DEACTIVATE:
 | 
			
		||||
      if (this->event_cb) {
 | 
			
		||||
        SpaEvent event;
 | 
			
		||||
 | 
			
		||||
        event.refcount = 1;
 | 
			
		||||
        event.notify = NULL;
 | 
			
		||||
        event.type = SPA_EVENT_TYPE_DEACTIVATED;
 | 
			
		||||
        event.port_id = -1;
 | 
			
		||||
        event.data = NULL;
 | 
			
		||||
        event.size = 0;
 | 
			
		||||
 | 
			
		||||
        this->event_cb (handle, &event, this->user_data);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case SPA_COMMAND_START:
 | 
			
		||||
    case SPA_COMMAND_STOP:
 | 
			
		||||
    case SPA_COMMAND_FLUSH:
 | 
			
		||||
    case SPA_COMMAND_DRAIN:
 | 
			
		||||
    case SPA_COMMAND_MARKER:
 | 
			
		||||
      return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_set_event_callback (SpaHandle        *handle,
 | 
			
		||||
                                    SpaEventCallback  event,
 | 
			
		||||
                                    void             *user_data)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  this->event_cb = event;
 | 
			
		||||
  this->user_data = user_data;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_n_ports (SpaHandle     *handle,
 | 
			
		||||
                             unsigned int  *n_input_ports,
 | 
			
		||||
                             unsigned int  *max_input_ports,
 | 
			
		||||
                             unsigned int  *n_output_ports,
 | 
			
		||||
                             unsigned int  *max_output_ports)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports)
 | 
			
		||||
    *n_input_ports = 1;
 | 
			
		||||
  if (n_output_ports)
 | 
			
		||||
    *n_output_ports = 1;
 | 
			
		||||
  if (max_input_ports)
 | 
			
		||||
    *max_input_ports = 1;
 | 
			
		||||
  if (max_output_ports)
 | 
			
		||||
    *max_output_ports = 1;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_port_ids (SpaHandle     *handle,
 | 
			
		||||
                              unsigned int   n_input_ports,
 | 
			
		||||
                              uint32_t      *input_ids,
 | 
			
		||||
                              unsigned int   n_output_ports,
 | 
			
		||||
                              uint32_t      *output_ids)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (n_input_ports > 0 && input_ids)
 | 
			
		||||
    input_ids[0] = 0;
 | 
			
		||||
  if (n_output_ports > 0 && output_ids)
 | 
			
		||||
    output_ids[0] = 1;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_add_port (SpaHandle      *handle,
 | 
			
		||||
                          SpaDirection    direction,
 | 
			
		||||
                          uint32_t       *port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_remove_port (SpaHandle      *handle,
 | 
			
		||||
                             uint32_t        port_id)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_enum_port_formats (SpaHandle        *handle,
 | 
			
		||||
                                   uint32_t          port_id,
 | 
			
		||||
                                   unsigned int      index,
 | 
			
		||||
                                   SpaFormat       **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id != 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  switch (index) {
 | 
			
		||||
    case 0:
 | 
			
		||||
      spa_audio_raw_format_init (&this->query_format);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_ENUM_END;
 | 
			
		||||
  }
 | 
			
		||||
  *format = &this->query_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_set_port_format (SpaHandle       *handle,
 | 
			
		||||
                                 uint32_t         port_id,
 | 
			
		||||
                                 int              test_only,
 | 
			
		||||
                                 const SpaFormat *format)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
  SpaResult res;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= 2)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (format == NULL) {
 | 
			
		||||
    this->ports[port_id].have_format = false;
 | 
			
		||||
    return SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((res = spa_audio_raw_format_parse (format, &this->current_format)) < 0)
 | 
			
		||||
    return res;
 | 
			
		||||
 | 
			
		||||
  this->ports[port_id].have_format = true;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_port_format (SpaHandle        *handle,
 | 
			
		||||
                                 uint32_t          port_id,
 | 
			
		||||
                                 const SpaFormat **format)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || format == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= 2)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->ports[port_id].have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *format = &this->current_format.format;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_port_info (SpaHandle          *handle,
 | 
			
		||||
                               uint32_t            port_id,
 | 
			
		||||
                               const SpaPortInfo **info)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= 2)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  *info = &this->ports[port_id].info;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_port_props (SpaHandle  *handle,
 | 
			
		||||
                                 uint32_t    port_id,
 | 
			
		||||
                                 SpaProps **props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_set_port_props (SpaHandle       *handle,
 | 
			
		||||
                                 uint32_t         port_id,
 | 
			
		||||
                                 const SpaProps *props)
 | 
			
		||||
{
 | 
			
		||||
  return SPA_RESULT_NOT_IMPLEMENTED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_get_port_status (SpaHandle            *handle,
 | 
			
		||||
                                 uint32_t              port_id,
 | 
			
		||||
                                 const SpaPortStatus **status)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || status == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (port_id >= 2)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->ports[port_id].have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  *status = &this->ports[port_id].status;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_push_port_input (SpaHandle      *handle,
 | 
			
		||||
                                 unsigned int    n_info,
 | 
			
		||||
                                 SpaInputInfo   *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
  SpaBuffer *buffer;
 | 
			
		||||
  SpaEvent *event;
 | 
			
		||||
  unsigned int i;
 | 
			
		||||
  bool have_error = false;
 | 
			
		||||
  bool have_enough = false;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < n_info; i++) {
 | 
			
		||||
    if (info[i].port_id != 0) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_PORT;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event = info[i].event;
 | 
			
		||||
    buffer = info[i].buffer;
 | 
			
		||||
 | 
			
		||||
    if (buffer == NULL && event == NULL) {
 | 
			
		||||
      info[i].status = SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
      have_error = true;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (buffer) {
 | 
			
		||||
      if (!this->ports[0].have_format) {
 | 
			
		||||
        info[i].status = SPA_RESULT_NO_FORMAT;
 | 
			
		||||
        have_error = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this->input_buffer != NULL) {
 | 
			
		||||
        info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
        have_enough = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      this->input_buffer = spa_buffer_ref (buffer);
 | 
			
		||||
 | 
			
		||||
      this->ports[0].status.flags &= ~SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
      this->ports[1].status.flags |= SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
    }
 | 
			
		||||
    if (event) {
 | 
			
		||||
      switch (event->type) {
 | 
			
		||||
        default:
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    info[i].status = SPA_RESULT_OK;
 | 
			
		||||
  }
 | 
			
		||||
  if (have_error)
 | 
			
		||||
    return SPA_RESULT_ERROR;
 | 
			
		||||
  if (have_enough)
 | 
			
		||||
    return SPA_RESULT_HAVE_ENOUGH_INPUT;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_node_pull_port_output (SpaHandle      *handle,
 | 
			
		||||
                                  unsigned int    n_info,
 | 
			
		||||
                                  SpaOutputInfo  *info)
 | 
			
		||||
{
 | 
			
		||||
  SpaVolume *this = (SpaVolume *) handle;
 | 
			
		||||
  unsigned int si, di, i, n_samples, n_bytes, soff, doff ;
 | 
			
		||||
  SpaBuffer *sbuf, *dbuf;
 | 
			
		||||
  SpaData *sd, *dd;
 | 
			
		||||
  uint16_t *src, *dst;
 | 
			
		||||
  double volume;
 | 
			
		||||
 | 
			
		||||
  if (handle == NULL || n_info == 0 || info == NULL)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  if (info->port_id != 1)
 | 
			
		||||
    return SPA_RESULT_INVALID_PORT;
 | 
			
		||||
 | 
			
		||||
  if (!this->ports[1].have_format)
 | 
			
		||||
    return SPA_RESULT_NO_FORMAT;
 | 
			
		||||
 | 
			
		||||
  if (this->input_buffer == NULL)
 | 
			
		||||
    return SPA_RESULT_NEED_MORE_INPUT;
 | 
			
		||||
 | 
			
		||||
  volume = this->props.volume;
 | 
			
		||||
 | 
			
		||||
  sbuf = this->input_buffer;
 | 
			
		||||
  dbuf = info->buffer ? info->buffer : this->input_buffer;
 | 
			
		||||
 | 
			
		||||
  si = di = 0;
 | 
			
		||||
  soff = doff = 0;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (si == sbuf->n_datas || di == dbuf->n_datas)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    sd = &sbuf->datas[si];
 | 
			
		||||
    dd = &dbuf->datas[di];
 | 
			
		||||
 | 
			
		||||
    if (sd->type != SPA_DATA_TYPE_MEMPTR) {
 | 
			
		||||
      si++;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (dd->type != SPA_DATA_TYPE_MEMPTR) {
 | 
			
		||||
      di++;
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    src = (uint16_t*) ((uint8_t*)sd->data + soff);
 | 
			
		||||
    dst = (uint16_t*) ((uint8_t*)dd->data + doff);
 | 
			
		||||
 | 
			
		||||
    n_bytes = MIN (sd->size - soff, dd->size - doff);
 | 
			
		||||
    n_samples = n_bytes / sizeof (uint16_t);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < n_samples; i++)
 | 
			
		||||
      *src++ = *dst++ * volume;
 | 
			
		||||
 | 
			
		||||
    soff += n_bytes;
 | 
			
		||||
    doff += n_bytes;
 | 
			
		||||
 | 
			
		||||
    if (soff >= sd->size) {
 | 
			
		||||
      si++;
 | 
			
		||||
      soff = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (doff >= dd->size) {
 | 
			
		||||
      di++;
 | 
			
		||||
      doff = 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (sbuf != dbuf)
 | 
			
		||||
    spa_buffer_unref (sbuf);
 | 
			
		||||
 | 
			
		||||
  this->input_buffer = NULL;
 | 
			
		||||
  info->buffer = dbuf;
 | 
			
		||||
 | 
			
		||||
  this->ports[0].status.flags |= SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
  this->ports[1].status.flags &= ~SPA_PORT_STATUS_FLAG_HAVE_OUTPUT;
 | 
			
		||||
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const SpaNode volume_node = {
 | 
			
		||||
  sizeof (SpaNode),
 | 
			
		||||
  spa_volume_node_get_props,
 | 
			
		||||
  spa_volume_node_set_props,
 | 
			
		||||
  spa_volume_node_send_command,
 | 
			
		||||
  spa_volume_node_set_event_callback,
 | 
			
		||||
  spa_volume_node_get_n_ports,
 | 
			
		||||
  spa_volume_node_get_port_ids,
 | 
			
		||||
  spa_volume_node_add_port,
 | 
			
		||||
  spa_volume_node_remove_port,
 | 
			
		||||
  spa_volume_node_enum_port_formats,
 | 
			
		||||
  spa_volume_node_set_port_format,
 | 
			
		||||
  spa_volume_node_get_port_format,
 | 
			
		||||
  spa_volume_node_get_port_info,
 | 
			
		||||
  spa_volume_node_get_port_props,
 | 
			
		||||
  spa_volume_node_set_port_props,
 | 
			
		||||
  spa_volume_node_get_port_status,
 | 
			
		||||
  spa_volume_node_push_port_input,
 | 
			
		||||
  spa_volume_node_pull_port_output,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SpaResult
 | 
			
		||||
spa_volume_get_interface (SpaHandle               *handle,
 | 
			
		||||
                          uint32_t                 interface_id,
 | 
			
		||||
                          const void             **interface)
 | 
			
		||||
{
 | 
			
		||||
  if (handle == NULL || interface == 0)
 | 
			
		||||
    return SPA_RESULT_INVALID_ARGUMENTS;
 | 
			
		||||
 | 
			
		||||
  switch (interface_id) {
 | 
			
		||||
    case SPA_INTERFACE_ID_NODE:
 | 
			
		||||
      *interface = &volume_node;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return SPA_RESULT_UNKNOWN_INTERFACE;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  return SPA_RESULT_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SpaHandle *
 | 
			
		||||
spa_volume_new (void)
 | 
			
		||||
{
 | 
			
		||||
  SpaHandle *handle;
 | 
			
		||||
  SpaVolume *this;
 | 
			
		||||
 | 
			
		||||
  handle = calloc (1, sizeof (SpaVolume));
 | 
			
		||||
  handle->get_interface = spa_volume_get_interface;
 | 
			
		||||
 | 
			
		||||
  this = (SpaVolume *) handle;
 | 
			
		||||
  this->props.props.n_prop_info = PROP_ID_LAST;
 | 
			
		||||
  this->props.props.prop_info = prop_info;
 | 
			
		||||
  this->props.props.set_prop = spa_props_generic_set_prop;
 | 
			
		||||
  this->props.props.get_prop = spa_props_generic_get_prop;
 | 
			
		||||
  reset_volume_props (&this->props);
 | 
			
		||||
 | 
			
		||||
  this->ports[0].info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_IN_PLACE;
 | 
			
		||||
  this->ports[1].info.flags = SPA_PORT_INFO_FLAG_CAN_GIVE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_CAN_USE_BUFFER |
 | 
			
		||||
                              SPA_PORT_INFO_FLAG_NO_REF;
 | 
			
		||||
 | 
			
		||||
  this->ports[0].status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT;
 | 
			
		||||
  this->ports[1].status.flags = SPA_PORT_STATUS_FLAG_NONE;
 | 
			
		||||
 | 
			
		||||
  return handle;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue