IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Java 解析摩尔斯电码并生成音频流/文件 -> 正文阅读

[网络协议]Java 解析摩尔斯电码并生成音频流/文件

旁白

这周老大安排了一个活儿,前端传一组摩尔斯电码过来,将其装成音频流通过WebSocket推给前端进行播放,由于场景的特殊还需要控制"点" "划"播报的时间长短。当时心想Java还能生成这个玩意儿?通过查阅了大量的资料后,确定了Java的确可以实现,不得不感叹这Java是真滴厉害。相关资料已放到下方链接。
音频基础知识
摩尔斯电码表
Java 生成摩尔斯电码音频流代码

转音频代码

初始化摩尔斯电码映射

public class MorseEncoder {
    /**
     * 存放摩尔斯电码
     */
    private final Map<String,String> codeMap = new HashMap<>();

    /**
     * 点 (控制播报速率倍速)
     */
    private Integer dotRatio = 1;

    /**
     * 划 (控制播报速率倍速)
     */
    private Integer rowRatio = 3;

    /**
     * 大间隔  (控制播报速率倍速)
     */
    private Integer blankRatio = 5;

    private final String dotString = ".";

    private final String rowString = "-";

    /**
     * 小间隔
     */
    private final String minInterval = "@";

    /**
     * 大间隔
     */
    private final String maxInterval = "/";

    private final String blank = " ";

    @PostConstruct
    public void initMap(){
        //初始化HashMap 中间有空格
        codeMap.put("A",". -");
        codeMap.put("B","- . . .");
        codeMap.put("C","- . - .");
        codeMap.put("D","- . .");
        codeMap.put("E",".");
        codeMap.put("F",". . - .");
        codeMap.put("G","- - .");
        codeMap.put("H",". . . .");
        codeMap.put("I",". .");
        codeMap.put("J",". - - -");
        codeMap.put("K","- . -");
        codeMap.put("L",". - . .");
        codeMap.put("M","- -");
        codeMap.put("N","- .");
        codeMap.put("O","- - -");
        codeMap.put("P",". - - .");
        codeMap.put("Q","- - . -");
        codeMap.put("R",". - .");
        codeMap.put("S",". . .");
        codeMap.put("T","-");
        codeMap.put("U",". . -");
        codeMap.put("V",". . . -");
        codeMap.put("W",". - -");
        codeMap.put("X","- . . -");
        codeMap.put("Y","- . - -");
        codeMap.put("Z","- - . .");
        codeMap.put("1",". - - - -");
        codeMap.put("2",". . - - -");
        codeMap.put("3",". . . - -");
        codeMap.put("4",". . . . -");
        codeMap.put("5",". . . . .");
        codeMap.put("6","- . . . .");
        codeMap.put("7","- - . . .");
        codeMap.put("8","- - - . .");
        codeMap.put("9","- - - - .");
        codeMap.put("0","- - - - -");
    }
}

字符串转电码

 /**
     *
     * @param args 字码
     * @param type 类型 1 单词 ["A","B","C"]2 词组 ["ABC","DEFG"]
     * @return
     */
    public String string2MorseCode(List<String> args,Integer type){
        StringBuilder result = new StringBuilder();
        if(type.compareTo(1) == 0 ){
            for (String arg : args) {
                String morse = codeMap.get(arg.toUpperCase());
                if(morse == null){
                    throw new RuntimeException(arg+"不是摩尔斯电码");
                }
                result.append(morse)
                        .append(minInterval);
            }
        }else {
            for (String arg : args) {
                for (int i = 0; i < arg.length(); i++) {
                    String code = String.valueOf(arg.charAt(i));
                    String morse = codeMap.get(code.toUpperCase());
                    if(morse == null){
                        throw new RuntimeException(arg+"不是摩尔斯电码");
                    }
                    result.append(morse)
                            .append(minInterval);

                }
                result.append(maxInterval);
            }
        }
        return result.toString();
    }

电码转音频

 /**
     * 将摩尔斯点码转成音频
     * @param codeString 摩尔斯电码
     * @param reta 速率
     * @return 音频流
     */
    public byte[] codeConvert2Sound(String codeString,int reta){
        int dot = reta * dotRatio;
        int row = reta * rowRatio;
        int blank = reta * blankRatio;
        //存放byte
        ArrayList<Byte> rawData = new ArrayList<Byte>();
        //计算每一个音波
        for(int i=0; i<codeString.length(); i++){
            // 时长
            int soundLength = 0;
            // 频率
            int frequency = 1450;
            String code = Character.toString(codeString.charAt(i));

            if (code.equals(rowString)) {
                soundLength = row;
            } else if (code.equals(dotString)) {
                soundLength = dot;
            } else if (code.equals(minInterval)) {
                soundLength = row;
                //将频率设置成 0
                frequency = 0;
            } else if (code.equals(maxInterval)) {
                soundLength = blank;
                //将频率设置成 0
                frequency = 0;
            }else if(code.equals(this.blank)){
                soundLength = dot;
                //将频率设置成 0
                frequency = 0;
            }

            // add beeps for letters
            for ( int k = 0; k < soundLength * (float)44100 / 1000; k++ ) {
                double angle = k / ( (float)44100 / frequency ) * 2.0 * Math.PI;
                rawData.add( (byte)( Math.sin( angle ) * 100 ) );
            }

            // add break between chars
            /*for (int j=0; j < 2000; j++){
                rawData.add((byte) 0);
            }*/
        }
        //将list转array
        byte[] audio = new byte[rawData.size()];
        for (int i=0; i<rawData.size(); i++){
            audio[i] = rawData.get(i);
        }
        //save2File(audio);
        return audio;
    }

存入到文件中 wav格式

  /**
     * 保存到文件中
     * @param audio
     */
    private void save2File(byte[] audio) {
        InputStream byteArrayInputStream = new ByteArrayInputStream(audio);
        AudioFormat audioFormat = new AudioFormat( (float)44100, 8, 1, true, false );
        AudioInputStream audioInputStream = new AudioInputStream( byteArrayInputStream, audioFormat, audio.length / audioFormat.getFrameSize() );

        try {
            String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH-mm-ss"));
            AudioSystem.write( audioInputStream, AudioFileFormat.Type.WAVE, new File( "D:\\morse-code-"+format +".wav" ));
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

生成WAV头文件

    /**
      2      * @param totalAudioLen  不包括header的音频数据总长度
      3      * @param longSampleRate 采样率,也就是录制时使用的频率、音频采样级别 8000 = 8KHz
      4      * @param channels       audioRecord的声道数1/2
      5      * @param audioFormat    采样精度; 譬如 16bit
      6      * @throws IOException 写文件错误
      7      */
    public static byte[] writeWavFileHeader(long totalAudioLen, long longSampleRate,
                                            int channels, int audioFormat) throws IOException {
        byte[] header = generateWavFileHeader(totalAudioLen, longSampleRate, channels, audioFormat);
        return header;
    }

 /**
 15      * @param totalAudioLen  不包括header的音频数据总长度
 16      * @param longSampleRate 采样率,也就是录制时使用的频率
 17      * @param channels       audioRecord的频道数量
 18      * @param audioFormat    采样精度; 譬如 16bit
 19      */
        private static byte[] generateWavFileHeader(long totalAudioLen, long longSampleRate, int channels,int audioFormat) {

            long totalDataLen = totalAudioLen + 36;

            long byteRate = longSampleRate * 2 * channels;

            byte[] header = new byte[44];

            header[0] = 'R'; // RIFF

            header[1] = 'I';

            header[2] = 'F';

            header[3] = 'F';

            //文件长度  4字节文件长度,这个长度不包括"RIFF"标志(4字节)和文件长度本身所占字节(4字节),即该长度等于整个文件长度 - 8

            header[4] = (byte) (totalDataLen & 0xff);

            header[5] = (byte) ((totalDataLen >> 8) & 0xff);

            header[6] = (byte) ((totalDataLen >> 16) & 0xff);

            header[7] = (byte) ((totalDataLen >> 24) & 0xff);

            //fcc type:4字节 "WAVE" 类型块标识, 大写

            header[8] = 'W';

            header[9] = 'A';

            header[10] = 'V';

            header[11] = 'E';

            //FMT Chunk   4字节 表示"fmt" chunk的开始,此块中包括文件内部格式信息,小写, 最后一个字符是空格

            header[12] = 'f'; // 'fmt '

            header[13] = 'm';

            header[14] = 't';

            header[15] = ' ';//过渡字节

            //数据大小  4字节,文件内部格式信息数据的大小,过滤字节(一般为00000010H)

            header[16] = 16;

            header[17] = 0;

            header[18] = 0;

            header[19] = 0;

            //编码方式 10H为PCM编码格式   FormatTag:2字节,音频数据的编码方式,1:表示是PCM 编码

            header[20] = 1; // format = 1

            header[21] = 0;

            //通道数  Channels:2字节,声道数,单声道为1,双声道为2

            header[22] = (byte) channels;

            header[23] = 0;

            //采样率,每个通道的播放速度

            header[24] = (byte) (longSampleRate & 0xff);

            header[25] = (byte) ((longSampleRate >> 8) & 0xff);

            header[26] = (byte) ((longSampleRate >> 16) & 0xff);

            header[27] = (byte) ((longSampleRate >> 24) & 0xff);

            //音频数据传送速率,采样率*通道数*采样深度/8

            //4字节,音频数据传送速率, 单位是字节。其值为采样率×每次采样大小。播放软件利用此值可以估计缓冲区的大小

            //byteRate = sampleRate * (bitsPerSample / 8) * channels

            header[28] = (byte) (byteRate & 0xff);

            header[29] = (byte) ((byteRate >> 8) & 0xff);

            header[30] = (byte) ((byteRate >> 16) & 0xff);

            header[31] = (byte) ((byteRate >> 24) & 0xff);

            // 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数

            header[32] = (byte) (2 * channels);

            header[33] = 0;

            //每个样本的数据位数

            //2字节,每个声道的采样精度; 譬如 16bit 在这里的值就是16。如果有多个声道,则每个声道的采样精度大小都一样的;

            header[34] = (byte) audioFormat;

            header[35] = 0;

            //Data chunk

            //ckid:4字节,数据标志符(data),表示 "data" chunk的开始。此块中包含音频数据,小写;

            header[36] = 'd';

            header[37] = 'a';

            header[38] = 't';

            header[39] = 'a';

            //音频数据的长度,4字节,audioDataLen = totalDataLen - 36 = fileLenIncludeHeader - 44

            header[40] = (byte) (totalAudioLen & 0xff);

            header[41] = (byte) ((totalAudioLen >> 8) & 0xff);

            header[42] = (byte) ((totalAudioLen >> 16) & 0xff);

            header[43] = (byte) ((totalAudioLen >> 24) & 0xff);

            return header;

        }

测试

@Test
    public void morseCodeTest() throws IOException {
        MorseEncoder encoder = new MorseEncoder();
        List<String> args = new ArrayList<>();
        args.add("ttt");
        args.add("000");
        String code = encoder.string2MorseCode(args, 2);
        byte[] bytes = encoder.codeConvert2Sound(code, 100);

        byte[] header = encoder.writeWavFileHeader(bytes.length, 44100, 1, 8);

        ByteArrayBuilder builder  = new ByteArrayBuilder();
        builder.write(header);
        builder.write(bytes);

        byte[] data = builder.toByteArray();

        File file = new File("D:\\test.wav");
        OutputStream os = new FileOutputStream(file);
        os.write(data);


    }

效果 播放出来的效果也是跟预期的一样
在这里插入图片描述
代码地址
https://gitee.com/wsl__cn/morse-code-convert-audio.git

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-27 11:39:57  更:2022-04-27 11:40:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:50:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码