用 Java 演奏千千阙歌是什么体验?

article/2024/4/20 14:40:22

JFugue简介

​JFugue 是一个开放源代码编程库,它允许人们使用 Java 编程语言来编程音乐,而无需 MIDI 的复杂性。它由 David Koelle 于 2002 年首次发布。当前版本是 JFugue 5.0,已于 2015 年 3 月发布。Brian Eubanks 将 JFugue 描述为 “对于需要快速便捷地播放音乐或生成 MIDI 文件的应用程序有用”。JFugue 是根据 Apache License 发布的免费软件。 ​

官网地址:JFugue

开启音乐编程之旅

引入pom依赖

    <dependencies><!-- https://mvnrepository.com/artifact/jfugue/jfugue --><dependency><groupId>jfugue</groupId><artifactId>jfugue</artifactId><version>5.0.9</version></dependency></dependencies><repositories><repository><id>jfugue repository</id><url>https://maven.sing-group.org/repository/maven/</url></repository></repositories>

HelloJFugue

public class HelloMusic {public static void main(String[] args) {Player player = new Player();player.play("C D E F G A B");}
}

运行程序即可听到电脑发出的音乐声

注:下面内容可能涉及到一些音乐基本知识和JFugue的语法问题,这里我在网上找了一篇比较不错的博客,供大家参考学习,JFugue4.0 中文说明_鼠晓的博客-CSDN博客

这里是JFugue4.0中文说明,引入的版本是5.0,我都试过,仅在一些类库上有一些升级。

演奏千千阙歌

下面是我从某宝购买的《千千阙歌》的简谱【五线谱入门门槛是在是高】

刚开始学习的我也是对照着语法一个音符一个音符手敲的,发现太费力气了,例如下面这样。

R5q R5q R5q G5s A5s C6s D6s E6q E6i D6s E6s_E6q C6s D6s E6s G6s A6q A6i G6s A6s_A6q A6s G6s E6s C6s D6qi D6s E6s D6i C6i A5i C6s C6ws C5q C5i D5s E5s_E5is G5s A5s C6is B5i B5i B5i G5i E5h D5s D5is D5i E5s F5s_F5is F5s A5s E6is D6q D6i D6s B5s_B5s G5is R5q C5q C5i D5s E5s_E5is G5s A5s C6is B5i B5i B5i G5i E5h D5s D5is D5i E5s F5s_F5is A5s C6s E6is D6i D6i D6i D6s B5s_B5s G5is R5q A5i G5s A5s_A5i G5s A5s B5s B5s A5s B5s_B5q D6s D6i D6s D6i B5i C6i D6s E6s_E6i E6s E6s D6i D6i D6i C6s E6s_E6q B5i G5i A5h R5q G5s A5s C6s D6s E6q E6i D6s E6s_E6q E6s D6s C6s D6s C6i A5s A5s_A5h G5s A5s C6s D6s E6q E6i D6s E6s_E6q E6i_G6i_A6i G6i E6s E6s_E6h R5i E6s E6s D6q D6i C6s D6s_D6s C6s A5i_A5i E6s E6s F6i E6i D6i C6s D6s_D6s E6s E6i_E6i D6s E6s D6q D6i C6s D6s_D6i C6i A5s C6is C6h R5q G5s A5s C6s D6s E6q E6i D6s E6s_E6q C6s D6s E6s G6s A6q A6i G6s A6s_A6q A6s G6s E6s C6s D6qi D6s E6s D6i C6i A5i C6s C6wsR5q R5q R5q R5q C3i G3i C4i E4i G4q E4q A3i E4i A4i C5i E5q C5q D3i A3i D4i A3i F3i C4i F4q C3i G3i C4i D4i E4h C3i G3i C4i G3i E4i G3i C4q E3i B3i E4i B3i G4i B3i E4i B3i D3i A3i D4i A3i F4i A3i D4q G3i D4i G4i B4i D5q G4q C3i G3i C4i G3i E4i G3i C4i G3i E3i B3i E4i B3i G4i B3i E4i B3i D3i A3i D4i A3i F4i A3i D4q G3i D4i G4i B4i D5q G4q F3s C4s F4s G4s A4q G3s D4s G4s A4s B4q E3s B3s E4s G4s B4q A3s E4s A4s B4s C5q D3s A3s D4s E4s F4q E3s B3s E4s G4s B4q A3s E4s A4s B4s C5i E4s+A3s E4s+A3s R5h C3i G3i C4i G3i E4i G3i C4i G3i A3i E4i A4i E4i C5i E4i A4i E4i C3i G3i C4i G3i E4i G3i C4q E3i B3i E4i B3i G4i B3i E4i B3i D3i A3i D4i A3i A3i E4i A4q D3i A3i D4i A3i A3i E4i A4q D3i A3i D4i A3i F3i C4i F4i C4i C3i G3i C4i G3i E4i G3i C4q C3i G3i C4i E4i G4q E4q A3i E4i A4i C5i E5q C5q D3i A3i D4i A3i F3i C4i F4q C3i G3i C4i D4i E4h

于是我在音符规则上包了一层属于自己的、比较好理解的数字规则【还是废力,但比以前好点】。

/*** 1.识别开头 1=?【需要推出规律】* 2.规定按照 " " 【空格符号】作为分隔每一拍进行处理*/
public class CreateMusicApp {private static final String[] C_HEADER = {"R", "C", "D", "E", "F", "G", "A", "B"};private static final Map<String, String> MUSIC_MAP;static {MUSIC_MAP = new HashMap<>();MUSIC_MAP.put("1", "w");MUSIC_MAP.put("2", "h");MUSIC_MAP.put("4", "q");MUSIC_MAP.put("8", "i");MUSIC_MAP.put("16", "s");MUSIC_MAP.put("32", "t");MUSIC_MAP.put("1644", "hs");MUSIC_MAP.put("1641", "qis");MUSIC_MAP.put("164", "qs");MUSIC_MAP.put("41", "qi");MUSIC_MAP.put("88", "q");MUSIC_MAP.put("81", "is");MUSIC_MAP.put("844", "hi");MUSIC_MAP.put("44", "h");MUSIC_MAP.put("84", "qi");MUSIC_MAP.put("1616", "i");MUSIC_MAP.put("168", "is");MUSIC_MAP.put("16411", "ws");MUSIC_MAP.put("444", "hq");MUSIC_MAP.put("161", "st");}public static String outputMusic(String filePath) {// 创建文件输入流并读取文件try (FileReader fileReader = new FileReader(filePath);BufferedReader bufferedReader = new BufferedReader(fileReader)) {String line;List<String> keys = new ArrayList<>();while ((line = bufferedReader.readLine()) != null) {// 2!! 1! 4^/*** pattern.add("T72 KEY:C V1 Rh C6i D6i E6i G6s G6hs | E6i D6i C6i D6s E6hs " +"| C6i D6i E6i B6s C7qis | C7i B6i C7s B6s_B6i G6s A6is | G6qi C6i D6i E6i G6s G6hs " +"| E6i D6i C6i D6s C6hs | C6i D6i E6i G6s C6qis | C6i C6i Bi Ai Bs C6hs Rq C6s E6i E6hs" +"| E6i D6i C6i D6q | E6qi Rq C6s E6i E6hs | F6i E6i G6is C6hs R | C6s F6i F6hs" +"| F6i E6i C6i Gq | E6qi B+G C6s E6i D6qs | C6s E6i D6qs C6s E6i D6qs Rh C6s E6i E6hs E6i D6i C6i D6q" +"| G6qi_G6q C6s E6i E6hs | F6i E6i G6i C6hi Rq C6s F6i F6hs | F6i E6i C6i G6q | E6q C7i_C7h" +"| Bi");*/// 音符-音调-时长// 1-6-8_2-4-5 1-6-8,2-4-5 1-4-5_1-6-8_2-4-5 1-6-8,2-4-5,3-4-5 1-6-8 2-7-8String[] strings = line.split(" ");for (String string : strings) {if (string.contains("_")) {String[] split1 = string.split("_");String key = "";for (int i = 0; i < split1.length; i++) {String[] split = split1[i].split("-");if (split.length != 3) {throw new RuntimeException("格式不正确...");}key += C_HEADER[Integer.parseInt(split[0])];key += split[1];key += MUSIC_MAP.get(split[2]);if (i != split1.length - 1) {key += "_";}}keys.add(key);} else if (string.contains(",")) {String[] split1 = string.split(",");String key = "";for (int i = 0; i < split1.length; i++) {String[] split = split1[i].split("-");if (split.length != 3) {throw new RuntimeException("格式不正确...");}key += C_HEADER[Integer.parseInt(split[0])];key += split[1];key += MUSIC_MAP.get(split[2]);if (i != split1.length - 1) {key += "+";}}keys.add(key);} else {String key = "";String[] split = string.split("-");if (split.length != 3) {throw new RuntimeException("格式不正确...");}key += C_HEADER[Integer.parseInt(split[0])];key += split[1];key += MUSIC_MAP.get(split[2]);keys.add(key);}}}String str = "";for (String key : keys) {str += " " + key;}return str;} catch (IOException e) {e.printStackTrace();}return null;}}

由原来的手敲字母变为了下面这样,手敲数字 doge【纯属实现功能,没关注代码整体设计方面】

0-5-4 0-5-4 0-5-4 5-5-16 6-5-16 1-6-16 2-6-16 3-6-4 3-6-8 2-6-16 3-6-16_3-6-4 1-6-16 2-6-16 3-6-16 5-6-16
6-6-4 6-6-8 5-6-16 6-6-16_6-6-4 6-6-16 5-6-16 3-6-16 1-6-16 2-6-41 2-6-16 3-6-16 2-6-8 1-6-8 6-5-8 1-6-16 1-6-16411
1-5-4 1-5-8 2-5-16 3-5-16_3-5-81 5-5-16 6-5-16 1-6-81 7-5-8 7-5-8 7-5-8 5-5-8 3-5-44 2-5-16 2-5-81 2-5-8 3-5-16 4-5-16_4-5-81 4-5-16 6-5-16 3-6-81
2-6-4 2-6-8 2-6-16 7-5-16_7-5-16 5-5-81 0-5-4 1-5-4 1-5-8 2-5-16 3-5-16_3-5-81 5-5-16 6-5-16 1-6-81 7-5-8 7-5-8 7-5-8 5-5-8 3-5-44
2-5-16 2-5-81 2-5-8 3-5-16 4-5-16_4-5-81 6-5-16 1-6-16 3-6-81 2-6-8 2-6-8 2-6-8 2-6-16 7-5-16_7-5-16 5-5-81 0-5-4 6-5-8 5-5-16 6-5-16_6-5-8 5-5-16 6-5-16 7-5-16 7-5-16 6-5-16 7-5-16_7-5-4
2-6-16 2-6-8 2-6-16 2-6-8 7-5-8 1-6-8 2-6-16 3-6-16_3-6-8 3-6-16 3-6-16 2-6-8 2-6-8 2-6-8 1-6-16 3-6-16_3-6-4 7-5-8 5-5-8
6-5-44 0-5-4 5-5-16 6-5-16 1-6-16 2-6-16 3-6-4 3-6-8 2-6-16 3-6-16_3-6-4 3-6-16 2-6-16 1-6-16 2-6-16
1-6-8 6-5-16 6-5-16_6-5-44 5-5-16 6-5-16 1-6-16 2-6-16 3-6-4 3-6-8 2-6-16 3-6-16_3-6-4 3-6-8_5-6-8_6-6-8 5-6-8 3-6-16 3-6-16_3-6-44 0-5-8 3-6-16 3-6-16
2-6-4 2-6-8 1-6-16 2-6-16_2-6-16 1-6-16 6-5-8_6-5-8 3-6-16 3-6-16 4-6-8 3-6-8 2-6-8 1-6-16 2-6-16_2-6-16 3-6-16 3-6-8_3-6-8 2-6-16 3-6-16 2-6-4 2-6-8 1-6-16 2-6-16_2-6-8 1-6-8 6-5-16 1-6-81
1-6-44 0-5-4 5-5-16 6-5-16 1-6-16 2-6-16 3-6-4 3-6-8 2-6-16 3-6-16_3-6-4 1-6-16 2-6-16 3-6-16 5-6-16 6-6-4 6-6-8 5-6-16 6-6-16_6-6-4 6-6-16 5-6-16 3-6-16 1-6-16
2-6-41 2-6-16 3-6-16 2-6-8 1-6-8 6-5-8 1-6-16 1-6-16411
0-5-4 0-5-4 0-5-4 0-5-4 1-3-8 5-3-8 1-4-8 3-4-8 5-4-4 3-4-4
6-3-8 3-4-8 6-4-8 1-5-8 3-5-4 1-5-4 2-3-8 6-3-8 2-4-8 6-3-8 4-3-8 1-4-8 4-4-4 1-3-8 5-3-8 1-4-8 2-4-8 3-4-44
1-3-8 5-3-8 1-4-8 5-3-8 3-4-8 5-3-8 1-4-4 3-3-8 7-3-8 3-4-8 7-3-8 5-4-8 7-3-8 3-4-8 7-3-8 2-3-8 6-3-8 2-4-8 6-3-8 4-4-8 6-3-8 2-4-4
5-3-8 2-4-8 5-4-8 7-4-8 2-5-4 5-4-4 1-3-8 5-3-8 1-4-8 5-3-8 3-4-8 5-3-8 1-4-8 5-3-8 3-3-8 7-3-8 3-4-8 7-3-8 5-4-8 7-3-8 3-4-8 7-3-8
2-3-8 6-3-8 2-4-8 6-3-8 4-4-8 6-3-8 2-4-4 5-3-8 2-4-8 5-4-8 7-4-8 2-5-4 5-4-4 4-3-16 1-4-16 4-4-16 5-4-16 6-4-4 5-3-16 2-4-16 5-4-16 6-4-16 7-4-4
3-3-16 7-3-16 3-4-16 5-4-16 7-4-4 6-3-16 3-4-16 6-4-16 7-4-16 1-5-4 2-3-16 6-3-16 2-4-16 3-4-16 4-4-4 3-3-16 7-3-16 3-4-16 5-4-16 7-4-4
6-3-16 3-4-16 6-4-16 7-4-16 1-5-8 3-4-16,6-3-16 3-4-16,6-3-16 0-5-2 1-3-8 5-3-8 1-4-8 5-3-8 3-4-8 5-3-8 1-4-8 5-3-8
6-3-8 3-4-8 6-4-8 3-4-8 1-5-8 3-4-8 6-4-8 3-4-8 1-3-8 5-3-8 1-4-8 5-3-8 3-4-8 5-3-8 1-4-4 3-3-8 7-3-8 3-4-8 7-3-8 5-4-8 7-3-8 3-4-8 7-3-8
2-3-8 6-3-8 2-4-8 6-3-8 6-3-8 3-4-8 6-4-4 2-3-8 6-3-8 2-4-8 6-3-8 6-3-8 3-4-8 6-4-4 2-3-8 6-3-8 2-4-8 6-3-8 4-3-8 1-4-8 4-4-8 1-4-8
1-3-8 5-3-8 1-4-8 5-3-8 3-4-8 5-3-8 1-4-4 1-3-8 5-3-8 1-4-8 3-4-8 5-4-4 3-4-4 6-3-8 3-4-8 6-4-8 1-5-8 3-5-4 1-5-4
2-3-8 6-3-8 2-4-8 6-3-8 4-3-8 1-4-8 4-4-4 1-3-8 5-3-8 1-4-8 2-4-8 3-4-44

有两份文件是因为简谱中将谱分为了左右手两个谱,对应简谱里就是两行。

0-5-4 可以理解为用 "-" 号将音符-音调-时长分隔开,"_" 代表连音符

主程序运行

    public static void main(String[] args) {String song1 = CreateMusicApp.outputMusic("E:\\IdeaCode\\Shuttle_Music\\src\\main\\resources\\qqqg\\temp.txt");String song2 = CreateMusicApp.outputMusic("E:\\IdeaCode\\Shuttle_Music\\src\\main\\resources\\qqqg\\temp2.txt");System.out.println(song1);System.out.println();System.out.println(song2);Player player = new Player();Pattern music = new Pattern();Pattern pattern1 = new Pattern("T60 KEY:C V0 " + song1);Pattern pattern2 = new Pattern("T60 KEY:C V1 " + song2);music.add(pattern1);music.add(pattern2);player.play(music);}

运行程序即可享受到动听的音乐~

如果想保存到本地可以调用以下方法保存为 mid 格式文件

MidiFileManager.savePatternToMidi(pattern, new File("千千阙歌.mid"));

写在最后

研究 Java 音乐编程纯属个人兴趣爱好,有想了解更多的朋友推荐看看JFugue5.0官方提供的电子书【虽然收费,而且很贵,25$,但没事,我买了...】

博客限制原因没法上传资源,评论区留言邮箱即可。


http://www.ngui.cc/article/show-1007559.html

相关文章

一个看起来非常科幻的人脸识别接口与其实现逻辑,用于二次开发

前言看起来非常高端的人脸识别接口&#xff0c;简单的进行二次开发就可以衍生为人脸识别考勤系统、人脸识别安全系统等等&#xff1b;展厅以及实现逻辑启动界面点击“是”&#xff1a;人脸已经录入数据库&#xff0c;识别失败弹出Warning可能因为误判&#xff0c;重新识别&…

Unity即时战略/塔防项目实战(一)——构造网格建造系统

Unity即时战略/塔防项目实战&#xff08;一&#xff09;—— 构造网格建造系统 效果展示 Unity RTS游戏网格建造系统实现原理 地形和格子划分&#xff0c;建造系统BuildManager构建 地形最终需要划分成一个一个的小方格&#xff0c;首先定义一下小方格&#xff1a; private…

2022(一等奖)D678基于改进结构函数法的大气气溶胶遥感反演

作品介绍 1 应用背景 大气气溶胶是大气中重要的成分之一&#xff0c;是悬浮于大气中的固体和液体微粒与它们的气体载体共同组成的多相体系&#xff0c;其尺度大约在10-3到102 μm之间。大气气溶胶的特性对空气质量具有良好的指示作用&#xff0c;气溶胶的研究对空气质量的监测…

北邮22信通:(7)实验1 题目四:一元多项式(节省内存版)

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 上一篇文章&#xff1a; 北邮22信通&#xff1a;&#xff08;6&#xff09;实验1 题目三 &#xff1a;通讯录管理_青山如墨雨如画的博客-CSDN博客 下一篇文…

pyhton第九天作业

目录 (最大数的出现)编写程序读取整数&#xff0c;找出它们中的最大值&#xff0c;然后计算它的出现次数。假设输入以数字0 结束。假设你输入的是“3 5 2 5 5 50”;序找出的最大数是而 的出现次数是4(提示:维护两个变量 max和count。变量 max 存储的是当前最大数&#xff0c;而…

C++语法(11)---- 模拟实现list

1.基础元素 struct list_node {list_node* _next;list_node* _prev;T _data;list_node(const T& x): _next(nullptr), _prev(nullptr), _data(x){} }; list链表&#xff0c;基本要素就是链表的一个小块&#xff0c;这个小块自己带着的数据以及指向前后位置的指针组成。初始…

关于VUE3的数据

接口请求出来的数据一般为对象类型的这里有三种方法存储数据&#xff1a;refreactive([])不太推荐reactive&#xff08;[]&#xff09;嵌套一个对象去存储&#xff0c;推荐1、ref<template><van-swipe :autoplay"3000"lazy-render><van-swipe-itemv-f…

2023-03-25 Android app 通过蓝牙(BLE低功耗蓝牙)实现设备间通讯的一个可用实例

一、两台android 手机之间的ble 蓝牙通信&#xff0c;不要蓝牙匹配&#xff0c;也是可以互传数据。 二、主要代码参考下面的文章&#xff1a; 1、主要参考 Android通过蓝牙&#xff08;BLE低功耗蓝牙&#xff09;实现设备间通讯 | 客户端 | 服务端_蓝牙beaon server_Code-Por…

TiDB K8S

1、 命名空间 k create ns ti k create namespace tidb-admin k create namespace tidb-clusteralias kkubectl alias tik -n tidb alias tiak -n tidb-admin alias tick -n tidb-cluster2、 Helm安装 tidb-operator helm repo add pingcap https://charts.pingcap.org/ helm…

论文解读:PP-LiteSeg: A Superior Real-Time Semantic Segmentation Model

发表时间&#xff1a;2022 论文地址&#xff1a;https://arxiv.org/abs/2204.02681 项目地址&#xff1a;https://github.com/PaddlePaddle/PaddleSeg PP-LiteSeg&#xff0c;一个新的轻量级实时语义分割任务模型&#xff0c;在分割精度和推理速度之间实现了一种最先进的权衡…