| 232 | } |
| 233 | |
| 234 | void ArcSpectrogram::run() { |
| 235 | // Initialize rainbow parameters |
| 236 | mParameters.ui.specImages[mParameters.ui.specType] = juce::Image(juce::Image::ARGB, mRainbowRect.getWidth(), mRainbowRect.getHeight(), true); |
| 237 | juce::Graphics g(mParameters.ui.specImages[mParameters.ui.specType]); |
| 238 | |
| 239 | // Audio waveform (1D) is handled a bit differently than its 2D spectrograms |
| 240 | if (mParameters.ui.specType == ParamUI::SpecType::WAVEFORM) { |
| 241 | juce::AudioBuffer<float>* audioBuffer = (juce::AudioBuffer<float>*)mBuffers[mParameters.ui.specType]; |
| 242 | const float* bufferSamples = audioBuffer->getReadPointer(0); |
| 243 | float maxMagnitude = audioBuffer->getMagnitude(0, audioBuffer->getNumSamples()); |
| 244 | |
| 245 | // Draw NUM_COLS worth of audio samples |
| 246 | juce::Point<float> prevPoint = mStartPoint.getPointOnCircumference(mStartRadius + mBowWidth / 2, mStartRadius + mBowWidth / 2, |
| 247 | -(juce::MathConstants<float>::pi / 2.0f)); |
| 248 | juce::Colour prevColour = juce::Colours::black; |
| 249 | for (auto i = 0; i < NUM_COLS; ++i) { |
| 250 | if (threadShouldExit()) { mIsProcessing = false; return; } |
| 251 | int sampleIdx = ((float)i / NUM_COLS) * audioBuffer->getNumSamples(); |
| 252 | float sampleRadius = |
| 253 | juce::jmap(bufferSamples[sampleIdx], -maxMagnitude, maxMagnitude, (float)mStartRadius, (float)mEndRadius); |
| 254 | |
| 255 | // Choose rainbow color depending on radius |
| 256 | auto rainbowColour = |
| 257 | juce::Colour::fromHSV(juce::jmap(bufferSamples[sampleIdx], -maxMagnitude, maxMagnitude, 0.0f, 1.0f), 1.0, 1.0f, 1.0f); |
| 258 | |
| 259 | // Draw a line connecting to the previous point, blending colours between them |
| 260 | float xPerc = ((float)i / NUM_COLS); |
| 261 | float angleRad = (juce::MathConstants<float>::pi * xPerc) - (juce::MathConstants<float>::pi / 2.0f); |
| 262 | juce::Point<float> p = mStartPoint.getPointOnCircumference(sampleRadius, sampleRadius, angleRad); |
| 263 | juce::ColourGradient gradient = juce::ColourGradient(prevColour, prevPoint, rainbowColour, p, false); |
| 264 | g.setGradientFill(gradient); |
| 265 | g.drawLine(juce::Line<float>(prevPoint, p), 2.0f); |
| 266 | prevPoint = p; |
| 267 | prevColour = rainbowColour; |
| 268 | } |
| 269 | } else { |
| 270 | // All other types of spectrograms |
| 271 | Utils::SpecBuffer& spec = *(Utils::SpecBuffer*)mBuffers[mParameters.ui.specType]; // cast to SpecBuffer |
| 272 | if (spec.empty() || threadShouldExit()) { mIsProcessing = false; return; } |
| 273 | |
| 274 | const float maxRow = |
| 275 | static_cast<float>((mParameters.ui.specType == ParamUI::SpecType::SPECTROGRAM) ? spec[0].size() / 8 : spec[0].size()); |
| 276 | |
| 277 | // Draw each column of frequencies |
| 278 | for (size_t i = 0; i < NUM_COLS; ++i) { |
| 279 | if (threadShouldExit()) return; |
| 280 | const float specCol = ((float)i / NUM_COLS) * spec.size(); |
| 281 | // Draw each row of frequencies |
| 282 | for (auto curRadius = mStartRadius; curRadius < mEndRadius; curRadius += 1) { |
| 283 | const float radPerc = (curRadius - mStartRadius) / (float)mBowWidth; |
| 284 | const float specRow = radPerc * maxRow; |
| 285 | |
| 286 | // Choose rainbow color depending on radius |
| 287 | const size_t colIndex = static_cast<size_t>(specCol); |
| 288 | const size_t rowIndex = static_cast<size_t>(specRow); |
| 289 | const float level = juce::jlimit(0.0f, 1.0f, spec[colIndex][rowIndex] * spec[colIndex][rowIndex] * COLOUR_MULTIPLIER); |
| 290 | auto rainbowColour = juce::Colour::fromHSV(radPerc, 1.0, 1.0f, level); |
| 291 | g.setColour(rainbowColour); |