compress.c: Simplify volume adjusting code.
[paraslash.git] / write.c
1 /*
2  * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file write.c Paraslash's standalone wav/raw player. */
8
9 #include <sys/types.h>
10 #include <dirent.h>
11
12 #include "para.h"
13 #include "string.h"
14 #include "write.cmdline.h"
15 #include "list.h"
16 #include "sched.h"
17 #include "stdin.h"
18 #include "write.h"
19 #include "write_common.h"
20 #include "fd.h"
21 #include "error.h"
22
23 INIT_WRITE_ERRLISTS;
24
25 /** Check if given buffer contains a valid wave header. */
26 struct check_wav_task {
27         /** The buffer to check. */
28         char *buf;
29         /** Number of bytes loaded in \a buf. */
30         size_t *loaded;
31         /** Non-zero if an error occurred or end of file was reached. */
32         int *error;
33         /** Number of channels specified in wav header given by \a buf. */
34         unsigned channels;
35         /** Sample rate specified in wav header given by \a buf. */
36         unsigned samplerate;
37         /** The task structure for this task. */
38         struct task task;
39 };
40
41 /** Delay writing until given time. */
42 struct initial_delay_task {
43         /** The time the first data should be written out. */
44         struct timeval start_time;
45         /** The task structure for this task. */
46         struct task task;
47 };
48
49 static struct write_args_info conf;
50 static struct stdin_task sit;
51 static struct check_wav_task cwt;
52 static struct initial_delay_task idt;
53 static struct writer_node_group *wng;
54
55 /** Length of a standard wav header. */
56 #define WAV_HEADER_LEN 44
57
58 /**
59  * Test if audio buffer contains a valid wave header.
60  *
61  * \return If not, return -E_NO_WAV_HEADER, otherwise, return zero. If
62  * there is less than WAV_HEADER_LEN bytes available, return one.
63  */
64 static void check_wav_pre_select(__a_unused struct sched *s, struct task *t)
65 {
66         struct check_wav_task *wt = t->private_data;
67         unsigned char *a;
68
69         if (*wt->loaded < WAV_HEADER_LEN) {
70                 t->ret = *wt->error? -E_PREMATURE_END : 1;
71                 return;
72         }
73         wt->channels = 2;
74         wt->samplerate = 44100;
75         a = (unsigned char*)wt->buf;
76         t->ret = -E_NO_WAV_HEADER;
77         if (a[0] != 'R' || a[1] != 'I' || a[2] != 'F' || a[3] != 'F')
78                 return;
79         wt->channels = (unsigned) a[22];
80         wt->samplerate = a[24] + (a[25] << 8) + (a[26] << 16) + (a[27] << 24);
81         *wt->loaded -= WAV_HEADER_LEN;
82         memmove(wt->buf, wt->buf + WAV_HEADER_LEN, *wt->loaded);
83         t->ret = -E_WAV_HEADER_SUCCESS;
84         PARA_INFO_LOG("channels: %d, sample rate: %d\n", wt->channels, wt->samplerate);
85 }
86
87 static void initial_delay_pre_select(struct sched *s, struct task *t)
88 {
89         struct initial_delay_task *dt = t->private_data;
90         struct timeval diff;
91
92         t->ret = -E_NO_DELAY;
93         if (!dt->start_time.tv_sec && !dt->start_time.tv_usec)
94                 return;
95         t->ret = -E_DELAY_TIMEOUT;
96         if (tv_diff(now, &dt->start_time, &diff) > 0)
97                 return;
98         t->ret = 1;
99         if (tv_diff(&s->timeout , &diff, NULL) > 0)
100                 s->timeout = diff;
101 }
102
103 INIT_STDERR_LOGGING(conf.loglevel_arg)
104
105 static struct writer_node_group *check_args(void)
106 {
107         int i, ret = -E_WRITE_SYNTAX;
108         struct writer_node_group *g = NULL;
109
110         if (conf.list_writers_given) {
111                 char *msg = NULL;
112                 FOR_EACH_WRITER(i) {
113                         char *tmp = make_message("%s%s%s",
114                                 i? msg : "",
115                                 i? " " : "",
116                                 writer_names[i]);
117                         free(msg);
118                         msg = tmp;
119                 }
120                 fprintf(stderr, "%s\n", msg);
121                 free(msg);
122                 exit(EXIT_SUCCESS);
123         }
124         if (conf.start_time_given) {
125                 long unsigned sec, usec;
126                 if (sscanf(conf.start_time_arg, "%lu:%lu",
127                                 &sec, &usec) != 2)
128                         goto out;
129                 idt.start_time.tv_sec = sec;
130                 idt.start_time.tv_usec = usec;
131         }
132         if (!conf.writer_given) {
133                 g = setup_default_wng();
134                 ret = 1;
135                 goto out;
136         }
137         g = wng_new(conf.writer_given);
138         ret = -E_WRITE_SYNTAX;
139         for (i = 0; i < conf.writer_given; i++) {
140                 int writer_num;
141                 g->writer_nodes[i].conf = check_writer_arg(
142                         conf.writer_arg[i], &writer_num);
143                 if (!g->writer_nodes[i].conf)
144                         goto out;
145                 g->writer_nodes[i].writer = &writers[writer_num];
146         }
147         ret = 1;
148 out:
149         if (ret > 0)
150                 return g;
151         free(g);
152         return NULL;
153 }
154
155 static void wng_event_handler(struct task *t)
156 {
157         struct writer_node_group *g = t->private_data;
158
159         PARA_INFO_LOG("%s\n", para_strerror(-t->ret));
160         unregister_task(t);
161         wng_close(g);
162 }
163
164
165 static void idt_event_handler(struct task *t)
166 {
167         int ret;
168
169         PARA_INFO_LOG("%s\n", para_strerror(-t->ret));
170         unregister_task(t);
171         wng->buf = sit.buf;
172         wng->loaded = &sit.loaded;
173         wng->input_error = &sit.error;
174         wng->task.event_handler = wng_event_handler;
175         wng->channels = &cwt.channels;
176         wng->samplerate = &cwt.samplerate;
177         ret = wng_open(wng);
178         if (ret < 0) {
179                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
180                 exit(EXIT_FAILURE);
181         }
182 }
183
184 static void cwt_event_handler(struct task *t)
185 {
186         if (t->ret != -E_NO_WAV_HEADER && t->ret != -E_WAV_HEADER_SUCCESS) {
187                 PARA_ERROR_LOG("%s\n", para_strerror(-t->ret));
188                 exit(EXIT_FAILURE);
189         }
190         PARA_INFO_LOG("%s\n", para_strerror(-t->ret));
191         unregister_task(t);
192 //      if (t->ret == -E_WAV_HEADER_SUCCESS) {
193 //              conf.channels_arg = cwt.channels;
194 //              conf.sample_rate_arg = cwt.sample_rate;
195 //      }
196         idt.task.pre_select = initial_delay_pre_select;
197         idt.task.private_data = &idt;
198         idt.task.event_handler = idt_event_handler;
199         sprintf(idt.task.status, "initial_delay");
200         register_task(&idt.task);
201 }
202
203 /**
204  * Para_write's main function.
205  *
206  * \param argc The usual argument counter.
207  * \param argv The usual argument vector.
208  *
209  * It registers the stdin task, the check_wav_task, the task for initial delay
210  * and all tasks for actually writing out the stream.
211  *
212  * \return \p EXIT_SUCCESS or EXIT_FAILURE
213  */
214 int main(int argc, char *argv[])
215 {
216         int ret = -E_WRITE_SYNTAX;
217         struct sched s;
218
219         write_cmdline_parser(argc, argv, &conf);
220         HANDLE_VERSION_FLAG("write", conf);
221         init_supported_writers();
222
223         wng = check_args();
224         if (!wng)
225                 goto out;
226         stdin_set_defaults(&sit);
227         if (conf.bufsize_given)
228                 sit.bufsize = conf.bufsize_arg;
229         sit.buf = para_malloc(sit.bufsize),
230         register_task(&sit.task);
231
232         cwt.task.pre_select = check_wav_pre_select;
233         cwt.task.private_data = &cwt;
234         cwt.task.event_handler = cwt_event_handler;
235         cwt.buf = sit.buf;
236         cwt.loaded = &sit.loaded;
237         cwt.error = &sit.error;
238         sprintf(cwt.task.status, "check wav");
239         register_task(&cwt.task);
240
241         s.default_timeout.tv_sec = 1;
242         s.default_timeout.tv_usec = 0;
243         ret = schedule(&s);
244
245 out:
246         if (ret < 0) {
247                 PARA_ERROR_LOG("%s\n", para_strerror(-ret));
248                 ret = EXIT_FAILURE;
249         } else
250                 ret = EXIT_SUCCESS;
251         return ret;
252 }