1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2 
3 #include "smifftsynth.hh"
4 #include "smmath.hh"
5 #include "smfft.hh"
6 #include "smblockutils.hh"
7 #include <assert.h>
8 #include <stdio.h>
9 
10 #include <map>
11 
12 using namespace SpectMorph;
13 
from_hex_nibble(char c)14 using std::vector;
15 using std::map;
16 
17 static map<size_t, IFFTSynthTable *> table_for_block_size;
18 
19 namespace SpectMorph {
20   vector<float> IFFTSynth::sin_table;
21 }
22 
23 IFFTSynth::IFFTSynth (size_t block_size, double mix_freq, WindowType win_type) :
24   block_size (block_size),
25   mix_freq (mix_freq)
decode(const string & str,vector<unsigned char> & out)26 {
27   zero_padding = 256;
28 
29   table = table_for_block_size[block_size];
30   if (!table)
31     {
32       const int range = 4;
33 
34       table = new IFFTSynthTable();
35 
36       const size_t win_size = block_size * zero_padding;
37       float *win = FFT::new_array_float (win_size);
38       float *wspectrum = FFT::new_array_float (win_size);
39 
40       std::fill (win, win + win_size, 0);  // most of it should be zero due to zeropadding
41       for (size_t i = 0; i < block_size; i++)
42         {
43           if (i < block_size / 2)
44             win[i] = window_blackman_harris_92 (double (block_size / 2 - i) / block_size * 2 - 1.0);
45           else
46             win[win_size - block_size + i] = window_blackman_harris_92 (double (i - block_size / 2) / block_size * 2 - 1.0);
47         }
48 
49       FFT::fftar_float (block_size * zero_padding, win, wspectrum, FFT::PLAN_ESTIMATE);
50 
51       // compute complete (symmetric) expanded window transform for all frequency fractions
52       for (int freq_frac = 0; freq_frac < zero_padding; freq_frac++)
53         {
54           for (int i = -range; i <= range; i++)
55             {
56               int pos = i * 256 - freq_frac;
57               table->win_trans.push_back (wspectrum[abs (pos * 2)]);
58             }
59         }
60       FFT::free_array_float (win);
61       FFT::free_array_float (wspectrum);
62 
63       table->win_scale = FFT::new_array_float (block_size); // SSE
64       for (size_t i = 0; i < block_size; i++)
65         table->win_scale[(i + block_size / 2) % block_size] = window_cos (2.0 * i / block_size - 1.0) / window_blackman_harris_92 (2.0 * i / block_size - 1.0);
66 
67       // we only need to do this once per block size (FIXME: not thread safe yet)
68       table_for_block_size[block_size] = table;
69     }
70   if (sin_table.empty())
71     {
72       // sin() table
73       sin_table.resize (SIN_TABLE_SIZE);
74       for (size_t i = 0; i < SIN_TABLE_SIZE; i++)
75         sin_table[i] = sin (i * 2 * M_PI / SIN_TABLE_SIZE);
76     }
77 
78   if (win_type == WIN_BLACKMAN_HARRIS_92)
79     win_scale = NULL;
80   else
81     win_scale = table->win_scale;
82 
83   fft_in = FFT::new_array_float (block_size);
84   fft_out = FFT::new_array_float (block_size);
85   freq256_factor = 1 / mix_freq * block_size * zero_padding;
86   mag_norm = 0.5 / block_size;
87 }
88 
89 IFFTSynth::~IFFTSynth()
90 {
91   FFT::free_array_float (fft_in);
92   FFT::free_array_float (fft_out);
93 }
94 
95 void
96 IFFTSynth::get_samples (float      *samples,
97                         OutputMode  output_mode)
98 {
99   FFT::fftsr_destructive_float (block_size, fft_in, fft_out);
100 
101   if (win_scale)
102     Block::mul (block_size, fft_out, win_scale);
103 
104   if (output_mode == REPLACE)
105     {
106       memcpy (samples, &fft_out[block_size / 2], sizeof (float) * block_size / 2);
107       memcpy (&samples[block_size / 2], fft_out, sizeof (float) * block_size / 2);
108     }
109   else if (output_mode == ADD)
110     {
111       Block::add (block_size / 2, samples, fft_out + block_size / 2);
112       Block::add (block_size / 2, samples + block_size / 2, fft_out);
113     }
114   else
115     {
116       assert (false);
117     }
118 }
119 
120 double
121 IFFTSynth::quantized_freq (double mf_freq)
122 {
123   const int freq256 = sm_round_positive (mf_freq * freq256_factor);
124   const double qfreq = freq256 * (1 / 256.0);
125   const double mf_qfreq = qfreq / block_size * mix_freq;
126 
127   return mf_qfreq;
128 }
129