code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    node.cpp
//
// Abstract:
//      This is the implementation file for the C++ class that exposes
//      functionality of KS nodes
//

#include "kssample.h"

// ============================================================================
//
//   CKsNode
//
//=============================================================================

////////////////////////////////////////////////////////////////////////////////
//
//  CKsNode::CKsNode()
//
//  Routine Description:
//      Copy constructor for CKsNode
//
//  Arguments:
//      A Node to copy from,
//      An HRESULT to indicate success or failure
//
//
//  Return Value:
//
//     S_OK on success
//

CKsNode::CKsNode
(
    CKsNode*      pksnCopy,
    HRESULT*        phr
) : m_nId(0),
    m_ulCpuResources(KSAUDIO_CPU_RESOURCES_UNINITIALIZED),
    m_guidType(GUID_NULL)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    if (pksnCopy)
    {
        m_nId = pksnCopy->m_nId;
        m_ulCpuResources = pksnCopy->m_ulCpuResources;
        m_guidType = pksnCopy->m_guidType;
    }

    TRACE_LEAVE_HRESULT(hr);
    *phr = hr;
    return;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsNode::CKsNode()
//
//  Routine Description:
//      Constructor for CKsNode
//
//  Arguments:
//      A node ID,
//      A type,
//      An HRESULT to indicate success or failure
//
//
//  Return Value:
//
//     S_OK on success
//

CKsNode::CKsNode
(
    ULONG           nID,
    REFGUID         guidType,
    HRESULT*        phr
) : m_nId(nID),
    m_ulCpuResources(KSAUDIO_CPU_RESOURCES_UNINITIALIZED),
    m_guidType(guidType)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    TRACE_LEAVE_HRESULT(hr);
    *phr = hr;
    return;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsNode::GetType()
//
//  Routine Description:
//      Returns the node type
//
//  Arguments:
//     None
//
//
//  Return Value:
//
//     The GUID representint the node type
//

GUID CKsNode::GetType()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return m_guidType;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsNode::GetId()
//
//  Routine Description:
//      Returns the node ID
//
//  Arguments:
//     None
//
//
//  Return Value:
//
//     The Node ID
//

ULONG CKsNode::GetId()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return m_nId;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsNode::GetCpuResources()
//
//  Routine Description:
//      Returns the CPU Resources
//
//  Arguments:
//     None
//
//
//  Return Value:
//
//     The CPU Resources
//

ULONG CKsNode::GetCpuResources()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return m_ulCpuResources;
}


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    node.h
//
// Abstract:
//      This is the header file for the C++ class that exposes
//      functionality of KS nodes
//

#pragma once
#ifndef __KSNODE_H
#define __KSNODE_H

// Reserved node identifiers
#define NODE_UNINITIALIZED  0xFFFFFFFF
#define NODE_WILDCARD       0xFFFFFFFE
#define KSAUDIO_CPU_RESOURCES_UNINITIALIZED 'ENON'

////////////////////////////////////////////////////////////////////////////////
//
//  class CKsNode
//
//  Class Description:
//      This is the base class for classes that proxy Ks filters from user mode.
//      This class wraps an irptarget and associated node id.  It simplifies property
//      calls on nodes.
//
//
//

class CKsNode
{
public:
    CKsNode(ULONG nID, REFGUID guidType, HRESULT *phr);
    CKsNode(CKsNode* pksnCopy, HRESULT *phr);
    GUID GetType();
    ULONG GetId();
    ULONG GetCpuResources();

protected:

private:
    ULONG           m_nId;
    ULONG           m_ulCpuResources;

    GUID            m_guidType;
};

typedef CKsNode *               PCKsNode;

#endif //__KSNODE_H

repo_name: ninjam/libninjam

license: mit


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    pin.cpp
//
// Abstract:
//      This is the implementation file for C++ classes that expose
//      functionality of KS pin objects
//

#include "kssample.h"

LPCSTR gpstrKsPinType[] = { "render", "capture" };
LPCSTR gpstrKsState[] = { "KSSTATE_STOP", "KSSTATE_ACQUIRE", "KSSTATE_PAUSE", "KSSTATE_RUN" };

typedef
KSDDKAPI
DWORD
WINAPI
KSCREATEPIN(
    IN HANDLE FilterHandle,
    IN PKSPIN_CONNECT Connect,
    IN ACCESS_MASK DesiredAccess,
    OUT PHANDLE ConnectionHandle
    ) ;

class CKSUSER
{
public:
    HMODULE         hmodKsuser;
    KSCREATEPIN*    DllKsCreatePin;

    CKSUSER(void)
    {
        hmodKsuser = NULL;
        DllKsCreatePin = NULL;
    }

    DWORD KsCreatePin
    (
        IN HANDLE           FilterHandle,
        IN PKSPIN_CONNECT   Connect,
        IN ACCESS_MASK      DesiredAccess,
        OUT PHANDLE         ConnectionHandle
    )
    {
        if (NULL == DllKsCreatePin)
        {
            if (NULL == hmodKsuser)
            {
                hmodKsuser = LoadLibrary(TEXT("ksuser.dll"));
                if (NULL == hmodKsuser) {
                    return ERROR_FILE_NOT_FOUND;
                }
            }

            DllKsCreatePin = (KSCREATEPIN*)GetProcAddress(hmodKsuser, "KsCreatePin");
            if (NULL == DllKsCreatePin) {
                return ERROR_FILE_NOT_FOUND;
            }
        }

        return DllKsCreatePin(FilterHandle,
                            Connect,
                            DesiredAccess,
                            ConnectionHandle);
    }
};

CKSUSER  KSUSER;

// ============================================================================
//
//   CKsPin
//
//=============================================================================

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::CKsPin()
//
//  Routine Description:
//      Copy constructor for CKsPin
//
//  Arguments:
//      A parent filter,
//      A Pin to copy from,
//      An HRESULT to indicate success or failure
//
//
//  Return Value:
//
//     S_OK on success
//

CKsPin::CKsPin(
    IN  CKsFilter*    pFilter,
    IN  ULONG           PinId,
    OUT HRESULT*        phr)
    : CKsIrpTarget(INVALID_HANDLE_VALUE),
    m_pFilter(pFilter),
    m_nId(PinId),
    m_dwAlignment(0),
    m_pLinkPin(NULL),
    m_fCompleteOnlyOnRunState(TRUE),
    m_fLooped(FALSE),
    m_eType(eUnknown),
    m_ksState(KSSTATE_STOP),
    m_pksPinCreate(NULL),
    m_cbPinCreateSize(0),
    m_guidCategory(GUID_NULL)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    // non-atomic initializations
    ZeroMemory(&m_Descriptor, sizeof m_Descriptor);
    ZeroMemory(m_szFriendlyName, sizeof m_szFriendlyName);

    //Initialize
    if (SUCCEEDED(hr))
    {
        hr = CKsPin::Init();
    }

     TRACE_LEAVE_HRESULT(hr);
    *phr = hr;
    return;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::~CKsPin()
//
//  Routine Description:
//      Destructor
//
//  Arguments:
//      None
//
//
//  Return Value:
//      None
//

CKsPin::~CKsPin()
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    ClosePin();

    // Need to cast to BYTE *, because that's how CKsAudRenPin::CKsAudRenPin
    // and CKsAudRenPin::SetFormat allocate the memory
    delete[] (BYTE *)m_pksPinCreate;

    // Need to cast to BYTE *, because that's how CKsFilter::GetPinPropertyMulti allocates the memory
    delete[] (BYTE *)m_Descriptor.pmiDataRanges;
    delete[] (BYTE *)m_Descriptor.pmiInterfaces;
    delete[] (BYTE *)m_Descriptor.pmiMediums;

    TRACE_LEAVE();
    return;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::ClosePin()
//
//  Routine Description:
//      Closes the pin
//
//  Arguments:
//      None
//
//
//  Return Value:
//
//     S_OK on success
//

void CKsPin::ClosePin(void)
{
    TRACE_ENTER();

    if (IsValidHandle(m_handle))
    {
        SetState(KSSTATE_STOP);

        SafeCloseHandle(m_handle);
    }

    TRACE_LEAVE();
    return;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::SetState()
//
//  Routine Description:
//      SetState
//
//  Arguments:
//      SetState
//
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::SetState(
    IN  KSSTATE ksState)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    hr = SetPropertySimple(
            KSPROPSETID_Connection,
            KSPROPERTY_CONNECTION_STATE,
            &ksState,
            sizeof (ksState));

    if (SUCCEEDED(hr))
    {
        m_ksState = ksState;
    }

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_ERROR,TEXT("Failed to Set Pin State"));
    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::Instantiate()
//
//  Routine Description:
//      Instantiate
//
//  Arguments:
//      fLooped
//
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::Instantiate(
    IN BOOL fLooped)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;
    DWORD dwResult = 0;

    if (!m_pksPinCreate)
        return FALSE;

    if (m_pLinkPin)
    {
        assert( KSPIN_COMMUNICATION_SINK == m_pLinkPin->m_Descriptor.Communication ||
            KSPIN_COMMUNICATION_BOTH == m_pLinkPin->m_Descriptor.Communication);

        m_pksPinCreate->PinToHandle = m_pLinkPin->m_handle;
    }

    m_fLooped = fLooped;
    m_pksPinCreate->Interface.Id =
        m_fLooped ? KSINTERFACE_STANDARD_LOOPED_STREAMING : KSINTERFACE_STANDARD_STREAMING;

    dwResult = KSUSER.KsCreatePin(
        m_pFilter->m_handle,
        m_pksPinCreate,
        GENERIC_WRITE | GENERIC_READ,
        &m_handle
        );

    if (ERROR_SUCCESS != dwResult)
    {
         hr = HRESULT_FROM_WIN32(dwResult);
         if (SUCCEEDED(hr))
         {
                // Sometimes the error codes don't map to error HRESULTs.
                hr = E_FAIL;
         }
    }

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_ERROR,TEXT("Failed to instantiate pin. hr=0x%08x"), hr);
    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::GetState()
//
//  Routine Description:
//      Gets the KSSTATE of Pin
//
//  Arguments:
//      PKSSTATE pksState -- State is returned here
//
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::GetState(
    KSSTATE* pksState)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    if (NULL == pksState)
    {
        hr = E_INVALIDARG;
        DebugPrintf(TRACE_ERROR,TEXT("pksState == NULL"));
    }
    else if (!IsValidHandle(m_handle))
    {
        hr = E_FAIL;
        DebugPrintf(TRACE_ERROR,TEXT("No valid pin handle"));
    }

    if (SUCCEEDED(hr))
    {
        hr = GetPropertySimple(
            KSPROPSETID_Connection,
            KSPROPERTY_CONNECTION_STATE,
            pksState,
            sizeof(pksState));
    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::Reset()
//
//  Routine Description:
//      Reset the pin
//
//  Arguments:
//      None
//
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::Reset(void)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;
    ULONG   ulIn=0;
    ULONG   ulBytesReturned = 0;

    ulIn = KSRESET_BEGIN;

    hr = SyncIoctl(
        m_handle,
        IOCTL_KS_RESET_STATE,
        &ulIn,
        sizeof(ULONG),
        NULL,
        0,
        &ulBytesReturned);

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_ERROR,TEXT("IOCTL_KS_RESET_STATE failed"));
    }

    ulIn = KSRESET_END;
    hr = SyncIoctl(
        m_handle,
        IOCTL_KS_RESET_STATE,
        &ulIn,
        sizeof(ULONG),
        NULL,
        0,
        &ulBytesReturned);

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_ERROR,TEXT("IOCTL_KS_RESET_STATE failed"));
    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::WriteData()
//
//  Routine Description:
//      Submit some data to the pin, using the provided KSSTREAM_HEADER and OVERLAPPED structures.
//      If the caller submits a valid event in the KSSTREAM_HEADER, the event must be unsignaled.
//
//  Arguments:
//      Pointer to the KSSTREAM_HEADER structure describing the data to send to the pin
//      Pointer to the OVERLAPPED structure to use when doing the asynchronous I/O
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::WriteData(
    KSSTREAM_HEADER *pKSSTREAM_HEADER,
    OVERLAPPED *pOVERLAPPED)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;
    DWORD dwError = 0;
    DWORD cbReturned = 0;
    BOOL fRes = TRUE;

    if (SUCCEEDED(hr))
    {
        // submit the data
        fRes = DeviceIoControl(
            m_handle,
            IOCTL_KS_WRITE_STREAM,
            NULL,
            0,
            pKSSTREAM_HEADER,
            pKSSTREAM_HEADER->Size,
            &cbReturned,
            pOVERLAPPED);

        // we're paused, we should return false!
        if (fRes)
        {
            //DebugPrintf(TRACE_HIGH,TEXT("DeviceIoControl returned TRUE even though the pin is paused"));
        }
        else
        {
            // if it did return FALSE then GetLAstError should return ERROR_IO_PENDING
            dwError = GetLastError();

            if (ERROR_IO_PENDING == dwError)
            {
                //Life is good
                hr = S_OK;
            }
            else
            {
                hr = E_FAIL;
                DebugPrintf(TRACE_ERROR,TEXT("CKsPin::WriteData's DeviceIoControl Failed!  Error=0x%#08x"),dwError);
            }
        }

    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::ReadData()
//
//  Routine Description:
//      Submit some memory for the pin to read into, using the provided KSSTREAM_HEADER and OVERLAPPED structures.
//      If the caller submits a valid event in the KSSTREAM_HEADER, the event must be unsignaled.
//
//  Arguments:
//      Pointer to the KSSTREAM_HEADER structure describing where to store the data read from the pin
//      Pointer to the OVERLAPPED structure to use when doing the asynchronous I/O
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::ReadData(
    KSSTREAM_HEADER *pKSSTREAM_HEADER,
    OVERLAPPED *pOVERLAPPED)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    DWORD dwError = 0;
    DWORD cbReturned = 0;
    BOOL fRes = TRUE;

    fRes = DeviceIoControl(
        m_handle,
        IOCTL_KS_READ_STREAM,
        NULL,
        0,
        pKSSTREAM_HEADER,
        pKSSTREAM_HEADER->Size,
        &cbReturned,
        pOVERLAPPED);

    // we're paused, we should return false!
    if (fRes)
    {
        //DebugPrintf(TRACE_HIGH,TEXT("DeviceIoControl returne TRUE even though the pin is paused"));
    }
    else
    {
        // if it did return FALSE then GetLAstError should return ERROR_IO_PENDING
        dwError = GetLastError();

        if (ERROR_IO_PENDING == dwError)
        {
            //Life is good
            hr = S_OK;
        }
        else
        {
            hr = E_FAIL;
            DebugPrintf(TRACE_ERROR,TEXT("DeviceIoControl Failed!  Error=0x%08x"),dwError);
        }

    }

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::Init()
//
//  Routine Description:
//      Does some basic data structure initialization
//
//  Arguments:
//     None
//
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::Init()
{
    TRACE_ENTER();
    HRESULT hr = S_OK;
    BOOL fFailure = FALSE;

    // Get COMMUNICATION
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_COMMUNICATION,
        &m_Descriptor.Communication,
        sizeof(KSPIN_COMMUNICATION));

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_COMMUNICATION on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

   //get PKSPIN_INTERFACES
    hr = m_pFilter->GetPinPropertyMulti(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_INTERFACES,
        &m_Descriptor.pmiInterfaces);

    if (SUCCEEDED(hr))
    {
        m_Descriptor.cInterfaces = m_Descriptor.pmiInterfaces->Count;
        m_Descriptor.pInterfaces = (PKSPIN_INTERFACE)(m_Descriptor.pmiInterfaces+1);
    }
    else
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPETY_PIN_INTERFACES on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // get PKSPIN_MEDIUMS
    hr = m_pFilter->GetPinPropertyMulti(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_MEDIUMS,
        &m_Descriptor.pmiMediums);

    if (SUCCEEDED(hr))
    {
        m_Descriptor.cMediums = m_Descriptor.pmiMediums->Count;
        m_Descriptor.pMediums = (PKSPIN_MEDIUM)(m_Descriptor.pmiMediums +1);
    }
    else
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_MEDIUMS on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // get PKSPIN_DATARANGES
    hr = m_pFilter->GetPinPropertyMulti(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_DATARANGES,
        &m_Descriptor.pmiDataRanges);

    if (SUCCEEDED(hr))
    {
        m_Descriptor.cDataRanges = m_Descriptor.pmiDataRanges->Count;
        m_Descriptor.pDataRanges = (PKSDATARANGE)(m_Descriptor.pmiDataRanges +1);
    }
    else
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_DATARANGES on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    //get dataflow information
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_DATAFLOW,
        &m_Descriptor.DataFlow,
        sizeof(KSPIN_DATAFLOW));

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_DATAFLOW on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    //get instance information
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_CINSTANCES,
        &m_Descriptor.CInstances,
        sizeof(KSPIN_CINSTANCES));

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_CINSTANCES on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // Get Global instance information
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_GLOBALCINSTANCES,
        &m_Descriptor.CInstancesGlobal,
        sizeof(KSPIN_CINSTANCES));

    if (FAILED(hr))
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_GLOBALCINSTANCES on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // Get Pin Category Information
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_CATEGORY,
        &m_guidCategory,
        sizeof(m_guidCategory));
    if (FAILED(hr))
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_CATEGORY on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // Get Pin Name
    hr = m_pFilter->GetPinPropertySimple(
        m_nId,
        KSPROPSETID_Pin,
        KSPROPERTY_PIN_NAME,
        &m_szFriendlyName,
        sizeof(m_szFriendlyName)-1);

    if (SUCCEEDED(hr))
    {
        //DebugPrintf(TRACE_LOW,TEXT("KSPROPERTY_PIN_NAME: %ls"),m_szFriendlyName);
    }
    else
    {
        DebugPrintf(TRACE_HIGH,TEXT("Failed to retrieve pin property KSPROPERTY_PIN_NAME on PinID: %d"),m_nId);
        fFailure = TRUE;
    }

    // if we experienced any failures, return S_FALSE, otherwise S_OK
    hr = fFailure ? S_FALSE : S_OK;

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::GetId()
//
//  Routine Description:
//      Returns the Pin ID
//
//  Arguments:
//      None
//
//
//  Return Value:
//
//     The Pin ID
//

ULONG CKsPin::GetId()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return m_nId;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::GetType()
//
//  Routine Description:
//      Returns the Pin type
//
//  Arguments:
//      None
//
//
//  Return Value:
//
//     The Pin type
//

ETechnology CKsPin::GetType()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return m_eType;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::SetPinConnect()
//
//  Routine Description:
//      Copies the pin connect structure
//
//  Arguments:
//      The structure and its size
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::SetPinConnect(PKSPIN_CONNECT pksPinConnect, ULONG cbKsPinConnect)
{
    HRESULT hr = S_OK;
    TRACE_ENTER();

    if ((NULL == pksPinConnect) || (cbKsPinConnect < sizeof (KSPIN_CONNECT)))
    {
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr))
    {
        // Free the existing Pin Connect structure
        if (m_pksPinCreate)
        {
            delete [] m_pksPinCreate;
            m_pksPinCreate = NULL;
        }

        // Allocate memory for the new pin connect structure
        m_pksPinCreate = reinterpret_cast<PKSPIN_CONNECT>(new BYTE[cbKsPinConnect]);
        if (NULL == m_pksPinCreate)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        // Copy over the KSPIN_CONNECT structure
        CopyMemory(m_pksPinCreate, pksPinConnect, cbKsPinConnect);
    }

    TRACE_LEAVE();
    return hr;
}

////////////////////////////////////////////////////////////////////////////////
//
//  CKsPin::GetPinDescriptor()
//
//  Routine Description:
//      Returns the Pin ID
//
//  Arguments:
//      None
//
//
//  Return Value:
//
//     The Pin ID
//

PPIN_DESCRIPTOR CKsPin::GetPinDescriptor()
{
    TRACE_ENTER();
    TRACE_LEAVE();
    return &m_Descriptor;
}

////////////////////////////////////////////////////////////////////////////////
//

//  CKsPin::SetFormat()
//
//  Routine Description:
//      Sets the format on the pin
//
//  Arguments:
//      pFormat  The data format to use
//
//  Return Value:
//
//     S_OK on success
//

HRESULT CKsPin::SetFormat(KSDATAFORMAT* pFormat)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    hr = SetPropertySimple(
            KSPROPSETID_Connection,
            KSPROPERTY_CONNECTION_DATAFORMAT,
            pFormat,
            pFormat->FormatSize,
            NULL,
            0);

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

HRESULT CKsPin::GetDataFlow(KSPIN_DATAFLOW* pDataFlow)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    assert(NULL != pDataFlow);

    *pDataFlow = m_Descriptor.DataFlow;

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

HRESULT CKsPin::GetCommunication(KSPIN_COMMUNICATION* pCommunication)
{
    TRACE_ENTER();
    HRESULT hr = S_OK;

    assert(NULL != pCommunication);

    *pCommunication = m_Descriptor.Communication;

    TRACE_LEAVE_HRESULT(hr);
    return hr;
}

BOOL CKsPin::HasDataRangeWithSpecifier(REFGUID guidFormatSpecifier)
{
    TRACE_ENTER();
    BOOL fFound = FALSE;
    KSDATARANGE* pdr;

    // Loop Through the DataRanges
    pdr = m_Descriptor.pDataRanges;
    for(ULONG i = 0; i < m_Descriptor.cDataRanges; i++)
    {
        // Check for a matching Specifier
        if (pdr->Specifier == guidFormatSpecifier)
        {
            // Found our matching Pin!
            fFound = TRUE;
            break;
        }

        // Advance to the next datarange.
        pdr = reinterpret_cast<KSDATARANGE*>(  reinterpret_cast<BYTE*>(pdr) + pdr->FormatSize );
    }

    TRACE_LEAVE();
    return fFound;
}


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    pin.h
//
// Abstract:
//      This is the header file for C++ classes that expose
//      functionality of KS pins
//

#pragma once
#ifndef __KSPIN_H
#define __KSPIN_H

typedef struct
{
    ULONG                       cInterfaces;
    PKSIDENTIFIER               pInterfaces;
    ULONG                       cMediums;
    PKSIDENTIFIER               pMediums;
    ULONG                       cDataRanges;
    PKSDATARANGE                pDataRanges;
    ULONG                       cConstrDataRanges;
    PKSDATARANGE                pConstrDataRanges;
    KSPIN_DATAFLOW              DataFlow;
    KSPIN_COMMUNICATION         Communication;
    KSPIN_CINSTANCES            CInstances;
    KSPIN_CINSTANCES            CInstancesGlobal;
    KSPIN_CINSTANCES            CInstancesNecessary;
    PKSPIN_PHYSICALCONNECTION   PhysicalConnection;
    GUID                        Category;
    PWCHAR                      Name;

    // these are redundant.  Free them when you are done with pInterfaces, pMediums, pDataRanges
    PKSMULTIPLE_ITEM            pmiDataRanges;
    PKSMULTIPLE_ITEM            pmiMediums;
    PKSMULTIPLE_ITEM            pmiInterfaces;

}
PIN_DESCRIPTOR, *PPIN_DESCRIPTOR;

////////////////////////////////////////////////////////////////////////////////
//
//  class CKsPin
//
//  Class Description:
//      This is the base class for classes that proxy Ks filters from user mode.
//      The pin class is still virtual and must be overidden to be useful.
//      specifically, the mpksPinCreate, m_eType need to be propperly
//      initialized in the derived class.
//
//
//

class CKsPin : public CKsIrpTarget
{
public:

    CKsPin(
        IN  CKsFilter* pFilter,
        IN  ULONG nId,
        OUT HRESULT* phr);

    virtual ~CKsPin(void);

    virtual HRESULT Instantiate(
        IN  BOOL fLooped = FALSE);

    void ClosePin(void);

    HRESULT SetFormat(KSDATAFORMAT* pFormat);

    HRESULT GetState(
        OUT PKSSTATE pksState);

    virtual HRESULT SetState(
        IN KSSTATE ksState);

    HRESULT Reset(void);

    HRESULT WriteData(
        IN  KSSTREAM_HEADER *pKSSTREAM_HEADER,
        IN  OVERLAPPED *pOVERLAPPED);

    HRESULT ReadData(
        IN  KSSTREAM_HEADER *pKSSTREAM_HEADER,
        IN  OVERLAPPED *pOVERLAPPED);

    ULONG GetId();
    ETechnology GetType(void);

    PPIN_DESCRIPTOR GetPinDescriptor();

    HRESULT SetPinConnect(PKSPIN_CONNECT pksPinConnect, ULONG cbKsPinConnect);

    HRESULT GetDataFlow(KSPIN_DATAFLOW* pDataFlow);
    HRESULT GetCommunication(KSPIN_COMMUNICATION* pCommunication);
    BOOL HasDataRangeWithSpecifier(REFGUID guidFormatSpecifier);

protected:
    DWORD           m_dwAlignment;
    PKSPIN_CONNECT  m_pksPinCreate;     // creation parameters of pin
    DWORD           m_cbPinCreateSize;  // size of the creation parameters
    ULONG           m_nId;
    CKsFilter*      m_pFilter;
    GUID            m_guidCategory;
    PIN_DESCRIPTOR  m_Descriptor;       // description of Pin
    ETechnology     m_eType;

private:
    HRESULT Init(void);

    // Private Data Members

    KSSTATE         m_ksState;          // state of pin
    TCHAR           m_szFriendlyName[MAX_PATH]; // friendly name of the pin
    BOOL            m_fLooped;
    CKsPin*         m_pLinkPin;         // optional pin to connect to
    BOOL            m_fCompleteOnlyOnRunState;

};

#endif //__KSPIN_H


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    tlist.h
//
// Abstract:
//      This is the header and implementation file for a template list class
//

typedef void* LISTPOS;

template <class T>
class TItem
{
public:
    TItem();
    ~TItem();
    TItem<T> * pNext;
    TItem<T> * pPrev;
    T* pData;
};

template <class T>
TItem<T>::TItem() :
   pNext(NULL),
   pPrev(NULL),
   pData(NULL)
{
    return;
}

template <class T>
TItem<T>::~TItem()
{
    return;
}

template <class T>
class TList
{
 public:
    TList();
    ~TList();
    BOOL GetHead( T** ppHead ) const;
    LISTPOS GetHeadPosition() const;
    LISTPOS AddHead(T* pNewHead);
    LISTPOS AddTail(T* pNewTail);
    BOOL RemoveTail( T** ppOldTail);
    BOOL RemoveHead(T** ppOldHead);
    void RemoveAll();
    LISTPOS Find( T* pSearchValue ) const;
    void RemoveAt(LISTPOS pos);
    BOOL GetNext(LISTPOS& rPos, T**ppNext) const;
    BOOL IsEmpty() const;
    DWORD GetCount() const;
    HRESULT Initialize(DWORD dwValue);
    BOOL GetAt( LISTPOS pos, T** ppElement) const;
    LISTPOS InsertBefore(LISTPOS pos, T* pNewElement);
private:
    TItem<T>* m_pHead;
    TItem<T>* m_pTail;
    LONG m_lCount;
};

template <class T>
TList<T>::TList() :
    m_pHead(NULL),
    m_pTail(NULL),
    m_lCount(0)
{
    return;
}

template <class T>
TList<T>::~TList()
{
    assert(NULL == m_pHead);
    assert(NULL == m_pTail);
    assert(0 == m_lCount);
    return;
}

template <class T>
BOOL TList<T>::GetHead(T** ppHead) const
{
    if (!m_pHead)
    {
        *ppHead = NULL;
        return FALSE;
    }

    *ppHead = m_pHead->pData;
    return TRUE;
}

template <class T>
LISTPOS TList<T>::GetHeadPosition() const
{
    return reinterpret_cast<LISTPOS>(m_pHead);
}

template <class T>
LISTPOS TList<T>::AddTail(T* pNewTail)
{
    LISTPOS pos = NULL;
    TItem<T>* pNewItem;

    pNewItem = new TItem<T>;
    if(NULL == pNewItem)
    {
        goto DONE;
    }

    pNewItem->pData = pNewTail;
    pNewItem->pPrev = m_pTail;

    if (NULL == m_pTail)
    {
        m_pHead = pNewItem;
    }
    else
    {
        m_pTail->pNext = pNewItem;
    }

    m_pTail = pNewItem;

    m_lCount++;

    DONE:

    return reinterpret_cast<LISTPOS>(pNewItem);
}

template <class T>
LISTPOS TList<T>::AddHead(T* pNewHead)
{
    TItem<T> *pNewItem;
    pNewItem = new TItem<T>;
    if( !pNewItem )
    {
        return( reinterpret_cast<LISTPOS>( NULL ) );
    }

    pNewItem->pData = pNewHead;
    pNewItem->pNext = m_pHead;
    pNewItem->pPrev = NULL;

    if( NULL != m_pHead )
    {
        m_pHead->pPrev = pNewItem;
    }
    else
    {
        m_pTail = pNewItem;
    }

    m_pHead = pNewItem;
    m_lCount++;

    return( reinterpret_cast<LISTPOS>( pNewItem) );

}

template <class T>
BOOL TList<T>::RemoveTail( T** ppOldTail)
{

    if (!ppOldTail || !m_pTail)
    {
        return FALSE;
    }

    TItem<T> *pOldItem = m_pTail;
    *ppOldTail = pOldItem->pData;

    m_pTail = pOldItem->pPrev;

    if( NULL != m_pTail )
    {
        m_pTail->pNext = NULL;
    }
    else
    {
        m_pHead = NULL;
    }

    delete pOldItem;
    m_lCount--;
    assert(m_lCount >= 0);

    return( TRUE );
}

template <class T>
BOOL TList<T>::RemoveHead(T** ppOldHead)
{
   if( !ppOldHead || !m_pHead )
    {
        return( FALSE );
    }

    TItem<T> *pOldItem = m_pHead;
    *ppOldHead = pOldItem->pData;

    m_pHead = pOldItem->pNext;

    if( NULL != m_pHead )
    {
        m_pHead->pPrev = NULL;
    }
    else
    {
        m_pTail = NULL;
    }

    delete pOldItem;
    m_lCount--;
    assert(m_lCount >= 0);

    return( TRUE );
}

template <class T>
void TList<T>::RemoveAll()
{
    TItem<T> *pNext;

    while( NULL != m_pHead )
    {
        pNext = m_pHead->pNext;
        delete m_pHead;
        m_pHead = pNext;
    }

    m_lCount = 0;
    m_pTail = NULL;

    return;
}

template <class T>
LISTPOS TList<T>::Find( T* pSearchValue ) const
{
    TItem<T> *pItem = m_pHead;

    for ( ; NULL != pItem; pItem = pItem->pNext )
    {
        if ( pItem->pData == pSearchValue )
        {
            return( reinterpret_cast<LISTPOS>(pItem) );
        }
    }

    return( reinterpret_cast<LISTPOS>(NULL) );
}

template <class T>
void TList<T>::RemoveAt(LISTPOS pos)
{
    // ASSERT( NULL != pos )
    TItem<T> *pOldItem = reinterpret_cast< TItem<T>* >(pos);

    // remove pOldItem from list
    if( pOldItem == m_pHead )
    {
        m_pHead = pOldItem->pNext;
    }
    else
    {
        assert( pOldItem->pPrev );
        pOldItem->pPrev->pNext = pOldItem->pNext;
    }

    if (pOldItem == m_pTail)
    {
        m_pTail = pOldItem->pPrev;
    }
    else
    {
        assert( pOldItem->pNext );
        pOldItem->pNext->pPrev = pOldItem->pPrev;
    }

    delete pOldItem;
    m_lCount--;
    assert(m_lCount >= 0);
    return;
}

template <class T>
BOOL TList<T>::GetNext(LISTPOS& rPos, T**ppNext) const
{
    TItem<T> *pItem = reinterpret_cast< TItem<T>* >(rPos);

    if( !pItem )
    {
        return( FALSE );
    }

    // NOTE: you may encounter an access violation here while running debug build
    //       if sound card is already in use - release build throws no exception
    rPos = reinterpret_cast<LISTPOS>(pItem->pNext);
    *ppNext = pItem->pData;

    return( TRUE );
}

template <class T>
BOOL TList<T>::IsEmpty() const
{
    return (0 == m_lCount);
}

template <class T>
HRESULT TList<T>::Initialize(DWORD dwValue)
{
    HRESULT hr = S_OK;
    return hr;
}

template <class T>
DWORD TList<T>::GetCount() const
{
    return static_cast<DWORD>(m_lCount);
}

template <class T>
 LISTPOS TList<T>::InsertBefore(LISTPOS pos, T* pNewElement)
{
    if( ( NULL == pos ) || ( ( reinterpret_cast<TItem<T>*>(pos) )->pPrev == NULL ) )
    {
        return( AddHead( pNewElement ) ); // insert before nothing -> head of the list
    }

    // Insert it before pos
    TItem<T> *pOldItem = reinterpret_cast<TItem<T>*>(pos);

    TItem<T> *pNewItem;

    pNewItem = new TItem<T>;
    if( !pNewItem )
    {
        return( reinterpret_cast<LISTPOS>(NULL) );
    }

    pNewItem->pData = pNewElement;
    pNewItem->pPrev = pOldItem->pPrev;
    pNewItem->pNext = pOldItem;

    pOldItem->pPrev->pNext = pNewItem;
    pOldItem->pPrev = pNewItem;

    m_lCount++;

    return( reinterpret_cast<LISTPOS>(pNewItem) );
}

template <class T>
BOOL TList<T>::GetAt( LISTPOS pos, T** ppElement) const
{
    TItem<T> *pItem = reinterpret_cast<TItem<T>*>(pos);

    if( !pItem )
    {
        return( FALSE );
    }

    *ppElement = pItem->pData;

    return( TRUE );
}


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    util.cpp
//
// Abstract:
//      This is the implementation file for general helper functions
//

#include "kssample.h"
#include <mmsystem.h>
#include <math.h>
#include <float.h>

////////////////////////////////////////////////////////////////////////////////
//
//  GetWfxSize()
//
//  Routine Description:
//      Returns the size of the wave format
//
//  Arguments:
//      Pointer to the wave format
//
//  Return Value:
//
//     Size of the wave format
//
DWORD GetWfxSize(const WAVEFORMATEX* pwfxSrc)
{
    assert(pwfxSrc);

    DWORD dwSize;
    if(WAVE_FORMAT_PCM == pwfxSrc->wFormatTag)
    {
        dwSize = sizeof(WAVEFORMATEX);
    }
    else
    {
        dwSize = sizeof(WAVEFORMATEX) + pwfxSrc->cbSize;
    }

    return dwSize;
}

////////////////////////////////////////////////////////////////////////////////
//
//  DebugPrintf()
//
//  Routine Description:
//      printf style function for logging to the debugger
//
//  Arguments:
//      debug level
//      printf style format string
//
//  Return Value:
//
//     None
//
LONG g_lDebugLevel = TRACE_MUTED;

void DebugPrintf(LONG lDebugLevel, LPCTSTR pszFormat, ... )
{
    TCHAR szOutput[MAX_PATH];

    if (lDebugLevel <= g_lDebugLevel)
    {
        va_list argList;
        va_start(argList, pszFormat);
        _vsntprintf(szOutput, MAX_PATH, pszFormat, argList);
        va_end(argList);
        OutputDebugString(szOutput);
    }
}

#define _8BIT_SILENCE       0x80
#define _8BIT_AMPLITUDE     0x7f

#define _16BIT_SILENCE      0
#define _16BIT_AMPLITUDE    0x7FFF

#define _20BIT_SILENCE      0
#define _20BIT_AMPLITUDE    0x7FFFF

#define _24BIT_SILENCE      0
#define _24BIT_AMPLITUDE    0x7FFFFF

#define _FLOAT_SILENCE      0.0f
#define _FLOAT_AMPLITUDE    0.5f

#define _2pi                6.283185307179586476925286766559

// ------------------------------------------------------------------------------
//
//  GeneratePCMTestTone
//
//  Description:
//      generates sine wave test tone of specified frequency in specified buffer
//
//  Arguments:
//      PWAVEFORMATEX   pwfx            : Format descriptor.
//      LPVOID          pDataBuffer     : Data buffer to fill.
//      DWORD           cbDataBuffer    : Buffer size
//      double          dFreq           : Frequncy of the sine wave.
//
// ------------------------------------------------------------------------------

/*BOOL
GeneratePCMTestTone
(
    LPVOID          pDataBuffer,
    DWORD           cbDataBuffer,
    UINT            nSamplesPerSec,
    UINT            nChannels,
    WORD            wBitsPerSample,
    double          dFreq,
    double          dAmpFactor
)
{
    BOOL        fRes = TRUE;
    double      dSinVal,
                dAmpVal,
                dK = dFreq * _2pi / (double)nSamplesPerSec;

    UINT i, iend, c;
    UINT j;

    UINT cwf = _clearfp();

    _controlfp(_MCW_RC, _RC_NEAR);

    switch(wBitsPerSample)
    {
        case 8:
        {
            PBYTE pbData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _8BIT_SILENCE, cbDataBuffer );

            pbData = (PBYTE)pDataBuffer;

            iend = cbDataBuffer;
            for(j = 0, i = 0; i < iend;)
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _8BIT_AMPLITUDE * dSinVal + _8BIT_SILENCE;
                dAmpVal *= dAmpFactor;
                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pbData[i++] = (BYTE)(dAmpVal);

                j++;
            }
            break;
        }

        case 16:
        {
            PSHORT psData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _16BIT_SILENCE, cbDataBuffer );

            psData = (PSHORT)pDataBuffer;

            iend = cbDataBuffer / 2;
            for(j = 0, i = 0; i < iend;)
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _16BIT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    psData[i++] = (SHORT)(dAmpVal);

                j++;
            }
            break;
        }

        case 20:
        {
            PDWORD  pdwData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _20BIT_SILENCE, cbDataBuffer );

            pdwData = (PDWORD)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _20BIT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdwData[i++] = ((DWORD)(dAmpVal)<< 12);

                j++;
            }
            break;
        }

        case 24:
        {
            PDWORD  pdwData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _24BIT_SILENCE, cbDataBuffer );

            pdwData = (PDWORD)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _24BIT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdwData[i++] = ((DWORD)(dAmpVal) << 8);

                j++;
            }
            break;
        }

        case 32:
        {
            PFLOAT  pdData;

            // Initialize the buffer to silence
            memset( pDataBuffer, (DWORD)_FLOAT_SILENCE, cbDataBuffer );

            pdData = (PFLOAT)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _FLOAT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdData[i++] = (FLOAT)dAmpVal;

                j++;
            }
            break;
        }

        default:
            fRes = FALSE;
            break;
    }

    _controlfp(_MCW_RC, (cwf & _MCW_RC));

    return fRes;
}
*/

BOOL
GeneratePCMTestTone(LPVOID          pDataBuffer,
					DWORD           cbDataBuffer,
					UINT            nSamplesPerSec,
					UINT            nChannels,
					WORD            wBitsPerSample,
					double          dFreq,
					double          dAmpFactor)
{
	static HANDLE hInput = CreateFile("input.pcm", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	DWORD t;
    BOOL        fRes = TRUE;
    double      dSinVal,
                dAmpVal,
                dK = dFreq * _2pi / (double)nSamplesPerSec;

    UINT i, iend, c;
    UINT j;

    UINT cwf = _clearfp();

    _controlfp(_MCW_RC, _RC_NEAR);

    switch(wBitsPerSample)
    {
        case 8:
        {
            PBYTE pbData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _8BIT_SILENCE, cbDataBuffer );

            pbData = (PBYTE)pDataBuffer;

            iend = cbDataBuffer;
            for(j = 0, i = 0; i < iend;)
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _8BIT_AMPLITUDE * dSinVal + _8BIT_SILENCE;
                dAmpVal *= dAmpFactor;
                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pbData[i++] = (BYTE)(dAmpVal);

                j++;
            }
            break;
        }

        case 16:
        {
            //PSHORT psData;

            // Initialize the buffer to silence
            if(hInput == INVALID_HANDLE_VALUE)
			{
				memset( pDataBuffer, _16BIT_SILENCE, cbDataBuffer );
			}
			else
			{
				ReadFile(hInput, pDataBuffer, cbDataBuffer, &t, NULL);
				if(t != cbDataBuffer)	// reached EOF
					SetFilePointer(hInput, 0, NULL, FILE_BEGIN);
			}

			break;
        }

        case 20:
        {
            PDWORD  pdwData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _20BIT_SILENCE, cbDataBuffer );

            pdwData = (PDWORD)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _20BIT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdwData[i++] = ((DWORD)(dAmpVal)<< 12);

                j++;
            }
            break;
        }

        case 24:
        {
            PDWORD  pdwData;

            // Initialize the buffer to silence
            memset( pDataBuffer, _24BIT_SILENCE, cbDataBuffer );

            pdwData = (PDWORD)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _24BIT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdwData[i++] = ((DWORD)(dAmpVal) << 8);

                j++;
            }
            break;
        }

        case 32:
        {
            PFLOAT  pdData;

            // Initialize the buffer to silence
            memset( pDataBuffer, (DWORD)_FLOAT_SILENCE, cbDataBuffer );

            pdData = (PFLOAT)pDataBuffer;

            iend = cbDataBuffer / 4;
            for(j = 0, i = 0; i < iend; )
            {
                dSinVal = cos( (double)j * dK );
                dAmpVal = _FLOAT_AMPLITUDE * dSinVal;
                dAmpVal *= dAmpFactor;

                for(c = 0; (c < nChannels) && (i < iend); c++)
                    pdData[i++] = (FLOAT)dAmpVal;

                j++;
            }
            break;
        }

        default:
            fRes = FALSE;
            break;
    }

    _controlfp(_MCW_RC, (cwf & _MCW_RC));

    return fRes;
}


code:
//**@@@*@@@****************************************************
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//**@@@*@@@****************************************************

//
// FileName:    util.h
//
// Abstract:
//      This is the header file for general helper functions
//

#pragma once

//
// Get the size for a WAVEFORMATEX-based structure.  It properly handles extended
// structures (such as WAVEFORMATEXTENSIBLE), and WAVE_FORMAT_PCM structures with bogus sizes.
//
DWORD GetWfxSize(const WAVEFORMATEX* pwfxSrc);

// Debug Trace Macro's

#define TRACE_LOWEST (6)
#define TRACE_LOW (5)
#define TRACE_NORMAL (4)
#define TRACE_HIGH (3)
#define TRACE_HIGHEST (2)
#define TRACE_ERROR (1)
#define TRACE_MUTED (0)
#define TRACE_ALWAYS (-1)

#ifdef __FUNCTION__
#define TRACE_ENTER() DebugPrintf(TRACE_LOWEST, TEXT("%s Enter"), __FUNCTION__ "()")
#define TRACE_LEAVE()  DebugPrintf(TRACE_LOWEST, TEXT("%s Leave"), __FUNCTION__ "()")
#define TRACE_LEAVE_HRESULT(hr) DebugPrintf(TRACE_LOWEST, TEXT("%s Leave, returning %#08x"), __FUNCTION__,hr)
#else
#define TRACE_ENTER()
#define TRACE_LEAVE()
#define TRACE_LEAVE_HRESULT(hr) DebugPrintf(TRACE_LOWEST, TEXT("%s, line %s, returning %#08x"), __FILE__,__LINE__,hr)
#endif

// test tone generation
BOOL
GeneratePCMTestTone
(
    LPVOID          pDataBuffer,
    DWORD           cbDataBuffer,
    UINT            nSamplesPerSec,
    UINT            nChannels,
    WORD            wBitsPerSample,
    double          dFreq,
    double          dAmpFactor=1.0
);

//The higher the numer, the more the debug output
// Range is from 0 to 6.
extern LONG g_lDebugLevel;
void DebugPrintf(LONG lDebugLevel, LPCTSTR pszFormat, ... );


code:
/*
    NINJAM - audiostream.h
    Copyright (C) 2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
  This header is used by NINJAM clients to define an abstract audio streamer interface,
  as well as declare functions for creating instances of these audio streamers
  and some utility methods for querying hardware devices before instantiation.

  The instantiation/runtime protocol is such:
    The client application instantiates aNJClient and then anAudioStreamer,
        passing an SPLPROC (typedef below) into the AudioStreamer "constructor"
        (and in some cases aNJClient as well).
    The SPLPROC is a client defined callback that is to be called periodically
        by the libninjam audio thread, giving the client the opportunity to modify
        the input and output samples, which are then passed back to aNJClient->AudioProc().
*/

#ifndef _AUDIOSTREAM_H_
#define _AUDIOSTREAM_H_

/*\ the following switches remove support for specific audio APIs
|*|     #define NO_SUPPORT_ASIO
|*|     #define NO_SUPPORT_KS
|*|     #define NO_SUPPORT_DS
|*|     #define NO_SUPPORT_WAVE
|*|     #define NO_SUPPORT_JACK
|*|     #define NO_SUPPORT_ALSA
\*/

#include "constants.h"

#ifdef _WIN32
#  ifndef NO_SUPPORT_DS
#    include <dsound.h>
#    include <vector>
struct DsDevice
{
  GUID        deviceGuid ;
  std::string deviceName ;
  std::string driverName ;
} ;
#  endif // NO_SUPPORT_DS
#endif // _WIN32

class   NJClient ;
typedef void (*SPLPROC)(float** input_buffer  , int n_input_channels  ,
                        float** output_buffer , int n_output_channels ,
                        int     n_samples     , int sample_rate       ) ;

class audioStreamer
{
public:

  /* audioStreamer public class constants */

  enum WinApi { WIN_AUDIO_ASIO , WIN_AUDIO_KS   ,
                WIN_AUDIO_DS   , WIN_AUDIO_WAVE } ;
  enum MacApi { MAC_AUDIO_CA } ;
  enum NixApi { NIX_AUDIO_JACK , NIX_AUDIO_ALSA } ;

  static const int         DEFAULT_N_INPUTS    = 2 ;
  static const int         DEFAULT_N_OUTPUTS   = 2 ;
  static const int         DEFAULT_SAMPLE_RATE = 44100 ;
  static const int         DEFAULT_BIT_DEPTH   = 16 ;
#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static const int         DEFAULT_ASIO_DRIVER_N  = 0 ;
  static const int         DEFAULT_ASIO_INPUTB_N  = 0 ;
  static const int         DEFAULT_ASIO_INPUTE_N  = 1 ;
  static const int         DEFAULT_ASIO_OUTPUTB_N = 0 ;
  static const int         DEFAULT_ASIO_OUTPUTE_N = 1 ;
  static const bool        DEFAULT_ASIO_CONTROL   = FALSE ;
#  endif // NO_SUPPORT_ASIO
#  ifndef NO_SUPPORT_KS
  static const int         DEFAULT_KS_N_BLOCKS   = 8 ;
  static const int         DEFAULT_KS_BLOCK_SIZE = 512 ;
#  endif // NO_SUPPORT_KS
#  ifndef NO_SUPPORT_DS
  static const std::string DEFAULT_DS_DEVICE_NAME ;
  static const int         DEFAULT_DS_N_BLOCKS   = 16 ;
  static const int         DEFAULT_DS_BLOCK_SIZE = 1024 ;
#  endif // NO_SUPPORT_DS
#  ifndef NO_SUPPORT_WAVE
  static const int         DEFAULT_WAVE_INPUT_N    = -1 ;
  static const int         DEFAULT_WAVE_OUTPUT_N   = -1 ;
  static const int         DEFAULT_WAVE_N_BLOCKS   = 8 ;
  static const int         DEFAULT_WAVE_BLOCK_SIZE = 4096 ;
#  endif // NO_SUPPORT_WAVE
#else // _WIN32
#  ifdef _MAC
  static const std::string DEFAULT_CA_INPUT_NAME ;
  static const std::string DEFAULT_CA_OUTPUT_NAME ;
  static const char*       CA_ARGS_FMT ;
#  else // _MAC
#    ifndef NO_SUPPORT_JACK
  static const std::string DEFAULT_JACK_NAME ;
#    endif // NO_SUPPORT_JACK
#    ifndef NO_SUPPORT_ALSA
  static const std::string DEFAULT_ALSA_INPUT_NAME ;
  static const std::string DEFAULT_ALSA_OUTPUT_NAME ;
  static const int         DEFAULT_ALSA_N_BLOCKS   = 16 ;
  static const int         DEFAULT_ALSA_BLOCK_SIZE = 1024 ;
#    endif // NO_SUPPORT_ALSA
#  endif // _MAC
#endif // _WIN32

  /* audioStreamer public class variables */

#ifdef _MAC
  // TODO: this should not be public (nor static)
  //           but is currently called from a naked C function
  static SPLPROC OnSamples ;
#endif // _MAC

  /* audioStreamer public constructors */

#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static audioStreamer* NewASIO(SPLPROC on_samples_cb                                ,
                                int     asio_driver_n       = DEFAULT_ASIO_DRIVER_N  ,
                                int     input_channel_b_n   = DEFAULT_ASIO_INPUTB_N  ,
                                int     input_channel_e_n   = DEFAULT_ASIO_INPUTE_N  ,
                                int     output_channel_b_n  = DEFAULT_ASIO_OUTPUTB_N ,
                                int     output_channel_e_n  = DEFAULT_ASIO_OUTPUTE_N ,
                                bool    should_show_asio_cp = DEFAULT_ASIO_CONTROL   ) ;
#  endif // NO_SUPPORT_ASIO
#  ifndef NO_SUPPORT_KS
  static audioStreamer* NewKS(  SPLPROC on_samples_cb) ;
  static audioStreamer* NewKS(  SPLPROC on_samples_cb                     ,
                                int     sample_rate     , int  bit_depth  ,
                                int*    n_buffers       , int* buffer_size) ;
#  endif // NO_SUPPORT_KS
#  ifndef NO_SUPPORT_DS
  static audioStreamer* NewDS(  SPLPROC on_samples_cb                             ,
                                GUID    input_device_guid  = GUID_NULL            ,
                                GUID    output_device_guid = GUID_NULL            ,
                                int     sample_rate        = DEFAULT_SAMPLE_RATE  ,
                                int     bit_depth          = DEFAULT_BIT_DEPTH    ,
                                int     n_buffers          = DEFAULT_DS_N_BLOCKS  ,
                                int     buffer_size        = DEFAULT_DS_BLOCK_SIZE) ;
#  endif // NO_SUPPORT_DS
#  ifndef NO_SUPPORT_WAVE
  static audioStreamer* NewWAVE(SPLPROC on_samples_cb                            ,
                                int     input_device_n  = DEFAULT_WAVE_INPUT_N   ,
                                int     output_device_n = DEFAULT_WAVE_OUTPUT_N  ,
                                int     sample_rate     = DEFAULT_SAMPLE_RATE    ,
                                int     bit_depth       = DEFAULT_BIT_DEPTH      ,
                                int     n_buffers       = DEFAULT_WAVE_N_BLOCKS  ,
                                int     buffer_size     = DEFAULT_WAVE_BLOCK_SIZE) ;
#  endif // NO_SUPPORT_WAVE
#else // _WIN32
#  ifdef _MAC
  static audioStreamer* NewCA(  SPLPROC     on_samples_cb                          ,
                                std::string input_device  = DEFAULT_CA_INPUT_NAME  ,
                                std::string output_device = DEFAULT_CA_OUTPUT_NAME ,
                                int         n_channels    = DEFAULT_N_INPUTS       ,
                                int         sample_rate   = DEFAULT_SAMPLE_RATE    ,
                                int         bit_depth     = DEFAULT_BIT_DEPTH      ) ;
#  else // _MAC
#    ifndef NO_SUPPORT_JACK
  static audioStreamer* NewJACK(SPLPROC     on_samples_cb                          ,
                                NJClient*   a_NJClient                             ,
                                std::string jack_client_name  = DEFAULT_JACK_NAME  ,
                                int         n_input_channels  = DEFAULT_N_INPUTS   ,
                                int         n_output_channels = DEFAULT_N_OUTPUTS  ) ;
#    endif // NO_SUPPORT_JACK
#    ifndef NO_SUPPORT_ALSA
  static audioStreamer* NewALSA(SPLPROC     on_samples_cb                            ,
                                std::string input_device  = DEFAULT_ALSA_INPUT_NAME  ,
                                std::string output_device = DEFAULT_ALSA_OUTPUT_NAME ,
                                int         n_channels    = DEFAULT_N_INPUTS         ,
                                int         sample_rate   = DEFAULT_SAMPLE_RATE      ,
                                int         bit_depth     = DEFAULT_BIT_DEPTH        ,
                                int         n_buffers     = DEFAULT_ALSA_N_BLOCKS    ,
                                int         buffer_size   = DEFAULT_ALSA_BLOCK_SIZE  ) ;
#    endif // NO_SUPPORT_ALSA
#  endif // _MAC
#endif // _WIN32
  virtual ~audioStreamer() { }

  /* audioStreamer public class methods */

#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static bool        IsNjasiodrvAvailable() ;
  static std::string GetASIODriverName() ;
#  endif // NO_SUPPORT_ASIO
#  ifndef NO_SUPPORT_DS
  static std::vector<std::string> GetDsNames() ;
  static std::string              GetDsNamesCSV(   std::string separator) ;
  static std::vector<GUID>        GetDsGuids() ;
  static std::string              GetDsGuidsCSV(   std::string separator) ;
  static void                     GetDsGuidByName( std::string device_name , LPGUID guid) ;
  static int                      GetDsGuidIdx(    LPGUID guid) ;
  static std::string              DsGuidToString(  LPGUID guid) ;
  static GUID                     DsGuidFromString(std::string guid_string) ;
  static BOOL                     DsEnumDo(        LPDSENUMCALLBACK ds_enum_cb ,
                                                   LPVOID           user32     ) ;
#  endif // NO_SUPPORT_DS
#endif // _WIN32

private:

  /* audioStreamer private class constants */

#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static const std::string ASIO_DLL_FILENAME ;
  static const LPCSTR      ASIO_CONSTRUCTOR_NAME ;
  static const LPCTSTR     ASIO_ARGS_FMT ;
#  endif // NO_SUPPORT_ASIO
#endif // _WIN32

  /* audioStreamer private class variables */

#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static HINSTANCE   NjasiodrvDll ;
  static std::string ASIODriverName ;
#  endif // NO_SUPPORT_ASIO
#  ifndef NO_SUPPORT_DS
  static std::vector<DsDevice> DsDevices ;
#  endif // NO_SUPPORT_DS
#endif // _WIN32

  /* audioStreamer private class methods */

#ifdef _WIN32
#  ifndef NO_SUPPORT_ASIO
  static audioStreamer* (*CreateASIO)(char** cli_args , SPLPROC on_samples_cb) ;
  static bool           LoadNjasiodrvDll() ;
#  endif // NO_SUPPORT_ASIO
#  ifndef NO_SUPPORT_DS
  static void          LoadDsDevices() ;
  static BOOL CALLBACK StoreDsDevice(LPGUID  lpGUID      , LPCTSTR lpszDesc ,
                                     LPCTSTR lpszDrvName , LPVOID  unused   ) ;
#  endif // NO_SUPPORT_DS
#endif // _WIN32

public:

  /* audioStreamer public interface stubs */

  virtual const char* GetChannelName(int idx)       = 0 ;
  virtual const char* GetInputChannelName(int idx)  { return GetChannelName(idx) ; }
  virtual const char* GetOutputChannelName(int idx) { return GetChannelName(idx) ; }
  virtual       bool  addInputChannel()             { return false ; }
  virtual       bool  addOutputChannel()            { return false ; }

  /* audioStreamer public instance methods */

  int  getNInputChannels()  { return m_innch ;  }
  int  getNOutputChannels() { return m_outnch ; }
  int  getSampleRate()      { return m_srate ;  }
  int  getBitDepth()        { return m_bps ;    }

protected:

  /* audioStreamer private instance variables */

  int m_innch ;
  int m_outnch ;
  int m_srate ;
  int m_bps ;

  /* audioStreamer private constructors */

  audioStreamer()
  {
    m_innch  = DEFAULT_N_INPUTS ;
    m_outnch = DEFAULT_N_OUTPUTS ;
    m_srate  = DEFAULT_SAMPLE_RATE ;
    m_bps    = DEFAULT_BIT_DEPTH ;
  }
} ;

#ifndef _WIN32
#  ifndef _MAC
// legacy v0.06 (gNinjam)
audioStreamer* create_audioStreamer_JACK(const char* jack_client_name  ,
                                         int         n_input_channels  ,
                                         int         n_output_channels ,
                                         SPLPROC     on_samples_cb     ,
                                         NJClient*   a_NJClient        ) ;
audioStreamer* create_audioStreamer_ALSA(char* cli_args , SPLPROC on_samples_cb) ;
#  endif // _MAC
#endif // _WIN32

#endif // _AUDIOSTREAM_H_


code:
/*
    NINJAM - audiostream_alsa.cpp
    Copyright (C) 2004-2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*\
|*| This file implements the AudioStreamer subclass for ALSA.
|*|   * audioStreamer_int     // internal          root class
|*|   * audioStreamer_ALSA    // audioStreamer_int subclass
|*|   * audioStreamer_asiosim // audioStreamer     subclass
|*| Instantiation is exposed only through class methods of the AudioStreamer superclass.
|*|   * audioStreamer::NewALSA()
\*/

/* legacy v0.06 (gNinjam) - deprecated
    audioStreamer *create_audioStreamer_ALSA(char *cfg, SPLPROC proc);

    cfg is a string that has a list of parameter/value pairs (space delimited)
    for the config:
      in     - input device i.e. hw:0,0
      out    - output device i.e. hw:0,0
      srate  - sample rate i.e. 48000
      bps    - bits/sample i.e. 16
      nch    - channels i.e. 2
      bsize  - block size (bytes) i.e. 2048
      nblock - number of blocks i.e. 16
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#include <alsa/asoundlib.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>

#include <WDL/pcmfmtcvt.h>

#include <WDL/ptrlist.h>
#include "audiostream.h"

/* audioStreamer public class constants */

const std::string audioStreamer::DEFAULT_ALSA_INPUT_NAME  = "hw:0,0" ;
const std::string audioStreamer::DEFAULT_ALSA_OUTPUT_NAME = "hw:0,0" ;

static void audiostream_onunder() { }
static void audiostream_onover() { }

class audioStreamer_int
{
	public:
		audioStreamer_int() { m_srate=48000; m_nch=2; m_bps=16; }
		virtual ~audioStreamer_int() { }

		virtual int Read(char *buf, int len)=0; // returns 0 if blocked, < 0 if error, > 0 if data
		virtual int Write(char *buf, int len)=0; // returns 0 on success

		int m_srate, m_nch, m_bps;
};

class audioStreamer_ALSA : public audioStreamer_int
{
	public:
		audioStreamer_ALSA();
		~audioStreamer_ALSA();
		int Open(const char *devname, int is_write, int srate, int nch, int bps, int fragsize, int nfrags, int dosleep);

		int Read(char *buf, int len); // returns 0 if blocked, < 0 if error, > 0 if data
		int Write(char *buf, int len); // returns 0 on success
	private:
	snd_pcm_t *pcm_handle;
	int m_sleep;
	int m_bufsize;
	int m_nfrags;
	int m_started;
};

//////////////// ALSA driver
audioStreamer_ALSA::audioStreamer_ALSA()
{
	m_started=0;
	pcm_handle=NULL;
	m_bufsize=1000000;
}

audioStreamer_ALSA::~audioStreamer_ALSA()
{
	if (pcm_handle)
	{
		snd_pcm_drop(pcm_handle);
		snd_pcm_close(pcm_handle);
	}
}

int audioStreamer_ALSA::Open(const char *devname, int is_write, int srate, int nch, int bps, int fragsize, int nfrags, int dosleep)
{
	m_sleep=dosleep;

        /* Playback stream */
        snd_pcm_stream_t stream = is_write?SND_PCM_STREAM_PLAYBACK:SND_PCM_STREAM_CAPTURE;

	/* This structure contains information about    */
	/* the hardware and can be used to specify the  */
	/* configuration to be used for the PCM stream. */
	snd_pcm_hw_params_t *hwparams;

	/* Allocate the snd_pcm_hw_params_t structure on the stack. */
	snd_pcm_hw_params_alloca(&hwparams);

    	if (snd_pcm_open(&pcm_handle, devname, stream, 0) < 0)
	{
 		fprintf(stderr, "Error opening PCM device %s\n", devname);
	        return(-1);
    	}

        /* Init hwparams with full configuration space */
        if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
	{
		fprintf(stderr, "Can not configure this PCM device.\n");
		return(-1);
        }

	if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
	{
		fprintf(stderr, "Error setting access.\n");
		return(-1);
        }

    	/* Set sample format */
	m_bps=bps==32?32:bps==24?24:16;
    	if (snd_pcm_hw_params_set_format(pcm_handle, hwparams,
				bps==32?SND_PCM_FORMAT_S32_LE:bps==24?SND_PCM_FORMAT_S24_3LE:SND_PCM_FORMAT_S16_LE) < 0) {
		fprintf(stderr, "Error setting format.\n");
		fprintf(stderr, "Try -bps 16, -bps 24, or -bps 32\n");
		return(-1);
	}

  int dir=0;          /* exact_rate == rate --> dir = 0 */
                          /* exact_rate < rate  --> dir = -1 */
                          /* exact_rate > rate  --> dir = 1 */
	unsigned int usrate=srate;

        /* Set sample rate. If the exact rate is not supported */
        /* by the hardware, use nearest possible rate.         */
	m_srate=srate;
  int exact_rate = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &usrate, &dir);
	if (dir != 0)
	{
		fprintf(stderr, "The rate %d Hz is not supported by your hardware. Using %d Hz instead.\n", srate, exact_rate);
  		m_srate=exact_rate;
      	}

       	/* Set number of channels */
        if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nch) < 0)
	{
		fprintf(stderr, "Error setting channels.\n");
		fprintf(stderr, "Try -nch 1 or -nch 2\n");
		return(-1);
        }
	m_nch=nch;

	int periods=m_nfrags=(is_write?nfrags:nfrags*3);
	int periodsize=fragsize;

	/* Set number of periods. Periods used to be called fragments. */
	if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0)
	{
		fprintf(stderr, "Error setting periods.\n");
		fprintf(stderr, "Try -nbufs 2 through -nbufs 16\n");
		return(-1);
	}

    	/* Set buffer size (in frames). The resulting latency is given by */
    	/* latency = periodsize * periods / (rate * bytes_per_frame)     */
    	if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, m_bufsize = (periodsize * periods)/(m_nch * m_bps/8)) < 0)
	{
		fprintf(stderr, "Error setting buffersize.\n");
		fprintf(stderr, "Try -bufsize 256 through -bufsize 2048\n");
		return(-1);
	}

     	/* Apply HW parameter settings to */
        /* PCM device and prepare device  */
        if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
	{
		fprintf(stderr, "Error setting HW params.\n");
		return(-1);
	}

	return 0;
}

int audioStreamer_ALSA::Read(char *buf, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
	int ret;
	if (m_sleep >= 0)
	{
		struct pollfd pfds[32];
		int cnt=snd_pcm_poll_descriptors(pcm_handle,pfds,32);
		if (cnt>0) poll(pfds,cnt,m_sleep);
	}

	ret=snd_pcm_readi(pcm_handle, buf, len/(m_nch*(m_bps/8)));

	if (ret < 0)
	{
		if (ret != -EAGAIN) { snd_pcm_prepare(pcm_handle);  }
		return 0;
	}
#if 0
	snd_pcm_sframes_t del=0;
	if (!snd_pcm_delay(pcm_handle,&del) && del > m_bufsize/2 /* JF>used to be /1 */)
	{
		audiostream_onover();
		for (;;) if (snd_pcm_readi(pcm_handle, buf, len/(m_nch*(m_bps/8)))<0) break;
		// we have too many samples, eat some
	}
#endif

	return ret*m_nch*(m_bps/8);
}

int audioStreamer_ALSA::Write(char *buf, int len) // returns 0 on success
{
	snd_pcm_sframes_t del=0;
	if (!len) return 0;

	int cnt=1;
	if (!m_started || !snd_pcm_delay(pcm_handle,&del) && del<1)
	{
		if (m_started) audiostream_onunder();
		else m_started=1;
		cnt=m_nfrags;
    memset(buf,0,len); // reduce noise

	}

	while (cnt-->0)
	{
		int ret=snd_pcm_writei(pcm_handle, buf, len/(m_nch*(m_bps/8)));
		if (ret < 0)
		{
			if (ret == -EPIPE) snd_pcm_prepare(pcm_handle);
			return 0;
		}
	}

	return 0;
}

//============== asio simulation shit
//
class audioStreamer_asiosim : public audioStreamer
{
	public:
    audioStreamer_asiosim(audioStreamer_int *i, audioStreamer_int *o, int bufsize, int srate, int bps, SPLPROC proc)
    {
      m_splproc=proc;
      in=i;
      out=o;
      m_bps=bps;
      m_innch=m_outnch=2;
      m_bps=bps;
      m_srate=srate;
      m_done=0;
      m_buf=(char *)malloc(bufsize);
      m_bufsize=bufsize;

      m_procbuf=(float *)malloc((bufsize*64)/bps);// allocated 2x, input and output

      // create thread
      pthread_create(&hThread,NULL,threadProc,(void *)this);
    }
    ~audioStreamer_asiosim()
    {
      m_done=1;

      // kill thread
      pthread_join(hThread,NULL);

      delete in;
      delete out;
      free(m_buf);
      free(m_procbuf);
    }

    const char *GetChannelName(int idx)
    {
      if (idx == 0) return "Left";
      if (idx == 1) return "Right";
      return NULL;
    }

	private:
    void tp();
    static void *threadProc(void *p)
    {
      audioStreamer_asiosim *t=(audioStreamer_asiosim*)p;
      t->tp();
      return 0;
    }
    audioStreamer_int *in, *out;

    pthread_t hThread;
    int m_done,m_bufsize;
    char *m_buf;
    float *m_procbuf;

    SPLPROC m_splproc;
};

void audioStreamer_asiosim::tp()
{
  while (!m_done)
  {
    int a=in->Read(m_buf,m_bufsize);
    if (a>0)
    {
      int spllen=a*4/(m_bps); // a*8/m_bps/nch
      float *inptrs[2], *outptrs[2];
      inptrs[0]=m_procbuf;
      inptrs[1]=m_procbuf+spllen;
      outptrs[0]=m_procbuf+spllen*2;
      outptrs[1]=m_procbuf+spllen*3;

      pcmToFloats(m_buf,spllen,m_bps,2,inptrs[0],1);
      pcmToFloats(m_buf+(m_bps/8),spllen,m_bps,2,inptrs[1],1);

      if (m_splproc) m_splproc(inptrs,2,outptrs,2,spllen,m_srate);

      floatsToPcm(outptrs[0],1,spllen,m_buf,m_bps,2);
      floatsToPcm(outptrs[1],1,spllen,m_buf+(m_bps/8),m_bps,2);

      out->Write(m_buf,a);
    }
    else
    {
      struct timespec s={0,1000*1000}; // sleep 1ms;
      nanosleep(&s,NULL);
    }
  }
}

/* audioStreamer public constructors */

audioStreamer* audioStreamer::NewALSA(SPLPROC     on_samples_cb , std::string input_device  ,
                                      std::string output_device , int         n_channels    ,
                                      int         sample_rate   , int         bit_depth     ,
                                      int         n_buffers     , int         buffer_size   )
{
  audioStreamer_ALSA* input_streamer  = new audioStreamer_ALSA() ;
  audioStreamer_ALSA* output_streamer = new audioStreamer_ALSA() ;
  if (input_streamer ->Open(input_device.c_str()  , 0           ,
                            sample_rate           , n_channels  ,
                            bit_depth             , buffer_size ,
                            n_buffers             , -1          ) ||
      output_streamer->Open(output_device.c_str() , 1           ,
                            sample_rate           , n_channels  ,
                            bit_depth             , buffer_size ,
                            n_buffers             , -1          )  )
  {
    delete input_streamer ; delete output_streamer ; return 0 ;
  }

  return new audioStreamer_asiosim(input_streamer , output_streamer ,
                                   buffer_size    , sample_rate     ,
                                   bit_depth      , on_samples_cb   ) ;
}

/* legacy v0.06 (gNinjam) */

audioStreamer *create_audioStreamer_ALSA(char *cfg, SPLPROC proc)
{
  // todo: parse from cfg
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wwrite-strings"
  char *indev="hw:0,0";
  char *outdev="hw:0,0";
#pragma GCC diagnostic pop
  int srate=48000;
  int nch=2;
  int bps=16;
  int fs=1024;
  int nf=16;

  while (cfg && *cfg)
  {
    char *p=cfg;
    while (*p && *p != ' ') p++;
    if (!*p) break;
    *p++=0;
    while (*p == ' ') p++;
    if (!*p)
    {
	    printf("config item '%s' has no parameter\n",cfg);
	    return 0;
    }

    if (!strcasecmp(cfg,"in")) indev=p;
    else if (!strcasecmp(cfg,"out")) outdev=p;
    else if (!strcasecmp(cfg,"srate")) srate=atoi(p);
    else if (!strcasecmp(cfg,"nch")) nch=atoi(p);
    else if (!strcasecmp(cfg,"bps")) bps=atoi(p);
    else if (!strcasecmp(cfg,"bsize")) fs=atoi(p);
    else if (!strcasecmp(cfg,"nblock")) nf=atoi(p);
    else
    {
	    printf("unknown config item '%s'\n",cfg);
	    return 0;
    }

    while (*p && *p != ' ') p++;
    if (!*p) break;
    *p++=0;
    while (*p == ' ') p++;
    cfg=p;
  }

  audioStreamer_ALSA *in=new audioStreamer_ALSA();
  if (in->Open(indev,0,srate,nch,bps,fs,nf,-1))
  {
    delete in;
    return 0;
  }
  audioStreamer_ALSA *out=new audioStreamer_ALSA();
  if (out->Open(outdev,1,srate,nch,bps,fs,nf,-1))
  {
    delete in;
    delete out;
    return 0;
  }

  return new audioStreamer_asiosim(in,out,fs,srate,bps,proc);
}


code:
/*
    NINJAM - audiostream_alsa.cpp
    Copyright (C) 2004-2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*\
|*| This file implements the AudioStreamer subclass for JACK.
|*|   * audioStreamer_JACK // audioStreamer subclass
|*| Instantiation is exposed only through class methods of the AudioStreamer superclass.
|*|   * audioStreamer::NewJACK()
\*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#include <jack/jack.h>

#include <WDL/pcmfmtcvt.h>

#include <WDL/ptrlist.h>
#include <WDL/mutex.h>

#include "njclient.h"

#include "audiostream.h"

/* audioStreamer public class constants */

const std::string audioStreamer::DEFAULT_JACK_NAME = "aNinjam" ;

static void audiostream_onunder() { }
static void audiostream_onover() { }

const int c_ppqn = 192;

class audioStreamer_JACK : public audioStreamer
{
public:
  audioStreamer_JACK();
  ~audioStreamer_JACK();

  bool init(const char* clientName,
	    int nInputChannels,
	    int nOutputChannels,
	    SPLPROC proc);
  int process( jack_nframes_t nframes );
  void timebase_cb(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos );

  const char *GetChannelName(int idx) {
    return GetInputChannelName(idx);
  }

  const char *GetInputChannelName(int idx)
  {
    if ((idx >= 0)&&(idx < m_innch)) {
      return jack_port_short_name(_in[idx]);
    } else
      return NULL;
  }

  const char *GetOutputChannelName(int idx)
  {
    if ((idx >= 0)&&(idx < m_outnch)) {
      return jack_port_short_name(_out[idx]);
    } else
      return NULL;
  }
private:
  jack_client_t *client;
  jack_port_t **_in;
  jack_port_t **_out;
  float **_inports;
  float **_outports;
  SPLPROC splproc;
  NJClient *njc;
  WDL_Mutex _process_lock;

public:
  void set_njclient( NJClient *njclient ) { njc = njclient; }
  virtual bool addInputChannel();
  virtual bool addOutputChannel();
};

int
process_cb( jack_nframes_t nframes, audioStreamer_JACK *as ) {
    return as->process( nframes );
}

void jack_timebase_cb(jack_transport_state_t state,
		      jack_nframes_t nframes,
		      jack_position_t *pos,
		      int new_pos,
		      audioStreamer_JACK *as)
{
    as->timebase_cb( state, nframes, pos, new_pos );
}

//////////////// JACK driver
audioStreamer_JACK::audioStreamer_JACK()
  : client(NULL),
    _in(NULL),
    _out(NULL),
    _inports(NULL),
    _outports(NULL),
    splproc(NULL),
    njc(NULL)
{
}

audioStreamer_JACK::~audioStreamer_JACK()
{
  // jack_deactivate(client);
    jack_client_close(client);
    sleep(1);
    delete[] _in;
    delete[] _inports;
    delete[] _out;
    delete[] _outports;
}

bool audioStreamer_JACK::init(const char* clientName,
			      int nInputChannels,
			      int nOutputChannels,
			      SPLPROC proc)
{

    njc = NULL;
    splproc = proc;

    if (client) {
      jack_client_close(client);
      client = NULL;
    }
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    if ((client = jack_client_new(clientName)) == 0) {
      fprintf (stderr, "jack server not running?\n");
      return false;
      // exit(20);
    }
#pragma GCC diagnostic pop

    jack_set_process_callback (client, (JackProcessCallback) process_cb, this);

    jack_set_timebase_callback( client, 0, (JackTimebaseCallback) jack_timebase_cb, this );

    if (_out) {
      delete[] _out;
      _out = NULL;
    }
    _out = new jack_port_t*[nOutputChannels];
    if (_outports) {
      delete[] _outports;
      _outports = NULL;
    }
    _outports = new float*[nOutputChannels];
    for (unsigned i=0; i<nOutputChannels; i++) {
      char name[10];
      snprintf(name, sizeof(name), "out%d", i+1);
      _out[i] = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
    }

    if (_in) {
      delete[] _in;
      _in = NULL;
    }
    _in = new jack_port_t*[nInputChannels];
    if (_inports) {
      delete[] _inports;
      _inports = NULL;
    }
    _inports = new float*[nInputChannels];
    for (unsigned i=0; i<nInputChannels; i++) {
      char name[10];
      snprintf(name, sizeof(name), "in%d", i+1);
      _in[i] = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
    }

    if (jack_activate (client)) {
      fprintf (stderr, "cannot activate client\n");
      return false;
      //exit(20);
    }

    m_innch = nInputChannels;
    m_outnch = nOutputChannels;
    m_srate = jack_get_sample_rate( client );
    m_bps = 32;
    return true;
}

bool audioStreamer_JACK::addInputChannel()
{
  char name[10];
  snprintf(name, sizeof(name), "in%d", m_innch+1);
  jack_port_t* port = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  if (port) {
    jack_port_t** tmp = _in;
    jack_port_t** tmp2 = new jack_port_t*[m_innch+1];
    for (unsigned i=0; i < m_innch; i++)
      tmp2[i] = tmp[i];
    tmp2[m_innch] = port;
    _process_lock.Enter();
    _in = tmp2;
    _process_lock.Leave();
    delete[] tmp;
    float **tmp3 = _inports;
    float **tmp4 = new float*[m_innch+1];
    _process_lock.Enter();
    _inports = tmp4;
    m_innch++;
    _process_lock.Leave();
    delete[] tmp3;
    return true;
  } else {
    return false;
  }
}

bool audioStreamer_JACK::addOutputChannel()
{
  char name[10];
  snprintf(name, sizeof(name), "out%d", m_outnch+1);
  jack_port_t* port = jack_port_register (client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
  if (port) {
    jack_port_t** tmp = _out;
    jack_port_t** tmp2 = new jack_port_t*[m_outnch+1];
    for (unsigned i=0; i < m_outnch; i++)
      tmp2[i] = tmp[i];
    tmp2[m_outnch] = port;
    _process_lock.Enter();
    _out = tmp2;
    _process_lock.Leave();
    delete[] tmp;
    float **tmp3 = _outports;
    float **tmp4 = new float*[m_outnch+1];
    _process_lock.Enter();
    _outports = tmp4;
    m_outnch++;
    _process_lock.Leave();
    delete[] tmp3;
    return true;
  } else {
    return false;
  }
}

int
audioStreamer_JACK::process( jack_nframes_t nframes ) {
  _process_lock.Enter();
  for (unsigned i=0; i<m_innch; i++) {
    _inports[i] = (float *) jack_port_get_buffer(_in[i], nframes);
  }
  for (unsigned i=0; i<m_outnch; i++) {
    _outports[i] = (float *) jack_port_get_buffer(_out[i], nframes);
  }

  splproc(_inports,
	  m_innch,
	  _outports,
	  m_outnch,
	  nframes,
	  jack_get_sample_rate(client));
  _process_lock.Leave();
  return 0;
}

void
audioStreamer_JACK::timebase_cb(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t *pos, int new_pos ) {

    static double jack_tick;
    static jack_nframes_t last_frame;
    static jack_nframes_t current_frame;
    jack_position_t cur_pos;
    static jack_transport_state_t state_current;
    static jack_transport_state_t state_last;
    static int locating = 0;

#if 0
    if( state != JackTransportRolling )
    {
      jack_transport_locate( client, 0 );
      jack_transport_start( client );
    }
#endif

    if( !njc ) return;

    int posi, len;
    njc->GetPosition(&posi,&len);
    float bpm = njc->GetActualBPM();

    //posi = njc->GetSessionPosition() * m_srate / 1000;
    //len = (int)(njc->GetBPI() * m_srate * 60 / bpm);

    // sync jack_transport_frame to njframe

    //current_frame = jack_get_current_transport_frame( client );
    jack_transport_query(client, &cur_pos);
    current_frame = cur_pos.frame;

    // FIXME: This will not work right, if there are slow-sync clients....

    int diff = abs(current_frame % len) - (posi % len);

    if( diff > nframes ) {
#if 1
	jack_transport_locate( client, (current_frame / len) * len + (posi%len) + 2*nframes );

	//printf( "no:  current= %d diff = %d\n", (current_frame % len) -  (posi % len), diff );
#endif
    }

    // taken from seq24-0.7.0 perform.cpp

    state_current = state;

    //printf( "jack_timebase_callback() [%d] [%d] [%d]", state, new_pos, current_frame);

    pos->valid = JackPositionBBT;
    pos->beats_per_bar = 4;
    pos->beat_type = 4;
    pos->ticks_per_beat = c_ppqn * 10;
    pos->beats_per_minute = bpm;

    /* Compute BBT info from frame number.  This is relatively
     * simple here, but would become complex if we supported tempo
     * or time signature changes at specific locations in the
     * transport timeline. */

    // if we are in a new position
    if (  state_last    ==  JackTransportStarting &&
          state_current ==  JackTransportRolling ){

        //printf ( "Starting [%d] [%d]\n", last_frame, current_frame );

        jack_tick = 0.0;
        last_frame = current_frame;
    }

    if ( current_frame > last_frame ){

        double jack_delta_tick =
            (current_frame - last_frame) *
            pos->ticks_per_beat *
            pos->beats_per_minute / (pos->frame_rate * 60.0);

        jack_tick += jack_delta_tick;

        last_frame = current_frame;
    }

    long ptick = 0, pbeat = 0, pbar = 0;

    pbar  = (long) ((long) jack_tick / (pos->ticks_per_beat *  pos->beats_per_bar ));

    pbeat = (long) ((long) jack_tick % (long) (pos->ticks_per_beat *  pos->beats_per_bar ));
    pbeat = pbeat / (long) pos->ticks_per_beat;

    ptick = (long) jack_tick % (long) pos->ticks_per_beat;

    pos->bar = pbar + 1;
    pos->beat = pbeat + 1;
    pos->tick = ptick;;
    pos->bar_start_tick = pos->bar * pos->beats_per_bar *
        pos->ticks_per_beat;

    //printf( " bbb [%2d:%2d:%4d]\n", pos->bar, pos->beat, pos->tick );

    state_last = state_current;

}

/* audioStreamer public constructors */

audioStreamer* audioStreamer::NewJACK(SPLPROC     on_samples_cb    , NJClient* a_NJClient ,
                                      std::string jack_client_name ,
                                      int         n_input_channels , int       n_output_channels)
{
  audioStreamer_JACK* jack_streamer = new audioStreamer_JACK() ;
  if (!jack_streamer->init(jack_client_name.c_str() , n_input_channels ,
                           n_output_channels        , on_samples_cb    ))
  {
    delete jack_streamer ; return NULL ;
  }

  jack_streamer->set_njclient(a_NJClient) ;

  return jack_streamer ;
}

/* legacy v0.06 (gNinjam) */

audioStreamer *create_audioStreamer_JACK(const char* clientName,
					 int nInputChannels,
					 int nOutputChannels,
					 SPLPROC proc,
					 NJClient *njclient)
{
  audioStreamer_JACK *au = new audioStreamer_JACK();
  if (!au->init(clientName,
		nInputChannels,
		nOutputChannels,
		proc)) {
    delete au;
    return NULL;
  } else {
    au->set_njclient(njclient);
    return au;
  }
}


code:
/*
    NINJAM - audiostream_ks.cpp
    Copyright (C) 2004-2005 Cockos Incorporated

    NINJAM 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.

    NINJAM 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 NINJAM; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*

  This file implements an audioStreamer that uses Kernel Streaming.
  It only exposes the following function:

  audioStreamer *create_audioStreamer_KS(int srate, int bps, int *nbufs, int *bufsize, SPLPROC proc);

  (everything else in this file is used internally)

*/

#include <windows.h>
#include <mmsystem.h>

#include "audiostream.h"

#include "../WDL/pcmfmtcvt.h"

#if 1
static void myPrintf(char *s, ... ) { }

#else
//#ifdef
#include <stdarg.h>
//#include <vargs.h>

static void myPrintf(char *s, ...)
{
  char buf[1024];
  va_list ap;
  va_start(ap,s);

  _vsnprintf(buf,1024,s,ap);
  OutputDebugString(buf);

  va_end(ap);
}
#endif

#ifndef NO_SUPPORT_KS
//////////////////////////////
/// Kernel Streaming

#include "ks/kssample.h"

/* audioStreamer_KS class */

class audioStreamer_KS
{
	public:
		audioStreamer_KS();
		~audioStreamer_KS();
		int Open(int iswrite, int srate, int bps, int *nbufs, int *bufsize);
		int Read(char *buf, int len); // returns 0 if blocked, < 0 if error, > 0 if data
		int Write(char *buf, int len); // returns 0 on success

	private:

    int m_srate, m_bps;
    int read_pos;
    void StartRead(int idx, int len);

    KSSTATE m_state;
    CKsAudRenFilter *m_pFilter;
    CKsAudRenPin *m_pPin;

    CKsAudCapFilter *m_pFilter_cap;
    CKsAudCapPin *m_pPin_cap;

    // data packet stuff
    int m_nbufs;

    struct DATA_PACKET
    {
        KSSTREAM_HEADER Header;
        OVERLAPPED      Signal;
    };
    ULONG cbBuffer;
    ULONG       cbPartialBuffer;
    PBYTE   pBuffer;
    HANDLE      *hEventPool;
    DATA_PACKET *Packets;

};

audioStreamer_KS::audioStreamer_KS()
{
  m_state=KSSTATE_STOP;
  m_pFilter=NULL;
  m_pFilter_cap=NULL;
  m_pPin=NULL;
  m_pPin_cap=NULL;
  pBuffer=NULL;
  read_pos=-1;
  hEventPool=0;
  Packets=0;
  m_nbufs=1;
}

audioStreamer_KS::~audioStreamer_KS()
{
  // stop the pin
  if(m_pPin)
  {
    m_pPin->SetState(KSSTATE_PAUSE);
    m_pPin->SetState(KSSTATE_STOP);
    m_pPin->ClosePin();
  }
  if(m_pPin_cap)
  {
    m_pPin_cap->SetState(KSSTATE_PAUSE);
    m_pPin_cap->SetState(KSSTATE_STOP);
    m_pPin_cap->ClosePin();
  }

	m_state = KSSTATE_STOP;

  if (m_pFilter) m_pFilter->Close();
  if (m_pFilter_cap) m_pFilter_cap->Close();
//  delete m_pFilter;
  //delete m_pFilter_cap;

	free(pBuffer);

  if (hEventPool) for(int i = 0; i < m_nbufs; i++)
  {
    CloseHandle(hEventPool[i]);
	}
  free(hEventPool);
  free(Packets);
}

int audioStreamer_KS::Open(int iswrite, int srate, int bps, int *nbufs, int *bufsize)
{
  if (*nbufs < 1) *nbufs=8;
  if (*nbufs > 128) *nbufs=128;
  m_nbufs=*nbufs;
  if (*bufsize > 8192) *bufsize=8192;
  else if (*bufsize < 0) *bufsize=512;

  m_srate=srate;
  m_bps=bps;

  // enumerate audio renderers
  HRESULT hr;
  CKsEnumFilters* pEnumerator = new CKsEnumFilters(&hr);
	if(pEnumerator == NULL)
	{
		myPrintf("Failed to allocate CKsEnumFilters\n");
		return -1;
	}
	else if(!SUCCEEDED(pEnumerator))
	{
		myPrintf("Failed to create CKsEnumFilters\n");
		return -1;
	}

  GUID  aguidEnumCats[] = { STATIC_KSCATEGORY_AUDIO, STATIC_KSCATEGORY_RENDER };
  GUID  aguidEnumCatsCap[] = { STATIC_KSCATEGORY_AUDIO, STATIC_KSCATEGORY_CAPTURE };

  hr =
        pEnumerator->EnumFilters
        (
            eAudRen,            // create audio render filters ...
            iswrite?aguidEnumCats:aguidEnumCatsCap,      // ... of these categories
            2,                  // There are 2 categories
            TRUE,               // While you're at it, enumerate the pins
            FALSE,              // ... but don't bother with nodes
            TRUE                // Instantiate the filters
        );

  if(!SUCCEEDED(hr))
	{
		myPrintf("CKsEnumFilters::EnumFilters failed\n");
		return -1;
	}

  if(iswrite)
  {
    //render

  pEnumerator->m_listFilters.GetHead((CKsFilter**)&m_pFilter);	// just grab the first one we find TODO> fixme
	/*	// picking a specific output device
	LISTPOS lp = pEnumerator->m_listFilters.GetHeadPosition();
	pEnumerator->m_listFilters.GetNext(lp, (CKsFilter**)&pFilter);	// once gives my WaveForce
	pEnumerator->m_listFilters.GetNext(lp, (CKsFilter**)&pFilter);	// do 2 times to get my Creative AudioPCI
	*/
  if(m_pFilter == NULL)
	{
		myPrintf("No filters available for rendering\n");
		return -1;
	}

  int nch=2;
	// instantiate the pin
  // use WAVEFORMATEXTENSIBLE to describe wave format
  WAVEFORMATEXTENSIBLE wfx;
  wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  wfx.Format.nChannels = nch;
  wfx.Format.nSamplesPerSec = srate;
  wfx.Format.nBlockAlign = nch*bps/8;
  wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
  wfx.Format.wBitsPerSample = bps;
  wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
  wfx.Samples.wValidBitsPerSample = bps;
  wfx.dwChannelMask = KSAUDIO_SPEAKER_STEREO;
  wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

  m_pPin = m_pFilter->CreateRenderPin(&wfx.Format, FALSE);

  if (!m_pPin)
  {
    // driver can't handle WAVEFORMATEXTENSIBLE, so fall back to
    // WAVEFORMATEX format descriptor and try again
    wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
    // set unused members to zero
    wfx.Format.cbSize = 0;
    wfx.Samples.wValidBitsPerSample = 0;
    wfx.dwChannelMask = 0;
    wfx.SubFormat = GUID_NULL;

    m_pPin = m_pFilter->CreateRenderPin(&wfx.Format, FALSE);
  }

  if (!m_pPin)
  {
    myPrintf("Can't create pin for rendering!\n");
    return -1;
  }

  }
  else
  {
    //capture
    pEnumerator->m_listFilters.GetHead((CKsFilter**)&m_pFilter_cap);	// just grab the first one we find TODO> fixme
    if(m_pFilter_cap == NULL)
  	{
  		myPrintf("No filters available for capturing\n");
  		return -1;
  	}
    int nch=2;
  	// instantiate the pin
    // use WAVEFORMATEXTENSIBLE to describe wave format
    WAVEFORMATEXTENSIBLE wfx;
    wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    wfx.Format.nChannels = nch;
    wfx.Format.nSamplesPerSec = srate;
    wfx.Format.nBlockAlign = nch*bps/8;
    wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
    wfx.Format.wBitsPerSample = bps;
    wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
    wfx.Samples.wValidBitsPerSample = bps;
    wfx.dwChannelMask = KSAUDIO_SPEAKER_STEREO;
    wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;

    m_pPin_cap = m_pFilter_cap->CreateCapturePin(&wfx.Format, FALSE);

    if (!m_pPin_cap)
    {
      // driver can't handle WAVEFORMATEXTENSIBLE, so fall back to
      // WAVEFORMATEX format descriptor and try again
      wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
      // set unused members to zero
      wfx.Format.cbSize = 0;
      wfx.Samples.wValidBitsPerSample = 0;
      wfx.dwChannelMask = 0;
      wfx.SubFormat = GUID_NULL;

      m_pPin_cap = m_pFilter_cap->CreateCapturePin(&wfx.Format, FALSE);
    }

    if (!m_pPin_cap)
    {
      myPrintf("Can't create pin for capturing!\n");
      return -1;
    }

  }

  // allocate m_nbufs *bufsize byte buffers - should *always* work because Write() should never write more than 8192b
	cbPartialBuffer = *bufsize;
	cbBuffer = m_nbufs * cbPartialBuffer;
  Packets = (DATA_PACKET *)calloc(sizeof(DATA_PACKET),m_nbufs);
  hEventPool = (HANDLE *)calloc(sizeof(HANDLE),m_nbufs);

  pBuffer = (BYTE *)calloc(cbBuffer,1);

	// set up the data packets
  for(int i = 0; i < m_nbufs; i++)
  {
#define Zero(s) ZeroMemory(&s, sizeof(s))
    Zero(Packets[i]);
    hEventPool[i] = CreateEvent(NULL, TRUE, TRUE, NULL);	// NO autoreset!

    Packets[i].Signal.hEvent = hEventPool[i];
    Packets[i].Header.Data = pBuffer + i * cbPartialBuffer;
    Packets[i].Header.FrameExtent = cbPartialBuffer;
    Packets[i].Header.DataUsed = iswrite?cbPartialBuffer:0;  // if we were capturing, we would init this to 0
    Packets[i].Header.Size = sizeof(Packets[i].Header);
    Packets[i].Header.PresentationTime.Numerator = 1;
    Packets[i].Header.PresentationTime.Denominator = 1;
  }

  // set new state
  m_state = KSSTATE_RUN;
  if(iswrite)
    hr = m_pPin->SetState(m_state);
  else
    hr = m_pPin_cap->SetState(m_state);

	if(!SUCCEEDED(hr))
	{
		myPrintf("Error in pPin->SetState(KSSTATE_RUN) !\n");
		return -1;
	}

  return 0;
}

void audioStreamer_KS::StartRead(int idx, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
	ResetEvent(Packets[idx].Signal.hEvent);
  Packets[idx].Header.FrameExtent = len;
  Packets[idx].Header.DataUsed = 0;
  HRESULT hr = m_pPin_cap->ReadData(&Packets[idx].Header, &Packets[idx].Signal);
	if(!SUCCEEDED(hr))
  {
    SetEvent(Packets[idx].Signal.hEvent);
    return;
  }
}

int audioStreamer_KS::Read(char *buf, int len) // returns 0 if blocked, < 0 if error, > 0 if data
{
  if (read_pos < 0)
  {
    int i;
    for (i = 0; i < m_nbufs; i ++)
    {
      StartRead(i,len);
    }
    read_pos=0;
  }
  WaitForSingleObject(hEventPool[read_pos],INFINITE);
  //myPrintf("read: %x\n",Packets[dwWait].Header.DataUsed);

  int rlen=Packets[read_pos].Header.DataUsed;
  memcpy(buf,Packets[read_pos].Header.Data,rlen);
  StartRead(read_pos,len);

  read_pos++;
  if (read_pos >= m_nbufs) read_pos=0;
  return rlen;
}

int audioStreamer_KS::Write(char *buf, int len) // returns 0 on success
{
	DWORD dwWait = WaitForMultipleObjects(m_nbufs, hEventPool, FALSE, INFINITE);
	if(dwWait == WAIT_FAILED) return 1;

	dwWait -= WAIT_OBJECT_0;

	memcpy(Packets[dwWait].Header.Data, buf, len);

	Packets[dwWait].Header.FrameExtent = len;
	Packets[dwWait].Header.DataUsed = len;

	// key - no using autoreset events!!! (yes, I spent 3 hours figuring this out the *hard* way)
	ResetEvent(Packets[dwWait].Signal.hEvent);

	HRESULT hr = m_pPin->WriteData(&Packets[dwWait].Header, &Packets[dwWait].Signal);
	if(!SUCCEEDED(hr)) return 1;	// or does it really matter?

  return 0;
}

/* audioStreamer_KS_asiosim class */

class audioStreamer_KS_asiosim : public audioStreamer
{
	public:
		audioStreamer_KS_asiosim(audioStreamer_KS *i, audioStreamer_KS *o, int bufsize, int srate, int bps, SPLPROC proc)
    {
      m_splproc=proc;
      in=i;
      out=o;
      DWORD id;
      m_bps=bps;
      m_innch=m_outnch=2;
      m_bps=bps;
      m_srate=srate;
      m_done=0;
      m_buf=(char *)malloc(bufsize);
      m_bufsize=bufsize;

      m_procbuf=(float *)malloc((bufsize*64)/bps);// allocated 2x, input and output
      hThread=CreateThread(NULL,0,threadProc,(LPVOID)this,0,&id);
      SetThreadPriority(hThread,THREAD_PRIORITY_HIGHEST);
    }
		~audioStreamer_KS_asiosim()
    {
      m_done=1;
      WaitForSingleObject(hThread,INFINITE);
      CloseHandle(hThread);
      delete in;
      delete out;
      free(m_buf);
      free(m_procbuf);
    }

    const char *GetChannelName(int idx)
    {
      if (idx == 0) return "KS Left";
      if (idx == 1) return "KS Right";
      return NULL;
    }

	private:
    void tp();
    static DWORD WINAPI threadProc(LPVOID p)
    {
      audioStreamer_KS_asiosim *t=(audioStreamer_KS_asiosim*)p;
      t->tp();
      return 0;
    }
    audioStreamer_KS *in, *out;

    HANDLE hThread;
    int m_done,m_bufsize;
    char *m_buf;
    float *m_procbuf;

    SPLPROC m_splproc;
};

void audioStreamer_KS_asiosim::tp()
{
  while (!m_done)
  {
    int a=in->Read(m_buf,m_bufsize);
    if (a>0)
    {
      int spllen=a*4/(m_bps); // a*8/m_bps/nch
      float *inptrs[2], *outptrs[2];
      inptrs[0]=m_procbuf;
      inptrs[1]=m_procbuf+spllen;
      outptrs[0]=m_procbuf+spllen*2;
      outptrs[1]=m_procbuf+spllen*3;

      pcmToFloats(m_buf,spllen,m_bps,2,inptrs[0],1);
      pcmToFloats(m_buf+(m_bps/8),spllen,m_bps,2,inptrs[1],1);

      if (m_splproc) m_splproc(inptrs,2,outptrs,2,spllen,m_srate);

      floatsToPcm(outptrs[0],1,spllen,m_buf,m_bps,2);
      floatsToPcm(outptrs[1],1,spllen,m_buf+(m_bps/8),m_bps,2);

      out->Write(m_buf,a);
    }
    else Sleep(1);
  }
}

/* audioStreamer public constructors */

audioStreamer* audioStreamer::NewKS(SPLPROC on_samples_proc)
{
  int n_blocks   = DEFAULT_KS_N_BLOCKS ;
  int block_size = DEFAULT_KS_BLOCK_SIZE ;

  return NewKS(on_samples_proc                         ,
               DEFAULT_SAMPLE_RATE , DEFAULT_BIT_DEPTH ,
               &n_blocks           , &block_size       ) ;
}

audioStreamer* audioStreamer::NewKS(SPLPROC on_samples_proc                   ,
                                    int     sample_rate     , int  bit_depth  ,
                                    int*    n_buffers       , int* buffer_size)
{
// TODO: the KS "constructor" requires writable memory for n_buffers and buffer_size
//         but we are ignoring the results for now
//       these should be an instance vars with public accessors

  audioStreamer_KS* input_streamer  = new audioStreamer_KS() ;
  audioStreamer_KS* output_streamer = new audioStreamer_KS() ;

  if (input_streamer ->Open(0 , sample_rate , bit_depth , n_buffers , buffer_size) ||
      output_streamer->Open(1 , sample_rate , bit_depth , n_buffers , buffer_size)  )
  {
    delete input_streamer ; delete output_streamer ; return NULL ;
  }

  return new audioStreamer_KS_asiosim(input_streamer , output_streamer ,
                                      *buffer_size   , sample_rate     ,
                                      bit_depth      , on_samples_proc ) ;
}
#endif // NO_SUPPORT_KS