📄 waveout_win32.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_win32.c 543 2006-01-07 22:06:24Z picard $
*
* The Core Pocket Media Player
* Copyright (c) 2004-2005 Gabor Kovacs
*
****************************************************************************/
#include "../common.h"
#include <math.h>
#if defined(TARGET_WIN32) || defined(TARGET_WINCE)
#define BUFFER_MUSIC 1*TICKSPERSEC
#define BUFFER_VIDEO 2*TICKSPERSEC
#ifndef STRICT
#define STRICT
#endif
#include <windows.h>
#define BENCH_SIZE 32
struct waveout;
typedef struct wavebuffer
{
WAVEHDR Head;
planes Planes;
struct wavebuffer* Next; //next in chain
struct wavebuffer* GlobalNext;
tick_t RefTime;
tick_t EstRefTime;
int Bytes;
block Block;
} wavebuffer;
typedef struct waveout
{
node Node;
node Timer;
pin Pin;
packetformat Input;
packetformat Output;
packetprocess Process;
wavebuffer* Buffers; // global chain
wavebuffer* FreeFirst;
wavebuffer* FreeLast;
int BufferLength; // one buffer length (waveout format)
tick_t BufferScaledTime; // scaled time of one waveout buffer (BufferLength)
int BufferScaledAdjust; // waveout bytes to scaled time convert (12bit fixed point)
int Total; // source format
int Dropped; // dropped packets
int Bytes; // output format
int FillPos;
int Skip;
wavebuffer* FillFirst;
wavebuffer* FillLast;
wavebuffer** Pausing;
tick_t FillLastTime;
HWAVEOUT Handle;
CRITICAL_SECTION Section;
void* PCM;
int PCMSpeed;
int BufferLimit;
int BufferLimitFull;
tick_t Tick;
int TimeRef;
LONG Waiting; // number of waiting buffers in WaveOut
LONG Used; // number of used buffers (waiting or in fill chain)
bool_t Play;
fraction Speed;
fraction SpeedTime;
int AdjustedRate;
bool_t Dither;
bool_t BufferMode;
int Stereo;
int Quality;
bool_t ForcePriority;
bool_t SoftwareVolume;
bool_t MonoVol;
bool_t Mute;
int PreAmp;
int VolumeDev; // backup value when mute is turned on
int VolumeSoft;
int VolumeSoftLog;
int VolumeRamp;
WAVEFORMATEX Format;
int BenchWait[BENCH_SIZE];
int BenchSum[BENCH_SIZE];
int BenchCurrSum;
int BenchAvg;
int BenchAdj;
size_t BenchAvgLimit;
int BenchSpeedAvg;
int BenchWaitPos;
} waveout;
#define WAVEOUT(p) ((waveout*)((char*)(p)-OFS(waveout,Timer)))
static void Pause(waveout* p);
static void Write(waveout* p, tick_t CurrTime);
static int SetVolumeSoft(waveout* p,int v,bool_t m)
{
int OldVolumeSoftLog = p->VolumeSoftLog;
bool_t OldSkip = p->Mute || p->VolumeSoft==0;
p->VolumeSoft = v;
p->Mute = m;
if (p->SoftwareVolume || p->PreAmp)
{
if (p->SoftwareVolume && p->Handle && (p->Mute || p->VolumeSoft==0)!=OldSkip)
{
if (!OldSkip)
Pause(p);
else
if (p->Play)
{
// adjust tick so packets without RefTime won't mess up timing
EnterCriticalSection(&p->Section);
p->Tick += Scale(GetTimeTick()-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
p->TimeRef = GetTimeTick();
LeaveCriticalSection(&p->Section);
}
}
v += p->PreAmp;
if (v<-40) v=-40;
p->VolumeSoftLog = (int)(pow(10,(50+v)/62.3));
if (p->VolumeSoftLog < 3)
p->VolumeSoftLog = 3;
}
else
p->VolumeSoftLog = 256;
if (p->Handle)
{
int Adjust = ScaleRound(p->VolumeSoftLog,256,OldVolumeSoftLog);
if (Adjust != 256)
{
wavebuffer* List;
EnterCriticalSection(&p->Section);
for (List=p->Buffers;List;List=List->GlobalNext)
VolumeMul(Adjust,(void*)List->Block.Ptr,p->BufferLength,&p->Output.Format.Audio);
LeaveCriticalSection(&p->Section);
}
}
return ERR_NONE;
}
static int GetVolume(waveout* p)
{
if (!p->SoftwareVolume)
{
DWORD Value;
if (waveOutGetVolume(NULL,&Value) == MMSYSERR_NOERROR)
{
if (p->MonoVol)
{
Value &= 0xFFFF;
Value |= Value << 16;
}
if (Value)
p->Mute = 0;
if (!p->Mute)
p->VolumeDev = ((LOWORD(Value)+HIWORD(Value)+600)*100) / (0xFFFF*2);
}
return p->VolumeDev;
}
return p->VolumeSoft;
}
static tick_t Time(waveout* p)
{
if (p->Speed.Num==0)
return TIME_BENCH;
if (p->Play)
{
tick_t t;
EnterCriticalSection(&p->Section);
t = p->Tick + Scale(GetTimeTick()-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
LeaveCriticalSection(&p->Section);
return t;
}
return p->Tick;
}
static int TimerGet(void* pt, int No, void* Data, int Size)
{
waveout* p = WAVEOUT(pt);
int Result = ERR_INVALID_PARAM;
switch (No)
{
case TIMER_PLAY: GETVALUE(p->Play,bool_t); break;
case TIMER_SPEED: GETVALUE(p->Speed,fraction); break;
case TIMER_TIME: GETVALUE(Time(p),tick_t); break;
}
return Result;
}
static int Get(waveout* 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->Process,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_VOLUME: GETVALUE(GetVolume(p),int); break;
case AOUT_MUTE: GETVALUE(p->Mute,bool_t); break;
case AOUT_PREAMP: GETVALUE(p->PreAmp,int); break;
case AOUT_STEREO: GETVALUE(p->Stereo,int); break;
case AOUT_MODE: GETVALUE(p->BufferMode,bool_t); break;
case AOUT_QUALITY: GETVALUE(p->Quality,int); break;
case AOUT_TIMER: GETVALUE(&p->Timer,node*); break;
}
return Result;
}
static void UpdateBenchAvg(waveout* p)
{
int n;
p->BenchAvg += 4;
p->BenchAdj = (24*16) / p->BenchAvg;
p->BenchSpeedAvg = SPEED_ONE/(p->BenchAvg*BENCH_SIZE);
n = p->Input.Format.Audio.Bits >> 3;
if (!(p->Input.Format.Audio.Flags & PCM_PLANES))
n *= p->Input.Format.Audio.Channels;
n = p->Output.Format.Audio.SampleRate;
if (n>0)
p->BenchAvgLimit = (n * p->BenchAvg) / 160;
else
p->BenchAvgLimit = MAX_INT;
p->BenchCurrSum += 2 * BENCH_SIZE;
for (n=0;n<BENCH_SIZE;++n)
{
p->BenchSum[n] += 2 * BENCH_SIZE;
p->BenchWait[n] += 2;
}
}
static void ReleaseBuffer(waveout* p,wavebuffer* Buffer,bool_t UpdateTick)
{
EnterCriticalSection(&p->Section);
if (UpdateTick)
{
tick_t Old;
int Time = GetTimeTick();
Old = p->Tick + Scale(Time-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
if (Buffer->RefTime >= 0)
p->Tick = Buffer->RefTime + p->BufferScaledTime;
else
p->Tick += p->BufferScaledTime;
p->TimeRef = Time;
//DEBUG_MSG2(-1,T("WaveOutTime: %d %d"),Old,p->Tick);
// if difference is little then just adjust (because GetTimeTick() is more linear)
if (abs(Old - p->Tick) < TICKSPERSEC/2)
p->Tick = Old + ((p->Tick - Old) >> 2);
}
Buffer->Next = NULL;
if (p->FreeLast)
p->FreeLast->Next = Buffer;
else
p->FreeFirst = Buffer;
p->FreeLast = Buffer;
p->Used--;
LeaveCriticalSection(&p->Section);
}
static void Reset(waveout* p)
{
int n;
// release buffers already sended to device
if (p->Handle)
{
tick_t OldTick = p->Tick;
waveOutReset(p->Handle);
p->Tick = OldTick;
}
if (p->FillLast && p->FillLast->RefTime >= 0)
p->FillLastTime = p->FillLast->RefTime;
// release fill chain
while (p->FillFirst)
{
wavebuffer* Buffer = p->FillFirst;
p->FillFirst = Buffer->Next;
ReleaseBuffer(p,Buffer,0);
}
p->FillLast = NULL;
p->FillPos = p->BufferLength;
p->Skip = 0;
p->Bytes = 0;
p->BufferLimit = p->BufferLimitFull;
p->BenchAvg = 16-4;
UpdateBenchAvg(p);
p->BenchWaitPos = 0;
p->BenchCurrSum = p->BenchAvg * BENCH_SIZE;
for (n=0;n<BENCH_SIZE;++n)
{
p->BenchSum[n] = p->BenchCurrSum;
p->BenchWait[n] = p->BenchAvg;
}
PCMReset(p->PCM); // init dither and subsample position
}
static wavebuffer* GetBuffer(waveout* p)
{
wavebuffer* Buffer;
// try to find a free buffer
EnterCriticalSection(&p->Section);
Buffer = p->FreeFirst;
if (Buffer)
{
p->FreeFirst = Buffer->Next;
if (Buffer == p->FreeLast)
p->FreeLast = NULL;
}
if (!Buffer)
{
block Block;
if (AllocBlock(p->BufferLength,&Block,p->Used>=20,HEAP_DYNAMIC))
{
Buffer = (wavebuffer*)malloc(sizeof(wavebuffer));
if (!Buffer)
FreeBlock(&Block);
}
if (Buffer)
{
Buffer->Block = Block;
Buffer->Planes[0] = (uint8_t*)Block.Ptr;
Buffer->Next = NULL;
memset(&Buffer->Head,0,sizeof(WAVEHDR));
Buffer->Head.lpData = (char*)Buffer->Block.Ptr;
Buffer->Head.dwUser = (DWORD)Buffer;
Buffer->Head.dwBufferLength = p->BufferLength;
Buffer->Head.dwBytesRecorded = p->BufferLength;
if (waveOutPrepareHeader(p->Handle, &Buffer->Head, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
{
FreeBlock(&Buffer->Block);
free(Buffer);
Buffer = NULL;
}
else
{
Buffer->GlobalNext = p->Buffers;
p->Buffers = Buffer;
}
}
else
{
p->BufferLimitFull = p->Used;
if (p->BufferLimit > p->Used)
p->BufferLimit = p->Used;
else
if (p->BufferLimit > 4)
p->BufferLimit--;
}
}
if (Buffer)
{
p->Used++;
Buffer->RefTime = -1;
Buffer->Next =NULL;
}
LeaveCriticalSection(&p->Section);
return Buffer;
}
static void Write(waveout* p, tick_t CurrTime)
{
if (p->Play)
{
while (p->FillFirst != p->FillLast)
{
wavebuffer* Buffer = p->FillFirst;
if (!p->Waiting && CurrTime >= 0 && Buffer->EstRefTime >= 0 && Buffer->EstRefTime > CurrTime + SHOWAHEAD)
break;
p->FillFirst = Buffer->Next;
if (p->SoftwareVolume && (p->Mute || p->VolumeSoft==0))
ReleaseBuffer(p,Buffer,0);
else
{
if (waveOutWrite(p->Handle, &Buffer->Head, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
ReleaseBuffer(p,Buffer,0);
else
InterlockedIncrement(&p->Waiting);
}
}
}
}
static int Send(waveout* p, const constplanes Planes, int Length, tick_t RefTime, tick_t CurrTime, int Speed)
{
wavebuffer* Buffer;
int DstLength;
int SrcLength;
planes DstPlanes;
constplanes SrcPlanes;
p->Total += Length;
SrcPlanes[0] = Planes[0];
SrcPlanes[1] = Planes[1];
if (p->Skip > 0)
{
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 (p->FillPos >= p->BufferLength)
{
// allocate new buffer
Buffer = GetBuffer(p);
if (!Buffer)
break;
if (p->FillLast)
{
wavebuffer* Last = p->FillLast;
if (Last->RefTime>=0)
p->FillLastTime = Last->EstRefTime = Last->RefTime;
else
{
if (p->FillLastTime>=0)
p->FillLastTime += p->BufferScaledTime;
Last->EstRefTime = p->FillLastTime;
}
Last->Next = Buffer;
}
else
p->FillFirst = Buffer;
p->FillLast = Buffer;
p->FillPos = 0;
Buffer->Bytes = p->Bytes;
}
else
Buffer = p->FillLast;
if (RefTime >= 0)
{
Buffer->RefTime = RefTime - ((p->FillPos * p->BufferScaledAdjust) >> 12);
if (Buffer->RefTime < 0)
Buffer->RefTime = 0;
RefTime = TIME_UNKNOWN;
}
SrcLength = Length;
DstLength = p->BufferLength - p->FillPos;
DstPlanes[0] = (uint8_t*)Buffer->Block.Ptr + p->FillPos;
PCMConvert(p->PCM,DstPlanes,SrcPlanes,&DstLength,&SrcLength,Speed,p->VolumeSoftLog);
if (p->VolumeRamp < RAMPLIMIT)
p->VolumeRamp = VolumeRamp(p->VolumeRamp,DstPlanes[0],DstLength,&p->Output.Format.Audio);
p->Bytes += DstLength;
p->FillPos += DstLength;
SrcPlanes[0] = (uint8_t*)SrcPlanes[0] + SrcLength;
SrcPlanes[1] = (uint8_t*)SrcPlanes[1] + SrcLength;
Length -= SrcLength;
if (!SrcLength)
break;
}
if (Length && p->Input.Format.Audio.BlockAlign>0)
p->Skip = p->Input.Format.Audio.BlockAlign - Length % p->Input.Format.Audio.BlockAlign;
Write(p,CurrTime);
return ERR_NONE;
}
static void CALLBACK WaveProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2)
{
if (uMsg == WOM_DONE)
{
wavebuffer* Buffer = (wavebuffer*)(((WAVEHDR*)dwParam1)->dwUser);
waveout* p = (waveout*)dwInstance;
if (p->ForcePriority)
{
void* Thread = GetCurrentThread();
if (GetThreadPriority(Thread)!=THREAD_PRIORITY_HIGHEST)
SetThreadPriority(Thread,THREAD_PRIORITY_HIGHEST);
}
if (p->Pausing)
{
// add buffer to fill chain
Buffer->Next = *p->Pausing;
*p->Pausing = Buffer;
p->Pausing = &Buffer->Next;
}
else
ReleaseBuffer(p,Buffer,1);
InterlockedDecrement(&p->Waiting);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -