···11-From deeb435829d73524df851f6f4c2d4be552c99230 Mon Sep 17 00:00:00 2001
22-From: Dmitry Vedenko <dmitry@crsib.me>
33-Date: Fri, 1 Oct 2021 16:21:22 +0300
44-Subject: [PATCH] Use a different approach to estimate the disk space usage
55-66-New a approach is a bit less precise, but removes the requirement for the "private" SQLite3 table and allows Audacity to be built against system SQLite3.
77----
88- cmake-proxies/sqlite/CMakeLists.txt | 5 -
99- src/DBConnection.h | 4 +-
1010- src/ProjectFileIO.cpp | 269 +++++-----------------------
1111- 3 files changed, 44 insertions(+), 234 deletions(-)
1212-1313-diff --git a/cmake-proxies/sqlite/CMakeLists.txt b/cmake-proxies/sqlite/CMakeLists.txt
1414-index 63d70637c..d7b9b95ef 100644
1515---- a/cmake-proxies/sqlite/CMakeLists.txt
1616-+++ b/cmake-proxies/sqlite/CMakeLists.txt
1717-@@ -19,11 +19,6 @@ list( APPEND INCLUDES
1818-1919- list( APPEND DEFINES
2020- PRIVATE
2121-- #
2222-- # We need the dbpage table for space calculations.
2323-- #
2424-- SQLITE_ENABLE_DBPAGE_VTAB=1
2525--
2626- # Can't be set after a WAL mode database is initialized, so change
2727- # the default here to ensure all project files get the same page
2828- # size.
2929-diff --git a/src/DBConnection.h b/src/DBConnection.h
3030-index 16a7fc9d4..07d3af95e 100644
3131---- a/src/DBConnection.h
3232-+++ b/src/DBConnection.h
3333-@@ -75,8 +75,8 @@ public:
3434- LoadSampleBlock,
3535- InsertSampleBlock,
3636- DeleteSampleBlock,
3737-- GetRootPage,
3838-- GetDBPage
3939-+ GetSampleBlockSize,
4040-+ GetAllSampleBlocksSize
4141- };
4242- sqlite3_stmt *Prepare(enum StatementID id, const char *sql);
4343-4444-diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp
4545-index 3b3e2e1fd..c9bc45af4 100644
4646---- a/src/ProjectFileIO.cpp
4747-+++ b/src/ProjectFileIO.cpp
4848-@@ -35,6 +35,7 @@ Paul Licameli split from AudacityProject.cpp
4949- #include "widgets/ProgressDialog.h"
5050- #include "wxFileNameWrapper.h"
5151- #include "xml/XMLFileReader.h"
5252-+#include "MemoryX.h"`
5353-5454- #undef NO_SHM
5555- #if !defined(__WXMSW__)
5656-@@ -2357,255 +2358,69 @@ int64_t ProjectFileIO::GetTotalUsage()
5757- }
5858-5959- //
6060--// Returns the amount of disk space used by the specified sample blockid or all
6161--// of the sample blocks if the blockid is 0. It does this by using the raw SQLite
6262--// pages available from the "sqlite_dbpage" virtual table to traverse the SQLite
6363--// table b-tree described here: https://www.sqlite.org/fileformat.html
6464-+// Returns the estimation of disk space used by the specified sample blockid or all
6565-+// of the sample blocks if the blockid is 0. This does not include small overhead
6666-+// of the internal SQLite structures, only the size used by the data
6767- //
6868- int64_t ProjectFileIO::GetDiskUsage(DBConnection &conn, SampleBlockID blockid /* = 0 */)
6969- {
7070-- // Information we need to track our travels through the b-tree
7171-- typedef struct
7272-- {
7373-- int64_t pgno;
7474-- int currentCell;
7575-- int numCells;
7676-- unsigned char data[65536];
7777-- } page;
7878-- std::vector<page> stack;
7979--
8080-- int64_t total = 0;
8181-- int64_t found = 0;
8282-- int64_t right = 0;
8383-- int rc;
8484-+ sqlite3_stmt* stmt = nullptr;
8585-8686-- // Get the rootpage for the sampleblocks table.
8787-- sqlite3_stmt *stmt =
8888-- conn.Prepare(DBConnection::GetRootPage,
8989-- "SELECT rootpage FROM sqlite_master WHERE tbl_name = 'sampleblocks';");
9090-- if (stmt == nullptr || sqlite3_step(stmt) != SQLITE_ROW)
9191-+ if (blockid == 0)
9292- {
9393-- return 0;
9494-- }
9595--
9696-- // And store it in our first stack frame
9797-- stack.push_back({sqlite3_column_int64(stmt, 0)});
9898-+ static const char* statement =
9999-+R"(SELECT
100100-+ sum(length(blockid) + length(sampleformat) +
101101-+ length(summin) + length(summax) + length(sumrms) +
102102-+ length(summary256) + length(summary64k) +
103103-+ length(samples))
104104-+FROM sampleblocks;)";
105105-106106-- // All done with the statement
107107-- sqlite3_clear_bindings(stmt);
108108-- sqlite3_reset(stmt);
109109--
110110-- // Prepare/retrieve statement to read raw database page
111111-- stmt = conn.Prepare(DBConnection::GetDBPage,
112112-- "SELECT data FROM sqlite_dbpage WHERE pgno = ?1;");
113113-- if (stmt == nullptr)
114114-- {
115115-- return 0;
116116-+ stmt = conn.Prepare(DBConnection::GetAllSampleBlocksSize, statement);
117117- }
118118--
119119-- // Traverse the b-tree until we've visited all of the leaf pages or until
120120-- // we find the one corresponding to the passed in sample blockid. Because we
121121-- // use an integer primary key for the sampleblocks table, the traversal will
122122-- // be in ascending blockid sequence.
123123-- do
124124-+ else
125125- {
126126-- // Acces the top stack frame
127127-- page &pg = stack.back();
128128-+ static const char* statement =
129129-+R"(SELECT
130130-+ length(blockid) + length(sampleformat) +
131131-+ length(summin) + length(summax) + length(sumrms) +
132132-+ length(summary256) + length(summary64k) +
133133-+ length(samples)
134134-+FROM sampleblocks WHERE blockid = ?1;)";
135135-136136-- // Read the page from the sqlite_dbpage table if it hasn't yet been loaded
137137-- if (pg.numCells == 0)
138138-- {
139139-- // Bind the page number
140140-- sqlite3_bind_int64(stmt, 1, pg.pgno);
141141-+ stmt = conn.Prepare(DBConnection::GetSampleBlockSize, statement);
142142-+ }
143143-144144-- // And retrieve the page
145145-- if (sqlite3_step(stmt) != SQLITE_ROW)
146146-+ auto cleanup = finally(
147147-+ [stmt]() {
148148-+ // Clear statement bindings and rewind statement
149149-+ if (stmt != nullptr)
150150- {
151151-- // REVIEW: Likely harmless failure - says size is zero on
152152-- // this error.
153153-- // LLL: Yea, but not much else we can do.
154154-- return 0;
155155-+ sqlite3_clear_bindings(stmt);
156156-+ sqlite3_reset(stmt);
157157- }
158158-+ });
159159-160160-- // Copy the page content to the stack frame
161161-- memcpy(&pg.data,
162162-- sqlite3_column_blob(stmt, 0),
163163-- sqlite3_column_bytes(stmt, 0));
164164--
165165-- // And retrieve the total number of cells within it
166166-- pg.numCells = get2(&pg.data[3]);
167167--
168168-- // Reset statement for next usage
169169-- sqlite3_clear_bindings(stmt);
170170-- sqlite3_reset(stmt);
171171-- }
172172--
173173-- //wxLogDebug("%*.*spgno %lld currentCell %d numCells %d", (stack.size() - 1) * 2, (stack.size() - 1) * 2, "", pg.pgno, pg.currentCell, pg.numCells);
174174--
175175-- // Process an interior table b-tree page
176176-- if (pg.data[0] == 0x05)
177177-- {
178178-- // Process the next cell if we haven't examined all of them yet
179179-- if (pg.currentCell < pg.numCells)
180180-- {
181181-- // Remember the right-most leaf page number.
182182-- right = get4(&pg.data[8]);
183183--
184184-- // Iterate over the cells.
185185-- //
186186-- // If we're not looking for a specific blockid, then we always push the
187187-- // target page onto the stack and leave the loop after a single iteration.
188188-- //
189189-- // Otherwise, we match the blockid against the highest integer key contained
190190-- // within the cell and if the blockid falls within the cell, we stack the
191191-- // page and stop the iteration.
192192-- //
193193-- // In theory, we could do a binary search for a specific blockid here, but
194194-- // because our sample blocks are always large, we will get very few cells
195195-- // per page...usually 6 or less.
196196-- //
197197-- // In both cases, the stacked page can be either an internal or leaf page.
198198-- bool stacked = false;
199199-- while (pg.currentCell < pg.numCells)
200200-- {
201201-- // Get the offset to this cell using the offset in the cell pointer
202202-- // array.
203203-- //
204204-- // The cell pointer array starts immediately after the page header
205205-- // at offset 12 and the retrieved offset is from the beginning of
206206-- // the page.
207207-- int celloff = get2(&pg.data[12 + (pg.currentCell * 2)]);
208208--
209209-- // Bump to the next cell for the next iteration.
210210-- pg.currentCell++;
211211--
212212-- // Get the page number this cell describes
213213-- int pagenum = get4(&pg.data[celloff]);
214214--
215215-- // And the highest integer key, which starts at offset 4 within the cell.
216216-- int64_t intkey = 0;
217217-- get_varint(&pg.data[celloff + 4], &intkey);
218218--
219219-- //wxLogDebug("%*.*sinternal - right %lld celloff %d pagenum %d intkey %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", right, celloff, pagenum, intkey);
220220--
221221-- // Stack the described page if we're not looking for a specific blockid
222222-- // or if this page contains the given blockid.
223223-- if (!blockid || blockid <= intkey)
224224-- {
225225-- stack.push_back({pagenum, 0, 0});
226226-- stacked = true;
227227-- break;
228228-- }
229229-- }
230230--
231231-- // If we pushed a new page onto the stack, we need to jump back up
232232-- // to read the page
233233-- if (stacked)
234234-- {
235235-- continue;
236236-- }
237237-- }
238238-+ if (blockid != 0)
239239-+ {
240240-+ int rc = sqlite3_bind_int64(stmt, 1, blockid);
241241-242242-- // We've exhausted all the cells with this page, so we stack the right-most
243243-- // leaf page. Ensure we only process it once.
244244-- if (right)
245245-- {
246246-- stack.push_back({right, 0, 0});
247247-- right = 0;
248248-- continue;
249249-- }
250250-- }
251251-- // Process a leaf table b-tree page
252252-- else if (pg.data[0] == 0x0d)
253253-+ if (rc != SQLITE_OK)
254254- {
255255-- // Iterate over the cells
256256-- //
257257-- // If we're not looking for a specific blockid, then just accumulate the
258258-- // payload sizes. We will be reading every leaf page in the sampleblocks
259259-- // table.
260260-- //
261261-- // Otherwise we break out when we find the matching blockid. In this case,
262262-- // we only ever look at 1 leaf page.
263263-- bool stop = false;
264264-- for (int i = 0; i < pg.numCells; i++)
265265-- {
266266-- // Get the offset to this cell using the offset in the cell pointer
267267-- // array.
268268-- //
269269-- // The cell pointer array starts immediately after the page header
270270-- // at offset 8 and the retrieved offset is from the beginning of
271271-- // the page.
272272-- int celloff = get2(&pg.data[8 + (i * 2)]);
273273--
274274-- // Get the total payload size in bytes of the described row.
275275-- int64_t payload = 0;
276276-- int digits = get_varint(&pg.data[celloff], &payload);
277277--
278278-- // Get the integer key for this row.
279279-- int64_t intkey = 0;
280280-- get_varint(&pg.data[celloff + digits], &intkey);
281281--
282282-- //wxLogDebug("%*.*sleaf - celloff %4d intkey %lld payload %lld", (stack.size() - 1) * 2, (stack.size() - 1) * 2, " ", celloff, intkey, payload);
283283--
284284-- // Add this payload size to the total if we're not looking for a specific
285285-- // blockid
286286-- if (!blockid)
287287-- {
288288-- total += payload;
289289-- }
290290-- // Otherwise, return the payload size for a matching row
291291-- else if (blockid == intkey)
292292-- {
293293-- return payload;
294294-- }
295295-- }
296296-+ conn.ThrowException(false);
297297- }
298298-+ }
299299-300300-- // Done with the current branch, so pop back up to the previous one (if any)
301301-- stack.pop_back();
302302-- } while (!stack.empty());
303303--
304304-- // Return the total used for all sample blocks
305305-- return total;
306306--}
307307--
308308--// Retrieves a 2-byte big-endian integer from the page data
309309--unsigned int ProjectFileIO::get2(const unsigned char *ptr)
310310--{
311311-- return (ptr[0] << 8) | ptr[1];
312312--}
313313--
314314--// Retrieves a 4-byte big-endian integer from the page data
315315--unsigned int ProjectFileIO::get4(const unsigned char *ptr)
316316--{
317317-- return ((unsigned int) ptr[0] << 24) |
318318-- ((unsigned int) ptr[1] << 16) |
319319-- ((unsigned int) ptr[2] << 8) |
320320-- ((unsigned int) ptr[3]);
321321--}
322322--
323323--// Retrieves a variable length integer from the page data. Returns the
324324--// number of digits used to encode the integer and the stores the
325325--// value at the given location.
326326--int ProjectFileIO::get_varint(const unsigned char *ptr, int64_t *out)
327327--{
328328-- int64_t val = 0;
329329-- int i;
330330-+ int rc = sqlite3_step(stmt);
331331-332332-- for (i = 0; i < 8; ++i)
333333-+ if (rc != SQLITE_ROW)
334334- {
335335-- val = (val << 7) + (ptr[i] & 0x7f);
336336-- if ((ptr[i] & 0x80) == 0)
337337-- {
338338-- *out = val;
339339-- return i + 1;
340340-- }
341341-+ conn.ThrowException(false);
342342- }
343343-344344-- val = (val << 8) + (ptr[i] & 0xff);
345345-- *out = val;
346346-+ const int64_t size = sqlite3_column_int64(stmt, 0);
347347-348348-- return 9;
349349-+ return size;
350350- }
351351-352352- InvisibleTemporaryProject::InvisibleTemporaryProject()
353353---
354354-2.33.1
355355-
+58-35
pkgs/applications/audio/audacity/default.nix
···33, fetchFromGitHub
44, fetchpatch
55, cmake
66+, makeWrapper
67, pkg-config
78, python3
89, gettext
···2021, libsndfile
2122, soxr
2223, flac
2424+, lame
2325, twolame
2426, expat
2527, libid3tag
2628, libopus
2929+, libuuid
2730, ffmpeg_4
2831, soundtouch
2932, pcre
3030-/*, portaudio - given up fighting their portaudio.patch */
3333+, portaudio # given up fighting their portaudio.patch?
3434+, portmidi
3135, linuxHeaders
3236, alsa-lib
3337, at-spi2-core
···3640, libXdmcp
3741, libXtst
3842, libpthreadstubs
4343+, libsbsms_2_3_0
3944, libselinux
4045, libsepol
4146, libxkbcommon
4247, util-linux
4348, wxGTK
4949+, libpng
5050+, libjpeg
4451, AppKit ? null
4552, AudioToolbox ? null
4653, AudioUnit ? null
···58655966let
6067 inherit (lib) optionals;
6868+ pname = "audacity";
6969+ version = "3.1.3";
61706271 wxWidgets_src = fetchFromGitHub {
6363- owner = "audacity";
7272+ owner = pname;
6473 repo = "wxWidgets";
6565- rev = "07e7d832c7a337aedba3537b90b2c98c4d8e2985";
6666- sha256 = "1mawnkcrmqj98jp0jxlnh9xkc950ca033ccb51c7035pzmi9if9a";
7474+ rev = "v${version}-${pname}";
7575+ sha256 = "sha256-KrmYYv23DHBYKIuxMYBioCQ2e4KWdgmuREnimtm0XNU=";
6776 fetchSubmodules = true;
6877 };
6978···7483 wxmac' = wxmac.overrideAttrs (oldAttrs: rec {
7584 src = wxWidgets_src;
7685 });
7777-7878-in
7979-stdenv.mkDerivation rec {
8080- pname = "audacity";
8181- # nixpkgs-update: no auto update
8282- # Humans too! Let's wait to see how the situation with
8383- # https://github.com/audacity/audacity/issues/1213 develops before
8484- # pulling any updates that are subject to this privacy policy. We
8585- # may wish to switch to a fork, but at the time of writing
8686- # (2021-07-05) it's too early to tell how well any of the forks will
8787- # be maintained.
8888- version = "3.0.2";
8686+in stdenv.mkDerivation rec {
8787+ inherit pname version;
89889089 src = fetchFromGitHub {
9191- owner = "audacity";
9292- repo = "audacity";
9090+ owner = pname;
9191+ repo = pname;
9392 rev = "Audacity-${version}";
9494- sha256 = "035qq2ff16cdl2cb9iply2bfjmhfl1dpscg79x6c9l0i9m8k41zj";
9393+ sha256 = "sha256-sdI4paxIHDZgoWTCekjrkFR4JFpQC6OatcnJdVXCCZk=";
9594 };
96959797- patches = [
9898- (fetchpatch {
9999- url = "https://github.com/audacity/audacity/commit/7f8135e112a0e1e8e906abab9339680d1e491441.patch";
100100- sha256 = "0zp2iydd46analda9cfnbmzdkjphz5m7dynrdj5qdnmq6j3px9fw";
101101- name = "audacity_xdg_paths.patch";
102102- })
103103- # This is required to make audacity work with nixpkgs’ sqlite
104104- # https://github.com/audacity/audacity/pull/1802 rebased onto 3.0.2
105105- ./0001-Use-a-different-approach-to-estimate-the-disk-space-.patch
106106- ];
107107-10896 postPatch = ''
109109- touch src/RevisionIdent.h
9797+ mkdir src/private
11098 '' + lib.optionalString stdenv.isLinux ''
111111- substituteInPlace src/FileNames.cpp \
9999+ substituteInPlace libraries/lib-files/FileNames.cpp \
112100 --replace /usr/include/linux/magic.h ${linuxHeaders}/include/linux/magic.h
113101 '';
114102···119107 python3
120108 ] ++ optionals stdenv.isLinux [
121109 linuxHeaders
110110+ makeWrapper
122111 ];
123112124113 buildInputs = [
···126115 ffmpeg_4
127116 file
128117 flac
118118+ lame
129119 libid3tag
130120 libjack2
131121 libmad
132122 libopus
123123+ libsbsms_2_3_0
133124 libsndfile
134125 libvorbis
135126 lilv
136127 lv2
137128 pcre
129129+ portmidi
138130 serd
139131 sord
140132 soundtouch
···143135 sratom
144136 suil
145137 twolame
138138+ portaudio
146139 ] ++ optionals stdenv.isLinux [
147140 alsa-lib # for portaudio
148141 at-spi2-core
···154147 libxkbcommon
155148 libselinux
156149 libsepol
150150+ libuuid
157151 util-linux
158152 wxGTK'
159153 wxGTK'.gtk
···163157 Cocoa
164158 CoreAudioKit
165159 AudioUnit AudioToolbox CoreAudio CoreServices Carbon # for portaudio
160160+ libpng
161161+ libjpeg
166162 ];
167163168164 cmakeFlags = [
169169- "-Daudacity_use_ffmpeg=linked"
165165+ "-DAUDACITY_REV_LONG=nixpkgs"
166166+ "-DAUDACITY_REV_TIME=nixpkgs"
170167 "-DDISABLE_DYNAMIC_LOADING_FFMPEG=ON"
168168+ "-Daudacity_conan_enabled=Off"
169169+ "-Daudacity_use_ffmpeg=loaded"
171170 ];
172171173172 doCheck = false; # Test fails
174173174174+ # Replace audacity's wrapper, to:
175175+ # - put it in the right place, it shouldn't be in "$out/audacity"
176176+ # - Add the ffmpeg dynamic dependency
177177+ postInstall = lib.optionalString stdenv.isLinux ''
178178+ rm "$out/audacity"
179179+ wrapProgram "$out/bin/audacity" \
180180+ --prefix LD_LIBRARY_PATH : "$out/lib/audacity":${lib.makeLibraryPath [ ffmpeg_4 ]} \
181181+ --suffix AUDACITY_MODULES_PATH : "$out/lib/audacity/modules" \
182182+ --suffix AUDACITY_PATH : "$out/share/audacity"
183183+ '';
184184+175185 meta = with lib; {
176186 description = "Sound editor with graphical UI";
177177- homepage = "https://www.audacityteam.org/";
178178- license = licenses.gpl2Plus;
187187+ homepage = "https://www.audacityteam.org";
188188+ changelog = "https://github.com/audacity/audacity/releases";
189189+ license = with licenses; [
190190+ gpl2Plus
191191+ # Must be GPL3 when building with "technologies that require it,
192192+ # such as the VST3 audio plugin interface".
193193+ # https://github.com/audacity/audacity/discussions/2142.
194194+ gpl3
195195+ # Documentation.
196196+ cc-by-30
197197+ ];
179198 maintainers = with maintainers; [ lheckemann veprbl ];
180199 platforms = platforms.unix;
200200+ # darwin-aarch due to qtbase broken for it.
201201+ # darwin-x86_64 due to
202202+ # https://logs.nix.ci/?attempt_id=5cbc4581-09b4-4148-82fe-0326411a56b3&key=nixos%2Fnixpkgs.152273.
203203+ broken = stdenv.isDarwin;
181204 };
182205}