C++ SDL2&;SMPEG2-尝试读取MP3的空声音缓冲区

C++ SDL2&;SMPEG2-尝试读取MP3的空声音缓冲区,c++,audio,mp3,sdl-2,C++,Audio,Mp3,Sdl 2,我正在尝试使用SDL2附带的SMPEG2库将MP3加载到缓冲区中。每个SMPEG函数调用都返回,没有错误,但当我完成时,声音缓冲区中充满了零 代码如下: bool LoadMP3(char* filename) { bool success = false; const Uint32 Mp3ChunkLen = 4096; SMPEG* mp3; SMPEG_Info infoMP3; Uint8 * ChunkBuffer; Uint32 MP3Length = 0;

我正在尝试使用SDL2附带的SMPEG2库将MP3加载到缓冲区中。每个SMPEG函数调用都返回,没有错误,但当我完成时,声音缓冲区中充满了零

代码如下:

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_混合器,基于其局限性,我无法将其用于我的projet

我知道Ogg Vorbis是一个更好的文件格式选择,但我正在移植一个非常老的项目,它完全与MP3一起工作

我确信音响系统初始化正确,因为我可以很好地播放WAV文件。它的初始化频率为44100,2个通道,1024个样本,并且采用音频_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播放着。所以,解码应该是有效的。但那不是我需要的;我真的需要声音缓冲区数据,以便将其发送到我的混音器。

在对SMPEG源代码进行了大量的修补、研究和挖掘之后,我意识到我必须将1作为SDLAudio参数传递给SMPEG_new_rwops函数

smpeg.h中的评论具有误导性:

sdl_音频参数指示SMPEG是否应初始化sdl音频子系统。如果没有,则必须使用下面的SMPEG_playaudio()函数来提取解码数据

由于音频子系统已经初始化,并且我正在使用SMPEG_playaudio()函数,所以我没有理由认为我需要这个参数为非零。在SMPEG中,此参数在打开时触发音频解压缩,但即使我调用了
SMPEG_enableaudio(mp3,1)永远不会重新解析数据。这可能是一个bug/一个可疑的特性

由于我自己释放了SDL_RWops对象,freesrc参数的另一个问题是必须为0

作为将来的参考,一旦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文件在播放过程中丢失了几毫秒,并且过早中断。有些人没有。我可以大胆地再现同样的行为,所以我不知道发生了什么。我的代码中可能还有一个bug,SMPEG中可能有一个bug,或者MP3格式本身可能存在一个已知的问题。如果有人可以提供和解释的意见,这将是伟大的