将Mix_Chunk更改为Mix_Music在SDL2中。

huangapple go评论60阅读模式
英文:

How to change Mix_Chunk to Mix_Music in SDL2

问题

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();
英文:
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?

答案1

得分: 1

"unknown wave format" 表示 Mix_LoadMUSType_RW 函数 在解释 SDL_RWops 对象 中的数据为 WAV 文件时出现问题。这可能是由于从 Mix_Chunk 对象 创建的 SDL_RWops 对象的方式引起的。

添加了头部之后,它可以正常工作,但是:

Mix_SetMusicPosition 函数 不起作用。它显示 "Position not implemented for Music type"。

这是预期的,因为 Mix_SetMusicPosition 主要用于具有流中位置概念的音乐格式,例如 MP3 或 OGG。

但是,WAV 格式通常不用于流式传输,可能不受 Mix_SetMusicPosition 支持。

如果您想继续使用 WAV 文件,并且需要类似于 Mix_SetMusicPosition 的功能(不支持 WAV 文件),您可以通过手动计算在音频缓冲区内开始播放的位置来实现此目标。

WAV 文件 通常包含原始 PCM 音频数据,音频流中的位置通常由样本数决定。采样率(例如 44.1 kHz)告诉您每秒音频中有多少样本。

要从 WAV 文件的特定位置开始播放:

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

// ... WavHeader 结构与您的代码中的相同 ...

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;

    // 计算要从哪个字节位置开始播放
    int secondsToSkip = 3;
    int bytesPerSample = h.bitpsample / 8;
    int position = secondsToSkip * h.sample * h.channel * bytesPerSample;

    // 使用位置偏移创建新的 SDL_RWops
    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;

    // 从计算的位置开始播放音乐
    Mix_PlayMusic(musicFromPosition, 1);

    char c;
    std::cin >> c;

    // 清理
    Mix_FreeMusic(musicFromPosition);
    Mix_FreeMusic(music);
    Mix_FreeChunk(wave);
    Mix_CloseAudio();
    free(wd);

    return 0;
}

这意味着您需要计算 WAV 数据中希望开始播放的字节位置。然后,从该位置创建一个新的 SDL_RWops 并将其加载为 Mix_Music。这应该会从所需位置开始播放 WAV 文件。请注意,此方法涉及将 WAV 文件加载到内存两次,对于大文件可能效率低下。

英文:

"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 &lt;iostream&gt;
#include &lt;SDL2/SDL.h&gt;
#include &lt;SDL2/SDL_mixer.h&gt;

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

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

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

    Mix_Music *music = Mix_LoadMUSType_RW(p, MUS_WAV, 1);
    if (!music) std::cout &lt;&lt; &quot; loadMus error &quot; &lt;&lt; SDL_GetError() &lt;&lt; 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-&gt;alen - position);
    Mix_Music *musicFromPosition = Mix_LoadMUSType_RW(rw, MUS_WAV, 1);
    if (!musicFromPosition) std::cout &lt;&lt; &quot; loadMus error &quot; &lt;&lt; SDL_GetError() &lt;&lt; std::endl;

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

    char c;
    std::cin &gt;&gt; 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.

答案2

得分: 1

我不需要翻译的代码部分:

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

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

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

英文:

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] = {&#39;R&#39;, &#39;I&#39;, &#39;F&#39;, &#39;F&#39;};
uint32_t size = 0;
char type[4] = {&#39;W&#39;, &#39;A&#39;, &#39;V&#39;, &#39;E&#39;};
char fmt[4] = {&#39;f&#39;, &#39;m&#39;, &#39;t&#39;, &#39; &#39;};
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] = {&#39;d&#39;, &#39;a&#39;, &#39;t&#39;, &#39;a&#39;};
uint32_t datasize = 0;
WavHeader(int sz) {
datasize = sz;
size = sz + 44;
}
};
int main(int argc, char* argv[]) 
{
if (SDL_Init(SDL_INIT_AUDIO) &lt; 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-&gt;alen};
void *wd = malloc(wave-&gt;alen + 44);
memcpy(wd, &amp;h, 44);
memcpy(wd + 44, wave-&gt;abuf, wave-&gt;alen);
if(!wave) cout &lt;&lt; SDL_GetError() &lt;&lt; endl;
auto *p = SDL_RWFromMem(wd, wave-&gt;alen + 44);
if(!p) cout &lt;&lt; SDL_GetError() &lt;&lt; endl;
Mix_Music *music = Mix_LoadMUSType_RW(p, MUS_WAV, 0);
if(!music) cout &lt;&lt;&quot; loasMus error &quot; &lt;&lt; SDL_GetError() &lt;&lt; endl;
Mix_PlayMusic(music, 2);
//Mix_PlayChannel(-1, wave, 1);
if(Mix_SetMusicPosition(3) == -1) cout &lt;&lt; SDL_GetError() &lt;&lt; endl;;
char c;
cin &gt;&gt; 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".

huangapple
  • 本文由 发表于 2023年6月27日 17:53:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76563665.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定