#include "AudioVisualizer.hh" #include "../../Lib/Audio.hh" using namespace Vivy; #define MAXPIXVALUE 7 // Some magix AV magic stuff AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent) : QWidget(parent) { if (!stream->isDecoded()) { stream->decodeData(); } double *decodedData = stream->getDecodedData(); if (decodedData == nullptr) throw std::logic_error("the passed stream is not decoded"); const size_t size = stream->getDecodedDataSize(); const size_t height = stream->getDecodedChunkSize(); const size_t decalage = stream->getDecodedDecalage(); const size_t width = (size - height) / decalage; uchar *pixels = new uchar[static_cast<size_t>(width * height / 2)](); FFTSamplePtr chunkData( reinterpret_cast<FFTSample *>(av_malloc_array(2 * height, sizeof(FFTSample))), fftSampleDeleter); RDFTContextPtr ctx(av_rdft_init((static_cast<int>(log2(static_cast<int>(height)))), DFT_R2C), rdftContextDeleter); if (!(chunkData && ctx)) { delete[] pixels; throw std::runtime_error("out of memory"); } /* Compute the image data */ for (size_t x = 0, i = 0; i < size - height; i += decalage, ++x) { parallel_for (size_t j = 0; j < height; j++) { const double curr_dat = decodedData[i + j]; const double window_modifier = (1 - cos(2 * M_PI * static_cast<double>(j) / static_cast<double>(height - 1))) / 2; const float value = std::clamp(static_cast<float>(window_modifier * curr_dat), -1.0f, 1.0f); chunkData[j] = value; } av_rdft_calc(ctx.get(), chunkData.get()); parallel_for (size_t j = 0; j < height / 2; j++) { const float im = chunkData[j * 2]; const float re = chunkData[j * 2 + 1]; const float mag = sqrtf(im * im + re * re); const size_t index = static_cast<size_t>(j * static_cast<ulong>(width) + x); pixels[index] = static_cast<unsigned char>((mag)*MAXPIXVALUE); } } QImage img = QImage(pixels, static_cast<int>(width), static_cast<int>(height / 2), static_cast<int>(width), QImage::Format_Grayscale8, pixelsDeleter, pixels) .mirrored(false, true); printSpectrum(img, stream->getDuration()); } void AudioVisualizer::printSpectrum(QImage pixmap, qreal audioLength) noexcept { TimingView *timer = new TimingView(pixmap, audioLength, this); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(timer); setLayout(layout); }