From b900d854a8021cad9a2c88a850dc49b81d42b411 Mon Sep 17 00:00:00 2001 From: stream Date: Tue, 30 Sep 2025 15:35:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=AD=E9=9F=B3=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/support/FlowNodeParamPreparer.java | 66 +++++++++++---- .../operator/llm/LLMTTSOperateService.java | 3 +- .../operator/vi/ViPlayOperateService.java | 84 +++++++++++++++---- 3 files changed, 120 insertions(+), 33 deletions(-) diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/support/FlowNodeParamPreparer.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/support/FlowNodeParamPreparer.java index 3949605..069de0b 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/support/FlowNodeParamPreparer.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/engine/support/FlowNodeParamPreparer.java @@ -92,22 +92,20 @@ public class FlowNodeParamPreparer { Object loopNumVal = input.get("loopNum"); int loopCount = 0; - if (loopNumVal instanceof Number) { - loopCount = ((Number) loopNumVal).intValue(); - } else if (loopNumVal instanceof JSONArray) { - // 支持 JSON 数组 - loopCount = ((JSONArray) loopNumVal).size(); - } - else if (loopNumVal instanceof Collection) { - // 支持所有 Java Collection - loopCount = ((Collection) loopNumVal).size(); - } - else if (loopNumVal != null && loopNumVal.getClass().isArray()) { - // 支持 Java 数组 - loopCount = Array.getLength(loopNumVal); - } else if (loopNumVal != null) { + // 根据迭代路径找到当前层的集合对象 + Object target = resolveLoopTarget(loopNumVal, rootMessage.getIterations(), 0); + + if (target instanceof JSONArray) { + loopCount = ((JSONArray) target).size(); + } else if (target instanceof Collection) { + loopCount = ((Collection) target).size(); + } else if (target != null && target.getClass().isArray()) { + loopCount = Array.getLength(target); + } else if (target instanceof Number) { + loopCount = ((Number) target).intValue(); + } else if (target != null) { try { - loopCount = Integer.parseInt(loopNumVal.toString()); + loopCount = Integer.parseInt(target.toString()); } catch (NumberFormatException e) { throw new GlobalException("loopNum 参数格式错误: " + loopNumVal); } @@ -165,4 +163,42 @@ public class FlowNodeParamPreparer { } return curr; } + + /** + * 根据迭代路径(iterations)逐层解析嵌套集合 + * 例子: + * - iterations = [] → 返回最外层对象 + * - iterations = [2] → 返回 list[1] + * - iterations = [2,3] → 返回 list[1][2] + */ + private Object resolveLoopTarget(Object obj, List iterations, int depth) { + if (obj == null) return null; + + // 已经走到当前循环层,返回目标 + if (depth >= iterations.size()) { + return obj; + } + + int idx = iterations.get(depth) - 1; + + if (obj instanceof List) { + List list = (List) obj; + if (idx >= 0 && idx < list.size()) { + return resolveLoopTarget(list.get(idx), iterations, depth + 1); + } + } else if (obj instanceof JSONArray) { + JSONArray arr = (JSONArray) obj; + if (idx >= 0 && idx < arr.size()) { + return resolveLoopTarget(arr.get(idx), iterations, depth + 1); + } + } else if (obj != null && obj.getClass().isArray()) { + int length = Array.getLength(obj); + if (idx >= 0 && idx < length) { + return resolveLoopTarget(Array.get(obj, idx), iterations, depth + 1); + } + } + + // 如果不是集合类型,直接返回 + return obj; + } } diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTTSOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTTSOperateService.java index 5ac5978..b251430 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTTSOperateService.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/llm/LLMTTSOperateService.java @@ -131,7 +131,7 @@ public class LLMTTSOperateService implements LLMOperateService { Map body = new HashMap<>(); body.put("text", text); body.put("dialect", "普通话"); - body.put("timbre", "标准男"); + body.put("timbre", "员工女"); body.put("language", "中文"); body.put("tone", "中性"); @@ -219,5 +219,4 @@ public class LLMTTSOperateService implements LLMOperateService { private final long generateMs; private final CountDownLatch latch; // 仅最后一段有 } - } \ No newline at end of file diff --git a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/vi/ViPlayOperateService.java b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/vi/ViPlayOperateService.java index 7fd33bb..c6a387e 100644 --- a/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/vi/ViPlayOperateService.java +++ b/cmvr-iot-test/src/main/java/com/cmvr/test/flow/runtime/operator/vi/ViPlayOperateService.java @@ -7,11 +7,17 @@ import com.cmvr.edge.client.service.EdgeSpeakerService; import com.cmvr.test.enums.ActionEnum; import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage; import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult; -import com.cmvr.test.flow.runtime.operator.edge.EdgeOperateService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; +import java.io.File; + @Slf4j @Service @RequiredArgsConstructor @@ -34,21 +40,67 @@ public class ViPlayOperateService implements VIOperateService { String deviceId = inputParams.getString("deviceId"); String audioPath = inputParams.getString("audioPath"); String terminalId = message.getTerminalId(); - log.info("播放语料成功,deviceId={},audioPath={},terminalId={}", deviceId, audioPath, terminalId); -// edgeSpeakerService.playAudio(terminalId, deviceId, audioPath); -// // 等待播放完成 -// while (true) { -// try { -// Thread.sleep(1000); -// } catch (InterruptedException e) { -// throw new RuntimeException(e); -// } -// SpeakerCommand.GetSpeakerStateCommand.Feedback status = edgeSpeakerService.getStatus(terminalId, deviceId); -// boolean isRunning = status.getState().getIsRunning(); -// if (!isRunning) { -// break; -// } -// } +// log.info("播放语料成功,deviceId={},audioPath={},terminalId={}", deviceId, audioPath, terminalId); + edgeSpeakerService.playAudio(terminalId, deviceId, audioPath); + // 等待播放完成 + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + SpeakerCommand.GetSpeakerStateCommand.Feedback status = edgeSpeakerService.getStatus(terminalId, deviceId); + boolean isRunning = status.getState().getIsRunning(); + if (!isRunning) { + break; + } + } + + // todo 本机测试播放 +// playAudio(audioPath); return TaskNodeExecuteResult.success(); } + + + /** + * 播放一个音频文件(阻塞直到播完) + */ + private void playAudio(String filePath) { + try (AudioInputStream sourceStream = AudioSystem.getAudioInputStream(new File(filePath))) { + AudioFormat baseFormat = sourceStream.getFormat(); + AudioFormat targetFormat = new AudioFormat( + AudioFormat.Encoding.PCM_SIGNED, + baseFormat.getSampleRate(), + 16, + baseFormat.getChannels(), + baseFormat.getChannels() * 2, + baseFormat.getSampleRate(), + false + ); + + try (AudioInputStream pcmStream = AudioSystem.getAudioInputStream(targetFormat, sourceStream)) { + DataLine.Info info = new DataLine.Info(SourceDataLine.class, targetFormat); + try (SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) { + line.open(targetFormat); + line.start(); + + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = pcmStream.read(buffer, 0, buffer.length)) != -1) { + line.write(buffer, 0, bytesRead); + } + + // 确保缓冲区播完 + line.drain(); + line.stop(); + } + } + + log.info("播放完成: {}", filePath); + + } catch (Exception e) { + throw new GlobalException("播放失败: ", e); + } + } + }