|
|
@@ -30,6 +30,8 @@ import javax.servlet.http.HttpServletResponse;
|
|
30
|
30
|
import javax.sound.sampled.*;
|
|
31
|
31
|
import java.io.*;
|
|
32
|
32
|
import java.util.*;
|
|
|
33
|
+import java.util.regex.Matcher;
|
|
|
34
|
+import java.util.regex.Pattern;
|
|
33
|
35
|
import java.util.stream.Collectors;
|
|
34
|
36
|
|
|
35
|
37
|
|
|
|
@@ -104,9 +106,111 @@ public class HomeController extends BaseController {
|
|
104
|
106
|
return Success("获取成功", map);
|
|
105
|
107
|
}
|
|
106
|
108
|
|
|
|
109
|
+ @ApiOperation("getVolume")
|
|
|
110
|
+ @GetMapping("/getVolume")
|
|
|
111
|
+ public AjaxResult getVolume() {
|
|
|
112
|
+ String audioFilePath ="D:/6047871_2034_150732.mp3";
|
|
|
113
|
+ try {
|
|
|
114
|
+ return Success(detectAudioVolume(audioFilePath)) ;
|
|
|
115
|
+ } catch (IOException e) {
|
|
|
116
|
+ e.printStackTrace();
|
|
|
117
|
+ } catch (InterruptedException e) {
|
|
|
118
|
+ throw new RuntimeException(e);
|
|
|
119
|
+ }
|
|
|
120
|
+ return Success("Hello World!", new Date());
|
|
|
121
|
+ }
|
|
|
122
|
+ public static String analyzeWavFile(String filePath) throws IOException {
|
|
|
123
|
+ File file = new File(filePath);
|
|
|
124
|
+ try (FileInputStream fis = new FileInputStream(file)) {
|
|
|
125
|
+ // 读取 WAV 文件头
|
|
|
126
|
+ byte[] header = new byte[44];
|
|
|
127
|
+ fis.read(header);
|
|
|
128
|
+
|
|
|
129
|
+ // 获取采样率
|
|
|
130
|
+ int sampleRate = ((header[27] & 0xFF) << 24) | ((header[26] & 0xFF) << 16) | ((header[25] & 0xFF) << 8) | (header[24] & 0xFF);
|
|
|
131
|
+
|
|
|
132
|
+ // 获取声道数
|
|
|
133
|
+ int numChannels = ((header[23] & 0xFF) << 8) | (header[22] & 0xFF);
|
|
|
134
|
+
|
|
|
135
|
+ // 获取位深度
|
|
|
136
|
+ int bitDepth = ((header[35] & 0xFF) << 8) | (header[34] & 0xFF);
|
|
|
137
|
+
|
|
|
138
|
+ // 计算音频总时长(秒)
|
|
|
139
|
+ long audioDataLength = file.length() - 44;
|
|
|
140
|
+ int bytesPerSample = bitDepth / 8;
|
|
|
141
|
+ long totalSamples = audioDataLength / (bytesPerSample * numChannels);
|
|
|
142
|
+ double duration = (double) totalSamples / sampleRate;
|
|
|
143
|
+
|
|
|
144
|
+ // 简单估算语速(这里以每秒采样点数表示,可根据实际情况调整)
|
|
|
145
|
+ double speechRate = totalSamples / duration;
|
|
|
146
|
+ System.out.println("语速(每秒采样点数): " + speechRate);
|
|
|
147
|
+
|
|
|
148
|
+ // 计算音量(分贝)
|
|
|
149
|
+ double volumeInDb = calculateVolumeInDb(fis, numChannels, bitDepth);
|
|
|
150
|
+ System.out.println("音量 (dB): " + volumeInDb);
|
|
|
151
|
+ return "语速(每秒采样点数): " + speechRate+"音量 (dB): " + volumeInDb;
|
|
|
152
|
+ }
|
|
|
153
|
+ }
|
|
107
|
154
|
|
|
|
155
|
+ private static double calculateVolumeInDb(FileInputStream fis, int numChannels, int bitDepth) throws IOException {
|
|
|
156
|
+ long sumSquares = 0;
|
|
|
157
|
+ int sampleSize = bitDepth / 8;
|
|
|
158
|
+ int bytesRead;
|
|
|
159
|
+ byte[] buffer = new byte[sampleSize * numChannels];
|
|
|
160
|
+ fis.getChannel().position(44); // 定位到音频数据起始位置
|
|
|
161
|
+ while ((bytesRead = fis.read(buffer)) != -1) {
|
|
|
162
|
+ for (int i = 0; i < bytesRead; i += sampleSize) {
|
|
|
163
|
+ long sample = 0;
|
|
|
164
|
+ if (bitDepth == 8) {
|
|
|
165
|
+ sample = buffer[i] & 0xFF;
|
|
|
166
|
+ } else if (bitDepth == 16) {
|
|
|
167
|
+ sample = ((buffer[i + 1] & 0xFF) << 8) | (buffer[i] & 0xFF);
|
|
|
168
|
+ }
|
|
|
169
|
+ sumSquares += sample * sample;
|
|
|
170
|
+ }
|
|
|
171
|
+ }
|
|
|
172
|
+ long numSamples = (fis.getChannel().size() - 44) / (sampleSize * numChannels);
|
|
|
173
|
+ double rms = Math.sqrt((double) sumSquares / numSamples);
|
|
108
|
174
|
|
|
|
175
|
+ // 将 RMS 转换为分贝
|
|
|
176
|
+ double reference = Math.pow(2, bitDepth - 1);
|
|
|
177
|
+ return 20 * Math.log10(rms / reference);
|
|
|
178
|
+ }
|
|
|
179
|
+ public static String detectAudioVolume(String audioFilePath) throws IOException, InterruptedException {
|
|
|
180
|
+ // 构建 FFmpeg 命令
|
|
|
181
|
+ ProcessBuilder processBuilder = new ProcessBuilder(
|
|
|
182
|
+ "ffmpeg",
|
|
|
183
|
+ "-i", audioFilePath,
|
|
|
184
|
+ "-filter_complex", "volumedetect",
|
|
|
185
|
+ "-f", "null",
|
|
|
186
|
+ "-"
|
|
|
187
|
+ );
|
|
|
188
|
+
|
|
|
189
|
+ // 启动进程
|
|
|
190
|
+ Process process = processBuilder.start();
|
|
|
191
|
+
|
|
|
192
|
+ // 读取进程的错误输出流(FFmpeg 的音量信息通常在错误输出中)
|
|
|
193
|
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
|
|
194
|
+ String line;
|
|
|
195
|
+ String regex = "mean_volume: (-?\\d+\\.?\\d*) dB";
|
|
|
196
|
+ Pattern pattern = Pattern.compile(regex);
|
|
|
197
|
+ while ((line = reader.readLine()) != null) {
|
|
|
198
|
+ Matcher matcher = pattern.matcher(line);
|
|
|
199
|
+ if (matcher.find()) {
|
|
|
200
|
+ String meanVolumeStr = matcher.group(1);
|
|
|
201
|
+ double meanVolume = Double.parseDouble(meanVolumeStr);
|
|
|
202
|
+ System.out.println("音频的平均音量分贝: " + meanVolume + " dB");
|
|
|
203
|
+ return "音频的平均音量分贝: " + meanVolume + " dB";
|
|
|
204
|
+ }
|
|
|
205
|
+ }
|
|
109
|
206
|
|
|
|
207
|
+ // 等待进程结束
|
|
|
208
|
+ int exitCode = process.waitFor();
|
|
|
209
|
+ if (exitCode != 0) {
|
|
|
210
|
+ System.err.println("FFmpeg 命令执行失败,退出码: " + exitCode);
|
|
|
211
|
+ }
|
|
|
212
|
+ return "";
|
|
|
213
|
+ }
|
|
110
|
214
|
@ApiOperation("Hello World")
|
|
111
|
215
|
@GetMapping("/")
|
|
112
|
216
|
public AjaxResult Info() {
|