317 lines
7.8 KiB
C
317 lines
7.8 KiB
C
|
/*****************************************************************************
|
||
|
*
|
||
|
* 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: waveout.c 548 2006-01-08 22:41:57Z picard $
|
||
|
*
|
||
|
* The Core Pocket Media Player
|
||
|
* Copyright (c) 2004-2005 Gabor Kovacs
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "common.h"
|
||
|
|
||
|
void VolumeMul(int Vol,void* Dst,int DstLength,const audio* Format)
|
||
|
{
|
||
|
uint8_t *u8,*u8e;
|
||
|
int16_t *s16,*s16e;
|
||
|
|
||
|
switch (Format->Bits)
|
||
|
{
|
||
|
case 8:
|
||
|
u8=Dst;
|
||
|
u8e=u8+DstLength;
|
||
|
for (;u8!=u8e;++u8)
|
||
|
{
|
||
|
int i = (((*u8 - 0x80) * Vol) >> 8) + 0x80;
|
||
|
*u8 = (uint8_t)(i>255?255:(i<0?0:i));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 16:
|
||
|
s16=Dst;
|
||
|
s16e=s16+(DstLength>>1);
|
||
|
for (;s16!=s16e;++s16)
|
||
|
{
|
||
|
int i = (*s16 * Vol) >> 8;
|
||
|
*s16 = (int16_t)(i>32767?32767:(i<-32768?-32768:i));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int VolumeRamp(int Ramp,void* Dst,int DstLength,const audio* Format)
|
||
|
{
|
||
|
uint8_t *u8,*u8e;
|
||
|
int16_t *s16,*s16e;
|
||
|
int Inc = (10*(1<<RAMPSIZE)) / (Format->Channels * Format->SampleRate);
|
||
|
if (Inc<=0) Inc=1;
|
||
|
|
||
|
switch (Format->Bits)
|
||
|
{
|
||
|
case 8:
|
||
|
u8=Dst;
|
||
|
u8e=u8+DstLength;
|
||
|
for (;u8!=u8e && Ramp<RAMPLIMIT;++u8,Ramp+=Inc)
|
||
|
*u8 = (uint8_t)((((*u8 - 0x80) * Ramp) >> RAMPSIZE) + 0x80);
|
||
|
break;
|
||
|
|
||
|
case 16:
|
||
|
s16=Dst;
|
||
|
s16e=s16+(DstLength>>1);
|
||
|
for (;s16!=s16e && Ramp<RAMPLIMIT;++s16,Ramp+=Inc)
|
||
|
*s16 = (int16_t)((*s16 * Ramp) >> RAMPSIZE);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Ramp = RAMPLIMIT;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return Ramp;
|
||
|
}
|
||
|
|
||
|
static void AlignRate(int* Rate,int Target)
|
||
|
{
|
||
|
if (*Rate > Target-16 && *Rate < Target+16)
|
||
|
*Rate = Target;
|
||
|
}
|
||
|
|
||
|
static int UpdateInput(waveout_base* p)
|
||
|
{
|
||
|
if (p->Done)
|
||
|
p->Done(p);
|
||
|
|
||
|
p->Total = 0;
|
||
|
p->Dropped = 0;
|
||
|
|
||
|
if (p->Input.Type == PACKET_AUDIO)
|
||
|
{
|
||
|
int Result = ERR_NONE;
|
||
|
int Try;
|
||
|
|
||
|
if (p->Input.Format.Audio.Format != AUDIOFMT_PCM)
|
||
|
{
|
||
|
PacketFormatClear(&p->Input);
|
||
|
return ERR_INVALID_DATA;
|
||
|
}
|
||
|
|
||
|
if (p->Input.Format.Audio.Channels == 0 ||
|
||
|
p->Input.Format.Audio.SampleRate == 0)
|
||
|
return ERR_NONE;
|
||
|
|
||
|
AlignRate(&p->Input.Format.Audio.SampleRate,8000);
|
||
|
AlignRate(&p->Input.Format.Audio.SampleRate,11025);
|
||
|
AlignRate(&p->Input.Format.Audio.SampleRate,16000);
|
||
|
AlignRate(&p->Input.Format.Audio.SampleRate,22050);
|
||
|
AlignRate(&p->Input.Format.Audio.SampleRate,44100);
|
||
|
|
||
|
PacketFormatClear(&p->Output);
|
||
|
p->Output.Type = PACKET_AUDIO;
|
||
|
p->Output.Format.Audio = p->Input.Format.Audio;
|
||
|
p->Output.Format.Audio.Flags = 0;
|
||
|
p->Dither = 0;
|
||
|
|
||
|
if (p->Stereo==STEREO_SWAPPED)
|
||
|
p->Output.Format.Audio.Flags |= PCM_SWAPPEDSTEREO;
|
||
|
else
|
||
|
if (p->Stereo!=STEREO_NORMAL)
|
||
|
{
|
||
|
p->Output.Format.Audio.Channels = 1;
|
||
|
if (p->Stereo==STEREO_LEFT)
|
||
|
p->Output.Format.Audio.Flags |= PCM_ONLY_LEFT;
|
||
|
if (p->Stereo==STEREO_RIGHT)
|
||
|
p->Output.Format.Audio.Flags |= PCM_ONLY_RIGHT;
|
||
|
}
|
||
|
|
||
|
switch (p->Quality)
|
||
|
{
|
||
|
case 0: // low quality for very poor devices
|
||
|
p->Output.Format.Audio.Bits = 8;
|
||
|
p->Output.Format.Audio.FracBits = 7;
|
||
|
p->Output.Format.Audio.Channels = 1;
|
||
|
p->Output.Format.Audio.SampleRate = 22050;
|
||
|
break;
|
||
|
|
||
|
case 1: // no dither and only standard samplerate
|
||
|
if (p->Output.Format.Audio.Bits > 8)
|
||
|
{
|
||
|
p->Output.Format.Audio.Bits = 16;
|
||
|
p->Output.Format.Audio.FracBits = 15;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->Output.Format.Audio.Bits = 8;
|
||
|
p->Output.Format.Audio.FracBits = 7;
|
||
|
}
|
||
|
if (p->Output.Format.Audio.SampleRate >= 44100)
|
||
|
p->Output.Format.Audio.SampleRate = 44100;
|
||
|
else
|
||
|
p->Output.Format.Audio.SampleRate = 22050;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
case 2: // original samplerate
|
||
|
if (p->Output.Format.Audio.Bits > 8)
|
||
|
{
|
||
|
p->Output.Format.Audio.Bits = 16;
|
||
|
p->Output.Format.Audio.FracBits = 15;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p->Output.Format.Audio.Bits = 8;
|
||
|
p->Output.Format.Audio.FracBits = 7;
|
||
|
}
|
||
|
p->Dither = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for (Try=0;;++Try)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.Bits <= 8)
|
||
|
p->Output.Format.Audio.Flags |= PCM_UNSIGNED;
|
||
|
|
||
|
Result = p->Init(p);
|
||
|
if (Result == ERR_NONE)
|
||
|
break;
|
||
|
|
||
|
if (p->Done)
|
||
|
p->Done(p);
|
||
|
|
||
|
if (Try==0)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.SampleRate > 36000)
|
||
|
p->Output.Format.Audio.SampleRate = 44100;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==1)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.SampleRate != 22050)
|
||
|
p->Output.Format.Audio.SampleRate = 22050;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
#ifdef TARGET_SYMBIAN
|
||
|
if (Try==2)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.SampleRate != 16000)
|
||
|
p->Output.Format.Audio.SampleRate = 16000;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==3)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.SampleRate != 11025)
|
||
|
p->Output.Format.Audio.SampleRate = 11025;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==4)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.SampleRate != 8000)
|
||
|
p->Output.Format.Audio.SampleRate = 8000;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==5)
|
||
|
break;
|
||
|
#else
|
||
|
if (Try==2)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.Channels > 1)
|
||
|
p->Output.Format.Audio.Channels = 1;
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==3)
|
||
|
{
|
||
|
if (p->Output.Format.Audio.Bits != 8)
|
||
|
{
|
||
|
p->Output.Format.Audio.Bits = 8;
|
||
|
p->Output.Format.Audio.FracBits = 7;
|
||
|
}
|
||
|
else
|
||
|
++Try;
|
||
|
}
|
||
|
if (Try==4)
|
||
|
break;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (Try==5)
|
||
|
{
|
||
|
PacketFormatClear(&p->Input);
|
||
|
if (Result == ERR_DEVICE_ERROR)
|
||
|
ShowError(p->Node.Class,ERR_ID,Result);
|
||
|
return Result;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
if (p->Input.Type != PACKET_NONE)
|
||
|
return ERR_INVALID_DATA;
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
int WaveOutPCM(waveout_base* p)
|
||
|
{
|
||
|
PCMRelease(p->PCM);
|
||
|
p->PCM = PCMCreate(&p->Output.Format.Audio,&p->Input.Format.Audio,p->Dither,0);
|
||
|
return p->PCM ? ERR_NONE:ERR_OUT_OF_MEMORY;
|
||
|
}
|
||
|
|
||
|
int WaveOutBaseGet(waveout_base* p, int No, void* Data, int Size)
|
||
|
{
|
||
|
int Result = ERR_INVALID_PARAM;
|
||
|
switch (No)
|
||
|
{
|
||
|
case OUT_INPUT: GETVALUE(p->Pin,pin); break;
|
||
|
case OUT_INPUT|PIN_FORMAT: GETVALUE(p->Input,packetformat); break;
|
||
|
case OUT_INPUT|PIN_PROCESS: GETVALUE(p->Input.Format.Audio.Channels?p->Process:DummyProcess,packetprocess); break;
|
||
|
case OUT_OUTPUT|PIN_FORMAT: GETVALUE(p->Output,packetformat); break;
|
||
|
case OUT_TOTAL:GETVALUE(p->Total,int); break;
|
||
|
case OUT_DROPPED:GETVALUE(p->Dropped,int); break;
|
||
|
case AOUT_STEREO: GETVALUE(p->Stereo,int); break;
|
||
|
case AOUT_QUALITY: GETVALUE(p->Quality,int); break;
|
||
|
case AOUT_TIMER: GETVALUE(&p->Timer,node*); break;
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int WaveOutBaseSet(waveout_base* p, int No, const void* Data, int Size)
|
||
|
{
|
||
|
int Result = ERR_INVALID_PARAM;
|
||
|
switch (No)
|
||
|
{
|
||
|
case OUT_INPUT|PIN_FORMAT:
|
||
|
if (PacketFormatSimilarAudio(&p->Input,(const packetformat*)Data))
|
||
|
{
|
||
|
PacketFormatCopy(&p->Input,(const packetformat*)Data);
|
||
|
return WaveOutPCM(p);
|
||
|
}
|
||
|
else
|
||
|
SETPACKETFORMATCMP(p->Input,packetformat,UpdateInput(p));
|
||
|
break;
|
||
|
case OUT_INPUT: SETVALUE(p->Pin,pin,ERR_NONE); break;
|
||
|
case OUT_TOTAL: SETVALUE(p->Total,int,ERR_NONE); break;
|
||
|
case OUT_DROPPED: SETVALUE(p->Dropped,int,ERR_NONE); break;
|
||
|
case AOUT_STEREO: SETVALUECMP(p->Stereo,int,UpdateInput(p),EqInt); break;
|
||
|
case AOUT_QUALITY: SETVALUECMP(p->Quality,int,UpdateInput(p),EqInt); break;
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|