Skip to content

Commit

Permalink
Rename endpoint_id to device_id (#54)
Browse files Browse the repository at this point in the history
Co-authored-by: Łukasz Kita <lukasz.kita0@gmail.com>
Co-authored-by: Mateusz Front <mateusz.front@swmansion.com>
3 people authored Mar 14, 2024
1 parent 491ac97 commit 765f00d
Showing 12 changed files with 81 additions and 51 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ The plugin that captures and plays sound using the multiplatform PortAudio libra
Add the following line to your `deps` in `mix.exs`. Run `mix deps.get`.

```elixir
{:membrane_portaudio_plugin, "~> 0.18.4"}
{:membrane_portaudio_plugin, "~> 0.19.0"}
```

This package depends on the [PortAudio](http://portaudio.com/) library. The precompiled build will be pulled and linked automatically. However, should there be any problems, consider installing it manually.
@@ -87,6 +87,7 @@ defmodule Membrane.ReleaseTest.Pipeline do
end
```

_***Note***: the endpoint_id option was recently renamed to device_id to be more in line with PortAudio's API._
## Testing

Tests contain some cases that use PortAudio stuff instead of mocking. Such cases require the presence of at least one input and output sound card, thus they are disabled by default. To enable them, run
47 changes: 22 additions & 25 deletions c_src/membrane_portaudio_plugin/pa_helper.c
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@

char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
PaStream **stream, void *state, PaSampleFormat sample_format,
double *sample_rate, int *channels, char *latency_str, int *latency_ms,
int pa_buffer_size, PaDeviceIndex endpoint_id,
double *sample_rate, int *channels, char *latency_str,
int *latency_ms, int pa_buffer_size, PaDeviceIndex device_id,
PaStreamCallback *callback) {
char *ret_error = NULL;
PaError pa_error;
@@ -18,15 +18,15 @@ char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
goto error;
}

if (endpoint_id == paNoDevice) {
endpoint_id =
if (device_id == paNoDevice) {
device_id =
direction ? Pa_GetDefaultOutputDevice() : Pa_GetDefaultInputDevice();
}

const PaDeviceInfo *device_info = Pa_GetDeviceInfo(endpoint_id);
const PaDeviceInfo *device_info = Pa_GetDeviceInfo(device_id);
if (!device_info) {
MEMBRANE_WARN(env, "Invalid endpoint id: %d", endpoint_id);
ret_error = "invalid_endpoint_id";
MEMBRANE_WARN(env, "Invalid endpoint id: %d", device_id);
ret_error = "invalid_device_id";
goto error;
}

@@ -40,10 +40,10 @@ char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
ret_error = "invalid_latency";
goto error;
}
switch(direction) {

switch (direction) {
case STREAM_DIRECTION_IN:
if(*channels == 0) {
if (*channels == 0) {
*channels = device_info->maxInputChannels;
} else if (*channels > device_info->maxInputChannels) {
return "Device doesn't support that many input channels";
@@ -59,19 +59,18 @@ char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
break;
}

if(*sample_rate <= 0.0) {
switch(direction) {
if (*sample_rate <= 0.0) {
switch (direction) {
case STREAM_DIRECTION_IN:
*sample_rate = device_info->defaultSampleRate;
break;

case STREAM_DIRECTION_OUT:
return "Invalid sample rate value";
return "Invalid sample rate value";
}
}


PaStreamParameters stream_params = {.device = endpoint_id,
PaStreamParameters stream_params = {.device = device_id,
.channelCount = *channels,
.sampleFormat = sample_format,
.suggestedLatency = latency,
@@ -87,7 +86,7 @@ char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
pa_error =
Pa_OpenStream(stream, input_stream_params_ptr, output_stream_params_ptr,
*sample_rate, pa_buffer_size, paNoFlag, callback,
state // passed to the callback
state // passed to the callback
);

if (pa_error != paNoError) {
@@ -129,17 +128,15 @@ char *destroy_pa(UnifexEnv *env, char *log_tag, PaStream *stream) {
if (pa_error != paNoError) {
MEMBRANE_WARN(env, "Pa_StopStream: error = %d (%s)", pa_error,
Pa_GetErrorText(pa_error));
if (!error)
error = "pa_stop_stream";
if (!error) error = "pa_stop_stream";
}
}

pa_error = Pa_CloseStream(stream);
if (pa_error != paNoError && pa_error != paNotInitialized) {
MEMBRANE_WARN(env, "Pa_CloseStream: error = %d (%s)", pa_error,
Pa_GetErrorText(pa_error));
if (!error)
error = "pa_close_stream";
if (!error) error = "pa_close_stream";
}
}

@@ -152,8 +149,8 @@ char *destroy_pa(UnifexEnv *env, char *log_tag, PaStream *stream) {
return error;
}

PaSampleFormat string_to_PaSampleFormat(char* format) {
if(strcmp(format, "f32le") == 0) {
PaSampleFormat string_to_PaSampleFormat(char *format) {
if (strcmp(format, "f32le") == 0) {
return paFloat32;
} else if (strcmp(format, "s32le") == 0) {
return paInt32;
@@ -166,7 +163,7 @@ PaSampleFormat string_to_PaSampleFormat(char* format) {
} else if (strcmp(format, "u8") == 0) {
return paUInt8;
}

return UNSUPPORTED_SAMPLE_FORMAT;
}

@@ -185,7 +182,7 @@ int sample_size(PaSampleFormat sample_format) {
case paInt8:
case paUInt8:
return 1;

default:
return 0;
}
8 changes: 4 additions & 4 deletions c_src/membrane_portaudio_plugin/pa_helper.h
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
#include <membrane/membrane.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wextra"
#include <portaudio.h>
#pragma GCC diagnostic pop
#include <stdio.h>
@@ -14,13 +14,13 @@ typedef enum { STREAM_DIRECTION_IN, STREAM_DIRECTION_OUT } StreamDirection;

char *init_pa(UnifexEnv *env, char *log_tag, StreamDirection direction,
PaStream **stream, void *state, PaSampleFormat sample_format,
double *sample_rate, int *channels, char *latency_str, int *latency_ms,
int pa_buffer_size, PaDeviceIndex endpoint_id,
double *sample_rate, int *channels, char *latency_str,
int *latency_ms, int pa_buffer_size, PaDeviceIndex device_id,
PaStreamCallback *callback);

char *destroy_pa(UnifexEnv *env, char *log_tag, PaStream *stream);

#define UNSUPPORTED_SAMPLE_FORMAT 0
PaSampleFormat string_to_PaSampleFormat(char* format);
PaSampleFormat string_to_PaSampleFormat(char *format);

int sample_size(PaSampleFormat);
7 changes: 3 additions & 4 deletions c_src/membrane_portaudio_plugin/sink.c
Original file line number Diff line number Diff line change
@@ -5,8 +5,7 @@
#define BUFFERS_PER_TICK 100

void handle_destroy_state(UnifexEnv *env, SinkState *state) {
if (state->is_content_destroyed)
return;
if (state->is_content_destroyed) return;
SinkState *temp_state = unifex_alloc_state(env);
memcpy(temp_state, state, sizeof(SinkState));

@@ -58,7 +57,7 @@ static int callback(const void *_input_buffer, void *output_buffer,
}

UNIFEX_TERM create(UnifexEnv *env, UnifexPid demand_handler,
UnifexPid membrane_clock, int endpoint_id, int sample_rate,
UnifexPid membrane_clock, int device_id, int sample_rate,
int channels, char *sample_format_str, int ringbuffer_size,
int pa_buffer_size, char *latency) {
MEMBRANE_DEBUG(env, "initializing");
@@ -103,7 +102,7 @@ UNIFEX_TERM create(UnifexEnv *env, UnifexPid demand_handler,

error = init_pa(env, MEMBRANE_LOG_TAG, STREAM_DIRECTION_OUT, &(state->stream),
state, sample_format, &sample_rate_double, &channels, latency,
&latency_ms, pa_buffer_size, endpoint_id, callback);
&latency_ms, pa_buffer_size, device_id, callback);

if (error) {
goto error;
2 changes: 1 addition & 1 deletion c_src/membrane_portaudio_plugin/sink.spec.exs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ state_type "SinkState"
spec create(
demand_handler :: pid,
clock :: pid,
endpoint_id :: int,
device_id :: int,
sample_rate :: int,
channels :: int,
sample_format :: atom,
7 changes: 3 additions & 4 deletions c_src/membrane_portaudio_plugin/source.c
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@
#include <membrane/log.h>

void handle_destroy_state(UnifexEnv *env, SourceState *state) {
if (state->is_content_destroyed)
return;
if (state->is_content_destroyed) return;
SourceState *temp_state = unifex_alloc_state(env);
memcpy(temp_state, state, sizeof(SourceState));

@@ -42,7 +41,7 @@ static int callback(const void *input_buffer, void *_output_buffer,
return paContinue;
}

UNIFEX_TERM create(UnifexEnv *env, UnifexPid destination, int endpoint_id,
UNIFEX_TERM create(UnifexEnv *env, UnifexPid destination, int device_id,
int pa_buffer_size, char *latency, char *sample_format_str,
int channels, int sample_rate_int) {
MEMBRANE_DEBUG(env, "Initializing");
@@ -59,7 +58,7 @@ UNIFEX_TERM create(UnifexEnv *env, UnifexPid destination, int endpoint_id,
char *error =
init_pa(env, MEMBRANE_LOG_TAG, STREAM_DIRECTION_IN, &(state->stream),
state, sample_format, &sample_rate, &channels, latency,
&_latency_ms, pa_buffer_size, endpoint_id, callback);
&_latency_ms, pa_buffer_size, device_id, callback);

state->channels = channels;
state->frame_size = channels * sample_size(sample_format);
2 changes: 1 addition & 1 deletion c_src/membrane_portaudio_plugin/source.spec.exs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ state_type "SourceState"

spec create(
destination :: pid,
endpoint_id :: int,
device_id :: int,
pa_buffer_size :: int,
latency :: atom,
sample_format :: atom,
25 changes: 21 additions & 4 deletions lib/membrane_portaudio_plugin/sink.ex
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@ defmodule Membrane.PortAudio.Sink do

import Mockery.Macro

require Membrane.Logger

alias __MODULE__.Native
alias Membrane.Buffer
alias Membrane.PortAudio.SyncExecutor
@@ -26,7 +28,7 @@ defmodule Membrane.PortAudio.Sink do
accepted_format:
%RawAudio{sample_format: format} when format in [:f32le, :s32le, :s24le, :s16le, :s8, :u8]

def_options endpoint_id: [
def_options device_id: [
type: :integer,
spec: integer | :default,
default: :default,
@@ -37,6 +39,14 @@ defmodule Membrane.PortAudio.Sink do
`Membrane.PortAudio.print_devices/0`.
"""
],
endpoint_id: [
type: nil,
spec: nil,
default: nil,
description: """
Deprecated. Please use device_id instead.
"""
],
ringbuffer_size: [
type: :integer,
spec: pos_integer,
@@ -56,6 +66,13 @@ defmodule Membrane.PortAudio.Sink do
description: "Latency of the output device"
]

@impl true
def handle_init(ctx, %__MODULE__{endpoint_id: endpoint_id} = options)
when endpoint_id != nil do
Membrane.Logger.warning("endpoint_id option has been renamed to device_id")
handle_init(ctx, Map.delete(options, :endpoint_id))
end

@impl true
def handle_init(_ctx, %__MODULE__{} = options) do
{[],
@@ -70,19 +87,19 @@ defmodule Membrane.PortAudio.Sink do
@impl true
def handle_stream_format(:input, %Membrane.RawAudio{} = format, ctx, state) do
%{
endpoint_id: endpoint_id,
device_id: device_id,
ringbuffer_size: ringbuffer_size,
portaudio_buffer_size: pa_buffer_size,
latency: latency
} = state

endpoint_id = if endpoint_id == :default, do: @pa_no_device, else: endpoint_id
device_id = if device_id == :default, do: @pa_no_device, else: device_id

with {:ok, {latency_ms, native}} <-
SyncExecutor.apply(Native, :create, [
self(),
ctx.clock,
endpoint_id,
device_id,
format.sample_rate,
format.channels,
format.sample_format,
25 changes: 21 additions & 4 deletions lib/membrane_portaudio_plugin/source.ex
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@ defmodule Membrane.PortAudio.Source do

use Membrane.Source

require Membrane.Logger

alias __MODULE__.Native
alias Membrane.Buffer
alias Membrane.PortAudio.{OSXPermissions, SyncExecutor}
@@ -20,7 +22,7 @@ defmodule Membrane.PortAudio.Source do
flow_control: :push,
accepted_format: %RawAudio{sample_format: format} when format in @sample_formats

def_options endpoint_id: [
def_options device_id: [
spec: integer() | :default,
default: :default,
description: """
@@ -30,6 +32,14 @@ defmodule Membrane.PortAudio.Source do
`Membrane.PortAudio.print_devices/0`.
"""
],
endpoint_id: [
type: nil,
spec: nil,
default: nil,
description: """
Deprecated. Please use device_id instead.
"""
],
portaudio_buffer_size: [
spec: pos_integer(),
default: 256,
@@ -66,6 +76,13 @@ defmodule Membrane.PortAudio.Source do
"""
]

@impl true
def handle_init(ctx, %__MODULE__{endpoint_id: endpoint_id} = options)
when endpoint_id != nil do
Membrane.Logger.warning("endpoint_id option has been renamed to device_id")
handle_init(ctx, Map.delete(options, :endpoint_id))
end

@impl true
def handle_init(_ctx, %__MODULE__{} = options) do
if Code.ensure_loaded?(OSXPermissions), do: apply(OSXPermissions, :request_mic, [])
@@ -81,7 +98,7 @@ defmodule Membrane.PortAudio.Source do
@impl true
def handle_playing(ctx, state) do
%{
endpoint_id: endpoint_id,
device_id: device_id,
portaudio_buffer_size: pa_buffer_size,
latency: latency,
sample_format: sample_format,
@@ -90,12 +107,12 @@ defmodule Membrane.PortAudio.Source do
init_time: nil
} = state

endpoint_id = if endpoint_id == :default, do: @pa_no_device, else: endpoint_id
device_id = if device_id == :default, do: @pa_no_device, else: device_id

with {:ok, native, channels, sample_rate} <-
SyncExecutor.apply(Native, :create, [
self(),
endpoint_id,
device_id,
pa_buffer_size,
latency,
sample_format,
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ defmodule Membrane.PortAudio.Mixfile do
use Mix.Project

@github_url "https://github.com/membraneframework/membrane_portaudio_plugin"
@version "0.18.4"
@version "0.19.0"

def project do
[
2 changes: 1 addition & 1 deletion test/membrane_portaudio_plugin/sink_test.exs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ defmodule Membrane.Portaudio.SinkTest do
defp state(ctx) do
{_actions, state} =
@module.handle_init(ctx, %Sink{
endpoint_id: :default,
device_id: :default,
ringbuffer_size: 4096,
portaudio_buffer_size: 256,
latency: :high
2 changes: 1 addition & 1 deletion test/membrane_portaudio_plugin/source_test.exs
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ defmodule Membrane.Portaudio.SourceTest do

defp state(_ctx) do
state = %{
endpoint_id: :default,
device_id: :default,
portaudio_buffer_size: 256,
sample_format: :s16le,
channels: 0,

0 comments on commit 765f00d

Please sign in to comment.