source: roaraudio/plugins/alsa/thread.c @ 5157:065c4c7f1be2

Last change on this file since 5157:065c4c7f1be2 was 5157:065c4c7f1be2, checked in by phi, 13 years ago

made it build again

File size: 6.2 KB
Line 
1//thread.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2010-2011
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// Writes to the FIFO buffer. Waits until there is room to write.
45size_t roar_plugin_write(struct roar_alsa_pcm *self, const char *buf, size_t size) {
46 /* Wait until we have a ready buffer */
47 while (1) {
48  /* Should the thread be shut down while we're running, return with error */
49  if ( !self->thread_active )
50   return 0;
51
52  //fprintf(stderr, "%d + %d : %d\n", (int)self->bufptr, (int)size, (int)self->bufsize);
53  pthread_mutex_lock(&(self->lock));
54  if (self->bufptr + size <= self->bufsize) {
55   pthread_mutex_unlock(&(self->lock));
56   break;
57  }
58  pthread_mutex_unlock(&(self->lock));
59
60  pthread_cond_signal(&(self->cond));
61  /* Sleeps until we can write to the FIFO. */
62  pthread_mutex_lock(&(self->cond_lock));
63  pthread_cond_wait(&(self->cond), &(self->cond_lock));
64  pthread_mutex_unlock(&(self->cond_lock));
65 }
66
67 pthread_mutex_lock(&(self->lock));
68 memcpy(self->buffer + self->bufptr, buf, size);
69 self->bufptr += (int)size;
70 pthread_mutex_unlock(&(self->lock));
71
72 /* Send signal to thread that buffer has been updated */
73 pthread_cond_signal(&(self->cond));
74
75 return size;
76}
77
78#define _TEST_CANCEL() do { \
79 if ( !self->thread_active ) \
80  goto test_quit; \
81} while(0)
82
83// Attemps to drain the buffer at all times and write to libroar.
84// If there is no data, it will wait for roar_plugin_write() to fill up more data.
85void* roar_plugin_thread (void * thread_data) {
86 /* We share data between thread and callable functions */
87 struct roar_alsa_pcm *self = thread_data;
88 int rc;
89
90 /* Plays back data as long as there is data in the buffer. Else, sleep until it can. */
91 /* Two loops! :3 Beware! */
92 while(1) {
93  while(1) {
94   _TEST_CANCEL();
95   // We ask the server to send its latest backend data. Do not really care about errors atm.
96   // 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.
97
98   /* If the buffer is empty or we've stopped the stream. Jump out of this for loop */
99   pthread_mutex_lock(&(self->lock));
100   if ( self->bufptr < CHUNK_SIZE ) {
101    pthread_mutex_unlock(&(self->lock));
102    break;
103   }
104   pthread_mutex_unlock(&(self->lock));
105
106   rc = roar_vio_write(&(self->stream_vio), self->buffer, CHUNK_SIZE);
107
108   /* If this happens, we should make sure that subsequent and current calls to rsd_write() will fail. */
109   if ( rc < 0 ) {
110    _TEST_CANCEL();
111    roar_plugin_reset(self);
112
113    /* Wakes up a potentially sleeping fill_buffer() */
114    pthread_cond_signal(&(self->cond));
115
116    /* This thread will not be joined, so detach. */
117    pthread_detach(pthread_self());
118    pthread_exit(NULL);
119   }
120
121   if ( !self->has_written ) {
122    pthread_mutex_lock(&(self->lock));
123    clock_gettime(CLOCK_MONOTONIC, &(self->start_tv));
124    self->has_written = 1;
125    pthread_mutex_unlock(&(self->lock));
126   }
127
128   pthread_mutex_lock(&(self->lock));
129   self->total_written += rc;
130   pthread_mutex_unlock(&(self->lock));
131
132   /* "Drains" the buffer. This operation looks kinda expensive with large buffers, but hey. D: */
133   pthread_mutex_lock(&(self->lock));
134   memmove(self->buffer, self->buffer + rc, self->bufsize - rc);
135   self->bufptr -= rc;
136   pthread_mutex_unlock(&(self->lock));
137   ROAR_DBG("roar_plugin_thread(*): Wrote data to vio. New bufptr: %i", (int)self->bufptr);
138
139   /* Buffer has decreased, signal fill_buffer() */
140   pthread_cond_signal(&(self->cond));
141
142  }
143
144  /* If we're still good to go, sleep. We are waiting for fill_buffer() to fill up some data. */
145test_quit:
146  if ( self->thread_active ) {
147   pthread_cond_signal(&(self->cond));
148   pthread_mutex_lock(&(self->cond_lock));
149   pthread_cond_wait(&(self->cond), &(self->cond_lock));
150   pthread_mutex_unlock(&(self->cond_lock));
151  } else {
152   /* Abandon the ship, chap. */
153   pthread_cond_signal(&(self->cond));
154   pthread_exit(NULL);
155  }
156
157 }
158}
159
160void roar_plugin_drain(struct roar_alsa_pcm *self) {
161 struct timespec now_tv;
162 int64_t temp, temp2;
163
164 /* If the audio playback has started on the server we need to use timers. */
165 if ( self->has_written ) {
166
167  /* Falls back to gettimeofday() when CLOCK_MONOTONIC is not supported */
168
169  /* Calculates the amount of bytes that the server has consumed. */
170  clock_gettime(CLOCK_MONOTONIC, &now_tv);
171
172  temp   = (int64_t)now_tv.tv_sec - (int64_t)self->start_tv.tv_sec;
173
174  temp  *= self->info.rate * self->info.channels * self->info.bits / 8;
175
176  temp2  = (int64_t)now_tv.tv_nsec - (int64_t)self->start_tv.tv_nsec;
177  temp2 *= self->info.rate * self->info.channels * self->info.bits / 8;
178  temp2 /= 1000000000LL;
179  temp  += temp2;
180  /* Calculates the amount of data we have in our virtual buffer. Only used to calculate delay. */
181  self->bytes_in_buffer = (int)((int64_t)self->total_written + (int64_t)self->bufptr - temp);
182 } else {
183  self->bytes_in_buffer = self->bufptr;
184 }
185}
186
187//ll
Note: See TracBrowser for help on using the repository browser.