From c03bea4c6b41c949187b1052578cc77bfb49b702 Mon Sep 17 00:00:00 2001 From: huliyong <2783385703@qq.com> Date: Sun, 21 Sep 2025 19:38:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=9F=B3=E9=A2=91=E5=A4=84=E7=90=86):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=9F=B3=E9=A2=91=E6=97=B6=E9=95=BF=E7=B2=BE?= =?UTF-8?q?=E7=A1=AE=E8=AE=A1=E7=AE=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 引入AudioDurationUtil工具类,通过解析音频数据获取真实时长,替代原有的文本估算方法。当解析失败时仍保留文本估算作为备选方案,提高时长计算的准确性。 --- .../service/impl/AppletApiTTServiceImpl.java | 18 ++- .../modules/applet/util/AudioDurationUtil.java | 140 +++++++++++++++++++++ 2 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/AudioDurationUtil.java diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java index de6ed97..f2c6790 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java @@ -18,6 +18,7 @@ import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre; import org.jeecg.modules.demo.appletTtsTimbre.service.IAppletTtsTimbreService; import org.jeecg.modules.demo.appletTtsCache.entity.AppletTtsCache; import org.jeecg.modules.demo.appletTtsCache.service.IAppletTtsCacheService; +import org.jeecg.modules.applet.util.AudioDurationUtil; import java.io.ByteArrayInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -153,11 +154,18 @@ public class AppletApiTTServiceImpl implements AppletApiTTService { cache.setSuccess("Y"); cache.setCreateTime(new java.util.Date()); - // 计算音频时长(简单估算,实际可以通过音频文件解析获得) - if (text != null) { - // 按照平均语速估算时长(字符数 / 5 秒) - double estimatedDuration = text.length() / 5.0; - cache.setDuration(estimatedDuration); + // 计算音频时长(通过音频文件解析获得真实时长) + Double realDuration = AudioDurationUtil.calculateDuration(audioData); + if (realDuration != null) { + cache.setDuration(realDuration); + log.info("音频真实时长计算成功: {}秒", realDuration); + } else { + // 如果真实时长计算失败,使用文本长度估算作为备选方案 + if (text != null) { + double estimatedDuration = text.length() / 5.0; + cache.setDuration(estimatedDuration); + log.warn("音频真实时长计算失败,使用文本长度估算: {}秒", estimatedDuration); + } } appletTtsCacheService.save(cache); diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/AudioDurationUtil.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/AudioDurationUtil.java new file mode 100644 index 0000000..e31a224 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/util/AudioDurationUtil.java @@ -0,0 +1,140 @@ +package org.jeecg.modules.applet.util; + +import lombok.extern.log4j.Log4j2; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * 音频时长计算工具类 + * + * @author jeecg-boot + * @date 2025-01-22 + */ +@Log4j2 +public class AudioDurationUtil { + + /** + * 计算音频数据的时长(秒) + * + * @param audioData 音频字节数据 + * @return 音频时长(秒),如果计算失败返回null + */ + public static Double calculateDuration(byte[] audioData) { + if (audioData == null || audioData.length == 0) { + log.warn("音频数据为空,无法计算时长"); + return null; + } + + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(audioData); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(byteArrayInputStream)) { + + AudioFormat format = audioInputStream.getFormat(); + long frames = audioInputStream.getFrameLength(); + + if (frames == AudioSystem.NOT_SPECIFIED) { + log.warn("无法获取音频帧数,尝试通过数据大小估算"); + return calculateDurationByDataSize(audioData, format); + } + + double durationInSeconds = (frames + 0.0) / format.getFrameRate(); + log.debug("音频时长计算成功: {}秒, 帧数: {}, 帧率: {}", durationInSeconds, frames, format.getFrameRate()); + + return durationInSeconds; + + } catch (UnsupportedAudioFileException e) { + log.warn("不支持的音频格式,尝试通过采样率估算时长: {}", e.getMessage()); + return estimateDurationBySampleRate(audioData); + } catch (IOException e) { + log.error("读取音频数据时发生IO异常: {}", e.getMessage(), e); + return null; + } catch (Exception e) { + log.error("计算音频时长时发生未知异常: {}", e.getMessage(), e); + return null; + } + } + + /** + * 通过音频数据大小和格式估算时长 + * + * @param audioData 音频数据 + * @param format 音频格式 + * @return 估算的时长(秒) + */ + private static Double calculateDurationByDataSize(byte[] audioData, AudioFormat format) { + try { + int frameSize = format.getFrameSize(); + float frameRate = format.getFrameRate(); + + if (frameSize <= 0 || frameRate <= 0) { + log.warn("音频格式信息不完整,frameSize: {}, frameRate: {}", frameSize, frameRate); + return null; + } + + long estimatedFrames = audioData.length / frameSize; + double duration = estimatedFrames / frameRate; + + log.debug("通过数据大小估算音频时长: {}秒", duration); + return duration; + + } catch (Exception e) { + log.error("通过数据大小估算时长失败: {}", e.getMessage(), e); + return null; + } + } + + /** + * 通过标准采样率估算时长(适用于WAV格式) + * + * @param audioData 音频数据 + * @return 估算的时长(秒) + */ + private static Double estimateDurationBySampleRate(byte[] audioData) { + try { + // WAV文件的标准参数:16位深度,单声道,16000Hz采样率 + int bitsPerSample = 16; + int channels = 1; + int sampleRate = 16000; + + // 跳过WAV文件头(通常44字节) + int headerSize = 44; + int audioDataSize = Math.max(0, audioData.length - headerSize); + + // 计算样本数 + int bytesPerSample = bitsPerSample / 8; + long totalSamples = audioDataSize / (bytesPerSample * channels); + + // 计算时长 + double duration = (double) totalSamples / sampleRate; + + log.debug("通过采样率估算音频时长: {}秒 (数据大小: {}字节)", duration, audioDataSize); + return duration; + + } catch (Exception e) { + log.error("通过采样率估算时长失败: {}", e.getMessage(), e); + return null; + } + } + + /** + * 格式化时长显示 + * + * @param durationSeconds 时长(秒) + * @return 格式化的时长字符串 (mm:ss) + */ + public static String formatDuration(Double durationSeconds) { + if (durationSeconds == null || durationSeconds < 0) { + return "00:00"; + } + + int totalSeconds = (int) Math.round(durationSeconds); + int minutes = totalSeconds / 60; + int seconds = totalSeconds % 60; + + return String.format("%02d:%02d", minutes, seconds); + } +} \ No newline at end of file