Fixes to the UI

This commit is contained in:
Tim
2025-10-26 00:49:50 +01:00
parent 0785f6fedd
commit c5105693a2
76 changed files with 2280 additions and 32 deletions

View File

@@ -0,0 +1,52 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
This is the header file that your files should include in order to get all the
JUCE library headers. You should avoid including the JUCE headers directly in
your own source files, because that wouldn't pick up the correct configuration
options for your app.
*/
#pragma once
#include <juce_audio_basics/juce_audio_basics.h>
#include <juce_audio_devices/juce_audio_devices.h>
#include <juce_audio_formats/juce_audio_formats.h>
#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_utils/juce_audio_utils.h>
#include <juce_core/juce_core.h>
#include <juce_data_structures/juce_data_structures.h>
#include <juce_events/juce_events.h>
#include <juce_graphics/juce_graphics.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_gui_extra/juce_gui_extra.h>
#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION
/** If you've hit this error then the version of the Projucer that was used to generate this project is
older than the version of the JUCE modules being included. To fix this error, re-save your project
using the latest version of the Projucer or, if you aren't using the Projucer to manage your project,
remove the JUCE_PROJUCER_VERSION define.
*/
#error "This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error."
#endif
#if ! DONT_SET_USING_JUCE_NAMESPACE
// If your code uses a lot of JUCE classes, then this will obviously save you
// a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE.
using namespace juce;
#endif
#if ! JUCE_DONT_DECLARE_PROJECTINFO
namespace ProjectInfo
{
const char* const projectName = "WavetableSynthTutorial";
const char* const companyName = "JUCE";
const char* const versionString = "1.0.0";
const int versionNumber = 0x10000;
}
#endif

View File

@@ -0,0 +1,12 @@
Important Note!!
================
The purpose of this folder is to contain files that are auto-generated by the Projucer,
and ALL files in this folder will be mercilessly DELETED and completely re-written whenever
the Projucer saves your project.
Therefore, it's a bad idea to make any manual changes to the files in here, or to
put any of your own files in here if you don't want to lose them. (Of course you may choose
to add the folder's contents to your version-control system so that you can re-merge your own
modifications after the Projucer has saved its changes).

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_basics/juce_audio_basics.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_basics/juce_audio_basics.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_devices/juce_audio_devices.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_devices/juce_audio_devices.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_formats/juce_audio_formats.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_formats/juce_audio_formats.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_processors/juce_audio_processors.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_processors/juce_audio_processors.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_processors/juce_audio_processors_ara.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_processors/juce_audio_processors_lv2_libs.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_utils/juce_audio_utils.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_audio_utils/juce_audio_utils.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_core/juce_core.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_core/juce_core.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_core/juce_core_CompilationTime.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_data_structures/juce_data_structures.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_data_structures/juce_data_structures.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_events/juce_events.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_events/juce_events.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_graphics/juce_graphics.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_graphics/juce_graphics.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_graphics/juce_graphics_Harfbuzz.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_graphics/juce_graphics_Sheenbidi.c>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_gui_basics/juce_gui_basics.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_gui_basics/juce_gui_basics.mm>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_gui_extra/juce_gui_extra.cpp>

View File

@@ -0,0 +1,8 @@
/*
IMPORTANT! This file is auto-generated each time you save your
project - if you alter its contents, your changes may be overwritten!
*/
#include <juce_gui_extra/juce_gui_extra.mm>

View File

@@ -0,0 +1,96 @@
/*
==============================================================================
This file contains the startup code for a PIP.
==============================================================================
*/
#include <JuceHeader.h>
#include "WavetableSynthTutorial_01.h"
class Application : public juce::JUCEApplication
{
public:
//==============================================================================
Application() = default;
const juce::String getApplicationName() override { return "WavetableSynthTutorial"; }
const juce::String getApplicationVersion() override { return "1.0.0"; }
void initialise (const juce::String&) override
{
mainWindow.reset (new MainWindow ("WavetableSynthTutorial", std::make_unique<MainContentComponent>(), *this));
}
void shutdown() override { mainWindow = nullptr; }
private:
class MainWindow : public juce::DocumentWindow
{
public:
MainWindow (const juce::String& name, std::unique_ptr<juce::Component> c, JUCEApplication& a)
: DocumentWindow (name, juce::Desktop::getInstance().getDefaultLookAndFeel()
.findColour (ResizableWindow::backgroundColourId),
juce::DocumentWindow::allButtons),
app (a)
{
setUsingNativeTitleBar (true);
#if JUCE_ANDROID || JUCE_IOS
setContentOwned (new SafeAreaComponent { std::move (c) }, true);
setFullScreen (true);
#else
setContentOwned (c.release(), true);
setResizable (true, false);
setResizeLimits (300, 250, 10000, 10000);
centreWithSize (getWidth(), getHeight());
#endif
setVisible (true);
}
void closeButtonPressed() override
{
app.systemRequestedQuit();
}
#if JUCE_ANDROID || JUCE_IOS
class SafeAreaComponent : public juce::Component
{
public:
explicit SafeAreaComponent (std::unique_ptr<Component> c)
: content (std::move (c))
{
addAndMakeVisible (*content);
}
void resized() override
{
if (const auto* d = Desktop::getInstance().getDisplays().getDisplayForRect (getLocalBounds()))
content->setBounds (d->safeAreaInsets.subtractedFrom (getLocalBounds()));
}
private:
std::unique_ptr<Component> content;
};
void parentSizeChanged() override
{
if (auto* c = getContentComponent())
c->resized();
}
#endif
private:
JUCEApplication& app;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
};
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
START_JUCE_APPLICATION (Application)

View File

@@ -0,0 +1,163 @@
/*
==============================================================================
This file is part of the JUCE tutorials.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WavetableSynthTutorial
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Wavetable synthesiser.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
type: Component
mainClass: MainContentComponent
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class SineOscillator
{
public:
SineOscillator() {}
void setFrequency (float frequency, float sampleRate)
{
auto cyclesPerSample = frequency / sampleRate;
angleDelta = cyclesPerSample * juce::MathConstants<float>::twoPi;
}
forcedinline void updateAngle() noexcept
{
currentAngle += angleDelta;
if (currentAngle >= juce::MathConstants<float>::twoPi)
currentAngle -= juce::MathConstants<float>::twoPi;
}
forcedinline float getNextSample() noexcept
{
auto currentSample = std::sin (currentAngle);
updateAngle();
return currentSample;
}
private:
float currentAngle = 0.0f, angleDelta = 0.0f;
};
//==============================================================================
class MainContentComponent : public juce::AudioAppComponent,
public juce::Timer
{
public:
MainContentComponent()
{
cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification);
cpuUsageText.setJustificationType (juce::Justification::right);
addAndMakeVisible (cpuUsageLabel);
addAndMakeVisible (cpuUsageText);
setSize (400, 200);
setAudioChannels (0, 2); // no inputs, two outputs
startTimer (50);
}
~MainContentComponent() override
{
shutdownAudio();
}
void resized() override
{
cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20);
cpuUsageText .setBounds (10, 10, getWidth() - 20, 20);
}
void timerCallback() override
{
auto cpu = deviceManager.getCpuUsage() * 100;
cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification);
}
void prepareToPlay (int, double sampleRate) override
{
auto numberOfOscillators = 200; // [1]
for (auto i = 0; i < numberOfOscillators; ++i)
{
auto* oscillator = new SineOscillator(); // [2]
auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0; // [3]
auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0); // [4]
oscillator->setFrequency ((float) frequency, (float) sampleRate); // [5]
oscillators.add (oscillator);
}
level = 0.25f / (float) numberOfOscillators; // [6]
}
void releaseResources() override {}
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); // [7]
auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);
bufferToFill.clearActiveBufferRegion();
for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex)
{
auto* oscillator = oscillators.getUnchecked (oscillatorIndex); // [8]
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
{
auto levelSample = oscillator->getNextSample() * level; // [9]
leftBuffer[sample] += levelSample; // [10]
rightBuffer[sample] += levelSample;
}
}
}
private:
juce::Label cpuUsageLabel;
juce::Label cpuUsageText;
float level = 0.0f;
juce::OwnedArray<SineOscillator> oscillators;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

View File

@@ -0,0 +1,196 @@
/*
==============================================================================
This file is part of the JUCE tutorials.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WavetableSynthTutorial
version: 2.0.0
vendor: JUCE
website: http://juce.com
description: Wavetable synthesiser.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
type: Component
mainClass: MainContentComponent
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class WavetableOscillator
{
public:
WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse)
: wavetable (wavetableToUse)
{
jassert (wavetable.getNumChannels() == 1);
}
void setFrequency (float frequency, float sampleRate)
{
auto tableSizeOverSampleRate = (float) wavetable.getNumSamples() / sampleRate;
tableDelta = frequency * tableSizeOverSampleRate;
}
forcedinline float getNextSample() noexcept
{
auto tableSize = (unsigned int) wavetable.getNumSamples();
auto index0 = (unsigned int) currentIndex; // [6]
auto index1 = index0 == (tableSize - 1) ? (unsigned int) 0 : index0 + 1;
auto frac = currentIndex - (float) index0; // [7]
auto* table = wavetable.getReadPointer (0); // [8]
auto value0 = table[index0];
auto value1 = table[index1];
auto currentSample = value0 + frac * (value1 - value0); // [9]
if ((currentIndex += tableDelta) > (float) tableSize) // [10]
currentIndex -= (float) tableSize;
return currentSample;
}
private:
const juce::AudioSampleBuffer& wavetable;
float currentIndex = 0.0f, tableDelta = 0.0f;
};
//==============================================================================
class MainContentComponent : public juce::AudioAppComponent,
public juce::Timer
{
public:
MainContentComponent()
{
cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification);
cpuUsageText.setJustificationType (juce::Justification::right);
addAndMakeVisible (cpuUsageLabel);
addAndMakeVisible (cpuUsageText);
createWavetable();
setSize (400, 200);
setAudioChannels (0, 2); // no inputs, two outputs
startTimer (50);
}
~MainContentComponent() override
{
shutdownAudio();
}
void resized() override
{
cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20);
cpuUsageText .setBounds (10, 10, getWidth() - 20, 20);
}
void timerCallback() override
{
auto cpu = deviceManager.getCpuUsage() * 100;
cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification);
}
void createWavetable()
{
sineTable.setSize (1, (int) tableSize);
auto* samples = sineTable.getWritePointer (0); // [3]
auto angleDelta = juce::MathConstants<double>::twoPi / (double) (tableSize - 1); // [4]
auto currentAngle = 0.0;
for (unsigned int i = 0; i < tableSize; ++i)
{
auto sample = std::sin (currentAngle); // [5]
samples[i] = (float) sample;
currentAngle += angleDelta;
}
}
void prepareToPlay (int, double sampleRate) override
{
auto numberOfOscillators = 200;
for (auto i = 0; i < numberOfOscillators; ++i)
{
auto* oscillator = new WavetableOscillator (sineTable);
auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0;
auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0);
oscillator->setFrequency ((float) frequency, (float) sampleRate);
oscillators.add (oscillator);
}
level = 0.25f / (float) numberOfOscillators;
}
void releaseResources() override {}
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);
bufferToFill.clearActiveBufferRegion();
for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex)
{
auto* oscillator = oscillators.getUnchecked (oscillatorIndex);
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
{
auto levelSample = oscillator->getNextSample() * level;
leftBuffer[sample] += levelSample;
rightBuffer[sample] += levelSample;
}
}
}
private:
juce::Label cpuUsageLabel;
juce::Label cpuUsageText;
const unsigned int tableSize = 1 << 7; // [2]
float level = 0.0f;
juce::AudioSampleBuffer sineTable; // [1]
juce::OwnedArray<WavetableOscillator> oscillators;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

View File

@@ -0,0 +1,198 @@
/*
==============================================================================
This file is part of the JUCE tutorials.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WavetableSynthTutorial
version: 3.0.0
vendor: JUCE
website: http://juce.com
description: Wavetable synthesiser.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
type: Component
mainClass: MainContentComponent
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class WavetableOscillator
{
public:
WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse)
: wavetable (wavetableToUse),
tableSize (wavetable.getNumSamples() - 1)
{
jassert (wavetable.getNumChannels() == 1);
}
void setFrequency (float frequency, float sampleRate)
{
auto tableSizeOverSampleRate = (float) tableSize / sampleRate;
tableDelta = frequency * tableSizeOverSampleRate;
}
forcedinline float getNextSample() noexcept
{
auto index0 = (unsigned int) currentIndex;
auto index1 = index0 + 1;
auto frac = currentIndex - (float) index0;
auto* table = wavetable.getReadPointer (0);
auto value0 = table[index0];
auto value1 = table[index1];
auto currentSample = value0 + frac * (value1 - value0);
if ((currentIndex += tableDelta) > (float) tableSize)
currentIndex -= (float) tableSize;
return currentSample;
}
private:
const juce::AudioSampleBuffer& wavetable;
const int tableSize;
float currentIndex = 0.0f, tableDelta = 0.0f;
};
//==============================================================================
class MainContentComponent : public juce::AudioAppComponent,
public juce::Timer
{
public:
MainContentComponent()
{
cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification);
cpuUsageText.setJustificationType (juce::Justification::right);
addAndMakeVisible (cpuUsageLabel);
addAndMakeVisible (cpuUsageText);
createWavetable();
setSize (400, 200);
setAudioChannels (0, 2); // no inputs, two outputs
startTimer (50);
}
~MainContentComponent() override
{
shutdownAudio();
}
void resized() override
{
cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20);
cpuUsageText .setBounds (10, 10, getWidth() - 20, 20);
}
void timerCallback() override
{
auto cpu = deviceManager.getCpuUsage() * 100;
cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification);
}
void createWavetable()
{
sineTable.setSize (1, (int) tableSize + 1);
auto* samples = sineTable.getWritePointer (0);
auto angleDelta = juce::MathConstants<double>::twoPi / (double) (tableSize - 1);
auto currentAngle = 0.0;
for (unsigned int i = 0; i < tableSize; ++i)
{
auto sample = std::sin (currentAngle);
samples[i] = (float) sample;
currentAngle += angleDelta;
}
samples[tableSize] = samples[0];
}
void prepareToPlay (int, double sampleRate) override
{
auto numberOfOscillators = 200;
for (auto i = 0; i < numberOfOscillators; ++i)
{
auto* oscillator = new WavetableOscillator (sineTable);
auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0;
auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0);
oscillator->setFrequency ((float) frequency, (float) sampleRate);
oscillators.add (oscillator);
}
level = 0.25f / (float) numberOfOscillators;
}
void releaseResources() override {}
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);
bufferToFill.clearActiveBufferRegion();
for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex)
{
auto* oscillator = oscillators.getUnchecked (oscillatorIndex);
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
{
auto levelSample = oscillator->getNextSample() * level;
leftBuffer[sample] += levelSample;
rightBuffer[sample] += levelSample;
}
}
}
private:
juce::Label cpuUsageLabel;
juce::Label cpuUsageText;
const unsigned int tableSize = 1 << 7;
float level = 0.0f;
juce::AudioSampleBuffer sineTable;
juce::OwnedArray<WavetableOscillator> oscillators;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

View File

@@ -0,0 +1,208 @@
/*
==============================================================================
This file is part of the JUCE tutorials.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WavetableSynthTutorial
version: 4.0.0
vendor: JUCE
website: http://juce.com
description: Wavetable synthesiser.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
type: Component
mainClass: MainContentComponent
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class WavetableOscillator
{
public:
WavetableOscillator (const juce::AudioSampleBuffer& wavetableToUse)
: wavetable (wavetableToUse),
tableSize (wavetable.getNumSamples() - 1)
{
jassert (wavetable.getNumChannels() == 1);
}
void setFrequency (float frequency, float sampleRate)
{
auto tableSizeOverSampleRate = (float) tableSize / sampleRate;
tableDelta = frequency * tableSizeOverSampleRate;
}
forcedinline float getNextSample() noexcept
{
auto index0 = (unsigned int) currentIndex;
auto index1 = index0 + 1;
auto frac = currentIndex - (float) index0;
auto* table = wavetable.getReadPointer (0);
auto value0 = table[index0];
auto value1 = table[index1];
auto currentSample = value0 + frac * (value1 - value0);
if ((currentIndex += tableDelta) > (float) tableSize)
currentIndex -= (float) tableSize;
return currentSample;
}
private:
const juce::AudioSampleBuffer& wavetable;
const int tableSize;
float currentIndex = 0.0f, tableDelta = 0.0f;
};
//==============================================================================
class MainContentComponent : public juce::AudioAppComponent,
public juce::Timer
{
public:
MainContentComponent()
{
cpuUsageLabel.setText ("CPU Usage", juce::dontSendNotification);
cpuUsageText.setJustificationType (juce::Justification::right);
addAndMakeVisible (cpuUsageLabel);
addAndMakeVisible (cpuUsageText);
createWavetable();
setSize (400, 200);
setAudioChannels (0, 2); // no inputs, two outputs
startTimer (50);
}
~MainContentComponent() override
{
shutdownAudio();
}
void resized() override
{
cpuUsageLabel.setBounds (10, 10, getWidth() - 20, 20);
cpuUsageText .setBounds (10, 10, getWidth() - 20, 20);
}
void timerCallback() override
{
auto cpu = deviceManager.getCpuUsage() * 100;
cpuUsageText.setText (juce::String (cpu, 6) + " %", juce::dontSendNotification);
}
void createWavetable()
{
sineTable.setSize (1, (int) tableSize + 1);
sineTable.clear();
auto* samples = sineTable.getWritePointer (0);
int harmonics[] = { 1, 3, 5, 6, 7, 9, 13, 15 };
float harmonicWeights[] = { 0.5f, 0.1f, 0.05f, 0.125f, 0.09f, 0.005f, 0.002f, 0.001f }; // [1]
jassert (juce::numElementsInArray (harmonics) == juce::numElementsInArray (harmonicWeights));
for (auto harmonic = 0; harmonic < juce::numElementsInArray (harmonics); ++harmonic)
{
auto angleDelta = juce::MathConstants<double>::twoPi / (double) (tableSize - 1) * harmonics[harmonic]; // [2]
auto currentAngle = 0.0;
for (unsigned int i = 0; i < tableSize; ++i)
{
auto sample = std::sin (currentAngle);
samples[i] += (float) sample * harmonicWeights[harmonic]; // [3]
currentAngle += angleDelta;
}
}
samples[tableSize] = samples[0];
}
void prepareToPlay (int, double sampleRate) override
{
auto numberOfOscillators = 10;
for (auto i = 0; i < numberOfOscillators; ++i)
{
auto* oscillator = new WavetableOscillator (sineTable);
auto midiNote = juce::Random::getSystemRandom().nextDouble() * 36.0 + 48.0;
auto frequency = 440.0 * pow (2.0, (midiNote - 69.0) / 12.0);
oscillator->setFrequency ((float) frequency, (float) sampleRate);
oscillators.add (oscillator);
}
level = 0.25f / (float) numberOfOscillators;
}
void releaseResources() override {}
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* leftBuffer = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);
bufferToFill.clearActiveBufferRegion();
for (auto oscillatorIndex = 0; oscillatorIndex < oscillators.size(); ++oscillatorIndex)
{
auto* oscillator = oscillators.getUnchecked (oscillatorIndex);
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
{
auto levelSample = oscillator->getNextSample() * level;
leftBuffer[sample] += levelSample;
rightBuffer[sample] += levelSample;
}
}
}
private:
juce::Label cpuUsageLabel;
juce::Label cpuUsageText;
const unsigned int tableSize = 1 << 7;
float level = 0.0f;
juce::AudioSampleBuffer sineTable;
juce::OwnedArray<WavetableOscillator> oscillators;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT name="WavetableSynthTutorial" companyName="JUCE" version="1.0.0"
userNotes="Wavetable synthesiser." companyWebsite="http://juce.com"
projectType="guiapp" useAppConfig="0" addUsingNamespaceToJuceHeader="1"
id="dBFoEo" jucerFormatVersion="1">
<MAINGROUP id="auHq7A" name="WavetableSynthTutorial">
<GROUP id="{45834796-7C04-D188-9F98-9AE7211B6CF5}" name="Source">
<FILE id="iPmvtR" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
<FILE id="EKiEzl" name="WavetableSynthTutorial_01.h" compile="0" resource="0"
file="Source/WavetableSynthTutorial_01.h"/>
</GROUP>
</MAINGROUP>
<MODULES>
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
</MODULES>
<EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="WavetableSynthTutorial"/>
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="WavetableSynthTutorial"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path=""/>
<MODULEPATH id="juce_audio_devices" path=""/>
<MODULEPATH id="juce_audio_formats" path=""/>
<MODULEPATH id="juce_audio_processors" path=""/>
<MODULEPATH id="juce_audio_utils" path=""/>
<MODULEPATH id="juce_core" path=""/>
<MODULEPATH id="juce_data_structures" path=""/>
<MODULEPATH id="juce_events" path=""/>
<MODULEPATH id="juce_graphics" path=""/>
<MODULEPATH id="juce_gui_basics" path=""/>
<MODULEPATH id="juce_gui_extra" path=""/>
</MODULEPATHS>
</XCODE_MAC>
<VS2019 targetFolder="Builds/VisualStudio2019">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="WavetableSynthTutorial"/>
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="WavetableSynthTutorial"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path=""/>
<MODULEPATH id="juce_audio_devices" path=""/>
<MODULEPATH id="juce_audio_formats" path=""/>
<MODULEPATH id="juce_audio_processors" path=""/>
<MODULEPATH id="juce_audio_utils" path=""/>
<MODULEPATH id="juce_core" path=""/>
<MODULEPATH id="juce_data_structures" path=""/>
<MODULEPATH id="juce_events" path=""/>
<MODULEPATH id="juce_graphics" path=""/>
<MODULEPATH id="juce_gui_basics" path=""/>
<MODULEPATH id="juce_gui_extra" path=""/>
</MODULEPATHS>
</VS2019>
<LINUX_MAKE targetFolder="Builds/LinuxMakefile">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="WavetableSynthTutorial"/>
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="WavetableSynthTutorial"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path=""/>
<MODULEPATH id="juce_audio_devices" path=""/>
<MODULEPATH id="juce_audio_formats" path=""/>
<MODULEPATH id="juce_audio_processors" path=""/>
<MODULEPATH id="juce_audio_utils" path=""/>
<MODULEPATH id="juce_core" path=""/>
<MODULEPATH id="juce_data_structures" path=""/>
<MODULEPATH id="juce_events" path=""/>
<MODULEPATH id="juce_graphics" path=""/>
<MODULEPATH id="juce_gui_basics" path=""/>
<MODULEPATH id="juce_gui_extra" path=""/>
</MODULEPATHS>
</LINUX_MAKE>
</EXPORTFORMATS>
<JUCEOPTIONS/>
</JUCERPROJECT>