2015-12-16 69 views
2

我試圖使用SDL2附帶的SMPEG2庫在緩衝區中加載MP3。每個SMPEG函數調用返回都沒有錯誤,但是當我完成時,聲音緩衝區充滿了零。SDL2&SMPEG2 - 試圖讀取MP3的空聲音緩衝區

下面的代碼:

bool LoadMP3(char* filename) 
{ 
    bool success = false; 
    const Uint32 Mp3ChunkLen = 4096; 

    SMPEG* mp3; 
    SMPEG_Info infoMP3; 
    Uint8 * ChunkBuffer; 
    Uint32 MP3Length = 0; 

    // Allocate a chunk buffer 
    ChunkBuffer = (Uint8*)malloc(Mp3ChunkLen); 

    SDL_RWops *mp3File = SDL_RWFromFile(filename, "rb"); 
    if (mp3File != NULL) 
    { 
    mp3 = SMPEG_new_rwops(mp3File, &infoMP3, 1, 0); 

    if(mp3 != NULL) 
    { 
     if(infoMP3.has_audio) 
     { 
     Uint32 readLen; 

     // Inform the MP3 of the output audio specifications 
     SMPEG_actualSpec(mp3, &asDeviceSpecs); // static SDL_AudioSpec asDeviceSpecs; containing valid values after a call to SDL_OpenAudioDevice 

     // Enable the audio and disable the video. 
     SMPEG_enableaudio(mp3, 1); 
     SMPEG_enablevideo(mp3, 0); 

     // Play the MP3 once to get the size of the needed finale buffer 
     SMPEG_play(mp3); 
     while ((readLen = SMPEG_playAudio(mp3, ChunkBuffer, Mp3ChunkLen)) > 0) 
     { 
      MP3Length += readLen; 
     } 
     SMPEG_stop(mp3); 

     if(MP3Length > 0) 
     { 
      // Reallocate the buffer with the new length (if needed) 
      if (MP3Length != Mp3ChunkLen) 
      { 
      ChunkBuffer = (Uint8*)realloc(ChunkBuffer, MP3Length); 
      } 

      // Replay the entire MP3 into the new ChunkBuffer. 
      SMPEG_rewind(mp3); 
      SMPEG_play(mp3); 
      bool readBackSuccess = (MP3Length == SMPEG_playAudio(mp3, ChunkBuffer, MP3Length)); 
      SMPEG_stop(mp3); 
      if(readBackSuccess) 
      { 
      // !!! Here, ChunkBuffer contains only zeros !!! 

      success = true; 
      } 
     } 
     } 
     SMPEG_delete(mp3); 
     mp3 = NULL; 
    } 
    SDL_RWclose(mp3File); 
    mp3File = NULL; 
    } 

    free(ChunkBuffer); 
    return success; 
} 

廣泛基礎上SDL_Mixer,我不能爲我謨,是根據它的侷限性使用該代碼的。

我知道Ogg Vorbis會是一個更好的文件格式選擇,但我正在移植一個非常古老的項目,而且它完全可以用MP3播放。

我確定音響系統已經正確初始化,因爲我可以播放WAV文件。它的初始化頻率爲44100,2個通道,1024個採樣和AUDIO_S16SYS格式(後者是我從SMPEG源中瞭解到的,是強制性的)。

我已經根據比特率,MP3數據量和OpenAudioDevice音頻規格計算了預期的緩衝區大小,並且所有內容都是一致的。

我不明白爲什麼除了緩衝區數據以外的任何東西似乎都在工作。

更新#1

仍在試圖弄清楚什麼是錯的,我想支持MP3可能無法正常工作,所以我創建了以下功能:

SMPEG *mpeg; 
SMPEG_Info info; 
mpeg = SMPEG_new(filename,&info, 1); 
SMPEG_play(mpeg); 
do { SDL_Delay(50); } while(SMPEG_status(mpeg) == SMPEG_PLAYING); 
SMPEG_delete(mpeg); 

的MP3播放。所以,解碼應該實際上是工作。但那不是我需要的;我真的需要聲音緩衝區數據,所以我可以把它發送到我的調音臺。

回答

2

經過許多修補,研究和挖掘SMPEG源代碼後,我意識到必須將SDLAudio參數傳遞給SMPEG_new_rwops函數。

在smpeg.h找到的評論是一種誤導:

的sdl_audio參數指示是否應該SMPEG初始化SDL音頻子系統。否則,您將不得不使用下面的SMPEG_playaudio()函數來提取解碼數據。

由於音頻子系統已經初始化,我用的是SMPEG_playaudio()函數,我沒有理由認爲我需要這個參數是非零。在SMPEG中,此參數在開始時觸發音頻解壓縮,但即使我調用了SMPEG_enableaudio(mp3, 1);,數據也不會被重新解壓。這可能是一個bug /一個陰暗的功能。

我有freesrc參數需要爲0的另一個問題,因爲我自己釋放了SDL_RWops對象。

爲了將來的參考,一旦ChunkBuffer具有MP3數據,如果要通過已打開的音頻設備播放,則需要通過SDL_BuildAudioCVT/SDL_ConvertAudio。

最後的工作代碼爲:

// bool ReadMP3ToBuffer(char* filename) 
bool success = false; 
const Uint32 Mp3ChunkLen = 4096; 
SDL_AudioSpec mp3Specs; 

SMPEG* mp3; 
SMPEG_Info infoMP3; 
Uint8 * ChunkBuffer; 
Uint32 MP3Length = 0; 

// Allocate a chunk buffer 
ChunkBuffer = (Uint8*)malloc(Mp3ChunkLen); 
memset(ChunkBuffer, 0, Mp3ChunkLen); 

SDL_RWops *mp3File = SDL_RWFromFile(filename, "rb"); // filename is a char* passed to the function. 
if (mp3File != NULL) 
{ 
    mp3 = SMPEG_new_rwops(mp3File, &infoMP3, 0, 1); 

    if(mp3 != NULL) 
    { 
    if(infoMP3.has_audio) 
    { 
     Uint32 readLen; 

     // Get the MP3 audio specs for later conversion 
     SMPEG_wantedSpec(mp3, &mp3Specs); 

     SMPEG_enablevideo(mp3, 0); 

     // Play the MP3 once to get the size of the needed buffer in relation with the audio specs 
     SMPEG_play(mp3); 
     while ((readLen = SMPEG_playAudio(mp3, ChunkBuffer, Mp3ChunkLen)) > 0) 
     { 
     MP3Length += readLen; 
     } 
     SMPEG_stop(mp3); 

     if(MP3Length > 0) 
     { 
     // Reallocate the buffer with the new length (if needed) 
     if (MP3Length != Mp3ChunkLen) 
     { 
      ChunkBuffer = (Uint8*)realloc(ChunkBuffer, MP3Length); 
      memset(ChunkBuffer, 0, MP3Length); 
     } 

     // Replay the entire MP3 into the new ChunkBuffer. 
     SMPEG_rewind(mp3); 
     SMPEG_play(mp3); 

     bool readBackSuccess = (MP3Length == SMPEG_playAudio(mp3, ChunkBuffer, MP3Length)); 
     SMPEG_stop(mp3); 
     if(readBackSuccess) 
     { 
      SDL_AudioCVT convertedSound; 
      // NOTE : static SDL_AudioSpec asDeviceSpecs; containing valid values after a call to SDL_OpenAudioDevice 
      if(SDL_BuildAudioCVT(&convertedSound, mp3Specs.format, mp3Specs.channels, mp3Specs.freq, asDeviceSpecs.format, asDeviceSpecs.channels, asDeviceSpecs.freq) >= 0) 
      { 
      Uint32 newBufferLen = MP3Length*convertedSound.len_mult; 

      // Make sure the audio length is a multiple of a sample size to avoid sound clicking 
      int sampleSize = ((asDeviceSpecs.format & 0xFF)/8)*asDeviceSpecs.channels; 
      newBufferLen &= ~(sampleSize-1); 

      // Allocate the new buffer and proceed with the actual conversion. 
      convertedSound.buf = (Uint8*)malloc(newBufferLen); 
      memcpy(convertedSound.buf, ChunkBuffer, MP3Length); 

      convertedSound.len = MP3Length; 
      if(SDL_ConvertAudio(&convertedSound) == 0) 
      { 
       // Save convertedSound.buf and convertedSound.len_cvt for future use in your mixer code. 
       // Dont forget to free convertedSound.buf once it's not used anymore. 
       success = true; 
      } 
      } 
     } 
     } 
    } 
    SMPEG_delete(mp3); 
    mp3 = NULL; 
    } 
    SDL_RWclose(mp3File); 
    mp3File = NULL; 
} 

free(ChunkBuffer); 

return success; 

注意:我嘗試了一些MP3文件播放過程中丟失幾毫秒的時間和截止太早,當我與此代碼重新取樣它們。其他一些沒有。我可以在Audacity中重現相同的行爲,所以我不確定發生了什麼。我的代碼可能還存在一個錯誤,SMPEG中存在一個錯誤,或者它可能是MP3格式本身的一個已知問題。如果有人可以在評論中提供和解釋,那會很棒!