21 #include "hx3d/audio/display/spectrum.hpp" 22 #include "hx3d/audio/fft.hpp" 23 #include "hx3d/audio/audio.hpp" 25 #include "hx3d/core/core.hpp" 27 #include "hx3d/utils/log.hpp" 29 #include "hx3d/math/number_utils.hpp" 30 #include "hx3d/math/interpolation.hpp" 35 Spectrum::Spectrum(
const unsigned int minFreq,
const unsigned int maxFreq,
const unsigned int barCount):
36 Spectrum(minFreq, maxFreq, barCount, 50)
39 Spectrum::Spectrum(
const unsigned int minFreq,
const unsigned int maxFreq,
const unsigned int barCount,
const int refreshDelay):
46 Spectrum::~Spectrum() {}
51 _rawValues =
new Complex[resolutionX];
52 for (
unsigned int i = 0; i < resolutionX; ++i) {
53 _rawValues[i] = Complex(0, 0);
56 _fftValues.resize(resolutionX / 2);
57 for (
unsigned int i = 0; i < _fftValues.size(); ++i) {
61 _barValues.resize(_barCount);
62 for (
unsigned int i = 0; i < _barValues.size(); ++i) {
66 _normalizedBarValues.resize(_barCount);
67 for (
unsigned int i = 0; i < _normalizedBarValues.size(); ++i) {
68 _normalizedBarValues[i] = 0.f;
71 _barFrequencies.resize(_barCount);
72 for (
unsigned int i = 0; i < _barFrequencies.size(); ++i) {
73 _barFrequencies[i] = 0;
84 int step = length / w;
90 for (
int i = 0; i < w; ++i) {
91 float norm_amp = stream[i * step] * (1.f/32767.f);
92 _rawValues[i] = Complex(norm_amp, 0);
95 std::valarray<Complex> fftArray(_rawValues, w);
98 for (
int i = 0; i < w/2; ++i) {
99 _fftValues[i] = std::log10(std::abs(fftArray[i])) * 20;
102 float octaves = calculateOctave(_minFreq, _maxFreq, _barCount);
103 unsigned int currentFreq = _minFreq;
104 for (
unsigned int i = 0; i < _barCount; ++i) {
106 int low = lowerLimitSample(currentFreq, octaves, w/2);
107 int hi = upperLimitSample(currentFreq, octaves, w/2);
109 if (currentFreq >
Core::Audio()->getFrequencyRate() || hi > w/2)
114 _barFrequencies[i] = currentFreq;
116 _barValues[i] = averageFreq(_fftValues, low, hi);
117 currentFreq = nextCenterFreq(currentFreq, octaves);
125 int bar_size = w / (float)_barCount;
126 for (
unsigned int i = 0; i < _barCount; ++i) {
128 float scale_val = std::max(0.f, _barValues[i]);
129 float norm_val = scale_val / 30.f;
131 _normalizedBarValues[i] = norm_val;
138 int y = h - norm_val * h;
139 _image.
setRect(i*bar_size, y + 1, bar_size, h - y - 2, color);
150 unsigned int maxFrequency = _barFrequencies[_barCount - 1];
152 if (frequency > maxFrequency) {
153 Log.
Info(
"Spectrum: frequency `%d` off-limits (max: %d)", frequency, maxFrequency);
157 unsigned int lowFreq = frequency - range;
158 unsigned int hiFreq = frequency + range;
159 unsigned int lowBar = 0;
160 unsigned int hiBar = _barCount;
162 for (
unsigned int i = 0; i < _barCount; ++i) {
163 unsigned int currentFreq = _barFrequencies[i];
164 if (lowFreq >= currentFreq) {
168 if (hiFreq >= currentFreq) {
172 Log.
Info(
"Freq[%d]: %d", i, currentFreq);
175 Log.
Info(
"Freq: %d range %d [Min: %d / Max: %d] [I: %d / %d]", frequency, range, lowFreq, hiFreq, lowBar, hiBar);
177 return averageFreq(_normalizedBarValues, lowBar, hiBar);
186 int Spectrum::nextCenterFreq(
const int centerFreq,
const float octaves) {
187 return centerFreq * std::pow(2, 1.f / octaves);
189 int Spectrum::prevCenterFreq(
const int centerFreq,
const float octaves) {
190 return centerFreq / std::pow(2, 1.f / octaves);
193 int Spectrum::lowerLimit(
const int centerFreq,
const float octaves) {
194 return centerFreq / std::pow(2, 1.f / (2.f * octaves));
196 int Spectrum::upperLimit(
const int centerFreq,
const float octaves) {
197 return centerFreq * std::pow(2, 1.f / (2.f * octaves));
200 int Spectrum::lowerLimitSample(
const int centerFreq,
const float octaves,
const int samplesLength) {
203 int Spectrum::upperLimitSample(
const int centerFreq,
const float octaves,
const int samplesLength) {
207 float Spectrum::averageFreq(
const std::vector<float>& values,
const int low,
const int hi) {
214 for (
int i = low; i < hi; ++i) {
215 const float val = values[i];
225 float Spectrum::calculateOctave(
const unsigned int lowFreq,
const unsigned int hiFreq,
const unsigned int bars) {
226 return (bars - 1) / (
math::log2(hiFreq / (
float)lowFreq));
void update(float delta)
Update the timer.
void reset()
Reset the timer.
Audio spectrum displaying.
void drawBorders()
Draw white borders.
static void fft(std::valarray< Complex > &vector)
Cooley–Tukey FFT (in-place, divide-and-conquer)
void setRect(unsigned int x, unsigned int y, unsigned int w, unsigned int h, Color color)
Set a rectangle with a color.
Four [0..255] components defined color.
unsigned int getHeight()
Get the image height.
bool hasEnded()
Test if the timer has ended.
T interpolate(T a, T b, float t, Interpolation type)
Interpolate between two values.
void updateTextureZone(unsigned int x, unsigned int y, unsigned int w, unsigned int h)
Update an existing texture zone.
virtual void update(const Sint16 *stream, const int length, const float delta) override
Update the display.
Timer _timer
Refresh timer.
unsigned int getWidth()
Get the image width.
static hx3d::LogImpl Log
Current log implementation.
Spectrum(const unsigned int minFreq, const unsigned int maxFreq, const unsigned int barCount)
Create an empty spectrum with a refresh delay of 50.
graphics::Image _image
Drawing image.
float getNormalizedFrequencyAmplitude(const unsigned int frequency, const unsigned int range)
Get the average normalized amplitude for a center frequency and a range.
virtual void onInitialization() override
Use this to execute code after initialization.
unsigned int getFrequencyRate()
Get the audio device frequency rate.
bool _initialized
Is the display initialized ?
float log2(int a)
Calculate the log2.
unsigned int getBarCount()
Get the bar count.
static audio::AudioDevice * Audio()
Get the audio device instance.
void Info(const std::string fmt,...)
Write an info message.