Audacity  3.0.3
SimpleBlockFile.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  SimpleBlockFile.cpp
6 
7  Joshua Haberman
8  Markus Meyer
9 
10 *******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
37 
38 #include "../Audacity.h"
39 #include "SimpleBlockFile.h"
40 
41 #include <wx/wx.h>
42 #include <wx/filefn.h>
43 #include <wx/ffile.h>
44 #include <wx/utils.h>
45 #include <wx/log.h>
46 
47 #include "../DirManager.h"
48 #include "../Prefs.h"
49 
50 #include "../FileFormats.h"
51 
52 #include "sndfile.h"
53 
54 
55 static wxUint32 SwapUintEndianess(wxUint32 in)
56 {
57  wxUint32 out;
58  unsigned char *p_in = (unsigned char *) &in;
59  unsigned char *p_out = (unsigned char *) &out;
60  p_out[0] = p_in[3];
61  p_out[1] = p_in[2];
62  p_out[2] = p_in[1];
63  p_out[3] = p_in[0];
64  return out;
65 }
66 
78  samplePtr sampleData, size_t sampleLen,
80  BlockFile {
81  (baseFileName.SetExt(wxT("au")), std::move(baseFileName)),
82  sampleLen
83  }
84 {
85  mFormat = format;
86 
87  bool bSuccess = WriteSimpleBlockFile(sampleData, sampleLen, format, NULL);
88  if (!bSuccess)
89  throw FileException{
90  FileException::Cause::Write, GetFileName().name };
91 }
92 
98  float min, float max, float rms):
99  BlockFile{ std::move(existingFile), len }
100 {
101  // Set an invalid format to force GetSpaceUsage() to read it from the file.
102  mFormat = (sampleFormat) 0;
103 
104  mMin = min;
105  mMax = max;
106  mRMS = rms;
107 }
108 
110 {
111 }
112 
114  samplePtr sampleData,
115  size_t sampleLen,
117  void* summaryData)
118 {
119  wxFFile file(mFileName.GetFullPath(), wxT("wb"));
120  if( !file.IsOpened() ){
121  // Can't do anything else.
122  return false;
123  }
124 
125  auHeader header;
126 
127  // AU files can be either big or little endian. Which it is is
128  // determined implicitly by the byte-order of the magic 0x2e736e64
129  // (.snd). We want it to be native-endian, so we write the magic
130  // to memory and then let it write that to a file in native
131  // endianness
132  header.magic = 0x2e736e64;
133 
134  // We store the summary data at the end of the header, so the data
135  // offset is the length of the summary data plus the length of the header
136  header.dataOffset = sizeof(auHeader) + mSummaryInfo.totalSummaryBytes;
137 
138  // dataSize is optional, and we opt out
139  header.dataSize = 0xffffffff;
140 
141  switch(format) {
142  case int16Sample:
143  header.encoding = AU_SAMPLE_FORMAT_16;
144  break;
145 
146  case int24Sample:
147  header.encoding = AU_SAMPLE_FORMAT_24;
148  break;
149 
150  case floatSample:
152  break;
153  }
154 
155  // TODO: don't fabricate
156  header.sampleRate = 44100;
157 
158  // BlockFiles are always mono
159  header.channels = 1;
160 
161  // Write the file
162  ArrayOf<char> cleanup;
163  if (!summaryData)
164  summaryData = /*BlockFile::*/CalcSummary(sampleData, sampleLen, format, cleanup);
165  // PRL: cleanup fixes a possible memory leak!
166 
167  size_t nBytesToWrite = sizeof(header);
168  size_t nBytesWritten = file.Write(&header, nBytesToWrite);
169  if (nBytesWritten != nBytesToWrite)
170  {
171  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
172  return false;
173  }
174 
175  nBytesToWrite = mSummaryInfo.totalSummaryBytes;
176  nBytesWritten = file.Write(summaryData, nBytesToWrite);
177  if (nBytesWritten != nBytesToWrite)
178  {
179  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
180  return false;
181  }
182 
183  if( format == int24Sample )
184  {
185  // we can't write the buffer directly to disk, because 24-bit samples
186  // on disk need to be packed, not padded to 32 bits like they are in
187  // memory
188  int *int24sampleData = (int*)sampleData;
189 
190  for( size_t i = 0; i < sampleLen; i++ )
191  {
192  nBytesToWrite = 3;
193  nBytesWritten =
194  #if wxBYTE_ORDER == wxBIG_ENDIAN
195  file.Write((char*)&int24sampleData[i] + 1, nBytesToWrite);
196  #else
197  file.Write((char*)&int24sampleData[i], nBytesToWrite);
198  #endif
199  if (nBytesWritten != nBytesToWrite)
200  {
201  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
202  return false;
203  }
204  }
205  }
206  else
207  {
208  // for all other sample formats we can write straight from the buffer
209  // to disk
210  nBytesToWrite = sampleLen * SAMPLE_SIZE(format);
211  nBytesWritten = file.Write(sampleData, nBytesToWrite);
212  if (nBytesWritten != nBytesToWrite)
213  {
214  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
215  return false;
216  }
217  }
218 
219  return true;
220 }
221 
227 {
228  data.reinit( mSummaryInfo.totalSummaryBytes );
229  //wxLogDebug("SimpleBlockFile::ReadSummary(): Reading summary from disk.");
230 
231  wxFFile file(mFileName.GetFullPath(), wxT("rb"));
232 
233  {
234  Optional<wxLogNull> silence{};
235  if (mSilentLog)
236  silence.emplace();
237  // FIXME: TRAP_ERR no report to user of absent summary files?
238  // filled with zero instead.
239  if (!file.IsOpened()){
240  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
241  mSilentLog = TRUE;
242  return false;
243  }
244  }
245  mSilentLog = FALSE;
246 
247  // The offset is just past the au header
248  if( !file.Seek(sizeof(auHeader)) ||
249  file.Read(data.get(), mSummaryInfo.totalSummaryBytes) !=
250  mSummaryInfo.totalSummaryBytes ) {
251  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
252  return false;
253  }
254 
255  FixSummary(data.get());
256 
257  return true;
258 }
259 
268  size_t start, size_t len, bool mayThrow) const
269 {
270  return CommonReadData( mayThrow,
271  mFileName, mSilentLog, nullptr, 0, 0, data, format, start, len);
272 }
273 
275 // may throw
276 {
277  xmlFile.StartTag(wxT("simpleblockfile"));
278 
279  xmlFile.WriteAttr(wxT("filename"), mFileName.GetFullName());
280  xmlFile.WriteAttr(wxT("len"), mLen);
281  xmlFile.WriteAttr(wxT("min"), mMin);
282  xmlFile.WriteAttr(wxT("max"), mMax);
283  xmlFile.WriteAttr(wxT("rms"), mRMS);
284 
285  xmlFile.EndTag(wxT("simpleblockfile"));
286 }
287 
288 // BuildFromXML methods should always return a BlockFile, not NULL,
289 // even if the result is flawed (e.g., refers to nonexistent file),
290 // as testing will be done in ProjectFSCK().
292 BlockFilePtr SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
293 {
294  wxFileNameWrapper fileName;
295  float min = 0.0f, max = 0.0f, rms = 0.0f;
296  size_t len = 0;
297  double dblValue;
298  long nValue;
299 
300  while(*attrs)
301  {
302  const wxChar *attr = *attrs++;
303  const wxChar *value = *attrs++;
304  if (!value)
305  break;
306 
307  const wxString strValue = value;
308  if (!wxStricmp(attr, wxT("filename")) &&
309  // Can't use XMLValueChecker::IsGoodFileName here, but do part of its test.
311  (strValue.length() + 1 + dm.GetProjectDataDir().length() <= PLATFORM_MAX_PATH))
312  {
313  if (!dm.AssignFile(fileName, strValue, false))
314  // Make sure fileName is back to uninitialized state so we can detect problem later.
315  fileName.Clear();
316  }
317  else if (!wxStrcmp(attr, wxT("len")) &&
318  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue) &&
319  nValue > 0)
320  len = nValue;
321  else if (XMLValueChecker::IsGoodString(strValue) && Internat::CompatibleToDouble(strValue, &dblValue))
322  { // double parameters
323  if (!wxStricmp(attr, wxT("min")))
324  min = dblValue;
325  else if (!wxStricmp(attr, wxT("max")))
326  max = dblValue;
327  else if (!wxStricmp(attr, wxT("rms")) && (dblValue >= 0.0))
328  rms = dblValue;
329  }
330  }
331 
332  return make_blockfile<SimpleBlockFile>
333  (std::move(fileName), len, min, max, rms);
334 }
335 
339 BlockFilePtr SimpleBlockFile::Copy(wxFileNameWrapper &&newFileName)
340 {
341  auto newBlockFile = make_blockfile<SimpleBlockFile>
342  (std::move(newFileName), mLen, mMin, mMax, mRMS);
343 
344  return newBlockFile;
345 }
346 
347 auto SimpleBlockFile::GetSpaceUsage() const -> DiskByteCount
348 {
349  // Don't know the format, so it must be read from the file
350  if (mFormat == (sampleFormat) 0)
351  {
352  // Check sample format
353  wxFFile file(mFileName.GetFullPath(), wxT("rb"));
354  if (!file.IsOpened())
355  {
356  return 0;
357  }
358 
359  auHeader header;
360 
361  if (file.Read(&header, sizeof(header)) != sizeof(header))
362  {
363  // Corrupt file
364  return 0;
365  }
366 
367  wxUint32 encoding;
368 
369  if (header.magic == 0x2e736e64)
370  encoding = header.encoding; // correct endianness
371  else
372  encoding = SwapUintEndianess(header.encoding);
373 
374  switch (encoding)
375  {
376  case AU_SAMPLE_FORMAT_16:
378  break;
379  case AU_SAMPLE_FORMAT_24:
381  break;
382  default:
383  // floatSample is a safe default (we will never loose data)
385  break;
386  }
387 
388  file.Close();
389  }
390 
391  return (
392  sizeof(auHeader) +
393  mSummaryInfo.totalSummaryBytes +
394  (GetLength() * SAMPLE_SIZE_DISK(mFormat))
395  );
396 }
397 
399  wxFFile file(mFileName.GetFullPath(), wxT("wb"));
400 
401  if( !file.IsOpened() ){
402  // Can't do anything else.
403  return;
404  }
405 
406  auHeader header;
407  header.magic = 0x2e736e64;
408  header.dataOffset = sizeof(auHeader) + mSummaryInfo.totalSummaryBytes;
409 
410  // dataSize is optional, and we opt out
411  header.dataSize = 0xffffffff;
412  header.encoding = AU_SAMPLE_FORMAT_16;
413  header.sampleRate = 44100;
414  header.channels = 1;
415  file.Write(&header, sizeof(header));
416 
417  for(decltype(mSummaryInfo.totalSummaryBytes) i = 0;
418  i < mSummaryInfo.totalSummaryBytes; i++)
419  file.Write(wxT("\0"),1);
420 
421  for(decltype(mLen) i = 0; i < mLen * 2; i++)
422  file.Write(wxT("\0"),1);
423 
424 }
425 
426 static DirManager::RegisteredBlockFileDeserializer sRegistration {
427  "simpleblockfile",
428  []( DirManager &dm, const wxChar **attrs ){
429  return SimpleBlockFile::BuildFromXML( dm, attrs );
430  }
431 };
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
XMLWriter::EndTag
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:99
SimpleBlockFile::GetSpaceUsage
DiskByteCount GetSpaceUsage() const override
Definition: SimpleBlockFile.cpp:347
Optional::emplace
X & emplace(Args &&... args)
Definition: MemoryX.h:193
AU_SAMPLE_FORMAT_FLOAT
@ AU_SAMPLE_FORMAT_FLOAT
Definition: SimpleBlockFile.h:22
Optional
Like a smart pointer, allows for object to not exist (nullptr)
Definition: MemoryX.h:144
wxFileNameWrapper
Definition: wxFileNameWrapper.h:21
XMLValueChecker::IsGoodInt
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
Definition: XMLTagHandler.cpp:157
SAMPLE_SIZE_DISK
#define SAMPLE_SIZE_DISK(SampleFormat)
Return the size on disk of one uncompressed sample (bytes)
Definition: SampleFormat.h:59
FileException::Cause::Write
@ Write
most important to detect when storage space is exhausted
auHeader::dataOffset
wxUint32 dataOffset
Definition: SimpleBlockFile.h:27
SimpleBlockFile::SaveXML
void SaveXML(XMLWriter &xmlFile) override
Write an XML representation of this file.
Definition: SimpleBlockFile.cpp:274
auHeader::dataSize
wxUint32 dataSize
Definition: SimpleBlockFile.h:28
ArrayOf::reinit
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
FileException
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
auHeader::channels
wxUint32 channels
Definition: SimpleBlockFile.h:31
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
SimpleBlockFile::Copy
BlockFilePtr Copy(wxFileNameWrapper &&newFileName) override
Create a NEW block file identical to this one.
Definition: SimpleBlockFile.cpp:339
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
SimpleBlockFile::mFormat
sampleFormat mFormat
Definition: SimpleBlockFile.h:73
XMLValueChecker::IsGoodString
static bool IsGoodString(const wxString &str)
Definition: XMLTagHandler.cpp:38
floatSample
@ floatSample
Definition: SampleFormat.h:34
auHeader::sampleRate
wxUint32 sampleRate
Definition: SimpleBlockFile.h:30
SimpleBlockFile::SimpleBlockFile
SimpleBlockFile(wxFileNameWrapper &&baseFileName, samplePtr sampleData, size_t sampleLen, sampleFormat format)
Create a disk file and write summary and sample data to it.
Definition: SimpleBlockFile.cpp:77
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
SimpleBlockFile::ReadSummary
bool ReadSummary(ArrayOf< char > &data) override
Read the summary section of the disk file.
Definition: SimpleBlockFile.cpp:226
SimpleBlockFile::ReadData
size_t ReadData(samplePtr data, sampleFormat format, size_t start, size_t len, bool mayThrow) const override
Read the data section of the disk file.
Definition: SimpleBlockFile.cpp:267
format
int format
Definition: ExportPCM.cpp:56
SwapUintEndianess
static wxUint32 SwapUintEndianess(wxUint32 in)
Definition: SimpleBlockFile.cpp:55
XMLValueChecker::IsGoodFileString
static bool IsGoodFileString(const FilePath &str)
Definition: XMLTagHandler.cpp:70
SimpleBlockFile.h
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
SimpleBlockFile::WriteSimpleBlockFile
bool WriteSimpleBlockFile(samplePtr sampleData, size_t sampleLen, sampleFormat format, void *summaryData)
Definition: SimpleBlockFile.cpp:113
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
auHeader::magic
wxUint32 magic
Definition: SimpleBlockFile.h:26
XMLWriter::WriteAttr
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:34
SimpleBlockFile::Recover
void Recover() override
Definition: SimpleBlockFile.cpp:398
PLATFORM_MAX_PATH
#define PLATFORM_MAX_PATH
Definition: FileNames.h:22
auHeader
The auHeader is a structure used by SimpleBlockFile for .au file format. There probably is an 'offici...
Definition: SimpleBlockFile.h:25
AU_SAMPLE_FORMAT_24
@ AU_SAMPLE_FORMAT_24
Definition: SimpleBlockFile.h:21
SimpleBlockFile::BuildFromXML
static BlockFilePtr BuildFromXML(DirManager &dm, const wxChar **attrs)
static
Definition: SimpleBlockFile.cpp:292
sRegistration
static DirManager::RegisteredBlockFileDeserializer sRegistration
Definition: SimpleBlockFile.cpp:426
SimpleBlockFile::~SimpleBlockFile
virtual ~SimpleBlockFile()
Definition: SimpleBlockFile.cpp:109
ArrayOf< char >
XMLWriter::StartTag
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:76
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
AU_SAMPLE_FORMAT_16
@ AU_SAMPLE_FORMAT_16
Definition: SimpleBlockFile.h:20
auHeader::encoding
wxUint32 encoding
Definition: SimpleBlockFile.h:29