Skip to content
Open
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
4 changes: 1 addition & 3 deletions src/AudioCapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ void AudioCapture::initialize(Poco::Util::Application& app)
{
_config = app.config().createView("audio");

auto& projectMWrapper = app.getSubsystem<ProjectMWrapper>();

if (!_impl)
{
_impl = new AudioCaptureImpl;
Expand All @@ -31,7 +29,7 @@ void AudioCapture::initialize(Poco::Util::Application& app)

PrintDeviceList(deviceList);

_impl->StartRecording(projectMWrapper.ProjectM(), audioDeviceIndex);
_impl->StartRecording(audioDeviceIndex);
}

void AudioCapture::uninitialize()
Expand Down
3 changes: 1 addition & 2 deletions src/AudioCaptureImpl_CoreAudioTap.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ class CoreAudioTapCapture

/**
* @brief Starts capturing system audio and forwarding it to projectM.
* @param projectMHandle projectM instance handle that will receive the captured data.
* @param audioDeviceIndex Ignored in v1 (single system-audio source).
*/
void StartRecording(projectm* projectMHandle, int audioDeviceIndex);
void StartRecording(int audioDeviceIndex);

/**
* @brief Stops capturing and tears down the tap and aggregate device.
Expand Down
16 changes: 8 additions & 8 deletions src/AudioCaptureImpl_CoreAudioTap.mm
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "AudioCaptureImpl_CoreAudioTap.h"

#include <projectM-4/projectM.h>
#include "notifications/AudioDataAvailableNotification.h"

#include <Poco/NotificationCenter.h>

#import <CoreAudio/CoreAudio.h>
#import <Foundation/Foundation.h>
Expand All @@ -26,9 +28,8 @@
return {{-1, _systemAudioDeviceName}};
}

void CoreAudioTapCapture::StartRecording(projectm* projectMHandle, int audioDeviceIndex)
void CoreAudioTapCapture::StartRecording(int audioDeviceIndex)
{
_projectMHandle = projectMHandle;
_currentAudioDeviceIndex = -1; // Only one source in v1.

if (StartTap())
Expand Down Expand Up @@ -188,15 +189,14 @@
// 4. Install the IO proc. Runs on Core Audio's realtime thread; keep it allocation-
// and lock-free. Core Audio delivers deinterleaved float buffers (one buffer per
// channel) for tap aggregates, so forward the first buffer's frames to projectM.
projectm* handle = _projectMHandle;
uint32_t channels = _channels;

status = AudioDeviceCreateIOProcIDWithBlock(
&_ioProcID, _aggregateDeviceID, nullptr,
^(const AudioTimeStamp* /*inNow*/, const AudioBufferList* inInputData,
const AudioTimeStamp* /*inInputTime*/, AudioBufferList* /*outOutputData*/,
const AudioTimeStamp* /*inOutputTime*/) {
if (handle == nullptr || inInputData == nullptr || inInputData->mNumberBuffers == 0)
if (inInputData == nullptr || inInputData->mNumberBuffers == 0)
{
return;
}
Expand All @@ -210,10 +210,10 @@
auto* samples = static_cast<float*>(buffer.mData);
// mNumberChannels reflects the interleaving of this buffer.
uint32_t bufferChannels = buffer.mNumberChannels > 0 ? buffer.mNumberChannels : channels;
unsigned int frameCount = buffer.mDataByteSize / sizeof(float) / bufferChannels;
unsigned int sampleCount = buffer.mDataByteSize / sizeof(float);

projectm_pcm_add_float(handle, samples, frameCount,
static_cast<projectm_channels>(bufferChannels));
Poco::NotificationCenter::defaultCenter().postNotification(
new AudioDataAvailableNotification(bufferChannels, samples, sampleCount));
});

if (status != noErr || _ioProcID == nullptr)
Expand Down
27 changes: 17 additions & 10 deletions src/AudioCaptureImpl_SDL.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include "AudioCaptureImpl_SDL.h"

#include <Poco/Util/Application.h>
#include "notifications/AudioDataAvailableNotification.h"

#include <projectM-4/projectM.h>
#include <Poco/NotificationCenter.h>
#include <Poco/Util/Application.h>

AudioCaptureImpl::AudioCaptureImpl()
: _requestedSampleCount(projectm_pcm_get_max_samples())
{
auto targetFps = Poco::Util::Application::instance().config().getUInt("projectM.fps", 60);
if (targetFps > 0)
Expand Down Expand Up @@ -50,9 +50,8 @@ std::map<int, std::string> AudioCaptureImpl::AudioDeviceList()
return deviceList;
}

void AudioCaptureImpl::StartRecording(projectm* projectMHandle, int audioDeviceIndex)
void AudioCaptureImpl::StartRecording(int audioDeviceIndex)
{
_projectMHandle = projectMHandle;
_currentAudioDeviceIndex = audioDeviceIndex;

poco_debug_f1(_logger, "Using SDL audio driver \"%s\".", std::string(SDL_GetCurrentAudioDriver()));
Expand Down Expand Up @@ -84,7 +83,7 @@ void AudioCaptureImpl::NextAudioDevice()
// Will wrap around to default capture device (-1).
int nextAudioDeviceId = ((_currentAudioDeviceIndex + 2) % (SDL_GetNumAudioDevices(true) + 1)) - 1;

StartRecording(_projectMHandle, nextAudioDeviceId);
StartRecording(nextAudioDeviceId);
}

void AudioCaptureImpl::AudioDeviceIndex(int index)
Expand All @@ -93,7 +92,7 @@ void AudioCaptureImpl::AudioDeviceIndex(int index)
{
StopRecording();
_currentAudioDeviceIndex = index;
StartRecording(_projectMHandle, index);
StartRecording(index);
}
}

Expand Down Expand Up @@ -153,10 +152,18 @@ bool AudioCaptureImpl::OpenAudioDevice()
void AudioCaptureImpl::AudioInputCallback(void* userData, unsigned char* stream, int len)
{
poco_assert_dbg(userData);

if (len < sizeof(float))
{
return;
}

auto instance = reinterpret_cast<AudioCaptureImpl*>(userData);

unsigned int samples = len / sizeof(float) / instance->_channels;
unsigned int samples = len / sizeof(float);

projectm_pcm_add_float(instance->_projectMHandle, reinterpret_cast<float*>(stream), samples,
static_cast<projectm_channels>(instance->_channels));
Poco::NotificationCenter::defaultCenter().postNotification(
new AudioDataAvailableNotification(instance->_channels,
reinterpret_cast<float*>(stream),
samples));
}
9 changes: 2 additions & 7 deletions src/AudioCaptureImpl_SDL.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include <Poco/Logger.h>

#include <string>
#include <vector>

class projectm;

Expand All @@ -29,11 +28,10 @@ class AudioCaptureImpl

/**
* @brief Starts audio capturing with the first available device.
* @param projectMHandle projectM instance handle that will receive the captured data.
* @param audioDeviceIndex The initial audio device ID to capture from. Use -1 to select the implementation's
* default device.
*/
void StartRecording(projectm* projectMHandle, int audioDeviceIndex);
void StartRecording(int audioDeviceIndex);

/**
* @brief Stops audio recording.
Expand Down Expand Up @@ -66,9 +64,7 @@ class AudioCaptureImpl
/**
* @brief Asks the capture client to fill projectM's audio buffer for the next frame.
*
* As of now, SDL uses async callbacks to directly fill projectM's audio buffer.
*
* @todo Store audio samples internally and push them to projectM when requested.
* SDL uses async callbacks to directly fill the staging audio buffer(s) as soon as new data is available.
*/
void FillBuffer(){};

Expand All @@ -90,7 +86,6 @@ class AudioCaptureImpl
*/
static void AudioInputCallback(void* userData, unsigned char* stream, int len);

projectm* _projectMHandle{nullptr}; //!< Handle if the projectM instance that will receive the audio data.
int32_t _currentAudioDeviceIndex{-1}; //!< Currently selected audio device index.
SDL_AudioDeviceID _currentAudioDeviceID{0}; //!< Device ID of the currently opened audio device.
uint32_t _channels{2};
Expand Down
15 changes: 9 additions & 6 deletions src/AudioCaptureImpl_WASAPI.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include "AudioCaptureImpl_WASAPI.h"

#include <projectM-4/projectM.h>
#include "notifications/AudioDataAvailableNotification.h"

#include <Poco/UnicodeConverter.h>

#include <Poco/NotificationCenter.h>
#include <functiondiscoverykeys_devpkey.h>
#include <mmdeviceapi.h>
#include <objbase.h>
Expand Down Expand Up @@ -42,9 +43,8 @@ std::map<int, std::string> AudioCaptureImpl::AudioDeviceList()
return deviceList;
}

void AudioCaptureImpl::StartRecording(projectm* projectMHandle, int audioDeviceIndex)
void AudioCaptureImpl::StartRecording(int audioDeviceIndex)
{
_projectMHandle = projectMHandle;
_currentAudioDeviceIndex = audioDeviceIndex;

_isCapturing = true;
Expand Down Expand Up @@ -74,7 +74,7 @@ void AudioCaptureImpl::NextAudioDevice()
// Will wrap around to loopback capture device (-1).
int nextAudioDeviceId = ((_currentAudioDeviceIndex + 2) % (static_cast<int>(captureDevices.size()) + 1)) - 1;

StartRecording(_projectMHandle, nextAudioDeviceId);
StartRecording(nextAudioDeviceId);
}

void AudioCaptureImpl::AudioDeviceIndex(int index)
Expand All @@ -87,7 +87,7 @@ void AudioCaptureImpl::AudioDeviceIndex(int index)
{
_currentAudioDeviceIndex = index;
StopRecording();
StartRecording(_projectMHandle, index);
StartRecording(index);
}
}

Expand Down Expand Up @@ -447,7 +447,10 @@ void AudioCaptureImpl::CaptureThread()

if (framesAvailable > 0 && data != nullptr)
{
projectm_pcm_add_float(_projectMHandle, reinterpret_cast<float*>(data), framesAvailable, static_cast<projectm_channels>(_channels));
Poco::NotificationCenter::defaultCenter().postNotification(
new AudioDataAvailableNotification(_channels,
reinterpret_cast<float*>(data),
framesAvailable * _channels));
}

_audioCaptureClient->ReleaseBuffer(framesAvailable);
Expand Down
3 changes: 1 addition & 2 deletions src/AudioCaptureImpl_WASAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ class AudioCaptureImpl : public IMMNotificationClient

/**
* @brief Starts audio capturing with the first available device.
* @param projectMHandle projectM instance handle that will receive the captured data.
* @param audioDeviceIndex The initial audio device ID to capture from. Use -1 to select the implementation's
* default device.
*/
void StartRecording(projectm* projectMHandle, int audioDeviceIndex);
void StartRecording(int audioDeviceIndex);

/**
* @brief Stops audio recording.
Expand Down
2 changes: 1 addition & 1 deletion src/AudioCaptureImpl_macOS.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AudioCaptureImpl

std::map<int, std::string> AudioDeviceList();

void StartRecording(projectm* projectMHandle, int audioDeviceIndex);
void StartRecording(int audioDeviceIndex);

void StopRecording();

Expand Down
6 changes: 3 additions & 3 deletions src/AudioCaptureImpl_macOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
return _sdl->AudioDeviceList();
}

void AudioCaptureImpl::StartRecording(projectm* projectMHandle, int audioDeviceIndex)
void AudioCaptureImpl::StartRecording(int audioDeviceIndex)
{
if (_tap)
{
_tap->StartRecording(projectMHandle, audioDeviceIndex);
_tap->StartRecording(audioDeviceIndex);
return;
}
_sdl->StartRecording(projectMHandle, audioDeviceIndex);
_sdl->StartRecording(audioDeviceIndex);
}

void AudioCaptureImpl::StopRecording()
Expand Down
28 changes: 27 additions & 1 deletion src/ProjectMWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ void ProjectMWrapper::initialize(Poco::Util::Application& app)
}

Poco::NotificationCenter::defaultCenter().addObserver(_playbackControlNotificationObserver);
Poco::NotificationCenter::defaultCenter().addObserver(_audioDataAvailableNotificationObserver);

// Observe user configuration changes (set via the settings window)
_userConfig->propertyChanged += Poco::delegate(this, &ProjectMWrapper::OnConfigurationPropertyChanged);
Expand All @@ -119,6 +120,7 @@ void ProjectMWrapper::uninitialize()
_userConfig->propertyRemoved -= Poco::delegate(this, &ProjectMWrapper::OnConfigurationPropertyRemoved);
_userConfig->propertyChanged -= Poco::delegate(this, &ProjectMWrapper::OnConfigurationPropertyChanged);
Poco::NotificationCenter::defaultCenter().removeObserver(_playbackControlNotificationObserver);
Poco::NotificationCenter::defaultCenter().removeObserver(_audioDataAvailableNotificationObserver);

if (_projectM)
{
Expand Down Expand Up @@ -153,7 +155,7 @@ void ProjectMWrapper::UpdateRealFPS(float fps)
projectm_set_fps(_projectM, static_cast<uint32_t>(std::round(fps)));
}

void ProjectMWrapper::RenderFrame() const
void ProjectMWrapper::RenderFrame()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Expand All @@ -167,6 +169,16 @@ void ProjectMWrapper::RenderFrame() const
projectm_set_mesh_size(_projectM, _projectMConfigView->getInt("meshX", 220), _projectMConfigView->getInt("meshY", 125));
}

// Add new audio data to the instance from the staging buffer
{
Poco::ScopedLock lock(_audioBufferMutex);
if (!_audioStagingBuffer.empty())
{
projectm_pcm_add_float(_projectM, _audioStagingBuffer.data(), _audioStagingBuffer.size() / _audioChannels, static_cast<projectm_channels>(_audioChannels));
_audioStagingBuffer.clear();
}
}

projectm_opengl_render_frame(_projectM);
}

Expand Down Expand Up @@ -263,6 +275,20 @@ void ProjectMWrapper::PlaybackControlNotificationHandler(const Poco::AutoPtr<Pla
}
}

void ProjectMWrapper::AudioDataAvailableNotificationHandler(const Poco::AutoPtr<AudioDataAvailableNotification>& notification)
{
Poco::ScopedLock lock(_audioBufferMutex);

// Clear existing buffer data if channel count differs, e.g. if audio device has changed
if (_audioChannels != notification->Channels())
{
_audioChannels = notification->Channels();
_audioStagingBuffer.clear();
}

std::copy(notification->Samples().cbegin(), notification->Samples().cend(), std::back_inserter(_audioStagingBuffer));
}

std::vector<std::string> ProjectMWrapper::GetPathListWithDefault(const std::string& baseKey, const std::string& defaultPath)
{
using Poco::Util::AbstractConfiguration;
Expand Down
10 changes: 9 additions & 1 deletion src/ProjectMWrapper.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "notifications/AudioDataAvailableNotification.h"
#include "notifications/PlaybackControlNotification.h"

#include <projectM-4/projectM.h>
Expand Down Expand Up @@ -37,7 +38,7 @@ class ProjectMWrapper : public Poco::Util::Subsystem
/**
* Renders a single projectM frame.
*/
void RenderFrame() const;
void RenderFrame();

/**
* @brief Returns the targeted FPS value.
Expand Down Expand Up @@ -91,6 +92,8 @@ class ProjectMWrapper : public Poco::Util::Subsystem

void PlaybackControlNotificationHandler(const Poco::AutoPtr<PlaybackControlNotification>& notification);

void AudioDataAvailableNotificationHandler(const Poco::AutoPtr<AudioDataAvailableNotification>& notification);

std::vector<std::string> GetPathListWithDefault(const std::string& baseKey, const std::string& defaultPath);

/**
Expand All @@ -111,7 +114,12 @@ class ProjectMWrapper : public Poco::Util::Subsystem
projectm_handle _projectM{nullptr}; //!< Pointer to the projectM instance used by the application.
projectm_playlist_handle _playlist{nullptr}; //!< Pointer to the projectM playlist manager instance.

uint32_t _audioChannels{0}; //!< Number of audio channels of the current capture device.
std::vector<float> _audioStagingBuffer; //!< Buffer which receives audio data from the capture implementation.
mutable Poco::Mutex _audioBufferMutex; //!< Mutex protecting access to the audio staging buffer.

Poco::NObserver<ProjectMWrapper, PlaybackControlNotification> _playbackControlNotificationObserver{*this, &ProjectMWrapper::PlaybackControlNotificationHandler};
Poco::NObserver<ProjectMWrapper, AudioDataAvailableNotification> _audioDataAvailableNotificationObserver{*this, &ProjectMWrapper::AudioDataAvailableNotificationHandler};

Poco::Logger& _logger{Poco::Logger::get("SDLRenderingWindow")}; //!< The class logger.
};
Loading