IntercomTestClient.java 4.66 KB
package com.genersoft.iot.vmp;

import com.alibaba.fastjson2.JSONObject;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Base64;
import java.util.Timer;
import java.util.TimerTask;

public class IntercomTestClient {

    // 【配置】修改为你的测试 SIM 卡号 (注意补全12位)
    private static final String SIM = "040028816490";
    // 【配置】网关地址
    private static final String WS_URL = "ws://118.113.164.50:10202?sim=" + SIM;

    public static void main(String[] args) throws Exception {
        System.out.println("正在连接 WebSocket: " + WS_URL);

        WebSocketClient client = new WebSocketClient(new URI(WS_URL)) {
            private Timer audioTimer;

            @Override
            public void onOpen(ServerHandshake handshakedata) {
                System.out.println("✅ WebSocket 连接成功");

                // 1. 发送注册包 (模拟前端逻辑)
                JSONObject registerMsg = new JSONObject();
                registerMsg.put("type", "register");
                registerMsg.put("stream_id", SIM);
                send(registerMsg.toJSONString());
                System.out.println(">> 发送注册包: " + registerMsg.toJSONString());

                // 2. 开启定时任务,模拟由麦克风产生的音频流 (每40ms发送一次)
                startSendingAudio();
            }

            @Override
            public void onMessage(String message) {
                System.out.println("<< 收到消息: " + message);
            }

            @Override
            public void onClose(int code, String reason, boolean remote) {
                System.out.println("❌ 连接关闭: " + reason);
                if (audioTimer != null) audioTimer.cancel();
            }

            @Override
            public void onError(Exception ex) {
                ex.printStackTrace();
            }

            // 模拟发送音频流
            private void startSendingAudio() {
                audioTimer = new Timer();
                // 生成 8000Hz 的正弦波数据 (模拟 '滴-----' 的声音)
                // 每次发送 320 字节 (160个采样点, 20ms数据,或者根据网关调整)
                // 这里发送 40ms 数据 = 320采样点 * 2字节 = 640字节
                final byte[] pcmData = generateSineWave(8000, 440, 320);

                audioTimer.scheduleAtFixedRate(new TimerTask() {
                    @Override
                    public void run() {
                        if (isOpen()) {
                            try {
                                // 1. Base64 编码
                                String base64Audio = Base64.getEncoder().encodeToString(pcmData);

                                // 2. 封装 JSON (完全模仿 Vue 代码结构)
                                JSONObject audioMsg = new JSONObject();
                                audioMsg.put("type", "audio_data");
                                audioMsg.put("stream_id", SIM);
                                audioMsg.put("audio_data", base64Audio);
                                audioMsg.put("format", "pcm16");
                                audioMsg.put("sample_rate", 8000);
                                audioMsg.put("channels", 1);

                                send(audioMsg.toJSONString());
                                // System.out.print("."); // 打印点表示正在发送
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }, 0, 40); // 每40ms发送一次
                System.out.println(">> 开始持续发送音频流 (标准正弦波)...");
            }
        };

        client.connect();
    }

    /**
     * 生成 PCM16 正弦波音频数据 (标准的“滴”声)
     * @param sampleRate 采样率 (8000)
     * @param frequency 频率 (440Hz = 标准音A)
     * @param samples 采样点数量
     */
    private static byte[] generateSineWave(int sampleRate, int frequency, int samples) {
        ByteBuffer buffer = ByteBuffer.allocate(samples * 2);
        buffer.order(ByteOrder.LITTLE_ENDIAN); // 对应前端 view.setInt16(..., true)

        double period = (double) sampleRate / frequency;
        for (int i = 0; i < samples; i++) {
            double angle = 2.0 * Math.PI * i / period;
            // 生成正弦波,振幅 0x4000 (稍微大一点声音)
            short sample = (short) (Math.sin(angle) * 0x4000);
            buffer.putShort(sample);
        }
        return buffer.array();
    }
}