Convert to Android Studio

This commit makes the project build on Android Studio. It also deletes
a whole lot of unused code. Further, in this patch, the NEON code is
not build, just C++.
master
Raph Levien 9 years ago
parent 6b8187fba8
commit 076ab6eab8
  1. 8
      .gitignore
  2. 1
      .idea/.name
  3. 22
      .idea/compiler.xml
  4. 3
      .idea/copyright/profiles_settings.xml
  5. 6
      .idea/encodings.xml
  6. 23
      .idea/gradle.xml
  7. 46
      .idea/misc.xml
  8. 9
      .idea/modules.xml
  9. 12
      .idea/runConfigurations.xml
  10. 6
      .idea/vcs.xml
  11. 13
      android/.classpath
  12. 10
      android/.externalToolBuilders/NDK Builder.launch
  13. 10
      android/.externalToolBuilders/Proto Builder.launch
  14. 3
      android/.gitignore
  15. 75
      android/.project
  16. 280
      android/.settings/org.eclipse.jdt.core.prefs
  17. 8
      android/.settings/org.eclipse.jdt.ui.prefs
  18. 84
      android/AndroidManifest.xml
  19. 11
      android/default.properties
  20. 88
      android/jni/Android.mk
  21. 2
      android/jni/Application.mk
  22. 14
      android/project.properties
  23. 279
      android/src/com/levien/synthesizer/android/Storage.java
  24. 61
      android/src/com/levien/synthesizer/android/ui/AmplificationActivity.java
  25. 73
      android/src/com/levien/synthesizer/android/ui/ChordGridActivity.java
  26. 141
      android/src/com/levien/synthesizer/android/ui/EditInstrumentActivity.java
  27. 52
      android/src/com/levien/synthesizer/android/ui/EffectsActivity.java
  28. 81
      android/src/com/levien/synthesizer/android/ui/InstrumentListActivity.java
  29. 59
      android/src/com/levien/synthesizer/android/ui/KarplusStrongActivity.java
  30. 67
      android/src/com/levien/synthesizer/android/ui/LowPassFilterActivity.java
  31. 107
      android/src/com/levien/synthesizer/android/ui/MainActivity.java
  32. 66
      android/src/com/levien/synthesizer/android/ui/OscillatorActivity.java
  33. 79
      android/src/com/levien/synthesizer/android/ui/PianoActivity.java
  34. 141
      android/src/com/levien/synthesizer/android/ui/ScoreActivity.java
  35. 189
      android/src/com/levien/synthesizer/android/ui/SynthesizerActivity.java
  36. 68
      android/src/com/levien/synthesizer/android/ui/TremoloActivity.java
  37. 65
      android/src/com/levien/synthesizer/android/ui/VibratoActivity.java
  38. 329
      android/src/com/levien/synthesizer/android/widgets/ChordGridView.java
  39. 95
      android/src/com/levien/synthesizer/android/widgets/piano/BlackPianoKey.java
  40. 52
      android/src/com/levien/synthesizer/android/widgets/piano/NotePianoKey.java
  41. 111
      android/src/com/levien/synthesizer/android/widgets/piano/OctavePianoKey.java
  42. 179
      android/src/com/levien/synthesizer/android/widgets/piano/PianoKey.java
  43. 458
      android/src/com/levien/synthesizer/android/widgets/piano/PianoView.java
  44. 32
      android/src/com/levien/synthesizer/android/widgets/piano/PianoViewListener.java
  45. 72
      android/src/com/levien/synthesizer/android/widgets/piano/WhitePianoKey.java
  46. 490
      android/src/com/levien/synthesizer/android/widgets/score/EditEventTool.java
  47. 97
      android/src/com/levien/synthesizer/android/widgets/score/HideChannelButton.java
  48. 118
      android/src/com/levien/synthesizer/android/widgets/score/NewEventTool.java
  49. 118
      android/src/com/levien/synthesizer/android/widgets/score/NewLoopTool.java
  50. 156
      android/src/com/levien/synthesizer/android/widgets/score/PlayButton.java
  51. 231
      android/src/com/levien/synthesizer/android/widgets/score/PlayTool.java
  52. 767
      android/src/com/levien/synthesizer/android/widgets/score/ScoreView.java
  53. 27
      android/src/com/levien/synthesizer/android/widgets/score/ScoreViewListener.java
  54. 81
      android/src/com/levien/synthesizer/android/widgets/score/ScoreViewTool.java
  55. 222
      android/src/com/levien/synthesizer/android/widgets/score/ScoreViewToolbar.java
  56. 100
      android/src/com/levien/synthesizer/android/widgets/score/SelectChannelButton.java
  57. 134
      android/src/com/levien/synthesizer/android/widgets/score/SnapTool.java
  58. 259
      android/src/com/levien/synthesizer/android/widgets/score/ViewportTool.java
  59. 27
      android/src/com/levien/synthesizer/android/widgets/waveform/WaveformListener.java
  60. 167
      android/src/com/levien/synthesizer/android/widgets/waveform/WaveformRowView.java
  61. 470
      android/src/com/levien/synthesizer/android/widgets/waveform/WaveformView.java
  62. 1
      app/.gitignore
  63. 41
      app/build.gradle
  64. 17
      app/proguard-rules.pro
  65. 13
      app/src/androidTest/java/com/levien/synthesizer/ApplicationTest.java
  66. 76
      app/src/main/AndroidManifest.xml
  67. 0
      app/src/main/java/com/levien/synthesizer/android/AndroidGlue.java
  68. 10
      app/src/main/java/com/levien/synthesizer/android/service/SynthesizerService.java
  69. 20
      app/src/main/java/com/levien/synthesizer/android/service/SynthesizerThread.java
  70. 0
      app/src/main/java/com/levien/synthesizer/android/stats/JitterStats.java
  71. 2
      app/src/main/java/com/levien/synthesizer/android/ui/PianoActivity2.java
  72. 0
      app/src/main/java/com/levien/synthesizer/android/ui/SettingsActivity.java
  73. 0
      app/src/main/java/com/levien/synthesizer/android/ui/SynthActivity.java
  74. 0
      app/src/main/java/com/levien/synthesizer/android/usb/UsbMidiDevice.java
  75. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/keyboard/KeySpec.java
  76. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/keyboard/KeyboardSpec.java
  77. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/keyboard/KeyboardView.java
  78. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/keyboard/ScrollStripView.java
  79. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/knob/KnobListener.java
  80. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/knob/KnobPlaceholderView.java
  81. 0
      app/src/main/java/com/levien/synthesizer/android/widgets/knob/KnobPreference.java
  82. 33
      app/src/main/java/com/levien/synthesizer/android/widgets/knob/KnobView.java
  83. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MessageFromBytes.java
  84. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MessageInputProcessor.java
  85. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MessageOutputProcessor.java
  86. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MessageTee.java
  87. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiAdapter.java
  88. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiChannelFilter.java
  89. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiEvent.java
  90. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiFile.java
  91. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiFilePlayer.java
  92. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiHeader.java
  93. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiListener.java
  94. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiListenerProxy.java
  95. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiReader.java
  96. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiTrack.java
  97. 0
      app/src/main/java/com/levien/synthesizer/core/midi/MidiUtil.java
  98. 0
      app/src/main/jni/SynthApp.gyp
  99. 0
      app/src/main/jni/SynthApp.xcodeproj/project.pbxproj
  100. 0
      app/src/main/jni/SynthApp/English.lproj/InfoPlist.strings
  101. Some files were not shown because too many files have changed in this diff Show More

8
.gitignore vendored

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

@ -0,0 +1 @@
Music Synthesizer

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="myModules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/music-synthesizer-for-android.iml" filepath="$PROJECT_DIR$/music-synthesizer-for-android.iml" />
</modules>
</component>
</project>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/.svn/*" kind="src" path="core"/>
<classpathentry excluding="**/.svn/*" kind="src" path="core-gen"/>
<classpathentry excluding="**/.svn/*" kind="src" path="src"/>
<classpathentry excluding="**/.svn/*" kind="src" path="test"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="lib" path="core-lib/libprotobuf.jar"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/libs&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/jni&quot; type=&quot;2&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${system_property:user.home}/dl/android-ndk-r9/ndk-build"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc}"/>
</launchConfiguration>

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/core/com/levien/synthesizer/core/model/composite/Presets.proto&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/core/com/levien/synthesizer/core/model/sample/Proto.proto&quot; type=&quot;1&quot;/&gt;&#10;&lt;item path=&quot;/MusicSynthesizer/core/com/levien/synthesizer/core/music/Music.proto&quot; type=&quot;1&quot;/&gt;&#10;&lt;/resources&gt;}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/../core/bin/protoc"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="--java_out=../core/gen src/com/levien/synthesizer/core/model/composite/Presets.proto src/com/levien/synthesizer/core/music/Music.proto"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc}/../core"/>
</launchConfiguration>

@ -1,3 +0,0 @@
# Android NDK output files
obj/**
libs/**

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MusicSynthesizer</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>auto,full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/Proto Builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>auto,full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/NDK Builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>core</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/core/src</locationURI>
</link>
<link>
<name>core-gen</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/core/gen</locationURI>
</link>
<link>
<name>core-lib</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/core/lib</locationURI>
</link>
<link>
<name>test</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/test/src</locationURI>
</link>
</linkedResources>
</projectDescription>

@ -1,280 +0,0 @@
#Thu Jan 27 01:33:26 PST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.5
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
org.eclipse.jdt.core.formatter.comment.format_line_comments=true
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=100
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=4
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=4
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
org.eclipse.jdt.core.formatter.indentation.size=2
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=true
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=true
org.eclipse.jdt.core.formatter.lineSplit=100
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=space
org.eclipse.jdt.core.formatter.tabulation.size=2
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true

@ -1,8 +0,0 @@
#Thu Jan 27 01:35:20 PST 2011
eclipse.preferences.version=1
formatter_profile=_MusicSynthesizer
formatter_settings_version=11
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
org.eclipse.jdt.ui.ondemandthreshold=99
org.eclipse.jdt.ui.staticondemandthreshold=99

@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.levien.synthesizer"
android:versionCode="7"
android:versionName="0.95">
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="17"
/> <!-- 9 = Gingerbread -->
<uses-feature android:name="android.hardware.usb.host"
android:required="false"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:theme="@style/LightThemeSelector">
<activity
android:name="com.levien.synthesizer.android.ui.PianoActivity2"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
<activity
android:name="com.levien.synthesizer.android.ui.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.ScoreActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.ChordGridActivity"
android:label="@string/chord_grid"
android:screenOrientation="portrait" />
<activity
android:name="com.levien.synthesizer.android.ui.InstrumentListActivity"
android:label="@string/instrument_list"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.EditInstrumentActivity"
android:label="@string/edit_instrument"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.VibratoActivity"
android:label="@string/vibrato"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.OscillatorActivity"
android:label="@string/oscillator"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.TremoloActivity"
android:label="@string/tremolo"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.LowPassFilterActivity"
android:label="@string/low_pass_filter"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.AmplificationActivity"
android:label="@string/amplification"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.EffectsActivity"
android:label="@string/effects"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.KarplusStrongActivity"
android:label="@string/karplus_strong"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.SettingsActivity"
android:label="@string/settings" />
<service android:name=".android.service.SynthesizerService">
</service>
</application>
</manifest>

@ -1,11 +0,0 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-9

@ -1,88 +0,0 @@
LOCAL_PATH := $(call my-dir)/../../cpp/src
include $(CLEAR_VARS)
LOCAL_MODULE := synth
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := android_glue.cc \
dx7note.cc \
env.cc \
exp2.cc \
fir.cc \
fm_core.cc \
fm_op_kernel.cc \
freqlut.cc \
lfo.cc \
patch.cc \
pitchenv.cc \
resofilter.cc \
ringbuffer.cc \
sawtooth.cc \
sin.cc \
synth_unit.cc
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_ARM_NEON := true
LOCAL_CFLAGS := -DHAVE_NEON=1
LOCAL_SRC_FILES += neon_fm_kernel.s \
neon_ladder.s \
neon_fir.s
endif
# for native audio
LOCAL_LDLIBS += -lOpenSLES
# for logging
LOCAL_LDLIBS += -llog
LOCAL_STATIC_LIBRARIES += cpufeatures
LOCAL_CFLAGS += -O3
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := test_neon.cc \
resofilter.cc
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_ARM_NEON := true
LOCAL_CFLAGS := -DHAVE_NEON=1
LOCAL_SRC_FILES += neon_fm_kernel.s \
neon_ladder.s \
neon_fir.s
endif
LOCAL_CFLAGS += -O3
LOCAL_STATIC_LIBRARIES += cpufeatures
LOCAL_MODULE := test_neon
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := test_filter.cc \
fir.cc \
sawtooth.cc \
exp2.cc \
sin.cc \
fm_op_kernel.cc \
resofilter.cc \
freqlut.cc
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_ARM_NEON := true
LOCAL_CFLAGS := -DHAVE_NEON=1
LOCAL_SRC_FILES += neon_fir.s \
neon_iir.s \
neon_fm_kernel.s \
neon_ladder.s
endif
LOCAL_CFLAGS += -O3
LOCAL_STATIC_LIBRARIES += cpufeatures
LOCAL_MODULE := test_filter
include $(BUILD_EXECUTABLE)
$(call import-module,android/cpufeatures)

@ -1,2 +0,0 @@
APP_ABI := all

@ -1,14 +0,0 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-17

@ -1,279 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Environment;
import android.widget.EditText;
import android.widget.Toast;
import com.levien.synthesizer.core.music.Music.Score;
/**
* A collection of functions for storing and retrieving scores.
*/
public class Storage {
/**
* Opens the score stored with the name "_default", which should always be the current score
* being edited. This may be called at any time to restore saved state, since Android Activities
* can come and go.
*
* @param score - The mutable score to populate with the stored data.
* @param context - Android application context.
*/
public static void openDefaultScore(Score.Builder score, Context context) throws IOException {
openScore(score, "_default", context);
}
/**
* Saves a score with the name "_default", which should always be the current score being edited.
* This may be called at any time to save state, since Android Activities can come and go.
*
* @param score - The score data to save.
* @param context - Android application context.
*/
public static void saveDefaultScore(Score score, Context context) throws IOException {
saveScore(score, "_default", true, context);
}
/**
* Opens the score with the given name. The name should be name of a valid score file in storage.
* The file must be in the root external files directory for the app.
*
* @param score - The mutable score to update with the data from storage.
* @param name - The name of the file, minus the ".pb" extension.
* @param context - The Android application context.
*/
public static void openScore(Score.Builder score, String name, Context context) throws IOException {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) &&
!Environment.MEDIA_MOUNTED_READ_ONLY.equals(Environment.getExternalStorageState())) {
throw new IOException("External storage is not readable.");
}
File path = context.getExternalFilesDir(null);
File file = new File(path, name + ".pb");
FileInputStream in = new FileInputStream(file);
score.clear();
score.mergeFrom(in);
in.close();
}
/**
* Saves the score with the given name. Files are stored in the root external files directory for
* the app.
*
* @param score - The score to save.
* @param name - The name of the file, without any extension.
* @param overwrite - If true, replace the existing file, if one already exists.
* @param context - The Android application context.
* @throws IOException - On any kind of IO error, or if name is "", or the file already exists.
*/
public static void saveScore(Score score,
String name,
boolean overwrite,
Context context) throws IOException {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
throw new IOException("External storage is not writeable.");
}
File path = context.getExternalFilesDir(null);
name = cleanupName(name);
if (name.length() == 0) {
throw new IOException("Can't save score without a name.");
}
File file = new File(path, name + ".pb");
if (!overwrite && file.exists()) {
throw new IOException("File already exists.");
}
FileOutputStream out = new FileOutputStream(file);
score.writeTo(out);
out.close();
}
/**
* Returns the list of all valid names of scores that are currently in storage.
*/
public static String[] getScoreNames(Context context) throws IOException {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) &&
!Environment.MEDIA_MOUNTED_READ_ONLY.equals(Environment.getExternalStorageState())) {
throw new IOException("External storage is not readable.");
}
File path = context.getExternalFilesDir(null);
File[] files = path.listFiles();
ArrayList<String> names = new ArrayList<String>();
for (File file : files) {
names.add(file.getName().replaceAll("\\.pb", ""));
}
return names.toArray(new String[0]);
}
/**
* Returns true iff there is a score in storage with the given name.
*/
public static boolean scoreExists(String name, Context context) throws IOException {
String[] names = getScoreNames(context);
for (String existingName : names) {
if (name.equals(existingName)) {
return true;
}
}
return false;
}
/**
* Interface used to notify callers of openScoreWithDialog() when the opening has completed.
*/
public interface OpenScoreListener {
void onOpenScore(Score.Builder score);
}
/**
* Shows UI to allow the user to pick one of the current scores in storage, and then populates
* score with the data from that file.
*
* @param score - The mutable score to update.
* @param listener - A listener that is notified after the score is updated. Can be null.
* @param context - An Android application context.
*/
public static void openScoreWithDialog(final Score.Builder score,
final OpenScoreListener listener,
final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Open score...");
try {
final String[] scoreNames = getScoreNames(context);
builder.setItems(scoreNames, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
openScore(score, scoreNames[which], context);
if (listener != null) {
listener.onOpenScore(score);
}
dialog.dismiss();
} catch (IOException e) {
Logger logger = Logger.getLogger(Storage.class.getName());
logger.log(Level.SEVERE,
"Error opening score \"" + scoreNames[which] + "\" with dialog.", e);
Toast.makeText(context,
"Unable to open \"" + scoreNames[which] + "\".",
Toast.LENGTH_SHORT).show();
}
}
});
} catch (IOException e) {
Logger logger = Logger.getLogger(Storage.class.getName());
logger.log(Level.SEVERE,
"Error getting score names.", e);
Toast.makeText(context,
"Unable to get existing score names.",
Toast.LENGTH_SHORT).show();
}
AlertDialog dialog = builder.create();
dialog.show();
}
/**
* Shows UI to allow the user to pick a name and save the given score in storage.
*
* @param score - The score to save.
* @param context - An Android application context.
*/
public static void saveScoreWithDialog(final Score score,
final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Save score as...");
builder.setMessage("Name: ");
final EditText input = new EditText(context);
builder.setView(input);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface nameDialog, int which) {
final String name = cleanupName(input.getText().toString());
if (name.length() == 0) {
Toast.makeText(context,
"Name must not be empty.",
Toast.LENGTH_SHORT).show();
} else {
try {
if (scoreExists(name, context)) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Overwrite?");
builder.setMessage(
"A score named " + name + " already exists. Would you like to overwrite it?");
builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface confirmDialog, int which) {
try {
saveScore(score, name, true, context);
confirmDialog.dismiss();
nameDialog.dismiss();
} catch (IOException e) {
Logger logger = Logger.getLogger(Storage.class.getName());
logger.log(Level.SEVERE,
"Error saving score \"" + name + "\" with dialog.", e);
Toast.makeText(context,
"Unable to save \"" + name + "\".",
Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog confirmDialog = builder.create();
confirmDialog.show();
} else {
saveScore(score, name, false, context);
nameDialog.dismiss();
}
} catch (IOException e) {
Logger logger = Logger.getLogger(Storage.class.getName());
logger.log(Level.SEVERE,
"Error saving score \"" + name + "\" with dialog.", e);
Toast.makeText(context,
"Unable to save \"" + name + "\".",
Toast.LENGTH_SHORT).show();
}
}
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
/**
* Internal method to turn a user input string into a valid file name.
*/
private static String cleanupName(String name) {
name = name.trim();
name = name.replaceAll("[^A-Za-z0-9_-]", "_");
return name;
}
}

@ -1,61 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
/**
* Activity for modifying amplification/adsr envelope.
* TODO(klimt): Add the ability to switch channels.
*/
public class AmplificationActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.amplification);
attackKnob_ = (KnobView)findViewById(R.id.attackKnob);
decayKnob_ = (KnobView)findViewById(R.id.decayKnob);
sustainKnob_ = (KnobView)findViewById(R.id.sustainKnob);
releaseKnob_ = (KnobView)findViewById(R.id.releaseKnob);
volumeKnob_ = (KnobView)findViewById(R.id.volumeKnob);
piano_ = (PianoView)findViewById(R.id.piano);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
attackKnob_.bindTo(synthesizer_, channel, Setting.ATTACK);
decayKnob_.bindTo(synthesizer_, channel, Setting.DECAY);
sustainKnob_.bindTo(synthesizer_, channel, Setting.SUSTAIN);
releaseKnob_.bindTo(synthesizer_, channel, Setting.RELEASE);
volumeKnob_.bindTo(synthesizer_, channel, Setting.VOLUME);
piano_.bindTo(synthesizer_, channel);
}
private KnobView attackKnob_;
private KnobView decayKnob_;
private KnobView sustainKnob_;
private KnobView releaseKnob_;
private KnobView volumeKnob_;
private PianoView piano_;
}

@ -1,73 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.ui;
import java.util.ArrayList;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.android.widgets.ChordGridView;
/**
* Activity for playing whole chords at a time, arranged in a circle of fifths.
*/
public class ChordGridActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chord_grid);
chordGrid_ = (ChordGridView)findViewById(R.id.chord_grid);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (synthesizer_ == null) {
return;
}
if (position > 0) {
chordGrid_.bindTo(synthesizer_, position - 1);
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
chordGrid_.bindTo(synthesizer_, 0);
ArrayList<String> presetNames = new ArrayList<String>();
presetNames.add("");
synthesizer_.getPresetNames(presetNames);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item, presetNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
}
private ChordGridView chordGrid_;
private Spinner presetSpinner_;
}

@ -1,141 +0,0 @@
/*
* 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.
*/
package com.levien.synthesizer.android.ui;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.service.SynthesizerService;
/**
* An Activity to let the user choose a subset of a presets settings in order to edit them.
*/
public class EditInstrumentActivity extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
String[] sections = getResources().getStringArray(R.array.sections);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, sections);
setListAdapter(adapter);
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, SynthesizerService.class),
synthesizerConnection_, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(synthesizerConnection_);
}
@Override
protected void onListItemClick(ListView list, View view, int position, long id) {
int channel = SynthesizerActivity.getIntentChannel(this);
switch (position) {
case 0:
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(channel),
this,
VibratoActivity.class));
break;
case 1:
this.startActivity(new Intent(null, SynthesizerActivity.makeUri(
channel,
Setting.OSCILLATOR_1_WAVEFORM,
Setting.OSCILLATOR_1_GLIDE,
Setting.OSCILLATOR_1_COARSE,
Setting.OSCILLATOR_1_FINE,
Setting.OSCILLATOR_1_VIBRATO,
Setting.BALANCE),
this, OscillatorActivity.class));
break;
case 2:
this.startActivity(new Intent(null, SynthesizerActivity.makeUri(
channel,
Setting.OSCILLATOR_2_WAVEFORM,
Setting.OSCILLATOR_2_GLIDE,
Setting.OSCILLATOR_2_COARSE,
Setting.OSCILLATOR_2_FINE,
Setting.OSCILLATOR_2_VIBRATO,
Setting.BALANCE),
this, OscillatorActivity.class));
break;
case 3:
this.startActivity(new Intent(null, SynthesizerActivity.makeUri(
channel,
Setting.OSCILLATOR_1_BLEND,
Setting.OSCILLATOR_1_STRETCH,
Setting.OSCILLATOR_1_EXCITEMENT),
this, KarplusStrongActivity.class));
break;
case 4:
this.startActivity(new Intent(null, SynthesizerActivity.makeUri(
channel,
Setting.OSCILLATOR_2_BLEND,
Setting.OSCILLATOR_2_STRETCH,
Setting.OSCILLATOR_2_EXCITEMENT),
this, KarplusStrongActivity.class));
break;
case 5:
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(channel),
this,
TremoloActivity.class));
break;
case 6:
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(channel),
this,
LowPassFilterActivity.class));
break;
case 7:
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(channel),
this,
AmplificationActivity.class));
break;
case 8:
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(channel),
this,
EffectsActivity.class));
break;
}
}
private ServiceConnection synthesizerConnection_ = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
}
public void onServiceDisconnected(ComponentName className) {
}
};
}

@ -1,52 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
/**
* Activity for modifying effects like echo.
* TODO(klimt): Add the ability to switch channels.
*/
public class EffectsActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.effects);
piano_ = (PianoView)findViewById(R.id.piano);
echoMixKnob_ = (KnobView)findViewById(R.id.echoMixKnob);
echoDelayKnob_ = (KnobView)findViewById(R.id.echoDelayKnob);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
piano_.bindTo(synthesizer_, channel);
echoMixKnob_.bindTo(synthesizer_, channel, Setting.ECHO_MIX);
echoDelayKnob_.bindTo(synthesizer_, channel, Setting.ECHO_DELAY);
}
private PianoView piano_;
private KnobView echoMixKnob_;
private KnobView echoDelayKnob_;
}

@ -1,81 +0,0 @@
/*
* 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.
*/
package com.levien.synthesizer.android.ui;
import java.util.ArrayList;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.android.service.SynthesizerService;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* An activity that shows the list of available presets (aka instruments), and let's the user click
* on one of them to begin editing it.
*/
public class InstrumentListActivity extends ListActivity {
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, SynthesizerService.class),
synthesizerConnection_, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(synthesizerConnection_);
}
@Override
protected void onListItemClick(ListView list, View view, int position, long id) {
this.startActivity(new Intent(null,
SynthesizerActivity.makeUri(position),
this,
EditInstrumentActivity.class));
}
private ServiceConnection synthesizerConnection_ = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
MultiChannelSynthesizer synthesizer =
((SynthesizerService.LocalBinder)service).getSynthesizer();
ArrayList<String> presets = new ArrayList<String>();
synthesizer.getPresetNames(presets);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
InstrumentListActivity.this,
android.R.layout.simple_list_item_1,
presets.toArray(new String[0]));
InstrumentListActivity.this.runOnUiThread(new Runnable() {
public void run() {
InstrumentListActivity.this.setListAdapter(adapter);
InstrumentListActivity.this.getListView().invalidate();
}
});
}
public void onServiceDisconnected(ComponentName className) {
}
};
}

@ -1,59 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
/**
* Activity for modifying Karplus-Strong parameters.
* TODO(klimt): Add the ability to switch channels.
*/
public class KarplusStrongActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.karplus_strong);
piano_ = (PianoView)findViewById(R.id.piano);
blendKnob_ = (KnobView)findViewById(R.id.blendKnob);
stretchKnob_ = (KnobView)findViewById(R.id.stretchKnob);
excitementKnob_ = (KnobView)findViewById(R.id.excitementKnob);
PianoView piano = (PianoView)findViewById(R.id.piano);
piano.bindTo(synthesizer_, 0);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
Setting[] settings = getIntentSettings(this);
piano_.bindTo(synthesizer_, channel);
blendKnob_.bindTo(synthesizer_, channel, settings[0]);
stretchKnob_.bindTo(synthesizer_, channel, settings[1]);
excitementKnob_.bindTo(synthesizer_, channel, settings[2]);
}
private PianoView piano_;
private KnobView blendKnob_;
private KnobView stretchKnob_;
private KnobView excitementKnob_;
}

@ -1,67 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
/**
* Activity for modifying low-pass filter settings.
* TODO(klimt): Add the ability to switch channels.
*/
public class LowPassFilterActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.low_pass_filter);
piano_ = (PianoView)findViewById(R.id.piano);
cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob);
depthKnob_ = (KnobView)findViewById(R.id.depthKnob);
attackKnob_ = (KnobView)findViewById(R.id.attackKnob);
decayKnob_ = (KnobView)findViewById(R.id.decayKnob);
sustainKnob_ = (KnobView)findViewById(R.id.sustainKnob);
releaseKnob_ = (KnobView)findViewById(R.id.releaseKnob);
PianoView piano = (PianoView)findViewById(R.id.piano);
piano.bindTo(synthesizer_, 0);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
piano_.bindTo(synthesizer_, channel);
cutoffKnob_.bindTo(synthesizer_, channel, Setting.FILTER_CUTOFF);
depthKnob_.bindTo(synthesizer_, channel, Setting.FILTER_DEPTH);
attackKnob_.bindTo(synthesizer_, channel, Setting.FILTER_ATTACK);
decayKnob_.bindTo(synthesizer_, channel, Setting.FILTER_DECAY);
sustainKnob_.bindTo(synthesizer_, channel, Setting.FILTER_SUSTAIN);
releaseKnob_.bindTo(synthesizer_, channel, Setting.FILTER_RELEASE);
}
private PianoView piano_;
private KnobView cutoffKnob_;
private KnobView depthKnob_;
private KnobView attackKnob_;
private KnobView decayKnob_;
private KnobView sustainKnob_;
private KnobView releaseKnob_;
}

@ -1,107 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import java.util.ArrayList;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.android.widgets.piano.PianoView;
// TODO(klimt): Add the ability to switch channels.
public class MainActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
piano_ = (PianoView)findViewById(R.id.piano);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
final Button playButton = (Button)findViewById(R.id.playButton);
final Button recordButton = (Button)findViewById(R.id.recordButton);
playButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (synthesizer_.getChannel(0).isPlaying()) {
synthesizer_.getChannel(0).stopPlaying();
playButton.setText(R.string.play);
recordButton.setText(R.string.record);
} else {
synthesizer_.getChannel(0).startPlaying();
playButton.setText(R.string.stop);
recordButton.setText(R.string.record);
}
}
});
recordButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
if (synthesizer_.getChannel(0).isRecording()) {
synthesizer_.getChannel(0).stopRecording();
playButton.setText(R.string.play);
recordButton.setText(R.string.record);
} else {
synthesizer_.getChannel(0).startRecording();
playButton.setEnabled(true);
playButton.setText(R.string.play);
recordButton.setText(R.string.stop);
}
}
});
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (synthesizer_ == null) {
return;
}
String preset = presetSpinner_.getItemAtPosition(position).toString();
if (!preset.equals("")) {
synthesizer_.getChannel(0).setPreset(preset);
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
piano_.bindTo(synthesizer_, 0);
ArrayList<String> presetNames = new ArrayList<String>();
presetNames.add("");
synthesizer_.getPresetNames(presetNames);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item, presetNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
}
private PianoView piano_;
private Spinner presetSpinner_;
}

@ -1,66 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
import com.levien.synthesizer.android.widgets.waveform.WaveformRowView;
/**
* Activity for modifying oscillator parameters.
* TODO(klimt): Add the ability to switch channels.
*/
public class OscillatorActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.oscillator);
piano_ = (PianoView)findViewById(R.id.piano);
waveformView_ = (WaveformRowView)findViewById(R.id.waveform);
glideKnob_ = (KnobView)findViewById(R.id.glideKnob);
coarseKnob_ = (KnobView)findViewById(R.id.coarseKnob);
fineKnob_ = (KnobView)findViewById(R.id.fineKnob);
vibratoDepthKnob_ = (KnobView)findViewById(R.id.vibratoDepthKnob);
balanceKnob_ = (KnobView)findViewById(R.id.balanceKnob);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
Setting[] settings = getIntentSettings(this);
piano_.bindTo(synthesizer_, channel);
waveformView_.bindTo(synthesizer_, channel, settings[0]);
glideKnob_.bindTo(synthesizer_, channel, settings[1]);
coarseKnob_.bindTo(synthesizer_, channel, settings[2]);
fineKnob_.bindTo(synthesizer_, channel, settings[3]);
vibratoDepthKnob_.bindTo(synthesizer_, channel, settings[4]);
balanceKnob_.bindTo(synthesizer_, channel, settings[5]);
}
private PianoView piano_;
private WaveformRowView waveformView_;
private KnobView glideKnob_;
private KnobView coarseKnob_;
private KnobView fineKnob_;
private KnobView vibratoDepthKnob_;
private KnobView balanceKnob_;
}

@ -1,79 +0,0 @@
/*
* 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.
*/
package com.levien.synthesizer.android.ui;
import java.util.ArrayList;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.AdapterView.OnItemSelectedListener;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
/**
* Activity for simply playing the piano.
*/
public class PianoActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.piano);
piano_ = (PianoView)findViewById(R.id.piano);
volumeKnob_ = (KnobView)findViewById(R.id.volumeKnob);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (synthesizer_ == null) {
return;
}
if (position > 0) {
piano_.bindTo(synthesizer_, position - 1);
volumeKnob_.bindTo(synthesizer_, position - 1, Setting.VOLUME);
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
piano_.bindTo(synthesizer_, 0);
volumeKnob_.bindTo(synthesizer_, 0, Setting.VOLUME);
ArrayList<String> presetNames = new ArrayList<String>();
presetNames.add("");
synthesizer_.getPresetNames(presetNames);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_spinner_item, presetNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
}
private PianoView piano_;
private KnobView volumeKnob_;
private Spinner presetSpinner_;
}

@ -1,141 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.levien.synthesizer.R;
import com.levien.synthesizer.android.Storage;
import com.levien.synthesizer.android.widgets.score.ScoreView;
import com.levien.synthesizer.android.widgets.score.ScoreViewToolbar;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.music.Music.Score.Builder;
/**
* An Activity for editing or playing a score.
*/
public class ScoreActivity extends SynthActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.score);
logger_ = Logger.getLogger(getClass().getName());
scoreView_ = (ScoreView)findViewById(R.id.score);
scoreViewToolbar_ = (ScoreViewToolbar)findViewById(R.id.toolbar);
scoreViewToolbar_.setScoreView(scoreView_);
}
@Override
protected void onStart() {
super.onStart();
try {
Storage.openDefaultScore(scoreView_.getScore(), this.getApplicationContext());
scoreView_.invalidate();
scoreViewToolbar_.invalidate();
} catch (IOException e) {
logger_.log(Level.SEVERE, "Unable to open score.", e);
}
}
@Override
protected void onStop() {
try {
Storage.saveDefaultScore(scoreView_.getScore().build(), this.getApplicationContext());
} catch (IOException e) {
logger_.log(Level.SEVERE, "Unable to save score.", e);
}
super.onStop();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.score_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.new_score:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("New score...");
builder.setMessage("This will erase any unsaved work. Are you sure?");
builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
scoreView_.getScore().clear();
scoreView_.invalidate();
scoreViewToolbar_.invalidate();
dialog.dismiss();
}
});
builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
case R.id.open_score:
Storage.openScoreWithDialog(scoreView_.getScore(), new Storage.OpenScoreListener() {
public void onOpenScore(Builder score) {
scoreView_.invalidate();
scoreViewToolbar_.invalidate();
}
}, this);
return true;
case R.id.save_score:
Storage.saveScoreWithDialog(scoreView_.getScore().build(), this);
return true;
case R.id.piano:
this.startActivity(new Intent(this, PianoActivity.class));
return true;
case R.id.chord_grid:
this.startActivity(new Intent(this, ChordGridActivity.class));
return true;
case R.id.edit_instrument:
this.startActivity(new Intent(this, InstrumentListActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void onSynthConnected() {
final MidiListener synthMidi = synthesizerService_.getMidiListener();
scoreView_.bindTo(synthMidi);
}
private ScoreView scoreView_;
private ScoreViewToolbar scoreViewToolbar_;
private Logger logger_;
}

@ -1,189 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.service.SynthesizerService;
/**
* A base class for any Android Activity that wants to interact with the SynthesizerService.
*/
public abstract class SynthesizerActivity extends Activity {
/**
* Called when the synthesizer model changes.
*/
protected abstract void onSynthesizerUpdate(MultiChannelSynthesizer synth);
/**
* Creates a URI for a specific synthesizer path.
* @param path - The absolute path of the component.
*/
public static Uri makeUri(int channel, Setting... settings) {
StringBuilder uri = new StringBuilder("content://com.levien.synthesizer/" + channel + "/");
boolean first = true;
for (Setting setting : settings) {
if (!first) {
uri.append(',');
}
uri.append(setting.getNumber());
first = false;
}
return Uri.parse(uri.toString());
}
/**
* Returns the path part of the URI that invoked an activity.
*/
private static String getPath(Activity activity) {
Intent intent = activity.getIntent();
if (intent == null) {
Log.e(SynthesizerActivity.class.getName(),
"Attempted to get Intent module for SynthesizerActivity with no Intent.");
return null;
}
Uri uri = intent.getData();
if (uri == null) {
Log.e(SynthesizerActivity.class.getName(),
"Attempted to get Intent module for Intent with no URI: " + intent);
return null;
}
String path = uri.getPath();
if (path == null) {
Log.e(SynthesizerActivity.class.getName(),
"Attempted to get Intent module for URI with no path: " + uri);
return null;
}
if (path.startsWith("/")) {
path = path.substring(1);
}
return path;
}
/**
* Gets the modules specified by the URI for the intent given to this Activity.
* @return - The list of settings found, if any. Otherwise, null.
*/
public static Setting[] getIntentSettings(Activity activity) {
Setting[] settings = null;
String path = getPath(activity);
// Clip off the channel, if it's there...
if (path.indexOf('/') >= 0) {
path = path.substring(path.indexOf('/') + 1);
}
String[] parts = path.split(", *");
settings = new Setting[parts.length];
for (int i = 0; i < parts.length; ++i) {
try {
int id = Integer.parseInt(parts[i]);
settings[i] = Setting.valueOf(id);
} catch (NumberFormatException e) {
Log.e(SynthesizerActivity.class.getName(),
"Unable to convert number \"" + parts[i] + "\" in path: " + path);
}
}
return settings;
}
/**
* Gets the channel specified by the URI for the intent given to this Activity.
* @return - The channel in the intent, or 0 if none was found.
*/
public static int getIntentChannel(Activity activity) {
String path = getPath(activity);
int firstSlash = path.indexOf('/');
if (firstSlash < 0) {
Log.e(SynthesizerActivity.class.getName(),
"Unable to find channel number in path: " + path);
return 0;
}
String channelString = path.substring(0, firstSlash);
try {
return Integer.parseInt(channelString);
} catch (NumberFormatException e) {
Log.e(SynthesizerActivity.class.getName(),
"Unable to convert channel number \"" + channelString + "\" in path: " + path);
return 0;
}
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, SynthesizerService.class),
synthesizerConnection_, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(synthesizerConnection_);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.piano:
this.startActivity(new Intent(this, PianoActivity.class));
return true;
case R.id.chord_grid:
this.startActivity(new Intent(this, ChordGridActivity.class));
return true;
case R.id.edit_instrument:
this.startActivity(new Intent(this, InstrumentListActivity.class));
return true;
case R.id.compose:
this.startActivity(new Intent(this, ScoreActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private ServiceConnection synthesizerConnection_ = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// synthesizer_ = ISynthesizerService.Stub.asInterface(service);
synthesizer_ = ((SynthesizerService.LocalBinder)service).getSynthesizer();
SynthesizerActivity.this.onSynthesizerUpdate(synthesizer_);
}
public void onServiceDisconnected(ComponentName className) {
synthesizer_ = null;
SynthesizerActivity.this.onSynthesizerUpdate(synthesizer_);
}
};
protected MultiChannelSynthesizer synthesizer_ = null;
}

@ -1,68 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
import com.levien.synthesizer.android.widgets.waveform.WaveformRowView;
/**
* Activity for modifying tremolo.
* TODO(klimt): Add the ability to switch channels.
*/
public class TremoloActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tremolo);
piano_ = (PianoView)findViewById(R.id.piano);
waveformView_ = (WaveformRowView)findViewById(R.id.waveform);
rateKnob_ = (KnobView)findViewById(R.id.rateKnob);
depthKnob_ = (KnobView)findViewById(R.id.depthKnob);
attackKnob_ = (KnobView)findViewById(R.id.attackKnob);
decayKnob_ = (KnobView)findViewById(R.id.decayKnob);
sustainKnob_ = (KnobView)findViewById(R.id.sustainKnob);
releaseKnob_ = (KnobView)findViewById(R.id.releaseKnob);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
piano_.bindTo(synthesizer_, channel);
waveformView_.bindTo(synthesizer_, channel, Setting.TREMOLO_WAVEFORM);
rateKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_RATE);
depthKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_DEPTH);
attackKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_ATTACK);
decayKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_DECAY);
sustainKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_SUSTAIN);
releaseKnob_.bindTo(synthesizer_, channel, Setting.TREMOLO_RELEASE);
}
private PianoView piano_;
private WaveformRowView waveformView_;
private KnobView rateKnob_;
private KnobView depthKnob_;
private KnobView attackKnob_;
private KnobView decayKnob_;
private KnobView sustainKnob_;
private KnobView releaseKnob_;
}

@ -1,65 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.ui;
import android.os.Bundle;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
import com.levien.synthesizer.android.widgets.waveform.WaveformRowView;
/**
* Activity for modifying vibrato.
* TODO(klimt): Add the ability to switch channels.
*/
public class VibratoActivity extends SynthesizerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.vibrato);
piano_ = (PianoView)findViewById(R.id.piano);
waveformView_ = (WaveformRowView)findViewById(R.id.waveform);
rateKnob_ = (KnobView)findViewById(R.id.rateKnob);
attackKnob_ = (KnobView)findViewById(R.id.attackKnob);
decayKnob_ = (KnobView)findViewById(R.id.decayKnob);
sustainKnob_ = (KnobView)findViewById(R.id.sustainKnob);
releaseKnob_ = (KnobView)findViewById(R.id.releaseKnob);
}
@Override
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) {
int channel = getIntentChannel(this);
piano_.bindTo(synthesizer_, channel);
waveformView_.bindTo(synthesizer_, channel, Setting.VIBRATO_WAVEFORM);
rateKnob_.bindTo(synthesizer_, channel, Setting.VIBRATO_RATE);
attackKnob_.bindTo(synthesizer_, channel, Setting.VIBRATO_ATTACK);
decayKnob_.bindTo(synthesizer_, channel, Setting.VIBRATO_DECAY);
sustainKnob_.bindTo(synthesizer_, channel, Setting.VIBRATO_SUSTAIN);
releaseKnob_.bindTo(synthesizer_, channel, Setting.VIBRATO_RELEASE);
}
private PianoView piano_;
private WaveformRowView waveformView_;
private KnobView rateKnob_;
private KnobView attackKnob_;
private KnobView decayKnob_;
private KnobView sustainKnob_;
private KnobView releaseKnob_;
}

@ -1,329 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.music.Note;
import com.levien.synthesizer.android.widgets.piano.PianoViewListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* ChordGridView is an alternative interface for performing that has keys for chords amongst
* fundamentals arranged in a circle of fifths.
*/
public class ChordGridView extends View {
/**
* Basic android widget constructor.
*/
public ChordGridView(Context context, AttributeSet attrs) {
super(context, attrs);
// Get the xml attributes for this instance.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChordGridView);
firstOctave_ = a.getInteger(R.styleable.ChordGridView_octave, 4);
pressedRow_ = -1;
pressedColumn_ = -1;
drawingRect_ = new Rect();
path_ = new Path();
strokePaint_ = new Paint();
fillPaint_ = new Paint();
strokePaint_.setStyle(Style.STROKE);
fillPaint_.setStyle(Style.FILL);
}
/**
* Sets the listener that will receive events from this widget.
*/
public void setPianoViewListener(PianoViewListener pianoViewListener) {
pianoViewListener_ = pianoViewListener;
}
/**
* Signals the listener that a new note was pressed.
* @param logFrequency - the log frequency of the new note.
* @param retriggerIfOn - true if this is a new touch, rather than just moving.
*/
private void notifyNoteDown(double logFrequency, int finger, boolean retriggerIfOn) {
if (pianoViewListener_ != null) {
pianoViewListener_.noteDown(logFrequency, finger, retriggerIfOn, 1.0f);
}
}
/**
* Signals the listener that a note was released.
*/
private void notifyNoteUp(int finger) {
if (pianoViewListener_ != null) {
pianoViewListener_.noteUp(finger);
}
}
/**
* Handler for all touch events.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) {
int tileWidth = drawingRect_.width() / COLUMNS;
int tileHeight = drawingRect_.height() / ROWS;
pressedColumn_ = (int)(event.getX() - drawingRect_.left) / tileWidth;
pressedRow_ = (int)(event.getY() - drawingRect_.top) / tileHeight;
redraw = true;
getTileInfo(pressedRow_, pressedColumn_);
notifyNoteDown(Note.computeLog12TET(tileNote1_, tileOctave1_), 0, true);
notifyNoteDown(Note.computeLog12TET(tileNote2_, tileOctave2_), 1, true);
notifyNoteDown(Note.computeLog12TET(tileNote3_, tileOctave3_), 2, true);
} else if (actionCode == MotionEvent.ACTION_MOVE) {
int tileWidth = drawingRect_.width() / COLUMNS;
int tileHeight = drawingRect_.height() / ROWS;
int newPressedColumn_ = (int)(event.getX() - drawingRect_.left) / tileWidth;
int newPressedRow_ = (int)(event.getY() - drawingRect_.top) / tileHeight;
if (pressedColumn_ != newPressedColumn_ || pressedRow_ != newPressedRow_) {
pressedColumn_ = newPressedColumn_;
pressedRow_ = newPressedRow_;
redraw = true;
getTileInfo(pressedRow_, pressedColumn_);
notifyNoteDown(Note.computeLog12TET(tileNote1_, tileOctave1_), 0, false);
notifyNoteDown(Note.computeLog12TET(tileNote2_, tileOctave2_), 1, false);
notifyNoteDown(Note.computeLog12TET(tileNote3_, tileOctave3_), 2, false);
}
} else if (actionCode == MotionEvent.ACTION_UP) {
pressedColumn_ = -1;
pressedRow_ = -1;
notifyNoteUp(0);
notifyNoteUp(1);
notifyNoteUp(2);
redraw = true;
} else {
return super.onTouchEvent(event);
}
if (redraw) {
invalidate();
}
return true;
}
/**
* Draws the widget.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(drawingRect_);
int tileWidth = drawingRect_.width() / COLUMNS;
int tileHeight = drawingRect_.height() / ROWS;
for (int row = 0; row < ROWS; ++row) {
for (int column = 0; column < COLUMNS; ++column) {
getTileInfo(row, column);
int foreground;
int background;
if (Note.isNatural(tileNote1_)) {
if (row == pressedRow_ && column == pressedColumn_) {
foreground = Color.GREEN;
} else {
foreground = Color.WHITE;
}
background = Color.BLACK;
} else {
foreground = Color.BLACK;
if (row == pressedRow_ && column == pressedColumn_) {
background = Color.GREEN;
} else {
background = Color.WHITE;
}
}
int textColor = background;
fillPaint_.setColor(foreground);
strokePaint_.setColor(background);
canvas.drawRect(tileWidth * column,
tileHeight * row,
tileWidth * (column + 1),
tileHeight * (row + 1),
fillPaint_);
canvas.drawRect(tileWidth * column + 1,
tileHeight * row + 1,
tileWidth * (column + 1) - 2,
tileHeight * (row + 1) - 2,
strokePaint_);
if (tileIsMajor_) {
textColor = foreground;
strokePaint_.setColor(foreground);
fillPaint_.setColor(background);
path_.reset();
path_.moveTo(tileWidth * column + 15, tileHeight * (row + 1) - 15);
path_.lineTo(tileWidth * (column + 1) - 15, tileHeight * (row + 1) - 15);
path_.lineTo(tileWidth * column + tileWidth / 2, tileHeight * row + 15);
path_.close();
canvas.drawPath(path_, fillPaint_);
}
if (tileIsMinor_) {
textColor = foreground;
strokePaint_.setColor(foreground);
fillPaint_.setColor(background);
path_.reset();
path_.moveTo(tileWidth * column + 15, tileHeight * row + tileHeight / 2);
path_.lineTo(tileWidth * (column + 1) - 15, tileHeight * (row + 1) - 15);
path_.lineTo(tileWidth * (column + 1) - 15, tileHeight * row + 15);
path_.close();
canvas.drawPath(path_, fillPaint_);
}
if (tileNote1_ == Note.C && !tileIsMinor_ && !tileIsMajor_) {
fillPaint_.setColor(background);
canvas.drawCircle(tileWidth * column + tileWidth / 2,
tileHeight * row + tileHeight / 2,
10,
fillPaint_);
}
strokePaint_.setColor(textColor);
canvas.drawText(Note.getName(tileNote1_),
tileWidth * column + tileWidth / 2,
tileHeight * row + tileHeight / 2,
strokePaint_);
}
}
}
/**
* Populates the tile* fields for the tile at the given row and column.
*/
private void getTileInfo(int row, int column) {
int startIndex = 5 + firstOctave_ * 36;
int absoluteIndex = startIndex + row + column * 14;
if (absoluteIndex % 3 == 0) {
// Fundamental key.
tileOctave1_ = absoluteIndex / 36;
tileNote1_ = (absoluteIndex % 36) / 3;
tileOctave2_ = tileOctave1_;
tileNote2_ = tileNote1_;
tileOctave3_ = tileOctave1_;
tileNote3_ = tileNote1_;
tileIsMajor_ = false;
tileIsMinor_ = false;
} else if (absoluteIndex % 3 == 1) {
// Minor chord key.
tileOctave3_ = (int)((absoluteIndex - 1) / 36) + 1;
tileNote3_ = ((absoluteIndex - 1) % 36) / 3;
if (tileNote3_ >= 4) {
tileOctave2_ = tileOctave3_;
tileNote2_ = tileNote3_ - 4;
} else {
tileOctave2_ = tileOctave3_ - 1;
tileNote2_ = tileNote3_ + 8;
}
if (tileNote3_ >= 7) {
tileOctave1_ = tileOctave3_;
tileNote1_ = tileNote3_ - 7;
} else {
tileOctave1_ = tileOctave3_ - 1;
tileNote1_ = tileNote3_ + 5;
}
tileIsMajor_ = false;
tileIsMinor_ = true;
} else if (absoluteIndex % 3 == 2) {
// Major chord key.
tileOctave1_ = (int)((absoluteIndex + 1) / 36);
tileNote1_ = ((absoluteIndex + 1) % 36) / 3;
if (tileNote1_ < 8) {
tileOctave2_ = tileOctave1_;
tileNote2_ = tileNote1_ + 4;
} else {
tileOctave2_ = tileOctave1_ + 1;
tileNote2_ = tileNote1_ - 8;
}
if (tileNote1_ >= 7) {
tileOctave3_ = tileOctave1_;
tileNote3_ = tileNote1_ + 7;
} else {
tileOctave3_ = tileOctave1_ + 1;
tileNote3_ = tileNote1_ - 5;
}
tileIsMajor_ = true;
tileIsMinor_ = false;
}
}
/**
* Connects the ChordGridView to a Synthesizer.
* @synth - The synthesizer to connect to.
*/
public void bindTo(final MultiChannelSynthesizer synth, final int channel) {
this.setPianoViewListener(new PianoViewListener() {
public void noteDown(double logFrequency, int finger, boolean retriggerIfOn,
float pressure) {
synth.getChannel(channel).setPitch(logFrequency, finger);
synth.getChannel(channel).turnOn(retriggerIfOn, finger);
}
public void noteUp(int finger) {
synth.getChannel(channel).turnOff(finger);
}
});
}
/**
* Populated by getTileInfo, these fields will contain the info for a particular key.
*/
private int tileOctave1_;
private int tileOctave2_;
private int tileOctave3_;
private int tileNote1_;
private int tileNote2_;
private int tileNote3_;
private boolean tileIsMajor_;
private boolean tileIsMinor_;
// The coordinates of the key currently being pressed.
private int pressedColumn_;
private int pressedRow_;
// The current octave the keyboard is on.
private int firstOctave_;
// The listener to receive key events.
private PianoViewListener pianoViewListener_;
// These are basically stack variables for onDraw. They're member variables only so that we can
// avoid reallocating them every time the keyboard is redrawn.
//
// The most recent screen rect that this keyboard was drawn into.
private Rect drawingRect_;
private Path path_;
private Paint strokePaint_;
private Paint fillPaint_;
private static final int ROWS = 8;
private static final int COLUMNS = 5;
}

@ -1,95 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import com.levien.synthesizer.core.music.Note;
/**
* One of the black (non-natural) keys on the piano.
*/
public class BlackPianoKey extends NotePianoKey {
/**
* Creates a new key.
* @param piano - the piano this key is on.
* @param octaveOffset - octave of the key, relative to the leftmost octave of the piano.
* @param key - offset of the key from the start of the octave.
*/
public BlackPianoKey(PianoView piano, int octave, int key) {
super(piano, octave, key);
}
/**
* Sets rect_ to the position of this key, based on the drawing rect of the piano it's on.
* @param drawingRect - the position of the piano itself.
* @param octaves - the number of octaves visible on the piano keyboard.
*/
public void layout(Rect drawingRect, int octaves) {
int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves);
int blackKeyWidth = getBlackKeyWidth(drawingRect, octaves);
rect_.top = 0;
rect_.bottom = rect_.top + getBlackKeyHeight(drawingRect);
rect_.left = ((octaveOffset_ * WHITE_KEYS.length + key_ + 2) * whiteKeyWidth) -
(blackKeyWidth/2);
rect_.right = rect_.left + blackKeyWidth;
}
/**
* Returns the log frequency of the note of the key.
*/
public double getLogFrequency() {
return Note.computeLog12TET(BLACK_KEYS[key_], octaveOffset_ + piano_.getFirstOctave());
}
/**
* Draws the key in the current rect_.
*/
public void draw(Canvas canvas) {
strokePaint_.setColor(Color.BLACK);
if (isPressed()) {
fillPaint_.setColor(Color.GREEN);
} else {
fillPaint_.setColor(Color.BLACK);
}
canvas.drawRect(rect_, fillPaint_);
canvas.drawRect(rect_, strokePaint_);
}
/**
* Returns true if this is one of the black key positions that should actually have a key.
*/
public static boolean isValid(int note) {
return BLACK_KEYS[note] != Note.NONE;
}
/**
* Utility function to calculate the width that a standard black key on this keyboard should be.
*/
protected static int getBlackKeyWidth(Rect drawingRect, int octaves) {
return (getWhiteKeyWidth(drawingRect, octaves) * 2) / 3;
}
/**
* Utility function to calculate the height that a standard black key on this keyboard should be.
*/
protected static int getBlackKeyHeight(Rect drawingRect) {
return getWhiteKeyHeight(drawingRect) / 2;
}
}

@ -1,52 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
/**
* Abstract base class for keys on the piano that play a note when pressed.
*/
public abstract class NotePianoKey extends PianoKey {
/**
* Creates a new key.
* @param piano - the piano this key is on.
* @param octaveOffset - octave of the key, relative to the leftmost octave of the piano.
* @param key - offset of the key from the start of the octave.
*/
public NotePianoKey(PianoView piano, int octaveOffset, int key) {
super(piano);
octaveOffset_ = octaveOffset;
key_ = key;
}
/**
* Called when the pressed_ state has changed.
*/
protected void onPressedChanged(boolean move) {
}
/**
* Returns the log frequency of the note of the key.
*/
abstract protected double getLogFrequency();
// Octave of the key, relative to the leftmost octave of the piano.
protected int octaveOffset_;
// Offset of the key from the start of the octave.
protected int key_;
}

@ -1,111 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.Rect;
/**
* A key on the piano for changing the octave up or down.
*/
public class OctavePianoKey extends PianoKey {
/**
* Creates the key.
* @param piano - the piano this key is on.
* @param delta - how the octave should change when this key is pressed.
*/
public OctavePianoKey(PianoView piano, int delta) {
super(piano);
arrow_ = new Path();
delta_ = delta;
}
/**
* Sets rect_ to the position of this key, based on the drawing rect of the piano it's on.
* @param drawingRect - the position of the piano itself.
* @param octaves - the number of octaves visible on the piano keyboard.
*/
public void layout(Rect drawingRect, int octaves) {
int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves);
rect_.top = 0;
rect_.bottom = getWhiteKeyHeight(drawingRect);
if (delta_ <= 0) {
rect_.left = 0;
rect_.right = rect_.left + whiteKeyWidth;
} else {
rect_.right = drawingRect.right;
rect_.left = rect_.right - whiteKeyWidth;
}
}
/**
* Returns true if the current octave of the piano could be changed by delta and still be valid.
*/
private boolean isValid() {
return (piano_.getFirstOctave() + delta_ >= 0 &&
piano_.getFirstOctave() + piano_.getOctaves() + delta_ <= 8);
}
/**
* Draws the key in the current rect_.
*/
public void draw(Canvas canvas) {
strokePaint_.setColor(Color.BLACK);
fillPaint_.setColor(Color.BLACK);
if (isPressed() && isValid()) {
fillPaint_.setColor(Color.GREEN);
}
canvas.drawRect(rect_, fillPaint_);
canvas.drawRect(rect_, strokePaint_);
// Draw an arrow in the direction of the delta.
if (isValid()) {
arrow_.reset();
if (delta_ <= 0) {
arrow_.moveTo(rect_.left + 2, rect_.height() / 2);
arrow_.lineTo(rect_.right - 2, rect_.height() / 2 - 20);
arrow_.lineTo(rect_.right - 2, rect_.height() / 2 + 20);
} else {
arrow_.moveTo(rect_.right - 2, rect_.height() / 2);
arrow_.lineTo(rect_.left + 2, rect_.height() / 2 - 20);
arrow_.lineTo(rect_.left + 2, rect_.height() / 2 + 20);
}
arrow_.close();
fillPaint_.setColor(Color.WHITE);
canvas.drawPath(arrow_, fillPaint_);
}
}
/**
* Called when the pressed_ state has changed.
*/
@Override
protected void onPressedChanged(boolean move) {
if (isPressed() && isValid()) {
piano_.changeOctave(delta_);
}
}
// This is just used for drawing, but we don't want to pay to reallocate it every time.
private Path arrow_;
// How the octave should change when this key is pressed.
private int delta_;
}

@ -1,179 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.piano;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;
import com.levien.synthesizer.core.music.Note;
/**
* PianoKey is the abstract base class for any key on the piano.
* It keeps track of whether the key is currently being pressed.
*/
public abstract class PianoKey {
public PianoKey(PianoView piano) {
piano_ = piano;
pressed_ = new boolean[PianoView.FINGERS];
rect_ = new Rect();
for (int i = 0; i < pressed_.length; ++i) {
pressed_[i] = false;
}
// Set up some default objects for the key to draw itself with.
fillPaint_ = new Paint();
strokePaint_ = new Paint();
fillPaint_.setStyle(Paint.Style.FILL);
strokePaint_.setStyle(Paint.Style.STROKE);
strokePaint_.setColor(Color.BLACK);
float strokeWidth = 1.0f * piano.getResources().getDisplayMetrics().density;
strokePaint_.setStrokeWidth(strokeWidth);
}
/**
* Sets rect_ to the position of this key, based on the drawing rect of the piano it's on.
* @param drawingRect - the position of the piano itself.
* @param octaves - the number of octaves visible on the piano keyboard.
*/
abstract public void layout(Rect drawingRect, int octaves);
/**
* Draws the key in the current rect_.
*/
abstract public void draw(Canvas canvas);
/**
* Called when the key's pressed_ state has changed.
* @param move - true if the key became pressed because the touch moved onto it.
*/
abstract protected void onPressedChanged(boolean move);
/**
* Returns true if the given co-ordinate is inside the key's current rect_.
*/
public boolean contains(int x_, int y_) {
return rect_.contains(x_, y_);
}
/**
* Returns true if any finger is pressing this key.
*/
public boolean isPressed() {
for (int i = 0; i < pressed_.length; ++i) {
if (pressed_[i]) {
return true;
}
}
return false;
}
/**
* Called when a finger has touched down onto this key.
* Returns true iff whether the pressed state changed.
*/
final public boolean onTouchDown(int finger) {
if (finger >= pressed_.length) {
Log.e(getClass().getName(),
"Finger " + finger + " was pressed down, but PianoKey only supports " +
pressed_.length + " fingers.");
}
boolean wasPressed = isPressed();
pressed_[finger] = true;
if (!wasPressed) {
onPressedChanged(false);
return true;
}
return false;
}
/**
* Called on a touch event where this key is not being touched. It may already be up.
* Returns true iff whether the pressed state changed.
*/
final public boolean onTouchUp(int finger) {
if (finger >= pressed_.length) {
Log.e(getClass().getName(),
"Finger " + finger + " was released, but PianoKey only supports " +
pressed_.length + " fingers.");
}
boolean wasPressed = isPressed();
pressed_[finger] = false;
boolean isPressed = isPressed();
if (wasPressed && !isPressed) {
onPressedChanged(false);
return true;
}
return false;
}
/**
* Called when there's a touch event where the finger was moved onto this key.
* Returns true iff whether the pressed state changed.
*/
final public boolean onTouchMoved(int finger) {
if (finger >= pressed_.length) {
Log.e(getClass().getName(),
"Finger " + finger + " was pressed down, but PianoKey only supports " +
pressed_.length + " fingers.");
}
boolean wasPressed = isPressed();
pressed_[finger] = true;
if (!wasPressed) {
onPressedChanged(true);
return true;
}
return false;
}
/**
* Utility function to calculate the width that a standard white key on this keyboard should be.
*/
protected static int getWhiteKeyWidth(Rect drawingRect, int octaves) {
// It's +2 to reserve space for the octave-up/down buttons.
return drawingRect.width() / ((WHITE_KEYS.length * octaves) + 2);
}
/**
* Utility function to calculate the height that a standard white key on this keyboard should be.
*/
protected static int getWhiteKeyHeight(Rect drawingRect) {
return drawingRect.height();
}
// The piano this key is on.
protected PianoView piano_;
// Is each keys currently being pressed?
protected boolean[] pressed_;
// The area this key occupies.
protected Rect rect_;
// Objects for subclasses to use for painting, just so they don't have to reallocate every time.
protected Paint fillPaint_;
protected Paint strokePaint_;
// Constants to map notes onto the keys.
protected static final int WHITE_KEYS[] = {
Note.C, Note.D, Note.E, Note.F, Note.G, Note.A, Note.B };
protected static final int BLACK_KEYS[] = {
Note.C_SHARP, Note.E_FLAT, Note.NONE, Note.F_SHARP, Note.A_FLAT, Note.B_FLAT, Note.NONE };
}

@ -1,458 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.music.Note;
/**
* PianoView is a UI widget that simulates a music keyboard.
*/
public class PianoView extends View {
/**
* Basic android widget constructor.
*/
public PianoView(Context context, AttributeSet attrs) {
super(context, attrs);
// Get the xml attributes for this instance.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PianoView);
octaves_ = a.getInteger(R.styleable.PianoView_octaves, 1);
firstOctave_ = a.getInteger(R.styleable.PianoView_first_octave, 4);
a.recycle();
// Set up basic drawing structs, just so we don't have to allocate this later when we draw.
drawingRect_ = new Rect();
// Generate the set of keys. There are 12 music keys per octave, plus the octave change button
// on either end.
keys_ = new PianoKey[12 * octaves_ + 2];
int key = 0;
// Create the white keys.
for (int octave = 0; octave < octaves_; ++octave) {
for (int note = 0; note < 7; ++note) {
keys_[key++] = new WhitePianoKey(this, octave, note);
}
}
// Create the black keys.
for (int octave = 0; octave < octaves_; ++octave) {
for (int note = 0; note < 7; ++note) {
if (BlackPianoKey.isValid(note)) {
keys_[key++] = new BlackPianoKey(this, octave, note);
}
}
}
// Create the octave changing keys.
keys_[key++] = new OctavePianoKey(this, -1);
keys_[key++] = new OctavePianoKey(this, 1);
// The listener will have to be set later.
pianoViewListener_ = null;
}
/**
* Returns the absolute octave of the left-most key.
*/
public int getFirstOctave() {
return firstOctave_;
}
/**
* Returns the number of octaves covered by all of the keys.
*/
public int getOctaves() {
return octaves_;
}
/**
* Shifts the octave of all of the keys.
* @param delta - The number (and direction) of octaves to shift by.
*/
public void changeOctave(int delta) {
firstOctave_ += delta;
}
/**
* Sets the listener that will receive events from this widget.
*/
public void setPianoViewListener(PianoViewListener pianoViewListener) {
pianoViewListener_ = pianoViewListener;
}
/**
* Signals the listener that a new note was pressed.
* @param logFrequency - the log frequency of the new note.
* @param retriggerIfOn - true if this is a new touch, rather than just moving.
*/
private void notifyNoteDown(double logFrequency, int finger, boolean retriggerIfOn,
float pressure) {
if (pianoViewListener_ != null) {
pianoViewListener_.noteDown(logFrequency, finger, retriggerIfOn, pressure);
}
}
/**
* Signals the listener that a note was released.
*/
private void notifyNoteUp(int finger) {
if (pianoViewListener_ != null) {
pianoViewListener_.noteUp(finger);
}
}
/**
* Draws the widget.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(drawingRect_);
for (int i = 0; i < keys_.length; ++i) {
keys_[i].layout(drawingRect_, octaves_);
}
for (int i = 0; i < keys_.length; ++i) {
keys_[i].draw(canvas);
}
}
/**
* Called to handle touch down events.
* Returns true iff we need to redraw.
*/
protected boolean onTouchDown(int finger, int x, int y, float pressure) {
// Look through keys from top to bottom, and set the first one found as down, the rest as up.
PianoKey keyDown = null;
boolean redraw = false;
for (int i = keys_.length - 1; i >= 0; --i) {
if (keyDown != null) {
// If we already found a key that's being touched, then none of the rest can be.
redraw |= keys_[i].onTouchUp(finger);
} else if (keys_[i].contains(x, y)) {
// This key is being touched.
redraw |= keys_[i].onTouchDown(finger);
keyDown = keys_[i];
} else {
// This key is not being touched.
redraw |= keys_[i].onTouchUp(finger);
}
}
if (!usePressure_) {
pressure = 0.5f;
}
if (keyDown instanceof NotePianoKey) {
notifyNoteDown(((NotePianoKey)keyDown).getLogFrequency(), finger, true, pressure);
}
return redraw;
}
/**
* Called to handle touch move events.
*/
protected boolean onTouchMove(int finger, int x, int y, float pressure) {
// Look through keys from top to bottom, and set the first one found as moved, the rest as up.
PianoKey keyDown = null;
boolean redraw = false;
boolean wasPressed = false;
for (int i = keys_.length - 1; i >= 0; --i) {
if (keyDown != null) {
// If we already found a key that's being touched, then none of the rest can be.
redraw |= keys_[i].onTouchUp(finger);
} else if (keys_[i].contains(x, y)) {
// This key is being pressed.
wasPressed = keys_[i].isPressed();
redraw |= keys_[i].onTouchMoved(finger);
keyDown = keys_[i];
} else {
// This key is not being pressed.
redraw |= keys_[i].onTouchUp(finger);
}
}
if (keyDown instanceof NotePianoKey) {
if (!usePressure_) {
pressure = 0.5f;
}
if (!wasPressed) {
notifyNoteDown(((NotePianoKey)keyDown).getLogFrequency(), finger, false, pressure);
}
} else {
notifyNoteUp(finger);
}
return redraw;
}
/**
* Called to handle touch up events.
*/
protected boolean onTouchUp(int finger) {
// Set all keys as up.
boolean redraw = false;
for (int i = 0; i < keys_.length; ++i) {
redraw |= keys_[i].onTouchUp(finger);
}
notifyNoteUp(finger);
return redraw;
}
/**
* Handler for all touch events.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
//Log.i("synth", "actionCode = " + actionCode);
boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0);
if (pointerId < FINGERS) {
int x = (int)event.getX();
int y = (int)event.getY();
float pressure = event.getPressure();
redraw |= onTouchDown(pointerId, x, y, pressure);
} else {
Log.i("synth", "Discarded ACTION_DOWN pointerId=" + pointerId);
}
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
if (pointerId < FINGERS && pointerId >= 0) {
int x = (int)event.getX(pointerIndex);
int y = (int)event.getY(pointerIndex);
float pressure = event.getPressure(pointerIndex);
redraw |= onTouchDown(pointerId, x, y, pressure);
} else {
Log.i("synth", "Discarded ACTION_POINTER_DOWN pointerId=" + pointerId);
}
} else if (actionCode == MotionEvent.ACTION_MOVE) {
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
int pointerId = event.getPointerId(pointerIndex);
if (pointerId >= FINGERS) {
continue;
}
if (pointerIndex >= 0) {
int x = (int)event.getX(pointerIndex);
int y = (int)event.getY(pointerIndex);
float pressure = event.getPressure(pointerIndex);
redraw |= onTouchMove(pointerId, x, y, pressure);
}
}
} else if (actionCode == MotionEvent.ACTION_UP) {
int pointerId = event.getPointerId(0);
if (pointerId < FINGERS) {
redraw |= onTouchUp(pointerId);
}
// Clean up any other pointers that have disappeared.
for (pointerId = 0; pointerId < FINGERS; ++pointerId) {
boolean found = false;
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
if (pointerId == event.getPointerId(pointerIndex)) {
found = true;
break;
}
}
if (!found) {
boolean thisRedraw = onTouchUp(pointerId);
if (thisRedraw) {
Log.i("synth", "ACTION_UP cleaned up pointerId=" + pointerId);
}
redraw |= thisRedraw;
}
}
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex);
if (pointerId < FINGERS) {
redraw |= onTouchUp(pointerId);
}
// Clean up any other pointers that have disappeared. Note: this is probably not necessary.
for (pointerId = 0; pointerId < FINGERS; ++pointerId) {
boolean found = false;
for (pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
if (pointerId == event.getPointerId(pointerIndex)) {
found = true;
break;
}
}
if (!found) {
boolean thisRedraw = onTouchUp(pointerId);
if (thisRedraw) {
Log.i("synth", "ACTION_POINTER_UP cleaned up pointerId=" + pointerId);
}
redraw |= thisRedraw;
}
}
} else {
return super.onTouchEvent(event);
}
if (redraw) {
invalidate();
}
return true;
}
/**
* Layout measurement for this widget.
* This method just sets a basic minimum size and makes the widget maximized otherwise.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
height = 10;
break;
}
setMeasuredDimension(width, height);
}
/**
* Connects the PianoView to a Synthesizer.
* @synth - The synthesizer to connect to.
* @channel - Which of the synthesizer's channels to bind to.
*/
public void bindTo(final MultiChannelSynthesizer synth, final int channel) {
this.setPianoViewListener(new PianoViewListener() {
public void noteDown(double logFrequency, int finger, boolean retriggerIfOn,
float pressure) {
synth.getChannel(channel).setPitch(logFrequency, finger);
synth.getChannel(channel).turnOn(retriggerIfOn, finger);
}
public void noteUp(int finger) {
synth.getChannel(channel).turnOff(finger);
}
});
}
/**
* Connects the PianoView to an MidiListener.
*/
public void bindTo(final MidiListener midiListener) {
this.setPianoViewListener(new PianoViewListener() {
{
fingerMap_ = new HashMap<Integer, Integer>();
}
public void noteDown(double logFrequency, int finger, boolean retriggerIfOn,
float pressure) {
noteUp(finger);
int midiNote = Note.getKeyforLog12TET(logFrequency);
fingerMap_.put(finger, midiNote);
int midiPressure = Math.max(1, Math.min(127, (int)(127 * pressure)));
midiListener.onNoteOn(0, midiNote, midiPressure);
}
public void noteUp(int finger) {
if (fingerMap_.containsKey(finger)) {
int midiNote = fingerMap_.get(finger);
fingerMap_.remove(finger);
midiListener.onNoteOff(0, midiNote, 64);
}
}
private Map<Integer, Integer> fingerMap_;
});
}
public void setNoteOn(int note, boolean on) {
// This is somewhat painful, hopefully can be done better.
for (int i = 0; i < keys_.length; i++) {
PianoKey key = keys_[i];
if (key instanceof NotePianoKey) {
int midiNote = Note.getKeyforLog12TET(((NotePianoKey) key).getLogFrequency());
if (midiNote == note) {
boolean redraw;
if (on) {
// using the last finger is something of a hack
redraw = key.onTouchDown(FINGERS - 1);
} else {
redraw = key.onTouchUp(FINGERS - 1);
}
if (redraw) {
invalidate();
}
break;
}
}
}
}
// The most recent screen rect that this keyboard was drawn into.
//
// This is basically a stack variable for onDraw. It's a member variable only so that we can
// avoid reallocating them every time the keyboard is redrawn.
private Rect drawingRect_;
// The set of keys on the keyboard.
private PianoKey[] keys_;
// The current octave the keyboard is on.
private int firstOctave_;
// The total number of octaves the keyboard displays at any one time.
private final int octaves_;
// The listener to receive key events.
private PianoViewListener pianoViewListener_;
// The number of simultaneous fingers supported by this control.
protected static final int FINGERS = 10;
// Whether to use pressure (doesn't work well on all hardware)
private boolean usePressure_ = false;
}

@ -1,32 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
// Simple interface for listening to piano widget events.
public interface PianoViewListener {
/**
* A note was pressed.
* @param logFrequency - the log frequency of the note pressed.
* @param retriggerIfOn - true if this is a new touch, rather than just moving.
*/
void noteDown(double logFrequency, int finger, boolean retriggerIfOn, float pressure);
/**
* The note was released.
*/
void noteUp(int finger);
}

@ -1,72 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.piano;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import com.levien.synthesizer.core.music.Note;
/**
* A white key on the piano.
*/
public class WhitePianoKey extends NotePianoKey {
/**
* Creates a new key.
* @param piano - the piano this key is on.
* @param octaveOffset - octave of the key, relative to the leftmost octave of the piano.
* @param key - offset of the key from the start of the octave.
*/
public WhitePianoKey(PianoView piano, int octave, int key) {
super(piano, octave, key);
}
/**
* Sets rect_ to the position of this key, based on the drawing rect of the piano it's on.
* @param drawingRect - the position of the piano itself.
* @param octaves - the number of octaves visible on the piano keyboard.
*/
public void layout(Rect drawingRect, int octaves) {
int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves);
rect_.top = 0;
rect_.bottom = getWhiteKeyHeight(drawingRect);
rect_.left = ((octaveOffset_ * WHITE_KEYS.length + key_ + 1) * whiteKeyWidth);
rect_.right = rect_.left + whiteKeyWidth;
}
/**
* Returns the log frequency of the note of the key.
*/
public double getLogFrequency() {
return Note.computeLog12TET(WHITE_KEYS[key_], octaveOffset_ + piano_.getFirstOctave());
}
/**
* Draws the key in the current rect_.
*/
public void draw(Canvas canvas) {
strokePaint_.setColor(Color.BLACK);
if (isPressed()) {
fillPaint_.setColor(Color.GREEN);
} else {
fillPaint_.setColor(Color.WHITE);
}
canvas.drawRect(rect_, fillPaint_);
canvas.drawRect(rect_, strokePaint_);
}
}

@ -1,490 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.music.Music.Event;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
/**
* A tool for editing the events in a score. If the user touches an event, they can move it or
* resize it. If the event is already selected, all selected events will be moved or resized in the
* same way. If the user touches outside any events, a selection rectangle allows them to select
* one or more events.
*/
public class EditEventTool extends ScoreViewTool {
/**
* Creates a new EditEventTool with the given context for loading resources.
*/
EditEventTool(Context context) {
logger_ = Logger.getLogger(getClass().getName());
// Set up basic drawing structs, just so we don't have to allocate this later when we draw.
paint_ = new Paint();
path_ = new Path();
selection_ = new Rect();
trashVisible_ = false;
trashRect_ = new Rect();
trashIcon_ = context.getResources().getDrawable(R.drawable.trash);
icon_ = context.getResources().getDrawable(R.drawable.arrow);
}
/**
* Starts this tool editing a particular event, and sets it to invoke another tool when done.
* Called by NewEventTool to handle sizing the tool as it's being created.
* @param view - The ScoreView for this tool.
* @param event - The event to start editing.
* @param physicalX - The current x of the user's finger, in screen coordinates.
* @param physicalY -The current y of the user's finger, in screen coordinates.
* @param nextTool - The tool to select when the user is done editing this event. (on touch up)
*/
public void pickupEvent(ScoreView view,
Event.Builder event,
int physicalX,
int physicalY,
ScoreViewTool nextTool) {
// Save the tool to bring up after this operation is complete.
nextTool_ = nextTool;
// De-select everything.
for (int j = 0; j < view.getScore().getEventCount(); ++j) {
view.getScore().getEventBuilder(j).setSelected(false);
}
// Select just the one that was picked up.
event.setSelected(true);
mode_ = RESIZING_RIGHT;
// Make sure to snap the event.
if (!event.hasUnsnappedStart()) {
event.setUnsnappedStart(event.getStart());
}
if (!event.hasUnsnappedEnd()) {
event.setUnsnappedEnd(event.getEnd());
}
if (view.getSnapTo() != 0) {
event.setStart(((int)(event.getUnsnappedStart() / view.getSnapTo())) * view.getSnapTo());
event.setEnd(((int)(event.getUnsnappedEnd() / view.getSnapTo())) * view.getSnapTo());
} else {
event.setStart(event.getUnsnappedStart());
event.setEnd(event.getUnsnappedEnd());
}
// Store the coordinates where it was picked up.
double time = view.getTimeAt(physicalX);
double note = view.getNoteAt(physicalY);
previousTime_ = time;
previousNote_ = note;
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
icon_.setBounds(rect);
icon_.draw(canvas);
}
/**
* Called on finger down.
*/
private void onTouchDown(ScoreView view, int physicalX, int physicalY) {
double time = view.getTimeAt(physicalX);
double note = view.getNoteAt(physicalY);
// When we're done editing this item, this tool stays selected.
nextTool_ = this;
// See if there's an event being touched.
Event.Builder event = view.getEventAt(physicalX, physicalY);
if (event != null) {
if (!event.getSelected()) {
// De-select everything.
for (int j = 0; j < view.getScore().getEventCount(); ++j) {
view.getScore().getEventBuilder(j).setSelected(false);
}
// Select just the one that was pressed.
event.setSelected(true);
}
// Compute the handle size.
int physicalHandleSize = view.getNoteY(0) - view.getNoteY(1);
double handleWidth = view.getTimeAt(physicalHandleSize) - view.getTimeAt(0);
boolean largeEnough = event.getEnd() - event.getStart() > handleWidth * 2;
// See if any handle was touched.
if (largeEnough && time <= event.getStart() + handleWidth) {
mode_ = RESIZING_LEFT;
} else if (largeEnough && time >= event.getEnd() - handleWidth) {
mode_ = RESIZING_RIGHT;
} else {
mode_ = MOVING;
}
} else {
// De-select everything.
for (int j = 0; j < view.getScore().getEventCount(); ++j) {
view.getScore().getEventBuilder(j).setSelected(false);
}
selection_.left = physicalX;
selection_.right = physicalX;
selection_.top = physicalY;
selection_.bottom = physicalY;
mode_ = SELECTING;
}
view.invalidate();
previousTime_ = time;
previousNote_ = note;
}
/**
* Called on finger movements.
*/
private void onTouchMove(ScoreView view, int physicalX, int physicalY) {
double time = view.getTimeAt(physicalX);
double note = view.getNoteAt(physicalY);
double deltaTime = time - previousTime_;
double deltaNote = note - previousNote_;
switch (mode_) {
case RESIZING_RIGHT: {
for (int i = 0; i < view.getScore().getEventCount(); ++i) {
Event.Builder event = view.getScore().getEventBuilder(i);
if (!event.getSelected()) {
continue;
}
if (!event.hasUnsnappedEnd()) {
event.setUnsnappedEnd(event.getEnd());
}
event.setUnsnappedEnd(event.getUnsnappedEnd() + deltaTime);
if (view.getSnapTo() != 0) {
event.setEnd(((int)(event.getUnsnappedEnd() / view.getSnapTo())) * view.getSnapTo());
} else {
event.setEnd(event.getUnsnappedEnd());
}
if (event.getEnd() <= event.getStart()) {
event.setEnd(event.getStart() + 1/32.0f);
}
}
break;
}
case RESIZING_LEFT: {
for (int i = 0; i < view.getScore().getEventCount(); ++i) {
Event.Builder event = view.getScore().getEventBuilder(i);
if (!event.getSelected()) {
continue;
}
if (!event.hasUnsnappedStart()) {
event.setUnsnappedStart(event.getStart());
}
event.setUnsnappedStart(event.getUnsnappedStart() + deltaTime);
if (view.getSnapTo() != 0) {
event.setStart(((int)(event.getUnsnappedStart() / view.getSnapTo())) * view.getSnapTo());
} else {
event.setStart(event.getUnsnappedStart());
}
if (event.getStart() >= event.getEnd()) {
event.setEnd(event.getEnd() - 1/32.0f);
}
}
break;
}
case MOVING: {
for (int i = 0; i < view.getScore().getEventCount(); ++i) {
Event.Builder event = view.getScore().getEventBuilder(i);
if (!event.getSelected()) {
continue;
}
if (!event.hasUnsnappedStart()) {
event.setUnsnappedStart(event.getStart());
}
event.setUnsnappedStart(event.getUnsnappedStart() + deltaTime);
if (view.getSnapTo() != 0) {
event.setStart(((int)(event.getUnsnappedStart() / view.getSnapTo())) *
view.getSnapTo());
} else {
event.setStart(event.getUnsnappedStart());
}
if (!event.hasUnsnappedEnd()) {
event.setUnsnappedEnd(event.getEnd());
}
event.setUnsnappedEnd(event.getUnsnappedEnd() + deltaTime);
if (view.getSnapTo() != 0) {
event.setEnd(((int)(event.getUnsnappedEnd() / view.getSnapTo())) * view.getSnapTo());
} else {
event.setEnd(event.getUnsnappedEnd());
}
if (event.getEnd() <= event.getStart()) {
event.setEnd(event.getStart() + 1/32.0f);
}
if (!event.hasUnsnappedKey()) {
event.setUnsnappedKey(event.getKey());
}
event.setUnsnappedKey(event.getUnsnappedKey() + deltaNote);
event.setKey((int)event.getUnsnappedKey());
trashVisible_ = true;
trashRect_.top = view.getDrawingRect().top;
trashRect_.bottom = view.getDrawingRect().top +
Math.max(200, trashIcon_.getIntrinsicHeight());
trashRect_.left = view.getDrawingRect().right -
Math.max(200, trashIcon_.getIntrinsicWidth());
trashRect_.right = view.getDrawingRect().right;
trashIcon_.setBounds(trashRect_);
}
break;
}
case SELECTING: {
// Update the selection rectangle.
selection_.right = physicalX;
selection_.bottom = physicalY;
// Update event selections.
for (int i = 0; i < view.getScore().getEventCount(); ++i) {
Event.Builder event = view.getScore().getEventBuilder(i);
if (selection_.intersects(view.getTimeX(event.getStart()),
view.getNoteY(event.getKey() + 1),
view.getTimeX(event.getEnd()),
view.getNoteY(event.getKey()))) {
view.getScore().getEventBuilder(i).setSelected(true);
} else {
view.getScore().getEventBuilder(i).setSelected(false);
}
}
break;
}
}
view.invalidate();
previousTime_ = time;
previousNote_ = note;
}
/**
* Called on finger up.
*/
private void onTouchUp(ScoreView view, int physicalX, int physicalY) {
trashVisible_ = false;
if (trashRect_.contains(physicalX, physicalY)) {
for (int i = view.getScore().getEventCount() - 1; i >= 0; --i) {
if (view.getScore().getEventBuilder(i).getSelected()) {
view.getScore().removeEvent(i);
}
}
}
// Make all snapping permanent.
for (int i = 0; i < view.getScore().getEventCount(); ++i) {
Event.Builder event = view.getScore().getEventBuilder(i);
event.clearUnsnappedStart();
event.clearUnsnappedEnd();
event.clearUnsnappedKey();
}
mode_ = NONE;
view.setTool(nextTool_);
view.invalidate();
}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
@Override
public boolean onTouch(ScoreView view, MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
if (actionCode == MotionEvent.ACTION_DOWN) {
pointerId_ = event.getPointerId(0);
onTouchDown(view, (int)event.getX(), (int)event.getY());
return true;
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
return false;
} else if (actionCode == MotionEvent.ACTION_MOVE) {
// Find the current positions of the fingers.
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
int pointerId = event.getPointerId(pointerIndex);
if (pointerId >= 0 && pointerId == pointerId_) {
int physicalX = (int)event.getX(pointerIndex);
int physicalY = (int)event.getY(pointerIndex);
onTouchMove(view, physicalX, physicalY);
return true;
}
}
return false;
} else if (actionCode == MotionEvent.ACTION_UP) {
onTouchUp(view, (int)event.getX(), (int)event.getY());
return true;
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
return false;
} else {
return false;
}
}
/**
* Called after each event is drawn, to give this tool a chance to draw over it.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param event - The event that was drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
@Override
public void afterDrawEvent(Event event,
Canvas canvas,
Rect rect) {
if (rect.right >= rect.left + rect.height() * 2) {
// Draw left arrow.
float margin = 1.0f;
paint_.setStrokeWidth(1.0f);
paint_.setStyle(Paint.Style.STROKE);
if (event.hasKeyEvent()) {
paint_.setColor(Color.BLACK);
} else {
paint_.setColor(Color.WHITE);
}
canvas.drawRect(rect.left, rect.top,
rect.left + rect.height(), rect.top + rect.height(),
paint_);
paint_.setStyle(Paint.Style.FILL);
path_.reset();
path_.moveTo(rect.left + rect.height() - margin, rect.top + margin);
path_.lineTo(rect.left + rect.height() - margin, rect.bottom - margin);
path_.lineTo(rect.left + margin, (rect.top + rect.bottom) / 2.0f);
path_.close();
canvas.drawPath(path_, paint_);
// Draw right arrow.
paint_.setStyle(Paint.Style.STROKE);
if (event.hasKeyEvent()) {
paint_.setColor(Color.BLACK);
} else {
paint_.setColor(Color.WHITE);
}
canvas.drawRect(rect.right - rect.height(), rect.top, rect.right, rect.bottom, paint_);
paint_.setStyle(Paint.Style.FILL);
path_.reset();
path_.moveTo(rect.right - (rect.height() - (margin + 1)), rect.top + margin);
path_.lineTo(rect.right - (rect.height() - (margin + 1)), rect.bottom - margin);
path_.lineTo(rect.right - margin, (rect.top + rect.bottom) / 2.0f);
path_.close();
canvas.drawPath(path_, paint_);
}
}
/**
* Called after the entire score is drawn, to give this tool a chance to draw over it.
* Draws the selection box, and possibly the trash icon.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param view - The ScoreView being drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
@Override
public void afterDrawScore(ScoreView view, Canvas canvas, Rect rect) {
if (mode_ == SELECTING) {
paint_.setStyle(Paint.Style.FILL);
paint_.setColor(Color.CYAN);
paint_.setAlpha(127);
canvas.drawRect(selection_, paint_);
paint_.setAlpha(255);
}
// Draw the trash can.
if (trashVisible_) {
trashIcon_.draw(canvas);
}
}
// The id of the finger doing the editing.
private int pointerId_;
// The most recent previous position of the finger.
private double previousTime_;
private double previousNote_;
// A tool to select the next time the user finishes editing an event.
// Can be "this", but not null.
private ScoreViewTool nextTool_;
// While the user is drawing a selection rectangle, this is it, in screen (physical) coordinates.
private Rect selection_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
protected Paint paint_;
protected Path path_;
private Drawable icon_;
// The mode of the tool, depending mostly on where the user's finger was when they first touched.
private int mode_;
private static final int NONE = 0;
private static final int RESIZING_LEFT = 1;
private static final int RESIZING_RIGHT = 2;
private static final int MOVING = 3;
private static final int SELECTING = 4;
// Members for controlling how the trash can icon gets drawn while moving events.
private boolean trashVisible_;
private Rect trashRect_; // in screen (physical) coordinates.
private Drawable trashIcon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,97 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
/**
* A button that toggles between showing the events for all of the synthesizer channels or showing
* just the currently selected channel.
*/
public class HideChannelButton extends ScoreViewTool {
/**
* Creates a new HideChannelButton, using the given context for loading resources.
*/
HideChannelButton(Context context) {
logger_ = Logger.getLogger(getClass().getName());
visibleIcon_ = context.getResources().getDrawable(R.drawable.open_eye);
hiddenIcon_ = context.getResources().getDrawable(R.drawable.closed_eye);
paint_ = new Paint();
}
/**
* Called when this tool is selected.
* Changes the channel visibility and then reselects the previous tool.
* @param view - The ScoreView that this toolbar is for.
* @param previousTool - The tool that was selected when this one was chosen.
*/
@Override
public void onSelect(ScoreView view, ScoreViewTool previousTool) {
view.setOtherChannelsVisible(!view.getOtherChannelsVisible());
view.setTool(previousTool);
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
if (score.getOtherChannelsVisible()) {
visibleIcon_.setBounds(rect);
visibleIcon_.draw(canvas);
} else {
hiddenIcon_.setBounds(rect);
hiddenIcon_.draw(canvas);
}
}
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable visibleIcon_;
private Drawable hiddenIcon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,118 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.music.Music.Event;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
/**
* A tool for creating new events in the score.
*/
public class NewEventTool extends ScoreViewTool {
/**
* Creates a new NewEventTool.
* @param context - The context to use for loading resources.
* @param eventTool - The tool to use for editing the event as it's created.
*/
NewEventTool(Context context, EditEventTool eventTool) {
logger_ = Logger.getLogger(getClass().getName());
eventTool_ = eventTool;
icon_ = context.getResources().getDrawable(R.drawable.add);
paint_ = new Paint();
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
icon_.setBounds(rect);
icon_.draw(canvas);
}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
@Override
public boolean onTouch(ScoreView view, MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
if (actionCode == MotionEvent.ACTION_DOWN) {
double time = view.getTimeAt((int)event.getX());
double note = view.getNoteAt((int)event.getY());
if (view.getSnapTo() != 0) {
time = ((int)(time / view.getSnapTo())) * view.getSnapTo();
}
Event.Builder e = view.getScore().addEventBuilder();
e.setStart(time);
e.setEnd(time + view.getSnapTo());
e.setKey((int)note);
e.getKeyEventBuilder().setChannel(view.getCurrentChannel());
eventTool_.pickupEvent(view, e, (int)event.getX(), (int)event.getY(), this);
view.setTool(eventTool_);
return true;
} else {
return false;
}
}
// The tool that's used to edit the event after it's created, but only until the finger is up.
// Then control returns back to this control.
private EditEventTool eventTool_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable icon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,118 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.music.Music.Event;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
/**
* A tool for creating new events in the score.
*/
public class NewLoopTool extends ScoreViewTool {
/**
* Creates a new NewEventTool.
* @param context - The context to use for loading resources.
* @param eventTool - The tool to use for editing the event as it's created.
*/
NewLoopTool(Context context, EditEventTool eventTool) {
logger_ = Logger.getLogger(getClass().getName());
eventTool_ = eventTool;
icon_ = context.getResources().getDrawable(R.drawable.loop);
paint_ = new Paint();
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
icon_.setBounds(rect);
icon_.draw(canvas);
}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
@Override
public boolean onTouch(ScoreView view, MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
if (actionCode == MotionEvent.ACTION_DOWN) {
double time = view.getTimeAt((int)event.getX());
double note = view.getNoteAt((int)event.getY());
if (view.getSnapTo() != 0) {
time = ((int)(time / view.getSnapTo())) * view.getSnapTo();
}
Event.Builder e = view.getScore().addEventBuilder();
e.setStart(time);
e.setEnd(time + view.getSnapTo());
e.setKey((int)note);
e.getLoopEventBuilder().setCount(1);
eventTool_.pickupEvent(view, e, (int)event.getX(), (int)event.getY(), this);
view.setTool(eventTool_);
return true;
} else {
return false;
}
}
// The tool that's used to edit the event after it's created, but only until the finger is up.
// Then control returns back to this control.
private EditEventTool eventTool_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable icon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,156 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.music.ScorePlayer;
import com.levien.synthesizer.core.music.ScorePlayerListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
/**
* A button to start the current score playing.
*/
public class PlayButton extends ScoreViewTool implements ScorePlayerListener {
/**
* Creates a new play button, loading resources from the given context.
*/
PlayButton(ScoreViewToolbar toolbar, Context context) {
toolbar_ = toolbar;
logger_ = Logger.getLogger(getClass().getName());
player_ = new ScorePlayer();
playing_ = false;
playingIcon_ = context.getResources().getDrawable(R.drawable.stop);
stoppedIcon_ = context.getResources().getDrawable(R.drawable.play);
paint_ = new Paint();
}
/**
* Called when this tool is selected. Starts the score playing.
* @param view - The ScoreView that this toolbar is for.
* @param previousTool - The tool that was selected when this one was chosen.
*/
@Override
public void onSelect(ScoreView view, ScoreViewTool previousTool) {
view_ = view;
if (playing_) {
player_.stopPlaying();
} else {
player_.startPlaying(view.getSynthesizer(), view.getScore().build(), 120.0, 4, this);
}
view.setTool(previousTool);
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
if (playing_) {
playingIcon_.setBounds(rect);
playingIcon_.draw(canvas);
} else {
stoppedIcon_.setBounds(rect);
stoppedIcon_.draw(canvas);
}
}
/**
* Called when the score starts playing.
*/
public void onStart() {
view_.post(new Thread("PlayButton.onStart()") {
public void run() {
playing_ = true;
view_.invalidate();
toolbar_.invalidate();
}
});
}
/**
* Called every so often during playback.
* @param time - the time in measures from the start of the song.
*/
public void onTimeUpdate(final double time) {
view_.post(new Thread("PlayButton.onTimeUpdate()") {
public void run() {
view_.setCursor(time);
view_.invalidate();
toolbar_.invalidate();
}
});
}
/**
* Called when the score stops playing.
*/
public void onStop() {
view_.post(new Thread("PlayButton.onStop()") {
public void run() {
playing_ = false;
view_.invalidate();
}
});
}
// The ScoreView that this button controls.
private ScoreView view_;
private ScoreViewToolbar toolbar_;
// ScorePlayer to play the score.
private ScorePlayer player_;
// Is the score playing?
private boolean playing_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable playingIcon_;
private Drawable stoppedIcon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,231 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.music.Note;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
/**
* A button to enable "play" mode of the ScoreView. In play mode, pressing anywhere plays the note
* for the key that's being pressed. @see ScoreView.
*/
public class PlayTool extends ScoreViewTool {
/**
* Creates a new PlayTool, loading resources from the given context.
*/
PlayTool(Context context) {
logger_ = Logger.getLogger(getClass().getName());
keysDown_ = new int[FINGERS];
for (int i = 0; i < keysDown_.length; ++i) {
keysDown_[i] = -1;
}
icon_ = context.getResources().getDrawable(R.drawable.play_piano);
paint_ = new Paint();
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
icon_.setBounds(rect);
icon_.draw(canvas);
}
/**
* Called after each key is drawn, to give this tool a chance to draw over it.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param key - The key that was drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
@Override
public void afterDrawKey(int key,
Canvas canvas,
Rect rect) {
for (int keyDown : keysDown_) {
if (key == keyDown) {
paint_.setColor(Color.GREEN);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
}
}
}
/**
* Called to handle touch down events.
* Returns true iff we need to redraw.
*/
private boolean onTouchDown(ScoreView view, int finger, int physicalX, int physicalY) {
int note = (int)Math.floor(view.getNoteAt(physicalY));
view.getSynthesizer().onNoteOn(view.getCurrentChannel(), note, 64);
keysDown_[finger] = note;
return true;
}
/**
* Called to handle touch move events.
*/
private boolean onTouchMove(ScoreView view, int finger, int physicalX, int physicalY) {
int note = (int)Math.floor(view.getNoteAt(physicalY));
int oldNote = keysDown_[finger];
if (oldNote >= 0) {
view.getSynthesizer().onNoteOff(view.getCurrentChannel(), oldNote, 64);
}
view.getSynthesizer().onNoteOn(view.getCurrentChannel(), note, 64);
keysDown_[finger] = (int)note;
return true;
}
/**
* Called to handle touch up events.
*/
protected boolean onTouchUp(ScoreView view, int finger) {
int note = keysDown_[finger];
view.getSynthesizer().onNoteOff(view.getCurrentChannel(), note, 64);
keysDown_[finger] = -1;
return true;
}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
@Override
public boolean onTouch(ScoreView view, MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0);
if (pointerId < FINGERS) {
int x = (int)event.getX();
int y = (int)event.getY();
redraw |= onTouchDown(view, pointerId, x, y);
}
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
int pointerId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
if (pointerId < FINGERS) {
int pointerIndex = event.findPointerIndex(pointerId);
if (pointerIndex >= 0) {
int x = (int)event.getX(pointerIndex);
int y = (int)event.getY(pointerIndex);
redraw |= onTouchDown(view, pointerId, x, y);
}
}
} else if (actionCode == MotionEvent.ACTION_MOVE) {
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
int pointerId = event.getPointerId(pointerIndex);
if (pointerId >= FINGERS) {
continue;
}
if (pointerIndex >= 0) {
int x = (int)event.getX(pointerIndex);
int y = (int)event.getY(pointerIndex);
redraw |= onTouchMove(view, pointerId, x, y);
}
}
} else if (actionCode == MotionEvent.ACTION_UP) {
int pointerId = event.getPointerId(0);
if (pointerId < FINGERS) {
redraw |= onTouchUp(view, pointerId);
}
// Clean up any other pointers that have disappeared.
for (pointerId = 0; pointerId < FINGERS; ++pointerId) {
boolean found = false;
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
if (pointerId == event.getPointerId(pointerIndex)) {
found = true;
break;
}
}
if (!found) {
redraw |= onTouchUp(view, pointerId);
}
}
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
int pointerId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
if (pointerId < FINGERS) {
redraw |= onTouchUp(view, pointerId);
}
// Clean up any other pointers that have disappeared.
for (pointerId = 0; pointerId < FINGERS; ++pointerId) {
boolean found = false;
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
if (pointerId == event.getPointerId(pointerIndex)) {
found = true;
break;
}
}
if (!found) {
redraw |= onTouchUp(view, pointerId);
}
}
} else {
return false;
}
if (redraw) {
view.invalidate();
}
return true;
}
// The piano key each finger is holding down, or -1 if a finger is not pressing any key.
private int[] keysDown_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable icon_;
// The number of simultaneous fingers supported by this control.
protected static final int FINGERS = 5;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,767 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.levien.synthesizer.R;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.music.Music.Event;
import com.levien.synthesizer.core.music.Music.Score;
import com.levien.synthesizer.core.music.Note;
/**
* ScoreView is a UI widget that allows editing a musical score, as well as live playing. The
* majority of the ScoreView area shows a subsequence of the current musical score. Along the
* y-axis are the keys of a piano. Time is along the x-axis. Along the bottom, there is a toolbar,
* which allows selecting various "tools" to use on the score.
*
* A score is composed of various "events", such as playing a note for a certain duration at a
* certain time, using a certain channel. A ScoreView lets the user create or edit these events.
*
* PlayTool - When selected, pressing plays the note at that x using the selected channel.
* ViewportTool - Sets the currently visible part of the score by touching or dragging.
* NewEventTool - Creates new events.
* EditEventTool - Edits an existing event.
* PlayButton - Starts the score playing audibly.
* SelectChannelButton - Selects a particular channel (instrument) for editing or playing.
* HideChannelButton - Toggles whether to show/hide the channels not currently being edited.
* SnapTool - Changes the "snap to" setting for this ScoreView.
*/
public class ScoreView extends View {
/**
* Basic android widget constructor.
*/
public ScoreView(Context context, AttributeSet attrs) {
super(context, attrs);
logger_ = Logger.getLogger(getClass().getName());
// Set the default time to be 20 measures, and show 5 measures to start.
minTime_ = 0.0;
maxTime_ = 20.0;
timeZoom_ = 0.25;
timeOffset_ = 0.0;
// Set the piano keys to the range of a normal piano, showing one octave to start.
minNote_ = Note.A;
maxNote_ = Note.A + 88.0;
noteZoom_ = 8.0 / 88.0;
noteOffset_ = 44.0;
// Snap to eighth notes to start.
snapTo_ = 1.0 / 8.0;
// Create the score to edit.
score_ = Score.newBuilder();
// Setup the channels.
currentChannel_ = 0;
showOtherChannels_ = true;
// Load the icon to use for each channel.
iconForChannel_ = new Drawable[CHANNELS];
iconForChannel_[0] = context.getResources().getDrawable(R.drawable.guitar);
iconForChannel_[1] = context.getResources().getDrawable(R.drawable.bass);
iconForChannel_[2] = context.getResources().getDrawable(R.drawable.voice);
iconForChannel_[3] = context.getResources().getDrawable(R.drawable.flute);
iconForChannel_[4] = context.getResources().getDrawable(R.drawable.drums);
arrowsVisible_ = true;
upSelected_ = false;
downSelected_ = false;
upIcon_ = context.getResources().getDrawable(R.drawable.up);
downIcon_ = context.getResources().getDrawable(R.drawable.down);
// Set up basic drawing structs, just so we don't have to allocate them later when we draw.
drawingRect_ = new Rect();
keyRect_ = new Rect();
eventRect_ = new Rect();
fillPaint_ = new Paint();
strokePaint_ = new Paint();
marginPaint_ = new Paint();
fillPaint_.setStyle(Paint.Style.FILL);
strokePaint_.setStyle(Paint.Style.STROKE);
marginPaint_.setStyle(Paint.Style.FILL);
marginPaint_.setColor(Color.GRAY);
}
/**
* Returns a mutable copy of the score being edited.
*/
public Score.Builder getScore() {
return score_;
}
/**
* Returns the currently selected channel (instrument).
*/
public int getCurrentChannel() {
return currentChannel_;
}
/**
* Selects the given channel.
* @param channel - The channel to select.
*/
public void setCurrentChannel(int channel) {
currentChannel_ = channel;
}
/**
* Returns true iff the given channel is visible in the score.
*/
public boolean isChannelVisible(int channel) {
if (showOtherChannels_) {
return true;
} else {
return channel == currentChannel_;
}
}
/**
* Returns true iff channels that aren't the current one are visible in the score.
*/
public boolean getOtherChannelsVisible() {
return showOtherChannels_;
}
/**
* Sets whether channels that aren't the current one are visible in the score.
*/
public void setOtherChannelsVisible(boolean visible) {
showOtherChannels_ = visible;
}
/**
* Returns the currently selected tool for this ScoreView.
*/
public ScoreViewTool getTool() {
return currentTool_;
}
/**
* Sets a tool to be the current tool for this ScoreView. Informs listeners of the change.
*/
public void setTool(ScoreViewTool tool) {
ScoreViewTool previousTool = currentTool_;
currentTool_ = tool;
tool.onSelect(this, previousTool);
if (listener_ != null) {
listener_.onSetTool(tool);
}
invalidate();
}
/**
* Returns the "snap to" setting for this ScoreView. @see setSnapTo().
* @return the note that should be snapped to. For example, if editing should snap to the nearest
* quarter note, then returns 0.25. For a whole note, 1.0. For no snapping, returns 0.0.
*/
public double getSnapTo() {
return snapTo_;
}
/**
* Sets the "snap to" setting for this ScoreView. @see getSnapTo().
* @param snapTo - the note that should be snapped to. For example, if editing should snap to the
* nearest quarter note, then 0.25. For a whole note, 1.0. For no snapping, 0.0.
*/
public void setSnapTo(double snapTo) {
snapTo_ = snapTo;
}
/**
* Returns the zoom setting for this control on the x-axis. @see setTimeZoom().
* @return the multiplier for the x-axis on the viewport. 1.0 means the entire time of the score
* is visible. 0.5 means only half of the score (time-wise) is visible. 2.0 means that the
* entire score is shown, but only takes up half the screen. Any excess space is just "margin".
*/
public double getTimeZoom() {
return timeZoom_;
}
/**
* Sets the zoom level for this control on the x-axis. @see getTimeZoom().
* @param zoom - the multiplier for the x-axis on the viewport. 1.0 means the entire time of the
* score is visible. 0.5 means only half of the score (time-wise) is visible. 2.0 means that the
* entire score is shown, but only takes up half the screen. Any excess space is just "margin".
*/
public void setTimeZoom(double zoom) {
timeZoom_ = zoom;
}
/**
* Returns the zoom setting for this control on the y-axis. @see setNoteZoom().
* @return the multiplier for the y-axis on the viewport, which controls how many note keys are
* visible. 1.0 means show the entire 88 keys of the piano are visible. N/88 means exactly N
* keys are visible. Values larger than 1.0 means extra "margin" is shown at the top and bottom.
*/
public double getNoteZoom() {
return noteZoom_;
}
/**
* Sets the zoom setting for this control on the y-axis. @see getNoteZoom().
* @param zoom - the multiplier for the y-axis on the viewport, which controls how many note keys
* are visible. 1.0 means show the entire 88 keys of the piano are visible. N/88 means exactly N
* keys are visible. Values larger than 1.0 means extra "margin" is shown at the top and bottom.
*/
public void setNoteZoom(double zoom) {
noteZoom_ = zoom;
}
/**
* Returns the left-most time currently visible in this control. @see setTimeOffset().
* @return the time, in measures, from the beginning of the score to the first visible time
* in the ScoreView. For example, 5.25 in 4/4 time would mean one quarter note past the end of
* the 5th measure. Negative values mean margin is shown on the left side.
*/
public double getTimeOffset() {
return timeOffset_;
}
/**
* Sets the left-most time currently visible in this control. @see getTimeOffset().
* @param offset - The time, in measures, from the beginning of the score to the first visible
* time in the ScoreView. For example, 5.25 in 4/4 time would mean one quarter note past the end
* of the 5th measure. Negative values mean margin is shown on the left side.
*/
public void setTimeOffset(double offset) {
timeOffset_ = offset;
}
/**
* Returns the bottom-most note key currently visible in this control. @see setNoteOffset().
* @return the note number of the bottom key visible on the screen. 0.0 means the lowest note is
* fully visible, with its bottom along the bottom edge of the screen. 1.0 means the lowest note
* is not visible, but its top edge is along the bottom of the screen. 88.0 means no keys are
* visible, but the top edge of the highest key is along the bottom of the screen.
*/
public double getNoteOffset() {
return noteOffset_;
}
/**
* Sets the bottom-most note key currently visible in this control. @see getNoteOffset().
* @param offset - the note number of the bottom key visible on the screen. 0.0 means the lowest
* note is fully visible, with its bottom along the bottom edge of the screen. 1.0 means the
* lowest note is not visible, but its top edge is along the bottom of the screen. 88.0 means no
* keys are visible, but the top edge of the highest key is along the bottom of the screen.
*/
public void setNoteOffset(double offset) {
noteOffset_ = offset;
}
/**
* Returns the max time viewable or editable by this ScoreView. @see setMaxTime().
* @return the time, where 0.0 means no time, 1.0 means one measure, and 10 means ten measures.
*/
public double getMaxTime() {
return maxTime_;
}
/**
* Sets the max time viewable or editable by this ScoreView. @see getMaxTime().
* @param max - the time, where 0.0 means no time, 1.0 means one measure, and 10 for ten measures.
*/
public void setMaxTime(double max) {
maxTime_ = max;
}
/**
* Returns the max possible note viewable or editable by this ScoreView. @see setMaxNote().
* @return the note number. For a normal piano layout, this method should always return 88.0.
*/
public double getMaxNote() {
return maxNote_;
}
/**
* Sets the max possible note viewable or editable by this ScoreView. @see getMaxNote().
* @param max - the note number. For a normal piano layout, this should always be 88.0.
*/
public void setMaxNote(double max) {
maxNote_ = max;
}
/**
* Returns the rectangle, in screen coordinates, where this ScoreView was most recently drawn.
* @return a reference to the Rect.
*/
public Rect getDrawingRect() {
return drawingRect_;
}
/**
* Returns the time (logical x) that corresponds to the given pixel (physical x).
* @param pixelX - the x in screen coordinates.
* @return the x in logical coordinates (the time, in measures, from the score start).
*/
public double getTimeAt(int pixelX) {
return timeOffset_ + ((double)(pixelX - drawingRect_.left) / drawingRect_.width()) / timeZoom_;
}
/**
* Returns the pixel (physical x) that corresponds to the given time (logical x).
* @param time - the time, in measures, from the score start.
* @return the x offset of the given time, in screen coordinates.
*/
public int getTimeX(double time) {
return (int)(((time - timeOffset_) * timeZoom_) * drawingRect_.width() +
drawingRect_.left + 0.5);
}
/**
* Returns the note (logical y) that corresponds to the given pixel (physical y).
* @param pixelY - the y in screen coordinates.
* @return the y in logical coordinates (the note key, typically from 0 to 88.0).
*/
public double getNoteAt(int pixelY) {
return ((double)(drawingRect_.bottom - pixelY) / drawingRect_.height()) / noteZoom_ + noteOffset_;
}
/**
* Returns the pixel (physical y) that corresponds to the given note (logical y).
* @param note - the note key.
* @return the y offset of the given note, in screen coordinates.
*/
public int getNoteY(double note) {
return (int)(drawingRect_.bottom - (note - noteOffset_) * drawingRect_.height() * noteZoom_);
}
/**
* Returns the top-most event at the given coordinates.
* @param physicalX - the x in screen coordinates.
* @param physicalY - the y in screen coordinates.
* @return the mutable event.
*/
public Event.Builder getEventAt(int physicalX, int physicalY) {
double time = getTimeAt(physicalX);
double note = getNoteAt(physicalY);
for (int i = score_.getEventCount() - 1; i >= 0; --i) {
double eventStartTime = score_.getEvent(i).getStart();
double eventEndTime = score_.getEvent(i).getEnd();
double eventMinNote = score_.getEvent(i).getKey();
double eventMaxNote = score_.getEvent(i).getKey() + 1;
if (time >= eventStartTime &&
time < eventEndTime &&
note >= eventMinNote &&
note < eventMaxNote) {
return score_.getEventBuilder(i);
}
}
return null;
}
/**
* Sets the cursor position that shows where playback is in the score.
* This should only be called from the Android UI thread.
*/
public void setCursor(double cursor) {
cursor_ = cursor;
invalidate();
}
/**
* Returns the color to use for representing the given channel in this ScoreView.
* @param channel - the channel.
* @return the color to use, Android style.
*/
public int getColorForChannel(int channel) {
switch (channel % CHANNELS) {
case 0: return Color.rgb(0, 255, 255);
case 1: return Color.rgb(255, 0, 255);
case 2: return Color.rgb(255, 255, 0);
case 3: return Color.rgb(255, 0, 0);
case 4: return Color.rgb(0, 255, 0);
case 5: return Color.rgb(0, 0, 255);
}
return Color.BLACK;
}
/**
* Returns the icon to use for representing the given channel in this ScoreView.
* @param channel - the channel.
* @return the icon to use, as a Drawable.
*/
public Drawable getIconForChannel(int channel) {
return iconForChannel_[channel % iconForChannel_.length];
}
/**
* Called to draw the ScoreView widget.
* @param canvas - the canvas to draw the widget on.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(drawingRect_);
// Clear the rectangle.
fillPaint_.setColor(Color.WHITE);
canvas.drawRect(drawingRect_, fillPaint_);
strokePaint_.setStrokeWidth(1.0f);
// Draw piano keys to mark the frequencies.
for (int note = (int)minNote_; note < (int)maxNote_; ++note) {
// Draw a single key that fills up a row.
keyRect_.bottom = getNoteY(note);
keyRect_.top = getNoteY(note + 1);
keyRect_.left = getTimeX(0.0);
keyRect_.right = getTimeX(maxTime_);
strokePaint_.setStrokeWidth(2.0f);
strokePaint_.setColor(Color.LTGRAY);
if (Note.isNatural(note)) {
fillPaint_.setColor(Color.WHITE);
} else {
fillPaint_.setColor(Color.LTGRAY);
}
canvas.drawRect(keyRect_, fillPaint_);
canvas.drawRect(keyRect_, strokePaint_);
if (currentTool_ != null) {
currentTool_.afterDrawKey(note, canvas, keyRect_);
}
}
// Draw lines to mark the measures.
for (double i = minTime_ + 1; i < maxTime_; ++i) {
strokePaint_.setColor(Color.LTGRAY);
int x = getTimeX(i - 0.75);
canvas.drawLine(x, drawingRect_.top, x, drawingRect_.bottom, strokePaint_);
strokePaint_.setColor(Color.GRAY);
x = getTimeX(i - 0.5);
canvas.drawLine(x, drawingRect_.top, x, drawingRect_.bottom, strokePaint_);
strokePaint_.setColor(Color.LTGRAY);
x = getTimeX(i - 0.25);
canvas.drawLine(x, drawingRect_.top, x, drawingRect_.bottom, strokePaint_);
strokePaint_.setColor(Color.BLACK);
x = getTimeX(i);
canvas.drawLine(x, drawingRect_.top, x, drawingRect_.bottom, strokePaint_);
}
// Draw the margins.
double leftMargin = getTimeX(minTime_);
if (leftMargin > drawingRect_.left) {
canvas.drawRect(drawingRect_.left,
drawingRect_.top,
(float)leftMargin,
drawingRect_.bottom,
marginPaint_);
}
double rightMargin = getTimeX(maxTime_);
if (rightMargin < drawingRect_.right) {
canvas.drawRect((float)rightMargin,
drawingRect_.top,
drawingRect_.right,
drawingRect_.bottom,
marginPaint_);
}
double topMargin = getNoteY(maxNote_);
if (topMargin > drawingRect_.top) {
canvas.drawRect(drawingRect_.left,
drawingRect_.top,
drawingRect_.right,
(float)topMargin,
marginPaint_);
}
double bottomMargin = getNoteY(minNote_);
if (bottomMargin < drawingRect_.bottom) {
canvas.drawRect(drawingRect_.left,
(float)bottomMargin,
drawingRect_.right,
drawingRect_.bottom,
marginPaint_);
}
// Draw the sequence.
for (int i = 0; i < score_.getEventCount(); ++i) {
Event event = score_.getEvent(i);
eventRect_.left = getTimeX(event.getStart());
eventRect_.top = getNoteY(event.getKey() + 1);
eventRect_.right = getTimeX(event.getEnd());
eventRect_.bottom = getNoteY(event.getKey());
if (!event.hasKeyEvent() || isChannelVisible(event.getKeyEvent().getChannel())) {
if (event.getSelected()) {
if (event.hasKeyEvent()) {
fillPaint_.setColor(getColorForChannel(event.getKeyEvent().getChannel()));
strokePaint_.setColor(Color.BLACK);
} else {
fillPaint_.setColor(Color.BLACK);
strokePaint_.setColor(Color.WHITE);
}
fillPaint_.setAlpha(255);
strokePaint_.setStrokeWidth(5.0f);
} else {
if (event.hasKeyEvent()) {
fillPaint_.setColor(getColorForChannel(event.getKeyEvent().getChannel()));
strokePaint_.setColor(Color.BLACK);
} else {
fillPaint_.setColor(Color.BLACK);
strokePaint_.setColor(Color.WHITE);
}
fillPaint_.setAlpha(127);
strokePaint_.setStrokeWidth(1.0f);
}
canvas.drawRect(eventRect_, fillPaint_);
canvas.drawRect(eventRect_, strokePaint_);
if (currentTool_ != null) {
currentTool_.afterDrawEvent(event, canvas, eventRect_);
}
}
}
// Draw the cursor.
strokePaint_.setColor(Color.rgb(0, 175, 0));
strokePaint_.setStrokeWidth(8.0f);
canvas.drawLine(getTimeX(cursor_), drawingRect_.top,
getTimeX(cursor_), drawingRect_.bottom, strokePaint_);
// Draw the scroll arrows, if visible.
if (arrowsVisible_) {
upIcon_.setBounds(getDrawingRect().left + 50,
getDrawingRect().top + 50,
getDrawingRect().left + 50 + upIcon_.getIntrinsicWidth(),
getDrawingRect().top + 50 + downIcon_.getIntrinsicHeight());
downIcon_.setBounds(getDrawingRect().left + 50,
(getDrawingRect().bottom - 50) - downIcon_.getIntrinsicHeight(),
getDrawingRect().left + 50 + upIcon_.getIntrinsicWidth(),
getDrawingRect().bottom - 50);
if (upSelected_) {
fillPaint_.setColor(Color.WHITE);
} else {
fillPaint_.setColor(Color.BLACK);
}
canvas.drawCircle(upIcon_.getBounds().centerX(),
upIcon_.getBounds().centerY(),
upIcon_.getBounds().width(),
fillPaint_);
upIcon_.draw(canvas);
if (downSelected_) {
fillPaint_.setColor(Color.WHITE);
} else {
fillPaint_.setColor(Color.BLACK);
}
canvas.drawCircle(downIcon_.getBounds().centerX(),
downIcon_.getBounds().centerY(),
downIcon_.getBounds().width(),
fillPaint_);
downIcon_.draw(canvas);
// Make the bounds for the icons a little larger so they're easier to hit.
downIcon_.getBounds().inset(-50, -50);
upIcon_.getBounds().inset(-50, -50);
}
if (currentTool_ != null) {
currentTool_.afterDrawScore(this, canvas, drawingRect_);
}
}
/**
* Handler for all touch events.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
// Check if they touched the arrows.
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
if (actionCode == MotionEvent.ACTION_DOWN) {
if (upIcon_.getBounds().contains((int)event.getX(), (int)event.getY())) {
upSelected_ = true;
invalidate();
return true;
} else if (downIcon_.getBounds().contains((int)event.getX(), (int)event.getY())) {
downSelected_ = true;
invalidate();
return true;
} else {
arrowsVisible_ = false;
invalidate();
}
} else if (actionCode == MotionEvent.ACTION_UP) {
arrowsVisible_ = true;
if (upSelected_) {
// Scroll up.
if (getNoteOffset() < maxNote_) {
setNoteOffset(getNoteOffset() + 1);
}
upSelected_ = false;
}
if (downSelected_) {
// Scroll down.
if (getNoteOffset() > minNote_) {
setNoteOffset(getNoteOffset() - 1);
}
downSelected_ = false;
}
invalidate();
}
// Delegate the touch to the current tool.
if (!upSelected_ && !downSelected_ && currentTool_ != null) {
return currentTool_.onTouch(this, event);
} else {
return false;
}
}
/**
* Layout measurement for this widget.
* This method just sets a basic minimum size and makes the widget maximized otherwise.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
height = 10;
break;
}
setMeasuredDimension(width, height);
}
/**
* Connects the ScoreView to a Synthesizer for playback.
* @synth - The synthesizer to connect to.
*/
public void bindTo(final MidiListener synth) {
synthesizer_ = synth;
}
/**
* Returns the synthesizer connected to this ScoreView.
* @return the connected synthesizer.
*/
public MidiListener getSynthesizer() {
return synthesizer_;
}
/**
* Sets the listener to notify of events in this control.
* @param listener - the listener to notify.
*/
public void setListener(ScoreViewListener listener) {
listener_ = listener;
}
// The score being edited, played, etc by this control.
private Score.Builder score_;
// The current tool being used.
private ScoreViewTool currentTool_;
// The currently selected channel (instrument).
private int currentChannel_;
// Whether to show channels other than the currently selected one.
private boolean showOtherChannels_;
// The set of icons to use for each channel.
private Drawable[] iconForChannel_;
// What granularity of note to snap to when editing. See getSnapTo and setSnapTo().
private double snapTo_;
// The min, max and current viewport for the x and y axes.
private double timeZoom_;
private double timeOffset_;
private double minTime_;
private double maxTime_;
private double noteZoom_;
private double noteOffset_;
private double minNote_;
private double maxNote_;
// A cursor that indicates where playback is in the score, in logical coordinates.
private double cursor_;
// The synthesizer this control is bound to.
private MidiListener synthesizer_;
// The listener to notify of events in this control.
private ScoreViewListener listener_;
// Buttons to let the user move up and down without switching to the viewport tool.
private boolean arrowsVisible_;
private boolean upSelected_;
private boolean downSelected_;
private Drawable upIcon_;
private Drawable downIcon_;
// These are basically stack variables for onDraw. They're member variables only so that we can
// avoid reallocating them every time the keyboard is redrawn.
//
// The most recent screen rect that this keyboard was drawn into.
private Rect drawingRect_;
private Rect keyRect_;
private Rect eventRect_;
private Paint fillPaint_;
private Paint strokePaint_;
private Paint marginPaint_;
// The number of channels (instruments) edittable by this control.
private static final int CHANNELS = 5;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,27 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
/**
* A listener for events happening in a ScoreView.
*/
public interface ScoreViewListener {
/**
* Called when a new tool is selected for the ScoreView.
*/
void onSetTool(ScoreViewTool tool);
}

@ -1,81 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import com.levien.synthesizer.core.music.Music.Event;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.MotionEvent;
/**
* A base class for tools on the ScoreView's toolbar.
*/
public abstract class ScoreViewTool {
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this tool is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
public abstract void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin);
/**
* Called when this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param previousTool - The tool that was selected when this one was chosen.
*/
public void onSelect(ScoreView view, ScoreViewTool previousTool) {}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
public boolean onTouch(ScoreView view, MotionEvent event) {
return false;
}
/**
* Called after each key is drawn, to give this tool a chance to draw over it.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param key - The key that was drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
public void afterDrawKey(int key, Canvas canvas, Rect rect) {}
/**
* Called after each event is drawn, to give this tool a chance to draw over it.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param event - The event that was drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
public void afterDrawEvent(Event event, Canvas canvas, Rect rect) {}
/**
* Called after the entire score is drawn, to give this tool a chance to draw over it.
* See ScoreView.onDraw() for more information on how ScoreView is drawn.
* @param view - The ScoreView being drawn.
* @param canvas - The canvas the key is drawn into.
* @param rect - The area of the key on the canvas.
*/
public void afterDrawScore(ScoreView view, Canvas canvas, Rect rect) {}
}

@ -1,222 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* A toolbar that goes in a ScoreView and lets the user choose which tool to use on it.
*/
public class ScoreViewToolbar extends View implements ScoreViewListener {
/** Basic constructor for an Android widget. */
public ScoreViewToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
paint_ = new Paint();
rect_ = new Rect();
textRect_ = new Rect();
// Just hard-code the list of tools.
tool_ = new ScoreViewTool[13];
tool_[0] = new PlayButton(this, context);
tool_[1] = new ViewportTool(context);
tool_[2] = new EditEventTool(context);
tool_[3] = new NewEventTool(context, (EditEventTool)tool_[2]);
tool_[4] = new NewLoopTool(context, (EditEventTool)tool_[2]);
tool_[5] = new HideChannelButton(context);
tool_[6] = new PlayTool(context);
tool_[7] = new SelectChannelButton(context, 0);
tool_[8] = new SelectChannelButton(context, 1);
tool_[9] = new SelectChannelButton(context, 2);
tool_[10] = new SelectChannelButton(context, 3);
tool_[11] = new SelectChannelButton(context, 4);
tool_[12] = new SnapTool(context);
toolRect_ = new Rect[tool_.length];
for (int i = 0; i < tool_.length; ++i) {
toolRect_[i] = new Rect();
}
}
/**
* Sets the ScoreView that this toolbar controls.
* @param score - the ScoreView.
*/
public void setScoreView(ScoreView score) {
score_ = score;
score_.setListener(this);
score_.setTool(tool_[1]);
invalidate();
}
/**
* Sets the current tool.
* @param tool - the tool to use.
*/
public void onSetTool(ScoreViewTool tool) {
invalidate();
}
/**
* Touch event handler.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
getDrawingRect(rect_);
for (int i = 0; i < tool_.length; ++i) {
if (toolRect_[i].contains((int)event.getX(), (int)event.getY())) {
score_.setTool(tool_[i]);
invalidate();
break;
}
}
break;
}
case MotionEvent.ACTION_MOVE: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
/**
* Drawing handler.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(rect_);
//rect_.set(rect_);
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect_, paint_);
// Draw the buttons.
float margin = 15.0f;
float waveHeight = (rect_.height() - 2.0f * margin);
float waveWidth = waveHeight;
float xOffset = margin;
float yOffset = margin;
for (int i = 0; i < tool_.length; ++i) {
paint_.setTextSize(24.0f);
paint_.setColor(Color.WHITE);
if (i == 1) {
xOffset += 2 * margin;
paint_.getTextBounds("Tools ", 0, 6, textRect_);
canvas.drawText("Tools ", xOffset, yOffset + (waveHeight + textRect_.height()) / 2, paint_);
xOffset += textRect_.width();
xOffset += margin;
} else if (i == 7) {
xOffset += 2 * margin;
paint_.getTextBounds("Instruments ", 0, 12, textRect_);
canvas.drawText("Instruments ", xOffset, yOffset + (waveHeight + textRect_.height()) / 2, paint_);
xOffset += textRect_.width();
xOffset += margin;
} else if (i == 12) {
xOffset += 2 * margin;
paint_.getTextBounds("Snap to ", 0, 8, textRect_);
canvas.drawText("Snap to ", xOffset, yOffset + (waveHeight + textRect_.height()) / 2, paint_);
xOffset += textRect_.width();
xOffset += margin;
}
paint_.setColor(Color.BLACK);
toolRect_[i].left = (int)xOffset;
toolRect_[i].top = (int)yOffset;
toolRect_[i].right = (int)(xOffset + waveWidth);
toolRect_[i].bottom = (int)(yOffset + waveHeight);
tool_[i].drawButton(canvas, score_, toolRect_[i], margin);
xOffset += waveWidth;
xOffset += margin;
}
}
/**
* Layout measurement for this widget.
* This method just sets a basic minimum size and makes the width maximized otherwise.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = 150;
break;
case MeasureSpec.UNSPECIFIED:
height = 10;
break;
}
setMeasuredDimension(width, height);
}
// The score being edited.
private ScoreView score_;
// Structures used in drawing that we don't want to reallocate every time we draw.
private Paint paint_;
private Rect rect_;
private Rect textRect_;
// The set of available tools.
private ScoreViewTool[] tool_;
// The rect that each tool occupied the last time it was drawn.
private Rect[] toolRect_;
}

@ -1,100 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
/**
* A tool for selecting a particular channel in a ScoreView.
*/
public class SelectChannelButton extends ScoreViewTool {
/**
* Creates a tool for selecting the given channel, loading resources using the given context.
*/
SelectChannelButton(Context context, int channel) {
logger_ = Logger.getLogger(getClass().getName());
channel_ = channel;
paint_ = new Paint();
}
/**
* Returns the channel that this control selects.
*/
public int getChannel() {
return channel_;
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
if (getChannel() == score.getCurrentChannel()) {
paint_.setColor(score.getColorForChannel(getChannel()));
} else {
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
Drawable icon = score.getIconForChannel(getChannel());
icon.setBounds(rect);
icon.draw(canvas);
}
/**
* Called when this tool is selected.
* Changes the selected channel for the view and then reselects the previously selected tool.
* @param view - The ScoreView that this toolbar is for.
* @param previousTool - The tool that was selected when this one was chosen.
*/
@Override
public void onSelect(ScoreView view, ScoreViewTool previousTool) {
view.setCurrentChannel(channel_);
view.setTool(previousTool);
}
// The channel this control selects.
private int channel_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,134 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import com.levien.synthesizer.R;
import java.util.logging.Logger;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
/**
* A control for selecting the "snap to" setting of a ScoreView.
* @see ScoreView
*/
public class SnapTool extends ScoreViewTool {
/**
* Creates a new SnapTool, loading resources from the given context.
*/
SnapTool(Context context) {
logger_ = Logger.getLogger(getClass().getName());
customIcon_ = context.getResources().getDrawable(R.drawable.unknown_note);
noneIcon_ = context.getResources().getDrawable(R.drawable.no_note);
thirtySecondIcon_ = context.getResources().getDrawable(R.drawable.thirtysecond_note);
sixteenthIcon_ = context.getResources().getDrawable(R.drawable.sixteenth_note);
eighthIcon_ = context.getResources().getDrawable(R.drawable.eighth_note);
quarterIcon_ = context.getResources().getDrawable(R.drawable.quarter_note);
halfIcon_ = context.getResources().getDrawable(R.drawable.half_note);
wholeIcon_ = context.getResources().getDrawable(R.drawable.whole_note);
paint_ = new Paint();
}
/**
* Called when this tool is selected. Changes the "snap to" setting for the score view and then
* reselects the previously selected tool.
* @param view - The ScoreView that this toolbar is for.
* @param previousTool - The tool that was selected when this one was chosen.
*/
@Override
public void onSelect(ScoreView view, ScoreViewTool previousTool) {
if (view.getSnapTo() == 0.0) {
view.setSnapTo(1.0);
} else if (view.getSnapTo() <= 0.03125) {
view.setSnapTo(0.0);
} else {
view.setSnapTo(view.getSnapTo() / 2.0);
}
view.setTool(previousTool);
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
if (score.getSnapTo() == 1.0) {
wholeIcon_.setBounds(rect);
wholeIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.5) {
halfIcon_.setBounds(rect);
halfIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.25) {
quarterIcon_.setBounds(rect);
quarterIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.125) {
eighthIcon_.setBounds(rect);
eighthIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.0625) {
sixteenthIcon_.setBounds(rect);
sixteenthIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.03125) {
thirtySecondIcon_.setBounds(rect);
thirtySecondIcon_.draw(canvas);
} else if (score.getSnapTo() == 0.0) {
noneIcon_.setBounds(rect);
noneIcon_.draw(canvas);
} else {
customIcon_.setBounds(rect);
customIcon_.draw(canvas);
}
}
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable customIcon_;
private Drawable noneIcon_;
private Drawable thirtySecondIcon_;
private Drawable sixteenthIcon_;
private Drawable eighthIcon_;
private Drawable quarterIcon_;
private Drawable halfIcon_;
private Drawable wholeIcon_;
@SuppressWarnings("unused")
private Logger logger_;
}

@ -1,259 +0,0 @@
/*
* Copyright 2011 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.
*/
package com.levien.synthesizer.android.widgets.score;
import java.util.logging.Logger;
import com.levien.synthesizer.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
/**
* A tool for adjusting the visible logical area of a ScoreView.
* When this tool is selected, touching moves around the viewport, and pinching zooms in/out.
*/
public class ViewportTool extends ScoreViewTool {
/**
* Creates a new ViewportTool, loading resources from the given context.
*/
ViewportTool(Context context) {
logger_ = Logger.getLogger(getClass().getName());
startX1_ = 0;
startX2_ = 0;
startY1_ = 0;
startY2_ = 0;
icon_ = context.getResources().getDrawable(R.drawable.zoom);
paint_ = new Paint();
}
/**
* Draws the button on the toolbar.
* @param canvas - The canvas to draw the button on.
* @param score - The ScoreView that this toolbar is for.
* @param rect - The area of the button to be drawn, including any margin.
* @param margin - The preferred margin around the button, in screen coordinates.
*/
@Override
public void drawButton(Canvas canvas, ScoreView score, Rect rect, float margin) {
if (score.getTool() == this) {
paint_.setColor(Color.WHITE);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect.left - margin / 2,
rect.top - margin / 2,
rect.right + margin / 2,
rect.bottom + margin / 2,
paint_);
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect, paint_);
icon_.setBounds(rect);
icon_.draw(canvas);
}
/**
* Called when the user touches the ScoreView while this tool is selected.
* @param view - The ScoreView that this tool is for.
* @param event - The touch event that triggered this handler.
* @return true iff this tool handled the touch event.
*/
@Override
public boolean onTouch(ScoreView view, MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
boolean redraw = false;
double timeZoom = view.getTimeZoom();
double timeOffset = view.getTimeOffset();
double noteZoom = view.getNoteZoom();
double noteOffset = view.getNoteOffset();
if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0);
startX1_ = (int)event.getX();
startY1_ = (int)event.getY();
if (pointerId != 0) {
logger_.severe("Initial pointer has id " + pointerId);
}
} else if (actionCode == MotionEvent.ACTION_POINTER_DOWN) {
int pointerId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerIndex = event.findPointerIndex(pointerId);
if (pointerId == 1 && pointerIndex >= 0) {
startX2_ = (int)event.getX(pointerIndex);
startY2_ = (int)event.getY(pointerIndex);
}
} else if (actionCode == MotionEvent.ACTION_MOVE) {
double currentX1 = 0;
double currentX2 = 0;
double currentY1 = 0;
double currentY2 = 0;
boolean has1 = false;
boolean has2 = false;
// Find the current positions of the fingers.
for (int pointerIndex = 0; pointerIndex < event.getPointerCount(); ++pointerIndex) {
int pointerId = event.getPointerId(pointerIndex);
if (pointerId >= 0) {
int x = (int)event.getX(pointerIndex);
int y = (int)event.getY(pointerIndex);
if (pointerId == 0) {
currentX1 = x;
currentY1 = y;
has1 = true;
} else if (pointerId == 1) {
currentX2 = x;
currentY2 = y;
has2 = true;
}
}
}
if (has1 && has2) {
// Enforce that finger 1 is to the left of and above finger 2.
if (currentX2 < currentX1) {
double temp = currentX1;
currentX1 = currentX2;
currentX2 = temp;
}
if (currentY2 < currentY1) {
double temp = currentY1;
currentY1 = currentY2;
currentY2 = temp;
}
if (startX2_ < startX1_) {
double temp = startX1_;
startX1_ = startX2_;
startX2_ = temp;
}
if (startY2_ < startY1_) {
double temp = startY1_;
startY1_ = startY2_;
startY2_ = temp;
}
// Make sure the fingers aren't too close together.
if (startX2_ - startX1_ < 50.0) {
startX2_ = startX1_ + 50.0;
}
if (currentX2 - currentX1 < 50.0) {
currentX2 = currentX1 + 50.0;
}
if (startY2_ - startY1_ < 50.0) {
startY2_ = startY1_ + 50.0;
}
if (currentY2 - currentY1 < 50.0) {
currentY2 = currentY1 + 50.0;
}
// Figure out the parameters of the new viewport.
double scaleXFactor = Math.abs((currentX1 - currentX2) / (startX1_ - startX2_));
timeOffset += (startX1_ - currentX1 / scaleXFactor) /
(timeZoom * view.getDrawingRect().width());
timeZoom *= scaleXFactor;
double scaleYFactor = Math.abs((currentY1 - currentY2) / (startY1_ - startY2_));
noteOffset -= (startY1_ - currentY1/scaleYFactor -
view.getDrawingRect().bottom * (1 - 1/scaleYFactor)) /
(noteZoom * view.getDrawingRect().height());
noteZoom *= scaleYFactor;
// Update the tracking.
startX1_ = currentX1;
startX2_ = currentX2;
startY1_ = currentY1;
startY2_ = currentY2;
redraw = true;
} else if (has1) {
// Move the viewport.
timeOffset += (startX1_ - currentX1) / (timeZoom * view.getDrawingRect().width());
startX1_ = currentX1;
noteOffset -= (startY1_ - currentY1) / (noteZoom * view.getDrawingRect().height());
startY1_ = currentY1;
redraw = true;
}
} else if (actionCode == MotionEvent.ACTION_UP) {
// Snap back so we aren't showing much margin.
// This code is commented out because it's actually much more intuitive if it doesn't snap
// back but just shows some margin.
/*
if (scaleX < 1.0 / maxX) {
offsetX = 0.0;
scaleX = 1.0 / maxX;
redraw = true;
}
if (scaleY < 1.0 / maxY) {
offsetY = 0.0;
scaleY = 1.0 / maxY;
redraw = true;
}
if (offsetX < 0.0) {
offsetX = 0.0;
redraw = true;
}
if (offsetY < 0.0) {
offsetY = 0.0;
redraw = true;
}
if (offsetX > maxX - 1.0 / scaleX) {
offsetX = maxX - 1.0 / scaleX;
redraw = true;
}
if (offsetY > maxY - 1.0 / scaleY) {
offsetY = maxY - 1.0 / scaleY;
redraw = true;
}
*/
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) {
} else {
return view.onTouchEvent(event);
}
view.setTimeZoom(timeZoom);
view.setTimeOffset(timeOffset);
view.setNoteZoom(noteZoom);
view.setNoteOffset(noteOffset);
if (redraw) {
view.invalidate();
}
return true;
}
// The screen coordinates of the first and second fingers pressed on the ScoreView.
// These are updated every time the viewport is updated.
private double startX1_;
private double startX2_;
private double startY1_;
private double startY2_;
// Some objects used in drawing. They are owned here so that they don't have to be reallocated
// and garbage collected for every pass of drawing.
private Paint paint_;
private Drawable icon_;
private Logger logger_;
}

@ -1,27 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.waveform;
/**
* WaveformListener is an interface for getting events when a WaveformView's value changes.
*/
public interface WaveformListener {
/**
* Called when the value changes.
*/
void onWaveformChanged(String waveform);
}

@ -1,167 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.waveform;
import com.levien.synthesizer.core.model.WaveformInput;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* WaveformRowView is like a WaveformView, but it arranges its buttons in a row.
*/
public class WaveformRowView extends WaveformView {
/** Basic constructor for an Android widget. */
public WaveformRowView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Touch event handler.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
getDrawingRect(rect_);
double x = (event.getX() - rect_.left) / rect_.width();
if (x < 1.0/6.0f) {
setWaveform(WaveformInput.SINE);
} else if (x < 2.0/6.0f) {
setWaveform(WaveformInput.TRIANGLE);
} else if (x < 3.0/6.0f) {
setWaveform(WaveformInput.SQUARE);
} else if (x < 4.0/6.0f) {
setWaveform(WaveformInput.SAWTOOTH);
} else if (x < 5.0/6.0f) {
setWaveform(WaveformInput.NOISE);
} else {
CharSequence[] items = new CharSequence[input_.getWaveformCount()];
for (int i = 0; i < items.length; ++i) {
items[i] = input_.getWaveform(i);
}
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setWaveform(input_.getWaveform(which));
}
});
builder.create().show();
}
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
/**
* Drawing handler.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(rect_);
rect_.set(rect_);
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect_, paint_);
// Draw waveforms.
float lineWidth = 5.0f;
float margin = 15.0f;
float waveWidth = (rect_.width() - 7.0f * margin) / 6.0f;
float waveHeight = (rect_.height() - 2.0f * margin);
float xOffset = margin;
float yOffset = margin;
drawSine(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
xOffset += waveWidth;
xOffset += margin;
drawTriangle(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
xOffset += waveWidth;
xOffset += margin;
drawSquare(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
xOffset += waveWidth;
xOffset += margin;
drawSawtooth(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
xOffset += waveWidth;
xOffset += margin;
drawNoise(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
xOffset += waveWidth;
xOffset += margin;
drawOther(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
}
/**
* Layout measurement for this widget.
* This method just sets a basic minimum size and makes the width maximized otherwise.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = 150;
break;
case MeasureSpec.UNSPECIFIED:
height = 10;
break;
}
setMeasuredDimension(width, height);
}
}

@ -1,470 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.levien.synthesizer.android.widgets.waveform;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.levien.synthesizer.core.model.WaveformInput;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
/**
* WaveformView is a control for selecting from among available waveforms.
* It's designed to occupy the same space as a KnobView.
*/
public class WaveformView extends View {
/** Basic constructor for an Android widget. */
public WaveformView(Context context, AttributeSet attrs) {
super(context, attrs);
waveform_ = WaveformInput.SINE;
// Set up the drawing structures.
paint_ = new Paint();
path_ = new Path();
rect_ = new Rect();
// The listener has to be set later.
listener_ = null;
setPadding(3, 3, 3, 3);
}
/**
* Touch event handler.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: {
getDrawingRect(rect_);
double x = (event.getX() - rect_.left) / rect_.width();
double y = (event.getY() - rect_.top) / rect_.height();
if (x < 0.5) {
if (y < 0.34) {
setWaveform(WaveformInput.SINE);
} else if (y < 0.67) {
setWaveform(WaveformInput.TRIANGLE);
} else {
setWaveform(WaveformInput.SQUARE);
}
} else {
if (y < 0.34) {
setWaveform(WaveformInput.SAWTOOTH);
} else if (y < 0.67) {
setWaveform(WaveformInput.NOISE);
}
}
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
/**
* Sets the listener to receive events when the value changes.
*/
public void setWaveformListener(WaveformListener listener) {
listener_ = listener;
}
/**
* Sets the current value of the knob.
*/
public void setWaveform(String waveform) {
waveform_ = waveform;
if (listener_ != null) {
listener_.onWaveformChanged(waveform);
}
invalidate();
}
/**
* Returns the current value of the knob.
*/
public String getWaveform() {
return waveform_;
}
/**
* Draws a button for selecting a sine waveform.
*/
protected void drawSine(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
int steps = 12;
// Sine wave.
path_.reset();
path_.moveTo(x, y + (height / 2));
for (int i = 0; i < steps + 1; i++) {
float x1 = x + (i / (float)steps) * width;
float y1 = y + -1 * (height/2) * (float)Math.sin(2.0f/steps * Math.PI * i) + height/2;
path_.lineTo(x1, y1);
}
paint_.setColor(Color.WHITE);
if (waveform_.equals(WaveformInput.SINE)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path_, paint_);
}
/**
* Draws a button for selecting a triangle waveform.
*/
protected void drawTriangle(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
// Triangle Wave.
path_.reset();
path_.moveTo(x, y + (height / 2));
path_.lineTo(x + width / 4, y);
path_.lineTo(x + width * (3.0f / 4.0f), y + height);
path_.lineTo(x + width, y + (height / 2));
paint_.setColor(Color.WHITE);
if (waveform_.equals(WaveformInput.TRIANGLE)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path_, paint_);
}
/**
* Draws a button for selecting a square waveform.
*/
protected void drawSquare(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
// Square Wave.
path_.reset();
path_.moveTo(x, y + height);
path_.lineTo(x + width / 4, y + height);
path_.lineTo(x + width / 4, y);
path_.lineTo(x + width * (3.0f / 4.0f), y);
path_.lineTo(x + width * (3.0f / 4.0f), y + height);
path_.lineTo(x + width, y + height);
paint_.setColor(Color.WHITE);
if (waveform_.equals(WaveformInput.SQUARE)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path_, paint_);
}
/**
* Draws a button for selecting a sawtooth waveform.
*/
protected void drawSawtooth(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
// Sawtooth Wave.
path_.reset();
path_.moveTo(x, y + height);
path_.lineTo(x, y);
path_.lineTo(x + width / 2, y + height);
path_.lineTo(x + width / 2, y);
path_.lineTo(x + width, y + height);
paint_.setColor(Color.WHITE);
if (waveform_.equals(WaveformInput.SAWTOOTH)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path_, paint_);
}
/**
* Draws a button for selecting a noise waveform.
*/
protected void drawNoise(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
// Noise.
path_.reset();
path_.moveTo(x, y + height * 0.5f);
path_.lineTo(x + 0.125f * width, y + height * 0.4f);
path_.lineTo(x + 0.25f * width, y + height * 1.0f);
path_.lineTo(x + 0.375f * width, y + height * 0.3f);
path_.lineTo(x + 0.5f * width, y + height * 0.7f);
path_.lineTo(x + 0.625f * width, y + height * 0.0f);
path_.lineTo(x + 0.75f * width, y + height * 0.8f);
path_.lineTo(x + 0.875f * width, y + height * 0.2f);
path_.lineTo(x + 1.0f * width, y + height * 0.5f);
paint_.setColor(Color.WHITE);
if (waveform_.equals(WaveformInput.NOISE)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path_, paint_);
}
/**
* Draws a button for selecting a Karplus-Strong waveform.
*/
protected void drawOther(Canvas canvas,
float x, float y,
float width, float height,
float margin,
float lineWidth) {
int steps = 12;
paint_.setColor(Color.WHITE);
if (!waveform_.equals(WaveformInput.SINE) &&
!waveform_.equals(WaveformInput.TRIANGLE) &&
!waveform_.equals(WaveformInput.SAWTOOTH) &&
!waveform_.equals(WaveformInput.SQUARE) &&
!waveform_.equals(WaveformInput.NOISE)) {
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(x - margin / 2,
y - margin / 2,
x + width + margin / 2,
y + height + margin / 2,
paint_);
paint_.setColor(Color.BLACK);
}
paint_.setStyle(Paint.Style.STROKE);
paint_.setStrokeWidth(lineWidth);
paint_.setStrokeJoin(Paint.Join.ROUND);
path_.reset();
path_.moveTo(x, y + (height / 2));
for (int i = 0; i < steps + 1; i++) {
float x1 = x + (i / (float)steps) * width;
float y1 = y + -1 * (height/2) * (float)Math.sin(2.0f/steps * Math.PI * i) + height/2;
path_.lineTo(x1, y1);
}
canvas.drawPath(path_, paint_);
path_.reset();
path_.moveTo(x, y + (height / 2));
for (int i = 0; i < steps + 1; i++) {
float x1 = x + (i / (float)steps) * width;
float y1 = y + -0.6f * (height/2) * (float)Math.sin(2.0f/steps * Math.PI * (steps-i)) + height/2;
path_.lineTo(x1, y1);
}
canvas.drawPath(path_, paint_);
}
/**
* Drawing handler.
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
getDrawingRect(rect_);
rect_.set(rect_);
// Make it square.
if (rect_.height() > rect_.width()) {
int center = rect_.centerY();
rect_.top = center - rect_.width() / 2;
rect_.bottom = center + rect_.width() / 2;
} else {
int center = rect_.centerX();
rect_.left = center - rect_.height() / 2;
rect_.right = center + rect_.height() / 2;
}
paint_.setColor(Color.BLACK);
paint_.setStyle(Paint.Style.FILL);
canvas.drawRect(rect_, paint_);
// Draw waveforms.
float lineWidth = 5.0f;
float margin = 15.0f;
float waveWidth = (rect_.width() - 3.0f * margin) / 2.0f;
float waveHeight = (rect_.height() - 4.0f * margin) / 3.0f;
float xOffset = margin;
float yOffset = margin;
drawSine(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
yOffset += waveHeight;
yOffset += margin;
drawTriangle(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
yOffset += waveHeight;
yOffset += margin;
drawSquare(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
yOffset = margin;
xOffset += waveWidth;
xOffset += margin;
drawSawtooth(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
yOffset += waveHeight;
yOffset += margin;
drawNoise(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
yOffset += waveHeight;
yOffset += margin;
drawOther(canvas, xOffset, yOffset, waveWidth, waveHeight, margin, lineWidth);
}
/**
* Controls how the knob is sized; it is square, and prefers to be 100x100 pixels.
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Specify that 100 is preferred for both dimensions.
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
width = 100;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
height = 100;
break;
}
// Make it square.
if (width > height && widthMode != MeasureSpec.EXACTLY) {
width = height;
}
if (height > width && heightMode != MeasureSpec.EXACTLY) {
height = width;
}
setMeasuredDimension(width, height);
}
/**
* Connects control to a WaveformInput.
* @input - The synthesizer input to connect to.
*/
public void bindTo(WaveformInput waveform) {
input_ = waveform;
setWaveform(waveform.getWaveform(waveform.getSelected()));
setWaveformListener(new WaveformListener() {
public void onWaveformChanged(String newValue) {
input_.select(newValue);
}
});
}
/**
* Connects control to a WaveformSelector module.
* @synth - The synthesizer to connect to.
* @setting - The setting to connect to.
* @return - True on success, false on failure.
*/
public boolean bindTo(final MultiChannelSynthesizer synth, int channel, Setting setting) {
WaveformInput input = synth.getChannel(0).getWaveformInput(setting);
if (input != null) {
bindTo(input);
return true;
} else {
Log.e(getClass().getName(), "Unable to bind to setting " + setting.name() + ".");
return false;
}
}
// Currently selected waveform.
private String waveform_;
protected WaveformInput input_;
// Structures used in drawing that we don't want to reallocate every time we draw.
protected Paint paint_;
protected Path path_;
protected Rect rect_;
// Object listening for events when the knob's value changes.
private WaveformListener listener_;
}

1
app/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,41 @@
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.levien.synthesizer"
minSdkVersion.apiLevel 16
targetSdkVersion.apiLevel 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-android.txt'))
}
}
ndk {
moduleName "synth"
ldLibs.addAll(['log', 'OpenSLES'])
}
sources {
main {
jni {
source {
excludes.addAll(["main.cc", "wavout.cc", "test_*.cc", "SynthApp/*"])
}
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/raph/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

@ -0,0 +1,13 @@
package com.levien.synthesizer;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

@ -0,0 +1,76 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.levien.synthesizer">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name="com.levien.synthesizer.android.ui.PianoActivity2"
android:label="@string/app_name"
android:screenOrientation="landscape"
android:theme="@style/LightThemeSelector"
android:icon="@drawable/icon"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.levien.synthesizer.android.ui.MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.ScoreActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.ChordGridActivity"
android:label="@string/chord_grid"
android:screenOrientation="portrait" />
<activity
android:name="com.levien.synthesizer.android.ui.InstrumentListActivity"
android:label="@string/instrument_list"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.EditInstrumentActivity"
android:label="@string/edit_instrument"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.VibratoActivity"
android:label="@string/vibrato"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.OscillatorActivity"
android:label="@string/oscillator"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.TremoloActivity"
android:label="@string/tremolo"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.LowPassFilterActivity"
android:label="@string/low_pass_filter"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.AmplificationActivity"
android:label="@string/amplification"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.EffectsActivity"
android:label="@string/effects"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.KarplusStrongActivity"
android:label="@string/karplus_strong"
android:screenOrientation="landscape" />
<activity
android:name="com.levien.synthesizer.android.ui.SettingsActivity"
android:label="@string/settings" />
<service android:name=".android.service.SynthesizerService">
</service>
</application>
</manifest>

@ -43,7 +43,6 @@ import com.levien.synthesizer.android.AndroidGlue;
import com.levien.synthesizer.android.usb.UsbMidiDevice; import com.levien.synthesizer.android.usb.UsbMidiDevice;
import com.levien.synthesizer.core.midi.MessageTee; import com.levien.synthesizer.core.midi.MessageTee;
import com.levien.synthesizer.core.midi.MidiListener; import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
/** /**
* An Android Service that plays audio from a synthesizer. * An Android Service that plays audio from a synthesizer.
@ -56,15 +55,6 @@ public class SynthesizerService extends Service {
public SynthesizerService getService() { public SynthesizerService getService() {
return SynthesizerService.this; return SynthesizerService.this;
} }
/**
* Gets the underlying synthesizer powering this service.
*
* Obsolete, to be deleted.
*/
public MultiChannelSynthesizer getSynthesizer() {
return null;
}
} }
/** /**

@ -14,14 +14,14 @@
* limitations under the License. * limitations under the License.
*/ */
// This class is probably obsolete and should be deleted
package com.levien.synthesizer.android.service; package com.levien.synthesizer.android.service;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.AudioTrack; import android.media.AudioTrack;
import android.util.Log; import android.util.Log;
import com.levien.synthesizer.core.model.SynthesisTime;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
/** /**
* SynthesizerThread is a thread-safe interface to a thread that constantly plays sampled audio data * SynthesizerThread is a thread-safe interface to a thread that constantly plays sampled audio data
@ -30,18 +30,14 @@ import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
public class SynthesizerThread { public class SynthesizerThread {
/** /**
* Creates a new SynthesizerThread that will play audio from synthesizer. * Creates a new SynthesizerThread that will play audio from synthesizer.
* @param synthesizer - the source of the audio data to output.
*/ */
public SynthesizerThread(MultiChannelSynthesizer synthesizer, int sampleRateInHz) { public SynthesizerThread(int sampleRateInHz) {
playingLock_ = new Object(); playingLock_ = new Object();
playing_ = false; playing_ = false;
shouldDie_ = false; shouldDie_ = false;
audioTrackLock_ = new Object(); audioTrackLock_ = new Object();
audioTrack_ = null; audioTrack_ = null;
synthesizer_ = synthesizer;
sampleRateInHz_ = sampleRateInHz; sampleRateInHz_ = sampleRateInHz;
time_ = new SynthesisTime();
time_.setSampleRate(sampleRateInHz);
} }
/** /**
@ -141,7 +137,6 @@ public class SynthesizerThread {
bufferSizeInBytes, // int bufferSizeInBytes, bufferSizeInBytes, // int bufferSizeInBytes,
AudioTrack.MODE_STREAM); // int mode); AudioTrack.MODE_STREAM); // int mode);
buffer_ = new short[bufferSizeInBytes / 8]; buffer_ = new short[bufferSizeInBytes / 8];
time_.reset();
} }
} }
@ -177,7 +172,7 @@ public class SynthesizerThread {
for (int i = 0; i < buffer_.length; ++i) { for (int i = 0; i < buffer_.length; ++i) {
// Change the output range from [-1, 1] to [-32767, 32767]. // Change the output range from [-1, 1] to [-32767, 32767].
// 16-bit signed output is fairly standard, and hard-coded. // 16-bit signed output is fairly standard, and hard-coded.
double output = synthesizer_.getValue(time_); double output = 0;
// Clamp values out of range. // Clamp values out of range.
if (output < -1.0) { if (output < -1.0) {
output = -1.0; output = -1.0;
@ -186,7 +181,6 @@ public class SynthesizerThread {
output = 1.0; output = 1.0;
} }
buffer_[i] = (short)(32767 * output); buffer_[i] = (short)(32767 * output);
time_.advance();
} }
synchronized (audioTrackLock_) { synchronized (audioTrackLock_) {
if (audioTrack_ != null) { if (audioTrack_ != null) {
@ -221,10 +215,4 @@ public class SynthesizerThread {
// The sample rate of the synthesizer. // The sample rate of the synthesizer.
private int sampleRateInHz_; private int sampleRateInHz_;
// Tracker for time since synthesis started.
private SynthesisTime time_;
// Module to provide sampled audio data to be output.
private MultiChannelSynthesizer synthesizer_;
} }

@ -89,9 +89,11 @@ public class PianoActivity2 extends SynthActivity implements OnSharedPreferenceC
case R.id.settings: case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class)); startActivity(new Intent(this, SettingsActivity.class));
return true; return true;
/*
case R.id.compose: case R.id.compose:
startActivity(new Intent(this, ScoreActivity.class)); startActivity(new Intent(this, ScoreActivity.class));
return true; return true;
*/
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

@ -34,9 +34,6 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.levien.synthesizer.R; import com.levien.synthesizer.R;
import com.levien.synthesizer.core.model.SynthesizerInput;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
import com.levien.synthesizer.core.model.composite.Presets.Setting;
/** /**
* KnobView is a widget for setting a real value by turning a virtual "knob". * KnobView is a widget for setting a real value by turning a virtual "knob".
@ -262,36 +259,6 @@ public class KnobView extends View {
setMeasuredDimension(width, height); setMeasuredDimension(width, height);
} }
/**
* Connects knob to a SynthesizerInput.
* @input - The synthesizer input to connect to.
*/
public void bindTo(final SynthesizerInput input) {
setValue(input.getSynthesizerInputValue());
setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
input.setValue(newValue);
}
});
}
/**
* Connects knob to a SynthesizerInput.
* @synth - The synthesizer to connect to.
* @path - The setting to connect to.
* @return - True on success, false on failure.
*/
public boolean bindTo(final MultiChannelSynthesizer synth, int channel, Setting setting) {
SynthesizerInput input = synth.getChannel(channel).getSynthesizerInput(setting);
if (input != null) {
bindTo(input);
return true;
} else {
Log.e(getClass().getName(), "Unable to bind to setting " + setting.name() + ".");
return false;
}
}
// Knob's current value, ranges from 0 - 1.0. // Knob's current value, ranges from 0 - 1.0.
private double knobValue_; private double knobValue_;
private double min_; private double min_;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save