dropped portaudio for miniaudio
This commit is contained in:
@@ -19,7 +19,6 @@ find_package(OpenGL REQUIRED)
|
|||||||
|
|
||||||
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
|
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
|
||||||
pkg_check_modules(FFTW3F REQUIRED IMPORTED_TARGET fftw3f)
|
pkg_check_modules(FFTW3F REQUIRED IMPORTED_TARGET fftw3f)
|
||||||
pkg_check_modules(PORTAUDIO REQUIRED IMPORTED_TARGET portaudio-2.0)
|
|
||||||
pkg_check_modules(SNDFILE REQUIRED IMPORTED_TARGET sndfile)
|
pkg_check_modules(SNDFILE REQUIRED IMPORTED_TARGET sndfile)
|
||||||
|
|
||||||
# ── ImGui via FetchContent ────────────────────────────────────────────────────
|
# ── ImGui via FetchContent ────────────────────────────────────────────────────
|
||||||
@@ -55,7 +54,7 @@ set(SOURCES
|
|||||||
src/dsp/WindowFunctions.cpp
|
src/dsp/WindowFunctions.cpp
|
||||||
src/dsp/FFTProcessor.cpp
|
src/dsp/FFTProcessor.cpp
|
||||||
src/dsp/SpectrumAnalyzer.cpp
|
src/dsp/SpectrumAnalyzer.cpp
|
||||||
src/audio/PortAudioSource.cpp
|
src/audio/MiniAudioSource.cpp
|
||||||
src/audio/FileSource.cpp
|
src/audio/FileSource.cpp
|
||||||
src/ui/ColorMap.cpp
|
src/ui/ColorMap.cpp
|
||||||
src/ui/WaterfallDisplay.cpp
|
src/ui/WaterfallDisplay.cpp
|
||||||
@@ -71,13 +70,12 @@ target_link_libraries(baudline PRIVATE
|
|||||||
imgui
|
imgui
|
||||||
PkgConfig::SDL2
|
PkgConfig::SDL2
|
||||||
PkgConfig::FFTW3F
|
PkgConfig::FFTW3F
|
||||||
PkgConfig::PORTAUDIO
|
|
||||||
PkgConfig::SNDFILE
|
PkgConfig::SNDFILE
|
||||||
OpenGL::GL
|
OpenGL::GL
|
||||||
pthread
|
pthread
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link math library on Unix
|
# Link math library and dl on Unix (dl needed by miniaudio for backend loading)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(baudline PRIVATE m)
|
target_link_libraries(baudline PRIVATE m dl)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
157
src/audio/MiniAudioSource.cpp
Normal file
157
src/audio/MiniAudioSource.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
#define MINIAUDIO_IMPLEMENTATION
|
||||||
|
#include "audio/miniaudio.h"
|
||||||
|
#include "audio/MiniAudioSource.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace baudline {
|
||||||
|
|
||||||
|
// ── Shared context (lazy-initialized) ────────────────────────────────────────
|
||||||
|
|
||||||
|
static ma_context* sharedContext() {
|
||||||
|
static ma_context ctx;
|
||||||
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
if (ma_context_init(nullptr, 0, nullptr, &ctx) != MA_SUCCESS) {
|
||||||
|
std::fprintf(stderr, "miniaudio: failed to init context\n");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
return &ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Device enumeration ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
std::vector<MiniAudioSource::DeviceInfo> MiniAudioSource::listInputDevices() {
|
||||||
|
std::vector<DeviceInfo> result;
|
||||||
|
ma_context* ctx = sharedContext();
|
||||||
|
if (!ctx) return result;
|
||||||
|
|
||||||
|
ma_device_info* captureDevices;
|
||||||
|
ma_uint32 captureCount;
|
||||||
|
if (ma_context_get_devices(ctx, nullptr, nullptr,
|
||||||
|
&captureDevices, &captureCount) != MA_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ma_uint32 i = 0; i < captureCount; ++i) {
|
||||||
|
// Query full device info for channel count and sample rate.
|
||||||
|
ma_device_info fullInfo;
|
||||||
|
if (ma_context_get_device_info(ctx, ma_device_type_capture,
|
||||||
|
&captureDevices[i].id, &fullInfo) != MA_SUCCESS) {
|
||||||
|
fullInfo = captureDevices[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxCh = static_cast<int>(fullInfo.nativeDataFormatCount > 0
|
||||||
|
? fullInfo.nativeDataFormats[0].channels : 2);
|
||||||
|
// Find max channels across native formats.
|
||||||
|
for (ma_uint32 f = 1; f < fullInfo.nativeDataFormatCount; ++f) {
|
||||||
|
int ch = static_cast<int>(fullInfo.nativeDataFormats[f].channels);
|
||||||
|
if (ch > maxCh) maxCh = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
double defaultSR = 48000.0;
|
||||||
|
if (fullInfo.nativeDataFormatCount > 0 && fullInfo.nativeDataFormats[0].sampleRate > 0)
|
||||||
|
defaultSR = static_cast<double>(fullInfo.nativeDataFormats[0].sampleRate);
|
||||||
|
|
||||||
|
result.push_back({
|
||||||
|
static_cast<int>(i),
|
||||||
|
fullInfo.name,
|
||||||
|
maxCh,
|
||||||
|
defaultSR
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Construction / destruction ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
MiniAudioSource::MiniAudioSource(double sampleRate, int channels, int deviceIndex)
|
||||||
|
: sampleRate_(sampleRate)
|
||||||
|
, channels_(channels)
|
||||||
|
, deviceIndex_(deviceIndex)
|
||||||
|
{
|
||||||
|
size_t ringSize = static_cast<size_t>(sampleRate * channels * 2); // ~2 seconds
|
||||||
|
ringBuf_ = std::make_unique<RingBuffer<float>>(ringSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
MiniAudioSource::~MiniAudioSource() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Callback ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void MiniAudioSource::dataCallback(ma_device* device, void* /*output*/,
|
||||||
|
const void* input, unsigned int frameCount) {
|
||||||
|
auto* self = static_cast<MiniAudioSource*>(device->pUserData);
|
||||||
|
if (input) {
|
||||||
|
const auto* in = static_cast<const float*>(input);
|
||||||
|
self->ringBuf_->write(in, frameCount * self->channels_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Open / close / read ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
bool MiniAudioSource::open() {
|
||||||
|
if (opened_) return true;
|
||||||
|
|
||||||
|
ma_context* ctx = sharedContext();
|
||||||
|
if (!ctx) return false;
|
||||||
|
|
||||||
|
// Resolve device ID if a specific index was requested.
|
||||||
|
ma_device_id* pDeviceID = nullptr;
|
||||||
|
ma_device_id deviceID{};
|
||||||
|
|
||||||
|
if (deviceIndex_ >= 0) {
|
||||||
|
ma_device_info* captureDevices;
|
||||||
|
ma_uint32 captureCount;
|
||||||
|
if (ma_context_get_devices(ctx, nullptr, nullptr,
|
||||||
|
&captureDevices, &captureCount) == MA_SUCCESS) {
|
||||||
|
if (deviceIndex_ < static_cast<int>(captureCount)) {
|
||||||
|
deviceID = captureDevices[deviceIndex_].id;
|
||||||
|
pDeviceID = &deviceID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_device_config config = ma_device_config_init(ma_device_type_capture);
|
||||||
|
config.capture.pDeviceID = pDeviceID;
|
||||||
|
config.capture.format = ma_format_f32;
|
||||||
|
config.capture.channels = static_cast<ma_uint32>(channels_);
|
||||||
|
config.sampleRate = static_cast<ma_uint32>(sampleRate_);
|
||||||
|
config.dataCallback = dataCallback;
|
||||||
|
config.pUserData = this;
|
||||||
|
config.periodSizeInFrames = 512;
|
||||||
|
|
||||||
|
device_ = std::make_unique<ma_device>();
|
||||||
|
if (ma_device_init(ctx, &config, device_.get()) != MA_SUCCESS) {
|
||||||
|
std::fprintf(stderr, "miniaudio: failed to init device\n");
|
||||||
|
device_.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ma_device_start(device_.get()) != MA_SUCCESS) {
|
||||||
|
std::fprintf(stderr, "miniaudio: failed to start device\n");
|
||||||
|
ma_device_uninit(device_.get());
|
||||||
|
device_.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
opened_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MiniAudioSource::close() {
|
||||||
|
if (device_) {
|
||||||
|
ma_device_uninit(device_.get());
|
||||||
|
device_.reset();
|
||||||
|
}
|
||||||
|
opened_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MiniAudioSource::read(float* buffer, size_t frames) {
|
||||||
|
return ringBuf_->read(buffer, frames * channels_) / channels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace baudline
|
||||||
53
src/audio/MiniAudioSource.h
Normal file
53
src/audio/MiniAudioSource.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "audio/AudioSource.h"
|
||||||
|
#include "core/RingBuffer.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Forward-declare miniaudio types to avoid pulling the huge header into every TU.
|
||||||
|
struct ma_device;
|
||||||
|
struct ma_context;
|
||||||
|
|
||||||
|
namespace baudline {
|
||||||
|
|
||||||
|
class MiniAudioSource : public AudioSource {
|
||||||
|
public:
|
||||||
|
// deviceIndex = -1 for default input device
|
||||||
|
MiniAudioSource(double sampleRate = 48000.0, int channels = 1,
|
||||||
|
int deviceIndex = -1);
|
||||||
|
~MiniAudioSource() override;
|
||||||
|
|
||||||
|
bool open() override;
|
||||||
|
void close() override;
|
||||||
|
size_t read(float* buffer, size_t frames) override;
|
||||||
|
|
||||||
|
double sampleRate() const override { return sampleRate_; }
|
||||||
|
int channels() const override { return channels_; }
|
||||||
|
bool isRealTime() const override { return true; }
|
||||||
|
bool isEOF() const override { return false; }
|
||||||
|
|
||||||
|
// List available input devices (for UI enumeration).
|
||||||
|
struct DeviceInfo {
|
||||||
|
int index;
|
||||||
|
std::string name;
|
||||||
|
int maxInputChannels;
|
||||||
|
double defaultSampleRate;
|
||||||
|
};
|
||||||
|
static std::vector<DeviceInfo> listInputDevices();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void dataCallback(ma_device* device, void* output,
|
||||||
|
const void* input, unsigned int frameCount);
|
||||||
|
|
||||||
|
double sampleRate_;
|
||||||
|
int channels_;
|
||||||
|
int deviceIndex_;
|
||||||
|
bool opened_ = false;
|
||||||
|
|
||||||
|
std::unique_ptr<ma_device> device_;
|
||||||
|
std::unique_ptr<RingBuffer<float>> ringBuf_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace baudline
|
||||||
95864
src/audio/miniaudio.h
Normal file
95864
src/audio/miniaudio.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -78,7 +78,7 @@ bool Application::init(int argc, char** argv) {
|
|||||||
ImGui_ImplOpenGL3_Init("#version 120");
|
ImGui_ImplOpenGL3_Init("#version 120");
|
||||||
|
|
||||||
// Enumerate audio devices
|
// Enumerate audio devices
|
||||||
paDevices_ = PortAudioSource::listInputDevices();
|
paDevices_ = MiniAudioSource::listInputDevices();
|
||||||
|
|
||||||
// Load saved config (overwrites defaults for FFT size, overlap, window, etc.)
|
// Load saved config (overwrites defaults for FFT size, overlap, window, etc.)
|
||||||
loadConfig();
|
loadConfig();
|
||||||
@@ -302,7 +302,7 @@ void Application::render() {
|
|||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Open PortAudio")) {
|
if (ImGui::MenuItem("Open Audio Device")) {
|
||||||
openPortAudio();
|
openPortAudio();
|
||||||
updateAnalyzerSettings();
|
updateAnalyzerSettings();
|
||||||
}
|
}
|
||||||
@@ -1094,14 +1094,14 @@ void Application::openPortAudio() {
|
|||||||
if (paDeviceIdx_ >= 0 && paDeviceIdx_ < static_cast<int>(paDevices_.size()))
|
if (paDeviceIdx_ >= 0 && paDeviceIdx_ < static_cast<int>(paDevices_.size()))
|
||||||
reqCh = std::min(paDevices_[paDeviceIdx_].maxInputChannels, kMaxChannels);
|
reqCh = std::min(paDevices_[paDeviceIdx_].maxInputChannels, kMaxChannels);
|
||||||
if (reqCh < 1) reqCh = 1;
|
if (reqCh < 1) reqCh = 1;
|
||||||
auto src = std::make_unique<PortAudioSource>(sr, reqCh, deviceIdx);
|
auto src = std::make_unique<MiniAudioSource>(sr, reqCh, deviceIdx);
|
||||||
if (src->open()) {
|
if (src->open()) {
|
||||||
audioSource_ = std::move(src);
|
audioSource_ = std::move(src);
|
||||||
settings_.sampleRate = sr;
|
settings_.sampleRate = sr;
|
||||||
settings_.isIQ = false;
|
settings_.isIQ = false;
|
||||||
settings_.numChannels = audioSource_->channels();
|
settings_.numChannels = audioSource_->channels();
|
||||||
} else {
|
} else {
|
||||||
std::fprintf(stderr, "Failed to open PortAudio device\n");
|
std::fprintf(stderr, "Failed to open audio device\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "dsp/SpectrumAnalyzer.h"
|
#include "dsp/SpectrumAnalyzer.h"
|
||||||
#include "audio/AudioSource.h"
|
#include "audio/AudioSource.h"
|
||||||
#include "audio/PortAudioSource.h"
|
#include "audio/MiniAudioSource.h"
|
||||||
#include "ui/ColorMap.h"
|
#include "ui/ColorMap.h"
|
||||||
#include "ui/WaterfallDisplay.h"
|
#include "ui/WaterfallDisplay.h"
|
||||||
#include "ui/SpectrumDisplay.h"
|
#include "ui/SpectrumDisplay.h"
|
||||||
@@ -144,7 +144,7 @@ private:
|
|||||||
bool fileLoop_ = true;
|
bool fileLoop_ = true;
|
||||||
|
|
||||||
// Device selection
|
// Device selection
|
||||||
std::vector<PortAudioSource::DeviceInfo> paDevices_;
|
std::vector<MiniAudioSource::DeviceInfo> paDevices_;
|
||||||
int paDeviceIdx_ = 0;
|
int paDeviceIdx_ = 0;
|
||||||
|
|
||||||
// Channel colors (up to kMaxChannels). Defaults: L=purple, R=green.
|
// Channel colors (up to kMaxChannels). Defaults: L=purple, R=green.
|
||||||
|
|||||||
Reference in New Issue
Block a user