Initial commit of C++ synth codebase

bklimt
Raph Levien 13 years ago
parent 72190eccf2
commit a99ac7a38c
  1. 20
      cpp/README
  2. 38
      cpp/src/SynthApp.gyp
  3. 306
      cpp/src/SynthApp.xcodeproj/project.pbxproj
  4. 2
      cpp/src/SynthApp/English.lproj/InfoPlist.strings
  5. 3617
      cpp/src/SynthApp/English.lproj/MainMenu.xib
  6. 32
      cpp/src/SynthApp/Synth-Info.plist
  7. 28
      cpp/src/SynthApp/SynthAppDelegate.h
  8. 49
      cpp/src/SynthApp/SynthAppDelegate.mm
  9. 7
      cpp/src/SynthApp/SynthApp_Prefix.pch
  10. 41
      cpp/src/SynthApp/SynthMain.h
  11. 183
      cpp/src/SynthApp/SynthMain.mm
  12. 22
      cpp/src/SynthApp/main.m
  13. 63
      cpp/src/SynthApp/midi_in_mac.cc
  14. 46
      cpp/src/SynthApp/midi_in_mac.h
  15. 23
      cpp/src/core.gyp
  16. 193
      cpp/src/core.xcodeproj/project.pbxproj
  17. 177
      cpp/src/dx7note.cc
  18. 59
      cpp/src/dx7note.h
  19. 99
      cpp/src/env.cc
  20. 62
      cpp/src/env.h
  21. 127
      cpp/src/fm_core.cc
  22. 35
      cpp/src/fm_core.h
  23. 241
      cpp/src/fm_op_kernel.cc
  24. 35
      cpp/src/fm_op_kernel.h
  25. 58
      cpp/src/freqlut.cc
  26. 21
      cpp/src/freqlut.h
  27. 238
      cpp/src/main.cc
  28. 17
      cpp/src/main.gyp
  29. 25
      cpp/src/module.h
  30. 66
      cpp/src/resofilter.cc
  31. 27
      cpp/src/resofilter.h
  32. 71
      cpp/src/ringbuffer.cc
  33. 41
      cpp/src/ringbuffer.h
  34. 192
      cpp/src/sawtooth.cc
  35. 27
      cpp/src/sawtooth.h
  36. 142
      cpp/src/sin.cc
  37. 62
      cpp/src/sin.h
  38. 42
      cpp/src/synth.h
  39. 159
      cpp/src/synth_unit.cc
  40. 46
      cpp/src/synth_unit.h
  41. 70
      cpp/src/test_ringbuffer.cc
  42. 75
      cpp/src/wavout.cc
  43. 29
      cpp/src/wavout.h

@ -0,0 +1,20 @@
README for C++ codebase
The C++ codebase will eventually be the primary sound generation module
for this app. It's still experimental, and not yet wired up to the Android
parts, but can be used to make sound. The best way is to use the simple
test app for the Mac.
To build, edit src/SynthApp/SynthMain.mm to change the path to ROM1A.SYX
to the actual path. These patches can be downloaded from:
http://www.abdn.ac.uk/~mth192/dx7/dx7patch.zip
Also change the "KeyRig 49" string to match the actual USB name of your
MIDI controller. Then "open src/SynthApp.xcodeproj", then do "Build and
Run".
The File Open menu command is hooked up as well and will load SYX format
DX7 patch files (32 patches per file). Send program change midi events
with the first 32 program numbers.

@ -0,0 +1,38 @@
{
'targets': [
{
'target_name': 'SynthApp',
'type': 'executable',
'mac_bundle': 1,
'include_dirs': ['.'],
'sources': [
'SynthApp/main.m',
'SynthApp/midi_in_mac.cc',
'SynthApp/SynthAppDelegate.mm',
'SynthApp/SynthApp_Prefix.pch',
'SynthApp/SynthMain.mm',
],
'dependencies': [
'core.gyp:core',
],
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/AudioToolbox.framework',
'$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework',
'$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
'$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreAudio.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
'$(SDKROOT)/System/Library/Frameworks/CoreMIDI.framework',
],
},
'xcode_settings': {
'INFOPLIST_FILE': 'SynthApp/Synth-Info.plist',
},
'mac_bundle_resources': [
'SynthApp/English.lproj/InfoPlist.strings',
'SynthApp/English.lproj/MainMenu.xib',
],
},
],
}

@ -0,0 +1,306 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
070048C57FCB5F31827D61CD /* midi_in_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9CC658C4805259001BB2BA70 /* midi_in_mac.cc */; };
444FC1A8A4F4BFBB71A55578 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 106567A2B2FDE65A2547B9C0 /* InfoPlist.strings */; };
4EC6E0A88AFE158936E7DF22 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3EDCE57490308092B70255E /* CoreFoundation.framework */; };
6004825C9C1BA9246F032431 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D3126A377165245C44B6C8A /* CoreAudio.framework */; };
6824545AE8F4DAFB95F6CDDB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FAB723D6CDE6B962FBA69894 /* main.m */; };
752469DD01013D889966B42C /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F664EAAFF41058799553650 /* AudioUnit.framework */; };
7E84D499B82B6C02EB8AA96C /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D651559601BF84BEA261778D /* MainMenu.xib */; };
7F9707F48C04E2AE1EB5C866 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4618975BF19F21FC2A429474 /* Cocoa.framework */; };
9366D617A2CE978AEA3555B3 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BACEAAFABBABE1D12902220B /* libcore.a */; };
982E3B747974F63D8B4482B2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9DC34125BEDD68ABCAB055D /* AudioToolbox.framework */; };
98F8F0314510BD0F6AA320CE /* SynthMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFA13CCA8E0B32555D59A9B3 /* SynthMain.mm */; };
B6BA9FF151AD9A0D309C0D53 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1680278C574FAEFF7657992 /* CoreMIDI.framework */; };
C5E74E9A85CDC29EB3DCCBF7 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0638CA1C453E326E762DA4BE /* Carbon.framework */; };
D6CAF0C6797908C34B18E987 /* SynthAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 22758A226C30B8A0C2E095B2 /* SynthAppDelegate.mm */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0113EA7215BA932B68889274 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B5F1A11C57F7DE82D8F9F9C2 /* core.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 5A9991BB6607533745115226;
remoteInfo = core;
};
8733BC1F6527748907794EA9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B5F1A11C57F7DE82D8F9F9C2 /* core.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8B1FC9FF853D5C32F4771091;
remoteInfo = core;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0638CA1C453E326E762DA4BE /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
22758A226C30B8A0C2E095B2 /* SynthAppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SynthAppDelegate.mm; sourceTree = "<group>"; };
2D3126A377165245C44B6C8A /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
3C854865960DE58DBB62E200 /* SynthApp_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = text; path = SynthApp_Prefix.pch; sourceTree = "<group>"; };
4618975BF19F21FC2A429474 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
47E3EC1A3593850470FB7E33 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
645192D86D7D39B0CAB2E0FC /* SynthApp.gyp */ = {isa = PBXFileReference; lastKnownFileType = text; path = SynthApp.gyp; sourceTree = "<group>"; };
6F664EAAFF41058799553650 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
9CC658C4805259001BB2BA70 /* midi_in_mac.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = midi_in_mac.cc; sourceTree = "<group>"; };
AC00D3CE197617EB5BC7110C /* English */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
AFA13CCA8E0B32555D59A9B3 /* SynthMain.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SynthMain.mm; sourceTree = "<group>"; };
B1680278C574FAEFF7657992 /* CoreMIDI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = System/Library/Frameworks/CoreMIDI.framework; sourceTree = SDKROOT; };
B5F1A11C57F7DE82D8F9F9C2 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = core.xcodeproj; sourceTree = SOURCE_ROOT; };
C3EDCE57490308092B70255E /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
E4B4F7598D96CC2CE29666B4 /* SynthApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SynthApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
F9DC34125BEDD68ABCAB055D /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
FAB723D6CDE6B962FBA69894 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
E0443A39F1982DD2A0D4C8E6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9366D617A2CE978AEA3555B3 /* libcore.a in Frameworks */,
982E3B747974F63D8B4482B2 /* AudioToolbox.framework in Frameworks */,
752469DD01013D889966B42C /* AudioUnit.framework in Frameworks */,
C5E74E9A85CDC29EB3DCCBF7 /* Carbon.framework in Frameworks */,
7F9707F48C04E2AE1EB5C866 /* Cocoa.framework in Frameworks */,
6004825C9C1BA9246F032431 /* CoreAudio.framework in Frameworks */,
4EC6E0A88AFE158936E7DF22 /* CoreFoundation.framework in Frameworks */,
B6BA9FF151AD9A0D309C0D53 /* CoreMIDI.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08D7B67857704EB61BBC1112 /* Frameworks */ = {
isa = PBXGroup;
children = (
F9DC34125BEDD68ABCAB055D /* AudioToolbox.framework */,
6F664EAAFF41058799553650 /* AudioUnit.framework */,
0638CA1C453E326E762DA4BE /* Carbon.framework */,
4618975BF19F21FC2A429474 /* Cocoa.framework */,
2D3126A377165245C44B6C8A /* CoreAudio.framework */,
C3EDCE57490308092B70255E /* CoreFoundation.framework */,
B1680278C574FAEFF7657992 /* CoreMIDI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
26BC9EF25519FDFECC66EBA5 /* Products */ = {
isa = PBXGroup;
children = (
E4B4F7598D96CC2CE29666B4 /* SynthApp.app */,
);
name = Products;
sourceTree = "<group>";
};
56669AEF9C728CE587C3E8A3 = {
isa = PBXGroup;
children = (
C611344839FF3B4608474E37 /* Source */,
710624B4F3ADB1F020942DB0 /* Projects */,
08D7B67857704EB61BBC1112 /* Frameworks */,
26BC9EF25519FDFECC66EBA5 /* Products */,
FF670D1787E80284877493D1 /* Build */,
);
sourceTree = "<group>";
};
710624B4F3ADB1F020942DB0 /* Projects */ = {
isa = PBXGroup;
children = (
B5F1A11C57F7DE82D8F9F9C2 /* core.xcodeproj */,
);
name = Projects;
sourceTree = "<group>";
};
C611344839FF3B4608474E37 /* Source */ = {
isa = PBXGroup;
children = (
106567A2B2FDE65A2547B9C0 /* InfoPlist.strings */,
D651559601BF84BEA261778D /* MainMenu.xib */,
22758A226C30B8A0C2E095B2 /* SynthAppDelegate.mm */,
3C854865960DE58DBB62E200 /* SynthApp_Prefix.pch */,
AFA13CCA8E0B32555D59A9B3 /* SynthMain.mm */,
FAB723D6CDE6B962FBA69894 /* main.m */,
9CC658C4805259001BB2BA70 /* midi_in_mac.cc */,
);
name = Source;
path = SynthApp;
sourceTree = "<group>";
};
D9149B7557AF29282FBD3555 /* Products */ = {
isa = PBXGroup;
children = (
BACEAAFABBABE1D12902220B /* libcore.a */,
);
name = Products;
sourceTree = "<group>";
};
FF670D1787E80284877493D1 /* Build */ = {
isa = PBXGroup;
children = (
645192D86D7D39B0CAB2E0FC /* SynthApp.gyp */,
);
name = Build;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
C05003BAF5004F3B18F94002 /* SynthApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = DB5E551BF701B14CD4C2B627 /* Build configuration list for PBXNativeTarget "SynthApp" */;
buildPhases = (
11D527F4085F0D823076B687 /* Resources */,
BCACCEFB309FFDB59EE9B9A1 /* Sources */,
E0443A39F1982DD2A0D4C8E6 /* Frameworks */,
);
buildRules = (
);
dependencies = (
27F0280BCDE369FB81181FD4 /* PBXTargetDependency */,
);
name = SynthApp;
productName = SynthApp;
productReference = E4B4F7598D96CC2CE29666B4 /* SynthApp.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
F6DFBBC1A435D3962598EE50 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
};
buildConfigurationList = F2D5F35CD2FF169326ADC8E8 /* Build configuration list for PBXProject "SynthApp" */;
compatibilityVersion = "Xcode 3.2";
hasScannedForEncodings = 1;
mainGroup = 56669AEF9C728CE587C3E8A3;
projectDirPath = "";
projectReferences = (
{
ProductGroup = D9149B7557AF29282FBD3555 /* Products */;
ProjectRef = B5F1A11C57F7DE82D8F9F9C2 /* core.xcodeproj */;
},
);
projectRoot = "";
targets = (
C05003BAF5004F3B18F94002 /* SynthApp */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
BACEAAFABBABE1D12902220B /* libcore.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libcore.a;
remoteRef = 8733BC1F6527748907794EA9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
11D527F4085F0D823076B687 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
444FC1A8A4F4BFBB71A55578 /* InfoPlist.strings in Resources */,
7E84D499B82B6C02EB8AA96C /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
BCACCEFB309FFDB59EE9B9A1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6824545AE8F4DAFB95F6CDDB /* main.m in Sources */,
070048C57FCB5F31827D61CD /* midi_in_mac.cc in Sources */,
D6CAF0C6797908C34B18E987 /* SynthAppDelegate.mm in Sources */,
98F8F0314510BD0F6AA320CE /* SynthMain.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
27F0280BCDE369FB81181FD4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = core;
targetProxy = 0113EA7215BA932B68889274 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
106567A2B2FDE65A2547B9C0 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
AC00D3CE197617EB5BC7110C /* English */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
D651559601BF84BEA261778D /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
47E3EC1A3593850470FB7E33 /* English */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
200985946FE77FF1BE0AA81C /* Default */ = {
isa = XCBuildConfiguration;
buildSettings = {
INTERMEDIATE_DIR = "$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)";
SHARED_INTERMEDIATE_DIR = "$(SYMROOT)/DerivedSources/$(CONFIGURATION)";
};
name = Default;
};
F637FD3979A20A160561D04A /* Default */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = .;
INFOPLIST_FILE = "SynthApp/Synth-Info.plist";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks";
PRODUCT_NAME = SynthApp;
WRAPPER_PREFIX = "";
};
name = Default;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DB5E551BF701B14CD4C2B627 /* Build configuration list for PBXNativeTarget "SynthApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F637FD3979A20A160561D04A /* Default */,
);
defaultConfigurationIsVisible = 1;
defaultConfigurationName = Default;
};
F2D5F35CD2FF169326ADC8E8 /* Build configuration list for PBXProject "SynthApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
200985946FE77FF1BE0AA81C /* Default */,
);
defaultConfigurationIsVisible = 1;
defaultConfigurationName = Default;
};
/* End XCConfigurationList section */
};
rootObject = F6DFBBC1A435D3962598EE50 /* Project object */;
}

@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,32 @@
<?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 version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

@ -0,0 +1,28 @@
/*
* 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.
*/
#import <Cocoa/Cocoa.h>
#import <SynthMain.h>
@interface SynthAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
SynthMain synthMain;
}
@property (assign) IBOutlet NSWindow *window;
@end

@ -0,0 +1,49 @@
/*
* 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.
*/
#import "SynthMain.h"
#import "SynthAppDelegate.h"
@implementation SynthAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
synthMain.SynthInit();
}
- (void)openDocument:(id)sender {
NSLog(@"openDocument");
NSOpenPanel *openPanel;
openPanel = [NSOpenPanel openPanel];
if (NSOKButton == [openPanel runModal]) {
NSArray *selectedPaths = [openPanel filenames];
NSEnumerator *enumerator = [selectedPaths objectEnumerator];
NSString *currentPath;
while (nil != (currentPath = [enumerator nextObject])) {
const char *filename = [currentPath UTF8String];
synthMain.Load(filename);
}
}
}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
NSLog(@"open");
}
@end

@ -0,0 +1,7 @@
//
// Prefix header for all source files of the 'Empty' target in the 'Empty' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif

@ -0,0 +1,41 @@
/*
* 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_SYNTH_MAIN_H
#define __SYNTH_SYNTH_MAIN_H
#include <AudioUnit/AudioUnit.h>
#include "synth.h"
#include "midi_in_mac.h"
#include "synth_unit.h"
class SynthMain {
public:
int SynthInit();
int SynthDone();
int Load(const char *filename);
private:
OSStatus setupplayback(SynthUnit *synth_unit);
OSStatus startplayback();
OSStatus stopplayback();
AudioUnit audioUnit_;
MidiInMac midi_in_mac_;
RingBuffer ring_buffer_;
SynthUnit *synth_unit_;
};
#endif // __SYNTH_SYNTH_MAIN_H

@ -0,0 +1,183 @@
/*
* 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 "SynthMain.h"
#include <iostream>
#include <fstream>
#include <cstdlib>
#import <Carbon/Carbon.h>
#import <AudioToolbox/AudioToolbox.h>
#import "synth.h"
#import "module.h"
#import "freqlut.h"
#import "sin.h"
#import "sawtooth.h"
using namespace ::std;
OSStatus audiocallback(void *data,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
//cout << "callback!" << inNumberFrames << endl;
SynthUnit *synth_unit = (SynthUnit *)data;
SInt16 *buffer = (SInt16 *)ioData->mBuffers[0].mData;
synth_unit->GetSamples(inNumberFrames, buffer);
return noErr;
}
// Set up audio playback for Mac
OSStatus SynthMain::setupplayback(SynthUnit *synth_unit) {
Component component;
ComponentDescription description;
OSStatus err = noErr;
AURenderCallbackStruct callback;
description.componentType = kAudioUnitType_Output;
description.componentSubType = kAudioUnitSubType_HALOutput;
description.componentManufacturer = kAudioUnitManufacturer_Apple;
description.componentFlags = 0;
description.componentFlagsMask = 0;
if (component = FindNextComponent(NULL, &description)) {
err = OpenAComponent(component, &audioUnit_);
if (err != noErr) return err;
}
UInt32 param = 0;
err = AudioUnitSetProperty(audioUnit_, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &param, sizeof(param));
if (err != noErr) return err;
param = 1;
err = AudioUnitSetProperty(audioUnit_, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, 0, &param, sizeof(param));
if (err != noErr) return err;
AudioDeviceID deviceId = kAudioObjectUnknown;
UInt32 deviceIdSize = sizeof(deviceId);
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDefaultOutputDevice, // mSelector
kAudioObjectPropertyScopeGlobal, // mScope
kAudioObjectPropertyElementMaster // mElement
};
err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&propertyAddress,
0,
NULL,
&deviceIdSize,
&deviceId);
if (err != noErr) return err;
err = AudioUnitSetProperty(audioUnit_, kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global, 0, &deviceId, sizeof(deviceId));
if (err != noErr) return err;
callback.inputProc = audiocallback;
callback.inputProcRefCon = synth_unit;
err = AudioUnitSetProperty(audioUnit_, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &callback, sizeof(callback));
if (err != noErr) return err;
AudioStreamBasicDescription deviceFormat;
deviceFormat.mChannelsPerFrame = 1;
deviceFormat.mSampleRate = 44100.0;
deviceFormat.mFormatID = kAudioFormatLinearPCM;
deviceFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger |
kAudioFormatFlagIsPacked;
deviceFormat.mBytesPerFrame = 2;
deviceFormat.mBitsPerChannel = deviceFormat.mBytesPerFrame * 8;
deviceFormat.mFramesPerPacket = 1;
deviceFormat.mBytesPerPacket = deviceFormat.mBytesPerFrame;
err = AudioUnitSetProperty(audioUnit_, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &deviceFormat, sizeof(deviceFormat));
if (err != noErr) return err;
err = AudioUnitInitialize(audioUnit_);
if (err != noErr) return err;
}
OSStatus SynthMain::startplayback() {
return AudioOutputUnitStart(audioUnit_);
}
OSStatus SynthMain::stopplayback() {
return AudioOutputUnitStop(audioUnit_);
}
int SynthMain::Load(const char *filename) {
uint8_t syx_data[4104];
ifstream fp_in;
fp_in.open(filename, ifstream::in);
if (fp_in.fail()) {
std::cerr << "error opening file" << std::endl;
return 1;
}
fp_in.read((char *)syx_data, 4104);
if (fp_in.fail()) {
std::cerr << "error reading file" << std::endl;
return 1;
}
ring_buffer_.Write(syx_data, 4104);
#if 0
const uint8_t *data = syx_data + 6 + 128 * patch_num;
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 17; j++) {
if (j == 4 || j == 8) std::cout << " |";
std::cout << " " << (int)data[i * 17 + j];
}
std::cout << std::endl;
}
for (int j = 102; j < 118; j++) {
if (j == 106 || j == 110) std::cout << " |";
std::cout << " " << (int)data[j];
}
std::cout << std::endl;
#endif
}
int SynthMain::SynthInit() {
double sample_rate = 44100.0;
Freqlut::init(sample_rate);
Sawtooth::init(sample_rate);
Sin::init();
synth_unit_ = new SynthUnit(&ring_buffer_);
if (true) {
const char *fn = "/Users/raph/dx7/ROM1A.SYX";
Load(fn);
}
OSStatus err = setupplayback(synth_unit_);
if (err != noErr) {
cout << err << endl;
return 1;
}
midi_in_mac_.Init(CFSTR("KeyRig 49"), &ring_buffer_);
startplayback();
return 0;
}
int SynthMain::SynthDone() {
midi_in_mac_.Done();
stopplayback();
delete synth_unit_;
return 0;
}

@ -0,0 +1,22 @@
/*
* 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.
*/
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **) argv);
}

@ -0,0 +1,63 @@
/*
* 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 <iostream>
#include "midi_in_mac.h"
void MidiInMac::OnRead(const MIDIPacketList *pktlist) {
const MIDIPacket *packet = &(pktlist->packet[0]);
for (int i = 0; i < pktlist->numPackets; ++i) {
ring_buffer_->Write(packet->data, packet->length);
packet = MIDIPacketNext(packet);
}
}
extern "C" void ReadProc(const MIDIPacketList *pktlist, void *readProcRefCon,
void *srcConnRefCon) {
MidiInMac *self = (MidiInMac *)readProcRefCon;
self->OnRead(pktlist);
}
bool MidiInMac::Init(CFStringRef name, RingBuffer *ring_buffer) {
ring_buffer_ = ring_buffer;
OSStatus s = MIDIClientCreate(CFSTR("synth"), NULL, NULL, &client_);
if (s != noErr) return false;
s = MIDIInputPortCreate(client_, CFSTR("synthin"), ReadProc, (void *)this,
&port_);
ItemCount n = MIDIGetNumberOfDevices();
for (int i = 0; i < n; ++i) {
MIDIDeviceRef device_ref = MIDIGetDevice(i);
CFPropertyListRef midi_device_properties;
MIDIObjectGetProperties(device_ref, &midi_device_properties, true);
CFStringRef dev_name = NULL;
s = MIDIObjectGetStringProperty(device_ref, kMIDIPropertyName, &dev_name);
CFComparisonResult comparison = CFStringCompare(dev_name, name, 0);
CFRelease(dev_name);
if (comparison == kCFCompareEqualTo) {
std::cout << "found!" << std::endl;
MIDIEntityRef entity = MIDIDeviceGetEntity(device_ref, 0);
MIDIEndpointRef endpoint_ = MIDIEntityGetSource(entity, 0);
s = MIDIPortConnectSource(port_, endpoint_, NULL);
return true;
}
}
return false;
}
void MidiInMac::Done() {
MIDIEndpointDispose(endpoint_);
}

@ -0,0 +1,46 @@
/*
* 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.
*/
// Interface for receiving MIDI events
#include <CoreMIDI/CoreMIDI.h>
#include "ringbuffer.h"
// At some point, we may want to have a generic MidiIn interface that gets
// implemented differently on different platforms, but for now we keep it
// simple (threading and lifetime might be different on other platforms,
// so it's not obvious what the interface might look like).
class MidiInMac {
public:
//MidiInMac();
// Return true on success. While running (ie until Done() is called,
// MIDI bytes from the device are written to the ring buffer.
bool Init(CFStringRef name, RingBuffer *ring_buffer);
void Done();
// Effectively private - only called from ReadProc
void OnRead(const MIDIPacketList *pktlist);
private:
RingBuffer *ring_buffer_;
MIDIClientRef client_;
MIDIPortRef port_;
MIDIEndpointRef endpoint_;
};

@ -0,0 +1,23 @@
{
'targets': [
{
'target_name': 'core',
'type': 'static_library',
'sources': [
'dx7note.cc',
'env.cc',
'fm_core.cc',
'fm_op_kernel.cc',
'freqlut.cc',
'resofilter.cc',
'ringbuffer.cc',
'sawtooth.cc',
'sin.cc',
'synth_unit.cc',
'test_ringbuffer.cc',
],
'include_dirs': ['.'],
},
],
}

@ -0,0 +1,193 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
1DBA85B761236957DF00CF7B /* fm_core.cc in Sources */ = {isa = PBXBuildFile; fileRef = DA7AAEE2AD874001F6B71D52 /* fm_core.cc */; };
2FB5AF9855596821669CAE1F /* dx7note.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA00975977E2704F74104728 /* dx7note.cc */; };
36D08A0FEA591FC0EF188469 /* env.cc in Sources */ = {isa = PBXBuildFile; fileRef = B73D485E55EBD9CD5950A375 /* env.cc */; };
4FB7FFF436D333023EC91E2C /* sin.cc in Sources */ = {isa = PBXBuildFile; fileRef = D1D8B6FB01C9E7E2D99378F0 /* sin.cc */; };
5FA224E159A6B07188657BA8 /* freqlut.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FF02D488CE6FD5017D7D81A /* freqlut.cc */; };
71911D7305F0175DC4A3FF17 /* sawtooth.cc in Sources */ = {isa = PBXBuildFile; fileRef = 48B6535400CF3AC8BABB3299 /* sawtooth.cc */; };
80D3C6DC6F5236826B6AB404 /* test_ringbuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 521793C71CAA078F5598EFC0 /* test_ringbuffer.cc */; };
8D41064389CC8FD281956BF7 /* fm_op_kernel.cc in Sources */ = {isa = PBXBuildFile; fileRef = 509D811344DB98984FD6C126 /* fm_op_kernel.cc */; };
908EAB1EE59231C41FD88BCD /* ringbuffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 68FD17910F296961541A67E4 /* ringbuffer.cc */; };
A16F70FD02394895C6FA7326 /* synth_unit.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2082841A11DF6E62596265CF /* synth_unit.cc */; };
D5ECD09EBEB1684C00616248 /* resofilter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 97A5CBACD479212282D0BFD6 /* resofilter.cc */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2082841A11DF6E62596265CF /* synth_unit.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = synth_unit.cc; sourceTree = "<group>"; };
48B6535400CF3AC8BABB3299 /* sawtooth.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sawtooth.cc; sourceTree = "<group>"; };
509D811344DB98984FD6C126 /* fm_op_kernel.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fm_op_kernel.cc; sourceTree = "<group>"; };
521793C71CAA078F5598EFC0 /* test_ringbuffer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_ringbuffer.cc; sourceTree = "<group>"; };
68FD17910F296961541A67E4 /* ringbuffer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ringbuffer.cc; sourceTree = "<group>"; };
6C1B71FBACD041F20E7F828A /* core.gyp */ = {isa = PBXFileReference; lastKnownFileType = text; path = core.gyp; sourceTree = "<group>"; };
8B1FC9FF853D5C32F4771091 /* libcore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcore.a; sourceTree = BUILT_PRODUCTS_DIR; };
97A5CBACD479212282D0BFD6 /* resofilter.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = resofilter.cc; sourceTree = "<group>"; };
9FF02D488CE6FD5017D7D81A /* freqlut.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = freqlut.cc; sourceTree = "<group>"; };
B73D485E55EBD9CD5950A375 /* env.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = env.cc; sourceTree = "<group>"; };
BA00975977E2704F74104728 /* dx7note.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dx7note.cc; sourceTree = "<group>"; };
D1D8B6FB01C9E7E2D99378F0 /* sin.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sin.cc; sourceTree = "<group>"; };
DA7AAEE2AD874001F6B71D52 /* fm_core.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fm_core.cc; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
59A50EE4C8CF7BE6875289EF /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1CE02724939AB46300B24440 = {
isa = PBXGroup;
children = (
521E615727EAD5BFC2BFE8C3 /* Source */,
ADABDC1FC75C07B5469F92DE /* Products */,
72A5A7469C1AF9FDDAB23BD1 /* Build */,
);
sourceTree = "<group>";
};
521E615727EAD5BFC2BFE8C3 /* Source */ = {
isa = PBXGroup;
children = (
BA00975977E2704F74104728 /* dx7note.cc */,
B73D485E55EBD9CD5950A375 /* env.cc */,
DA7AAEE2AD874001F6B71D52 /* fm_core.cc */,
509D811344DB98984FD6C126 /* fm_op_kernel.cc */,
9FF02D488CE6FD5017D7D81A /* freqlut.cc */,
97A5CBACD479212282D0BFD6 /* resofilter.cc */,
68FD17910F296961541A67E4 /* ringbuffer.cc */,
48B6535400CF3AC8BABB3299 /* sawtooth.cc */,
D1D8B6FB01C9E7E2D99378F0 /* sin.cc */,
2082841A11DF6E62596265CF /* synth_unit.cc */,
521793C71CAA078F5598EFC0 /* test_ringbuffer.cc */,
);
name = Source;
sourceTree = "<group>";
};
72A5A7469C1AF9FDDAB23BD1 /* Build */ = {
isa = PBXGroup;
children = (
6C1B71FBACD041F20E7F828A /* core.gyp */,
);
name = Build;
sourceTree = "<group>";
};
ADABDC1FC75C07B5469F92DE /* Products */ = {
isa = PBXGroup;
children = (
8B1FC9FF853D5C32F4771091 /* libcore.a */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
5A9991BB6607533745115226 /* core */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6B3D26CA3D8A77B9BBF035FF /* Build configuration list for PBXNativeTarget "core" */;
buildPhases = (
CE1A34D2C5345E1B084CD2DB /* Sources */,
59A50EE4C8CF7BE6875289EF /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = core;
productName = core;
productReference = 8B1FC9FF853D5C32F4771091 /* libcore.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
64E0BDEAB5AF6CD7ECF6A5F6 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
};
buildConfigurationList = 2F684B8427DC8F5001D9A5B0 /* Build configuration list for PBXProject "core" */;
compatibilityVersion = "Xcode 3.2";
hasScannedForEncodings = 1;
mainGroup = 1CE02724939AB46300B24440;
projectDirPath = "";
projectRoot = "";
targets = (
5A9991BB6607533745115226 /* core */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
CE1A34D2C5345E1B084CD2DB /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2FB5AF9855596821669CAE1F /* dx7note.cc in Sources */,
36D08A0FEA591FC0EF188469 /* env.cc in Sources */,
1DBA85B761236957DF00CF7B /* fm_core.cc in Sources */,
8D41064389CC8FD281956BF7 /* fm_op_kernel.cc in Sources */,
5FA224E159A6B07188657BA8 /* freqlut.cc in Sources */,
D5ECD09EBEB1684C00616248 /* resofilter.cc in Sources */,
908EAB1EE59231C41FD88BCD /* ringbuffer.cc in Sources */,
71911D7305F0175DC4A3FF17 /* sawtooth.cc in Sources */,
4FB7FFF436D333023EC91E2C /* sin.cc in Sources */,
A16F70FD02394895C6FA7326 /* synth_unit.cc in Sources */,
80D3C6DC6F5236826B6AB404 /* test_ringbuffer.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
0B978C26C6EBEB8479A59BA7 /* Default */ = {
isa = XCBuildConfiguration;
buildSettings = {
INTERMEDIATE_DIR = "$(PROJECT_DERIVED_FILE_DIR)/$(CONFIGURATION)";
SHARED_INTERMEDIATE_DIR = "$(SYMROOT)/DerivedSources/$(CONFIGURATION)";
};
name = Default;
};
5A3F0CBEC18C4B4A90EF08F4 /* Default */ = {
isa = XCBuildConfiguration;
buildSettings = {
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = .;
PRODUCT_NAME = core;
};
name = Default;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2F684B8427DC8F5001D9A5B0 /* Build configuration list for PBXProject "core" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0B978C26C6EBEB8479A59BA7 /* Default */,
);
defaultConfigurationIsVisible = 1;
defaultConfigurationName = Default;
};
6B3D26CA3D8A77B9BBF035FF /* Build configuration list for PBXNativeTarget "core" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5A3F0CBEC18C4B4A90EF08F4 /* Default */,
);
defaultConfigurationIsVisible = 1;
defaultConfigurationName = Default;
};
/* End XCConfigurationList section */
};
rootObject = 64E0BDEAB5AF6CD7ECF6A5F6 /* Project object */;
}

@ -0,0 +1,177 @@
/*
* 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 <iostream>
#include <math.h>
#include "synth.h"
#include "freqlut.h"
#include "dx7note.h"
using namespace std;
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);
}
// TODO: detune
} else {
// ((1 << 24) * log(10) / log(2) * .01) << 3
logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3;
// TODO: detune
}
int32_t base_freq = Freqlut::lookup(logfreq);
return base_freq;
}
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 = std::max(0, std::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 = std::min(31, std::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[std::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);
}
}
// Considering making this an init method...
Dx7Note::Dx7Note(const char patch[128], int midinote, int velocity) {
for (int op = 0; op < 6; op++) {
int off = op * 17;
int rates[4];
int levels[4];
for (int i = 0; i < 4; i++) {
rates[i] = patch[off + i];
levels[i] = patch[off + 4 + i];
}
int outlevel = patch[off + 14];
for (int j = 8; j < 12; j++) {
cout << (int)patch[off + j] << " ";
}
int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9],
patch[off + 10], patch[off + 11] & 3, patch[off + 11] >> 2);
outlevel += level_scaling;
outlevel = std::min(99, outlevel);
cout << op << ": " << level_scaling << " " << outlevel << endl;
outlevel = outlevel << 5;
outlevel += ScaleVelocity(velocity, patch[off + 13] >> 2);
outlevel = std::max(0, outlevel);
int rate_scaling = ScaleRate(midinote, patch[off + 12] & 7);
env_[op].init(rates, levels, outlevel, rate_scaling);
int mode = patch[off + 15] & 1;
int coarse = patch[off + 15] >> 1;
int fine = patch[off + 16];
int detune = (patch[off + 12] >> 3) - 7;
int32_t freq = osc_freq(midinote, mode, coarse, fine, detune);
params_[op].freq = freq;
// cout << op << " freq: " << freq << endl;
params_[op].phase = 0;
params_[op].gain[1] = 0;
}
algorithm_ = patch[110];
int feedback = patch[111] & 7;
fb_shift_ = feedback != 0 ? 8 - feedback : 16;
}
void Dx7Note::compute(int32_t *buf) {
for (int op = 0; op < 6; op++) {
params_[op].gain[0] = params_[op].gain[1];
int32_t level = env_[op].getsample();
// TODO: replace pow with faster calculation
int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24)));
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);
}
}

@ -0,0 +1,59 @@
/*
* 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 "fm_core.h"
class Dx7Note {
public:
// Interesting question: should the setup be in the constructor, or should
// there be an init method? The latter would make it easier to use a fixed
// pool of note objects.
Dx7Note(const char patch[128], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's
// worth it...
void compute(int32_t *buf);
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.
private:
FmCore core_;
Env env_[6];
FmOpParams params_[6];
int32_t fb_buf_[2];
int32_t fb_shift_;
int algorithm_;
};
#endif // SYNTH_DX7NOTE_H_

@ -0,0 +1,99 @@
/*
* 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 <iostream>
#include "synth.h"
#include "env.h"
using namespace std;
void Env::init(const int r[4], const int l[4], int32_t ol, int rate_scaling) {
for (int i = 0; i < 4; i++) {
rates_[i] = r[i];
levels_[i] = l[i];
}
outlevel_ = ol;
rate_scaling_ = rate_scaling;
level_ = 0;
down_ = true;
advance(0);
}
int32_t Env::getsample() {
if (ix_ < 3 || (ix_ < 4) && !down_) {
if (rising_) {
const int jumptarget = 1716;
if (level_ < (jumptarget << 16)) {
level_ = jumptarget << 16;
}
level_ += (((17 << 24) - level_) >> 24) * inc_;
// TODO: should probably be more accurate when inc is large
if (level_ >= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
} else { // !rising
level_ -= inc_;
if (level_ <= targetlevel_) {
level_ = targetlevel_;
advance(ix_ + 1);
}
}
}
// TODO: this would be a good place to set level to 0 when under threshold
return level_;
}
void Env::keydown(bool d) {
if (down_ != d) {
down_ = d;
advance(d ? 0 : 3);
}
}
void Env::setparam(int param, int value) {
if (param < 4) {
rates_[param] = value;
} else if (param < 8) {
levels_[param - 4] = value;
}
// Unknown parameter, ignore for now
}
const int levellut[] = {
0, 2, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22
};
void Env::advance(int newix) {
ix_ = newix;
if (ix_ < 4) {
int newlevel = levels_[ix_];
int actuallevel = newlevel >= 19 ?
14 + (newlevel >> 1) : levellut[newlevel];
actuallevel = (actuallevel << 6) + outlevel_ - 3360;
actuallevel = actuallevel < 16 ? 16 : actuallevel;
// level here is same as Java impl
targetlevel_ = actuallevel << 16;
rising_ = (targetlevel_ > level_);
// rate
int qrate = (rates_[ix_] * 41) >> 6;
qrate += rate_scaling_;
qrate = std::min(qrate, 63);
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2));
}
}

@ -0,0 +1,62 @@
/*
* 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 __ENV_H
#define __ENV_H
// DX7 envelope generation
class Env {
public:
// The rates and levels arrays are calibrated to match the Dx7 parameters
// (ie, value 0..99). The outlevel parameter is calibrated in microsteps
// (ie units of approx .023 dB), with 99 * 32 = nominal full scale. The
// rate_scaling parameter is in qRate units (ie 0..63).
void init(const int rates[4], const int levels[4], int outlevel,
int rate_scaling);
// Result is in Q24/doubling log format. Also, result is subsampled
// for every N samples.
// A couple more things need to happen for this to be used as a gain
// value. First, the # of outputs scaling needs to be applied. Also,
// modulation.
// Then, of course, log to linear.
int32_t getsample();
void keydown(bool down);
void setparam(int param, int value);
private:
int rates_[4];
int levels_[4];
int outlevel_;
int rate_scaling_;
// Level is stored so that 2^24 is one doubling, ie 16 more bits than
// the DX7 itself (fraction is stored in level rather than separate
// counter)
int32_t level_;
int targetlevel_;
bool rising_;
int ix_;
int inc_;
bool down_;
void advance(int newix);
};
#endif // __ENV_H

@ -0,0 +1,127 @@
#include <iostream>
#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() {
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;
}
}
void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm,
int32_t *fb_buf, int feedback_shift) {
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];
int32_t gain1 = param.gain[0];
int32_t gain2 = param.gain[1];
if (gain1 != 0 || gain2 != 0) {
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], param.phase, param.freq,
gain1, gain2, add);
}
has_contents[outbus] = true;
}
param.phase += param.freq << LG_N;
}
}

@ -0,0 +1,35 @@
/*
* 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
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:
int32_t buf_[2][N];
};
#endif // __FM_CORE_H

@ -0,0 +1,241 @@
/*
* 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 <iostream>
using namespace std;
#include "synth.h"
#include "sin.h"
#include "fm_op_kernel.h"
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 (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 (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;
}

@ -0,0 +1,35 @@
/*
* 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);
};

@ -0,0 +1,58 @@
/*
* 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.
*/
// Resolve frequency signal (1.0 in Q24 format = 1 octave) to phase delta.
// The LUT is just a global, and we'll need the init function to be called before
// use.
#include <iostream> // for debugging
#include <stdint.h>
#include <math.h>
using namespace std;
#include "freqlut.h"
#define LG_N_SAMPLES 10
#define N_SAMPLES (1 << LG_N_SAMPLES)
#define SAMPLE_SHIFT (24 - LG_N_SAMPLES)
#define MAX_LOGFREQ_INT 20
int32_t lut[N_SAMPLES + 1];
void Freqlut::init(double sample_rate) {
double y = (1LL << (24 + MAX_LOGFREQ_INT)) / sample_rate;
double inc = pow(2, 1.0 / N_SAMPLES);
for (int i = 0; i < N_SAMPLES + 1; i++) {
lut[i] = (int32_t)floor(y + 0.5);
y *= inc;
}
}
// Note: if logfreq is more than 20.0, the results will be inaccurate. However,
// that will be many times the Nyquist rate.
int32_t Freqlut::lookup(int32_t logfreq) {
int ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT;
int32_t y0 = lut[ix];
int32_t y1 = lut[ix + 1];
int lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1);
int32_t y = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> SAMPLE_SHIFT);
int hibits = logfreq >> 24;
return y >> (MAX_LOGFREQ_INT - hibits);
}

@ -0,0 +1,21 @@
/*
* 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 Freqlut {
public:
static void init(double sample_rate);
static int32_t lookup(int32_t logfreq);
};

@ -0,0 +1,238 @@
/*
* 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 <iostream>
#include <cstdlib>
#include <math.h>
#include "synth.h"
#include "module.h"
#include "freqlut.h"
#include "wavout.h"
#include "sawtooth.h"
#include "sin.h"
#include "resofilter.h"
#include "fm_core.h"
#include "fm_op_kernel.h"
#include "env.h"
#include "dx7note.h"
using namespace std;
void benchmark_sin() {
int32_t x;
for (int j = 0; j < 1000; j++) {
for (int i = 0; i < 1000000; i++) {
x += Sin::lookup(i);
}
}
cout << x << endl; // to make sure it gets used
}
void benchmark_fm_op() {
int32_t buf[64];
int32_t fb_buf[2];
for (int i = 0; i < 15625000; i++)
FmOpKernel::compute_fb(buf, 0, 123456, 1 << 24, 1 << 24,
fb_buf, 1, false);
}
void test_sin_accuracy() {
double maxerr = 0;
for (int i = 0; i < 1000000; i++) {
int32_t phase = rand() & ((1 << 24) - 1);
int32_t y = Sin::compute(phase);
double yd = (1 << 24) * sin(phase * (M_PI / (1 << 23)));
double err = fabs(y - yd);
if (err > maxerr) maxerr = err;
}
cout << "Max error: " << maxerr;
}
void test_pure_accuracy() {
int32_t worstfreq;
int32_t worstphase;
int32_t worsterr = 0;
double errsum = 0;
for (int i = 0; i < 1000000; i++) {
int32_t freq = rand() & 0x7fffff;
int32_t phase = rand() & 0xffffff;
int32_t gain = 1 << 24;
int32_t buf[64];
FmOpKernel::compute_pure(buf, phase, freq, gain, gain, false);
int32_t maxerr = 0;
for (int j = 0; j < 64; j++) {
double y = gain * sin((phase + j * freq) * (2.0 * M_PI / (1 << 24)));
int32_t accurate = (int32_t)floor(y + 0.5);
int32_t err = abs(buf[j] - accurate);
if (err > maxerr) maxerr = err;
}
errsum += maxerr;
if (maxerr > worsterr) {
worsterr = maxerr;
worstfreq = freq;
worstphase = phase;
}
if (i < 10)
cout << phase << " " << freq << " " << maxerr << endl;
}
cout << worstphase << " " << worstfreq << " " << worsterr << endl;
cout << "Mean: " << (errsum * 1e-6) << endl;
}
void mksaw(double sample_rate) {
const int n_samples = 400 * 1024;
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
Sawtooth s;
int32_t control_last[1];
int32_t control[1];
ResoFilter rf;
int32_t fc_last[2];
int32_t fc[2];
fc[0] = 0; // TODO
fc[1] = 4.2 * (1 << 24);
fc_last[0] = fc[0];
fc_last[1] = fc[1];
double ramp = 1e-7;
double f = ramp * (64 + 1);
control[0] = (1 << 24) * log(f * sample_rate) / log(2);
int32_t buf[64];
int32_t buf2[64];
int32_t *bufs[1];
int32_t *bufs2[1];
bufs[0] = buf;
bufs2[0] = buf2;
int32_t phase = 0;
for (int i = 0; i < n_samples; i += 64) {
double f = ramp * (i + 64 + 1);
// f = 44.0 / sample_rate;
control_last[0] = control[0];
control[0] = (1 << 24) * log(f * sample_rate) / log(2);
fc_last[1] = fc[1];
fc[1] = 4.0 * i * (1 << 24) / n_samples;
s.process((const int32_t **)0, control, control_last, bufs);
rf.process((const int32_t **)bufs, fc, fc_last, bufs2);
for (int j = 0; j < 64; j++) {
buf2[j] = buf[j] >> 1;
//phase += 100000;
//buf2[j] = (Sin::compute(phase) - (int32_t)((1<< 24) * sin(phase * 2 * M_PI / (1 << 24)))) << 12;
}
w.write_data(buf2, 64);
}
w.close();
}
void mknote(double sample_rate) {
const int n_samples = 400 * 1024;
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
int32_t freq = 150358;
int32_t phase = 0;
int rates[4] = {70, 50, 30, 80};
int levels[4] = {99, 90, 70, 0};
Env e;
e.init(rates, levels, 99, 0);
int rates2[4] = {70, 50, 30, 80};
int levels2[4] = {99, 90, 70, 0};
Env e2;
e2.init(rates, levels, 90, 0);
int32_t buf[64];
int32_t gain1, gain2;
gain2 = 0;
int32_t gain21, gain22;
gain22 = 0;
for (int i = 0; i < n_samples; i += N) {
gain1 = gain2;
gain21 = gain22;
if (i == n_samples / 2) {
e.keydown(false);
e2.keydown(false);
}
int32_t level = e.getsample();
gain2 = (1<<8) * pow(2, level * (1.0 / (1 << 24)));
FmOpKernel::compute_pure(buf, phase, freq, gain1, gain2, false);
level = e2.getsample();
gain22 = (1<<8) * pow(2, level * (1.0 / (1 << 24)));
FmOpKernel::compute(buf, buf, phase, freq, gain21, gain22, false);
phase += freq << LG_N;
w.write_data(buf, N);
}
w.close();
}
char epiano[] = {
95, 29, 20, 50, 99, 95, 0, 0, 41, 0, 19, 0, 115, 24, 79, 2, 0, 95, 20, 20,
50, 99, 95, 0, 0, 0, 0, 0, 0, 3, 0, 99, 2, 0, 95, 29, 20, 50, 99, 95, 0, 0,
0, 0, 0, 0, 59, 24, 89, 2, 0, 95, 20, 20, 50, 99, 95, 0, 0, 0, 0, 0, 0, 59,
8, 99, 2, 0, 95, 50, 35, 78, 99, 75, 0, 0, 0, 0, 0, 0, 59, 28, 58, 28, 0, 96,
25, 25, 67, 99, 75, 0, 0, 0, 0, 0, 0, 83, 8, 99, 2, 0, 94, 67, 95, 60, 50,
50, 50, 50, 4, 6, 34, 33, 0, 0, 56, 24, 69, 46, 80, 73, 65, 78, 79, 32, 49,
32
};
void mkdx7note(double sample_rate) {
const int n_samples = 400 * 1024;
WavOut w("/tmp/foo.wav", sample_rate, n_samples);
Dx7Note note(epiano, 57, 64);
int32_t buf[N];
for (int i = 0; i < n_samples; i += N) {
for (int j = 0; j < N; j++) {
buf[j] = 0;
}
if (i == n_samples / 2) {
note.keyup();
}
note.compute(buf);
for (int j = 0; j < N; j++) {
buf[j] >>= 2;
}
w.write_data(buf, N);
}
w.close();
}
void test_ringbuffer();
int main(int argc, char **argv) {
double sample_rate = 44100.0;
Freqlut::init(sample_rate);
Sawtooth::init(sample_rate);
Sin::init();
//FmCore::dump();
//test_sin_accuracy();
//benchmark_fm_op();
//test_pure_accuracy();
//benchmark_sin();
//int32_t freq = atoi(argv[1]);
//cout << "Logfreq(" << freq << ") = " << Freqlut::lookup(freq) << endl;
//mkdx7note(sample_rate);
mksaw(sample_rate);
//test_ringbuffer();
return 0;
}

@ -0,0 +1,17 @@
{
'targets': [
{
'target_name': 'main',
'type': 'executable',
'sources': [
'main.cc',
'wavout.cc',
],
'dependencies': [
'core.gyp:core',
],
'include_dirs': ['.'],
},
],
}

@ -0,0 +1,25 @@
/*
* 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 <stdint.h>
class Module {
public:
static const int lg_n = 6;
static const int n = 1 << lg_n;
virtual void process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs) = 0;
};

@ -0,0 +1,66 @@
/*
* 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 "module.h"
#include "resofilter.h"
double this_sample_rate;
void ResoFilter::init(double sample_rate) {
this_sample_rate = sample_rate;
}
ResoFilter::ResoFilter() {
for (int i = 0; i < 4; i++) {
x[i] = 0;
}
}
int32_t compute_alpha(int32_t logf) {
// TODO
return 1 << 21;
}
void ResoFilter::process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs) {
int32_t alpha = compute_alpha(control_last[0]);
int32_t alpha_in = compute_alpha(control_in[0]);
int32_t delta_alpha = (alpha_in - alpha) >> lg_n;
int32_t k = control_last[1];
int32_t k_in = control_in[1];
int32_t delta_k = (k_in - k) >> lg_n;
const int32_t *ibuf = inbufs[0];
int32_t *obuf = outbufs[0];
int x0 = x[0];
int x1 = x[1];
int x2 = x[2];
int x3 = x[3];
for (int i = 0; i < n; i++) {
alpha += delta_alpha;
k += delta_k;
int32_t signal = ibuf[i];
int32_t fb = ((int64_t)k * (int64_t)x3) >> 24;
x0 = x0 + ((((int64_t)(signal - fb - x0) * (int64_t)alpha)) >> 24);
x1 = x1 + ((((int64_t)(x0 - x1) * (int64_t)alpha)) >> 24);
x2 = x2 + ((((int64_t)(x1 - x2) * (int64_t)alpha)) >> 24);
x3 = x3 + ((((int64_t)(x2 - x3) * (int64_t)alpha)) >> 24);
obuf[i] = x3;
}
x[0] = x0;
x[1] = x1;
x[2] = x2;
x[3] = x3;
}

@ -0,0 +1,27 @@
/*
* 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 ResoFilter : Module {
public:
ResoFilter();
static void init(double sample_rate);
static int32_t lookup(int32_t phase, int32_t log_f);
void process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs);
private:
int32_t x[4];
};

@ -0,0 +1,71 @@
/*
* 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 <time.h>
#include <string.h>
#include <algorithm>
#include "synth.h"
#include "ringbuffer.h"
RingBuffer::RingBuffer() {
rd_ix_ = 0;
wr_ix_ = 0;
}
int RingBuffer::BytesAvailable() {
return (wr_ix_ - rd_ix_) & (kBufSize - 1);
}
int RingBuffer::Read(int size, uint8_t *bytes) {
int rd_ix = rd_ix_;
SynthMemoryBarrier(); // read barrier, make sure data is committed before ix
int fragment_size = std::min(size, kBufSize - rd_ix);
memcpy(bytes, buf_ + rd_ix, fragment_size);
if (size > fragment_size) {
memcpy(bytes + fragment_size, buf_, size - fragment_size);
}
SynthMemoryBarrier(); // full barrier, make sure read commits before updating
rd_ix_ = (rd_ix + size) & (kBufSize - 1);
return size;
}
int RingBuffer::Write(const uint8_t *bytes, int size) {
int remaining = size;
while (remaining > 0) {
int rd_ix = rd_ix_;
int wr_ix = wr_ix_;
int space_available = (rd_ix - wr_ix - 1) & (kBufSize - 1);
if (space_available == 0) {
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 1000000;
nanosleep(&sleepTime, NULL);
} else {
int wr_size = std::min(remaining, space_available);
int fragment_size = std::min(wr_size, kBufSize - wr_ix);
memcpy(buf_ + wr_ix, bytes, fragment_size);
if (wr_size > fragment_size) {
memcpy(buf_, bytes + fragment_size, wr_size - fragment_size);
}
SynthMemoryBarrier(); // write barrier, make sure data commits
wr_ix_ = (wr_ix + wr_size) & (kBufSize - 1);
remaining -= wr_size;
bytes += wr_size;
}
}
}

@ -0,0 +1,41 @@
/*
* 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_RINGBUFFER_H_
#define SYNTH_RINGBUFFER_H_
class RingBuffer {
public:
RingBuffer();
// Returns number of bytes available for reading.
int BytesAvailable();
// Reads bytes. It is the caller's responsibility to make sure that
// size <= a previous value of BytesAvailable().
int Read(int size, uint8_t *bytes);
// Writes bytes into the buffer. If the buffer is full, the method will
// block until space is available.
int Write(const uint8_t *bytes, int size);
private:
static const int kBufSize = 8192;
uint8_t buf_[kBufSize];
volatile int rd_ix_;
volatile int wr_ix_;
};
#endif // SYNTH_RINGBUFFER_H_

@ -0,0 +1,192 @@
/*
* 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 <iostream>
using namespace std;
#include "module.h"
#include "sawtooth.h"
#include "freqlut.h"
// There's a fair amount of lookup table and so on that needs to be set before
// generating any signal. In Java, this would be done by a separate factory class.
// Here, we're just going to do it as globals.
#define HIGH_QUALITY
#define noLOW_FREQ_HACK
#define FANCY_GOERTZEL_SIN
#define noPRINT_ERROR
#define R (1 << 29)
#ifdef LOW_FREQ_HACK
#define LG_N_SAMPLES 9
#else
#define LG_N_SAMPLES 11
#endif
#define N_SAMPLES (1 << LG_N_SAMPLES)
#define N_PARTIALS_MAX (N_SAMPLES / 2)
#define LG_SLICES_PER_OCTAVE 2
#define SLICES_PER_OCTAVE (1 << LG_SLICES_PER_OCTAVE)
#define SLICE_SHIFT (24 - LG_SLICES_PER_OCTAVE)
#define LG_N_SLICES (LG_SLICES_PER_OCTAVE + 4)
#define N_SLICES (1 << LG_N_SLICES)
#define NEG2OVERPI -0.63661977236758138
int32_t sawtooth[N_SLICES][N_SAMPLES];
void Sawtooth::init(double sample_rate) {
int32_t lut[N_SAMPLES / 2];
for (int i = 0; i < N_SAMPLES / 2; i++) {
lut[i] = 0;
}
double slice_inc = pow(2.0, 1.0 / SLICES_PER_OCTAVE);
double f_0 = pow(slice_inc, N_SLICES - 1);
int n_partials_last = 0;
for (int j = N_SLICES - 1; j >= 0; j--) {
int n_partials = floor(0.5 * sample_rate / f_0);
n_partials = n_partials < N_PARTIALS_MAX ? n_partials : N_PARTIALS_MAX;
for (int k = n_partials_last + 1; k <= n_partials; k++) {
double scale = NEG2OVERPI / k;
scale = (N_PARTIALS_MAX - k) > (N_PARTIALS_MAX >> 2) ? scale :
scale * (N_PARTIALS_MAX - k) / (N_PARTIALS_MAX >> 2);
double dphase = k * 2 * M_PI / N_SAMPLES;
#ifdef PRINT_ERROR
int32_t maxerr = 0;
#endif
#ifdef FANCY_GOERTZEL_SIN
double ds_d = (1 << 30) * scale * sin(dphase);
double cm2_d = (1 << 29) * (2 * (cos(dphase) - 1));
int dshift = 0;
for (dshift = 0; dshift < 16; dshift++) {
if (ds_d < -(1 << (30 - dshift))) break;
if (cm2_d < -(1 << (30 - dshift))) break;
}
int32_t ds = (int32_t)floor((1 << dshift) * ds_d + 0.5);
int32_t cm2 = (int32_t)floor((1 << dshift) * cm2_d + 0.5);
// cout << cm2_d << " " << cm2 << " " << dphase << " " << ds << " " << dshift << endl;
int32_t s = 0;
int32_t round = (1 << dshift) >> 1;
for (int i = 0; i < N_SAMPLES / 2; i++) {
lut[i] += s;
#ifdef PRINT_ERROR
int32_t good = (int32_t)floor(scale * sin(dphase * i) * (1 << 30) + 0.5);
int err = s - good;
int abs_err = err > 0 ? err : -err;
maxerr = abs_err > maxerr ? abs_err : maxerr;
#endif
ds += ((int64_t)cm2 * (int64_t)s + R) >> 29;
s += (ds + round) >> dshift;
}
#else
int32_t c = (int32_t)floor(cos(dphase) * (1 << 30) + 0.5);
int32_t s = (int32_t)floor(sin(dphase) * (1 << 30) + 0.5);
int32_t u = (int32_t)floor(scale * (1 << 30));
int32_t v = 0;
for (int i = 0; i < N_SAMPLES / 2; i++) {
lut[i] += v;
#ifdef PRINT_ERROR
int32_t good = (int32_t)floor(scale * sin(dphase * i) * (1 << 30) + 0.5);
int err = v - good;
int abs_err = err > 0 ? err : -err;
maxerr = abs_err > maxerr ? abs_err : maxerr;
#endif
int32_t t = ((int64_t)u * (int64_t)s + (int64_t)v * (int64_t)c + R) >> 30;
u = ((int64_t)u * (int64_t)c - (int64_t)v * (int64_t)s + R) >> 30;
v = t;
}
#endif
#ifdef PRINT_ERROR
cout << maxerr << endl;
#endif
}
sawtooth[j][0] = 0;
sawtooth[j][N_SAMPLES / 2] = 0;
for (int i = 1; i < N_SAMPLES / 2; i++) {
int32_t value = (lut[i] + 32) >> 6;
sawtooth[j][i] = value;
sawtooth[j][N_SAMPLES - i] = -value;
}
n_partials_last = n_partials;
f_0 *= 1.0 / slice_inc;
}
}
Sawtooth::Sawtooth() {
phase = 0;
}
int32_t Sawtooth::lookup(int32_t phase, int32_t log_f) {
log_f = log_f < 0 ? 0 : log_f;
int slice = (log_f + (1 << SLICE_SHIFT) - 1) >> SLICE_SHIFT;
int phase_int = (phase >> (24 - LG_N_SAMPLES)) & (N_SAMPLES - 1);
int lowbits = phase & ((1 << (24 - LG_N_SAMPLES)) - 1);
int y0 = sawtooth[slice][phase_int];
int y1 = sawtooth[slice][(phase_int + 1) & (N_SAMPLES - 1)];
int y4 = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> (24 - LG_N_SAMPLES));
// TODO: lift this out of loop
// TODO: optimal threshold probably depends on sample rate
#ifdef LOW_FREQ_HACK
if (log_f < (8 << 24))
y4 = phase * 2 - (1 << 24);
#endif
#ifdef HIGH_QUALITY
int y2 = sawtooth[slice + 1][phase_int];
int y3 = sawtooth[slice + 1][(phase_int + 1) & (N_SAMPLES - 1)];
int y5 = y2 + ((((int64_t)(y3 - y2) * (int64_t)lowbits)) >> (24 - LG_N_SAMPLES));
#ifdef LOW_FREQ_HACK
if (log_f < (8 << 24) - (1 << SLICE_SHIFT))
y5 = phase * 2 - (1 << 24);
#endif
int slice_lowbits = log_f & ((1 << SLICE_SHIFT) - 1);
int y = y4 + ((((int64_t)(y5 - y4) * (int64_t)slice_lowbits)) >> SLICE_SHIFT);
return y;
#else
return y4;
#endif
}
void Sawtooth::process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs) {
int32_t logf = control_last[0];
int32_t logf_in = control_in[0];
int32_t *obuf = outbufs[0];
int32_t delta_logf = (logf_in - logf) >> lg_n;
int f = Freqlut::lookup(logf);
int f_in = Freqlut::lookup(logf_in);
int32_t delta_f = (f_in - f) >> lg_n;
int32_t p = phase;
for (int i = 0; i < n; i++) {
f += delta_f;
logf += delta_logf;
obuf[i] = lookup(p, logf);
p += f;
p &= (1 << 24) - 1;
}
phase = p;
}

@ -0,0 +1,27 @@
/*
* 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 Sawtooth : Module {
public:
Sawtooth();
static void init(double sample_rate);
static int32_t lookup(int32_t phase, int32_t log_f);
void process(const int32_t **inbufs, const int32_t *control_in,
const int32_t *control_last, int32_t **outbufs);
private:
int32_t phase;
};

@ -0,0 +1,142 @@
/*
* 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 <iostream>
using namespace std;
#include "sin.h"
#define R (1 << 29)
#ifdef SIN_DELTA
int32_t sintab[SIN_N_SAMPLES << 1];
#else
int32_t sintab[SIN_N_SAMPLES + 1];
#endif
void Sin::init() {
double dphase = 2 * M_PI / SIN_N_SAMPLES;
int32_t c = (int32_t)floor(cos(dphase) * (1 << 30) + 0.5);
int32_t s = (int32_t)floor(sin(dphase) * (1 << 30) + 0.5);
int32_t u = 1 << 30;
int32_t v = 0;
for (int i = 0; i < SIN_N_SAMPLES / 2; i++) {
#ifdef SIN_DELTA
sintab[(i << 1) + 1] = (v + 32) >> 6;
sintab[((i + SIN_N_SAMPLES / 2) << 1) + 1] = -((v + 32) >> 6);
#else
sintab[i] = (v + 32) >> 6;
sintab[i + SIN_N_SAMPLES / 2] = -((v + 32) >> 6);
#endif
int32_t t = ((int64_t)u * (int64_t)s + (int64_t)v * (int64_t)c + R) >> 30;
u = ((int64_t)u * (int64_t)c - (int64_t)v * (int64_t)s + R) >> 30;
v = t;
}
#ifdef SIN_DELTA
for (int i = 0; i < SIN_N_SAMPLES - 1; i++) {
sintab[i << 1] = sintab[(i << 1) + 3] - sintab[(i << 1) + 1];
}
sintab[(SIN_N_SAMPLES << 1) - 2] = -sintab[(SIN_N_SAMPLES << 1) - 1];
#else
sintab[SIN_N_SAMPLES] = 0;
#endif
}
#ifndef SIN_INLINE
int32_t Sin::lookup(int32_t phase) {
const int SHIFT = 24 - SIN_LG_N_SAMPLES;
int lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int dy = sintab[phase_int];
int y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int y0 = sintab[phase_int];
int y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif
}
#endif
#if 0
// The following is an implementation designed not to use any lookup tables,
// based on the following implementation by Basile Graf:
// http://www.rossbencina.com/static/code/sinusoids/even_polynomial_sin_approximation.txt
#define C0 (1 << 24)
#define C1 (331121857 >> 2)
#define C2 (1084885537 >> 4)
#define C3 (1310449902 >> 6)
int32_t Sin::compute(int32_t phase) {
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 22;
int32_t x4 = ((int64_t)x2 * (int64_t)x2) >> 24;
int32_t x6 = ((int64_t)x2 * (int64_t)x4) >> 24;
int32_t y = C0 -
(((int64_t)C1 * (int64_t)x2) >> 24) +
(((int64_t)C2 * (int64_t)x4) >> 24) -
(((int64_t)C3 * (int64_t)x6) >> 24);
y ^= -((phase >> 23) & 1);
return y;
}
#endif
#if 1
// coefficients are Chebyshev polynomial, computed by compute_cos_poly.py
#define C8_0 16777216
#define C8_2 -331168742
#define C8_4 1089453524
#define C8_6 -1430910663
#define C8_8 950108533
int32_t Sin::compute(int32_t phase) {
int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 16;
int32_t y = (((((((((((((int64_t)C8_8
* (int64_t)x2) >> 32) + C8_6)
* (int64_t)x2) >> 32) + C8_4)
* (int64_t)x2) >> 32) + C8_2)
* (int64_t)x2) >> 32) + C8_0);
y ^= -((phase >> 23) & 1);
return y;
}
#endif
#define C10_0 (1 << 30)
#define C10_2 -1324675874 // scaled * 4
#define C10_4 1089501821
#define C10_6 -1433689867
#define C10_8 1009356886
#define C10_10 -421101352
int32_t Sin::compute10(int32_t phase) {
int32_t x = (phase & ((1 << 29) - 1)) - (1 << 28);
int32_t x2 = ((int64_t)x * (int64_t)x) >> 26;
int32_t y = ((((((((((((((((int64_t)C10_10
* (int64_t)x2) >> 34) + C10_8)
* (int64_t)x2) >> 34) + C10_6)
* (int64_t)x2) >> 34) + C10_4)
* (int64_t)x2) >> 32) + C10_2)
* (int64_t)x2) >> 30) + C10_0);
y ^= -((phase >> 29) & 1);
return y;
}

@ -0,0 +1,62 @@
/*
* 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 Sin {
public:
Sin();
static void init();
static int32_t lookup(int32_t phase);
static int32_t compute(int32_t phase);
// A more accurate sine, both input and output Q30
static int32_t compute10(int32_t phase);
};
#define SIN_LG_N_SAMPLES 10
#define SIN_N_SAMPLES (1 << SIN_LG_N_SAMPLES)
#define SIN_INLINE
// Use twice as much RAM for the LUT but avoid a little computation
#define SIN_DELTA
#ifdef SIN_DELTA
extern int32_t sintab[SIN_N_SAMPLES << 1];
#else
extern int32_t sintab[SIN_N_SAMPLES + 1];
#endif
#ifdef SIN_INLINE
inline
int32_t Sin::lookup(int32_t phase) {
const int SHIFT = 24 - SIN_LG_N_SAMPLES;
int lowbits = phase & ((1 << SHIFT) - 1);
#ifdef SIN_DELTA
int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1);
int dy = sintab[phase_int];
int y0 = sintab[phase_int + 1];
return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT);
#else
int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1);
int y0 = sintab[phase_int];
int y1 = sintab[phase_int + 1];
return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT);
#endif
}
#endif

@ -0,0 +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.
*/
#ifndef __SYNTH_H
#define __SYNTH_H
// This may not be present on MSVC.
// See http://stackoverflow.com/questions/126279/c99-stdint-h-header-and-ms-visual-studio
#include <stdint.h>
#define LG_N 6
#define N (1 << LG_N)
#if defined(__APPLE__)
#include <libkern/OSAtomic.h>
#define SynthMemoryBarrier() OSMemoryBarrier()
#elif defined(__GNUC__)
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
#define SynthMemoryBarrier __sync_synchronize()
#else
#warning Memory barrier is not enabled
#endif
#warning Memory barrier is not enabled
#endif
// #undef SynthMemoryBarrier()
// #define SynthMemoryBarrier()
#endif // __SYNTH_H

@ -0,0 +1,159 @@
/*
* 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 <iostream>
#include "synth.h"
#include "synth_unit.h"
char epiano[] = {
95, 29, 20, 50, 99, 95, 0, 0, 41, 0, 19, 0, 115, 24, 79, 2, 0,
95, 20, 20, 50, 99, 95, 0, 0, 0, 0, 0, 0, 3, 0, 99, 2, 0,
95, 29, 20, 50, 99, 95, 0, 0, 0, 0, 0, 0, 59, 24, 89, 2, 0,
95, 20, 20, 50, 99, 95, 0, 0, 0, 0, 0, 0, 59, 8, 99, 2, 0,
95, 50, 35, 78, 99, 75, 0, 0, 0, 0, 0, 0, 59, 28, 58, 28, 0,
96, 25, 25, 67, 99, 75, 0, 0, 0, 0, 0, 0, 83, 8, 99, 2, 0,
94, 67, 95, 60, 50, 50, 50, 50, 4, 6, 34, 33, 0, 0, 56, 24,
69, 46, 80, 73, 65, 78, 79, 32, 49, 32
};
SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
ring_buffer_ = ring_buffer;
for (int note = 0; note < max_active_notes; ++note) {
active_note_[note].dx7_note = NULL;
}
input_buffer_index_ = 0;
std::memcpy(patch_data_, epiano, sizeof(epiano));
current_patch_ = 0;
}
// Transfer as many bytes as possible from ring buffer to input buffer.
// Note that this implementation has a fair amount of copying - we'd probably
// do it a bit differently if it were bulk data, but in this case we're
// optimizing for simplicity of implementation.
void SynthUnit::TransferInput() {
size_t bytes_available = ring_buffer_->BytesAvailable();
int bytes_to_read = std::min(bytes_available,
sizeof(input_buffer_) - input_buffer_index_);
if (bytes_to_read > 0) {
ring_buffer_->Read(bytes_to_read, input_buffer_ + input_buffer_index_);
input_buffer_index_ += bytes_to_read;
}
}
void SynthUnit::ConsumeInput(int n_input_bytes) {
if (n_input_bytes < input_buffer_index_) {
std::memmove(input_buffer_, input_buffer_ + n_input_bytes,
input_buffer_index_ - n_input_bytes);
}
input_buffer_index_ -= n_input_bytes;
}
int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
uint8_t cmd = buf[0];
uint8_t cmd_type = cmd & 0xf0;
if (cmd_type == 0x80 || (cmd_type == 0x90 && buf[2] == 0)) {
if (buf_size >= 3) {
// note off
for (int note = 0; note < max_active_notes; ++note) {
if (active_note_[note].midi_note == buf[1] &&
active_note_[note].dx7_note != NULL) {
active_note_[note].dx7_note->keyup();
}
}
return 3;
}
return 0;
} else if (cmd_type == 0x90) {
if (buf_size >= 3) {
// note on
// Note that this implements round-robin (same as original Dx7). A more
// sophisticated note-stealing algorithm is probably worthwhile.
delete active_note_[current_note_].dx7_note;
active_note_[current_note_].midi_note = buf[1];
const uint8_t *patch = patch_data_ + 128 * current_patch_;
active_note_[current_note_].dx7_note =
new Dx7Note((const char *)patch, buf[1], buf[2]);
current_note_ = (current_note_ + 1) % max_active_notes;
return 3;
}
return 0;
} else if (cmd_type == 0xc0) {
if (buf_size >= 2) {
// program change
int program_number = buf[1];
current_patch_ = std::min(program_number, 31);
char name[11];
std::memcpy(name, patch_data_ + 128 * current_patch_ + 118, 10);
name[10] = 0;
std::cout << "Loaded patch " << current_patch_ << ": " << name << "\r";
std::cout.flush();
return 2;
}
return 0;
} else if (cmd == 0xf0) {
// sysex
if (buf_size >= 6 && buf[1] == 0x43 && buf[2] == 0x00 && buf[3] == 0x09 &&
buf[4] == 0x20 && buf[5] == 0x00) {
if (buf_size >= 4104) {
// TODO: check checksum?
std::memcpy(patch_data_, buf + 6, 4096);
return 4104;
}
return 0;
}
}
// TODO: more robust handling
std::cout << "Unknown message " << std::hex << (int)cmd <<
", skipping " << std::dec << buf_size << " bytes" << std::endl;
return buf_size;
}
void SynthUnit::GetSamples(int n_samples, int16_t *buffer) {
TransferInput();
size_t input_offset;
for (input_offset = 0; input_offset < input_buffer_index_; ) {
int bytes_available = input_buffer_index_ - input_offset;
int bytes_consumed = ProcessMidiMessage(input_buffer_ + input_offset,
bytes_available);
if (bytes_consumed == 0) {
break;
}
input_offset += bytes_consumed;
}
ConsumeInput(input_offset);
for (int i = 0; i < n_samples; i += N) {
int32_t audiobuf[N];
for (int j = 0; j < N; ++j) {
audiobuf[j] = 0;
}
for (int note = 0; note < max_active_notes; ++note) {
if (active_note_[note].dx7_note != NULL) {
active_note_[note].dx7_note->compute(audiobuf);
}
}
for (int j = 0; j < N; ++j) {
int32_t val = audiobuf[j] >> 4;
int clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff :
val >> 9;
// TODO: maybe some dithering?
buffer[i + j] = clip_val;
}
}
}

@ -0,0 +1,46 @@
/*
* 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 "dx7note.h"
#include "ringbuffer.h"
struct ActiveNote {
int midi_note;
Dx7Note *dx7_note;
};
class SynthUnit {
public:
explicit SynthUnit(RingBuffer *ring_buffer);
void GetSamples(int n_samples, int16_t *buffer);
private:
void TransferInput();
void ConsumeInput(int n_input_bytes);
int ProcessMidiMessage(const uint8_t *buf, int buf_size);
RingBuffer *ring_buffer_;
static const int max_active_notes = 16;
ActiveNote active_note_[max_active_notes];
int current_note_;
uint8_t input_buffer_[8192];
size_t input_buffer_index_;
uint8_t patch_data_[4096];
int current_patch_;
};

@ -0,0 +1,70 @@
/*
* 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 <time.h>
#include <pthread.h>
#include <iostream>
#include "ringbuffer.h"
#define kNumIter 100000
#define kBufSize 256
using namespace ::std;
void *consumer_thread(void *arg) {
RingBuffer *rb = (RingBuffer *)arg;
uint8_t buf[kBufSize];
for (int i = 0; i < kNumIter; ++i) {
int bytes_available = rb->BytesAvailable();
// cout << "bytes_available = " << bytes_available << endl;
while (bytes_available < kBufSize) {
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 1000000;
nanosleep(&sleepTime, NULL);
bytes_available = rb->BytesAvailable();
}
// cout << "reading..." << endl;
rb->Read(kBufSize, buf);
for (int j = 0; j < kBufSize; ++j) {
int expected = (i + j) & 0xff;
if (buf[j] != expected) {
cout << "error at " << i << ", " << j << " expected " << expected <<
" got " << buf[j] << endl;
}
}
}
return NULL;
}
void test_ringbuffer() {
RingBuffer rb;
uint8_t buf[kBufSize];
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&thread, &attr, consumer_thread, (void *)&rb);
for (int i = 0; i < kNumIter; ++i) {
for (int j = 0; j < kBufSize; ++j) {
buf[j] = i + j;
}
// cout << "writing..." << rb.BytesAvailable() << endl;
rb.Write(buf, kBufSize);
}
pthread_join(thread, NULL);
}

@ -0,0 +1,75 @@
/*
* 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.
*/
// Create a WAV file
#include "wavout.h"
using namespace std;
void w32le(char *buf, int offset, int32_t val) {
buf[offset + 0] = val & 0xff;
buf[offset + 1] = (val >> 8) & 0xff;
buf[offset + 2] = (val >> 16) & 0xff;
buf[offset + 3] = (val >> 24) & 0xff;
}
void w16le(char *buf, int offset, int val) {
buf[offset + 0] = val & 0xff;
buf[offset + 1] = (val >> 8) & 0xff;
}
void w4cc(char *buf, int offset, const char *s) {
for (int i = 0; i < 4; i++) {
buf[offset + i] = s[i];
}
}
WavOut::WavOut(const char *filename, double sample_rate, int n_samples) {
char header[44];
fs = new fstream(filename, fstream::out | fstream::trunc | fstream::binary);
w4cc(header, 0, "RIFF");
w32le(header, 4, 36 + 2 * n_samples);
w4cc(header, 8, "WAVE");
w4cc(header, 12, "fmt ");
w32le(header, 16, 16);
w16le(header, 20, 1); // AudioFormat
w16le(header, 22, 1); // NumChannels
w32le(header, 24, (int32_t)sample_rate);
w32le(header, 28, 2 * (int32_t)sample_rate);
w16le(header, 32, 2); // BlockAlign
w16le(header, 34, 16); // BitsPerSample
w4cc(header, 36, "data");
w32le(header, 40, 2 * n_samples);
fs->write(header, 44);
}
void WavOut::write_data(const int32_t *buf, int n) {
int32_t delta = 0x100;
for (int i = 0; i < n; i++) {
int32_t val = buf[i];
int clip_val = val < -(1 << 24) ? 0x8000 : (val >= (1 << 24) ? 0x7fff :
(val + delta) >> 9);
delta = (delta + val) & 0x1ff;
sample_buf[i * 2] = clip_val & 0xff;
sample_buf[i * 2 + 1] = (clip_val >> 8) & 0xff;
}
fs->write(sample_buf, n * 2);
}
void WavOut::close() {
fs->close();
}

@ -0,0 +1,29 @@
/*
* 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 <fstream>
class WavOut {
public:
WavOut(const char *filename, double sample_rate, int n_samples);
void write_data(const int32_t *buf, int n);
void close();
private:
char sample_buf[128];
std::fstream *fs;
};
Loading…
Cancel
Save