迎接2023,用JAVA演奏“新年”

news/2024/11/17 4:52:10/

😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔2023年快要到来啦,再此祝大家诸事顺遂,所见所盼皆如愿。
🔔本文讲解如何使用Java演奏一首歌曲,一起卷起来叭!
在这里插入图片描述

众所周知,语言=算法+数据结构,那么音乐=曲谱+音质,曲谱定义了音的高低、长短,力度,我们常常又根据这些对歌曲常分为主奏和伴奏。因为一切皆文件嘛,凭借这些规则,我们通过编程将曲谱读取得到对应的音符的高低、长短、力度,然后依次调用音质文件,最后组成音乐。

先上视频演示,代码在文末:

演奏视频


目录

    • 1、音质
    • 2、编写主奏、伴奏曲谱
    • 3、编写项目

1、音质

主奏与伴奏中支持输入的35个音符:
“1–” ~ “7–”, “1-” ~ “7-”, “1” ~ “7”, “1+” ~ “7+”, “1++” ~ “7++” 分别代表倍低音、低音、中音、高音、倍高音一共35个音符

这里我们提前找到对应的音(音色可以自己找,上网一搜八十八个高音音频即可):

在这里插入图片描述

2、编写主奏、伴奏曲谱

2、分别在主奏(.note)与伴奏(.accompaniments)中输入需要自动弹奏的音符,定义规则如下:

  • 每个音符之间用空格隔开(任意多个空格,推荐每个音符包括空格共占用4个占位符,以便主奏和伴奏音符对齐)

  • 输入字符"0",则会使音长额外延长一倍;

  • 输入除了上面35个音符以及“0”以外的任意字符不会对弹奏起任何作用;

  • 如果需要换行填写,则需在上一行的末尾以及下一行的开头都加上空格;

  • 音长里输入每两个音符之间的间隔时长,单位是毫秒(ms)

这里我找了一份蜜雪冰城的谱子:
在这里插入图片描述
然后将它转化为咱们的规则:
主旋律:

 0     0 3+ 4+ 5+ 0 5+ 0 5+   0 0   6+ 5+ 0 3+ 0 1+   0 1+ 2+ 3+ 0 3+ 0 2+   0 2+ 0 1+ 0 0   0 1++ 0 0   0 3+ 0 5+ 0 5+ 0 0 6+ 5+ 0 3+ 0 1+ 0 1+ 2+ 3+ 0 3+ 0 2+ 0 1+ 0 2+ 0 0 0 0 0 0 0 3+ 0 5+ 0 5+ 0 0 6+ 5+ 0 3+ 0 1+ 0 1+ 2+ 3+ 0 3+ 0 2+ 0 2+ 0 1+ 0 0 0 0 0 0 0 4+ 0 0 0 4+ 0 0 0 4+ 0 6+ 0 0 0 0 0 5+ 0 0 0 5+ 0 3+ 0 2+ 0 0 0 0 0 0 0 3+ 0 5+ 0 5+ 0 0 6+ 5+ 0 3+ 0 1+ 0 1+ 2+ 3+ 0 3+ 0 2+ 0 2+ 0 1+ 0 0 0 0 0 0 0 

伴奏:

 0   0 0  0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 5-- 0 2- 0 1- 0 3- 0 3-  0 0 0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 1-  0 3- 0 5-- 0 2- 0 5-- 0 2- 0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 1-  0 3- 0 1- 0 3- 0 5-- 0 2- 0 1- 0 3- 0 1- 0 3- 0 4-- 0 1- 0 4-- 0 1- 0 4-- 0 1- 0 4-- 0 1- 0 1- 0 3- 0 1- 0 3- 0 5-- 0 2- 0 5-- 0 2- 0 1- 0 3- 0 1- 0 3- 0 1- 0 3- 0 1- 0 3- 0 1- 0 3- 0 5-- 0 2- 0 1- 0 3- 0 1++ 0 0 0 

3、编写项目

我们新建Maven项目并将上述文件分别放到resoucres目录下:
在这里插入图片描述
导入需要的依赖:

		<dependency><groupId>javazoom</groupId><artifactId>jlayer</artifactId><version>1.0.1</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-core</artifactId><version>5.6.5</version></dependency>

编写启动类,类名叫Happpy叭:

public class Happy {public static void main(String[] args) {String musicName = "蜜雪冰城";FileUtils.play(musicName);}
}

编写FileUtils(实现读取曲谱的功能)

public class FileUtils {static void play(String musicName) {System.out.println("载入歌曲:" + musicName);String path =new File("").getAbsolutePath() + File.separator + "src/main/resources/notes" + File.separator;String notesPath = path + musicName +  ".notes";String accompanimentsPath = path + musicName +  ".accompaniments";String notes = fileToStr(notesPath);String accompaniments = fileToStr(accompanimentsPath);new AudioPlay(180).loadNotes(notes).start();new AudioPlay(180).loadNotes(accompaniments).start();new Animation(180).loadNotes(notes).start();}static String fileToStr(String musicPath) {StringBuilder stringBuilder = new StringBuilder();try {BufferedReader reader = new BufferedReader(new FileReader(musicPath));String line = null;String ls = System.getProperty("line.separator");while ((line = reader.readLine()) != null) {stringBuilder.append(line);stringBuilder.append(ls);}// 删除最后一个新行分隔符stringBuilder.deleteCharAt(stringBuilder.length() - 1);reader.close();} catch (Exception ignored) {}return stringBuilder.toString();}
}

规则编写:
新建AudioPlay类,将每个音符对应每个读取到的字符:

package com.example.demo.play;import cn.hutool.core.io.FileUtil;/*** @author xh* @Date 2022/12/27*/
public class AudioPlay extends Thread{/** 音符 */private String[] notes;/** 间隔时间(单位:毫秒) */private int times;public AudioPlay(String[] notes, int times){this.notes = notes;this.times = times;}public AudioPlay(String filePath, int times){String content = FileUtil.readString(filePath,"UTF-8");this.notes = content.split(" ");this.times = times;}public AudioPlay(int times){this.times = times;}public String[] getNotes(){return this.notes;}public void setNotes(String[] notes){this.notes = notes;}public AudioPlay loadNotes(String notes){this.notes = notes.split(" ");return this;}public int getTimes(){return this.times;}public void setTimes(int times){this.times = times;}@Overridepublic void run(){try{int times = this.times;new Audio("audio/test.mp3").start();sleep(1000);for (String note : notes) {if (note.length() < 1) {continue;}switch (note) {case "1--":new Audio("audio/ll1.mp3").start();sleep(times / 2);break;case "2--":new Audio("audio/ll2.mp3").start();sleep(times / 2);break;case "3--":new Audio("audio/ll3.mp3").start();sleep(times / 2);break;case "4--":new Audio("audio/ll4.mp3").start();sleep(times / 2);break;case "5--":new Audio("audio/ll5.mp3").start();sleep(times / 2);break;case "6--":new Audio("audio/ll6.mp3").start();sleep(times / 2);break;case "7--":new Audio("audio/ll7.mp3").start();sleep(times / 2);break;case "1-":new Audio("audio/l1.mp3").start();sleep(times / 2);break;case "2-":new Audio("audio/l2.mp3").start();sleep(times / 2);break;case "3-":new Audio("audio/l3.mp3").start();sleep(times / 2);break;case "4-":new Audio("audio/l4.mp3").start();sleep(times / 2);break;case "5-":new Audio("audio/l5.mp3").start();sleep(times / 2);break;case "6-":new Audio("audio/l6.mp3").start();sleep(times / 2);break;case "7-":new Audio("audio/l7.mp3").start();sleep(times / 2);break;case "1":new Audio("audio/m1.mp3").start();sleep(times / 2);break;case "2":new Audio("audio/m2.mp3").start();sleep(times / 2);break;case "3":new Audio("audio/m3.mp3").start();sleep(times / 2);break;case "4":new Audio("audio/m4.mp3").start();sleep(times / 2);break;case "5":new Audio("audio/m5.mp3").start();sleep(times / 2);break;case "6":new Audio("audio/m6.mp3").start();sleep(times / 2);break;case "7":new Audio("audio/m7.mp3").start();sleep(times / 2);break;case "1+":new Audio("audio/h1.mp3").start();sleep(times / 2);break;case "2+":new Audio("audio/h2.mp3").start();sleep(times / 2);break;case "3+":new Audio("audio/h3.mp3").start();sleep(times / 2);break;case "4+":new Audio("audio/h4.mp3").start();sleep(times / 2);break;case "5+":new Audio("audio/h5.mp3").start();sleep(times / 2);break;case "6+":new Audio("audio/h6.mp3").start();sleep(times / 2);break;case "7+":new Audio("audio/h7.mp3").start();sleep(times / 2);break;case "1++":new Audio("audio/hh1.mp3").start();sleep(times / 2);break;case "2++":new Audio("audio/hh2.mp3").start();sleep(times / 2);break;case "3++":new Audio("audio/hh3.mp3").start();sleep(times / 2);break;case "4++":new Audio("audio/hh4.mp3").start();sleep(times / 2);break;case "5++":new Audio("audio/hh5.mp3").start();sleep(times / 2);break;case "6++":new Audio("audio/hh6.mp3").start();sleep(times / 2);break;case "7++":new Audio("audio/hh7.mp3").start();sleep(times / 2);break;case "0":sleep(times / 2);break;default:continue;}sleep(times / 2);times = this.times;}}catch (Exception e){throw new RuntimeException(e);}}}

读取曲谱文件并打印曲谱:

package com.example.demo.play;/*** @author xh* @Date 2022/12/27*/
public class Animation extends Thread{/** 音符 */private String[] notes;/** 间隔时间(单位:毫秒) */private int times;public Animation(int times) {this.times = times;}public Animation(String[] notes, int times) {this.notes = notes;this.times = times;}public String[] getNotes() {return this.notes;}public void setNotes(String[] notes) {this.notes = notes;}public int getTimes() {return this.times;}public void setTimes(int times) {this.times = times;}public Animation loadNotes(String notes) {this.notes = notes.split(" ");return this;}@Overridepublic void run() {try {int times = this.times;new Audio("audio/test.mp3").start();sleep(1000);int no = 1;for (String note : this.notes) {if (note.length() < 1) {continue;}// 3+ 0 3+ 3+ 3+ 3+ 0 3+ 3+ 3+ 4+ 2+ 0 2+ 2+ 2+ 2+ 0 2+ 2+ 2+ 3+ 1+String n = note.replace("+", "").replace("-", "");if ("\n".equals(n) || "\r".equals(n) || "\r\n".equals(n)) {System.out.print("\n");no++;continue;}switch (n) {case "0":System.out.print("_");break;case "1":System.out.print("▁");break;case "2":System.out.print("▂");break;case "3":System.out.print("▃");break;case "4":System.out.print("▄");break;case "5":System.out.print("▅");break;case "6":System.out.print("▆");break;case "7":System.out.print("▇");break;}System.out.print(" ");sleep(times);}} catch (InterruptedException e) {throw new RuntimeException(e);}}}

控制音频播放Audio类:

package com.example.demo.play;import cn.hutool.core.io.resource.ResourceUtil;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** @author xh* @Date 2022/12/27*/
public class Audio {private static InputStream is;private Player player;ExecutorService service = Executors.newCachedThreadPool();public Audio(String path){is = ResourceUtil.getStream(path);try{player = new Player(is);}catch (JavaLayerException e){e.printStackTrace();}}public void start(){service.submit(() -> {try{player.play();}catch (JavaLayerException ignored){}});}
}

项目整体结构:
在这里插入图片描述

代码已经开源到Github上,欢迎大家玩!传送门

在这里插入图片描述


http://www.ppmy.cn/news/8441.html

相关文章

CSS 奇技淫巧Box-shadow实现圆环进度条

CSS 奇技淫巧Box-shadow实现圆环进度条 文章目录CSS 奇技淫巧Box-shadow实现圆环进度条一、Box-shadow圆环进度条二、效果预览三、原理刨析四、实际应用五、总结六、参考资料&#x1f498;七、推荐博文&#x1f357;一、Box-shadow圆环进度条 实现圆环进度条的方法用很多种&am…

【Mongoose笔记】Websocket 服务器

【Mongoose笔记】Websocket 服务器 简介 Mongoose 笔记系列用于记录学习 Mongoose 的一些内容。 Mongoose 是一个 C/C 的网络库。它为 TCP、UDP、HTTP、WebSocket、MQTT 实现了事件驱动的、非阻塞的 API。 项目地址&#xff1a; https://github.com/cesanta/mongoose学习 …

shell第五天作业——函数与数组

题目 一、编写函数&#xff0c;实现打印绿色OK和红色FAILED 二、编写函数&#xff0c;实现判断是否有位置参数&#xff0c;如无参数&#xff0c;提示错误 三、编写函数实现两个数字做为参数&#xff0c;返回最大值 四、编写函数&#xff0c;实现两个整数为参数&#xff0c;…

windows11 wsl kali子系统 adb调试

环境准备 在Windows中 安装usbipd 参考资料&#xff1a;https://learn.microsoft.com/zh-cn/windows/wsl/connect-usb 方法一&#xff1a; 在powershell中执行winget install --interactive --exact dorssel.usbipd-win 方法二&#xff1a;去https://github.com/dorssel/usbi…

从umati 看德国人如何玩OPCUA的

到目前为止&#xff0c;机器的联网标准缺乏统一的协议和语义标准。比较知名的要数每个的MTConnect。fanuc机床的focas协议。未来的发展方向是OPCUA协议。但是实现这个目标并非一日之功。德国的umati 社区也许给我们一些启发。 为了推进机床行业的数字化进程&#xff0c;VDW&…

Good Bye 2022: 2023 is NEAR

A.B略 C.鸽巢原理中国剩余定理 首先,显然所有数必须两两相同,不然加上任何整数必定会产生gcd > 2的组合 然后我们手玩可以发现奇偶性,我们至少不能使得有两个数同时是偶数,如果出现两个偶数,两个奇数,例如2 4 5 7,那么无论x选择什么,都至少会出现两个偶数,进一步地,我们发…

Linux多进程编程之exec函数族使用

Linux多进程编程之exec函数族使用1.exec函数族是什么2.execl函数具体使用3.execlp4.exec后面不同字母所代表的含义1.exec函数族是什么 顾名思义&#xff0c;它并不只是一个函数&#xff0c;而是以exec开头的六个函数&#xff0c;并且是没有exec这个函数的&#xff08;就像TCP/…

C++中STL的vector扩容机制

目录前言发生扩容扩容机制size()和capacity()reserve()和resize()前言 前阵子面试的时候&#xff0c;被问到往vector中插入一个数据可能会发生什么&#xff1f; 我答:可能会扩容; 为啥vector支持变长&#xff1f; 我答:它实在堆上动态申请内存&#xff0c;因此有自己的一套扩容…