mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
feat: accept any audio input (any rate/channels, wav/mp3/flac) via miniaudio (#2994)
* feat(runtime): accept any audio (any sample-rate/channels, wav/mp3/flac) via miniaudio Replaces the 16k-mono-PCM16-only WAV reader with a miniaudio-based loader (funasr-common/funasr_audio.h) that decodes wav/mp3/flac at any sample rate and channel count, resampling to 16 kHz mono. Verified: a 44.1k stereo file and an 8k mono file both transcribe correctly across cli/sensevoice/paraformer. * fix(audio): inline loader (avoid duplicate symbols), null-path guard, check decoder read result (gemini review) --------- Co-authored-by: LauraGPT <[email protected]>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
set(TARGET llama-funasr-cli)
|
||||
add_executable(${TARGET} funasr-cli.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../funasr-common)
|
||||
target_link_libraries(${TARGET} PRIVATE llama ggml ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
@@ -23,34 +23,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// ======================= WAV (16 kHz mono PCM16) =======================
|
||||
static bool read_wav16(const char * path, std::vector<float> & out) {
|
||||
FILE * f = fopen(path, "rb"); if (!f) return false;
|
||||
char riff[4]; fread(riff, 1, 4, f);
|
||||
if (strncmp(riff, "RIFF", 4)) { fclose(f); return false; }
|
||||
fseek(f, 4, SEEK_CUR); char wave[4]; fread(wave, 1, 4, f);
|
||||
if (strncmp(wave, "WAVE", 4)) { fclose(f); return false; }
|
||||
uint16_t ch = 1, bits = 16; uint32_t sr = 16000;
|
||||
while (!feof(f)) {
|
||||
char id[4]; if (fread(id, 1, 4, f) != 4) break;
|
||||
uint32_t sz; if (fread(&sz, 4, 1, f) != 1) break;
|
||||
if (!strncmp(id, "fmt ", 4)) {
|
||||
uint16_t fmt; fread(&fmt, 2, 1, f); fread(&ch, 2, 1, f);
|
||||
fread(&sr, 4, 1, f); fseek(f, 6, SEEK_CUR); fread(&bits, 2, 1, f);
|
||||
if (sz > 16) fseek(f, sz - 16, SEEK_CUR);
|
||||
} else if (!strncmp(id, "data", 4)) {
|
||||
if (ch < 1 || bits != 16) { fclose(f); fprintf(stderr, "only 16-bit PCM WAV supported (ch=%u bits=%u)\n", ch, bits); return false; }
|
||||
int n = sz / (bits / 8);
|
||||
std::vector<int16_t> pcm(n); fread(pcm.data(), 2, n, f);
|
||||
out.resize(n / ch);
|
||||
for (int i = 0; i < n / ch; i++) out[i] = pcm[i * ch] / 32768.0f; // ch0
|
||||
fclose(f);
|
||||
if (sr != 16000) fprintf(stderr, "warning: sample rate %u != 16000\n", sr);
|
||||
return true;
|
||||
} else fseek(f, sz, SEEK_CUR);
|
||||
}
|
||||
fclose(f); return false;
|
||||
}
|
||||
// any audio (wav/mp3/flac, any rate/channels) -> 16 kHz mono f32, via miniaudio
|
||||
#define FUNASR_AUDIO_IMPLEMENTATION
|
||||
#include "funasr_audio.h"
|
||||
|
||||
// ======================= kaldi fbank + LFR =======================
|
||||
static const int FS=16000, WINLEN=400, SHIFT=160, NFFT=512, NMEL=80, LFR_M=7, LFR_N=6;
|
||||
@@ -202,7 +177,7 @@ int main(int argc,char**argv){
|
||||
if(enc_path.empty()||llm_path.empty()||wav_path.empty()){fprintf(stderr,"missing args\n");return 1;}
|
||||
|
||||
std::vector<float> wav;
|
||||
if(!read_wav16(wav_path.c_str(),wav)){fprintf(stderr,"failed to read wav\n");return 1;}
|
||||
if(!funasr_load_audio_16k_mono(wav_path.c_str(),wav)){fprintf(stderr,"failed to read audio\n");return 1;}
|
||||
int64_t t0=ggml_time_us();
|
||||
|
||||
enc_model em; if(!load_enc(enc_path.c_str(),em))return 1;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// funasr_audio.h — load any audio file (WAV/MP3/FLAC/OGG...) as 16 kHz mono f32,
|
||||
// via miniaudio's decoder (handles arbitrary sample rate, channels, bit depth).
|
||||
// One TU must define FUNASR_AUDIO_IMPLEMENTATION before including.
|
||||
#ifndef FUNASR_AUDIO_H
|
||||
#define FUNASR_AUDIO_H
|
||||
#include <vector>
|
||||
// returns true on success; out = mono 16 kHz float samples in [-1,1]
|
||||
inline bool funasr_load_audio_16k_mono(const char * path, std::vector<float> & out);
|
||||
#endif
|
||||
|
||||
#ifdef FUNASR_AUDIO_IMPLEMENTATION
|
||||
// keep only the decoder + data conversion (resampler/channel mixer); drop playback/capture
|
||||
#define MA_NO_DEVICE_IO
|
||||
#define MA_NO_ENGINE
|
||||
#define MA_NO_GENERATION
|
||||
#define MA_NO_THREADING
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
#include <cstdio>
|
||||
inline bool funasr_load_audio_16k_mono(const char * path, std::vector<float> & out) {
|
||||
if (!path) { fprintf(stderr, "audio: null path\n"); return false; }
|
||||
ma_decoder_config cfg = ma_decoder_config_init(ma_format_f32, 1, 16000); // f32, mono, 16k
|
||||
ma_decoder dec;
|
||||
if (ma_decoder_init_file(path, &cfg, &dec) != MA_SUCCESS) {
|
||||
fprintf(stderr, "audio: failed to open/decode %s (supported: wav/mp3/flac)\n", path);
|
||||
return false;
|
||||
}
|
||||
out.clear();
|
||||
ma_uint64 nframes = 0;
|
||||
if (ma_decoder_get_length_in_pcm_frames(&dec, &nframes) == MA_SUCCESS && nframes > 0) {
|
||||
out.resize(nframes);
|
||||
ma_uint64 got = 0;
|
||||
ma_result r = ma_decoder_read_pcm_frames(&dec, out.data(), nframes, &got);
|
||||
if (r != MA_SUCCESS && r != MA_AT_END) { ma_decoder_uninit(&dec); fprintf(stderr, "audio: decode error\n"); return false; }
|
||||
out.resize(got);
|
||||
} else {
|
||||
// length unknown (e.g. some mp3 streams): read in chunks until EOF
|
||||
std::vector<float> buf(16000);
|
||||
for (;;) {
|
||||
ma_uint64 got = 0;
|
||||
ma_result r = ma_decoder_read_pcm_frames(&dec, buf.data(), buf.size(), &got);
|
||||
out.insert(out.end(), buf.begin(), buf.begin() + got);
|
||||
if (got < buf.size() || (r != MA_SUCCESS && r != MA_AT_END)) break;
|
||||
}
|
||||
}
|
||||
ma_decoder_uninit(&dec);
|
||||
return !out.empty();
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
set(TARGET llama-funasr-paraformer)
|
||||
add_executable(${TARGET} funasr-paraformer.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../funasr-common)
|
||||
target_link_libraries(${TARGET} PRIVATE ggml ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
@@ -19,13 +19,9 @@
|
||||
|
||||
static const float LN_EPS = 1e-5f;
|
||||
|
||||
// ===== WAV + kaldi fbank (shared) =====
|
||||
static bool read_wav16(const char*p,std::vector<float>&o){FILE*f=fopen(p,"rb");if(!f)return false;char r[4];fread(r,1,4,f);
|
||||
if(strncmp(r,"RIFF",4)){fclose(f);return false;}fseek(f,4,SEEK_CUR);char wv[4];fread(wv,1,4,f);if(strncmp(wv,"WAVE",4)){fclose(f);return false;}
|
||||
uint16_t ch=1,bits=16;uint32_t sr=16000;while(!feof(f)){char id[4];if(fread(id,1,4,f)!=4)break;uint32_t sz;if(fread(&sz,4,1,f)!=1)break;
|
||||
if(!strncmp(id,"fmt ",4)){uint16_t fm;fread(&fm,2,1,f);fread(&ch,2,1,f);fread(&sr,4,1,f);fseek(f,6,SEEK_CUR);fread(&bits,2,1,f);if(sz>16)fseek(f,sz-16,SEEK_CUR);}
|
||||
else if(!strncmp(id,"data",4)){if(ch<1||bits!=16){fclose(f);fprintf(stderr,"only 16-bit PCM WAV (ch=%u bits=%u)\n",ch,bits);return false;}int n=sz/(bits/8);std::vector<int16_t>pc(n);fread(pc.data(),2,n,f);o.resize(n/ch);for(int i=0;i<n/ch;i++)o[i]=pc[i*ch]/32768.0f;fclose(f);return true;}
|
||||
else fseek(f,sz,SEEK_CUR);}fclose(f);return false;}
|
||||
// ===== audio loader: any wav/mp3/flac, any rate/channels -> 16k mono (miniaudio) =====
|
||||
#define FUNASR_AUDIO_IMPLEMENTATION
|
||||
#include "funasr_audio.h"
|
||||
static const int FS=16000,WINLEN=400,SHIFT=160,NFFT=512,NMEL=80,LFR_M=7,LFR_N=6;
|
||||
static const float PREEMPH=0.97f,LOWF=20.0f,HIGHF=8000.0f;
|
||||
static inline float melf(float f){return 1127.0f*logf(1.0f+f/700.0f);}
|
||||
@@ -135,7 +131,7 @@ int main(int argc,char**argv){
|
||||
const int D=m.c.d_model,F=560,V=m.c.vocab;
|
||||
|
||||
// fbank
|
||||
std::vector<float>wav;if(!read_wav16(wav_path.c_str(),wav)){fprintf(stderr,"read wav failed\n");return 1;}
|
||||
std::vector<float>wav;if(!funasr_load_audio_16k_mono(wav_path.c_str(),wav)){fprintf(stderr,"read audio failed\n");return 1;}
|
||||
int64_t t0=ggml_time_us();int T=0;auto fb=compute_fbank(wav,T);
|
||||
// CMVN (Paraformer applies it): (x+shift)*scale
|
||||
float*shift=(float*)m.g("cmvn.shift")->data,*scale=(float*)m.g("cmvn.scale")->data;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
set(TARGET llama-funasr-sensevoice)
|
||||
add_executable(${TARGET} funasr-sensevoice.cpp)
|
||||
install(TARGETS ${TARGET} RUNTIME)
|
||||
target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../funasr-common)
|
||||
target_link_libraries(${TARGET} PRIVATE ggml ${CMAKE_THREAD_LIBS_INIT})
|
||||
target_compile_features(${TARGET} PRIVATE cxx_std_17)
|
||||
|
||||
@@ -20,18 +20,9 @@
|
||||
|
||||
static const float LN_EPS = 1e-5f;
|
||||
|
||||
// ---- WAV (16k mono PCM16) + kaldi fbank (shared with funasr-cli) ----
|
||||
static bool read_wav16(const char*path,std::vector<float>&out){
|
||||
FILE*f=fopen(path,"rb"); if(!f)return false; char r[4]; fread(r,1,4,f);
|
||||
if(strncmp(r,"RIFF",4)){fclose(f);return false;} fseek(f,4,SEEK_CUR); char wv[4]; fread(wv,1,4,f);
|
||||
if(strncmp(wv,"WAVE",4)){fclose(f);return false;} uint16_t ch=1,bits=16; uint32_t sr=16000;
|
||||
while(!feof(f)){char id[4]; if(fread(id,1,4,f)!=4)break; uint32_t sz; if(fread(&sz,4,1,f)!=1)break;
|
||||
if(!strncmp(id,"fmt ",4)){uint16_t fmt; fread(&fmt,2,1,f); fread(&ch,2,1,f); fread(&sr,4,1,f); fseek(f,6,SEEK_CUR); fread(&bits,2,1,f); if(sz>16)fseek(f,sz-16,SEEK_CUR);}
|
||||
else if(!strncmp(id,"data",4)){if(ch<1||bits!=16){fclose(f);fprintf(stderr,"only 16-bit PCM WAV (ch=%u bits=%u)\n",ch,bits);return false;} int n=sz/(bits/8); std::vector<int16_t> pcm(n); fread(pcm.data(),2,n,f); out.resize(n/ch);
|
||||
for(int i=0;i<n/ch;i++)out[i]=pcm[i*ch]/32768.0f; fclose(f); if(sr!=16000)fprintf(stderr,"warn: sr %u\n",sr); return true;}
|
||||
else fseek(f,sz,SEEK_CUR);}
|
||||
fclose(f); return false;
|
||||
}
|
||||
// ---- audio loader: any wav/mp3/flac, any rate/channels -> 16k mono (miniaudio) ----
|
||||
#define FUNASR_AUDIO_IMPLEMENTATION
|
||||
#include "funasr_audio.h"
|
||||
static const int FS=16000,WINLEN=400,SHIFT=160,NFFT=512,NMEL=80,LFR_M=7,LFR_N=6;
|
||||
static const float PREEMPH=0.97f,LOWF=20.0f,HIGHF=8000.0f;
|
||||
static inline float melf(float f){return 1127.0f*logf(1.0f+f/700.0f);}
|
||||
@@ -122,7 +113,7 @@ int main(int argc,char**argv){
|
||||
// fbank [T,F]: from wav (-a) or precomputed (-f)
|
||||
int32_t T=0,Fc=F; std::vector<float> fb;
|
||||
if(!wav_path.empty()){
|
||||
std::vector<float> wav; if(!read_wav16(wav_path.c_str(),wav)){fprintf(stderr,"read wav failed\n");return 1;}
|
||||
std::vector<float> wav; if(!funasr_load_audio_16k_mono(wav_path.c_str(),wav)){fprintf(stderr,"read audio failed\n");return 1;}
|
||||
int t=0; fb=compute_fbank(wav,t); T=t;
|
||||
} else {
|
||||
FILE*f=fopen(fbank_path.c_str(),"rb"); if(!f){fprintf(stderr,"open fbank\n");return 1;}
|
||||
|
||||
Reference in New Issue
Block a user