@@ -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) | |||||
}; |