source: roaraudio/plugins/alsavs/thread.c @ 5823:f9f70dbaa376

Last change on this file since 5823:f9f70dbaa376 was 5823:f9f70dbaa376, checked in by phi, 11 years ago

updated copyright

File size: 8.3 KB
Line 
1//thread.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2013
5 *      Copyright (C) Hans-Kristian 'maister' Arntzen - 2010
6 *
7 *  This file is part of libroar a part of RoarAudio,
8 *  a cross-platform sound system for both, home and professional use.
9 *  See README for details.
10 *
11 *  This file is free software; you can redistribute it and/or modify
12 *  it under the terms of the GNU General Public License version 3
13 *  as published by the Free Software Foundation.
14 *
15 *  libroar is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with this software; see the file COPYING.  If not, write to
22 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 *  Boston, MA 02110-1301, USA.
24 *
25 *  NOTE for everyone want's to change something and send patches:
26 *  read README and HACKING! There a addition information on
27 *  the license of this document you need to read before you send
28 *  any patches.
29 *
30 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
31 *  or libpulse*:
32 *  The libs libroaresd, libroararts and libroarpulse link this lib
33 *  and are therefore GPL. Because of this it may be illigal to use
34 *  them with any software that uses libesd, libartsc or libpulse*.
35 */
36
37//#define DEBUG
38
39#include "roar.h"
40#define CHUNK_SIZE 2048
41//#define CHUNK_SIZE 256
42
43
44
45/*
46 * The following function should be re-written:
47 * write in a loop untill all date is written,
48 * if vs write returns less than requested call the thread and ask it to iterate.
49 */
50
51// Writes to the FIFO buffer. Waits until there is room to write.
52size_t roar_plugin_write(struct roar_alsa_pcm *self, const char *buf, size_t size) {
53 ssize_t ret;
54
55 ROAR_DBG("roar_plugin_write(self=%p, buf=%p, size=%llu) = ?", self, buf, (long long unsigned int)size);
56
57#if 0
58 /* Wait until we have a ready buffer */
59 while (1) {
60  /* Should the thread be shut down while we're running, return with error */
61  if ( !self->thread_active )
62   return 0;
63
64  //fprintf(stderr, "%d + %d : %d\n", (int)self->bufptr, (int)size, (int)self->bufsize);
65  pthread_mutex_lock(&(self->lock));
66  if (roar_vs_get_avail_write(self->vss, NULL) >= (ssize_t)size) {
67   pthread_mutex_unlock(&(self->lock));
68   break;
69  }
70  pthread_mutex_unlock(&(self->lock));
71
72  ROAR_DBG("roar_plugin_write(self=%p, buf=%p, size=%llu): Send signal", self, buf, (long long unsigned int)size);
73  pthread_cond_signal(&(self->cond));
74  /* Sleeps until we can write to the FIFO. */
75  ROAR_DBG("roar_plugin_write(self=%p, buf=%p, size=%llu): Waiting for answer", self, buf, (long long unsigned int)size);
76  pthread_mutex_lock(&(self->cond_lock));
77  pthread_cond_wait(&(self->cond), &(self->cond_lock));
78  pthread_mutex_unlock(&(self->cond_lock));
79 }
80#endif
81
82
83 while (size) {
84  pthread_mutex_lock(&(self->lock));
85  ret = roar_vs_write(self->vss, buf, size, NULL);
86  pthread_mutex_unlock(&(self->lock));
87
88  if ( ret == -1 ) {
89   ROAR_WARN("roar_plugin_write(self=%p, buf=?, size=?): Can not write data: %s", self, roar_error2str(roar_error));
90   return -1;
91  }
92
93  size -= ret;
94  buf  += ret;
95
96  /* Send signal to thread that buffer has been updated */
97  ROAR_DBG("roar_plugin_write(self=%p, buf=%p, size=%llu): Send signal", self, buf, (long long unsigned int)size);
98  pthread_cond_signal(&(self->cond));
99 }
100
101 return size;
102}
103
104#define _TEST_CANCEL() do { \
105 if ( !self->thread_active ) \
106  goto test_quit; \
107} while(0)
108
109// Attemps to drain the buffer at all times and write to libroar.
110// If there is no data, it will wait for roar_plugin_write() to fill up more data.
111void * roar_plugin_thread (void * thread_data) {
112 /* We share data between thread and callable functions */
113 struct roar_alsa_pcm *self = thread_data;
114 int rc;
115 int err;
116
117 ROAR_DBG("roar_plugin_thread(thread_data=%p) = ?", thread_data);
118 pthread_mutex_unlock(&(self->lock)); // we are now up.
119
120 /* Plays back data as long as there is data in the buffer. Else, sleep until it can. */
121 /* Two loops! :3 Beware! */
122 while(1) {
123  ROAR_DBG("roar_plugin_thread(thread_data=%p): got woken up.", thread_data);
124  while(1) {
125   _TEST_CANCEL();
126   // We ask the server to send its latest backend data. Do not really care about errors atm.
127   // We only bother to check after 1 sec of audio has been played, as it might be quite inaccurate in the start of the stream.
128
129   // do wee need to lock before this?
130   pthread_mutex_lock(&(self->lock));
131   ROAR_DBG("roar_plugin_thread(thread_data=%p): Start iterate", thread_data);
132   rc = roar_vs_iterate(self->vss, ROAR_VS_WAIT, &err);
133   ROAR_DBG("roar_plugin_thread(thread_data=%p): Ended iterate", thread_data);
134   pthread_mutex_unlock(&(self->lock));
135
136   if ( rc == 0 ) {
137    break;
138   } else if ( rc < 0 ) {
139    _TEST_CANCEL();
140    roar_plugin_reset(self);
141
142    /* Wakes up a potentially sleeping fill_buffer() */
143    pthread_cond_signal(&(self->cond));
144
145    /* This thread will not be joined, so detach. */
146    pthread_detach(pthread_self());
147    pthread_exit(NULL);
148   }
149
150   pthread_cond_signal(&(self->cond));
151
152#if 0
153   /* If the buffer is empty or we've stopped the stream. Jump out of this for loop */
154   pthread_mutex_lock(&(self->lock));
155   if ( self->bufptr < CHUNK_SIZE ) {
156    pthread_mutex_unlock(&(self->lock));
157    break;
158   }
159   pthread_mutex_unlock(&(self->lock));
160
161   rc = roar_vio_write(&(self->stream_vio), self->buffer, CHUNK_SIZE);
162
163   /* If this happens, we should make sure that subsequent and current calls to rsd_write() will fail. */
164   if ( rc < 0 ) {
165    _TEST_CANCEL();
166    roar_plugin_reset(self);
167
168    /* Wakes up a potentially sleeping fill_buffer() */
169    pthread_cond_signal(&(self->cond));
170
171    /* This thread will not be joined, so detach. */
172    pthread_detach(pthread_self());
173    pthread_exit(NULL);
174   }
175
176   if ( !self->has_written ) {
177    pthread_mutex_lock(&(self->lock));
178    clock_gettime(CLOCK_MONOTONIC, &(self->start_tv));
179    self->has_written = 1;
180    pthread_mutex_unlock(&(self->lock));
181   }
182
183   pthread_mutex_lock(&(self->lock));
184   self->total_written += rc;
185   pthread_mutex_unlock(&(self->lock));
186
187   /* "Drains" the buffer. This operation looks kinda expensive with large buffers, but hey. D: */
188   pthread_mutex_lock(&(self->lock));
189   memmove(self->buffer, self->buffer + rc, self->bufsize - rc);
190   self->bufptr -= rc;
191   pthread_mutex_unlock(&(self->lock));
192   ROAR_DBG("roar_plugin_thread(*): Wrote data to vio. New bufptr: %i", (int)self->bufptr);
193
194   /* Buffer has decreased, signal fill_buffer() */
195   pthread_cond_signal(&(self->cond));
196#endif
197
198  }
199  ROAR_DBG("roar_plugin_thread(thread_data=%p): going to sleep again.", thread_data);
200
201  /* If we're still good to go, sleep. We are waiting for fill_buffer() to fill up some data. */
202test_quit:
203  if ( self->thread_active ) {
204   //struct timespec abstime = {0, 1000*1000*100};
205   pthread_cond_signal(&(self->cond));
206   ROAR_DBG("roar_plugin_thread(thread_data=%p): Wating for wakeup", thread_data);
207   pthread_mutex_lock(&(self->cond_lock));
208   pthread_cond_wait(&(self->cond), &(self->cond_lock));
209   pthread_mutex_unlock(&(self->cond_lock));
210  } else {
211   /* Abandon the ship, chap. */
212   pthread_cond_signal(&(self->cond));
213   pthread_exit(NULL);
214  }
215
216 }
217}
218
219void roar_plugin_drain(struct roar_alsa_pcm *self) {
220#if 0
221 struct timespec now_tv;
222 int64_t temp, temp2;
223
224 /* If the audio playback has started on the server we need to use timers. */
225 if ( self->has_written ) {
226
227  /* Falls back to gettimeofday() when CLOCK_MONOTONIC is not supported */
228
229  /* Calculates the amount of bytes that the server has consumed. */
230  clock_gettime(CLOCK_MONOTONIC, &now_tv);
231
232  temp   = (int64_t)now_tv.tv_sec - (int64_t)self->start_tv.tv_sec;
233
234  temp  *= self->info.rate * self->info.channels * self->info.bits / 8;
235
236  temp2  = (int64_t)now_tv.tv_nsec - (int64_t)self->start_tv.tv_nsec;
237  temp2 *= self->info.rate * self->info.channels * self->info.bits / 8;
238  temp2 /= 1000000000LL;
239  temp  += temp2;
240  /* Calculates the amount of data we have in our virtual buffer. Only used to calculate delay. */
241  self->bytes_in_buffer = (int)((int64_t)self->total_written + (int64_t)self->bufptr - temp);
242 } else {
243  self->bytes_in_buffer = self->bufptr;
244 }
245#endif
246 ROAR_DBG("roar_plugin_drain(self=%p) = (void)", self);
247}
248
249//ll
Note: See TracBrowser for help on using the repository browser.