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