Skip to content

Commit 1a5bca9

Browse files
committed
feat: 新增 silkToPcm 方法直接解码 Silk 到 PCM
- 新增 Java_me_yun_silk_SilkCodec_silkToPcm 函数 - 直接解码 Silk 到 PCM,避免 Silk → MP3 → PCM 的间接转换 - 添加 JNI 方法声明 - 优化 AacCodec 的 silkToM4a 和 silkToAac 方法 - 提高转换效率,避免 MP3 编码器带来的音质损失
1 parent 5bdd81a commit 1a5bca9

2 files changed

Lines changed: 113 additions & 7 deletions

File tree

silk-codec/src/main/java/me/yun/silk/SilkCodec.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
public class SilkCodec {
44

5-
/** 时长限制:60秒 */
6-
public static final long MAX_DURATION_MS = 60_000L;
7-
85
static {
96
System.loadLibrary("silk");
107
}
@@ -93,6 +90,18 @@ public class SilkCodec {
9390
*/
9491
public native int silkToMp3(String silkPath, String mp3Path, int hz);
9592

93+
// ==================== Silk 转 PCM ====================
94+
95+
/**
96+
* Silk 转 PCM
97+
*
98+
* @param silkPath 输入 Silk 文件路径
99+
* @param pcmPath 输出 PCM 文件路径
100+
* @param hz 输出 PCM 采样率
101+
* @return 0=成功, 负数=错误码
102+
*/
103+
public native int silkToPcm(String silkPath, String pcmPath, int hz);
104+
96105
// ==================== 转 PCM ====================
97106

98107
/**
@@ -149,13 +158,17 @@ public class SilkCodec {
149158
public native long getDuration(String filePath);
150159

151160
/**
152-
* 获取限制后的音频时长(毫秒)。若超过 {@link #MAX_DURATION_MS} 则截断。
161+
* 获取限制后的音频时长(毫秒) 如果时长超过 60 秒,则强制返回 60000 毫秒
153162
*
154163
* @param filePath 音频文件路径
155-
* @return 时长(毫秒),最高 {@value #MAX_DURATION_MS}
164+
* @return 时长(毫秒),最高 60000
156165
*/
157-
public long getDurationLimited(String filePath) {
166+
public long getDurations(String filePath) {
158167
long duration = getDuration(filePath);
159-
return duration > MAX_DURATION_MS ? MAX_DURATION_MS : duration;
168+
// 60秒 = 60 * 1000 毫秒
169+
if (duration > 60000) {
170+
return 60000;
171+
}
172+
return duration;
160173
}
161174
}

silk-codec/src/main/jni/silk_codec.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,99 @@ JNIEXPORT jint JNICALL Java_me_yun_silk_SilkCodec_silkToMp3(
10781078
return 0;
10791079
}
10801080

1081+
/**
1082+
*
1083+
* 参数: silkPath - 输入 Silk 文件路径
1084+
* pcmPath - 输出 PCM 文件路径
1085+
* hz - 输出 PCM 采样率
1086+
*
1087+
* 返回值: 0=成功, 负数=错误码
1088+
**/
1089+
JNIEXPORT jint JNICALL Java_me_yun_silk_SilkCodec_silkToPcm(
1090+
JNIEnv *env, jobject thiz, jstring silkPath, jstring pcmPath, jint hz) {
1091+
const char *in_p = env->GetStringUTFChars(silkPath, 0);
1092+
const char *out_p = env->GetStringUTFChars(pcmPath, 0);
1093+
1094+
/* 打开输入文件 */
1095+
FILE *fin = fopen(in_p, "rb");
1096+
if (!fin) {
1097+
env->ReleaseStringUTFChars(silkPath, in_p);
1098+
env->ReleaseStringUTFChars(pcmPath, out_p);
1099+
return -201;
1100+
}
1101+
1102+
/* 打开输出文件 */
1103+
FILE *fout = fopen(out_p, "wb");
1104+
if (!fout) {
1105+
fclose(fin);
1106+
env->ReleaseStringUTFChars(silkPath, in_p);
1107+
env->ReleaseStringUTFChars(pcmPath, out_p);
1108+
return -202;
1109+
}
1110+
1111+
setvbuf(fin, NULL, _IOFBF, 65536);
1112+
setvbuf(fout, NULL, _IOFBF, 65536);
1113+
1114+
/* 解析 Silk 文件头 */
1115+
char head_buf[16];
1116+
int read_len = fread(head_buf, 1, 16, fin);
1117+
int data_start = 0;
1118+
1119+
for (int i = 0; i <= read_len - 9; i++) {
1120+
if (memcmp(head_buf + i, "#!SILK_V3", 9) == 0) {
1121+
data_start = i + 9;
1122+
break;
1123+
}
1124+
}
1125+
fseek(fin, data_start, SEEK_SET);
1126+
1127+
/* 初始化 Silk 解码器 */
1128+
SKP_int32 decSize;
1129+
SKP_Silk_SDK_Get_Decoder_Size(&decSize);
1130+
void *psDec = malloc(decSize);
1131+
SKP_Silk_SDK_InitDecoder(psDec);
1132+
1133+
/* 解码参数配置 */
1134+
SKP_SILK_SDK_DecControlStruct decCtrl;
1135+
memset(&decCtrl, 0, sizeof(decCtrl));
1136+
decCtrl.API_sampleRate = hz;
1137+
1138+
/* 缓冲区 */
1139+
SKP_int16 nBytesIn;
1140+
SKP_uint8 inBuf[MAX_ARITHM_BYTES];
1141+
SKP_int16 pcmBuf[MAX_API_FS_KHZ * FRAME_LENGTH_MS];
1142+
1143+
/* 解码循环 */
1144+
while (fread(&nBytesIn, 2, 1, fin) == 1) {
1145+
if (nBytesIn <= 0 || nBytesIn > MAX_ARITHM_BYTES)
1146+
break;
1147+
fread(inBuf, 1, nBytesIn, fin);
1148+
1149+
SKP_int16 nSamplesOut;
1150+
if (SKP_Silk_SDK_Decode(psDec, &decCtrl, 0, inBuf, (SKP_int)nBytesIn,
1151+
pcmBuf, &nSamplesOut) == 0) {
1152+
/* 直接写入 PCM 数据 (16-bit little endian) */
1153+
fwrite(pcmBuf, 2, nSamplesOut, fout);
1154+
}
1155+
1156+
while (decCtrl.moreInternalDecoderFrames) {
1157+
if (SKP_Silk_SDK_Decode(psDec, &decCtrl, 0, NULL, 0, pcmBuf,
1158+
&nSamplesOut) == 0) {
1159+
fwrite(pcmBuf, 2, nSamplesOut, fout);
1160+
}
1161+
}
1162+
}
1163+
1164+
/* 资源释放 */
1165+
free(psDec);
1166+
fclose(fin);
1167+
fclose(fout);
1168+
1169+
env->ReleaseStringUTFChars(silkPath, in_p);
1170+
env->ReleaseStringUTFChars(pcmPath, out_p);
1171+
return 0;
1172+
}
1173+
10811174
/**
10821175
* 自动识别音频格式并转 Silk (统一入口)
10831176
*

0 commit comments

Comments
 (0)