前言

最近辞职在找工作,才发现今年的就业形势太难了,在家里蹲的时候突然想写个小软件玩,要用到tts功能,就白嫖了微软的tts,不过tts返回的mp3比特率是48bit,java sound播放出来声音异常,不知道是哪里的问题,这是一个悲伤的故事,没办法只好用jave修改比特率,其间无意间发现了一篇关于java sound 节律纯音 的文章,觉得挺有意思,又刚好在用酷狗的3D环绕音听歌(突然间有些怀念天天动听了,j2me时代的听歌神器,可惜凉了),就想到用声音合成的方式生成3D环绕音,经过小半天的研究后实现。

代码实现

1、参考文章

Java Sound 简明教程以及节律纯音实现

blog.mazhangjing.comhttps://blog.mazhangjing.com/2019/04/27/java_sound/ps:网站突然打不开了,不知道是网络还是服务器的问题

2、依赖

代码中使用的歌曲是MP3格式,java sound原生不支持的,需要依赖相关库,依赖后java sound会自动调用

  • mp3spi1.9.5.jar

  • jl1.0.1.jar
  • tritonus_share.jar

3.实现代码

代码会对整首歌曲进行处理,然后可以播放和储存为文件。如果要实时处理,则需要修改代码,javasound解码后读取缓冲区数据,处理后再喂给播放管道。

package util;import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.Date;public class AudioSynth1 extends JFrame {private AudioFormat audioFormat;private AudioInputStream audioInputStream;private SourceDataLine sourceDataLine;private static float sampleRate = 16000.0F;private static int channels = 1;//一个缓冲器,用于在 16000 sampsec 下保存 2 秒单声道数据和 1 秒立体声数据,用于 16 位采样private byte[] audioData = new byte[16000 * 4];//处理前数据存放的缓冲区private byte[] audioDataCopy = new byte[16000 * 4];//处理后数据存放的缓冲区private final JButton generateBtn = new JButton("开始处理");private final JButton playOrFileBtn = new JButton("播放/文件");private final JLabel elapsedTimeMeter = new JLabel("0000");private final JRadioButton stereoPingpong = new JRadioButton("3D旋转测试");private final JRadioButton listen = new JRadioButton("直接播放",true);private final JTextField fileName = new JTextField("E:/test.wav",10);public static void main(String[] args){new AudioSynth1();}public AudioSynth1(){final JPanel controlButtonPanel = new JPanel();controlButtonPanel.setBorder(BorderFactory.createEtchedBorder());final JPanel synButtonPanel = new JPanel();final ButtonGroup synButtonGroup = new ButtonGroup();final JPanel centerPanel = new JPanel();final JPanel outputButtonPanel = new JPanel();outputButtonPanel.setBorder(BorderFactory.createEtchedBorder());final ButtonGroup outputButtonGroup = new ButtonGroup();playOrFileBtn.setEnabled(false);generateBtn.addActionListener(e -> {try {playOrFileBtn.setEnabled(false);//adddata("E:/我的文件/音乐/邓丽君 - 小城故事.mp3");adddata("C:\\Users\\tangtang1600\\dist\\tem.mp3");System.out.println(audioData.length);new SynGen().getSyntheticData(audioData, audioDataCopy);playOrFileBtn.setEnabled(true);} catch (Exception ex) {ex.printStackTrace();}});playOrFileBtn.addActionListener(e -> playOrFileData());controlButtonPanel.add(generateBtn);controlButtonPanel.add(playOrFileBtn);controlButtonPanel.add(elapsedTimeMeter);synButtonGroup.add(stereoPingpong);synButtonPanel.setLayout(new GridLayout(0,1));synButtonPanel.add(stereoPingpong);centerPanel.add(synButtonPanel);outputButtonGroup.add(listen);JRadioButton file = new JRadioButton("文件");outputButtonGroup.add(file);outputButtonPanel.add(listen,BorderLayout.WEST);outputButtonPanel.add(file, BorderLayout.CENTER);outputButtonPanel.add(fileName,BorderLayout.EAST);getContentPane().add(controlButtonPanel,BorderLayout.NORTH);getContentPane().add(centerPanel, BorderLayout.CENTER);getContentPane().add(outputButtonPanel, BorderLayout.SOUTH);setTitle("Copyright 2023,tangtang1600");setDefaultCloseOperation(EXIT_ON_CLOSE);setSize(300,275);setVisible(true);}private  void adddata(String filePath) throws Exception{AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath));// 文件编码AudioFormat audioFormat = audioInputStream.getFormat();// 转换文件编码if (audioFormat.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {sampleRate =  audioFormat.getSampleRate();channels = audioFormat.getChannels();System.out.println("sampleRate:" + sampleRate);System.out.println("channels:" + channels);audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sampleRate, 16, audioFormat.getChannels(), audioFormat.getChannels() * 2, sampleRate, true);// 将数据流也转换成指定编码audioInputStream = AudioSystem.getAudioInputStream(audioFormat, audioInputStream);}// 打开输出设备DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);// 使数据行得到一个播放设备SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);// 将数据行用指定的编码打开sourceDataLine.open(audioFormat);// 使数据行得到数据时就开始播放for (AudioFormat format : dataLineInfo.getFormats()) {System.out.println("信息:" + format.toString());}sourceDataLine.start();int bytesPerFrame = audioInputStream.getFormat().getFrameSize();// 将流数据逐渐写入数据行,边写边播int numBytes = 1024 * bytesPerFrame;byte[] audioBytes = new byte[numBytes];while (audioInputStream.read(audioBytes) != -1) {audioData = ArrayUtil.addAllforByte(audioData,audioBytes);}sourceDataLine.drain();sourceDataLine.close();audioInputStream.close();if (channels ==  2) {audioDataCopy = new byte[audioData.length];}else {audioDataCopy = new byte[audioData.length*2];}System.out.println("size:" + audioDataCopy.length);}private void playOrFileData() {try{InputStream byteArrayInputStream = new ByteArrayInputStream(audioDataCopy);boolean bigEndian = true;boolean signed = true;//Allowable 8000,11025,16000,22050,44100int sampleSizeInBits = 16;if (channels == 1){channels = 2;}audioFormat = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat, audioDataCopy.length/audioFormat.getFrameSize());DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);System.out.println("listen isSelected:" + listen.isSelected());if(listen.isSelected()){new ListenThread().start();} else{generateBtn.setEnabled(false);playOrFileBtn.setEnabled(false);try{File file = new File(fileName.getText());if (!file.exists()){file.createNewFile();}AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE,file );}catch (Exception e) {e.printStackTrace();System.exit(0);}generateBtn.setEnabled(true);playOrFileBtn.setEnabled(true);}}catch (Exception e) {e.printStackTrace();System.exit(0);}}class ListenThread extends Thread{byte[] playBuffer = new byte[16384];public void run(){try{generateBtn.setEnabled(false);playOrFileBtn.setEnabled(false);sourceDataLine.open(audioFormat);sourceDataLine.start();int cnt;long startTime = new Date().getTime();while((cnt = audioInputStream.read(playBuffer, 0, playBuffer.length)) != -1){if(cnt > 0){sourceDataLine.write(playBuffer, 0, cnt);}}sourceDataLine.drain();int elapsedTime = (int)(new Date().getTime() - startTime);elapsedTimeMeter.setText("" + elapsedTime);sourceDataLine.stop();sourceDataLine.close();generateBtn.setEnabled(true);playOrFileBtn.setEnabled(true);}catch (Exception e) {e.printStackTrace();System.exit(0);}}}class SynGen{ByteBuffer byteBufferCopy;ByteBuffer byteBuffer;ShortBuffer shortBufferCopy;ShortBuffer shortBuffer;int byteLength;public void getSyntheticData(byte[] synDataBuffer,byte[] synDataBuffercopy){byteBufferCopy = ByteBuffer.wrap(synDataBuffercopy);shortBufferCopy = byteBufferCopy.asShortBuffer();byteBuffer = ByteBuffer.wrap(synDataBuffer);shortBuffer = byteBuffer.asShortBuffer();byteLength = synDataBuffer.length;stereoSurringRound();}public void stereoSurringRound(){//channels = 2;//声道int bytesPerSamp =channels*2;//帧所包含字节,基于声道,声道数*2int sampLength = byteLength/bytesPerSamp;//帧数double leftGain =0-sampleRate;//左增益-44100double rightGain =0;//右增益0 44100-sampleRate//我这里采用的mp3的采样率是44100,增益值设置为-44100到0double leftGaintemp =0-sampleRate;;double rightGaintemp =0;System.out.println("sampLength:"+sampLength);//将整首歌依据帧数分成28个步长,4个步长为一组环绕(趋向左-左-趋向右-右)//既一首歌环绕28/4=7次int stepLength = (int)sampLength/28;System.out.println("step:"+stepLength);int totalLength = 0;for(int cnt = 0; cnt < sampLength; cnt++){//根据帧数判断if(totalLength>= 0 && totalLength < stepLength ){//趋向左leftGaintemp -= leftGain/stepLength;//0rightGaintemp += leftGain/stepLength;//-> -44100}if(totalLength>= stepLength && totalLength <stepLength*2 ){//左边增益leftGaintemp = rightGain;//0rightGaintemp = leftGain;//-44100}if(totalLength >= stepLength*2 && totalLength < stepLength*3){//趋向右leftGaintemp += leftGain/stepLength;//-> -44100rightGaintemp -= leftGain/stepLength;//->0}if(totalLength >= stepLength*3 && totalLength < stepLength*4){//右边增益leftGaintemp = leftGain;// -44100rightGaintemp = rightGain;//0}//为左通道生成数据short sinValue =0;if (channels == 2) {sinValue=  shortBuffer.get(cnt * 2);}else {sinValue=  shortBuffer.get(cnt);}shortBufferCopy.put((short)(sinValue/sampleRate*(leftGaintemp+sampleRate)));//为右通道生成数据if (channels == 2) {sinValue = shortBuffer.get(cnt * 2 + 1);}shortBufferCopy.put((short)(sinValue/sampleRate*(rightGaintemp+sampleRate)));if(totalLength == stepLength*4-1){totalLength = -1;}totalLength++;}}}}

4、附件

mp3依赖包和代码里用到的歌

java播放MP3依赖库

用Java实现酷狗音乐3D环绕音相关推荐

  1. java接收的文件转换成临时文件,java实现酷狗音乐临时缓存文件转换为MP3文件的方法...

    这篇文章主要介绍了java实现酷狗音乐临时缓存文件转换为MP3文件的方法,涉及java针对文件操作的相关技巧,需要的朋友可以参考下 本文实例讲述了java实现酷狗音乐临时缓存文件转换为MP3文件的方法 ...

  2. java仿酷狗音乐源码_【附项目源码】仿酷狗音乐客户端,浅淡动感歌词补充

    原标题:[附项目源码]仿酷狗音乐客户端,浅淡动感歌词补充 1.前言 之前写了几篇关于动感歌词的简单介绍,相信大家还有印象,这里就不多说了,这篇要说的是,关于翻译歌词和音译歌词,以及我在解析和显示这两种 ...

  3. java爬虫 酷狗音乐

    1.pom.xml   有些依赖与本项目无关 <?xml version="1.0" encoding="UTF-8"?> <project ...

  4. Java爬虫系列之实战:爬取酷狗音乐网 TOP500 的歌曲(附源码)

    在前面分享的两篇随笔中分别介绍了HttpClient和Jsoup以及简单的代码案例: Java爬虫系列二:使用HttpClient抓取页面HTML Java爬虫系列三:使用Jsoup解析HTML 今天 ...

  5. java 爬取网页版的酷狗音乐,下载到本地

    代码实现 import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSONObject; import lombok.extern.sl ...

  6. 酷狗音乐下载|酷狗音乐播放器下载

    酷狗音乐是中国领先的音乐播放器,很受大家喜爱,乐库里面包含了最全的新歌,老歌,流行歌曲和中国风等一大批各种类型的歌曲,你还可以自己搜索自己想听的歌曲,酷狗音乐里面还有很多音效,我个人比较喜欢3D丽音, ...

  7. 酷狗音乐的大数据实践

    2015-06-03 王劲 高可用架构 高可用架构 此文是根据酷狗音乐大数据架构师王劲在[QCON高可用架构群]中的分享内容整理而成,转发请注明出处. 王劲:目前就职酷狗音乐,大数据架构师,负责酷狗大 ...

  8. 酷狗音乐9.2.0_酷狗音乐安卓版 v9.2.0下载 - 艾薇下载站

    酷狗音乐是一款能在手机听歌下载的app.它是中国领先的互联网音乐公司,在开发优秀音乐类互联网应用.服务及核心技术方面,保持国内业界的领先地位.采用先进的构架设计研发,为用户设计了高传输效果的文件下载功 ...

  9. 无乐不作android手机版,酷狗音乐9.4.4版本

    <酷狗音乐9.4.4版本>这是一款广大用户都在使用的音乐播放器软件,也是大家熟悉的酷狗音乐,不仅可以在这里收获许多优质的歌曲,而且只要是当下流行热门歌曲都可以在这里免费听到,而且去除内置广 ...

最新文章

  1. JavaScript基础初始时期分支(018)
  2. 数据库-SQL中like的用法
  3. Angular Filter实现页面搜索
  4. php errno 28,php7.28 编译出错 一直通不过去
  5. MySQL中空字符串与null的区别:计数 判断 时间
  6. cmake学习(五) 系统默认变量和内置变量
  7. jQuery学习笔记:Ajax(二)
  8. 【OpenCV 例程200篇】91. 高斯噪声、瑞利噪声、爱尔兰噪声
  9. nginx 403 Forbidden
  10. 听说……明天上线?!
  11. js------match() 方法
  12. centos出现“FirewallD is not running”
  13. java虚拟机工作原理_java虚拟机原理及工作原理都是什么?java虚拟机如何运行?...
  14. matlab结构数组22,matlab结构数组
  15. Identifying Encrypted Malware Traffic with Contextual Flow Data
  16. 有discuz数据库,忘了管理员密码,怎样进后台
  17. 电脑怎么了--电脑通电电源风扇不转动
  18. java毕业设计点餐系统设计Mybatis+系统+数据库+调试部署
  19. 通过实例理解Go逃逸分析
  20. 【寻东】source insight4.0模仿sublime text的配色方案

热门文章

  1. pythongui日历控件_Selenium2+python自动化25-js处理日历控件(修改readonly属性)
  2. An Integrated Neighborhood Dependent Approach for Nonlinear Enhancement of Color Images
  3. paddlenlp Windows本地搭建语义检索系统
  4. unity出现“Error refreshing packages”怎么办?
  5. CentOS 7 初始化系统
  6. 使用阿里云短信通知服务发送短信--工具类
  7. 2D射影儿何和变换——柱面投影,图像拼接柱面投影
  8. layui使用tips_layui的tips层怎么用?
  9. Golang中单引号、双引号、反引号
  10. 自学笔记-SpringBoot集成ElasticSearch