主机环境:Win7
SDL版本:SDL2.0.3
开发环境:CodeBlocks13.12
关于SDL音频播放的分析可以查看SDL2源代码中test目录下的loopwave.c文件来帮助我们学习,源代码如下
/* Copyright (C) 1997-2014 Sam Lantinga <[email protected]> This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. */ /* Program to load a wave file and loop playing it using SDL sound */ /* loopwaves.c is much more robust in handling WAVE files -- This is only for simple WAVEs */ #include "SDL_config.h" #include <stdio.h> #include <stdlib.h> #if HAVE_SIGNAL_H #include <signal.h> #endif #include "SDL.h" #include "SDL_audio.h" struct { SDL_AudioSpec spec; Uint8 *sound; /* Pointer to wave data */ Uint32 soundlen; /* Length of wave data */ int soundpos; /* Current play position */ } wave; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) { SDL_Quit(); exit(rc); } void SDLCALL fillerup(void *unused, Uint8 * stream, int len) { Uint8 *waveptr; int waveleft; /* Set up the pointers */ waveptr = wave.sound + wave.soundpos; waveleft = wave.soundlen - wave.soundpos; /* Go! */ while (waveleft <= len) { SDL_memcpy(stream, waveptr, waveleft); stream += waveleft; len -= waveleft; waveptr = wave.sound; waveleft = wave.soundlen; wave.soundpos = 0; } SDL_memcpy(stream, waveptr, len); wave.soundpos += len; } static int done = 0; void poked(int sig) { done = 1; } int main(int argc, char *argv[]) { int i; char filename[4096]; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); /* Load the SDL library */ if (SDL_Init(SDL_INIT_AUDIO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return (1); } if (argc >= 1) { SDL_strlcpy(filename, argv[1], sizeof(filename)); } else { SDL_strlcpy(filename, "sample.wav", sizeof(filename)); } /* Load the wave file into memory */ if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", argv[1], SDL_GetError()); quit(1); } wave.spec.callback = fillerup; #if HAVE_SIGNAL_H /* Set the signals */ #ifdef SIGHUP signal(SIGHUP, poked); #endif signal(SIGINT, poked); #ifdef SIGQUIT signal(SIGQUIT, poked); #endif signal(SIGTERM, poked); #endif /* HAVE_SIGNAL_H */ /* Show the list of available drivers */ SDL_Log("Available audio drivers:"); for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) { SDL_Log("%i: %s", i, SDL_GetAudioDriver(i)); } /* Initialize fillerup() variables */ if (SDL_OpenAudio(&wave.spec, NULL) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); SDL_FreeWAV(wave.sound); quit(2); } SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver()); /* Let the audio run */ SDL_PauseAudio(0); while (!done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING)) SDL_Delay(1000); /* Clean up on signal */ SDL_CloseAudio(); SDL_FreeWAV(wave.sound); SDL_Quit(); return (0); } /* vi: set ts=4 sw=4 expandtab: */
/* Copyright (C) 1997-2014 Sam Lantinga <[email protected]> This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. */ /* Program to load a wave file and loop playing it using SDL sound */ /* loopwaves.c is much more robust in handling WAVE files -- This is only for simple WAVEs */ #include <SDL2/SDL.h> #include <SDL2/SDL_audio.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> struct { SDL_AudioSpec spec; Uint8 *sound; /* Pointer to wave data */ Uint32 soundlen; /* Length of wave data */ int soundpos; /* Current play position */ } wave; static int done = 0; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) { SDL_Quit(); exit(rc); } void SDLCALL fillerup(void *unused, Uint8 * stream, int len) { Uint8 *waveptr; int waveleft; printf("callback len:%d\n",len); /* Set up the pointers */ waveptr = wave.sound + wave.soundpos; waveleft = wave.soundlen - wave.soundpos; /* Go! */ while (waveleft <= len) { SDL_memcpy(stream, waveptr, waveleft); SDL_PauseAudio(1); return; } SDL_memcpy(stream, waveptr, len); wave.soundpos += len; } int main(int argc, char *argv[]) { int i; char filename[4096]="sample.wav"; /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); /* Load the SDL library */ if (SDL_Init(SDL_INIT_AUDIO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); return (1); } /* Load the wave file into memory */ if (SDL_LoadWAV(filename, &wave.spec, &wave.sound, &wave.soundlen) == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s\n", argv[1], SDL_GetError()); quit(1); } wave.spec.callback = fillerup;//设置回调函数 /* Show the list of available drivers */ SDL_Log("Available audio drivers:"); for (i = 0; i < SDL_GetNumAudioDrivers(); ++i) { SDL_Log("%i: %s", i, SDL_GetAudioDriver(i)); } /* Initialize fillerup() variables */ if (SDL_OpenAudio(&wave.spec, NULL) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); SDL_FreeWAV(wave.sound); quit(2); } SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver()); printf("SDL_AudioSpec.samples:%d",wave.spec.samples); printf("SDL_AudioSpec sample size:%d",wave.spec.format);//AUDIO_S16LSB printf("SDL_AudioSpec.channels:%d",wave.spec.channels); printf("SDL_AudioSpec.size:%d",wave.spec.size);//跟回调函数的len相等应该是样品数*样品所占字节数 /* Let the audio run */ SDL_PauseAudio(0); while ((SDL_GetAudioStatus() == SDL_AUDIO_PLAYING))//获取音频状态 SDL_Delay(1000); /* Clean up on signal */ SDL_CloseAudio();//关掉音频进程以及音频设备 SDL_FreeWAV(wave.sound);//释放数据由SDL_LoadWAV申请的 SDL_Quit(); printf("=========over==========\n"); system("pause"); return (0); } /* vi: set ts=4 sw=4 expandtab: */
int |
freq |
DSP frequency (samples per second); see Remarks for details |
SDL_AudioFormat |
format |
audio data format; see Remarks for details |
Uint8 |
channels |
number of separate sound channels: see Remarks for details |
Uint8 |
silence |
audio buffer silence value (calculated) |
Uint16 |
samples |
audio buffer size in samples (power of 2); see Remarks for details |
Uint32 |
size |
audio buffer size in bytes (calculated) |
SDL_AudioCallback |
callback |
the function to call when the audio device needs more data; see Remarks for details |
void* |
userdata |
a pointer that is passed to callback (otherwise ignored by SDL) |
freq:音频采样率,如22025、44100等
format:播放的音频格式8位、16位、32位、浮点数等一般16位最常用
channels:音频通道数如 1 (mono), 2 (stereo), 4 (quad), and 6 (5.1)
samples:样品数,因为音频数据不可能一下子播放完,需分段播放,样品数定义了分段的大小
size:音频样品所占的字节数,该值可通过音频格式、通道数、样品数计算可得size=channels*format*samples,如格式为16位,通道数为2,样品数为1024,则size=2*2*1024=4096
callback:音频播放的回调函数,当样品播放完毕后,SDL2需要更多的音频数据,会调用该函数
userdata:用户数据,通过callback回调函数传递给用户,如果不需要该参数可忽略。
loopwav.c代码很简单,以修改过后的代码来查看,使用SDL_loadWAV函数来加载sample.wav文件,该函数会给SDL_AudioSpec结构体赋值,同时获取了音频数据指针以wave.sound及音频数据大小wave.soundlen,之后设置SDL_AudioSpec的回调函数为fillerup,接下来根据SDL_AudioSpec参数打开音频设备
/* Initialize fillerup() variables */ if (SDL_OpenAudio(&wave.spec, NULL) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open audio: %s\n", SDL_GetError()); SDL_FreeWAV(wave.sound); quit(2); }
由图可知样品数为4096,音频格式是32784=0x8010,根据SDl2中有关音频格式的说明如下所示:
/** * \brief Audio format flags. * * These are what the 16 bits in SDL_AudioFormat currently mean... * (Unspecified bits are always zero). * * \verbatim ++-----------------------sample is signed if set || || ++-----------sample is bigendian if set || || || || ++---sample is float if set || || || || || || +---sample bit size---+ || || || | | 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 \endverbatim * * There are macros in SDL 2.0 and later to query these bits. */
void SDL_AudioCallback(void* userdata, Uint8* stream, int len)
void SDLCALL fillerup(void *unused, Uint8 * stream, int len) { Uint8 *waveptr; int waveleft; printf("callback len:%d\n",len); /* Set up the pointers */ waveptr = wave.sound + wave.soundpos; waveleft = wave.soundlen - wave.soundpos; /* Go! */ while (waveleft <= len) { SDL_memcpy(stream, waveptr, waveleft); SDL_PauseAudio(1); return; } SDL_memcpy(stream, waveptr, len); wave.soundpos += len; }
主程序中我们获取了音频播放的状态,如果不是播放状态就关闭音频设备,释放音频数据,终止程序运行,
typedef enum { SDL_AUDIO_STOPPED = 0, SDL_AUDIO_PLAYING, SDL_AUDIO_PAUSED } SDL_AudioStatus; extern DECLSPEC SDL_AudioStatus SDLCALL SDL_GetAudioStatus(void);
有关SDL_mixer的使用可以查看该指导:http://lazyfoo.net/tutorials/SDL/21_sound_effects_and_music/index.php