1
0
Fork 0
forked from len0rd/rockbox

First Rockbox version of ALAC decoder - porting to work in Rockbox environment and some simple (but significant) optimisations

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7544 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dave Chapman 2005-09-22 20:46:58 +00:00
parent 567718d837
commit debbe9747e
8 changed files with 405 additions and 197 deletions

View file

@ -0,0 +1,47 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id$
#
INCLUDES=-I$(APPSDIR) -I.. -I. -I$(FIRMDIR)/include -I$(FIRMDIR)/export \
-I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(BUILDDIR)
ifdef APPEXTRA
INCLUDES += -I$(APPSDIR)/$(APPEXTRA)
endif
ALACOPTS = -O3
CFLAGS = $(GCCOPTS) $(ALACOPTS) $(INCLUDES) $(TARGET) $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
# This sets up 'SRC' based on the files mentioned in SOURCES
include $(TOOLSDIR)/makesrc.inc
SOURCES = $(SRC)
OBJS2 := $(SRC:%.c=$(OBJDIR)/%.o)
OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
DEPFILE = $(OBJDIR)/dep-libalac
DIRS =
OUTPUT = $(BUILDDIR)/libalac.a
all: $(OUTPUT)
$(OUTPUT): $(OBJS)
@echo "AR $@"
@$(AR) ruv $@ $+ >/dev/null 2>&1
$(OBJDIR)/libalac/%.o: $(APPSDIR)/codecs/libalac/%.c
@echo "(libalac) CC $<"
@$(CC) -c $(CFLAGS) -I$(APPSDIR)/codecs/libalac/ $< -o $@
include $(TOOLSDIR)/make.inc
clean:
@echo "cleaning libalac"
@rm -f $(OBJS) $(OUTPUT) $(DEPFILE)
-include $(DEPFILE)

View file

@ -0,0 +1,80 @@
Library: Reverse-engineered ALAC decoder v0.1.0
Imported: 2005-08-14 by Dave Chapman
This directory contains a local version of an ALAC (Apple Lossless Audio
Codec) for use by Rockbox for software decoding of ALAC files. It is
based on the reverse-engineered decoder by David Hamilton.
LICENSING INFORMATION
/*
* ALAC (Apple Lossless Audio Codec) decoder
* Copyright (c) 2005 David Hammerton
* All rights reserved.
*
* This is the actual decoder.
*
* http://crazney.net/programs/itunes/alac.html
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
IMPORT DETAILS
The base version first imported into Rockbox was the first release
(v0.1.0) of the ALAC decoder by David Hammerton.
Only the files alac.[ch], demux.[ch] and stream.h were used.
stream.c (the original FILE* based I/O implementation) was replaced with
functions in the ALAC codec - to interface with the Rockbox audio playback
system.
References to <stdint.h> were replaced with <inttypes.h> and debugging
calls to fprintf were removed.
The ALAC decoder itself was modified to return samples in host-endian
order, instead of little-endian.
The run-time detection of CPU endianness was replaced with
compile-time tests of the ROCKBOX_LITTLE_ENDIAN define.
All malloc calls were removed from alac.c, but some are still present
in the metadata parser in demux.c - to store unbounded data such as
the size in bytes of each compressed block in the file.
The only changes to demux.c were to remove debugging calls to fprintf.
The most-used buffers (the temporary 32-bit output buffer) were moved
into IRAM (on the iRiver). This was enough to make the decoder work
in real-time.
A point of interest - the -O3 gcc option (the setting used in the
original Makefile provided with the alac decoder) gives a significant
speedup compared to -O2. With -O2, the Coldfire runs at a constant
120MHz, but with -O3, it can power-down to 40MHz for a small amount of
time.
The file alac.c contained some hints from the original author for
places where major optimisations can be made - specifically the
unrolling and optimisation of certain cases of general loops.

View file

@ -0,0 +1,2 @@
alac.c
demux.c

View file

@ -33,8 +33,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <inttypes.h>
#include "../codec.h"
#include "decomp.h" #include "decomp.h"
#define _Swap32(v) do { \ #define _Swap32(v) do { \
@ -47,53 +48,13 @@
v = (((v) & 0x00FF) << 0x08) | \ v = (((v) & 0x00FF) << 0x08) | \
(((v) & 0xFF00) >> 0x08); } while (0) (((v) & 0xFF00) >> 0x08); } while (0)
int16_t predictor_coef_table[32] IDATA_ATTR;
extern int host_bigendian; int16_t predictor_coef_table_a[32] IDATA_ATTR;
int16_t predictor_coef_table_b[32] IDATA_ATTR;
struct alac_file int32_t predicterror_buffer_a[4096];
{ int32_t predicterror_buffer_b[4096];
unsigned char *input_buffer; int32_t outputsamples_buffer_a[4096] IDATA_ATTR;
int input_buffer_bitaccumulator; /* used so we can do arbitary int32_t outputsamples_buffer_b[4096] IDATA_ATTR;
bit reads */
int samplesize;
int numchannels;
int bytespersample;
/* buffers */
int32_t *predicterror_buffer_a;
int32_t *predicterror_buffer_b;
int32_t *outputsamples_buffer_a;
int32_t *outputsamples_buffer_b;
/* stuff from setinfo */
uint32_t setinfo_max_samples_per_frame; /* 0x1000 = 4096 */ /* max samples per frame? */
uint8_t setinfo_7a; /* 0x00 */
uint8_t setinfo_sample_size; /* 0x10 */
uint8_t setinfo_rice_historymult; /* 0x28 */
uint8_t setinfo_rice_initialhistory; /* 0x0a */
uint8_t setinfo_rice_kmodifier; /* 0x0e */
uint8_t setinfo_7f; /* 0x02 */
uint16_t setinfo_80; /* 0x00ff */
uint32_t setinfo_82; /* 0x000020e7 */
uint32_t setinfo_86; /* 0x00069fe4 */
uint32_t setinfo_8a_rate; /* 0x0000ac44 */
/* end setinfo stuff */
};
static void allocate_buffers(alac_file *alac)
{
alac->predicterror_buffer_a = malloc(alac->setinfo_max_samples_per_frame * 4);
alac->predicterror_buffer_b = malloc(alac->setinfo_max_samples_per_frame * 4);
alac->outputsamples_buffer_a = malloc(alac->setinfo_max_samples_per_frame * 4);
alac->outputsamples_buffer_b = malloc(alac->setinfo_max_samples_per_frame * 4);
}
void alac_set_info(alac_file *alac, char *inputbuffer) void alac_set_info(alac_file *alac, char *inputbuffer)
{ {
@ -107,8 +68,9 @@ void alac_set_info(alac_file *alac, char *inputbuffer)
ptr += 4; /* 0 ? */ ptr += 4; /* 0 ? */
alac->setinfo_max_samples_per_frame = *(uint32_t*)ptr; /* buffer size / 2 ? */ alac->setinfo_max_samples_per_frame = *(uint32_t*)ptr; /* buffer size / 2 ? */
if (!host_bigendian) #ifdef ROCKBOX_LITTLE_ENDIAN
_Swap32(alac->setinfo_max_samples_per_frame); _Swap32(alac->setinfo_max_samples_per_frame);
#endif
ptr += 4; ptr += 4;
alac->setinfo_7a = *(uint8_t*)ptr; alac->setinfo_7a = *(uint8_t*)ptr;
ptr += 1; ptr += 1;
@ -123,24 +85,25 @@ void alac_set_info(alac_file *alac, char *inputbuffer)
alac->setinfo_7f = *(uint8_t*)ptr; alac->setinfo_7f = *(uint8_t*)ptr;
ptr += 1; ptr += 1;
alac->setinfo_80 = *(uint16_t*)ptr; alac->setinfo_80 = *(uint16_t*)ptr;
if (!host_bigendian) #ifdef ROCKBOX_LITTLE_ENDIAN
_Swap16(alac->setinfo_80); _Swap16(alac->setinfo_80);
#endif
ptr += 2; ptr += 2;
alac->setinfo_82 = *(uint32_t*)ptr; alac->setinfo_82 = *(uint32_t*)ptr;
if (!host_bigendian) #ifdef ROCKBOX_LITTLE_ENDIAN
_Swap32(alac->setinfo_82); _Swap32(alac->setinfo_82);
#endif
ptr += 4; ptr += 4;
alac->setinfo_86 = *(uint32_t*)ptr; alac->setinfo_86 = *(uint32_t*)ptr;
if (!host_bigendian) #ifdef ROCKBOX_LITTLE_ENDIAN
_Swap32(alac->setinfo_86); _Swap32(alac->setinfo_86);
#endif
ptr += 4; ptr += 4;
alac->setinfo_8a_rate = *(uint32_t*)ptr; alac->setinfo_8a_rate = *(uint32_t*)ptr;
if (!host_bigendian) #ifdef ROCKBOX_LITTLE_ENDIAN
_Swap32(alac->setinfo_8a_rate); _Swap32(alac->setinfo_8a_rate);
#endif
ptr += 4; ptr += 4;
allocate_buffers(alac);
} }
/* stream reading */ /* stream reading */
@ -432,23 +395,150 @@ static void predictor_decompress_fir_adapt(int32_t *error_buffer,
} }
} }
#if 0 /* 4 and 8 are very common cases (the only ones i've seen).
/* 4 and 8 are very common cases (the only ones i've seen). these
* should be unrolled and optimised The following code is an initial attempt to unroll and optimise
these two cases by the Rockbox project. More work is needed.
*/ */
/* optimised case: 4 */
if (predictor_coef_num == 4) if (predictor_coef_num == 4)
{ {
/* FIXME: optimised general case */ for (i = 4 + 1; i < output_size; i++)
return;
}
if (predictor_coef_table == 8)
{ {
/* FIXME: optimised general case */ int sum = 0;
int outval;
int error_val = error_buffer[i];
sum = (buffer_out[4] - buffer_out[0]) * predictor_coef_table[0]
+ (buffer_out[3] - buffer_out[0]) * predictor_coef_table[1]
+ (buffer_out[2] - buffer_out[0]) * predictor_coef_table[2]
+ (buffer_out[1] - buffer_out[0]) * predictor_coef_table[3];
outval = (1 << (predictor_quantitization-1)) + sum;
outval = outval >> predictor_quantitization;
outval = outval + buffer_out[0] + error_val;
outval = SIGN_EXTENDED32(outval, readsamplesize);
buffer_out[4+1] = outval;
if (error_val > 0)
{
int predictor_num = 4 - 1;
while (predictor_num >= 0 && error_val > 0)
{
int val = buffer_out[0] - buffer_out[4 - predictor_num];
if (val!=0) {
if (val < 0) {
predictor_coef_table[predictor_num]++;
val=-val;
} else {
predictor_coef_table[predictor_num]--;;
}
error_val -= ((val >> predictor_quantitization) * (4 - predictor_num));
}
predictor_num--;
}
}
else if (error_val < 0)
{
int predictor_num = 4 - 1;
while (predictor_num >= 0 && error_val < 0)
{
int val = buffer_out[0] - buffer_out[4 - predictor_num];
if (val != 0) {
if (val > 0) {
predictor_coef_table[predictor_num]++;
val=-val; /* neg value */
} else {
predictor_coef_table[predictor_num]--;
}
error_val -= ((val >> predictor_quantitization) * (4 - predictor_num));
}
predictor_num--;
}
}
buffer_out++;
}
return; return;
} }
#endif
/* optimised case: 8 */
if (predictor_coef_num == 8)
{
for (i = 8 + 1;
i < output_size;
i++)
{
int sum;
int outval;
int error_val = error_buffer[i];
sum = (buffer_out[8] - buffer_out[0]) * predictor_coef_table[0]
+ (buffer_out[7] - buffer_out[0]) * predictor_coef_table[1]
+ (buffer_out[6] - buffer_out[0]) * predictor_coef_table[2]
+ (buffer_out[5] - buffer_out[0]) * predictor_coef_table[3]
+ (buffer_out[4] - buffer_out[0]) * predictor_coef_table[4]
+ (buffer_out[3] - buffer_out[0]) * predictor_coef_table[5]
+ (buffer_out[2] - buffer_out[0]) * predictor_coef_table[6]
+ (buffer_out[1] - buffer_out[0]) * predictor_coef_table[7];
outval = (1 << (predictor_quantitization-1)) + sum;
outval = outval >> predictor_quantitization;
outval = outval + buffer_out[0] + error_val;
outval = SIGN_EXTENDED32(outval, readsamplesize);
buffer_out[8+1] = outval;
if (error_val > 0)
{
int predictor_num = 8 - 1;
while (predictor_num >= 0 && error_val > 0)
{
int val = buffer_out[0] - buffer_out[8 - predictor_num];
if (val!=0) {
if (val < 0) {
predictor_coef_table[predictor_num]++;
val=-val;
} else {
predictor_coef_table[predictor_num]--;;
}
error_val -= ((val >> predictor_quantitization) * (8 - predictor_num));
}
predictor_num--;
}
}
else if (error_val < 0)
{
int predictor_num = 8 - 1;
while (predictor_num >= 0 && error_val < 0)
{
int val = buffer_out[0] - buffer_out[8 - predictor_num];
if (val != 0) {
if (val > 0) {
predictor_coef_table[predictor_num]++;
val=-val; /* neg value */
} else {
predictor_coef_table[predictor_num]--;
}
error_val -= ((val >> predictor_quantitization) * (8 - predictor_num));
}
predictor_num--;
}
}
buffer_out++;
}
return;
}
/* general case */ /* general case */
if (predictor_coef_num > 0) if (predictor_coef_num > 0)
@ -534,26 +624,12 @@ void deinterlace_16(int32_t *buffer_a, int32_t *buffer_b,
for (i = 0; i < numsamples; i++) for (i = 0; i < numsamples; i++)
{ {
int32_t difference, midright; int32_t difference, midright;
int16_t left;
int16_t right;
midright = buffer_a[i]; midright = buffer_a[i];
difference = buffer_b[i]; difference = buffer_b[i];
buffer_out[i*numchannels] = (midright - ((difference * interlacing_leftweight) >> interlacing_shift)) + difference;
right = midright - ((difference * interlacing_leftweight) >> interlacing_shift); buffer_out[i*numchannels + 1] = midright - ((difference * interlacing_leftweight) >> interlacing_shift);
left = (midright - ((difference * interlacing_leftweight) >> interlacing_shift))
+ difference;
/* output is always little endian */
if (host_bigendian)
{
_Swap16(left);
_Swap16(right);
}
buffer_out[i*numchannels] = left;
buffer_out[i*numchannels + 1] = right;
} }
return; return;
@ -562,34 +638,27 @@ void deinterlace_16(int32_t *buffer_a, int32_t *buffer_b,
/* otherwise basic interlacing took place */ /* otherwise basic interlacing took place */
for (i = 0; i < numsamples; i++) for (i = 0; i < numsamples; i++)
{ {
int16_t left, right; buffer_out[i*numchannels] = buffer_a[i];
buffer_out[i*numchannels + 1] = buffer_b[i];
left = buffer_a[i];
right = buffer_b[i];
/* output is always little endian */
if (host_bigendian)
{
_Swap16(left);
_Swap16(right);
}
buffer_out[i*numchannels] = left;
buffer_out[i*numchannels + 1] = right;
} }
} }
void decode_frame(alac_file *alac, int16_t* decode_frame(alac_file *alac,
unsigned char *inbuffer, unsigned char *inbuffer,
void *outbuffer, int *outputsize) int *outputsize)
{ {
int channels; int channels;
int16_t* outbuffer;
int32_t outputsamples = alac->setinfo_max_samples_per_frame; int32_t outputsamples = alac->setinfo_max_samples_per_frame;
/* setup the stream */ /* setup the stream */
alac->input_buffer = inbuffer; alac->input_buffer = inbuffer;
alac->input_buffer_bitaccumulator = 0; alac->input_buffer_bitaccumulator = 0;
/* We can share the same buffer for outputbuffer
and outputsamples_buffer_b - and hence have them both in IRAM*/
outbuffer=(int16_t*)outputsamples_buffer_b;
channels = readbits(alac, 3); channels = readbits(alac, 3);
*outputsize = outputsamples * alac->bytespersample; *outputsize = outputsamples * alac->bytespersample;
@ -631,7 +700,6 @@ void decode_frame(alac_file *alac,
if (!isnotcompressed) if (!isnotcompressed)
{ /* so it is compressed */ { /* so it is compressed */
int16_t predictor_coef_table[32];
int predictor_coef_num; int predictor_coef_num;
int prediction_type; int prediction_type;
int prediction_quantitization; int prediction_quantitization;
@ -659,11 +727,11 @@ void decode_frame(alac_file *alac,
/* these bytes seem to have something to do with /* these bytes seem to have something to do with
* > 2 channel files. * > 2 channel files.
*/ */
fprintf(stderr, "FIXME: unimplemented, unhandling of wasted_bytes\n"); //fprintf(stderr, "FIXME: unimplemented, unhandling of wasted_bytes\n");
} }
basterdised_rice_decompress(alac, basterdised_rice_decompress(alac,
alac->predicterror_buffer_a, predicterror_buffer_a,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
alac->setinfo_rice_initialhistory, alac->setinfo_rice_initialhistory,
@ -673,8 +741,8 @@ void decode_frame(alac_file *alac,
if (prediction_type == 0) if (prediction_type == 0)
{ /* adaptive fir */ { /* adaptive fir */
predictor_decompress_fir_adapt(alac->predicterror_buffer_a, predictor_decompress_fir_adapt(predicterror_buffer_a,
alac->outputsamples_buffer_a, outputsamples_buffer_a,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
predictor_coef_table, predictor_coef_table,
@ -683,7 +751,7 @@ void decode_frame(alac_file *alac,
} }
else else
{ {
fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type); //fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type);
/* i think the only other prediction type (or perhaps this is just a /* i think the only other prediction type (or perhaps this is just a
* boolean?) runs adaptive fir twice.. like: * boolean?) runs adaptive fir twice.. like:
* predictor_decompress_fir_adapt(predictor_error, tempout, ...) * predictor_decompress_fir_adapt(predictor_error, tempout, ...)
@ -704,7 +772,7 @@ void decode_frame(alac_file *alac,
audiobits = SIGN_EXTENDED32(audiobits, readsamplesize); audiobits = SIGN_EXTENDED32(audiobits, readsamplesize);
alac->outputsamples_buffer_a[i] = audiobits; outputsamples_buffer_a[i] = audiobits;
} }
} }
else else
@ -722,7 +790,7 @@ void decode_frame(alac_file *alac,
audiobits |= readbits(alac, readsamplesize - 16); audiobits |= readbits(alac, readsamplesize - 16);
alac->outputsamples_buffer_a[i] = audiobits; outputsamples_buffer_a[i] = audiobits;
} }
} }
/* wasted_bytes = 0; // unused */ /* wasted_bytes = 0; // unused */
@ -735,17 +803,16 @@ void decode_frame(alac_file *alac,
int i; int i;
for (i = 0; i < outputsamples; i++) for (i = 0; i < outputsamples; i++)
{ {
int16_t sample = alac->outputsamples_buffer_a[i]; /* Output mono data as stereo */
if (host_bigendian) outbuffer[i*2] = outputsamples_buffer_a[i];
_Swap16(sample); outbuffer[i*2+1] = outputsamples_buffer_a[i];
((int16_t*)outbuffer)[i * alac->numchannels] = sample;
} }
break; break;
} }
case 20: case 20:
case 24: case 24:
case 32: case 32:
fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); //fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size);
break; break;
default: default:
break; break;
@ -788,13 +855,11 @@ void decode_frame(alac_file *alac,
if (!isnotcompressed) if (!isnotcompressed)
{ /* compressed */ { /* compressed */
int16_t predictor_coef_table_a[32];
int predictor_coef_num_a; int predictor_coef_num_a;
int prediction_type_a; int prediction_type_a;
int prediction_quantitization_a; int prediction_quantitization_a;
int ricemodifier_a; int ricemodifier_a;
int16_t predictor_coef_table_b[32];
int predictor_coef_num_b; int predictor_coef_num_b;
int prediction_type_b; int prediction_type_b;
int prediction_quantitization_b; int prediction_quantitization_b;
@ -834,12 +899,12 @@ void decode_frame(alac_file *alac,
/*********************/ /*********************/
if (wasted_bytes) if (wasted_bytes)
{ /* see mono case */ { /* see mono case */
fprintf(stderr, "FIXME: unimplemented, unhandling of wasted_bytes\n"); //fprintf(stderr, "FIXME: unimplemented, unhandling of wasted_bytes\n");
} }
/* channel 1 */ /* channel 1 */
basterdised_rice_decompress(alac, basterdised_rice_decompress(alac,
alac->predicterror_buffer_a, predicterror_buffer_a,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
alac->setinfo_rice_initialhistory, alac->setinfo_rice_initialhistory,
@ -849,8 +914,8 @@ void decode_frame(alac_file *alac,
if (prediction_type_a == 0) if (prediction_type_a == 0)
{ /* adaptive fir */ { /* adaptive fir */
predictor_decompress_fir_adapt(alac->predicterror_buffer_a, predictor_decompress_fir_adapt(predicterror_buffer_a,
alac->outputsamples_buffer_a, outputsamples_buffer_a,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
predictor_coef_table_a, predictor_coef_table_a,
@ -859,12 +924,12 @@ void decode_frame(alac_file *alac,
} }
else else
{ /* see mono case */ { /* see mono case */
fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_a); //fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_a);
} }
/* channel 2 */ /* channel 2 */
basterdised_rice_decompress(alac, basterdised_rice_decompress(alac,
alac->predicterror_buffer_b, predicterror_buffer_b,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
alac->setinfo_rice_initialhistory, alac->setinfo_rice_initialhistory,
@ -874,8 +939,8 @@ void decode_frame(alac_file *alac,
if (prediction_type_b == 0) if (prediction_type_b == 0)
{ /* adaptive fir */ { /* adaptive fir */
predictor_decompress_fir_adapt(alac->predicterror_buffer_b, predictor_decompress_fir_adapt(predicterror_buffer_b,
alac->outputsamples_buffer_b, outputsamples_buffer_b,
outputsamples, outputsamples,
readsamplesize, readsamplesize,
predictor_coef_table_b, predictor_coef_table_b,
@ -884,7 +949,7 @@ void decode_frame(alac_file *alac,
} }
else else
{ {
fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_b); //fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_b);
} }
} }
else else
@ -902,8 +967,8 @@ void decode_frame(alac_file *alac,
audiobits_a = SIGN_EXTENDED32(audiobits_a, alac->setinfo_sample_size); audiobits_a = SIGN_EXTENDED32(audiobits_a, alac->setinfo_sample_size);
audiobits_b = SIGN_EXTENDED32(audiobits_b, alac->setinfo_sample_size); audiobits_b = SIGN_EXTENDED32(audiobits_b, alac->setinfo_sample_size);
alac->outputsamples_buffer_a[i] = audiobits_a; outputsamples_buffer_a[i] = audiobits_a;
alac->outputsamples_buffer_b[i] = audiobits_b; outputsamples_buffer_b[i] = audiobits_b;
} }
} }
else else
@ -923,8 +988,8 @@ void decode_frame(alac_file *alac,
audiobits_b = audiobits_b >> (32 - alac->setinfo_sample_size); audiobits_b = audiobits_b >> (32 - alac->setinfo_sample_size);
audiobits_b |= readbits(alac, alac->setinfo_sample_size - 16); audiobits_b |= readbits(alac, alac->setinfo_sample_size - 16);
alac->outputsamples_buffer_a[i] = audiobits_a; outputsamples_buffer_a[i] = audiobits_a;
alac->outputsamples_buffer_b[i] = audiobits_b; outputsamples_buffer_b[i] = audiobits_b;
} }
} }
/* wasted_bytes = 0; */ /* wasted_bytes = 0; */
@ -936,8 +1001,8 @@ void decode_frame(alac_file *alac,
{ {
case 16: case 16:
{ {
deinterlace_16(alac->outputsamples_buffer_a, deinterlace_16(outputsamples_buffer_a,
alac->outputsamples_buffer_b, outputsamples_buffer_b,
(int16_t*)outbuffer, (int16_t*)outbuffer,
alac->numchannels, alac->numchannels,
outputsamples, outputsamples,
@ -948,7 +1013,7 @@ void decode_frame(alac_file *alac,
case 20: case 20:
case 24: case 24:
case 32: case 32:
fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); //fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size);
break; break;
default: default:
break; break;
@ -957,16 +1022,12 @@ void decode_frame(alac_file *alac,
break; break;
} }
} }
return outbuffer;
} }
alac_file *create_alac(int samplesize, int numchannels) void create_alac(int samplesize, int numchannels, alac_file* alac)
{ {
alac_file *newfile = malloc(sizeof(alac_file)); alac->samplesize = samplesize;
alac->numchannels = numchannels;
newfile->samplesize = samplesize; alac->bytespersample = (samplesize / 8) * numchannels;
newfile->numchannels = numchannels;
newfile->bytespersample = (samplesize / 8) * numchannels;
return newfile;
} }

View file

@ -1,12 +1,34 @@
#ifndef __ALAC__DECOMP_H #ifndef __ALAC__DECOMP_H
#define __ALAC__DECOMP_H #define __ALAC__DECOMP_H
typedef struct alac_file alac_file; typedef struct
{
unsigned char *input_buffer;
int input_buffer_bitaccumulator; /* used so we can do arbitary
bit reads */
int samplesize;
int numchannels;
int bytespersample;
alac_file *create_alac(int samplesize, int numchannels); /* stuff from setinfo */
void decode_frame(alac_file *alac, uint32_t setinfo_max_samples_per_frame; /* 0x1000 = 4096 */ /* max samples per frame? */
uint8_t setinfo_7a; /* 0x00 */
uint8_t setinfo_sample_size; /* 0x10 */
uint8_t setinfo_rice_historymult; /* 0x28 */
uint8_t setinfo_rice_initialhistory; /* 0x0a */
uint8_t setinfo_rice_kmodifier; /* 0x0e */
uint8_t setinfo_7f; /* 0x02 */
uint16_t setinfo_80; /* 0x00ff */
uint32_t setinfo_82; /* 0x000020e7 */
uint32_t setinfo_86; /* 0x00069fe4 */
uint32_t setinfo_8a_rate; /* 0x0000ac44 */
/* end setinfo stuff */
} alac_file;
void create_alac(int samplesize, int numchannels, alac_file* alac);
int16_t* decode_frame(alac_file *alac,
unsigned char *inbuffer, unsigned char *inbuffer,
void *outbuffer, int *outputsize); int *outputsize);
void alac_set_info(alac_file *alac, char *inputbuffer); void alac_set_info(alac_file *alac, char *inputbuffer);
#endif /* __ALAC__DECOMP_H */ #endif /* __ALAC__DECOMP_H */

View file

@ -32,9 +32,11 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include "../codec.h"
#include "stream.h" #include "stream.h"
#include "demux.h" #include "demux.h"
@ -56,7 +58,7 @@ static void read_chunk_ftyp(qtmovie_t *qtmovie, size_t chunk_len)
size_remaining-=4; size_remaining-=4;
if (type != MAKEFOURCC('M','4','A',' ')) if (type != MAKEFOURCC('M','4','A',' '))
{ {
fprintf(stderr, "not M4A file\n"); //fprintf(stderr, "not M4A file\n");
return; return;
} }
minor_ver = stream_read_uint32(qtmovie->stream); minor_ver = stream_read_uint32(qtmovie->stream);
@ -151,7 +153,7 @@ static void read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len)
if (numentries != 1) if (numentries != 1)
{ {
fprintf(stderr, "only expecting one entry in sample description atom!\n"); //fprintf(stderr, "only expecting one entry in sample description atom!\n");
return; return;
} }
@ -173,8 +175,8 @@ static void read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len)
entry_remaining -= 6; entry_remaining -= 6;
version = stream_read_uint16(qtmovie->stream); version = stream_read_uint16(qtmovie->stream);
if (version != 1) // if (version != 1)
fprintf(stderr, "unknown version??\n"); //fprintf(stderr, "unknown version??\n");
entry_remaining -= 2; entry_remaining -= 2;
/* revision level */ /* revision level */
@ -245,8 +247,8 @@ static void read_chunk_stsd(qtmovie_t *qtmovie, size_t chunk_len)
if (qtmovie->res->format != MAKEFOURCC('a','l','a','c')) if (qtmovie->res->format != MAKEFOURCC('a','l','a','c'))
{ {
fprintf(stderr, "expecting 'alac' data format, got %c%c%c%c\n", // fprintf(stderr, "expecting 'alac' data format, got %c%c%c%c\n",
SPLITFOURCC(qtmovie->res->format)); // SPLITFOURCC(qtmovie->res->format));
return; return;
} }
} }
@ -282,7 +284,7 @@ static void read_chunk_stts(qtmovie_t *qtmovie, size_t chunk_len)
if (size_remaining) if (size_remaining)
{ {
fprintf(stderr, "ehm, size remianing?\n"); //fprintf(stderr, "ehm, size remianing?\n");
stream_skip(qtmovie->stream, size_remaining); stream_skip(qtmovie->stream, size_remaining);
} }
} }
@ -305,7 +307,7 @@ static void read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len)
/* default sample size */ /* default sample size */
if (stream_read_uint32(qtmovie->stream) != 0) if (stream_read_uint32(qtmovie->stream) != 0)
{ {
fprintf(stderr, "i was expecting variable samples sizes\n"); //fprintf(stderr, "i was expecting variable samples sizes\n");
stream_read_uint32(qtmovie->stream); stream_read_uint32(qtmovie->stream);
size_remaining -= 4; size_remaining -= 4;
return; return;
@ -326,7 +328,7 @@ static void read_chunk_stsz(qtmovie_t *qtmovie, size_t chunk_len)
if (size_remaining) if (size_remaining)
{ {
fprintf(stderr, "ehm, size remianing?\n"); //fprintf(stderr, "ehm, size remianing?\n");
stream_skip(qtmovie->stream, size_remaining); stream_skip(qtmovie->stream, size_remaining);
} }
} }
@ -343,7 +345,7 @@ static void read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len)
sub_chunk_len = stream_read_uint32(qtmovie->stream); sub_chunk_len = stream_read_uint32(qtmovie->stream);
if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining)
{ {
fprintf(stderr, "strange size for chunk inside stbl\n"); //fprintf(stderr, "strange size for chunk inside stbl\n");
return; return;
} }
@ -366,8 +368,8 @@ static void read_chunk_stbl(qtmovie_t *qtmovie, size_t chunk_len)
stream_skip(qtmovie->stream, sub_chunk_len - 8); stream_skip(qtmovie->stream, sub_chunk_len - 8);
break; break;
default: default:
fprintf(stderr, "(stbl) unknown chunk id: %c%c%c%c\n", // fprintf(stderr, "(stbl) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); // SPLITFOURCC(sub_chunk_id));
return; return;
} }
@ -383,12 +385,12 @@ static void read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len)
/**** SOUND HEADER CHUNK ****/ /**** SOUND HEADER CHUNK ****/
if (stream_read_uint32(qtmovie->stream) != 16) if (stream_read_uint32(qtmovie->stream) != 16)
{ {
fprintf(stderr, "unexpected size in media info\n"); //fprintf(stderr, "unexpected size in media info\n");
return; return;
} }
if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('s','m','h','d')) if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('s','m','h','d'))
{ {
fprintf(stderr, "not a sound header! can't handle this.\n"); //fprintf(stderr, "not a sound header! can't handle this.\n");
return; return;
} }
/* now skip the rest */ /* now skip the rest */
@ -400,7 +402,7 @@ static void read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len)
dinf_size = stream_read_uint32(qtmovie->stream); dinf_size = stream_read_uint32(qtmovie->stream);
if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('d','i','n','f')) if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('d','i','n','f'))
{ {
fprintf(stderr, "expected dinf, didn't get it.\n"); //fprintf(stderr, "expected dinf, didn't get it.\n");
return; return;
} }
/* skip it */ /* skip it */
@ -413,7 +415,7 @@ static void read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len)
stbl_size = stream_read_uint32(qtmovie->stream); stbl_size = stream_read_uint32(qtmovie->stream);
if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('s','t','b','l')) if (stream_read_uint32(qtmovie->stream) != MAKEFOURCC('s','t','b','l'))
{ {
fprintf(stderr, "expected stbl, didn't get it.\n"); //fprintf(stderr, "expected stbl, didn't get it.\n");
return; return;
} }
read_chunk_stbl(qtmovie, stbl_size); read_chunk_stbl(qtmovie, stbl_size);
@ -421,7 +423,7 @@ static void read_chunk_minf(qtmovie_t *qtmovie, size_t chunk_len)
if (size_remaining) if (size_remaining)
{ {
fprintf(stderr, "oops\n"); //fprintf(stderr, "oops\n");
stream_skip(qtmovie->stream, size_remaining); stream_skip(qtmovie->stream, size_remaining);
} }
} }
@ -438,7 +440,7 @@ static void read_chunk_mdia(qtmovie_t *qtmovie, size_t chunk_len)
sub_chunk_len = stream_read_uint32(qtmovie->stream); sub_chunk_len = stream_read_uint32(qtmovie->stream);
if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining)
{ {
fprintf(stderr, "strange size for chunk inside mdia\n"); //fprintf(stderr, "strange size for chunk inside mdia\n");
return; return;
} }
@ -456,8 +458,8 @@ static void read_chunk_mdia(qtmovie_t *qtmovie, size_t chunk_len)
read_chunk_minf(qtmovie, sub_chunk_len); read_chunk_minf(qtmovie, sub_chunk_len);
break; break;
default: default:
fprintf(stderr, "(mdia) unknown chunk id: %c%c%c%c\n", // fprintf(stderr, "(mdia) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); // SPLITFOURCC(sub_chunk_id));
return; return;
} }
@ -478,7 +480,7 @@ static void read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len)
sub_chunk_len = stream_read_uint32(qtmovie->stream); sub_chunk_len = stream_read_uint32(qtmovie->stream);
if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining)
{ {
fprintf(stderr, "strange size for chunk inside trak\n"); //fprintf(stderr, "strange size for chunk inside trak\n");
return; return;
} }
@ -493,8 +495,8 @@ static void read_chunk_trak(qtmovie_t *qtmovie, size_t chunk_len)
read_chunk_mdia(qtmovie, sub_chunk_len); read_chunk_mdia(qtmovie, sub_chunk_len);
break; break;
default: default:
fprintf(stderr, "(trak) unknown chunk id: %c%c%c%c\n", // fprintf(stderr, "(trak) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); // SPLITFOURCC(sub_chunk_id));
return; return;
} }
@ -533,7 +535,7 @@ static void read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len)
sub_chunk_len = stream_read_uint32(qtmovie->stream); sub_chunk_len = stream_read_uint32(qtmovie->stream);
if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining)
{ {
fprintf(stderr, "strange size for chunk inside moov\n"); //fprintf(stderr, "strange size for chunk inside moov\n");
return; return;
} }
@ -551,8 +553,8 @@ static void read_chunk_moov(qtmovie_t *qtmovie, size_t chunk_len)
read_chunk_udta(qtmovie, sub_chunk_len); read_chunk_udta(qtmovie, sub_chunk_len);
break; break;
default: default:
fprintf(stderr, "(moov) unknown chunk id: %c%c%c%c\n", // fprintf(stderr, "(moov) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(sub_chunk_id)); // SPLITFOURCC(sub_chunk_id));
return; return;
} }
@ -574,14 +576,11 @@ static void read_chunk_mdat(qtmovie_t *qtmovie, size_t chunk_len)
int qtmovie_read(stream_t *file, demux_res_t *demux_res) int qtmovie_read(stream_t *file, demux_res_t *demux_res)
{ {
qtmovie_t *qtmovie; qtmovie_t qtmovie;
qtmovie = (qtmovie_t*)malloc(sizeof(qtmovie_t));
/* construct the stream */ /* construct the stream */
qtmovie->stream = file; qtmovie.stream = file;
qtmovie.res = demux_res;
qtmovie->res = demux_res;
/* read the chunks */ /* read the chunks */
while (1) while (1)
@ -589,26 +588,26 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res)
size_t chunk_len; size_t chunk_len;
fourcc_t chunk_id; fourcc_t chunk_id;
chunk_len = stream_read_uint32(qtmovie->stream); chunk_len = stream_read_uint32(qtmovie.stream);
if (stream_eof(qtmovie->stream)) if (stream_eof(qtmovie.stream))
{ {
return 0; return 0;
} }
if (chunk_len == 1) if (chunk_len == 1)
{ {
fprintf(stderr, "need 64bit support\n"); //fprintf(stderr, "need 64bit support\n");
return 0; return 0;
} }
chunk_id = stream_read_uint32(qtmovie->stream); chunk_id = stream_read_uint32(qtmovie.stream);
switch (chunk_id) switch (chunk_id)
{ {
case MAKEFOURCC('f','t','y','p'): case MAKEFOURCC('f','t','y','p'):
read_chunk_ftyp(qtmovie, chunk_len); read_chunk_ftyp(&qtmovie, chunk_len);
break; break;
case MAKEFOURCC('m','o','o','v'): case MAKEFOURCC('m','o','o','v'):
read_chunk_moov(qtmovie, chunk_len); read_chunk_moov(&qtmovie, chunk_len);
break; break;
/* once we hit mdat we stop reading and return. /* once we hit mdat we stop reading and return.
* this is on the assumption that there is no furhter interesting * this is on the assumption that there is no furhter interesting
@ -617,16 +616,16 @@ int qtmovie_read(stream_t *file, demux_res_t *demux_res)
* for the decoder. And we don't want to rely on fseek/ftell, * for the decoder. And we don't want to rely on fseek/ftell,
* as they may not always be avilable */ * as they may not always be avilable */
case MAKEFOURCC('m','d','a','t'): case MAKEFOURCC('m','d','a','t'):
read_chunk_mdat(qtmovie, chunk_len); read_chunk_mdat(&qtmovie, chunk_len);
return 1; return 1;
/* these following atoms can be skipped !!!! */ /* these following atoms can be skipped !!!! */
case MAKEFOURCC('f','r','e','e'): case MAKEFOURCC('f','r','e','e'):
stream_skip(qtmovie->stream, chunk_len - 8); /* FIXME not 8 */ stream_skip(qtmovie.stream, chunk_len - 8); /* FIXME not 8 */
break; break;
default: default:
fprintf(stderr, "(top) unknown chunk id: %c%c%c%c\n", // fprintf(stderr, "(top) unknown chunk id: %c%c%c%c\n",
SPLITFOURCC(chunk_id)); // SPLITFOURCC(chunk_id));
return 0; return 0;
} }

View file

@ -1,7 +1,7 @@
#ifndef DEMUX_H #ifndef DEMUX_H
#define DEMUX_H #define DEMUX_H
#include <stdint.h> #include <inttypes.h>
#include "stream.h" #include "stream.h"
typedef uint32_t fourcc_t; typedef uint32_t fourcc_t;

View file

@ -3,9 +3,11 @@
/* stream.h */ /* stream.h */
#include <stdint.h> #include <inttypes.h>
typedef struct stream_tTAG stream_t; typedef struct {
int eof;
} stream_t;
void stream_read(stream_t *stream, size_t len, void *buf); void stream_read(stream_t *stream, size_t len, void *buf);
@ -22,9 +24,4 @@ void stream_skip(stream_t *stream, size_t skip);
int stream_eof(stream_t *stream); int stream_eof(stream_t *stream);
stream_t *stream_create_file(FILE *file,
int bigendian);
void stream_destroy(stream_t *stream);
#endif /* STREAM_H */ #endif /* STREAM_H */