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