WMA Voice now plays and seeks in the sim. The code is still in floating point, and is not added to the main build. There's still a bug with the decoder in the current state that it outputs a fewer number of samples than ffmpeg's.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27744 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Mohamed Tarek 2010-08-07 17:55:02 +00:00
parent eb369699c6
commit 4ff2cf4f0c
9 changed files with 296 additions and 7 deletions

View file

@ -7,6 +7,7 @@
#define ASF_CODEC_ID_WMAV1 0x160 #define ASF_CODEC_ID_WMAV1 0x160
#define ASF_CODEC_ID_WMAV2 0x161 #define ASF_CODEC_ID_WMAV2 0x161
#define ASF_CODEC_ID_WMAPRO 0x162 #define ASF_CODEC_ID_WMAPRO 0x162
#define ASF_CODEC_ID_WMAVOICE 0x00A
enum asf_error_e { enum asf_error_e {
ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
@ -33,7 +34,7 @@ struct asf_waveformatex_s {
uint16_t bitspersample; uint16_t bitspersample;
uint16_t datalen; uint16_t datalen;
uint16_t numpackets; uint16_t numpackets;
uint8_t data[18]; uint8_t data[46];
}; };
typedef struct asf_waveformatex_s asf_waveformatex_t; typedef struct asf_waveformatex_s asf_waveformatex_t;

View file

@ -0,0 +1,17 @@
acelp_filters.c
acelp_vectors.c
avfft.c
bitstream.c
celp_filters.c
celp_math.c
dct.c
fft.c
lsp.c
mdct.c
rdft.c
utils.c
wmavoice.c
libavutil/log.c
libavutil/lzo.c
libavutil/mem.c
libavutil/mathematics.c

View file

@ -0,0 +1,37 @@
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# $Id: libwmavoice.make 27586 2010-07-27 06:48:15Z nls $
#
# libwmavoice
WMAVOICELIB := $(CODECDIR)/libwmavoice.a
WMAVOICELIB_SRC := $(call preprocess, $(APPSDIR)/codecs/libwmavoice/SOURCES)
WMAVOICELIB_OBJ := $(call c2obj, $(WMAVOICELIB_SRC))
OTHER_SRC += $(WMAVOICELIB_SRC)
$(WMAVOICELIB): $(WMAVOICELIB_OBJ)
$(SILENT)$(shell rm -f $@)
$(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null
WMAVOICEFLAGS = -I$(APPSDIR)/codecs/libwmavoice $(filter-out -O%,$(CODECFLAGS))
ifeq ($(CPU),coldfire)
WMAVOICEFLAGS += -O2
else
WMAVOICEFLAGS += -O1
endif
ifeq ($(APP_TYPE),sdl-sim)
# wmavoice needs libm in the simulator
$(CODECDIR)/wmavoice.codec: $(CODECDIR)/wmavoice.o
$(call PRINTS,LD $(@F))$(CC) $(CODECFLAGS) -o $(CODECDIR)/wmavoice.elf \
$(filter %.o, $^) \
$(filter %.a, $+) \
-lgcc -lm $(CODECLDFLAGS)
$(SILENT)cp $(CODECDIR)/wmavoice.elf $@
endif

View file

@ -26,7 +26,7 @@
*/ */
#include <math.h> #include <math.h>
#include "avcodec.h" #include "wmavoice.h"
#include "get_bits.h" #include "get_bits.h"
#include "put_bits.h" #include "put_bits.h"
#include "wmavoice_data.h" #include "wmavoice_data.h"
@ -286,6 +286,10 @@ typedef struct {
*/ */
} WMAVoiceContext; } WMAVoiceContext;
/* global decode context */
static WMAVoiceContext globWMAVoiceCtx;
/** /**
* Set up the variable bit mode (VBM) tree from container extradata. * Set up the variable bit mode (VBM) tree from container extradata.
* @param gb bit I/O context. * @param gb bit I/O context.
@ -330,9 +334,10 @@ static av_cold int decode_vbmtree(GetBitContext *gb, int8_t vbm_tree[25])
/** /**
* Set up decoder with parameters from demuxer (extradata etc.). * Set up decoder with parameters from demuxer (extradata etc.).
*/ */
static av_cold int wmavoice_decode_init(AVCodecContext *ctx) av_cold int wmavoice_decode_init(AVCodecContext *ctx)
{ {
int n, flags, pitch_range, lsp16_flag; int n, flags, pitch_range, lsp16_flag;
ctx->priv_data = &globWMAVoiceCtx;
WMAVoiceContext *s = ctx->priv_data; WMAVoiceContext *s = ctx->priv_data;
/** /**
@ -1743,7 +1748,7 @@ static int synth_superframe(AVCodecContext *ctx,
* the wild yet. */ * the wild yet. */
if (!get_bits1(gb)) { if (!get_bits1(gb)) {
av_log_missing_feature(ctx, "WMAPro-in-WMAVoice support", 1); av_log_missing_feature(ctx, "WMAPro-in-WMAVoice support", 1);
return -1; return ERROR_WMAPRO_IN_WMAVOICE;
} }
/* (optional) nr. of samples in superframe; always <= 480 and >= 0 */ /* (optional) nr. of samples in superframe; always <= 480 and >= 0 */
@ -1893,7 +1898,7 @@ static void copy_bits(PutBitContext *pb,
* *
* For more information about frames, see #synth_superframe(). * For more information about frames, see #synth_superframe().
*/ */
static int wmavoice_decode_packet(AVCodecContext *ctx, void *data, int wmavoice_decode_packet(AVCodecContext *ctx, void *data,
int *data_size, AVPacket *avpkt) int *data_size, AVPacket *avpkt)
{ {
WMAVoiceContext *s = ctx->priv_data; WMAVoiceContext *s = ctx->priv_data;
@ -1936,6 +1941,15 @@ static int wmavoice_decode_packet(AVCodecContext *ctx, void *data,
s->sframe_cache_size += s->spillover_nbits; s->sframe_cache_size += s->spillover_nbits;
if ((res = synth_superframe(ctx, data, data_size)) == 0 && if ((res = synth_superframe(ctx, data, data_size)) == 0 &&
*data_size > 0) { *data_size > 0) {
/* convert the float values to int32 for rockbox */
int i;
int32_t *iptr = data;
float *fptr = data;
for(i = 0; i < *data_size/sizeof(float); i++)
{
fptr[i] *= (float)(INT32_MAX);
iptr[i] = (int32_t)fptr[i];
}
cnt += s->spillover_nbits; cnt += s->spillover_nbits;
s->skip_bits_next = cnt & 7; s->skip_bits_next = cnt & 7;
return cnt >> 3; return cnt >> 3;
@ -1957,12 +1971,21 @@ static int wmavoice_decode_packet(AVCodecContext *ctx, void *data,
} else if (*data_size > 0) { } else if (*data_size > 0) {
int cnt = get_bits_count(gb); int cnt = get_bits_count(gb);
s->skip_bits_next = cnt & 7; s->skip_bits_next = cnt & 7;
/* convert the float values to int32 for rockbox */
int i;
int32_t *iptr = data;
float *fptr = data;
for(i = 0; i < *data_size/sizeof(float); i++)
{
fptr[i] *= (float)(INT32_MAX);
iptr[i] = (int32_t)fptr[i];
}
return cnt >> 3; return cnt >> 3;
} else if ((s->sframe_cache_size = pos) > 0) { } else if ((s->sframe_cache_size = pos) > 0) {
/* rewind bit reader to start of last (incomplete) superframe... */ /* rewind bit reader to start of last (incomplete) superframe... */
init_get_bits(gb, avpkt->data, size << 3); init_get_bits(gb, avpkt->data, size << 3);
skip_bits_long(gb, (size << 3) - pos); skip_bits_long(gb, (size << 3) - pos);
assert(get_bits_left(gb) == pos); //assert(get_bits_left(gb) == pos);
/* ...and cache it for spillover in next packet */ /* ...and cache it for spillover in next packet */
init_put_bits(&s->pb, s->sframe_cache, SFRAME_CACHE_MAXSIZE); init_put_bits(&s->pb, s->sframe_cache, SFRAME_CACHE_MAXSIZE);

View file

@ -0,0 +1,7 @@
#include "avcodec.h"
#define ERROR_WMAPRO_IN_WMAVOICE -0x162
av_cold int wmavoice_decode_init(AVCodecContext *ctx);
int wmavoice_decode_packet(AVCodecContext *ctx, void *data,
int *data_size, AVPacket *avpkt);

195
apps/codecs/wmavoice.c Normal file
View file

@ -0,0 +1,195 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Mohamed Tarek
*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "codeclib.h"
#include "libasf/asf.h"
#include "libwmavoice/wmavoice.h"
CODEC_HEADER
static AVCodecContext avctx;
static AVPacket avpkt;
#define MAX_FRAMES 3 /*maximum number of frames per superframe*/
#define MAX_FRAMESIZE 160 /* maximum number of samples per frame */
#define BUFSIZE MAX_FRAMES*MAX_FRAMESIZE
static int32_t decoded[BUFSIZE] IBSS_ATTR;
/* This function initialises AVCodecContext with the data needed for the wmapro
* decoder to work. The required data is taken from asf_waveformatex_t because that's
* what the rockbox asf metadata parser fill/work with. In the future, when the
* codec is being optimised for on-target playback this function should not be needed. */
static void init_codec_ctx(AVCodecContext *avctx, asf_waveformatex_t *wfx)
{
/* Copy the extra-data */
avctx->extradata_size = wfx->datalen;
avctx->extradata = (uint8_t *)malloc(wfx->datalen*sizeof(uint8_t));
memcpy(avctx->extradata, wfx->data, wfx->datalen*sizeof(uint8_t));
avctx->block_align = wfx->blockalign;
avctx->sample_rate = wfx->rate;
avctx->channels = wfx->channels;
}
/* this is the codec entry point */
enum codec_status codec_main(void)
{
uint32_t elapsedtime;
int retval;
asf_waveformatex_t wfx; /* Holds the stream properties */
size_t resume_offset;
int res; /* Return values from asf_read_packet() and decode_packet() */
uint8_t* audiobuf; /* Pointer to the payload of one wma pro packet */
int audiobufsize; /* Payload size */
int packetlength = 0; /* Logical packet size (minus the header size) */
int outlen = 0; /* Number of bytes written to the output buffer */
int pktcnt = 0; /* Count of the packets played */
/* Generic codec initialisation */
ci->configure(DSP_SET_SAMPLE_DEPTH, 31);
next_track:
/* Wait for the metadata to be read */
while (!*ci->taginfo_ready && !ci->stop_codec)
ci->sleep(1);
retval = CODEC_OK;
/* Remember the resume position */
resume_offset = ci->id3->offset;
restart_track:
if (codec_init()) {
LOGF("(WMA Voice) Error: Error initialising codec\n");
retval = CODEC_ERROR;
goto done;
}
/* Copy the format metadata we've stored in the id3 TOC field. This
saves us from parsing it again here. */
memcpy(&wfx, ci->id3->toc, sizeof(wfx));
memset(&avctx, 0, sizeof(AVCodecContext));
memset(&avpkt, 0, sizeof(AVPacket));
ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
STEREO_MONO : STEREO_INTERLEAVED);
codec_set_replaygain(ci->id3);
/* Initialise the AVCodecContext */
init_codec_ctx(&avctx, &wfx);
if (wmavoice_decode_init(&avctx) < 0) {
LOGF("(WMA Voice) Error: Unsupported or corrupt file\n");
retval = CODEC_ERROR;
goto done;
}
/* Now advance the file position to the first frame */
ci->seek_buffer(ci->id3->first_frame_offset);
elapsedtime = 0;
resume_offset = 0;
/* The main decoding loop */
while (pktcnt < wfx.numpackets)
{
ci->yield();
if (ci->stop_codec || ci->new_track) {
goto done;
}
/* Deal with any pending seek requests */
if (ci->seek_time){
if (ci->seek_time == 1) {
ci->seek_complete();
goto restart_track; /* Pretend you never saw this... */
}
elapsedtime = asf_seek(ci->seek_time, &wfx);
if (elapsedtime < 1){
ci->seek_complete();
goto next_track;
}
ci->set_elapsed(elapsedtime);
ci->seek_complete();
}
new_packet:
res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
if (res < 0) {
LOGF("(WMA Voice) read_packet error %d\n",res);
goto done;
} else {
avpkt.data = audiobuf;
avpkt.size = audiobufsize;
pktcnt++;
while(avpkt.size > 0)
{
/* wmavoice_decode_packet checks for the output buffer size to
avoid overflows */
outlen = BUFSIZE*sizeof(int32_t);
res = wmavoice_decode_packet(&avctx, decoded, &outlen, &avpkt);
if(res < 0) {
LOGF("(WMA Voice) Error: decode_packet returned %d", res);
if(res == ERROR_WMAPRO_IN_WMAVOICE){
/* Just skip this packet */
ci->advance_buffer(packetlength);
goto new_packet;
}
else
goto done;
}
avpkt.data += res;
avpkt.size -= res;
if(outlen) {
ci->yield ();
outlen /= sizeof(int32_t);
ci->pcmbuf_insert(decoded, NULL, outlen);
elapsedtime += outlen*10/(wfx.rate/100);
ci->set_elapsed(elapsedtime);
ci->yield ();
}
}
}
/* Advance to the next logical packet */
ci->advance_buffer(packetlength);
}
retval = CODEC_OK;
done:
if (ci->request_next_track())
goto next_track;
return retval;
}

View file

@ -182,6 +182,9 @@ const struct afmt_entry audio_formats[AFMT_NUM_CODECS] =
/* True Audio */ /* True Audio */
[AFMT_TTA] = [AFMT_TTA] =
AFMT_ENTRY("TTA", "tta", NULL, "tta\0" ), AFMT_ENTRY("TTA", "tta", NULL, "tta\0" ),
/* WMA Voice in ASF */
[AFMT_WMAVOICE] =
AFMT_ENTRY("WMAVoice", "wmavoice", NULL, "wma\0wmv\0asf\0" ),
#endif #endif
}; };
@ -297,7 +300,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
} }
break; break;
case AFMT_WMA: case AFMT_WMA:
if (!get_asf_metadata(fd, id3)) if (!get_asf_metadata(fd, id3))
{ {

View file

@ -84,6 +84,7 @@ enum
AFMT_VOX, /* VOX */ AFMT_VOX, /* VOX */
AFMT_WAVE64, /* Wave64 */ AFMT_WAVE64, /* Wave64 */
AFMT_TTA, /* True Audio */ AFMT_TTA, /* True Audio */
AFMT_WMAVOICE, /* WMA Voice in ASF */
#endif #endif
/* add new formats at any index above this line to have a sensible order - /* add new formats at any index above this line to have a sensible order -

View file

@ -357,6 +357,11 @@ static int asf_parse_header(int fd, struct mp3entry* id3,
wfx->audiostream = flags&0x7f; wfx->audiostream = flags&0x7f;
/* Correct codectype to redirect playback to the proper .codec */ /* Correct codectype to redirect playback to the proper .codec */
id3->codectype = AFMT_WMAPRO; id3->codectype = AFMT_WMAPRO;
} else if (wfx->codec_id == ASF_CODEC_ID_WMAVOICE) {
read(fd, wfx->data, wfx->datalen);
lseek(fd,current.size - 24 - 72 - wfx->datalen,SEEK_CUR);
wfx->audiostream = flags&0x7f;
id3->codectype = AFMT_WMAVOICE;
} else { } else {
DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n"); DEBUGF("Unsupported WMA codec (Lossless, Voice, etc)\n");
lseek(fd,current.size - 24 - 72,SEEK_CUR); lseek(fd,current.size - 24 - 72,SEEK_CUR);