Ogg, mp3 audio format support added.
This commit is contained in:
parent
dc21cba1fe
commit
f38cc57127
6 changed files with 415 additions and 6 deletions
|
@ -34,7 +34,7 @@ MODULE = sound
|
|||
HDRS = libcda.h
|
||||
|
||||
SRCS = sound.c sound_server.c ccl_sound.c sound_id.c \
|
||||
flac.c wav.c unitsound.c libcda.c oss_audio.c sdl_audio.c
|
||||
mad.c ogg.c flac.c wav.c unitsound.c libcda.c oss_audio.c sdl_audio.c
|
||||
|
||||
OBJS = $(addprefix $(OBJDIR), $(SRCS:.c=.$(OE)))
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ local void FLAC_metadata_callback(const FLAC__StreamDecoder * stream,
|
|||
**
|
||||
** @return Error status.
|
||||
*/
|
||||
static FLAC__StreamDecoderWriteStatus FLAC_write_callback(const
|
||||
local FLAC__StreamDecoderWriteStatus FLAC_write_callback(const
|
||||
FLAC__StreamDecoder * stream, const FLAC__Frame * frame,
|
||||
const FLAC__int32 * buffer[], void *user)
|
||||
{
|
||||
|
@ -207,6 +207,7 @@ global Sample* LoadFlac(const char* name)
|
|||
}
|
||||
CLread(f,&magic,sizeof(magic));
|
||||
if( magic!=0x43614C66 ) { // "fLaC" in ASCII
|
||||
CLclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -259,7 +260,7 @@ global Sample* LoadFlac(const char* name)
|
|||
FLAC__stream_decoder_delete(stream);
|
||||
CLclose(f);
|
||||
|
||||
DebugLevel0Fn(" %d\n", user.Sample->Length);
|
||||
DebugLevel3Fn(" %d\n", user.Sample->Length);
|
||||
IfDebug( AllocatedSoundMemory+=user.Sample->Length; );
|
||||
|
||||
return user.Sample;
|
||||
|
|
229
src/sound/mad.cpp
Normal file
229
src/sound/mad.cpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
// ___________ _________ _____ __
|
||||
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
|
||||
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
|
||||
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
|
||||
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
|
||||
// \/ \/ \/ \/ \/
|
||||
// ______________________ ______________________
|
||||
// T H E W A R B E G I N S
|
||||
// FreeCraft - A free fantasy real time strategy game engine
|
||||
//
|
||||
/**@name mad.c - mp3 support with libmad */
|
||||
//
|
||||
// (c) Copyright 2002 by Lutz Sammer
|
||||
//
|
||||
// FreeCraft is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation; only version 2 of the License.
|
||||
//
|
||||
// FreeCraft is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
//@{
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Includes
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freecraft.h"
|
||||
|
||||
#if defined(WITH_SOUND) && defined(USE_MAD) // {
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "mad.h"
|
||||
|
||||
#include "iolib.h"
|
||||
#include "sound_server.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Declaration
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
** My user data for mad callbacks.
|
||||
*/
|
||||
typedef struct _my_user_ {
|
||||
CLFile* File; // File handle
|
||||
Sample* Sample; // Sample buffer
|
||||
char Buffer[4096]; // Decoded buffer
|
||||
} MyUser;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
** MAD read callback.
|
||||
**
|
||||
** @param user Our user pointer.
|
||||
** @param stream MP3 stream.
|
||||
**
|
||||
** @return MAP_FLOW_STOP if eof, MAD_FLOW_CONTINUE otherwise.
|
||||
*/
|
||||
local enum mad_flow MAD_read(void *user, struct mad_stream *stream)
|
||||
{
|
||||
int i;
|
||||
CLFile *f;
|
||||
MyUser *u;
|
||||
|
||||
DebugLevel3Fn("Read callback\n");
|
||||
|
||||
u = (MyUser *) user;
|
||||
f = u->File;
|
||||
|
||||
if ((i = CLread(f, u->Buffer, sizeof(u->Buffer))) != sizeof(u->Buffer)) {
|
||||
if (!i) {
|
||||
return MAD_FLOW_STOP;
|
||||
}
|
||||
}
|
||||
DebugLevel3Fn("%d bytes\n", i);
|
||||
mad_stream_buffer(stream, u->Buffer, i);
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
** This is the output callback function. It is called after each frame of
|
||||
** MPEG audio data has been completely decoded. The purpose of this
|
||||
** callback is to output the decoded PCM audio.
|
||||
*/
|
||||
local enum mad_flow MAD_write(void *user, struct mad_header const *header,
|
||||
struct mad_pcm *pcm)
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
int c;
|
||||
int channels;
|
||||
Sample* sample;
|
||||
short* p;
|
||||
|
||||
n=pcm->length;
|
||||
channels=pcm->channels;
|
||||
|
||||
DebugLevel3Fn("%d channels %d samples\n",channels,n);
|
||||
|
||||
sample = ((MyUser *) user)->Sample;
|
||||
|
||||
if( !sample->SampleSize ) {
|
||||
sample->Frequency = pcm->samplerate;
|
||||
sample->Channels = channels;
|
||||
sample->SampleSize = 16;
|
||||
}
|
||||
|
||||
i = n * channels * 2;
|
||||
|
||||
((MyUser *) user)->Sample = sample =
|
||||
realloc(sample, sizeof(*sample) + sample->Length + i);
|
||||
if (!sample) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
CLclose(((MyUser *) user)->File);
|
||||
exit(-1);
|
||||
}
|
||||
p = (short*)(sample->Data + sample->Length);
|
||||
sample->Length += i;
|
||||
|
||||
for( i=0; i<n; ++i ) {
|
||||
for( c=0; c<channels; ++c ) {
|
||||
mad_fixed_t b;
|
||||
|
||||
b = pcm->samples[c][i];
|
||||
// round
|
||||
b += (1L << (MAD_F_FRACBITS - 16));
|
||||
// clip
|
||||
if (b >= MAD_F_ONE) {
|
||||
b = MAD_F_ONE - 1;
|
||||
} else if (b < -MAD_F_ONE) {
|
||||
b = -MAD_F_ONE;
|
||||
}
|
||||
// quantize
|
||||
*p++ = b >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
}
|
||||
|
||||
return MAD_FLOW_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
** This is the error callback function. It is called whenever a decoding
|
||||
** error occurs. The error is indicated by stream->error; the list of
|
||||
** possible MAD_ERROR_* errors can be found in the mad.h (or
|
||||
** libmad/stream.h) header file.
|
||||
*/
|
||||
local enum mad_flow MAD_error(void *user, struct mad_stream *stream,
|
||||
struct mad_frame *frame)
|
||||
{
|
||||
fprintf(stderr, "decoding error 0x%04x (%s)\n",
|
||||
stream->error, mad_stream_errorstr(stream));
|
||||
|
||||
return MAD_FLOW_BREAK;
|
||||
}
|
||||
|
||||
/**
|
||||
** Load mp3.
|
||||
**
|
||||
** @param name File name.
|
||||
**
|
||||
** @return Returns the loaded sample.
|
||||
*/
|
||||
global Sample *LoadMp3(const char* name)
|
||||
{
|
||||
MyUser user;
|
||||
CLFile* f;
|
||||
unsigned char magic[2];
|
||||
struct mad_decoder decoder;
|
||||
Sample* sample;
|
||||
|
||||
if (!(f = CLopen(name))) {
|
||||
fprintf(stderr, "Can't open file `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
CLread(f, &magic, sizeof(magic));
|
||||
// 0xFF 0xE? for mp3 stream
|
||||
if (magic[0] != 0xFF || (magic[1]&0xE0) != 0xE0 ) {
|
||||
CLclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// FIXME: ugly way to rewind.
|
||||
CLclose(f);
|
||||
if (!(f = CLopen(name))) {
|
||||
fprintf(stderr, "Can't open file `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DebugLevel2Fn("Have mp3 file %s\n", name);
|
||||
|
||||
sample = malloc(sizeof(*sample));
|
||||
sample->Channels = 0;
|
||||
sample->SampleSize = 0;
|
||||
sample->Frequency = 0;
|
||||
sample->Length = 0;
|
||||
|
||||
// configure input, output, and error functions
|
||||
user.File = f;
|
||||
user.Sample = sample;
|
||||
|
||||
mad_decoder_init(&decoder, &user,
|
||||
MAD_read, 0 /* header */, 0 /* filter */, MAD_write,
|
||||
MAD_error, 0 /* message */);
|
||||
|
||||
// start decoding
|
||||
|
||||
mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
|
||||
|
||||
// release the decoder
|
||||
|
||||
mad_decoder_finish(&decoder);
|
||||
|
||||
CLclose(f);
|
||||
|
||||
return user.Sample;
|
||||
}
|
||||
|
||||
#endif // } WITH_SOUND && USE_MAD
|
||||
|
||||
//@}
|
168
src/sound/ogg.cpp
Normal file
168
src/sound/ogg.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
// ___________ _________ _____ __
|
||||
// \_ _____/______ ____ ____ \_ ___ \____________ _/ ____\/ |_
|
||||
// | __) \_ __ \_/ __ \_/ __ \/ \ \/\_ __ \__ \\ __\\ __\
|
||||
// | \ | | \/\ ___/\ ___/\ \____| | \// __ \| | | |
|
||||
// \___ / |__| \___ >\___ >\______ /|__| (____ /__| |__|
|
||||
// \/ \/ \/ \/ \/
|
||||
// ______________________ ______________________
|
||||
// T H E W A R B E G I N S
|
||||
// FreeCraft - A free fantasy real time strategy game engine
|
||||
//
|
||||
/**@name ogg.c - ogg support */
|
||||
//
|
||||
// (c) Copyright 2002 by Lutz Sammer
|
||||
//
|
||||
// FreeCraft is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published
|
||||
// by the Free Software Foundation; only version 2 of the License.
|
||||
//
|
||||
// FreeCraft is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
//@{
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Includes
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freecraft.h"
|
||||
|
||||
#if defined(WITH_SOUND) && defined(USE_OGG) // {
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "vorbis/codec.h"
|
||||
#include "vorbis/vorbisfile.h"
|
||||
|
||||
#include "iolib.h"
|
||||
#include "sound_server.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Declaration
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
-- Functions
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
** OGG vorbis read callback.
|
||||
*/
|
||||
local size_t OGG_read(void *ptr, size_t size, size_t nmemb, void *user)
|
||||
{
|
||||
|
||||
return CLread(user, ptr, size * nmemb) / size;
|
||||
}
|
||||
|
||||
/**
|
||||
** OGG vorbis seek callback.
|
||||
*/
|
||||
local int OGG_seek(void* user,int64_t offset,int whence)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
** OGG vorbis tell callback.
|
||||
*/
|
||||
local long OGG_tell(void* user)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
** OGG vorbis close callback.
|
||||
*/
|
||||
local int OGG_close(void* user)
|
||||
{
|
||||
return CLclose(user);
|
||||
}
|
||||
|
||||
/**
|
||||
** Load ogg.
|
||||
**
|
||||
** @param name File name.
|
||||
**
|
||||
** @return Returns the loaded sample.
|
||||
**
|
||||
** @todo FIXME: Should rewrite loop and the sample structure.
|
||||
*/
|
||||
global Sample *LoadOgg(const char* name)
|
||||
{
|
||||
CLFile* f;
|
||||
Sample* sample;
|
||||
OggVorbis_File vf[1];
|
||||
unsigned long magic;
|
||||
vorbis_info* info;
|
||||
static const ov_callbacks vc = { OGG_read, OGG_seek, OGG_close, OGG_tell };
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
if (!(f = CLopen(name))) {
|
||||
fprintf(stderr, "Can't open file `%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
CLread(f, &magic, sizeof(magic));
|
||||
if (magic != 0x5367674F) { // "OggS" in ASCII
|
||||
CLclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DebugLevel2Fn("Have ogg file %s\n", name);
|
||||
|
||||
if (ov_open_callbacks(f, vf, (char *)&magic, sizeof(magic), vc)) {
|
||||
fprintf(stderr, "Can't initialize ogg decoder\n");
|
||||
CLclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = ov_info(vf, -1);
|
||||
if( !info ) {
|
||||
fprintf(stderr, "no ogg stream\n");
|
||||
CLclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sample = malloc(sizeof(*sample));
|
||||
sample->Channels = info->channels;
|
||||
sample->SampleSize = 16;
|
||||
sample->Frequency = info->rate;
|
||||
n = sample->Length = 0;
|
||||
p = sample->Data;
|
||||
|
||||
for (;;) {
|
||||
int bitstream;
|
||||
int i;
|
||||
|
||||
if (n < 4096) {
|
||||
sample = realloc(sample, sizeof(*sample) + sample->Length + 8192);
|
||||
p = sample->Data + sample->Length;
|
||||
n = 8192;
|
||||
}
|
||||
|
||||
i = ov_read(vf, p, 4096, 0, 2, 1, &bitstream);
|
||||
if (i <= 0) {
|
||||
break;
|
||||
}
|
||||
p += i;
|
||||
sample->Length += i;
|
||||
n -= i;
|
||||
}
|
||||
sample = realloc(sample, sizeof(*sample) + sample->Length);
|
||||
|
||||
ov_clear(vf);
|
||||
|
||||
DebugLevel3Fn(" %d\n", sample->Length);
|
||||
IfDebug( AllocatedSoundMemory += sample->Length; );
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
#endif // } WITH_SOUND && USE_OGG
|
||||
|
||||
//@}
|
|
@ -938,15 +938,25 @@ local Sample* LoadSample(const char* name)
|
|||
char* buf;
|
||||
|
||||
buf = strdcat3(FreeCraftLibPath, "/sounds/", name);
|
||||
#ifdef USE_OGG
|
||||
if( (sample=LoadOgg(buf)) ) {
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FLAC
|
||||
if( (sample=LoadFlac(buf)) ) {
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_MAD
|
||||
if( (sample=LoadMp3(buf)) ) {
|
||||
return sample;
|
||||
}
|
||||
#endif
|
||||
if( !(sample=LoadWav(buf)) ) {
|
||||
fprintf(stderr,"Can't load the sound `%s'\n",name);
|
||||
}
|
||||
// FIXME: should support more sample formats, ogg and flac.
|
||||
// FIXME: should support more sample formats, mp3.
|
||||
free(buf);
|
||||
return sample;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
** @return Returns the loaded sample.
|
||||
**
|
||||
** @note A second wav loader is in libmodplug!
|
||||
**
|
||||
** @todo Add ADPCM loading support!
|
||||
*/
|
||||
global Sample* LoadWav(const char* name)
|
||||
{
|
||||
|
@ -214,8 +216,7 @@ global Sample* LoadWav(const char* name)
|
|||
|
||||
CLclose(f);
|
||||
|
||||
IfDebug(AllocatedSoundMemory += sample->Length;
|
||||
);
|
||||
IfDebug( AllocatedSoundMemory += sample->Length; );
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue