Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
waveformgenerator.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // PCM Waveform generator using FFTW3
22 //=============================================================================
23 
24 #include "waveformgenerator.h"
25 
26 #include <random>
27 #include "../../math/mathtools.h"
28 #include "../../system/log.h"
29 
30 //-----------------------------------------------------------------------------
31 // Constructor
32 //-----------------------------------------------------------------------------
33 
37 
39  mNumberOfKeys(),
40  mLibrary(),
41  mLibraryMutex(256),
42  mComputing(),
43  mIn(mWaveformSize/2+1),
44  mOut(mWaveformSize),
45  mFFT(),
46  mQueue()
47 {}
48 
49 
50 //-----------------------------------------------------------------------------
51 // Init
52 //-----------------------------------------------------------------------------
53 
65 
66 void WaveformGenerator::init (int numberOfKeys, int samplerate)
67 {
68  EptAssert(numberOfKeys > 0 and numberOfKeys < 256,
69  "Number of keys out of range");
70  EptAssert(samplerate>0 and samplerate<50000,
71  "Range of sample rate invalid");
72  mNumberOfKeys = numberOfKeys;
73  mLibrary.resize(mNumberOfKeys);
74  mSampleRate = samplerate;
76  mIn.resize(mWaveformSize/2+1);
77  mOut.resize(mWaveformSize);
78 
79  for (auto &wave : mLibrary)
80  {
81  wave.resize(mWaveformSize);
82  wave.assign(mWaveformSize,0);
83  }
84  mComputing.resize(mNumberOfKeys);
85  mComputing.assign(mNumberOfKeys,false);
86 }
87 
88 
89 //-----------------------------------------------------------------------------
90 // pre-calculate waveform
91 //-----------------------------------------------------------------------------
92 
104 
105 void WaveformGenerator::preCalculate(int keynumber, const Spectrum &spectrum)
106 {
107  if (spectrum.size()==0) return;
108  mQueueMutex.lock();
109  mQueue[keynumber] = spectrum;
110  mComputing[keynumber] = true;
111  mQueueMutex.unlock();
112 }
113 
114 
115 //-----------------------------------------------------------------------------
116 // get pre-calculated waveform
117 //-----------------------------------------------------------------------------
118 
124 
126 {
127  if (keynumber < 0 or keynumber >= mNumberOfKeys) return Waveform();
128  std::lock_guard<std::mutex> lock(mLibraryMutex[keynumber]);
129  return mLibrary[keynumber];
130 }
131 
132 
133 //-----------------------------------------------------------------------------
134 // PCM interpolation
135 //-----------------------------------------------------------------------------
136 
150 
151 float WaveformGenerator::getInterpolation (const Waveform &W, const double t)
152 {
153  float realindex = t*mWaveformSize/mWaveformTime;
154  int index = static_cast<int> (realindex);
155  float leftvalue = W[index%mWaveformSize];
156  return leftvalue + (realindex-index)*(W[(index+1)%mWaveformSize]-leftvalue);
157 }
158 
159 
160 //-----------------------------------------------------------------------------
161 // Check if computation is ready
162 //-----------------------------------------------------------------------------
163 
175 
176 bool WaveformGenerator::isComputing (const int keynumber)
177 {
178  std::lock_guard<std::mutex> lock(mQueueMutex);
179  return mComputing[keynumber];
180 }
181 
182 //-----------------------------------------------------------------------------
183 // Main thread function
184 //-----------------------------------------------------------------------------
185 
194 
196 {
197  setThreadName("Waveformer");
198  LogI("Waveform generator thread started");
199  Spectrum spectrum;
200  std::default_random_engine generator;
201  std::uniform_real_distribution<double> distribution(0.0,MathTools::PI*2);
202  int counter = 0;
203  while (not cancelThread())
204  {
205  int keynumber = -1;
206 
207  // Check the queue for new keys to be computed
208  mQueueMutex.lock();
209  if(not mQueue.empty())
210  {
211  auto element = mQueue.begin();
212  keynumber = element->first;
213  spectrum = element->second;
214  mQueue.erase(element);
215  }
216  mQueueMutex.unlock();
217 
218  // If so, compute the waveform
219  if (keynumber >= 0 and keynumber<=mNumberOfKeys and spectrum.size()>0)
220  {
221  if (counter == 0) LogI("Waveform generator starting in background")
222  counter ++;
223  double norm=0;
224  for (auto &partial : spectrum) norm += partial.second;
225  mIn.assign(mWaveformSize/2+1,0);
226  if (norm>0)
227  {
228  for (auto &partial : spectrum)
229  {
230  const double frequency = partial.first;
231  const double intensity = sqrt(partial.second / norm);
232  int k = MathTools::roundToInteger(frequency*mWaveformTime);
233  if (k>0 and k<mWaveformSize/2+1)
234  {
235  std::complex<double> phase(0,distribution(generator));
236  mIn[k] = exp(phase) * intensity;
237  }
238  }
240  mLibraryMutex[keynumber].lock();
241  for (int i=0; i<mWaveformSize; i++) mLibrary[keynumber][i]=mOut[i];
242  mLibraryMutex[keynumber].unlock();
243  }
244  }
245  else
246  {
247  if (counter > 0) LogI("Waveform generator stops after %d waveforms.",counter);
248  counter = 0;
249  msleep(20);
250  }
251 
252  // Finally register the job as being done
253  if (keynumber >= 0 and keynumber<=mNumberOfKeys)
254  {
255  mQueueMutex.lock();
256  mComputing[keynumber] = false;
257  mQueueMutex.unlock();
258  }
259  }
260 }
261 
262 
std::map< int, Spectrum > mQueue
void msleep(double milliseconds)
Sleep function for staying idle.
FFT_Implementation mFFT
int roundToInteger(T x)
Round a floating point number to an integer.
Definition: mathtools.h:43
FFTRealVector mOut
std::map< double, double > Spectrum
static void setThreadName(std::string s)
Specify the name of the thread.
const double PI
Definition: mathtools.h:37
std::vector< Waveform > mLibrary
void init(int numberOfKeys, int samplerate)
Initialization procedure of the waveform generator.
const double mWaveformTime
FFTComplexVector mIn
#define LogI(...)
Definition: log.h:50
std::vector< bool > mComputing
std::vector< float > Waveform
Waveform getWaveForm(const int keynumber)
Mutexed getter function to obtain the calculated waveform.
bool isComputing(const int keynumber)
Find out whether the WaveformGenerator is still computing.
#define EptAssert(a, b)
Definition: eptexception.h:47
float getInterpolation(const Waveform &W, const double t)
Get an a linearly interpolated value of the waveform.
void preCalculate(int keynumber, const Spectrum &spectrum)
pre-calculate a waveform
bool cancelThread() const
Cancel-flag getter method, thread-safe.
std::vector< std::mutex > mLibraryMutex
void workerFunction()
Main thread function for wavefrom generation.
WaveformGenerator()
Constructor, resetting the member variables.
void calculateFFT(const FFTRealVector &in, FFTComplexVector &out)
Foward FFT, mapping a real vector with size N to a complex one with size N/2+1.