289 lines
5.2 KiB
C
289 lines
5.2 KiB
C
/*
|
|
* SimpleMixer 1.1
|
|
*
|
|
* Simple sound mixing example for SDL.
|
|
*
|
|
* (C) David Olofson, 2003
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include "SDL.h"
|
|
#include "SDL_audio.h"
|
|
|
|
#define SM_SOUNDS 4
|
|
#define SM_VOICES 4
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Uint8 *data;
|
|
Uint32 length;
|
|
} SM_sound;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Sint16 *data;
|
|
int length;
|
|
int position;
|
|
int l_vol;
|
|
int r_vol;
|
|
} SM_voice;
|
|
|
|
|
|
SM_sound sounds[SM_SOUNDS];
|
|
SM_voice voices[SM_VOICES];
|
|
SDL_AudioSpec audiospec;
|
|
int die = 0;
|
|
|
|
|
|
void sm_play(unsigned voice, unsigned sound, float lvol, float rvol)
|
|
{
|
|
if(voice >= SM_VOICES || sound >= SM_SOUNDS)
|
|
return;
|
|
|
|
/* Stop voice */
|
|
voices[voice].data = NULL;
|
|
|
|
/* Reprogram */
|
|
voices[voice].length = sounds[sound].length / 2;
|
|
voices[voice].position = 0;
|
|
voices[voice].l_vol = (int)(lvol * 256.0);
|
|
voices[voice].r_vol = (int)(rvol * 256.0);
|
|
|
|
/* Start! */
|
|
voices[voice].data = (Sint16*)(sounds[sound].data);
|
|
}
|
|
|
|
|
|
static void sm_mixer(void *ud, Uint8 *stream, int len)
|
|
{
|
|
int vi, s;
|
|
Sint16 *buf = (Sint16 *)stream;
|
|
|
|
/* Clear the buffer */
|
|
memset(buf, 0, len);
|
|
|
|
/* 2 channels, 2 bytes/sample = 4 bytes/frame */
|
|
len /= 4;
|
|
|
|
/* For each voice... */
|
|
for(vi = 0; vi < SM_VOICES; ++vi)
|
|
{
|
|
SM_voice *v = &voices[vi];
|
|
if(!v->data)
|
|
continue;
|
|
|
|
/* For each sample... */
|
|
for(s = 0; s < len; ++s)
|
|
{
|
|
if(v->position >= v->length)
|
|
{
|
|
v->data = NULL;
|
|
break;
|
|
}
|
|
buf[s * 2] += v->data[v->position] * v->l_vol >> 8;
|
|
buf[s * 2 + 1] += v->data[v->position] * v->r_vol >> 8;
|
|
++v->position;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int sm_open(void)
|
|
{
|
|
SDL_AudioSpec as;
|
|
|
|
memset(sounds, 0, sizeof(sounds));
|
|
memset(voices, 0, sizeof(voices));
|
|
|
|
if(SDL_InitSubSystem(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0)
|
|
return -2;
|
|
|
|
as.freq = 44100;
|
|
as.format = AUDIO_S16SYS;
|
|
as.channels = 2;
|
|
as.samples = 1024;
|
|
as.callback = sm_mixer;
|
|
if(SDL_OpenAudio(&as, &audiospec) < 0)
|
|
return -3;
|
|
|
|
if(audiospec.format != AUDIO_S16SYS)
|
|
return -4;
|
|
|
|
SDL_PauseAudio(0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void sm_close(void)
|
|
{
|
|
int i;
|
|
SDL_PauseAudio(1);
|
|
for(i = 0; i < SM_VOICES; ++i)
|
|
voices[i].data = NULL;
|
|
SDL_CloseAudio();
|
|
for(i = 0; i < SM_SOUNDS; ++i)
|
|
SDL_FreeWAV(sounds[i].data);
|
|
memset(sounds, 0, sizeof(sounds));
|
|
memset(voices, 0, sizeof(voices));
|
|
}
|
|
|
|
|
|
void flip_endian(Uint8 *data, int length)
|
|
{
|
|
int i;
|
|
for(i = 0; i < length; i += 2)
|
|
{
|
|
int x = data[i];
|
|
data[i] = data[i + 1];
|
|
data[i + 1] = x;
|
|
}
|
|
}
|
|
|
|
|
|
int sm_load(int sound, const char *file)
|
|
{
|
|
int failed = 0;
|
|
SDL_AudioSpec spec;
|
|
if(sounds[sound].data)
|
|
SDL_FreeWAV(sounds[sound].data);
|
|
sounds[sound].data = NULL;
|
|
if(SDL_LoadWAV(file, &spec, &sounds[sound].data,
|
|
&sounds[sound].length) == NULL)
|
|
return -1;
|
|
if(spec.freq != 44100)
|
|
fprintf(stderr, "WARNING: File '%s' is not 44.1 kHz."
|
|
" Might sound weird...\n", file);
|
|
if(spec.channels != 1)
|
|
{
|
|
fprintf(stderr, "Only mono sounds are supported!\n");
|
|
failed = 1;
|
|
}
|
|
switch(spec.format)
|
|
{
|
|
case AUDIO_S16LSB:
|
|
case AUDIO_S16MSB:
|
|
if(spec.format != AUDIO_S16SYS)
|
|
flip_endian(sounds[sound].data, sounds[sound].length);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unsupported sample format!\n");
|
|
failed = 1;
|
|
break;
|
|
}
|
|
if(failed)
|
|
{
|
|
SDL_FreeWAV(sounds[sound].data);
|
|
sounds[sound].data = NULL;
|
|
return -2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void breakhandler(int a)
|
|
{
|
|
die = 1;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const char *bd = "#...#...#...#..##...#...#...#.##";
|
|
const char *cl = "....#..*....#....*..#....*..#.**";
|
|
const char *cb = "#...*..#..*...#.#...*..#..*..*#*";
|
|
const char *hh = "..#...#...#...#...#...#...#...#.";
|
|
int res;
|
|
Uint32 step = 0;
|
|
Sint32 timer;
|
|
SDL_Event event;
|
|
int color = 0;
|
|
if(SDL_Init(0) < 0)
|
|
return -1;
|
|
|
|
/*
|
|
atexit(SDL_Quit);
|
|
signal(SIGTERM, breakhandler);
|
|
signal(SIGINT, breakhandler);
|
|
*/
|
|
|
|
if(sm_open() < 0)
|
|
{
|
|
fprintf(stderr, "Couldn't start mixer!\n");
|
|
SDL_Quit();
|
|
return -1;
|
|
}
|
|
|
|
SDL_SetVideoMode(320, 200, 16, SDL_SWSURFACE);
|
|
|
|
res = 0;
|
|
res |= sm_load(0, "808-bassdrum.wav");
|
|
res |= sm_load(1, "808-clap.wav");
|
|
res |= sm_load(2, "808-cowbell.wav");
|
|
res |= sm_load(3, "808-hihat.wav");
|
|
if(res)
|
|
{
|
|
sm_close();
|
|
SDL_Quit();
|
|
fprintf(stderr, "Couldn't load sounds!\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Of course, playing this stuff would be
|
|
* much better done in the audio callback,
|
|
* since the timing out here jitters a lot.
|
|
*
|
|
* However, this is just a fun hack to get
|
|
* the thing to make some sound, without
|
|
* slapping in a game or something. :-)
|
|
*/
|
|
SDL_Delay(200);
|
|
timer = (Sint32)SDL_GetTicks();
|
|
while(!die)
|
|
{
|
|
if('#' == bd[step])
|
|
sm_play(0, 0, 1.0, 1.0);
|
|
|
|
if('#' == cl[step])
|
|
sm_play(1, 1, 0.6, 0.5);
|
|
else if('*' == cl[step])
|
|
sm_play(1, 1, 0.2, 0.3);
|
|
|
|
if('#' == cb[step])
|
|
sm_play(2, 2, 0.3, 0.2);
|
|
else if('*' == cb[step])
|
|
sm_play(2, 2, 0.1, 0.2);
|
|
|
|
if('#' == hh[step])
|
|
sm_play(3, 3, 0.3, 0.4);
|
|
|
|
step = (step + 1) % 32;
|
|
|
|
timer += 120;
|
|
|
|
while(SDL_PollEvent(&event) > 0)
|
|
{
|
|
if(event.type & (SDL_KEYUP | SDL_KEYDOWN))
|
|
{
|
|
Uint8 *keys = SDL_GetKeyState(NULL);
|
|
if(keys[SDLK_ESCAPE])
|
|
die = 1;
|
|
}
|
|
}
|
|
|
|
SDL_FillRect(SDL_GetVideoSurface(), NULL, color);
|
|
color += 10;
|
|
SDL_Flip(SDL_GetVideoSurface()); // Program is required to call SDL_Flip(), or it will invoke Application Not Responding dialog
|
|
|
|
while(((Sint32)SDL_GetTicks() - timer) < 0)
|
|
SDL_Delay(10);
|
|
}
|
|
|
|
sm_close();
|
|
SDL_Quit();
|
|
return 0;
|
|
}
|