SpectMorph
|
00001 /* 00002 * Copyright (C) 2010 Stefan Westerfeld 00003 * 00004 * This library is free software; you can redistribute it and/or modify it 00005 * under the terms of the GNU Lesser General Public License as published by the 00006 * Free Software Foundation; either version 3 of the License, or (at your 00007 * option) any later version. 00008 * 00009 * This library is distributed in the hope that it will be useful, but WITHOUT 00010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 00012 * for more details. 00013 * 00014 * You should have received a copy of the GNU Lesser General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 00019 #ifndef SPECTMORPH_IFFT_SYNTH_HH 00020 #define SPECTMORPH_IFFT_SYNTH_HH 00021 00022 #include <sys/types.h> 00023 #include <vector> 00024 00025 #include "smmath.hh" 00026 00027 namespace SpectMorph { 00028 00029 class IFFTSynthTable; 00030 00031 class IFFTSynth 00032 { 00033 IFFTSynthTable *table; 00034 00035 int zero_padding; 00036 size_t block_size; 00037 double mix_freq; 00038 double freq256_factor; 00039 double mag_norm; 00040 00041 float *fft_in; 00042 float *fft_out; 00043 float *win_scale; 00044 00045 enum { 00046 SIN_TABLE_SIZE = 4096, 00047 SIN_TABLE_MASK = 4095 00048 }; 00049 00050 static std::vector<float> sin_table; 00051 00052 public: 00053 enum WindowType { WIN_BLACKMAN_HARRIS_92, WIN_HANNING }; 00054 enum OutputMode { REPLACE, ADD }; 00055 00056 IFFTSynth (size_t block_size, double mix_freq, WindowType win_type); 00057 ~IFFTSynth(); 00058 00059 void 00060 clear_partials() 00061 { 00062 zero_float_block (block_size, fft_in); 00063 } 00064 00065 float* 00066 fft_buffer() 00067 { 00068 return fft_in; 00069 } 00070 00071 inline void render_partial (double freq, double mag, double phase); 00072 void get_samples (float *samples, OutputMode output_mode = REPLACE); 00073 00074 double quantized_freq (double freq); 00075 }; 00076 00077 struct IFFTSynthTable 00078 { 00079 std::vector<float> win_trans; 00080 00081 float *win_scale; 00082 }; 00083 00084 inline void 00085 IFFTSynth::render_partial (double mf_freq, double mag, double phase) 00086 { 00087 const int range = 4; 00088 00089 const int freq256 = sm_round_positive (mf_freq * freq256_factor); 00090 const int ibin = freq256 >> 8; 00091 float *sp = fft_in + 2 * (ibin - range); 00092 const float *wmag_p = &table->win_trans[(freq256 & 0xff) * (range * 2 + 1)]; 00093 00094 const float nmag = mag * mag_norm; 00095 00096 // rotation for initial phase; scaling for magnitude 00097 00098 /* the following block computes sincos (phase + phase_adjust) */ 00099 int iarg = sm_round_positive (phase * (SIN_TABLE_SIZE / (2 * M_PI))); 00100 00101 // adjust phase to get the same output like vector sin (smmath.hh) 00102 // phase_adjust = freq256 * (M_PI / 256.0) - M_PI / 2; 00103 int iphase_adjust = freq256 * SIN_TABLE_SIZE / 512 + (SIN_TABLE_SIZE - SIN_TABLE_SIZE / 4); 00104 iarg += iphase_adjust; 00105 00106 const float phase_rsmag = sin_table [iarg & SIN_TABLE_MASK] * nmag; 00107 iarg += SIN_TABLE_SIZE / 4; 00108 const float phase_rcmag = sin_table [iarg & SIN_TABLE_MASK] * nmag; 00109 00110 /* compute FFT spectrum modifications */ 00111 if (ibin > range && 2 * (ibin + range) < static_cast<int> (block_size)) 00112 { 00113 for (int i = 0; i <= 2 * range; i++) 00114 { 00115 const float wmag = wmag_p[i]; 00116 *sp++ += phase_rcmag * wmag; 00117 *sp++ += phase_rsmag * wmag; 00118 } 00119 } 00120 else 00121 { 00122 wmag_p += range; // allow negative addressing 00123 for (int i = -range; i <= range; i++) 00124 { 00125 const float wmag = wmag_p[i]; 00126 if ((ibin + i) < 0) 00127 { 00128 fft_in[-(ibin + i) * 2] += phase_rcmag * wmag; 00129 fft_in[-(ibin + i) * 2 + 1] -= phase_rsmag * wmag; 00130 } 00131 else if ((ibin + i) == 0) 00132 { 00133 fft_in[0] += 2 * phase_rcmag * wmag; 00134 } 00135 else if (2 * (ibin + i) == static_cast<int> (block_size)) 00136 { 00137 fft_in[1] += 2 * phase_rcmag * wmag; 00138 } 00139 else if (2 * (ibin + i) > static_cast<int> (block_size)) 00140 { 00141 int p = block_size - (2 * (ibin + i) - block_size); 00142 00143 fft_in[p] += phase_rcmag * wmag; 00144 fft_in[p + 1] -= phase_rsmag * wmag; 00145 } 00146 else // no corner case 00147 { 00148 fft_in[(ibin + i) * 2] += phase_rcmag * wmag; 00149 fft_in[(ibin + i) * 2 + 1] += phase_rsmag * wmag; 00150 } 00151 } 00152 } 00153 } 00154 00155 } 00156 00157 #endif