1 /*
2 * cdda - CD Digital Audio support
3 *
4 * Copyright (C) 1993-2004 Ti Kan
5 * E-mail: xmcd@amb.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /*
23 * File and pipe only (no audio playback) write method
24 */
25 #ifndef lint
26 static char *_wr_fp_c_ident_ = "@(#)wr_fp.c 7.77 04/02/02";
27 #endif
28
29 #include "common_d/appenv.h"
30 #include "common_d/util.h"
31 #include "libdi_d/libdi.h"
32 #include "cdda_d/cdda.h"
33 #include "cdda_d/common.h"
34
35 #if defined(CDDA_WR_FP) && defined(CDDA_SUPPORTED)
36
37 #include "cdda_d/wr_gen.h"
38 #include "cdda_d/wr_fp.h"
39
40
41 extern appdata_t app_data;
42 extern FILE *errfp;
43
44 STATIC int fl_wsemid; /* Semaphores identifier */
45 STATIC pid_t fl_pipe_pid = -1; /* Pipe to program pid */
46 STATIC cd_state_t *fl_wcd; /* Shared memory pointer */
47 STATIC bool_t fl_write_file, /* Write to output file */
48 fl_write_pipe, /* Pipe to program */
49 fl_file_be = FALSE; /* Big endian output file */
50 STATIC gen_desc_t *fl_file_desc = NULL, /* Output file desc */
51 *fl_pipe_desc = NULL; /* Pipe to program desc */
52 STATIC unsigned int fl_file_mode; /* Output file mode */
53
54
55 /*
56 * fpaud_write
57 * Function responsible for writing from the buffer to the audio
58 * file.
59 *
60 * Args:
61 * s - Pointer to the curstat_t structure
62 *
63 * Return:
64 * FALSE - failure
65 * TRUE - success
66 */
67 STATIC bool_t
fpaud_write(curstat_t * s)68 fpaud_write(curstat_t *s)
69 {
70 byte_t *in_data,
71 *sw_data,
72 *le_data,
73 *be_data,
74 *file_data,
75 *pipe_data;
76 char *fname = NULL;
77 int trk_idx = -1,
78 hbcnt = 1;
79 time_t start_time,
80 tot_time;
81 bool_t ret;
82
83 /* Get memory for audio write buffer */
84 le_data = (byte_t *) MEM_ALLOC("le_data", fl_wcd->cds->chunk_bytes);
85 if (le_data == NULL) {
86 (void) sprintf(fl_wcd->i->msgbuf,
87 "fpaud_write: out of memory");
88 DBGPRN(DBG_GEN)(errfp, "%s\n", fl_wcd->i->msgbuf);
89 return FALSE;
90 }
91
92 /* Get memory for audio file write buffer */
93 be_data = (byte_t *) MEM_ALLOC("be_data", fl_wcd->cds->chunk_bytes);
94 if (be_data == NULL) {
95 MEM_FREE(le_data);
96 (void) sprintf(fl_wcd->i->msgbuf,
97 "fpaud_write: out of memory");
98 DBGPRN(DBG_GEN)(errfp, "%s\n", fl_wcd->i->msgbuf);
99 return FALSE;
100 }
101
102 (void) memset(le_data, 0, fl_wcd->cds->chunk_bytes);
103 (void) memset(be_data, 0, fl_wcd->cds->chunk_bytes);
104
105 in_data = app_data.cdda_bigendian ? be_data : le_data;
106 sw_data = app_data.cdda_bigendian ? le_data : be_data;
107
108 /* Start time */
109 start_time = time(NULL);
110
111 /* While not stopped */
112 ret = TRUE;
113 while (fl_wcd->i->state != CDSTAT_COMPLETED) {
114 /* Get lock */
115 cdda_waitsem(fl_wsemid, LOCK);
116
117 /* End if finished and nothing in the buffer */
118 if (fl_wcd->cdb->occupied == 0 && fl_wcd->i->cdda_done) {
119 fl_wcd->i->state = CDSTAT_COMPLETED;
120 cdda_postsem(fl_wsemid, LOCK);
121 break;
122 }
123
124 /* Wait until there is something there */
125 while (fl_wcd->cdb->occupied <= 0 &&
126 fl_wcd->i->state != CDSTAT_COMPLETED) {
127 cdda_postsem(fl_wsemid, LOCK);
128 cdda_waitsem(fl_wsemid, DATA);
129 cdda_waitsem(fl_wsemid, LOCK);
130 }
131
132 /* Break if stopped */
133 if (fl_wcd->i->state == CDSTAT_COMPLETED) {
134 cdda_postsem(fl_wsemid, LOCK);
135 break;
136 }
137
138 /* Copy a chunk from the nextout slot into the data buffers */
139 (void) memcpy(
140 in_data,
141 &fl_wcd->cdb->b[
142 fl_wcd->cds->chunk_bytes * fl_wcd->cdb->nextout
143 ],
144 fl_wcd->cds->chunk_bytes
145 );
146
147 /* Update pointers */
148 fl_wcd->cdb->nextout++;
149 fl_wcd->cdb->nextout %= fl_wcd->cds->buffer_chunks;
150 fl_wcd->cdb->occupied--;
151
152
153 /* Set heartbeat and interval, update stats */
154 if (--hbcnt == 0) {
155 cdda_heartbeat(&fl_wcd->i->writer_hb, CDDA_HB_WRITER);
156
157 tot_time = time(NULL) - start_time;
158 if (tot_time > 0 && fl_wcd->i->frm_played > 0) {
159 fl_wcd->i->frm_per_sec = (int)
160 ((float) fl_wcd->i->frm_played /
161 (float) tot_time) + 0.5;
162 }
163
164 hbcnt = cdda_heartbeat_interval(
165 fl_wcd->i->frm_per_sec
166 );
167 }
168
169 /* Signal room available */
170 cdda_postsem(fl_wsemid, ROOM);
171
172 /* Release lock */
173 cdda_postsem(fl_wsemid, LOCK);
174
175 /* Change channel routing and attenuation if necessary */
176 gen_chroute_att(fl_wcd->i->chroute,
177 fl_wcd->i->att,
178 (sword16_t *)(void *) in_data,
179 (size_t) fl_wcd->cds->chunk_bytes);
180
181 /* Prepare byte-swapped version of the data */
182 gen_byteswap(in_data, sw_data,
183 (size_t) fl_wcd->cds->chunk_bytes);
184
185 /* Set the current track and track length info */
186 cdda_wr_setcurtrk(fl_wcd);
187
188 /* Write to file */
189 if (fl_write_file) {
190 file_data = fl_file_be ? be_data : le_data;
191
192 /* Open new track file if necessary */
193 if (!app_data.cdda_trkfile) {
194 fname = s->trkinfo[0].outfile;
195 }
196 else if (trk_idx != fl_wcd->i->trk_idx) {
197 trk_idx = fl_wcd->i->trk_idx;
198
199 if (fl_file_desc != NULL &&
200 !gen_close_file(fl_file_desc)) {
201 fl_wcd->i->state = CDSTAT_COMPLETED;
202 cdda_postsem(fl_wsemid, LOCK);
203 ret = FALSE;
204 break;
205 }
206
207 fname = s->trkinfo[trk_idx].outfile;
208 fl_file_desc = gen_open_file(
209 fl_wcd,
210 fname,
211 O_WRONLY | O_TRUNC | O_CREAT,
212 (mode_t) fl_file_mode,
213 app_data.cdda_filefmt,
214 fl_wcd->i->trk_len
215 );
216 if (fl_file_desc == NULL) {
217 fl_wcd->i->state = CDSTAT_COMPLETED;
218 cdda_postsem(fl_wsemid, LOCK);
219 ret = FALSE;
220 break;
221 }
222 }
223
224 if (!gen_write_chunk(fl_file_desc, file_data,
225 (size_t) fl_wcd->cds->chunk_bytes)) {
226 fl_wcd->i->state = CDSTAT_COMPLETED;
227 cdda_postsem(fl_wsemid, LOCK);
228 ret = FALSE;
229 break;
230 }
231 }
232
233 /* Pipe to program */
234 if (fl_write_pipe) {
235 pipe_data = fl_file_be ? be_data : le_data;
236
237 if (!gen_write_chunk(fl_pipe_desc, pipe_data,
238 (size_t) fl_wcd->cds->chunk_bytes)) {
239 fl_wcd->i->state = CDSTAT_COMPLETED;
240 cdda_postsem(fl_wsemid, LOCK);
241 ret = FALSE;
242 break;
243 }
244 }
245
246 fl_wcd->i->frm_played += fl_wcd->cds->chunk_blocks;
247
248 while (fl_wcd->i->state == CDSTAT_PAUSED)
249 util_delayms(400);
250
251 /* Update debug level */
252 app_data.debug = fl_wcd->i->debug;
253 }
254
255 /* Wake up reader */
256 cdda_postsem(fl_wsemid, ROOM);
257
258 /* Free memory */
259 MEM_FREE(le_data);
260 MEM_FREE(be_data);
261
262 return (ret);
263 }
264
265
266 /*
267 * fp_winit
268 * Pre-playback support check function
269 *
270 * Args:
271 * None.
272 *
273 * Return:
274 * Bitmask of supported features
275 */
276 word32_t
fp_winit(void)277 fp_winit(void)
278 {
279 return (CDDA_WRITEFILE | CDDA_WRITEPIPE);
280 }
281
282
283 /*
284 * fp_write
285 * Opens audio file, attaches shared memory and semaphores.
286 * Continuously reads data from shared memory and writes it
287 * to the audio file.
288 *
289 * Args:
290 * s - Pointer to the curstat_t structure
291 *
292 * Return:
293 * FALSE - failure
294 * TRUE - success
295 */
296 bool_t
fp_write(curstat_t * s)297 fp_write(curstat_t *s)
298 {
299 size_t estlen;
300
301 if (!PLAYMODE_IS_CDDA(app_data.play_mode)) {
302 (void) fprintf(errfp, "fl_write: Nothing to do.\n");
303 return FALSE;
304 }
305
306 fl_write_file = (bool_t) ((app_data.play_mode & PLAYMODE_FILE) != 0);
307 fl_write_pipe = (bool_t) ((app_data.play_mode & PLAYMODE_PIPE) != 0);
308
309 /* Generic services initialization */
310 gen_write_init();
311
312 /* Allocate memory */
313 fl_wcd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
314 if (fl_wcd == NULL) {
315 (void) fprintf(errfp, "fl_write: out of memory\n");
316 return FALSE;
317 }
318 (void) memset(fl_wcd, 0, sizeof(cd_state_t));
319
320 /* Initialize shared memory and semaphores */
321 if ((fl_wsemid = cdda_initipc(fl_wcd)) < 0)
322 return FALSE;
323
324 (void) sscanf(app_data.cdinfo_filemode, "%o", &fl_file_mode);
325 /* Make sure file is at least accessible by user */
326 fl_file_mode |= S_IRUSR | S_IWUSR;
327 fl_file_mode &= ~(S_ISUID | S_ISGID | S_IWGRP | S_IWOTH);
328
329 estlen = (fl_wcd->i->end_lba - fl_wcd->i->start_lba + 1) * CDDA_BLKSZ;
330
331 /* Set heartbeat */
332 cdda_heartbeat(&fl_wcd->i->writer_hb, CDDA_HB_WRITER);
333
334 if (fl_write_file || fl_write_pipe) {
335 /* Open file and/or pipe */
336 if (!app_data.cdda_trkfile && fl_write_file) {
337 fl_file_desc = gen_open_file(
338 fl_wcd,
339 s->trkinfo[0].outfile,
340 O_WRONLY | O_TRUNC | O_CREAT,
341 (mode_t) fl_file_mode,
342 app_data.cdda_filefmt,
343 estlen
344 );
345 if (fl_file_desc == NULL)
346 return FALSE;
347 }
348
349 if (fl_write_pipe) {
350 fl_pipe_desc = gen_open_pipe(
351 fl_wcd,
352 app_data.pipeprog,
353 &fl_pipe_pid,
354 app_data.cdda_filefmt,
355 estlen
356 );
357 if (fl_pipe_desc == NULL)
358 return FALSE;
359
360 DBGPRN(DBG_SND)(errfp,
361 "\nfpaud_write: Pipe to [%s]: "
362 "chunk_blks=%d\n",
363 app_data.pipeprog,
364 fl_wcd->cds->chunk_blocks);
365 }
366
367 /* Set endian */
368 fl_file_be = gen_filefmt_be(app_data.cdda_filefmt);
369 }
370
371 /* Do the writing */
372 if (!fpaud_write(s))
373 return FALSE;
374
375 return TRUE;
376 }
377
378
379 /*
380 * fp_wdone
381 * Post-playback cleanup function
382 *
383 * Args:
384 * killreader - whether to terminate the reader thread or process
385 *
386 * Return:
387 * Nothing.
388 */
389 void
fp_wdone(bool_t killreader)390 fp_wdone(bool_t killreader)
391 {
392 DBGPRN(DBG_GEN)(errfp, "\nfp_wdone: Cleaning up writer\n");
393
394 if (fl_file_desc != NULL) {
395 (void) gen_close_file(fl_file_desc);
396 fl_file_desc = NULL;
397 }
398
399 if (fl_pipe_desc != NULL) {
400 (void) gen_close_pipe(fl_pipe_desc);
401 fl_pipe_desc = NULL;
402 }
403
404 cdda_yield();
405
406 if (fl_pipe_pid > 0) {
407 (void) kill(fl_pipe_pid, SIGTERM);
408 fl_pipe_pid = -1;
409 }
410
411 if (fl_wcd != NULL) {
412 if (fl_wcd->i != NULL) {
413 if (killreader && fl_wcd->i->reader != (thid_t) 0) {
414 cdda_kill(fl_wcd->i->reader, SIGTERM);
415 fl_wcd->i->state = CDSTAT_COMPLETED;
416 }
417
418 /* Reset frames played counter */
419 fl_wcd->i->frm_played = 0;
420 }
421
422 MEM_FREE(fl_wcd);
423 fl_wcd = NULL;
424 }
425
426 fl_write_file = fl_write_pipe = FALSE;
427 }
428
429
430 /*
431 * fp_winfo
432 * Append method identification to informational string.
433 *
434 * Args:
435 * str - The informational string
436 *
437 * Return:
438 * Nothing.
439 */
440 void
fp_winfo(char * str)441 fp_winfo(char *str)
442 {
443 (void) strcat(str, "file & pipe only\n");
444 }
445
446 #endif /* CDDA_WR_FP CDDA_SUPPORTED */
447
448