@@ -0,0 +1,27 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
<plist> | |||
<dict> | |||
<key>CFBundleExecutable</key> | |||
<string>${EXECUTABLE_NAME}</string> | |||
<key>CFBundleIconFile</key> | |||
<string></string> | |||
<key>CFBundleIdentifier</key> | |||
<string>com.timurdoumler.PluckedStringsDemo</string> | |||
<key>CFBundleName</key> | |||
<string>PluckedStringsDemo</string> | |||
<key>CFBundlePackageType</key> | |||
<string>APPL</string> | |||
<key>CFBundleSignature</key> | |||
<string>????</string> | |||
<key>CFBundleShortVersionString</key> | |||
<string>1.0.0</string> | |||
<key>CFBundleVersion</key> | |||
<string>1.0.0</string> | |||
<key>NSHumanReadableCopyright</key> | |||
<string></string> | |||
<key>NSHighResolutionCapable</key> | |||
<true/> | |||
</dict> | |||
</plist> |
@@ -0,0 +1,19 @@ | |||
Microsoft Visual Studio Solution File, Format Version 11.00 | |||
# Visual Studio 2013 | |||
Project("{798408B1-275B-0B42-1439-1A3AA75C50F3}") = "PluckedStringsDemo", "PluckedStringsDemo.vcxproj", "{A8C8CD6F-F9CA-737E-DC8B-DA724521F17B}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Win32 = Debug|Win32 | |||
Release|Win32 = Release|Win32 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{A8C8CD6F-F9CA-737E-DC8B-DA724521F17B}.Debug|Win32.ActiveCfg = Debug|Win32 | |||
{A8C8CD6F-F9CA-737E-DC8B-DA724521F17B}.Debug|Win32.Build.0 = Debug|Win32 | |||
{A8C8CD6F-F9CA-737E-DC8B-DA724521F17B}.Release|Win32.ActiveCfg = Release|Win32 | |||
{A8C8CD6F-F9CA-737E-DC8B-DA724521F17B}.Release|Win32.Build.0 = Release|Win32 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
EndGlobal |
@@ -0,0 +1,29 @@ | |||
#ifdef JUCE_USER_DEFINED_RC_FILE | |||
#include JUCE_USER_DEFINED_RC_FILE | |||
#else | |||
#undef WIN32_LEAN_AND_MEAN | |||
#define WIN32_LEAN_AND_MEAN | |||
#include <windows.h> | |||
VS_VERSION_INFO VERSIONINFO | |||
FILEVERSION 1,0,0,0 | |||
BEGIN | |||
BLOCK "StringFileInfo" | |||
BEGIN | |||
BLOCK "040904E4" | |||
BEGIN | |||
VALUE "FileDescription", "PluckedStringsDemo\0" | |||
VALUE "FileVersion", "1.0.0\0" | |||
VALUE "ProductName", "PluckedStringsDemo\0" | |||
VALUE "ProductVersion", "1.0.0\0" | |||
END | |||
END | |||
BLOCK "VarFileInfo" | |||
BEGIN | |||
VALUE "Translation", 0x409, 65001 | |||
END | |||
END | |||
#endif |
@@ -0,0 +1,179 @@ | |||
/* | |||
IMPORTANT! This file is auto-generated each time you save your | |||
project - if you alter its contents, your changes may be overwritten! | |||
There's a section below where you can add your own custom code safely, and the | |||
Introjucer will preserve the contents of that block, but the best way to change | |||
any of these definitions is by using the Introjucer's project settings. | |||
Any commented-out settings will assume their default values. | |||
*/ | |||
#ifndef __JUCE_APPCONFIG_JKHEJM__ | |||
#define __JUCE_APPCONFIG_JKHEJM__ | |||
//============================================================================== | |||
// [BEGIN_USER_CODE_SECTION] | |||
// (You can add your own code in this section, and the Introjucer will not overwrite it) | |||
// [END_USER_CODE_SECTION] | |||
//============================================================================== | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_processors 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_audio_utils 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_core 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_data_structures 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_events 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_graphics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_basics 1 | |||
#define JUCE_MODULE_AVAILABLE_juce_gui_extra 1 | |||
//============================================================================== | |||
// juce_audio_devices flags: | |||
#ifndef JUCE_ASIO | |||
//#define JUCE_ASIO | |||
#endif | |||
#ifndef JUCE_WASAPI | |||
//#define JUCE_WASAPI | |||
#endif | |||
#ifndef JUCE_WASAPI_EXCLUSIVE | |||
//#define JUCE_WASAPI_EXCLUSIVE | |||
#endif | |||
#ifndef JUCE_DIRECTSOUND | |||
//#define JUCE_DIRECTSOUND | |||
#endif | |||
#ifndef JUCE_ALSA | |||
//#define JUCE_ALSA | |||
#endif | |||
#ifndef JUCE_JACK | |||
//#define JUCE_JACK | |||
#endif | |||
#ifndef JUCE_USE_ANDROID_OPENSLES | |||
//#define JUCE_USE_ANDROID_OPENSLES | |||
#endif | |||
#ifndef JUCE_USE_CDREADER | |||
//#define JUCE_USE_CDREADER | |||
#endif | |||
#ifndef JUCE_USE_CDBURNER | |||
//#define JUCE_USE_CDBURNER | |||
#endif | |||
//============================================================================== | |||
// juce_audio_formats flags: | |||
#ifndef JUCE_USE_FLAC | |||
//#define JUCE_USE_FLAC | |||
#endif | |||
#ifndef JUCE_USE_OGGVORBIS | |||
//#define JUCE_USE_OGGVORBIS | |||
#endif | |||
#ifndef JUCE_USE_MP3AUDIOFORMAT | |||
//#define JUCE_USE_MP3AUDIOFORMAT | |||
#endif | |||
#ifndef JUCE_USE_LAME_AUDIO_FORMAT | |||
//#define JUCE_USE_LAME_AUDIO_FORMAT | |||
#endif | |||
#ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT | |||
//#define JUCE_USE_WINDOWS_MEDIA_FORMAT | |||
#endif | |||
//============================================================================== | |||
// juce_audio_processors flags: | |||
#ifndef JUCE_PLUGINHOST_VST | |||
//#define JUCE_PLUGINHOST_VST | |||
#endif | |||
#ifndef JUCE_PLUGINHOST_VST3 | |||
//#define JUCE_PLUGINHOST_VST3 | |||
#endif | |||
#ifndef JUCE_PLUGINHOST_AU | |||
//#define JUCE_PLUGINHOST_AU | |||
#endif | |||
//============================================================================== | |||
// juce_core flags: | |||
#ifndef JUCE_FORCE_DEBUG | |||
//#define JUCE_FORCE_DEBUG | |||
#endif | |||
#ifndef JUCE_LOG_ASSERTIONS | |||
//#define JUCE_LOG_ASSERTIONS | |||
#endif | |||
#ifndef JUCE_CHECK_MEMORY_LEAKS | |||
//#define JUCE_CHECK_MEMORY_LEAKS | |||
#endif | |||
#ifndef JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
//#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES | |||
#endif | |||
#ifndef JUCE_INCLUDE_ZLIB_CODE | |||
//#define JUCE_INCLUDE_ZLIB_CODE | |||
#endif | |||
//============================================================================== | |||
// juce_graphics flags: | |||
#ifndef JUCE_USE_COREIMAGE_LOADER | |||
//#define JUCE_USE_COREIMAGE_LOADER | |||
#endif | |||
#ifndef JUCE_USE_DIRECTWRITE | |||
//#define JUCE_USE_DIRECTWRITE | |||
#endif | |||
//============================================================================== | |||
// juce_gui_basics flags: | |||
#ifndef JUCE_ENABLE_REPAINT_DEBUGGING | |||
//#define JUCE_ENABLE_REPAINT_DEBUGGING | |||
#endif | |||
#ifndef JUCE_USE_XSHM | |||
//#define JUCE_USE_XSHM | |||
#endif | |||
#ifndef JUCE_USE_XRENDER | |||
//#define JUCE_USE_XRENDER | |||
#endif | |||
#ifndef JUCE_USE_XCURSOR | |||
//#define JUCE_USE_XCURSOR | |||
#endif | |||
//============================================================================== | |||
// juce_gui_extra flags: | |||
#ifndef JUCE_WEB_BROWSER | |||
//#define JUCE_WEB_BROWSER | |||
#endif | |||
#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR | |||
//#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR | |||
#endif | |||
#endif // __JUCE_APPCONFIG_JKHEJM__ |
@@ -0,0 +1,44 @@ | |||
/* | |||
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. | |||
*/ | |||
#ifndef __APPHEADERFILE_JKHEJM__ | |||
#define __APPHEADERFILE_JKHEJM__ | |||
#include "AppConfig.h" | |||
#include "modules/juce_audio_basics/juce_audio_basics.h" | |||
#include "modules/juce_audio_devices/juce_audio_devices.h" | |||
#include "modules/juce_audio_formats/juce_audio_formats.h" | |||
#include "modules/juce_audio_processors/juce_audio_processors.h" | |||
#include "modules/juce_audio_utils/juce_audio_utils.h" | |||
#include "modules/juce_core/juce_core.h" | |||
#include "modules/juce_data_structures/juce_data_structures.h" | |||
#include "modules/juce_events/juce_events.h" | |||
#include "modules/juce_graphics/juce_graphics.h" | |||
#include "modules/juce_gui_basics/juce_gui_basics.h" | |||
#include "modules/juce_gui_extra/juce_gui_extra.h" | |||
#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 = "PluckedStringsDemo"; | |||
const char* const versionString = "1.0.0"; | |||
const int versionNumber = 0x10000; | |||
} | |||
#endif | |||
#endif // __APPHEADERFILE_JKHEJM__ |
@@ -0,0 +1,12 @@ | |||
Important Note!! | |||
================ | |||
The purpose of this folder is to contain files that are auto-generated by the Introjucer, | |||
and ALL files in this folder will be mercilessly DELETED and completely re-written whenever | |||
the Introjucer 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 Introjucer has saved its changes). |
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_audio_basics/juce_audio_basics.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_audio_devices/juce_audio_devices.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_audio_formats/juce_audio_formats.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_audio_processors/juce_audio_processors.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_audio_utils/juce_audio_utils.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_core/juce_core.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_data_structures/juce_data_structures.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_events/juce_events.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_graphics/juce_graphics.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_gui_basics/juce_gui_basics.h" | |||
@@ -0,0 +1,5 @@ | |||
// This is an auto-generated file to redirect any included | |||
// module headers to the correct external folder. | |||
#include "../../../../../modules/juce_gui_extra/juce_gui_extra.h" | |||
@@ -0,0 +1,75 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<JUCERPROJECT id="jKHEJM" name="PluckedStringsDemo" projectType="guiapp" version="1.0.0" | |||
bundleIdentifier="com.timurdoumler.PluckedStringsDemo" includeBinaryInAppConfig="1" | |||
jucerVersion="3.1.1"> | |||
<MAINGROUP id="IWdVf7" name="PluckedStringsDemo"> | |||
<GROUP id="{8800CD50-6741-8B75-0305-FAF13427EF5D}" name="Source"> | |||
<FILE id="Z7hWe1" name="StringSynthesiser.h" compile="0" resource="0" | |||
file="Source/StringSynthesiser.h"/> | |||
<FILE id="UFFPPQ" name="StringComponent.h" compile="0" resource="0" | |||
file="Source/StringComponent.h"/> | |||
<FILE id="Sut3oJ" name="StringDemoComponent.h" compile="0" resource="0" | |||
file="Source/StringDemoComponent.h"/> | |||
<FILE id="Y5HoZY" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/> | |||
</GROUP> | |||
</MAINGROUP> | |||
<EXPORTFORMATS> | |||
<XCODE_MAC targetFolder="Builds/MacOSX"> | |||
<CONFIGURATIONS> | |||
<CONFIGURATION name="Debug" osxSDK="default" osxCompatibility="default" osxArchitecture="default" | |||
isDebug="1" optimisation="1" targetName="PluckedStringsDemo"/> | |||
<CONFIGURATION name="Release" osxSDK="default" osxCompatibility="default" osxArchitecture="default" | |||
isDebug="0" optimisation="2" targetName="PluckedStringsDemo"/> | |||
</CONFIGURATIONS> | |||
<MODULEPATHS> | |||
<MODULEPATH id="juce_core" path="../../modules"/> | |||
<MODULEPATH id="juce_events" path="../../modules"/> | |||
<MODULEPATH id="juce_graphics" path="../../modules"/> | |||
<MODULEPATH id="juce_data_structures" path="../../modules"/> | |||
<MODULEPATH id="juce_gui_basics" path="../../modules"/> | |||
<MODULEPATH id="juce_gui_extra" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_basics" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_devices" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_formats" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_processors" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_utils" path="../../modules"/> | |||
</MODULEPATHS> | |||
</XCODE_MAC> | |||
<VS2013 targetFolder="Builds/VisualStudio2013"> | |||
<CONFIGURATIONS> | |||
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit" | |||
isDebug="1" optimisation="1" targetName="PluckedStringsDemo"/> | |||
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit" | |||
isDebug="0" optimisation="2" targetName="PluckedStringsDemo"/> | |||
</CONFIGURATIONS> | |||
<MODULEPATHS> | |||
<MODULEPATH id="juce_gui_extra" path="../../modules"/> | |||
<MODULEPATH id="juce_gui_basics" path="../../modules"/> | |||
<MODULEPATH id="juce_graphics" path="../../modules"/> | |||
<MODULEPATH id="juce_events" path="../../modules"/> | |||
<MODULEPATH id="juce_data_structures" path="../../modules"/> | |||
<MODULEPATH id="juce_core" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_utils" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_processors" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_formats" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_devices" path="../../modules"/> | |||
<MODULEPATH id="juce_audio_basics" path="../../modules"/> | |||
</MODULEPATHS> | |||
</VS2013> | |||
</EXPORTFORMATS> | |||
<MODULES> | |||
<MODULES id="juce_audio_basics" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_audio_devices" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_audio_formats" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_audio_processors" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_audio_utils" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_core" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_data_structures" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_events" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_graphics" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_gui_basics" showAllCode="1" useLocalCopy="0"/> | |||
<MODULES id="juce_gui_extra" showAllCode="1" useLocalCopy="0"/> | |||
</MODULES> | |||
<JUCEOPTIONS/> | |||
</JUCERPROJECT> |
@@ -0,0 +1,92 @@ | |||
/* | |||
============================================================================== | |||
JUCE demo code - use at your own risk! | |||
============================================================================== | |||
*/ | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
#include "StringDemoComponent.h" | |||
//============================================================================== | |||
class PluckedStringsDemoApplication : public JUCEApplication | |||
{ | |||
public: | |||
//============================================================================== | |||
PluckedStringsDemoApplication() {} | |||
const String getApplicationName() override { return ProjectInfo::projectName; } | |||
const String getApplicationVersion() override { return ProjectInfo::versionString; } | |||
bool moreThanOneInstanceAllowed() override { return true; } | |||
//============================================================================== | |||
void initialise (const String& /*commandLine*/) override | |||
{ | |||
mainWindow = new MainWindow(); | |||
} | |||
void shutdown() override | |||
{ | |||
mainWindow = nullptr; // (deletes our window) | |||
} | |||
//============================================================================== | |||
void systemRequestedQuit() override | |||
{ | |||
// This is called when the app is being asked to quit: you can ignore this | |||
// request and let the app carry on running, or call quit() to allow the app to close. | |||
quit(); | |||
} | |||
void anotherInstanceStarted (const String& /*commandLine*/) override | |||
{ | |||
// When another instance of the app is launched while this one is running, | |||
// this method is invoked, and the commandLine parameter tells you what | |||
// the other instance's command-line arguments were. | |||
} | |||
//============================================================================== | |||
class MainWindow : public DocumentWindow | |||
{ | |||
public: | |||
MainWindow() : DocumentWindow (ProjectInfo::projectName, | |||
Colours::lightgrey, | |||
DocumentWindow::allButtons) | |||
{ | |||
setUsingNativeTitleBar (true); | |||
setContentOwned (new StringDemoComponent(), true); | |||
setResizable (true, false); | |||
centreWithSize (getWidth(), getHeight()); | |||
setVisible (true); | |||
} | |||
void closeButtonPressed() override | |||
{ | |||
// This is called when the user tries to close this window. Here, we'll just | |||
// ask the app to quit when this happens, but you can change this to do | |||
// whatever you need. | |||
JUCEApplication::getInstance()->systemRequestedQuit(); | |||
} | |||
/* Note: Be careful if you override any DocumentWindow methods - the base | |||
class uses a lot of them, so by overriding you might break its functionality. | |||
It's best to do all your work in your content component instead, but if | |||
you really have to override any DocumentWindow methods, make sure your | |||
subclass also calls the superclass's method. | |||
*/ | |||
private: | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) | |||
}; | |||
private: | |||
ScopedPointer<MainWindow> mainWindow; | |||
}; | |||
//============================================================================== | |||
// This macro generates the main() routine that launches the app. | |||
START_JUCE_APPLICATION (PluckedStringsDemoApplication) |
@@ -0,0 +1,88 @@ | |||
/* | |||
============================================================================== | |||
JUCE demo code - use at your own risk! | |||
============================================================================== | |||
*/ | |||
/* | |||
This component represents a horizontal vibrating musical string of fixed height | |||
and variable length. The string can be excited by calling stringPlucked(). | |||
*/ | |||
class StringComponent : public Component, | |||
private Timer | |||
{ | |||
public: | |||
StringComponent (int lengthInPixels, Colour stringColour) | |||
: length (lengthInPixels), colour (stringColour) | |||
{ | |||
// ignore mouse-clicks so that our parent can get them instead. | |||
setInterceptsMouseClicks (false, false); | |||
setSize (length, height); | |||
startTimerHz (60); | |||
} | |||
//======================================================================= | |||
void stringPlucked (float pluckPositionRelative) | |||
{ | |||
amplitude = maxAmplitude * std::sin (pluckPositionRelative * float_Pi); | |||
phase = float_Pi; | |||
} | |||
//======================================================================= | |||
void paint (Graphics& g) override | |||
{ | |||
g.setColour (colour); | |||
g.strokePath (generateStringPath(), PathStrokeType (2.0f)); | |||
} | |||
Path generateStringPath() const | |||
{ | |||
const float y = height / 2.0f; | |||
Path stringPath; | |||
stringPath.startNewSubPath (0, y); | |||
stringPath.quadraticTo (length / 2.0f, y + (std::sin (phase) * amplitude), (float) length, y); | |||
return stringPath; | |||
} | |||
//============================================================================== | |||
void timerCallback() override | |||
{ | |||
updateAmplitude(); | |||
updatePhase(); | |||
repaint(); | |||
} | |||
void updateAmplitude() | |||
{ | |||
// this determines the decay of the visible string vibration. | |||
amplitude *= 0.99f; | |||
} | |||
void updatePhase() | |||
{ | |||
// this determines the visible vibration frequency. | |||
// just an arbitrary number chosen to look OK: | |||
const float phaseStep = 400.0f / length; | |||
phase += phaseStep; | |||
if (phase > float_Pi) | |||
phase -= 2.0f * float_Pi; | |||
} | |||
private: | |||
//======================================================================= | |||
int length; | |||
Colour colour; | |||
int height = 20; | |||
float amplitude = 0.0f; | |||
const float maxAmplitude = 12.0f; | |||
float phase = 0.0f; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringComponent) | |||
}; |
@@ -0,0 +1,148 @@ | |||
/* | |||
============================================================================== | |||
JUCE demo code - use at your own risk! | |||
============================================================================== | |||
*/ | |||
#include "StringSynthesiser.h" | |||
#include "StringComponent.h" | |||
//============================================================================== | |||
class StringDemoComponent : public AudioAppComponent | |||
{ | |||
public: | |||
StringDemoComponent() | |||
{ | |||
createStringComponents(); | |||
setSize (800, 560); | |||
// specify the number of input and output channels that we want to open | |||
setAudioChannels (0, 2); | |||
} | |||
~StringDemoComponent() | |||
{ | |||
shutdownAudio(); | |||
} | |||
//======================================================================= | |||
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override | |||
{ | |||
generateStringSynths (sampleRate); | |||
} | |||
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override | |||
{ | |||
bufferToFill.clearActiveBufferRegion(); | |||
for (int channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel) | |||
{ | |||
float* const channelData = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample); | |||
if (channel == 0) | |||
{ | |||
for (auto synth : stringSynths) | |||
synth->generateAndAddData (channelData, bufferToFill.numSamples); | |||
} | |||
else | |||
{ | |||
memcpy (channelData, | |||
bufferToFill.buffer->getReadPointer (0), | |||
bufferToFill.numSamples * sizeof (float)); | |||
} | |||
} | |||
} | |||
void releaseResources() override | |||
{ | |||
stringSynths.clear(); | |||
} | |||
//======================================================================= | |||
void paint (Graphics& g) override | |||
{ | |||
g.fillAll (Colours::black); | |||
} | |||
void resized() override | |||
{ | |||
int xPos = 20; | |||
int yPos = 20; | |||
int yDistance = 50; | |||
for (auto stringLine : stringLines) | |||
{ | |||
stringLine->setTopLeftPosition (xPos, yPos); | |||
yPos += yDistance; | |||
addAndMakeVisible (stringLine); | |||
} | |||
} | |||
private: | |||
void mouseDown (const MouseEvent& e) override | |||
{ | |||
mouseDrag (e); | |||
} | |||
void mouseDrag (const MouseEvent& e) override | |||
{ | |||
for (int i = 0; i < stringLines.size(); ++i) | |||
{ | |||
auto* stringLine = stringLines.getUnchecked(i); | |||
if (stringLine->getBounds().contains (e.getPosition())) | |||
{ | |||
float position = (e.position.x - stringLine->getX()) / stringLine->getWidth(); | |||
stringLine->stringPlucked (position); | |||
stringSynths.getUnchecked(i)->stringPlucked (position); | |||
} | |||
} | |||
} | |||
//======================================================================= | |||
struct StringParameters | |||
{ | |||
StringParameters (int midiNote) | |||
: frequencyInHz (MidiMessage::getMidiNoteInHertz (midiNote)), | |||
lengthInPixels ((int) (760 / (frequencyInHz / MidiMessage::getMidiNoteInHertz (42)))) | |||
{ | |||
} | |||
double frequencyInHz; | |||
int lengthInPixels; | |||
}; | |||
static std::vector<StringParameters> getDefaultStringParameters() | |||
{ | |||
return { 42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70 }; | |||
} | |||
void createStringComponents() | |||
{ | |||
for (auto stringParams : getDefaultStringParameters()) | |||
{ | |||
stringLines.add (new StringComponent (stringParams.lengthInPixels, | |||
Colour::fromHSV (Random().nextFloat(), 0.6f, 0.9f, 1.0f))); | |||
} | |||
} | |||
void generateStringSynths (double sampleRate) | |||
{ | |||
stringSynths.clear(); | |||
for (auto stringParams : getDefaultStringParameters()) | |||
{ | |||
stringSynths.add (new StringSynthesiser (sampleRate, stringParams.frequencyInHz)); | |||
} | |||
} | |||
//======================================================================= | |||
OwnedArray<StringComponent> stringLines; | |||
OwnedArray<StringSynthesiser> stringSynths; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringDemoComponent) | |||
}; |
@@ -0,0 +1,125 @@ | |||
/* | |||
============================================================================== | |||
JUCE demo code - use at your own risk! | |||
============================================================================== | |||
*/ | |||
/** | |||
A very basic generator of a simulated plucked string sound, implementing | |||
the Karplus-Strong algorithm. | |||
Not performance-optimised! | |||
*/ | |||
class StringSynthesiser | |||
{ | |||
public: | |||
//======================================================================= | |||
/** Constructor. | |||
@param sampleRate The audio sample rate to use. | |||
@param frequencyInHz The fundamental frequency of the simulated string in | |||
Hertz. | |||
*/ | |||
StringSynthesiser (double sampleRate, double frequencyInHz) | |||
{ | |||
doPluckForNextBuffer.set (false); | |||
prepareSynthesiserState (sampleRate, frequencyInHz); | |||
} | |||
//======================================================================= | |||
/** Excite the simulated string by plucking it at a given position. | |||
@param pluckPosition The position of the plucking, relative to the length | |||
of the string. Must be between 0 and 1. | |||
*/ | |||
void stringPlucked (float pluckPosition) | |||
{ | |||
jassert (pluckPosition >= 0.0 && pluckPosition <= 1.0); | |||
// we choose a very simple approach to communicate with the audio thread: | |||
// simply tell the synth to perform the plucking excitation at the beginning | |||
// of the next buffer (= when generateAndAddData is called the next time). | |||
if (doPluckForNextBuffer.compareAndSetBool (1, 0)) | |||
{ | |||
// plucking in the middle gives the largest amplitude; | |||
// plucking at the very ends will do nothing. | |||
amplitude = std::sin (float_Pi * pluckPosition); | |||
} | |||
} | |||
//======================================================================= | |||
/** Generate next chunk of mono audio output and add it into a buffer. | |||
@param outBuffer Buffer to fill (one channel only). New sound will be | |||
added to existing content of the buffer (instead of | |||
replacing it). | |||
@param numSamples Number of samples to generate (make sure that outBuffer | |||
has enough space). | |||
*/ | |||
void generateAndAddData (float* outBuffer, int numSamples) | |||
{ | |||
if (doPluckForNextBuffer.compareAndSetBool (0, 1)) | |||
exciteInternalBuffer(); | |||
// cycle through the delay line and apply a simple averaging filter | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
const int nextPos = (pos + 1) % delayLine.size(); | |||
delayLine[nextPos] = (float) (decay * 0.5 * (delayLine[nextPos] + delayLine[pos])); | |||
outBuffer[i] += delayLine[pos]; | |||
pos = nextPos; | |||
} | |||
} | |||
private: | |||
//======================================================================= | |||
void prepareSynthesiserState (double sampleRate, double frequencyInHz) | |||
{ | |||
std::size_t delayLineLength = std::lround (sampleRate / frequencyInHz); | |||
// we need a minimum delay line length to get a reasonable synthesis. | |||
// if you hit this assert, increase sample rate or decrease frequency! | |||
jassert (delayLineLength > 50); | |||
delayLine.resize (delayLineLength); | |||
std::fill (delayLine.begin(), delayLine.end(), 0.0f); | |||
excitationSample.resize (delayLineLength); | |||
// as the excitation sample we use random noise between -1 and 1 | |||
// (as a simple approximation to a plucking excitation) | |||
std::generate (excitationSample.begin(), | |||
excitationSample.end(), | |||
[] { return (Random::getSystemRandom().nextFloat() * 2.0f) - 1.0f; } ); | |||
} | |||
void exciteInternalBuffer() | |||
{ | |||
// fill the buffer with the precomputed excitation sound (scaled with amplitude) | |||
jassert (delayLine.size() >= excitationSample.size()); | |||
std::transform (excitationSample.begin(), | |||
excitationSample.end(), | |||
delayLine.begin(), | |||
[this] (double sample) { return amplitude * sample; } ); | |||
}; | |||
//======================================================================= | |||
const double decay = 0.998; | |||
double amplitude = 0.0; | |||
Atomic<int> doPluckForNextBuffer; | |||
std::vector<float> excitationSample, delayLine; | |||
int pos = 0; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringSynthesiser) | |||
}; |