📄 waveout_win32.c
字号:
ReleaseBuffer(p,Buffer,1);
InterlockedDecrement(&p->Waiting);
//DEBUG_MSG1(-1,T("WAVEOUT Waiting %d"),p->Waiting);
}
}
static void UpdatePCM(waveout* p,const audio* InputFormat)
{
p->SpeedTime = p->Speed;
p->SpeedTime.Num *= TICKSPERSEC;
p->SpeedTime.Den *= GetTimeFreq();
p->BufferScaledTime = Scale(TICKSPERSEC,p->Speed.Num*p->BufferLength,p->Speed.Den*p->Format.nAvgBytesPerSec);
if (p->BufferLength)
p->BufferScaledAdjust = (p->BufferScaledTime*4096) / p->BufferLength;
else
p->BufferScaledAdjust = 0;
if (!p->Speed.Num)
p->PCMSpeed = SPEED_ONE;
else
p->PCMSpeed = Scale(SPEED_ONE,p->Speed.Num,p->Speed.Den);
p->AdjustedRate = Scale(p->OutputFormat.SampleRate,p->Speed.Den,p->Speed.Num);
if (p->Swap)
p->OutputFormat.Flags |= PCM_SWAPEDSTEREO;
else
p->OutputFormat.Flags &= ~PCM_SWAPEDSTEREO;
PCMRelease(p->PCM);
p->PCM = PCMCreate(&p->OutputFormat,InputFormat,p->Dither,p->SoftwareVolume);
}
static int UpdateBufferTime(waveout* p)
{
p->BufferLimit = Scale(p->BufferTime,p->Format.nAvgBytesPerSec,p->BufferLength*TICKSPERSEC);
p->BufferLimitFull = p->BufferLimit;
return ERR_NONE;
}
static bool_t FreeBuffers(waveout* p);
static int SetFormat(waveout* p, const audio* Format, bool_t KeepHandle )
{
Reset(p);
FreeBuffers(p);
if (p->Handle && !KeepHandle)
{
waveOutClose(p->Handle);
p->Handle = NULL;
}
p->TotalBytes = 0;
p->BytesPerSample = 1;
memset(&p->InputFormat,0,sizeof(audio));
if (Format)
{
if (!p->Handle || !KeepHandle)
{
MMRESULT MMResult;
int Try;
if (Format->Format != AUDIOFMT_PCM)
return ERR_INVALID_DATA;
if (Format->Channels == 0)
return ERR_NONE;
p->OutputFormat = *Format;
p->OutputFormat.Flags = 0;
p->Dither = 0;
if (p->Stereo>0)
{
p->OutputFormat.Channels = 1;
p->OutputFormat.Flags |= (p->Stereo==1) ? PCM_ONLY_LEFT:PCM_ONLY_RIGHT;
}
switch (p->Quality)
{
case 0: // low quality for very poor devices
p->OutputFormat.Bits = 8;
p->OutputFormat.FracBits = 7;
p->OutputFormat.Channels = 1;
p->OutputFormat.SampleRate = 22050;
break;
case 1: // no dither and only standard samplerate
p->OutputFormat.Bits = 16;
p->OutputFormat.FracBits = 15;
if (p->OutputFormat.SampleRate >= 44100)
p->OutputFormat.SampleRate = 44100;
else
p->OutputFormat.SampleRate = 22050;
break;
default:
case 2: // original samplerate
p->OutputFormat.Bits = 16;
p->OutputFormat.FracBits = 15;
p->Dither = 1;
break;
}
Try = 0;
do
{
if (p->OutputFormat.Bits <= 8)
p->OutputFormat.Flags |= PCM_UNSIGNED;
p->Format.wFormatTag = WAVE_FORMAT_PCM;
p->Format.nChannels = (WORD)p->OutputFormat.Channels;
p->Format.nSamplesPerSec = p->OutputFormat.SampleRate;
p->Format.wBitsPerSample = (WORD)p->OutputFormat.Bits;
p->Format.nBlockAlign = (WORD)((p->Format.nChannels * p->Format.wBitsPerSample) >> 3);
p->Format.nAvgBytesPerSec = p->Format.nSamplesPerSec * p->Format.nBlockAlign;
p->Format.cbSize = 0;
MMResult = waveOutOpen( &p->Handle, WAVE_MAPPER, &p->Format,(DWORD)WaveProc,
(DWORD)p,CALLBACK_FUNCTION);
if (MMResult==WAVERR_BADFORMAT)
{
++Try;
if (Try==1)
{
if (p->OutputFormat.SampleRate > 35000)
p->OutputFormat.SampleRate = 44100;
else
++Try;
}
if (Try==2)
{
if (p->OutputFormat.SampleRate != 22050)
p->OutputFormat.SampleRate = 22050;
else
++Try;
}
if (Try==3)
{
if (p->OutputFormat.Channels > 1)
p->OutputFormat.Channels = 1;
else
++Try;
}
if (Try==4)
{
if (p->OutputFormat.Bits != 8)
{
p->OutputFormat.Bits = 8;
p->OutputFormat.FracBits = 7;
}
else
++Try;
}
if (Try==5)
break;
}
}
while (MMResult==WAVERR_BADFORMAT);
}
if (p->Handle)
{
p->TimeRef = GetTimeTick();
p->InputFormat = *Format;
p->BufferLength = 4096;
if (p->Format.nAvgBytesPerSec > 65536)
p->BufferLength *= 2;
if (p->Format.nAvgBytesPerSec > 2*65536)
p->BufferLength *= 2;
UpdateBufferTime(p);
p->FillPos = p->BufferLength;
p->VolumeRamp = 0;
p->BytesPerSample = p->InputFormat.Bits >> 3;
if (!(p->InputFormat.Flags & PCM_PLANES))
p->BytesPerSample *= p->InputFormat.Channels;
UpdatePCM(p,&p->InputFormat);
Reset(p);
}
else
{
NodeError(&p->VMT,ERR_ID,ERR_DEVICE_ERROR);
return ERR_DEVICE_ERROR;
}
}
return ERR_NONE;
}
static int SetInput(waveout* p,const packet* Packet)
{
if (!p->Handle)
{
// act as a null device
if (Packet->CurrTime != -1 && (Packet->RefTime > Packet->CurrTime + SHOWAHEAD))
return ERR_BUFFER_FULL;
else
return ERR_NONE;
}
if (p->Speed.Num==0) // benchmark mode (auto adjust speed)
{
int Pos = p->BenchWaitPos;
int OldSum;
int Speed;
if (p->Play)
{
while (Packet->Length > p->BenchAvgLimit)
UpdateBenchAvg(p);
p->BenchCurrSum -= p->BenchWait[Pos];
p->BenchWait[Pos] = (p->Waiting * p->BufferLength) >> 12;
p->BenchCurrSum += p->BenchWait[Pos];
OldSum = p->BenchSum[Pos];
p->BenchSum[Pos] = p->BenchCurrSum;
if (++Pos == BENCH_SIZE)
Pos = 0;
p->BenchWaitPos = Pos;
if (p->BenchCurrSum < 2*BENCH_SIZE*p->BenchAvg)
Speed = (p->BenchCurrSum+1) * p->BenchSpeedAvg;
else
Speed = 2*SPEED_ONE+(p->BenchCurrSum-2*BENCH_SIZE*p->BenchAvg+1) * 4*p->BenchSpeedAvg;
Speed -= p->BenchAdj*(p->BenchCurrSum - OldSum);
if (p->Waiting < 3)
Speed -= SPEED_ONE/5;
}
else
Speed = SPEED_ONE;
//DEBUG_MSG3(-1,T("Audio speed:%d length:%d (wait:%d)"),Speed,Packet->Length,p->Waiting);
return Send(p,Packet->Data,Packet->Length,Packet->RefTime,Packet->CurrTime,Speed);
}
if (Packet->DropLevel)
return ERR_NONE;
if (p->Used >= p->BufferLimit)
{
Write(p,Packet->CurrTime);
return ERR_BUFFER_FULL;
}
DEBUG_MSG3(DEBUG_AUDIO,T("Waveout reftime:%d used:%d waiting:%d"),Packet->RefTime,p->Used,p->Waiting);
return Send(p,Packet->Data,Packet->Length,Packet->RefTime,Packet->CurrTime,p->PCMSpeed);
}
static int Update(waveout* p)
{
buffer *OldFirst;
buffer *OldLast;
audio OldFormat;
buffer* OldBuffers;
buffer* OldFill;
int OldFillPos;
int OldUsed;
bool_t OldVolume;
buffer *Buffer,*Next;
EnterCriticalSection(&p->Section);
OldVolume = p->SoftwareVolume;
OldUsed = p->Used;
OldFirst = p->FreeFirst;
OldLast = p->FreeLast;
OldFormat = p->OutputFormat;
OldFormat.SampleRate = p->AdjustedRate;
OldFill = p->FillFirst;
OldFillPos = p->FillPos;
p->FillFirst = NULL;
p->FillLast = NULL;
p->FillPos = p->BufferLength;
p->FreeFirst = NULL;
p->FreeLast = NULL;
LeaveCriticalSection(&p->Section);
Reset(p);
OldBuffers = p->FreeFirst;
if (p->FreeLast)
p->FreeLast->Next = OldFill;
else
OldBuffers = OldFill;
p->SoftwareVolume = 0;
p->Used = OldUsed;
p->FreeFirst = OldFirst;
p->FreeLast = OldLast;
// setup temporary format
UpdatePCM(p,&OldFormat);
for (Buffer=OldBuffers;Buffer;Buffer=Next)
{
Next = Buffer->Next;
Send(p,Buffer->Planes,Next ? p->BufferLength:OldFillPos,Buffer->RefTime,-1,p->PCMSpeed);
ReleaseBuffer(p,Buffer,0);
}
p->SoftwareVolume = OldVolume;
UpdatePCM(p,&p->InputFormat);
return ERR_NONE;
}
static bool_t FreeBuffers(waveout* p)
{
buffer** Ptr;
buffer* Buffer;
bool_t Changed = 0;
while (p->FreeFirst)
{
Buffer = p->FreeFirst;
p->FreeFirst = Buffer->Next;
// remove from global chain
Ptr = &p->Buffers;
while (*Ptr && *Ptr != Buffer)
Ptr = &(*Ptr)->GlobalNext;
if (*Ptr == Buffer)
*Ptr = Buffer->Next;
waveOutUnprepareHeader( p->Handle, &Buffer->Head, sizeof(WAVEHDR) );
FreeBlock(Buffer->Buffer);
Free(Buffer);
Changed = 1;
}
p->FreeLast = NULL;
return Changed;
}
static int Hibernate(waveout* p,int Mode)
{
bool_t Changed = 0;
EnterCriticalSection(&p->Section);
Changed = FreeBuffers(p);
LeaveCriticalSection(&p->Section);
return Changed ? ERR_NONE : ERR_OUT_OF_MEMORY;
}
static int UpdateFormat(waveout* p,bool_t KeepFormat)
{
audio SaveFormat = p->InputFormat;
return SetFormat(p,&SaveFormat,KeepFormat);
}
static int UpdatePlay(waveout* p)
{
if (p->Play)
{
p->TimeRef = GetTimeTick();
if (p->Handle)
Write(p,p->Tick);
}
else
{
if (!p->Waiting)
p->Tick += Scale(GetTimeTick()-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
else
if (p->Handle)
{
p->Pausing = &p->FillFirst;
waveOutReset(p->Handle);
p->Pausing = NULL;
p->FillLast = p->FillFirst;
if (p->FillLast)
while (p->FillLast->Next) p->FillLast = p->FillLast->Next;
}
}
return ERR_NONE;
}
static int SetVolume(waveout* p)
{
if (p->Mute)
waveOutSetVolume(NULL,0);
else
waveOutSetVolume(NULL,0x10001 * ((0xFFFF * p->Volume) / 100));
return ERR_NONE;
}
static int Set(waveout* p, int No, const void* Data, int Size )
{
int Result = ERR_INVALID_PARAM;
switch (No)
{
case AOUT_INPUT|PACKET_FORMAT:
if (!Size || Size == sizeof(audio))
{
if (!Size) Data = NULL;
Result = SetFormat(p,(const audio*)Data,0);
}
break;
case AOUT_INPUT:
if (Size == sizeof(packet))
Result = SetInput(p,(const packet*)Data);
break;
case AOUT_VOLUME:
if (Size==sizeof(int))
{
if (p->SoftwareVolume)
{
SetSoftVolume(*(int*)Data);
Result = UpdateVolumeSoftLog(p);
}
else
{
p->Volume = *(int*)Data;
Result = SetVolume(p);
}
}
break;
case AOUT_MUTE:
if (Size==sizeof(bool_t))
{
if (!p->Mute)
GetVolume(p); // save old volume to p->Volume
p->Mute = *(bool_t*)Data;
Result = SetVolume(p);
}
break;
case AOUT_SWAP: SETVALUECMP(p->Swap,bool_t,Update(p),EqBool); break;
case AOUT_STEREO: SETVALUECMP(p->Stereo,int,UpdateFormat(p,0),EqInt); break;
case AOUT_QUALITY: SETVALUECMP(p->Quality,int,UpdateFormat(p,0),EqInt); break;
case AOUT_BUFFER: SETVALUE(p->BufferTime,tick_t,UpdateBufferTime(p)); break;
case NODE_REFRESH:
if (p->Handle)
{
p->SoftwareVolume = QueryAdvPlatform(ADVPLATFORM_SYSTEMVOLUME)==0;
UpdateVolumeSoftLog(p);
UpdatePCM(p,&p->InputFormat);
}
break;
case NODE_HIBERNATE:
if (Size == sizeof(int))
Result = Hibernate(p,*(int*)Data);
break;
case FLOW_TOTAL:
if (Size == sizeof(int))
{
p->TotalBytes = *(const int*)Data * p->BytesPerSample;
Result = ERR_NONE;
}
break;
case FLOW_EMPTY:
Reset(p);
p->VolumeRamp = 0;
Result = ERR_NONE;
break;
case TIMER_PLAY: SETVALUECMP(p->Play,bool_t,UpdatePlay(p),EqBool); break;
case TIMER_TIME:
if (Size == sizeof(tick_t))
{
EnterCriticalSection(&p->Section);
p->Tick = *(tick_t*)Data;
p->TimeRef = GetTimeTick();
LeaveCriticalSection(&p->Section);
Result = ERR_NONE;
}
break;
case TIMER_SPEED:
if (Size == sizeof(fraction))
{
Result = ERR_NONE;
if (!EqFrac( &p->Speed, (const fraction*)Data ))
{
p->Speed = *(const fraction*)Data;
Result = Update(p);
}
}
break;
}
return Result;
}
static waveout WaveOut;
void WaveOut_Init()
{
WAVEOUTCAPS Caps;
waveout* p = &WaveOut;
NodeFill(&p->VMT,sizeof(waveout),Enum,Get,Set);
p->Volume = 70;
p->VolumeSoftLog = 256;
p->Quality = 2;
p->BufferTime = TICKSPERSEC*5;
p->Speed.Den = p->Speed.Num = 1;
p->SpeedTime.Num = TICKSPERSEC;
p->SpeedTime.Den = GetTimeFreq();
p->PCM = NULL;
waveOutGetDevCaps(WAVE_MAPPER,&Caps,sizeof(Caps));
p->MonoVol = (Caps.dwSupport & WAVECAPS_LRVOLUME) == 0;
p->SoftwareVolume = QueryAdvPlatform(ADVPLATFORM_SYSTEMVOLUME)==0;
InitializeCriticalSection(&p->Section);
if (waveOutGetNumDevs())
NodeRegister(&p->VMT,PRI_DEFAULT);
GetVolume(p);
UpdateVolumeSoftLog(p);
}
void WaveOut_Done()
{
waveout* p = &WaveOut;
PCMRelease(p->PCM);
DeleteCriticalSection(&p->Section);
NodeUnRegister(&p->VMT);
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -