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