2 * Sample Wine Driver for Advanced Linux Sound System (ALSA)
3 * Based on version <final> of the ALSA API
5 * Copyright 2007 - Maarten Lankhorst
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 /*======================================================================*
23 * Low level dsound input implementation *
24 *======================================================================*/
27 #include "wine/port.h"
39 #ifdef HAVE_SYS_IOCTL_H
40 # include <sys/ioctl.h>
42 #ifdef HAVE_SYS_MMAN_H
43 # include <sys/mman.h>
53 #include "wine/library.h"
54 #include "wine/unicode.h"
55 #include "wine/debug.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(dsalsa
);
61 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl
;
62 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl
;
64 struct IDsCaptureDriverImpl
67 const IDsCaptureDriverVtbl
*lpVtbl
;
70 /* IDsCaptureDriverImpl fields */
71 IDsCaptureDriverBufferImpl
* capture_buffer
;
75 struct IDsCaptureDriverBufferImpl
77 const IDsCaptureDriverBufferVtbl
*lpVtbl
;
79 IDsCaptureDriverImpl
* drv
;
81 CRITICAL_SECTION pcm_crst
;
82 LPBYTE mmap_buffer
, presented_buffer
;
83 DWORD mmap_buflen_bytes
, play_looping
, mmap_ofs_bytes
;
85 /* Note: snd_pcm_frames_to_bytes(This->pcm, mmap_buflen_frames) != mmap_buflen_bytes */
86 /* The actual buffer may differ in size from the wanted buffer size */
89 snd_pcm_hw_params_t
*hw_params
;
90 snd_pcm_sw_params_t
*sw_params
;
91 snd_pcm_uframes_t mmap_buflen_frames
, mmap_pos
;
95 /** Convert the position an application sees into a position ALSA sees */
96 static snd_pcm_uframes_t
fakepos_to_realpos(const IDsCaptureDriverBufferImpl
* This
, DWORD fakepos
)
98 snd_pcm_uframes_t realpos
;
99 if (fakepos
< This
->mmap_ofs_bytes
)
100 realpos
= This
->mmap_buflen_bytes
+ fakepos
- This
->mmap_ofs_bytes
;
101 else realpos
= fakepos
- This
->mmap_ofs_bytes
;
102 return snd_pcm_bytes_to_frames(This
->pcm
, realpos
) % This
->mmap_buflen_frames
;
106 /** Convert the position ALSA sees into a position an application sees */
107 static DWORD
realpos_to_fakepos(const IDsCaptureDriverBufferImpl
* This
, snd_pcm_uframes_t realpos
)
109 DWORD realposb
= snd_pcm_frames_to_bytes(This
->pcm
, realpos
);
110 return (realposb
+ This
->mmap_ofs_bytes
) % This
->mmap_buflen_bytes
;
113 /** Raw copy data, with buffer wrap around */
114 static void CopyDataWrap(const IDsCaptureDriverBufferImpl
* This
, LPBYTE dest
, DWORD fromwhere
, DWORD copylen
, DWORD buflen
)
116 DWORD remainder
= buflen
- fromwhere
;
117 if (remainder
>= copylen
)
119 CopyMemory(dest
, This
->mmap_buffer
+ fromwhere
, copylen
);
123 CopyMemory(dest
, This
->mmap_buffer
+ fromwhere
, remainder
);
124 copylen
-= remainder
;
125 CopyMemory(dest
, This
->mmap_buffer
, copylen
);
129 /** Copy data from the mmap buffer to backbuffer, taking into account all wraparounds that may occur */
130 static void CopyData(const IDsCaptureDriverBufferImpl
* This
, snd_pcm_uframes_t fromwhere
, snd_pcm_uframes_t len
)
132 DWORD dlen
= snd_pcm_frames_to_bytes(This
->pcm
, len
) % This
->mmap_buflen_bytes
;
135 DWORD ofs
= realpos_to_fakepos(This
, fromwhere
);
136 DWORD remainder
= This
->mmap_buflen_bytes
- ofs
;
139 DWORD realbuflen
= snd_pcm_frames_to_bytes(This
->pcm
, This
->mmap_buflen_frames
);
140 DWORD realofs
= snd_pcm_frames_to_bytes(This
->pcm
, fromwhere
);
142 if (remainder
>= dlen
)
144 CopyDataWrap(This
, This
->presented_buffer
+ ofs
, realofs
, dlen
, realbuflen
);
148 CopyDataWrap(This
, This
->presented_buffer
+ ofs
, realofs
, remainder
, realbuflen
);
150 CopyDataWrap(This
, This
->presented_buffer
, (realofs
+remainder
)%realbuflen
, dlen
, realbuflen
);
154 /** Fill buffers, for starting and stopping
155 * Alsa won't start playing until everything is filled up
156 * This also updates mmap_pos
158 * Returns: Amount of periods in use so snd_pcm_avail_update
159 * doesn't have to be called up to 4x in GetPosition()
161 static snd_pcm_uframes_t
CommitAll(IDsCaptureDriverBufferImpl
*This
, DWORD forced
)
163 const snd_pcm_channel_area_t
*areas
;
164 snd_pcm_uframes_t used
;
165 const snd_pcm_uframes_t commitahead
= This
->mmap_buflen_frames
;
167 used
= This
->mmap_buflen_frames
- snd_pcm_avail_update(This
->pcm
);
168 TRACE("%p needs to commit to %lu, used: %lu\n", This
, commitahead
, used
);
169 if (used
< commitahead
&& (forced
|| This
->play_looping
))
171 snd_pcm_uframes_t done
, putin
= commitahead
- used
;
172 snd_pcm_mmap_begin(This
->pcm
, &areas
, &This
->mmap_pos
, &putin
);
173 CopyData(This
, This
->mmap_pos
, putin
);
174 done
= snd_pcm_mmap_commit(This
->pcm
, This
->mmap_pos
, putin
);
175 This
->mmap_pos
+= done
;
177 putin
= commitahead
- used
;
179 if (This
->mmap_pos
== This
->mmap_buflen_frames
&& (snd_pcm_sframes_t
)putin
> 0 && This
->play_looping
)
181 snd_pcm_mmap_begin(This
->pcm
, &areas
, &This
->mmap_pos
, &putin
);
182 This
->mmap_ofs_bytes
+= snd_pcm_frames_to_bytes(This
->pcm
, This
->mmap_buflen_frames
);
183 This
->mmap_ofs_bytes
%= This
->mmap_buflen_bytes
;
184 CopyData(This
, This
->mmap_pos
, putin
);
185 done
= snd_pcm_mmap_commit(This
->pcm
, This
->mmap_pos
, putin
);
186 This
->mmap_pos
+= done
;
191 if (This
->mmap_pos
== This
->mmap_buflen_frames
)
193 This
->mmap_ofs_bytes
+= snd_pcm_frames_to_bytes(This
->pcm
, This
->mmap_buflen_frames
);
194 This
->mmap_ofs_bytes
%= This
->mmap_buflen_bytes
;
201 static void CheckXRUN(IDsCaptureDriverBufferImpl
* This
)
203 snd_pcm_state_t state
= snd_pcm_state(This
->pcm
);
204 snd_pcm_sframes_t delay
;
207 snd_pcm_hwsync(This
->pcm
);
208 snd_pcm_delay(This
->pcm
, &delay
);
209 if ( state
== SND_PCM_STATE_XRUN
)
211 err
= snd_pcm_prepare(This
->pcm
);
212 CommitAll(This
, FALSE
);
213 snd_pcm_start(This
->pcm
);
214 WARN("xrun occurred\n");
216 ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err
));
218 else if ( state
== SND_PCM_STATE_SUSPENDED
)
220 int err
= snd_pcm_resume(This
->pcm
);
221 TRACE("recovery from suspension occurred\n");
222 if (err
< 0 && err
!= -EAGAIN
){
223 err
= snd_pcm_prepare(This
->pcm
);
225 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err
));
228 else if ( state
!= SND_PCM_STATE_RUNNING
)
230 WARN("Unhandled state: %d\n", state
);
235 * Allocate the memory-mapped buffer for direct sound, and set up the
238 static int CreateMMAP(IDsCaptureDriverBufferImpl
* pdbi
)
240 snd_pcm_t
*pcm
= pdbi
->pcm
;
241 snd_pcm_format_t format
;
242 snd_pcm_uframes_t frames
, ofs
, avail
, psize
, boundary
;
243 unsigned int channels
, bits_per_sample
, bits_per_frame
;
245 const snd_pcm_channel_area_t
*areas
;
246 snd_pcm_hw_params_t
*hw_params
= pdbi
->hw_params
;
247 snd_pcm_sw_params_t
*sw_params
= pdbi
->sw_params
;
249 mmap_mode
= snd_pcm_type(pcm
);
251 if (mmap_mode
== SND_PCM_TYPE_HW
)
252 TRACE("mmap'd buffer is a direct hardware buffer.\n");
253 else if (mmap_mode
== SND_PCM_TYPE_DMIX
)
254 TRACE("mmap'd buffer is an ALSA dmix buffer\n");
256 TRACE("mmap'd buffer is an ALSA type %d buffer\n", mmap_mode
);
258 err
= snd_pcm_hw_params_get_period_size(hw_params
, &psize
, NULL
);
259 err
= snd_pcm_hw_params_get_format(hw_params
, &format
);
260 err
= snd_pcm_hw_params_get_buffer_size(hw_params
, &frames
);
261 err
= snd_pcm_hw_params_get_channels(hw_params
, &channels
);
262 bits_per_sample
= snd_pcm_format_physical_width(format
);
263 bits_per_frame
= bits_per_sample
* channels
;
265 if (TRACE_ON(dsalsa
))
266 ALSA_TraceParameters(hw_params
, NULL
, FALSE
);
268 TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n",
269 snd_pcm_format_name(format
), frames
, channels
, bits_per_sample
, bits_per_frame
);
271 pdbi
->mmap_buflen_frames
= frames
;
272 snd_pcm_sw_params_current(pcm
, sw_params
);
273 snd_pcm_sw_params_set_start_threshold(pcm
, sw_params
, 0);
274 snd_pcm_sw_params_get_boundary(sw_params
, &boundary
);
275 snd_pcm_sw_params_set_stop_threshold(pcm
, sw_params
, boundary
);
276 snd_pcm_sw_params_set_silence_threshold(pcm
, sw_params
, INT_MAX
);
277 snd_pcm_sw_params_set_silence_size(pcm
, sw_params
, 0);
278 snd_pcm_sw_params_set_avail_min(pcm
, sw_params
, 0);
279 snd_pcm_sw_params_set_xrun_mode(pcm
, sw_params
, SND_PCM_XRUN_NONE
);
280 err
= snd_pcm_sw_params(pcm
, sw_params
);
282 avail
= snd_pcm_avail_update(pcm
);
285 ERR("No buffer is available: %s.\n", snd_strerror(avail
));
286 return DSERR_GENERIC
;
288 err
= snd_pcm_mmap_begin(pcm
, &areas
, &ofs
, &avail
);
291 ERR("Can't map sound device for direct access: %s\n", snd_strerror(err
));
292 return DSERR_GENERIC
;
294 err
= snd_pcm_mmap_commit(pcm
, ofs
, 0);
295 pdbi
->mmap_buffer
= areas
->addr
;
297 TRACE("created mmap buffer of %ld frames (%d bytes) at %p\n",
298 frames
, pdbi
->mmap_buflen_bytes
, pdbi
->mmap_buffer
);
303 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_QueryInterface(PIDSCDRIVERBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
305 /* IDsCaptureDriverBufferImpl *This = (IDsCaptureDriverBufferImpl *)iface; */
306 FIXME("(): stub!\n");
307 return DSERR_UNSUPPORTED
;
310 static ULONG WINAPI
IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface
)
312 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
313 ULONG refCount
= InterlockedIncrement(&This
->ref
);
315 TRACE("(%p)->(ref before=%u)\n",This
, refCount
- 1);
320 static ULONG WINAPI
IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface
)
322 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
323 ULONG refCount
= InterlockedDecrement(&This
->ref
);
325 TRACE("(%p)->(ref before=%u)\n",This
, refCount
+ 1);
330 TRACE("mmap buffer %p destroyed\n", This
->mmap_buffer
);
332 This
->drv
->capture_buffer
= NULL
;
333 This
->pcm_crst
.DebugInfo
->Spare
[0] = 0;
334 DeleteCriticalSection(&This
->pcm_crst
);
336 snd_pcm_drop(This
->pcm
);
337 snd_pcm_close(This
->pcm
);
339 HeapFree(GetProcessHeap(), 0, This
->presented_buffer
);
340 HeapFree(GetProcessHeap(), 0, This
->sw_params
);
341 HeapFree(GetProcessHeap(), 0, This
->hw_params
);
342 HeapFree(GetProcessHeap(), 0, This
);
346 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Lock(PIDSCDRIVERBUFFER iface
, LPVOID
*ppvAudio1
,LPDWORD pdwLen1
,LPVOID
*ppvAudio2
,LPDWORD pdwLen2
, DWORD dwWritePosition
,DWORD dwWriteLen
, DWORD dwFlags
)
348 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
349 TRACE("(%p,%p,%p,%p,%p,%d,%d,0x%08x)\n", This
, ppvAudio1
, pdwLen1
, ppvAudio2
, pdwLen2
, dwWritePosition
, dwWriteLen
, dwFlags
);
352 *ppvAudio1
= (LPBYTE
)This
->presented_buffer
+ dwWritePosition
;
354 if (dwWritePosition
+ dwWriteLen
< This
->mmap_buflen_bytes
) {
356 *pdwLen1
= dwWriteLen
;
363 *pdwLen1
= This
->mmap_buflen_bytes
- dwWritePosition
;
365 *ppvAudio2
= This
->presented_buffer
;
367 *pdwLen2
= dwWriteLen
- (This
->mmap_buflen_bytes
- dwWritePosition
);
372 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Unlock(PIDSCDRIVERBUFFER iface
, LPVOID pvAudio1
,DWORD dwLen1
, LPVOID pvAudio2
,DWORD dwLen2
)
374 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
375 TRACE("(%p,%p,%d,%p,%d)\n",This
,pvAudio1
,dwLen1
,pvAudio2
,dwLen2
);
379 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_SetFormat(PIDSCDRIVERBUFFER iface
, LPWAVEFORMATEX pwfx
)
381 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
382 WINE_WAVEDEV
*wwi
= &WInDev
[This
->drv
->wDevID
];
383 snd_pcm_t
*pcm
= NULL
;
384 snd_pcm_hw_params_t
*hw_params
= This
->hw_params
;
385 snd_pcm_format_t format
= -1;
386 snd_pcm_uframes_t buffer_size
;
387 DWORD rate
= pwfx
->nSamplesPerSec
;
390 TRACE("(%p, %p)\n", iface
, pwfx
);
392 switch (pwfx
->wBitsPerSample
)
394 case 8: format
= SND_PCM_FORMAT_U8
; break;
395 case 16: format
= SND_PCM_FORMAT_S16_LE
; break;
396 case 24: format
= SND_PCM_FORMAT_S24_LE
; break;
397 case 32: format
= SND_PCM_FORMAT_S32_LE
; break;
398 default: FIXME("Unsupported bpp: %d\n", pwfx
->wBitsPerSample
); return DSERR_GENERIC
;
402 EnterCriticalSection(&This
->pcm_crst
);
404 err
= snd_pcm_open(&pcm
, wwi
->pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
408 if (errno
!= EBUSY
|| !This
->pcm
)
411 LeaveCriticalSection(&This
->pcm_crst
);
412 WARN("Cannot open sound device: %s\n", snd_strerror(err
));
413 return DSERR_GENERIC
;
415 snd_pcm_drop(This
->pcm
);
416 snd_pcm_close(This
->pcm
);
418 err
= snd_pcm_open(&pcm
, wwi
->pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
422 LeaveCriticalSection(&This
->pcm_crst
);
423 WARN("Cannot open sound device: %s\n", snd_strerror(err
));
424 return DSERR_BUFFERLOST
;
428 /* Set some defaults */
429 snd_pcm_hw_params_any(pcm
, hw_params
);
430 err
= snd_pcm_hw_params_set_channels(pcm
, hw_params
, pwfx
->nChannels
);
431 if (err
< 0) { WARN("Could not set channels to %d\n", pwfx
->nChannels
); goto err
; }
433 err
= snd_pcm_hw_params_set_format(pcm
, hw_params
, format
);
434 if (err
< 0) { WARN("Could not set format to %d bpp\n", pwfx
->wBitsPerSample
); goto err
; }
436 err
= snd_pcm_hw_params_set_rate_near(pcm
, hw_params
, &rate
, NULL
);
437 if (err
< 0) { rate
= pwfx
->nSamplesPerSec
; WARN("Could not set rate\n"); goto err
; }
439 if (!ALSA_NearMatch(rate
, pwfx
->nSamplesPerSec
))
441 WARN("Could not set sound rate to %d, but instead to %d\n", pwfx
->nSamplesPerSec
, rate
);
442 pwfx
->nSamplesPerSec
= rate
;
443 pwfx
->nAvgBytesPerSec
= rate
* pwfx
->nBlockAlign
;
444 /* Let DirectSound detect this */
447 snd_pcm_hw_params_set_periods_integer(pcm
, hw_params
);
448 buffer_size
= snd_pcm_bytes_to_frames(pcm
, This
->mmap_buflen_bytes
);
449 snd_pcm_hw_params_set_buffer_size_near(pcm
, hw_params
, &buffer_size
);
451 snd_pcm_hw_params_set_period_time_near(pcm
, hw_params
, (unsigned int*)&buffer_size
, NULL
);
452 err
= snd_pcm_hw_params(pcm
, hw_params
);
453 err
= snd_pcm_sw_params(pcm
, This
->sw_params
);
454 snd_pcm_prepare(pcm
);
458 snd_pcm_drop(This
->pcm
);
459 snd_pcm_close(This
->pcm
);
463 snd_pcm_prepare(This
->pcm
);
467 LeaveCriticalSection(&This
->pcm_crst
);
472 WARN("Failed to apply changes: %s\n", snd_strerror(err
));
480 snd_pcm_hw_params_current(This
->pcm
, This
->hw_params
);
483 LeaveCriticalSection(&This
->pcm_crst
);
484 return DSERR_BADFORMAT
;
487 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER iface
, LPDWORD lpdwCappos
, LPDWORD lpdwReadpos
)
489 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
490 snd_pcm_uframes_t hw_pptr
, hw_wptr
;
492 EnterCriticalSection(&This
->pcm_crst
);
496 FIXME("Bad pointer for pcm: %p\n", This
->pcm
);
497 LeaveCriticalSection(&This
->pcm_crst
);
498 return DSERR_GENERIC
;
501 if (snd_pcm_state(This
->pcm
) != SND_PCM_STATE_RUNNING
)
503 hw_pptr
= This
->mmap_pos
;
508 /* FIXME: Unused at the moment */
509 snd_pcm_uframes_t used
= CommitAll(This
, FALSE
);
511 if (This
->mmap_pos
> used
)
512 hw_pptr
= This
->mmap_pos
- used
;
514 hw_pptr
= This
->mmap_buflen_frames
- used
+ This
->mmap_pos
;
516 hw_wptr
= This
->mmap_pos
;
519 *lpdwCappos
= realpos_to_fakepos(This
, hw_wptr
);
521 *lpdwReadpos
= realpos_to_fakepos(This
, hw_wptr
);
523 LeaveCriticalSection(&This
->pcm_crst
);
525 TRACE("hw_pptr=0x%08x, hw_wptr=0x%08x playpos=%d, writepos=%d\n", (unsigned int)hw_pptr
, (unsigned int)hw_wptr
, lpdwCappos
?*lpdwCappos
:-1, lpdwReadpos
?*lpdwReadpos
:-1);
529 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetStatus(PIDSCDRIVERBUFFER iface
, LPDWORD lpdwStatus
)
531 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
532 snd_pcm_state_t state
;
533 TRACE("(%p,%p)\n",iface
,lpdwStatus
);
535 state
= snd_pcm_state(This
->pcm
);
538 case SND_PCM_STATE_XRUN
:
539 case SND_PCM_STATE_SUSPENDED
:
540 case SND_PCM_STATE_RUNNING
:
541 *lpdwStatus
= DSCBSTATUS_CAPTURING
| (This
->play_looping
? DSCBSTATUS_LOOPING
: 0);
548 TRACE("State: %d, flags: 0x%08x\n", state
, *lpdwStatus
);
552 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Start(PIDSCDRIVERBUFFER iface
, DWORD dwFlags
)
554 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
555 TRACE("(%p,%x)\n",iface
,dwFlags
);
558 EnterCriticalSection(&This
->pcm_crst
);
559 snd_pcm_start(This
->pcm
);
560 This
->play_looping
= !!(dwFlags
& DSCBSTART_LOOPING
);
561 if (!This
->play_looping
)
562 /* Not well supported because of the difference in ALSA size and DSOUND's notion of size
563 * what it does right now is fill the buffer once.. ALSA size */
564 FIXME("Non-looping buffers are not properly supported!\n");
565 CommitAll(This
, TRUE
);
567 LeaveCriticalSection(&This
->pcm_crst
);
571 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface
)
573 IDsCaptureDriverBufferImpl
*This
= (IDsCaptureDriverBufferImpl
*)iface
;
574 TRACE("(%p)\n",iface
);
577 EnterCriticalSection(&This
->pcm_crst
);
578 This
->play_looping
= FALSE
;
579 snd_pcm_drop(This
->pcm
);
580 snd_pcm_prepare(This
->pcm
);
582 LeaveCriticalSection(&This
->pcm_crst
);
586 static const IDsCaptureDriverBufferVtbl dsdbvt
=
588 IDsCaptureDriverBufferImpl_QueryInterface
,
589 IDsCaptureDriverBufferImpl_AddRef
,
590 IDsCaptureDriverBufferImpl_Release
,
591 IDsCaptureDriverBufferImpl_Lock
,
592 IDsCaptureDriverBufferImpl_Unlock
,
593 IDsCaptureDriverBufferImpl_SetFormat
,
594 IDsCaptureDriverBufferImpl_GetPosition
,
595 IDsCaptureDriverBufferImpl_GetStatus
,
596 IDsCaptureDriverBufferImpl_Start
,
597 IDsCaptureDriverBufferImpl_Stop
600 static HRESULT WINAPI
IDsCaptureDriverImpl_QueryInterface(PIDSCDRIVER iface
, REFIID riid
, LPVOID
*ppobj
)
602 /* IDsCaptureDriverImpl *This = (IDsCaptureDriverImpl *)iface; */
603 FIXME("(%p): stub!\n",iface
);
604 return DSERR_UNSUPPORTED
;
607 static ULONG WINAPI
IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface
)
609 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
610 ULONG refCount
= InterlockedIncrement(&This
->ref
);
612 TRACE("(%p)->(ref before=%u)\n",This
, refCount
- 1);
617 static ULONG WINAPI
IDsCaptureDriverImpl_Release(PIDSCDRIVER iface
)
619 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
620 ULONG refCount
= InterlockedDecrement(&This
->ref
);
622 TRACE("(%p)->(ref before=%u)\n",This
, refCount
+ 1);
627 HeapFree(GetProcessHeap(), 0, This
);
631 static HRESULT WINAPI
IDsCaptureDriverImpl_GetDriverDesc(PIDSCDRIVER iface
, PDSDRIVERDESC pDesc
)
633 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
634 TRACE("(%p,%p)\n",iface
,pDesc
);
635 memcpy(pDesc
, &(WInDev
[This
->wDevID
].ds_desc
), sizeof(DSDRIVERDESC
));
637 pDesc
->dnDevNode
= WInDev
[This
->wDevID
].waveDesc
.dnDevNode
;
639 pDesc
->wReserved
= 0;
640 pDesc
->ulDeviceNum
= This
->wDevID
;
641 pDesc
->dwHeapType
= DSDHEAP_NOHEAP
;
642 pDesc
->pvDirectDrawHeap
= NULL
;
643 pDesc
->dwMemStartAddress
= 0xDEAD0000;
644 pDesc
->dwMemEndAddress
= 0xDEAF0000;
645 pDesc
->dwMemAllocExtra
= 0;
646 pDesc
->pvReserved1
= NULL
;
647 pDesc
->pvReserved2
= NULL
;
651 static HRESULT WINAPI
IDsCaptureDriverImpl_Open(PIDSCDRIVER iface
)
654 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
656 snd_pcm_t
*pcm
= NULL
;
657 snd_pcm_hw_params_t
*hw_params
;
659 /* While this is not really needed, it is a good idea to do this,
660 * to see if sound can be initialized */
662 hw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_hw_params_sizeof());
665 hr
= DSERR_OUTOFMEMORY
;
666 WARN("--> %08x\n", hr
);
670 err
= snd_pcm_open(&pcm
, WOutDev
[This
->wDevID
].pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
671 if (err
< 0) goto err
;
672 err
= snd_pcm_hw_params_any(pcm
, hw_params
);
673 if (err
< 0) goto err
;
674 err
= snd_pcm_hw_params_set_access (pcm
, hw_params
, SND_PCM_ACCESS_MMAP_INTERLEAVED
);
675 if (err
< 0) goto err
;
679 HeapFree(GetProcessHeap(), 0, hw_params
);
684 WARN("Failed to open device: %s\n", snd_strerror(err
));
687 HeapFree(GetProcessHeap(), 0, hw_params
);
688 WARN("--> %08x\n", hr
);
692 static HRESULT WINAPI
IDsCaptureDriverImpl_Close(PIDSCDRIVER iface
)
694 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
695 TRACE("(%p) stub, harmless\n",This
);
699 static HRESULT WINAPI
IDsCaptureDriverImpl_GetCaps(PIDSCDRIVER iface
, PDSCDRIVERCAPS pCaps
)
701 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
702 WINE_WAVEDEV
*wwi
= &WInDev
[This
->wDevID
];
703 TRACE("(%p,%p)\n",iface
,pCaps
);
704 pCaps
->dwSize
= sizeof(DSCDRIVERCAPS
);
705 pCaps
->dwFlags
= wwi
->ds_caps
.dwFlags
;
706 pCaps
->dwFormats
= wwi
->incaps
.dwFormats
;
707 pCaps
->dwChannels
= wwi
->incaps
.wChannels
;
711 static HRESULT WINAPI
IDsCaptureDriverImpl_CreateCaptureBuffer(PIDSCDRIVER iface
,
713 DWORD dwFlags
, DWORD dwCardAddress
,
714 LPDWORD pdwcbBufferSize
,
718 IDsCaptureDriverImpl
*This
= (IDsCaptureDriverImpl
*)iface
;
719 IDsCaptureDriverBufferImpl
** ippdsdb
= (IDsCaptureDriverBufferImpl
**)ppvObj
;
722 TRACE("(%p,%p,%x,%x)\n",iface
,pwfx
,dwFlags
,dwCardAddress
);
724 if (This
->capture_buffer
)
725 return DSERR_ALLOCATED
;
727 This
->capture_buffer
= *ippdsdb
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IDsCaptureDriverBufferImpl
));
728 if (*ippdsdb
== NULL
)
729 return DSERR_OUTOFMEMORY
;
731 (*ippdsdb
)->hw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_hw_params_sizeof());
732 (*ippdsdb
)->sw_params
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, snd_pcm_sw_params_sizeof());
733 (*ippdsdb
)->presented_buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, *pdwcbBufferSize
);
734 if (!(*ippdsdb
)->hw_params
|| !(*ippdsdb
)->sw_params
|| !(*ippdsdb
)->presented_buffer
)
736 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->sw_params
);
737 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->hw_params
);
738 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->presented_buffer
);
739 return DSERR_OUTOFMEMORY
;
741 (*ippdsdb
)->lpVtbl
= &dsdbvt
;
743 (*ippdsdb
)->drv
= This
;
744 (*ippdsdb
)->mmap_buflen_bytes
= *pdwcbBufferSize
;
745 InitializeCriticalSection(&(*ippdsdb
)->pcm_crst
);
746 (*ippdsdb
)->pcm_crst
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": ALSA_DSCAPTURE.pcm_crst");
748 /* SetFormat initialises pcm */
749 err
= IDsDriverBuffer_SetFormat((IDsDriverBuffer
*)*ppvObj
, pwfx
);
752 WARN("Error occurred: %08x\n", err
);
755 *ppbBuffer
= (*ippdsdb
)->presented_buffer
;
757 /* buffer is ready to go */
758 TRACE("buffer created at %p\n", *ippdsdb
);
762 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->presented_buffer
);
763 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->sw_params
);
764 HeapFree(GetProcessHeap(), 0, (*ippdsdb
)->hw_params
);
765 HeapFree(GetProcessHeap(), 0, *ippdsdb
);
770 static const IDsCaptureDriverVtbl dscdvt
=
772 IDsCaptureDriverImpl_QueryInterface
,
773 IDsCaptureDriverImpl_AddRef
,
774 IDsCaptureDriverImpl_Release
,
775 IDsCaptureDriverImpl_GetDriverDesc
,
776 IDsCaptureDriverImpl_Open
,
777 IDsCaptureDriverImpl_Close
,
778 IDsCaptureDriverImpl_GetCaps
,
779 IDsCaptureDriverImpl_CreateCaptureBuffer
782 /**************************************************************************
783 * widDsCreate [internal]
785 DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
)
787 IDsCaptureDriverImpl
** idrv
= (IDsCaptureDriverImpl
**)drv
;
788 TRACE("(%d,%p)\n",wDevID
,drv
);
790 if (!(WInDev
[wDevID
].dwSupport
& WAVECAPS_DIRECTSOUND
))
792 WARN("Hardware accelerated capture not supported, falling back to wavein\n");
793 return MMSYSERR_NOTSUPPORTED
;
796 *idrv
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsCaptureDriverImpl
));
798 return MMSYSERR_NOMEM
;
799 (*idrv
)->lpVtbl
= &dscdvt
;
802 (*idrv
)->wDevID
= wDevID
;
803 return MMSYSERR_NOERROR
;
806 /**************************************************************************
807 * widDsDesc [internal]
809 DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
811 memcpy(desc
, &(WInDev
[wDevID
].ds_desc
), sizeof(DSDRIVERDESC
));
812 return MMSYSERR_NOERROR
;
815 #endif /* HAVE_ALSA */