switch source package format to 3.0 quilt
[debian/gnuradio] / gnuradio-core / src / lib / io / gr_wavfile_sink.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2004,2006,2007,2008,2009 Free Software Foundation, Inc.
4  *
5  * This file is part of GNU Radio
6  *
7  * GNU Radio 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 3, or (at your option)
10  * any later version.
11  *
12  * GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <gr_wavfile_sink.h>
28 #include <gr_io_signature.h>
29 #include <gri_wavfile.h>
30 #include <stdexcept>
31 #include <climits>
32 #include <cstring>
33 #include <cmath>
34 #include <fcntl.h>
35 #include <gruel/thread.h>
36
37 // win32 (mingw/msvc) specific
38 #ifdef HAVE_IO_H
39 #include <io.h>
40 #endif
41 #ifdef O_BINARY
42 #define OUR_O_BINARY O_BINARY
43 #else
44 #define OUR_O_BINARY 0
45 #endif
46
47 // should be handled via configure
48 #ifdef O_LARGEFILE
49 #define OUR_O_LARGEFILE O_LARGEFILE
50 #else
51 #define OUR_O_LARGEFILE 0
52 #endif
53
54
55 gr_wavfile_sink_sptr
56 gr_make_wavfile_sink(const char *filename,
57                      int n_channels,
58                      unsigned int sample_rate,
59                      int bits_per_sample)
60 {
61   return gr_wavfile_sink_sptr (new gr_wavfile_sink (filename,
62                                                     n_channels,
63                                                     sample_rate,
64                                                     bits_per_sample));
65 }
66
67 gr_wavfile_sink::gr_wavfile_sink(const char *filename,
68                                  int n_channels,
69                                  unsigned int sample_rate,
70                                  int bits_per_sample)
71   : gr_sync_block ("wavfile_sink",
72                    gr_make_io_signature(1, n_channels, sizeof(float)),
73                    gr_make_io_signature(0, 0, 0)),
74     d_sample_rate(sample_rate), d_nchans(n_channels),
75     d_fp(0), d_new_fp(0), d_updated(false)
76 {
77   if (bits_per_sample != 8 && bits_per_sample != 16) {
78     throw std::runtime_error("Invalid bits per sample (supports 8 and 16)");
79   }
80   d_bytes_per_sample = bits_per_sample / 8;
81   d_bytes_per_sample_new = d_bytes_per_sample;
82   
83   if (!open(filename)) {
84     throw std::runtime_error ("can't open file");
85   }
86
87   if (bits_per_sample == 8) {
88     d_max_sample_val = 0xFF;
89     d_min_sample_val = 0;
90     d_normalize_fac  = d_max_sample_val/2;
91     d_normalize_shift = 1;
92   } else {
93     d_max_sample_val = 0x7FFF;
94     d_min_sample_val = -0x7FFF;
95     d_normalize_fac  = d_max_sample_val;
96     d_normalize_shift = 0;
97     if (bits_per_sample != 16) {
98       fprintf(stderr, "Invalid bits per sample value requested, using 16");
99     }
100   }
101 }
102
103
104 bool
105 gr_wavfile_sink::open(const char* filename)
106 {
107   gruel::scoped_lock guard(d_mutex);
108   
109   // we use the open system call to get access to the O_LARGEFILE flag.
110   int fd;
111   if ((fd = ::open (filename,
112                     O_WRONLY|O_CREAT|O_TRUNC|OUR_O_LARGEFILE|OUR_O_BINARY,
113                     0664)) < 0){
114     perror (filename);
115     return false;
116   }
117
118   if (d_new_fp) {    // if we've already got a new one open, close it
119     fclose(d_new_fp);
120     d_new_fp = 0;
121   }
122   
123   if ((d_new_fp = fdopen (fd, "wb")) == NULL) {
124     perror (filename);
125     ::close(fd);  // don't leak file descriptor if fdopen fails.
126     return false;
127   }
128   d_updated = true;
129   
130   if (!gri_wavheader_write(d_new_fp,
131                            d_sample_rate,
132                            d_nchans,
133                            d_bytes_per_sample_new)) {
134     fprintf(stderr, "[%s] could not write to WAV file\n", __FILE__);
135     exit(-1);
136   }
137   
138   return true;
139 }
140
141
142 void
143 gr_wavfile_sink::close()
144 {
145   gruel::scoped_lock guard(d_mutex);
146   
147   if (!d_fp)
148     return;
149   
150   close_wav();
151 }
152
153 void gr_wavfile_sink::close_wav()
154 {
155   unsigned int byte_count = d_sample_count * d_bytes_per_sample;
156   
157   gri_wavheader_complete(d_fp, byte_count);
158   
159   fclose(d_fp);
160   d_fp = NULL;
161 }
162
163
164 gr_wavfile_sink::~gr_wavfile_sink ()
165 {
166   if (d_new_fp) {
167     fclose(d_new_fp);
168   }
169
170   close();
171 }
172
173
174 int
175 gr_wavfile_sink::work (int noutput_items,
176                        gr_vector_const_void_star &input_items,
177                        gr_vector_void_star &output_items)
178 {
179   float **in = (float **) &input_items[0];
180   int n_in_chans = input_items.size();
181   
182   short int sample_buf_s;
183   
184   int nwritten;
185   
186   do_update();  // update: d_fp is reqd
187   if (!d_fp)    // drop output on the floor
188     return noutput_items;
189   
190   for (nwritten = 0; nwritten < noutput_items; nwritten++) {
191     for (int chan = 0; chan < d_nchans; chan++) {
192       // Write zeros to channels which are in the WAV file
193       // but don't have any inputs here
194       if (chan < n_in_chans) {
195         sample_buf_s = 
196           convert_to_short(in[chan][nwritten]);
197       } else {
198         sample_buf_s = 0;
199       }
200       
201       gri_wav_write_sample(d_fp, sample_buf_s, d_bytes_per_sample);
202       
203       if (feof(d_fp) || ferror(d_fp)) {
204         fprintf(stderr, "[%s] file i/o error\n", __FILE__);
205         close();
206         exit(-1);
207       }
208       d_sample_count++;
209     }
210   }
211   
212   return nwritten;
213 }
214
215
216 short int
217 gr_wavfile_sink::convert_to_short(float sample)
218 {
219   sample += d_normalize_shift;
220   sample *= d_normalize_fac;
221   if (sample > d_max_sample_val) {
222     sample = d_max_sample_val;
223   } else if (sample < d_min_sample_val) {
224     sample = d_min_sample_val;
225   }
226   
227   return (short int) roundf(sample);
228 }
229
230
231 void
232 gr_wavfile_sink::set_bits_per_sample(int bits_per_sample)
233 {
234   gruel::scoped_lock guard(d_mutex);
235   if (bits_per_sample == 8 || bits_per_sample == 16) {
236     d_bytes_per_sample_new = bits_per_sample / 8;
237   }
238 }
239
240
241 void
242 gr_wavfile_sink::set_sample_rate(unsigned int sample_rate)
243 {
244   gruel::scoped_lock guard(d_mutex);
245   d_sample_rate = sample_rate;
246 }
247
248
249 void
250 gr_wavfile_sink::do_update()
251 {
252   if (!d_updated) {
253     return;
254   }
255   
256   gruel::scoped_lock guard(d_mutex);     // hold mutex for duration of this block
257   if (d_fp) {
258     close_wav();
259   }
260
261   d_fp = d_new_fp;                    // install new file pointer
262   d_new_fp  = 0;
263   d_sample_count = 0;
264   d_bytes_per_sample = d_bytes_per_sample_new;
265
266   if (d_bytes_per_sample == 1) {
267     d_max_sample_val = UCHAR_MAX;
268     d_min_sample_val = 0;
269     d_normalize_fac  = d_max_sample_val/2;
270     d_normalize_shift = 1;
271   } else if (d_bytes_per_sample == 2) {
272     d_max_sample_val = SHRT_MAX;
273     d_min_sample_val = SHRT_MIN;
274     d_normalize_fac  = d_max_sample_val;
275     d_normalize_shift = 0;
276   }
277   
278   d_updated = false;
279 }