audiorecord.cpp 10.9 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19
20
21
22
23
24
25
26
27
28
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
29
30
31
 */

#include "audiorecord.h"
32
#include <unistd.h>
33
#include <sstream> // for stringstream
34
35
#include <cstdio>
#include "logger.h"
36
#include "fileutils.h"
37

38
// structure for the wave header
39

40
struct wavhdr {
41
42
43
44
45
46
47
48
49
50
51
52
53
    char riff[4];           // "RIFF"
    SINT32 file_size;       // in bytes
    char wave[4];           // "WAVE"
    char fmt[4];            // "fmt "
    SINT32 chunk_size;      // in bytes (16 for PCM)
    SINT16 format_tag;      // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law
    SINT16 num_chans;       // 1=mono, 2=stereo
    SINT32 sample_rate;
    SINT32 bytes_per_sec;
    SINT16 bytes_per_samp;  // 2=16-bit mono, 4=16-bit stereo
    SINT16 bits_per_samp;
    char data[4];           // "data"
    SINT32 data_length;     // in bytes
54
};
55

56
AudioRecord::AudioRecord() : fileHandle_(NULL)
57
    , fileType_(FILE_INVALID)
58
59
60
61
62
63
    , channels_(1)
    , byteCounter_(0)
    , sndSmplRate_(8000)
    , nbSamplesMic_(0)
    , nbSamplesSpk_(0)
    , recordingEnabled_(false)
64
65
66
    , mixBuffer_()
    , micBuffer_()
    , spkBuffer_()
67
    , filename_()
68
    , savePath_()
69
{
70
    createFilename();
71
72
}

73
void AudioRecord::setSndSamplingRate(int smplRate)
74
75
{
    sndSmplRate_ = smplRate;
76
77
}

78
void AudioRecord::setRecordingOption(FILE_TYPE type, int sndSmplRate, const std::string &path)
79
{
80
81
82
    std::stringstream s;
    std::string filePath;

83
84
    // use HOME directory if path is empty, or if path does not exist
    if (path.empty() || !fileutils::check_dir(path.c_str())) {
85
86
87
88
89
90
91
        s << getenv("HOME");
        filePath = s.str();
    }
    else {
        filePath = path;
    }

92
93
94
    fileType_ = type;
    channels_ = 1;
    sndSmplRate_ = sndSmplRate;
95
    savePath_ = (*filePath.rbegin() == '/') ? filePath : filePath + "/";
96
97
}

98
void AudioRecord::initFilename(const std::string &peerNumber)
99
{
100
    std::string fName(filename_);
101
    fName.append("-" + peerNumber);
102
103

    if (fileType_ == FILE_RAW) {
104
105
        if (filename_.find(".raw") == std::string::npos) {
            DEBUG("AudioRecord: concatenate .raw file extension: name : %s", filename_.c_str());
106
            fName.append(".raw");
107
108
        }
    } else if (fileType_ == FILE_WAV) {
109
110
        if (filename_.find(".wav") == std::string::npos) {
            DEBUG("AudioRecord: concatenate .wav file extension: name : %s", filename_.c_str());
111
            fName.append(".wav");
112
113
114
        }
    }

115
    savePath_.append(fName);
116
117
}

118
std::string AudioRecord::getFilename() const
119
120
121
122
{
    return savePath_;
}

123
bool AudioRecord::openFile()
124
125
{
    bool result = false;
126

127
    if (not fileExists()) {
128
        DEBUG("AudioRecord: Filename does not exist, creating one");
129
        byteCounter_ = 0;
130

131
        if (fileType_ == FILE_RAW)
132
            result = setRawFile();
133
        else if (fileType_ == FILE_WAV)
134
135
            result = setWavFile();
    } else {
136
        DEBUG("AudioRecord: Filename already exists, opening it");
137
        if (fileType_ == FILE_RAW)
138
            result = openExistingRawFile();
139
        else if (fileType_ == FILE_WAV)
140
141
            result = openExistingWavFile();
    }
142
143

    return result;
144
145
}

146
147
void AudioRecord::closeFile()
{
148
    if (fileHandle_ == 0) return;
149
150

    if (fileType_ == FILE_RAW)
151
        fclose(fileHandle_);
152
    else if (fileType_ == FILE_WAV)
153
        closeWavFile();
154
155
}

156
bool AudioRecord::isOpenFile() const
157
{
158
    return fileHandle_ != 0;
159
160
}

161
bool AudioRecord::fileExists() const
162
{
163
    return access(savePath_.c_str(), F_OK) != -1;
164
165
}

166
bool AudioRecord::isRecording() const
167
{
168
    return recordingEnabled_;
alexandresavard's avatar
alexandresavard committed
169
170
}

171
void AudioRecord::setRecording()
172
173
{
    if (isOpenFile()) {
174
        recordingEnabled_ = !recordingEnabled_;
Julien Bonjean's avatar
Julien Bonjean committed
175
    } else {
176
177
178
179
180
181
182
        openFile();
        recordingEnabled_ = true; // once opend file, start recording
    }
}

void AudioRecord::stopRecording()
{
Tristan Matthews's avatar
Tristan Matthews committed
183
    DEBUG("AudioRecording: Stop recording");
184
    recordingEnabled_ = false;
185
186
}

187
188
void AudioRecord::createFilename()
{
189
190
    time_t rawtime = time(NULL);
    struct tm * timeinfo = localtime(&rawtime);
alexandresavard's avatar
alexandresavard committed
191

192
    std::stringstream out;
193

alexandresavard's avatar
alexandresavard committed
194
    // DATE
195
    out << timeinfo->tm_year + 1900;
196

alexandresavard's avatar
alexandresavard committed
197
    if (timeinfo->tm_mon < 9) // january is 01, not 1
198
199
        out << 0;

alexandresavard's avatar
alexandresavard committed
200
    out << timeinfo->tm_mon+1;
201

alexandresavard's avatar
alexandresavard committed
202
    if (timeinfo->tm_mday < 10) // 01 02 03, not 1 2 3
203
204
        out << 0;

alexandresavard's avatar
alexandresavard committed
205
    out << timeinfo->tm_mday;
206

alexandresavard's avatar
alexandresavard committed
207
    out << '-';
208

alexandresavard's avatar
alexandresavard committed
209
210
    // hour
    if (timeinfo->tm_hour < 10) // 01 02 03, not 1 2 3
211
212
        out << 0;

alexandresavard's avatar
alexandresavard committed
213
    out << timeinfo->tm_hour;
214

alexandresavard's avatar
alexandresavard committed
215
    out << ':';
216

alexandresavard's avatar
alexandresavard committed
217
    if (timeinfo->tm_min < 10) // 01 02 03, not 1 2 3
218
219
        out << 0;

alexandresavard's avatar
alexandresavard committed
220
    out << timeinfo->tm_min;
221

alexandresavard's avatar
alexandresavard committed
222
    out << ':';
223

alexandresavard's avatar
alexandresavard committed
224
    if (timeinfo->tm_sec < 10) // 01 02 03,  not 1 2 3
225
226
        out << 0;

alexandresavard's avatar
alexandresavard committed
227
    out << timeinfo->tm_sec;
228
    filename_ = out.str();
alexandresavard's avatar
alexandresavard committed
229

230
    DEBUG("AudioRecord: Generate filename for this call %s ", filename_.c_str());
alexandresavard's avatar
alexandresavard committed
231
232
}

233
234
bool AudioRecord::setRawFile()
{
235
    fileHandle_ = fopen(savePath_.c_str(), "wb");
236

237
    if (!fileHandle_) {
238
        WARN("AudioRecord: Could not create RAW file!");
239
240
241
        return false;
    }

242
    DEBUG("AudioRecord:setRawFile() : created RAW file.");
243
244

    return true;
245
246
}

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
namespace {
    std::string header_to_string(const wavhdr &hdr)
    {
        std::stringstream ss;
        ss << hdr.riff << "\0 "
           << hdr.file_size << " "
           << hdr.wave << "\0 "
           << hdr.fmt << "\0 "
           << hdr.chunk_size << " "
           << hdr.format_tag << " "
           << hdr.num_chans << " "
           << hdr.sample_rate << " "
           << hdr.bytes_per_sec << " "
           << hdr.bytes_per_samp << " "
           << hdr.bits_per_samp << " "
           << hdr.data << "\0 "
           << hdr.data_length;
        return ss.str();
    }
}

268
269
bool AudioRecord::setWavFile()
{
270
    DEBUG("AudioRecord: Create new wave file %s, sampling rate: %d", savePath_.c_str(), sndSmplRate_);
271

272
    fileHandle_ = fopen(savePath_.c_str(), "wb");
273

274
    if (!fileHandle_) {
275
        WARN("AudioRecord: Error: could not create WAV file.");
276
277
278
        return false;
    }

279
280
281
    /* The text fields are NOT supposed to be null terminated, so we have to
     * write them as arrays since strings enclosed in quotes include a
     * null character */
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    wavhdr hdr = {{'R', 'I', 'F', 'F'},
                  44,
                  {'W', 'A', 'V', 'E'},
                  {'f','m', 't', ' '},
                  16,
                  1,
                  channels_,
                  sndSmplRate_,
                  -1, /* initialized below */
                  -1, /* initialized below */
                  16,
                  {'d', 'a', 't', 'a'},
                  0};

    hdr.bytes_per_samp = channels_ * hdr.bits_per_samp / 8;
    hdr.bytes_per_sec = hdr.sample_rate * hdr.bytes_per_samp;
298

299
    if (fwrite(&hdr, 4, 11, fileHandle_) != 11) {
300
        WARN("AudioRecord: Error: could not write WAV header for file. ");
301
302
303
        return false;
    }

304
    DEBUG("AudioRecord: Wrote wave header \"%s\"", header_to_string(hdr).c_str());
305
    return true;
306
307
}

308
bool AudioRecord::openExistingRawFile()
309
{
310
    fileHandle_ = fopen(filename_.c_str(), "ab+");
311

312
    if (!fileHandle_) {
313
        WARN("AudioRecord: could not create RAW file!");
314
315
        return false;
    }
316

Emmanuel Milou's avatar
Emmanuel Milou committed
317
    return true;
318
}
319

320
bool AudioRecord::openExistingWavFile()
321
{
322
    DEBUG("%s(%s)\n", __PRETTY_FUNCTION__, filename_.c_str());
323

324
    fileHandle_ = fopen(filename_.c_str(), "rb+");
325

326
    if (!fileHandle_) {
327
        WARN("AudioRecord: Error: could not open WAV file!");
328
329
        return false;
    }
330

331
    if (fseek(fileHandle_, 40, SEEK_SET) != 0)  // jump to data length
332
        WARN("AudioRecord: Error: Couldn't seek offset 40 in the file ");
333

334
    if (fread(&byteCounter_, 4, 1, fileHandle_))
335
        WARN("AudioRecord: Error: bytecounter Read successfully ");
336

337
    if (fseek(fileHandle_, 0 , SEEK_END) != 0)
338
        WARN("AudioRecord: Error: Couldn't seek at the en of the file ");
339
340


341
    if (fclose(fileHandle_) != 0)
342
        WARN("AudioRecord: Error: Can't close file r+ ");
343

344
    fileHandle_ = fopen(filename_.c_str(), "ab+");
345

346
    if (!fileHandle_) {
347
        WARN("AudioRecord: Error: Could not createopen WAV file ab+!");
348
349
350
        return false;
    }

351
    if (fseek(fileHandle_, 4 , SEEK_END) != 0)
352
        WARN("AudioRecord: Error: Couldn't seek at the en of the file ");
353

Emmanuel Milou's avatar
Emmanuel Milou committed
354
    return true;
355

356
357
}

358
void AudioRecord::closeWavFile()
359
{
360
    if (fileHandle_ == 0) {
361
        DEBUG("AudioRecord: Can't closeWavFile, a file has not yet been opened!");
362
363
        return;
    }
364

365
    DEBUG("AudioRecord: Close wave file");
366

367
    SINT32 bytes = byteCounter_ * channels_;
368

369
    fseek(fileHandle_, 40, SEEK_SET);  // jump to data length
Julien Bonjean's avatar
Julien Bonjean committed
370

371
    if (ferror(fileHandle_))
372
        WARN("AudioRecord: Error: can't reach offset 40 while closing");
373

374
    fwrite(&bytes, sizeof(SINT32), 1, fileHandle_);
Julien Bonjean's avatar
Julien Bonjean committed
375

376
    if (ferror(fileHandle_))
377
        WARN("AudioRecord: Error: can't write bytes for data length ");
378
379
380

    bytes = byteCounter_ * channels_ + 44; // + 44 for the wave header

381
    fseek(fileHandle_, 4, SEEK_SET);  // jump to file size
Julien Bonjean's avatar
Julien Bonjean committed
382

383
    if (ferror(fileHandle_))
384
        WARN("AudioRecord: Error: can't reach offset 4");
385

386
    fwrite(&bytes, 4, 1, fileHandle_);
Julien Bonjean's avatar
Julien Bonjean committed
387

388
    if (ferror(fileHandle_))
389
        WARN("AudioRecord: Error: can't reach offset 4");
390

391
    if (fclose(fileHandle_) != 0)
392
        WARN("AudioRecord: Error: can't close file");
393
394
}

395
void AudioRecord::recData(SFLDataFormat* buffer, size_t nSamples)
396
{
Julien Bonjean's avatar
Julien Bonjean committed
397
    if (recordingEnabled_) {
398
        if (fileHandle_ == 0) {
399
            DEBUG("AudioRecord: Can't record data, a file has not yet been opened!");
400
401
402
            return;
        }

403
        if (fwrite(buffer, sizeof(SFLDataFormat), nSamples, fileHandle_) != nSamples)
404
            WARN("AudioRecord: Could not record data! ");
405
        else {
406
            fflush(fileHandle_);
407
            byteCounter_ += nSamples * sizeof(SFLDataFormat);
alexandresavard's avatar
alexandresavard committed
408
        }
409
    }
410
}