diff --git a/pom.xml b/pom.xml index 064243a8..ac4ace0a 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,16 @@ tinypinyin 2.0.3 - + + org + jaudiotagger + 2.0.3 + + + com.hynnet + jacob + 1.18 + org.springframework.boot @@ -143,11 +152,11 @@ 3.12.5 - - - - - + + org.bytedeco + javacv-platform + 1.5.6 + com.aliyun diff --git a/src/main/java/com/peanut/common/utils/BaiduVoicesUtils.java b/src/main/java/com/peanut/common/utils/BaiduVoicesUtils.java index a4ff33f9..00617b5d 100644 --- a/src/main/java/com/peanut/common/utils/BaiduVoicesUtils.java +++ b/src/main/java/com/peanut/common/utils/BaiduVoicesUtils.java @@ -5,24 +5,14 @@ import java.io.*; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.*; - import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.aliyun.oss.OSS; import com.aliyun.oss.OSSClientBuilder; -import com.jacob.activeX.ActiveXComponent; -import com.jacob.com.Dispatch; -import com.jacob.com.Variant; import org.apache.commons.lang.StringUtils; -import org.bytedeco.javacv.FFmpegFrameGrabber; -import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.audio.mp3.MP3AudioHeader; import org.jaudiotagger.audio.mp3.MP3File; -import org.jaudiotagger.tag.FieldDataInvalidException; -import org.jaudiotagger.tag.TagException; import org.joda.time.DateTime; import org.springframework.util.FileCopyUtils; @@ -46,15 +36,7 @@ public class BaiduVoicesUtils { // System.out.println("aaa: " + aaa); String text = "天地之道者 ,理也 ; 万物之始者 , 气也 ; 气之聚散者 , 象也; 物之终始者 ,数也 。为 医者尤需明理 ,理不明则法不清 , 法不清则 方药无凭 。 医之理者 ,病理也 , 然欲明病理 ,先知生理 ,故太湖无 中医基础理论课程而有生理 、病理 。生之理者 ,《 内经》 所谓人事 也 ,至于天人之学 , 尚需于太湖国学院求之 。经谓精光之论 , 大圣"; // shortText("补充一点,桂枝的主要有效成分是挥发油,大概占桂枝 重量的0.7%左右,不到1%。桂枝挥发油有一个特点,它由呼 吸道排出,对呼吸道炎症有明显的抗炎、祛痰、止咳作用,所 以在麻黄汤里,桂枝既增强了麻黄的发汗作用,又增强了杏仁 的化痰止咳平喘作用。"); - JacobUtil.text("F:\\a.mp3",text,100,0); -// List urls = new ArrayList<>(); -// urls.add("https://ehh-private-01.oss-cn-beijing.aliyuncs.com/2024/12/09/f56a35760be042debd177dbaac5e4e2e.mp3"); -// urls.add("https://ehh-private-01.oss-cn-beijing.aliyuncs.com/2024/12/09/dc5ffa920a91485982bac19f788ce33b.mp3"); -// urls.add("https://ehh-private-01.oss-cn-beijing.aliyuncs.com/2024/12/09/dc5ffa920a91485982bac19f788ce33b.mp3"); -// urls.add("https://ehh-private-01.oss-cn-beijing.aliyuncs.com/2024/12/09/dc5ffa920a91485982bac19f788ce33b.mp3"); -// File file = File.createTempFile(UUID.randomUUID().toString(),".mp3",new File("..\\")); -// mergeVoices(urls); - +// System.out.println(JacobUtil.toVoice(text)); // MP3File file = new MP3File("F:/a.mp3"); // MP3AudioHeader audioHeader = (MP3AudioHeader)file.getAudioHeader(); diff --git a/src/main/java/com/peanut/common/utils/JacobUtil.java b/src/main/java/com/peanut/common/utils/JacobUtil.java new file mode 100644 index 00000000..12361186 --- /dev/null +++ b/src/main/java/com/peanut/common/utils/JacobUtil.java @@ -0,0 +1,140 @@ +package com.peanut.common.utils; + +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.jacob.activeX.ActiveXComponent; +import com.jacob.com.Dispatch; +import com.jacob.com.Variant; +import net.sourceforge.lame.lowlevel.LameEncoder; +import net.sourceforge.lame.mp3.Lame; +import net.sourceforge.lame.mp3.MPEGMode; +import org.apache.commons.lang.StringUtils; +import org.jaudiotagger.audio.mp3.MP3AudioHeader; +import org.jaudiotagger.audio.mp3.MP3File; +import org.joda.time.DateTime; +import org.springframework.util.FileCopyUtils; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import java.io.*; +import java.math.BigDecimal; +import java.util.UUID; + +// +// com.hynnet +// jacob +// 1.18 +// + +//文字转语音离线工具,windows平台 +public class JacobUtil { + + public static String toVoice(String text) { + try { + File wavFile = File.createTempFile(UUID.randomUUID().toString(),".wav",new File("..\\")); + text(wavFile.getAbsolutePath(),text,100,0); + byte[] bytes = encodeToMp3(wavFile); + File file = File.createTempFile(UUID.randomUUID().toString(),".mp3",new File("..\\")); + FileCopyUtils.copy(bytes,file); + + String fileUrl = uploadFile(file,".mp3"); + if (StringUtils.isNotBlank(fileUrl)){ + MP3File mp3File = new MP3File(file); + MP3AudioHeader audioHeader = (MP3AudioHeader)mp3File.getAudioHeader(); + double intLen = audioHeader.getPreciseTrackLength(); + BigDecimal bd = new BigDecimal(intLen); + bd = bd.setScale(4, BigDecimal.ROUND_HALF_UP); + file.delete(); + fileUrl+=","+bd.toString(); + } + file.delete(); + return fileUrl; + }catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + public static byte[] encodeToMp3(File wavFile) throws Exception { + InputStream wavTestFileInputStream = new BufferedInputStream(new FileInputStream(wavFile)); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(wavTestFileInputStream); + // LameEncoder encoder = new LameEncoder(audioInputStream.getFormat(), 256, MPEGMode.STEREO, Lame.QUALITY_HIGHEST, false); + LameEncoder encoder = new LameEncoder(audioInputStream.getFormat(), 128, MPEGMode.STEREO, Lame.QUALITY_HIGHEST, false); + ByteArrayOutputStream mp3 = new ByteArrayOutputStream(); + byte[] inputBuffer = new byte[encoder.getPCMBufferSize()]; + byte[] outputBuffer = new byte[encoder.getPCMBufferSize()]; + int bytesRead; + int bytesWritten; + while(0 < (bytesRead = audioInputStream.read(inputBuffer))) { + bytesWritten = encoder.encodeBuffer(inputBuffer, 0, bytesRead, outputBuffer); + mp3.write(outputBuffer, 0, bytesWritten); + } + encoder.close(); + return mp3.toByteArray(); + } + public static String uploadFile(File file, String fileName) { + String endpoint = ConstantPropertiesUtils.END_POIND; + String accessKeyId = ConstantPropertiesUtils.ACCESS_KEY_ID; + String accessKeySecret = ConstantPropertiesUtils.ACCESS_KEY_SECRET; + String bucketName = ConstantPropertiesUtils.BUCKET_NAME; + try { + OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); + String uuid = UUID.randomUUID().toString().replaceAll("-", ""); + fileName = uuid + fileName; + String datePath = new DateTime().toString("yyyy/MM/dd"); + fileName = datePath + "/" + fileName; + ossClient.putObject(bucketName, fileName, file); + ossClient.shutdown(); + String url = "https://" + bucketName + "." + endpoint + "/" + fileName; + return url; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + public static boolean text(String path, String text, int volume, int speed) { + try { + // 调用dll朗读方法 + ActiveXComponent ax = new ActiveXComponent("Sapi.SpVoice"); + // 输入的语言内容 + Dispatch dispatch = ax.getObject(); + + //开始生成语音文件,构建文件流 + ax = new ActiveXComponent("Sapi.SpFileStream"); + Dispatch sfFileStream = ax.getObject(); + //设置文件生成格式 + ax = new ActiveXComponent("Sapi.SpAudioFormat"); + Dispatch fileFormat = ax.getObject(); + + // 设置音频流格式 + Dispatch.put(fileFormat, "Type", new Variant(22)); + // 设置文件输出流格式 + Dispatch.putRef(sfFileStream, "Format", fileFormat); + // 调用输出文件流打开方法,创建一个音频文件 + Dispatch.call(sfFileStream, "Open", new Variant(path), new Variant(3), new Variant(true)); + // 设置声音对应输出流为输出文件对象 + Dispatch.putRef(dispatch, "AudioOutputStream", sfFileStream); + // 设置音量 0 - 100 + Dispatch.put(dispatch, "Volume", new Variant(volume)); + // 设置速度 语音朗读速度 -10 到 +10 + Dispatch.put(dispatch, "Rate", new Variant(speed)); + // 执行朗读 + Dispatch.call(dispatch, "Speak", new Variant(text)); + // 关闭输出文件 + Dispatch.call(sfFileStream, "Close"); + Dispatch.putRef(dispatch, "AudioOutputStream", null); + + // 关闭资源 + sfFileStream.safeRelease(); + fileFormat.safeRelease(); + // 关闭朗读的操作 + dispatch.safeRelease(); + ax.safeRelease(); + return true; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + +} diff --git a/src/main/java/com/peanut/modules/book/controller/BookChapterContentController.java b/src/main/java/com/peanut/modules/book/controller/BookChapterContentController.java index 6f27b3f7..d29bd253 100644 --- a/src/main/java/com/peanut/modules/book/controller/BookChapterContentController.java +++ b/src/main/java/com/peanut/modules/book/controller/BookChapterContentController.java @@ -10,8 +10,7 @@ import java.util.concurrent.Executors; import com.alibaba.druid.util.StringUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.peanut.common.utils.BaiduVoicesUtils; -import com.peanut.common.utils.ConnUtil; +import com.peanut.common.utils.*; import com.peanut.modules.book.service.*; import com.peanut.modules.common.entity.*; import com.peanut.modules.oss.service.OssService; @@ -29,8 +28,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.peanut.common.utils.PageUtils; -import com.peanut.common.utils.R; import org.springframework.web.multipart.MultipartFile; @@ -63,7 +60,8 @@ public class BookChapterContentController { @RequestMapping("/contentToVoices") public R contentToVoices(@RequestBody Map params){ //调用百度语音合成 API - String voices = BaiduVoicesUtils.shortText(params.get("content").toString()); +// String voices = BaiduVoicesUtils.shortText(params.get("content").toString()); + String voices = JacobUtil.toVoice(params.get("content").toString()); if (StringUtils.isEmpty(voices)) { return R.error("语音上传失败"); } @@ -83,7 +81,8 @@ public class BookChapterContentController { public void run() { try { //调用百度语音合成 API - String voices = BaiduVoicesUtils.shortText(bcc.getContent()); +// String voices = BaiduVoicesUtils.shortText(bcc.getContent()); + String voices = JacobUtil.toVoice(bcc.getContent()); if (voices.contains(",")){ bcc.setVoices(voices.split(",")[0]); bcc.setVoicesSize(voices.split(",")[1]); diff --git a/src/main/resources/lib/net.sourceforge.lame-3.98.4.jar b/src/main/resources/lib/net.sourceforge.lame-3.98.4.jar new file mode 100644 index 00000000..e7e8257d Binary files /dev/null and b/src/main/resources/lib/net.sourceforge.lame-3.98.4.jar differ