file_types.h
Files as streams, mapped files, temporary files, wildcard filters, search paths, and the list of mounted volumes.
#include <sonore/file_types.h>- 8types
- 2other declarations
On this page
Design notes
files.h answers "what is in this folder" and path_io.h "open this UTF-8 path"; this is the layer a plugin reaches for after that, named as the reference names it so code written against it ports by renaming types:
FileInputStream / FileOutputStream a file as an InputStream/OutputStream
(streams.h), by UTF-8 path
FileInputSource a file that can be reopened per use
TemporaryFile write beside the target, then swap it
in -- a save that cannot leave half
a preset behind
MemoryMappedFile a file's bytes in memory, paged in
by the system as they are touched
WildcardFileFilter "*.wav;*.aif" matching
FileSearchPath a list of folders, searched in order
MountedVolumeListChangeDetector notice a drive arriving or leaving
Decisions worth knowing
UTF-8 EVERYWHERE. Every path in and out is UTF-8 and reaches the Windows wide API through path_io.h, long-path prefix included -- the same rule as the rest of the SDK, for the users whose folders are not named in English.
FileOutputStream APPENDS to a file that exists, as the reference's does. It is a trap, and it is kept because silently changing what a ported save routine does is a worse one: setPosition(0) and truncate() after opening (it opens at the end, where truncate() cuts nothing), or write through a TemporaryFile -- which is what a save should do anyway.
WILDCARDS IGNORE CASE on every platform, unlike the reference (which follows the file system's case rule). "*.WAV" refusing "kick.wav" on Linux helps nobody, and files.h's extension filter already ignores case.
MemoryMappedFile is the general form of the mapping system_font.h does for font files (mapFileBytes): the same calls, any range, read or read-write. A mapped file must not be truncated by someone else while mapped -- POSIX raises SIGBUS on the next touch of a page past the new end -- so map files the plugin owns (its samples, its bundle), and read anything else.
NOT FOR THE AUDIO THREAD. Opening, reading, writing, renaming and polling all do I/O and may block for as long as a disk or a network share takes. A touched page of a mapped file can block too: it is a disk read in disguise. The detector's callback runs on its own thread.
Types 8
class FileInputStream : public InputStream
A file, read as a stream.
class FileOutputStream : public OutputStream
A file, written as a stream. An existing file is opened at its END -- see the header -- and created when it does not exist (its folder must).
class FileInputSource : public InputSource
A file as an InputSource: reopened for each stream asked of it.
A file to write instead of the real one, swapped in when it is complete.
A save that writes the target directly and is interrupted -- a crash, a full disk, a host killed mid-save -- leaves a truncated preset where a good one was. Written beside the target and renamed over it, the target is either the old file or the new one, never half of each: the rename is atomic on one volume on every platform here.
The temporary file is deleted when this is destroyed, if it is still there.
A file, or a range of one, mapped into memory.
getData() is null when the file could not be opened or mapped, or when there is nothing to map: an empty file, or a range of no bytes -- never non-null with a size of 0. A range is clipped to the file -- a mapping never grows a file -- and may start anywhere: the system maps from the allocation boundary below it and getData() points at the byte asked for.
exclusive keeps other programs from opening the file while it is mapped (a share mode on Windows, an advisory flock elsewhere -- advisory meaning only programs that also ask are kept out).
"*.wav;*.aif?" against a file's NAME (not its folder).
Patterns are separated by ';' or ',', and spaces around them are ignored, so "*.wav; *.aiff" and the dialog filters' "*.wav;*.aiff" read alike. '*' is any run of characters, '?' any one character -- one UTF-8 code point, not one byte, so "?.wav" matches "é.wav". Case is ignored (ASCII), on every platform: see the header.
An empty pattern list matches everything, as the reference's does.
An ordered list of folders: where a plugin looks for its impulse responses, a sampler for its libraries. Written and read as one string, the folders separated by ';' -- on every platform, as the reference does, so a saved list moves between machines.
Tells you when a drive is plugged in or removed -- a sample library on an external disk appearing, a browser's list of locations going stale.
It POLLS, on its own thread, every pollIntervalMs (a second by default): the list of volumes is compared with the last one. The alternatives are worse for a plugin -- WM_DEVICECHANGE needs a top-level window and the message loop that owns it, which a plugin has neither of, and DiskArbitration and udev are each a library to load for one bit of information. Reading the drive mask costs microseconds.
onChange (or an override of mountedVolumeListChanged) runs on the detector's thread, not a UI thread: hand the news over (a UiEventQueue, an atomic flag the editor's tick reads). A subclass that overrides the virtual must call stop() in its own destructor, before its members go -- the thread may be calling it. The callback may delete the detector: the thread then ends without touching it again.
Functions 2
- fn
inline std::string temporaryDirectory()The system's folder for temporary files, without a trailing separator: %TEMP% on Windows (GetTempPath), $TMPDIR or /tmp elsewhere.file_types.h:121 - fn
inline std::vector<std::string> mountedVolumes()The volumes mounted now, sorted: drive roots on Windows ("C:\\", "D:\\"), "/" and the entries of /Volumes on macOS, and on Linux the mount points in /proc/self/mounts that hold files -- the kernel's own pseudo file systems (proc, sysfs, cgroup, devpts...) and snap packages' squashfs images are left out, since a snap refreshing itself is not a drive arriving.file_types.h:912