source: roaraudio/plugins/alsavs/thread.c @ 4708:c9d40761088a

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

updated copyright statements

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