栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 解析摩尔斯电码并生成音频流/文件

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 解析摩尔斯电码并生成音频流/文件

旁白

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

转音频代码

初始化摩尔斯电码映射

public class MorseEncoder {
    
    private final Map 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","- - - - -");
    }
}

字符串转电码

 
    public String string2MorseCode(List 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();
    }

电码转音频

 
    public byte[] codeConvert2Sound(String codeString,int reta){
        int dot = reta * dotRatio;
        int row = reta * rowRatio;
        int blank = reta * blankRatio;
        //存放byte
        ArrayList rawData = new ArrayList();
        //计算每一个音波
        for(int i=0; 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
            
        }
        //将list转array
        byte[] audio = new byte[rawData.size()];
        for (int i=0; i
            audio[i] = rawData.get(i);
        }
        //save2File(audio);
        return audio;
    }

存入到文件中 wav格式

  
    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头文件

    
    public static byte[] writeWavFileHeader(long totalAudioLen, long longSampleRate,
                                            int channels, int audioFormat) throws IOException {
        byte[] header = generateWavFileHeader(totalAudioLen, longSampleRate, channels, audioFormat);
        return header;
    }

 
        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 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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/838880.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号