Audacity  2.2.2
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 *******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
59 
60 #include "../Audacity.h"
61 #include "SimpleBlockFile.h"
62 
63 #include <wx/wx.h>
64 #include <wx/filefn.h>
65 #include <wx/ffile.h>
66 #include <wx/utils.h>
67 #include <wx/log.h>
68 
69 #include "../FileException.h"
70 #include "../Prefs.h"
71 
72 #include "../FileFormats.h"
73 
74 #include "sndfile.h"
75 #include "../Internat.h"
76 #include "../MemoryX.h"
77 
78 
79 static wxUint32 SwapUintEndianess(wxUint32 in)
80 {
81  wxUint32 out;
82  unsigned char *p_in = (unsigned char *) &in;
83  unsigned char *p_out = (unsigned char *) &out;
84  p_out[0] = p_in[3];
85  p_out[1] = p_in[2];
86  p_out[2] = p_in[1];
87  p_out[3] = p_in[0];
88  return out;
89 }
90 
102  samplePtr sampleData, size_t sampleLen,
103  sampleFormat format,
104  bool allowDeferredWrite /* = false */,
105  bool bypassCache /* = false */):
106  BlockFile {
107  (baseFileName.SetExt(wxT("au")), std::move(baseFileName)),
108  sampleLen
109  }
110 {
111  mFormat = format;
112 
113  mCache.active = false;
114 
115  bool useCache = GetCache() && (!bypassCache);
116 
117  if (!(allowDeferredWrite && useCache) && !bypassCache)
118  {
119  bool bSuccess = WriteSimpleBlockFile(sampleData, sampleLen, format, NULL);
120  if (!bSuccess)
121  throw FileException{
123  }
124 
125  if (useCache) {
126  //wxLogDebug("SimpleBlockFile::SimpleBlockFile(): Caching block file data.");
127  mCache.active = true;
128  mCache.needWrite = true;
129  mCache.format = format;
130  const auto sampleDataSize = sampleLen * SAMPLE_SIZE(format);
131  mCache.sampleData.reinit(sampleDataSize);
132  memcpy(mCache.sampleData.get(), sampleData, sampleDataSize);
133  ArrayOf<char> cleanup;
134  void* summaryData = BlockFile::CalcSummary(sampleData, sampleLen,
135  format, cleanup);
137  memcpy(mCache.summaryData.get(), summaryData,
139  }
140 }
141 
147  float min, float max, float rms):
148  BlockFile{ std::move(existingFile), len }
149 {
150  // Set an invalid format to force GetSpaceUsage() to read it from the file.
151  mFormat = (sampleFormat) 0;
152 
153  mMin = min;
154  mMax = max;
155  mRMS = rms;
156 
157  mCache.active = false;
158 }
159 
161 {
162 }
163 
165  samplePtr sampleData,
166  size_t sampleLen,
167  sampleFormat format,
168  void* summaryData)
169 {
170  wxFFile file(mFileName.GetFullPath(), wxT("wb"));
171  if( !file.IsOpened() ){
172  // Can't do anything else.
173  return false;
174  }
175 
176  auHeader header;
177 
178  // AU files can be either big or little endian. Which it is is
179  // determined implicitly by the byte-order of the magic 0x2e736e64
180  // (.snd). We want it to be native-endian, so we write the magic
181  // to memory and then let it write that to a file in native
182  // endianness
183  header.magic = 0x2e736e64;
184 
185  // We store the summary data at the end of the header, so the data
186  // offset is the length of the summary data plus the length of the header
188 
189  // dataSize is optional, and we opt out
190  header.dataSize = 0xffffffff;
191 
192  switch(format) {
193  case int16Sample:
194  header.encoding = AU_SAMPLE_FORMAT_16;
195  break;
196 
197  case int24Sample:
198  header.encoding = AU_SAMPLE_FORMAT_24;
199  break;
200 
201  case floatSample:
203  break;
204  }
205 
206  // TODO: don't fabricate
207  header.sampleRate = 44100;
208 
209  // BlockFiles are always mono
210  header.channels = 1;
211 
212  // Write the file
213  ArrayOf<char> cleanup;
214  if (!summaryData)
215  summaryData = /*BlockFile::*/CalcSummary(sampleData, sampleLen, format, cleanup);
216  //mchinen:allowing virtual override of calc summary for ODDecodeBlockFile.
217  // PRL: cleanup fixes a possible memory leak!
218 
219  size_t nBytesToWrite = sizeof(header);
220  size_t nBytesWritten = file.Write(&header, nBytesToWrite);
221  if (nBytesWritten != nBytesToWrite)
222  {
223  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
224  return false;
225  }
226 
227  nBytesToWrite = mSummaryInfo.totalSummaryBytes;
228  nBytesWritten = file.Write(summaryData, nBytesToWrite);
229  if (nBytesWritten != nBytesToWrite)
230  {
231  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
232  return false;
233  }
234 
235  if( format == int24Sample )
236  {
237  // we can't write the buffer directly to disk, because 24-bit samples
238  // on disk need to be packed, not padded to 32 bits like they are in
239  // memory
240  int *int24sampleData = (int*)sampleData;
241 
242  for( size_t i = 0; i < sampleLen; i++ )
243  {
244  nBytesToWrite = 3;
245  nBytesWritten =
246  #if wxBYTE_ORDER == wxBIG_ENDIAN
247  file.Write((char*)&int24sampleData[i] + 1, nBytesToWrite);
248  #else
249  file.Write((char*)&int24sampleData[i], nBytesToWrite);
250  #endif
251  if (nBytesWritten != nBytesToWrite)
252  {
253  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
254  return false;
255  }
256  }
257  }
258  else
259  {
260  // for all other sample formats we can write straight from the buffer
261  // to disk
262  nBytesToWrite = sampleLen * SAMPLE_SIZE(format);
263  nBytesWritten = file.Write(sampleData, nBytesToWrite);
264  if (nBytesWritten != nBytesToWrite)
265  {
266  wxLogDebug(wxT("Wrote %lld bytes, expected %lld."), (long long) nBytesWritten, (long long) nBytesToWrite);
267  return false;
268  }
269  }
270 
271  return true;
272 }
273 
274 // This function should try to fill the cache, but just return without effect
275 // (not throwing) if there is failure.
277 {
278  if (mCache.active)
279  return; // cache is already filled
280 
281  // Check sample format
282  wxFFile file(mFileName.GetFullPath(), wxT("rb"));
283  if (!file.IsOpened())
284  {
285  // Don't read into cache if file not available
286  return;
287  }
288 
289  auHeader header;
290 
291  if (file.Read(&header, sizeof(header)) != sizeof(header))
292  {
293  // Corrupt file
294  return;
295  }
296 
297  wxUint32 encoding;
298 
299  if (header.magic == 0x2e736e64)
300  encoding = header.encoding; // correct endianness
301  else
302  encoding = SwapUintEndianess(header.encoding);
303 
304  switch (encoding)
305  {
306  case AU_SAMPLE_FORMAT_16:
307  mCache.format = int16Sample;
308  break;
309  case AU_SAMPLE_FORMAT_24:
310  mCache.format = int24Sample;
311  break;
312  default:
313  // floatSample is a safe default (we will never loose data)
314  mCache.format = floatSample;
315  break;
316  }
317 
318  file.Close();
319 
320  // Read samples into cache
321  mCache.sampleData.reinit(mLen * SAMPLE_SIZE(mCache.format));
322  if (ReadData(mCache.sampleData.get(), mCache.format, 0, mLen,
323  // no exceptions!
324  false) != mLen)
325  {
326  // Could not read all samples
327  mCache.sampleData.reset();
328  return;
329  }
330 
331  // Read summary data into cache
332  // Fills with zeroes in case of failure:
334 
335  // Cache is active but already on disk
336  mCache.active = true;
337  mCache.needWrite = false;
338 
339  //wxLogDebug("SimpleBlockFile::FillCache(): Succesfully read simple block file into cache.");
340 }
341 
347 {
349  if (mCache.active) {
350  //wxLogDebug("SimpleBlockFile::ReadSummary(): Summary is already in cache.");
351  memcpy(data.get(), mCache.summaryData.get(), mSummaryInfo.totalSummaryBytes);
352  return true;
353  }
354  else
355  {
356  //wxLogDebug("SimpleBlockFile::ReadSummary(): Reading summary from disk.");
357 
358  wxFFile file(mFileName.GetFullPath(), wxT("rb"));
359 
360  {
361  Maybe<wxLogNull> silence{};
362  if (mSilentLog)
363  silence.create();
364  // FIXME: TRAP_ERR no report to user of absent summary files?
365  // filled with zero instead.
366  if (!file.IsOpened()){
367  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
368  mSilentLog = TRUE;
369  return false;
370  }
371  }
372  mSilentLog = FALSE;
373 
374  // The offset is just past the au header
375  if( !file.Seek(sizeof(auHeader)) ||
376  file.Read(data.get(), mSummaryInfo.totalSummaryBytes) !=
378  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
379  return false;
380  }
381 
382  FixSummary(data.get());
383 
384  return true;
385  }
386 }
387 
395 size_t SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
396  size_t start, size_t len, bool mayThrow) const
397 {
398  if (mCache.active)
399  {
400  //wxLogDebug("SimpleBlockFile::ReadData(): Data are already in cache.");
401 
402  auto framesRead = std::min(len, std::max(start, mLen) - start);
403  CopySamples(
404  (samplePtr)(mCache.sampleData.get() +
405  start * SAMPLE_SIZE(mCache.format)),
406  mCache.format, data, format, framesRead);
407 
408  if ( framesRead < len ) {
409  if (mayThrow)
410  // Not the best exception class?
412  ClearSamples(data, format, framesRead, len - framesRead);
413  }
414 
415  return framesRead;
416  }
417  else
418  return CommonReadData( mayThrow,
419  mFileName, mSilentLog, nullptr, 0, 0, data, format, start, len);
420 }
421 
423 // may throw
424 {
425  xmlFile.StartTag(wxT("simpleblockfile"));
426 
427  xmlFile.WriteAttr(wxT("filename"), mFileName.GetFullName());
428  xmlFile.WriteAttr(wxT("len"), mLen);
429  xmlFile.WriteAttr(wxT("min"), mMin);
430  xmlFile.WriteAttr(wxT("max"), mMax);
431  xmlFile.WriteAttr(wxT("rms"), mRMS);
432 
433  xmlFile.EndTag(wxT("simpleblockfile"));
434 }
435 
436 // BuildFromXML methods should always return a BlockFile, not NULL,
437 // even if the result is flawed (e.g., refers to nonexistent file),
438 // as testing will be done in DirManager::ProjectFSCK().
441 {
442  wxFileNameWrapper fileName;
443  float min = 0.0f, max = 0.0f, rms = 0.0f;
444  size_t len = 0;
445  double dblValue;
446  long nValue;
447 
448  while(*attrs)
449  {
450  const wxChar *attr = *attrs++;
451  const wxChar *value = *attrs++;
452  if (!value)
453  break;
454 
455  const wxString strValue = value;
456  if (!wxStricmp(attr, wxT("filename")) &&
457  // Can't use XMLValueChecker::IsGoodFileName here, but do part of its test.
459  (strValue.Length() + 1 + dm.GetProjectDataDir().Length() <= PLATFORM_MAX_PATH))
460  {
461  if (!dm.AssignFile(fileName, strValue, false))
462  // Make sure fileName is back to uninitialized state so we can detect problem later.
463  fileName.Clear();
464  }
465  else if (!wxStrcmp(attr, wxT("len")) &&
466  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue) &&
467  nValue > 0)
468  len = nValue;
469  else if (XMLValueChecker::IsGoodString(strValue) && Internat::CompatibleToDouble(strValue, &dblValue))
470  { // double parameters
471  if (!wxStricmp(attr, wxT("min")))
472  min = dblValue;
473  else if (!wxStricmp(attr, wxT("max")))
474  max = dblValue;
475  else if (!wxStricmp(attr, wxT("rms")) && (dblValue >= 0.0))
476  rms = dblValue;
477  }
478  }
479 
480  return make_blockfile<SimpleBlockFile>
481  (std::move(fileName), len, min, max, rms);
482 }
483 
488 {
489  auto newBlockFile = make_blockfile<SimpleBlockFile>
490  (std::move(newFileName), mLen, mMin, mMax, mRMS);
491 
492  return newBlockFile;
493 }
494 
496 {
497  if (mCache.active && mCache.needWrite)
498  {
499  // We don't know space usage yet
500  return 0;
501  }
502 
503  // Don't know the format, so it must be read from the file
504  if (mFormat == (sampleFormat) 0)
505  {
506  // Check sample format
507  wxFFile file(mFileName.GetFullPath(), wxT("rb"));
508  if (!file.IsOpened())
509  {
510  // Don't read into cache if file not available
511  return 0;
512  }
513 
514  auHeader header;
515 
516  if (file.Read(&header, sizeof(header)) != sizeof(header))
517  {
518  // Corrupt file
519  return 0;
520  }
521 
522  wxUint32 encoding;
523 
524  if (header.magic == 0x2e736e64)
525  encoding = header.encoding; // correct endianness
526  else
527  encoding = SwapUintEndianess(header.encoding);
528 
529  switch (encoding)
530  {
531  case AU_SAMPLE_FORMAT_16:
532  mFormat = int16Sample;
533  break;
534  case AU_SAMPLE_FORMAT_24:
535  mFormat = int24Sample;
536  break;
537  default:
538  // floatSample is a safe default (we will never loose data)
539  mFormat = floatSample;
540  break;
541  }
542 
543  file.Close();
544  }
545 
546  return {
547  sizeof(auHeader) +
550  };
551 }
552 
554  wxFFile file(mFileName.GetFullPath(), wxT("wb"));
555 
556  if( !file.IsOpened() ){
557  // Can't do anything else.
558  return;
559  }
560 
561  auHeader header;
562  header.magic = 0x2e736e64;
564 
565  // dataSize is optional, and we opt out
566  header.dataSize = 0xffffffff;
567  header.encoding = AU_SAMPLE_FORMAT_16;
568  header.sampleRate = 44100;
569  header.channels = 1;
570  file.Write(&header, sizeof(header));
571 
572  for(decltype(mSummaryInfo.totalSummaryBytes) i = 0;
574  file.Write(wxT("\0"),1);
575 
576  for(decltype(mLen) i = 0; i < mLen * 2; i++)
577  file.Write(wxT("\0"),1);
578 
579 }
580 
582 {
584  return;
585 
587  mCache.summaryData.get()))
588  mCache.needWrite = false;
589 }
590 
592 {
593  return mCache.active && mCache.needWrite;
594 }
595 
597 {
598 #ifdef DEPRECATED_AUDIO_CACHE
599  // See http://bugzilla.audacityteam.org/show_bug.cgi?id=545.
600  bool cacheBlockFiles = false;
601  gPrefs->Read(wxT("/Directories/CacheBlockFiles"), &cacheBlockFiles);
602  if (!cacheBlockFiles)
603  return false;
604 
605  int lowMem = gPrefs->Read(wxT("/Directories/CacheLowMem"), 16l);
606  if (lowMem < 16) {
607  lowMem = 16;
608  }
609  lowMem <<= 20;
610  return (GetFreeMemory() > lowMem);
611 #else
612  return false;
613 #endif
614 }
Creates and manages BlockFile objects.
Definition: DirManager.h:54
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:78
A BlockFile is a chunk of immutable audio data.
Definition: BlockFile.h:56
void SaveXML(XMLWriter &xmlFile) override
Write an XML representation of this file.
wxFileNameWrapper mFileName
Definition: BlockFile.h:243
virtual void WriteAttr(const wxString &name, const wxString &value)
Definition: XMLWriter.cpp:131
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:101
sampleFormat mFormat
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:499
void CopySamples(samplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, unsigned int len, bool highQuality, unsigned int srcStride, unsigned int dstStride)
void WriteCacheToDisk() override
void FillCache() override
bool mSilentLog
Definition: BlockFile.h:247
Definition: MemoryX.h:585
void Recover() override
if the on-disk state disappeared, either recover it (if it was
wxUint32 magic
virtual ~SimpleBlockFile()
SummaryInfo mSummaryInfo
Definition: BlockFile.h:245
wxMemorySize GetFreeMemory()
Definition: DirManager.cpp:120
BlockFilePtr Copy(wxFileNameWrapper &&newFileName) override
Create a NEW block file identical to this one.
float mMin
Definition: BlockFile.h:246
bool ReadSummary(ArrayOf< char > &data) override
Read the summary section of the disk file.
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
#define PLATFORM_MAX_PATH
Definition: Audacity.h:118
static wxUint32 SwapUintEndianess(wxUint32 in)
unsigned long long DiskByteCount
Definition: BlockFile.h:152
std::shared_ptr< BlockFile > BlockFilePtr
Definition: BlockFile.h:48
size_t GetLength() const
Definition: BlockFile.h:114
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxUint32 encoding
int format
Definition: ExportPCM.cpp:56
static bool IsGoodString(const wxString &str)
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:121
#define SAMPLE_SIZE_DISK(SampleFormat)
Return the size on disk of one uncompressed sample (bytes)
Definition: SampleFormat.h:43
float mRMS
Definition: BlockFile.h:246
size_t totalSummaryBytes
Definition: BlockFile.h:41
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.
ArrayOf< char > sampleData
int min(int a, int b)
SimpleBlockFileCache mCache
wxString GetProjectDataDir()
Definition: DirManager.cpp:679
wxUint32 dataOffset
wxUint32 dataSize
DiskByteCount GetSpaceUsage() const override
size_t mLen
Definition: BlockFile.h:244
virtual void * CalcSummary(samplePtr buffer, size_t len, sampleFormat format, ArrayOf< char > &cleanup)
Definition: BlockFile.cpp:181
static size_t CommonReadData(bool mayThrow, const wxFileName &fileName, bool &mSilentLog, const AliasBlockFile *pAliasFile, sampleCount origin, unsigned channel, samplePtr data, sampleFormat format, size_t start, size_t len, const sampleFormat *pLegacyFormat=nullptr, size_t legacyLen=0)
Definition: BlockFile.cpp:483
wxUint32 channels
bool GetNeedWriteCacheToDisk() override
bool WriteSimpleBlockFile(samplePtr sampleData, size_t sampleLen, sampleFormat format, void *summaryData)
static BlockFilePtr BuildFromXML(DirManager &dm, const wxChar **attrs)
static
static bool IsGoodFileString(const wxString &str)
virtual GetFileNameResult GetFileName() const
Definition: BlockFile.cpp:127
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
const wxFileName & name
Definition: BlockFile.h:99
wxUint32 sampleRate
The auHeader is a structure used by SimpleBlockFile for .au file format. There probably is an 'offici...
float mMax
Definition: BlockFile.h:246
ArrayOf< char > summaryData
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:22
static bool GetCache()
SimpleBlockFile(wxFileNameWrapper &&baseFileName, samplePtr sampleData, size_t sampleLen, sampleFormat format, bool allowDeferredWrite=false, bool bypassCache=false)
Create a disk file and write summary and sample data to it.
virtual void FixSummary(void *data)
Definition: BlockFile.cpp:331
void create(Args &&...args)
Definition: MemoryX.h:632
bool AssignFile(wxFileNameWrapper &filename, const wxString &value, bool check)
Definition: DirManager.cpp:752