audiorecord.cpp 5.69 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 *  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
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 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
32
33
34
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

35
#include "audiorecord.h"
36
#include <sndfile.hh>
37
#include <unistd.h>
38
#include <sstream> // for stringstream
39
#include <algorithm>
40
41
#include <cstdio>
#include "logger.h"
42
#include "fileutils.h"
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
namespace {
std::string
createFilename()
{
    time_t rawtime = time(NULL);
    struct tm * timeinfo = localtime(&rawtime);

    std::stringstream out;

    // DATE
    out << timeinfo->tm_year + 1900;

    if (timeinfo->tm_mon < 9) // january is 01, not 1
        out << 0;

    out << timeinfo->tm_mon + 1;

    if (timeinfo->tm_mday < 10) // 01 02 03, not 1 2 3
        out << 0;

    out << timeinfo->tm_mday;

    out << '-';

    // hour
    if (timeinfo->tm_hour < 10) // 01 02 03, not 1 2 3
        out << 0;

    out << timeinfo->tm_hour;

    out << ':';

    if (timeinfo->tm_min < 10) // 01 02 03, not 1 2 3
        out << 0;

    out << timeinfo->tm_min;

    out << ':';

    if (timeinfo->tm_sec < 10) // 01 02 03,  not 1 2 3
        out << 0;

    out << timeinfo->tm_sec;
    return out.str();
}
}


92
AudioRecord::AudioRecord() : fileHandle_(0)
93
94
95
    , channels_(1)
    , sndSmplRate_(8000)
    , recordingEnabled_(false)
96
    , filename_(createFilename())
97
    , savePath_()
98
{
99
    WARN("Generate filename for this call %s ", filename_.c_str());
100
101
}

102
103
104
105
106
AudioRecord::~AudioRecord()
{
    delete fileHandle_;
}

107
void AudioRecord::setSndSamplingRate(int smplRate)
108
109
{
    sndSmplRate_ = smplRate;
110
111
}

112
void AudioRecord::setRecordingOptions(int sndSmplRate, const std::string &path)
113
{
114
115
    std::string filePath;

116
    // use HOME directory if path is empty, or if path does not exist
117
    if (path.empty() or not fileutils::check_dir(path.c_str())) {
118
        filePath = fileutils::get_home_dir();
119
    } else {
120
121
122
        filePath = path;
    }

123
124
    channels_ = 1;
    sndSmplRate_ = sndSmplRate;
Tristan Matthews's avatar
Tristan Matthews committed
125
    savePath_ = (*filePath.rbegin() == DIR_SEPARATOR_CH) ? filePath : filePath + DIR_SEPARATOR_STR;
126
127
}

128
129
130
131
namespace {
bool
nonFilenameCharacter(char c)
{
132
    return not(std::isalnum(c) or c == '_' or c == '.');
133
134
135
136
137
138
139
140
141
142
143
}

// Replace any character that is inappropriate for a filename with '_'
std::string
sanitize(std::string s)
{
    std::replace_if(s.begin(), s.end(), nonFilenameCharacter, '_');
    return s;
}
}

144
void AudioRecord::initFilename(const std::string &peerNumber)
145
{
146
    std::string fName(filename_);
147
    fName.append("-" + sanitize(peerNumber) + "-" PACKAGE);
148

149
150
151
    if (filename_.find(".wav") == std::string::npos) {
        DEBUG("Concatenate .wav file extension: name : %s", filename_.c_str());
        fName.append(".wav");
152
153
    }

154
    savePath_.append(fName);
155
156
}

157
std::string AudioRecord::getFilename() const
158
159
160
161
{
    return savePath_;
}

162
bool AudioRecord::openFile()
163
164
{
    bool result = false;
165
166
167
    delete fileHandle_;
    const bool doAppend = fileExists();
    const int access = doAppend ? SFM_RDWR : SFM_WRITE;
168

169
    fileHandle_ = new SndfileHandle(savePath_.c_str(), access, SF_FORMAT_WAV | SF_FORMAT_PCM_16, channels_, sndSmplRate_);
170

171
172
173
174
175
176
    // check overloaded boolean operator
    if (!*fileHandle_) {
        WARN("Could not open WAV file!");
        delete fileHandle_;
        fileHandle_ = 0;
        return false;
177
    }
178

179
180
181
    if (doAppend and fileHandle_->seek(0, SEEK_END) < 0)
        WARN("Couldn't seek to the end of the file ");

182
    return result;
183
184
}

185
186
void AudioRecord::closeFile()
{
187
188
    delete fileHandle_;
    fileHandle_ = 0;
189
190
}

191
bool AudioRecord::isOpenFile() const
192
{
193
    return fileHandle_ != 0;
194
195
}

196
bool AudioRecord::fileExists() const
197
{
198
    return access(savePath_.c_str(), F_OK) != -1;
199
200
}

201
bool AudioRecord::isRecording() const
202
{
203
    return recordingEnabled_;
alexandresavard's avatar
alexandresavard committed
204
205
}

206
bool AudioRecord::toggleRecording()
207
208
{
    if (isOpenFile()) {
209
        recordingEnabled_ = !recordingEnabled_;
Julien Bonjean's avatar
Julien Bonjean committed
210
    } else {
211
        openFile();
212
        recordingEnabled_ = true;
213
    }
214

215
    return recordingEnabled_;
216
217
218
219
}

void AudioRecord::stopRecording()
{
220
    DEBUG("Stop recording");
221
    recordingEnabled_ = false;
222
223
}

224
void AudioRecord::recData(AudioBuffer& buffer)
225
{
226
227
228
229
230
231
232
    if (not recordingEnabled_)
        return;

    if (fileHandle_ == 0) {
        DEBUG("Can't record data, a file has not yet been opened!");
        return;
    }
233

234
    const int nSamples = buffer.frames();
235

236
    // FIXME: mono only
237
    if (fileHandle_->write(buffer.getChannel(0)->data(), nSamples) != nSamples) {
238
        WARN("Could not record data!");
239
    } else {
240
        fileHandle_->writeSync();
241
    }
242
}