转音频

This commit is contained in:
wuchunlei
2024-12-11 09:23:30 +08:00
parent 5e232e932a
commit 00ec90334e
4 changed files with 292 additions and 59 deletions

View File

@@ -2,21 +2,101 @@ package com.peanut.common.utils;
import java.io.*; import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Arrays; import java.nio.file.Files;
import java.util.UUID; import java.nio.file.StandardCopyOption;
import java.util.*;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS; import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder; 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.joda.time.DateTime;
import org.springframework.util.FileCopyUtils;
public class BaiduVoicesUtils { public class BaiduVoicesUtils {
// 填写申请百度语音申请的appkey 申请地址百度AI开放平台 // 填写申请百度语音申请的appkey 申请地址百度AI开放平台
private final static String appKey = "WOKN473V1o1gL8WMsAkfJZIY"; private final static String appKey = "WOKN473V1o1gL8WMsAkfJZIY";
private final static String secretKey = "CpgbKdB9aZu2esnq5lMdqRZG37Jn3a76"; private final static String secretKey = "CpgbKdB9aZu2esnq5lMdqRZG37Jn3a76";
//http://aipe-speech.bj.bcebos.com/text_to_speech/2024-12-06/6752c65ee615a10001435c06/speech/0.null?authorization=bce-auth-v1%2FALTAKjI91nE52nvtDNRgFlUCVz%2F2024-12-06T09%3A41%3A51Z%2F259200%2F%2F08aed96bc0e5808a83c1a80ee3a925866a2a7ac5b017e29670fe7951f713bbf6
public static void main(String[] arg){
try {
System.out.println(new Date().getTime());
// File audioFile = new File("F:\\111.wav");// 创建一个文件对象
// AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(audioFile);// 获音频文件格式
// float sampleRate = fileFormat.getFormat().getSampleRate();
// int samplesizeInBits = fileFormat.getFormat().getSampleSizeInBits();
// int aaa = fileFormat.getByteLength();
// System.out.println("sampleRate: " + sampleRate);
// System.out.println("samplesizeInBits: " + samplesizeInBits);
// System.out.println("aaa: " + aaa);
String text = "天地之道者 ,理也 万物之始者 气也 气之聚散者 象也; 物之终始者 ,数也 。为 医者尤需明理 ,理不明则法不清 法不清则 方药无凭 。 医之理者 ,病理也 然欲明病理 ,先知生理 ,故太湖无 中医基础理论课程而有生理 、病理 。生之理者 ,《 内经》 所谓人事 也 ,至于天人之学 尚需于太湖国学院求之 。经谓精光之论 大圣";
// shortText("补充一点,桂枝的主要有效成分是挥发油,大概占桂枝 重量的0.7%左右不到1%。桂枝挥发油有一个特点,它由呼 吸道排出,对呼吸道炎症有明显的抗炎、祛痰、止咳作用,所 以在麻黄汤里,桂枝既增强了麻黄的发汗作用,又增强了杏仁 的化痰止咳平喘作用。");
JacobUtil.text("F:\\a.mp3",text,100,0);
// List<String> 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);
// MP3File file = new MP3File("F:/a.mp3");
// MP3AudioHeader audioHeader = (MP3AudioHeader)file.getAudioHeader();
// int intLen = audioHeader.getTrackLength();
// System.out.println(intLen);
System.out.println(new Date().getTime());
}catch (Exception e) {
e.printStackTrace();
}
}
public static String mergeVoices(List<String> urls){
try {
File outFile = File.createTempFile(UUID.randomUUID().toString(),".mp3",new File("..\\"));
OutputStream os = new FileOutputStream(outFile) ;
for (String url : urls) {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
InputStream is = conn.getInputStream();
byte[] b = new byte[128];
int len = 0;
int index = 0;
while((len = is.read(b))!=-1){
index++;
if(index==1){
continue ;
}
os.write(b, 0, len) ;
}
is.close();
}
String fileUrl = uploadFile(new FileInputStream(outFile),".mp3");
outFile.delete();
os.flush();
os.close() ;
return fileUrl;
}catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static String shortText(String content){ public static String shortText(String content){
@@ -34,7 +114,19 @@ public class BaiduVoicesUtils {
String contentType = conn.getContentType(); String contentType = conn.getContentType();
if (contentType.contains("mp3")) { if (contentType.contains("mp3")) {
byte[] bytes = ConnUtil.getResponseBytes(conn); byte[] bytes = ConnUtil.getResponseBytes(conn);
String fileUrl = uploadFile(new ByteArrayInputStream(bytes),UUID.randomUUID().toString()+".mp3"); String fileUrl = uploadFile(new ByteArrayInputStream(bytes),".mp3");
if (StringUtils.isNotBlank(fileUrl)){
//获取音频时长
File file = File.createTempFile(UUID.randomUUID().toString(),".mp3",new File("..\\"));
FileCopyUtils.copy(bytes,file);
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();
}
return fileUrl; return fileUrl;
} else { } else {
System.err.println("ERROR: content-type= " + contentType); System.err.println("ERROR: content-type= " + contentType);
@@ -48,52 +140,67 @@ public class BaiduVoicesUtils {
} }
} }
//长文字查询 //长文字查询
public void queryVoice(String id) throws Exception { public static String queryVoice(String id){
URL url = new URL("https://aip.baidubce.com/rpc/2.0/tts/v1/query?access_token=" + getAccessToken()); try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); URL url = new URL("https://aip.baidubce.com/rpc/2.0/tts/v1/query?access_token=" + getAccessToken());
conn.setRequestMethod("POST"); HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept", "application/json"); conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/json");
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); conn.setDoOutput(true);
JSONObject jsonObject = new JSONObject(); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
jsonObject.put("task_ids", Arrays.asList(id)); JSONObject jsonObject = new JSONObject();
writer.write(jsonObject.toString()); jsonObject.put("task_ids", Arrays.asList(id));
writer.flush(); writer.write(jsonObject.toString());
writer.close(); writer.flush();
String result = ConnUtil.getResponseString(conn); writer.close();
JSONArray tasksInfo = (JSONArray)(JSONObject.parseObject(result).get("tasks_info")); String result = ConnUtil.getResponseString(conn);
JSONObject i = (JSONObject)tasksInfo.get(0); JSONArray tasksInfo = (JSONArray)(JSONObject.parseObject(result).get("tasks_info"));
String taskStatus = i.get("task_status").toString(); JSONObject info = (JSONObject)tasksInfo.get(0);
if ("Running".equals(taskStatus)){ String taskStatus = info.get("task_status").toString();
queryVoice(id); if ("Success".equals(taskStatus)){
JSONObject taskResult = (JSONObject)info.get("task_result");
String speechUrl = taskResult.get("speech_url").toString();
URL urll = new URL(speechUrl);
HttpURLConnection connn = (HttpURLConnection) urll.openConnection();
String fileUrl = uploadFile(connn.getInputStream(),UUID.randomUUID().toString()+".mp3");
return fileUrl;
}
return "";
}catch (Exception e) {
e.printStackTrace();
return "";
} }
System.out.println("taskId:" + result);
} }
//长文字转换 //长文字转换
public String excute(String content) throws Exception { public static String longText(String content) {
URL url = new URL("https://aip.baidubce.com/rpc/2.0/tts/v1/create?access_token=" + getAccessToken()); try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); URL url = new URL("https://aip.baidubce.com/rpc/2.0/tts/v1/create?access_token=" + getAccessToken());
conn.setRequestMethod("POST"); HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept", "application/json"); conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/json");
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); conn.setDoOutput(true);
JSONObject jsonObject = new JSONObject(); OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
jsonObject.put("text",content); JSONObject jsonObject = new JSONObject();
jsonObject.put("voice",1);//基础音库:度小宇=1度小美=0度逍遥基础=3度丫丫=4精品音库度逍遥精品=5003度小鹿=5118度博文=106度小童=110度小萌=111度米朵=103度小娇=5。默认为度小美 jsonObject.put("text",content);
jsonObject.put("lang", "zh");//固定值zh。语言选择,目前只有中英文混合模式填写固定值zh jsonObject.put("voice",5003);//基础音库:度小宇=1度小美=0度逍遥基础=3度丫丫=4精品音库度逍遥精品=5003度小鹿=5118度博文=106度小童=110度小萌=111度米朵=103度小娇=5。默认为度小美
jsonObject.put("speed", 3);//取值0-15默认为5中语速 jsonObject.put("lang", "zh");//固定值zh。语言选择,目前只有中英文混合模式填写固定值zh
jsonObject.put("pitch", 5);//取值0-15默认为5中语 jsonObject.put("speed", 4);//取值0-15默认为5中语
jsonObject.put("volume", 8);//取值0-15默认为5中音量取值为0时为音量最小值并非为无声 jsonObject.put("pitch", 5);//取值0-15默认为5中语调
jsonObject.put("enable_subtitle", 1);//取值范围0, 1, 2默认为0。0表示不开启字幕1表示开启句级别字幕2表示开启词级别字幕 jsonObject.put("volume", 8);//取值0-15默认为5中音量取值为0时为音量最小值并非为无声
writer.write(jsonObject.toString()); jsonObject.put("enable_subtitle", 1);//取值范围0, 1, 2默认为0。0表示不开启字幕1表示开启句级别字幕2表示开启词级别字幕
writer.flush(); writer.write(jsonObject.toString());
writer.close(); writer.flush();
String result = ConnUtil.getResponseString(conn); writer.close();
String taskId = JSONObject.parseObject(result).get("task_id").toString(); String result = ConnUtil.getResponseString(conn);
System.out.println("taskId result json:" + taskId); String taskId = JSONObject.parseObject(result).get("task_id").toString();
return taskId; System.out.println("taskId:" + taskId);
return taskId;
}catch (Exception e) {
e.printStackTrace();
return "";
}
} }
public static String getAccessToken() throws Exception { public static String getAccessToken() throws Exception {

View File

@@ -1,7 +1,8 @@
package com.peanut.modules.book.controller; package com.peanut.modules.book.controller;
import java.io.File; import java.io.*;
import java.io.FileInputStream; import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -10,14 +11,18 @@ import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.peanut.common.utils.BaiduVoicesUtils; import com.peanut.common.utils.BaiduVoicesUtils;
import com.peanut.common.utils.ConnUtil;
import com.peanut.modules.book.service.*; import com.peanut.modules.book.service.*;
import com.peanut.modules.common.entity.*; import com.peanut.modules.common.entity.*;
import com.peanut.modules.oss.service.OssService; import com.peanut.modules.oss.service.OssService;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@@ -65,24 +70,124 @@ public class BookChapterContentController {
return R.ok().put("voices", voices); return R.ok().put("voices", voices);
} }
//书籍全部章节转成音频 //按一句话转成音频
@RequestMapping("/bookToVoices") @RequestMapping("/bookToShortVoices")
public R bookToVoices(@RequestBody Map<String,Object> params){ public R bookToShortVoices(@RequestBody Map<String,Object> params){
// 1、创建服务创建线程池
ExecutorService service = Executors.newFixedThreadPool(5); ExecutorService service = Executors.newFixedThreadPool(5);
List<BookChapterContentEntity> list = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>() List<BookChapterContentEntity> list = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookId,params.get("bookId"))); .eq(BookChapterContentEntity::getBookId,params.get("bookId")));
for (BookChapterContentEntity bcc:list){ for (BookChapterContentEntity bcc:list){
if (!bcc.getContent().contains("https")&&!"".equals(bcc.getContent())){ if (!bcc.getContent().contains("https")&&!"".equals(bcc.getContent())){
service.execute(new Runnable() { service.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
try {
//调用百度语音合成 API //调用百度语音合成 API
String voices = BaiduVoicesUtils.shortText(bcc.getContent()); String voices = BaiduVoicesUtils.shortText(bcc.getContent());
bcc.setVoices(voices); if (voices.contains(",")){
bcc.setVoices(voices.split(",")[0]);
bcc.setVoicesSize(voices.split(",")[1]);
}else {
bcc.setVoices(voices);
}
bookChapterContentService.updateById(bcc); bookChapterContentService.updateById(bcc);
}catch (Exception e) {
e.printStackTrace();
} }
}); }
});
}
}
return R.ok();
}
//章节下所有音频合并成一个音频
@RequestMapping("/mergeVoices")
public R mergeVoices(@RequestBody Map<String,Object> params){
List<BookChapterContentEntity> bccs = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookChatperId,params.get("bookChapterId"))
.orderByAsc(BookChapterContentEntity::getNumber));
List<String> list = new ArrayList<>();
for (BookChapterContentEntity bcc : bccs) {
if (!StringUtils.isEmpty(bcc.getVoices())){
list.add(bcc.getVoices());
}
}
String url = BaiduVoicesUtils.mergeVoices(list);
if (!StringUtils.isEmpty(url)){
BookChapterEntity bc = bookChapterService.getById(params.get("bookChapterId").toString());
bc.setVoices(url);
bookChapterService.updateById(bc);
return R.ok();
}else {
return R.error();
}
}
//所有章节合并音频
@RequestMapping("/mergeVoicesByBookId")
@Transactional
public R mergeVoicesByBookId(@RequestBody Map<String,Object> params){
StringBuilder sb = new StringBuilder();
List<BookChapterEntity> bcs = bookChapterService.list(new LambdaQueryWrapper<BookChapterEntity>()
.eq(BookChapterEntity::getBookId,params.get("bookId"))
.orderByAsc(BookChapterEntity::getNumber));
for (BookChapterEntity bc:bcs) {
List<BookChapterContentEntity> bccs = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookChatperId,bc.getId())
.orderByAsc(BookChapterContentEntity::getNumber));
List<String> list = new ArrayList<>();
for (BookChapterContentEntity bcc : bccs) {
if (!StringUtils.isEmpty(bcc.getVoices())){
list.add(bcc.getVoices());
}
}
String url = BaiduVoicesUtils.mergeVoices(list);
if (!StringUtils.isEmpty(url)){
bc.setVoices(url);
bookChapterService.updateById(bc);
}else {
sb.append(bc.getChapter()+"-"+bc.getContent()+"错误");
}
}
return R.ok().put("info",sb.toString());
}
//按章节转成长音频
@RequestMapping("/bookToLongVoices")
public R bookToLongVoices(@RequestBody Map<String,Object> params){
List<BookChapterEntity> bookChapters = bookChapterService.list(new LambdaQueryWrapper<BookChapterEntity>()
.eq(BookChapterEntity::getBookId,params.get("bookId")));
for (BookChapterEntity bc:bookChapters){
StringBuffer sb = new StringBuffer();
List<BookChapterContentEntity> bookChapterContents = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookChatperId,bc.getId()));
for (BookChapterContentEntity bcc:bookChapterContents){
if (!bcc.getContent().contains("https")&&!"".equals(bcc.getContent())){
sb.append(bcc.getContent());
}
}
if (sb.length() > 0) {
//调用百度语音合成 API
String voices = BaiduVoicesUtils.longText(sb.toString());
bc.setVoices(voices);
bookChapterService.updateById(bc);
}
}
return R.ok();
}
//获取长音频
@RequestMapping("/queryBookVoices")
public R queryBookVoices(@RequestBody Map<String,Object> params){
List<BookChapterEntity> bookChapters = bookChapterService.list(new LambdaQueryWrapper<BookChapterEntity>()
.eq(BookChapterEntity::getBookId,params.get("bookId")));
for (BookChapterEntity bc:bookChapters){
if (!StringUtils.isEmpty(bc.getVoices())&&!bc.getVoices().contains("http")) {
//调用百度语音合成 API
String voices = BaiduVoicesUtils.queryVoice(bc.getVoices());
bc.setVoices(voices);
bookChapterService.updateById(bc);
} }
} }
return R.ok(); return R.ok();

View File

@@ -11,6 +11,7 @@ import com.peanut.modules.common.entity.*;
import com.peanut.modules.master.service.BookAbroadToLableService; import com.peanut.modules.master.service.BookAbroadToLableService;
import com.peanut.modules.bookAbroad.service.BookAbroadLableService; import com.peanut.modules.bookAbroad.service.BookAbroadLableService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.utils.DateUtils; import org.apache.http.client.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -187,14 +188,30 @@ public class HomeController {
//章节内容 //章节内容
@RequestMapping("/getBookChapterContent") @RequestMapping("/getBookChapterContent")
public R getBookChapterContent(@RequestBody Map<String,Object> params) { public R getBookChapterContent(@RequestBody Map<String,Object> params) {
Page<BookChapterContentEntity> contentPage = bookChapterContentService.page(new Page<>( List<BookChapterContentEntity> contentPage = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
Long.parseLong(params.get("current").toString()), Long.parseLong(params.get("limit").toString())),
new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookChatperId,params.get("chapterId")) .eq(BookChapterContentEntity::getBookChatperId,params.get("chapterId"))
.orderByAsc(BookChapterContentEntity::getNumber)); .orderByAsc(BookChapterContentEntity::getNumber));
return R.ok().put("contentPage",contentPage); return R.ok().put("contentPage",contentPage);
} }
//听书时章节内容
@RequestMapping("/getBookChapterContentListen")
public R getBookChapterContentListen(@RequestBody Map<String,Object> params) {
List<BookChapterContentEntity> bccs = bookChapterContentService.list(new LambdaQueryWrapper<BookChapterContentEntity>()
.eq(BookChapterContentEntity::getBookChatperId,params.get("chapterId"))
.notLike(BookChapterContentEntity::getContent,"http")
.orderByAsc(BookChapterContentEntity::getNumber));
//给每段话加上在总音频开始的秒数
double startInt = 0;
for (BookChapterContentEntity bcc : bccs) {
if (StringUtils.isNotBlank(bcc.getVoicesSize())){
bcc.setVoicesStart((int) startInt);
startInt+=Double.parseDouble(bcc.getVoicesSize());
}
}
return R.ok().put("bookChapterContents",bccs);
}
//已读\已买人数 //已读\已买人数
@RequestMapping("/getBookReadCount") @RequestMapping("/getBookReadCount")
public R getBookReadCount(@RequestBody Map<String,Object> params){ public R getBookReadCount(@RequestBody Map<String,Object> params){

View File

@@ -44,6 +44,10 @@ public class BookChapterContentEntity implements Serializable {
*/ */
private String voices; private String voices;
private String voicesSize;
@TableField(exist = false)
private int voicesStart;
private String otherContent;//如果内容是图片存放图片宽高 private String otherContent;//如果内容是图片存放图片宽高
/** /**
* *