From 3bc4db4063c9cf3cec27fb08f723deb6bcea726a Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sat, 21 Jan 2023 02:18:00 +0100 Subject: [PATCH 1/3] Plugins (portaudio): Add new input plugin from 0.4.x (based on 0.4.x commit 5a65ba361097c233e4442d0effea90383fc2ea80) --- .github/workflows/linux.yml | 3 +- .github/workflows/windows.yml | 1 + libvisual-plugins/CMakeLists.txt | 9 + .../plugins/input/CMakeLists.txt | 4 + .../plugins/input/portaudio/CMakeLists.txt | 4 + .../plugins/input/portaudio/input_portaudio.c | 173 ++++++++++++++++++ 6 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 libvisual-plugins/plugins/input/portaudio/CMakeLists.txt create mode 100644 libvisual-plugins/plugins/input/portaudio/input_portaudio.c diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 1a40a768e..2cd0f2d72 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -70,7 +70,8 @@ jobs: libasound2-dev \ libsdl1.2-dev \ libgl1-mesa-dev \ - pkg-config + pkg-config \ + portaudio19-dev - name: Install build dependency Clang ${{ matrix.clang_major_version }} if: "${{ contains(matrix.cxx, 'clang') }}" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index cdc9e72c1..908e2cf84 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -53,6 +53,7 @@ jobs: mingw-w64-x86_64-ninja ` mingw-w64-x86_64-gstreamer ` mingw-w64-x86_64-libpng ` + mingw-w64-x86_64-portaudio ` mingw-w64-x86_64-SDL ` mingw-w64-x86_64-gettext diff --git a/libvisual-plugins/CMakeLists.txt b/libvisual-plugins/CMakeLists.txt index 14b7b1b23..5b30e91a7 100644 --- a/libvisual-plugins/CMakeLists.txt +++ b/libvisual-plugins/CMakeLists.txt @@ -148,6 +148,7 @@ OPTION(ENABLE_NEBULUS "Build the Nebulus plugin" yes) OPTION(ENABLE_MPLAYER "Build the MPlayer input plugin" yes) OPTION(ENABLE_NASTYFFT "Build the NastyFFT plugin" yes) OPTION(ENABLE_PLAZMA "Build the Plazma plugin" yes) +OPTION(ENABLE_PORTAUDIO "Build the PortAudio input plugin" yes) OPTION(ENABLE_PULSEAUDIO "Build the PulseAudio input plugin" yes) OPTION(ENABLE_OINKSIE "Build the Oinksie plugin" yes) OPTION(ENABLE_SLIDE "Build the Slide morph plugin" yes) @@ -260,6 +261,14 @@ IF(ENABLE_NEBULUS) ENDIF() ENDIF() +IF(ENABLE_PORTAUDIO) + PKG_CHECK_MODULES(PORTAUDIO portaudio-2.0 IMPORTED_TARGET) + IF(NOT PORTAUDIO_FOUND) + MESSAGE(WARNING "PortAudio not found. The PortAudio input plugin will not be built.") + SET(ENABLE_PORTAUDIO no) + ENDIF() +ENDIF() + IF(ENABLE_PULSEAUDIO) PKG_CHECK_MODULES(PULSE libpulse>=${PULSE_REQUIRED_VERSION} IMPORTED_TARGET) IF(NOT PULSE_FOUND) diff --git a/libvisual-plugins/plugins/input/CMakeLists.txt b/libvisual-plugins/plugins/input/CMakeLists.txt index dec51f638..8fd315792 100644 --- a/libvisual-plugins/plugins/input/CMakeLists.txt +++ b/libvisual-plugins/plugins/input/CMakeLists.txt @@ -14,6 +14,10 @@ IF(ENABLE_MPLAYER) ADD_SUBDIRECTORY(mplayer) ENDIF() +IF(ENABLE_PORTAUDIO) + ADD_SUBDIRECTORY(portaudio) +ENDIF() + IF(ENABLE_PULSEAUDIO) ADD_SUBDIRECTORY(pulseaudio) ENDIF() diff --git a/libvisual-plugins/plugins/input/portaudio/CMakeLists.txt b/libvisual-plugins/plugins/input/portaudio/CMakeLists.txt new file mode 100644 index 000000000..7ab87510c --- /dev/null +++ b/libvisual-plugins/plugins/input/portaudio/CMakeLists.txt @@ -0,0 +1,4 @@ +LV_BUILD_INPUT_PLUGIN(portaudio + SOURCES input_portaudio.c + LINK_LIBS PkgConfig::PORTAUDIO +) diff --git a/libvisual-plugins/plugins/input/portaudio/input_portaudio.c b/libvisual-plugins/plugins/input/portaudio/input_portaudio.c new file mode 100644 index 000000000..ffa634fec --- /dev/null +++ b/libvisual-plugins/plugins/input/portaudio/input_portaudio.c @@ -0,0 +1,173 @@ +/* Libvisual-plugins - Standard plugins for libvisual + * + * Copyright (C) 2023 Sebastian Pipping + * + * Authors: Sebastian Pipping + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include // malloc + +#include +#include "gettext.h" +#include + +VISUAL_PLUGIN_API_VERSION_VALIDATOR + +#define SAMPLE_RATE 44100 +#define SAMPLE_RATE_TYPE_LV VISUAL_AUDIO_SAMPLE_RATE_44100 +#define CHANNELS 2 +#define CHANNELS_TYPE VISUAL_AUDIO_SAMPLE_CHANNEL_STEREO +#define SAMPLE_FORMAT_BITS 16 +#define SAMPLE_FORMAT_PA paInt16 +#define SAMPLE_FORMAT_LV VISUAL_AUDIO_SAMPLE_FORMAT_S16 + +#define FRAMES 1024 + +typedef struct { + PaStream *stream; + void * buffer; +} PortAudioPrivate; + +static int inp_portaudio_init (VisPluginData *plugin); +static int inp_portaudio_cleanup (VisPluginData *plugin); +static int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio); + +const VisPluginInfo *get_plugin_info (int *count) +{ + static VisInputPlugin input[] = {{ + .upload = inp_portaudio_upload + }}; + + static VisPluginInfo info[] = {{ + .type = VISUAL_PLUGIN_TYPE_INPUT, + + .plugname = "portaudio", + .name = "portaudio", + .author = "Sebastian Pipping ", + .version = "1.0", + .about = N_("PortAudio capture plugin"), + .help = N_("Use this plugin to capture PCM data from the PortAudio record device"), + .license = VISUAL_PLUGIN_LICENSE_LGPL, + + .init = inp_portaudio_init, + .cleanup = inp_portaudio_cleanup, + + .plugin = VISUAL_OBJECT (&input[0]) + }}; + + *count = sizeof (info) / sizeof (*info); + + return info; +} + +int inp_portaudio_init (VisPluginData *plugin) +{ +#if ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); +#endif + + PortAudioPrivate * const priv = visual_mem_new0 (PortAudioPrivate, 1); + visual_log_return_val_if_fail(priv != NULL, -1); + visual_object_set_private (VISUAL_OBJECT (plugin), priv); + + // Init PortAudio + const PaError init_error = Pa_Initialize(); + visual_log_return_val_if_fail (init_error == paNoError, -2); + + // Select input device + const PaDeviceIndex input_device = Pa_GetDefaultInputDevice (); + visual_log_return_val_if_fail (input_device != paNoDevice, -3); + + // Open stream + const double latency_seconds = 1.0 / SAMPLE_RATE * FRAMES; + const PaStreamParameters input_parameters = { + .device = input_device, + .channelCount = CHANNELS, + .sampleFormat = SAMPLE_FORMAT_PA, + .suggestedLatency = latency_seconds, + .hostApiSpecificStreamInfo = NULL}; + const PaError open_error = + Pa_OpenStream (&priv->stream, &input_parameters, NULL, + SAMPLE_RATE, FRAMES, paClipOff, NULL, NULL); + + // Allocate buffer + const visual_size_t buffer_size_bytes = FRAMES * CHANNELS * (SAMPLE_FORMAT_BITS / 8); + priv->buffer = malloc (buffer_size_bytes); + visual_log_return_val_if_fail (priv->buffer != NULL, -4); + + // Start stream + const PaError input_start_error = Pa_StartStream (priv->stream); + visual_log_return_val_if_fail (input_start_error == paNoError, -4); + + return 0; +} + +int inp_portaudio_cleanup (VisPluginData *plugin) +{ + visual_log_return_val_if_fail (plugin != NULL, -1); + + PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin)); + visual_log_return_val_if_fail (priv != NULL, -2); + + free (priv->buffer); + priv->buffer = NULL; + + Pa_StopStream (priv->stream); + Pa_CloseStream (priv->stream); + priv->stream = NULL; + + Pa_Terminate (); + + visual_mem_free (priv); + + return 0; +} + +int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio) +{ + visual_log_return_val_if_fail (plugin != NULL, -1); + visual_log_return_val_if_fail (audio != NULL, -2); + + PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin)); + visual_log_return_val_if_fail (priv != NULL, -3); + visual_log_return_val_if_fail (priv->stream != NULL, -4); + + for (;;) { + long frames_to_read = Pa_GetStreamReadAvailable (priv->stream); + if (frames_to_read < 1) { + return 0; + } + if (frames_to_read > FRAMES) { + frames_to_read = FRAMES; // because that's all that priv->buffer has space for + } + + const PaError read_error = Pa_ReadStream(priv->stream, priv->buffer, frames_to_read); + if (read_error != paNoError) { + return 0; + } + + const visual_size_t bytes_to_write = frames_to_read * CHANNELS * (SAMPLE_FORMAT_BITS / 8); + + VisBuffer buffer; + visual_buffer_init(&buffer, priv->buffer, bytes_to_write, NULL); + + visual_audio_samplepool_input(audio->samplepool, &buffer, SAMPLE_RATE_TYPE_LV, + SAMPLE_FORMAT_LV, CHANNELS_TYPE); + } + + // we never get here +} From 2f65726180bca30105b26312ab4fd5cab597ed7e Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Thu, 2 Feb 2023 04:26:55 +0100 Subject: [PATCH 2/3] Plugins (portaudio): Port to 0.5.x --- .../plugins/input/portaudio/input_portaudio.c | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/libvisual-plugins/plugins/input/portaudio/input_portaudio.c b/libvisual-plugins/plugins/input/portaudio/input_portaudio.c index ffa634fec..56f3f1b44 100644 --- a/libvisual-plugins/plugins/input/portaudio/input_portaudio.c +++ b/libvisual-plugins/plugins/input/portaudio/input_portaudio.c @@ -43,16 +43,16 @@ typedef struct { } PortAudioPrivate; static int inp_portaudio_init (VisPluginData *plugin); -static int inp_portaudio_cleanup (VisPluginData *plugin); +static void inp_portaudio_cleanup (VisPluginData *plugin); static int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio); -const VisPluginInfo *get_plugin_info (int *count) +const VisPluginInfo *get_plugin_info (void) { - static VisInputPlugin input[] = {{ + static VisInputPlugin input = { .upload = inp_portaudio_upload - }}; + }; - static VisPluginInfo info[] = {{ + static VisPluginInfo info = { .type = VISUAL_PLUGIN_TYPE_INPUT, .plugname = "portaudio", @@ -66,12 +66,10 @@ const VisPluginInfo *get_plugin_info (int *count) .init = inp_portaudio_init, .cleanup = inp_portaudio_cleanup, - .plugin = VISUAL_OBJECT (&input[0]) - }}; + .plugin = &input + }; - *count = sizeof (info) / sizeof (*info); - - return info; + return &info; } int inp_portaudio_init (VisPluginData *plugin) @@ -81,16 +79,16 @@ int inp_portaudio_init (VisPluginData *plugin) #endif PortAudioPrivate * const priv = visual_mem_new0 (PortAudioPrivate, 1); - visual_log_return_val_if_fail(priv != NULL, -1); - visual_object_set_private (VISUAL_OBJECT (plugin), priv); + visual_return_val_if_fail (priv != NULL, FALSE); + visual_plugin_set_private (plugin, priv); // Init PortAudio const PaError init_error = Pa_Initialize(); - visual_log_return_val_if_fail (init_error == paNoError, -2); + visual_return_val_if_fail (init_error == paNoError, FALSE); // Select input device const PaDeviceIndex input_device = Pa_GetDefaultInputDevice (); - visual_log_return_val_if_fail (input_device != paNoDevice, -3); + visual_return_val_if_fail (input_device != paNoDevice, FALSE); // Open stream const double latency_seconds = 1.0 / SAMPLE_RATE * FRAMES; @@ -103,25 +101,26 @@ int inp_portaudio_init (VisPluginData *plugin) const PaError open_error = Pa_OpenStream (&priv->stream, &input_parameters, NULL, SAMPLE_RATE, FRAMES, paClipOff, NULL, NULL); + visual_return_val_if_fail (open_error == paNoError, FALSE); // Allocate buffer const visual_size_t buffer_size_bytes = FRAMES * CHANNELS * (SAMPLE_FORMAT_BITS / 8); priv->buffer = malloc (buffer_size_bytes); - visual_log_return_val_if_fail (priv->buffer != NULL, -4); + visual_return_val_if_fail (priv->buffer != NULL, FALSE); // Start stream const PaError input_start_error = Pa_StartStream (priv->stream); - visual_log_return_val_if_fail (input_start_error == paNoError, -4); + visual_return_val_if_fail (input_start_error == paNoError, FALSE); - return 0; + return TRUE; } -int inp_portaudio_cleanup (VisPluginData *plugin) +void inp_portaudio_cleanup (VisPluginData *plugin) { - visual_log_return_val_if_fail (plugin != NULL, -1); + visual_return_if_fail (plugin != NULL); - PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin)); - visual_log_return_val_if_fail (priv != NULL, -2); + PortAudioPrivate * const priv = visual_plugin_get_private (plugin); + visual_return_if_fail (priv != NULL); free (priv->buffer); priv->buffer = NULL; @@ -133,23 +132,21 @@ int inp_portaudio_cleanup (VisPluginData *plugin) Pa_Terminate (); visual_mem_free (priv); - - return 0; } int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio) { - visual_log_return_val_if_fail (plugin != NULL, -1); - visual_log_return_val_if_fail (audio != NULL, -2); + visual_return_val_if_fail (plugin != NULL, FALSE); + visual_return_val_if_fail (audio != NULL, FALSE); - PortAudioPrivate * const priv = visual_object_get_private (VISUAL_OBJECT (plugin)); - visual_log_return_val_if_fail (priv != NULL, -3); - visual_log_return_val_if_fail (priv->stream != NULL, -4); + PortAudioPrivate * const priv = visual_plugin_get_private (plugin); + visual_return_val_if_fail (priv != NULL, FALSE); + visual_return_val_if_fail (priv->stream != NULL, FALSE); for (;;) { long frames_to_read = Pa_GetStreamReadAvailable (priv->stream); if (frames_to_read < 1) { - return 0; + return TRUE; } if (frames_to_read > FRAMES) { frames_to_read = FRAMES; // because that's all that priv->buffer has space for @@ -157,16 +154,17 @@ int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio) const PaError read_error = Pa_ReadStream(priv->stream, priv->buffer, frames_to_read); if (read_error != paNoError) { - return 0; + return TRUE; } const visual_size_t bytes_to_write = frames_to_read * CHANNELS * (SAMPLE_FORMAT_BITS / 8); - VisBuffer buffer; - visual_buffer_init(&buffer, priv->buffer, bytes_to_write, NULL); + VisBuffer * const buffer = visual_buffer_new_wrap_data (priv->buffer, bytes_to_write, FALSE); + visual_return_val_if_fail (priv != NULL, FALSE); + + visual_audio_input(audio, buffer, SAMPLE_RATE_TYPE_LV, SAMPLE_FORMAT_LV, CHANNELS_TYPE); - visual_audio_samplepool_input(audio->samplepool, &buffer, SAMPLE_RATE_TYPE_LV, - SAMPLE_FORMAT_LV, CHANNELS_TYPE); + visual_buffer_unref (buffer); } // we never get here From a70cd369f3e52b7199109f2aa6938bcbd99db01f Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sat, 21 Jan 2023 03:22:41 +0100 Subject: [PATCH 3/3] Plugins (portaudio): Enable translation (based on 0.4.x commit 85000f6b8becc68fae6dfb1707470ff83aaa40cd) --- libvisual-plugins/po/POTFILES.in | 1 + 1 file changed, 1 insertion(+) diff --git a/libvisual-plugins/po/POTFILES.in b/libvisual-plugins/po/POTFILES.in index 8a41e4bc0..c8f21b236 100644 --- a/libvisual-plugins/po/POTFILES.in +++ b/libvisual-plugins/po/POTFILES.in @@ -28,6 +28,7 @@ plugins/input/alsa/input_alsa.c plugins/input/debug/input_debug.c plugins/input/jack/input_jack.cpp plugins/input/mplayer/input_mplayer.c +plugins/input/portaudio/input_portaudio.c plugins/input/pulseaudio/input_pulseaudio.c plugins/input/wavein/input_wavein.cpp plugins/morph/alphablend/morph_alphablend.c