😊你好,我是小航,一个正在变秃、变强的文艺倾年。
🔔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;
    }
    @Override
    public 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;
    }
    @Override
    public 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上,欢迎大家玩!传送门






![2023新年祝福代码[css动画特效]](https://img-blog.csdnimg.cn/46d30533ba1f4facaa594a390b6f57c7.png)














