@ -0,0 +1,202 @@ |
|||||||
|
|
||||||
|
Apache License |
||||||
|
Version 2.0, January 2004 |
||||||
|
http://www.apache.org/licenses/ |
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||||
|
|
||||||
|
1. Definitions. |
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||||
|
the copyright owner that is granting the License. |
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||||
|
other entities that control, are controlled by, or are under common |
||||||
|
control with that entity. For the purposes of this definition, |
||||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||||
|
direction or management of such entity, whether by contract or |
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||||
|
exercising permissions granted by this License. |
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, |
||||||
|
including but not limited to software source code, documentation |
||||||
|
source, and configuration files. |
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical |
||||||
|
transformation or translation of a Source form, including but |
||||||
|
not limited to compiled object code, generated documentation, |
||||||
|
and conversions to other media types. |
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||||
|
Object form, made available under the License, as indicated by a |
||||||
|
copyright notice that is included in or attached to the work |
||||||
|
(an example is provided in the Appendix below). |
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||||
|
form, that is based on (or derived from) the Work and for which the |
||||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||||
|
of this License, Derivative Works shall not include works that remain |
||||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||||
|
the Work and Derivative Works thereof. |
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including |
||||||
|
the original version of the Work and any modifications or additions |
||||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||||
|
means any form of electronic, verbal, or written communication sent |
||||||
|
to the Licensor or its representatives, including but not limited to |
||||||
|
communication on electronic mailing lists, source code control systems, |
||||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||||
|
excluding communication that is conspicuously marked or otherwise |
||||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||||
|
subsequently incorporated within the Work. |
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||||
|
Work and such Derivative Works in Source or Object form. |
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||||
|
(except as stated in this section) patent license to make, have made, |
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||||
|
where such license applies only to those patent claims licensable |
||||||
|
by such Contributor that are necessarily infringed by their |
||||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||||
|
institute patent litigation against any entity (including a |
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||||
|
or a Contribution incorporated within the Work constitutes direct |
||||||
|
or contributory patent infringement, then any patent licenses |
||||||
|
granted to You under this License for that Work shall terminate |
||||||
|
as of the date such litigation is filed. |
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||||
|
Work or Derivative Works thereof in any medium, with or without |
||||||
|
modifications, and in Source or Object form, provided that You |
||||||
|
meet the following conditions: |
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or |
||||||
|
Derivative Works a copy of this License; and |
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices |
||||||
|
stating that You changed the files; and |
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||||
|
that You distribute, all copyright, patent, trademark, and |
||||||
|
attribution notices from the Source form of the Work, |
||||||
|
excluding those notices that do not pertain to any part of |
||||||
|
the Derivative Works; and |
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||||
|
distribution, then any Derivative Works that You distribute must |
||||||
|
include a readable copy of the attribution notices contained |
||||||
|
within such NOTICE file, excluding those notices that do not |
||||||
|
pertain to any part of the Derivative Works, in at least one |
||||||
|
of the following places: within a NOTICE text file distributed |
||||||
|
as part of the Derivative Works; within the Source form or |
||||||
|
documentation, if provided along with the Derivative Works; or, |
||||||
|
within a display generated by the Derivative Works, if and |
||||||
|
wherever such third-party notices normally appear. The contents |
||||||
|
of the NOTICE file are for informational purposes only and |
||||||
|
do not modify the License. You may add Your own attribution |
||||||
|
notices within Derivative Works that You distribute, alongside |
||||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||||
|
that such additional attribution notices cannot be construed |
||||||
|
as modifying the License. |
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and |
||||||
|
may provide additional or different license terms and conditions |
||||||
|
for use, reproduction, or distribution of Your modifications, or |
||||||
|
for any such Derivative Works as a whole, provided Your use, |
||||||
|
reproduction, and distribution of the Work otherwise complies with |
||||||
|
the conditions stated in this License. |
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||||
|
by You to the Licensor shall be under the terms and conditions of |
||||||
|
this License, without any additional terms or conditions. |
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||||
|
the terms of any separate license agreement you may have executed |
||||||
|
with Licensor regarding such Contributions. |
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||||
|
except as required for reasonable and customary use in describing the |
||||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||||
|
agreed to in writing, Licensor provides the Work (and each |
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||||
|
implied, including, without limitation, any warranties or conditions |
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||||
|
appropriateness of using or redistributing the Work and assume any |
||||||
|
risks associated with Your exercise of permissions under this License. |
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||||
|
whether in tort (including negligence), contract, or otherwise, |
||||||
|
unless required by applicable law (such as deliberate and grossly |
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||||
|
liable to You for damages, including any direct, indirect, special, |
||||||
|
incidental, or consequential damages of any character arising as a |
||||||
|
result of this License or out of the use or inability to use the |
||||||
|
Work (including but not limited to damages for loss of goodwill, |
||||||
|
work stoppage, computer failure or malfunction, or any and all |
||||||
|
other commercial damages or losses), even if such Contributor |
||||||
|
has been advised of the possibility of such damages. |
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||||
|
or other liability obligations and/or rights consistent with this |
||||||
|
License. However, in accepting such obligations, You may act only |
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||||
|
of any other Contributor, and only if You agree to indemnify, |
||||||
|
defend, and hold each Contributor harmless for any liability |
||||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||||
|
of your accepting any such warranty or additional liability. |
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS |
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work. |
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following |
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||||
|
replaced with your own identifying information. (Don't include |
||||||
|
the brackets!) The text should be enclosed in the appropriate |
||||||
|
comment syntax for the file format. We also recommend that a |
||||||
|
file or class name and description of purpose be included on the |
||||||
|
same "printed page" as the copyright notice for easier |
||||||
|
identification within third-party archives. |
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner] |
||||||
|
|
||||||
|
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. |
@ -0,0 +1,11 @@ |
|||||||
|
<?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 kind="lib" path="core-lib/libprotobuf.jar"/> |
||||||
|
<classpathentry kind="output" path="bin"/> |
||||||
|
</classpath> |
@ -0,0 +1,10 @@ |
|||||||
|
<?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/google/synthesizer/core/model/composite/Presets.proto" type="1"/> <item path="/MusicSynthesizer/core/com/google/synthesizer/core/model/sample/Proto.proto" type="1"/> <item path="/MusicSynthesizer/core/com/google/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/google/synthesizer/core/model/composite/Presets.proto src/com/google/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> |
@ -0,0 +1,65 @@ |
|||||||
|
<?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>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> |
@ -0,0 +1,280 @@ |
|||||||
|
#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 |
@ -0,0 +1,8 @@ |
|||||||
|
#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 |
@ -0,0 +1,73 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<manifest |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="com.google.synthesizer" |
||||||
|
android:versionCode="2" |
||||||
|
android:versionName="0.9"> |
||||||
|
<uses-sdk android:minSdkVersion="8" /> <!-- 8 = Froyo 2.2 --> |
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
||||||
|
<application android:icon="@drawable/icon" android:label="@string/app_name"> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.PianoActivity" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:screenOrientation="landscape"> |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.intent.action.MAIN" /> |
||||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||||
|
</intent-filter> |
||||||
|
</activity> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.MainActivity" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.ScoreActivity" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.ChordGridActivity" |
||||||
|
android:label="@string/chord_grid" |
||||||
|
android:screenOrientation="portrait" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.InstrumentListActivity" |
||||||
|
android:label="@string/instrument_list" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.EditInstrumentActivity" |
||||||
|
android:label="@string/edit_instrument" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.VibratoActivity" |
||||||
|
android:label="@string/vibrato" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.OscillatorActivity" |
||||||
|
android:label="@string/oscillator" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.TremoloActivity" |
||||||
|
android:label="@string/tremolo" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.LowPassFilterActivity" |
||||||
|
android:label="@string/low_pass_filter" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.AmplificationActivity" |
||||||
|
android:label="@string/amplification" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.EffectsActivity" |
||||||
|
android:label="@string/effects" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<activity |
||||||
|
android:name=".android.ui.KarplusStrongActivity" |
||||||
|
android:label="@string/karplus_strong" |
||||||
|
android:screenOrientation="landscape" /> |
||||||
|
<service android:name=".android.service.SynthesizerService"> |
||||||
|
<intent-filter> |
||||||
|
<action android:name=".android.service.ISynthesizerService" /> |
||||||
|
</intent-filter> |
||||||
|
</service> |
||||||
|
</application> |
||||||
|
</manifest> |
@ -0,0 +1,11 @@ |
|||||||
|
# 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-8 |
After Width: | Height: | Size: 356 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 928 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 731 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 602 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 935 B |
After Width: | Height: | Size: 731 B |
After Width: | Height: | Size: 765 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 722 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 854 B |
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,81 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/attack" |
||||||
|
android:id="@+id/attackLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/decay" |
||||||
|
android:id="@+id/decayLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/sustain" |
||||||
|
android:id="@+id/sustainLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/release" |
||||||
|
android:id="@+id/releaseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/volume" |
||||||
|
android:id="@+id/volumeLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/attackKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/decayKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/sustainKnob" |
||||||
|
app:value="1.0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/releaseKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/emptyKnob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/volumeKnob" |
||||||
|
app:value="1.0" |
||||||
|
app:min="0.0" |
||||||
|
app:max="25.0" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,32 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:orientation="horizontal"> |
||||||
|
<Spinner |
||||||
|
android:id="@+id/presetSpinner" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.ChordGridView |
||||||
|
android:id="@+id/chord_grid" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,76 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/echo_mix" |
||||||
|
android:id="@+id/echoMixLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/echo_delay" |
||||||
|
android:id="@+id/echoDelayLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty1Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty2Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty3Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty4Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/echoMixKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0.0" |
||||||
|
app:max="1.0" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/echoDelayKnob" |
||||||
|
app:value="0.5" |
||||||
|
app:min="0.1" |
||||||
|
app:max="2.0" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty1Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty2Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty3Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty4Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,79 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/blend" |
||||||
|
android:id="@+id/blendLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/stretch" |
||||||
|
android:id="@+id/stretchLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/excitement" |
||||||
|
android:id="@+id/excitementLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty1Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty2Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty3Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/blendKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/stretchKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/excitementKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty1Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty2Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty3Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4" /> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,88 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/cutoff" |
||||||
|
android:id="@+id/cutoffLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/depth" |
||||||
|
android:id="@+id/depthLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/attack" |
||||||
|
android:id="@+id/attackLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/decay" |
||||||
|
android:id="@+id/decayLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/sustain" |
||||||
|
android:id="@+id/sustainLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/release" |
||||||
|
android:id="@+id/releaseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/cutoffKnob" |
||||||
|
app:value="1" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/depthKnob" |
||||||
|
app:value="0" |
||||||
|
app:min="-1" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/attackKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/decayKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/sustainKnob" |
||||||
|
app:value="1.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/releaseKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,45 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:orientation="horizontal"> |
||||||
|
<Button |
||||||
|
android:text="@string/record" |
||||||
|
android:id="@+id/recordButton" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content"/> |
||||||
|
<Button |
||||||
|
android:text="@string/play" |
||||||
|
android:id="@+id/playButton" |
||||||
|
android:enabled="true" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content"/> |
||||||
|
<Spinner |
||||||
|
android:id="@+id/presetSpinner" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,90 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/glide" |
||||||
|
android:id="@+id/glideLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/coarse" |
||||||
|
android:id="@+id/coarseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/fine" |
||||||
|
android:id="@+id/fineLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/vibrato_depth" |
||||||
|
android:id="@+id/releaseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/empty1Label" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/balance" |
||||||
|
android:id="@+id/balanceLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/glideKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/coarseKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="-1.0" |
||||||
|
app:max="1.0" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/fineKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="-0.0833333333" |
||||||
|
app:max="0.0833333333" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/vibratoDepthKnob" |
||||||
|
app:value="0" |
||||||
|
app:min="0" |
||||||
|
app:max="0.1666666667" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/empty1Knob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/balanceKnob" |
||||||
|
app:value="0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.waveform.WaveformRowView |
||||||
|
android:id="@+id/waveform" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" /> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4" /> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,63 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/volume" |
||||||
|
android:id="@+id/volumeLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/volumeKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<Spinner |
||||||
|
android:id="@+id/presetSpinner" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_span="5" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4" /> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,29 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.score.ScoreView |
||||||
|
android:id="@+id/score" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4" /> |
||||||
|
<com.google.synthesizer.android.widgets.score.ScoreViewToolbar |
||||||
|
android:id="@+id/toolbar" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" /> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,93 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/rate" |
||||||
|
android:id="@+id/rateLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/depth" |
||||||
|
android:id="@+id/depthLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/attack" |
||||||
|
android:id="@+id/attackLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/decay" |
||||||
|
android:id="@+id/decayLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/sustain" |
||||||
|
android:id="@+id/sustainLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/release" |
||||||
|
android:id="@+id/releaseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/rateKnob" |
||||||
|
app:value="0" |
||||||
|
app:min="0" |
||||||
|
app:max="10" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/depthKnob" |
||||||
|
app:value="0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/attackKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/decayKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/sustainKnob" |
||||||
|
app:value="1.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/releaseKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.waveform.WaveformRowView |
||||||
|
android:id="@+id/waveform" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1"/> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,90 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TableLayout |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res/com.google.synthesizer" |
||||||
|
android:id="@+id/TableLayout01" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:stretchColumns="*" |
||||||
|
android:shrinkColumns="*"> |
||||||
|
<TableRow> |
||||||
|
<TextView |
||||||
|
android:text="@string/rate" |
||||||
|
android:id="@+id/rateLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="" |
||||||
|
android:id="@+id/emptyLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/attack" |
||||||
|
android:id="@+id/attackLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/decay" |
||||||
|
android:id="@+id/decayLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/sustain" |
||||||
|
android:id="@+id/sustainLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
<TextView |
||||||
|
android:text="@string/release" |
||||||
|
android:id="@+id/releaseLabel" |
||||||
|
android:gravity="center_horizontal" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/rateKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="10" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobPlaceholderView |
||||||
|
android:id="@+id/depthKnob" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/attackKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/decayKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/sustainKnob" |
||||||
|
app:value="1.0" |
||||||
|
app:min="0" |
||||||
|
app:max="1" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
<com.google.synthesizer.android.widgets.knob.KnobView |
||||||
|
android:id="@+id/releaseKnob" |
||||||
|
app:value="0.0" |
||||||
|
app:min="0" |
||||||
|
app:max="5" |
||||||
|
android:layout_margin="2px" /> |
||||||
|
</TableRow> |
||||||
|
<TableRow> |
||||||
|
<LinearLayout |
||||||
|
android:layout_height="fill_parent" |
||||||
|
android:layout_span="6" |
||||||
|
android:orientation="vertical"> |
||||||
|
<com.google.synthesizer.android.widgets.waveform.WaveformRowView |
||||||
|
android:id="@+id/waveform" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1"/> |
||||||
|
<com.google.synthesizer.android.widgets.piano.PianoView |
||||||
|
android:id="@+id/piano" |
||||||
|
android:layout_width="fill_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" |
||||||
|
app:octaves="2" |
||||||
|
app:first_octave="4"/> |
||||||
|
</LinearLayout> |
||||||
|
</TableRow> |
||||||
|
</TableLayout> |
@ -0,0 +1,7 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<item android:id="@+id/piano" android:title="@string/piano" /> |
||||||
|
<item android:id="@+id/chord_grid" android:title="@string/chord_grid" /> |
||||||
|
<item android:id="@+id/edit_instrument" android:title="@string/edit_instrument" /> |
||||||
|
<item android:id="@+id/compose" android:title="@string/compose" /> |
||||||
|
</menu> |
@ -0,0 +1,9 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"> |
||||||
|
<item android:id="@+id/new_score" android:title="@string/new_score" /> |
||||||
|
<item android:id="@+id/save_score" android:title="@string/save_score" /> |
||||||
|
<item android:id="@+id/open_score" android:title="@string/open_score" /> |
||||||
|
<item android:id="@+id/piano" android:title="@string/piano" /> |
||||||
|
<item android:id="@+id/chord_grid" android:title="@string/chord_grid" /> |
||||||
|
<item android:id="@+id/edit_instrument" android:title="@string/edit_instrument" /> |
||||||
|
</menu> |
@ -0,0 +1,380 @@ |
|||||||
|
# |
||||||
|
# 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. |
||||||
|
# |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "organ" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 1.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.02 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "square" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: -1.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.01 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "square" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.75 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.2 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 0.15 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: FILTER_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: FILTER_DECAY value: 0.0 > |
||||||
|
input_setting < setting: FILTER_SUSTAIN value: 0.0 > |
||||||
|
input_setting < setting: FILTER_RELEASE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.01 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 0.01 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.10 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.10 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "rubberband" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "karplus-strong string" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.0 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 5.0 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "voice" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 5.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "triangle" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.05 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.17 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.01 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 0.5 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "flute" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.3 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 2.5 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.5 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.1 > |
||||||
|
input_setting < setting: DECAY value: 0.2 > |
||||||
|
input_setting < setting: SUSTAIN value: 0.9 > |
||||||
|
input_setting < setting: RELEASE value: 0.1 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "drums" |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "drums" > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.01 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 5.0 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: FILTER_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: FILTER_DECAY value: 0.0 > |
||||||
|
input_setting < setting: FILTER_SUSTAIN value: 0.0 > |
||||||
|
input_setting < setting: FILTER_RELEASE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "noise" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.01 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.01 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "noise" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 0.25 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: -1.0 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.01 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.01 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 0.25 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.01 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.01 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 0.01 > |
||||||
|
input_setting < setting: VOLUME value: 0.7 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "square" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "square" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.0 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 0.0 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.0 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.0 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
||||||
|
|
||||||
|
preset < |
||||||
|
name: "drawbar organ" |
||||||
|
|
||||||
|
input_setting < setting: VIBRATO_RATE value: 2.5 > |
||||||
|
input_setting < setting: VIBRATO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: VIBRATO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: VIBRATO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: VIBRATO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_1_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_1_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_1_WAVEFORM waveform: "drawbar organ" > |
||||||
|
|
||||||
|
input_setting < setting: OSCILLATOR_2_GLIDE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_COARSE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_FINE value: 0.0 > |
||||||
|
input_setting < setting: OSCILLATOR_2_VIBRATO value: 0.0 > |
||||||
|
waveform_setting < setting: OSCILLATOR_2_WAVEFORM waveform: "noise" > |
||||||
|
|
||||||
|
input_setting < setting: BALANCE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: TREMOLO_RATE value: 2.2 > |
||||||
|
input_setting < setting: TREMOLO_DEPTH value: 0.5 > |
||||||
|
input_setting < setting: TREMOLO_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_DECAY value: 0.0 > |
||||||
|
input_setting < setting: TREMOLO_SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: TREMOLO_RELEASE value: 0.0 > |
||||||
|
waveform_setting < setting: TREMOLO_WAVEFORM waveform: "sine" > |
||||||
|
|
||||||
|
input_setting < setting: FILTER_CUTOFF value: 1.0 > |
||||||
|
input_setting < setting: FILTER_DEPTH value: 0.0 > |
||||||
|
input_setting < setting: FILTER_ATTACK value: 0.0 > |
||||||
|
input_setting < setting: FILTER_DECAY value: 0.0 > |
||||||
|
input_setting < setting: FILTER_SUSTAIN value: 0.0 > |
||||||
|
input_setting < setting: FILTER_RELEASE value: 0.0 > |
||||||
|
|
||||||
|
input_setting < setting: ATTACK value: 0.01 > |
||||||
|
input_setting < setting: DECAY value: 0.0 > |
||||||
|
input_setting < setting: SUSTAIN value: 1.0 > |
||||||
|
input_setting < setting: RELEASE value: 0.01 > |
||||||
|
input_setting < setting: VOLUME value: 1.0 > |
||||||
|
|
||||||
|
input_setting < setting: ECHO_MIX value: 0.10 > |
||||||
|
input_setting < setting: ECHO_DELAY value: 0.10 > |
||||||
|
input_setting < setting: DELAY_MIX value: 0.5 > |
||||||
|
> |
@ -0,0 +1,15 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<declare-styleable name="ChordGridView"> |
||||||
|
<attr name="octave" format="integer" /> |
||||||
|
</declare-styleable> |
||||||
|
<declare-styleable name="KnobView"> |
||||||
|
<attr name="value" format="float" /> |
||||||
|
<attr name="min" format="float" /> |
||||||
|
<attr name="max" format="float" /> |
||||||
|
</declare-styleable> |
||||||
|
<declare-styleable name="PianoView"> |
||||||
|
<attr name="octaves" format="integer" /> |
||||||
|
<attr name="first_octave" format="integer" /> |
||||||
|
</declare-styleable> |
||||||
|
</resources> |
@ -0,0 +1,79 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<string name="app_name">Music Synthesizer</string> |
||||||
|
|
||||||
|
<!-- Sections --> |
||||||
|
<string name="new_score">New score</string> |
||||||
|
<string name="save_score">Save score as...</string> |
||||||
|
<string name="open_score">Open score...</string> |
||||||
|
|
||||||
|
<string name="compose">Compose</string> |
||||||
|
<string name="piano">Piano</string> |
||||||
|
<string name="edit_instrument">Edit instrument</string> |
||||||
|
<string name="instrument_list">Instrument List</string> |
||||||
|
|
||||||
|
<string name="chord_grid">Chord Grid</string> |
||||||
|
<string name="vibrato">Vibrato</string> |
||||||
|
<string name="oscillator">Oscillator</string> |
||||||
|
<string name="oscillator1">Oscillator 1</string> |
||||||
|
<string name="oscillator2">Oscillator 2</string> |
||||||
|
<string name="karplus_strong">Karplus-Strong</string> |
||||||
|
<string name="karplus_strong1">Karplus-Strong 1</string> |
||||||
|
<string name="karplus_strong2">Karplus-Strong 2</string> |
||||||
|
<string name="tremolo">Tremolo</string> |
||||||
|
<string name="low_pass_filter">Low Pass Filter</string> |
||||||
|
<string name="amplification">Amplification</string> |
||||||
|
<string name="effects">Effects</string> |
||||||
|
|
||||||
|
<!-- Vibrato --> |
||||||
|
<string name="rate">Rate</string> |
||||||
|
|
||||||
|
<!-- Karplus-Strong --> |
||||||
|
<string name="blend">Blend</string> |
||||||
|
<string name="stretch">Stretch</string> |
||||||
|
<string name="excitement">Excitement</string> |
||||||
|
|
||||||
|
<!-- Oscillator --> |
||||||
|
<string name="glide">Glide</string> |
||||||
|
<string name="coarse">Coarse</string> |
||||||
|
<string name="fine">Fine</string> |
||||||
|
<string name="vibrato_depth">Vibrato</string> |
||||||
|
|
||||||
|
<!-- Mixing --> |
||||||
|
<string name="balance">Balance</string> |
||||||
|
|
||||||
|
<!-- Tremolo --> |
||||||
|
<string name="depth">Depth</string> |
||||||
|
|
||||||
|
<!-- Low Pass Filter --> |
||||||
|
<string name="cutoff">Cutoff</string> |
||||||
|
|
||||||
|
<!-- Amp --> |
||||||
|
<string name="attack">Attack</string> |
||||||
|
<string name="decay">Decay</string> |
||||||
|
<string name="sustain">Sustain</string> |
||||||
|
<string name="release">Release</string> |
||||||
|
<string name="volume">Volume</string> |
||||||
|
|
||||||
|
<!-- Effects --> |
||||||
|
<string name="echo_mix">Echo Mix</string> |
||||||
|
<string name="echo_delay">Echo Delay</string> |
||||||
|
|
||||||
|
<!-- Delay --> |
||||||
|
<string name="play">Play</string> |
||||||
|
<string name="record">Record</string> |
||||||
|
<string name="stop">Stop</string> |
||||||
|
|
||||||
|
<!-- Array of Sections --> |
||||||
|
<string-array name="sections"> |
||||||
|
<item>Vibrato</item> |
||||||
|
<item>Oscillator 1</item> |
||||||
|
<item>Oscillator 2</item> |
||||||
|
<item>Karplus-Strong 1</item> |
||||||
|
<item>Karplus-Strong 2</item> |
||||||
|
<item>Tremolo</item> |
||||||
|
<item>Low Pass Filter</item> |
||||||
|
<item>Amplification</item> |
||||||
|
<item>Effects</item> |
||||||
|
</string-array> |
||||||
|
</resources> |
@ -0,0 +1,279 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.service; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.util.logging.Level; |
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import android.app.Service; |
||||||
|
import android.content.Intent; |
||||||
|
import android.media.AudioManager; |
||||||
|
import android.media.AudioTrack; |
||||||
|
import android.os.Binder; |
||||||
|
import android.os.IBinder; |
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.soundfont.SoundFontReader; |
||||||
|
|
||||||
|
/** |
||||||
|
* An Android Service that plays audio from a synthesizer. |
||||||
|
* The Service is created when the first Activity binds to it, and destroys itself when no more |
||||||
|
* Activities are bound to it. |
||||||
|
*/ |
||||||
|
public class SynthesizerService extends Service { |
||||||
|
// Class for local client access.
|
||||||
|
public class LocalBinder extends Binder { |
||||||
|
public SynthesizerService getService() { |
||||||
|
return SynthesizerService.this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the underlying synthesizer powering this service. |
||||||
|
*/ |
||||||
|
public MultiChannelSynthesizer getSynthesizer() { |
||||||
|
return SynthesizerService.this.synthesizer_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public SynthesizerService() { |
||||||
|
logger_ = Logger.getLogger(getClass().getName()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Run when the Service is first created. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void onCreate() { |
||||||
|
super.onCreate(); |
||||||
|
|
||||||
|
// Get the native audio settings.
|
||||||
|
int sampleRateInHz = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC); |
||||||
|
// For now, cap the sample rate to reduce cpu requirements.
|
||||||
|
sampleRateInHz = Math.min(sampleRateInHz, 11025); |
||||||
|
SoundFontReader sampleLibrary = null; |
||||||
|
InputStream sampleLibraryFile = getResources().openRawResource(R.raw.drums); |
||||||
|
try { |
||||||
|
sampleLibrary = new SoundFontReader(sampleLibraryFile); |
||||||
|
} catch (IOException e) { |
||||||
|
logger_.log(Level.SEVERE, "Unable to load sample library.", e); |
||||||
|
sampleLibrary = null; |
||||||
|
} |
||||||
|
synthesizer_ = new MultiChannelSynthesizer(CHANNELS, FINGERS, sampleRateInHz, sampleLibrary); |
||||||
|
|
||||||
|
// Load the presets from a file.
|
||||||
|
InputStream presetsFile = getResources().openRawResource(R.raw.presets); |
||||||
|
try { |
||||||
|
synthesizer_.loadLibraryFromText(presetsFile); |
||||||
|
} catch (IOException e) { |
||||||
|
Log.e(getClass().getName(), "Unable to load presets from raw file.", e); |
||||||
|
} |
||||||
|
|
||||||
|
// Start a synthsizer thread playing the data.
|
||||||
|
synthesizerThread_ = new SynthesizerThread(synthesizer_, sampleRateInHz); |
||||||
|
synthesizerThread_.play(); |
||||||
|
|
||||||
|
// No Activities are yet bound to this Service.
|
||||||
|
referenceCount_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Run when this Service is finally destroyed. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void onDestroy() { |
||||||
|
super.onDestroy(); |
||||||
|
|
||||||
|
// Free up the underlying data structures.
|
||||||
|
synthesizerThread_.stop(); |
||||||
|
synthesizerThread_ = null; |
||||||
|
synthesizer_ = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Run when an Activity binds to this Service. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public IBinder onBind(Intent intent) { |
||||||
|
++referenceCount_; |
||||||
|
return binder_; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Run when any Activity unbinds from this Service. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean onUnbind(Intent intent) { |
||||||
|
if (--referenceCount_ == 0) { |
||||||
|
// No more Activities are using this Service, so kill it.
|
||||||
|
stopSelf(); |
||||||
|
} |
||||||
|
return super.onUnbind(intent); |
||||||
|
} |
||||||
|
|
||||||
|
// Binder to use for Activities in this process.
|
||||||
|
private final IBinder binder_ = new LocalBinder(); |
||||||
|
|
||||||
|
// The module that provides the sampled audio data.
|
||||||
|
private MultiChannelSynthesizer synthesizer_; |
||||||
|
|
||||||
|
// The thread that actually does the work of playing the audio data.
|
||||||
|
private SynthesizerThread synthesizerThread_; |
||||||
|
|
||||||
|
// How many Activities are currently bound to this Service.
|
||||||
|
private int referenceCount_; |
||||||
|
|
||||||
|
// The number of channels the synthesizer supports.
|
||||||
|
private static final int CHANNELS = 8; |
||||||
|
|
||||||
|
// The number of fingers the synthesizer supports.
|
||||||
|
private static final int FINGERS = 5; |
||||||
|
|
||||||
|
private Logger logger_; |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.service; |
||||||
|
|
||||||
|
import android.media.AudioFormat; |
||||||
|
import android.media.AudioManager; |
||||||
|
import android.media.AudioTrack; |
||||||
|
import android.util.Log; |
||||||
|
import com.google.synthesizer.core.model.SynthesisTime; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
|
||||||
|
/** |
||||||
|
* SynthesizerThread is a thread-safe interface to a thread that constantly plays sampled audio data |
||||||
|
* from a given SignalProvider. |
||||||
|
*/ |
||||||
|
public class SynthesizerThread { |
||||||
|
/** |
||||||
|
* Creates a new SynthesizerThread that will play audio from synthesizer. |
||||||
|
* @param synthesizer - the source of the audio data to output. |
||||||
|
*/ |
||||||
|
public SynthesizerThread(MultiChannelSynthesizer synthesizer, int sampleRateInHz) { |
||||||
|
playingLock_ = new Object(); |
||||||
|
playing_ = false; |
||||||
|
shouldDie_ = false; |
||||||
|
audioTrackLock_ = new Object(); |
||||||
|
audioTrack_ = null; |
||||||
|
synthesizer_ = synthesizer; |
||||||
|
sampleRateInHz_ = sampleRateInHz; |
||||||
|
time_ = new SynthesisTime(); |
||||||
|
time_.setSampleRate(sampleRateInHz); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts the thread taking sampled audio data from the synthesizer and playing it. |
||||||
|
*/ |
||||||
|
public void play() { |
||||||
|
synchronized (playingLock_) { |
||||||
|
if (playing_) { |
||||||
|
// It's already playing.
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Set up the initial state.
|
||||||
|
playing_ = true; |
||||||
|
shouldDie_ = false; |
||||||
|
initSound(); |
||||||
|
fillBuffer(); |
||||||
|
|
||||||
|
// Start the audio track playing from the buffer.
|
||||||
|
startSound(); |
||||||
|
|
||||||
|
// Start the thread that grabs the output from the synthesizer and fills the output buffer.
|
||||||
|
speakerThread_ = new Thread(new Runnable() { |
||||||
|
public void run() { |
||||||
|
while (true) { |
||||||
|
synchronized (playingLock_) { |
||||||
|
// Check if the thread should die.
|
||||||
|
if (shouldDie_) { |
||||||
|
// This thread has been signalled to stop.
|
||||||
|
Log.i(getClass().getName(), "Dying now."); |
||||||
|
playing_ = false; |
||||||
|
shouldDie_ = false; |
||||||
|
cleanupSound(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
// Do the actual work.
|
||||||
|
fillBuffer(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
speakerThread_.setName(getClass().getName()); |
||||||
|
speakerThread_.setPriority(Thread.MAX_PRIORITY); |
||||||
|
speakerThread_.setDaemon(true); |
||||||
|
speakerThread_.start(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tells the thread it should stop getting sampled data and outputting it as soon as possible. |
||||||
|
*/ |
||||||
|
public void stop() { |
||||||
|
Log.i(getClass().getName(), "stop() is waiting for playingLock_."); |
||||||
|
synchronized (playingLock_) { |
||||||
|
Log.i(getClass().getName(), "stop() received."); |
||||||
|
shouldDie_ = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Blocks until the thread is no longer playing. |
||||||
|
* You better call stop() before calling this. |
||||||
|
*/ |
||||||
|
public void waitForStop() { |
||||||
|
while (true) { |
||||||
|
synchronized (playingLock_) { |
||||||
|
if (!playing_) { |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes the data structures needed for playing. |
||||||
|
*/ |
||||||
|
private void initSound() { |
||||||
|
synchronized (audioTrackLock_) { |
||||||
|
if (audioTrack_ != null) { |
||||||
|
cleanupSound(); |
||||||
|
} |
||||||
|
|
||||||
|
// Get the smallest buffer to minimize latency.
|
||||||
|
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz_, |
||||||
|
AudioFormat.CHANNEL_CONFIGURATION_MONO, |
||||||
|
AudioFormat.ENCODING_PCM_16BIT); |
||||||
|
Log.i(getClass().getName(), "Output sample rate: " + sampleRateInHz_); |
||||||
|
Log.i(getClass().getName(), "Minimum buffer size: " + bufferSizeInBytes); |
||||||
|
Log.i(getClass().getName(), "Minimum volume: " + AudioTrack.getMinVolume()); |
||||||
|
Log.i(getClass().getName(), "Maximum volume: " + AudioTrack.getMaxVolume()); |
||||||
|
|
||||||
|
audioTrack_ = new AudioTrack( |
||||||
|
AudioManager.STREAM_MUSIC, // int streamType,
|
||||||
|
sampleRateInHz_, // int sampleRateInHz,
|
||||||
|
AudioFormat.CHANNEL_OUT_MONO, // int channelConfig,
|
||||||
|
AudioFormat.ENCODING_PCM_16BIT, // int audioFormat,
|
||||||
|
bufferSizeInBytes, // int bufferSizeInBytes,
|
||||||
|
AudioTrack.MODE_STREAM); // int mode);
|
||||||
|
buffer_ = new short[bufferSizeInBytes / 8]; |
||||||
|
time_.reset(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Starts the AudioTrack playing. |
||||||
|
*/ |
||||||
|
private void startSound() { |
||||||
|
synchronized (audioTrackLock_) { |
||||||
|
if (audioTrack_ != null) { |
||||||
|
audioTrack_.play(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Cleans up the internal data structures. |
||||||
|
*/ |
||||||
|
private void cleanupSound() { |
||||||
|
synchronized (audioTrackLock_) { |
||||||
|
if (audioTrack_ != null) { |
||||||
|
audioTrack_.stop(); |
||||||
|
audioTrack_.release(); |
||||||
|
audioTrack_ = null; |
||||||
|
buffer_ = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Fills the buffer once, and then sends it off to the AudioTrack. |
||||||
|
*/ |
||||||
|
private void fillBuffer() { |
||||||
|
for (int i = 0; i < buffer_.length; ++i) { |
||||||
|
// Change the output range from [-1, 1] to [-32767, 32767].
|
||||||
|
// 16-bit signed output is fairly standard, and hard-coded.
|
||||||
|
double output = synthesizer_.getValue(time_); |
||||||
|
// Clamp values out of range.
|
||||||
|
if (output < -1.0) { |
||||||
|
output = -1.0; |
||||||
|
} |
||||||
|
if (output > 1.0) { |
||||||
|
output = 1.0; |
||||||
|
} |
||||||
|
buffer_[i] = (short)(32767 * output); |
||||||
|
time_.advance(); |
||||||
|
} |
||||||
|
synchronized (audioTrackLock_) { |
||||||
|
if (audioTrack_ != null) { |
||||||
|
// This call will block until the buffer has been copied, but will return before the
|
||||||
|
// sampled audio data has actually been output.
|
||||||
|
audioTrack_.write(buffer_, 0, buffer_.length); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// The actual thread that constantly loops, taking the output from the synthesizer and sending it
|
||||||
|
// to Android's audio output buffer.
|
||||||
|
Thread speakerThread_; |
||||||
|
|
||||||
|
// Lock guarding playing_, shouldDie_, and speakerThread_.
|
||||||
|
private Object playingLock_; |
||||||
|
|
||||||
|
// True iff the thread is in the "playing" state.
|
||||||
|
private boolean playing_; |
||||||
|
|
||||||
|
// Variable used to signal to the speakerThread that playing should stop.
|
||||||
|
private boolean shouldDie_; |
||||||
|
|
||||||
|
// Lock guarding audioTrack_, buffer_, and time_.
|
||||||
|
private Object audioTrackLock_; |
||||||
|
|
||||||
|
// Android object for outputting audio.
|
||||||
|
private AudioTrack audioTrack_; |
||||||
|
|
||||||
|
// Buffer for collecting output from the synthesizer until it is ready to be output.
|
||||||
|
private short[] buffer_; |
||||||
|
|
||||||
|
// The sample rate of the synthesizer.
|
||||||
|
private int sampleRateInHz_; |
||||||
|
|
||||||
|
// Tracker for time since synthesis started.
|
||||||
|
private SynthesisTime time_; |
||||||
|
|
||||||
|
// Module to provide sampled audio data to be output.
|
||||||
|
private MultiChannelSynthesizer synthesizer_; |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.ChordGridView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.service.SynthesizerService; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) { |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import com.google.synthesizer.android.service.SynthesizerService; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
|
||||||
|
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) { |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
|
||||||
|
// 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_; |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.android.widgets.waveform.WaveformRowView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,141 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.Storage; |
||||||
|
import com.google.synthesizer.android.widgets.score.ScoreView; |
||||||
|
import com.google.synthesizer.android.widgets.score.ScoreViewToolbar; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.music.Music.Score.Builder; |
||||||
|
|
||||||
|
/** |
||||||
|
* An Activity for editing or playing a score. |
||||||
|
*/ |
||||||
|
public class ScoreActivity extends SynthesizerActivity { |
||||||
|
@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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onSynthesizerUpdate(MultiChannelSynthesizer synth) { |
||||||
|
scoreView_.bindTo(synth); |
||||||
|
} |
||||||
|
|
||||||
|
private ScoreView scoreView_; |
||||||
|
private ScoreViewToolbar scoreViewToolbar_; |
||||||
|
|
||||||
|
private Logger logger_; |
||||||
|
} |
@ -0,0 +1,189 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.service.SynthesizerService; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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.google.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; |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.android.widgets.waveform.WaveformRowView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.ui; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.knob.KnobView; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoView; |
||||||
|
import com.google.synthesizer.android.widgets.waveform.WaveformRowView; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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_; |
||||||
|
} |
@ -0,0 +1,328 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.android.widgets.piano.PianoViewListener; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.music.Note; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) { |
||||||
|
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; |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.knob; |
||||||
|
|
||||||
|
/** |
||||||
|
* KnobListener is an interface for getting events when a KnobView's value changes. |
||||||
|
*/ |
||||||
|
public interface KnobListener { |
||||||
|
/** |
||||||
|
* Called when the value changes. |
||||||
|
*/ |
||||||
|
void onKnobChanged(double newValue); |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.knob; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* KnobPlaceholderView is an empty widget that behaves the same way as a KnobView when sizing. |
||||||
|
* It's useful for layouts that vary how many knobs they have, but want them sized consistently. |
||||||
|
*/ |
||||||
|
public class KnobPlaceholderView extends View { |
||||||
|
public KnobPlaceholderView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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); |
||||||
|
|
||||||
|
// Prefer 100 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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,390 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.knob; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.res.TypedArray; |
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.Paint; |
||||||
|
import android.graphics.RadialGradient; |
||||||
|
import android.graphics.Rect; |
||||||
|
import android.graphics.RectF; |
||||||
|
import android.graphics.SweepGradient; |
||||||
|
import android.graphics.Typeface; |
||||||
|
import android.graphics.Paint.Align; |
||||||
|
import android.graphics.Paint.Style; |
||||||
|
import android.graphics.Shader.TileMode; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.core.model.SynthesizerInput; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.model.composite.Presets.Setting; |
||||||
|
|
||||||
|
/** |
||||||
|
* KnobView is a widget for setting a real value by turning a virtual "knob". |
||||||
|
*/ |
||||||
|
public class KnobView extends View { |
||||||
|
/** Basic constructor for an Android widget. */ |
||||||
|
public KnobView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
|
||||||
|
// Load the xml attributes.
|
||||||
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KnobView); |
||||||
|
knobValue_ = a.getFloat(R.styleable.KnobView_value, 0.5f); |
||||||
|
min_ = a.getFloat(R.styleable.KnobView_min, 0.0f); |
||||||
|
max_ = a.getFloat(R.styleable.KnobView_max, 1.0f); |
||||||
|
|
||||||
|
// Set up the drawing structures.
|
||||||
|
knobPaint_ = new Paint(); |
||||||
|
knobPaint_.setAntiAlias(true); |
||||||
|
knobPaint_.setColor(Color.WHITE); |
||||||
|
rect_ = new Rect(); |
||||||
|
rectF_ = new RectF(); |
||||||
|
textRect_ = 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_POINTER_DOWN: { |
||||||
|
// Just record the current finger position.
|
||||||
|
getDrawingRect(rect_); |
||||||
|
previousX_ = event.getX() - rect_.centerX(); |
||||||
|
previousY_ = event.getY() - rect_.centerY(); |
||||||
|
currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0); |
||||||
|
diffAngle_ = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE: { |
||||||
|
getDrawingRect(rect_); |
||||||
|
|
||||||
|
// Compare the previous angle of the finger position (relative to the center of the control)
|
||||||
|
// to the new angle, and update the value accordingly.
|
||||||
|
currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0); |
||||||
|
currentX_ = event.getX() - rect_.centerX(); |
||||||
|
currentY_ = event.getY() - rect_.centerY(); |
||||||
|
diffAngle_ = Math.atan2(currentY_, currentX_) - Math.atan2(previousY_, previousX_); |
||||||
|
if (diffAngle_ > Math.PI) { |
||||||
|
diffAngle_ -= Math.PI * 2; |
||||||
|
} else if (diffAngle_ < -Math.PI) { |
||||||
|
diffAngle_ += Math.PI * 2; |
||||||
|
} |
||||||
|
currentTouchAngle_ += diffAngle_; |
||||||
|
knobValue_ = currentTouchAngle_ / (2.0 * Math.PI); |
||||||
|
if (knobValue_ < 0.1) knobValue_ = 0.1; |
||||||
|
if (knobValue_ > 0.9) knobValue_ = 0.9; |
||||||
|
knobValue_ -= 0.1; |
||||||
|
knobValue_ /= 0.8; |
||||||
|
previousX_ = currentX_; |
||||||
|
previousY_ = currentY_; |
||||||
|
|
||||||
|
// Notify listener and redraw.
|
||||||
|
if (listener_ != null) { |
||||||
|
listener_.onKnobChanged(getValue()); |
||||||
|
} |
||||||
|
invalidate(); |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case MotionEvent.ACTION_POINTER_UP: { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the listener to receive events when the knob's value changes. |
||||||
|
*/ |
||||||
|
public void setKnobListener(KnobListener listener) { |
||||||
|
listener_ = listener; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the value for the knob when it is turned all the way counter-clockwise. |
||||||
|
*/ |
||||||
|
public void setMin(double min) { |
||||||
|
min_ = min; |
||||||
|
invalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the value for the knob when it is turned all the way clockwise. |
||||||
|
*/ |
||||||
|
public void setMax(double max) { |
||||||
|
max_ = max; |
||||||
|
invalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the current value of the knob. |
||||||
|
*/ |
||||||
|
public void setValue(double value) { |
||||||
|
if (value < min_) { |
||||||
|
knobValue_ = 0.0; |
||||||
|
} else if (value > max_) { |
||||||
|
knobValue_ = 1.0; |
||||||
|
} else { |
||||||
|
knobValue_ = (value - min_) / (max_ - min_); |
||||||
|
} |
||||||
|
if (listener_ != null) { |
||||||
|
listener_.onKnobChanged(value); |
||||||
|
} |
||||||
|
invalidate(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the current value of the knob. |
||||||
|
*/ |
||||||
|
public double getValue() { |
||||||
|
return min_ + (knobValue_ * (max_ - min_)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Drawing handler. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void onDraw(Canvas canvas) { |
||||||
|
super.onDraw(canvas); |
||||||
|
|
||||||
|
getDrawingRect(rect_); |
||||||
|
rectF_.set(rect_); |
||||||
|
// Make it square.
|
||||||
|
if (rectF_.height() > rectF_.width()) { |
||||||
|
float center = rectF_.centerY(); |
||||||
|
rectF_.top = center - rectF_.width() / 2; |
||||||
|
rectF_.bottom = center + rectF_.width() / 2; |
||||||
|
} else { |
||||||
|
float center = rectF_.centerX(); |
||||||
|
rectF_.left = center - rectF_.height() / 2; |
||||||
|
rectF_.right = center + rectF_.height() / 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Draw outer white glow.
|
||||||
|
int[] radialGradientColors = {Color.WHITE, Color.WHITE, 0x00000000}; |
||||||
|
float[] radialGradientPositions = {0.0f, 0.87f, 1.0f}; |
||||||
|
radialGradient_ = new RadialGradient(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(), rect_.height()) / 2, |
||||||
|
radialGradientColors, |
||||||
|
radialGradientPositions, |
||||||
|
TileMode.CLAMP); |
||||||
|
|
||||||
|
knobPaint_.setShader(radialGradient_); |
||||||
|
canvas.drawCircle(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(), rect_.height()) / 2, |
||||||
|
knobPaint_); |
||||||
|
|
||||||
|
// Draw outer gauge.
|
||||||
|
final int fullDark = Color.BLACK; |
||||||
|
final int guageStartColor = 0xff202050; |
||||||
|
final int guageEndColor = 0xff4040A0; |
||||||
|
|
||||||
|
final int adjustedStartColor = Color.argb( |
||||||
|
0xFF, |
||||||
|
(int)(0.1875 * Color.red(guageStartColor) + (1.0 - 0.1875) * Color.red(guageEndColor)), |
||||||
|
(int)(0.1875 * Color.green(guageStartColor) + (1.0 - 0.1875) * Color.green(guageEndColor)), |
||||||
|
(int)(0.1875 * Color.blue(guageStartColor) + (1.0 - 0.1875) * Color.blue(guageEndColor))); |
||||||
|
|
||||||
|
int[] sweepGradientColors = { |
||||||
|
adjustedStartColor, |
||||||
|
guageEndColor, |
||||||
|
fullDark, |
||||||
|
fullDark, |
||||||
|
guageStartColor, |
||||||
|
adjustedStartColor}; |
||||||
|
float[] sweepGradientPositions = { 0.0f, 0.16f, 0.16f, 0.35f, 0.35f, 1.0f }; |
||||||
|
sweepGradient_ = new SweepGradient(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
sweepGradientColors, |
||||||
|
sweepGradientPositions); |
||||||
|
knobPaint_.setShader(sweepGradient_); |
||||||
|
canvas.drawCircle(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(), rect_.height()) / 2.4f, |
||||||
|
knobPaint_); |
||||||
|
|
||||||
|
// Draw inner gauge.
|
||||||
|
knobPaint_.setShader(null); |
||||||
|
knobPaint_.setStyle(Style.FILL); |
||||||
|
knobPaint_.setColor(Color.BLACK); |
||||||
|
canvas.drawCircle(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(), rect_.height()) / 4, |
||||||
|
knobPaint_); |
||||||
|
|
||||||
|
// Draw inner white glow.
|
||||||
|
int[] innerRadialGradientColors = { 0x00000000, 0x00000000, Color.WHITE }; |
||||||
|
float[] innerRadialGradientPositions = { 0.0f, 0.6f, 1.0f }; |
||||||
|
innerRadialGradient_ = new RadialGradient(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(), rect_.height()) / 4f, |
||||||
|
innerRadialGradientColors, |
||||||
|
innerRadialGradientPositions, |
||||||
|
TileMode.CLAMP); |
||||||
|
knobPaint_.setShader(innerRadialGradient_); |
||||||
|
canvas.drawCircle(rect_.exactCenterX(), |
||||||
|
rect_.exactCenterY(), |
||||||
|
Math.min(rect_.width(),rect_.height()) / 4, |
||||||
|
knobPaint_); |
||||||
|
|
||||||
|
// Draw indicator.
|
||||||
|
knobPaint_.setShader(null); |
||||||
|
knobPaint_.setColor(Color.WHITE); |
||||||
|
knobPaint_.setStyle(Style.STROKE); |
||||||
|
knobPaint_.setStrokeWidth(4.0f); |
||||||
|
final float arcWidth = 15.0f; |
||||||
|
canvas.drawArc(rectF_, |
||||||
|
(float)(knobValue_ * 360 * 0.8 + 90 - arcWidth / 2 + 36), |
||||||
|
(float)arcWidth, |
||||||
|
false, |
||||||
|
knobPaint_); |
||||||
|
|
||||||
|
// Draw text.
|
||||||
|
String knobValueString = String.format("%.2f", getValue()); |
||||||
|
Typeface typeface = Typeface.create(knobPaint_.getTypeface(), Typeface.BOLD); |
||||||
|
knobPaint_.setTypeface(typeface); |
||||||
|
knobPaint_.setTextAlign(Align.CENTER); |
||||||
|
knobPaint_.setTextSize(rectF_.width() / 8); |
||||||
|
knobPaint_.setSubpixelText(true); |
||||||
|
knobPaint_.setStyle(Style.FILL); |
||||||
|
knobPaint_.getTextBounds(knobValueString, 0, knobValueString.length(), textRect_); |
||||||
|
canvas.drawText(knobValueString, |
||||||
|
rect_.centerX(), |
||||||
|
rect_.centerY() + textRect_.height() / 2, |
||||||
|
knobPaint_); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 knob to a SynthesizerInput. |
||||||
|
* @input - The synthesizer input to connect to. |
||||||
|
*/ |
||||||
|
public void bindTo(final SynthesizerInput input) { |
||||||
|
setValue(input.getSynthesizerInputValue()); |
||||||
|
setKnobListener(new KnobListener() { |
||||||
|
public void onKnobChanged(double newValue) { |
||||||
|
input.setValue(newValue); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Connects knob to a SynthesizerInput. |
||||||
|
* @synth - The synthesizer to connect to. |
||||||
|
* @path - The setting to connect to. |
||||||
|
* @return - True on success, false on failure. |
||||||
|
*/ |
||||||
|
public boolean bindTo(final MultiChannelSynthesizer synth, int channel, Setting setting) { |
||||||
|
SynthesizerInput input = synth.getChannel(channel).getSynthesizerInput(setting); |
||||||
|
if (input != null) { |
||||||
|
bindTo(input); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
Log.e(getClass().getName(), "Unable to bind to setting " + setting.name() + "."); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Knob's current value, ranges from 0 - 1.0.
|
||||||
|
private double knobValue_; |
||||||
|
private double min_; |
||||||
|
private double max_; |
||||||
|
|
||||||
|
// Structures used in drawing that we don't want to reallocate every time we draw.
|
||||||
|
private Paint knobPaint_; |
||||||
|
private Rect rect_; |
||||||
|
private Rect textRect_; |
||||||
|
private RectF rectF_; |
||||||
|
private SweepGradient sweepGradient_; |
||||||
|
private RadialGradient radialGradient_; |
||||||
|
private RadialGradient innerRadialGradient_; |
||||||
|
|
||||||
|
// Position of the finger relative to the knob.
|
||||||
|
private double previousX_ = 0; |
||||||
|
private double previousY_ = 0; |
||||||
|
private double currentX_ = 0; |
||||||
|
private double currentY_ = 0; |
||||||
|
private double currentTouchAngle_ = 0; |
||||||
|
private double diffAngle_ = 0; |
||||||
|
|
||||||
|
// Object listening for events when the knob's value changes.
|
||||||
|
private KnobListener listener_; |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.piano; |
||||||
|
|
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.Rect; |
||||||
|
|
||||||
|
import com.google.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; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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_; |
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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_; |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.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); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 }; |
||||||
|
} |
@ -0,0 +1,373 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.piano; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.res.TypedArray; |
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.Rect; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.View; |
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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); |
||||||
|
|
||||||
|
// 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) { |
||||||
|
if (pianoViewListener_ != null) { |
||||||
|
pianoViewListener_.noteDown(logFrequency, finger, retriggerIfOn); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) { |
||||||
|
// 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 (keyDown instanceof NotePianoKey) { |
||||||
|
notifyNoteDown(((NotePianoKey)keyDown).getLogFrequency(), finger, true); |
||||||
|
} |
||||||
|
return redraw; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called to handle touch move events. |
||||||
|
*/ |
||||||
|
protected boolean onTouchMove(int finger, int x, int y) { |
||||||
|
// 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 (!wasPressed) { |
||||||
|
notifyNoteDown(((NotePianoKey)keyDown).getLogFrequency(), finger, false); |
||||||
|
} |
||||||
|
} 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; |
||||||
|
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(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(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(pointerId, x, y); |
||||||
|
} |
||||||
|
} |
||||||
|
} 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) { |
||||||
|
redraw |= onTouchUp(pointerId); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (actionCode == MotionEvent.ACTION_POINTER_UP) { |
||||||
|
int pointerId = action >> MotionEvent.ACTION_POINTER_ID_SHIFT; |
||||||
|
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) { |
||||||
|
redraw |= onTouchUp(pointerId); |
||||||
|
} |
||||||
|
} |
||||||
|
} 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) { |
||||||
|
synth.getChannel(channel).setPitch(logFrequency, finger); |
||||||
|
synth.getChannel(channel).turnOn(retriggerIfOn, finger); |
||||||
|
} |
||||||
|
public void noteUp(int finger) { |
||||||
|
synth.getChannel(channel).turnOff(finger); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// 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 = 5; |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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); |
||||||
|
|
||||||
|
/** |
||||||
|
* The note was released. |
||||||
|
*/ |
||||||
|
void noteUp(int finger); |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.piano; |
||||||
|
|
||||||
|
import android.graphics.Canvas; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.Rect; |
||||||
|
|
||||||
|
import com.google.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_); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,490 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,156 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.core.music.ScorePlayer; |
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,229 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.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) { |
||||||
|
double note = view.getNoteAt(physicalY); |
||||||
|
double logFrequency = Note.computeLog12TET(((int)note) % 12, ((int)note) / 12); |
||||||
|
view.getSynthesizer().getChannel(view.getCurrentChannel()).setPitch(logFrequency, finger); |
||||||
|
view.getSynthesizer().getChannel(view.getCurrentChannel()).turnOn(true, finger); |
||||||
|
keysDown_[finger] = (int)note; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called to handle touch move events. |
||||||
|
*/ |
||||||
|
private boolean onTouchMove(ScoreView view, int finger, int physicalX, int physicalY) { |
||||||
|
double note = view.getNoteAt(physicalY); |
||||||
|
double logFrequency = Note.computeLog12TET(((int)note) % 12, ((int)note) / 12); |
||||||
|
view.getSynthesizer().getChannel(view.getCurrentChannel()).setPitch(logFrequency, finger); |
||||||
|
keysDown_[finger] = (int)note; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called to handle touch up events. |
||||||
|
*/ |
||||||
|
protected boolean onTouchUp(ScoreView view, int finger) { |
||||||
|
view.getSynthesizer().getChannel(view.getCurrentChannel()).turnOff(finger); |
||||||
|
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_; |
||||||
|
} |
@ -0,0 +1,767 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.synthesizer.R; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.synthesizer.core.music.Music.Event; |
||||||
|
import com.google.synthesizer.core.music.Music.Score; |
||||||
|
import com.google.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.util.AttributeSet; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 MultiChannelSynthesizer synth) { |
||||||
|
synthesizer_ = synth; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the synthesizer connected to this ScoreView. |
||||||
|
* @return the connected synthesizer. |
||||||
|
*/ |
||||||
|
public MultiChannelSynthesizer 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 MultiChannelSynthesizer 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_; |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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); |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import com.google.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) {} |
||||||
|
} |
@ -0,0 +1,222 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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_; |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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_; |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,259 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.score; |
||||||
|
|
||||||
|
import java.util.logging.Logger; |
||||||
|
|
||||||
|
import com.google.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_; |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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); |
||||||
|
} |
@ -0,0 +1,167 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.android.widgets.waveform; |
||||||
|
|
||||||
|
import com.google.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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,470 @@ |
|||||||
|
/* |
||||||
|
* 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.google.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.google.synthesizer.core.model.WaveformInput; |
||||||
|
import com.google.synthesizer.core.model.composite.MultiChannelSynthesizer; |
||||||
|
import com.google.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,62 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
|
||||||
|
<!-- |
||||||
|
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. |
||||||
|
--> |
||||||
|
|
||||||
|
<project name="music-synthesizer" default="build"> |
||||||
|
|
||||||
|
<property file="build.properties"/> |
||||||
|
|
||||||
|
<target name="build"> |
||||||
|
<ant dir="core" target="build"/> |
||||||
|
<ant dir="test" target="build"/> |
||||||
|
<ant dir="j2se" target="build"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="clean"> |
||||||
|
<ant dir="core" target="clean"/> |
||||||
|
<ant dir="test" target="clean"/> |
||||||
|
<ant dir="j2se" target="clean"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="test" depends="build,clean"> |
||||||
|
<ant dir="test" target="test"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="javadoc"> |
||||||
|
<mkdir dir="docs/javadoc"/> |
||||||
|
<javadoc destdir="docs/javadoc" useexternalfile="true"> |
||||||
|
<sourcepath> |
||||||
|
<pathelement location="android/src"/> |
||||||
|
<pathelement location="core/src"/> |
||||||
|
<pathelement location="test/src"/> |
||||||
|
<pathelement location="j2se/src"/> |
||||||
|
</sourcepath> |
||||||
|
<classpath> |
||||||
|
<!-- TODO(klimt): Add android. --> |
||||||
|
</classpath> |
||||||
|
</javadoc> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="release" depends="build,clean,javadoc"> |
||||||
|
<zip file="music-synthesizer-${version}.zip"> |
||||||
|
<zipfileset dir="." prefix="music-synthesizer-${version}"> |
||||||
|
<exclude name="**/.*"/> |
||||||
|
</zipfileset> |
||||||
|
</zip> |
||||||
|
</target> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,73 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
|
||||||
|
<!-- |
||||||
|
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. |
||||||
|
--> |
||||||
|
|
||||||
|
<project name="core" default="build"> |
||||||
|
|
||||||
|
<property file="../build.properties"/> |
||||||
|
|
||||||
|
<target name="compile"> |
||||||
|
<mkdir dir="gen"/> |
||||||
|
<apply executable="bin/protoc" relative="true" failonerror="true"> |
||||||
|
<arg value="--java_out=gen/"/> |
||||||
|
<fileset dir="." includes="**/*.proto"/> |
||||||
|
</apply> |
||||||
|
<mkdir dir="build"/> |
||||||
|
<javac srcdir="gen" |
||||||
|
destdir="build" |
||||||
|
source="1.5" |
||||||
|
target="1.5" |
||||||
|
optimize="true" |
||||||
|
deprecation="true" |
||||||
|
debug="true" |
||||||
|
includeantruntime="false"> |
||||||
|
<classpath> |
||||||
|
<pathelement path="lib/libprotobuf.jar"/> |
||||||
|
</classpath> |
||||||
|
</javac> |
||||||
|
<javac srcdir="src" |
||||||
|
destdir="build" |
||||||
|
source="1.5" |
||||||
|
target="1.5" |
||||||
|
optimize="true" |
||||||
|
deprecation="true" |
||||||
|
debug="true" |
||||||
|
includeantruntime="false"> |
||||||
|
<classpath> |
||||||
|
<pathelement path="build"/> |
||||||
|
<pathelement path="lib/libprotobuf.jar"/> |
||||||
|
</classpath> |
||||||
|
</javac> |
||||||
|
<jar jarfile="core.jar" basedir="build"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="clean"> |
||||||
|
<delete dir="gen"/> |
||||||
|
<mkdir dir="gen"/> |
||||||
|
<delete dir="build"/> |
||||||
|
<delete dir="core.jar"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="build" depends="clean"> |
||||||
|
<antcall target="compile"> |
||||||
|
<param name="generate-debug" value="true"/> |
||||||
|
</antcall> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="test"/> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,284 @@ |
|||||||
|
/* |
||||||
|
* 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.google.synthesizer.core.midi; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* MessageInputProcessor takes Midi messages from an input stream and dispatches them to a |
||||||
|
* MidiListener. |
||||||
|
* @see MidiListener |
||||||
|
*/ |
||||||
|
public class MessageInputProcessor { |
||||||
|
/** |
||||||
|
* Creates a new MessageInputProcessor. |
||||||
|
*/ |
||||||
|
public MessageInputProcessor() { |
||||||
|
previousCode_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads one Midi message from input and dispatches any events to a listener. |
||||||
|
*/ |
||||||
|
public void process(InputStream input, MidiListener listener) throws IOException { |
||||||
|
previousCode_ = process(input, previousCode_, listener); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads one Midi message from input and dispatches any events to a listener. |
||||||
|
* @param input - The stream to read from. |
||||||
|
* @param previousCode - The previous message code in the stream, for "running status" encoding. |
||||||
|
* @param listener - The object to handle the event. |
||||||
|
*/ |
||||||
|
public static int process(InputStream input, |
||||||
|
int previousCode, |
||||||
|
MidiListener listener) throws IOException { |
||||||
|
if (!input.markSupported()) { |
||||||
|
throw new IOException("process() requires an InputStream that supports mark()."); |
||||||
|
} |
||||||
|
|
||||||
|
input.mark(1); |
||||||
|
int code = MidiUtil.readByte(input); |
||||||
|
if ((code & 0x80) == 0 && previousCode != 0) { |
||||||
|
code = previousCode; |
||||||
|
input.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
if (code == 0xFF) { |
||||||
|
processMetaMessage(input, listener); |
||||||
|
} else if (code == 0xFE) { |
||||||
|
listener.onActiveSensing(); |
||||||
|
} else if (code == 0xF8) { |
||||||
|
listener.onTimingClock(); |
||||||
|
} else if (code == 0xF0 || code == 0xF7) { |
||||||
|
processSysExMessage(input, listener); |
||||||
|
} else if ((code & 0x80) == 0x80 && (code & 0xF0) != 0xF0) { |
||||||
|
processControlMessage(input, code, listener); |
||||||
|
} else { |
||||||
|
throw new IOException("Invalid midi event code " + code + "."); |
||||||
|
} |
||||||
|
return code; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Processes control messages. |
||||||
|
* @param input - The stream to read from. |
||||||
|
* @param code - The code of the event, which has the type and the channel. |
||||||
|
* @param listener - The object to handle the event. |
||||||
|
*/ |
||||||
|
private static void processControlMessage(InputStream input, |
||||||
|
int code, |
||||||
|
MidiListener listener) throws IOException { |
||||||
|
int type = (code & 0xF0); |
||||||
|
int channel = (code & 0x0F); |
||||||
|
switch (type) { |
||||||
|
case 0x80: { |
||||||
|
int note = MidiUtil.readByte(input); |
||||||
|
int velocity = MidiUtil.readByte(input); |
||||||
|
listener.onNoteOff(channel, note, velocity); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x90: { |
||||||
|
int note = MidiUtil.readByte(input); |
||||||
|
int velocity = MidiUtil.readByte(input); |
||||||
|
listener.onNoteOn(channel, note, velocity); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0xA0: { |
||||||
|
int note = MidiUtil.readByte(input); |
||||||
|
int aftertouch = MidiUtil.readByte(input); |
||||||
|
listener.onNoteAftertouch(channel, note, aftertouch); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0xB0: { |
||||||
|
int control = MidiUtil.readByte(input); |
||||||
|
int value = MidiUtil.readByte(input); |
||||||
|
listener.onController(channel, control, value); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0xC0: { |
||||||
|
int program = MidiUtil.readByte(input); |
||||||
|
listener.onProgramChange(channel, program); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0xD0: { |
||||||
|
int aftertouch = MidiUtil.readByte(input); |
||||||
|
listener.onChannelAftertouch(channel, aftertouch); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0xE0: { |
||||||
|
int value = MidiUtil.readWord(input); |
||||||
|
listener.onPitchBend(channel, value); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
throw new IOException("Invalid midi control message type " + type + "."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Processes meta messages. |
||||||
|
* @param input - The stream to read from. |
||||||
|
* @param listener - The object to handle the event. |
||||||
|
*/ |
||||||
|
private static void processMetaMessage(InputStream input, |
||||||
|
MidiListener listener) throws IOException { |
||||||
|
int type = MidiUtil.readByte(input); |
||||||
|
int size = MidiUtil.readVarInt(input); |
||||||
|
switch (type) { |
||||||
|
case 0x00: { |
||||||
|
if (size != 2) { |
||||||
|
throw new IOException("Invalid length for sequence meta event " + size + "."); |
||||||
|
} |
||||||
|
listener.onSequenceNumber(MidiUtil.readWord(input)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x01: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onText(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x02: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onCopyrightNotice(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x03: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onSequenceName(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x04: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onInstrumentName(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x05: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onLyrics(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x06: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onMarker(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x07: { |
||||||
|
byte[] text = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, text); |
||||||
|
listener.onCuePoint(text); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x20: { |
||||||
|
if (size != 1) { |
||||||
|
throw new IOException("Invalid length for midi channel prefix " + size + "."); |
||||||
|
} |
||||||
|
listener.onChannelPrefix(MidiUtil.readByte(input)); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x21: { |
||||||
|
byte[] data = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, data); |
||||||
|
listener.onPort(data); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x2F: { |
||||||
|
if (size != 0) { |
||||||
|
throw new IOException("Invalid length for end of track " + size + "."); |
||||||
|
} |
||||||
|
listener.onEndOfTrack(); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x51: { |
||||||
|
if (size != 3) { |
||||||
|
throw new IOException("Invalid length for set tempo event " + size + "."); |
||||||
|
} |
||||||
|
// Stupid 3-byte value.
|
||||||
|
int w = MidiUtil.readWord(input); |
||||||
|
int b = MidiUtil.readByte(input); |
||||||
|
int mspqn = (w << 8) | b; |
||||||
|
listener.onSetTempo(mspqn); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x54: { |
||||||
|
if (size != 5) { |
||||||
|
throw new IOException("Invalid length for smpte offset event " + size + "."); |
||||||
|
} |
||||||
|
byte[] data = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, data); |
||||||
|
listener.onSmpteOffset(data); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x58: { |
||||||
|
if (size != 4) { |
||||||
|
throw new IOException("Invalid length for time signature event " + size + "."); |
||||||
|
} |
||||||
|
int numerator = MidiUtil.readByte(input); |
||||||
|
int denominator = MidiUtil.readByte(input); |
||||||
|
int metronomePulse = MidiUtil.readByte(input); |
||||||
|
int thirtySecondNotesPerQuarterNote = MidiUtil.readByte(input); |
||||||
|
listener.onTimeSignature(numerator, |
||||||
|
denominator, |
||||||
|
metronomePulse, |
||||||
|
thirtySecondNotesPerQuarterNote); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x59: { |
||||||
|
if (size != 2) { |
||||||
|
throw new IOException("Invalid length for key signature event " + size + "."); |
||||||
|
} |
||||||
|
int key = MidiUtil.readByte(input); |
||||||
|
boolean isMinor = (MidiUtil.readByte(input) != 0); |
||||||
|
listener.onKeySignature(key, isMinor); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 0x7F: { |
||||||
|
byte[] data = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, data); |
||||||
|
listener.onSequencerSpecificEvent(data); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: { |
||||||
|
throw new IOException("Invalid midi meta message type " + type + "."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Processes SysEx messages. |
||||||
|
* @param input - The stream to read from. |
||||||
|
* @param listener - The object to handle the event. |
||||||
|
*/ |
||||||
|
private static void processSysExMessage(InputStream input, |
||||||
|
MidiListener listener) throws IOException { |
||||||
|
int size = MidiUtil.readVarInt(input); |
||||||
|
byte[] data = new byte[size]; |
||||||
|
MidiUtil.readBytes(input, size, data); |
||||||
|
listener.onSysEx(data); |
||||||
|
} |
||||||
|
|
||||||
|
// The most recent code seen in the stream, used for "running status" encoding.
|
||||||
|
int previousCode_; |
||||||
|
} |