/***************************************************************************** * cylcstrm.cpp - Cyclic Stream implementation ***************************************************************************** * Copyright (c) C-Media Inc. 1997-2001. All rights reserved. */ #include "cylcstrm.h" #include "minwave.h" #include "pfxdebug.h" #define STR_MODULENAME "cylcstrm: " #define CHANNEL_TAG 'NAHC' #pragma code_seg("PAGE") /***************************************************************************** * CMiniportWaveCyclicStream::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. This function works just like a COM QueryInterface * call and is used if the object is not being aggregated. */ STDMETHODIMP CMiniportWaveCyclicStream::NonDelegatingQueryInterface ( IN REFIID Interface, OUT PVOID * Object ) { PAGED_CODE (); ASSERT (Object); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::NonDelegatingQueryInterface]")); if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { *Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLICSTREAM(this))); } else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclicStream)) { *Object = PVOID(PMINIPORTWAVECYCLICSTREAM(this)); } else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream)) { *Object = PVOID(PDRMAUDIOSTREAM(this)); } else if(IsEqualGUIDAligned(Interface,IID_IDmaChannel)) { *Object = PVOID(PDMACHANNEL(this)); } else { *Object = NULL; } if (*Object) { PUNKNOWN(*Object)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } // CMiniportWaveCyclicStream::NonDelegatingQueryInterface() /***************************************************************************** * CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream() ***************************************************************************** * Destructor. */ CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream (void) { PAGED_CODE (); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream]")); if (DrmRights.CopyProtect) { Miniport->CopyProtectCounter--; } if (DrmRights.DigitalOutputDisable) { Miniport->DigitalOutputDisableCounter--; if (Miniport->DigitalOutputDisableCounter==0) Miniport->SetConfig (&Miniport->SpdifConfig); } // TODO: We hope to protect all streaming destruction from service call KeWaitForSingleObject (&Miniport->ServiceSync, Executive, KernelMode, FALSE, NULL); if(Buffer) delete Buffer; if(OutputStream) { OutputStream->Stop(); OutputStream->Release(); } if(Miniport) { Miniport->RemoveActiveStream(); } KeReleaseMutex (&Miniport->ServiceSync, FALSE); if (Miniport && Pin != -1) { Miniport->Release (); } } // CMiniportWaveCyclicStream::~CMiniportWaveCyclicStream () /***************************************************************************** * CMiniportWaveCyclicStream::Init() ***************************************************************************** * Initializes a stream. */ NTSTATUS CMiniportWaveCyclicStream::Init ( IN CMiniportWaveCMI * Miniport_, IN ULONG Pin_, IN PWAVEFORMATEX WaveFormat, IN BOOL isFirstStream, IN BOOL isKaraoke ) { PAGED_CODE (); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::Init]")); ASSERT (Miniport_); ASSERT (WaveFormat); Miniport = Miniport_; Pin = Pin_; if (Pin != -1) { // We must add references because the caller will not do it for us. Miniport->AddRef (); } NTSTATUS ntStatus = InitFormat (WaveFormat); if (NT_SUCCESS (ntStatus)) { State = KSSTATE_STOP; NotificationInterval = 10; bIsFirstStream = isFirstStream; bKaraoke = isKaraoke; Offset = 0; IntendedBufSize = 0; ulMapSize = MIXING_CHANNEL_DEFAULT; Frequency = 0; Destination = NULL; OutputStream = NULL; Is3D = FALSE; Created3D = FALSE; m_ulGetDataBytes = 0; } return ntStatus; } // CMiniportWaveCyclicStream::Init() /***************************************************************************** * CMiniportWaveCyclicStream::SetNotificationFreq() ***************************************************************************** * Sets the notification frequency. */ STDMETHODIMP_(ULONG)CMiniportWaveCyclicStream::SetNotificationFreq ( IN ULONG Interval, OUT PULONG FramingSize ) { PAGED_CODE (); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::SetNotificationFreq]")); *FramingSize = WFX.nChannels * (WFX.wBitsPerSample/8) * (WFX.nSamplesPerSec * NotificationInterval / 1000); return NotificationInterval; } // CMiniportWaveCyclicStream::SetNotificationFreq() /***************************************************************************** * CMiniportWaveCyclicStream::SetFormat() ***************************************************************************** * Sets the wave format. */ STDMETHODIMP CMiniportWaveCyclicStream::SetFormat ( IN PKSDATAFORMAT Format ) { PAGED_CODE (); NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; LPWAVEFORMATEX pFormat; ASSERT (Format); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::SetFormat]")); if (State == KSSTATE_RUN) { // can't change format while running return ntStatus; } ntStatus = Miniport->ValidateFormat (FALSE, Format, &pFormat); if (!NT_SUCCESS(ntStatus)) { return STATUS_UNSUCCESSFUL; } // If the format didn't change, don't do anything if ((WFX.nSamplesPerSec == pFormat->nSamplesPerSec) && (WFX.nChannels == pFormat->nChannels) && (WFX.wBitsPerSample == pFormat->wBitsPerSample)) { return STATUS_SUCCESS; } ntStatus = SetChannelFormat (pFormat); if (NT_SUCCESS(ntStatus)) { ntStatus = InitFormat (pFormat); } return ntStatus; } // CMiniportWaveCyclicStream::SetFormat() /***************************************************************************** * CMiniportWaveCyclicStream::InitFormat() ***************************************************************************** * Sets the wave format. */ NTSTATUS CMiniportWaveCyclicStream::InitFormat ( IN LPWAVEFORMATEX p ) { PAGED_CODE (); ASSERT (p); // maximum frequency that GetData runs at is 100000 // each block is approx 11ms (512/44100) // therefore minimum size is 100000*512/44100 = 1161 samples // use twice this at least, i.e. 2322 // round up to nice number, 2560 DWORD dwBufferSize = 1168*(p->nChannels)*(p->wBitsPerSample/8); // DWORD dwBufferSize = 2560*(p->nChannels)*(p->wBitsPerSample/8); NTSTATUS ntStatus = AllocateBuffer (dwBufferSize,0); if (NT_SUCCESS (ntStatus)) { WFX = *p; } return ntStatus; } // CMiniportWaveCyclicStream::InitFormat() /***************************************************************************** * CMiniportWaveCyclicStream::SetState() ***************************************************************************** * Sets the state of the channel. */ STDMETHODIMP CMiniportWaveCyclicStream::SetState ( IN KSSTATE NewState ) { PAGED_CODE (); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::SetState]")); NTSTATUS ntStatus = STATUS_SUCCESS; // The acquire state is not distinguishable from the pause state for our // purposes. if (NewState == KSSTATE_ACQUIRE) { NewState = KSSTATE_PAUSE; } if (State != NewState) { switch (NewState) { case KSSTATE_PAUSE: if (State == KSSTATE_RUN) { ntStatus = Pause (); break; } break; case KSSTATE_STOP: ntStatus = Stop (); break; case KSSTATE_RUN: ntStatus = Start (BufferSize(),TRUE); break; } State = NewState; } return ntStatus; } // CMiniportWaveCyclicStream::SetState() /***************************************************************************** * CMiniportWaveCyclicStream::SetContentId ***************************************************************************** * This routine gets called by drmk.sys to pass the content to the driver. * The driver has to enforce the rights passed. */ STDMETHODIMP CMiniportWaveCyclicStream::SetContentId ( IN ULONG contentId, IN PCDRMRIGHTS drmRights ) { PAGED_CODE (); _DbgPrintF (DEBUGLVL_VERBOSE,("[CMiniportWaveCyclicStream::SetContentId]")); if (DrmRights.CopyProtect != drmRights->CopyProtect) { if (drmRights->CopyProtect) Miniport->CopyProtectCounter++; else Miniport->CopyProtectCounter--; DrmRights.CopyProtect = drmRights->CopyProtect; } if (DrmRights.DigitalOutputDisable != drmRights->DigitalOutputDisable) { if (drmRights->DigitalOutputDisable) Miniport->DigitalOutputDisableCounter++; else Miniport->DigitalOutputDisableCounter--; if (drmRights->DigitalOutputDisable && Miniport->DigitalOutputDisableCounter == 1) Miniport->SetConfig (&Miniport->SpdifConfig, TRUE); else if (!drmRights->DigitalOutputDisable && Miniport->DigitalOutputDisableCounter == 0) Miniport->SetConfig (&Miniport->SpdifConfig); DrmRights.DigitalOutputDisable = drmRights->DigitalOutputDisable; } return STATUS_SUCCESS; } STDMETHODIMP CMiniportWaveCyclicStream::PropertyHandler(PPCPROPERTY_REQUEST PropertyRequest, BOOL listener) { //TRACE("CMiniportWaveCyclicStream::PropertyHandler"); PAGED_CODE (); ASSERT (PropertyRequest); if (listener) return PropertyHandler3dListener(PropertyRequest); else return PropertyHandler3dBuffer(PropertyRequest); } // CMiniportWaveCyclicStream::PropertyHandler() #pragma code_seg() /***************************************************************************** * CMiniportWaveCyclicStream::GetPosition() ***************************************************************************** * Gets the current position. May be called at dispatch level. */ STDMETHODIMP CMiniportWaveCyclicStream::GetPosition ( OUT PULONG Position ) { ASSERT (Position); *Position = GetOffset (); return STATUS_SUCCESS; } /***************************************************************************** * CMiniportWaveCyclicStream::NormalizePhysicalPosition ***************************************************************************** * Routine Description: * Given a physical position based on the actual number of bytes transferred, * this function converts the position to a time-based value of 100ns units. */ STDMETHODIMP CMiniportWaveCyclicStream::NormalizePhysicalPosition ( IN OUT PLONGLONG PhysicalPosition ) { *PhysicalPosition = (_100NS_UNITS_PER_SECOND / (WFX.nChannels * (WFX.wBitsPerSample/8)) * *PhysicalPosition) / WFX.nSamplesPerSec; return STATUS_SUCCESS; } // CMiniportWaveCyclicStream::NormalizePhysicalPosition() /***************************************************************************** * CMiniportWaveCyclicStream::Silence() ***************************************************************************** * Fills a buffer with silence. */ STDMETHODIMP_(void) CMiniportWaveCyclicStream::Silence ( IN PVOID Buffer_, IN ULONG ByteCount ) { if (Buffer_ && Buffer) { ULONG BufEnd = ULONG(Buffer) + BufSize; if (((ULONG)Buffer_ >= ULONG(Buffer)) || ((ULONG)Buffer_ < BufEnd) || ((ULONG)Buffer_ + ByteCount <= BufEnd)) { RtlFillMemory (Buffer_, ByteCount, (WFX.wBitsPerSample == 8) ? 0x80 : 0); } } } ULONG CMiniportWaveCyclicStream::BytesPerSample() { //TRACE("CMiniportWaveCyclicStream::BytesPerSample"); ULONG bytes = WFX.nChannels * WFX.wBitsPerSample / 8; return bytes; } // CMiniportWaveCyclicStream::BytesPerSample()