source: roaraudio/roard/driver_wmm.c @ 3358:7f9d211148e0

Last change on this file since 3358:7f9d211148e0 was 2799:afea9a8fd5b5, checked in by phi, 15 years ago

test for the correct consts

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