hx3d  1
2D/3D Simple Game Framework
spectrum.cpp
1 /*
2  Spectrum display.
3  Copyright (C) 2015 Denis BOURGE
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18  USA
19 */
20 
21 #include "hx3d/audio/display/spectrum.hpp"
22 #include "hx3d/audio/fft.hpp"
23 #include "hx3d/audio/audio.hpp"
24 
25 #include "hx3d/core/core.hpp"
26 
27 #include "hx3d/utils/log.hpp"
28 
29 #include "hx3d/math/number_utils.hpp"
30 #include "hx3d/math/interpolation.hpp"
31 
32 namespace hx3d {
33 namespace audio {
34 
35 Spectrum::Spectrum(const unsigned int minFreq, const unsigned int maxFreq, const unsigned int barCount):
36  Spectrum(minFreq, maxFreq, barCount, 50)
37  {}
38 
39 Spectrum::Spectrum(const unsigned int minFreq, const unsigned int maxFreq, const unsigned int barCount, const int refreshDelay):
40  Display(refreshDelay),
41  _minFreq(minFreq),
42  _maxFreq(maxFreq),
43  _barCount(barCount)
44  {}
45 
46 Spectrum::~Spectrum() {}
47 
49  unsigned int resolutionX = _image.getWidth();
50 
51  _rawValues = new Complex[resolutionX];
52  for (unsigned int i = 0; i < resolutionX; ++i) {
53  _rawValues[i] = Complex(0, 0);
54  }
55 
56  _fftValues.resize(resolutionX / 2);
57  for (unsigned int i = 0; i < _fftValues.size(); ++i) {
58  _fftValues[i] = 0.f;
59  }
60 
61  _barValues.resize(_barCount);
62  for (unsigned int i = 0; i < _barValues.size(); ++i) {
63  _barValues[i] = 0.f;
64  }
65 
66  _normalizedBarValues.resize(_barCount);
67  for (unsigned int i = 0; i < _normalizedBarValues.size(); ++i) {
68  _normalizedBarValues[i] = 0.f;
69  }
70 
71  _barFrequencies.resize(_barCount);
72  for (unsigned int i = 0; i < _barFrequencies.size(); ++i) {
73  _barFrequencies[i] = 0;
74  }
75 }
76 
77 void Spectrum::update(const Sint16* stream, const int length, const float delta) {
78 
79  if (!_initialized || length == 0)
80  return;
81 
82  int w = _image.getWidth();
83  int h = _image.getHeight();
84  int step = length / w;
85 
86  if (_timer.hasEnded()) {
87 
88  /* FFT */
89 
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);
93  }
94 
95  std::valarray<Complex> fftArray(_rawValues, w);
96  audio::FFT::fft(fftArray);
97 
98  for (int i = 0; i < w/2; ++i) {
99  _fftValues[i] = std::log10(std::abs(fftArray[i])) * 20;
100  }
101 
102  float octaves = calculateOctave(_minFreq, _maxFreq, _barCount);
103  unsigned int currentFreq = _minFreq;
104  for (unsigned int i = 0; i < _barCount; ++i) {
105 
106  int low = lowerLimitSample(currentFreq, octaves, w/2);
107  int hi = upperLimitSample(currentFreq, octaves, w/2);
108 
109  if (currentFreq > Core::Audio()->getFrequencyRate() || hi > w/2)
110  break;
111 
112  // Log.Info("C: %d [L: %d / H: %d]", currentFreq, low, hi);
113 
114  _barFrequencies[i] = currentFreq;
115 
116  _barValues[i] = averageFreq(_fftValues, low, hi);
117  currentFreq = nextCenterFreq(currentFreq, octaves);
118  }
119 
120  /* END FFT */
121 
122  _image.setRect(0, 0, w, h, Color::Black);
123  drawBorders();
124 
125  int bar_size = w / (float)_barCount;
126  for (unsigned int i = 0; i < _barCount; ++i) {
127 
128  float scale_val = std::max(0.f, _barValues[i]);
129  float norm_val = scale_val / 30.f;
130 
131  _normalizedBarValues[i] = norm_val;
132 
133  Color color = math::interpolate(Color(255, 64, 0), Color::Red, norm_val, math::Interpolation::Linear);
134 
135  // Values
136  // Log.Info("Bar %d: %f", i, val);
137 
138  int y = h - norm_val * h;
139  _image.setRect(i*bar_size, y + 1, bar_size, h - y - 2, color);
140  }
141 
142  _image.updateTextureZone(0, 0, w, h);
143  _timer.reset();
144  }
145 
146  _timer.update(delta);
147 }
148 
149 float Spectrum::getNormalizedFrequencyAmplitude(const unsigned int frequency, const unsigned int range) {
150  unsigned int maxFrequency = _barFrequencies[_barCount - 1];
151 
152  if (frequency > maxFrequency) {
153  Log.Info("Spectrum: frequency `%d` off-limits (max: %d)", frequency, maxFrequency);
154  return 0.f;
155  }
156 
157  unsigned int lowFreq = frequency - range;
158  unsigned int hiFreq = frequency + range;
159  unsigned int lowBar = 0;
160  unsigned int hiBar = _barCount;
161 
162  for (unsigned int i = 0; i < _barCount; ++i) {
163  unsigned int currentFreq = _barFrequencies[i];
164  if (lowFreq >= currentFreq) {
165  lowBar = i;
166  }
167 
168  if (hiFreq >= currentFreq) {
169  hiBar = i;
170  }
171 
172  Log.Info("Freq[%d]: %d", i, currentFreq);
173  }
174 
175  Log.Info("Freq: %d range %d [Min: %d / Max: %d] [I: %d / %d]", frequency, range, lowFreq, hiFreq, lowBar, hiBar);
176 
177  return averageFreq(_normalizedBarValues, lowBar, hiBar);
178 }
179 
180 unsigned int Spectrum::getBarCount() {
181  return _barCount;
182 }
183 
185 
186 int Spectrum::nextCenterFreq(const int centerFreq, const float octaves) {
187  return centerFreq * std::pow(2, 1.f / octaves);
188 }
189 int Spectrum::prevCenterFreq(const int centerFreq, const float octaves) {
190  return centerFreq / std::pow(2, 1.f / octaves);
191 }
192 
193 int Spectrum::lowerLimit(const int centerFreq, const float octaves) {
194  return centerFreq / std::pow(2, 1.f / (2.f * octaves));
195 }
196 int Spectrum::upperLimit(const int centerFreq, const float octaves) {
197  return centerFreq * std::pow(2, 1.f / (2.f * octaves));
198 }
199 
200 int Spectrum::lowerLimitSample(const int centerFreq, const float octaves, const int samplesLength) {
201  return lowerLimit(centerFreq, octaves) / (Core::Audio()->getFrequencyRate() / samplesLength);
202 }
203 int Spectrum::upperLimitSample(const int centerFreq, const float octaves, const int samplesLength) {
204  return upperLimit(centerFreq, octaves) / (Core::Audio()->getFrequencyRate() / samplesLength);
205 }
206 
207 float Spectrum::averageFreq(const std::vector<float>& values, const int low, const int hi) {
208  if (low == hi)
209  return 0;
210 
211  float sum = 0;
212  int count = 1;
213 
214  for (int i = low; i < hi; ++i) {
215  const float val = values[i];
216  if (val != 0.f) {
217  sum += val;
218  ++count;
219  }
220  }
221 
222  return sum/count;
223 }
224 
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));
227 }
228 
229 } /* audio */
230 } /* hx3d */
void update(float delta)
Update the timer.
Definition: timer.cpp:76
void reset()
Reset the timer.
Definition: timer.cpp:41
Audio spectrum displaying.
Definition: spectrum.hpp:66
void drawBorders()
Draw white borders.
Definition: display.cpp:51
static void fft(std::valarray< Complex > &vector)
Cooley–Tukey FFT (in-place, divide-and-conquer)
Definition: fft.cpp:28
void setRect(unsigned int x, unsigned int y, unsigned int w, unsigned int h, Color color)
Set a rectangle with a color.
Definition: image.cpp:52
Four [0..255] components defined color.
Definition: color.hpp:32
Audio effect display.
Definition: display.hpp:41
unsigned int getHeight()
Get the image height.
Definition: image.cpp:112
bool hasEnded()
Test if the timer has ended.
Definition: timer.cpp:58
T interpolate(T a, T b, float t, Interpolation type)
Interpolate between two values.
hx3d framework namespace
Definition: audio.hpp:26
void updateTextureZone(unsigned int x, unsigned int y, unsigned int w, unsigned int h)
Update an existing texture zone.
Definition: image.cpp:85
virtual void update(const Sint16 *stream, const int length, const float delta) override
Update the display.
Definition: spectrum.cpp:77
Timer _timer
Refresh timer.
Definition: display.hpp:99
unsigned int getWidth()
Get the image width.
Definition: image.cpp:108
static hx3d::LogImpl Log
Current log implementation.
Definition: log.hpp:91
Spectrum(const unsigned int minFreq, const unsigned int maxFreq, const unsigned int barCount)
Create an empty spectrum with a refresh delay of 50.
Definition: spectrum.cpp:35
graphics::Image _image
Drawing image.
Definition: display.hpp:97
float getNormalizedFrequencyAmplitude(const unsigned int frequency, const unsigned int range)
Get the average normalized amplitude for a center frequency and a range.
Definition: spectrum.cpp:149
virtual void onInitialization() override
Use this to execute code after initialization.
Definition: spectrum.cpp:48
unsigned int getFrequencyRate()
Get the audio device frequency rate.
Definition: audio.cpp:90
bool _initialized
Is the display initialized ?
Definition: display.hpp:103
float log2(int a)
Calculate the log2.
unsigned int getBarCount()
Get the bar count.
Definition: spectrum.cpp:180
static audio::AudioDevice * Audio()
Get the audio device instance.
Definition: core.cpp:98
void Info(const std::string fmt,...)
Write an info message.
Definition: log.cpp:58