Jamoma API  0.6.0.a19
TTSoundfileRecorder.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup dspSoundFileLib
4  *
5  * @brief Jamoma DSP Soundfile Recorder
6  *
7  * @details
8  *
9  * @authors Timothy Place
10  *
11  * @copyright Copyright © 2010 by Timothy Place @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 #include "TTSoundfileRecorder.h"
17 
18 #define thisTTClass TTSoundfileRecorder
19 #define thisTTClassName "soundfile.recorder"
20 #define thisTTClassTags "dspSoundFileLib, audio, soundfile, record"
21 
22 
23 TT_AUDIO_CONSTRUCTOR,
24 mFilePath(kTTSymEmpty),
25 mFormat(kTTSymEmpty),
26 mSoundFile(NULL),
27 mRecord(false),
28 mNumChannels(0),
29 mNumBufferFrames(0),
30 mLength(0),
31 mLengthInSamples(0),
32 mCycles(0)
33 {
35  addAttribute( Format, kTypeSymbol);
37  addAttribute( NumChannels, kTypeUInt16);
38  addAttributeProperty( NumChannels, readOnly, TTValue(YES));
40  addAttributeProperty(Length, range, TTValue(0, 60000));
41  addAttributeProperty(Length, rangeChecking, TT("clip"));
42 
43 }
44 
45 
46 TTSoundfileRecorder::~TTSoundfileRecorder()
47 {
48  setAttributeValue(TT("record"), NO);
49  if (mSoundFile)
50  sf_close(mSoundFile);
51 }
52 
54 {
55  TTBoolean newRecordState = newValue;
56  TTErr err = kTTErrNone;
57 
58  if (newRecordState == 1){
59  if (mLength <= 0)
60  setProcessMethod(processAudioRecording);
61  else
62  setProcessMethod(processTimedAudioRecording);
63  }
64  else
65  setProcessMethod(processAudioBypass);
66 
67  if (mRecord != newRecordState) {
68  mRecord = newRecordState;
69  if (mRecord) { // start recording
70  mLengthInSamples = mLength * sr * 0.001; // reset the Sample counter
71  mCycles = 0;
72  //mNumChannels = 0; // set to zero so that the process method will set the num channels and trigger an open
73  }
74  else { // stop recording -- close the file
75  if (mSoundFile){
76  sf_close(mSoundFile);
77  mSoundFile = NULL;
78  }
79  }
80  }
81  return err;
82 }
83 
84 TTErr TTSoundfileRecorder::setLength(const TTValue& newValue)
85 { TTFloat64 newLength = newValue;
86  if (newLength != mLength){
87  mLength = newLength;
88  mNumBufferFrames = 0; //hack to force a resize of mBuffer in the process method
89  }
90  return kTTErrNone;
91 }
92 
93 
94 // "FLAC-24bit" -> SF_FORMAT_FLAC | SF_FORMAT_PCM_24
95 // something to consider when you want to write large amount of data as fast as possible: http://www.mega-nerd.com/libsndfile/FAQ.html#Q006
96 int TTSoundfileRecorder::translateFormatFromName(TTSymbol& name)
97 {
98  int format = 0;
99  char cname[64];
100  char* s;
101 
102  if (name)
103  strncpy(cname, name.c_str(), 64);
104  else
105  strncpy(cname, "CAF", 64);
106 
107  s = strrchr(cname, '-'); // look for subtype
108  if (s) {
109  *s = 0;
110  s++;
111  if (s) {
112  if (strstr(s, "16bit"))
113  format |= SF_FORMAT_PCM_16;
114  else if (strstr(s, "24bit"))
115  format |= SF_FORMAT_PCM_24;
116  else if (strstr(s, "32bit"))
117  format |= SF_FORMAT_PCM_32;
118  else
119  format |= SF_FORMAT_PCM_24;
120  }
121  }
122  else { // no subtype, set default
123  format |= SF_FORMAT_PCM_24;
124  }
125 
126  // now look at the primary type
127  if (strstr(cname, "FLAC"))
128  format |= SF_FORMAT_FLAC;
129  else if (strstr(cname, "AIFF"))
130  format |= SF_FORMAT_AIFF;
131  else if (strstr(cname, "WAV"))
132  format |= SF_FORMAT_WAV;
133  else if (strstr(cname, "Matlab"))
134  format |= SF_FORMAT_MAT4;
135  else
136  format |= SF_FORMAT_CAF;
137 
138  return format;
139 }
140 
141 
142 TTErr TTSoundfileRecorder::setFilePath(const TTValue& newValue)
143 {
144  mFilePath = newValue;
145  /*if ((mNumChannels) && (mFormat))
146  return openFile();
147  else */
148  return kTTErrNone;
149 }
150 
151 
152 TTErr TTSoundfileRecorder::openFile()
153 {
154  memset(&mSoundFileInfo, 0, sizeof(mSoundFileInfo));
155  mSoundFileInfo.channels = mNumChannels;
156  mSoundFileInfo.format = translateFormatFromName(mFormat);
157  mSoundFileInfo.samplerate = sr;
158 
159  mSoundFile = sf_open(mFilePath.c_str(), SFM_WRITE, &mSoundFileInfo);
160 
161  if (!mSoundFile) {
162  TTLogMessage("Can't create soundfile.\n");
163  return kTTErrGeneric;
164  }
165  return kTTErrNone;
166 }
167 
168 
169 TTErr TTSoundfileRecorder::processAudioRecording(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
170 {
171  TTAudioSignal& out = outputs->getSignal(0);
172  TTAudioSignal& in = inputs->getSignal(0);
173  TTSampleValuePtr outSample, inSample;
174  TTUInt16 channelCount = in.getNumChannelsAsInt();
175  TTUInt16 vs = in.getVectorSizeAsInt();
176  TTUInt16 n, channel;
177  TTBoolean bufferNeedsResize = NO;
178  //sf_count_t numSamplesWritten;
179  //TTUInt64 progress = mCycles*vs*channelCount;
180 
181  if (mNumChannels != channelCount) { // input channel number has changed, we need to set up the buffer
182  mNumChannels = channelCount;
183  bufferNeedsResize = YES;
184  }
185 
186  if (mNumBufferFrames != vs) { //vector size has changed, we need to set up the buffer
187  mNumBufferFrames = vs;
188  bufferNeedsResize = YES;
189  }
190  if (bufferNeedsResize){
191  TTUInt64 buffersize = mNumChannels * mNumBufferFrames;
192  mBuffer.resize(buffersize);
193  }
194 
195  if (!mSoundFile) { //if we don't have already a file open, do it now.
196  TTErr err = openFile();
197  if (err != kTTErrNone) // file didn't open, let's stop recording
198  return setRecord(0);
199  }
200 
201  for (channel=0; channel<channelCount; channel++) {
202  inSample = in.mSampleVectors[channel];
203  outSample = out.mSampleVectors[channel]; // sending audio out
204  for (n=0; n<vs; n++)
205  //mBuffer[progress+(n * channelCount + channel)] = inSample[n]; //sending audio to recording buffer
206  mBuffer[(n * channelCount + channel)] = inSample[n]; //sending audio to recording buffer
207  memcpy(outSample, inSample, sizeof(TTSampleValue) * vs);
208  }
209  //mCycles++;
210  //if (mCycles == 100){
211 
212  if (sf_writef_double(mSoundFile, &mBuffer[0], vs) != vs) {
213  char errorStr[256];
214  sf_error_str(mSoundFile, errorStr, 256);
215  TTLogError("Error while writing file: %s \n",errorStr);
216  }
217 
218  // mCycles = 0;
219  //}
220 
221  return kTTErrNone;
222 }
223 
224 
225 TTErr TTSoundfileRecorder::processTimedAudioRecording(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
226 {
227  TTAudioSignal& out = outputs->getSignal(0);
228  TTAudioSignal& in = inputs->getSignal(0);
229  TTSampleValuePtr outSample, inSample;
230  TTUInt16 channelCount = in.getNumChannelsAsInt();
231  TTUInt16 vs = in.getVectorSizeAsInt();
232  TTUInt16 n, channel;
233  TTBoolean bufferNeedsResize = NO;
234  //sf_count_t numSamplesWritten;
235  TTUInt64 progress = mCycles*vs*channelCount;
236 
237 
238  if (mNumChannels != channelCount) { // input channel number has changed, we need to set up the buffer
239  mNumChannels = channelCount;
240  bufferNeedsResize = YES;
241  }
242 
243  if (mNumBufferFrames != vs) { //vector size has changed, we need to set up the buffer
244  mNumBufferFrames = vs;
245  bufferNeedsResize = YES;
246  }
247  if (bufferNeedsResize){
248  TTUInt64 buffersize = mNumChannels * mNumBufferFrames * ceil((double)mLengthInSamples/(double)mNumBufferFrames);
249  mBuffer.resize(buffersize);
250  }
251 
252  if (!mSoundFile) {//if we don't have already a file open, do it now.
253  TTErr err = openFile();
254  if (err != kTTErrNone) // file didn't open, let's stop recording
255  return setRecord(0);
256  }
257 
258  for (channel=0; channel<channelCount; channel++) {
259  inSample = in.mSampleVectors[channel];
260  outSample = out.mSampleVectors[channel]; // sending audio out
261  for (n=0; n<vs; n++)
262  mBuffer[progress+(n * channelCount + channel)] = inSample[n]; //sending audio to recording buffer
263  memcpy(outSample, inSample, sizeof(TTSampleValue) * vs); // sending audio out
264  }
265 
266  mCycles++;
267  mLengthInSamples -= vs; // decreasing the samplecounter by a vs
268  if (mLengthInSamples <= 0){ //TODO: we might want to chop of the samples that were recorded too long
269  if (sf_writef_double(mSoundFile, &mBuffer[0], mCycles*vs) != mCycles*vs) {
270  char errorStr[256];
271  sf_error_str(mSoundFile, errorStr, 256);
272  TTLogError("Error while writing file: %s \n",errorStr);
273  }
274  return setRecord(0);
275  }
276  else return kTTErrNone;
277 }
278 
279 
280 TTErr TTSoundfileRecorder::processAudioBypass(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
281 {
282  TTAudioSignal& out = outputs->getSignal(0);
283  TTAudioSignal& in = inputs->getSignal(0);
284  TTSampleValuePtr outSample, inSample;
285  TTUInt16 channelCount = in.getNumChannelsAsInt();
286  TTUInt16 vs = in.getVectorSizeAsInt();
287  TTUInt16 channel;
288 
289 
290  // not recording, just bypassing audio and return
291  for (channel=0; channel<channelCount; channel++) {
292  inSample = in.mSampleVectors[channel];
293  outSample = out.mSampleVectors[channel]; // sending audio out
294  memcpy(outSample, inSample, sizeof(TTSampleValue) * vs); // sending audio out
295  }
296  return kTTErrNone;
297 }
298 
299 
300 
301 
302 
303 
304 
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
SNDFILE * mSoundFile
libsndfile handle for the actual file we open
std::uint64_t TTUInt64
64 bit unsigned integer
Definition: TTBase.h:180
#define addAttribute(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom getter...
Definition: TTAttribute.h:29
Jamoma DSP Soundfile Recorder.
#define setProcessMethod(methodName)
A convenience macro to be used by subclasses for setting the process method.
TTErr setAttributeValue(const TTSymbol name, TTValue &value)
Set an attribute value for an object.
TTSymbol mFormat
format of the file, e.g. "WAV", "AIFF", "FLAC", "FLAC-16bit", etc.
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
TTSymbol mFilePath
full POSIX path to the file, including file name
16-bit unsigned integer, range is 0 through 65,535.
Definition: TTBase.h:276
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
64-bit floating point
Definition: TTBase.h:272
#define addAttributeProperty(attributeName, propertyName, initialValue)
A convenience macro to be used for registering properties of attributes.
Definition: TTAttribute.h:68
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void TTFOUNDATION_EXPORT TTLogError(TTImmutableCString message,...)
Platform and host independent method for posting errors.
Definition: TTBase.cpp:572
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
The TTAudioSignal class represents N vectors of audio samples for M channels.
Definition: TTAudioSignal.h:57
TTFloat64 mLength
length of the file in ms
TTSampleValue ** mSampleVectors
An array of pointers to the first sample in each vector. Declared Public for fast access...
Definition: TTAudioSignal.h:74
const char * c_str() const
Return a pointer to the internal string as a C-string.
Definition: TTSymbol.h:77
TTUInt16 mNumChannels
read-only: number of channels in the open file
A simple container for an array of TTAudioSignal pointers.
void TTFOUNDATION_EXPORT TTLogMessage(TTImmutableCString message,...)
Platform and host independent method for posting log messages.
Definition: TTBase.cpp:534
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
SF_INFO mSoundFileInfo
libsndfile metadata for the file we open
TTUInt16 mNumBufferFrames
number of frames in the buffer to be read from the file at a time
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
No Error.
Definition: TTBase.h:343
TTSampleVector mBuffer
buffer of mNumBufferFrames * mNumChannels;
TTBoolean mRecord
is actively recording the file?
TTFloat64 TTSampleValue
A value representing a single audio sample.
Definition: TTBase.h:230
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTInt32 mLengthInSamples
length of the file in samples
TTUInt32 sr
Current sample rate being used by this object.
TTErr setRecord(const TTValue &value)
Setter.