Skip to content

Commit 3bc4db4

Browse files
committed
Plugins (portaudio): Add new input plugin from 0.4.x
(based on 0.4.x commit 5a65ba3)
1 parent c321cec commit 3bc4db4

6 files changed

Lines changed: 193 additions & 1 deletion

File tree

.github/workflows/linux.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ jobs:
7070
libasound2-dev \
7171
libsdl1.2-dev \
7272
libgl1-mesa-dev \
73-
pkg-config
73+
pkg-config \
74+
portaudio19-dev
7475
7576
- name: Install build dependency Clang ${{ matrix.clang_major_version }}
7677
if: "${{ contains(matrix.cxx, 'clang') }}"

.github/workflows/windows.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ jobs:
5353
mingw-w64-x86_64-ninja `
5454
mingw-w64-x86_64-gstreamer `
5555
mingw-w64-x86_64-libpng `
56+
mingw-w64-x86_64-portaudio `
5657
mingw-w64-x86_64-SDL `
5758
mingw-w64-x86_64-gettext
5859

libvisual-plugins/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ OPTION(ENABLE_NEBULUS "Build the Nebulus plugin" yes)
148148
OPTION(ENABLE_MPLAYER "Build the MPlayer input plugin" yes)
149149
OPTION(ENABLE_NASTYFFT "Build the NastyFFT plugin" yes)
150150
OPTION(ENABLE_PLAZMA "Build the Plazma plugin" yes)
151+
OPTION(ENABLE_PORTAUDIO "Build the PortAudio input plugin" yes)
151152
OPTION(ENABLE_PULSEAUDIO "Build the PulseAudio input plugin" yes)
152153
OPTION(ENABLE_OINKSIE "Build the Oinksie plugin" yes)
153154
OPTION(ENABLE_SLIDE "Build the Slide morph plugin" yes)
@@ -260,6 +261,14 @@ IF(ENABLE_NEBULUS)
260261
ENDIF()
261262
ENDIF()
262263

264+
IF(ENABLE_PORTAUDIO)
265+
PKG_CHECK_MODULES(PORTAUDIO portaudio-2.0 IMPORTED_TARGET)
266+
IF(NOT PORTAUDIO_FOUND)
267+
MESSAGE(WARNING "PortAudio not found. The PortAudio input plugin will not be built.")
268+
SET(ENABLE_PORTAUDIO no)
269+
ENDIF()
270+
ENDIF()
271+
263272
IF(ENABLE_PULSEAUDIO)
264273
PKG_CHECK_MODULES(PULSE libpulse>=${PULSE_REQUIRED_VERSION} IMPORTED_TARGET)
265274
IF(NOT PULSE_FOUND)

libvisual-plugins/plugins/input/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ IF(ENABLE_MPLAYER)
1414
ADD_SUBDIRECTORY(mplayer)
1515
ENDIF()
1616

17+
IF(ENABLE_PORTAUDIO)
18+
ADD_SUBDIRECTORY(portaudio)
19+
ENDIF()
20+
1721
IF(ENABLE_PULSEAUDIO)
1822
ADD_SUBDIRECTORY(pulseaudio)
1923
ENDIF()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
LV_BUILD_INPUT_PLUGIN(portaudio
2+
SOURCES input_portaudio.c
3+
LINK_LIBS PkgConfig::PORTAUDIO
4+
)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/* Libvisual-plugins - Standard plugins for libvisual
2+
*
3+
* Copyright (C) 2023 Sebastian Pipping <sebastian@pipping.org>
4+
*
5+
* Authors: Sebastian Pipping <sebastian@pipping.org>
6+
*
7+
* This program is free software; you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation; either version 2.1
10+
* of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20+
*/
21+
22+
#include <stdlib.h> // malloc
23+
24+
#include <libvisual/libvisual.h>
25+
#include "gettext.h"
26+
#include <portaudio.h>
27+
28+
VISUAL_PLUGIN_API_VERSION_VALIDATOR
29+
30+
#define SAMPLE_RATE 44100
31+
#define SAMPLE_RATE_TYPE_LV VISUAL_AUDIO_SAMPLE_RATE_44100
32+
#define CHANNELS 2
33+
#define CHANNELS_TYPE VISUAL_AUDIO_SAMPLE_CHANNEL_STEREO
34+
#define SAMPLE_FORMAT_BITS 16
35+
#define SAMPLE_FORMAT_PA paInt16
36+
#define SAMPLE_FORMAT_LV VISUAL_AUDIO_SAMPLE_FORMAT_S16
37+
38+
#define FRAMES 1024
39+
40+
typedef struct {
41+
PaStream *stream;
42+
void * buffer;
43+
} PortAudioPrivate;
44+
45+
static int inp_portaudio_init (VisPluginData *plugin);
46+
static int inp_portaudio_cleanup (VisPluginData *plugin);
47+
static int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio);
48+
49+
const VisPluginInfo *get_plugin_info (int *count)
50+
{
51+
static VisInputPlugin input[] = {{
52+
.upload = inp_portaudio_upload
53+
}};
54+
55+
static VisPluginInfo info[] = {{
56+
.type = VISUAL_PLUGIN_TYPE_INPUT,
57+
58+
.plugname = "portaudio",
59+
.name = "portaudio",
60+
.author = "Sebastian Pipping <sebastian@pipping.org>",
61+
.version = "1.0",
62+
.about = N_("PortAudio capture plugin"),
63+
.help = N_("Use this plugin to capture PCM data from the PortAudio record device"),
64+
.license = VISUAL_PLUGIN_LICENSE_LGPL,
65+
66+
.init = inp_portaudio_init,
67+
.cleanup = inp_portaudio_cleanup,
68+
69+
.plugin = VISUAL_OBJECT (&input[0])
70+
}};
71+
72+
*count = sizeof (info) / sizeof (*info);
73+
74+
return info;
75+
}
76+
77+
int inp_portaudio_init (VisPluginData *plugin)
78+
{
79+
#if ENABLE_NLS
80+
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
81+
#endif
82+
83+
PortAudioPrivate * const priv = visual_mem_new0 (PortAudioPrivate, 1);
84+
visual_log_return_val_if_fail(priv != NULL, -1);
85+
visual_object_set_private (VISUAL_OBJECT (plugin), priv);
86+
87+
// Init PortAudio
88+
const PaError init_error = Pa_Initialize();
89+
visual_log_return_val_if_fail (init_error == paNoError, -2);
90+
91+
// Select input device
92+
const PaDeviceIndex input_device = Pa_GetDefaultInputDevice ();
93+
visual_log_return_val_if_fail (input_device != paNoDevice, -3);
94+
95+
// Open stream
96+
const double latency_seconds = 1.0 / SAMPLE_RATE * FRAMES;
97+
const PaStreamParameters input_parameters = {
98+
.device = input_device,
99+
.channelCount = CHANNELS,
100+
.sampleFormat = SAMPLE_FORMAT_PA,
101+
.suggestedLatency = latency_seconds,
102+
.hostApiSpecificStreamInfo = NULL};
103+
const PaError open_error =
104+
Pa_OpenStream (&priv->stream, &input_parameters, NULL,
105+
SAMPLE_RATE, FRAMES, paClipOff, NULL, NULL);
106+
107+
// Allocate buffer
108+
const visual_size_t buffer_size_bytes = FRAMES * CHANNELS * (SAMPLE_FORMAT_BITS / 8);
109+
priv->buffer = malloc (buffer_size_bytes);
110+
visual_log_return_val_if_fail (priv->buffer != NULL, -4);
111+
112+
// Start stream
113+
const PaError input_start_error = Pa_StartStream (priv->stream);
114+
visual_log_return_val_if_fail (input_start_error == paNoError, -4);
115+
116+
return 0;
117+
}
118+
119+
int inp_portaudio_cleanup (VisPluginData *plugin)
120+
{
121+
visual_log_return_val_if_fail (plugin != NULL, -1);
122+
123+
PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin));
124+
visual_log_return_val_if_fail (priv != NULL, -2);
125+
126+
free (priv->buffer);
127+
priv->buffer = NULL;
128+
129+
Pa_StopStream (priv->stream);
130+
Pa_CloseStream (priv->stream);
131+
priv->stream = NULL;
132+
133+
Pa_Terminate ();
134+
135+
visual_mem_free (priv);
136+
137+
return 0;
138+
}
139+
140+
int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio)
141+
{
142+
visual_log_return_val_if_fail (plugin != NULL, -1);
143+
visual_log_return_val_if_fail (audio != NULL, -2);
144+
145+
PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin));
146+
visual_log_return_val_if_fail (priv != NULL, -3);
147+
visual_log_return_val_if_fail (priv->stream != NULL, -4);
148+
149+
for (;;) {
150+
long frames_to_read = Pa_GetStreamReadAvailable (priv->stream);
151+
if (frames_to_read < 1) {
152+
return 0;
153+
}
154+
if (frames_to_read > FRAMES) {
155+
frames_to_read = FRAMES; // because that's all that priv->buffer has space for
156+
}
157+
158+
const PaError read_error = Pa_ReadStream(priv->stream, priv->buffer, frames_to_read);
159+
if (read_error != paNoError) {
160+
return 0;
161+
}
162+
163+
const visual_size_t bytes_to_write = frames_to_read * CHANNELS * (SAMPLE_FORMAT_BITS / 8);
164+
165+
VisBuffer buffer;
166+
visual_buffer_init(&buffer, priv->buffer, bytes_to_write, NULL);
167+
168+
visual_audio_samplepool_input(audio->samplepool, &buffer, SAMPLE_RATE_TYPE_LV,
169+
SAMPLE_FORMAT_LV, CHANNELS_TYPE);
170+
}
171+
172+
// we never get here
173+
}

0 commit comments

Comments
 (0)