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\n"
+ "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\n"
+ "audio format: " + audioFormat);
} // require little endian
if (audioFormat.isBigEndian()) {
throw new IllegalArgumentException("StdAudio.read() currently supports only audio stored using little endian\n"
+ "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();
}
}
WebSocket接收音频,并推送到声卡上的更多相关文章
- springboot整合websocket实现一对一消息推送和广播消息推送
maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- springboot+websocket+sockjs进行消息推送【基于STOMP协议】
springboot+websocket+sockjs进行消息推送[基于STOMP协议] WebSocket是在HTML5基础上单个TCP连接上进行全双工通讯的协议,只要浏览器和服务器进行一次握手,就 ...
- 基于 WebSocket 的 MQTT 移动推送方案
WebSphere MQ Telemetry Transport 简介 WebSphere MQ Telemetry Transport (MQTT) 是一项异步消息传输协议,是 IBM 在分析了他们 ...
- spring boot 集成 websocket 实现消息主动推送
spring boot 集成 websocket 实现消息主动 前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单 ...
- Android APP切换到后台接收不到推送消息
1. Android端进程被杀死后,目前自带的保护后台接收消息活跃机制.暂时没有什么好的机制保持任何情况下都活跃 android原生系统用home键杀进程可以起来,如果是强行停止就只能用户自己手动 ...
- 将本地的项目推送到github上
好像还是不能用git在本地直接建一个repository,然后推送到github,这是把本地项目推送到github上已经建好的裤 …or create a new repository on the ...
- 怎么把宿主机上的镜像推送到hub上
怎么把宿主机上的镜像推送到hub上: 1.查看系统中存在的镜像: [root@izuf63bjp8ts8nkl13pxh1z devicemapper]# docker imagesREPOSITOR ...
- 数据测试002:利用Jmeter推送测试数据(上)
数据测试002:利用Jmeter推送测试数据(上) 刚才用Jmeter配置一下MySQL数据库花了点时间,好在最后都解决了,注意下面几个问题: 1)没有配置 “Cannot load JDBC dr ...
- WebSocket(4)---实现定时推送比特币交易信息
实现定时推送比特币交易信息 实现功能:跟虚拟币交易所一样,时时更新当前比特币的价格,最高价,最低价,买一价等等...... 提示:(1)本篇博客是在上一遍基础上搭建,上一篇博客地址:[WebSocke ...
随机推荐
- RCNN,Fast RCNN,Faster RCNN 的前生今世:(1) Selective Search
Selective Search for Object Recoginition 这篇论文是J.R.R. Uijlings发表在2012 IJCV上的一篇文章,主要介绍了选择性搜索(Selective ...
- python open 函数的一些坑
(1)路径问题 open一个同py文件同一个目录的文件的时候,用以下: txt = open('/filtered_words.txt','rb') words = txt.readline() fi ...
- 用jquery快速解决IE输入框不能输入的问题_jquery
代码如下: 在IE10以上版本,微软为了提高IE输入框的便利性,增加了文本内容全部删除和密码眼睛功能,但是有些时候打开新的页面里,输入框却被锁定无法编辑,需要刷新一下页面,或者如果输入框有内容需要点击 ...
- go函数类型的使用
Go函数类型的使用 type myfunc func() string // 声明函数变量 func main() { fn := func() string { return "bbb&q ...
- learning java BigDecimal类
使用BiDecimal类是了为防止运算时精度丢失: var f1 = new BigDecimal("0.05"); var f2 = BigDecimal.valueOf(0.0 ...
- Intel 80386 CPU
一.80386 概述 80386处理器被广泛应用在1980年代中期到1990年代中期的IBM PC相容机中.这些PC机称为「80386电脑」或「386电脑」,有时也简称「80386」或「386」.80 ...
- piplinedb 团队加入confluen
这个消息对于使用pipelinedb 的人来说,可能有点不好,因为官方已经明确说明了,pipelinedb 截止到1.0 版本,将不再维护了, 基本就要靠社区了,但是pipelinedb 团队还是比较 ...
- 如何在Eclipse中写Processing的sketch
有时候人们需要写更复杂的sketch,此时Processing提供的IDE就略显单薄,下面将介绍如何在eclipse中开发Processing. 一共分4步: 一.搭建环境:安装JRE.JDK.Ecl ...
- Docker 常用命令,自用,持续更
1.进入容器 docker exec -it 容器id /bin/bash docker exec -it db30f533ee1b /bin/bash 2.复制文件到容器 docker cp 文件路 ...
- Android中相对布局的两个控件
<Button android:id="@+id/button3" android:layout_width="wrap_content" android ...