Skip to content
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 .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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') }}"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions libvisual-plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions libvisual-plugins/plugins/input/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions libvisual-plugins/plugins/input/portaudio/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
LV_BUILD_INPUT_PLUGIN(portaudio
SOURCES input_portaudio.c
LINK_LIBS PkgConfig::PORTAUDIO
)
171 changes: 171 additions & 0 deletions libvisual-plugins/plugins/input/portaudio/input_portaudio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* Libvisual-plugins - Standard plugins for libvisual
*
* Copyright (C) 2023 Sebastian Pipping <sebastian@pipping.org>
*
* Authors: Sebastian Pipping <sebastian@pipping.org>
*
* 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 <stdlib.h> // malloc

#include <libvisual/libvisual.h>
#include "gettext.h"
#include <portaudio.h>

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 void inp_portaudio_cleanup (VisPluginData *plugin);
static int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio);

const VisPluginInfo *get_plugin_info (void)
{
static VisInputPlugin input = {
.upload = inp_portaudio_upload
};

static VisPluginInfo info = {
.type = VISUAL_PLUGIN_TYPE_INPUT,

.plugname = "portaudio",
.name = "portaudio",
.author = "Sebastian Pipping <sebastian@pipping.org>",
.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 = &input
};

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_return_val_if_fail (priv != NULL, FALSE);
visual_plugin_set_private (plugin, priv);

// Init PortAudio
const PaError init_error = Pa_Initialize();
visual_return_val_if_fail (init_error == paNoError, FALSE);

// Select input device
const PaDeviceIndex input_device = Pa_GetDefaultInputDevice ();
visual_return_val_if_fail (input_device != paNoDevice, FALSE);

// 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);
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_return_val_if_fail (priv->buffer != NULL, FALSE);

// Start stream
const PaError input_start_error = Pa_StartStream (priv->stream);
visual_return_val_if_fail (input_start_error == paNoError, FALSE);

return TRUE;
}

void inp_portaudio_cleanup (VisPluginData *plugin)
{
visual_return_if_fail (plugin != NULL);

PortAudioPrivate * const priv = visual_plugin_get_private (plugin);
visual_return_if_fail (priv != NULL);

free (priv->buffer);
priv->buffer = NULL;

Pa_StopStream (priv->stream);
Pa_CloseStream (priv->stream);
priv->stream = NULL;

Pa_Terminate ();

visual_mem_free (priv);
}

int inp_portaudio_upload (VisPluginData *plugin, VisAudio *audio)
{
visual_return_val_if_fail (plugin != NULL, FALSE);
visual_return_val_if_fail (audio != NULL, FALSE);

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 TRUE;
}
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 TRUE;
}

const visual_size_t bytes_to_write = frames_to_read * CHANNELS * (SAMPLE_FORMAT_BITS / 8);

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_buffer_unref (buffer);
}

// we never get here
}
1 change: 1 addition & 0 deletions libvisual-plugins/po/POTFILES.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down