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

📄 waveout_win32.c

📁 betaplayer的源码 tcpmp的老版本
💻 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 202 2005-01-25 01:27:33Z picard $
 *
 * BetaPlayer Core
 * Copyright (c) 2004 Gabor Kovacs
 *
 ****************************************************************************/

#include "../stdafx.h"
#include <math.h>

#if defined(_WIN32)

#ifndef STRICT
#define STRICT
#endif
#include <windows.h>

#define RAMPSIZE		15

#define BENCH_SIZE		32

struct waveout;

typedef struct buffer
{
	WAVEHDR Head;
	planes Planes;
	struct buffer* Next; //next in chain
	struct buffer* GlobalNext;
	tick_t RefTime;
	int Bytes;
	char* Buffer;

} buffer;

typedef struct waveout
{
	node VMT;

	audio InputFormat;
	audio OutputFormat;

	buffer* Buffers; // global chain

	buffer* FreeFirst;
	buffer* 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 BytesPerSample;
	int TotalBytes; // source format
	int Bytes; // output format
	int FillPos;
	int Skip;
	buffer* FillFirst;
	buffer* FillLast;
	buffer** Pausing;

	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)

	int Play;
	fraction Speed;
	fraction SpeedTime;
	int AdjustedRate;

	bool_t Dither;
	bool_t Swap;
	int Stereo;
	int Quality;
	tick_t BufferTime;

	bool_t SoftwareVolume;
	bool_t MonoVol;
	bool_t Mute; // just for caching
	int Volume; // backup value when mute is turned on
	int VolumeRamp;
	int VolumeSoftLog;

	WAVEFORMATEX Format;

	int BenchWait[BENCH_SIZE];
	int BenchSum[BENCH_SIZE];
	int BenchCurrSum;
	int BenchAvg;
	int BenchAdj;
	int BenchAvgLimit;
	int BenchSpeedAvg;
	int BenchWaitPos;
	
} waveout;

static int Enum( waveout* p, int No, datadef* Param )
{
	return NodeEnumType(&No,Param,NodeParams,TimerParams,FlowParams,OutputParams,AOutputParams,NULL);
}

static int UpdateVolumeSoftLog(waveout* p)
{
	int OldVolumeSoftLog = p->VolumeSoftLog;

	if (p->SoftwareVolume)
		p->VolumeSoftLog = (int)(pow(10,(50+GetSoftVolume())/62.3));
	else
		p->VolumeSoftLog = 256;

	if (p->Handle && OldVolumeSoftLog)
	{
		buffer* List;
		int Adjust = (p->VolumeSoftLog * 256)/OldVolumeSoftLog;
		if (Adjust != 256)
		{
			void* PCM = PCMCreate(&p->OutputFormat,&p->OutputFormat,0,1);
			void* Buffer = Alloc(p->BufferLength);

			if (Buffer && PCM)
				for (List=p->Buffers;List;List=List->GlobalNext)
				{
					int DstLength;
					int SrcLength;
					planes DstPlanes;
					planes SrcPlanes;

					DstLength = SrcLength = p->BufferLength;
					SrcPlanes[0] = List->Buffer;
					DstPlanes[0] = Buffer;

					PCMConvert(PCM,DstPlanes,SrcPlanes,&DstLength,&SrcLength,SPEED_ONE,Adjust);
					memcpy(List->Buffer,Buffer,p->BufferLength);
				}

			Free(Buffer);
			PCMRelease(PCM);
		}
	}

	return ERR_NONE;
}

static bool_t GetMute(waveout* p)
{
	DWORD Value = 0;
	waveOutGetVolume(NULL,&Value);
	if (p->MonoVol) Value &= 0xFFFF;
	p->Mute = Value==0;
	return p->Mute;
}

static int GetVolume(waveout* p)
{
	DWORD Value;
	if (waveOutGetVolume(NULL,&Value) == MMSYSERR_NOERROR)
	{
		if (p->MonoVol)
		{
			Value &= 0xFFFF;
			Value |= Value << 16;
		}
		p->Mute = Value==0;
		if (!p->Mute)
			p->Volume = ((LOWORD(Value)+HIWORD(Value)+600)*100) / (0xFFFF*2);
	}

	if (p->SoftwareVolume)
		return GetSoftVolume();

	return p->Volume;
}

static int Get( waveout* p, int No, void* Data, int Size )
{
	int Result = ERR_INVALID_PARAM;
	switch (No)
	{
	case NODE_ID: GETVALUE(WAVEOUT_ID,int); break;
	case FLOW_EMPTY: GETVALUE(p->Waiting<=0,bool_t); break;
	case AOUT_INPUT|PACKET_FORMAT: GETVALUE(p->InputFormat,audio); break;
	case AOUT_OUTPUT|PACKET_FORMAT: GETVALUE(p->OutputFormat,audio); break;
	case AOUT_VOLUME: GETVALUE(GetVolume(p),int); break;
	case AOUT_MUTE: GETVALUE(GetMute(p),bool_t); break;
	case AOUT_STEREO: GETVALUE(p->Stereo,int); break; 
	case AOUT_SWAP: GETVALUE(p->Swap,bool_t); break;
	case AOUT_BUFFER: GETVALUE(p->BufferTime,tick_t); break;
	case AOUT_QUALITY: GETVALUE(p->Quality,int); break;
	case FLOW_TOTAL:GETVALUE(p->BytesPerSample ? (p->TotalBytes / p->BytesPerSample):0,int); break;
	case TIMER_PLAY: GETVALUE(p->Play,bool_t); break;
	case TIMER_SPEED: GETVALUE(p->Speed,fraction); break;
	case TIMER_TIME:
		if (Size == sizeof(tick_t))
		{
			tick_t t;
			if (p->Speed.Num==0)
				t = -1;
			else
			if (p->Play)
			{
				EnterCriticalSection(&p->Section);
				t = p->Tick + Scale(GetTimeTick()-p->TimeRef,p->SpeedTime.Num,p->SpeedTime.Den);
				LeaveCriticalSection(&p->Section);
			}
			else
				t = p->Tick;

			*(tick_t*)Data = t;
			Result = ERR_NONE;
		}
		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->BytesPerSample * p->OutputFormat.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,buffer* Buffer,bool_t Update)
{
	EnterCriticalSection(&p->Section);

	if (Update)
	{
		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;
	}
	
	// release fill chain
	while (p->FillFirst)
	{
		buffer* 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 buffer* GetBuffer(waveout* p)
{
	buffer* 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)
	{
		char* i = (char*) AllocBlock(p->BufferLength,p->Used>=20);
		if (i)
		{
			Buffer = (buffer*) Alloc(sizeof(buffer));
			if (!Buffer)
				FreeBlock(i);
		}

		if (Buffer)
		{
			Buffer->Buffer = i;
			Buffer->Planes[0] = Buffer->Buffer;
			Buffer->Next = NULL;

			memset(&Buffer->Head,0,sizeof(WAVEHDR));
			Buffer->Head.lpData = Buffer->Buffer;
			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->Buffer);
				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)
		{
			buffer* Buffer = p->FillFirst;

			if (!p->Waiting && CurrTime >= 0 && Buffer->RefTime >= 0 && Buffer->RefTime > CurrTime + SHOWAHEAD)
				break;

			p->FillFirst = Buffer->Next;

			if (waveOutWrite( p->Handle, &Buffer->Head, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
				ReleaseBuffer(p,Buffer,0);
			else
				InterlockedIncrement(&p->Waiting);
		}
}

static int Send(waveout* p, const planes Planes, int Length, tick_t RefTime, tick_t CurrTime, int Speed )
{
	buffer* Buffer;
	int DstLength;
	int SrcLength;
	planes DstPlanes;
	planes SrcPlanes;

	p->TotalBytes += Length;

	SrcPlanes[0] = Planes[0];
	SrcPlanes[1] = Planes[1];

	if (p->Skip > 0)
	{
		SrcLength = p->Skip;
		if (SrcLength > Length)
			SrcLength = Length;

		SrcPlanes[0] = (uint8_t*)SrcPlanes[0] + SrcLength;
		SrcPlanes[1] = (uint8_t*)SrcPlanes[1] + SrcLength;
		Length -= SrcLength;
	}

	while (Length > 0)
	{
		if (p->FillPos >= p->BufferLength)
		{
			// allocate new buffer
			Buffer = GetBuffer(p);
			if (!Buffer)
				break;

			if (p->FillLast)
				p->FillLast->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);
			RefTime = -1;
		}

		SrcLength = Length;
		DstLength = p->BufferLength - p->FillPos;
		DstPlanes[0] = Buffer->Buffer + p->FillPos;

		PCMConvert(p->PCM,DstPlanes,SrcPlanes,&DstLength,&SrcLength,Speed,p->VolumeSoftLog);

		if (p->VolumeRamp < (1<<RAMPSIZE))
		{
			uint8_t *u8,*u8e;
			int16_t *s16,*s16e;
			int Ramp = p->VolumeRamp;
			int Inc = (10*(1<<RAMPSIZE)) / (p->OutputFormat.Channels * p->OutputFormat.SampleRate);
			if (Inc<=0) Inc=1;

			switch (p->OutputFormat.Bits)
			{
			case 8:
				u8=DstPlanes[0];
				u8e=u8+DstLength;
				for (;u8!=u8e && Ramp<(1<<RAMPSIZE);++u8,Ramp+=Inc)
					*u8 = (uint8_t)((((*u8 - 0x80) * Ramp) >> RAMPSIZE) + 0x80);
				break;

			case 16:
				s16=DstPlanes[0];
				s16e=s16+(DstLength>>1);
				for (;s16!=s16e && Ramp<(1<<RAMPSIZE);++s16,Ramp+=Inc)
					*s16 = (int16_t)((*s16 * Ramp) >> RAMPSIZE);
				break;

			default:
				Ramp = RAMPSIZE;
				break;
			}

			p->VolumeRamp = Ramp;
		}

		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->InputFormat.BlockAlign>0)
		p->Skip = p->InputFormat.BlockAlign - Length % p->InputFormat.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)
    {
		buffer* Buffer = (buffer*)(((WAVEHDR*)dwParam1)->dwUser);
		waveout* p = (waveout*)dwInstance;
		if (p->Pausing)
		{
			// add buffer to fill chain
			Buffer->Next = *p->Pausing;
			*p->Pausing = Buffer;
			p->Pausing = &Buffer->Next;
		}
		else

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -