Exclusive-Mode Akışları
Daha önce açıklandığı gibi, bir uygulama özel kullanım modunda bir akış açarsa, uygulama akışı çalan veya kaydeden ses uç noktası cihazını özel kullanımda kullanır. Buna karşılık, çeşitli uygulamalar cihazda paylaşılan mod akışlarını açarak bir ses uç noktası cihazını paylaşabilir.
Bir ses cihazına özel mod erişimi önemli sistem seslerini engelleyebilir, diğer uygulamalarla birlikte çalışabilirliği önleyebilir ve kullanıcı deneyimini düşürebilir. Bu sorunları azaltmak için, özel mod akışı olan bir uygulama genellikle ön plan işlemi olmadığında veya etkin bir şekilde akış olmadığında ses cihazının denetimini kaldırır.
Akış gecikmesi, bir uygulamanın uç nokta arabelleğine bir ses uç noktası cihazı bağlayan veri yolundaki gecikmedir. İşleme akışı için gecikme süresi, uygulamanın uç nokta arabelleğine örnek yazmasından örneğin hoparlörler aracılığıyla duyulmasına kadar olan en uzun gecikme süresidir. Yakalama akışı için gecikme süresi, sesin mikrofona girdiği zamandan uygulamanın uç nokta arabelleğinden bu ses için örneği okuyabileceği zamana kadar olan en uzun gecikme süresidir.
Özel kullanım modu akışları kullanan uygulamalar genellikle ses uç noktası cihazları ile uç nokta arabelleklerine erişen uygulama iş parçacıkları arasındaki veri yollarında düşük gecikme süreleri gerektirdiğinden bunu yapar. Genellikle, bu iş parçacıkları görece yüksek önceliğe sahip olarak çalışır ve ardışık işleme geçişlerini ses donanımı tarafından ayıran düzenli aralıklara yakın veya aynı düzenli aralıklarla çalışacak şekilde zamanlanır. Her geçiş sırasında ses donanımı uç nokta arabelleklerindeki yeni verileri işler.
En küçük akış gecikme sürelerini elde etmek için, bir uygulama hem özel ses donanımı hem de hafifçe yüklenen bir bilgisayar sistemi gerektirebilir. Ses donanımını zamanlama sınırlarının ötesine taşıma veya sistemi rakip yüksek öncelikli görevlerle yükleme düşük gecikmeli ses akışında hataya neden olabilir. Örneğin, bir işleme akışı için, ses donanımı arabelleği okumadan önce uygulama bir uç nokta arabelleğine yazamıyorsa veya donanım arabellek yürütülmek üzere zamanlanmadan önce arabelleği okuyamıyorsa bir hata oluşabilir. Genellikle, çok çeşitli ses donanımlarında ve çok çeşitli sistemlerde çalıştırılması amaçlanan bir uygulama, tüm hedef ortamlardaki hataları önlemek için zamanlama gereksinimlerini yeterince gevşetmelidir.
Windows Vista,düşük gecikme süreli ses akışları gerektiren uygulamaları desteklemek için çeşitli özelliklere sahiptir. User-Mode Ses Bileşenleri'nde açıklandığı gibi, zaman açısından kritik işlemler gerçekleştiren uygulamalar, CPU kaynaklarını düşük öncelikli uygulamalara reddetmeden iş parçacığı önceliğini artırmak için Multimedya Sınıf Zamanlayıcı Hizmeti (MMCSS) işlevlerini çağırabilir. Ayrıca, IAudioClient::Initialize yöntemi, bir uygulamanın arabellek hizmeti iş parçacığının ses cihazından yeni bir arabellek kullanılabilir olduğunda yürütülmesini zamanlamasını sağlayan bir AUDCLNT_STREAMFLAGS_EVENTCALLBACK bayrağını destekler. Bir uygulama iş parçacığı, bu özellikleri kullanarak ne zaman yürütüleceği konusundaki belirsizliği azaltabilir ve böylece düşük gecikme süreli bir ses akışında hata riskini azaltabilir.
Eski ses bağdaştırıcıları için sürücülerin WaveCyclic veya WavePci cihaz sürücü arabirimini (DDI) kullanma olasılığı yüksekken, daha yeni ses bağdaştırıcıları için sürücülerin WaveRT DDI'yi destekleme olasılığı daha yüksektir. Özel mod uygulamaları için WaveRT sürücüleri WaveCyclic veya WavePci sürücülerinden daha iyi performans sağlayabilir, ancak WaveRT sürücüleri ek donanım özellikleri gerektirir. Bu özellikler, donanım arabelleklerini uygulamalarla doğrudan paylaşma özelliğini içerir. Doğrudan paylaşımda, özel modlu bir uygulama ile ses donanımı arasında veri aktarmak için sistem müdahalesi gerekmez. Buna karşılık, WaveCyclic ve WavePci sürücüleri eski ve daha az özellikli ses bağdaştırıcıları için uygundur. Bu bağdaştırıcılar, uygulama arabellekleri ve donanım arabellekleri arasında veri bloklarını (sistem G/Ç istek paketlerine veya IRP'lere bağlı) taşımak için sistem yazılımını kullanır. Buna ek olarak, USB ses cihazları uygulama arabellekleri ve donanım arabellekleri arasında veri taşımak için sistem yazılımı kullanır. WASAPI, veri aktarımı için sisteme bağlı ses cihazlarına bağlanan özel kullanım modu uygulamalarının performansını artırmak için uygulamalar ve donanım arasında veri aktaran sistem iş parçacıklarının önceliğini otomatik olarak artırır. WASAPI, iş parçacığı önceliğini artırmak için MMCSS kullanır. Windows Vista'da bir sistem iş parçacığı özel mod ses kayıttan yürütme akışı için veri aktarımını PCM biçiminde ve 10 milisaniyeden kısa bir cihaz süresiyle yönetiyorsa WASAPI, MMCSS görev adını iş parçacığına "Pro Audio" olarak atar. Akışın cihaz süresi 10 milisaniyeden büyük veya buna eşitse WASAPI, MMCSS görev adını iş parçacığına "Ses" olarak atar. WaveCyclic, WavePci ve WaveRT DDI'leri hakkında daha fazla bilgi için Windows DDK belgelerine bakın. Uygun bir cihaz dönemi seçme hakkında bilgi için bkz. IAudioClient::GetDevicePeriod.
Oturum Birim Denetimleri'nde açıklandığı gibi WASAPI, paylaşılan mod ses akışlarının ses düzeylerini denetlemek için ISimpleAudioVolume, IChannelAudioVolumeve IAudioStreamVolume arabirimlerisağlar. Ancak, bu arabirimlerdeki denetimlerin özel kullanım modu akışları üzerinde hiçbir etkisi yoktur. Bunun yerine, özel mod akışlarını yöneten uygulamalar genellikle bu akışların birim düzeylerini denetlemek için EndpointVolume API'sindeki IAudioEndpointVolume arabirimini kullanır. Bu arabirim hakkında bilgi için bkz. Uç Nokta Birim Denetimleri.
Sistemdeki her kayıttan yürütme cihazı ve yakalama cihazı için kullanıcı cihazın özel kullanım modunda kullanılıp kullanılamayacağını denetleyebiliyor. Kullanıcı cihazın özel kullanım modunu devre dışı bırakırsa, cihaz yalnızca paylaşılan mod akışlarını oynatmak veya kaydetmek için kullanılabilir.
Kullanıcı cihazın özel kullanım modunu etkinleştirirse, kullanıcı bir uygulamanın cihazı özel kullanım modunda kullanma isteğinin cihaz üzerinden paylaşılan mod akışlarını yürüten veya kaydeden uygulamalar tarafından kullanılmasını önleyip başlatmayacağını da denetleyebilir. Ön kullanım etkinse, bir uygulama tarafından cihazın özel denetimini alma isteği, cihaz şu anda kullanımda değilse veya cihaz paylaşılan modda kullanılıyorsa başarılı olur, ancak başka bir uygulamanın cihaz üzerinde özel denetimi varsa istek başarısız olur. Ön kullanım devre dışı bırakılırsa, bir uygulama tarafından cihazın özel denetimini alma isteği, cihaz şu anda kullanımda değilse başarılı olur, ancak cihaz zaten paylaşılan modda veya özel kullanım modunda kullanılıyorsa istek başarısız olur.
Windows Vista'da, bir ses uç noktası cihazı için varsayılan ayarlar şunlardır:
- Cihaz, özel mod akışlarını oynatmak veya kaydetmek için kullanılabilir.
- Özel modlu bir akışı oynatmak veya kaydetmek için cihaz kullanma isteği, şu anda cihaz üzerinden oynatılmakta olan veya kaydedilen tüm paylaşılan mod akışlarını ön plana çıkarır.
Kayıttan yürütme veya kayıt cihazının özel mod ayarlarını değiştirmek için
- Görev çubuğunun sağ tarafında bulunan bildirim alanında hoparlör simgesine sağ tıklayın ve kayıttan yürütme cihazları veya Kayıt Cihazları seçin. (Alternatif olarak, Komut İstemi penceresinden Mmsys.cplWindows multimedya denetim masası'nı çalıştırın. Daha fazla bilgi için bkz. DEVICE_STATE_XXX Sabitleraçıklamalar.)
- Ses penceresi görüntülendikten sonra kayıttan yürütme veya kayıt öğesini seçin. Ardından, cihaz adları listesinden bir girdi seçin ve Özellikleröğesine tıklayın.
- Özellikler penceresi görüntülendikten sonra Gelişmiş öğesine tıklayın.
- Uygulamaların cihazı özel kullanım modunda kullanmasını sağlamak için Uygulamaların bu cihazın özel denetimini almasına izin veretiketli kutuyu işaretleyin. Cihazın özel kullanım modunu devre dışı bırakmak için onay kutusunun işaretini kaldırın.
- Cihazın özel kullanım modu etkinse, cihaz şu anda paylaşılan mod akışlarını yürütüyor veya kaydediyorsa, cihazın özel kullanım denetimi isteğinin başarılı olup olmayacağını belirtebilirsiniz. Özel mod uygulamalarına paylaşılan mod uygulamalarına göre öncelik vermek için, Özel mod uygulamalarına öncelik veretiketli kutuyu işaretleyin. Paylaşılan mod uygulamalarına göre özel kullanım modu uygulamalarının önceliğini reddetmek için onay kutusunun işaretini kaldırın.
Aşağıdaki kod örneği, özel kullanım modunda kullanılmak üzere yapılandırılmış bir ses işleme cihazında düşük gecikme süreli bir ses akışının nasıl yürütüldüğünü gösterir:
//-----------------------------------------------------------
// Play an exclusive-mode stream on the default audio
// rendering device. The PlayExclusiveStream function uses
// event-driven buffering and MMCSS to play the stream at
// the minimum latency supported by the device.
//-----------------------------------------------------------
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
HRESULT PlayExclusiveStream(MyAudioSource *pMySource)
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = 0;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioRenderClient *pRenderClient = NULL;
WAVEFORMATEX *pwfx = NULL;
HANDLE hEvent = NULL;
HANDLE hTask = NULL;
UINT32 bufferFrameCount;
BYTE *pData;
DWORD flags = 0;
DWORD taskIndex = 0;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDefaultAudioEndpoint(
eRender, eConsole, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(
IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)
// Call a helper function to negotiate with the audio
// device for an exclusive-mode stream format.
hr = GetStreamFormat(pAudioClient, &pwfx);
EXIT_ON_ERROR(hr)
// Initialize the stream to play at the minimum latency.
hr = pAudioClient->GetDevicePeriod(NULL, &hnsRequestedDuration);
EXIT_ON_ERROR(hr)
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
hnsRequestedDuration,
hnsRequestedDuration,
pwfx,
NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
// Align the buffer if needed, see IAudioClient::Initialize() documentation
UINT32 nFrames = 0;
hr = pAudioClient->GetBufferSize(&nFrames);
EXIT_ON_ERROR(hr)
hnsRequestedDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC / pwfx->nSamplesPerSec * nFrames + 0.5);
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
hnsRequestedDuration,
hnsRequestedDuration,
pwfx,
NULL);
}
EXIT_ON_ERROR(hr)
// Tell the audio source which format to use.
hr = pMySource->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Create an event handle and register it for
// buffer-event notifications.
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent == NULL)
{
hr = E_FAIL;
goto Exit;
}
hr = pAudioClient->SetEventHandle(hEvent);
EXIT_ON_ERROR(hr);
// Get the actual size of the two allocated buffers.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(
IID_IAudioRenderClient,
(void**)&pRenderClient);
EXIT_ON_ERROR(hr)
// To reduce latency, load the first buffer with data
// from the audio source before starting the stream.
hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
EXIT_ON_ERROR(hr)
hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
EXIT_ON_ERROR(hr)
hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
EXIT_ON_ERROR(hr)
// Ask MMCSS to temporarily boost the thread priority
// to reduce glitches while the low-latency stream plays.
hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
if (hTask == NULL)
{
hr = E_FAIL;
EXIT_ON_ERROR(hr)
}
hr = pAudioClient->Start(); // Start playing.
EXIT_ON_ERROR(hr)
// Each loop fills one of the two buffers.
while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
{
// Wait for next buffer event to be signaled.
DWORD retval = WaitForSingleObject(hEvent, 2000);
if (retval != WAIT_OBJECT_0)
{
// Event handle timed out after a 2-second wait.
pAudioClient->Stop();
hr = ERROR_TIMEOUT;
goto Exit;
}
// Grab the next empty buffer from the audio device.
hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
EXIT_ON_ERROR(hr)
// Load the buffer with data from the audio source.
hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
EXIT_ON_ERROR(hr)
hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
EXIT_ON_ERROR(hr)
}
// Wait for the last buffer to play before stopping.
Sleep((DWORD)(hnsRequestedDuration/REFTIMES_PER_MILLISEC));
hr = pAudioClient->Stop(); // Stop playing.
EXIT_ON_ERROR(hr)
Exit:
if (hEvent != NULL)
{
CloseHandle(hEvent);
}
if (hTask != NULL)
{
AvRevertMmThreadCharacteristics(hTask);
}
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pRenderClient)
return hr;
}
Yukarıdaki kod örneğinde PlayExclusiveStream işlevi, bir işleme akışı yürütülürken uç nokta arabelleğine hizmet veren uygulama iş parçacığında çalışır. İşlev, istemci tanımlı myAudioSource sınıfına ait bir nesnenin işaretçisi olan pMySource adlı tek bir parametre alır. Bu sınıfın kod örneğinde çağrılan LoadData ve SetFormat adlı iki üye işlevi vardır. MyAudioSource, akış İşleme bölümünde açıklanmıştır.
PlayExclusiveStream işlevi, cihazın uygulama tarafından kullanıma uygun özel mod akış biçimini destekleyip desteklemediğini belirlemek için varsayılan işleme cihazıyla anlaşmaya varan getStreamFormat yardımcı işlevini çağırır. GetStreamFormat işlevinin kodu kod örneğinde görünmez; bunun nedeni, uygulamanın ayrıntılarının tamamen uygulamanın gereksinimlerine bağlı olmasıdır. Ancak, GetStreamFormat işlevinin işlemi basitçe açıklanabilir; cihazın uygun bir biçimi destekleyip desteklemediğini belirlemek için IAudioClient::IsFormatSupported yöntemini bir veya daha fazla kez çağırır. Uygulamanın gereksinimleri, GetStreamFormat'ın hangi biçimlerin IsFormatSupported yöntemine ve bunları sunma sırasına göre belirlenir. IsFormatSupportedhakkında daha fazla bilgi için bkz. Cihaz Biçimleri.
GetStreamFormat çağrısından sonra PlayExclusiveStream işlevi, ses donanımı tarafından desteklenen en düşük cihaz süresini elde etmek için IAudioClient::GetDevicePeriod yöntemini çağırır. Ardından işlev, en düşük süreye eşit bir arabellek süresi istemek için IAudioClient::Initialize yöntemini çağırır. Çağrı başarılı olursa Initialize yöntemi, her biri minimum süreye eşit olan iki uç nokta arabelleği ayırır. Daha sonra ses akışı çalışmaya başladığında, uygulama ve ses donanımı iki arabelleği "ping-pong" biçiminde paylaşır; yani uygulama bir arabelleğe yazarken, donanım diğer arabellekten okur.
Akışı başlatmadan önce PlayExclusiveStream işlevi aşağıdakileri yapar:
- Arabellekler doldurulmaya hazır olduğunda bildirim alacağı olay tutamacını oluşturur ve kaydeder.
- Akışın çalışmaya başlamasından ilk sesin duyulmasına kadar olan gecikme süresini azaltmak için ilk arabelleği ses kaynağındaki verilerle doldurur.
- MMCSS'nin PlayExclusiveStream'in yürütüldiği iş parçacığının önceliğini artırmasını istemek için AvSetMmThreadCharacteristics işlevini çağırır. (Akış çalışmayı durdurduğunda AvRevertMmThreadCharacteristics işlev çağrısı özgün iş parçacığı önceliğini geri yükler.)
AvSetMmThreadCharacteristics ve AvRevertMmThreadCharacteristicshakkında daha fazla bilgi için Windows SDK belgelerine bakın.
Akış çalışırken, önceki kod örneğindeki -döngüsü bir uç nokta arabelleği doldururkenher yinelemesi. Yinelemeler arasında WaitForSingleObject işlev çağrısı, olay tanıtıcısının işaretlenmesini bekler. Tanıtıcı işaretlendiğinde, döngü gövdesi aşağıdakileri yapar:
- Sonraki arabelleği almak için IAudioRenderClient::GetBuffer yöntemini çağırır.
- Arabelleği doldurur.
- Arabelleği serbest bırakmak için IAudioRenderClient::ReleaseBuffer yöntemini çağırır.
WaitForSingleObjecthakkında daha fazla bilgi için Windows SDK belgelerine bakın.
Ses bağdaştırıcısı bir WaveRT sürücüsü tarafından denetleniyorsa, olay tutamacının sinyali ses donanımından DMA aktarım bildirimlerine bağlıdır. BIR USB ses cihazı veya WaveCyclic veya WavePci sürücüsü tarafından denetlenen bir ses cihazı için olay tutamacının sinyalleri, uygulama arabelleğinden donanım arabelleğine veri aktaran IP'lerin tamamlanmalarına bağlıdır.
Yukarıdaki kod örneği, ses donanımını ve bilgisayar sistemini performans sınırlarına iter. İlk olarak, akış gecikme süresini azaltmak için uygulama, arabellek bakım iş parçacığını ses donanımının destekleyeceği en düşük cihaz süresini kullanacak şekilde zamanlar. İkinci olarak, iş parçacığının her cihaz süresi içinde güvenilir bir şekilde yürütülmesini sağlamak için, AvSetMmThreadCharacteristics işlev çağrısı TaskName parametresini "Pro Audio" olarak ayarlar; yani Windows Vista'da en yüksek önceliğe sahip varsayılan görev adıdır. Uygulamanızın zamanlama gereksinimlerinin yararlılığından ödün vermeden gevşetilip gevşetilemeyeceğini göz önünde bulundurun. Örneğin, uygulama arabellek hizmeti iş parçacığını minimumdan daha uzun bir süre kullanacak şekilde zamanlayabilir. Daha uzun bir süre daha düşük bir iş parçacığı önceliğinin kullanılmasına güvenli bir şekilde izin verebilir.
İlgili konular