Files
inspectrum/inputsource.cpp
Mike Walters c2018ba7ec Don't force height to be a multiple of FFT stride
This was the wrong fix for an earlier bug that ended up sticking around
Fixes #19
2015-07-23 18:05:38 +01:00

118 lines
3.1 KiB
C++

#include "inputsource.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
static const double Tau = M_PI * 2.0;
InputSource::InputSource(const char *filename, int fft_size) {
m_file = fopen(filename, "rb");
if (m_file == nullptr)
throw std::runtime_error("Error opening file");
struct stat sb;
if (fstat(fileno(m_file), &sb) != 0)
throw std::runtime_error("Error fstating file");
m_file_size = sb.st_size;
m_data = (fftwf_complex*)mmap(NULL, m_file_size, PROT_READ, MAP_SHARED, fileno(m_file), 0);
if (m_data == 0)
throw std::runtime_error("Error mmapping file");
setFFTSize(fft_size);
}
InputSource::~InputSource() {
cleanupFFTW();
munmap(m_data, m_file_size);
fclose(m_file);
}
void InputSource::cleanupFFTW() {
if (m_fftw_plan != nullptr) fftwf_destroy_plan(m_fftw_plan);
if (m_fftw_in != nullptr) fftwf_free(m_fftw_in);
if (m_fftw_out != nullptr) fftwf_free(m_fftw_out);
}
void InputSource::getViewport(float *dest, int x, int y, int width, int height, int zoom) {
fftwf_complex *sample_ptr = &m_data[y * getFFTStride()];
for (int i = 0; i < height; i++) {
// Abort if sampling more data than is actually available
if (sample_ptr > m_data + (m_file_size/sizeof(fftwf_complex)))
break;
memcpy(m_fftw_in, sample_ptr, m_fft_size * sizeof(fftwf_complex));
// Apply window
for (int j = 0; j < m_fft_size; j++) {
m_fftw_in[j][0] *= m_window[j];
m_fftw_in[j][1] *= m_window[j];
}
fftwf_execute(m_fftw_plan);
for (int j = x; j < width; j++) {
int k = (j + m_fft_size / 2) % m_fft_size;
float re = m_fftw_out[k][0];
float im = m_fftw_out[k][1];
float mag = sqrt(re * re + im * im) / m_fft_size;
float magdb = 10 * log2(mag) / log2(10);
*dest = magdb;
dest++;
}
sample_ptr += getFFTStride();
}
}
int InputSource::getHeight() {
return m_file_size / sizeof(fftwf_complex) / getFFTStride();
}
int InputSource::getWidth() {
return m_fft_size;
}
void InputSource::setFFTSize(int size) {
cleanupFFTW();
m_fft_size = size;
m_fftw_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * m_fft_size);
m_fftw_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * m_fft_size);
m_fftw_plan = fftwf_plan_dft_1d(m_fft_size, m_fftw_in, m_fftw_out, FFTW_FORWARD, FFTW_MEASURE);
m_window.reset(new float[m_fft_size]);
for (int i = 0; i < m_fft_size; i++) {
m_window[i] = 0.5f * (1.0f - cos(Tau * i / (m_fft_size - 1)));
}
m_zoom = 0;
m_max_zoom = floor(log2(m_fft_size));
}
bool InputSource::zoomIn() {
m_zoom++;
if (m_zoom > m_max_zoom) {
m_zoom = m_max_zoom;
return false;
}
return true;
}
bool InputSource::zoomOut() {
m_zoom--;
if (m_zoom < 0) {
m_zoom = 0;
return false;
}
return true;
}
int InputSource::getFFTStride() {
return m_fft_size / pow(2, m_zoom);
}