Ogg, mp3 audio format support added.

This commit is contained in:
johns 2002-03-26 17:11:38 +00:00
parent dc21cba1fe
commit f38cc57127
6 changed files with 415 additions and 6 deletions

View file

@ -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)))

View file

@ -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
View 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
View 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
//@}

View file

@ -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;
}

View file

@ -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;
}