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
24 class SqliteSampleBlockFactory;
25
26 ///\brief Implementation of @ref SampleBlock using Sqlite database
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
40 //! Numbers of bytes needed for 256 and for 64k summaries
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;
52 sampleFormat GetSampleFormat() const;
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
61 /// Gets extreme values for the specified region
62 MinMaxRMS DoGetMinMaxRMS(size_t start, size_t len) override;
63
64 /// Gets extreme values for the entire block
65 MinMaxRMS DoGetMinMaxRMS() const override;
66
67 size_t GetSpaceUsage() const override;
68 void SaveXML(XMLWriter &xmlFile) override;
69
70 private:
IsSilent() const71 bool IsSilent() const { return mBlockID <= 0; }
72 void Load(SampleBlockID sbid);
73 bool GetSummary(float *dest,
74 size_t frameoffset,
75 size_t numframes,
76 DBConnection::StatementID id,
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:
93 //! This must never be called for silent blocks
94 /*! @post return value is not null */
95 DBConnection *Conn() const;
DB() const96 sqlite3 *DB() const
97 {
98 return Conn()->DB();
99 }
100
101 friend SqliteSampleBlockFactory;
102
103 const std::shared_ptr<SqliteSampleBlockFactory> mpFactory;
104 bool mValid{ false };
105 bool mLocked = false;
106
107 SampleBlockID mBlockID{ 0 };
108
109 ArrayOf<char> mSamples;
110 size_t mSampleBytes;
111 size_t mSampleCount;
112 sampleFormat mSampleFormat;
113
114 ArrayOf<char> mSummary256;
115 ArrayOf<char> mSummary64k;
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> >
129 sSilentBlocks;
130
131 ///\brief Implementation of @ref SampleBlockFactory using Sqlite database
132 class SqliteSampleBlockFactory final
133 : public SampleBlockFactory
134 , public std::enable_shared_from_this<SqliteSampleBlockFactory>
135 {
136 public:
137 explicit SqliteSampleBlockFactory( AudacityProject &project );
138
139 ~SqliteSampleBlockFactory() override;
140
141 SampleBlockIDs GetActiveBlockIDs() override;
142
143 SampleBlockPtr DoCreate(constSamplePtr src,
144 size_t numsamples,
145 sampleFormat srcformat) override;
146
147 SampleBlockPtr DoCreateSilent(
148 size_t numsamples,
149 sampleFormat srcformat) override;
150
151 SampleBlockPtr DoCreateFromXML(
152 sampleFormat srcformat,
153 const AttributesList &attrs) override;
154
155 BlockDeletionCallback SetBlockDeletionCallback(
156 BlockDeletionCallback callback ) override;
157
158 private:
159 friend SqliteSampleBlock;
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 > >;
169 AllBlocksMap mAllBlocks;
170
171 BlockDeletionCallback mCallback;
172 };
173
SqliteSampleBlockFactory(AudacityProject & project)174 SqliteSampleBlockFactory::SqliteSampleBlockFactory( AudacityProject &project )
175 : mppConnection{ ConnectionPtr::Get(project).shared_from_this() }
176 {
177
178 }
179
180 SqliteSampleBlockFactory::~SqliteSampleBlockFactory() = default;
181
DoCreate(constSamplePtr src,size_t numsamples,sampleFormat srcformat)182 SampleBlockPtr SqliteSampleBlockFactory::DoCreate(
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
GetActiveBlockIDs()192 auto SqliteSampleBlockFactory::GetActiveBlockIDs() -> SampleBlockIDs
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
DoCreateSilent(size_t numsamples,sampleFormat)207 SampleBlockPtr SqliteSampleBlockFactory::DoCreateSilent(
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
DoCreateFromXML(sampleFormat srcformat,const AttributesList & attrs)225 SampleBlockPtr SqliteSampleBlockFactory::DoCreateFromXML(
226 sampleFormat srcformat, const AttributesList &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 for (auto pair : attrs)
234 {
235 auto attr = pair.first;
236 auto value = pair.second;
237
238 long long nValue;
239
240 if (attr == "blockid" && value.TryGet(nValue))
241 {
242 if (nValue <= 0) {
243 sb = DoCreateSilent( -nValue, floatSample );
244 }
245 else {
246 // First see if this block id was previously loaded
247 auto &wb = mAllBlocks[ nValue ];
248 auto pb = wb.lock();
249 if (pb)
250 // Reuse the block
251 sb = pb;
252 else {
253 // First sight of this id
254 auto ssb =
255 std::make_shared<SqliteSampleBlock>(shared_from_this());
256 wb = ssb;
257 sb = ssb;
258 ssb->mSampleFormat = srcformat;
259 // This may throw database errors
260 // It initializes the rest of the fields
261 ssb->Load((SampleBlockID) nValue);
262 }
263 }
264 found++;
265 }
266 }
267
268 // Were all attributes found?
269 if (found != 1)
270 {
271 return nullptr;
272 }
273
274 return sb;
275 }
276
SetBlockDeletionCallback(BlockDeletionCallback callback)277 auto SqliteSampleBlockFactory::SetBlockDeletionCallback(
278 BlockDeletionCallback callback ) -> BlockDeletionCallback
279 {
280 auto result = mCallback;
281 mCallback = std::move( callback );
282 return result;
283 }
284
SqliteSampleBlock(const std::shared_ptr<SqliteSampleBlockFactory> & pFactory)285 SqliteSampleBlock::SqliteSampleBlock(
286 const std::shared_ptr<SqliteSampleBlockFactory> &pFactory)
287 : mpFactory(pFactory)
288 {
289 mSampleFormat = floatSample;
290 mSampleBytes = 0;
291 mSampleCount = 0;
292
293 mSumMin = 0.0;
294 mSumMax = 0.0;
295 mSumRms = 0.0;
296 }
297
~SqliteSampleBlock()298 SqliteSampleBlock::~SqliteSampleBlock()
299 {
300 if (mpFactory) {
301 auto &callback = mpFactory->mCallback;
302 if (callback)
303 GuardedCall( [&]{ callback( *this ); } );
304 }
305
306 if (IsSilent()) {
307 // The block object was constructed but failed to Load() or Commit().
308 // Or it's a silent block with no row in the database.
309 // Just let the stack unwind. Don't violate the assertion in
310 // Delete(), which may do odd recursive things in debug builds when it
311 // yields to the UI to put up a dialog, but then dispatches timer
312 // events that try again to finish recording.
313 return;
314 }
315
316 // See ProjectFileIO::Bypass() for a description of mIO.mBypass
317 GuardedCall( [this]{
318 if (!mLocked && !Conn()->ShouldBypass())
319 {
320 // In case Delete throws, don't let an exception escape a destructor,
321 // but we can still enqueue the delayed handler so that an error message
322 // is presented to the user.
323 // The failure in this case may be a less harmful waste of space in the
324 // database, which should not cause aborting of the attempted edit.
325 Delete();
326 }
327 } );
328 }
329
Conn() const330 DBConnection *SqliteSampleBlock::Conn() const
331 {
332 if (!mpFactory)
333 return nullptr;
334
335 auto &pConnection = mpFactory->mppConnection->mpConnection;
336 if (!pConnection) {
337 throw SimpleMessageBoxException
338 {
339 ExceptionType::Internal,
340 XO("Connection to project file is null"),
341 XO("Warning"),
342 "Error:_Disk_full_or_not_writable"
343 };
344 }
345 return pConnection.get();
346 }
347
CloseLock()348 void SqliteSampleBlock::CloseLock()
349 {
350 mLocked = true;
351 }
352
GetBlockID() const353 SampleBlockID SqliteSampleBlock::GetBlockID() const
354 {
355 return mBlockID;
356 }
357
GetSampleFormat() const358 sampleFormat SqliteSampleBlock::GetSampleFormat() const
359 {
360 return mSampleFormat;
361 }
362
GetSampleCount() const363 size_t SqliteSampleBlock::GetSampleCount() const
364 {
365 return mSampleCount;
366 }
367
DoGetSamples(samplePtr dest,sampleFormat destformat,size_t sampleoffset,size_t numsamples)368 size_t SqliteSampleBlock::DoGetSamples(samplePtr dest,
369 sampleFormat destformat,
370 size_t sampleoffset,
371 size_t numsamples)
372 {
373 if (IsSilent()) {
374 auto size = SAMPLE_SIZE(destformat);
375 memset(dest, 0, numsamples * size);
376 return numsamples;
377 }
378
379 // Prepare and cache statement...automatically finalized at DB close
380 sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::GetSamples,
381 "SELECT samples FROM sampleblocks WHERE blockid = ?1;");
382
383 return GetBlob(dest,
384 destformat,
385 stmt,
386 mSampleFormat,
387 sampleoffset * SAMPLE_SIZE(mSampleFormat),
388 numsamples * SAMPLE_SIZE(mSampleFormat)) / SAMPLE_SIZE(mSampleFormat);
389 }
390
SetSamples(constSamplePtr src,size_t numsamples,sampleFormat srcformat)391 void SqliteSampleBlock::SetSamples(constSamplePtr src,
392 size_t numsamples,
393 sampleFormat srcformat)
394 {
395 auto sizes = SetSizes(numsamples, srcformat);
396 mSamples.reinit(mSampleBytes);
397 memcpy(mSamples.get(), src, mSampleBytes);
398
399 CalcSummary( sizes );
400
401 Commit( sizes );
402 }
403
GetSummary256(float * dest,size_t frameoffset,size_t numframes)404 bool SqliteSampleBlock::GetSummary256(float *dest,
405 size_t frameoffset,
406 size_t numframes)
407 {
408 return GetSummary(dest, frameoffset, numframes, DBConnection::GetSummary256,
409 "SELECT summary256 FROM sampleblocks WHERE blockid = ?1;");
410 }
411
GetSummary64k(float * dest,size_t frameoffset,size_t numframes)412 bool SqliteSampleBlock::GetSummary64k(float *dest,
413 size_t frameoffset,
414 size_t numframes)
415 {
416 return GetSummary(dest, frameoffset, numframes, DBConnection::GetSummary64k,
417 "SELECT summary64k FROM sampleblocks WHERE blockid = ?1;");
418 }
419
GetSummary(float * dest,size_t frameoffset,size_t numframes,DBConnection::StatementID id,const char * sql)420 bool SqliteSampleBlock::GetSummary(float *dest,
421 size_t frameoffset,
422 size_t numframes,
423 DBConnection::StatementID id,
424 const char *sql)
425 {
426 // Non-throwing, it returns true for success
427 bool silent = IsSilent();
428 if (!silent) {
429 // Not a silent block
430 try {
431 // Prepare and cache statement...automatically finalized at DB close
432 auto stmt = Conn()->Prepare(id, sql);
433 // Note GetBlob returns a size_t, not a bool
434 // REVIEW: An error in GetBlob() will throw an exception.
435 GetBlob(dest,
436 floatSample,
437 stmt,
438 floatSample,
439 frameoffset * fields * SAMPLE_SIZE(floatSample),
440 numframes * fields * SAMPLE_SIZE(floatSample));
441 return true;
442 }
443 catch ( const AudacityException & ) {
444 }
445 }
446 memset(dest, 0, 3 * numframes * sizeof( float ));
447 // Return true for success only if we didn't catch
448 return silent;
449 }
450
GetSumMin() const451 double SqliteSampleBlock::GetSumMin() const
452 {
453 return mSumMin;
454 }
455
GetSumMax() const456 double SqliteSampleBlock::GetSumMax() const
457 {
458 return mSumMax;
459 }
460
GetSumRms() const461 double SqliteSampleBlock::GetSumRms() const
462 {
463 return mSumRms;
464 }
465
466 /// Retrieves the minimum, maximum, and maximum RMS of the
467 /// specified sample data in this block.
468 ///
469 /// @param start The offset in this block where the region should begin
470 /// @param len The number of samples to include in the region
DoGetMinMaxRMS(size_t start,size_t len)471 MinMaxRMS SqliteSampleBlock::DoGetMinMaxRMS(size_t start, size_t len)
472 {
473 if (IsSilent())
474 return {};
475
476 float min = FLT_MAX;
477 float max = -FLT_MAX;
478 float sumsq = 0;
479
480 if (!mValid)
481 {
482 Load(mBlockID);
483 }
484
485 if (start < mSampleCount)
486 {
487 len = std::min(len, mSampleCount - start);
488
489 // TODO: actually use summaries
490 SampleBuffer blockData(len, floatSample);
491 float *samples = (float *) blockData.ptr();
492
493 size_t copied = DoGetSamples((samplePtr) samples, floatSample, start, len);
494 for (size_t i = 0; i < copied; ++i, ++samples)
495 {
496 float sample = *samples;
497
498 if (sample > max)
499 {
500 max = sample;
501 }
502
503 if (sample < min)
504 {
505 min = sample;
506 }
507
508 sumsq += (sample * sample);
509 }
510 }
511
512 return { min, max, (float) sqrt(sumsq / len) };
513 }
514
515 /// Retrieves the minimum, maximum, and maximum RMS of this entire
516 /// block. This is faster than the other GetMinMax function since
517 /// these values are already computed.
DoGetMinMaxRMS() const518 MinMaxRMS SqliteSampleBlock::DoGetMinMaxRMS() const
519 {
520 return { (float) mSumMin, (float) mSumMax, (float) mSumRms };
521 }
522
GetSpaceUsage() const523 size_t SqliteSampleBlock::GetSpaceUsage() const
524 {
525 if (IsSilent())
526 return 0;
527 else
528 return ProjectFileIO::GetDiskUsage(*Conn(), mBlockID);
529 }
530
GetBlob(void * dest,sampleFormat destformat,sqlite3_stmt * stmt,sampleFormat srcformat,size_t srcoffset,size_t srcbytes)531 size_t SqliteSampleBlock::GetBlob(void *dest,
532 sampleFormat destformat,
533 sqlite3_stmt *stmt,
534 sampleFormat srcformat,
535 size_t srcoffset,
536 size_t srcbytes)
537 {
538 auto db = DB();
539
540 wxASSERT(!IsSilent());
541
542 if (!mValid)
543 {
544 Load(mBlockID);
545 }
546
547 int rc;
548 size_t minbytes = 0;
549
550 // Bind statement parameters
551 // Might return SQLITE_MISUSE which means it's our mistake that we violated
552 // preconditions; should return SQL_OK which is 0
553 if (sqlite3_bind_int64(stmt, 1, mBlockID))
554 {
555 ADD_EXCEPTION_CONTEXT(
556 "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
557 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::GetBlob::bind");
558
559 wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
560 }
561
562 // Execute the statement
563 rc = sqlite3_step(stmt);
564 if (rc != SQLITE_ROW)
565 {
566 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
567 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::GetBlob::step");
568
569 wxLogDebug(wxT("SqliteSampleBlock::GetBlob - SQLITE error %s"), sqlite3_errmsg(db));
570
571 // Clear statement bindings and rewind statement
572 sqlite3_clear_bindings(stmt);
573 sqlite3_reset(stmt);
574
575 // Just showing the user a simple message, not the library error too
576 // which isn't internationalized
577 // Actually this can lead to 'Could not read from file' error message
578 // but it can also lead to no error message at all and a flat line,
579 // depending on where GetBlob is called from.
580 // The latter can happen when repainting the screen.
581 // That possibly happens on a very slow machine. Possibly that's the
582 // right trade off when a machine can't keep up?
583 // ANSWER-ME: Do we always report an error when we should here?
584 Conn()->ThrowException( false );
585 }
586
587 // Retrieve returned data
588 samplePtr src = (samplePtr) sqlite3_column_blob(stmt, 0);
589 size_t blobbytes = (size_t) sqlite3_column_bytes(stmt, 0);
590
591 srcoffset = std::min(srcoffset, blobbytes);
592 minbytes = std::min(srcbytes, blobbytes - srcoffset);
593
594 if (srcoffset != 0)
595 {
596 srcoffset += 0;
597 }
598
599 /*
600 Will dithering happen in CopySamples? Answering this as of 3.0.3 by
601 examining all uses.
602
603 As this function is called from GetSummary, no, because destination format
604 is float.
605
606 There is only one other call to this function, in DoGetSamples. At one
607 call to that function, in DoGetMinMaxRMS, again format is float always.
608
609 There is only one other call to DoGetSamples, in SampleBlock::GetSamples().
610 In one call to that function, in WaveformView.cpp, again format is float.
611
612 That leaves two calls in Sequence.cpp. One of those can be proved to be
613 used only in copy and paste operations, always supplying the same sample
614 format as the samples were stored in, therefore no dither.
615
616 That leaves uses of Sequence::Read(). There are uses of Read() in internal
617 operations also easily shown to use only the saved format, and
618 GetWaveDisplay() always reads as float.
619
620 The remaining use of Sequence::Read() is in Sequence::Get(). That is used
621 by WaveClip::Resample(), always fetching float. It is also used in
622 WaveClip::GetSamples().
623
624 There is only one use of that function not always fetching float, in
625 WaveTrack::Get().
626
627 It can be shown that the only paths to WaveTrack::Get() not specifying
628 floatSample are in Benchmark, which is only a diagnostic test, and there
629 the sample format is the same as what the track was constructed with.
630
631 Therefore, no dithering even there!
632 */
633 wxASSERT(destformat == floatSample || destformat == srcformat);
634
635 CopySamples(src + srcoffset,
636 srcformat,
637 (samplePtr) dest,
638 destformat,
639 minbytes / SAMPLE_SIZE(srcformat));
640
641 dest = ((samplePtr) dest) + minbytes;
642
643 if (srcbytes - minbytes)
644 {
645 memset(dest, 0, srcbytes - minbytes);
646 }
647
648 // Clear statement bindings and rewind statement
649 sqlite3_clear_bindings(stmt);
650 sqlite3_reset(stmt);
651
652 return srcbytes;
653 }
654
Load(SampleBlockID sbid)655 void SqliteSampleBlock::Load(SampleBlockID sbid)
656 {
657 auto db = DB();
658 int rc;
659
660 wxASSERT(sbid > 0);
661
662 mValid = false;
663 mSampleCount = 0;
664 mSampleBytes = 0;
665 mSumMin = FLT_MAX;
666 mSumMax = -FLT_MAX;
667 mSumMin = 0.0;
668
669 // Prepare and cache statement...automatically finalized at DB close
670 sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::LoadSampleBlock,
671 "SELECT sampleformat, summin, summax, sumrms,"
672 " length(samples)"
673 " FROM sampleblocks WHERE blockid = ?1;");
674
675 // Bind statement parameters
676 // Might return SQLITE_MISUSE which means it's our mistake that we violated
677 // preconditions; should return SQL_OK which is 0
678 if (sqlite3_bind_int64(stmt, 1, sbid))
679 {
680
681 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
682 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Load::bind");
683
684 wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
685 }
686
687 // Execute the statement
688 rc = sqlite3_step(stmt);
689 if (rc != SQLITE_ROW)
690 {
691
692 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
693 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Load::step");
694
695
696 wxLogDebug(wxT("SqliteSampleBlock::Load - SQLITE error %s"), sqlite3_errmsg(db));
697
698 // Clear statement bindings and rewind statement
699 sqlite3_clear_bindings(stmt);
700 sqlite3_reset(stmt);
701
702 // Just showing the user a simple message, not the library error too
703 // which isn't internationalized
704 Conn()->ThrowException( false );
705 }
706
707 // Retrieve returned data
708 mBlockID = sbid;
709 mSampleFormat = (sampleFormat) sqlite3_column_int(stmt, 0);
710 mSumMin = sqlite3_column_double(stmt, 1);
711 mSumMax = sqlite3_column_double(stmt, 2);
712 mSumRms = sqlite3_column_double(stmt, 3);
713 mSampleBytes = sqlite3_column_int(stmt, 4);
714 mSampleCount = mSampleBytes / SAMPLE_SIZE(mSampleFormat);
715
716 // Clear statement bindings and rewind statement
717 sqlite3_clear_bindings(stmt);
718 sqlite3_reset(stmt);
719
720 mValid = true;
721 }
722
Commit(Sizes sizes)723 void SqliteSampleBlock::Commit(Sizes sizes)
724 {
725 const auto mSummary256Bytes = sizes.first;
726 const auto mSummary64kBytes = sizes.second;
727
728 auto db = DB();
729 int rc;
730
731 // Prepare and cache statement...automatically finalized at DB close
732 sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::InsertSampleBlock,
733 "INSERT INTO sampleblocks (sampleformat, summin, summax, sumrms,"
734 " summary256, summary64k, samples)"
735 " VALUES(?1,?2,?3,?4,?5,?6,?7);");
736
737 // Bind statement parameters
738 // Might return SQLITE_MISUSE which means it's our mistake that we violated
739 // preconditions; should return SQL_OK which is 0
740 if (sqlite3_bind_int(stmt, 1, mSampleFormat) ||
741 sqlite3_bind_double(stmt, 2, mSumMin) ||
742 sqlite3_bind_double(stmt, 3, mSumMax) ||
743 sqlite3_bind_double(stmt, 4, mSumRms) ||
744 sqlite3_bind_blob(stmt, 5, mSummary256.get(), mSummary256Bytes, SQLITE_STATIC) ||
745 sqlite3_bind_blob(stmt, 6, mSummary64k.get(), mSummary64kBytes, SQLITE_STATIC) ||
746 sqlite3_bind_blob(stmt, 7, mSamples.get(), mSampleBytes, SQLITE_STATIC))
747 {
748
749 ADD_EXCEPTION_CONTEXT(
750 "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
751 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Commit::bind");
752
753
754 wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
755 }
756
757 // Execute the statement
758 rc = sqlite3_step(stmt);
759 if (rc != SQLITE_DONE)
760 {
761 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
762 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Commit::step");
763
764 wxLogDebug(wxT("SqliteSampleBlock::Commit - SQLITE error %s"), sqlite3_errmsg(db));
765
766 // Clear statement bindings and rewind statement
767 sqlite3_clear_bindings(stmt);
768 sqlite3_reset(stmt);
769
770 // Just showing the user a simple message, not the library error too
771 // which isn't internationalized
772 Conn()->ThrowException( true );
773 }
774
775 // Retrieve returned data
776 mBlockID = sqlite3_last_insert_rowid(db);
777
778 // Reset local arrays
779 mSamples.reset();
780 mSummary256.reset();
781 mSummary64k.reset();
782
783 // Clear statement bindings and rewind statement
784 sqlite3_clear_bindings(stmt);
785 sqlite3_reset(stmt);
786
787 mValid = true;
788 }
789
Delete()790 void SqliteSampleBlock::Delete()
791 {
792 auto db = DB();
793 int rc;
794
795 wxASSERT(!IsSilent());
796
797 // Prepare and cache statement...automatically finalized at DB close
798 sqlite3_stmt *stmt = Conn()->Prepare(DBConnection::DeleteSampleBlock,
799 "DELETE FROM sampleblocks WHERE blockid = ?1;");
800
801 // Bind statement parameters
802 // Might return SQLITE_MISUSE which means it's our mistake that we violated
803 // preconditions; should return SQL_OK which is 0
804 if (sqlite3_bind_int64(stmt, 1, mBlockID))
805 {
806 ADD_EXCEPTION_CONTEXT(
807 "sqlite3.rc", std::to_string(sqlite3_errcode(Conn()->DB())));
808 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Delete::bind");
809
810 wxASSERT_MSG(false, wxT("Binding failed...bug!!!"));
811 }
812
813 // Execute the statement
814 rc = sqlite3_step(stmt);
815 if (rc != SQLITE_DONE)
816 {
817 ADD_EXCEPTION_CONTEXT("sqlite3.rc", std::to_string(rc));
818 ADD_EXCEPTION_CONTEXT("sqlite3.context", "SqliteSampleBlock::Delete::step");
819
820 wxLogDebug(wxT("SqliteSampleBlock::Load - SQLITE error %s"), sqlite3_errmsg(db));
821
822 // Clear statement bindings and rewind statement
823 sqlite3_clear_bindings(stmt);
824 sqlite3_reset(stmt);
825
826 // Just showing the user a simple message, not the library error too
827 // which isn't internationalized
828 Conn()->ThrowException( true );
829 }
830
831 // Clear statement bindings and rewind statement
832 sqlite3_clear_bindings(stmt);
833 sqlite3_reset(stmt);
834 }
835
SaveXML(XMLWriter & xmlFile)836 void SqliteSampleBlock::SaveXML(XMLWriter &xmlFile)
837 {
838 xmlFile.WriteAttr(wxT("blockid"), mBlockID);
839 }
840
SetSizes(size_t numsamples,sampleFormat srcformat)841 auto SqliteSampleBlock::SetSizes(
842 size_t numsamples, sampleFormat srcformat ) -> Sizes
843 {
844 mSampleFormat = srcformat;
845 mSampleCount = numsamples;
846 mSampleBytes = mSampleCount * SAMPLE_SIZE(mSampleFormat);
847
848 int frames64k = (mSampleCount + 65535) / 65536;
849 int frames256 = frames64k * 256;
850 return { frames256 * bytesPerFrame, frames64k * bytesPerFrame };
851 }
852
853 /// Calculates summary block data describing this sample data.
854 ///
855 /// This method also has the side effect of setting the mSumMin,
856 /// mSumMax, and mSumRms members of this class.
857 ///
CalcSummary(Sizes sizes)858 void SqliteSampleBlock::CalcSummary(Sizes sizes)
859 {
860 const auto mSummary256Bytes = sizes.first;
861 const auto mSummary64kBytes = sizes.second;
862
863 Floats samplebuffer;
864 float *samples;
865
866 if (mSampleFormat == floatSample)
867 {
868 samples = (float *) mSamples.get();
869 }
870 else
871 {
872 samplebuffer.reinit((unsigned) mSampleCount);
873 SamplesToFloats(mSamples.get(), mSampleFormat,
874 samplebuffer.get(), mSampleCount);
875 samples = samplebuffer.get();
876 }
877
878 mSummary256.reinit(mSummary256Bytes);
879 mSummary64k.reinit(mSummary64kBytes);
880
881 float *summary256 = (float *) mSummary256.get();
882 float *summary64k = (float *) mSummary64k.get();
883
884 float min;
885 float max;
886 float sumsq;
887 double totalSquares = 0.0;
888 double fraction = 0.0;
889
890 // Recalc 256 summaries
891 int sumLen = (mSampleCount + 255) / 256;
892 int summaries = 256;
893
894 for (int i = 0; i < sumLen; ++i)
895 {
896 min = samples[i * 256];
897 max = samples[i * 256];
898 sumsq = min * min;
899
900 int jcount = 256;
901 if (jcount > mSampleCount - i * 256)
902 {
903 jcount = mSampleCount - i * 256;
904 fraction = 1.0 - (jcount / 256.0);
905 }
906
907 for (int j = 1; j < jcount; ++j)
908 {
909 float f1 = samples[i * 256 + j];
910 sumsq += f1 * f1;
911
912 if (f1 < min)
913 {
914 min = f1;
915 }
916 else if (f1 > max)
917 {
918 max = f1;
919 }
920 }
921
922 totalSquares += sumsq;
923
924 summary256[i * fields] = min;
925 summary256[i * fields + 1] = max;
926 // The rms is correct, but this may be for less than 256 samples in last loop.
927 summary256[i * fields + 2] = (float) sqrt(sumsq / jcount);
928 }
929
930 for (int i = sumLen, frames256 = mSummary256Bytes / bytesPerFrame;
931 i < frames256; ++i)
932 {
933 // filling in the remaining bits with non-harming/contributing values
934 // rms values are not "non-harming", so keep count of them:
935 summaries--;
936 summary256[i * fields] = FLT_MAX; // min
937 summary256[i * fields + 1] = -FLT_MAX; // max
938 summary256[i * fields + 2] = 0.0f; // rms
939 }
940
941 // Calculate now while we can do it accurately
942 mSumRms = sqrt(totalSquares / mSampleCount);
943
944 // Recalc 64K summaries
945 sumLen = (mSampleCount + 65535) / 65536;
946
947 for (int i = 0; i < sumLen; ++i)
948 {
949 min = summary256[3 * i * 256];
950 max = summary256[3 * i * 256 + 1];
951 sumsq = summary256[3 * i * 256 + 2];
952 sumsq *= sumsq;
953
954 for (int j = 1; j < 256; ++j)
955 {
956 // we can overflow the useful summary256 values here, but have put
957 // non-harmful values in them
958 if (summary256[3 * (i * 256 + j)] < min)
959 {
960 min = summary256[3 * (i * 256 + j)];
961 }
962
963 if (summary256[3 * (i * 256 + j) + 1] > max)
964 {
965 max = summary256[3 * (i * 256 + j) + 1];
966 }
967
968 float r1 = summary256[3 * (i * 256 + j) + 2];
969 sumsq += r1 * r1;
970 }
971
972 double denom = (i < sumLen - 1) ? 256.0 : summaries - fraction;
973 float rms = (float) sqrt(sumsq / denom);
974
975 summary64k[i * fields] = min;
976 summary64k[i * fields + 1] = max;
977 summary64k[i * fields + 2] = rms;
978 }
979
980 for (int i = sumLen, frames64k = mSummary64kBytes / bytesPerFrame;
981 i < frames64k; ++i)
982 {
983 wxASSERT_MSG(false, wxT("Out of data for mSummaryInfo")); // Do we ever get here?
984
985 summary64k[i * fields] = 0.0f; // probably should be FLT_MAX, need a test case
986 summary64k[i * fields + 1] = 0.0f; // probably should be -FLT_MAX, need a test case
987 summary64k[i * fields + 2] = 0.0f; // just padding
988 }
989
990 // Recalc block-level summary (mRMS already calculated)
991 min = summary64k[0];
992 max = summary64k[1];
993
994 for (int i = 1; i < sumLen; ++i)
995 {
996 if (summary64k[i * fields] < min)
997 {
998 min = summary64k[i * fields];
999 }
1000
1001 if (summary64k[i * fields + 1] > max)
1002 {
1003 max = summary64k[i * fields + 1];
1004 }
1005 }
1006
1007 mSumMin = min;
1008 mSumMax = max;
1009 }
1010
1011 // Inject our database implementation at startup
1012 static struct Injector
1013 {
InjectorInjector1014 Injector()
1015 {
1016 // Do this some time before the first project is created
1017 (void) SampleBlockFactory::RegisterFactoryFactory(
1018 []( AudacityProject &project )
1019 {
1020 return std::make_shared<SqliteSampleBlockFactory>( project );
1021 }
1022 );
1023 }
1024 } injector;
1025
1026