zoukankan      html  css  js  c++  java
  • WebSocket接收音频,并推送到声卡上

    使用信息

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.ImmutableMap;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.text.StringEscapeUtils;
    import org.java_websocket.client.WebSocketClient;
    import org.java_websocket.drafts.Draft;
    import org.java_websocket.drafts.Draft_6455;
    import org.java_websocket.handshake.ServerHandshake;
    
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.nio.ByteBuffer;
    import java.nio.charset.Charset;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.List;
    import java.util.Queue;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadFactory;
    
    @Slf4j
    public class WebSocketTTS extends WebSocketClient {
    
        //https://github.com/alumae/kaldi-gstreamer-server
        //https://realpython.com/python-speech-recognition/
        public static String WS_URL = "ws://xxx.xxx.xxx.xxx/service";
    
        private boolean isWsClosed = false;
    
    
        Queue<byte[]> audioQueue = new LinkedBlockingQueue<>();
    
    
        ExecutorService exec = Executors.newFixedThreadPool(1,
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        Thread t = Executors.defaultThreadFactory().newThread(r);
                        t.setDaemon(true);
                        return t;
                    }
                });
    
    
        public WebSocketTTS(URI serverURI) {
            super(serverURI, new Draft_6455());
            exec.execute(this::play);
        }
    
        public WebSocketTTS(URI serverURI, Draft draft) {
            super(serverURI, draft);
        }
    
        @Override
        public void onClose(int code, String reason, boolean remote) {
    
            System.out.println("code = " + code + "; reason = " + reason);
            System.out.println("Connection closed by " + (remote ? "remote peer" : "us"));
            isWsClosed = true;
            System.out.println("已关闭连接");
        }
    
        @Override
        public void onError(Exception arg0) {
            System.out.println("made mistakes");
        }
    
        @Override
        public void onMessage(ByteBuffer bytes) {
            audioQueue.add(bytes.array());
            log.info("接收到:{}字节,音频时长:{}秒", bytes.capacity(),bytes.capacity()/16000.0f);
        }
    
        public void play() {
            while (true) {
                byte[] data = audioQueue.poll();
                if (data != null) {
                    StdAudio.play(data);
                } else if (isWsClosed) {
                    break;
                }
            }
        }
    
    
        @Override
        public void onMessage(String message) {
    
            String json = StringEscapeUtils.unescapeJava(message);
            System.out.println(json);
        }
    
        @Override
        public void onOpen(ServerHandshake arg0) {
            System.out.println("已经连接到websocket接口");
        }
    
        public static void main(String[] args) throws URISyntaxException, InterruptedException, IOException {
    
            WebSocketTTS websocket = new WebSocketTTS(new URI(WS_URL));
            if (!websocket.connectBlocking()) {
                System.err.println("Could not connect to the server.");
                return;
            }
    
            List<String> lines = Files.readAllLines(Paths.get("d:/测试文本.txt"), Charset.forName("UTF-8"));
    
            for (String line : lines) {
                ObjectMapper objectMapper = new ObjectMapper();
                ImmutableMap<String, Object> immutableMap = ImmutableMap.of("text", line, "param", "value");
                String json = objectMapper.writeValueAsString(immutableMap);
                websocket.send(json);
            }
            websocket.send("<eos>");
    
            while (!(websocket.isWsClosed&&websocket.audioQueue.isEmpty()))
            {
                Thread.sleep(5000);
                log.info("正在等待服务退出");
            }
    
        }
    
    
    }

     StdAudio.java

    /******************************************************************************
     *  Compilation:  javac StdAudio.java
     *  Execution:    java StdAudio
     *  Dependencies: none
     *
     *  Simple library for reading, writing, and manipulating .wav files.
     *
     *
     *  Limitations
     *  -----------
     *    - Assumes the audio is monaural, little endian, with sampling rate
     *      of 44,100
     *    - check when reading .wav files from a .jar file ?
     *
     ******************************************************************************/
    
    import javax.sound.sampled.AudioFileFormat;
    import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.Clip;
    import javax.sound.sampled.DataLine;
    import javax.sound.sampled.LineUnavailableException;
    import javax.sound.sampled.SourceDataLine;
    import javax.sound.sampled.UnsupportedAudioFileException;
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     *  <i>Standard audio</i>. This class provides a basic capability for
     *  creating, reading, and saving audio.
     *  <p>
     *  The audio format uses a sampling rate of 44,100 Hz, 16-bit, monaural.
     *
     *  <p>
     *  For additional documentation, see <a href="https://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
     *  <i>Computer Science: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
     *
     *  @author Robert Sedgewick
     *  @author Kevin Wayne
     */
    public final class StdAudio {
    
        /**
         *  The sample rate: 44,100 Hz for CD quality audio.
         */
        public static final int SAMPLE_RATE = 16000;//44100;
    
        private static final int BYTES_PER_SAMPLE = 2;       // 16-bit audio
        private static final int BITS_PER_SAMPLE = 16;       // 16-bit audio
        private static final double MAX_16_BIT = 32768;
        private static final int SAMPLE_BUFFER_SIZE = 4096;
    
        private static final int MONO   = 1;
        private static final int STEREO = 2;
        private static final boolean LITTLE_ENDIAN = false;
        private static final boolean BIG_ENDIAN    = true;
        private static final boolean SIGNED        = true;
        private static final boolean UNSIGNED      = false;
    
    
        private static SourceDataLine line;   // to play the sound
        private static byte[] buffer;         // our internal buffer
        private static int bufferSize = 0;    // number of samples currently in internal buffer
    
        private StdAudio() {
            // can not instantiate
        }
    
        // static initializer
        static {
            init();
        }
    
        // open up an audio stream
        private static void init() {
            try {
                // 44,100 Hz, 16-bit audio, mono, signed PCM, little endian
                AudioFormat format = new AudioFormat((float) SAMPLE_RATE, BITS_PER_SAMPLE, MONO, SIGNED, LITTLE_ENDIAN);
                DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
    
                line = (SourceDataLine) AudioSystem.getLine(info);
                line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
    
                // the internal buffer is a fraction of the actual buffer size, this choice is arbitrary
                // it gets divided because we can't expect the buffered data to line up exactly with when
                // the sound card decides to push out its samples.
                buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE/3];
            }
            catch (LineUnavailableException e) {
                System.out.println(e.getMessage());
            }
    
            // no sound gets made before this call
            line.start();
        }
    
        // get an AudioInputStream object from a file
        private static AudioInputStream getAudioInputStreamFromFile(String filename) {
            if (filename == null) {
                throw new IllegalArgumentException("filename is null");
            }
    
            try {
                // first try to read file from local file system
                File file = new File(filename);
                if (file.exists()) {
                    return AudioSystem.getAudioInputStream(file);
                }
    
                // resource relative to .class file
                InputStream is1 = StdAudio.class.getResourceAsStream(filename);
                if (is1 != null) {
                    return AudioSystem.getAudioInputStream(is1);
                }
    
                // resource relative to classloader root
                InputStream is2 = StdAudio.class.getClassLoader().getResourceAsStream(filename);
                if (is2 != null) {
                    return AudioSystem.getAudioInputStream(is2);
                }
    
                // give up
                else {
                    throw new IllegalArgumentException("could not read '" + filename + "'");
                }
            }
            catch (IOException e) {
                throw new IllegalArgumentException("could not read '" + filename + "'", e);
            }
            catch (UnsupportedAudioFileException e) {
                throw new IllegalArgumentException("file of unsupported audio format: '" + filename + "'", e);
            }
        }
    
        /**
         * Closes standard audio.
         */
        public static void close() {
            line.drain();
            line.stop();
        }
    
        /**
         * Writes one sample (between -1.0 and +1.0) to standard audio.
         * If the sample is outside the range, it will be clipped.
         *
         * @param  sample the sample to play
         * @throws IllegalArgumentException if the sample is {@code Double.NaN}
         */
        public static void play(double sample) {
            if (Double.isNaN(sample)) throw new IllegalArgumentException("sample is NaN");
    
            // clip if outside [-1, +1]
            if (sample < -1.0) sample = -1.0;
            if (sample > +1.0) sample = +1.0;
    
            // convert to bytes
            short s = (short) (MAX_16_BIT * sample);
            if (sample == 1.0) s = Short.MAX_VALUE;   // special case since 32768 not a short
            buffer[bufferSize++] = (byte) s;
            buffer[bufferSize++] = (byte) (s >> 8);   // little endian
    
            // send to sound card if buffer is full
            if (bufferSize >= buffer.length) {
                line.write(buffer, 0, buffer.length);
                bufferSize = 0;
            }
        }
    
    
        public static void play(byte[] samples)
        {
            for (int i = 0; i < samples.length; i++) {
                play(samples[i]);
            }
        }
    
        public static void play(byte sample) {
            buffer[bufferSize++] = sample;
            // send to sound card if buffer is full
            if (bufferSize >= buffer.length) {
                line.write(buffer, 0, buffer.length);
                bufferSize = 0;
            }
        }
        /**
         * Writes the array of samples (between -1.0 and +1.0) to standard audio.
         * If a sample is outside the range, it will be clipped.
         *
         * @param  samples the array of samples to play
         * @throws IllegalArgumentException if any sample is {@code Double.NaN}
         * @throws IllegalArgumentException if {@code samples} is {@code null}
         */
        public static void play(double[] samples) {
            if (samples == null) throw new IllegalArgumentException("argument to play() is null");
            for (int i = 0; i < samples.length; i++) {
                play(samples[i]);
            }
        }
    
        /**
         * Reads audio samples from a file (in .wav or .au format) and returns
         * them as a double array with values between -1.0 and +1.0.
         * The audio file must be 16-bit with a sampling rate of 44,100.
         * It can be mono or stereo.
         *
         * @param  filename the name of the audio file
         * @return the array of samples
         */
        public static double[] read(String filename) {
    
            // make sure that AudioFormat is 16-bit, 44,100 Hz, little endian
            final AudioInputStream ais = getAudioInputStreamFromFile(filename);
            AudioFormat audioFormat = ais.getFormat();
    
            // require sampling rate = 44,100 Hz
            if (audioFormat.getSampleRate() != SAMPLE_RATE) {
                throw new IllegalArgumentException("StdAudio.read() currently supports only a sample rate of " + SAMPLE_RATE + " Hz
    "
                        + "audio format: " + audioFormat);
            }
    
            // require 16-bit audio
            if (audioFormat.getSampleSizeInBits() != BITS_PER_SAMPLE) {
                throw new IllegalArgumentException("StdAudio.read() currently supports only " + BITS_PER_SAMPLE + "-bit audio
    "
                        + "audio format: " + audioFormat);
            }
    
            // require little endian
            if (audioFormat.isBigEndian()) {
                throw new IllegalArgumentException("StdAudio.read() currently supports only audio stored using little endian
    "
                        + "audio format: " + audioFormat);
            }
    
            byte[] bytes = null;
            try {
                int bytesToRead = ais.available();
                bytes = new byte[bytesToRead];
                int bytesRead = ais.read(bytes);
                if (bytesToRead != bytesRead) {
                    throw new IllegalStateException("read only " + bytesRead + " of " + bytesToRead + " bytes");
                }
            }
            catch (IOException ioe) {
                throw new IllegalArgumentException("could not read '" + filename + "'", ioe);
            }
    
            int n = bytes.length;
    
            // little endian, mono
            if (audioFormat.getChannels() == MONO) {
                double[] data = new double[n/2];
                for (int i = 0; i < n/2; i++) {
                    // little endian, mono
                    data[i] = ((short) (((bytes[2*i+1] & 0xFF) << 8) | (bytes[2*i] & 0xFF))) / ((double) MAX_16_BIT);
                }
                return data;
            }
    
            // little endian, stereo
            else if (audioFormat.getChannels() == STEREO) {
                double[] data = new double[n/4];
                for (int i = 0; i < n/4; i++) {
                    double left  = ((short) (((bytes[4*i+1] & 0xFF) << 8) | (bytes[4*i + 0] & 0xFF))) / ((double) MAX_16_BIT);
                    double right = ((short) (((bytes[4*i+3] & 0xFF) << 8) | (bytes[4*i + 2] & 0xFF))) / ((double) MAX_16_BIT);
                    data[i] = (left + right) / 2.0;
                }
                return data;
            }
    
            // TODO: handle big endian (or other formats)
            else throw new IllegalStateException("audio format is neither mono or stereo");
        }
    
        /**
         * Saves the double array as an audio file (using .wav or .au format).
         *
         * @param  filename the name of the audio file
         * @param  samples the array of samples
         * @throws IllegalArgumentException if unable to save {@code filename}
         * @throws IllegalArgumentException if {@code samples} is {@code null}
         * @throws IllegalArgumentException if {@code filename} is {@code null}
         * @throws IllegalArgumentException if {@code filename} extension is not {@code .wav}
         *         or {@code .au}
         */
        public static void save(String filename, double[] samples) {
            if (filename == null) {
                throw new IllegalArgumentException("filenameis null");
            }
            if (samples == null) {
                throw new IllegalArgumentException("samples[] is null");
            }
    
            // assumes 16-bit samples with sample rate = 44,100 Hz
            // use 16-bit audio, mono, signed PCM, little Endian
            AudioFormat format = new AudioFormat(SAMPLE_RATE, 16, MONO, SIGNED, LITTLE_ENDIAN);
            byte[] data = new byte[2 * samples.length];
            for (int i = 0; i < samples.length; i++) {
                int temp = (short) (samples[i] * MAX_16_BIT);
                if (samples[i] == 1.0) temp = Short.MAX_VALUE;   // special case since 32768 not a short
                data[2*i + 0] = (byte) temp;
                data[2*i + 1] = (byte) (temp >> 8);   // little endian
            }
    
            // now save the file
            try {
                ByteArrayInputStream bais = new ByteArrayInputStream(data);
                AudioInputStream ais = new AudioInputStream(bais, format, samples.length);
                if (filename.endsWith(".wav") || filename.endsWith(".WAV")) {
                    AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File(filename));
                }
                else if (filename.endsWith(".au") || filename.endsWith(".AU")) {
                    AudioSystem.write(ais, AudioFileFormat.Type.AU, new File(filename));
                }
                else {
                    throw new IllegalArgumentException("file type for saving must be .wav or .au");
                }
            }
            catch (IOException ioe) {
                throw new IllegalArgumentException("unable to save file '" + filename + "'", ioe);
            }
        }
    
    
    
        /**
         * Plays an audio file (in .wav, .mid, or .au format) in a background thread.
         *
         * @param filename the name of the audio file
         * @throws IllegalArgumentException if unable to play {@code filename}
         * @throws IllegalArgumentException if {@code filename} is {@code null}
         */
        public static synchronized void play(final String filename) {
            new Thread(new Runnable() {
                public void run() {
                    AudioInputStream ais = getAudioInputStreamFromFile(filename);
                    stream(ais);
                }
            }).start();
        }
    
    
        // https://www3.ntu.edu.sg/home/ehchua/programming/java/J8c_PlayingSound.html
        // play a wav or aif file
        // javax.sound.sampled.Clip fails for long clips (on some systems), perhaps because
        // JVM closes (see remedy in loop)
        private static void stream(AudioInputStream ais) {
            SourceDataLine line = null;
            int BUFFER_SIZE = 4096; // 4K buffer
    
            try {
                AudioFormat audioFormat = ais.getFormat();
                DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
                line = (SourceDataLine) AudioSystem.getLine(info);
                line.open(audioFormat);
                line.start();
                byte[] samples = new byte[BUFFER_SIZE];
                int count = 0;
                while ((count = ais.read(samples, 0, BUFFER_SIZE)) != -1) {
                    line.write(samples, 0, count);
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            catch (LineUnavailableException e) {
                e.printStackTrace();
            }
            finally {
                if (line != null) {
                    line.drain();
                    line.close();
                }
            }
        }
    
        /**
         * Loops an audio file (in .wav, .mid, or .au format) in a background thread.
         *
         * @param filename the name of the audio file
         * @throws IllegalArgumentException if {@code filename} is {@code null}
         */
        public static synchronized void loop(String filename) {
            if (filename == null) throw new IllegalArgumentException();
    
            final AudioInputStream ais = getAudioInputStreamFromFile(filename);
    
            try {
                Clip clip = AudioSystem.getClip();
                // Clip clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
                clip.open(ais);
                clip.loop(Clip.LOOP_CONTINUOUSLY);
            }
            catch (LineUnavailableException e) {
                e.printStackTrace();
            }
            catch (IOException e) {
                e.printStackTrace();
            }
    
            // keep JVM open
            new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    
    
        /***************************************************************************
         * Unit tests {@code StdAudio}.
         ***************************************************************************/
    
        // create a note (sine wave) of the given frequency (Hz), for the given
        // duration (seconds) scaled to the given volume (amplitude)
        private static double[] note(double hz, double duration, double amplitude) {
            int n = (int) (StdAudio.SAMPLE_RATE * duration);
            double[] a = new double[n+1];
            for (int i = 0; i <= n; i++)
                a[i] = amplitude * Math.sin(2 * Math.PI * i * hz / StdAudio.SAMPLE_RATE);
            return a;
        }
    
        /**
         * Test client - play an A major scale to standard audio.
         *
         * @param args the command-line arguments
         */
        /**
         * Test client - play an A major scale to standard audio.
         *
         * @param args the command-line arguments
         */
        public static void main(String[] args) {
    
            // 440 Hz for 1 sec
            double freq = 440.0;
            for (int i = 0; i <= StdAudio.SAMPLE_RATE; i++) {
                StdAudio.play(0.5 * Math.sin(2*Math.PI * freq * i / StdAudio.SAMPLE_RATE));
            }
    
            // scale increments
            int[] steps = { 0, 2, 4, 5, 7, 9, 11, 12 };
            for (int i = 0; i < steps.length; i++) {
                double hz = 440.0 * Math.pow(2, steps[i] / 12.0);
                StdAudio.play(note(hz, 1.0, 0.5));
            }
    
    
            // need to call this in non-interactive stuff so the program doesn't terminate
            // until all the sound leaves the speaker.
            StdAudio.close();
        }
    }
  • 相关阅读:
    40个GitHub上最受欢迎的iOS开源项目
    Swift应用开源项目推荐
    swift在github上开源的地址
    必须Mark!43个优秀的Swift开源项目推荐
    Swift中编写单例的正确方式
    Android提高第十一篇之模拟信号示波器
    Android平台音频信号FFT的实现
    ios UINavigationController
    工作记录8:iOS 传值问题总结(7种传值完美介绍)
    利用协议代理实现导航控制器UINavigationController视图之间的正向传值和反向传值
  • 原文地址:https://www.cnblogs.com/passedbylove/p/11832835.html
Copyright © 2011-2022 走看看