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