Engin resolution + am implementation

pull/1/head
asb2m10 10 years ago
parent 77fa26a066
commit 05cd3aeba9
  1. 4742
      Builds/MacOSX/Dexed.xcodeproj/project.pbxproj
  2. BIN
      Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate
  3. 12
      Builds/VisualStudio2012/Dexed.sln
  4. 1081
      Builds/VisualStudio2012/Dexed.vcxproj
  5. 119
      Builds/VisualStudio2012/Dexed.vcxproj.filters
  6. 38
      README.md
  7. 4
      Source/Dexed.h
  8. 82
      Source/ParamDialog.cpp
  9. 5
      Source/ParamDialog.h
  10. 9
      Source/PluginEditor.cpp
  11. 5
      Source/PluginParam.cpp
  12. 40
      Source/PluginProcessor.cpp
  13. 10
      Source/PluginProcessor.h
  14. 5
      Source/msfa/controllers.h
  15. 529
      Source/msfa/dx7note.cc
  16. 147
      Source/msfa/dx7note.h
  17. 303
      Source/msfa/fm_core.cc
  18. 78
      Source/msfa/fm_core.h
  19. 573
      Source/msfa/fm_op_kernel.cc
  20. 77
      Source/msfa/fm_op_kernel.h

File diff suppressed because it is too large Load Diff

@ -1,23 +1,17 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Windows Desktop
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dexed", "Dexed.vcxproj", "{1A9EF105-5BF5-9FB6-9634-A91A6D840866}"
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2012
Project("{BD26B4C3-163D-4785-A63F-D3E66858BFF3}") = "Dexed", "Dexed.vcxproj", "{1A9EF105-5BF5-9FB6-9634-A91A6D840866}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Debug|Win32.ActiveCfg = Debug|Win32
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Debug|Win32.Build.0 = Debug|Win32
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Debug|x64.ActiveCfg = Debug|x64
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Debug|x64.Build.0 = Debug|x64
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Release|Win32.ActiveCfg = Release|Win32
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Release|Win32.Build.0 = Release|Win32
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Release|x64.ActiveCfg = Release|x64
{1A9EF105-5BF5-9FB6-9634-A91A6D840866}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

File diff suppressed because it is too large Load Diff

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Dexed">
@ -384,6 +385,9 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_basics\synthesisers\juce_Synthesiser.cpp">
<Filter>Juce Modules\juce_audio_basics\synthesisers</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AU\juce_AU_Wrapper.mm">
<Filter>Juce Modules\juce_audio_plugin_client\AU</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode1.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\RTAS</Filter>
</ClCompile>
@ -399,15 +403,27 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_Wrapper.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\RTAS</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_MacUtilities.mm">
<Filter>Juce Modules\juce_audio_plugin_client\RTAS</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\VST</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.mm">
<Filter>Juce Modules\juce_audio_plugin_client\VST</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\VST3</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.mm">
<Filter>Juce Modules\juce_audio_plugin_client\VST3</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\AAX</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.mm">
<Filter>Juce Modules\juce_audio_plugin_client\AAX</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\utility\juce_PluginUtilities.cpp">
<Filter>Juce Modules\juce_audio_plugin_client\utility</Filter>
</ClCompile>
@ -432,6 +448,9 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_processors\format\juce_AudioPluginFormatManager.cpp">
<Filter>Juce Modules\juce_audio_processors\format</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\juce_AudioUnitPluginFormat.mm">
<Filter>Juce Modules\juce_audio_processors\format_types</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_processors\format_types\juce_LADSPAPluginFormat.cpp">
<Filter>Juce Modules\juce_audio_processors\format_types</Filter>
</ClCompile>
@ -672,6 +691,21 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_linux_Threads.cpp">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_mac_Files.mm">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_mac_Network.mm">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_mac_Strings.mm">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_mac_SystemStats.mm">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_mac_Threads.mm">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_core\native\juce_posix_NamedPipe.cpp">
<Filter>Juce Modules\juce_core\native</Filter>
</ClCompile>
@ -744,9 +778,15 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_events\native\juce_android_Messaging.cpp">
<Filter>Juce Modules\juce_events\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_events\native\juce_ios_MessageManager.mm">
<Filter>Juce Modules\juce_events\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_events\native\juce_linux_Messaging.cpp">
<Filter>Juce Modules\juce_events\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_events\native\juce_mac_MessageManager.mm">
<Filter>Juce Modules\juce_events\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_events\native\juce_win32_Messaging.cpp">
<Filter>Juce Modules\juce_events\native</Filter>
</ClCompile>
@ -846,6 +886,12 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_graphics\native\juce_linux_Fonts.cpp">
<Filter>Juce Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_graphics\native\juce_mac_CoreGraphicsContext.mm">
<Filter>Juce Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_graphics\native\juce_mac_Fonts.mm">
<Filter>Juce Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_graphics\native\juce_win32_Direct2DGraphicsContext.cpp">
<Filter>Juce Modules\juce_graphics\native</Filter>
</ClCompile>
@ -1194,6 +1240,12 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_android_Windowing.cpp">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_ios_UIViewComponentPeer.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_ios_Windowing.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_linux_Clipboard.cpp">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
@ -1203,6 +1255,21 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_linux_Windowing.cpp">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_mac_FileChooser.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_mac_MainMenu.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_mac_MouseCursor.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_mac_NSViewComponentPeer.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_mac_Windowing.mm">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_basics\native\juce_win32_DragAndDrop.cpp">
<Filter>Juce Modules\juce_gui_basics\native</Filter>
</ClCompile>
@ -1257,15 +1324,27 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_android_WebBrowserComponent.cpp">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_ios_UIViewComponent.mm">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_linux_SystemTrayIcon.cpp">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_linux_WebBrowserComponent.cpp">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_mac_AppleRemote.mm">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_mac_NSViewComponent.mm">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_mac_SystemTrayIcon.cpp">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_mac_WebBrowserComponent.mm">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_gui_extra\native\juce_win32_ActiveXComponent.cpp">
<Filter>Juce Modules\juce_gui_extra\native</Filter>
</ClCompile>
@ -1338,42 +1417,6 @@
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp">
<Filter>Juce Library Code</Filter>
</ClCompile>
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\utility\juce_PluginUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode1.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode2.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode3.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_WinUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\utility\juce_PluginUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode1.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode2.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode3.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_WinUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\utility\juce_PluginUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode1.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode2.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode3.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_WinUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\AAX\juce_AAX_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\utility\juce_PluginUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode1.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode2.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_DigiCode3.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_WinUtilities.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\RTAS\juce_RTAS_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST3\juce_VST3_Wrapper.cpp" />
<ClCompile Include="..\..\JuceLibraryCode\modules\juce_audio_plugin_client\VST\juce_VST_Wrapper.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\Source\Dexed.h">
@ -2671,4 +2714,4 @@
<Filter>Juce Library Code</Filter>
</ResourceCompile>
</ItemGroup>
</Project>
</Project>

@ -33,11 +33,13 @@ new version here but you see it in the change log, it's because this version is
* Version 0.6.0 [vst win32/x64](http://le-son666.com/software/dexed/dexed-0.6.0-win.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.6.0-osx.vst.zip)
* Version 0.5.1 [vst win32](http://le-son666.com/software/dexed/dexed-0.5.1-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.5.1-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.5.1-osx.vst.zip)
* Version 0.5.0 [vst win32](http://le-son666.com/software/dexed/dexed-0.5.0a-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.5.0a-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.5.0-osx.vst.zip)
* Version 0.4.0 [vst win32](http://le-son666.com/software/dexed/dexed-0.4.0-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.4.0-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.4.0-osx.vst.zip)
* Version 0.3.0 [vst win32](http://le-son666.com/software/dexed/dexed-0.3.0-win32.zip) - [vst win64](http://le-son666.com/software/dexed/dexed-0.3.0-win64.zip) - [vst os x](http://le-son666.com/software/dexed/dexed-0.3.0-osx.vst.zip)
Changelog
---------
#### Version 0.7.0 (current sprint)
* DX Engine customizable bitrate engine
* DX Engine AM implementation
#### Version 0.6.1
* Mouse over + LFO type fix + pitch eg values
@ -49,20 +51,6 @@ Changelog
* Knobs now works with vertical mouse drags
* User DX7 zip cartridges
#### Version 0.5.1
* Fix distortion issue with FL (DAW blocksize not multiple of 64)
* OS X 64bit build (the VST package contains both 32bit and 64bit)
#### Version 0.5.0
* Linux support
* Fix large DAW blocksize with midi events
#### Version 0.4.0
* Modulation wheel support
* Now using the [Obxd](https://obxd.wordpress.com) 4-pole lowpass filter implementation
* Pitchbend range / step
* Output/Volume VU status
User DX7 zip cartridges
-----------------------
It is possible to enjoy your DX7 sysex collection from one single zip file. Simply move your zipped
@ -71,6 +59,23 @@ sysex content to where you have installed Dexed (VST plugins dir). Then rename t
file is changed. Directories in the zip file will be transformed into submenu when you hit the
[CART] button. Watch out; Windows hides the .zip extension by default !
Engine resolutions
------------------
Dexed can be configured to try to use the original math limitation of a DX synthesizer. And when I say
math limitation, I'm not only talking about the DAC, it is also about the sin LUT lookup table, multiply
resolution and original DX sampling rate. This is a work in progress and this might take time to be able
to perfect.
If you look at the original DX7 and implementation a DX engine with 10-bit sin lookup and 12 mul possibility,
you get something "not quite there". Yamaha did a lot of hacks to be able to squeeze this into something
musical and expressive. It is those 'hacks' that we need to recreate to be able to find that original
DX sound.
Dexed comes with 3 engine resolution.
* Modern : this is the original 24-bit music-synthesizer-for-android implementation.
* Mark I : this is a pale implementation of the limitation of a Yamaha DX7 Mark I with the 12-bit (with the 4-bit attenuator hack) DAC.
* OPL Series : this is a experimental implementation of Yamaha 4-ops that used the YM2151 chip. These chips were supposed to be even more limited to the DX7 but gave a very interesting distinctive sound.
Using as a DX7 editor
---------------------
You can use this plugin to edit your real DX7 patchs. Since midi sysex send/receive are quirky for the
@ -142,5 +147,4 @@ TODO - msfa
-----------
* The sample rate should not change the response of the envelopes
* Portamento implementation
* LFO/Mod-wheel amplitude
* Algo 4 & 6 feedback

@ -24,14 +24,14 @@
void dexed_trace(const char *source, const char *fmt, ...);
#ifdef DEBUG
#define DEXED_VERSION "0.6.1 DEBUG"
#define DEXED_VERSION "0.7.0 BETA DEBUG"
#ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif
#else
#define DEXED_VERSION "0.6.1"
#define DEXED_VERSION "0.7.0 BETA"
#define TRACE(fmt, ...)
#endif

@ -62,11 +62,21 @@ ParamDialog::ParamDialog ()
sysexChl->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20);
sysexChl->addListener (this);
addAndMakeVisible (engineReso = new ComboBox ("new combo box"));
engineReso->setEditableText (false);
engineReso->setJustificationType (Justification::centredLeft);
engineReso->setTextWhenNothingSelected (String::empty);
engineReso->setTextWhenNoChoicesAvailable (TRANS("(no choices)"));
engineReso->addItem (TRANS("Modern (Direct)"), 1);
engineReso->addItem (TRANS("Mark I"), 2);
engineReso->addItem (TRANS("OPL Series"), 3);
engineReso->addListener (this);
//[UserPreSize]
//[/UserPreSize]
setSize (330, 300);
setSize (350, 350);
//[Constructor] You can add your own custom stuff here..
@ -95,6 +105,7 @@ ParamDialog::~ParamDialog()
sysexIn = nullptr;
sysexOut = nullptr;
sysexChl = nullptr;
engineReso = nullptr;
//[Destructor]. You can add your own custom destruction code here..
@ -124,32 +135,45 @@ void ParamDialog::paint (Graphics& g)
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("DX7 In"),
20, 178, 131, 23,
28, 226, 131, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("DX7 Out"),
19, 218, 131, 23,
30, 267, 131, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("DX7 Channel"),
19, 258, 245, 23,
27, 306, 245, 23,
Justification::centredLeft, true);
g.setColour (Colours::white);
g.setFont (Font (15.00f, Font::plain));
g.drawText (TRANS("Engine Resolution"),
22, 154, 276, 23,
Justification::centredLeft, true);
g.setColour (Colours::black);
g.fillRect (22, 138, 306, 1);
g.setColour (Colours::black);
g.fillRect (22, 194, 306, 1);
//[UserPaint] Add your own custom painting code here..
//[/UserPaint]
}
void ParamDialog::resized()
{
pitchRange->setBounds (240, 16, 72, 24);
pitchStep->setBounds (240, 56, 72, 24);
sysexIn->setBounds (96, 176, 208, 24);
sysexOut->setBounds (96, 216, 208, 24);
sysexChl->setBounds (240, 256, 72, 24);
pitchRange->setBounds (264, 16, 72, 24);
pitchStep->setBounds (264, 56, 72, 24);
sysexIn->setBounds (104, 224, 224, 24);
sysexOut->setBounds (104, 264, 224, 24);
sysexChl->setBounds (264, 304, 72, 24);
engineReso->setBounds (160, 152, 168, 24);
//[UserResized] Add your own custom resize handling here..
//[/UserResized]
}
@ -195,6 +219,11 @@ void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
//[UserComboBoxCode_sysexOut] -- add your combo box handling code here..
//[/UserComboBoxCode_sysexOut]
}
else if (comboBoxThatHasChanged == engineReso)
{
//[UserComboBoxCode_engineReso] -- add your combo box handling code here..
//[/UserComboBoxCode_engineReso]
}
//[UsercomboBoxChanged_Post]
//[/UsercomboBoxChanged_Post]
@ -204,7 +233,7 @@ void ParamDialog::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr) {
void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr, int reso) {
pitchRange->setValue(c.values_[kControllerPitchRange]);
pitchStep->setValue(c.values_[kControllerPitchStep]);
sysexChl->setValue(mgr.getChl() + 1);
@ -216,12 +245,13 @@ void ParamDialog::setDialogValues(Controllers &c, SysexComm &mgr) {
StringArray outputs = MidiOutput::getDevices();
idx = outputs.indexOf(mgr.getOutput());
TRACE("output idx %d output %s", idx, mgr.getOutput().toRawUTF8());
idx = idx == -1 ? 0 : idx + 1;
sysexOut->setSelectedItemIndex(idx);
engineReso->setSelectedItemIndex(reso);
}
bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr) {
bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr, int *reso) {
bool ret = true;
c.values_[kControllerPitchRange] = pitchRange->getValue();
@ -230,6 +260,8 @@ bool ParamDialog::getDialogValues(Controllers &c, SysexComm &mgr) {
ret &= mgr.setOutput(sysexOut->getItemText(sysexOut->getSelectedItemIndex()));
mgr.setChl(sysexChl->getValue() - 1);
*reso = engineReso->getSelectedItemIndex();
return ret;
}
@ -248,37 +280,45 @@ BEGIN_JUCER_METADATA
<JUCER_COMPONENT documentType="Component" className="ParamDialog" componentName=""
parentClasses="public Component" constructorParams="" variableInitialisers=""
snapPixels="8" snapActive="1" snapShown="1" overlayOpacity="0.330"
fixedSize="1" initialWidth="330" initialHeight="300">
fixedSize="1" initialWidth="350" initialHeight="350">
<BACKGROUND backgroundColour="ff4e270d">
<TEXT pos="20 18 276 23" fill="solid: ffffffff" hasStroke="0" text="Pitch Bend Range"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="20 58 276 23" fill="solid: ffffffff" hasStroke="0" text="Pitch Bend Step"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="20 178 131 23" fill="solid: ffffffff" hasStroke="0" text="DX7 In"
<TEXT pos="28 226 131 23" fill="solid: ffffffff" hasStroke="0" text="DX7 In"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="30 267 131 23" fill="solid: ffffffff" hasStroke="0" text="DX7 Out"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="19 218 131 23" fill="solid: ffffffff" hasStroke="0" text="DX7 Out"
<TEXT pos="27 306 245 23" fill="solid: ffffffff" hasStroke="0" text="DX7 Channel"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<TEXT pos="19 258 245 23" fill="solid: ffffffff" hasStroke="0" text="DX7 Channel"
<TEXT pos="22 154 276 23" fill="solid: ffffffff" hasStroke="0" text="Engine Resolution"
fontname="Default font" fontsize="15" bold="0" italic="0" justification="33"/>
<RECT pos="22 138 306 1" fill="solid: ff000000" hasStroke="0"/>
<RECT pos="22 194 306 1" fill="solid: ff000000" hasStroke="0"/>
</BACKGROUND>
<SLIDER name="pitchRange" id="7409be5a8dfaa91" memberName="pitchRange"
virtualName="" explicitFocusOrder="0" pos="240 16 72 24" min="0"
virtualName="" explicitFocusOrder="0" pos="264 16 72 24" min="0"
max="12" int="1" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<SLIDER name="pitchStep" id="b86af4b792e768ca" memberName="pitchStep"
virtualName="" explicitFocusOrder="0" pos="240 56 72 24" min="0"
virtualName="" explicitFocusOrder="0" pos="264 56 72 24" min="0"
max="12" int="1" style="RotaryVerticalDrag" textBoxPos="TextBoxLeft"
textBoxEditable="1" textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<COMBOBOX name="sysexIn" id="3750642d8b5be11" memberName="sysexIn" virtualName=""
explicitFocusOrder="0" pos="96 176 208 24" editable="0" layout="33"
explicitFocusOrder="0" pos="104 224 224 24" editable="0" layout="33"
items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
<COMBOBOX name="sysexOut" id="44730115841c2214" memberName="sysexOut" virtualName=""
explicitFocusOrder="0" pos="96 216 208 24" editable="0" layout="33"
explicitFocusOrder="0" pos="104 264 224 24" editable="0" layout="33"
items="" textWhenNonSelected="" textWhenNoItems="(no choices)"/>
<SLIDER name="sysexChl" id="7fdc8830f90a7c86" memberName="sysexChl" virtualName=""
explicitFocusOrder="0" pos="240 256 72 24" min="1" max="16" int="1"
explicitFocusOrder="0" pos="264 304 72 24" min="1" max="16" int="1"
style="RotaryVerticalDrag" textBoxPos="TextBoxLeft" textBoxEditable="1"
textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>
<COMBOBOX name="new combo box" id="4087ff978c3d9e8d" memberName="engineReso"
virtualName="" explicitFocusOrder="0" pos="160 152 168 24" editable="0"
layout="33" items="Modern (Direct)&#10;Mark I&#10;OPL Series"
textWhenNonSelected="" textWhenNoItems="(no choices)"/>
</JUCER_COMPONENT>
END_JUCER_METADATA

@ -47,8 +47,8 @@ public:
//==============================================================================
//[UserMethods] -- You can add your own custom methods in this section.
void setDialogValues(Controllers &c, SysexComm &mgr);
bool getDialogValues(Controllers &c, SysexComm &mgr);
void setDialogValues(Controllers &c, SysexComm &mgr, int reso);
bool getDialogValues(Controllers &c, SysexComm &mgr, int *reso);
//[/UserMethods]
void paint (Graphics& g);
@ -68,6 +68,7 @@ private:
ScopedPointer<ComboBox> sysexIn;
ScopedPointer<ComboBox> sysexOut;
ScopedPointer<Slider> sysexChl;
ScopedPointer<ComboBox> engineReso;
//==============================================================================

@ -27,6 +27,8 @@
#include "math.h"
#include <fstream>
#include "msfa/fm_op_kernel.h"
using namespace ::std;
class AboutBox : public DialogWindow {
@ -316,10 +318,12 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) {
}
if (buttonThatWasClicked == settingsButton) {
int reso = processor->getEngineResolution();
AlertWindow window("","", AlertWindow::NoIcon, this);
ParamDialog param;
param.setColour(AlertWindow::backgroundColourId, Colour(0x32FFFFFF));
param.setDialogValues(processor->controllers, processor->sysexComm);
param.setDialogValues(processor->controllers, processor->sysexComm, reso);
window.addCustomComponent(&param);
window.addButton("OK", 0);
@ -327,7 +331,8 @@ void DexedAudioProcessorEditor::buttonClicked(Button *buttonThatWasClicked) {
if ( window.runModalLoop() != 0 )
return;
bool ret = param.getDialogValues(processor->controllers, processor->sysexComm);
bool ret = param.getDialogValues(processor->controllers, processor->sysexComm, &reso);
processor->setEngineResolution(reso);
processor->savePreference();
if ( ret == false ) {

@ -523,6 +523,9 @@ void DexedAudioProcessor::loadPreference() {
sysexComm.setChl( prop.getIntValue( String("sysexChl") ) );
}
if ( prop.containsKey( String("engineResolution" ) ) ) {
engineResolution = prop.getIntValue( String(engineResolution) );
}
}
void DexedAudioProcessor::savePreference() {
@ -536,6 +539,8 @@ void DexedAudioProcessor::savePreference() {
prop.setValue(String("sysexOut"), sysexComm.getOutput());
prop.setValue(String("sysexChl"), sysexComm.getChl());
//prop.setValue(String("engineResolution"), engineResolution);
prop.save();
}

@ -28,6 +28,7 @@
#include "msfa/exp2.h"
#include "msfa/pitchenv.h"
#include "msfa/aligned_buf.h"
#include "msfa/fm_op_kernel.h"
//==============================================================================
@ -54,6 +55,7 @@ DexedAudioProcessor::DexedAudioProcessor() {
normalizeDxVelocity = false;
sysexComm.listener = this;
keyboardState.addListener(&sysexComm);
engineResolution = -1;
memset(&voiceStatus, 0, sizeof(VoiceStatus));
@ -66,11 +68,14 @@ DexedAudioProcessor::DexedAudioProcessor() {
controllers.values_[kControllerPitchStep] = 0;
loadPreference();
setEngineResolution(DEXED_RESO_MODERN);
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) {
voices[note].dx7_note = NULL;
}
nextMidi = NULL;
midiMsg = NULL;
}
DexedAudioProcessor::~DexedAudioProcessor() {
@ -184,9 +189,14 @@ void DexedAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& mi
for (int note = 0; note < MAX_ACTIVE_NOTES; ++note) {
if (voices[note].live) {
voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers);
uint32_t state = 0;
for (int j=0; j < N; ++j) {
int32_t val = audiobuf.get()[j] >> 4;
int32 r = rand() & 0xFFFF;
int32_t val = audiobuf.get()[j]; //& 0xFFFFF000);// + r - state;
state = r;
val = val >> 4;
int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9;
float f = ((float) clip_val) / (float) 32768;
if( f > 1 ) f = 1;
@ -411,6 +421,32 @@ void DexedAudioProcessor::handleIncomingMidiMessage(MidiInput* source, const Mid
forceRefreshUI = true;
}
int DexedAudioProcessor::getEngineResolution() {
return engineResolution;
}
void DexedAudioProcessor::setEngineResolution(int rs) {
switch (rs) {
case DEXED_RESO_MODERN :
controllers.sinBitFilter = -1;
controllers.dacBitFilter = -1;
controllers.mulBitFilter = -1;
break;
case DEXED_RESO_MARKI:
controllers.sinBitFilter = 0xFFFFC000; // 10 bit
controllers.dacBitFilter = 0xFFFFF000; // semi 14 bit
break;
case DEXED_RESO_OPL:
controllers.sinBitFilter = 0xFFFF8000; // 9 bit
controllers.dacBitFilter = 0xFFFF0000;
break;
}
engineResolution = rs;
}
// ====================================================================
bool DexedAudioProcessor::peekVoiceStatus() {
if ( currentNote == -1 )
@ -507,7 +543,7 @@ AudioProcessorEditor* DexedAudioProcessor::createEditor() {
}
void DexedAudioProcessor::handleAsyncUpdate() {
updateUI();
updateUI();
}
void dexed_trace(const char *source, const char *fmt, ...) {

@ -40,6 +40,12 @@ struct ProcessorVoice {
Dx7Note *dx7_note;
};
enum DexedEngineResolution {
DEXED_RESO_MODERN,
DEXED_RESO_MARKI,
DEXED_RESO_OPL
};
//==============================================================================
/**
*/
@ -98,6 +104,7 @@ class DexedAudioProcessor : public AudioProcessor, public AsyncUpdater, public
bool getNextEvent(MidiBuffer::Iterator* iter,const int samplePos);
void handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message);
uint32_t engineResolution;
public :
// in MIDI units (0x4000 is neutral)
@ -114,6 +121,9 @@ public :
bool forceRefreshUI;
float vuSignal;
int getEngineResolution();
void setEngineResolution(int rs);
Array<Ctrl*> ctrl;

@ -26,6 +26,11 @@ const int kControllerPitchStep = 130;
class Controllers {
public:
int values_[131];
// engine bit filters
uint32_t sinBitFilter; // bit filter based upon sin LUT lookup
uint32_t dacBitFilter; // bit filter based upon DAC resolution
uint32_t mulBitFilter; // bit filter based upon multipliers (?????)
};
#endif // __CONTROLLERS_H

@ -1,257 +1,272 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef VERBOSE
#include <iostream>
#endif
#include <math.h>
#include "synth.h"
#include "freqlut.h"
#include "exp2.h"
#include "controllers.h"
#include "dx7note.h"
using namespace std;
void dexed_trace(const char *source, const char *fmt, ...);
#ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif
int32_t midinote_to_logfreq(int midinote) {
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
return base + step * midinote;
}
const int32_t coarsemul[] = {
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858,
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032,
81503396, 82323963, 83117622
};
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
logfreq = midinote_to_logfreq(midinote);
logfreq += coarsemul[coarse & 31];
if (fine) {
// (1 << 24) / log(2)
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5);
}
// This was measured at 7.213Hz per count at 9600Hz, but the exact
// value is somewhat dependent on midinote. Close enough for now.
logfreq += 12606 * (detune - 7);
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
logfreq += detune > 7 ? 13457 * (detune - 7) : 0;
}
return logfreq;
}
const uint8_t velocity_data[64] = {
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163,
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202,
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232,
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251,
252, 253, 254
};
// See "velocity" section of notes. Returns velocity delta in microsteps.
int ScaleVelocity(int velocity, int sensitivity) {
int clamped_vel = max(0, min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
}
int ScaleRate(int midinote, int sensitivity) {
int x = min(31, max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
#ifdef SUPER_PRECISE
int rem = x & 7;
if (sensitivity == 3 && rem == 3) {
qratedelta -= 1;
} else if (sensitivity == 7 && rem > 0 && rem < 4) {
qratedelta += 1;
}
#endif
return qratedelta;
}
const uint8_t exp_scale_data[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250
};
int ScaleCurve(int group, int depth, int curve) {
int scale;
if (curve == 0 || curve == 3) {
// linear
scale = (group * depth * 329) >> 12;
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
scale = -scale;
}
return scale;
}
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth,
int left_curve, int right_curve) {
int offset = midinote - break_pt - 17;
if (offset >= 0) {
return ScaleCurve(offset / 3, right_depth, right_curve);
} else {
return ScaleCurve((-offset) / 3, left_depth, left_curve);
}
}
static const uint8_t pitchmodsenstab[] = {
0, 10, 20, 33, 55, 92, 153, 255
};
void Dx7Note::init(const char patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
#ifdef VERBOSE
for (int j = 8; j < 12; j++) {
cout << (int)patch[off + j] << " ";
}
#endif
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
#ifdef VERBOSE
cout << op << ": " << level_scaling << " " << outlevel << endl;
#endif
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init(rates, levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
basepitch_[op] = freq;
// cout << op << " freq: " << freq << endl;
params_[op].phase = 0;
params_[op].gain[1] = 0;
}
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
pitchenv_.set(rates, levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
}
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls) {
int32_t pitchmod = pitchenv_.getsample();
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
// TODO: add modulation sources (mod wheel, etc)
uint32_t pwmd = (ctrls->values_[kControllerModWheel] * 0.7874) * (1 << 24);
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23));
pitchmod += (((int64_t)pwmd) * (int64_t)senslfo) >> 39;
pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39;
int pitchbend = ctrls->values_[kControllerPitch];
int32_t pb = (pitchbend - 0x2000);
if ( pb != 0 ) {
if ( ctrls->values_[kControllerPitchStep] == 0 ) {
pb = ((float)(pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0;
} else {
int stp = 12 / ctrls->values_[kControllerPitchStep];
pb = pb * stp / 8191;
pb = (pb * (8191/stp)) << 11;
}
}
pitchmod += pb;
for (int op = 0; op < 6; op++) {
params_[op].gain[0] = params_[op].gain[1];
int32_t level = env_[op].getsample();
int32_t gain = Exp2::lookup(level - (14 * (1 << 24)));
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod);
params_[op].gain[1] = gain;
}
core_.compute(buf, params_, algorithm_, fb_buf_, fb_shift_);
}
void Dx7Note::keyup() {
for (int op = 0; op < 6; op++) {
env_[op].keydown(false);
pitchenv_.keydown(false);
}
}
void Dx7Note::update(const char patch[156], int midinote) {
for (int op = 0; op < 6; op++) {
int off = op * 21;
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune);
}
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
}
void Dx7Note::peekVoiceStatus(VoiceStatus &status) {
for(int i=0;i<6;i++) {
status.amp[i] = params_[i].gain[1];
env_[i].getPosition(&status.ampStep[i]);
}
pitchenv_.getPosition(&status.pitchStep);
}
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef VERBOSE
#include <iostream>
using namespace std;
#endif
#include <math.h>
#include "synth.h"
#include "freqlut.h"
#include "exp2.h"
#include "controllers.h"
#include "dx7note.h"
void dexed_trace(const char *source, const char *fmt, ...);
#ifdef _MSC_VER
#define TRACE(fmt, ...) dexed_trace(__FUNCTION__,fmt,##__VA_ARGS__)
#else
#define TRACE(fmt, ...) dexed_trace(__PRETTY_FUNCTION__,fmt,##__VA_ARGS__)
#endif
int32_t midinote_to_logfreq(int midinote) {
const int base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12)
const int step = (1 << 24) / 12;
return base + step * midinote;
}
const int32_t coarsemul[] = {
-16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600,
50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816,
65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858,
74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032,
81503396, 82323963, 83117622
};
int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) {
// TODO: pitch randomization
int32_t logfreq;
if (mode == 0) {
logfreq = midinote_to_logfreq(midinote);
logfreq += coarsemul[coarse & 31];
if (fine) {
// (1 << 24) / log(2)
logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5);
}
// This was measured at 7.213Hz per count at 9600Hz, but the exact
// value is somewhat dependent on midinote. Close enough for now.
logfreq += 12606 * (detune - 7);
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
logfreq += detune > 7 ? 13457 * (detune - 7) : 0;
}
return logfreq;
}
const uint8_t velocity_data[64] = {
0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163,
166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202,
205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232,
233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251,
252, 253, 254
};
// See "velocity" section of notes. Returns velocity delta in microsteps.
int ScaleVelocity(int velocity, int sensitivity) {
int clamped_vel = max(0, min(127, velocity));
int vel_value = velocity_data[clamped_vel >> 1] - 239;
int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4;
return scaled_vel;
}
int ScaleRate(int midinote, int sensitivity) {
int x = min(31, max(0, midinote / 3 - 7));
int qratedelta = (sensitivity * x) >> 3;
#ifdef SUPER_PRECISE
int rem = x & 7;
if (sensitivity == 3 && rem == 3) {
qratedelta -= 1;
} else if (sensitivity == 7 && rem > 0 && rem < 4) {
qratedelta += 1;
}
#endif
return qratedelta;
}
const uint8_t exp_scale_data[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66,
80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250
};
int ScaleCurve(int group, int depth, int curve) {
int scale;
if (curve == 0 || curve == 3) {
// linear
scale = (group * depth * 329) >> 12;
} else {
// exponential
int n_scale_data = sizeof(exp_scale_data);
int raw_exp = exp_scale_data[min(group, n_scale_data - 1)];
scale = (raw_exp * depth * 329) >> 15;
}
if (curve < 2) {
scale = -scale;
}
return scale;
}
int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth,
int left_curve, int right_curve) {
int offset = midinote - break_pt - 17;
if (offset >= 0) {
return ScaleCurve(offset / 3, right_depth, right_curve);
} else {
return ScaleCurve((-offset) / 3, left_depth, left_curve);
}
}
static const uint8_t pitchmodsenstab[] = {
0, 10, 20, 33, 55, 92, 153, 255
};
// PG: we need to find the real values
static const uint8_t ampmodsenstab[] = {
0, 33, 153, 255
};
void Dx7Note::init(const char patch[156], int midinote, int velocity) {
int rates[4];
int levels[4];
for (int op = 0; op < 6; op++) {
int off = op * 21;
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 16];
outlevel = Env::scaleoutlevel(outlevel);
#ifdef VERBOSE
for (int j = 8; j < 12; j++) {
cout << (int)patch[off + j] << " ";
}
#endif
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11], patch[off + 12]);
outlevel += level_scaling;
outlevel = min(127, outlevel);
#ifdef VERBOSE
cout << op << ": " << level_scaling << " " << outlevel << endl;
#endif
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 15]);
outlevel = max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 13]);
env_[op].init(rates, levels, outlevel, rate_scaling);
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
basepitch_[op] = freq;
// cout << op << " freq: " << freq << endl;
params_[op].phase = 0;
params_[op].gain[1] = 0;
ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3];
TRACE("operator set: %d %d", op, ampmodsens_[op]);
}
for (int i = 0; i < 4; i++) {
rates[i] = patch[126 + i];
levels[i] = patch[130 + i];
}
pitchenv_.set(rates, levels);
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
ampmoddepth_ = (patch[140] * 165) >> 6;
}
void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls) {
int32_t pitchmod = pitchenv_.getsample();
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
// TODO(PG) : make this integer friendly
uint32_t pwmd = (ctrls->values_[kControllerModWheel] * 0.7874) * (1 << 24);
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23));
uint32_t amd = ampmoddepth_ * lfo_delay; // Q32 :D
pitchmod += (((int64_t)pwmd) * (int64_t)senslfo) >> 39;
pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39;
int pitchbend = ctrls->values_[kControllerPitch];
int32_t pb = (pitchbend - 0x2000);
if ( pb != 0 ) {
if ( ctrls->values_[kControllerPitchStep] == 0 ) {
pb = ((float)(pb << 11)) * ((float)ctrls->values_[kControllerPitchRange]) / 12.0;
} else {
int stp = 12 / ctrls->values_[kControllerPitchStep];
pb = pb * stp / 8191;
pb = (pb * (8191/stp)) << 11;
}
}
pitchmod += pb;
for (int op = 0; op < 6; op++) {
params_[op].gain[0] = params_[op].gain[1];
//int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchmod);
int32_t level = env_[op].getsample();
if ( ampmodsens_[op] != 0 ) {
uint32_t sensamp = ampmodsens_[op] * (lfo_val - (1 << 23));
uint32_t amd_level = (((int64_t)amd) * (int64_t)sensamp) >> 40;
level -= amd_level;
}
int32_t gain = Exp2::lookup(level - (14 * (1 << 24)));
params_[op].gain[1] = gain;
}
core_.compute(buf, params_, algorithm_, fb_buf_, fb_shift_, ctrls);
}
void Dx7Note::keyup() {
for (int op = 0; op < 6; op++) {
env_[op].keydown(false);
pitchenv_.keydown(false);
}
}
void Dx7Note::update(const char patch[156], int midinote) {
for (int op = 0; op < 6; op++) {
int off = op * 21;
int mode = patch[off + 17];
int coarse = patch[off + 18];
int fine = patch[off + 19];
int detune = patch[off + 20];
basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune);
}
algorithm_ = patch[134];
int feedback = patch[135];
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
pitchmoddepth_ = (patch[139] * 165) >> 6;
pitchmodsens_ = pitchmodsenstab[patch[143] & 7];
}
void Dx7Note::peekVoiceStatus(VoiceStatus &status) {
for(int i=0;i<6;i++) {
status.amp[i] = params_[i].gain[1];
env_[i].getPosition(&status.ampStep[i]);
}
pitchenv_.getPosition(&status.pitchStep);
}

@ -1,73 +1,74 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SYNTH_DX7NOTE_H_
#define SYNTH_DX7NOTE_H_
// This is the logic to put together a note from the MIDI description
// and run the low-level modules.
// It will continue to evolve a bit, as note-stealing logic, scaling,
// and real-time control of parameters live here.
#include "env.h"
#include "pitchenv.h"
#include "fm_core.h"
struct VoiceStatus {
uint32_t amp[6];
char ampStep[6];
char pitchStep;
};
class Dx7Note {
public:
void init(const char patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls);
void keyup();
// TODO: parameter changes
// TODO: some way of indicating end-of-note. Maybe should be a return
// value from the compute method? (Having a count return from keyup
// is also tempting, but if there's a dynamic parameter change after
// keyup, that won't work.
// PG:add the update
void update(const char patch[156], int midinote);
void peekVoiceStatus(VoiceStatus &status);
private:
FmCore core_;
Env env_[6];
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2];
int32_t fb_shift_;
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
};
#endif // SYNTH_DX7NOTE_H_
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SYNTH_DX7NOTE_H_
#define SYNTH_DX7NOTE_H_
// This is the logic to put together a note from the MIDI description
// and run the low-level modules.
// It will continue to evolve a bit, as note-stealing logic, scaling,
// and real-time control of parameters live here.
#include "env.h"
#include "pitchenv.h"
#include "fm_core.h"
struct VoiceStatus {
uint32_t amp[6];
char ampStep[6];
char pitchStep;
};
class Dx7Note {
public:
void init(const char patch[156], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay,
const Controllers *ctrls);
void keyup();
// TODO: parameter changes
// TODO: some way of indicating end-of-note. Maybe should be a return
// value from the compute method? (Having a count return from keyup
// is also tempting, but if there's a dynamic parameter change after
// keyup, that won't work.
// PG:add the update
void update(const char patch[156], int midinote);
void peekVoiceStatus(VoiceStatus &status);
private:
FmCore core_;
Env env_[6];
FmOpParams params_[6];
PitchEnv pitchenv_;
int32_t basepitch_[6];
int32_t fb_buf_[2];
int32_t fb_shift_;
int32_t ampmodsens_[6];
int ampmoddepth_;
int algorithm_;
int pitchmoddepth_;
int pitchmodsens_;
};
#endif // SYNTH_DX7NOTE_H_

@ -1,151 +1,152 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef VERBOSE
#include <iostream>
#endif
#include "synth.h"
#include "fm_op_kernel.h"
#include "fm_core.h"
using namespace std;
struct FmOperatorInfo {
int in;
int out;
};
enum FmOperatorFlags {
OUT_BUS_ONE = 1 << 0,
OUT_BUS_TWO = 1 << 1,
OUT_BUS_ADD = 1 << 2,
IN_BUS_ONE = 1 << 4,
IN_BUS_TWO = 1 << 5,
FB_IN = 1 << 6,
FB_OUT = 1 << 7
};
struct FmAlgorithm {
int ops[6];
};
const FmAlgorithm algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0x41, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0x41, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
};
int n_out(const FmAlgorithm &alg) {
int count = 0;
for (int i = 0; i < 6; i++) {
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++;
}
return count;
}
void FmCore::dump() {
#ifdef VERBOSE
for (int i = 0; i < 32; i++) {
cout << (i + 1) << ":";
const FmAlgorithm &alg = algorithms[i];
for (int j = 0; j < 6; j++) {
int flags = alg.ops[j];
cout << " ";
if (flags & FB_IN) cout << "[";
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->";
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0");
if (flags & OUT_BUS_ADD) cout << "+";
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
if (flags & FB_OUT) cout << "]";
}
cout << " " << n_out(alg);
cout << endl;
}
#endif
}
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift) {
const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int inbus = (flags >> 4) & 3;
int outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain[0];
int32_t gain2 = param.gain[1];
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// todo: more than one op in a feedback loop
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl;
FmOpKernel::compute_fb(outptr, param.phase, param.freq,
gain1, gain2,
fb_buf, feedback_shift, add);
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
FmOpKernel::compute_pure(outptr, param.phase, param.freq,
gain1, gain2, add);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
FmOpKernel::compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef VERBOSE
#include <iostream>
#endif
#include "synth.h"
#include "fm_op_kernel.h"
#include "fm_core.h"
using namespace std;
struct FmOperatorInfo {
int in;
int out;
};
enum FmOperatorFlags {
OUT_BUS_ONE = 1 << 0,
OUT_BUS_TWO = 1 << 1,
OUT_BUS_ADD = 1 << 2,
IN_BUS_ONE = 1 << 4,
IN_BUS_TWO = 1 << 5,
FB_IN = 1 << 6,
FB_OUT = 1 << 7
};
struct FmAlgorithm {
int ops[6];
};
const FmAlgorithm algorithms[32] = {
{ { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1
{ { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2
{ { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3
{ { 0x41, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4
{ { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5
{ { 0x41, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6
{ { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7
{ { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8
{ { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9
{ { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10
{ { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11
{ { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12
{ { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13
{ { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14
{ { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15
{ { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16
{ { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17
{ { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18
{ { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20
{ { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21
{ { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22
{ { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23
{ { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24
{ { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25
{ { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26
{ { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27
{ { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28
{ { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29
{ { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30
{ { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31
{ { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32
};
int n_out(const FmAlgorithm &alg) {
int count = 0;
for (int i = 0; i < 6; i++) {
if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++;
}
return count;
}
void FmCore::dump() {
#ifdef VERBOSE
for (int i = 0; i < 32; i++) {
cout << (i + 1) << ":";
const FmAlgorithm &alg = algorithms[i];
for (int j = 0; j < 6; j++) {
int flags = alg.ops[j];
cout << " ";
if (flags & FB_IN) cout << "[";
cout << (flags & IN_BUS_ONE ? "1" : flags & IN_BUS_TWO ? "2" : "0") << "->";
cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0");
if (flags & OUT_BUS_ADD) cout << "+";
//cout << alg.ops[j].in << "->" << alg.ops[j].out;
if (flags & FB_OUT) cout << "]";
}
cout << " " << n_out(alg);
cout << endl;
}
#endif
}
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift, const Controllers *controllers) {
const int kLevelThresh = 1120;
const FmAlgorithm alg = algorithms[algorithm];
bool has_contents[3] = { true, false, false };
for (int op = 0; op < 6; op++) {
int flags = alg.ops[op];
bool add = (flags & OUT_BUS_ADD) != 0;
FmOpParams &param = params[op];
int inbus = (flags >> 4) & 3;
int outbus = flags & 3;
int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get();
int32_t gain1 = param.gain[0];
int32_t gain2 = param.gain[1];
if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) {
if (!has_contents[outbus]) {
add = false;
}
if (inbus == 0 || !has_contents[inbus]) {
// todo: more than one op in a feedback loop
if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) {
// cout << op << " fb " << inbus << outbus << add << endl;
FmOpKernel::compute_fb(outptr, param.phase, param.freq,
gain1, gain2,
fb_buf, feedback_shift, add, controllers);
} else {
// cout << op << " pure " << inbus << outbus << add << endl;
FmOpKernel::compute_pure(outptr, param.phase, param.freq,
gain1, gain2, add, controllers);
}
} else {
// cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl;
FmOpKernel::compute(outptr, buf_[inbus - 1].get(),
param.phase, param.freq, gain1, gain2, add, controllers);
}
has_contents[outbus] = true;
} else if (!add) {
has_contents[outbus] = false;
}
param.phase += param.freq << LG_N;
}
}

@ -1,38 +1,40 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __FM_CORE_H
#define __FM_CORE_H
#include "aligned_buf.h"
#include "synth.h"
struct FmOpParams {
int32_t gain[2];
int32_t freq;
int32_t phase;
};
class FmCore {
public:
static void dump();
void compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int32_t feedback_gain);
private:
AlignedBuf<int32_t, N>buf_[2];
};
#endif // __FM_CORE_H
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __FM_CORE_H
#define __FM_CORE_H
#include "aligned_buf.h"
#include "fm_op_kernel.h"
#include "synth.h"
#include "controllers.h"
struct FmOpParams {
int32_t gain[2];
int32_t freq;
int32_t phase;
};
class FmCore {
public:
static void dump();
void compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int32_t feedback_gain, const Controllers *controller);
private:
AlignedBuf<int32_t, N>buf_[2];
};
#endif // __FM_CORE_H

@ -1,274 +1,299 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <math.h>
#ifdef HAVE_NEON
#include <cpu-features.h>
#endif
#include "synth.h"
#include "sin.h"
#include "fm_op_kernel.h"
#ifdef HAVE_NEON
static bool hasNeon() {
return true;
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
}
extern "C"
void neon_fm_kernel(const int *in, const int *busin, int *out, int count,
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain);
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0};
#else
static bool hasNeon() {
return false;
}
#endif
void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(input, add ? output : zeros, output, N,
phase0, freq, gain, dgain);
#endif
} else {
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
output[i] += ((int64_t)y * (int64_t)gain) >> 24;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
output[i] = ((int64_t)y * (int64_t)gain) >> 24;
phase += freq;
}
}
}
}
#if 1
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(zeros, add ? output : zeros, output, N,
phase0, freq, gain, dgain);
#endif
} else {
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
output[i] += ((int64_t)y * (int64_t)gain) >> 24;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
output[i] = ((int64_t)y * (int64_t)gain) >> 24;
phase += freq;
}
}
}
}
#endif
#define noDOUBLE_ACCURACY
#define HIGH_ACCURACY
// Experimental sine wave generators below
#if 0
// Results: accuracy 64.3 mean, 170 worst case
// high accuracy: 5.0 mean, 49 worst case
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
u = ((int64_t)u * gain) >> 30;
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
v = ((int64_t)v * gain) >> 30;
int32_t s = Sin::compute10(freq << 6);
int32_t c = Sin::compute10((freq << 6) + (1 << 28));
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
v = ((int64_t)v * gain) >> 24;
int32_t s = Sin::compute(freq) << 6;
int32_t c = Sin::compute(freq + (1 << 22)) << 6;
#endif
for (int i = 0; i < N; i++) {
output[i] = u;
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30;
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30;
v = t;
}
}
#endif
#if 0
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
// for freq < 0.25, 275.2 mean, 716 worst
// high accuracy: 57.4 mean, 7559 worst
// freq < 0.25: 17.9 mean, 78 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5);
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5);
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5);
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
v = ((int64_t)v * gain) >> 24;
int32_t a = Sin::compute(freq >> 1) << 1;
#endif
for (int i = 0; i < N; i++) {
output[i] = u;
v -= ((int64_t)a * (int64_t)u) >> 24;
u += ((int64_t)a * (int64_t)v) >> 24;
}
}
#endif
#if 0
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef DOUBLE_ACCURACY
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5);
double a_d = sin(freq * (M_PI / (1 << 24)));
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) *
(M_PI / (1 << 23))) + 0.5);
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5);
#else
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5));
int32_t a = Sin::compute10(freq << 5);
v = ((int64_t)v * (int64_t)a) >> 29;
int32_t aa = ((int64_t)a * (int64_t)a) >> 29;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
int32_t a = Sin::compute(freq >> 1);
v = ((int64_t)v * (int64_t)a) >> 17;
int32_t aa = ((int64_t)a * (int64_t)a) >> 17;
#endif
#endif
if (aa < 0) aa = (1 << 31) - 1;
for (int i = 0; i < N; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
v -= ((int64_t)aa * (int64_t)u) >> 29;
u += v;
}
}
#endif
#if 0
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
// high accuracy 2.9 mean, 143 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t lastu = Sin::compute10((phase - freq) << 6);
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t lastu = Sin::compute(phase - freq) << 6;
int32_t a = Sin::compute(freq + (1 << 22)) << 7;
#endif
if (a < 0 && freq < 256) a = (1 << 31) - 1;
if (a > 0 && freq > 0x7fff00) a = -(1 << 31);
for (int i = 0; i < N; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
//output[i] = u;
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu;
lastu = u;
u = newu;
}
}
#endif
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase += freq;
}
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <math.h>
#include <cstdlib>
#ifdef HAVE_NEON
#include <cpu-features.h>
#endif
#include "synth.h"
#include "sin.h"
#include "fm_op_kernel.h"
uint32_t restdither() {
return rand() & 0x3FFF;
return 0;
}
#ifdef HAVE_NEONx
static bool hasNeon() {
return true;
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
}
extern "C"
void neon_fm_kernel(const int *in, const int *busin, int *out, int count,
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain);
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0};
#else
static bool hasNeon() {
return false;
}
#endif
//#define BIT8 0xFFFFFF00
void FmOpKernel::compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(input, add ? output : zeros, output, N,
phase0, freq, gain, dgain);
#endif
} else {
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase + input[i]);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
}
}
}
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
if (hasNeon()) {
#ifdef HAVE_NEON
neon_fm_kernel(zeros, add ? output : zeros, output, N,
phase0, freq, gain, dgain);
#endif
} else {
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y1;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t y = Sin::lookup(phase);
y &= controllers->sinBitFilter;
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y1;
phase += freq;
}
}
}
}
#define noDOUBLE_ACCURACY
#define HIGH_ACCURACY
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_shift, bool add, const Controllers *controllers) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
int32_t y0 = fb_buf[0];
int32_t y = fb_buf[1];
if (add) {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y &= controllers->sinBitFilter;
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] += y;
phase += freq;
}
} else {
for (int i = 0; i < N; i++) {
gain += dgain;
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1);
y0 = y;
y = Sin::lookup(phase + scaled_fb);
y &= controllers->sinBitFilter;
y = ((int64_t)y * (int64_t)gain) >> 24;
output[i] = y;
phase += freq;
}
}
fb_buf[0] = y0;
fb_buf[1] = y;
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Experimental sine wave generators below
#if 0
// Results: accuracy 64.3 mean, 170 worst case
// high accuracy: 5.0 mean, 49 worst case
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
u = ((int64_t)u * gain) >> 30;
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
v = ((int64_t)v * gain) >> 30;
int32_t s = Sin::compute10(freq << 6);
int32_t c = Sin::compute10((freq << 6) + (1 << 28));
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
v = ((int64_t)v * gain) >> 24;
int32_t s = Sin::compute(freq) << 6;
int32_t c = Sin::compute(freq + (1 << 22)) << 6;
#endif
for (int i = 0; i < N; i++) {
output[i] = u;
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30;
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30;
v = t;
}
}
#endif
#if 0
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
// for freq < 0.25, 275.2 mean, 716 worst
// high accuracy: 57.4 mean, 7559 worst
// freq < 0.25: 17.9 mean, 78 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5);
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5);
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5);
#else
int32_t u = Sin::compute(phase);
u = ((int64_t)u * gain) >> 24;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
v = ((int64_t)v * gain) >> 24;
int32_t a = Sin::compute(freq >> 1) << 1;
#endif
for (int i = 0; i < N; i++) {
output[i] = u;
v -= ((int64_t)a * (int64_t)u) >> 24;
u += ((int64_t)a * (int64_t)v) >> 24;
}
}
#endif
#if 0
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef DOUBLE_ACCURACY
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5);
double a_d = sin(freq * (M_PI / (1 << 24)));
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) *
(M_PI / (1 << 23))) + 0.5);
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5);
#else
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5));
int32_t a = Sin::compute10(freq << 5);
v = ((int64_t)v * (int64_t)a) >> 29;
int32_t aa = ((int64_t)a * (int64_t)a) >> 29;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1));
int32_t a = Sin::compute(freq >> 1);
v = ((int64_t)v * (int64_t)a) >> 17;
int32_t aa = ((int64_t)a * (int64_t)a) >> 17;
#endif
#endif
if (aa < 0) aa = (1 << 31) - 1;
for (int i = 0; i < N; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
v -= ((int64_t)aa * (int64_t)u) >> 29;
u += v;
}
}
#endif
#if 0
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
// high accuracy 2.9 mean, 143 worst
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add) {
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N;
int32_t gain = gain1;
int32_t phase = phase0;
#ifdef HIGH_ACCURACY
int32_t u = Sin::compute10(phase << 6);
int32_t lastu = Sin::compute10((phase - freq) << 6);
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1;
#else
int32_t u = Sin::compute(phase) << 6;
int32_t lastu = Sin::compute(phase - freq) << 6;
int32_t a = Sin::compute(freq + (1 << 22)) << 7;
#endif
if (a < 0 && freq < 256) a = (1 << 31) - 1;
if (a > 0 && freq > 0x7fff00) a = -(1 << 31);
for (int i = 0; i < N; i++) {
gain += dgain;
output[i] = ((int64_t)u * (int64_t)gain) >> 30;
//output[i] = u;
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu;
lastu = u;
u = newu;
}
}
#endif

@ -1,35 +1,42 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class FmOpKernel {
public:
// gain1 and gain2 represent linear step: gain for sample i is
// gain1 + (1 + i) / 64 * (gain2 - gain1)
// This is the basic FM operator. No feedback.
static void compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add);
// This is a sine generator, no feedback.
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add);
// One op with feedback, no add.
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_gain, bool add);
};
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __FM_OP_KERNEL_H
#define __FM_OP_KERNEL_H
#include "controllers.h"
class FmOpKernel {
public:
// gain1 and gain2 represent linear step: gain for sample i is
// gain1 + (1 + i) / 64 * (gain2 - gain1)
// This is the basic FM operator. No feedback.
static void compute(int32_t *output, const int32_t *input,
int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers);
// This is a sine generator, no feedback.
static void compute_pure(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2, bool add, const Controllers *controllers);
// One op with feedback, no add.
static void compute_fb(int32_t *output, int32_t phase0, int32_t freq,
int32_t gain1, int32_t gain2,
int32_t *fb_buf, int fb_gain, bool add, const Controllers *controllers);
};
#endif

Loading…
Cancel
Save