Convert into time-domain wave tables. One table is created for each range for non-aliasing playback at different playback rates. Thus, higher ranges have more high-frequency partials culled out.
| 135 | // One table is created for each range for non-aliasing playback at different playback rates. |
| 136 | // Thus, higher ranges have more high-frequency partials culled out. |
| 137 | void PeriodicWave::createBandLimitedTables(const float * realData, const float * imagData, int numberOfComponents) |
| 138 | { |
| 139 | float normalizationScale = 1.f; |
| 140 | |
| 141 | int fftSize = periodicWaveSize(); |
| 142 | int halfSize = fftSize / 2 + 1; |
| 143 | int i; |
| 144 | |
| 145 | numberOfComponents = std::min(numberOfComponents, halfSize); |
| 146 | |
| 147 | m_bandLimitedTables.reserve(numberOfRanges()); |
| 148 | |
| 149 | for (int rangeIndex = 0; rangeIndex < numberOfRanges(); ++rangeIndex) |
| 150 | { |
| 151 | |
| 152 | // This FFTFrame is used to cull partials (represented by frequency bins). |
| 153 | FFTFrame frame(fftSize); |
| 154 | float * realP = frame.realData(); |
| 155 | float * imagP = frame.imagData(); |
| 156 | |
| 157 | // Copy from loaded frequency data and generate the complex conjugate because of the way the |
| 158 | // inverse FFT is defined versus the values in the arrays. Note also that although the IFFT |
| 159 | // does a scaling by 1/N, we take care of this when the normalization scaling is done. |
| 160 | float minusOne = -1; |
| 161 | memcpy(realP, realData, numberOfComponents * sizeof(*realP)); |
| 162 | vsmul(imagData, 1, &minusOne, imagP, 1, numberOfComponents); |
| 163 | |
| 164 | // Find the starting bin where we should start culling. We need to clear out the highest |
| 165 | // frequencies to band-limit the waveform. |
| 166 | int numberOfPartials = numberOfPartialsForRange(rangeIndex); |
| 167 | |
| 168 | // If fewer components were provided than 1/2 FFT size, then clear the remaining bins. |
| 169 | // We also need to cull the aliasing partials for this pitch range. |
| 170 | for (i = std::min(numberOfComponents, numberOfPartials + 1); i < halfSize; ++i) |
| 171 | { |
| 172 | realP[i] = 0; |
| 173 | imagP[i] = 0; |
| 174 | } |
| 175 | |
| 176 | // Clear packed-nyquist and any DC-offset. |
| 177 | realP[0] = 0; |
| 178 | imagP[0] = 0; |
| 179 | |
| 180 | // Create the band-limited table. |
| 181 | m_bandLimitedTables.push_back(std::unique_ptr<lab::AudioFloatArray>(new lab::AudioFloatArray(periodicWaveSize()))); |
| 182 | |
| 183 | // Apply an inverse FFT to generate the time-domain table data. |
| 184 | float * data = m_bandLimitedTables[rangeIndex]->data(); |
| 185 | frame.computeInverseFFT(data); |
| 186 | |
| 187 | // For the first range (which has the highest power), calculate its peak value then compute normalization scale. |
| 188 | if (!rangeIndex) |
| 189 | { |
| 190 | float maxValue; |
| 191 | vmaxmgv(data, 1, &maxValue, fftSize); |
| 192 | |
| 193 | if (maxValue) |
| 194 | normalizationScale = 1.0f / maxValue; |