split play.c and rename para_play to para_write
[paraslash.git] / write.c
1 /*
2  * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */
18
19 #include "para.h"
20 #include "string.h"
21 #include "write.cmdline.h"
22 #include "write.h"
23 #include "write_common.h"
24 #include "fd.h"
25
26 #include <sys/time.h> /* gettimeofday */
27
28 #include "error.h"
29
30 #define WAV_HEADER_LEN 44
31
32 static unsigned char *audiobuf;
33 static struct timeval *start_time;
34 struct gengetopt_args_info conf;
35
36 INIT_WRITE_ERRLISTS;
37
38 void para_log(int ll, const char* fmt,...)
39 {
40         va_list argp;
41
42         if (ll < conf.loglevel_arg)
43                 return;
44         va_start(argp, fmt);
45         vfprintf(stderr, fmt, argp);
46         va_end(argp);
47 }
48
49 /**
50  * read WAV_HEADER_LEN bytes from stdin to audio buffer
51  *
52  * \return -E_READ_HDR on errors and on eof before WAV_HEADER_LEN could be
53  * read. A positive return value indicates success.
54  */
55 static int read_wav_header(void)
56 {
57         ssize_t ret, count = 0;
58
59         while (count < WAV_HEADER_LEN) {
60                 ret = read(STDIN_FILENO, audiobuf + count, WAV_HEADER_LEN - count);
61                 if (ret <= 0)
62                         return -E_READ_HDR;
63                 count += ret;
64         }
65         return 1;
66 }
67
68 /**
69  * check if current time is later than start_time
70  * \param diff pointer to write remaining time to
71  *
72  * If start_time was not given, or current time is later than given
73  * start_time, return 0. Otherwise, return 1 and write the time
74  * difference between current time and start_time to diff. diff may be
75  * NULL.
76  *
77  */
78 static int start_time_in_future(struct timeval *diff)
79 {
80         struct timeval now;
81
82         if (!conf.start_time_given)
83                 return 0;
84         gettimeofday(&now, NULL);
85         return tv_diff(start_time, &now, diff) > 0? 1 : 0;
86 }
87
88 /**
89  * sleep until time given at command line
90  *
91  * This is called if the initial buffer is filled. It returns
92  * immediately if no start_time was given at the command line
93  * or if the given start time is in the past.
94  *
95  */
96 static void do_initial_delay(struct timeval *delay)
97 {
98         do
99                 para_select(1, NULL, NULL, delay);
100         while (start_time_in_future(delay));
101 }
102
103 static int read_stdin(char *buf, size_t bytes_to_load, size_t *loaded)
104 {
105         ssize_t ret;
106
107         while (*loaded < bytes_to_load) {
108                 ret = read(STDIN_FILENO, buf + *loaded, bytes_to_load - *loaded);
109                 if (ret <= 0) {
110                         if (ret < 0)
111                                 ret = -E_READ_STDIN;
112                         return ret;
113                 }
114                 *loaded += ret;
115         }
116         return 1;
117 }
118 /**
119  * play raw pcm data
120  * \param loaded number of bytes already loaded
121  *
122  * If start_time was given, prebuffer data until buffer is full or
123  * start_time is reached. In any case, do not start playing before
124  * start_time.
125  *
126  * \return positive on success, negative on errors.
127  */
128 static int pcm_write(struct writer_node_group *wng, size_t loaded)
129 {
130         size_t bufsize, prebuf_size, bytes_to_load;
131         struct timeval delay;
132         int ret, not_yet_started = 1;
133
134         ret = wng_open(wng);
135         if (ret < 0)
136                 goto out;
137         PARA_INFO_LOG("max chunk_bytes: %d\n", wng->max_chunk_bytes);
138         bufsize = (conf.bufsize_arg * 1024 / wng->max_chunk_bytes)
139                 * wng->max_chunk_bytes;
140         audiobuf = para_realloc(audiobuf, bufsize);
141         prebuf_size = conf.prebuffer_arg * bufsize / 100;
142         bytes_to_load =  PARA_MAX(prebuf_size, wng->max_chunk_bytes);
143         ret = read_stdin(audiobuf, bytes_to_load, &loaded);
144         if (ret <= 0 || loaded < bytes_to_load) {
145                 if (ret >= 0)
146                         ret = -E_PREMATURE_END;
147                 goto out;
148         }
149         if (not_yet_started && start_time && start_time_in_future(&delay))
150                 do_initial_delay(&delay);
151         not_yet_started = 0;
152 again:
153         ret = wng_write(wng, audiobuf, &loaded);
154         if (ret <= 0)
155                 goto out;
156         ret = -E_WRITE_OVERRUN;
157         if (loaded >= bufsize)
158                 goto out;
159         bytes_to_load = PARA_MIN(wng->max_chunk_bytes, bufsize);
160         ret = read_stdin(audiobuf, bytes_to_load, &loaded);
161         if (ret < 0)
162                 goto out;
163         if (!ret)
164                 wng->eof = 1;
165         goto again;
166 out:
167         wng_close(wng);
168         return ret;
169 }
170 /* write.c */
171
172 struct writer_node_group *check_args(void)
173 {
174         int i, ret = -E_WRITE_SYNTAX;
175         static struct timeval tv;
176         struct writer_node_group *wng = NULL;
177
178         if (conf.list_writers_given) {
179                 char *msg = NULL;
180                 FOR_EACH_WRITER(i) {
181                         char *tmp = make_message("%s%s%s",
182                                 i? msg : "",
183                                 i? " " : "",
184                                 writer_names[i]);
185                         free(msg);
186                         msg = tmp;
187                 }
188                 fprintf(stderr, "%s\n", msg);
189                 free(msg);
190                 exit(EXIT_SUCCESS);
191         }
192         if (conf.prebuffer_arg < 0 || conf.prebuffer_arg > 100)
193                 goto out;
194         if (conf.start_time_given) {
195                 long unsigned sec, usec;
196                 if (sscanf(conf.start_time_arg, "%lu:%lu",
197                                 &sec, &usec) != 2)
198                         goto out;
199                 tv.tv_sec = sec;
200                 tv.tv_usec = usec;
201                 start_time = &tv;
202         }
203         if (!conf.writer_given) {
204                 wng = setup_default_wng();
205                 ret = 1;
206                 goto out;
207         }
208         wng = wng_new(conf.writer_given);
209         for (i = 0; i < conf.writer_given; i++) {
210                 ret = check_writer_arg(conf.writer_arg[i]);
211                 if (ret < 0)
212                         goto out;
213                 wng->writer_nodes[i].writer = &writers[ret];
214         }
215         ret = 1;
216 out:
217         if (ret > 0)
218                 return wng;
219         free(wng);
220         return NULL;
221 }
222
223 /**
224  * test if audio buffer contains a valid wave header
225  *
226  * \return If not, return 0, otherwise, store number of channels and sample rate
227  * in struct conf and return WAV_HEADER_LEN.
228  */
229 static size_t check_wave(void)
230 {
231         unsigned char *a = audiobuf;
232         if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F')
233                 return WAV_HEADER_LEN;
234         conf.channels_arg = (unsigned) a[22];
235         conf.sample_rate_arg = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
236         return 0;
237 }
238
239 int main(int argc, char *argv[])
240 {
241         int ret = -E_WRITE_SYNTAX;
242         struct writer_node_group *wng = NULL;
243
244         cmdline_parser(argc, argv, &conf);
245         wng = check_args();
246         if (!wng)
247                 goto out;
248         init_supported_writers();
249         audiobuf = para_malloc(WAV_HEADER_LEN);
250         ret = read_wav_header();
251         if (ret < 0)
252                 goto out;
253         ret = pcm_write(wng, check_wave());
254 out:
255         wng_destroy(wng);
256         free(audiobuf);
257         if (ret < 0)
258                 PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
259         return ret;
260 }