mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-07-14 05:12:25 +08:00
feat: swap android's fft implementation (which wasn't right) with kissfft's and fix update frequency issues
This commit is contained in:
parent
7bff63c037
commit
02f1676db0
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -12,4 +12,7 @@
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "src/External/MimeTypes"]
|
||||
path = src/External/MimeTypes
|
||||
url = https://github.com/lasselukkari/MimeTypes.git
|
||||
url = https://github.com/lasselukkari/MimeTypes.git
|
||||
[submodule "src/External/kissfft"]
|
||||
path = src/External/kissfft
|
||||
url = https://github.com/mborgerding/kissfft.git
|
||||
|
@ -34,6 +34,8 @@ find_package(PulseAudio REQUIRED)
|
||||
|
||||
set(ENABLE_OPT OFF)
|
||||
set(SPIRV_CROSS_FORCE_PIC ON)
|
||||
set(KISSFFT_TEST OFF)
|
||||
set(KISSFFT_TOOLS OFF)
|
||||
|
||||
# Download CEF of specified version for current platform
|
||||
# Specify the CEF distribution version.
|
||||
@ -107,6 +109,7 @@ set(TARGET_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build)
|
||||
add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper)
|
||||
add_subdirectory(src/External/glslang-WallpaperEngine glslang)
|
||||
add_subdirectory(src/External/SPIRV-Cross-WallpaperEngine spirv-cross)
|
||||
add_subdirectory(src/External/kissfft kissfft)
|
||||
|
||||
# try to enable wayland builds when possible
|
||||
pkg_check_modules(WAYLAND_SUPPORT wayland-cursor wayland-protocols egl wayland-egl)
|
||||
@ -221,6 +224,7 @@ include_directories(
|
||||
src/External/MimeTypes
|
||||
src/External/json/include
|
||||
src/External/stb
|
||||
src/External/kissfft
|
||||
${MPV_INCLUDE_DIR}
|
||||
${GLEW_INCLUDE_DIR}
|
||||
${LZ4_INCLUDE_DIR}
|
||||
@ -235,9 +239,6 @@ add_executable(
|
||||
linux-wallpaperengine
|
||||
src/main.cpp
|
||||
|
||||
src/External/Android/fft.cpp
|
||||
src/External/Android/fft.h
|
||||
|
||||
src/External/MimeTypes/MimeTypes.cpp
|
||||
src/External/MimeTypes/MimeTypes.h
|
||||
|
||||
@ -497,6 +498,7 @@ target_link_libraries (linux-wallpaperengine PUBLIC
|
||||
${PULSEAUDIO_LIBRARY}
|
||||
${WAYLAND_LIBRARIES}
|
||||
${X11_LIBRARIES}
|
||||
kissfft
|
||||
glslang
|
||||
spirv-cross-core
|
||||
spirv-cross-glsl
|
||||
|
162
src/External/Android/fft.cpp
vendored
162
src/External/Android/fft.cpp
vendored
@ -1,162 +0,0 @@
|
||||
#include "fft.h"
|
||||
|
||||
namespace External::Android {
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* A Fixed point implementation of Fast Fourier Transform (FFT). Complex numbers
|
||||
* are represented by 32-bit integers, where higher 16 bits are real part and
|
||||
* lower ones are imaginary part. Few compromises are made between efficiency,
|
||||
* accuracy, and maintainability. To make it fast, arithmetic shifts are used
|
||||
* instead of divisions, and bitwise inverses are used instead of negates. To
|
||||
* keep it small, only radix-2 Cooley-Tukey algorithm is implemented, and only
|
||||
* half of the twiddle factors are stored. Although there are still ways to make
|
||||
* it even faster or smaller, it costs too much on one of the aspects.
|
||||
*/
|
||||
#ifdef __arm__
|
||||
#include <machine/cpu-features.h>
|
||||
#endif
|
||||
#define LOG_FFT_SIZE 10
|
||||
#define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
|
||||
static const int32_t twiddle [MAX_FFT_SIZE / 4] = {
|
||||
0x00008000, 0xff378001, 0xfe6e8002, 0xfda58006, 0xfcdc800a, 0xfc13800f, 0xfb4a8016, 0xfa81801e, 0xf9b88027,
|
||||
0xf8ef8032, 0xf827803e, 0xf75e804b, 0xf6958059, 0xf5cd8068, 0xf5058079, 0xf43c808b, 0xf374809e, 0xf2ac80b2,
|
||||
0xf1e480c8, 0xf11c80de, 0xf05580f6, 0xef8d8110, 0xeec6812a, 0xedff8146, 0xed388163, 0xec718181, 0xebab81a0,
|
||||
0xeae481c1, 0xea1e81e2, 0xe9588205, 0xe892822a, 0xe7cd824f, 0xe7078276, 0xe642829d, 0xe57d82c6, 0xe4b982f1,
|
||||
0xe3f4831c, 0xe3308349, 0xe26d8377, 0xe1a983a6, 0xe0e683d6, 0xe0238407, 0xdf61843a, 0xde9e846e, 0xdddc84a3,
|
||||
0xdd1b84d9, 0xdc598511, 0xdb998549, 0xdad88583, 0xda1885be, 0xd95885fa, 0xd8988637, 0xd7d98676, 0xd71b86b6,
|
||||
0xd65c86f6, 0xd59e8738, 0xd4e1877b, 0xd42487c0, 0xd3678805, 0xd2ab884c, 0xd1ef8894, 0xd13488dd, 0xd0798927,
|
||||
0xcfbe8972, 0xcf0489be, 0xce4b8a0c, 0xcd928a5a, 0xccd98aaa, 0xcc218afb, 0xcb698b4d, 0xcab28ba0, 0xc9fc8bf5,
|
||||
0xc9468c4a, 0xc8908ca1, 0xc7db8cf8, 0xc7278d51, 0xc6738dab, 0xc5c08e06, 0xc50d8e62, 0xc45b8ebf, 0xc3a98f1d,
|
||||
0xc2f88f7d, 0xc2488fdd, 0xc198903e, 0xc0e990a1, 0xc03a9105, 0xbf8c9169, 0xbedf91cf, 0xbe329236, 0xbd86929e,
|
||||
0xbcda9307, 0xbc2f9371, 0xbb8593dc, 0xbadc9448, 0xba3394b5, 0xb98b9523, 0xb8e39592, 0xb83c9603, 0xb7969674,
|
||||
0xb6f196e6, 0xb64c9759, 0xb5a897ce, 0xb5059843, 0xb46298b9, 0xb3c09930, 0xb31f99a9, 0xb27f9a22, 0xb1df9a9c,
|
||||
0xb1409b17, 0xb0a29b94, 0xb0059c11, 0xaf689c8f, 0xaecc9d0e, 0xae319d8e, 0xad979e0f, 0xacfd9e91, 0xac659f14,
|
||||
0xabcd9f98, 0xab36a01c, 0xaaa0a0a2, 0xaa0aa129, 0xa976a1b0, 0xa8e2a238, 0xa84fa2c2, 0xa7bda34c, 0xa72ca3d7,
|
||||
0xa69ca463, 0xa60ca4f0, 0xa57ea57e, 0xa4f0a60c, 0xa463a69c, 0xa3d7a72c, 0xa34ca7bd, 0xa2c2a84f, 0xa238a8e2,
|
||||
0xa1b0a976, 0xa129aa0a, 0xa0a2aaa0, 0xa01cab36, 0x9f98abcd, 0x9f14ac65, 0x9e91acfd, 0x9e0fad97, 0x9d8eae31,
|
||||
0x9d0eaecc, 0x9c8faf68, 0x9c11b005, 0x9b94b0a2, 0x9b17b140, 0x9a9cb1df, 0x9a22b27f, 0x99a9b31f, 0x9930b3c0,
|
||||
0x98b9b462, 0x9843b505, 0x97ceb5a8, 0x9759b64c, 0x96e6b6f1, 0x9674b796, 0x9603b83c, 0x9592b8e3, 0x9523b98b,
|
||||
0x94b5ba33, 0x9448badc, 0x93dcbb85, 0x9371bc2f, 0x9307bcda, 0x929ebd86, 0x9236be32, 0x91cfbedf, 0x9169bf8c,
|
||||
0x9105c03a, 0x90a1c0e9, 0x903ec198, 0x8fddc248, 0x8f7dc2f8, 0x8f1dc3a9, 0x8ebfc45b, 0x8e62c50d, 0x8e06c5c0,
|
||||
0x8dabc673, 0x8d51c727, 0x8cf8c7db, 0x8ca1c890, 0x8c4ac946, 0x8bf5c9fc, 0x8ba0cab2, 0x8b4dcb69, 0x8afbcc21,
|
||||
0x8aaaccd9, 0x8a5acd92, 0x8a0cce4b, 0x89becf04, 0x8972cfbe, 0x8927d079, 0x88ddd134, 0x8894d1ef, 0x884cd2ab,
|
||||
0x8805d367, 0x87c0d424, 0x877bd4e1, 0x8738d59e, 0x86f6d65c, 0x86b6d71b, 0x8676d7d9, 0x8637d898, 0x85fad958,
|
||||
0x85beda18, 0x8583dad8, 0x8549db99, 0x8511dc59, 0x84d9dd1b, 0x84a3dddc, 0x846ede9e, 0x843adf61, 0x8407e023,
|
||||
0x83d6e0e6, 0x83a6e1a9, 0x8377e26d, 0x8349e330, 0x831ce3f4, 0x82f1e4b9, 0x82c6e57d, 0x829de642, 0x8276e707,
|
||||
0x824fe7cd, 0x822ae892, 0x8205e958, 0x81e2ea1e, 0x81c1eae4, 0x81a0ebab, 0x8181ec71, 0x8163ed38, 0x8146edff,
|
||||
0x812aeec6, 0x8110ef8d, 0x80f6f055, 0x80def11c, 0x80c8f1e4, 0x80b2f2ac, 0x809ef374, 0x808bf43c, 0x8079f505,
|
||||
0x8068f5cd, 0x8059f695, 0x804bf75e, 0x803ef827, 0x8032f8ef, 0x8027f9b8, 0x801efa81, 0x8016fb4a, 0x800ffc13,
|
||||
0x800afcdc, 0x8006fda5, 0x8002fe6e, 0x8001ff37,
|
||||
};
|
||||
|
||||
/* Returns the multiplication of \conj{a} and {b}. */
|
||||
static inline int32_t mult (int32_t a, int32_t b) {
|
||||
#if __ARM_ARCH__ >= 6
|
||||
int32_t t = b;
|
||||
__asm__ ("smuad %0, %0, %1" : "+r"(t) : "r"(a));
|
||||
__asm__ ("smusdx %0, %0, %1" : "+r"(b) : "r"(a));
|
||||
__asm__ ("pkhtb %0, %0, %1, ASR #16" : "+r"(t) : "r"(b));
|
||||
return t;
|
||||
#else
|
||||
return (((a >> 16) * (b >> 16) + static_cast<int16_t> (a) * static_cast<int16_t> (b)) & ~0xFFFF) |
|
||||
((((a >> 16) * static_cast<int16_t> (b) - static_cast<int16_t> (a) * (b >> 16)) >> 16) & 0xFFFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int32_t half (int32_t a) {
|
||||
#if __ARM_ARCH__ >= 6
|
||||
__asm__ ("shadd16 %0, %0, %1" : "+r"(a) : "r"(0));
|
||||
return a;
|
||||
#else
|
||||
return ((a >> 1) & ~0x8000) | (a & 0x8000);
|
||||
#endif
|
||||
}
|
||||
|
||||
void fixed_fft (int n, int32_t* v) {
|
||||
int scale = LOG_FFT_SIZE, i, p, r;
|
||||
for (r = 0, i = 1; i < n; ++i) {
|
||||
for (p = n; !(p & r); p >>= 1, r ^= p)
|
||||
;
|
||||
if (i < r) {
|
||||
int32_t t = v [i];
|
||||
v [i] = v [r];
|
||||
v [r] = t;
|
||||
}
|
||||
}
|
||||
for (p = 1; p < n; p <<= 1) {
|
||||
--scale;
|
||||
for (i = 0; i < n; i += p << 1) {
|
||||
int32_t x = half (v [i]);
|
||||
int32_t y = half (v [i + p]);
|
||||
v [i] = x + y;
|
||||
v [i + p] = x - y;
|
||||
}
|
||||
for (r = 1; r < p; ++r) {
|
||||
int32_t w = MAX_FFT_SIZE / 4 - (r << scale);
|
||||
i = w >> 31;
|
||||
w = twiddle [(w ^ i) - i] ^ (i << 16);
|
||||
for (i = r; i < n; i += p << 1) {
|
||||
int32_t x = half (v [i]);
|
||||
int32_t y = mult (w, v [i + p]);
|
||||
v [i] = x - y;
|
||||
v [i + p] = x + y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fixed_fft_real (int n, int32_t* v) {
|
||||
int scale = LOG_FFT_SIZE, m = n >> 1, i;
|
||||
fixed_fft (n, v);
|
||||
for (i = 1; i <= n; i <<= 1, --scale)
|
||||
;
|
||||
v [0] = mult (~v [0], 0x80008000);
|
||||
v [m] = half (v [m]);
|
||||
for (i = 1; i < n >> 1; ++i) {
|
||||
int32_t x = half (v [i]);
|
||||
int32_t z = half (v [n - i]);
|
||||
int32_t y = z - (x ^ 0xFFFF);
|
||||
x = half (x + (z ^ 0xFFFF));
|
||||
y = mult (y, twiddle [i << scale]);
|
||||
v [i] = x - y;
|
||||
v [n - i] = (x + y) ^ 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
bool doFft (uint8_t* fft, uint8_t* waveform) {
|
||||
int32_t workspace [WAVE_BUFFER_SIZE >> 1];
|
||||
int32_t nonzero = 0;
|
||||
for (uint32_t i = 0; i < WAVE_BUFFER_SIZE; i += 2) {
|
||||
workspace [i >> 1] = ((waveform [i] ^ 0x80) << 24) | ((waveform [i + 1] ^ 0x80) << 8);
|
||||
nonzero |= workspace [i >> 1];
|
||||
}
|
||||
if (nonzero) {
|
||||
fixed_fft_real (WAVE_BUFFER_SIZE >> 1, workspace);
|
||||
}
|
||||
for (uint32_t i = 0; i < WAVE_BUFFER_SIZE; i += 2) {
|
||||
short tmp = workspace [i >> 1] >> 21;
|
||||
while (tmp > 127 || tmp < -128)
|
||||
tmp >>= 1;
|
||||
fft [i] = tmp;
|
||||
tmp = workspace [i >> 1];
|
||||
tmp >>= 5;
|
||||
while (tmp > 127 || tmp < -128)
|
||||
tmp >>= 1;
|
||||
fft [i + 1] = tmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace External::Android
|
11
src/External/Android/fft.h
vendored
11
src/External/Android/fft.h
vendored
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#define WAVE_BUFFER_SIZE 1024
|
||||
|
||||
namespace External::Android {
|
||||
bool doFft (uint8_t* fft, uint8_t* waveform);
|
||||
}
|
1
src/External/kissfft
vendored
Submodule
1
src/External/kissfft
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit febd4caeed32e33ad8b2e0bb5ea77542c40f18ec
|
@ -1,11 +1,9 @@
|
||||
#include "CPulseAudioPlaybackRecorder.h"
|
||||
#include "External/Android/fft.h"
|
||||
#include "WallpaperEngine/Logging/CLog.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <glm/common.hpp>
|
||||
|
||||
namespace WallpaperEngine::Audio::Drivers::Recorders {
|
||||
float movetowards (float current, float target, float maxDelta) {
|
||||
if (abs (target - current) <= maxDelta)
|
||||
return target;
|
||||
@ -13,6 +11,7 @@ float movetowards (float current, float target, float maxDelta) {
|
||||
return current + glm::sign (target - current) * maxDelta;
|
||||
}
|
||||
|
||||
namespace WallpaperEngine::Audio::Drivers::Recorders {
|
||||
void pa_stream_notify_cb (pa_stream* stream, void* /*userdata*/) {
|
||||
switch (pa_stream_get_state (stream)) {
|
||||
case PA_STREAM_FAILED: sLog.error ("Cannot open stream for capture. Audio processing is disabled"); break;
|
||||
@ -22,7 +21,7 @@ void pa_stream_notify_cb (pa_stream* stream, void* /*userdata*/) {
|
||||
}
|
||||
|
||||
void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userdata) {
|
||||
auto* recorder = static_cast<CPulseAudioPlaybackRecorder*> (userdata);
|
||||
auto* recorder = static_cast<CPulseAudioPlaybackRecorder::SPulseAudioData*> (userdata);
|
||||
|
||||
// Careful when to pa_stream_peek() and pa_stream_drop()!
|
||||
// c.f. https://www.freedesktop.org/software/pulseaudio/doxygen/stream_8h.html#ac2838c449cde56e169224d7fe3d00824
|
||||
@ -47,27 +46,37 @@ void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userda
|
||||
} else if (currentSize > 0 && data) {
|
||||
size_t dataToCopy = std::min (currentSize, WAVE_BUFFER_SIZE - recorder->currentWritePointer);
|
||||
|
||||
memcpy (&recorder->audio_buffer_tmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
|
||||
// depending on the amount of data available, we might want to read one or multiple frames
|
||||
size_t end = recorder->currentWritePointer + dataToCopy;
|
||||
|
||||
recorder->currentWritePointer += dataToCopy;
|
||||
// this packet will fill the buffer, perform some extra checks for extra full buffers and get the latest one
|
||||
if (end == WAVE_BUFFER_SIZE) {
|
||||
size_t numberOfFullBuffers = (currentSize - dataToCopy) / WAVE_BUFFER_SIZE;
|
||||
|
||||
if (recorder->currentWritePointer == WAVE_BUFFER_SIZE) {
|
||||
// copy to the final buffer
|
||||
memcpy (recorder->audio_buffer, recorder->audio_buffer_tmp, WAVE_BUFFER_SIZE * sizeof (uint8_t));
|
||||
// reset the write pointer
|
||||
recorder->currentWritePointer = 0;
|
||||
recorder->fullframeReady = true;
|
||||
}
|
||||
if (numberOfFullBuffers > 0) {
|
||||
// calculate the start of the last block (we need the end of the previous block, hence the - 1)
|
||||
size_t startOfLastBuffer = dataToCopy + ((numberOfFullBuffers - 1) * WAVE_BUFFER_SIZE);
|
||||
// copy directly into the final buffer
|
||||
memcpy (recorder->audioBuffer, &data [startOfLastBuffer], WAVE_BUFFER_SIZE * sizeof (uint8_t));
|
||||
// copy whatever is left to the read/write buffer
|
||||
recorder->currentWritePointer = currentSize - startOfLastBuffer - WAVE_BUFFER_SIZE;
|
||||
memcpy (recorder->audioBufferTmp, &data [startOfLastBuffer + WAVE_BUFFER_SIZE], recorder->currentWritePointer * sizeof (uint8_t));
|
||||
} else {
|
||||
// okay, no full extra packets available, copy the rest of the data and flip the buffers
|
||||
memcpy (&recorder->audioBufferTmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
|
||||
uint8_t* tmp = recorder->audioBuffer;
|
||||
recorder->audioBuffer = recorder->audioBufferTmp;
|
||||
recorder->audioBufferTmp = tmp;
|
||||
// reset write pointer
|
||||
recorder->currentWritePointer = 0;
|
||||
}
|
||||
|
||||
// any data read left?
|
||||
if (dataToCopy < currentSize) {
|
||||
while ((currentSize - dataToCopy) > WAVE_BUFFER_SIZE)
|
||||
dataToCopy += WAVE_BUFFER_SIZE; // there's more than one full frame available, skip it entirely
|
||||
|
||||
// data pending, keep it in the buffer
|
||||
memcpy (recorder->audio_buffer_tmp, data + dataToCopy, (currentSize - dataToCopy) * sizeof (uint8_t));
|
||||
|
||||
recorder->currentWritePointer = currentSize - dataToCopy;
|
||||
// signal a new frame is ready
|
||||
recorder->fullFrameReady = true;
|
||||
} else {
|
||||
// copy over available data to the tmp buffer and everything should be set
|
||||
memcpy (&recorder->audioBufferTmp [recorder->currentWritePointer], data, dataToCopy * sizeof (uint8_t));
|
||||
recorder->currentWritePointer += dataToCopy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,31 +86,34 @@ void pa_stream_read_cb (pa_stream* stream, const size_t /*nbytes*/, void* userda
|
||||
}
|
||||
|
||||
void pa_server_info_cb (pa_context* ctx, const pa_server_info* info, void* userdata) {
|
||||
auto* recorder = static_cast<CPulseAudioPlaybackRecorder*> (userdata);
|
||||
auto* recorder = static_cast<CPulseAudioPlaybackRecorder::SPulseAudioData*> (userdata);
|
||||
|
||||
pa_sample_spec spec;
|
||||
spec.format = PA_SAMPLE_U8;
|
||||
spec.rate = 44100;
|
||||
spec.channels = 1;
|
||||
|
||||
if (recorder->getCaptureStream ()) {
|
||||
pa_stream_unref (recorder->getCaptureStream ());
|
||||
// get rid of the reference just in case
|
||||
recorder->setCaptureStream (nullptr);
|
||||
if (recorder->captureStream) {
|
||||
pa_stream_unref (recorder->captureStream);
|
||||
}
|
||||
|
||||
pa_stream* captureStream = pa_stream_new (ctx, "output monitor", &spec, nullptr);
|
||||
recorder->captureStream = pa_stream_new (ctx, "output monitor", &spec, nullptr);
|
||||
|
||||
// store the stream first, if the record start fails there'll still be a reference to it
|
||||
// so it can be free'd later
|
||||
recorder->setCaptureStream (captureStream),
|
||||
|
||||
pa_stream_set_state_callback (captureStream, &pa_stream_notify_cb, userdata);
|
||||
pa_stream_set_read_callback (captureStream, &pa_stream_read_cb, userdata);
|
||||
pa_stream_set_state_callback (recorder->captureStream, &pa_stream_notify_cb, userdata);
|
||||
pa_stream_set_read_callback (recorder->captureStream, &pa_stream_read_cb, userdata);
|
||||
|
||||
std::string monitor_name (info->default_sink_name);
|
||||
monitor_name += ".monitor";
|
||||
if (pa_stream_connect_record (captureStream, monitor_name.c_str (), nullptr, PA_STREAM_NOFLAGS) != 0) {
|
||||
|
||||
// setup latency
|
||||
pa_buffer_attr attr = {0};
|
||||
|
||||
// 10 = latency msecs, 750 = max msecs to store
|
||||
size_t bytesPerSec = pa_bytes_per_second (&spec);
|
||||
attr.fragsize = bytesPerSec * 10 / 100;
|
||||
attr.maxlength = attr.fragsize + bytesPerSec * 750 / 100;
|
||||
|
||||
if (pa_stream_connect_record (recorder->captureStream, monitor_name.c_str (), &attr, PA_STREAM_ADJUST_LATENCY) != 0) {
|
||||
sLog.error ("Failed to connect to input for recording");
|
||||
}
|
||||
}
|
||||
@ -138,12 +150,20 @@ void pa_context_notify_cb (pa_context* ctx, void* userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () : m_captureStream (nullptr) {
|
||||
CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () :
|
||||
m_captureStream (nullptr),
|
||||
m_captureData({
|
||||
.kisscfg = kiss_fftr_alloc (WAVE_BUFFER_SIZE, 0, nullptr, nullptr),
|
||||
.audioBuffer = new uint8_t [WAVE_BUFFER_SIZE],
|
||||
.audioBufferTmp = new uint8_t [WAVE_BUFFER_SIZE]
|
||||
}),
|
||||
m_audioFFTbuffer {0},
|
||||
m_FFTinfo {0} {
|
||||
this->m_mainloop = pa_mainloop_new ();
|
||||
this->m_mainloopApi = pa_mainloop_get_api (this->m_mainloop);
|
||||
this->m_context = pa_context_new (this->m_mainloopApi, "wallpaperengine-audioprocessing");
|
||||
|
||||
pa_context_set_state_callback (this->m_context, &pa_context_notify_cb, this);
|
||||
pa_context_set_state_callback (this->m_context, &pa_context_notify_cb, &this->m_captureData);
|
||||
|
||||
if (pa_context_connect (this->m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
|
||||
sLog.error ("PulseAudio connection failed! Audio processing is disabled");
|
||||
@ -156,54 +176,58 @@ CPulseAudioPlaybackRecorder::CPulseAudioPlaybackRecorder () : m_captureStream (n
|
||||
}
|
||||
|
||||
CPulseAudioPlaybackRecorder::~CPulseAudioPlaybackRecorder () {
|
||||
delete [] this->m_captureData.audioBufferTmp;
|
||||
delete [] this->m_captureData.audioBuffer;
|
||||
free (this->m_captureData.kisscfg);
|
||||
|
||||
pa_context_disconnect (this->m_context);
|
||||
pa_mainloop_free (this->m_mainloop);
|
||||
}
|
||||
|
||||
pa_stream* CPulseAudioPlaybackRecorder::getCaptureStream () {
|
||||
return this->m_captureStream;
|
||||
}
|
||||
|
||||
void CPulseAudioPlaybackRecorder::setCaptureStream (pa_stream* stream) {
|
||||
this->m_captureStream = stream;
|
||||
}
|
||||
|
||||
void CPulseAudioPlaybackRecorder::update () {
|
||||
pa_mainloop_iterate (this->m_mainloop, 0, nullptr);
|
||||
|
||||
// interpolate current values to the destination
|
||||
for (int i = 0; i < 64; i++) {
|
||||
this->audio64 [i] = movetowards (this->audio64 [i], fft_destination64 [i], 0.1f);
|
||||
this->audio64 [i] = movetowards (this->audio64 [i], this->m_FFTdestination64 [i], 0.3f);
|
||||
if (i >= 32)
|
||||
continue;
|
||||
this->audio32 [i] = movetowards (this->audio32 [i], fft_destination32 [i], 0.1f);
|
||||
this->audio32 [i] = movetowards (this->audio32 [i], this->m_FFTdestination32 [i], 0.3f);
|
||||
if (i >= 16)
|
||||
continue;
|
||||
this->audio16 [i] = movetowards (this->audio16 [i], fft_destination16 [i], 0.1f);
|
||||
this->audio16 [i] = movetowards (this->audio16 [i], this->m_FFTdestination16 [i], 0.3f);
|
||||
}
|
||||
|
||||
if (!this->fullframeReady)
|
||||
if (!this->m_captureData.fullFrameReady)
|
||||
return;
|
||||
|
||||
this->fullframeReady = false;
|
||||
this->m_captureData.fullFrameReady = false;
|
||||
|
||||
External::Android::doFft (audio_fft, audio_buffer);
|
||||
// convert audio data to deltas so the fft library can properly handle it
|
||||
for (int i = 0; i < WAVE_BUFFER_SIZE; i ++) {
|
||||
this->m_audioFFTbuffer [i] = (this->m_captureData.audioBuffer[i] - 128) / 128.0f;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
const int paramInt = (i + 2) * 2;
|
||||
float f1 = audio_fft [paramInt];
|
||||
float f2 = audio_fft [paramInt + 1];
|
||||
f2 = f1 * f1 + f2 * f2;
|
||||
f1 = 0.0F;
|
||||
if (f2 > 0.0F)
|
||||
f1 = 0.35F * static_cast<float> (log10 (f2));
|
||||
// perform full fft pass
|
||||
kiss_fftr (this->m_captureData.kisscfg, this->m_audioFFTbuffer, this->m_FFTinfo);
|
||||
|
||||
this->fft_destination64 [i] =
|
||||
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 63.0F) * 1.0f - 0.5f)));
|
||||
this->fft_destination32 [i >> 1] =
|
||||
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 31.0F) * 1.0f - 0.5f)));
|
||||
this->fft_destination16 [i >> 2] =
|
||||
fmin (1.0F, f1 * static_cast<float> (2.0f - pow (M_E, (1.0F - i / 15.0F) * 1.0f - 0.5f)));
|
||||
// now reduce to the different bands
|
||||
// use just one for loop to produce all 3
|
||||
for (int band = 0; band < 64; band ++) {
|
||||
int index = band * 2;
|
||||
float f1 = this->m_FFTinfo[index].r;
|
||||
float f2 = this->m_FFTinfo[index].i;
|
||||
f2 = f1 * f1 + f2 * f2; // magnitude
|
||||
f1 = 0.0f;
|
||||
|
||||
if (f2 > 0.0f) {
|
||||
f1 = 0.35f * log10 (f2);
|
||||
}
|
||||
|
||||
this->m_FFTdestination64 [band] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 63.0f) * 1.0f - 0.5f)));
|
||||
this->m_FFTdestination32 [band >> 1] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 31.0f) * 1.0f - 0.5f)));
|
||||
this->m_FFTdestination16 [band >> 2] = fmin (1.0f, f1 * static_cast<float> (2.0f - pow (M_E, (1.0f - band / 15.0f) * 1.0f - 0.5f)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WallpaperEngine::Audio::Drivers::Recorders
|
@ -1,43 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "kiss_fftr.h"
|
||||
#include "CPlaybackRecorder.h"
|
||||
#include "External/Android/fft.h"
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#define WAVE_BUFFER_SIZE 1024
|
||||
|
||||
namespace WallpaperEngine::Audio::Drivers::Recorders {
|
||||
class CPlaybackRecorder;
|
||||
|
||||
class CPulseAudioPlaybackRecorder final : public CPlaybackRecorder {
|
||||
public:
|
||||
/**
|
||||
* Struct that contains all the required data for the PulseAudio callbacks
|
||||
*/
|
||||
struct SPulseAudioData {
|
||||
kiss_fftr_cfg kisscfg;
|
||||
uint8_t* audioBuffer;
|
||||
uint8_t* audioBufferTmp;
|
||||
size_t currentWritePointer;
|
||||
bool fullFrameReady;
|
||||
pa_stream* captureStream;
|
||||
};
|
||||
|
||||
CPulseAudioPlaybackRecorder ();
|
||||
~CPulseAudioPlaybackRecorder () override;
|
||||
|
||||
void update () override;
|
||||
|
||||
/**
|
||||
* @return The current stream we're capturing from
|
||||
*/
|
||||
[[nodiscard]] pa_stream* getCaptureStream ();
|
||||
|
||||
/**
|
||||
* @param stream The new stream to be capturing off from
|
||||
*/
|
||||
void setCaptureStream (pa_stream* stream);
|
||||
|
||||
uint8_t audio_buffer [WAVE_BUFFER_SIZE] = {0x80};
|
||||
uint8_t audio_buffer_tmp [WAVE_BUFFER_SIZE] = {0x80};
|
||||
uint8_t audio_fft [WAVE_BUFFER_SIZE] = {0};
|
||||
size_t currentWritePointer = 0;
|
||||
bool fullframeReady = false;
|
||||
|
||||
private:
|
||||
pa_mainloop* m_mainloop;
|
||||
pa_mainloop_api* m_mainloopApi;
|
||||
pa_context* m_context;
|
||||
pa_stream* m_captureStream;
|
||||
SPulseAudioData m_captureData;
|
||||
|
||||
float fft_destination64 [64] = {0};
|
||||
float fft_destination32 [32] = {0};
|
||||
float fft_destination16 [16] = {0};
|
||||
float m_audioFFTbuffer [WAVE_BUFFER_SIZE];
|
||||
kiss_fft_cpx m_FFTinfo [WAVE_BUFFER_SIZE / 2 + 1] = {0};
|
||||
float m_FFTdestination64 [64] = {0};
|
||||
float m_FFTdestination32 [32] = {0};
|
||||
float m_FFTdestination16 [16] = {0};
|
||||
};
|
||||
} // namespace WallpaperEngine::Audio::Drivers::Recorders
|
||||
|
Loading…
Reference in New Issue
Block a user