Unverified Commit eb92f5a6 authored by bobparadiso's avatar bobparadiso Committed by GitHub
Browse files

initial commit of working Ogg (#158)

* -initial commit of working Ogg

* -added some missing includes for OSX
-removed a log print
parent 26c2c102
......@@ -305,7 +305,7 @@ CORE_DIR := .
include Makefile.common
OBJS += $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o)
OBJS += $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o) $(SOURCES_ASM:.S=.o)
CFLAGS += -Wall -pedantic $(fpic) $(INCFLAGS)
......
......@@ -3,6 +3,8 @@ LUALIB_DIR := $(CORE_DIR)/deps/lua/src
INCFLAGS := -I$(CORE_DIR) \
-I$(CORE_DIR)/libretro-common/include \
-I$(CORE_DIR)/deps/zlib \
-I$(CORE_DIR)/deps/vorbis \
-I$(CORE_DIR)/deps/ogg \
-I$(CORE_DIR)/deps \
-I$(LUALIB_DIR)
......@@ -14,6 +16,7 @@ SOURCES_C := $(CORE_DIR)/libretro.c \
$(CORE_DIR)/graphics.c \
$(CORE_DIR)/input.c \
$(CORE_DIR)/audio.c \
$(CORE_DIR)/decoder.c \
$(CORE_DIR)/event.c \
$(CORE_DIR)/keyboard.c \
$(CORE_DIR)/sound.c \
......@@ -64,6 +67,9 @@ SOURCES_C += $(CORE_DIR)/libretro-common/formats/png/rpng_decode.c \
ifneq ($(STATIC_LINKING), 1)
SOURCES_C += \
$(CORE_DIR)/libretro-common/file/file_path.c \
$(CORE_DIR)/libretro-common/audio/conversion/float_to_s16.c \
$(CORE_DIR)/libretro-common/audio/conversion/s16_to_float.c \
$(CORE_DIR)/libretro-common/audio/audio_mix.c \
$(CORE_DIR)/libretro-common/compat/compat_posix_string.c \
$(CORE_DIR)/libretro-common/memmap/memmap.c \
$(CORE_DIR)/libretro-common/compat/compat_strcasestr.c \
......@@ -72,6 +78,11 @@ SOURCES_C += \
$(CORE_DIR)/libretro-common/streams/file_stream.c \
$(CORE_DIR)/libretro-common/vfs/vfs_implementation.c \
$(CORE_DIR)/libretro-common/compat/fopen_utf8.c
ifeq ($(HAVE_NEON),1)
SOURCES_ASM += \
$(CORE_DIR)/libretro-common/audio/conversion/float_to_s16_neon.S \
$(CORE_DIR)/libretro-common/audio/conversion/s16_to_float_neon.S
endif
endif
# Lua UTF-8
......@@ -166,3 +177,31 @@ ifeq ($(WANT_PHYSFS), 1)
DEFINES += -DPHYSFS_PLATFORM_LINUX
endif
endif
# Ogg Vorbis
SOURCES_C += \
$(CORE_DIR)/deps/ogg/bitwise.c \
$(CORE_DIR)/deps/ogg/framing.c \
$(CORE_DIR)/deps/vorbis/analysis.c \
$(CORE_DIR)/deps/vorbis/barkmel.c \
$(CORE_DIR)/deps/vorbis/bitrate.c \
$(CORE_DIR)/deps/vorbis/block.c \
$(CORE_DIR)/deps/vorbis/codebook.c \
$(CORE_DIR)/deps/vorbis/envelope.c \
$(CORE_DIR)/deps/vorbis/floor0.c \
$(CORE_DIR)/deps/vorbis/floor1.c \
$(CORE_DIR)/deps/vorbis/info.c \
$(CORE_DIR)/deps/vorbis/lookup.c \
$(CORE_DIR)/deps/vorbis/lpc.c \
$(CORE_DIR)/deps/vorbis/lsp.c \
$(CORE_DIR)/deps/vorbis/mapping0.c \
$(CORE_DIR)/deps/vorbis/mdct.c \
$(CORE_DIR)/deps/vorbis/psy.c \
$(CORE_DIR)/deps/vorbis/registry.c \
$(CORE_DIR)/deps/vorbis/res0.c \
$(CORE_DIR)/deps/vorbis/sharedbook.c \
$(CORE_DIR)/deps/vorbis/smallft.c \
$(CORE_DIR)/deps/vorbis/synthesis.c \
$(CORE_DIR)/deps/vorbis/vorbisenc.c \
$(CORE_DIR)/deps/vorbis/vorbisfile.c \
$(CORE_DIR)/deps/vorbis/window.c
......@@ -2,8 +2,11 @@
#include "lutro.h"
#include "compat/strl.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <file/file_path.h>
#include <audio/conversion/float_to_s16.h>
#include <math.h>
/* TODO/FIXME - no sound on big-endian */
......@@ -12,6 +15,10 @@ static unsigned num_sources = 0;
static audio_Source** sources = NULL;
static float volume = 1.0;
#define CHANNELS 2
float floatBuffer[AUDIO_FRAMES * CHANNELS];
int16_t convBuffer[AUDIO_FRAMES * CHANNELS];
// The following types are acceptable for pre-saturated mixing, as they meet the requirement for
// having a larger range than the saturated mixer result type of int16_t. double precision should
// be preferred on x86/amd64, and single precision on ARM. float16 could also work as an input
......@@ -37,8 +44,11 @@ static int16_t saturate(mixer_presaturate_t in) {
void mixer_render(int16_t *buffer)
{
// Clear buffer
memset(buffer, 0, AUDIO_FRAMES * 2 * sizeof(int16_t));
// Clear buffers
memset(buffer, 0, AUDIO_FRAMES * CHANNELS * sizeof(int16_t));
memset(floatBuffer, 0, AUDIO_FRAMES * CHANNELS * sizeof(float));
bool floatBufferUsed = false;
// Loop over audio sources
for (unsigned i = 0; i < num_sources; i++)
......@@ -46,6 +56,16 @@ void mixer_render(int16_t *buffer)
if (sources[i]->state == AUDIO_STOPPED)
continue;
//currently Ogg Vorbis
if (sources[i]->oggData)
{
bool finished = decoder_decodeOgg(sources[i]->oggData, floatBuffer, sources[i]->volume, sources[i]->loop);
if (finished)
sources[i]->state = AUDIO_STOPPED;
floatBufferUsed = true;
continue;
}
uint8_t* rawsamples8 = calloc(
AUDIO_FRAMES * sources[i]->bps, sizeof(uint8_t));
......@@ -81,6 +101,14 @@ void mixer_render(int16_t *buffer)
free(rawsamples8);
}
//add in accumulated float buffer
if (floatBufferUsed)
{
convert_float_to_s16(convBuffer, floatBuffer, AUDIO_FRAMES * CHANNELS); //convert to int
for (unsigned j = 0; j < AUDIO_FRAMES * CHANNELS; j++)
buffer[j] += convBuffer[j] * volume;
}
}
int lutro_audio_preload(lua_State *L)
......@@ -114,6 +142,15 @@ void lutro_audio_deinit()
{
if (sources)
{
for (unsigned i = 0; i < num_sources; i++)
{
if (sources[i]->oggData)
{
ov_clear(&sources[i]->oggData->vf);
free(sources[i]->oggData);
}
}
free(sources);
sources = NULL;
num_sources = 0;
......@@ -128,6 +165,8 @@ int audio_newSource(lua_State *L)
return luaL_error(L, "lutro.audio.newSource requires 1 or 2 arguments, %d given.", n);
audio_Source* self = (audio_Source*)lua_newuserdata(L, sizeof(audio_Source));
self->oggData = NULL;
self->sndta.fp = NULL;
void *p = lua_touserdata(L, 1);
if (p == NULL)
......@@ -138,12 +177,28 @@ int audio_newSource(lua_State *L)
strlcpy(fullpath, settings.gamedir, sizeof(fullpath));
strlcat(fullpath, path, sizeof(fullpath));
FILE *fp = fopen(fullpath, "rb");
if (!fp)
return -1;
//get file extension
char ext[PATH_MAX_LENGTH];
strcpy(ext, path_get_extension(path));
for(int i = 0; ext[i]; i++)
ext[i] = tolower(ext[i]);
//ogg
if (strstr(ext, "ogg"))
{
self->oggData = malloc(sizeof(OggData));
decoder_initOgg(self->oggData, fullpath);
}
//default: WAV file
else
{
FILE *fp = fopen(fullpath, "rb");
if (!fp)
return -1;
fread(&self->sndta.head, sizeof(uint8_t), WAV_HEADER_SIZE, fp);
self->sndta.fp = fp;
fread(&self->sndta.head, sizeof(uint8_t), WAV_HEADER_SIZE, fp);
self->sndta.fp = fp;
}
}
else
{
......@@ -151,13 +206,17 @@ int audio_newSource(lua_State *L)
self->sndta = *sndta;
}
self->bps = self->sndta.head.NumChannels * self->sndta.head.BitsPerSample / 8;
self->loop = false;
self->volume = 1.0;
self->pos = 0;
self->state = AUDIO_STOPPED;
fseek(self->sndta.fp, 0, SEEK_END);
//WAV file
if (self->sndta.fp)
{
self->bps = self->sndta.head.NumChannels * self->sndta.head.BitsPerSample / 8;
fseek(self->sndta.fp, 0, SEEK_END);
}
num_sources++;
sources = (audio_Source**)realloc(sources, num_sources * sizeof(audio_Source));
sources[num_sources-1] = self;
......@@ -327,9 +386,24 @@ int source_gc(lua_State *L)
int audio_play(lua_State *L)
{
audio_Source* self = (audio_Source*)luaL_checkudata(L, 1, "Source");
bool success = fseek(self->sndta.fp, WAV_HEADER_SIZE, SEEK_SET) == 0;
if (success)
self->state = AUDIO_PLAYING;
bool success = false;
//WAV file
if (self->sndta.fp)
{
success = fseek(self->sndta.fp, WAV_HEADER_SIZE, SEEK_SET) == 0;
if (success)
self->state = AUDIO_PLAYING;
}
//OGG file
else if (self->oggData)
{
success = decoder_seekStart(self->oggData);
if (success)
self->state = AUDIO_PLAYING;
}
lua_pushboolean(L, success);
return 1;
}
......@@ -337,9 +411,23 @@ int audio_play(lua_State *L)
int audio_stop(lua_State *L)
{
audio_Source* self = (audio_Source*)luaL_checkudata(L, 1, "Source");
bool success = fseek(self->sndta.fp, WAV_HEADER_SIZE, SEEK_SET) == 0;
if (success)
self->state = AUDIO_STOPPED;
bool success = false;
//WAV file
if (self->sndta.fp)
{
success = fseek(self->sndta.fp, WAV_HEADER_SIZE, SEEK_SET) == 0;
if (success)
self->state = AUDIO_STOPPED;
}
//OGG file
else if (self->oggData)
{
success = decoder_seekStart(self->oggData);
if (success)
self->state = AUDIO_STOPPED;
}
lua_pushboolean(L, success);
return 1;
}
......@@ -7,6 +7,7 @@
#include <stdbool.h>
#include "runtime.h"
#include "sound.h"
#include "decoder.h"
#define AUDIO_FRAMES (44100 / 60)
......@@ -19,8 +20,13 @@ typedef enum
typedef struct
{
//currently for WAV
snd_SoundData sndta;
unsigned bps; // bytes per sample
//currently for Ogg Vorbis
OggData *oggData;
bool loop;
float volume;
float pitch;
......
#include <libretro.h>
#include <audio/audio_mix.h>
#include <audio/conversion/float_to_s16.h>
#include "decoder.h"
#include "audio.h"
#define CHANNELS 2
float floatData[AUDIO_FRAMES * CHANNELS];
//
bool decoder_initOgg(OggData *data, char *filename)
{
data->info = NULL;
if (ov_fopen(filename, &data->vf) < 0)
{
printf("Failed to open vorbis file: %s", filename);
return false;
}
printf("vorbis info:\n");
printf("\tnum streams: %d\n", ov_streams(&data->vf));
data->info = (vorbis_info*)ov_info(&data->vf, 0);
if (!data->info)
{
printf("couldn't get info for file");
return false;
}
printf("\tnum channels: %d\n", data->info->channels);
printf("\tsample rate: %d\n", data->info->rate);
if (data->info->channels != 1 && data->info->channels != 2)
{
printf("unsupported number of channels\n");
return false;
}
if (data->info->rate != 44100)
{
printf("unsupported sample rate\n");
return false;
}
printf("vorbis init success\n");
return true;
}
//
bool decoder_seekStart(OggData *data)
{
return ov_time_seek(&data->vf, 0.0) == 0;
}
//
bool decoder_decodeOgg(OggData *data, float *buffer, float volume, bool loop)
{
//printf("decoder_decodeOgg\n");
size_t frames = AUDIO_FRAMES;
size_t rendered = 0;
bool finished = false;
float *dst = floatData;
while (frames)
{
float **pcm;
int bitstream;
//printf("pcmoffs: %d\n", data->vf.pcm_offset);
long ret = ov_read_float(&data->vf, &pcm, frames, &bitstream);
if (ret < 0)
{
printf("Vorbis decoding failed with: %d\n", ret);
return true;
}
if (ret == 0) // EOF
{
if (loop)
{
if (ov_time_seek(&data->vf, 0.0) == 0)
continue;
else
finished = true;
}
else
finished = true;
break;
}
if (data->info->channels == 2)
{
for (long i = 0; i < ret; i++)
{
dst[i * CHANNELS + 0] = pcm[0][i];
dst[i * CHANNELS + 1] = pcm[1][i];
}
}
else
{
for (long i = 0; i < ret; i++)
{
dst[i * CHANNELS + 0] = pcm[0][i];
dst[i * CHANNELS + 1] = pcm[0][i];
}
}
dst += ret * CHANNELS;
frames -= ret;
rendered += ret;
}
audio_mix_volume(buffer, floatData, volume, rendered * CHANNELS);
return finished;
}
#ifndef DECODER_H
#define DECODER_H
#include <vorbis/vorbisfile.h>
typedef struct
{
OggVorbis_File vf;
vorbis_info *info;
} OggData;
bool decoder_initOgg(OggData *data, char *filename);
bool decoder_seekStart(OggData *data);
bool decoder_decodeOgg(OggData *data, float *buffer, float volume, bool loop);
#endif // DECODER_H
This diff is collapsed.
#ifndef __CONFIG_TYPES_H__
#define __CONFIG_TYPES_H__
/* these are filled in by configure */
#define INCLUDE_INTTYPES_H 1
#define INCLUDE_STDINT_H 1
#define INCLUDE_SYS_TYPES_H 1
#if INCLUDE_INTTYPES_H
# include <inttypes.h>
#endif
#if INCLUDE_STDINT_H
# include <stdint.h>
#endif
#if INCLUDE_SYS_TYPES_H
# include <sys/types.h>
#endif
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
#endif
This diff is collapsed.
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 *
* by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: toplevel libogg include
last mod: $Id: ogg.h 18044 2011-08-01 17:55:20Z gmaxwell $
********************************************************************/
#ifndef _OGG_H
#define _OGG_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <ogg/os_types.h>
typedef struct {
void *iov_base;
size_t iov_len;
} ogg_iovec_t;
typedef struct {
long endbyte;
int endbit;
unsigned char *buffer;
unsigned char *ptr;
long storage;
} oggpack_buffer;
/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
typedef struct {
unsigned char *header;
long header_len;
unsigned char *body;
long body_len;
} ogg_page;
/* ogg_stream_state contains the current encode/decode state of a logical
Ogg bitstream **********************************************************/
typedef struct {
unsigned char *body_data; /* bytes from packet bodies */
long body_storage; /* storage elements allocated */
long body_fill; /* elements stored; fill mark */
long body_returned; /* elements of fill returned */
int *lacing_vals; /* The values that will go to the segment table */
ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
this way, but it is simple coupled to the
lacing fifo */
long lacing_storage;
long lacing_fill;
long lacing_packet;
long lacing_returned;
unsigned char header[282]; /* working space for header encode */
int header_fill;
int e_o_s; /* set when we have buffered the last packet in the
logical bitstream */
int b_o_s; /* set after we've written the initial page
of a logical bitstream */
long serialno;
long pageno;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
ogg_int64_t granulepos;
} ogg_stream_state;
/* ogg_packet is used to encapsulate the data and metadata belonging
to a single raw Ogg/Vorbis packet *************************************/
typedef struct {
unsigned char *packet;
long bytes;
long b_o_s;
long e_o_s;
ogg_int64_t granulepos;
ogg_int64_t packetno; /* sequence number for decode; the framing
knows where there's a hole in the data,
but we need coupling so that the codec
(which is in a separate abstraction
layer) also knows about the gap */
} ogg_packet;
typedef struct {
unsigned char *data;
int storage;
int fill;
int returned;
int unsynced;
int headerbytes;
int bodybytes;
} ogg_sync_state;
/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
extern void oggpack_writeinit(oggpack_buffer *b);
extern int oggpack_writecheck(oggpack_buffer *b);
extern void oggpack_writetrunc(oggpack_buffer *b,long bits);
extern void oggpack_writealign(oggpack_buffer *b);
extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpack_reset(oggpack_buffer *b);
extern void oggpack_writeclear(oggpack_buffer *b);
extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpack_look(oggpack_buffer *b,int bits);
extern long oggpack_look1(oggpack_buffer *b);
extern void oggpack_adv(oggpack_buffer *b,int bits);
extern void oggpack_adv1(oggpack_buffer *b);
extern long oggpack_read(oggpack_buffer *b,int bits);
extern long oggpack_read1(oggpack_buffer *b);
extern long oggpack_bytes(oggpack_buffer *b);
extern long oggpack_bits(oggpack_buffer *b);
extern unsigned char *oggpack_get_buffer(oggpack_buffer *b);
extern void oggpackB_writeinit(oggpack_buffer *b);
extern int oggpackB_writecheck(oggpack_buffer *b);
extern void oggpackB_writetrunc(oggpack_buffer *b,long bits);
extern void oggpackB_writealign(oggpack_buffer *b);
extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits);
extern void oggpackB_reset(oggpack_buffer *b);
extern void oggpackB_writeclear(oggpack_buffer *b);
extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits);
extern long oggpackB_look(oggpack_buffer *b,int bits);
extern long oggpackB_look1(oggpack_buffer *b);
extern void oggpackB_adv(oggpack_buffer *b,int bits);
extern void oggpackB_adv1(oggpack_buffer *b);
extern long oggpackB_read(oggpack_buffer *b,int bits);
extern long oggpackB_read1(oggpack_buffer *b);
extern long oggpackB_bytes(oggpack_buffer *b);
extern long oggpackB_bits(oggpack_buffer *b);
extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b);
/* Ogg BITSTREAM PRIMITIVES: encoding **************************/
extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov,
int count, long e_o_s, ogg_int64_t granulepos);
extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill);
extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
extern int ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill);
/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
extern int ogg_sync_init(ogg_sync_state *oy);