gps/GPSResources/tcpmp/splitter/wav.c

324 lines
8.0 KiB
C
Executable File

/*****************************************************************************
*
* 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 program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: wav.c 543 2006-01-07 22:06:24Z picard $
*
* The Core Pocket Media Player
* Copyright (c) 2004-2005 Gabor Kovacs
*
****************************************************************************/
#include "../common/common.h"
#include "wav.h"
typedef struct wav_t
{
format_base Format;
filepos_t FileSize;
filepos_t DataPos;
filepos_t DataEnd;
int Samples;
} wav;
#define ALIGN2(x) (((x)+1)&~1)
static int Init(wav* p)
{
p->DataPos = -1;
p->DataEnd = -1;
p->FileSize = 0;
p->Samples = -1;
return ERR_NONE;
}
static NOINLINE void BeginData(wav* p,format_reader* Reader,int Length)
{
p->DataPos = Reader->FilePos;
p->DataEnd = Length + Reader->FilePos;
p->Format.HeaderLoaded = 1;
if (p->Format.StreamCount)
{
tick_t Duration = 0;
format_stream* Stream = p->Format.Streams[0];
if (p->Samples >= 0)
Duration = Scale(p->Samples,TICKSPERSEC,Stream->Format.Format.Audio.SampleRate);
else
if (Stream->Format.ByteRate)
Duration = Scale(Length,TICKSPERSEC,Stream->Format.ByteRate);
if (Duration>0 && p->Format.Duration<Duration)
p->Format.Duration = Duration;
}
}
static NOINLINE int Comment(wav* p,format_reader* Reader,int Id,int Len)
{
char s8[256];
tchar_t s[256];
if (p->Format.Comment.Node)
{
Len = Reader->Read(Reader,s8,min(Len,sizeof(s8)-1));
if (Len>0)
{
s8[Len] = 0;
tcscpy_s(s,TSIZEOF(s),PlayerComment(Id));
tcscat_s(s,TSIZEOF(s),T("="));
StrToTcs(s+tcslen(s),TSIZEOF(s)-tcslen(s),s8);
p->Format.Comment.Node->Set(p->Format.Comment.Node,p->Format.Comment.No,s,(tcslen(s)+1)*sizeof(tchar_t));
}
else
Len=0;
}
else
Len = 0;
return Len;
}
static int ReadExtendedBE(format_reader* Reader)
{
int exp = 0x403E - Reader->ReadBE16(Reader);
uint64_t v = Reader->ReadBE64(Reader);
return (int)(v>>exp);
}
static int ReadPacket(wav* p, format_reader* Reader, format_packet* Packet)
{
format_stream* Stream;
int Type,Length,DataLength,Offset;
if (p->DataPos < 0 || Reader->FilePos < p->DataPos)
{
if (p->Format.Format.Class == WAV_ID)
{
Type = Reader->ReadLE32(Reader);
DataLength = Reader->ReadLE32(Reader);
Length = ALIGN2(DataLength);
switch (Type)
{
case FOURCCLE('R','M','P','3'):
case FOURCCLE('R','I','F','F'):
case FOURCCLE('S','D','S','S'):
p->FileSize = Length + 8;
Reader->ReadLE32(Reader); // 'WAVE'
return ERR_NONE;
case FOURCCLE('f','m','t',' '):
Stream = Format_AddStream(&p->Format,sizeof(format_stream));
if (Stream)
{
Format_WaveFormat(Reader,Stream,Length);
Stream->Fragmented = 0;
Format_PrepairStream(&p->Format,Stream);
}
else
Reader->Skip(Reader,Length);
return ERR_NONE;
case FOURCCLE('f','a','c','t'):
p->Samples = Reader->ReadLE32(Reader);
Length -= 4;
break;
case FOURCCLE('d','a','t','a'):
BeginData(p,Reader,DataLength);
return ERR_NONE;
}
}
else
{
Type = Reader->ReadLE32(Reader);
DataLength = Reader->ReadBE32(Reader);
Length = ALIGN2(DataLength);
switch (Type)
{
case FOURCCLE('F','O','R','M'):
p->FileSize = Length + 8;
Reader->ReadLE32(Reader); // 'AIFF' or 'AIFC'
return ERR_NONE;
case FOURCCLE('C','O','M','M'):
Stream = Format_AddStream(&p->Format,sizeof(format_stream));
if (Stream)
{
PacketFormatClear(&Stream->Format);
Stream->Format.Type = PACKET_AUDIO;
Stream->Format.Format.Audio.Format = AUDIOFMT_PCM;
#ifndef IS_BIG_ENDIAN
Stream->Format.Format.Audio.Flags = PCM_SWAPEDBYTES;
#endif
Stream->Format.Format.Audio.Channels = Reader->ReadBE16(Reader);
p->Samples = Reader->ReadBE32(Reader);
Stream->Format.Format.Audio.Bits = (Reader->ReadBE16(Reader)+7) & ~7;
Stream->Format.Format.Audio.SampleRate = ReadExtendedBE(Reader);
Length -= 2+4+2+10;
if (Length>=4)
{
//compression
uint32_t Type = Reader->ReadLE32(Reader);
Length -= 4;
switch (Type)
{
case FOURCCLE('i','m','a','4'):
Stream->Format.Format.Audio.Format = AUDIOFMT_ADPCM_IMA;
break;
case FOURCCLE('u','l','a','w'):
Stream->Format.Format.Audio.Format = AUDIOFMT_MULAW;
break;
case FOURCCLE('a','l','a','w'):
Stream->Format.Format.Audio.Format = AUDIOFMT_ALAW;
break;
}
}
PacketFormatDefault(&Stream->Format);
Stream->Format.Format.Audio.Flags &= ~PCM_UNSIGNED;
Stream->Fragmented = 0;
Format_PrepairStream(&p->Format,Stream);
}
break;
case FOURCCLE('N','A','M','E'):
Length -= Comment(p,Reader,COMMENT_TITLE,DataLength);
break;
case FOURCCLE('(','c',')',' '):
Length -= Comment(p,Reader,COMMENT_COPYRIGHT,DataLength);
break;
case FOURCCLE('A','U','T','H'):
Length -= Comment(p,Reader,COMMENT_AUTHOR,DataLength);
break;
case FOURCCLE('S','S','N','D'):
Offset = Reader->ReadBE32(Reader);
Reader->ReadBE32(Reader); //blocksize
Reader->Skip(Reader,Offset);
BeginData(p,Reader,DataLength-8-Offset);
return ERR_NONE;
}
}
if (Reader->FilePos + Length < p->FileSize)
Reader->Skip(Reader,Length);
}
else
{
if (Reader->FilePos >= p->DataEnd)
return ERR_END_OF_FILE;
if (p->Format.StreamCount)
{
int Length;
Stream = p->Format.Streams[0];
if (Reader->FilePos == p->DataPos)
Packet->RefTime = 0;
else
if (Stream->Format.ByteRate)
Packet->RefTime = Scale(Reader->FilePos - p->DataPos,TICKSPERSEC,Stream->Format.ByteRate);
else
Packet->RefTime = TIME_UNKNOWN;
Length = p->DataEnd - Reader->FilePos;
if (Length > 4096) Length = 4096;
Packet->Data = Reader->ReadAsRef(Reader,-Length);
Packet->Stream = Stream;
}
}
return ERR_NONE;
}
static int Seek(wav* p, tick_t Time, filepos_t FilePos,bool_t PrevKey)
{
format_stream* Stream;
if (p->DataPos < 0 || !p->Format.StreamCount)
{
// no avi header yet, only seek to the beginning supported
if (Time > 0 || FilePos > 0)
return ERR_NOT_SUPPORTED;
return Format_Seek(&p->Format,0,SEEK_SET);
}
Stream = p->Format.Streams[0];
if (FilePos < 0)
{
if (Time > 0 && !Stream->Format.ByteRate)
return ERR_NOT_SUPPORTED;
FilePos = Scale(Time,Stream->Format.ByteRate,TICKSPERSEC);
}
if (Stream->Format.Format.Audio.BlockAlign > 1)
FilePos = (FilePos / Stream->Format.Format.Audio.BlockAlign) * Stream->Format.Format.Audio.BlockAlign;
if (FilePos<0) FilePos=0;
if (FilePos>p->DataEnd-p->DataPos) FilePos=p->DataEnd-p->DataPos;
return Format_Seek(&p->Format,p->DataPos+FilePos,SEEK_SET);
}
static int Create(wav* p)
{
p->Format.Init = (fmtfunc)Init;
p->Format.Seek = (fmtseek)Seek;
p->Format.ReadPacket = (fmtreadpacket)ReadPacket;
return ERR_NONE;
}
static const nodedef WAV =
{
sizeof(wav),
WAV_ID,
FORMATBASE_CLASS,
PRI_DEFAULT-5,
(nodecreate)Create,
NULL,
};
static const nodedef AIFF =
{
0,
AIFF_ID,
WAV_ID,
PRI_DEFAULT-5,
};
void WAV_Init()
{
NodeRegisterClass(&WAV);
NodeRegisterClass(&AIFF);
}
void WAV_Done()
{
NodeUnRegisterClass(AIFF_ID);
NodeUnRegisterClass(WAV_ID);
}