SDL_mixer函数Mix_LoadMUS_RW导致访问冲突

时间:2015-06-30 08:27:20

标签: c++ sdl-2 sdl-mixer

我在使用SDL_mixer从内存加载音乐时遇到问题。 以下“最小”示例(包括一些错误检查)将始终因Music :: play中的访问冲突而崩溃。

#include <SDL\SDL_mixer.h>
#include <SDL\SDL.h>
#include <vector>
#include <iostream>
#include <string>
#include <fstream>

class Music {
public:
    void play(int loops = 1);
    SDL_RWops* m_rw;
    std::vector<unsigned char> m_file;
    Mix_Music * m_music = nullptr;
};

void Music::play(int loops) {
    if (Mix_PlayMusic(m_music, loops) == -1)
        std::cout << "Error playing music " + std::string(Mix_GetError()) + " ...\n";
}

void readFileToBuffer(std::vector<unsigned char>& buffer, std::string filePath) {
    std::ifstream file(filePath, std::ios::binary);

    file.seekg(0, std::ios::end);
    int fileSize = file.tellg();
    file.seekg(0, std::ios::beg);
    fileSize -= file.tellg();

    buffer.resize(fileSize);
    file.read((char *)&(buffer[0]), fileSize);

    file.close();
}

void writeFileToBuffer(std::vector<unsigned char>& buffer, std::string filePath) {
    std::ofstream file(filePath, std::ios::out | std::ios::binary);
    for (size_t i = 0; i < buffer.size(); i++)
        file << buffer[i];
    file.close();
}

Music loadMusic(std::string filePath) {
    Music music;

    readFileToBuffer(music.m_file, filePath);
    music.m_rw = SDL_RWFromMem(&music.m_file[0], music.m_file.size());

    // Uncommenting the next block runs without problems
    /*
    writeFileToBuffer(music.m_file, filePath);
    music.m_rw = SDL_RWFromFile(filePath.c_str(), "r");
    */

    if (music.m_rw == nullptr)
        std::cout << "Error creating RW " + std::string(Mix_GetError()) + " ...\n";

    music.m_music = Mix_LoadMUSType_RW(music.m_rw, Mix_MusicType::MUS_OGG, SDL_FALSE);

    if (music.m_music == nullptr)
        std::cout << "Error creating music " + std::string(Mix_GetError()) + " ...\n";

    return music;
}

int main(int argc, char** argv) {
    SDL_Init(SDL_INIT_AUDIO);
    Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG);
    Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024);

    Music music = loadMusic("Sound/music/XYZ.ogg");

    music.play();

    std::cin.ignore();

    return 0;
}

我的ArchiveManager确实可以工作,也可以看到,因为ucomment将缓冲区写入文件并从中创建SDL_RW的块将运行正常。 我加载的音乐文件只是假设是一个ogg文件,在这种情况下,因此从文件创建SDL_RW工作正常。意思是什么都没有崩溃,音乐正常播放开始结束。

音乐课是我的理解太大了。我只是保持缓冲区m_file以及SDL_RW,以确保问题不是来自被释放的数据。使用SDL_FALSE运行Mix_LoadMUS_RW还应确保不释放RW。

值得注意的是,使用Mix_LoadWAV_RW从同一档案中加载wav文件的类似示例可以正常工作:

Mix_Chunk * chunk;
std::vector<unsigned char> fileBuf = ArchiveManager::loadFileFromArchive(filePath);
chunk = Mix_LoadWAV_RW(SDL_RWFromConstMem(&fileBuf[0], fileBuf.size()), SDL_TRUE);

在这里,我甚至没有保持缓冲区,直到调用Mix_PlayCannel。另外在这里我用SDL_TRUE调用load函数,因为我没有创建一个显式的SDL_RW。尝试加载音乐时类似的东西也无济于事。

我研究了SDL_mixer源代码,但它对我没有帮助。也许我的知识还不够,或者我错过了至关重要的事情。

要明白:访问违规的来源是什么,我该如何预防?

编辑:更改了示例代码,以便任何人都可以直接重现它。所以没有ArchiveManager或类似的东西,只需将ogg直接读入内存。关键部分只是loadMusic中的几行。

1 个答案:

答案 0 :(得分:0)

Music music = loadMusic("Sound/music/XYZ.ogg");
music.play();

第一行会将右侧 class Music 类型的对象复制到名为 music 的新对象中。这将导致向量 m_file 被复制,包括其中的数据。我们的新对象 music 的向量数据显然将存储在与 loadMusic 返回的对象向量不同的内存位置。然后, loadMusic 返回的对象将从堆栈中删除,并且它的向量数据将被释放,从而使先前创建的 Mix_Music 对象无效并导致访问违反第二行。

这可以通过仅创建一个 Music 对象来解决,例如通过堆上的 new 创建它并让 loadMusic 返回指向该对象的指针。

Music* music = loadMusic("Sound/music/XYZ.ogg");
music->play();

无论如何,在堆上而不是在堆栈上为整个文件分配内存可能是更好的选择,尽管我猜测向量会在内部执行此操作。

如此短暂的版本,它(我认为)是一个新手的错误,而且我过分注重责备SDL_Mixer。不好的主意。