mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
fix(waveform): legal notices and reformatting
This commit effectively grants [the "Atoll" project](https://github.com/Ebullioscopic/Atoll) permission from [ZephyrCodesStuff](https://github.com/ZephyrCodesStuff) for using, modifying and distributing the source code contained in [`rtaudio`](https://github.com/ZephyrCodesStuff/rtaudio) in compliance with the terms of the GPLv3 license, as an alternative to its original AGPLv3 license for this specific integration.
This commit is contained in:
parent
2ca62735b7
commit
c7d470da2f
7 changed files with 215 additions and 120 deletions
|
|
@ -1,14 +1,22 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors and zeph
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
* Adapted from rtaudio (https://github.com/ZephyrCodesStuff/rtaudio)
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
*
|
*
|
||||||
* Objective-C bridge to expose C++ AudioProcessor to Swift.
|
* Objective-C bridge to expose C++ AudioProcessor to Swift.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
@ -19,8 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
@interface AudioBridge : NSObject
|
@interface AudioBridge : NSObject
|
||||||
|
|
||||||
- (void)processBuffer:(const float *)buffer count:(int)count;
|
- (void)processBuffer:(const float *)buffer count:(int)count;
|
||||||
|
|
||||||
// Reads atomically from the processor — safe to call from any thread
|
|
||||||
- (simd_float4)getSmoothedMagnitudes;
|
- (simd_float4)getSmoothedMagnitudes;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors and zeph
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
* Adapted from rtaudio (https://github.com/ZephyrCodesStuff/rtaudio)
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
*
|
*
|
||||||
* Objective-C++ implementation bridging AudioProcessor to Swift.
|
* Objective-C++ implementation bridging AudioProcessor to Swift.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "AudioBridge.h"
|
#import "AudioBridge.h"
|
||||||
|
|
@ -31,8 +39,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (simd_float4)getSmoothedMagnitudes {
|
- (simd_float4)getSmoothedMagnitudes {
|
||||||
// Calls getBand() which does memory_order_relaxed atomic loads —
|
|
||||||
// no heap allocation, safe to call from the render thread
|
|
||||||
return simd_make_float4(
|
return simd_make_float4(
|
||||||
processor->getBand(0),
|
processor->getBand(0),
|
||||||
processor->getBand(1),
|
processor->getBand(1),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,141 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
|
*
|
||||||
|
* Real-time audio processor using Accelerate framework for efficient RMS
|
||||||
|
* and biquad filtering.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AudioProcessor.hpp"
|
#include "AudioProcessor.hpp"
|
||||||
|
|
||||||
|
#include <Accelerate/Accelerate.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
AudioProcessor::AudioProcessor( float sr ) : sampleRate( sr )
|
||||||
|
{
|
||||||
|
// Single biquad per band
|
||||||
|
setupBiquad( 0, FilterType::LowPass, 250.0f, 0.707f );
|
||||||
|
setupBiquad( 1, FilterType::BandPass, 707.0f, 0.40f );
|
||||||
|
setupBiquad( 2, FilterType::BandPass, 3464.0f, 0.85f );
|
||||||
|
setupBiquad( 3, FilterType::HighPass, 6000.0f, 0.707f );
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioProcessor::~AudioProcessor()
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < kBands; ++i )
|
||||||
|
{
|
||||||
|
vDSP_biquad_DestroySetup( setups[i] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioProcessor::process( const float* __restrict__ buffer,
|
||||||
|
int totalSamples )
|
||||||
|
{
|
||||||
|
if ( __builtin_expect( totalSamples <= 0, 0 ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float* src = buffer;
|
||||||
|
int remaining = totalSamples;
|
||||||
|
|
||||||
|
while ( remaining > 0 )
|
||||||
|
{
|
||||||
|
int toCopy = std::min( remaining, kBlockSize - writePos );
|
||||||
|
memcpy( mono + writePos, src, toCopy * sizeof( float ) );
|
||||||
|
writePos += toCopy;
|
||||||
|
src += toCopy;
|
||||||
|
remaining -= toCopy;
|
||||||
|
|
||||||
|
if ( writePos >= kBlockSize )
|
||||||
|
{
|
||||||
|
processBlock();
|
||||||
|
writePos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float AudioProcessor::getBand( int i ) const
|
||||||
|
{
|
||||||
|
return bandParams[i].load( std::memory_order_relaxed );
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioProcessor::processBlock()
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < kBands; ++i )
|
||||||
|
{
|
||||||
|
vDSP_biquad( setups[i], delays[i], mono, 1, filtered, 1, kBlockSize );
|
||||||
|
|
||||||
|
// RMS over the block — more stable than peak for driving animation
|
||||||
|
float rms = 0.0f;
|
||||||
|
vDSP_rmsqv( filtered, 1, &rms, kBlockSize );
|
||||||
|
|
||||||
|
if ( __builtin_expect( !std::isfinite( rms ), 0 ) )
|
||||||
|
{
|
||||||
|
rms = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float boostedRms = rms * kGains[i];
|
||||||
|
boostedRms = std::min( boostedRms, 1.0f );
|
||||||
|
|
||||||
|
// Asymmetric envelope: attack fast, release slow
|
||||||
|
float prev = envelopes[i];
|
||||||
|
envelopes[i] = ( boostedRms > prev )
|
||||||
|
? prev * ( 1.0f - kAttack ) + boostedRms * kAttack
|
||||||
|
: prev * ( 1.0f - kRelease ) + boostedRms * kRelease;
|
||||||
|
|
||||||
|
bandParams[i].store( envelopes[i], std::memory_order_relaxed );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioProcessor::setupBiquad( int idx, FilterType type, float freq,
|
||||||
|
float q )
|
||||||
|
{
|
||||||
|
const double w0 = 2.0 * M_PI * freq / sampleRate;
|
||||||
|
const double sinw = std::sin( w0 );
|
||||||
|
const double cosw = std::cos( w0 );
|
||||||
|
const double alpha = sinw / ( 2.0 * q );
|
||||||
|
|
||||||
|
double b0, b1, b2;
|
||||||
|
const double a0 = 1.0 + alpha;
|
||||||
|
const double a1 = -2.0 * cosw;
|
||||||
|
const double a2 = 1.0 - alpha;
|
||||||
|
|
||||||
|
switch ( type )
|
||||||
|
{
|
||||||
|
case FilterType::LowPass:
|
||||||
|
b0 = ( 1.0 - cosw ) * 0.5;
|
||||||
|
b1 = 1.0 - cosw;
|
||||||
|
b2 = b0;
|
||||||
|
break;
|
||||||
|
case FilterType::HighPass:
|
||||||
|
b0 = ( 1.0 + cosw ) * 0.5;
|
||||||
|
b1 = -( 1.0 + cosw );
|
||||||
|
b2 = b0;
|
||||||
|
break;
|
||||||
|
case FilterType::BandPass:
|
||||||
|
b0 = alpha;
|
||||||
|
b1 = 0.0;
|
||||||
|
b2 = -alpha;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double coeffs[5] = { b0 / a0, b1 / a0, b2 / a0, a1 / a0, a2 / a0 };
|
||||||
|
setups[idx] = vDSP_biquad_CreateSetup( coeffs, 1 );
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,134 +1,68 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors and zeph
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
*
|
*
|
||||||
* Real-time audio processor using Accelerate framework for efficient FFT
|
* Real-time audio processor using Accelerate framework for efficient RMS
|
||||||
* and band filtering. Adapted from rtaudio project (https://github.com/ZephyrCodesStuff/rtaudio)
|
* and biquad filtering.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef AudioProcessor_hpp
|
#ifndef AUDIOPROCESSOR_HPP
|
||||||
#define AudioProcessor_hpp
|
#define AUDIOPROCESSOR_HPP
|
||||||
|
|
||||||
#include <Accelerate/Accelerate.h>
|
#include <Accelerate/Accelerate.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
class AudioProcessor {
|
class AudioProcessor {
|
||||||
private:
|
private:
|
||||||
static constexpr int kBlockSize = 512; // lower latency than 1024
|
static constexpr int kBlockSize = 512;
|
||||||
static constexpr int kBands = 4;
|
static constexpr int kBands = 4;
|
||||||
static constexpr float kAttack = 0.85f; // fast rise
|
static constexpr float kAttack = 0.85f; // fast rise
|
||||||
static constexpr float kRelease = 0.40f; // slower fall — more musical
|
static constexpr float kRelease = 0.40f; // slower fall — more musical
|
||||||
static constexpr float kGains[kBands] = {3.5f, 6.0f, 9.0f, 20.0f};
|
static constexpr float kGains[kBands] = { 3.5f, 6.0f, 9.0f, 20.0f };
|
||||||
|
|
||||||
float sampleRate;
|
float sampleRate;
|
||||||
int writePos = 0;
|
int writePos = 0;
|
||||||
float mono[kBlockSize] = {};
|
float mono[kBlockSize] = {};
|
||||||
float filtered[kBlockSize] = {};
|
float filtered[kBlockSize] = {};
|
||||||
|
|
||||||
vDSP_biquad_Setup setups[kBands];
|
vDSP_biquad_Setup setups[kBands];
|
||||||
alignas(16) float delays[kBands][4] = {};
|
alignas( 16 ) float delays[kBands][4] = {};
|
||||||
|
|
||||||
float envelopes[kBands] = {};
|
float envelopes[kBands] = {};
|
||||||
|
|
||||||
// Lock-free: audio thread writes, render thread reads
|
// Lock-free: audio thread writes, render thread reads
|
||||||
// One atomic per band — fits in a cache line
|
// One atomic per band — fits in a cache line
|
||||||
alignas(64) std::atomic<float> bandParams[kBands] = {};
|
alignas( 64 ) std::atomic< float > bandParams[kBands] = {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AudioProcessor(float sr = 48000.0f) : sampleRate(sr) {
|
explicit AudioProcessor( float sr = 48000.0f );
|
||||||
// Single biquad per band — just enough separation, minimal CPU
|
~AudioProcessor();
|
||||||
setupBiquad(0, FilterType::LowPass, 250.0f, 0.707f);
|
|
||||||
setupBiquad(1, FilterType::BandPass, 707.0f, 0.40f);
|
|
||||||
setupBiquad(2, FilterType::BandPass, 3464.0f, 0.85f);
|
|
||||||
setupBiquad(3, FilterType::HighPass, 6000.0f, 0.707f);
|
|
||||||
}
|
|
||||||
|
|
||||||
~AudioProcessor() {
|
|
||||||
for (int i = 0; i < kBands; ++i)
|
|
||||||
vDSP_biquad_DestroySetup(setups[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by CoreAudio — must be real-time safe (no alloc, no locks)
|
// Called by CoreAudio — must be real-time safe (no alloc, no locks)
|
||||||
void process(const float* __restrict__ buffer, int totalSamples) {
|
void process( const float* __restrict__ buffer, int totalSamples );
|
||||||
if (__builtin_expect(totalSamples <= 0, 0)) return;
|
|
||||||
|
|
||||||
const float* src = buffer;
|
|
||||||
int remaining = totalSamples;
|
|
||||||
|
|
||||||
while (remaining > 0) {
|
|
||||||
int toCopy = std::min(remaining, kBlockSize - writePos);
|
|
||||||
memcpy(mono + writePos, src, toCopy * sizeof(float));
|
|
||||||
writePos += toCopy;
|
|
||||||
src += toCopy;
|
|
||||||
remaining -= toCopy;
|
|
||||||
|
|
||||||
if (writePos >= kBlockSize) {
|
|
||||||
processBlock();
|
|
||||||
writePos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by render thread — lock-free read
|
// Called by render thread — lock-free read
|
||||||
float getBand(int i) const {
|
float getBand( int i ) const;
|
||||||
return bandParams[i].load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class FilterType { LowPass, BandPass, HighPass };
|
enum class FilterType { LowPass, BandPass, HighPass };
|
||||||
|
|
||||||
void processBlock() {
|
void processBlock();
|
||||||
for (int i = 0; i < kBands; ++i) {
|
void setupBiquad( int idx, FilterType type, float freq, float q );
|
||||||
vDSP_biquad(setups[i], delays[i], mono, 1, filtered, 1, kBlockSize);
|
|
||||||
|
|
||||||
// RMS over the block — more stable than peak for driving animation
|
|
||||||
float rms = 0.0f;
|
|
||||||
vDSP_rmsqv(filtered, 1, &rms, kBlockSize);
|
|
||||||
|
|
||||||
if (__builtin_expect(!std::isfinite(rms), 0)) rms = 0.0f;
|
|
||||||
|
|
||||||
float boostedRms = rms * kGains[i];
|
|
||||||
boostedRms = std::min(boostedRms, 1.0f);
|
|
||||||
|
|
||||||
// Asymmetric envelope: attack fast, release slow
|
|
||||||
float prev = envelopes[i];
|
|
||||||
envelopes[i] = (boostedRms > prev)
|
|
||||||
? prev * (1.0f - kAttack) + boostedRms * kAttack
|
|
||||||
: prev * (1.0f - kRelease) + boostedRms * kRelease;
|
|
||||||
|
|
||||||
bandParams[i].store(envelopes[i], std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setupBiquad(int idx, FilterType type, float freq, float q) {
|
|
||||||
const double w0 = 2.0 * M_PI * freq / sampleRate;
|
|
||||||
const double sinw = std::sin(w0);
|
|
||||||
const double cosw = std::cos(w0);
|
|
||||||
const double alpha = sinw / (2.0 * q);
|
|
||||||
|
|
||||||
double b0, b1, b2;
|
|
||||||
const double a0 = 1.0 + alpha;
|
|
||||||
const double a1 = -2.0 * cosw;
|
|
||||||
const double a2 = 1.0 - alpha;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case FilterType::LowPass:
|
|
||||||
b0 = (1.0 - cosw) * 0.5; b1 = 1.0 - cosw; b2 = b0; break;
|
|
||||||
case FilterType::HighPass:
|
|
||||||
b0 = (1.0 + cosw) * 0.5; b1 = -(1.0 + cosw); b2 = b0; break;
|
|
||||||
case FilterType::BandPass:
|
|
||||||
b0 = alpha; b1 = 0.0; b2 = -alpha; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const double coeffs[5] = { b0/a0, b1/a0, b2/a0, a1/a0, a2/a0 };
|
|
||||||
setups[idx] = vDSP_biquad_CreateSetup(coeffs, 1);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !AudioProcessor_hpp
|
#endif // AUDIOPROCESSOR_HPP
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors and zeph
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
*
|
*
|
||||||
* CoreAudio tap for capturing real-time audio from music applications.
|
* CoreAudio tap for capturing real-time audio from music applications.
|
||||||
* Uses macOS 14.2+ Process Tap API for efficient audio capture.
|
* Uses macOS 14.2+ Process Tap API for efficient audio capture.
|
||||||
* Adapted from rtaudio project (https://github.com/ZephyrCodesStuff/rtaudio)
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,22 @@
|
||||||
/*
|
/*
|
||||||
* Atoll (DynamicIsland)
|
* Atoll (DynamicIsland)
|
||||||
* Copyright (C) 2024-2026 Atoll Contributors and zeph
|
* Original work Copyright (C) 2026 ZephyrCodesStuff (https://github.com/ZephyrCodesStuff/rtaudio)
|
||||||
|
* Modified work Copyright (C) 2026 Atoll Contributors
|
||||||
*
|
*
|
||||||
* Real-time audio spectrum visualization using CoreAudio tap data.
|
* Real-time audio spectrum visualization using CoreAudio tap data.
|
||||||
* Adapted from rtaudio project (https://github.com/ZephyrCodesStuff/rtaudio)
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This file 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 General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue