source: roaraudio/roard/driver_wmm.c @ 5056:b31e60545552

Last change on this file since 5056:b31e60545552 was 5056:b31e60545552, checked in by phi, 13 years ago

support auto reconf bits=32->16 in case not supported (See: #48)

File size: 8.7 KB
Line 
1//driver_wmm.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2011
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "roard.h"
27
28#ifdef ROAR_HAVE_LIBWINMM
29
30static int _alloc_wave_headers(struct driver_wmm * self) {
31  int bytesPerBlock = self->wavefmt.nBlockAlign * self->splPerBlock;
32  /*   int bytes = internal->blocks * (sizeof(WAVEHDR) + bytesPerBlock); */
33  int bytes = self->blocks * (sizeof(*self->wh) + bytesPerBlock);
34  int res;
35  MMRESULT mmres;
36
37  self->bigbuffer = roar_mm_malloc(bytes);
38  if (self->bigbuffer != NULL) {
39    int i;
40    BYTE * b;
41
42    memset(self->bigbuffer,0,bytes);
43    self->wh = self->bigbuffer;
44    self->spl = (LPBYTE) (self->wh+self->blocks);
45    for (i=0, b=self->spl; i<self->blocks; ++i, b+=bytesPerBlock) {
46      self->wh[i].data = b;
47      self->wh[i].wh.lpData = self->wh[i].data;
48      self->wh[i].length = bytesPerBlock;
49      self->wh[i].wh.dwBufferLength = self->wh[i].length;
50      self->wh[i].wh.dwUser = (DWORD_PTR)self;
51      mmres = waveOutPrepareHeader(self->hwo,
52                                   &self->wh[i].wh,sizeof(WAVEHDR));
53      if (MMSYSERR_NOERROR != mmres) {
54        break;
55      }
56    }
57    if (i<self->blocks) {
58      while (--i >= 0) {
59        waveOutUnprepareHeader(self->hwo,
60                               &self->wh[i].wh,sizeof(WAVEHDR));
61      }
62      roar_mm_free(self->bigbuffer);
63      self->wh        = 0;
64      self->spl       = 0;
65      self->bigbuffer = 0;
66    } else {
67      /* all ok ! */
68    }
69  }
70
71  res = (self->bigbuffer != NULL);
72  return res;
73}
74
75static int _get_free_block(struct driver_wmm * self);
76static int _wait_wave_headers(struct driver_wmm * self, int wait_all) {
77  int res = 1;
78
79  while (self->sent_blocks > 0) {
80    int n;
81    _get_free_block(self);
82    n = self->sent_blocks;
83    if (n > 0) {
84      unsigned int ms = (self->msPerBlock>>1)+1;
85      if (wait_all) ms *= n;
86      Sleep(ms);
87    }
88  }
89
90  res &= !self->sent_blocks;
91  return res;
92}
93
94static int _get_free_block(struct driver_wmm * self) {
95  const int idx = self->widx;
96  int ridx = self->ridx;
97
98  while (self->wh[ridx].sent && !!(self->wh[ridx].wh.dwFlags & WHDR_DONE)) {
99    /* block successfully sent to hardware, release it */
100    /*debug("_ao_get_free_block: release block %d\n",ridx);*/
101    self->wh[ridx].sent = 0;
102    self->wh[ridx].wh.dwFlags &= ~WHDR_DONE;
103
104    --self->full_blocks;
105    if (self->full_blocks<0) {
106      self->full_blocks = 0;
107    }
108
109    --self->sent_blocks;
110    if (self->sent_blocks<0) {
111      self->sent_blocks = 0;
112    }
113    if (++ridx >= self->blocks) ridx = 0;
114  }
115  self->ridx = ridx;
116
117  return self->wh[idx].sent
118    ? -1
119    : idx;
120}
121
122static int _free_wave_headers(struct driver_wmm * self) {
123  MMRESULT mmres;
124  int res = 1;
125
126  if (self->wh) {
127    int i;
128
129    /* Reset so we dont need to wait ... Just a satefy net
130     * since _ao_wait_wave_headers() has been called once before.
131     */
132    mmres = waveOutReset(self->hwo);
133    /* Wait again to be sure reseted waveheaders has been released. */
134    _wait_wave_headers(self, 0);
135
136    for (i=self->blocks; --i>=0; ) {
137      mmres = waveOutUnprepareHeader(self->hwo,
138                                     &self->wh[i].wh,sizeof(WAVEHDR));
139
140      res &= mmres == MMSYSERR_NOERROR;
141    }
142    self->wh  = 0;
143    self->spl = 0;
144  }
145
146  return res;
147}
148
149/* Send a block to audio hardware */
150static int _send_block(struct driver_wmm * self, const int idx) {
151  MMRESULT mmres;
152
153  /* Satanity checks */
154  if (self->wh[idx].sent) {
155    return 0;
156  }
157  if (!!(self->wh[idx].wh.dwFlags & WHDR_DONE)) {
158    return 0;
159  }
160
161  /* count <= 0, just pretend it's been sent */
162  if (self->wh[idx].count <= 0) {
163    self->wh[idx].sent = 2; /* set with 2 so we can track these special cases */
164    self->wh[idx].wh.dwFlags |= WHDR_DONE;
165    ++self->sent_blocks;
166    return 1;
167  }
168
169  self->wh[idx].wh.dwBufferLength = self->wh[idx].count;
170  self->wh[idx].count = 0;
171  mmres = waveOutWrite(self->hwo,
172                       &self->wh[idx].wh, sizeof(WAVEHDR));
173  self->wh[idx].sent = (mmres == MMSYSERR_NOERROR);
174  /*&& !(internal->wh[idx].wh.dwFlags & WHDR_DONE);*/
175  self->sent_blocks += self->wh[idx].sent;
176  return mmres == MMSYSERR_NOERROR;
177}
178
179int     driver_wmm_open_vio(struct roar_vio_calls * inst, char * device, struct roar_audio_info * info, int fh, struct roar_stream_server * sstream) {
180 struct driver_wmm * self;
181 WAVEFORMATEX wavefmt;
182 MMRESULT mmres;
183
184 if ( (self = roar_mm_malloc(sizeof(struct driver_wmm))) == NULL )
185  return -1;
186
187 memset(self, 0, sizeof(struct driver_wmm));
188
189 // VIO Setup:
190 memset(inst, 0, sizeof(struct roar_vio_calls));
191 inst->inst     = self;
192 inst->close    = driver_wmm_close_vio;
193 inst->write    = driver_wmm_write;
194 inst->nonblock = driver_dummy_nonblock;
195
196 // WMM Setup:
197 memset(&wavefmt, 0, sizeof(wavefmt));
198
199 info->codec = ROAR_CODEC_PCM_S_LE;
200
201 self->wavefmt.wFormatTag      = WAVE_FORMAT_PCM;
202 self->wavefmt.nChannels       = info->channels;
203 self->wavefmt.wBitsPerSample  = info->bits;
204 self->wavefmt.nSamplesPerSec  = info->rate;
205 self->wavefmt.nBlockAlign     = (self->wavefmt.wBitsPerSample>>3)*self->wavefmt.nChannels;
206 self->wavefmt.nAvgBytesPerSec = self->wavefmt.nSamplesPerSec*self->wavefmt.nBlockAlign;
207 self->wavefmt.cbSize          = 0;
208
209 /* $$$ later this should be optionnal parms */
210  self->id          = WAVE_MAPPER;
211  self->blocks      = 64;
212  self->splPerBlock = 512;
213  self->msPerBlock  =
214    (self->splPerBlock * 1000 + info->rate - 1) / info->rate;
215
216  mmres =
217    waveOutOpen(&(self->hwo),
218                self->id,
219                &(self->wavefmt),
220                (DWORD_PTR)0/* waveOutProc */,
221                (DWORD_PTR)self,
222                CALLBACK_NULL/* |WAVE_FORMAT_DIRECT */|WAVE_ALLOWSYNC);
223 if ( mmres != MMSYSERR_NOERROR ) {
224  driver_wmm_close_vio(inst);
225  return -1;
226 }
227
228 // FIXME: do error checking
229 waveOutGetID(self->hwo, &(self->id));
230
231 _alloc_wave_headers(self);
232
233 self->opened = 1;
234
235 ROAR_DBG("driver_wmm_open_vio(*) = 0");
236
237 return 0;
238}
239
240int     driver_wmm_close_vio(struct roar_vio_calls * vio) {
241 struct driver_wmm * self;
242
243 if ( vio == NULL )
244  return -1;
245
246 if ( (self = vio->inst) == NULL )
247  return -1;
248
249 if ( self->opened ) {
250  _wait_wave_headers(self, 1);
251  _free_wave_headers(self);
252  waveOutClose(self->hwo);
253 }
254
255 roar_mm_free(self);
256
257 ROAR_DBG("driver_wmm_close_vio(*) = 0");
258
259 return 0;
260}
261
262ssize_t driver_wmm_write(struct roar_vio_calls * vio, void *buf, size_t count) {
263 struct driver_wmm * self;
264 ssize_t ret_ok = count;
265 int ret = 1;
266
267 ROAR_DBG("driver_wmm_write(vio=%p, buf=%p, count=%lu) = ?", vio, buf, (unsigned long)count);
268
269 if ( vio == NULL )
270  return -1;
271
272 if ( (self = vio->inst) == NULL )
273  return -1;
274
275 if ( ! self->opened )
276  return -1;
277
278  while(ret && count > 0) {
279    int n;
280    const int idx = _get_free_block(self);
281
282    if (idx == -1) {
283      /* debug("sleep %dms, rem %d bytes\n",internal->msPerBlock,num_bytes); */
284      Sleep(self->msPerBlock);
285      continue;
286    }
287
288    /* Get free bytes in the block */
289    n = self->wh[idx].wh.dwBufferLength
290      - self->wh[idx].count;
291
292    /*     debug("free in block %d : %d/%d\n", */
293    /*    idx,n,internal->wh[idx].dwBufferLength); */
294
295    /* Get amount to copy */
296    if (n > (int)count) {
297      n = count;
298    }
299    /*     debug("copy = %d\n",n); */
300
301
302
303    /* Do copy */
304    CopyMemory((char*)self->wh[idx].wh.lpData
305               + self->wh[idx].count,
306               buf, n);
307
308    /* Updates pointers and counters */
309    buf += n;
310    count -= n;
311    /*     debug("rem = %d\n",num_bytes); */
312    self->wh[idx].count += n;
313
314    /* Is this block full ? */
315    if (self->wh[idx].count
316        == self->wh[idx].wh.dwBufferLength) {
317      ++self->full_blocks;
318      /*       debug("blocks %d full, total:%d\n",internal->widx,internal->full_blocks); */
319      if (++self->widx == self->blocks) {
320        self->widx = 0;
321      }
322      ret = _send_block(self, idx);
323    }
324  }
325
326 ROAR_DBG("driver_wmm_write(vio=%p, buf=%p, count=%lu): ret=%i", vio, buf, (unsigned long)count, ret);
327
328  /*   debug("ao_wmm_play => %d rem => [%s]\n",num_bytes,ret?"success":"error"); */
329  return ret > -1 ? ret_ok : -1;
330}
331
332#endif
333
334//ll
Note: See TracBrowser for help on using the repository browser.