Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

725 portaudio rename endpoint_id to device_id #54

Merged
merged 7 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
47 changes: 22 additions & 25 deletions c_src/membrane_portaudio_plugin/pa_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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";
Expand All @@ -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,
Expand All @@ -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) {
Expand Down Expand Up @@ -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";
}
}

Expand All @@ -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;
Expand All @@ -166,7 +163,7 @@ PaSampleFormat string_to_PaSampleFormat(char* format) {
} else if (strcmp(format, "u8") == 0) {
return paUInt8;
}

return UNSUPPORTED_SAMPLE_FORMAT;
}

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

default:
return 0;
}
Expand Down
8 changes: 4 additions & 4 deletions c_src/membrane_portaudio_plugin/pa_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand Up @@ -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));

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion c_src/membrane_portaudio_plugin/sink.spec.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 3 additions & 4 deletions c_src/membrane_portaudio_plugin/source.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion c_src/membrane_portaudio_plugin/source.spec.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 21 additions & 4 deletions lib/membrane_portaudio_plugin/sink.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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
{[],
Expand All @@ -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,
Expand Down
25 changes: 21 additions & 4 deletions lib/membrane_portaudio_plugin/source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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: [
Comment on lines -23 to +25
Copy link
Member

@mat-hek mat-hek Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd leave the old option too, but document it as deprecated and warn in handle_init

spec: integer() | :default,
default: :default,
description: """
Expand All @@ -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,
Expand Down Expand Up @@ -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, [])
Expand All @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
[
Expand Down
Loading