Audacity  2.2.2
ODPCMAliasBlockFile.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ODPCMAliasBlockFile.cpp
6 
7  Created by Michael Chinen (mchinen)
8  Audacity(R) is copyright (c) 1999-2008 Audacity Team.
9  License: GPL v2. See License.txt.
10 
11 ******************************************************************//*******************************************************************/
18 
19 #include "../Audacity.h"
20 #include "ODPCMAliasBlockFile.h"
21 
22 #include <float.h>
23 
24 #include <wx/file.h>
25 #include <wx/utils.h>
26 #include <wx/wxchar.h>
27 #include <wx/log.h>
28 #include <wx/thread.h>
29 #include <sndfile.h>
30 
31 #include "../AudacityApp.h"
32 #include "PCMAliasBlockFile.h"
33 #include "../FileFormats.h"
34 #include "../Internat.h"
35 
36 #include "../ondemand/ODManager.h"
37 #include "../AudioIO.h"
38 
40 
41 //#include <errno.h>
42 
43 extern AudioIO *gAudioIO;
44 
45 const int aheaderTagLen = 20;
46 char aheaderTag[aheaderTagLen + 1] = "AudacityBlockFile112";
47 
48 
50  wxFileNameWrapper &&fileName,
51  wxFileNameWrapper &&aliasedFileName,
52  sampleCount aliasStart,
53  size_t aliasLen, int aliasChannel)
54 : PCMAliasBlockFile { std::move(fileName), std::move(aliasedFileName),
55  aliasStart, aliasLen, aliasChannel, false }
56 {
57  mSummaryAvailable = mSummaryBeingComputed = mHasBeenSaved = false;
58 }
59 
62  wxFileNameWrapper &&existingSummaryFileName,
63  wxFileNameWrapper &&aliasedFileName,
64  sampleCount aliasStart,
65  size_t aliasLen, int aliasChannel,
66  float min, float max, float rms, bool summaryAvailable)
67 : PCMAliasBlockFile(std::move(existingSummaryFileName), std::move(aliasedFileName),
68  aliasStart, aliasLen,
69  aliasChannel, min, max, rms)
70 {
71  mSummaryAvailable=summaryAvailable;
73  }
74 
76 {
77 }
78 
79 
80 
81 //Check to see if we have the file for these calls.
83 {
84  if(IsSummaryAvailable())
85  {
86  DiskByteCount ret;
87  mFileNameMutex.Lock();
88  wxFFile summaryFile(mFileName.GetFullPath());
89  ret= summaryFile.Length();
90  mFileNameMutex.Unlock();
91  return ret;
92  }
93  else
94  {
95  return 0;
96  }
97 }
98 
102 {
105 }
106 
107 //when the file closes, it locks the blockfiles, but only conditionally.
108 // It calls this so we can check if it has been saved before.
110 {
111  if(mHasBeenSaved)
113 }
114 
115 
119 {
120  if(IsSummaryAvailable() && IsLocked())
122 }
123 
124 
127  size_t start, size_t len, bool mayThrow) const -> MinMaxRMS
128 {
129  if(IsSummaryAvailable())
130  {
131  return PCMAliasBlockFile::GetMinMaxRMS(start, len, mayThrow);
132  }
133  else
134  {
135  if (mayThrow)
136  throw NotYetAvailableException{ GetAliasedFileName() };
137 
138  //fake values. These values are used usually for normalization and amplifying, so we want
139  //the max to be maximal and the min to be minimal
140  return {
143  0.707f //sin with amp of 1 rms
144  };
145  }
146 }
147 
149 auto ODPCMAliasBlockFile::GetMinMaxRMS(bool mayThrow) const -> MinMaxRMS
150 {
151  if(IsSummaryAvailable())
152  {
153  return PCMAliasBlockFile::GetMinMaxRMS(mayThrow);
154  }
155  else
156  {
157  if (mayThrow)
158  throw NotYetAvailableException{ GetAliasedFileName() };
159 
160  //fake values. These values are used usually for normalization and amplifying, so we want
161  //the max to be maximal and the min to be minimal
162  return {
165  0.707f //sin with amp of 1 rms
166  };
167  }
168 }
169 
172 bool ODPCMAliasBlockFile::Read256(float *buffer, size_t start, size_t len)
173 {
174  if(IsSummaryAvailable())
175  {
176  return PCMAliasBlockFile::Read256(buffer,start,len);
177  }
178  else
179  {
180  //return nothing.
181  ClearSamples((samplePtr)buffer, floatSample, 0, len);
182  return false;
183  }
184 }
185 
188 bool ODPCMAliasBlockFile::Read64K(float *buffer, size_t start, size_t len)
189 {
190  if(IsSummaryAvailable())
191  {
192  return PCMAliasBlockFile::Read64K(buffer,start,len);
193  }
194  else
195  {
196  //return nothing - it hasn't been calculated yet
197  ClearSamples((samplePtr)buffer, floatSample, 0, len);
198  return false;
199  }
200 }
201 
207 {
208  BlockFilePtr newBlockFile;
209 
210  //mAliasedFile can change so we lock readdatamutex, which is responsible for it.
211  auto locker = LockForRead();
212  //If the file has been written AND it has been saved, we create a PCM alias blockfile because for
213  //all intents and purposes, it is the same.
214  //However, if it hasn't been saved yet, we shouldn't create one because the default behavior of the
215  //PCMAliasBlockFile is to lock on exit, and this will cause orphaned blockfiles..
217  {
218  newBlockFile = make_blockfile<PCMAliasBlockFile>
219  (std::move(newFileName), wxFileNameWrapper{mAliasedFileName},
221 
222  }
223  else
224  {
225  //Summary File might exist in this case, but it might not.
226  newBlockFile = make_blockfile<ODPCMAliasBlockFile>
227  (std::move(newFileName), wxFileNameWrapper{mAliasedFileName},
230  //The client code will need to schedule this blockfile for OD summarizing if it is going to a NEW track.
231  }
232 
233  return newBlockFile;
234 }
235 
236 
242 // may throw
243 {
244  //we lock this so that mAliasedFileName doesn't change.
245  auto locker = LockForRead();
246  if(IsSummaryAvailable())
247  {
249  mHasBeenSaved = true;
250  }
251  else
252  {
253  xmlFile.StartTag(wxT("odpcmaliasblockfile"));
254 
255  //unlock to prevent deadlock and resume lock after.
256  {
257  auto suspension = locker.Suspend();
258  ODLocker locker2 { &mFileNameMutex };
259  xmlFile.WriteAttr(wxT("summaryfile"), mFileName.GetFullName());
260  }
261 
262  xmlFile.WriteAttr(wxT("aliasfile"), mAliasedFileName.GetFullPath());
263  xmlFile.WriteAttr(wxT("aliasstart"),
264  mAliasStart.as_long_long());
265  xmlFile.WriteAttr(wxT("aliaslen"), mLen);
266  xmlFile.WriteAttr(wxT("aliaschannel"), mAliasChannel);
267 
268  xmlFile.EndTag(wxT("odpcmaliasblockfile"));
269  }
270 }
271 
274 // BuildFromXML methods should always return a BlockFile, not NULL,
275 // even if the result is flawed (e.g., refers to nonexistent file),
276 // as testing will be done in DirManager::ProjectFSCK().
278 {
279  wxFileNameWrapper summaryFileName;
280  wxFileNameWrapper aliasFileName;
281  sampleCount aliasStart = 0;
282  size_t aliasLen = 0;
283  int aliasChannel=0;
284  long nValue;
285  long long nnValue;
286 
287  while(*attrs)
288  {
289  const wxChar *attr = *attrs++;
290  const wxChar *value = *attrs++;
291  if (!value)
292  break;
293 
294  const wxString strValue = value;
295  if (!wxStricmp(attr, wxT("summaryfile")) &&
296  // Can't use XMLValueChecker::IsGoodFileName here, but do part of its test.
298  (strValue.Length() + 1 + dm.GetProjectDataDir().Length() <= PLATFORM_MAX_PATH))
299  {
300  if (!dm.AssignFile(summaryFileName, strValue, false))
301  // Make sure summaryFileName is back to uninitialized state so we can detect problem later.
302  summaryFileName.Clear();
303  }
304  else if( !wxStricmp(attr, wxT("aliasfile")) )
305  {
306  if (XMLValueChecker::IsGoodPathName(strValue))
307  aliasFileName.Assign(strValue);
308  else if (XMLValueChecker::IsGoodFileName(strValue, dm.GetProjectDataDir()))
309  // Allow fallback of looking for the file name, located in the data directory.
310  aliasFileName.Assign(dm.GetProjectDataDir(), strValue);
311  else if (XMLValueChecker::IsGoodPathString(strValue))
312  // If the aliased file is missing, we failed XMLValueChecker::IsGoodPathName()
313  // and XMLValueChecker::IsGoodFileName, because both do existence tests,
314  // but we want to keep the reference to the missing file because it's a good path string.
315  aliasFileName.Assign(strValue);
316  }
317  else if ( !wxStricmp(attr, wxT("aliasstart")) )
318  {
319  if (XMLValueChecker::IsGoodInt64(strValue) &&
320  strValue.ToLongLong(&nnValue) && (nnValue >= 0))
321  aliasStart = nnValue;
322  }
323  else if (XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
324  { // integer parameters
325  if (!wxStricmp(attr, wxT("aliaslen")) && (nValue >= 0))
326  aliasLen = nValue;
327  else if (!wxStricmp(attr, wxT("aliaschannel")) && XMLValueChecker::IsValidChannel(aliasChannel))
328  aliasChannel = nValue;
329  }
330  }
331 
332  return make_blockfile<ODPCMAliasBlockFile>
333  (std::move(summaryFileName), std::move(aliasFileName),
334  aliasStart, aliasLen, aliasChannel, 0, 0, 0, false);
335 }
336 
337 
338 
340 {
341  if(IsSummaryAvailable())
342  {
343  WriteSummary();
344  }
345 }
346 
348 {
349  bool retval;
350  mSummaryAvailableMutex.Lock();
351  retval= mSummaryAvailable;
352  mSummaryAvailableMutex.Unlock();
353  return retval;
354 }
355 
358 {
359  ODLocker locker { &mWriteSummaryMutex };
360  if(!IsSummaryAvailable())
361  WriteSummary();
362 }
363 
366 {
367  mFileNameMutex.Lock();
368  mFileName = std::move(name);
369  mFileNameMutex.Unlock();
370 }
371 
374 {
375  return { mFileName, ODLocker{ &mFileNameMutex } };
376 }
377 
380 {
381  // To build the summary data, call ReadData (implemented by the
382  // derived classes) to get the sample data
383  // Call this first, so that in case of exceptions from ReadData, there is
384  // no NEW output file
385  SampleBuffer sampleData(mLen, floatSample);
386  this->ReadData(sampleData.ptr(), floatSample, 0, mLen, true);
387 
388  ArrayOf< char > fileNameChar;
389  FILE *summaryFile{};
390  {
391  //the mFileName path may change, for example, when the project is saved.
392  //(it moves from /tmp/ to wherever it is saved to.
393  ODLocker locker { &mFileNameMutex };
394 
395  //wxFFile is not thread-safe - if any error occurs in opening the file,
396  // it posts a wxlog message which WILL crash
397  // Audacity because it goes into the wx GUI.
398  // For this reason I left the wxFFile method commented out. (mchinen)
399  // wxFFile summaryFile(mFileName.GetFullPath(), wxT("wb"));
400 
401  // ...and we use fopen instead.
402  wxString sFullPath = mFileName.GetFullPath();
403  fileNameChar.reinit( strlen(sFullPath.mb_str(wxConvFile)) + 1 );
404  strcpy(fileNameChar.get(), sFullPath.mb_str(wxConvFile));
405  summaryFile = fopen(fileNameChar.get(), "wb");
406  }
407 
408  // JKC ANSWER-ME: Whay is IsOpened() commented out?
409  if (!summaryFile){//.IsOpened() ){
410 
411  // Never silence the Log w.r.t write errors; they always count
412  //however, this is going to be called from a non-main thread,
413  //and wxLog calls are not thread safe.
414  wxPrintf("Unable to write summary data to file: %s", fileNameChar.get());
415 
416  throw FileException{
417  FileException::Cause::Read, wxFileName{ fileNameChar.get() } };
418  }
419 
420  ArrayOf<char> cleanup;
421  void *summaryData = CalcSummary(sampleData.ptr(), mLen,
422  floatSample, cleanup);
423 
424  //summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes);
425  fwrite(summaryData, 1, mSummaryInfo.totalSummaryBytes, summaryFile);
426  fclose(summaryFile);
427 
428 
429  // wxPrintf("write successful. filename: %s\n", fileNameChar);
430 
431  mSummaryAvailableMutex.Lock();
432  mSummaryAvailable=true;
433  mSummaryAvailableMutex.Unlock();
434 }
435 
436 
437 
455 void *ODPCMAliasBlockFile::CalcSummary(samplePtr buffer, size_t len,
456  sampleFormat format, ArrayOf<char> &cleanup)
457 {
459  char* localFullSummary = cleanup.get();
460 
461  memcpy(localFullSummary, aheaderTag, aheaderTagLen);
462 
463  float *summary64K = (float *)(localFullSummary + mSummaryInfo.offset64K);
464  float *summary256 = (float *)(localFullSummary + mSummaryInfo.offset256);
465 
466  Floats floats;
467  float *fbuffer;
468 
469  //mchinen: think we can hack this - don't allocate and copy if we don't need to.,
470  if(format==floatSample)
471  {
472  fbuffer = (float*)buffer;
473  }
474  else
475  {
476  floats.reinit(len);
477  fbuffer = floats.get();
478  CopySamples(buffer, format,
479  (samplePtr)fbuffer, floatSample, len);
480  }
481 
482  BlockFile::CalcSummaryFromBuffer(fbuffer, len, summary256, summary64K);
483 
484  return localFullSummary;
485 }
486 
487 
488 
489 
498 size_t ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
499  size_t start, size_t len, bool mayThrow) const
500 {
501 
502  auto locker = LockForRead();
503 
504  if(!mAliasedFileName.IsOk()){ // intentionally silenced
505  memset(data,0,SAMPLE_SIZE(format)*len);
506  return len;
507  }
508 
509  return CommonReadData( mayThrow,
511  data, format, start, len);
512 }
513 
521 {
523 
524  ODLocker locker{ &mFileNameMutex };
525  wxFFile summaryFile(mFileName.GetFullPath(), wxT("rb"));
526 
527  if( !summaryFile.IsOpened() ) {
528 
529  // NEW model; we need to return valid data
530  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
531 
532  // we silence the logging for this operation in this object
533  // after first occurrence of error; it's already reported and
534  // spewing at the user will complicate the user's ability to
535  // deal
536  mSilentLog = TRUE;
537 
538  return false;
539  }
540  else
541  mSilentLog = FALSE; // worked properly, any future error is NEW
542 
543  auto read = summaryFile.Read(data.get(), mSummaryInfo.totalSummaryBytes);
544 
545  if (read != mSummaryInfo.totalSummaryBytes) {
546  memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
547  return false;
548  }
549 
550  FixSummary(data.get());
551 
552  return true;
553 }
554 
557 {
558  mReadDataMutex.Lock();
559 }
562 {
563  mReadDataMutex.Unlock();
564 }
565 
566 
ReadLock LockForRead() const
Definition: BlockFile.h:208
AudioIO uses the PortAudio library to play and record sound.
Definition: AudioIO.h:143
Creates and manages BlockFile objects.
Definition: DirManager.h:54
const int mAliasChannel
Definition: BlockFile.h:299
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:78
GetFileNameResult GetFileName() const override
sets the file name the summary info will be saved in. threadsafe.
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
void CalcSummaryFromBuffer(const float *fbuffer, size_t len, float *summary256, float *summary64K)
Definition: BlockFile.cpp:203
static bool IsValidChannel(const int nValue)
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:499
ODPCMAliasBlockFile(wxFileNameWrapper &&baseFileName, wxFileNameWrapper &&aliasedFileName, sampleCount aliasStart, size_t aliasLen, int aliasChannel)
Constructs a PCMAliasBlockFile, writing the summary to disk.
int offset64K
Definition: BlockFile.h:38
void CopySamples(samplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, unsigned int len, bool highQuality, unsigned int srcStride, unsigned int dstStride)
static bool IsGoodInt64(const wxString &strInt)
Check that the supplied string can be converted to a 64bit integer.
void SaveXML(XMLWriter &xmlFile) override
Stores a representation of this file in XML.
#define JUST_BELOW_MAX_AUDIO
Definition: Audacity.h:213
bool Read256(float *buffer, size_t start, size_t len) override
Returns the 256 byte summary data block.
void DoWriteSummary()
A public interface to WriteSummary.
bool mSilentLog
Definition: BlockFile.h:247
static bool IsGoodPathName(const wxString &strPathName)
SummaryInfo mSummaryInfo
Definition: BlockFile.h:245
virtual bool Read256(float *buffer, size_t start, size_t len)
Returns the 256 byte summary data block.
Definition: BlockFile.cpp:415
bool Read64K(float *buffer, size_t start, size_t len) override
Returns the 64K summary data block.
void Lock()
Locks the blockfile only if it has a file that exists.
float mMin
Definition: BlockFile.h:246
static BlockFilePtr BuildFromXML(DirManager &dm, const wxChar **attrs)
Reconstructs from XML a ODPCMAliasBlockFile and reschedules it for OD loading.
wxFileNameWrapper mAliasedFileName
Definition: BlockFile.h:297
static bool IsGoodPathString(const wxString &str)
virtual bool IsLocked()
Returns TRUE if this BlockFile is locked.
Definition: BlockFile.cpp:162
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
AudioIO * gAudioIO
Definition: AudioIO.cpp:481
#define PLATFORM_MAX_PATH
Definition: Audacity.h:118
BlockFilePtr Copy(wxFileNameWrapper &&fileName) override
Makes NEW ODPCMAliasBlockFile or PCMAliasBlockFile depending on summary availability.
unsigned long long DiskByteCount
Definition: BlockFile.h:152
std::shared_ptr< BlockFile > BlockFilePtr
Definition: BlockFile.h:48
int format
Definition: ExportPCM.cpp:56
float mRMS
Definition: BlockFile.h:246
void Recover(void) override
Writes the summary file if summary data is available.
size_t totalSummaryBytes
Definition: BlockFile.h:41
void * CalcSummary(samplePtr buffer, size_t len, sampleFormat format, ArrayOf< char > &cleanup) override
void Unlock()
Unlocks the blockfile only if it has a file that exists.
char aheaderTag[aheaderTagLen+1]
virtual void Lock()
Locks this BlockFile, to prevent it from being moved.
Definition: BlockFile.cpp:148
int min(int a, int b)
samplePtr ptr() const
Definition: SampleFormat.h:81
bool IsSummaryAvailable() const override
Returns TRUE if this block's complete summary has been computed and is ready (for OD) ...
bool mSilentAliasLog
Definition: BlockFile.h:300
wxString GetProjectDataDir()
Definition: DirManager.cpp:679
sampleCount mAliasStart
Definition: BlockFile.h:298
size_t mLen
Definition: BlockFile.h:244
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
void SetFileName(wxFileNameWrapper &&name) override
sets the file name the summary info will be saved in. threadsafe.
DiskByteCount GetSpaceUsage() const override
virtual void Unlock()
Unlock this BlockFile, allowing it to be moved.
Definition: BlockFile.cpp:155
const wxChar * name
Definition: Distortion.cpp:94
static bool IsGoodFileString(const wxString &str)
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
static bool IsGoodFileName(const wxString &strFileName, const wxString &strDirName=wxEmptyString)
MinMaxRMS GetMinMaxRMS(size_t start, size_t len, bool mayThrow) const override
Gets extreme values for the specified region.
virtual MinMaxRMS GetMinMaxRMS(size_t start, size_t len, bool mayThrow=true) const
Gets extreme values for the specified region.
Definition: BlockFile.cpp:369
bool ReadSummary(ArrayOf< char > &data) override
Read the summary into a buffer.
int offset256
Definition: BlockFile.h:40
void WriteSummary() override
Write the summary to disk, using the derived ReadData() to get the data.
float mMax
Definition: BlockFile.h:246
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:22
void UnlockRead() const override
Allows reading on other threads.
void SaveXML(XMLWriter &xmlFile) override
Saves as xml ODPCMAliasBlockFile or PCMAliasBlockFile depending on summary availability.
size_t ReadData(samplePtr data, sampleFormat format, size_t start, size_t len, bool mayThrow) const override
Reads the specified data from the aliased file using libsndfile.
virtual void FixSummary(void *data)
Definition: BlockFile.cpp:331
An AliasBlockFile that references uncompressed data in an existing file.
const int aheaderTagLen
bool AssignFile(wxFileNameWrapper &filename, const wxString &value, bool check)
Definition: DirManager.cpp:752
void LockRead() const override
Prevents a read on other threads.
virtual bool Read64K(float *buffer, size_t start, size_t len)
Returns the 64K summary data block.
Definition: BlockFile.cpp:454