dropped portaudio for miniaudio
This commit is contained in:
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");
|
||||
|
||||
// Enumerate audio devices
|
||||
paDevices_ = PortAudioSource::listInputDevices();
|
||||
paDevices_ = MiniAudioSource::listInputDevices();
|
||||
|
||||
// Load saved config (overwrites defaults for FFT size, overlap, window, etc.)
|
||||
loadConfig();
|
||||
@@ -302,7 +302,7 @@ void Application::render() {
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
if (ImGui::MenuItem("Open PortAudio")) {
|
||||
if (ImGui::MenuItem("Open Audio Device")) {
|
||||
openPortAudio();
|
||||
updateAnalyzerSettings();
|
||||
}
|
||||
@@ -1094,14 +1094,14 @@ void Application::openPortAudio() {
|
||||
if (paDeviceIdx_ >= 0 && paDeviceIdx_ < static_cast<int>(paDevices_.size()))
|
||||
reqCh = std::min(paDevices_[paDeviceIdx_].maxInputChannels, kMaxChannels);
|
||||
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()) {
|
||||
audioSource_ = std::move(src);
|
||||
settings_.sampleRate = sr;
|
||||
settings_.isIQ = false;
|
||||
settings_.numChannels = audioSource_->channels();
|
||||
} 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 "dsp/SpectrumAnalyzer.h"
|
||||
#include "audio/AudioSource.h"
|
||||
#include "audio/PortAudioSource.h"
|
||||
#include "audio/MiniAudioSource.h"
|
||||
#include "ui/ColorMap.h"
|
||||
#include "ui/WaterfallDisplay.h"
|
||||
#include "ui/SpectrumDisplay.h"
|
||||
@@ -144,7 +144,7 @@ private:
|
||||
bool fileLoop_ = true;
|
||||
|
||||
// Device selection
|
||||
std::vector<PortAudioSource::DeviceInfo> paDevices_;
|
||||
std::vector<MiniAudioSource::DeviceInfo> paDevices_;
|
||||
int paDeviceIdx_ = 0;
|
||||
|
||||
// Channel colors (up to kMaxChannels). Defaults: L=purple, R=green.
|
||||
|
||||
Reference in New Issue
Block a user