Audacity  3.0.3
SqliteSampleBlock.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 SqliteSampleBlock.cpp
6 
7 Paul Licameli -- split from SampleBlock.cpp and SampleBlock.h
8 
9 **********************************************************************/
10 
11 #include <float.h>
12 #include <sqlite3.h>
13 
14 #include "DBConnection.h"
15 #include "ProjectFileIO.h"
16 #include "SampleFormat.h"
17 #include "XMLTagHandler.h"
18 
19 #include "SampleBlock.h" // to inherit
20 
21 #include "SentryHelper.h"
22 #include <wx/log.h>
23 
25 
27 class SqliteSampleBlock final : public SampleBlock
28 {
29 public:
30 
31  explicit SqliteSampleBlock(
32  const std::shared_ptr<SqliteSampleBlockFactory> &pFactory);
33  ~SqliteSampleBlock() override;
34 
35  void CloseLock() override;
36 
37  void SetSamples(
38  constSamplePtr src, size_t numsamples, sampleFormat srcformat);
39 
41  using Sizes = std::pair< size_t, size_t >;
42  void Commit(Sizes sizes);
43 
44  void Delete();
45 
46  SampleBlockID GetBlockID() const override;
47 
48  size_t DoGetSamples(samplePtr dest,
49  sampleFormat destformat,
50  size_t sampleoffset,
51  size_t numsamples) override;
53  size_t GetSampleCount() const override;
54 
55  bool GetSummary256(float *dest, size_t frameoffset, size_t numframes) override;
56  bool GetSummary64k(float *dest, size_t frameoffset, size_t numframes) override;
57  double GetSumMin() const;
58  double GetSumMax() const;
59  double GetSumRms() const;
60 
62  MinMaxRMS DoGetMinMaxRMS(size_t start, size_t len) override;
63 
65  MinMaxRMS DoGetMinMaxRMS() const override;
66 
67  size_t GetSpaceUsage() const override;
68  void SaveXML(XMLWriter &xmlFile) override;
69 
70 private:
71  bool IsSilent() const { return mBlockID <= 0; }
72  void Load(SampleBlockID sbid);
73  bool GetSummary(float *dest,
74  size_t frameoffset,
75  size_t numframes,
77  const char *sql);
78  size_t GetBlob(void *dest,
79  sampleFormat destformat,
80  sqlite3_stmt *stmt,
81  sampleFormat srcformat,
82  size_t srcoffset,
83  size_t srcbytes);
84 
85  enum {
86  fields = 3, /* min, max, rms */
87  bytesPerFrame = fields * sizeof(float),
88  };
89  Sizes SetSizes( size_t numsamples, sampleFormat srcformat );
90  void CalcSummary(Sizes sizes);
91 
92 private:
94 
95  DBConnection *Conn() const;
96  sqlite3 *DB() const
97  {
98  return Conn()->DB();
99  }
100 
102 
103  const std::shared_ptr<SqliteSampleBlockFactory> mpFactory;
104  bool mValid{ false };
105  bool mLocked = false;
106 
108 
110  size_t mSampleBytes;
111  size_t mSampleCount;
113 
116  double mSumMin;
117  double mSumMax;
118  double mSumRms;
119 
120 #if defined(WORDS_BIGENDIAN)
121 #error All sample block data is little endian...big endian not yet supported
122 #endif
123 };
124 
125 // Silent blocks use nonpositive id values to encode a length
126 // and don't occupy any rows in the database; share blocks for repeatedly
127 // used length values
128 static std::map< SampleBlockID, std::shared_ptr<SqliteSampleBlock> >
130 
133  : public SampleBlockFactory
134  , public std::enable_shared_from_this<SqliteSampleBlockFactory>
135 {
136 public:
137  explicit SqliteSampleBlockFactory( AudacityProject &project );
138 
140 
142 
144  size_t numsamples,
145  sampleFormat srcformat) override;
146 
148  size_t numsamples,
149  sampleFormat srcformat) override;
150 
152  sampleFormat srcformat,
153  const wxChar **attrs) override;
154 
156  BlockDeletionCallback callback ) override;
157 
158 private:
160 
161  const std::shared_ptr<ConnectionPtr> mppConnection;
162 
163  // Track all blocks that this factory has created, but don't control
164  // their lifetimes (so use weak_ptr)
165  // (Must also use weak pointers because the blocks have shared pointers
166  // to the factory and we can't have a leaky cycle of shared pointers)
167  using AllBlocksMap =
168  std::map< SampleBlockID, std::weak_ptr< SqliteSampleBlock > >;
170 
172 };
173 
175  : mppConnection{ ConnectionPtr::Get(project).shared_from_this() }
176 {
177 
178 }
179 
181 
183  constSamplePtr src, size_t numsamples, sampleFormat srcformat )
184 {
185  auto sb = std::make_shared<SqliteSampleBlock>(shared_from_this());
186  sb->SetSamples(src, numsamples, srcformat);
187  // block id has now been assigned
188  mAllBlocks[ sb->GetBlockID() ] = sb;
189  return sb;
190 }
191 
193 {
194  SampleBlockIDs result;
195  for (auto end = mAllBlocks.end(), it = mAllBlocks.begin(); it != end;) {
196  if (it->second.expired())
197  // Tighten up the map
198  it = mAllBlocks.erase(it);
199  else {
200  result.insert( it->first );
201  ++it;
202  }
203  }
204  return result;
205 }
206 
208  size_t numsamples, sampleFormat )
209 {
210  auto id = -static_cast< SampleBlockID >(numsamples);
211  auto &result = sSilentBlocks[ id ];
212  if ( !result ) {
213  result = std::make_shared<SqliteSampleBlock>(nullptr);
214  result->mBlockID = id;
215 
216  // Ignore the supplied sample format
217  result->SetSizes(numsamples, floatSample);
218  result->mValid = true;
219  }
220 
221  return result;
222 }
223 
224 
226  sampleFormat srcformat, const wxChar **attrs )
227 {
228  std::shared_ptr<SampleBlock> sb;
229 
230  int found = 0;
231 
232  // loop through attrs, which is a null-terminated list of attribute-value pairs
233  while(*attrs)
234  {
235  const wxChar *attr = *attrs++;
236  const wxChar *value = *attrs++;
237 
238  if (!value)
239  {
240  break;
241  }
242 
243  const wxString strValue = value; // promote string, we need this for all
244  double dblValue;
245  long long nValue;
246 
247  if (wxStrcmp(attr, wxT("blockid")) == 0 &&
248  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLongLong(&nValue))
249  {
250  if (nValue <= 0) {
251  sb = DoCreateSilent( -nValue, floatSample );
252  }
253  else {
254  // First see if this block id was previously loaded
255  auto &wb = mAllBlocks[ nValue ];
256  auto pb = wb.lock();
257  if (pb)
258  // Reuse the block
259  sb = pb;
260  else {
261  // First sight of this id
262  auto ssb =
263  std::make_shared<SqliteSampleBlock>(shared_from_this());
264  wb = ssb;
265  sb = ssb;
266  ssb->mSampleFormat = srcformat;
267  // This may throw database errors
268  // It initializes the rest of the fields
269  ssb->Load((SampleBlockID) nValue);
270  }
271  }
272  found++;
273  }
274  }
275 
276  // Were all attributes found?
277  if (found != 1)
278  {
279  return nullptr;
280  }
281 
282  return sb;
283 }
284 
287 {
288  auto result = mCallback;
289  mCallback = std::move( callback );
290  return result;
291 }
292 
294  const std::shared_ptr<SqliteSampleBlockFactory> &pFactory)
295 : mpFactory(pFactory)
296 {
298  mSampleBytes = 0;
299  mSampleCount = 0;
300 
301  mSumMin = 0.0;
302  mSumMax = 0.0;
303  mSumRms = 0.0;
304 }
305 
307 {
308  if (mpFactory) {
309  auto &callback = mpFactory->mCallback;
310  if (callback)
311  GuardedCall( [&]{ callback( *this ); } );
312  }
313 
314  if (IsSilent()) {
315  // The block object was constructed but failed to Load() or Commit().
316  // Or it's a silent block with no row in the database.
317  // Just let the stack unwind. Don't violate the assertion in
318  // Delete(), which may do odd recursive things in debug builds when it
319  // yields to the UI to put up a dialog, but then dispatches timer
320  // events that try again to finish recording.
321  return;
322  }
323 
324  // See ProjectFileIO::Bypass() for a description of mIO.mBypass
325  GuardedCall( [this]{
326  if (!mLocked && !Conn()->ShouldBypass())
327  {
328  // In case Delete throws, don't let an exception escape a destructor,
329  // but we can still enqueue the delayed handler so that an error message
330  // is presented to the user.
331  // The failure in this case may be a less harmful waste of space in the
332  // database, which should not cause aborting of the attempted edit.
333  Delete();
334  }
335  } );
336 }
337 
339 {
340  if (!mpFactory)
341  return nullptr;
342 
343  auto &pConnection = mpFactory->mppConnection->mpConnection;
344  if (!pConnection) {
346  {
348  XO("Connection to project file is null"),
349  XO("Warning"),
350  "Error:_Disk_full_or_not_writable"
351  };
352  }
353  return pConnection.get();
354 }
355 
357 {
358  mLocked = true;
359 }
360 
362 {
363  return mBlockID;
364 }
365 
367 {
368  return mSampleFormat;
369 }
370 
372 {
373  return mSampleCount;
374 }
375 
377  sampleFormat destformat,
378  size_t sampleoffset,
379  size_t numsamples)
380 {
381  if (IsSilent()) {
382  auto size = SAMPLE_SIZE(destformat);
383  memset(dest, 0, numsamples * size);
384  return numsamples;
385  }
386 
387  // Prepare and cache statement...automatically finalized at DB close
388  sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::GetSamples,
389  "SELECT samples FROM sampleblocks WHERE blockid = ?1;");
390 
391  return GetBlob(dest,
392  destformat,
393  stmt,
395  sampleoffset * SAMPLE_SIZE(mSampleFormat),
397 }
398 
400  size_t numsamples,
401  sampleFormat srcformat)
402 {
403  auto sizes = SetSizes(numsamples, srcformat);
405  memcpy(mSamples.get(), src, mSampleBytes);
406 
407  CalcSummary( sizes );
408 
409  Commit( sizes );
410 }
411 
413  size_t frameoffset,
414  size_t numframes)
415 {
416  return GetSummary(dest, frameoffset, numframes, DBConnection::GetSummary256,
417  "SELECT summary256 FROM sampleblocks WHERE blockid = ?1;");
418 }
419 
421  size_t frameoffset,
422  size_t numframes)
423 {
424  return GetSummary(dest, frameoffset, numframes, DBConnection::GetSummary64k,
425  "SELECT summary64k FROM sampleblocks WHERE blockid = ?1;");
426 }
427 
429  size_t frameoffset,
430  size_t numframes,
432  const char *sql)
433 {
434  // Non-throwing, it returns true for success
435  bool silent = IsSilent();
436  if (!silent) {
437  // Not a silent block
438  try {
439  // Prepare and cache statement...automatically finalized at DB close
440  auto stmt = Conn()->Prepare(id, sql);
441  // Note GetBlob returns a size_t, not a bool
442  // REVIEW: An error in GetBlob() will throw an exception.
443  GetBlob(dest,
444  floatSample,
445  stmt,
446  floatSample,
447  frameoffset * fields * SAMPLE_SIZE(floatSample),
448  numframes * fields * SAMPLE_SIZE(floatSample));
449  return true;
450  }
451  catch ( const AudacityException & ) {
452  }
453  }
454  memset(dest, 0, 3 * numframes * sizeof( float ));
455  // Return true for success only if we didn't catch
456  return silent;
457 }
458 
460 {
461  return mSumMin;
462 }
463 
465 {
466  return mSumMax;
467 }
468 
470 {
471  return mSumRms;
472 }
473 
480 {
481  if (IsSilent())
482  return {};
483 
484  float min = FLT_MAX;
485  float max = -FLT_MAX;
486  float sumsq = 0;
487 
488  if (!mValid)
489  {
490  Load(mBlockID);
491  }
492 
493  if (start < mSampleCount)
494  {
495  len = std::min(len, mSampleCount - start);
496 
497  // TODO: actually use summaries
498  SampleBuffer blockData(len, floatSample);
499  float *samples = (float *) blockData.ptr();
500 
501  size_t copied = DoGetSamples((samplePtr) samples, floatSample, start, len);
502  for (size_t i = 0; i < copied; ++i, ++samples)
503  {
504  float sample = *samples;
505 
506  if (sample > max)
507  {
508  max = sample;
509  }
510 
511  if (sample < min)
512  {
513  min = sample;
514  }
515 
516  sumsq += (sample * sample);
517  }
518  }
519 
520  return { min, max, (float) sqrt(sumsq / len) };
521 }
522 
527 {
528  return { (float) mSumMin, (float) mSumMax, (float) mSumRms };
529 }
530 
532 {
533  if (IsSilent())
534  return 0;
535  else
537 }
538 
539 size_t SqliteSampleBlock::GetBlob(void *dest,
540  sampleFormat destformat,
541  sqlite3_stmt *stmt,
542  sampleFormat srcformat,
543  size_t srcoffset,
544  size_t srcbytes)
545 {
546  auto db = DB();
547 
548  wxASSERT(!IsSilent());
549 
550  if (!mValid)
551  {
552  Load(mBlockID);
553  }
554 
555  int rc;
556  size_t minbytes = 0;
557 
558  // Bind statement parameters
559  // Might return SQLITE_MISUSE which means it's our mistake that we violated
560  // preconditions; should return SQL_OK which is 0
561  if (sqlite3_bind_int64(stmt, 1, mBlockID))
562  {
564  "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
565  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::GetBlob::bind");
566 
567  wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
568  }
569 
570  // Execute the statement
571  rc = sqlite3_step(stmt);
572  if (rc != SQLITE_ROW)
573  {
574  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
575  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::GetBlob::step");
576 
577  wxLogDebug(wxT("SqliteSampleBlock::GetBlob - SQLITE error %s"), sqlite3_errmsg(db));
578 
579  // Clear statement bindings and rewind statement
580  sqlite3_clear_bindings(stmt);
581  sqlite3_reset(stmt);
582 
583  // Just showing the user a simple message, not the library error too
584  // which isn't internationalized
585  // Actually this can lead to 'Could not read from file' error message
586  // but it can also lead to no error message at all and a flat line,
587  // depending on where GetBlob is called from.
588  // The latter can happen when repainting the screen.
589  // That possibly happens on a very slow machine. Possibly that's the
590  // right trade off when a machine can't keep up?
591  // ANSWER-ME: Do we always report an error when we should here?
592  Conn()->ThrowException( false );
593  }
594 
595  // Retrieve returned data
596  samplePtr src = (samplePtr) sqlite3_column_blob(stmt, 0);
597  size_t blobbytes = (size_t) sqlite3_column_bytes(stmt, 0);
598 
599  srcoffset = std::min(srcoffset, blobbytes);
600  minbytes = std::min(srcbytes, blobbytes - srcoffset);
601 
602  if (srcoffset != 0)
603  {
604  srcoffset += 0;
605  }
606 
607  /*
608  Will dithering happen in CopySamples? Answering this as of 3.0.3 by
609  examining all uses.
610 
611  As this function is called from GetSummary, no, because destination format
612  is float.
613 
614  There is only one other call to this function, in DoGetSamples. At one
615  call to that function, in DoGetMinMaxRMS, again format is float always.
616 
617  There is only one other call to DoGetSamples, in SampleBlock::GetSamples().
618  In one call to that function, in WaveformView.cpp, again format is float.
619 
620  That leaves two calls in Sequence.cpp. One of those can be proved to be
621  used only in copy and paste operations, always supplying the same sample
622  format as the samples were stored in, therefore no dither.
623 
624  That leaves uses of Sequence::Read(). There are uses of Read() in internal
625  operations also easily shown to use only the saved format, and
626  GetWaveDisplay() always reads as float.
627 
628  The remaining use of Sequence::Read() is in Sequence::Get(). That is used
629  by WaveClip::Resample(), always fetching float. It is also used in
630  WaveClip::GetSamples().
631 
632  There is only one use of that function not always fetching float, in
633  WaveTrack::Get().
634 
635  It can be shown that the only paths to WaveTrack::Get() not specifying
636  floatSample are in Benchmark, which is only a diagnostic test, and there
637  the sample format is the same as what the track was constructed with.
638 
639  Therefore, no dithering even there!
640  */
641  wxASSERT(destformat == floatSample || destformat == srcformat);
642 
643  CopySamples(src + srcoffset,
644  srcformat,
645  (samplePtr) dest,
646  destformat,
647  minbytes / SAMPLE_SIZE(srcformat));
648 
649  dest = ((samplePtr) dest) + minbytes;
650 
651  if (srcbytes - minbytes)
652  {
653  memset(dest, 0, srcbytes - minbytes);
654  }
655 
656  // Clear statement bindings and rewind statement
657  sqlite3_clear_bindings(stmt);
658  sqlite3_reset(stmt);
659 
660  return srcbytes;
661 }
662 
664 {
665  auto db = DB();
666  int rc;
667 
668  wxASSERT(sbid > 0);
669 
670  mValid = false;
671  mSampleCount = 0;
672  mSampleBytes = 0;
673  mSumMin = FLT_MAX;
674  mSumMax = -FLT_MAX;
675  mSumMin = 0.0;
676 
677  // Prepare and cache statement...automatically finalized at DB close
678  sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::LoadSampleBlock,
679  "SELECT sampleformat, summin, summax, sumrms,"
680  " length(samples)"
681  " FROM sampleblocks WHERE blockid = ?1;");
682 
683  // Bind statement parameters
684  // Might return SQLITE_MISUSE which means it's our mistake that we violated
685  // preconditions; should return SQL_OK which is 0
686  if (sqlite3_bind_int64(stmt, 1, sbid))
687  {
688 
689  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
690  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Load::bind");
691 
692  wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
693  }
694 
695  // Execute the statement
696  rc = sqlite3_step(stmt);
697  if (rc != SQLITE_ROW)
698  {
699 
700  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
701  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Load::step");
702 
703 
704  wxLogDebug(wxT("SqliteSampleBlock::Load - SQLITE error %s"), sqlite3_errmsg(db));
705 
706  // Clear statement bindings and rewind statement
707  sqlite3_clear_bindings(stmt);
708  sqlite3_reset(stmt);
709 
710  // Just showing the user a simple message, not the library error too
711  // which isn't internationalized
712  Conn()->ThrowException( false );
713  }
714 
715  // Retrieve returned data
716  mBlockID = sbid;
717  mSampleFormat = (sampleFormat) sqlite3_column_int(stmt, 0);
718  mSumMin = sqlite3_column_double(stmt, 1);
719  mSumMax = sqlite3_column_double(stmt, 2);
720  mSumRms = sqlite3_column_double(stmt, 3);
721  mSampleBytes = sqlite3_column_int(stmt, 4);
723 
724  // Clear statement bindings and rewind statement
725  sqlite3_clear_bindings(stmt);
726  sqlite3_reset(stmt);
727 
728  mValid = true;
729 }
730 
732 {
733  const auto mSummary256Bytes = sizes.first;
734  const auto mSummary64kBytes = sizes.second;
735 
736  auto db = DB();
737  int rc;
738 
739  // Prepare and cache statement...automatically finalized at DB close
740  sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::InsertSampleBlock,
741  "INSERT INTO sampleblocks (sampleformat, summin, summax, sumrms,"
742  " summary256, summary64k, samples)"
743  " VALUES(?1,?2,?3,?4,?5,?6,?7);");
744 
745  // Bind statement parameters
746  // Might return SQLITE_MISUSE which means it's our mistake that we violated
747  // preconditions; should return SQL_OK which is 0
748  if (sqlite3_bind_int(stmt, 1, mSampleFormat) ||
749  sqlite3_bind_double(stmt, 2, mSumMin) ||
750  sqlite3_bind_double(stmt, 3, mSumMax) ||
751  sqlite3_bind_double(stmt, 4, mSumRms) ||
752  sqlite3_bind_blob(stmt, 5, mSummary256.get(), mSummary256Bytes, SQLITE_STATIC) ||
753  sqlite3_bind_blob(stmt, 6, mSummary64k.get(), mSummary64kBytes, SQLITE_STATIC) ||
754  sqlite3_bind_blob(stmt, 7, mSamples.get(), mSampleBytes, SQLITE_STATIC))
755  {
756 
758  "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
759  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Commit::bind");
760 
761 
762  wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
763  }
764 
765  // Execute the statement
766  rc = sqlite3_step(stmt);
767  if (rc != SQLITE_DONE)
768  {
769  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
770  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Commit::step");
771 
772  wxLogDebug(wxT("SqliteSampleBlock::Commit - SQLITE error %s"), sqlite3_errmsg(db));
773 
774  // Clear statement bindings and rewind statement
775  sqlite3_clear_bindings(stmt);
776  sqlite3_reset(stmt);
777 
778  // Just showing the user a simple message, not the library error too
779  // which isn't internationalized
780  Conn()->ThrowException( true );
781  }
782 
783  // Retrieve returned data
784  mBlockID = sqlite3_last_insert_rowid(db);
785 
786  // Reset local arrays
787  mSamples.reset();
788  mSummary256.reset();
789  mSummary64k.reset();
790 
791  // Clear statement bindings and rewind statement
792  sqlite3_clear_bindings(stmt);
793  sqlite3_reset(stmt);
794 
795  mValid = true;
796 }
797 
799 {
800  auto db = DB();
801  int rc;
802 
803  wxASSERT(!IsSilent());
804 
805  // Prepare and cache statement...automatically finalized at DB close
806  sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::DeleteSampleBlock,
807  "DELETE FROM sampleblocks WHERE blockid = ?1;");
808 
809  // Bind statement parameters
810  // Might return SQLITE_MISUSE which means it's our mistake that we violated
811  // preconditions; should return SQL_OK which is 0
812  if (sqlite3_bind_int64(stmt, 1, mBlockID))
813  {
815  "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
816  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Delete::bind");
817 
818  wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
819  }
820 
821  // Execute the statement
822  rc = sqlite3_step(stmt);
823  if (rc != SQLITE_DONE)
824  {
825  ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
826  ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Delete::step");
827 
828  wxLogDebug(wxT("SqliteSampleBlock::Load - SQLITE error %s"), sqlite3_errmsg(db));
829 
830  // Clear statement bindings and rewind statement
831  sqlite3_clear_bindings(stmt);
832  sqlite3_reset(stmt);
833 
834  // Just showing the user a simple message, not the library error too
835  // which isn't internationalized
836  Conn()->ThrowException( true );
837  }
838 
839  // Clear statement bindings and rewind statement
840  sqlite3_clear_bindings(stmt);
841  sqlite3_reset(stmt);
842 }
843 
845 {
846  xmlFile.WriteAttr(wxT("blockid"), mBlockID);
847 }
848 
850  size_t numsamples, sampleFormat srcformat ) -> Sizes
851 {
852  mSampleFormat = srcformat;
853  mSampleCount = numsamples;
854  mSampleBytes = mSampleCount * SAMPLE_SIZE(mSampleFormat);
855 
856  int frames64k = (mSampleCount + 65535) / 65536;
857  int frames256 = frames64k * 256;
858  return { frames256 * bytesPerFrame, frames64k * bytesPerFrame };
859 }
860 
867 {
868  const auto mSummary256Bytes = sizes.first;
869  const auto mSummary64kBytes = sizes.second;
870 
871  Floats samplebuffer;
872  float *samples;
873 
874  if (mSampleFormat == floatSample)
875  {
876  samples = (float *) mSamples.get();
877  }
878  else
879  {
880  samplebuffer.reinit((unsigned) mSampleCount);
882  samplebuffer.get(), mSampleCount);
883  samples = samplebuffer.get();
884  }
885 
886  mSummary256.reinit(mSummary256Bytes);
887  mSummary64k.reinit(mSummary64kBytes);
888 
889  float *summary256 = (float *) mSummary256.get();
890  float *summary64k = (float *) mSummary64k.get();
891 
892  float min;
893  float max;
894  float sumsq;
895  double totalSquares = 0.0;
896  double fraction = 0.0;
897 
898  // Recalc 256 summaries
899  int sumLen = (mSampleCount + 255) / 256;
900  int summaries = 256;
901 
902  for (int i = 0; i < sumLen; ++i)
903  {
904  min = samples[i * 256];
905  max = samples[i * 256];
906  sumsq = min * min;
907 
908  int jcount = 256;
909  if (jcount > mSampleCount - i * 256)
910  {
911  jcount = mSampleCount - i * 256;
912  fraction = 1.0 - (jcount / 256.0);
913  }
914 
915  for (int j = 1; j < jcount; ++j)
916  {
917  float f1 = samples[i * 256 + j];
918  sumsq += f1 * f1;
919 
920  if (f1 < min)
921  {
922  min = f1;
923  }
924  else if (f1 > max)
925  {
926  max = f1;
927  }
928  }
929 
930  totalSquares += sumsq;
931 
932  summary256[i * fields] = min;
933  summary256[i * fields + 1] = max;
934  // The rms is correct, but this may be for less than 256 samples in last loop.
935  summary256[i * fields + 2] = (float) sqrt(sumsq / jcount);
936  }
937 
938  for (int i = sumLen, frames256 = mSummary256Bytes / bytesPerFrame;
939  i < frames256; ++i)
940  {
941  // filling in the remaining bits with non-harming/contributing values
942  // rms values are not "non-harming", so keep count of them:
943  summaries--;
944  summary256[i * fields] = FLT_MAX; // min
945  summary256[i * fields + 1] = -FLT_MAX; // max
946  summary256[i * fields + 2] = 0.0f; // rms
947  }
948 
949  // Calculate now while we can do it accurately
950  mSumRms = sqrt(totalSquares / mSampleCount);
951 
952  // Recalc 64K summaries
953  sumLen = (mSampleCount + 65535) / 65536;
954 
955  for (int i = 0; i < sumLen; ++i)
956  {
957  min = summary256[3 * i * 256];
958  max = summary256[3 * i * 256 + 1];
959  sumsq = summary256[3 * i * 256 + 2];
960  sumsq *= sumsq;
961 
962  for (int j = 1; j < 256; ++j)
963  {
964  // we can overflow the useful summary256 values here, but have put
965  // non-harmful values in them
966  if (summary256[3 * (i * 256 + j)] < min)
967  {
968  min = summary256[3 * (i * 256 + j)];
969  }
970 
971  if (summary256[3 * (i * 256 + j) + 1] > max)
972  {
973  max = summary256[3 * (i * 256 + j) + 1];
974  }
975 
976  float r1 = summary256[3 * (i * 256 + j) + 2];
977  sumsq += r1 * r1;
978  }
979 
980  double denom = (i < sumLen - 1) ? 256.0 : summaries - fraction;
981  float rms = (float) sqrt(sumsq / denom);
982 
983  summary64k[i * fields] = min;
984  summary64k[i * fields + 1] = max;
985  summary64k[i * fields + 2] = rms;
986  }
987 
988  for (int i = sumLen, frames64k = mSummary64kBytes / bytesPerFrame;
989  i < frames64k; ++i)
990  {
991  wxASSERT_MSG(false, wxT("Out of data for mSummaryInfo")); // Do we ever get here?
992 
993  summary64k[i * fields] = 0.0f; // probably should be FLT_MAX, need a test case
994  summary64k[i * fields + 1] = 0.0f; // probably should be -FLT_MAX, need a test case
995  summary64k[i * fields + 2] = 0.0f; // just padding
996  }
997 
998  // Recalc block-level summary (mRMS already calculated)
999  min = summary64k[0];
1000  max = summary64k[1];
1001 
1002  for (int i = 1; i < sumLen; ++i)
1003  {
1004  if (summary64k[i * fields] < min)
1005  {
1006  min = summary64k[i * fields];
1007  }
1008 
1009  if (summary64k[i * fields + 1] > max)
1010  {
1011  max = summary64k[i * fields + 1];
1012  }
1013  }
1014 
1015  mSumMin = min;
1016  mSumMax = max;
1017 }
1018 
1019 // Inject our database implementation at startup
1020 static struct Injector
1021 {
1023  {
1024  // Do this some time before the first project is created
1026  []( AudacityProject &project )
1027  {
1028  return std::make_shared<SqliteSampleBlockFactory>( project );
1029  }
1030  );
1031  }
1033 
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
SampleBlockFactory::BlockDeletionCallback
std::function< void(const SampleBlock &) > BlockDeletionCallback
Type of function that is informed when a block is about to be deleted.
Definition: SampleBlock.h:139
DBConnection::DeleteSampleBlock
@ DeleteSampleBlock
Definition: DBConnection.h:79
SqliteSampleBlock::Conn
DBConnection * Conn() const
This must never be called for silent blocks.
Definition: SqliteSampleBlock.cpp:338
GuardedCall
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), std::function< void(AudacityException *)> delayedHandler=DefaultDelayedHandlerAction{})
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Definition: AudacityException.h:202
DBConnection::StatementID
StatementID
Definition: DBConnection.h:73
SqliteSampleBlock::mSampleBytes
size_t mSampleBytes
Definition: SqliteSampleBlock.cpp:110
ProjectFileIO.h
SqliteSampleBlock::mSampleFormat
sampleFormat mSampleFormat
Definition: SqliteSampleBlock.cpp:112
SqliteSampleBlock::mSamples
ArrayOf< char > mSamples
Definition: SqliteSampleBlock.cpp:109
SentryHelper.h
SqliteSampleBlock::GetSummary64k
bool GetSummary64k(float *dest, size_t frameoffset, size_t numframes) override
Non-throwing, should fill with zeroes on failure.
Definition: SqliteSampleBlock.cpp:420
SqliteSampleBlock::mBlockID
SampleBlockID mBlockID
Definition: SqliteSampleBlock.cpp:107
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
SqliteSampleBlock::Delete
void Delete()
Definition: SqliteSampleBlock.cpp:798
SqliteSampleBlock::GetSumMin
double GetSumMin() const
Definition: SqliteSampleBlock.cpp:459
SqliteSampleBlock::mpFactory
const std::shared_ptr< SqliteSampleBlockFactory > mpFactory
Definition: SqliteSampleBlock.cpp:103
SqliteSampleBlockFactory::mppConnection
const std::shared_ptr< ConnectionPtr > mppConnection
Definition: SqliteSampleBlock.cpp:161
SampleBlockPtr
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: SampleBlock.h:23
SqliteSampleBlockFactory::DoCreateFromXML
SampleBlockPtr DoCreateFromXML(sampleFormat srcformat, const wxChar **attrs) override
Definition: SqliteSampleBlock.cpp:225
ArrayOf::reinit
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
SqliteSampleBlock::SetSizes
Sizes SetSizes(size_t numsamples, sampleFormat srcformat)
Definition: SqliteSampleBlock.cpp:849
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
XO
#define XO(s)
Definition: Internat.h:31
MinMaxRMS
Definition: SampleBlock.h:32
SqliteSampleBlock::~SqliteSampleBlock
~SqliteSampleBlock() override
Definition: SqliteSampleBlock.cpp:306
SqliteSampleBlockFactory::~SqliteSampleBlockFactory
~SqliteSampleBlockFactory() override
SamplesToFloats
void SamplesToFloats(constSamplePtr src, sampleFormat srcFormat, float *dst, size_t len, size_t srcStride, size_t dstStride)
Copy samples from any format into the widest format, which is 32 bit float, with no dithering.
Definition: SampleFormat.cpp:102
DBConnection.h
Declare DBConnection, which maintains database connection and associated status and background thread...
SqliteSampleBlock::GetSumRms
double GetSumRms() const
Definition: SqliteSampleBlock.cpp:469
DBConnection
Definition: DBConnection.h:40
DBConnection::GetSamples
@ GetSamples
Definition: DBConnection.h:74
SqliteSampleBlockFactory::DoCreate
SampleBlockPtr DoCreate(constSamplePtr src, size_t numsamples, sampleFormat srcformat) override
Definition: SqliteSampleBlock.cpp:182
SqliteSampleBlock::Sizes
std::pair< size_t, size_t > Sizes
Numbers of bytes needed for 256 and for 64k summaries.
Definition: SqliteSampleBlock.cpp:41
SampleBlockID
long long SampleBlockID
Definition: ProjectFileIO.h:39
SqliteSampleBlock::mSumMax
double mSumMax
Definition: SqliteSampleBlock.cpp:117
SqliteSampleBlockFactory
Implementation of SampleBlockFactory using Sqlite database.
Definition: SqliteSampleBlock.cpp:135
DBConnection::DB
sqlite3 * DB()
Definition: DBConnection.cpp:410
SampleBlock.h
SqliteSampleBlockFactory::SetBlockDeletionCallback
BlockDeletionCallback SetBlockDeletionCallback(BlockDeletionCallback callback) override
Install a callback, returning the previously installed callback.
Definition: SqliteSampleBlock.cpp:285
floatSample
@ floatSample
Definition: SampleFormat.h:34
injector
static struct Injector injector
SqliteSampleBlock::mValid
bool mValid
Definition: SqliteSampleBlock.cpp:104
SqliteSampleBlock::GetBlob
size_t GetBlob(void *dest, sampleFormat destformat, sqlite3_stmt *stmt, sampleFormat srcformat, size_t srcoffset, size_t srcbytes)
Definition: SqliteSampleBlock.cpp:539
AudacityException
Base class for exceptions specially processed by the application.
Definition: AudacityException.h:33
sSilentBlocks
static std::map< SampleBlockID, std::shared_ptr< SqliteSampleBlock > > sSilentBlocks
Definition: SqliteSampleBlock.cpp:129
SqliteSampleBlock::fields
@ fields
Definition: SqliteSampleBlock.cpp:86
XMLTagHandler.h
SqliteSampleBlock::mLocked
bool mLocked
Definition: SqliteSampleBlock.cpp:105
CopySamples
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
Definition: SampleFormat.cpp:111
SqliteSampleBlock::SqliteSampleBlock
SqliteSampleBlock(const std::shared_ptr< SqliteSampleBlockFactory > &pFactory)
Definition: SqliteSampleBlock.cpp:293
constSamplePtr
const char * constSamplePtr
Definition: SampleFormat.h:50
SqliteSampleBlock
Implementation of SampleBlock using Sqlite database.
Definition: SqliteSampleBlock.cpp:28
SqliteSampleBlock::GetBlockID
SampleBlockID GetBlockID() const override
Definition: SqliteSampleBlock.cpp:361
Injector
Definition: SqliteSampleBlock.cpp:1021
SqliteSampleBlock::DB
sqlite3 * DB() const
Definition: SqliteSampleBlock.cpp:96
SqliteSampleBlock::DoGetSamples
size_t DoGetSamples(samplePtr dest, sampleFormat destformat, size_t sampleoffset, size_t numsamples) override
Definition: SqliteSampleBlock.cpp:376
SampleBlockFactory::RegisterFactoryFactory
static SampleBlockFactoryFactory RegisterFactoryFactory(SampleBlockFactoryFactory newFactory)
Definition: SampleBlock.cpp:22
SqliteSampleBlock::SaveXML
void SaveXML(XMLWriter &xmlFile) override
Definition: SqliteSampleBlock.cpp:844
SqliteSampleBlock::mSumMin
double mSumMin
Definition: SqliteSampleBlock.cpp:116
DBConnection::ThrowException
void ThrowException(bool write) const
throw and show appropriate message box
Definition: DBConnection.cpp:340
SampleBlockFactory::SampleBlockIDs
std::unordered_set< SampleBlockID > SampleBlockIDs
Definition: SampleBlock.h:134
SqliteSampleBlockFactory::mAllBlocks
AllBlocksMap mAllBlocks
Definition: SqliteSampleBlock.cpp:169
SqliteSampleBlock::SetSamples
void SetSamples(constSamplePtr src, size_t numsamples, sampleFormat srcformat)
Definition: SqliteSampleBlock.cpp:399
SqliteSampleBlock::IsSilent
bool IsSilent() const
Definition: SqliteSampleBlock.cpp:71
SampleBuffer
Definition: SampleFormat.h:69
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
SqliteSampleBlock::GetSummary
bool GetSummary(float *dest, size_t frameoffset, size_t numframes, DBConnection::StatementID id, const char *sql)
Definition: SqliteSampleBlock.cpp:428
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
SqliteSampleBlock::Commit
void Commit(Sizes sizes)
Definition: SqliteSampleBlock.cpp:731
id
int id
Definition: WaveTrackControls.cpp:577
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
SqliteSampleBlock::GetSummary256
bool GetSummary256(float *dest, size_t frameoffset, size_t numframes) override
Non-throwing, should fill with zeroes on failure.
Definition: SqliteSampleBlock.cpp:412
DBConnection::GetSummary256
@ GetSummary256
Definition: DBConnection.h:75
SqliteSampleBlock::mSummary64k
ArrayOf< char > mSummary64k
Definition: SqliteSampleBlock.cpp:115
DBConnection::GetSummary64k
@ GetSummary64k
Definition: DBConnection.h:76
Injector::Injector
Injector()
Definition: SqliteSampleBlock.cpp:1022
SqliteSampleBlock::GetSpaceUsage
size_t GetSpaceUsage() const override
Definition: SqliteSampleBlock.cpp:531
SqliteSampleBlock::CalcSummary
void CalcSummary(Sizes sizes)
Definition: SqliteSampleBlock.cpp:866
DBConnection::InsertSampleBlock
@ InsertSampleBlock
Definition: DBConnection.h:78
ConnectionPtr::Get
static ConnectionPtr & Get(AudacityProject &project)
Definition: DBConnection.cpp:728
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
SqliteSampleBlock::mSummary256
ArrayOf< char > mSummary256
Definition: SqliteSampleBlock.cpp:114
SqliteSampleBlock::bytesPerFrame
@ bytesPerFrame
Definition: SqliteSampleBlock.cpp:87
SampleBlock
Abstract class allows access to contents of a block of sound samples, serialization as XML,...
Definition: SampleBlock.h:45
SqliteSampleBlock::SqliteSampleBlockFactory
friend SqliteSampleBlockFactory
Definition: SqliteSampleBlock.cpp:101
DBConnection::LoadSampleBlock
@ LoadSampleBlock
Definition: DBConnection.h:77
XMLWriter::WriteAttr
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:34
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
SqliteSampleBlock::Load
void Load(SampleBlockID sbid)
Definition: SqliteSampleBlock.cpp:663
SampleBlockFactory
abstract base class with methods to produce SampleBlock objects
Definition: SampleBlock.h:106
SqliteSampleBlockFactory::AllBlocksMap
std::map< SampleBlockID, std::weak_ptr< SqliteSampleBlock > > AllBlocksMap
Definition: SqliteSampleBlock.cpp:168
ProjectFileIO::GetDiskUsage
static int64_t GetDiskUsage(DBConnection &conn, SampleBlockID blockid)
Definition: ProjectFileIO.cpp:2372
SqliteSampleBlock::GetSumMax
double GetSumMax() const
Definition: SqliteSampleBlock.cpp:464
SqliteSampleBlock::CloseLock
void CloseLock() override
Definition: SqliteSampleBlock.cpp:356
ADD_EXCEPTION_CONTEXT
#define ADD_EXCEPTION_CONTEXT(name, value)
Definition: SentryHelper.h:21
SqliteSampleBlockFactory::SqliteSampleBlock
friend SqliteSampleBlock
Definition: SqliteSampleBlock.cpp:159
SimpleMessageBoxException
A MessageBoxException that shows a given, unvarying string.
Definition: AudacityException.h:95
SqliteSampleBlock::mSumRms
double mSumRms
Definition: SqliteSampleBlock.cpp:118
DBConnection::Prepare
sqlite3_stmt * Prepare(enum StatementID id, const char *sql)
Definition: DBConnection.cpp:427
SampleFormat.h
SqliteSampleBlock::GetSampleCount
size_t GetSampleCount() const override
Definition: SqliteSampleBlock.cpp:371
ArrayOf< char >
SqliteSampleBlockFactory::SqliteSampleBlockFactory
SqliteSampleBlockFactory(AudacityProject &project)
Definition: SqliteSampleBlock.cpp:174
SqliteSampleBlockFactory::mCallback
BlockDeletionCallback mCallback
Definition: SqliteSampleBlock.cpp:171
SqliteSampleBlock::mSampleCount
size_t mSampleCount
Definition: SqliteSampleBlock.cpp:111
SqliteSampleBlockFactory::DoCreateSilent
SampleBlockPtr DoCreateSilent(size_t numsamples, sampleFormat srcformat) override
Definition: SqliteSampleBlock.cpp:207
SqliteSampleBlockFactory::GetActiveBlockIDs
SampleBlockIDs GetActiveBlockIDs() override
Definition: SqliteSampleBlock.cpp:192
SqliteSampleBlock::DoGetMinMaxRMS
MinMaxRMS DoGetMinMaxRMS() const override
Gets extreme values for the entire block.
Definition: SqliteSampleBlock.cpp:526
SqliteSampleBlock::GetSampleFormat
sampleFormat GetSampleFormat() const
Definition: SqliteSampleBlock.cpp:366
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98