📄 waveout_palmos.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_palmos.c 623 2006-02-01 13:19:15Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/#include "../common.h"#if defined(TARGET_PALMOS)
#define DRVBUFFER 4096
#define BUFFER_MIN 4*DRVBUFFER
#define BUFFER_MUSIC 65536*8
#define BUFFER_VIDEO 65536
#define VOLLIMIT (16384-64)
#include "pace.h"
typedef struct waveout_palm
{
waveout_base Base;
SndStreamRef Stream;
sysregs SysRegs;
void* PCMCopy;
int CopySpeed;
bool_t Mute;
int Volume;
int PreAmp;
int Pan;
bool_t Play;
bool_t Started;
fraction Speed;
fraction SpeedTime;
buffer Buffer;
bool_t BufferMode;
int VolumeRamp;
int BytePerSample; // output bytes per sample
int BytePerSec; // output bytes per seconds (before speed adjustment)
int SpeedBytePerSec; // speed adjusted BytePerSec
int Ratio; //input to output bytepersec (8.8 fixed point)
int Skip;
int DrvBufferSize;
tick_t DrvBufferDelay;
tick_t BufferTick;
tick_t Tick;
int TimeRef;
int BenchSpeed;
int BenchLeft;
bool_t AlwaysClose;
bool_t Force16Bits;
int Vol;
Boolean Native;
m68kcallback CallBack;
} waveout_palm;
#define WAVEOUT(p) ((waveout_palm*)((char*)(p)-OFS(waveout_palm,Base.Timer)))
static void ManualSleep(int MSec)
{
int t0 = GetTimeTick();
int Wait = (GetTimeFreq()*MSec)/1024;
if (Wait==0) Wait=1;
while (GetTimeTick()-t0<Wait);
}
static int Stop(waveout_palm* p)
{
if (p->Started)
{
p->Started = 0;
SndStreamStop(p->Stream);
ManualSleep(50); // try to make sure callback exited
if (p->AlwaysClose)
{
SndStreamDelete(p->Stream);
p->Stream = 0;
}
p->DrvBufferSize = 0;
p->DrvBufferDelay = 0;
}
return ERR_NONE;
}
static int CreateStream(waveout_palm* p);
static void Start(waveout_palm* p,tick_t CurrTime)
{
if (p->Play && !p->Started && p->Buffer.WritePos != p->Buffer.ReadPos &&
(CurrTime<0 || p->BufferTick <= CurrTime+SHOWAHEAD) &&
(p->Stream || CreateStream(p)==ERR_NONE))
{
p->Started = 1;
SndStreamStart(p->Stream);
}
}
static int UpdateBufferMode(waveout_palm* p)
{
int Size;
if (p->Started)
return ERR_NONE;
BufferPack(&p->Buffer,0);
Size = p->BufferMode ? BUFFER_VIDEO : BUFFER_MUSIC;
if (Context()->LowMemory)
Size >>= 1;
if (Size < BUFFER_MIN)
Size = BUFFER_MIN;
Size = ALIGN16(Size);
DisableOutOfMemory();
for (;Size >= BUFFER_MIN;Size >>= 1)
if (BufferAlloc(&p->Buffer,Size,1))
{
EnableOutOfMemory();
if (p->Buffer.WritePos >= p->Buffer.Allocated)
p->Buffer.WritePos = 0;
return ERR_NONE;
}
EnableOutOfMemory();
ShowOutOfMemory();
return ERR_OUT_OF_MEMORY;
}
static Err Callback(waveout_palm* p,SndStreamRef Stream,void *Buffer,uint32_t Frames)
{
// as a general rule we don't call any OS callbacks here, because
// we only have one emulstate structure and if both threads
// call an m68k syscall this could mean problems...
constplanes SrcPlanes;
planes DstPlanes;
int SrcLength,DstLength;
int WritePos,ReadPos,Length,Speed;
LoadSysRegs(&p->SysRegs);
Length = Frames * p->BytePerSample;
DstPlanes[0] = Buffer;
if (p->Started)
{
WritePos = p->Buffer.WritePos;
ReadPos = p->Buffer.ReadPos;
if (p->DrvBufferSize != Length)
{
//assuming double buffering
p->DrvBufferSize = Length;
p->DrvBufferDelay = Scale(2*Length,TICKSPERSEC,p->SpeedBytePerSec);
}
Speed = p->CopySpeed;
if (Speed <= 0)
{
/* int Middle = p->Buffer.Allocated >> 1;
int Left = ReadPos - WritePos;
if (Left<0)
Left += p->Buffer.Allocated;
Speed = p->BenchSpeed;
if (Left > Middle && p->BenchLeft < Left)
{
Speed -= Left - p->BenchLeft;
if (Speed < SPEED_ONE/8)
Speed = SPEED_ONE/8;
}
if (Left < Middle && p->BenchLeft > Left)
Speed -= Left - p->BenchLeft;
p->BenchSpeed = Speed;
p->BenchLeft = Left;
*/
Speed = SPEED_ONE;
}
while (Length>0)
{
if (WritePos < ReadPos)
SrcLength = p->Buffer.Allocated - ReadPos;
else
SrcLength = WritePos - ReadPos;
SrcPlanes[0] = p->Buffer.Data + ReadPos;
DstLength = Length;
PCMConvert(p->PCMCopy,DstPlanes,SrcPlanes,&DstLength,&SrcLength,Speed,256);
DstPlanes[0] = (uint8_t*)DstPlanes[0] + DstLength;
Length -= DstLength;
ReadPos += SrcLength;
if (ReadPos == p->Buffer.Allocated)
ReadPos = 0;
if (!DstLength)
break;
}
p->Buffer.ReadPos = ReadPos;
}
if (Length)
memset(DstPlanes[0],(p->Base.Output.Format.Audio.Flags & PCM_UNSIGNED)?0x80:0x00,Length);
return errNone;
}
DLLEXPORT unsigned long WaveOutCallBack(const void *emulStateP, void *userData68KP, Call68KFuncType *call68KFuncP)
{
UInt32 Param[4];
memcpy(Param,userData68KP,16);
return Callback((waveout_palm*)SWAP32(Param[0]),(SndStreamRef)SWAP32(Param[1]),
(void*)SWAP32(Param[2]),SWAP32(Param[3]));
}
static int Process(waveout_palm* p,const packet* Packet,const flowstate* State)
{
constplanes SrcPlanes;
planes DstPlanes;
int Length,SrcLength,DstLength,Left,ReadPos,WritePos;
ReadPos = p->Buffer.ReadPos;
WritePos = p->Buffer.WritePos;
if (!Packet)
{
if (State->DropLevel)
++p->Base.Dropped;
return (!p->Started || !p->Speed.Num || WritePos==ReadPos) ? ERR_NONE : ERR_BUFFER_FULL;
}
DstLength = (Packet->Length * p->Ratio + 255) >> 8; // it may give little larger as needed length
if (DstLength > p->Buffer.Allocated - DRVBUFFER*2)
{
++p->Base.Dropped;
return ERR_NONE; // drop large packets
}
// avoid filling the buffer full (it would mean buffer is empty)
ReadPos -= p->BytePerSample;
if (ReadPos < 0)
ReadPos += p->Buffer.Allocated;
// check if there is enough free space in buffer
Left = ReadPos - WritePos;
if (Left<0)
Left += p->Buffer.Allocated;
if (DstLength > Left)
{
if (!p->Started)
Start(p,State->CurrTime);
if (p->Speed.Num)
return ERR_BUFFER_FULL;
// in benchmark mode we need the package to
// be processed to measure real performance
WritePos = ReadPos - DstLength;
WritePos &= ~3; // align to dword (DstLength is just an approx)
if (WritePos<0)
WritePos += p->Buffer.Allocated;
Left = ReadPos - WritePos;
if (Left<0)
Left += p->Buffer.Allocated;
}
// process packet
SrcPlanes[0] = Packet->Data[0];
SrcPlanes[1] = Packet->Data[1];
Length = Packet->Length;
p->Base.Total += Length;
if (Packet->RefTime >= 0)
{
int Diff = p->Buffer.Allocated - Left; // bytes already in the buffer (with 100% speed)
p->BufferTick = Packet->RefTime - Scale(Diff,TICKSPERSEC,p->BytePerSec) - p->DrvBufferDelay;
if (p->BufferTick < 0)
p->BufferTick = 0;
if (p->Started)
{
// adjust timer if it's already running
int Time = GetTimeTick();
tick_t Tick = Scale(Time-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
tick_t Old = p->Tick + Tick;
// if difference is little then just adjust (because GetTimeTick() is more linear)
if (abs(Old - p->BufferTick) < TICKSPERSEC/2)
p->BufferTick = Old + ((p->BufferTick - Old) >> 2);
p->Tick = p->BufferTick;
p->TimeRef = Time;
}
}
if (p->Skip)
{
SrcLength = min(p->Skip,Length);
SrcPlanes[0] = (uint8_t*)SrcPlanes[0] + SrcLength;
SrcPlanes[1] = (uint8_t*)SrcPlanes[1] + SrcLength;
Length -= SrcLength;
p->Skip -= SrcLength;
}
while (Length>0)
{
if (ReadPos < WritePos)
DstLength = p->Buffer.Allocated - WritePos;
else
DstLength = ReadPos - WritePos;
DstPlanes[0] = p->Buffer.Data + WritePos;
SrcLength = Length;
PCMConvert(p->Base.PCM,DstPlanes,SrcPlanes,&DstLength,&SrcLength,SPEED_ONE,p->Vol/4); //volume needed only for lifedrive fix
if (p->VolumeRamp < RAMPLIMIT)
p->VolumeRamp = VolumeRamp(p->VolumeRamp,DstPlanes[0],DstLength,&p->Base.Output.Format.Audio);
SrcPlanes[0] = (uint8_t*)SrcPlanes[0] + SrcLength;
SrcPlanes[1] = (uint8_t*)SrcPlanes[1] + SrcLength;
Length -= SrcLength;
WritePos += DstLength;
if (WritePos == p->Buffer.Allocated)
WritePos = 0;
if (!SrcLength) // unaligned input (not supported)
break;
}
p->Buffer.WritePos = WritePos;
if (Length>0 && p->Base.Input.Format.Audio.BlockAlign>0)
p->Skip = p->Base.Input.Format.Audio.BlockAlign - Length % p->Base.Input.Format.Audio.BlockAlign;
if (!p->Started)
Start(p,State->CurrTime);
return ERR_NONE;
}
static int Flush(waveout_palm* p)
{
Stop(p);
p->Skip = 0;
p->VolumeRamp = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -