reactos/drivers/wdm/audio/filters/kmixer/pin.c

739 lines
22 KiB
C

/*
* PROJECT: ReactOS Kernel Streaming Mixer
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/wdm/audio/filters/kmixer/kmixer.c
* PURPOSE: Pin functions
* PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#include "kmixer.h"
#include <samplerate.h>
#define NDEBUG
#include <debug.h>
const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
NTSTATUS
PerformSampleRateConversion(
PUCHAR Buffer,
ULONG BufferLength,
ULONG OldRate,
ULONG NewRate,
ULONG BytesPerSample,
ULONG NumChannels,
PVOID * Result,
PULONG ResultLength)
{
KFLOATING_SAVE FloatSave;
NTSTATUS Status;
ULONG Index;
SRC_STATE * State;
SRC_DATA Data;
PUCHAR ResultOut;
int error;
PFLOAT FloatIn, FloatOut;
ULONG NumSamples;
ULONG NewSamples;
DPRINT("PerformSampleRateConversion OldRate %u NewRate %u BytesPerSample %u NumChannels %u Irql %u\n", OldRate, NewRate, BytesPerSample, NumChannels, KeGetCurrentIrql());
ASSERT(BytesPerSample == 1 || BytesPerSample == 2 || BytesPerSample == 4);
/* first acquire float save context */
Status = KeSaveFloatingPointState(&FloatSave);
if (!NT_SUCCESS(Status))
{
DPRINT1("KeSaveFloatingPointState failed with %x\n", Status);
return Status;
}
NumSamples = BufferLength / (BytesPerSample * NumChannels);
FloatIn = ExAllocatePool(NonPagedPool, NumSamples * NumChannels * sizeof(FLOAT));
if (!FloatIn)
{
KeRestoreFloatingPointState(&FloatSave);
return STATUS_INSUFFICIENT_RESOURCES;
}
NewSamples = ((((ULONG64)NumSamples * NewRate) + (OldRate / 2)) / OldRate) + 2;
FloatOut = ExAllocatePool(NonPagedPool, NewSamples * NumChannels * sizeof(FLOAT));
if (!FloatOut)
{
ExFreePool(FloatIn);
KeRestoreFloatingPointState(&FloatSave);
return STATUS_INSUFFICIENT_RESOURCES;
}
ResultOut = ExAllocatePool(NonPagedPool, NewSamples * NumChannels * BytesPerSample);
if (!ResultOut)
{
ExFreePool(FloatIn);
ExFreePool(FloatOut);
KeRestoreFloatingPointState(&FloatSave);
return STATUS_INSUFFICIENT_RESOURCES;
}
State = src_new(SRC_SINC_FASTEST, NumChannels, &error);
if (!State)
{
DPRINT1("src_new failed with %x\n", error);
KeRestoreFloatingPointState(&FloatSave);
ExFreePool(FloatIn);
ExFreePool(FloatOut);
ExFreePool(ResultOut);
return STATUS_UNSUCCESSFUL;
}
/* fixme use asm */
if (BytesPerSample == 1)
{
for(Index = 0; Index < NumSamples * NumChannels; Index++)
FloatIn[Index] = (float)(Buffer[Index] / (1.0 * 0x80));
}
else if (BytesPerSample == 2)
{
src_short_to_float_array((short*)Buffer, FloatIn, NumSamples * NumChannels);
}
else if (BytesPerSample == 4)
{
src_int_to_float_array((int*)Buffer, FloatIn, NumSamples * NumChannels);
}
Data.data_in = FloatIn;
Data.data_out = FloatOut;
Data.input_frames = NumSamples;
Data.output_frames = NewSamples;
Data.src_ratio = (double)NewRate / (double)OldRate;
error = src_process(State, &Data);
if (error)
{
DPRINT1("src_process failed with %x\n", error);
KeRestoreFloatingPointState(&FloatSave);
ExFreePool(FloatIn);
ExFreePool(FloatOut);
ExFreePool(ResultOut);
return STATUS_UNSUCCESSFUL;
}
if (BytesPerSample == 1)
{
/* FIXME perform over/under clipping */
for(Index = 0; Index < Data.output_frames_gen * NumChannels; Index++)
ResultOut[Index] = (lrintf(FloatOut[Index]) >> 24);
}
else if (BytesPerSample == 2)
{
PUSHORT Res = (PUSHORT)ResultOut;
src_float_to_short_array(FloatOut, (short*)Res, Data.output_frames_gen * NumChannels);
}
else if (BytesPerSample == 4)
{
PULONG Res = (PULONG)ResultOut;
src_float_to_int_array(FloatOut, (int*)Res, Data.output_frames_gen * NumChannels);
}
*Result = ResultOut;
*ResultLength = Data.output_frames_gen * BytesPerSample * NumChannels;
ExFreePool(FloatIn);
ExFreePool(FloatOut);
src_delete(State);
KeRestoreFloatingPointState(&FloatSave);
return STATUS_SUCCESS;
}
NTSTATUS
PerformChannelConversion(
PUCHAR Buffer,
ULONG BufferLength,
ULONG OldChannels,
ULONG NewChannels,
ULONG BitsPerSample,
PVOID * Result,
PULONG ResultLength)
{
ULONG Samples;
ULONG NewIndex, OldIndex;
Samples = BufferLength / (BitsPerSample / 8) / OldChannels;
if (NewChannels > OldChannels)
{
if (BitsPerSample == 8)
{
PUCHAR BufferOut = ExAllocatePool(NonPagedPool, Samples * NewChannels);
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
{
ULONG SubIndex = 0;
RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(UCHAR));
do
{
/* 2 channel stretched to 4 looks like LRLR */
BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
}while(SubIndex++ < NewChannels - OldChannels);
}
*Result = BufferOut;
*ResultLength = Samples * NewChannels;
}
else if (BitsPerSample == 16)
{
PUSHORT BufferOut = ExAllocatePool(NonPagedPool, Samples * NewChannels);
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
{
ULONG SubIndex = 0;
RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(USHORT));
do
{
BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
}while(SubIndex++ < NewChannels - OldChannels);
}
*Result = BufferOut;
*ResultLength = Samples * NewChannels;
}
else if (BitsPerSample == 24)
{
PUCHAR BufferOut = ExAllocatePool(NonPagedPool, Samples * NewChannels);
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
{
ULONG SubIndex = 0;
RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * 3);
do
{
RtlMoveMemory(&BufferOut[(NewIndex+OldChannels + SubIndex) * 3], &Buffer[(OldIndex + (SubIndex % OldChannels)) * 3], 3);
}while(SubIndex++ < NewChannels - OldChannels);
}
*Result = BufferOut;
*ResultLength = Samples * NewChannels;
}
else if (BitsPerSample == 32)
{
PULONG BufferOut = ExAllocatePool(NonPagedPool, Samples * NewChannels);
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
{
ULONG SubIndex = 0;
RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(ULONG));
do
{
BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
}while(SubIndex++ < NewChannels - OldChannels);
}
*Result = BufferOut;
*ResultLength = Samples * NewChannels;
}
}
else
{
PUSHORT BufferOut = ExAllocatePool(NonPagedPool, Samples * NewChannels);
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
{
/* TODO
* mix stream instead of just dumping part of it ;)
*/
RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], NewChannels * (BitsPerSample/8));
}
*Result = BufferOut;
*ResultLength = Samples * NewChannels;
}
return STATUS_SUCCESS;
}
NTSTATUS
PerformQualityConversion(
PUCHAR Buffer,
ULONG BufferLength,
ULONG OldWidth,
ULONG NewWidth,
PVOID * Result,
PULONG ResultLength)
{
ULONG Samples;
ULONG Index;
ASSERT(OldWidth != NewWidth);
Samples = BufferLength / (OldWidth / 8);
//DPRINT("Samples %u BufferLength %u\n", Samples, BufferLength);
if (OldWidth == 8 && NewWidth == 16)
{
USHORT Sample;
PUSHORT BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(USHORT));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = Buffer[Index];
Sample *= 2;
#ifdef _X86_
Sample = _byteswap_ushort(Sample);
#endif
BufferOut[Index] = Sample;
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(USHORT);
}
else if (OldWidth == 8 && NewWidth == 32)
{
ULONG Sample;
PULONG BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(ULONG));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = Buffer[Index];
Sample *= 16777216;
#ifdef _X86_
Sample = _byteswap_ulong(Sample);
#endif
BufferOut[Index] = Sample;
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(ULONG);
}
else if (OldWidth == 16 && NewWidth == 32)
{
ULONG Sample;
PUSHORT BufferIn = (PUSHORT)Buffer;
PULONG BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(ULONG));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = BufferIn[Index];
Sample *= 65536;
#ifdef _X86_
Sample = _byteswap_ulong(Sample);
#endif
BufferOut[Index] = Sample;
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(ULONG);
}
else if (OldWidth == 16 && NewWidth == 8)
{
USHORT Sample;
PUSHORT BufferIn = (PUSHORT)Buffer;
PUCHAR BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(UCHAR));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = BufferIn[Index];
#ifdef _X86_
Sample = _byteswap_ushort(Sample);
#endif
Sample /= 256;
BufferOut[Index] = (Sample & 0xFF);
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(UCHAR);
}
else if (OldWidth == 32 && NewWidth == 8)
{
ULONG Sample;
PULONG BufferIn = (PULONG)Buffer;
PUCHAR BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(UCHAR));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = BufferIn[Index];
#ifdef _X86_
Sample = _byteswap_ulong(Sample);
#endif
Sample /= 16777216;
BufferOut[Index] = (Sample & 0xFF);
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(UCHAR);
}
else if (OldWidth == 32 && NewWidth == 16)
{
USHORT Sample;
PULONG BufferIn = (PULONG)Buffer;
PUSHORT BufferOut = ExAllocatePool(NonPagedPool, Samples * sizeof(USHORT));
if (!BufferOut)
return STATUS_INSUFFICIENT_RESOURCES;
for(Index = 0; Index < Samples; Index++)
{
Sample = BufferIn[Index];
#ifdef _X86_
Sample = _byteswap_ulong(Sample);
#endif
Sample /= 65536;
BufferOut[Index] = (Sample & 0xFFFF);
}
*Result = BufferOut;
*ResultLength = Samples * sizeof(USHORT);
}
else
{
DPRINT1("Not implemented conversion OldWidth %u NewWidth %u\n", OldWidth, NewWidth);
return STATUS_NOT_IMPLEMENTED;
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
Pin_fnDeviceIoControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
PIO_STACK_LOCATION IoStack;
PKSP_PIN Property;
//DPRINT1("Pin_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject);
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (IoStack->Parameters.DeviceIoControl.InputBufferLength == sizeof(KSP_PIN) && IoStack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(KSDATAFORMAT_WAVEFORMATEX))
{
Property = (PKSP_PIN)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
if (IsEqualGUIDAligned(&Property->Property.Set, &KSPROPSETID_Connection))
{
if (Property->Property.Id == KSPROPERTY_CONNECTION_DATAFORMAT && Property->Property.Flags == KSPROPERTY_TYPE_SET)
{
PKSDATAFORMAT_WAVEFORMATEX Formats;
PKSDATAFORMAT_WAVEFORMATEX WaveFormat;
Formats = (PKSDATAFORMAT_WAVEFORMATEX)IoStack->FileObject->FsContext2;
WaveFormat = (PKSDATAFORMAT_WAVEFORMATEX)Irp->UserBuffer;
ASSERT(Property->PinId == 0 || Property->PinId == 1);
ASSERT(Formats);
ASSERT(WaveFormat);
Formats[Property->PinId].WaveFormatEx.nChannels = WaveFormat->WaveFormatEx.nChannels;
Formats[Property->PinId].WaveFormatEx.wBitsPerSample = WaveFormat->WaveFormatEx.wBitsPerSample;
Formats[Property->PinId].WaveFormatEx.nSamplesPerSec = WaveFormat->WaveFormatEx.nSamplesPerSec;
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
}
}
DPRINT1("Size %u Expected %u\n",IoStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(KSDATAFORMAT_WAVEFORMATEX));
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
Pin_fnRead(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
Pin_fnWrite(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
Pin_fnFlush(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
Pin_fnClose(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
Pin_fnQuerySecurity(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
Pin_fnSetSecurity(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
UNIMPLEMENTED;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
BOOLEAN
NTAPI
Pin_fnFastDeviceIoControl(
PFILE_OBJECT FileObject,
BOOLEAN Wait,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
ULONG IoControlCode,
PIO_STATUS_BLOCK IoStatus,
PDEVICE_OBJECT DeviceObject)
{
UNIMPLEMENTED;
return FALSE;
}
BOOLEAN
NTAPI
Pin_fnFastRead(
PFILE_OBJECT FileObject,
PLARGE_INTEGER FileOffset,
ULONG Length,
BOOLEAN Wait,
ULONG LockKey,
PVOID Buffer,
PIO_STATUS_BLOCK IoStatus,
PDEVICE_OBJECT DeviceObject)
{
UNIMPLEMENTED;
return FALSE;
}
BOOLEAN
NTAPI
Pin_fnFastWrite(
PFILE_OBJECT FileObject,
PLARGE_INTEGER FileOffset,
ULONG Length,
BOOLEAN Wait,
ULONG LockKey,
PVOID Buffer,
PIO_STATUS_BLOCK IoStatus,
PDEVICE_OBJECT DeviceObject)
{
PKSSTREAM_HEADER StreamHeader;
PVOID BufferOut;
ULONG BufferLength;
NTSTATUS Status = STATUS_SUCCESS;
PKSDATAFORMAT_WAVEFORMATEX Formats;
PKSDATAFORMAT_WAVEFORMATEX InputFormat, OutputFormat;
DPRINT("Pin_fnFastWrite called DeviceObject %p Irp %p\n", DeviceObject);
Formats = (PKSDATAFORMAT_WAVEFORMATEX)FileObject->FsContext2;
InputFormat = Formats;
OutputFormat = (Formats + 1);
StreamHeader = (PKSSTREAM_HEADER)Buffer;
DPRINT("Num Channels %u Old Channels %u\n SampleRate %u Old SampleRate %u\n BitsPerSample %u Old BitsPerSample %u\n",
InputFormat->WaveFormatEx.nChannels, OutputFormat->WaveFormatEx.nChannels,
InputFormat->WaveFormatEx.nSamplesPerSec, OutputFormat->WaveFormatEx.nSamplesPerSec,
InputFormat->WaveFormatEx.wBitsPerSample, OutputFormat->WaveFormatEx.wBitsPerSample);
if (InputFormat->WaveFormatEx.wBitsPerSample != OutputFormat->WaveFormatEx.wBitsPerSample)
{
Status = PerformQualityConversion(StreamHeader->Data,
StreamHeader->DataUsed,
InputFormat->WaveFormatEx.wBitsPerSample,
OutputFormat->WaveFormatEx.wBitsPerSample,
&BufferOut,
&BufferLength);
if (NT_SUCCESS(Status))
{
ExFreePool(StreamHeader->Data);
StreamHeader->Data = BufferOut;
StreamHeader->DataUsed = BufferLength;
}
}
if (InputFormat->WaveFormatEx.nChannels != OutputFormat->WaveFormatEx.nChannels)
{
Status = PerformChannelConversion(StreamHeader->Data,
StreamHeader->DataUsed,
InputFormat->WaveFormatEx.nChannels,
OutputFormat->WaveFormatEx.nChannels,
OutputFormat->WaveFormatEx.wBitsPerSample,
&BufferOut,
&BufferLength);
if (NT_SUCCESS(Status))
{
ExFreePool(StreamHeader->Data);
StreamHeader->Data = BufferOut;
StreamHeader->DataUsed = BufferLength;
}
}
if (InputFormat->WaveFormatEx.nSamplesPerSec != OutputFormat->WaveFormatEx.nSamplesPerSec)
{
Status = PerformSampleRateConversion(StreamHeader->Data,
StreamHeader->DataUsed,
InputFormat->WaveFormatEx.nSamplesPerSec,
OutputFormat->WaveFormatEx.nSamplesPerSec,
OutputFormat->WaveFormatEx.wBitsPerSample / 8,
OutputFormat->WaveFormatEx.nChannels,
&BufferOut,
&BufferLength);
if (NT_SUCCESS(Status))
{
ExFreePool(StreamHeader->Data);
StreamHeader->Data = BufferOut;
StreamHeader->DataUsed = BufferLength;
}
}
IoStatus->Status = Status;
if (NT_SUCCESS(Status))
return TRUE;
else
return FALSE;
}
static KSDISPATCH_TABLE PinTable =
{
Pin_fnDeviceIoControl,
Pin_fnRead,
Pin_fnWrite,
Pin_fnFlush,
Pin_fnClose,
Pin_fnQuerySecurity,
Pin_fnSetSecurity,
Pin_fnFastDeviceIoControl,
Pin_fnFastRead,
Pin_fnFastWrite,
};
NTSTATUS
CreatePin(
IN PIRP Irp)
{
NTSTATUS Status;
KSOBJECT_HEADER ObjectHeader;
PKSDATAFORMAT DataFormat;
PIO_STACK_LOCATION IoStack;
DataFormat = ExAllocatePool(NonPagedPool, sizeof(KSDATAFORMAT_WAVEFORMATEX) * 2);
if (!DataFormat)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(DataFormat, sizeof(KSDATAFORMAT_WAVEFORMATEX) * 2);
IoStack = IoGetCurrentIrpStackLocation(Irp);
IoStack->FileObject->FsContext2 = (PVOID)DataFormat;
/* allocate object header */
Status = KsAllocateObjectHeader(&ObjectHeader, 0, NULL, Irp, &PinTable);
return Status;
}
void * calloc(size_t Elements, size_t ElementSize)
{
ULONG Index;
PUCHAR Block = ExAllocatePool(NonPagedPool, Elements * ElementSize);
if (!Block)
return NULL;
for(Index = 0; Index < Elements * ElementSize; Index++)
Block[Index] = 0;
return Block;
}
void free(PVOID Block)
{
ExFreePool(Block);
}