⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 waveout_win32.c

📁 大名鼎鼎的CE下播放软件,TCPPMP的源代码!!!2410下可以流畅的解QVGA的H264,MPEG4等格式.
💻 C
📖 第 1 页 / 共 2 页
字号:
/*****************************************************************************
 *
 * 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 + -