1 /*
2  *  export_pcm.c
3  *
4  *  Copyright (C) Thomas Oestreich - May 2001
5  *
6  *  This file is part of transcode, a video stream processing tool
7  *
8  *  transcode is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2, or (at your option)
11  *  any later version.
12  *
13  *  transcode is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with GNU Make; see the file COPYING.  If not, write to
20  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 
29 #include "transcode.h"
30 #include "avilib/avilib.h"
31 #include "libtc/libtc.h"
32 
33 #define MOD_NAME    "export_pcm.so"
34 #define MOD_VERSION "v0.1.0 (2007-08-25)"
35 #define MOD_CODEC   "(audio) PCM (non-interleaved)"
36 
37 
38 static int verbose_flag=TC_QUIET;
39 static int capability_flag=TC_CAP_PCM|TC_CAP_RGB|TC_CAP_YUV|TC_CAP_VID;
40 
41 #define MOD_PRE tc_pcm
42 #include "export_def.h"
43 
44 static struct wave_header rtf;
45 static int fd_r = -1, fd_l = -1, fd_c = -1;
46 static int fd_ls = -1, fd_rs = -1, fd_lfe = -1;
47 
48 typedef enum {
49     CHANNEL_CENTER = 1,
50     CHANNEL_STEREO = 2,
51     CHANNEL_FRONT  = 4,
52     CHANNEL_LFE    = 8
53 } PCMChannels;
54 
55 static PCMChannels chan_settings[8] = {
56     0,                                                       /* 0: nothing */
57     CHANNEL_CENTER,                                          /* 1: mono */
58     CHANNEL_STEREO,                                          /* 2: stereo */
59     CHANNEL_STEREO|CHANNEL_CENTER,                           /* 3: 2.1 */
60     CHANNEL_STEREO|CHANNEL_FRONT,                            /* 4: 2+2 */
61     CHANNEL_STEREO|CHANNEL_FRONT|CHANNEL_CENTER,             /* 5: 2+2+1 */
62     CHANNEL_STEREO|CHANNEL_FRONT|CHANNEL_CENTER|CHANNEL_LFE, /* 6: 5.1 */
63     0,                                                       /* nothing */
64 };
65 
66 /* ------------------------------------------------------------
67  *
68  * init codec
69  *
70  * ------------------------------------------------------------*/
71 
72 MOD_init
73 {
74     int rate;
75 
76     if (param->flag == TC_VIDEO) {
77         return TC_EXPORT_OK;
78     }
79     if (param->flag == TC_AUDIO) {
80         memset(&rtf, 0, sizeof(rtf));
81 
82         strlcpy(rtf.riff.id, "RIFF", 4);
83         strlcpy(rtf.riff.wave_id, "WAVE", 4);
84         strlcpy(rtf.format.id, "fmt ", 4);
85 
86         rtf.format.len = sizeof(struct common_struct);
87         rtf.common.wFormatTag = CODEC_PCM;
88 
89         rate = (vob->mp3frequency != 0) ?vob->mp3frequency :vob->a_rate;
90 
91         rtf.common.dwSamplesPerSec = rate;
92         rtf.common.dwAvgBytesPerSec = vob->dm_chan * rate * vob->dm_bits/8;
93         rtf.common.wBitsPerSample = vob->dm_bits;
94         rtf.common.wBlockAlign = vob->dm_chan*vob->dm_bits/8;
95 
96         if(vob->dm_chan >= 1 && vob->dm_chan <= 6) { /* sanity check */
97             rtf.common.wChannels=vob->dm_chan;
98         } else {
99             tc_log_error(MOD_NAME, "bad PCM channel number: %i",
100                                    vob->dm_chan);
101             return TC_EXPORT_ERROR;
102         }
103         if (!vob->a_codec_flag
104           || !rtf.common.dwSamplesPerSec
105           || !rtf.common.wBitsPerSample
106           || !rtf.common.wBlockAlign) {
107             tc_log_error(MOD_NAME, "cannot export PCM, invalid format "
108                                    "(no audio track at all?)");
109             return TC_EXPORT_ERROR;
110         }
111 
112         rtf.riff.len = 0x7FFFFFFF;
113         rtf.data.len = 0x7FFFFFFF;
114 
115         strlcpy(rtf.data.id, "data",4);
116 
117         return TC_EXPORT_OK;
118     }
119     // invalid flag
120     return TC_EXPORT_ERROR;
121 }
122 
123 /* ------------------------------------------------------------
124  *
125  * open outputfile
126  *
127  * ------------------------------------------------------------*/
128 
129 /* XXX */
130 #define FD_OPEN(fd) do { \
131     fd = open(fname, O_RDWR|O_CREAT|O_TRUNC, \
132 	                 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); \
133     if (fd < 0) { \
134         tc_log_error(MOD_NAME, "opening file: %s", strerror(errno)); \
135         return TC_EXPORT_ERROR; \
136     } \
137 } while (0)
138 
139 MOD_open
140 {
141     char fname[TC_BUF_LINE];
142     int chan_flags = chan_settings[rtf.common.wChannels];
143 
144     if (param->flag == TC_AUDIO) {
145         if (chan_flags & CHANNEL_LFE) {
146             tc_snprintf(fname, sizeof(fname), "%s_lfe.pcm",
147                         vob->audio_out_file);
148             FD_OPEN(fd_lfe);
149         }
150 
151         if (chan_flags & CHANNEL_FRONT) {
152 	        tc_snprintf(fname, sizeof(fname), "%s_ls.pcm",
153                         vob->audio_out_file);
154             FD_OPEN(fd_ls);
155 
156             tc_snprintf(fname, sizeof(fname), "%s_rs.pcm",
157                         vob->audio_out_file);
158             FD_OPEN(fd_rs);
159         }
160 
161         if (chan_flags & CHANNEL_STEREO) {
162 	        tc_snprintf(fname, sizeof(fname), "%s_l.pcm",
163                         vob->audio_out_file);
164             FD_OPEN(fd_l);
165 
166             tc_snprintf(fname, sizeof(fname), "%s_r.pcm",
167                         vob->audio_out_file);
168 	        FD_OPEN(fd_r);
169         }
170 
171         if (chan_flags & CHANNEL_CENTER) {
172 	        tc_snprintf(fname, sizeof(fname), "%s_c.pcm",
173                         vob->audio_out_file);
174             FD_OPEN(fd_c);
175         }
176 
177         return TC_EXPORT_OK;
178     }
179 
180     if (param->flag == TC_VIDEO) {
181         return TC_EXPORT_OK;
182     }
183 
184     // invalid flag
185     return TC_EXPORT_ERROR;
186 }
187 
188 /* ------------------------------------------------------------
189  *
190  * encode and export
191  *
192  * ------------------------------------------------------------*/
193 
194 #define FD_WRITE(fd, buf, size) do { \
195       if(fd != -1 && tc_pwrite(fd, buf, size) != size) { \
196           tc_log_error(MOD_NAME,  "writing audio frame: %s", \
197                        strerror(errno)); \
198           return TC_EXPORT_ERROR; \
199       } \
200 } while (0)
201 
202 MOD_encode
203 {
204     if (param->flag == TC_VIDEO) {
205         return TC_EXPORT_OK;
206     }
207     if (param->flag == TC_AUDIO) {
208         int size = (int)(param->size/rtf.common.wChannels);
209 
210         switch (rtf.common.wChannels) {
211           case 6:
212             FD_WRITE(fd_rs,  param->buffer + 5 * size, size);
213             FD_WRITE(fd_ls,  param->buffer + 4 * size, size);
214             FD_WRITE(fd_r,   param->buffer + 3 * size, size);
215             FD_WRITE(fd_c,   param->buffer + 2 * size, size);
216             FD_WRITE(fd_l,   param->buffer +     size, size);
217             FD_WRITE(fd_lfe, param->buffer,            size);
218             break;
219 
220           case 2:
221             FD_WRITE(fd_r, param->buffer + size, size);
222             FD_WRITE(fd_l, param->buffer,        size);
223             break;
224 
225           case 1:
226             FD_WRITE(fd_c, param->buffer, size);
227             break;
228         }
229 
230         return TC_EXPORT_OK;
231     }
232 
233     // invalid flag
234     return TC_EXPORT_ERROR;
235 }
236 
237 /* ------------------------------------------------------------
238  *
239  * close outputfiles
240  *
241  * ------------------------------------------------------------*/
242 
243 #define FD_CLOSE(fd) do { \
244         if (fd != -1) { \
245             close(fd);\
246         } \
247 } while (0)
248 
249 MOD_close
250 {
251     if (param->flag == TC_VIDEO) {
252         return TC_EXPORT_OK;
253     }
254     if (param->flag == TC_AUDIO) {
255         FD_CLOSE(fd_l);
256         FD_CLOSE(fd_c);
257         FD_CLOSE(fd_r);
258         FD_CLOSE(fd_ls);
259         FD_CLOSE(fd_rs);
260         FD_CLOSE(fd_lfe);
261         return TC_EXPORT_OK;
262     }
263     return TC_EXPORT_ERROR;
264 }
265 
266 /* ------------------------------------------------------------
267  *
268  * stop encoder
269  *
270  * ------------------------------------------------------------*/
271 
272 MOD_stop
273 {
274     if (param->flag == TC_VIDEO) {
275         return TC_EXPORT_OK;
276     }
277     if (param->flag == TC_AUDIO) {
278         return TC_EXPORT_OK;
279     }
280     return TC_EXPORT_ERROR;
281 }
282 
283 /*************************************************************************/
284 
285 /*
286  * Local variables:
287  *   c-file-style: "stroustrup"
288  *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
289  *   indent-tabs-mode: nil
290  * End:
291  *
292  * vim: expandtab shiftwidth=4:
293  */
294