当前位置: 首页 > news >正文

【安卓开发】【Android】做一个简单的钢琴模拟器

说明

仅作为学习用途。

一、效果展示

从豪华别墅进入钢琴房,点击钢琴,呼出键盘(约1个八度),点击相应的键位,即可弹奏。效果如下:

piano

图片效果:

在这里插入图片描述
在这里插入图片描述

二、活动代码

// 颐居·沙发房
package com.lycbuaacs.my_hg;import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Typeface;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;public class MainActivity23 extends AppCompatActivity {public static class ToneGenerator {private static final int SAMPLE_RATE = 44100; // 采样率public void playTone(double frequencyHz, int durationMs) {// 1. 计算所需样本数int numSamples = (int)(durationMs * SAMPLE_RATE / 1000.0);short[] buffer = new short[numSamples];double angularFrequency = 2 * Math.PI * frequencyHz / SAMPLE_RATE;// 2. 生成正弦波样本for (int i = 0; i < numSamples; i++) {double sample = Math.sin(i * angularFrequency);buffer[i] = (short)(sample * Short.MAX_VALUE); // 16位PCM}// 3. 配置AudioTrackAudioTrack audioTrack = new AudioTrack(new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build(),new AudioFormat.Builder().setSampleRate(SAMPLE_RATE).setEncoding(AudioFormat.ENCODING_PCM_16BIT).setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build(),buffer.length * 2, // 缓冲区大小(字节)AudioTrack.MODE_STATIC,AudioManager.AUDIO_SESSION_ID_GENERATE);// 4. 播放音频audioTrack.write(buffer, 0, buffer.length);audioTrack.play();// 5. 释放资源(实际使用需异步处理)new Thread(() -> {try {Thread.sleep(durationMs);} catch (InterruptedException e) {e.printStackTrace();}audioTrack.stop();audioTrack.release();}).start();}}int play = 0;@SuppressLint("ClickableViewAccessibility")@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main23);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});ToneGenerator ge = new ToneGenerator();ImageView image = findViewById(R.id.ins_1);// 羽毛图片(拖拽效果)
//        ImageView feather = findViewById(R.id.fea);ImageView clear = findViewById(R.id.clear);SingleTouchView pic1 = findViewById(R.id.pic1);ImageView pic2 = findViewById(R.id.pic2);TextView text = findViewById(R.id.pal_1);Typeface type = Typeface.createFromAsset(getAssets(), "kaishu.TTF");text.setTypeface(type);EditText ed_ = findViewById(R.id.reply);
//        image_2.setImageResource(R.drawable.b_s_5);
//        image_2.setVisibility(VISIBLE);clear.setOnClickListener(v -> {pic1.setVisibility(INVISIBLE);pic2.setVisibility(INVISIBLE);text.setVisibility(INVISIBLE);});image.setOnTouchListener((v1, event) -> {String x = new String(String.valueOf(event.getX()));String y = new String(String.valueOf(event.getY()));Toast.makeText(getApplicationContext(), x, Toast.LENGTH_SHORT).show();Toast.makeText(getApplicationContext(), y, Toast.LENGTH_SHORT).show();//        1、测试:(471,594) +- 25if ((event.getX() > 446) && (event.getX() < 496) && (event.getY() > 569) && (event.getY() < 619)) {text.setText(R.string.line_132);text.setVisibility(VISIBLE);pic1.setImageDrawable(R.drawable.mi_1);pic1.setVisibility(VISIBLE);}
//        2、钢琴:(809,764) +- 25if ((event.getX() > 784) && (event.getX() < 834) && (event.getY() > 739) && (event.getY() < 789)) {text.setText(R.string.line_167);text.setVisibility(VISIBLE);pic2.setImageResource(R.drawable.piano);pic2.setVisibility(VISIBLE);play = 2;}return false;});pic2.setOnTouchListener((v1, event) -> {String x = new String(String.valueOf(event.getX()));String y = new String(String.valueOf(event.getY()));Toast.makeText(getApplicationContext(), x, Toast.LENGTH_SHORT).show();Toast.makeText(getApplicationContext(), y, Toast.LENGTH_SHORT).show();//        1、do:(838,293) +- x +- 15 y +- 30if ((event.getX() > 823) && (event.getX() < 853) && (event.getY() > 278) && (event.getY() < 338)) {ge.playTone(261.63, 800);  // do}
//        2、#do:(879,112) +- x +- 15 y +- 30if ((event.getX() > 864) && (event.getX() < 894) && (event.getY() > 82) && (event.getY() < 142)) {ge.playTone(277.20, 800);  // #do}
//        3、re:(917,293) +- x +- 15 y +- 30if ((event.getX() > 902) && (event.getX() < 932) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(293.69, 800);  // re}
//        4、#re:(963,112) +- x +- 15 y +- 30if ((event.getX() > 948) && (event.getX() < 978) && (event.getY() > 82) && (event.getY() < 142)) {ge.playTone(311.16, 800);  // #re}
//        5、mi:(984,293) +- x +- 15 y +- 30if ((event.getX() > 969) && (event.getX() < 999) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(329.68, 800);  // re}
//        6、fa:(1056,293) +- x +- 15 y +- 30if ((event.getX() > 1041) && (event.getX() < 1071) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(349.29, 800);  // re}
//        7、#fa:(1094,112) +- x +- 15 y +- 30if ((event.getX() > 1079) && (event.getX() < 1109) && (event.getY() > 82) && (event.getY() < 142)) {ge.playTone(370.08, 800);  // #re}
//        8、sol:(1124,293) +- x +- 15 y +- 30if ((event.getX() > 1109) && (event.getX() < 1139) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(392.10, 800);  // re}
//        9、#sol:(1161,112) +- x +- 15 y +- 30if ((event.getX() > 1146) && (event.getX() < 1176) && (event.getY() > 82) && (event.getY() < 142)) {ge.playTone(415.43, 800);  // #re}
//        10、la:(1200,293) +- x +- 15 y +- 30if ((event.getX() > 1185) && (event.getX() < 1215) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(440.145, 800);  // re}
//        11、bsi:(1247,112) +- x +- 15 y +- 30if ((event.getX() > 1232) && (event.getX() < 1262) && (event.getY() > 82) && (event.getY() < 142)) {ge.playTone(466.33, 800);  // #re}
//        12、si:(1266,293) +- x +- 15 y +- 30if ((event.getX() > 1251) && (event.getX() < 1281) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(494.08, 800);  // re}
//        13、do:(1343,293) +- x +- 15 y +- 30if ((event.getX() > 1328) && (event.getX() < 1358) && (event.getY() > 263) && (event.getY() < 323)) {ge.playTone(523.26, 800);  // re}return false;});}
}

二、音阶原理(高中数学内容)

标准的C大调,中央C(do-4)的频率为261.63赫兹(更标准的参考为la-4,频率为440赫兹)。从中央C上行一个八度,到达do-5,频率翻倍,即为523.26赫兹。从do-4上行至do-5,中间共12个半音(do, #do, re, #re, mi, fa, #fa, sol, #sol, la, bsi, si),呈等比数列递增,公比为2开12次根号。如何求得这个公比值呢?我们利用Python,类似牛顿·拉夫森法,简便地得到近似值。代码如下:

import mathstart = 1
while math.pow(start, 12) <= 1.999999:start += 0.0001print(start)
print(math.pow(1.0594999999999934, 12))
print(math.pow(1.0595, 12))

好了,就用1.0595作为2开12次根号的近似值。然后,就可以依次计算出每一个半音的音高(赫兹值),例如#do为277.20,re为293.69,以此类推。就有了上述活动代码当中,每个音符对应的频率值。据此,调用ToneGenerator的playTone()函数,即可播放对应频率的音高。

http://www.dtcms.com/a/490523.html

相关文章:

  • C#控制反转
  • 【Java 开发日记】什么是线程池?它的工作原理?
  • 黄页网站数据来源wordpress 最新漏洞
  • 如何评价3D高斯泼溅(3DGS)技术为数字孪生与实时渲染带来的突破性进展?
  • 技术解析:如何将NX(UG)模型高效转换为3DXML格式
  • 阿里云智能建站网络类黄页
  • SAP MIR7 模拟过账没有这个按钮
  • Redis 分布式锁实战:解决马拉松报名并发冲突与 Lua 原子性优化
  • 光刻胶化学基础:聚合物分子量分布对分辨率的影响与控制,以及国内技术突破
  • Lua C API 中的注册表介绍
  • 广州做网站哪家公司最好wordpress html调用php
  • 神经网络之计算图
  • Hatch 故障排除指南
  • 神经网络之计算图分支节点
  • 【表格对比分析】Java集合体系、Java并发编程、JVM核心知识、Golang go-zero微服务框架
  • 【任务管理软件】实用工具之ToDoList 9.0.6 详细图文安装教程:高效任务管理的完美起点
  • Linux中zonelist分配策略初始化
  • hadoop的三副本数据冗余策略
  • 岳阳网站建设企业足球比赛直播app下载
  • React 三元运算符页面切换:完整进出流程
  • NumPy zeros_like() 函数详解
  • 网站建设要后台吗公司网页制作哪家好
  • 天津网站建设优化网页设计图片代码
  • CXR SDK实战指南:跨设备AR应用开发
  • 已知明文攻击(Known plaintext):原理、方法与防御体系深度剖析
  • ​SPI四种工作模式
  • 深度学习------YOLOV1和YOLOV2
  • 最小二乘问题详解5:非线性最小二乘求解实例
  • 算法入门数学基础
  • 错误边界:用componentDidCatch筑起React崩溃防火墙