2
if (SDL_Init(SDL_INIT_AUDIO) < 0) return -1;
        
if( Mix_OpenAudio( 48000, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1 ) return -1; 
Mix_Chunk *wave = Mix_LoadWAV("a.wav");
auto *p = SDL_RWFromMem(wave->abuf, wave->alen);
if(!p || !wave) return -1;
Mix_Music *music = Mix_LoadMUSType_RW(p, MUS_WAV, 0);
if(!music) cout <<"load Mus error " << SDL_GetError() << endl;
Mix_PlayMusic(music, 2);
//Mix_PlayChannel(-1, wave, 1);
char c;
cin >> c;

Mix_FreeMusic(music);
Mix_FreeChunk(wave);
Mix_CloseAudio();

I want to manipulate some wave data and store it in Mix_Music format with SDL2_Mixer. Above code gives me "load Mus error unknown wave format" error.

commented Mix_PlayChannel function works fine.

How can I change this code to make Mix_PlayMusic function work?

Zeta
  • 913
  • 10
  • 24

2 Answers2

1

"unknown wave format" should mean the Mix_LoadMUSType_RW function is having trouble interpreting the data from the SDL_RWops object as a WAV file.
This could be due to the way the SDL_RWops object is created from the Mix_Chunk object.

Once you have added the header, it works, but:

Mix_SetMusicPosition function does not work. It says "Position not implemented for Music type".

Which... is expected, considering Mix_SetMusicPosition is primarily intended for music formats that have a notion of position within the stream, such as MP3 or OGG.
However, the WAV format is not typically used for streaming and might not be supported by Mix_SetMusicPosition.

If you would like to continue using WAV files, and you need functionality similar to Mix_SetMusicPosition (which is not supported for WAV files), you can achieve this by manually calculating the position to start playing within the audio buffer.

WAV files usually contain raw PCM audio data, and the position within the audio stream is typically determined by the number of samples. The sample rate (e.g. 44.1 kHz) tells you how many samples are in one second of audio.

To start playing from a specific position within the WAV file:

#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>

// ... WavHeader structure as in your code ...

int main(int argc, char* argv[]) 
{
    if (SDL_Init(SDL_INIT_AUDIO) < 0) return -1;
    if( Mix_OpenAudio(48000, MIX_DEFAULT_FORMAT, 2, 4096) == -1 ) return -1; 

    Mix_Chunk *wave = Mix_LoadWAV("a.wav");
    WavHeader h{wave->alen};
    void *wd = malloc(wave->alen + 44);
    memcpy(wd, &h, 44);
    memcpy((char *)wd + 44, wave->abuf, wave->alen);
    
    if (!wave) std::cout << SDL_GetError() << std::endl;
    auto *p = SDL_RWFromMem(wd, wave->alen + 44);
    if (!p) std::cout << SDL_GetError() << std::endl;

    Mix_Music *music = Mix_LoadMUSType_RW(p, MUS_WAV, 1);
    if (!music) std::cout << " loadMus error " << SDL_GetError() << std::endl;

    // Calculate the byte position to start playing from
    int secondsToSkip = 3;
    int bytesPerSample = h.bitpsample / 8;
    int position = secondsToSkip * h.sample * h.channel * bytesPerSample;

    // Create new SDL_RWops with the position offset
    SDL_RWops *rw = SDL_RWFromMem((char *)wd + 44 + position, wave->alen - position);
    Mix_Music *musicFromPosition = Mix_LoadMUSType_RW(rw, MUS_WAV, 1);
    if (!musicFromPosition) std::cout << " loadMus error " << SDL_GetError() << std::endl;

    // Play the music from the calculated position
    Mix_PlayMusic(musicFromPosition, 1);

    char c;
    std::cin >> c;

    // Clean up
    Mix_FreeMusic(musicFromPosition);
    Mix_FreeMusic(music);
    Mix_FreeChunk(wave);
    Mix_CloseAudio();
    free(wd);

    return 0;
}

Meaning: you would need to calculate the byte position within the WAV data where you want to start playing.
Then, create a new SDL_RWops from that position and load it as Mix_Music. This should start playing the WAV file from the desired position. Please note that this approach involves loading the WAV file into memory twice, which could be inefficient for large files.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I manually added header to the front of wav data. But there comes another problem, It can't set music play position. Please see my own answer. Could you solve this problem? – Zeta Jun 30 '23 at 10:51
  • @Zeta OK. I have rewritten the answer to address your comment. – VonC Jun 30 '23 at 19:50
  • It might work. But, copying memory every setposition will cost too much in my case. I just changed wav format to ogg format with vorbis library and was able to setPosition. Thanks. – Zeta Jul 02 '23 at 04:48
1

I had to add Wav header to make it work properly.

I did this manually, but, there might be a better way.

struct WavHeader
{
    char riff[4] = {'R', 'I', 'F', 'F'};
    uint32_t size = 0;
    char type[4] = {'W', 'A', 'V', 'E'};
    char fmt[4] = {'f', 'm', 't', ' '};
    uint32_t len = 16;
    uint16_t pcm = 1;
    uint16_t channel = 2;
    uint32_t sample = 48000;
    uint32_t sam = 48000 * 16 * 2 / 8;
    uint16_t bitstereo = 4;
    uint16_t bitpsample = 16;
    char data[4] = {'d', 'a', 't', 'a'};
    uint32_t datasize = 0;
    WavHeader(int sz) {
        datasize = sz;
        size = sz + 44;
    }
};

int main(int argc, char* argv[]) 
{
    if (SDL_Init(SDL_INIT_AUDIO) < 0) return -1;
    if( Mix_OpenAudio( 48000, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1 ) return -1; 

    Mix_Chunk *wave = Mix_LoadWAV(WAV_PATH);
    WavHeader h{wave->alen};
    void *wd = malloc(wave->alen + 44);
    memcpy(wd, &h, 44);
    memcpy(wd + 44, wave->abuf, wave->alen);
    if(!wave) cout << SDL_GetError() << endl;
    auto *p = SDL_RWFromMem(wd, wave->alen + 44);
    if(!p) cout << SDL_GetError() << endl;

    Mix_Music *music = Mix_LoadMUSType_RW(p, MUS_WAV, 0);
    if(!music) cout <<" loasMus error " << SDL_GetError() << endl;
    Mix_PlayMusic(music, 2);
    //Mix_PlayChannel(-1, wave, 1);
    
    if(Mix_SetMusicPosition(3) == -1) cout << SDL_GetError() << endl;;

    char c;
    cin >> c;

    Mix_FreeMusic(music);
    Mix_FreeChunk(wave);
    Mix_CloseAudio();
    free(wd);
    
    return 0;
}

But there is another problem, Mix_SetMusicPosition function does not work. It says "Position not implemented for Music type".

halfer
  • 19,824
  • 17
  • 99
  • 186
Zeta
  • 913
  • 10
  • 24