AJA NTV2 SDK  17.5.0.1242
NTV2 SDK 17.5.0.1242
wavewriter.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
8 #include "wavewriter.h"
9 
12 #include <time.h>
13 #include <assert.h>
14 
15 #if defined(AJA_LINUX) || defined(AJA_BAREMETAL)
16 #include <string.h>
17 #include <stdint.h>
18 #ifndef UINT32_C
19 #define UINT32_C(x) x##U
20 #endif
21 #endif
22 
23 const int sizeOf_riff = 12;
24 const int sizeOf_bext_v1= 610;
25 const int sizeOf_fmt = 24;
26 const int sizeOf_data = 8;
27 
28 #if defined(AJA_LITTLE_ENDIAN)
29 #define AjaWavLittleEndianHw
30 #else
31 #define AjaWavBigEndianHw
32 #endif
33 
34 #define AjaWavSwap16(x) ( ((uint16_t(x) & 0xFF00) >> 8) | \
35  ((uint16_t(x) & 0x00FF) << 8) )
36 
37 #define AjaWavSwap32(x) ( ((uint32_t(x) & 0xFF000000) >> 24) | \
38  ((uint32_t(x) & 0x00FF0000) >> 8) | \
39  ((uint32_t(x) & 0x0000FF00) << 8) | \
40  ((uint32_t(x) & 0x000000FF) << 24) )
41 
42 #define AjaWavSwap64(x) ( ((uint64_t(x) & 0xFF00000000000000ULL) >> 56) | \
43  ((uint64_t(x) & 0x00FF000000000000ULL) >> 40) | \
44  ((uint64_t(x) & 0x0000FF0000000000ULL) >> 24) | \
45  ((uint64_t(x) & 0x000000FF00000000ULL) >> 8) | \
46  ((uint64_t(x) & 0x00000000FF000000ULL) << 8) | \
47  ((uint64_t(x) & 0x0000000000FF0000ULL) << 24) | \
48  ((uint64_t(x) & 0x000000000000FF00ULL) << 40) | \
49  ((uint64_t(x) & 0x00000000000000FFULL) << 56) )
50 
51 #ifdef AjaWavBigEndianHw
52  #define AjaWavBigEndian16(x) (x)
53  #define AjaWavLittleEndian16(x) AjaWavSwap16(x)
54  #define AjaWavBigEndian32(x) (x)
55  #define AjaWavLittleEndian32(x) AjaWavSwap32(x)
56  #define AjaWavBigEndian64(x) (x)
57  #define AjaWavLittleEndian64(x) AjaWavSwap64(x)
58 #else
59  #define AjaWavLittleEndian16(x) (x)
60  #define AjaWavBigEndian16(x) AjaWavSwap16(x)
61  #define AjaWavLittleEndian32(x) (x)
62  #define AjaWavBigEndian32(x) AjaWavSwap32(x)
63  #define AjaWavLittleEndian64(x) (x)
64  #define AjaWavBigEndian64(x) AjaWavSwap64(x)
65 #endif
66 
67 static void getDataAndTimeInBextFormat(std::string& formattedDate, std::string& formattedTime)
68 {
69  char tmp[16];
70  time_t now = time(NULL);
71  struct tm lcl = *localtime(&now);
72  ::strftime(tmp,16,"%Y:%m:%d",&lcl); //"yyyy:mm:dd"
73  formattedDate.assign(tmp);
74 
75  ::strftime(tmp,16,"%H:%M:%S",&lcl); //"hh:mm:ss"
76  formattedTime.assign(tmp);
77 }
78 
79 AJAWavWriter::AJAWavWriter(const std::string & name, const AJAWavWriterAudioFormat & audioFormat, const AJAWavWriterVideoFormat & videoFormat,
80  const std::string & startTimecode, AJAWavWriterChunkFlag flags,
81  bool useFloatNotPCM)
82 : AJAFileIO(), mFileName(name), mAudioFormat(audioFormat), mVideoFormat(videoFormat), mStartTimecode(startTimecode), mFlags(flags), mLittleEndian(true),
83  mUseFloatData(useFloatNotPCM)
84 {
85  mSizeOfHeader = sizeOf_riff + sizeOf_fmt + sizeOf_data;
86 
87  if (mFlags & AJAWavWriterChunkFlagBextV1)
88  {
89  mSizeOfHeader += sizeOf_bext_v1;
90  }
91 }
92 
93 // making this call not public
94 AJAStatus AJAWavWriter::Open(const std::string& fileName, int flags, AJAFileProperties properties)
95 {
96  return AJAFileIO::Open(fileName,flags,properties);
97 }
98 
99 // making this call not public
101 {
102  return AJAFileIO::Close();
103 }
104 
106 {
107  bool retVal = false;
109  if(result == AJA_STATUS_SUCCESS)
110  {
111  writeHeader();
112  retVal = true;
113  }
114 
115  return retVal;
116 }
117 
118 uint32_t AJAWavWriter::write(const char* data, uint32_t len)
119 {
120  return writeRawData(data,len);
121 }
122 
123 uint32_t AJAWavWriter::writeRawData(const char* data,uint32_t len)
124 {
125  return writeRawData((char*)data,len);
126 }
127 
128 uint32_t AJAWavWriter::writeRawData(char* data,uint32_t len)
129 {
130  return Write((uint8_t*)data,len);
131 }
132 
133 uint32_t AJAWavWriter::writeRaw_uint8_t(uint8_t value, uint32_t count)
134 {
135  uint32_t bytesWritten = 0;
136 
137  for(uint32_t i=0;i<count;i++)
138  {
139  bytesWritten += Write(&value,1);
140  }
141 
142  return bytesWritten;
143 }
144 
145 uint32_t AJAWavWriter::writeRaw_uint16_t(uint16_t value, uint32_t count)
146 {
147  uint32_t bytesWritten = 0;
148 
149  if(mLittleEndian)
150  value = AjaWavLittleEndian16(value);
151  else
152  value = AjaWavBigEndian16(value);
153 
154  for(uint32_t i=0;i<count;i++)
155  {
156  bytesWritten += Write((uint8_t*)&value, sizeof(uint16_t));
157  }
158 
159  return bytesWritten;
160 }
161 
162 uint32_t AJAWavWriter::writeRaw_uint32_t(uint32_t value, uint32_t count)
163 {
164  uint32_t bytesWritten = 0;
165 
166  if(mLittleEndian)
167  value = AjaWavLittleEndian32(value);
168  else
169  value = AjaWavBigEndian32(value);
170 
171  for(uint32_t i=0;i<count;i++)
172  {
173  bytesWritten += Write((uint8_t*)&value, sizeof(uint32_t));
174  }
175 
176  return bytesWritten;
177 }
178 
179 void AJAWavWriter::writeHeader()
180 {
181  mLittleEndian = true;
182 
183  uint32_t wtn = 0;
184 
185  // RIFF chunk
186  wtn += writeRawData("RIFF", 4);
187  wtn += writeRaw_uint32_t(0); // Placeholder for the RIFF chunk size (filled by close())
188  wtn += writeRawData("WAVE", 4);
189 
190  if (mFlags & AJAWavWriterChunkFlagBextV1)
191  {
192  // bext chunk (version 1)
193  // Calculate the sequence timecode start values
194  // These are calculated as:
195  // Start timecode converted to seconds * audio sample rate
196  // then split into a High and Low
197 
198  uint32_t scale = mVideoFormat.rateScale;
199  uint32_t duration = mVideoFormat.rateDuration;
200  //according to Canon they always use 1000 for 23.98, just go with it
201  if(scale == 24000 && duration == 1001)
202  duration = 1000;
203 
204  AJATimeBase tb(scale,duration);
205  tb.SetAudioRate(mAudioFormat.sampleRate);
206 
207  AJATimeCode tc(mStartTimecode.c_str(), tb);
208 
209  uint64_t frames = tc.QueryFrame();
210  uint64_t numAudioSamplesPerSecond = tb.FramesToSamples(frames);
211 
212  uint32_t startSequenceTcLow = numAudioSamplesPerSecond & 0x00000000FFFFFFFF;
213  uint32_t startSequenceTcHigh = numAudioSamplesPerSecond >> 32;
214 
215  std::string formattedDate;
216  std::string formattedTime;
217  getDataAndTimeInBextFormat(formattedDate, formattedTime);
218 
219  char emptyBuf[256];
220  memset(emptyBuf,0,256);
221 
222  wtn += writeRawData("bext",4);
223  wtn += writeRaw_uint32_t(602); //size of extension chunk
224  wtn += writeRawData(emptyBuf,256); //description of sound sequence
225  wtn += writeRawData(emptyBuf,32); //name of originator
226  wtn += writeRawData(emptyBuf,32); //reference of originator
227  wtn += writeRawData(formattedDate.c_str(),10); //origination date <<yyyy:mm:dd>>
228  wtn += writeRawData(formattedTime.c_str(),8); //origination time <<hh:mm:ss>>
229  wtn += writeRaw_uint32_t(startSequenceTcLow); //timecode of sequence (low)
230  wtn += writeRaw_uint32_t(startSequenceTcHigh); //timecode of sequence (high)
231  wtn += writeRaw_uint16_t(1); //BWF version
232  wtn += writeRaw_uint8_t(0,64); //U8 * 64, From UMID_0 to UMID_63
233  wtn += writeRaw_uint8_t(0,190); //U8 * 190 reserved
234  }
235 
236  // Format description chunk
237  wtn += writeRawData("fmt ", 4);
238  wtn += writeRaw_uint32_t(16); // "fmt " chunk size (always 16 for PCM)
239  int audioFormat = 1; //PCM
240  if (mUseFloatData)
241  {
242  audioFormat = 3; //IEEE Float
243  }
244  wtn += writeRaw_uint16_t(audioFormat); // data format (1 => PCM), (3 => IEEE Float)
245  wtn += writeRaw_uint16_t(mAudioFormat.channelCount);
246  wtn += writeRaw_uint32_t(mAudioFormat.sampleRate);
247  wtn += writeRaw_uint32_t(mAudioFormat.sampleRate * mAudioFormat.channelCount * mAudioFormat.sampleSize / 8 ); // bytes per second
248  wtn += writeRaw_uint16_t(mAudioFormat.channelCount * mAudioFormat.sampleSize / 8); // Block align
249  wtn += writeRaw_uint16_t(mAudioFormat.sampleSize); // Significant Bits Per Sample
250 
251  // data chunk
252  wtn += writeRawData("data", 4);
253  wtn += writeRaw_uint32_t(0); // Placeholder for the data chunk size (filled by close())
254  AJA_UNUSED(wtn);
255  assert(Tell() == mSizeOfHeader);
256 }
257 
259 {
260  // Fill the header size placeholders
261  uint32_t fileSize = (uint32_t)Tell();
262 
263  mLittleEndian = true;
264 
265  // RIFF chunk size
266  Seek(4,eAJASeekSet);
267  writeRaw_uint32_t(fileSize - sizeOf_data);
268 
269  // data chunk size
270  Seek(mSizeOfHeader-4,eAJASeekSet);
271  writeRaw_uint32_t(fileSize - mSizeOfHeader);
272 
273  Close();
274 }
AJAWavWriter::Open
AJAStatus Open(const std::string &fileName, int flags, AJAFileProperties properties)
Definition: wavewriter.cpp:94
nlohmann::json_abiNLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON_v3_11_NLOHMANN_JSON_VERSION_PATCH::detail::parse_event_t::value
@ value
the parser finished reading a JSON value
AJAFileIO::Write
uint32_t Write(const uint8_t *pBuffer, const uint32_t length) const
Definition: file_io.cpp:380
sizeOf_fmt
const int sizeOf_fmt
Definition: wavewriter.cpp:25
sizeOf_bext_v1
const int sizeOf_bext_v1
Definition: wavewriter.cpp:24
sizeOf_riff
const int sizeOf_riff
Definition: wavewriter.cpp:23
AJAWavWriter::AJAWavWriter
AJAWavWriter(const std::string &name, const AJAWavWriterAudioFormat &audioFormat=AJAWavWriterAudioFormat(), const AJAWavWriterVideoFormat &videoFormat=AJAWavWriterVideoFormat(), const std::string &startTimecode="00:00:00;00", AJAWavWriterChunkFlag flags=AJAWavWriterChunkFlagStandard, bool useFloatNotPCM=false)
Definition: wavewriter.cpp:79
AJAWavWriterAudioFormat::channelCount
int channelCount
Definition: wavewriter.h:24
sizeOf_data
const int sizeOf_data
Definition: wavewriter.cpp:26
NULL
#define NULL
Definition: ntv2caption608types.h:19
eAJAWriteOnly
@ eAJAWriteOnly
Definition: file_io.h:32
eAJACreateAlways
@ eAJACreateAlways
Definition: file_io.h:27
AJA_STATUS_SUCCESS
@ AJA_STATUS_SUCCESS
Definition: types.h:381
AJAWavWriter::close
void close()
Definition: wavewriter.cpp:258
wavewriter.h
Declares the AJAWavWriter class.
AJAWavWriterAudioFormat::sampleSize
int sampleSize
Definition: wavewriter.h:26
timecode.h
Declares the AJATimeCode class.
AJA_UNUSED
#define AJA_UNUSED(_x_)
Definition: types.h:424
AJAWavWriterVideoFormat::rateDuration
uint32_t rateDuration
Definition: wavewriter.h:34
AJAWavWriterAudioFormat
Definition: wavewriter.h:15
AJAFileIO::Open
AJAStatus Open(const std::string &fileName, const int flags, const int properties)
Definition: file_io.cpp:201
AJAStatus
AJAStatus
Definition: types.h:378
AJAWavWriterAudioFormat::sampleRate
int sampleRate
Definition: wavewriter.h:25
AJAWavWriter::open
bool open()
Definition: wavewriter.cpp:105
AJATimeBase
Definition: timebase.h:18
AJAFileIO::Seek
AJAStatus Seek(const int64_t distance, const AJAFileSetFlag flag) const
Definition: file_io.cpp:545
AJAWavWriterChunkFlag
AJAWavWriterChunkFlag
Definition: wavewriter.h:39
getDataAndTimeInBextFormat
static void getDataAndTimeInBextFormat(std::string &formattedDate, std::string &formattedTime)
Definition: wavewriter.cpp:67
AjaWavBigEndian32
#define AjaWavBigEndian32(x)
Definition: wavewriter.cpp:54
AJAWavWriterVideoFormat
Definition: wavewriter.h:30
AJAWavWriter::Close
AJAStatus Close(void)
Definition: wavewriter.cpp:100
AJAWavWriterVideoFormat::rateScale
uint32_t rateScale
Definition: wavewriter.h:35
AjaWavLittleEndian32
#define AjaWavLittleEndian32(x)
Definition: wavewriter.cpp:55
AJAFileIO::Close
AJAStatus Close()
Definition: file_io.cpp:281
AJAFileProperties
AJAFileProperties
Definition: file_io.h:37
eAJABuffered
@ eAJABuffered
Definition: file_io.h:39
AJAFileIO::Tell
int64_t Tell()
Definition: file_io.cpp:503
true
#define true
Definition: ntv2devicefeatures.h:26
AJAFileIO
Definition: file_io.h:64
AjaWavBigEndian16
#define AjaWavBigEndian16(x)
Definition: wavewriter.cpp:52
eAJASeekSet
@ eAJASeekSet
Definition: file_io.h:47
AJAWavWriter::write
uint32_t write(const char *data, uint32_t len)
Definition: wavewriter.cpp:118
AjaWavLittleEndian16
#define AjaWavLittleEndian16(x)
Definition: wavewriter.cpp:53
AJAWavWriterChunkFlagBextV1
@ AJAWavWriterChunkFlagBextV1
Definition: wavewriter.h:42
AJATimeCode
Utility class for timecodes.
Definition: timecode.h:17
timebase.h
Declares the AJATimeBase class.