source: roaraudio/plugins/alsa/thread.c @ 5381:430b1d26e12d

Last change on this file since 5381:430b1d26e12d was 5381:430b1d26e12d, checked in by phi, 12 years ago

updated copyright years

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