VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/HDAStreamPeriod.cpp@ 69498

Last change on this file since 69498 was 69119, checked in by vboxsync, 7 years ago

Audio: More cleanups (missing keywords, incorrect #endif docs, stuff)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: HDAStreamPeriod.cpp 69119 2017-10-17 19:08:38Z vboxsync $ */
2/** @file
3 * HDAStreamPeriod.cpp - Stream period functions for HD Audio.
4 *
5 * Utility functions for handling HDA audio stream periods.
6 * Stream period handling is needed in order to keep track of a stream's timing
7 * and processed audio data.
8 *
9 * As the HDA device only has one bit clock (WALCLK) but audio streams can be processed
10 * at certain points in time, these functions can be used to estimate and schedule the
11 * wall clock (WALCLK) for all streams accordingly.
12 */
13
14/*
15 * Copyright (C) 2017 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#define LOG_GROUP LOG_GROUP_DEV_HDA
31#include <VBox/log.h>
32
33#include <iprt/asm-math.h> /* For ASMMultU64ByU32DivByU32(). */
34
35#include <VBox/vmm/pdmdev.h>
36#include <VBox/vmm/pdmaudioifs.h>
37
38#include "DrvAudio.h"
39#include "HDAStreamPeriod.h"
40
41#ifdef IN_RING3
42/**
43 * Creates a stream period.
44 *
45 * @return IPRT status code.
46 * @param pPeriod Stream period to initialize.
47 */
48int hdaStreamPeriodCreate(PHDASTREAMPERIOD pPeriod)
49{
50 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_FLAG_VALID));
51
52 int rc = RTCritSectInit(&pPeriod->CritSect);
53 if (RT_SUCCESS(rc))
54 {
55 pPeriod->fStatus = HDASTREAMPERIOD_FLAG_VALID;
56 }
57
58 return VINF_SUCCESS;
59}
60
61/**
62 * Destroys a formerly created stream period.
63 *
64 * @param pPeriod Stream period to destroy.
65 */
66void hdaStreamPeriodDestroy(PHDASTREAMPERIOD pPeriod)
67{
68 if (pPeriod->fStatus & HDASTREAMPERIOD_FLAG_VALID)
69 {
70 RTCritSectDelete(&pPeriod->CritSect);
71
72 pPeriod->fStatus = HDASTREAMPERIOD_FLAG_NONE;
73 }
74}
75
76/**
77 * Initializes a given stream period with needed parameters.
78 *
79 * @param pPeriod Stream period to (re-)initialize. Must be created with hdaStreamPeriodCreate() first.
80 * @param u8SD Stream descriptor (serial data #) number to assign this stream period to.
81 * @param u16LVI The HDA stream's LVI value to use for the period calculation.
82 * @param u32CBL The HDA stream's CBL value to use for the period calculation.
83 * @param pStreamCfg Audio stream configuration to use for this period.
84 */
85void hdaStreamPeriodInit(PHDASTREAMPERIOD pPeriod,
86 uint8_t u8SD, uint16_t u16LVI, uint32_t u32CBL, PPDMAUDIOSTREAMCFG pStreamCfg)
87{
88
89 /* Sanity. */
90 AssertReturnVoid(u16LVI);
91 AssertReturnVoid(u32CBL);
92 AssertReturnVoid(DrvAudioHlpStreamCfgIsValid(pStreamCfg));
93
94 /*
95 * Linux guests (at least Ubuntu):
96 * 17632 bytes (CBL) / 4 (frame size) = 4408 frames / 4 (LVI) = 1102 frames per period
97 *
98 * Windows guests (Win10 AU):
99 * 3584 bytes (CBL) / 4 (frame size) = 896 frames / 2 (LVI) = 448 frames per period
100 */
101 unsigned cTotalPeriods = u16LVI;
102
103 if (cTotalPeriods <= 1)
104 cTotalPeriods = 2; /* At least two periods *must* be present (LVI >= 1). */
105
106 uint32_t framesToTransfer = (u32CBL / 4 /** @todo Define frame size? */) / cTotalPeriods;
107
108 pPeriod->u8SD = u8SD;
109 pPeriod->u64StartWalClk = 0;
110 pPeriod->u32Hz = pStreamCfg->Props.uHz;
111 pPeriod->u64DurationWalClk = hdaStreamPeriodFramesToWalClk(pPeriod, framesToTransfer);
112 pPeriod->u64ElapsedWalClk = 0;
113 pPeriod->i64DelayWalClk = 0;
114 pPeriod->framesToTransfer = framesToTransfer;
115 pPeriod->framesTransferred = 0;
116 pPeriod->cIntPending = 0;
117
118 Log3Func(("[SD%RU8] %RU64 long, Hz=%RU32, CBL=%RU32, LVI=%RU16 -> %u periods, %RU32 frames each\n",
119 pPeriod->u8SD, pPeriod->u64DurationWalClk, pPeriod->u32Hz, u32CBL, u16LVI,
120 cTotalPeriods, pPeriod->framesToTransfer));
121}
122
123/**
124 * Resets a stream period to its initial state.
125 *
126 * @param pPeriod Stream period to reset.
127 */
128void hdaStreamPeriodReset(PHDASTREAMPERIOD pPeriod)
129{
130 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
131
132 if (pPeriod->cIntPending)
133 LogRelMax(50, ("HDA: Warning: %RU8 interrupts for stream #%RU8 still pending -- so a period reset might trigger audio hangs\n",
134 pPeriod->cIntPending, pPeriod->u8SD));
135
136 pPeriod->fStatus &= ~HDASTREAMPERIOD_FLAG_ACTIVE;
137 pPeriod->u64StartWalClk = 0;
138 pPeriod->u64ElapsedWalClk = 0;
139 pPeriod->framesTransferred = 0;
140 pPeriod->cIntPending = 0;
141#ifdef DEBUG
142 pPeriod->Dbg.tsStartNs = 0;
143#endif
144}
145
146/**
147 * Begins a new period life span of a given period.
148 *
149 * @return IPRT status code.
150 * @param pPeriod Stream period to begin new life span for.
151 * @param u64WalClk Wall clock (WALCLK) value to set for the period's starting point.
152 */
153int hdaStreamPeriodBegin(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
154{
155 Assert(!(pPeriod->fStatus & HDASTREAMPERIOD_FLAG_ACTIVE)); /* No nested calls. */
156
157 pPeriod->fStatus |= HDASTREAMPERIOD_FLAG_ACTIVE;
158 pPeriod->u64StartWalClk = u64WalClk;
159 pPeriod->u64ElapsedWalClk = 0;
160 pPeriod->framesTransferred = 0;
161 pPeriod->cIntPending = 0;
162#ifdef DEBUG
163 pPeriod->Dbg.tsStartNs = RTTimeNanoTS();
164#endif
165
166 Log3Func(("[SD%RU8] Starting @ %RU64 (%RU64 long)\n",
167 pPeriod->u8SD, pPeriod->u64StartWalClk, pPeriod->u64DurationWalClk));
168
169 return VINF_SUCCESS;
170}
171
172/**
173 * Ends a formerly begun period life span.
174 *
175 * @param pPeriod Stream period to end life span for.
176 */
177void hdaStreamPeriodEnd(PHDASTREAMPERIOD pPeriod)
178{
179 Log3Func(("[SD%RU8] Took %zuus\n", pPeriod->u8SD, (RTTimeNanoTS() - pPeriod->Dbg.tsStartNs) / 1000));
180
181 if (!(pPeriod->fStatus & HDASTREAMPERIOD_FLAG_ACTIVE))
182 return;
183
184 /* Sanity. */
185 AssertMsg(pPeriod->cIntPending == 0,
186 ("%RU8 interrupts for stream #%RU8 still pending -- so ending a period might trigger audio hangs\n",
187 pPeriod->cIntPending, pPeriod->u8SD));
188 Assert(hdaStreamPeriodIsComplete(pPeriod));
189
190 pPeriod->fStatus &= ~HDASTREAMPERIOD_FLAG_ACTIVE;
191}
192
193/**
194 * Pauses a period. All values remain intact.
195 *
196 * @param pPeriod Stream period to pause.
197 */
198void hdaStreamPeriodPause(PHDASTREAMPERIOD pPeriod)
199{
200 AssertMsg((pPeriod->fStatus & HDASTREAMPERIOD_FLAG_ACTIVE), ("Period %p already in inactive state\n", pPeriod));
201
202 pPeriod->fStatus &= ~HDASTREAMPERIOD_FLAG_ACTIVE;
203
204 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
205}
206
207/**
208 * Resumes a formerly paused period.
209 *
210 * @param pPeriod Stream period to resume.
211 */
212void hdaStreamPeriodResume(PHDASTREAMPERIOD pPeriod)
213{
214 AssertMsg(!(pPeriod->fStatus & HDASTREAMPERIOD_FLAG_ACTIVE), ("Period %p already in active state\n", pPeriod));
215
216 pPeriod->fStatus |= HDASTREAMPERIOD_FLAG_ACTIVE;
217
218 Log3Func(("[SD%RU8]\n", pPeriod->u8SD));
219}
220
221/**
222 * Locks a stream period for serializing access.
223 *
224 * @return true if locking was successful, false if not.
225 * @param pPeriod Stream period to lock.
226 */
227bool hdaStreamPeriodLock(PHDASTREAMPERIOD pPeriod)
228{
229 return RT_SUCCESS(RTCritSectEnter(&pPeriod->CritSect));
230}
231
232/**
233 * Unlocks a formerly locked stream period.
234 *
235 * @param pPeriod Stream period to unlock.
236 */
237void hdaStreamPeriodUnlock(PHDASTREAMPERIOD pPeriod)
238{
239 int rc2 = RTCritSectLeave(&pPeriod->CritSect);
240 AssertRC(rc2);
241}
242
243/**
244 * Returns the wall clock (WALCLK) value for a given amount of stream period audio frames.
245 *
246 * @return Calculated wall clock value.
247 * @param pPeriod Stream period to calculate wall clock value for.
248 * @param uFrames Number of audio frames to calculate wall clock value for.
249 *
250 * @remark Calculation depends on the given stream period and assumes a 24 MHz wall clock counter (WALCLK).
251 */
252uint64_t hdaStreamPeriodFramesToWalClk(PHDASTREAMPERIOD pPeriod, uint32_t uFrames)
253{
254 /* Prevent division by zero. */
255 const uint32_t uHz = (pPeriod->u32Hz ? pPeriod->u32Hz : 1);
256
257 /* 24 MHz wall clock (WALCLK): 42ns resolution. */
258 return ASMMultU64ByU32DivByU32(uFrames, 24000000, uHz);
259}
260
261/**
262 * Returns the absolute wall clock (WALCLK) value for the already elapsed time of
263 * a given stream period.
264 *
265 * @return Absolute elapsed time as wall clock (WALCLK) value.
266 * @param pPeriod Stream period to use.
267 */
268uint64_t hdaStreamPeriodGetAbsElapsedWalClk(PHDASTREAMPERIOD pPeriod)
269{
270 return pPeriod->u64StartWalClk
271 + pPeriod->u64ElapsedWalClk
272 + pPeriod->i64DelayWalClk;
273}
274
275/**
276 * Returns the absolute wall clock (WALCLK) value for the calculated end time of
277 * a given stream period.
278 *
279 * @return Absolute end time as wall clock (WALCLK) value.
280 * @param pPeriod Stream period to use.
281 */
282uint64_t hdaStreamPeriodGetAbsEndWalClk(PHDASTREAMPERIOD pPeriod)
283{
284 return pPeriod->u64StartWalClk + pPeriod->u64DurationWalClk;
285}
286
287/**
288 * Returns the remaining audio frames to process for a given stream period.
289 *
290 * @return Number of remaining audio frames to process. 0 if all were processed.
291 * @param pPeriod Stream period to return value for.
292 */
293uint32_t hdaStreamPeriodGetRemainingFrames(PHDASTREAMPERIOD pPeriod)
294{
295 Assert(pPeriod->framesToTransfer >= pPeriod->framesTransferred);
296 return pPeriod->framesToTransfer - pPeriod->framesTransferred;
297}
298
299/**
300 * Tells whether a given stream period has elapsed (time-wise) or not.
301 *
302 * @return true if the stream period has elapsed, false if not.
303 * @param pPeriod Stream period to get status for.
304 */
305bool hdaStreamPeriodHasElapsed(PHDASTREAMPERIOD pPeriod)
306{
307 return (pPeriod->u64ElapsedWalClk >= pPeriod->u64DurationWalClk);
308}
309
310/**
311 * Tells whether a given stream period has passed the given absolute wall clock (WALCLK)
312 * time or not
313 *
314 * @return true if the stream period has passed the given time, false if not.
315 * @param pPeriod Stream period to get status for.
316 * @param u64WalClk Absolute wall clock (WALCLK) time to check for.
317 */
318bool hdaStreamPeriodHasPassedAbsWalClk(PHDASTREAMPERIOD pPeriod, uint64_t u64WalClk)
319{
320 /* Period not in use? */
321 if (!(pPeriod->fStatus & HDASTREAMPERIOD_FLAG_ACTIVE))
322 return true; /* ... implies that it has passed. */
323
324 if (hdaStreamPeriodHasElapsed(pPeriod))
325 return true; /* Period already has elapsed. */
326
327 return (pPeriod->u64StartWalClk + pPeriod->u64ElapsedWalClk) >= u64WalClk;
328}
329
330/**
331 * Tells whether a given stream period has some required interrupts pending or not.
332 *
333 * @return true if period has interrupts pending, false if not.
334 * @param pPeriod Stream period to get status for.
335 */
336bool hdaStreamPeriodNeedsInterrupt(PHDASTREAMPERIOD pPeriod)
337{
338 return pPeriod->cIntPending > 0;
339}
340
341/**
342 * Acquires (references) an (pending) interrupt for a given stream period.
343 *
344 * @param pPeriod Stream period to acquire interrupt for.
345 *
346 * @remark This routine does not do any actual interrupt processing; it only
347 * keeps track of the required (pending) interrupts for a stream period.
348 */
349void hdaStreamPeriodAcquireInterrupt(PHDASTREAMPERIOD pPeriod)
350{
351 uint32_t cIntPending = pPeriod->cIntPending;
352 if (cIntPending)
353 {
354 Log3Func(("[SD%RU8] Already pending\n", pPeriod->u8SD));
355 return;
356 }
357
358 pPeriod->cIntPending++;
359
360 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
361}
362
363/**
364 * Releases (dereferences) a pending interrupt.
365 *
366 * @param pPeriod Stream period to release pending interrupt for.
367 */
368void hdaStreamPeriodReleaseInterrupt(PHDASTREAMPERIOD pPeriod)
369{
370 Assert(pPeriod->cIntPending);
371 pPeriod->cIntPending--;
372
373 Log3Func(("[SD%RU8] %RU32\n", pPeriod->u8SD, pPeriod->cIntPending));
374}
375
376/**
377 * Adds an amount of (processed) audio frames to a given stream period.
378 *
379 * @return IPRT status code.
380 * @param pPeriod Stream period to add audio frames to.
381 * @param framesInc Audio frames to add.
382 */
383void hdaStreamPeriodInc(PHDASTREAMPERIOD pPeriod, uint32_t framesInc)
384{
385 pPeriod->framesTransferred += framesInc;
386 Assert(pPeriod->framesTransferred <= pPeriod->framesToTransfer);
387
388 pPeriod->u64ElapsedWalClk = hdaStreamPeriodFramesToWalClk(pPeriod, pPeriod->framesTransferred);
389 Assert(pPeriod->u64ElapsedWalClk <= pPeriod->u64DurationWalClk);
390
391 Log3Func(("[SD%RU8] cbTransferred=%RU32, u64ElapsedWalClk=%RU64\n",
392 pPeriod->u8SD, pPeriod->framesTransferred, pPeriod->u64ElapsedWalClk));
393}
394
395/**
396 * Tells whether a given stream period is considered as complete or not.
397 *
398 * @return true if stream period is complete, false if not.
399 * @param pPeriod Stream period to report status for.
400 *
401 * @remark A stream period is considered complete if it has 1) passed (elapsed) its calculated period time
402 * and 2) processed all required audio frames.
403 */
404bool hdaStreamPeriodIsComplete(PHDASTREAMPERIOD pPeriod)
405{
406 const bool fIsComplete = /* Has the period elapsed time-wise? */
407 hdaStreamPeriodHasElapsed(pPeriod)
408 /* All frames transferred? */
409 && pPeriod->framesTransferred >= pPeriod->framesToTransfer;
410#ifdef VBOX_STRICT
411 if (fIsComplete)
412 {
413 Assert(pPeriod->framesTransferred == pPeriod->framesToTransfer);
414 Assert(pPeriod->u64ElapsedWalClk == pPeriod->u64DurationWalClk);
415 }
416#endif
417
418 Log3Func(("[SD%RU8] Period %s - runtime %RU64 / %RU64 (abs @ %RU64, starts @ %RU64, ends @ %RU64), %RU8 IRQs pending\n",
419 pPeriod->u8SD,
420 fIsComplete ? "COMPLETE" : "NOT COMPLETE YET",
421 pPeriod->u64ElapsedWalClk, pPeriod->u64DurationWalClk,
422 hdaStreamPeriodGetAbsElapsedWalClk(pPeriod), pPeriod->u64StartWalClk,
423 hdaStreamPeriodGetAbsEndWalClk(pPeriod), pPeriod->cIntPending));
424
425 return fIsComplete;
426}
427#endif /* IN_RING3 */
428
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette