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
parent
6b8187fba8
commit
076ab6eab8
@ -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:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/MusicSynthesizer/libs" type="2"/> </resources>}"/> |
|
||||||
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/> |
|
||||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/MusicSynthesizer/jni" type="2"/> </resources>}"/> |
|
||||||
<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:<?xml version="1.0" encoding="UTF-8"?> <resources> <item path="/MusicSynthesizer/core/com/levien/synthesizer/core/model/composite/Presets.proto" type="1"/> <item path="/MusicSynthesizer/core/com/levien/synthesizer/core/model/sample/Proto.proto" type="1"/> <item path="/MusicSynthesizer/core/com/levien/synthesizer/core/music/Music.proto" type="1"/> </resources>}"/> |
|
||||||
<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><project>/.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><project>/.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_; |
|
||||||
} |
|
@ -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> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue