├── .gitignore ├── README.md ├── dub.json ├── examples ├── dump-patterns │ ├── dub.json │ └── source │ │ └── main.d ├── midi2wav │ ├── dub.sdl │ └── source │ │ ├── main.d │ │ └── nukedopl3.d └── transcode │ ├── dub.json │ └── source │ └── main.d └── source └── audioformats ├── dopus.d ├── drflac.d ├── internals.d ├── io.d ├── libxm.d ├── minimp3.d ├── minimp3_ex.d ├── package.d ├── pocketmod.d ├── qoa.d ├── stb_vorbis2.d ├── stream.d └── wav.d /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.o 3 | *.obj 4 | 5 | # Compiled Dynamic libraries 6 | *.so 7 | *.dylib 8 | *.dll 9 | 10 | # Compiled Static libraries 11 | *.a 12 | *.lib 13 | 14 | # Executables 15 | *.exe 16 | 17 | # DUB 18 | .dub 19 | docs.json 20 | __dummy.html 21 | docs/ 22 | 23 | # Code coverage 24 | *.lst 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # audio-formats 2 | 3 | `audio-formats` is meant to be the easiest package to load and write sounds in D. 4 | 5 | ## Features 6 | 7 | - ✅ Decode from WAV / QOA / MP3 / FLAC / OPUS / OGG / MOD / XM 8 | - ✅ Encode to WAV / QOA 9 | - ✅ File and memory support 10 | - ✅ Seeking support 11 | - ✅ Chunked support 12 | - ✅ `float` and `double` support 13 | - ✅ Encoding with dithering when reducing bit-depth 14 | - ✅ `nothrow @nogc` API 15 | - ✅ Archs: `x86` / `x86_64` / `arm64` 16 | 17 | 18 | 19 | ## Changelog 20 | 21 | ### 🔔 `audio-formats` v3 22 | - Complete removal of exceptions. The API is now 100% `nothrow @nogc`. 23 | `AudioFormatsException` doesn't exist anymore. 24 | - **BREAKING** Instead, use `.isError` and `.isValid` to check for errors. 25 | 26 | ### 🔔 `audio-formats` v2 27 | 28 | - Doesn't depend upon `dplug:core` anymore. 29 | - All exceptions thrown by `audio-formats` are now `AudioFormatsException`. 30 | They must be clean-up with `destroyAudioFormatException`. 31 | - **v2.1** QOA format decoding support (https://github.com/phoboslab/qoa). 32 | Note that the QOA bitstream isn't finalized, and will change. 33 | - **v2.2** QOA format encoding support. 34 | 35 | ### 🔔 `audio-formats` v1 36 | - Initial release. 37 | 38 | 39 | ## How to use it? 40 | 41 | - Add `audio-formats` as dependency to your `dub.json` or `dub.sdl`. 42 | - See the [transcode example](https://github.com/AuburnSounds/audio-formats/blob/master/examples/transcode/source/main.d) for usage. 43 | 44 | ## What formats are supported exactly? 45 | 46 | | | Decoding | Encoding | Seeking support | 47 | |-------|------------|----------|-----------------| 48 | | 📀 WAV | Yes | Yes | Sample | 49 | | 📀 MP3 | Yes | No | Sample | 50 | | 📀 FLAC | Yes | No | Sample | 51 | | 📀 OPUS | Yes (LGPL) | No | Sample | 52 | | 📀 OGG | Yes | No | Sample | 53 | | 📀 QOA | Yes | Yes | Sample | 54 | | 📀 MOD | Yes | No | Pattern+Row | 55 | | 📀 XM | Yes | No | Pattern+Row | 56 | 57 | 58 | _Some of these decoders were originally translated by Ketmar, who did the heavy-lifting._ 59 | 60 | 61 | ## License 62 | 63 | - ⚖️ Boost license otherwise. 64 | - ⚖️ MIT license when including QOA. 65 | - ⚖️ LGPL v2.1 for OPUS. 66 | (use DUB subconfigurations) to choose, default is boost. 67 | 68 | ## External links and references 69 | 70 | - https://github.com/Zoadian/mp3decoder 71 | - https://github.com/rombankzero/pocketmod 72 | - https://github.com/Artefact2/libxm 73 | 74 | ## Ultra secret options 75 | -The following `version` identifiers can be used to enable/disable decoder level features 76 | | Version Identifier | Feature | 77 | |--------------------|---------------------------------------------------------------| 78 | | AF_LINEAR | Use linear sampling for MOD modules instead of Amiga sampling | 79 | | | | 80 | 81 | ## Bugs 82 | 83 | - `framesRemainingInPattern` is unimplemented for XM currently. 84 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "audio-formats", 3 | "description": "Streaming decoding support for popular audio formats.", 4 | "license": "BSL-1.0", 5 | 6 | "configurations": [ 7 | { 8 | "name": "boost", 9 | "versions": ["decodeWAV", "decodeMP3", "decodeFLAC", "decodeOGG", "decodeMOD", "decodeXM", 10 | "encodeWAV"] 11 | }, 12 | { 13 | "name": "mit", 14 | "versions": ["decodeWAV", "decodeQOA", "encodeQOA", "decodeMP3", "decodeFLAC", "decodeOGG", "decodeMOD", "decodeXM", 15 | "encodeWAV" ] 16 | }, 17 | { 18 | "name": "lgpl", 19 | "versions": ["decodeWAV", "decodeQOA", "encodeQOA", "decodeMP3", "decodeFLAC", "decodeOGG", "decodeOPUS", "decodeMOD", "decodeXM", 20 | "encodeWAV"] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /examples/dump-patterns/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dump-patterns", 3 | "description": "Attempt at decode a XM/MOD while reorganizing it. Testbed for module functions.", 4 | "license": "BSL-1.0", 5 | 6 | "dependencies": 7 | { 8 | "audio-formats": { "path": "../.." } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/dump-patterns/source/main.d: -------------------------------------------------------------------------------- 1 | module main; 2 | 3 | import std.stdio; 4 | import std.file; 5 | 6 | import audioformats; 7 | 8 | // Currently it can take a MOD and dump patterns in the wrong order... 9 | // Is or seek function conceptually correct? 10 | 11 | void main(string[] args) 12 | { 13 | if (args.length != 3) 14 | throw new Exception("usage: dump-patterns input.{mod|xm} doubled.wav"); 15 | 16 | string inputPath = args[1]; 17 | string outputPath = args[2]; 18 | 19 | try 20 | { 21 | 22 | AudioStream input, output; 23 | 24 | input.openFromFile(args[1]); 25 | if (input.isError) 26 | throw new Exception(input.errorMessage); 27 | float sampleRate = input.getSamplerate(); 28 | int channels = input.getNumChannels(); 29 | long lengthFrames = input.getLengthInFrames(); 30 | 31 | if (!input.isModule) 32 | throw new Exception("Must be a module"); 33 | if (!input.canSeek) 34 | throw new Exception("Must be seekable"); 35 | 36 | float[] buf; 37 | output.openToFile(outputPath, AudioFileFormat.wav, sampleRate, channels); 38 | if (output.isError) 39 | throw new Exception(output.errorMessage); 40 | 41 | 42 | int patternCount = input.getModuleLength(); 43 | 44 | for (int pattern = 0; pattern < patternCount; ++pattern) // Note: that iterated patterns in order, but that's not the right order in the MOD 45 | { 46 | input.seekPosition(pattern, 0); 47 | 48 | // how many remaining frames in this pattern? 49 | int remain = cast(int) input.framesRemainingInPattern(); 50 | buf.length = remain * channels; 51 | int framesRead = input.readSamplesFloat(buf); // this should read the whole pattern 52 | if (input.isError) 53 | throw new Exception(input.errorMessage); 54 | assert( (framesRead == remain) ); 55 | 56 | output.writeSamplesFloat(buf[0..framesRead*channels]); 57 | if (output.isError) 58 | throw new Exception(output.errorMessage); 59 | } 60 | 61 | output.destroy(); 62 | 63 | writefln("=> %s patterns decoded and encoded to %s", patternCount, outputPath); 64 | } 65 | catch(Exception e) 66 | { 67 | writeln(e.msg); 68 | } 69 | } -------------------------------------------------------------------------------- /examples/midi2wav/dub.sdl: -------------------------------------------------------------------------------- 1 | name "midi2wav" 2 | license "GPL-v2" 3 | dependency "audio-formats" path="../.." -------------------------------------------------------------------------------- /examples/midi2wav/source/main.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.file; 3 | import std.conv; 4 | 5 | import audioformats; 6 | import nukedopl3; 7 | 8 | void usage() 9 | { 10 | writeln("Usage:"); 11 | writeln(" midi2wav input.mid output.wav [-sr 96000][-opl2|-opl3]"); 12 | writeln(); 13 | writeln("Description:"); 14 | writeln(" This describes a WAV files and its content."); 15 | writeln; 16 | writeln("Flags:"); 17 | writeln(" -h Shows this help"); 18 | writeln(" -sr Specify sampling rate of output (default = 48000)"); 19 | writeln(" -opl2 OPL2 mode"); 20 | writeln(" -opl3 OPL3 mode (default)"); 21 | writeln(" -amp Volume (defaut = 1.0)"); 22 | writeln; 23 | } 24 | 25 | 26 | // Use arsd.nukedopl3 to transcode MIDI to wav. 27 | int main(string[] args) 28 | { 29 | bool help = false; 30 | int samplerate = 48000; 31 | float volume = 1.0f; 32 | bool opl3mode = true; 33 | string inputPath = null; 34 | string outputPath = null; 35 | for (int i = 1; i < args.length; ++ i) 36 | { 37 | string arg = args[i]; 38 | 39 | if (arg == "-h") 40 | help = true; 41 | else if (arg == "-opl2") 42 | opl3mode = false; 43 | else if (arg == "-opl3") 44 | opl3mode = true; 45 | else if (arg == "-sr") 46 | { 47 | ++i; 48 | samplerate = to!int(args[i]); 49 | } 50 | else if (arg == "-amp") 51 | { 52 | ++i; 53 | volume = to!double(args[i]); 54 | } 55 | else if (inputPath is null) 56 | { 57 | inputPath = arg; 58 | } 59 | else if (outputPath is null) 60 | { 61 | outputPath = arg; 62 | } 63 | else 64 | { 65 | writeln("error: too many files given"); 66 | usage; 67 | return 1; 68 | } 69 | } 70 | 71 | if (help) 72 | { 73 | usage; 74 | return 0; 75 | } 76 | 77 | if (inputPath is null || outputPath is null) 78 | { 79 | usage; 80 | return 1; 81 | } 82 | 83 | try 84 | { 85 | 86 | bool stereo = true; 87 | OPLPlayer synth = new OPLPlayer(samplerate, opl3mode, stereo); 88 | 89 | ubyte[] songBytes = cast(ubyte[]) std.file.read(inputPath); 90 | if (!synth.load(songBytes)) 91 | throw new Exception("Can't load song"); 92 | 93 | if (!synth.play ()) 94 | throw new Exception("Can't play song"); 95 | 96 | short[2 * 1024] buf; 97 | float[2 * 1024] fbuf; 98 | 99 | AudioStream stream; 100 | 101 | AudioFileFormat outFormat = AudioFileFormat.wav; 102 | int channels = 2; 103 | stream.openToFile(outputPath, outFormat, samplerate, channels); 104 | if (stream.isError) 105 | throw new Exception(stream.errorMessage); 106 | 107 | ulong totalFrames = 0; 108 | while(true) 109 | { 110 | uint frames = synth.generate(buf[]); 111 | 112 | if (frames == 0) 113 | break; 114 | 115 | totalFrames += frames; 116 | 117 | for (uint n = 0; n < frames*2; ++n) 118 | { 119 | fbuf[n] = buf[n] / 32767.0f; 120 | } 121 | stream.writeSamplesFloat(fbuf[0..frames*channels]); 122 | if (stream.isError) 123 | throw new Exception(stream.errorMessage); 124 | } 125 | 126 | writefln("%s frames transcoded from %s to %s", totalFrames, inputPath, outputPath); 127 | return 0; 128 | } 129 | catch(Exception e) 130 | { 131 | writeln(e.msg); 132 | return 1; 133 | } 134 | } -------------------------------------------------------------------------------- /examples/transcode/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "transcode", 3 | "description": "Decode MP3/WAV input, output a WAV.", 4 | "license": "LGPL-2.1+BSL-1.0+MIT", 5 | 6 | "dependencies": 7 | { 8 | "audio-formats": { "path": "../.." } 9 | }, 10 | 11 | "subConfigurations": 12 | { 13 | "audio-formats": "lgpl" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/transcode/source/main.d: -------------------------------------------------------------------------------- 1 | module main; 2 | 3 | import std.stdio; 4 | import std.file; 5 | 6 | import audioformats; 7 | import core.stdc.stdlib; 8 | 9 | 10 | //debug = checkSeeking; 11 | 12 | void main(string[] args) 13 | { 14 | if (args.length != 3) 15 | throw new Exception("usage: transcode input.{mp3|wav|flac|ogg|opus|qoa|mod|xm} output.{wav|qoa}"); 16 | 17 | string inputPath = args[1]; 18 | string outputPath = args[2]; 19 | 20 | try 21 | { 22 | void check(ref AudioStream stream) 23 | { 24 | if (stream.isError) 25 | throw new Exception(stream.errorMessage); 26 | } 27 | 28 | AudioStream input, output; 29 | 30 | input.openFromFile(args[1]); 31 | check(input); 32 | float sampleRate = input.getSamplerate(); 33 | int channels = input.getNumChannels(); 34 | long lengthFrames = input.getLengthInFrames(); 35 | 36 | writefln("Opening %s:", inputPath); 37 | writefln(" * format = %s", convertAudioFileFormatToString(input.getFormat()) ); 38 | writefln(" * samplerate = %s Hz", sampleRate); 39 | writefln(" * channels = %s", channels); 40 | if (lengthFrames == audiostreamUnknownLength) 41 | { 42 | writefln(" * length = unknown"); 43 | } 44 | else 45 | { 46 | double seconds = lengthFrames / cast(double) sampleRate; 47 | writefln(" * length = %.3g seconds (%s frames)", seconds, lengthFrames); 48 | } 49 | 50 | debug(checkSeeking) additionalTests(input); 51 | 52 | float[] buf = new float[1024 * channels]; 53 | 54 | EncodingOptions options; 55 | options.sampleFormat = AudioSampleFormat.s24; 56 | options.enableDither = true; 57 | 58 | bool isQOA = outputPath.length > 4 && outputPath[$-4..$] == ".qoa"; 59 | AudioFileFormat outFormat = AudioFileFormat.wav; 60 | if (isQOA) 61 | { 62 | outFormat = AudioFileFormat.qoa; 63 | } 64 | 65 | output.openToFile(outputPath, outFormat, sampleRate, channels, options); 66 | check(output); 67 | 68 | // Chunked encode/decode 69 | int totalFrames = 0; 70 | int framesRead; 71 | do 72 | { 73 | framesRead = input.readSamplesFloat(buf); 74 | check(input); 75 | output.writeSamplesFloat(buf[0..framesRead*channels]); 76 | check(output); 77 | totalFrames += framesRead; 78 | } while(framesRead > 0); 79 | 80 | output.destroy(); 81 | 82 | writefln("=> %s frames decoded and encoded to %s", totalFrames, outputPath); 83 | } 84 | catch(Exception e) 85 | { 86 | writeln(e.msg); 87 | } 88 | } 89 | 90 | debug(checkSeeking) 91 | { 92 | // For audio-formats debug purpose. 93 | void additionalTests(ref AudioStream input) 94 | { 95 | int channels = input.getNumChannels(); 96 | long lengthFrames = input.getLengthInFrames(); 97 | if (lengthFrames == audiostreamUnknownLength) 98 | return; 99 | 100 | int maxFrame = cast(int) lengthFrames; 101 | 102 | // Check that seeking work 103 | if (input.canSeek() && !input.isModule()) 104 | { 105 | assert(input.tellPosition() == 0); 106 | 107 | // Seeking at beginning is always legal. 108 | bool res = input.seekPosition(0); 109 | assert(res && input.tellPosition() == 0); 110 | 111 | // Seeking past the end is illegal and is a no-op 112 | res = input.seekPosition(maxFrame + 1); 113 | assert(!res && input.tellPosition() == 0); 114 | 115 | // Seeking before beginning is illegal and is a no-op 116 | res = input.seekPosition(-1); 117 | assert(!res && input.tellPosition() == 0); 118 | 119 | // Seeking in the middle is of course legal 120 | { 121 | int where = maxFrame / 2; 122 | res = input.seekPosition(where); 123 | int here = input.tellPosition(); 124 | assert(res && here == where); 125 | } 126 | 127 | // It is legal to seek just before the end. 128 | if (maxFrame > 0) 129 | { 130 | res = input.seekPosition(maxFrame - 1); 131 | int pos = input.tellPosition(); 132 | assert(res && pos == maxFrame - 1); 133 | 134 | AudioFileFormat fmt = input.getFormat(); 135 | { 136 | // Where the remaining decoding should yield one frame 137 | float[] smallbuf = new float[16 * channels]; 138 | int read = input.readSamplesFloat(smallbuf); 139 | assert(read == 1); 140 | 141 | res = input.seekPosition(maxFrame); 142 | pos = input.tellPosition(); 143 | assert(res && pos == maxFrame); 144 | // Where the remaining decoding should yield 0 frame 145 | read = input.readSamplesFloat(smallbuf); 146 | assert(read == 0); 147 | 148 | // And at this point we cannot seek to beginning in OGG, since stream is finished (has returned 0 samples). 149 | } 150 | } 151 | 152 | // Come back at start, read 16 frames. 153 | res = input.seekPosition(0); 154 | assert(res && input.tellPosition() == 0); 155 | { 156 | float[] smallbuf = new float[16 * channels]; 157 | int read = input.readSamplesFloat(smallbuf); 158 | assert(read == 16); 159 | } 160 | res = input.seekPosition(0); 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /source/audioformats/internals.d: -------------------------------------------------------------------------------- 1 | /** 2 | This is helpful to break dependency upon dplug:core. 3 | 4 | Copyright: Guillaume Piolats 2022. 5 | License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 | */ 7 | module audioformats.internals; 8 | 9 | 10 | import core.stdc.stdlib: malloc, free, realloc; 11 | import core.stdc.string: memcpy; 12 | import core.exception: onOutOfMemoryErrorNoGC; 13 | import std.conv: emplace; 14 | import std.traits; 15 | 16 | static immutable string 17 | kErrorUnsupportedEncodingFormat = "Unsupported encoding format, maybe check your audio-formats configuration", 18 | kErrorDecoderInitializationFailed = "Decoder initialization failed", 19 | kErrorFileOpenFailed = "Couldn't open file", 20 | kErrorFlushFailed = "Flushing stream failed", 21 | kErrorDecodingError = "Decoder encountered an error", 22 | kErrorEncodingError = "Encoder encountered an error", 23 | kErrorUnknownFormat = "Cannot decode stream: unrecognized encoding."; 24 | 25 | // 26 | // Constructing and destroying without the GC. 27 | // 28 | 29 | /// Allocates and construct a struct or class object. 30 | /// Returns: Newly allocated object. 31 | auto mallocNew(T, Args...)(Args args) 32 | { 33 | static if (is(T == class)) 34 | immutable size_t allocSize = __traits(classInstanceSize, T); 35 | else 36 | immutable size_t allocSize = T.sizeof; 37 | 38 | void* rawMemory = malloc(allocSize); 39 | if (!rawMemory) 40 | onOutOfMemoryErrorNoGC(); 41 | 42 | static if (is(T == class)) 43 | { 44 | T obj = emplace!T(rawMemory[0 .. allocSize], args); 45 | } 46 | else 47 | { 48 | T* obj = cast(T*)rawMemory; 49 | emplace!T(obj, args); 50 | } 51 | 52 | return obj; 53 | } 54 | 55 | /// Destroys and frees a class object created with $(D mallocEmplace). 56 | void destroyFree(T)(T p) if (is(T == class)) 57 | { 58 | if (p !is null) 59 | { 60 | destroyNoGC(p); 61 | free(cast(void*)p); 62 | } 63 | } 64 | 65 | /// Destroys and frees an interface object created with $(D mallocEmplace). 66 | void destroyFree(T)(T p) if (is(T == interface)) 67 | { 68 | if (p !is null) 69 | { 70 | void* here = cast(void*)(cast(Object)p); 71 | destroyNoGC(p); 72 | free(cast(void*)here); 73 | } 74 | } 75 | 76 | /// Destroys and frees a non-class object created with $(D mallocEmplace). 77 | void destroyFree(T)(T* p) if (!is(T == class)) 78 | { 79 | if (p !is null) 80 | { 81 | destroyNoGC(p); 82 | free(cast(void*)p); 83 | } 84 | } 85 | 86 | 87 | unittest 88 | { 89 | class A 90 | { 91 | int _i; 92 | this(int i) 93 | { 94 | _i = i; 95 | } 96 | } 97 | 98 | struct B 99 | { 100 | int i; 101 | } 102 | 103 | void testMallocEmplace() 104 | { 105 | A a = mallocNew!A(4); 106 | destroyFree(a); 107 | 108 | B* b = mallocNew!B(5); 109 | destroyFree(b); 110 | } 111 | 112 | testMallocEmplace(); 113 | } 114 | 115 | 116 | // 117 | // Optimistic .destroy, which is @nogc nothrow by breaking the type-system 118 | // 119 | 120 | // for classes 121 | void destroyNoGC(T)(T x) nothrow @nogc if (is(T == class) || is(T == interface)) 122 | { 123 | assumeNothrowNoGC( 124 | (T x) 125 | { 126 | return destroy(x); 127 | })(x); 128 | } 129 | 130 | // for struct 131 | void destroyNoGC(T)(ref T obj) nothrow @nogc if (is(T == struct)) 132 | { 133 | assumeNothrowNoGC( 134 | (ref T x) 135 | { 136 | return destroy(x); 137 | })(obj); 138 | } 139 | 140 | void destroyNoGC(T)(ref T obj) nothrow @nogc 141 | if (!is(T == struct) && !is(T == class) && !is(T == interface)) 142 | { 143 | assumeNothrowNoGC( 144 | (ref T x) 145 | { 146 | return destroy(x); 147 | })(obj); 148 | } 149 | 150 | 151 | auto assumeNothrowNoGC(T) (T t) 152 | { 153 | static if (isFunctionPointer!T || isDelegate!T) 154 | { 155 | enum attrs = functionAttributes!T | FunctionAttribute.nogc | FunctionAttribute.nothrow_; 156 | return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 157 | } 158 | else 159 | static assert(false); 160 | } 161 | 162 | 163 | void reallocBuffer(T)(ref T[] buffer, size_t length) nothrow @nogc 164 | { 165 | static if (is(T == struct) && hasElaborateDestructor!T) 166 | { 167 | static assert(false); // struct with destructors not supported 168 | } 169 | 170 | /// Size 0 is special-case to free the slice. 171 | if (length == 0) 172 | { 173 | free(buffer.ptr); 174 | buffer = null; 175 | return; 176 | } 177 | 178 | T* pointer = cast(T*) realloc(buffer.ptr, T.sizeof * length); 179 | if (pointer is null) 180 | buffer = null; // alignment 1 can still return null 181 | else 182 | buffer = pointer[0..length]; 183 | } 184 | 185 | 186 | alias CString = CStringImpl!char; 187 | alias CString16 = CStringImpl!wchar; 188 | 189 | /// Zero-terminated C string, to replace toStringz and toUTF16z 190 | struct CStringImpl(CharType) if (is(CharType: char) || is(CharType: wchar)) 191 | { 192 | public: 193 | nothrow: 194 | @nogc: 195 | 196 | const(CharType)* storage = null; 197 | alias storage this; 198 | 199 | 200 | this(const(CharType)[] s) 201 | { 202 | // Always copy. We can't assume anything about the input. 203 | size_t len = s.length; 204 | CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 205 | buffer[0..len] = s[0..len]; 206 | buffer[len] = '\0'; 207 | storage = buffer; 208 | wasAllocated = true; 209 | } 210 | 211 | // The constructor taking immutable can safely assume that such memory 212 | // has been allocated by the GC or malloc, or an allocator that align 213 | // pointer on at least 4 bytes. 214 | this(immutable(CharType)[] s) 215 | { 216 | // Same optimizations that for toStringz 217 | if (s.length == 0) 218 | { 219 | enum emptyString = cast(CharType[])""; 220 | storage = emptyString.ptr; 221 | return; 222 | } 223 | 224 | /* Peek past end of s[], if it's 0, no conversion necessary. 225 | * Note that the compiler will put a 0 past the end of static 226 | * strings, and the storage allocator will put a 0 past the end 227 | * of newly allocated char[]'s. 228 | */ 229 | const(CharType)* p = s.ptr + s.length; 230 | // Is p dereferenceable? A simple test: if the p points to an 231 | // address multiple of 4, then conservatively assume the pointer 232 | // might be pointing to another block of memory, which might be 233 | // unreadable. Otherwise, it's definitely pointing to valid 234 | // memory. 235 | if ((cast(size_t) p & 3) && *p == 0) 236 | { 237 | storage = s.ptr; 238 | return; 239 | } 240 | 241 | size_t len = s.length; 242 | CharType* buffer = cast(CharType*) malloc((len + 1) * CharType.sizeof); 243 | buffer[0..len] = s[0..len]; 244 | buffer[len] = '\0'; 245 | storage = buffer; 246 | wasAllocated = true; 247 | } 248 | 249 | ~this() 250 | { 251 | if (wasAllocated) 252 | free(cast(void*)storage); 253 | } 254 | 255 | @disable this(this); 256 | 257 | private: 258 | bool wasAllocated = false; 259 | } 260 | 261 | 262 | /// Duplicates a slice with `malloc`. Equivalent to `.dup` 263 | /// Has to be cleaned-up with `free(slice.ptr)` or `freeSlice(slice)`. 264 | T[] mallocDup(T)(const(T)[] slice) nothrow @nogc if (!is(T == struct)) 265 | { 266 | T[] copy = mallocSliceNoInit!T(slice.length); 267 | memcpy(copy.ptr, slice.ptr, slice.length * T.sizeof); 268 | return copy; 269 | } 270 | 271 | /// Allocates a slice with `malloc`, but does not initialize the content. 272 | T[] mallocSliceNoInit(T)(size_t count) nothrow @nogc 273 | { 274 | T* p = cast(T*) malloc(count * T.sizeof); 275 | return p[0..count]; 276 | } 277 | 278 | 279 | /// Kind of a std::vector replacement. 280 | /// Grow-only array, points to a (optionally aligned) memory location. 281 | /// This can also work as an output range. 282 | /// `Vec` is designed to work even when uninitialized, without `makeVec`. 283 | struct Vec(T) 284 | { 285 | nothrow: 286 | @nogc: 287 | public 288 | { 289 | /// Creates an aligned buffer with given initial size. 290 | this(size_t initialSize) 291 | { 292 | _size = 0; 293 | _allocated = 0; 294 | _data = null; 295 | resize(initialSize); 296 | } 297 | 298 | ~this() 299 | { 300 | if (_data !is null) 301 | { 302 | free(_data); 303 | _data = null; 304 | _allocated = 0; 305 | } 306 | } 307 | 308 | @disable this(this); 309 | 310 | /// Returns: Length of buffer in elements. 311 | size_t length() pure const 312 | { 313 | return _size; 314 | } 315 | 316 | /// Returns: Length of buffer in elements. 317 | alias opDollar = length; 318 | 319 | /// Resizes a buffer to hold $(D askedSize) elements. 320 | void resize(size_t askedSize) 321 | { 322 | // grow only 323 | if (_allocated < askedSize) 324 | { 325 | size_t numBytes = askedSize * 2 * T.sizeof; // gives 2x what is asked to make room for growth 326 | _data = cast(T*)(realloc(_data, numBytes)); 327 | _allocated = askedSize * 2; 328 | } 329 | _size = askedSize; 330 | } 331 | 332 | /// Pop last element 333 | T popBack() 334 | { 335 | assert(_size > 0); 336 | _size = _size - 1; 337 | return _data[_size]; 338 | } 339 | 340 | /// Append an element to this buffer. 341 | void pushBack(T x) 342 | { 343 | size_t i = _size; 344 | resize(_size + 1); 345 | _data[i] = x; 346 | } 347 | 348 | // DMD 2.088 deprecates the old D1-operators 349 | static if (__VERSION__ >= 2088) 350 | { 351 | ///ditto 352 | void opOpAssign(string op)(T x) if (op == "~") 353 | { 354 | pushBack(x); 355 | } 356 | } 357 | else 358 | { 359 | ///ditto 360 | void opCatAssign(T x) 361 | { 362 | pushBack(x); 363 | } 364 | } 365 | 366 | // Output range support 367 | alias put = pushBack; 368 | 369 | /// Finds an item, returns -1 if not found 370 | int indexOf(T x) 371 | { 372 | foreach(int i; 0..cast(int)_size) 373 | if (_data[i] is x) 374 | return i; 375 | return -1; 376 | } 377 | 378 | /// Removes an item and replaces it by the last item. 379 | /// Warning: this reorders the array. 380 | void removeAndReplaceByLastElement(size_t index) 381 | { 382 | assert(index < _size); 383 | _data[index] = _data[--_size]; 384 | } 385 | 386 | /// Removes an item and shift the rest of the array to front by 1. 387 | /// Warning: O(N) complexity. 388 | void removeAndShiftRestOfArray(size_t index) 389 | { 390 | assert(index < _size); 391 | for (; index + 1 < _size; ++index) 392 | _data[index] = _data[index+1]; 393 | } 394 | 395 | /// Appends another buffer to this buffer. 396 | void pushBack(ref Vec other) 397 | { 398 | size_t oldSize = _size; 399 | resize(_size + other._size); 400 | memcpy(_data + oldSize, other._data, T.sizeof * other._size); 401 | } 402 | 403 | /// Appends a slice to this buffer. 404 | /// `slice` should not belong to the same buffer _data. 405 | void pushBack(T[] slice) 406 | { 407 | size_t oldSize = _size; 408 | size_t newSize = _size + slice.length; 409 | resize(newSize); 410 | for (size_t n = 0; n < slice.length; ++n) 411 | _data[oldSize + n] = slice[n]; 412 | } 413 | 414 | /// Returns: Raw pointer to data. 415 | @property inout(T)* ptr() inout 416 | { 417 | return _data; 418 | } 419 | 420 | /// Returns: n-th element. 421 | ref inout(T) opIndex(size_t i) pure inout 422 | { 423 | return _data[i]; 424 | } 425 | 426 | T opIndexAssign(T x, size_t i) 427 | { 428 | return _data[i] = x; 429 | } 430 | 431 | /// Sets size to zero, but keeps allocated buffers. 432 | void clearContents() 433 | { 434 | _size = 0; 435 | } 436 | 437 | /// Returns: Whole content of the array in one slice. 438 | inout(T)[] opSlice() inout 439 | { 440 | return opSlice(0, length()); 441 | } 442 | 443 | /// Returns: A slice of the array. 444 | inout(T)[] opSlice(size_t i1, size_t i2) inout 445 | { 446 | return _data[i1 .. i2]; 447 | } 448 | 449 | /// Fills the buffer with the same value. 450 | void fill(T x) 451 | { 452 | _data[0.._size] = x; 453 | } 454 | 455 | /// Move. Give up owner ship of the data. 456 | T[] releaseData() 457 | { 458 | T[] data = _data[0.._size]; 459 | this._data = null; 460 | this._size = 0; 461 | this._allocated = 0; 462 | return data; 463 | } 464 | } 465 | 466 | private 467 | { 468 | size_t _size = 0; 469 | T* _data = null; 470 | size_t _allocated = 0; 471 | } 472 | } -------------------------------------------------------------------------------- /source/audioformats/io.d: -------------------------------------------------------------------------------- 1 | module audioformats.io; 2 | 3 | import audioformats.internals; 4 | 5 | nothrow @nogc 6 | { 7 | alias ioSeekCallback = bool function(long offset, bool relative, void* userData); // return true on success 8 | alias ioTellCallback = long function( void* userData); 9 | alias ioGetFileLengthCallback = long function( void* userData); 10 | alias ioReadCallback = int function(void* outData, int bytes, void* userData); // returns number of read bytes 11 | alias ioWriteCallback = int function(void* inData, int bytes, void* userData); // returns number of written bytes 12 | alias ioSkipCallback = bool function(int bytes, void* userData); 13 | alias ioFlushCallback = bool function( void* userData); 14 | } 15 | 16 | struct IOCallbacks 17 | { 18 | nothrow @nogc: 19 | 20 | ioSeekCallback seek; 21 | ioTellCallback tell; 22 | ioGetFileLengthCallback getFileLength; 23 | ioReadCallback read; 24 | ioWriteCallback write; 25 | ioSkipCallback skip; 26 | ioFlushCallback flush; 27 | 28 | 29 | // Now, some helpers for binary parsing based on these callbacks 30 | 31 | // 32 | 33 | bool nothingToReadAnymore(void* userData) 34 | { 35 | return remainingBytesToRead(userData) <= 0; 36 | } 37 | 38 | long remainingBytesToRead(void* userData) 39 | { 40 | long cursor = tell(userData); 41 | long fileLength = getFileLength(userData); 42 | assert(cursor <= fileLength); 43 | return fileLength - cursor; 44 | } 45 | 46 | /// Read one ubyte from stream and do not advance the stream cursor. 47 | /// On error, return an error, the stream then is considered invalid. 48 | ubyte peek_ubyte(void* userData, bool* err) 49 | { 50 | ubyte b = read_ubyte(userData, err); 51 | if (*err) 52 | return 0; 53 | 54 | *err = !seek(tell(userData) - 1, false, userData); 55 | return b; 56 | } 57 | 58 | /// Read one ubyte from stream and advance the stream cursor. 59 | /// On error, return 0 and an error, stream is then considered invalid. 60 | ubyte read_ubyte(void* userData, bool* err) 61 | { 62 | ubyte b; 63 | if (1 == read(&b, 1, userData)) 64 | { 65 | *err = false; 66 | return b; 67 | } 68 | *err = true; 69 | return 0; 70 | } 71 | 72 | /// Reads a 16-byte UUID from the stream and advance cursor. 73 | /// On error, the stream is considered invalid, and the return value is undefined behaviour. 74 | ubyte[16] read_guid(void* userData, bool* err) 75 | { 76 | ubyte[16] b; 77 | if (16 == read(&b, 16, userData)) 78 | { 79 | *err = false; 80 | return b; 81 | } 82 | *err = true; 83 | return b; 84 | } 85 | 86 | /// Read one Little Endian ushort from stream and advance the stream cursor. 87 | /// On error, return 0 and an error, stream is then considered invalid. 88 | ushort read_ushort_LE(void* userData, bool* err) 89 | { 90 | ubyte[2] v; 91 | if (2 == read(v.ptr, 2, userData)) 92 | { 93 | version(BigEndian) 94 | { 95 | ubyte v0 = v[0]; 96 | v[0] = v[1]; 97 | v[1] = v0; 98 | } 99 | 100 | *err = false; 101 | return *cast(ushort*)(v.ptr); 102 | } 103 | else 104 | { 105 | *err = true; 106 | return 0; 107 | } 108 | } 109 | 110 | /// Read one Big Endian 32-bit unsigned int from stream and advance the stream cursor. 111 | /// On error, return 0 and an error, stream is then considered invalid. 112 | uint read_uint_BE(void* userData, bool* err) 113 | { 114 | ubyte[4] v; 115 | if (4 == read(v.ptr, 4, userData)) 116 | { 117 | version(LittleEndian) 118 | { 119 | ubyte v0 = v[0]; 120 | v[0] = v[3]; 121 | v[3] = v0; 122 | ubyte v1 = v[1]; 123 | v[1] = v[2]; 124 | v[2] = v1; 125 | } 126 | *err = false; 127 | return *cast(uint*)(v.ptr); 128 | } 129 | else 130 | { 131 | *err = true; 132 | return 0; 133 | } 134 | } 135 | 136 | /// Read one Little Endian 32-bit unsigned int from stream and advance the stream cursor. 137 | /// On error, return 0 and an error, stream is then considered invalid. 138 | uint read_uint_LE(void* userData, bool* err) 139 | { 140 | ubyte[4] v; 141 | if (4 == read(v.ptr, 4, userData)) 142 | { 143 | version(BigEndian) 144 | { 145 | ubyte v0 = v[0]; 146 | v[0] = v[3]; 147 | v[3] = v0; 148 | ubyte v1 = v[1]; 149 | v[1] = v[2]; 150 | v[2] = v1; 151 | } 152 | *err = false; 153 | return *cast(uint*)(v.ptr); 154 | } 155 | else 156 | { 157 | *err = true; 158 | return 0; 159 | } 160 | } 161 | 162 | /// Read one Big Endian 64-bit integer from stream and advance the stream cursor. 163 | /// On error, return `0` and an error, stream is then considered invalid. 164 | ulong read_ulong_BE(void* userData, bool* err) 165 | { 166 | ubyte[8] v; 167 | if (8 == read(v.ptr, 8, userData)) 168 | { 169 | version(LittleEndian) 170 | { 171 | ubyte v0 = v[0]; 172 | v[0] = v[7]; 173 | v[7] = v0; 174 | ubyte v1 = v[1]; 175 | v[1] = v[6]; 176 | v[6] = v1; 177 | ubyte v2 = v[2]; 178 | v[2] = v[5]; 179 | v[5] = v2; 180 | ubyte v3 = v[3]; 181 | v[3] = v[4]; 182 | v[4] = v3; 183 | } 184 | *err = false; 185 | return *cast(ulong*)(v.ptr); 186 | } 187 | else 188 | { 189 | *err = true; 190 | return 0; 191 | } 192 | } 193 | 194 | /// Read one Little Endian 24-bit unsigned int from stream and advance the stream cursor. 195 | /// On error, return 0 and an error, stream is then considered invalid. 196 | uint read_24bits_LE(void* userData, bool *err) 197 | { 198 | ubyte[3] v; 199 | if (3 == read(v.ptr, 3, userData)) 200 | { 201 | *err = false; 202 | return v[0] | (v[1] << 8) | (v[2] << 16); 203 | } 204 | else 205 | { 206 | *err = true; 207 | return 0; 208 | } 209 | } 210 | 211 | /// Read one Little Endian 32-bit float from stream and advance the stream cursor. 212 | /// On error, return `float.nan` and an error, stream is then considered invalid. 213 | float read_float_LE(void* userData, bool* err) 214 | { 215 | uint u = read_uint_LE(userData, err); 216 | if (*err) 217 | return float.nan; 218 | else 219 | return *cast(float*)(&u); 220 | } 221 | 222 | /// Read one Little Endian 64-bit float from stream and advance the stream cursor. 223 | /// On error, return `double.nan` and an error, stream is then considered invalid. 224 | double read_double_LE(void* userData, bool* err) 225 | { 226 | ubyte[8] v; 227 | if (8 == read(v.ptr, 8, userData)) 228 | { 229 | version(BigEndian) 230 | { 231 | ubyte v0 = v[0]; 232 | v[0] = v[7]; 233 | v[7] = v0; 234 | ubyte v1 = v[1]; 235 | v[1] = v[6]; 236 | v[6] = v1; 237 | ubyte v2 = v[2]; 238 | v[2] = v[5]; 239 | v[5] = v2; 240 | ubyte v3 = v[3]; 241 | v[3] = v[4]; 242 | v[4] = v3; 243 | } 244 | *err = false; 245 | return *cast(double*)(v.ptr); 246 | } 247 | else 248 | { 249 | *err = true; 250 | return double.nan; 251 | } 252 | } 253 | 254 | /// Read one Lthe two fields of a RIFF header: chunk ID and chunk size. 255 | /// On error, return values are undefined behaviour. 256 | void readRIFFChunkHeader(void* userData, 257 | ref uint chunkId, 258 | ref uint chunkSize, 259 | bool* err) 260 | { 261 | // chunk ID is read as Big Endian uint 262 | chunkId = read_uint_BE(userData, err); 263 | if (*err) 264 | return; 265 | 266 | // chunk size is read as Little Endian uint 267 | chunkSize = read_uint_LE(userData, err); 268 | } 269 | 270 | // 271 | 272 | // 273 | 274 | string writeFailureMessage = "write failure"; 275 | 276 | /// Write a Big Endian 32-bit unsigned int to stream and advance cursor. 277 | /// Returns: `true` on success, else stream is invalid. 278 | bool write_uint_BE(void* userData, uint value) 279 | { 280 | ubyte[4] v; 281 | *cast(uint*)(v.ptr) = value; 282 | version(LittleEndian) 283 | { 284 | ubyte v0 = v[0]; 285 | v[0] = v[3]; 286 | v[3] = v0; 287 | ubyte v1 = v[1]; 288 | v[1] = v[2]; 289 | v[2] = v1; 290 | } 291 | return (4 == write(v.ptr, 4, userData)); 292 | } 293 | 294 | /// Write a 8-bit signed int to stream and advance cursor. 295 | /// Returns: `true` on success, else stream is invalid. 296 | bool write_byte(void* userData, byte value) 297 | { 298 | return 1 == write(&value, 1, userData); 299 | } 300 | 301 | /// Write a 8-bit signed int to stream and advance cursor. 302 | /// Returns: `true` on success, else stream is invalid. 303 | bool write_short_LE(void* userData, short value) 304 | { 305 | ubyte[2] v; 306 | *cast(ushort*)(v.ptr) = value; 307 | version(BigEndian) 308 | { 309 | ubyte v0 = v[0]; 310 | v[0] = v[1]; 311 | v[1] = v0; 312 | } 313 | return 2 == write(v.ptr, 2, userData); 314 | } 315 | 316 | /// Write a Little Endian 24-bit signed int to stream and advance cursor. 317 | /// Returns: `true` on success, else stream is invalid. 318 | bool write_24bits_LE(void* userData, int value) 319 | { 320 | ubyte[4] v; 321 | *cast(int*)(v.ptr) = value; 322 | version(BigEndian) 323 | { 324 | ubyte v0 = v[0]; 325 | v[0] = v[3]; 326 | v[3] = v0; 327 | ubyte v1 = v[1]; 328 | v[1] = v[2]; 329 | v[2] = v1; 330 | } 331 | return 3 == write(v.ptr, 3, userData); 332 | } 333 | 334 | /// Write a Little Endian 32-bit float to stream and advance cursor. 335 | /// Returns: `true` on success, else stream is invalid. 336 | bool write_float_LE(void* userData, float value) 337 | { 338 | return write_uint_LE(userData, *cast(uint*)(&value)); 339 | } 340 | 341 | /// Write a Little Endian 64-bit double to stream and advance cursor. 342 | /// Returns: `true` on success, else stream is invalid. 343 | bool write_double_LE(void* userData, float value) 344 | { 345 | return write_ulong_LE(userData, *cast(ulong*)(&value)); 346 | } 347 | 348 | /// Write a Little Endian 64-bit unsigned integer to stream and advance cursor. 349 | /// Returns: `true` on success, else stream is invalid. 350 | bool write_ulong_LE(void* userData, ulong value) 351 | { 352 | ubyte[8] v; 353 | *cast(ulong*)(v.ptr) = value; 354 | version(BigEndian) 355 | { 356 | ubyte v0 = v[0]; 357 | v[0] = v[7]; 358 | v[7] = v0; 359 | ubyte v1 = v[1]; 360 | v[1] = v[6]; 361 | v[6] = v1; 362 | ubyte v2 = v[2]; 363 | v[2] = v[5]; 364 | v[5] = v2; 365 | ubyte v3 = v[3]; 366 | v[3] = v[4]; 367 | v[4] = v3; 368 | } 369 | 370 | return 8 == write(v.ptr, 8, userData); 371 | } 372 | 373 | /// Write a Little Endian 64-bit unsigned integer to stream and advance cursor. 374 | /// Returns: `true` on success, else stream is invalid. 375 | bool write_ulong_BE(void* userData, ulong value) 376 | { 377 | ubyte[8] v; 378 | *cast(ulong*)(v.ptr) = value; 379 | version(LittleEndian) 380 | { 381 | ubyte v0 = v[0]; 382 | v[0] = v[7]; 383 | v[7] = v0; 384 | ubyte v1 = v[1]; 385 | v[1] = v[6]; 386 | v[6] = v1; 387 | ubyte v2 = v[2]; 388 | v[2] = v[5]; 389 | v[5] = v2; 390 | ubyte v3 = v[3]; 391 | v[3] = v[4]; 392 | v[4] = v3; 393 | } 394 | 395 | return 8 == write(v.ptr, 8, userData); 396 | } 397 | 398 | /// Write a Little Endian 32-bit unsigned integer to stream and advance cursor. 399 | /// Returns: `true` on success, else stream is invalid. 400 | bool write_uint_LE(void* userData, uint value) 401 | { 402 | ubyte[4] v; 403 | *cast(uint*)(v.ptr) = value; 404 | version(BigEndian) 405 | { 406 | ubyte v0 = v[0]; 407 | v[0] = v[3]; 408 | v[3] = v0; 409 | ubyte v1 = v[1]; 410 | v[1] = v[2]; 411 | v[2] = v1; 412 | } 413 | 414 | return 4 == write(v.ptr, 4, userData); 415 | } 416 | 417 | /// Write a Little Endian 16-bit unsigned integer to stream and advance cursor. 418 | /// Returns: `true` on success, else stream is invalid. 419 | bool write_ushort_LE(void* userData, ushort value) 420 | { 421 | ubyte[2] v; 422 | *cast(ushort*)(v.ptr) = value; 423 | version(BigEndian) 424 | { 425 | ubyte v0 = v[0]; 426 | v[0] = v[1]; 427 | v[1] = v0; 428 | } 429 | return 2 == write(v.ptr, 2, userData); 430 | } 431 | 432 | /// Write a RIFF header to stream and advance cursor. 433 | /// Returns: `true` on success, else stream is invalid. 434 | bool writeRIFFChunkHeader(void* userData, uint chunkId, uint chunkSize) 435 | { 436 | if (!write_uint_BE(userData, chunkId)) 437 | return false; 438 | return write_uint_LE(userData, chunkSize); 439 | } 440 | 441 | // 442 | } 443 | 444 | 445 | template RIFFChunkId(string id) 446 | { 447 | static assert(id.length == 4); 448 | __gshared uint RIFFChunkId = (cast(ubyte)(id[0]) << 24) 449 | | (cast(ubyte)(id[1]) << 16) 450 | | (cast(ubyte)(id[2]) << 8) 451 | | (cast(ubyte)(id[3])); 452 | } 453 | -------------------------------------------------------------------------------- /source/audioformats/minimp3_ex.d: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/lieff/minimp3 3 | To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. 4 | This software is distributed without any warranty. 5 | See . 6 | */ 7 | /** 8 | Translated to D by Guillaume Piolat. 9 | Stripped down a bit for the needs of audio-formats. 10 | */ 11 | module audioformats.minimp3_ex; 12 | 13 | import core.stdc.stdlib; 14 | import core.stdc.string; 15 | 16 | import audioformats.minimp3; 17 | 18 | nothrow: 19 | @nogc: 20 | 21 | enum MP3D_SEEK_TO_BYTE = 0; 22 | enum MP3D_SEEK_TO_SAMPLE = 1; 23 | 24 | enum MINIMP3_PREDECODE_FRAMES = 2; /* frames to pre-decode and skip after seek (to fill internal structures) */ 25 | /*#define MINIMP3_SEEK_IDX_LINEAR_SEARCH*/ /* define to use linear index search instead of binary search on seek */ 26 | enum MINIMP3_IO_SIZE = (128*1024); /* io buffer size for streaming functions, must be greater than MINIMP3_BUF_SIZE */ 27 | enum MINIMP3_BUF_SIZE = (16*1024); /* buffer which can hold minimum 10 consecutive mp3 frames (~16KB) worst case */ 28 | enum MINIMP3_ENABLE_RING = 0; /* enable hardware magic ring buffer if available, to make less input buffer memmove(s) in callback IO mode */ 29 | 30 | enum MP3D_E_PARAM = -1; 31 | enum MP3D_E_MEMORY = -2; 32 | enum MP3D_E_IOERROR = -3; 33 | enum MP3D_E_USER = -4; /* can be used to stop processing from callbacks without indicating specific error */ 34 | enum MP3D_E_DECODE = -5; /* decode error which can't be safely skipped, such as sample rate, layer and channels change */ 35 | 36 | struct mp3dec_file_info_t 37 | { 38 | mp3d_sample_t *buffer; 39 | size_t samples; /* channels included, byte size = samples*sizeof(mp3d_sample_t) */ 40 | int channels, hz, layer, avg_bitrate_kbps; 41 | } 42 | 43 | struct mp3dec_map_info_t 44 | { 45 | const(uint8_t) *buffer; 46 | size_t size; 47 | } 48 | 49 | struct mp3dec_frame_t 50 | { 51 | uint64_t sample; 52 | uint64_t offset; 53 | } 54 | 55 | struct mp3dec_index_t 56 | { 57 | mp3dec_frame_t *frames; 58 | size_t num_frames, capacity; 59 | } 60 | 61 | alias MP3D_READ_CB = size_t function(void *buf, size_t size, void *user_data); 62 | alias MP3D_SEEK_CB = int function(uint64_t position, void *user_data); 63 | 64 | 65 | struct mp3dec_io_t 66 | { 67 | MP3D_READ_CB read; 68 | void *read_data; 69 | MP3D_SEEK_CB seek; 70 | void *seek_data; 71 | } 72 | 73 | struct mp3dec_ex_t 74 | { 75 | mp3dec_t mp3d; 76 | mp3dec_map_info_t file; 77 | mp3dec_io_t *io; 78 | mp3dec_index_t index; 79 | uint64_t offset, samples, detected_samples, cur_sample, start_offset, end_offset; 80 | mp3dec_frame_info_t info; 81 | mp3d_sample_t[MINIMP3_MAX_SAMPLES_PER_FRAME] buffer; 82 | size_t input_consumed, input_filled; 83 | int is_file, seek_method, vbr_tag_found; 84 | int free_format_bytes; 85 | int buffer_samples, buffer_consumed, to_skip, start_delay; 86 | int last_error; 87 | } 88 | 89 | alias MP3D_ITERATE_CB = int function(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, uint64_t offset, mp3dec_frame_info_t *info); 90 | alias MP3D_PROGRESS_CB = int function(void *user_data, size_t file_size, uint64_t offset, mp3dec_frame_info_t *info); 91 | 92 | 93 | void mp3dec_skip_id3v1(const uint8_t *buf, size_t *pbuf_size) 94 | { 95 | size_t buf_size = *pbuf_size; 96 | if (buf_size >= 128 && !memcmp(buf + buf_size - 128, "TAG".ptr, 3)) 97 | { 98 | buf_size -= 128; 99 | if (buf_size >= 227 && !memcmp(buf + buf_size - 227, "TAG+".ptr, 4)) 100 | buf_size -= 227; 101 | } 102 | if (buf_size > 32 && !memcmp(buf + buf_size - 32, "APETAGEX".ptr, 8)) 103 | { 104 | buf_size -= 32; 105 | const uint8_t *tag = buf + buf_size + 8 + 4; 106 | uint32_t tag_size = cast(uint32_t)(tag[3] << 24) | (tag[2] << 16) | (tag[1] << 8) | tag[0]; 107 | if (buf_size >= tag_size) 108 | buf_size -= tag_size; 109 | } 110 | *pbuf_size = buf_size; 111 | } 112 | 113 | enum MINIMP3_ID3_DETECT_SIZE = 10; 114 | 115 | size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size) 116 | { 117 | if (buf_size >= MINIMP3_ID3_DETECT_SIZE && !memcmp(buf, "ID3".ptr, 3) && !((buf[5] & 15) || (buf[6] & 0x80) || (buf[7] & 0x80) || (buf[8] & 0x80) || (buf[9] & 0x80))) 118 | { 119 | size_t id3v2size = (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10; 120 | if ((buf[5] & 16)) 121 | id3v2size += 10; /* footer */ 122 | return id3v2size; 123 | }enum MINIMP3_ID3_DETECT_SIZE = 10; 124 | return 0; 125 | } 126 | 127 | void mp3dec_skip_id3(const(uint8_t)**pbuf, size_t *pbuf_size) 128 | { 129 | uint8_t *buf = cast(uint8_t *)(*pbuf); 130 | size_t buf_size = *pbuf_size; 131 | size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size); 132 | if (id3v2size) 133 | { 134 | if (id3v2size >= buf_size) 135 | id3v2size = buf_size; 136 | buf += id3v2size; 137 | buf_size -= id3v2size; 138 | } 139 | mp3dec_skip_id3v1(buf, &buf_size); 140 | *pbuf = cast(const uint8_t *)buf; 141 | *pbuf_size = buf_size; 142 | } 143 | 144 | static int mp3dec_check_vbrtag(const uint8_t *frame, int frame_size, uint32_t *frames, int *delay, int *padding) 145 | { 146 | static immutable char[4] g_xing_tag = "Xing"; 147 | static immutable char[4] g_info_tag = "Info"; 148 | 149 | enum FRAMES_FLAG = 1; 150 | enum BYTES_FLAG = 2; 151 | enum TOC_FLAG = 4; 152 | enum VBR_SCALE_FLAG = 8; 153 | /* Side info offsets after header: 154 | / Mono Stereo 155 | / MPEG1 17 32 156 | / MPEG2 & 2.5 9 17*/ 157 | bs_t[1] bs; 158 | L3_gr_info_t[4] gr_info; 159 | bs_init(bs.ptr, frame + HDR_SIZE, frame_size - HDR_SIZE); 160 | if (HDR_IS_CRC(frame)) 161 | get_bits(bs.ptr, 16); 162 | if (L3_read_side_info(bs.ptr, gr_info.ptr, frame) < 0) 163 | return 0; /* side info corrupted */ 164 | 165 | const(uint8_t)*tag = frame + HDR_SIZE + bs[0].pos/8; 166 | if (memcmp(g_xing_tag.ptr, tag, 4) && memcmp(g_info_tag.ptr, tag, 4)) 167 | return 0; 168 | int flags = tag[7]; 169 | if (!((flags & FRAMES_FLAG))) 170 | return -1; 171 | tag += 8; 172 | *frames = cast(uint32_t)(tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3]; 173 | tag += 4; 174 | if (flags & BYTES_FLAG) 175 | tag += 4; 176 | if (flags & TOC_FLAG) 177 | tag += 100; 178 | if (flags & VBR_SCALE_FLAG) 179 | tag += 4; 180 | *delay = *padding = 0; 181 | if (*tag) 182 | { /* extension, LAME, Lavc, etc. Should be the same structure. */ 183 | tag += 21; 184 | if (tag - frame + 14 >= frame_size) 185 | return 0; 186 | *delay = ((tag[0] << 4) | (tag[1] >> 4)) + (528 + 1); 187 | *padding = (((tag[1] & 0xF) << 8) | tag[2]) - (528 + 1); 188 | } 189 | return 1; 190 | } 191 | 192 | int mp3dec_detect_buf(const uint8_t *buf, size_t buf_size) 193 | { 194 | return mp3dec_detect_cb(null, cast(uint8_t *)buf, buf_size); 195 | } 196 | 197 | int mp3dec_detect_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size) 198 | { 199 | if (!buf || cast(size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE)) 200 | return MP3D_E_PARAM; 201 | size_t filled = buf_size; 202 | if (io) 203 | { 204 | if (io.seek(0, io.seek_data)) 205 | return MP3D_E_IOERROR; 206 | filled = io.read(buf, MINIMP3_ID3_DETECT_SIZE, io.read_data); 207 | if (filled > MINIMP3_ID3_DETECT_SIZE) 208 | return MP3D_E_IOERROR; 209 | } 210 | if (filled < MINIMP3_ID3_DETECT_SIZE) 211 | return MP3D_E_USER; /* too small, can't be mp3/mpa */ 212 | if (mp3dec_skip_id3v2(buf, filled)) 213 | return 0; /* id3v2 tag is enough evidence */ 214 | if (io) 215 | { 216 | size_t readed = io.read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io.read_data); 217 | if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) 218 | return MP3D_E_IOERROR; 219 | filled += readed; 220 | if (filled < MINIMP3_BUF_SIZE) 221 | mp3dec_skip_id3v1(buf, &filled); 222 | } else 223 | { 224 | mp3dec_skip_id3v1(buf, &filled); 225 | if (filled > MINIMP3_BUF_SIZE) 226 | filled = MINIMP3_BUF_SIZE; 227 | } 228 | int free_format_bytes, frame_size; 229 | mp3d_find_frame(buf, cast(int)filled, &free_format_bytes, &frame_size); 230 | if (frame_size) 231 | return 0; /* MAX_FRAME_SYNC_MATCHES consecutive frames found */ 232 | return MP3D_E_USER; 233 | } 234 | 235 | int mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) 236 | { 237 | return mp3dec_load_cb(dec, null, cast(uint8_t *)buf, buf_size, info, progress_cb, user_data); 238 | } 239 | 240 | int mp3dec_load_cb(mp3dec_t *dec, mp3dec_io_t *io, uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) 241 | { 242 | if (!dec || !buf || !info || cast(size_t)-1 == buf_size || (io && buf_size < MINIMP3_BUF_SIZE)) 243 | return MP3D_E_PARAM; 244 | uint64_t detected_samples = 0; 245 | size_t orig_buf_size = buf_size; 246 | int to_skip = 0; 247 | mp3dec_frame_info_t frame_info; 248 | memset(info, 0, (*info).sizeof); 249 | memset(&frame_info, 0, (frame_info).sizeof); 250 | 251 | /* skip id3 */ 252 | size_t filled = 0, consumed = 0; 253 | int eof = 0, ret2 = 0; 254 | if (io) 255 | { 256 | if (io.seek(0, io.seek_data)) 257 | return MP3D_E_IOERROR; 258 | filled = io.read(buf, MINIMP3_ID3_DETECT_SIZE, io.read_data); 259 | if (filled > MINIMP3_ID3_DETECT_SIZE) 260 | return MP3D_E_IOERROR; 261 | if (MINIMP3_ID3_DETECT_SIZE != filled) 262 | return 0; 263 | size_t id3v2size = mp3dec_skip_id3v2(buf, filled); 264 | if (id3v2size) 265 | { 266 | if (io.seek(id3v2size, io.seek_data)) 267 | return MP3D_E_IOERROR; 268 | filled = io.read(buf, buf_size, io.read_data); 269 | if (filled > buf_size) 270 | return MP3D_E_IOERROR; 271 | } else 272 | { 273 | size_t readed = io.read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io.read_data); 274 | if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) 275 | return MP3D_E_IOERROR; 276 | filled += readed; 277 | } 278 | if (filled < MINIMP3_BUF_SIZE) 279 | mp3dec_skip_id3v1(buf, &filled); 280 | } else 281 | { 282 | mp3dec_skip_id3(cast(const(uint8_t)**)&buf, &buf_size); 283 | if (!buf_size) 284 | return 0; 285 | } 286 | /* try to make allocation size assumption by first frame or vbr tag */ 287 | mp3dec_init(dec); 288 | int samples; 289 | do 290 | { 291 | uint32_t frames; 292 | int i, delay, padding, free_format_bytes = 0, frame_size = 0; 293 | const(uint8_t) *hdr; 294 | if (io) 295 | { 296 | if (!eof && filled - consumed < MINIMP3_BUF_SIZE) 297 | { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ 298 | memmove(buf, buf + consumed, filled - consumed); 299 | filled -= consumed; 300 | consumed = 0; 301 | size_t readed = io.read(buf + filled, buf_size - filled, io.read_data); 302 | if (readed > (buf_size - filled)) 303 | return MP3D_E_IOERROR; 304 | if (readed != (buf_size - filled)) 305 | eof = 1; 306 | filled += readed; 307 | if (eof) 308 | mp3dec_skip_id3v1(buf, &filled); 309 | } 310 | i = mp3d_find_frame(buf + consumed, cast(int)(filled - consumed), &free_format_bytes, &frame_size); 311 | consumed += i; 312 | hdr = buf + consumed; 313 | } else 314 | { 315 | i = mp3d_find_frame(buf, cast(int)buf_size, &free_format_bytes, &frame_size); 316 | buf += i; 317 | buf_size -= i; 318 | hdr = buf; 319 | } 320 | if (i && !frame_size) 321 | continue; 322 | if (!frame_size) 323 | return 0; 324 | frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; 325 | frame_info.hz = hdr_sample_rate_hz(hdr); 326 | frame_info.layer = 4 - HDR_GET_LAYER(hdr); 327 | frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); 328 | frame_info.frame_bytes = frame_size; 329 | samples = hdr_frame_samples(hdr)*frame_info.channels; 330 | if (3 != frame_info.layer) 331 | break; 332 | int ret = mp3dec_check_vbrtag(hdr, frame_size, &frames, &delay, &padding); 333 | if (ret > 0) 334 | { 335 | padding *= frame_info.channels; 336 | to_skip = delay*frame_info.channels; 337 | detected_samples = samples*cast(uint64_t)frames; 338 | if (detected_samples >= cast(uint64_t)to_skip) 339 | detected_samples -= to_skip; 340 | if (padding > 0 && detected_samples >= cast(uint64_t)padding) 341 | detected_samples -= padding; 342 | if (!detected_samples) 343 | return 0; 344 | } 345 | if (ret) 346 | { 347 | if (io) 348 | { 349 | consumed += frame_size; 350 | } else 351 | { 352 | buf += frame_size; 353 | buf_size -= frame_size; 354 | } 355 | } 356 | break; 357 | } while(1); 358 | size_t allocated = MINIMP3_MAX_SAMPLES_PER_FRAME*(mp3d_sample_t.sizeof); 359 | if (detected_samples) 360 | allocated += detected_samples*(mp3d_sample_t.sizeof); 361 | else 362 | allocated += (buf_size/frame_info.frame_bytes)*samples*(mp3d_sample_t.sizeof); 363 | info.buffer = cast(mp3d_sample_t*) malloc(allocated); 364 | if (!info.buffer) 365 | return MP3D_E_MEMORY; 366 | /* save info */ 367 | info.channels = frame_info.channels; 368 | info.hz = frame_info.hz; 369 | info.layer = frame_info.layer; 370 | /* decode all frames */ 371 | size_t avg_bitrate_kbps = 0, frames = 0; 372 | do 373 | { 374 | if ((allocated - info.samples*(mp3d_sample_t.sizeof)) < MINIMP3_MAX_SAMPLES_PER_FRAME*(mp3d_sample_t.sizeof)) 375 | { 376 | allocated *= 2; 377 | mp3d_sample_t *alloc_buf = cast(mp3d_sample_t*)realloc(info.buffer, allocated); 378 | if (!alloc_buf) 379 | return MP3D_E_MEMORY; 380 | info.buffer = alloc_buf; 381 | } 382 | if (io) 383 | { 384 | if (!eof && filled - consumed < MINIMP3_BUF_SIZE) 385 | { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ 386 | memmove(buf, buf + consumed, filled - consumed); 387 | filled -= consumed; 388 | consumed = 0; 389 | size_t readed = io.read(buf + filled, buf_size - filled, io.read_data); 390 | if (readed != (buf_size - filled)) 391 | eof = 1; 392 | filled += readed; 393 | if (eof) 394 | mp3dec_skip_id3v1(buf, &filled); 395 | } 396 | samples = mp3dec_decode_frame(dec, buf + consumed, cast(int)(filled - consumed), info.buffer + info.samples, &frame_info); 397 | consumed += frame_info.frame_bytes; 398 | } else 399 | { 400 | samples = mp3dec_decode_frame(dec, buf, cast(int)MINIMP3_MIN(buf_size, cast(size_t)int.max), info.buffer + info.samples, &frame_info); 401 | buf += frame_info.frame_bytes; 402 | buf_size -= frame_info.frame_bytes; 403 | } 404 | if (samples) 405 | { 406 | if (info.hz != frame_info.hz || info.layer != frame_info.layer) 407 | { 408 | ret2 = MP3D_E_DECODE; 409 | break; 410 | } 411 | if (info.channels && info.channels != frame_info.channels) 412 | { 413 | ret2 = MP3D_E_DECODE; 414 | break; 415 | } 416 | samples *= frame_info.channels; 417 | if (to_skip) 418 | { 419 | size_t skip = MINIMP3_MIN(samples, to_skip); 420 | to_skip -= skip; 421 | samples -= skip; 422 | memmove(info.buffer, info.buffer + skip, samples); 423 | } 424 | info.samples += samples; 425 | avg_bitrate_kbps += frame_info.bitrate_kbps; 426 | frames++; 427 | if (progress_cb) 428 | { 429 | ret2 = progress_cb(user_data, orig_buf_size, (orig_buf_size - buf_size), &frame_info); 430 | if (ret2) 431 | break; 432 | } 433 | } 434 | } while (frame_info.frame_bytes); 435 | if (detected_samples && info.samples > detected_samples) 436 | info.samples = cast(size_t) detected_samples; /* cut padding */ 437 | /* reallocate to normal buffer size */ 438 | if (allocated != info.samples*(mp3d_sample_t.sizeof)) 439 | { 440 | mp3d_sample_t *alloc_buf = cast(mp3d_sample_t*)realloc(info.buffer, info.samples*(mp3d_sample_t.sizeof)); 441 | if (!alloc_buf && info.samples) 442 | return MP3D_E_MEMORY; 443 | info.buffer = alloc_buf; 444 | } 445 | if (frames) 446 | info.avg_bitrate_kbps = cast(int)(avg_bitrate_kbps/frames); 447 | return ret2; 448 | } 449 | 450 | int mp3dec_iterate_buf(const(uint8_t)*buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data) 451 | { 452 | const uint8_t *orig_buf = buf; 453 | if (!buf || cast(size_t)-1 == buf_size || !callback) 454 | return MP3D_E_PARAM; 455 | /* skip id3 */ 456 | mp3dec_skip_id3(&buf, &buf_size); 457 | if (!buf_size) 458 | return 0; 459 | mp3dec_frame_info_t frame_info; 460 | memset(&frame_info, 0, (frame_info.sizeof)); 461 | do 462 | { 463 | int free_format_bytes = 0, frame_size = 0, ret; 464 | int i = mp3d_find_frame(buf, cast(int)buf_size, &free_format_bytes, &frame_size); 465 | buf += i; 466 | buf_size -= i; 467 | if (i && !frame_size) 468 | continue; 469 | if (!frame_size) 470 | break; 471 | const uint8_t *hdr = buf; 472 | frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; 473 | frame_info.hz = hdr_sample_rate_hz(hdr); 474 | frame_info.layer = 4 - HDR_GET_LAYER(hdr); 475 | frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); 476 | frame_info.frame_bytes = frame_size; 477 | 478 | if (callback) 479 | { 480 | ret = callback(user_data, hdr, frame_size, free_format_bytes, buf_size, (hdr - orig_buf), &frame_info); 481 | if (ret) 482 | return ret; 483 | } 484 | buf += frame_size; 485 | buf_size -= frame_size; 486 | } while (1); 487 | return 0; 488 | } 489 | 490 | int mp3dec_iterate_cb(mp3dec_io_t *io, uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data) 491 | { 492 | if (!io || !buf || cast(size_t)-1 == buf_size || buf_size < MINIMP3_BUF_SIZE || !callback) 493 | return MP3D_E_PARAM; 494 | size_t filled = io.read(buf, MINIMP3_ID3_DETECT_SIZE, io.read_data), consumed = 0; 495 | uint64_t readed2 = 0; 496 | mp3dec_frame_info_t frame_info; 497 | int eof = 0; 498 | memset(&frame_info, 0, (frame_info.sizeof)); 499 | if (filled > MINIMP3_ID3_DETECT_SIZE) 500 | return MP3D_E_IOERROR; 501 | if (MINIMP3_ID3_DETECT_SIZE != filled) 502 | return 0; 503 | size_t id3v2size = mp3dec_skip_id3v2(buf, filled); 504 | if (id3v2size) 505 | { 506 | if (io.seek(id3v2size, io.seek_data)) 507 | return MP3D_E_IOERROR; 508 | filled = io.read(buf, buf_size, io.read_data); 509 | if (filled > buf_size) 510 | return MP3D_E_IOERROR; 511 | readed2 += id3v2size; 512 | } else 513 | { 514 | size_t readed = io.read(buf + MINIMP3_ID3_DETECT_SIZE, buf_size - MINIMP3_ID3_DETECT_SIZE, io.read_data); 515 | if (readed > (buf_size - MINIMP3_ID3_DETECT_SIZE)) 516 | return MP3D_E_IOERROR; 517 | filled += readed; 518 | } 519 | if (filled < MINIMP3_BUF_SIZE) 520 | mp3dec_skip_id3v1(buf, &filled); 521 | do 522 | { 523 | int free_format_bytes = 0, frame_size = 0, ret; 524 | int i = mp3d_find_frame(buf + consumed, cast(int)(filled - consumed), &free_format_bytes, &frame_size); 525 | if (i && !frame_size) 526 | { 527 | consumed += i; 528 | continue; 529 | } 530 | if (!frame_size) 531 | break; 532 | const uint8_t *hdr = buf + consumed + i; 533 | frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2; 534 | frame_info.hz = hdr_sample_rate_hz(hdr); 535 | frame_info.layer = 4 - HDR_GET_LAYER(hdr); 536 | frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr); 537 | frame_info.frame_bytes = frame_size; 538 | 539 | readed2 += i; 540 | if (callback) 541 | { 542 | ret = callback(user_data, hdr, frame_size, free_format_bytes, filled - consumed, readed2, &frame_info); 543 | if (ret) 544 | return ret; 545 | } 546 | readed2 += frame_size; 547 | consumed += i + frame_size; 548 | if (!eof && filled - consumed < MINIMP3_BUF_SIZE) 549 | { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ 550 | memmove(buf, buf + consumed, filled - consumed); 551 | filled -= consumed; 552 | consumed = 0; 553 | size_t readed = io.read(buf + filled, buf_size - filled, io.read_data); 554 | if (readed > (buf_size - filled)) 555 | return MP3D_E_IOERROR; 556 | if (readed != (buf_size - filled)) 557 | eof = 1; 558 | filled += readed; 559 | if (eof) 560 | mp3dec_skip_id3v1(buf, &filled); 561 | } 562 | } while (1); 563 | return 0; 564 | } 565 | 566 | static int mp3dec_load_index(void *user_data, const uint8_t *frame, int frame_size, int free_format_bytes, size_t buf_size, uint64_t offset, mp3dec_frame_info_t *info) 567 | { 568 | mp3dec_frame_t *idx_frame; 569 | mp3dec_ex_t *dec = cast(mp3dec_ex_t *)user_data; 570 | if (!dec.index.frames && !dec.start_offset) 571 | { /* detect VBR tag and try to avoid full scan */ 572 | uint32_t frames; 573 | int delay, padding; 574 | dec.info = *info; 575 | dec.start_offset = dec.offset = offset; 576 | dec.end_offset = (offset + buf_size); 577 | dec.free_format_bytes = free_format_bytes; /* should not change */ 578 | if (3 == dec.info.layer) 579 | { 580 | int ret = mp3dec_check_vbrtag(frame, frame_size, &frames, &delay, &padding); 581 | if (ret) 582 | dec.start_offset = dec.offset = offset + frame_size; 583 | if (ret > 0) 584 | { 585 | padding *= info.channels; 586 | dec.start_delay = dec.to_skip = delay*info.channels; 587 | dec.samples = hdr_frame_samples(frame)*info.channels*cast(uint64_t)frames; 588 | if (dec.samples >= cast(uint64_t)dec.start_delay) 589 | dec.samples -= dec.start_delay; 590 | if (padding > 0 && dec.samples >= cast(uint64_t)padding) 591 | dec.samples -= padding; 592 | dec.detected_samples = dec.samples; 593 | dec.vbr_tag_found = 1; 594 | return MP3D_E_USER; 595 | } else if (ret < 0) 596 | return 0; 597 | } 598 | } 599 | if (dec.index.num_frames + 1 > dec.index.capacity) 600 | { 601 | if (!dec.index.capacity) 602 | dec.index.capacity = 4096; 603 | else 604 | dec.index.capacity *= 2; 605 | mp3dec_frame_t *alloc_buf = cast(mp3dec_frame_t *)realloc(cast(void*)dec.index.frames, (mp3dec_frame_t.sizeof)*dec.index.capacity); 606 | if (!alloc_buf) 607 | return MP3D_E_MEMORY; 608 | dec.index.frames = alloc_buf; 609 | } 610 | idx_frame = &dec.index.frames[dec.index.num_frames++]; 611 | idx_frame.offset = offset; 612 | idx_frame.sample = dec.samples; 613 | if (!dec.buffer_samples && dec.index.num_frames < 256) 614 | { /* for some cutted mp3 frames, bit-reservoir not filled and decoding can't be started from first frames */ 615 | /* try to decode up to 255 first frames till samples starts to decode */ 616 | dec.buffer_samples = mp3dec_decode_frame(&dec.mp3d, frame, cast(int)MINIMP3_MIN(buf_size, cast(size_t)int.max), dec.buffer.ptr, info); 617 | dec.samples += dec.buffer_samples*info.channels; 618 | } else 619 | dec.samples += hdr_frame_samples(frame)*info.channels; 620 | return 0; 621 | } 622 | 623 | int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method) 624 | { 625 | if (!dec || !buf || cast(size_t)-1 == buf_size || !(MP3D_SEEK_TO_BYTE == seek_method || MP3D_SEEK_TO_SAMPLE == seek_method)) 626 | return MP3D_E_PARAM; 627 | memset(dec, 0, (*dec).sizeof); 628 | dec.file.buffer = buf; 629 | dec.file.size = buf_size; 630 | dec.seek_method = seek_method; 631 | mp3dec_init(&dec.mp3d); 632 | int ret = mp3dec_iterate_buf(dec.file.buffer, dec.file.size, &mp3dec_load_index, dec); 633 | if (ret && MP3D_E_USER != ret) 634 | return ret; 635 | mp3dec_init(&dec.mp3d); 636 | dec.buffer_samples = 0; 637 | return 0; 638 | } 639 | 640 | size_t mp3dec_idx_binary_search(mp3dec_index_t *idx, uint64_t position) 641 | { 642 | size_t end = idx.num_frames, start = 0, index = 0; 643 | while (start <= end) 644 | { 645 | size_t mid = (start + end) / 2; 646 | if (idx.frames[mid].sample >= position) 647 | { /* move left side. */ 648 | if (idx.frames[mid].sample == position) 649 | return mid; 650 | end = mid - 1; 651 | } else 652 | { /* move to right side */ 653 | index = mid; 654 | start = mid + 1; 655 | if (start == idx.num_frames) 656 | break; 657 | } 658 | } 659 | return index; 660 | } 661 | 662 | int mp3dec_ex_seek(mp3dec_ex_t *dec, uint64_t position) 663 | { 664 | size_t i; 665 | if (!dec) 666 | return MP3D_E_PARAM; 667 | if (MP3D_SEEK_TO_BYTE == dec.seek_method) 668 | { 669 | if (dec.io) 670 | { 671 | dec.offset = position; 672 | } else 673 | { 674 | dec.offset = MINIMP3_MIN(position, dec.file.size); 675 | } 676 | dec.cur_sample = 0; 677 | goto do_exit; 678 | } 679 | dec.cur_sample = position; 680 | position += dec.start_delay; 681 | if (0 == position) 682 | { /* optimize seek to zero, no index needed */ 683 | seek_zero: 684 | dec.offset = dec.start_offset; 685 | dec.to_skip = 0; 686 | goto do_exit; 687 | } 688 | if (!dec.index.frames && dec.vbr_tag_found) 689 | { /* no index created yet (vbr tag used to calculate track length) */ 690 | dec.samples = 0; 691 | dec.buffer_samples = 0; 692 | if (dec.io) 693 | { 694 | if (dec.io.seek(dec.start_offset, dec.io.seek_data)) 695 | return MP3D_E_IOERROR; 696 | int ret = mp3dec_iterate_cb(dec.io, cast(uint8_t *)dec.file.buffer, dec.file.size, &mp3dec_load_index, dec); 697 | if (ret && MP3D_E_USER != ret) 698 | return ret; 699 | } else 700 | { 701 | int ret = mp3dec_iterate_buf(dec.file.buffer + dec.start_offset, cast(size_t)(dec.file.size - dec.start_offset), &mp3dec_load_index, dec); 702 | if (ret && MP3D_E_USER != ret) 703 | return ret; 704 | } 705 | for (i = 0; i < dec.index.num_frames; i++) 706 | dec.index.frames[i].offset += dec.start_offset; 707 | dec.samples = dec.detected_samples; 708 | } 709 | if (!dec.index.frames) 710 | goto seek_zero; /* no frames in file - seek to zero */ 711 | i = mp3dec_idx_binary_search(&dec.index, position); 712 | if (i) 713 | { 714 | int to_fill_bytes = 511; 715 | int skip_frames = MINIMP3_PREDECODE_FRAMES; 716 | i -= MINIMP3_MIN(i, cast(size_t)skip_frames); 717 | if (3 == dec.info.layer) 718 | { 719 | while (i && to_fill_bytes) 720 | { /* make sure bit-reservoir is filled when we start decoding */ 721 | bs_t[1] bs; 722 | L3_gr_info_t[4] gr_info; 723 | int frame_bytes, frame_size; 724 | const(uint8_t) *hdr; 725 | if (dec.io) 726 | { 727 | hdr = dec.file.buffer; 728 | if (dec.io.seek(dec.index.frames[i - 1].offset, dec.io.seek_data)) 729 | return MP3D_E_IOERROR; 730 | size_t readed = dec.io.read(cast(uint8_t *)hdr, HDR_SIZE, dec.io.read_data); 731 | if (readed != HDR_SIZE) 732 | return MP3D_E_IOERROR; 733 | frame_size = hdr_frame_bytes(hdr, dec.free_format_bytes) + hdr_padding(hdr); 734 | readed = dec.io.read(cast(uint8_t *)hdr + HDR_SIZE, frame_size - HDR_SIZE, dec.io.read_data); 735 | if (readed != cast(size_t)(frame_size - HDR_SIZE)) 736 | return MP3D_E_IOERROR; 737 | bs_init(bs.ptr, hdr + HDR_SIZE, frame_size - HDR_SIZE); 738 | } else 739 | { 740 | hdr = dec.file.buffer + dec.index.frames[i - 1].offset; 741 | frame_size = hdr_frame_bytes(hdr, dec.free_format_bytes) + hdr_padding(hdr); 742 | bs_init(bs.ptr, hdr + HDR_SIZE, frame_size - HDR_SIZE); 743 | } 744 | if (HDR_IS_CRC(hdr)) 745 | get_bits(bs.ptr, 16); 746 | i--; 747 | if (L3_read_side_info(bs.ptr, gr_info.ptr, hdr) < 0) 748 | break; /* frame not decodable, we can start from here */ 749 | frame_bytes = (bs[0].limit - bs[0].pos)/8; 750 | to_fill_bytes -= MINIMP3_MIN(to_fill_bytes, frame_bytes); 751 | } 752 | } 753 | } 754 | dec.offset = dec.index.frames[i].offset; 755 | dec.to_skip = cast(int)(position - dec.index.frames[i].sample); 756 | while ((i + 1) < dec.index.num_frames && !dec.index.frames[i].sample && !dec.index.frames[i + 1].sample) 757 | { /* skip not decodable first frames */ 758 | const(uint8_t) *hdr; 759 | if (dec.io) 760 | { 761 | hdr = dec.file.buffer; 762 | if (dec.io.seek(dec.index.frames[i].offset, dec.io.seek_data)) 763 | return MP3D_E_IOERROR; 764 | size_t readed = dec.io.read(cast(uint8_t *)hdr, HDR_SIZE, dec.io.read_data); 765 | if (readed != HDR_SIZE) 766 | return MP3D_E_IOERROR; 767 | } else 768 | hdr = dec.file.buffer + dec.index.frames[i].offset; 769 | dec.to_skip += hdr_frame_samples(hdr)*dec.info.channels; 770 | i++; 771 | } 772 | do_exit: 773 | if (dec.io) 774 | { 775 | if (dec.io.seek(dec.offset, dec.io.seek_data)) 776 | return MP3D_E_IOERROR; 777 | } 778 | dec.buffer_samples = 0; 779 | dec.buffer_consumed = 0; 780 | dec.input_consumed = 0; 781 | dec.input_filled = 0; 782 | dec.last_error = 0; 783 | mp3dec_init(&dec.mp3d); 784 | return 0; 785 | } 786 | 787 | size_t mp3dec_ex_read(mp3dec_ex_t *dec, mp3d_sample_t *buf, size_t samples) 788 | { 789 | if (!dec || !buf) 790 | return MP3D_E_PARAM; 791 | uint64_t end_offset = dec.end_offset ? dec.end_offset : dec.file.size; 792 | size_t samples_requested = samples; 793 | int eof = 0; 794 | mp3dec_frame_info_t frame_info; 795 | memset(&frame_info, 0, (frame_info.sizeof)); 796 | if (dec.detected_samples && dec.cur_sample >= dec.detected_samples) 797 | return 0; /* at end of stream */ 798 | if (dec.last_error) 799 | return 0; /* error eof state, seek can reset it */ 800 | if (dec.buffer_consumed < dec.buffer_samples) 801 | { 802 | size_t to_copy = MINIMP3_MIN(cast(size_t)(dec.buffer_samples - dec.buffer_consumed), samples); 803 | if (dec.detected_samples) 804 | { /* count decoded samples to properly cut padding */ 805 | if (dec.cur_sample + to_copy >= dec.detected_samples) 806 | to_copy = cast(size_t)(dec.detected_samples - dec.cur_sample); 807 | } 808 | dec.cur_sample += to_copy; 809 | memcpy(buf, dec.buffer.ptr + dec.buffer_consumed, to_copy*(mp3d_sample_t.sizeof)); 810 | buf += to_copy; 811 | dec.buffer_consumed += to_copy; 812 | samples -= to_copy; 813 | } 814 | while (samples) 815 | { 816 | if (dec.detected_samples && dec.cur_sample >= dec.detected_samples) 817 | break; 818 | const(uint8_t) *dec_buf; 819 | if (dec.io) 820 | { 821 | if (!eof && (dec.input_filled - dec.input_consumed) < MINIMP3_BUF_SIZE) 822 | { /* keep minimum 10 consecutive mp3 frames (~16KB) worst case */ 823 | memmove(cast(uint8_t*)dec.file.buffer, cast(uint8_t*)dec.file.buffer + dec.input_consumed, dec.input_filled - dec.input_consumed); 824 | dec.input_filled -= dec.input_consumed; 825 | dec.input_consumed = 0; 826 | size_t readed = dec.io.read(cast(uint8_t*)dec.file.buffer + dec.input_filled, dec.file.size - dec.input_filled, dec.io.read_data); 827 | if (readed > (dec.file.size - dec.input_filled)) 828 | { 829 | dec.last_error = MP3D_E_IOERROR; 830 | readed = 0; 831 | } 832 | if (readed != (dec.file.size - dec.input_filled)) 833 | eof = 1; 834 | dec.input_filled += readed; 835 | if (eof) 836 | mp3dec_skip_id3v1(cast(uint8_t*)dec.file.buffer, &dec.input_filled); 837 | } 838 | dec_buf = dec.file.buffer + dec.input_consumed; 839 | if (!(dec.input_filled - dec.input_consumed)) 840 | break; 841 | dec.buffer_samples = mp3dec_decode_frame(&dec.mp3d, dec_buf, cast(int)(dec.input_filled - dec.input_consumed), dec.buffer.ptr, &frame_info); 842 | dec.input_consumed += frame_info.frame_bytes; 843 | } else 844 | { 845 | dec_buf = dec.file.buffer + dec.offset; 846 | uint64_t buf_size = end_offset - dec.offset; 847 | if (!buf_size) 848 | break; 849 | dec.buffer_samples = mp3dec_decode_frame(&dec.mp3d, dec_buf, cast(int) MINIMP3_MIN(buf_size, cast(uint64_t)int.max), dec.buffer.ptr, &frame_info); 850 | } 851 | dec.buffer_consumed = 0; 852 | if (dec.info.hz != frame_info.hz || dec.info.layer != frame_info.layer 853 | || dec.info.channels != frame_info.channels 854 | ) 855 | { 856 | dec.last_error = MP3D_E_DECODE; 857 | break; 858 | } 859 | if (dec.buffer_samples) 860 | { 861 | dec.buffer_samples *= frame_info.channels; 862 | if (dec.to_skip) 863 | { 864 | size_t skip = MINIMP3_MIN(dec.buffer_samples, dec.to_skip); 865 | dec.buffer_consumed += skip; 866 | dec.to_skip -= skip; 867 | } 868 | size_t to_copy = MINIMP3_MIN(cast(size_t)(dec.buffer_samples - dec.buffer_consumed), samples); 869 | if (dec.detected_samples) 870 | { /* ^ handle padding */ 871 | if (dec.cur_sample + to_copy >= dec.detected_samples) 872 | to_copy = cast(size_t)(dec.detected_samples - dec.cur_sample); 873 | } 874 | dec.cur_sample += to_copy; 875 | memcpy(buf, dec.buffer.ptr + dec.buffer_consumed, to_copy*(mp3d_sample_t.sizeof)); 876 | buf += to_copy; 877 | dec.buffer_consumed += to_copy; 878 | samples -= to_copy; 879 | } else if (dec.to_skip) 880 | { /* In mp3 decoding not always can start decode from any frame because of bit reservoir, 881 | count skip samples for such frames */ 882 | int frame_samples = hdr_frame_samples(dec_buf)*frame_info.channels; 883 | dec.to_skip -= MINIMP3_MIN(frame_samples, dec.to_skip); 884 | } 885 | dec.offset += frame_info.frame_bytes; 886 | } 887 | return samples_requested - samples; 888 | } 889 | 890 | 891 | void mp3dec_close_file(mp3dec_map_info_t *map_info) 892 | { 893 | if (map_info.buffer) 894 | free(cast(void *)map_info.buffer); 895 | map_info.buffer = null; 896 | map_info.size = 0; 897 | } 898 | 899 | int mp3dec_detect_mapinfo(mp3dec_map_info_t *map_info) 900 | { 901 | int ret = mp3dec_detect_buf(map_info.buffer, map_info.size); 902 | mp3dec_close_file(map_info); 903 | return ret; 904 | } 905 | 906 | int mp3dec_load_mapinfo(mp3dec_t *dec, mp3dec_map_info_t *map_info, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data) 907 | { 908 | int ret = mp3dec_load_buf(dec, map_info.buffer, map_info.size, info, progress_cb, user_data); 909 | mp3dec_close_file(map_info); 910 | return ret; 911 | } 912 | 913 | int mp3dec_iterate_mapinfo(mp3dec_map_info_t *map_info, MP3D_ITERATE_CB callback, void *user_data) 914 | { 915 | int ret = mp3dec_iterate_buf(map_info.buffer, map_info.size, callback, user_data); 916 | mp3dec_close_file(map_info); 917 | return ret; 918 | } 919 | 920 | static int mp3dec_ex_open_mapinfo(mp3dec_ex_t *dec, int seek_method) 921 | { 922 | int ret = mp3dec_ex_open_buf(dec, dec.file.buffer, dec.file.size, seek_method); 923 | dec.is_file = 1; 924 | if (ret) 925 | mp3dec_ex_close(dec); 926 | return ret; 927 | } 928 | 929 | int mp3dec_ex_open_cb(mp3dec_ex_t *dec, mp3dec_io_t *io, int seek_method) 930 | { 931 | if (!dec || !io || !(MP3D_SEEK_TO_BYTE == seek_method || MP3D_SEEK_TO_SAMPLE == seek_method)) 932 | return MP3D_E_PARAM; 933 | memset(dec, 0, (*dec).sizeof); 934 | dec.file.size = MINIMP3_IO_SIZE; 935 | dec.file.buffer = cast(const uint8_t*)malloc(dec.file.size); 936 | if (!dec.file.buffer) 937 | return MP3D_E_MEMORY; 938 | dec.seek_method = seek_method; 939 | dec.io = io; 940 | mp3dec_init(&dec.mp3d); 941 | if (io.seek(0, io.seek_data)) 942 | return MP3D_E_IOERROR; 943 | int ret = mp3dec_iterate_cb(io, cast(uint8_t *)dec.file.buffer, dec.file.size, &mp3dec_load_index, dec); 944 | if (ret && MP3D_E_USER != ret) 945 | return ret; 946 | if (dec.io.seek(dec.start_offset, dec.io.seek_data)) 947 | return MP3D_E_IOERROR; 948 | mp3dec_init(&dec.mp3d); 949 | dec.buffer_samples = 0; 950 | return 0; 951 | } 952 | 953 | void mp3dec_ex_close(mp3dec_ex_t *dec) 954 | { 955 | if (dec.index.frames) 956 | free(dec.index.frames); 957 | memset(dec, 0, (*dec).sizeof); 958 | } 959 | 960 | 961 | -------------------------------------------------------------------------------- /source/audioformats/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Library for sound file decoding and encoding. See README.md for licence explanations. 3 | 4 | Copyright: Guillaume Piolats 2020. 5 | License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 | */ 7 | module audioformats; 8 | 9 | 10 | // Public API 11 | 12 | public import audioformats.stream; 13 | 14 | 15 | import core.stdc.stdlib: free; 16 | 17 | nothrow @nogc: 18 | 19 | /// Encode a slice to a WAV file. 20 | /// 21 | /// Returns: `true` on success. 22 | bool saveAsWAV(const(float)[] data, 23 | const(char)[] filePath, 24 | int numChannels = 1, 25 | float sampleRate = 44100.0f, 26 | EncodingOptions options = EncodingOptions.init) 27 | { 28 | return saveAsWAVImpl!float(data, filePath, numChannels, sampleRate, options); 29 | } 30 | ///ditto 31 | bool saveAsWAV(const(double)[] data, 32 | const(char)[] filePath, 33 | int numChannels = 1, 34 | float sampleRate = 44100.0f, 35 | EncodingOptions options = EncodingOptions.init) 36 | { 37 | return saveAsWAVImpl!double(data, filePath, numChannels, sampleRate, options); 38 | } 39 | 40 | 41 | /// Encode a slice to a WAV in memory. 42 | /// The returned slice MUST be freed with `freeEncodedAudio`. 43 | /// 44 | /// Returns: `null` in case of error. 45 | const(ubyte)[] toWAV(const(float)[] data, 46 | int numChannels = 1, 47 | float sampleRate = 44100.0f, 48 | EncodingOptions options = EncodingOptions.init) 49 | { 50 | return toWAVImpl!float(data, numChannels, sampleRate, options); 51 | } 52 | ///ditto 53 | const(ubyte)[] toWAV(const(double)[] data, 54 | int numChannels = 1, 55 | float sampleRate = 44100.0f, 56 | EncodingOptions options = EncodingOptions.init) 57 | { 58 | return toWAVImpl!double(data, numChannels, sampleRate, options); 59 | } 60 | 61 | 62 | /// Disowned audio buffers (with eg. `encodeToWAV`) must be freed with this function. 63 | void freeEncodedAudio(const(ubyte)[] encoded) 64 | { 65 | free(cast(void*)encoded.ptr); 66 | } 67 | 68 | 69 | private: 70 | 71 | 72 | const(ubyte)[] toWAVImpl(T)(const(T)[] data, int numChannels, float sampleRate, EncodingOptions options) 73 | { 74 | assert(data !is null); 75 | import core.stdc.string: strlen; 76 | 77 | AudioStream encoder; 78 | encoder.openToBuffer(AudioFileFormat.wav, sampleRate, numChannels, options); 79 | if (encoder.isError) 80 | return null; 81 | static if (is(T == float)) 82 | encoder.writeSamplesFloat(data); 83 | else 84 | encoder.writeSamplesDouble(data); 85 | if (encoder.isError) 86 | return null; 87 | const(ubyte)[] r = encoder.finalizeAndGetEncodedResultDisown(); 88 | return r; 89 | } 90 | 91 | bool saveAsWAVImpl(T)(const(T)[] data, 92 | const(char)[] filePath, 93 | int numChannels, 94 | float sampleRate, 95 | EncodingOptions options) 96 | { 97 | if (data is null) 98 | return false; 99 | if (filePath is null) 100 | return false; 101 | 102 | import core.stdc.string: strlen; 103 | 104 | AudioStream encoder; 105 | encoder.openToFile(filePath, AudioFileFormat.wav, sampleRate, numChannels, options); 106 | if (encoder.isError) 107 | return false; // opening failed 108 | 109 | static if (is(T == float)) 110 | encoder.writeSamplesFloat(data); 111 | else 112 | encoder.writeSamplesDouble(data); 113 | if (encoder.isError) 114 | return false; // writing samples failed 115 | 116 | encoder.flush(); 117 | if (encoder.isError) 118 | return false; // flushing failed 119 | 120 | encoder.finalizeEncoding(); 121 | if (encoder.isError) 122 | return false; // finalizing encoding failed 123 | return true; 124 | } -------------------------------------------------------------------------------- /source/audioformats/pocketmod.d: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2018 rombankzero 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | *******************************************************************************/ 26 | 27 | // Translated to D by Guillaume Piolat Copyright 2021. 28 | module audioformats.pocketmod; 29 | 30 | nothrow: 31 | @nogc: 32 | 33 | enum POCKETMOD_MAX_CHANNELS = 32; 34 | enum POCKETMOD_MAX_SAMPLES = 31; 35 | 36 | struct _pocketmod_sample 37 | { 38 | byte* data; /* Sample data buffer */ 39 | uint length; /* Data length (in bytes) */ 40 | } 41 | 42 | struct _pocketmod_chan 43 | { 44 | ubyte dirty; /* Pitch/volume dirty flags */ 45 | ubyte sample; /* Sample number (0..31) */ 46 | ubyte volume; /* Base volume without tremolo (0..64) */ 47 | ubyte balance; /* Stereo balance (0..255) */ 48 | ushort period; /* Note period (113..856) */ 49 | ushort delayed; /* Delayed note period (113..856) */ 50 | ushort target; /* Target period (for tone portamento) */ 51 | ubyte finetune; /* Note finetune (0..15) */ 52 | ubyte loop_count; /* E6x loop counter */ 53 | ubyte loop_line; /* E6x target line */ 54 | ubyte lfo_step; /* Vibrato/tremolo LFO step counter */ 55 | ubyte[2] lfo_type; /* LFO type for vibrato/tremolo */ 56 | ubyte effect; /* Current effect (0x0..0xf or 0xe0..0xef) */ 57 | ubyte param; /* Raw effect parameter value */ 58 | ubyte param3; /* Parameter memory for 3xx */ 59 | ubyte param4; /* Parameter memory for 4xy */ 60 | ubyte param7; /* Parameter memory for 7xy */ 61 | ubyte param9; /* Parameter memory for 9xx */ 62 | ubyte paramE1; /* Parameter memory for E1x */ 63 | ubyte paramE2; /* Parameter memory for E2x */ 64 | ubyte paramEA; /* Parameter memory for EAx */ 65 | ubyte paramEB; /* Parameter memory for EBx */ 66 | ubyte real_volume; /* Volume (with tremolo adjustment) */ 67 | float position; /* Position in sample data buffer */ 68 | float increment; /* Position increment per output sample */ 69 | } 70 | 71 | struct pocketmod_context 72 | { 73 | /* Read-only song data */ 74 | _pocketmod_sample[POCKETMOD_MAX_SAMPLES] samples; 75 | ubyte *source; /* Pointer to source MOD data */ 76 | ubyte *order; /* Pattern order table */ 77 | ubyte *patterns; /* Start of pattern data */ 78 | ubyte length; /* Patterns in the order (1..128) */ 79 | ubyte reset; /* Pattern to loop back to (0..127) */ 80 | ubyte num_patterns; /* Patterns in the file (1..128) */ 81 | ubyte num_samples; /* Sample count (15 or 31) */ 82 | ubyte num_channels; /* Channel count (1..32) */ 83 | 84 | /* Timing variables */ 85 | int samples_per_second; /* Sample rate (set by user) */ 86 | int ticks_per_line; /* A.K.A. song speed (initially 6) */ 87 | float samples_per_tick; /* Depends on sample rate and BPM */ 88 | 89 | /* Loop detection state */ 90 | ubyte[16] visited; /* Bit mask of previously visited patterns */ 91 | int loop_count; /* How many times the song has looped */ 92 | 93 | /* Render state */ 94 | _pocketmod_chan[POCKETMOD_MAX_CHANNELS] channels; 95 | ubyte pattern_delay;/* EEx pattern delay counter */ 96 | uint lfo_rng; /* RNG used for the random LFO waveform */ 97 | 98 | /* Position in song (from least to most granular) */ 99 | byte pattern; /* Current pattern in order */ 100 | byte line; /* Current line in pattern */ 101 | short tick; /* Current tick in line */ 102 | float sample; /* Current sample in tick */ 103 | } 104 | 105 | /* Memorize a parameter unless the new value is zero */ 106 | void POCKETMOD_MEM_ubyte(ref ubyte dst, ubyte src) 107 | { 108 | dst = src ? src : dst; 109 | } 110 | 111 | void POCKETMOD_MEM_ushort(ref ushort dst, ushort src) 112 | { 113 | dst = src ? src : dst; 114 | } 115 | 116 | void POCKETMOD_MEM2(ref ubyte dst, ubyte src) 117 | { 118 | dst = ((src & 0x0f) ? (src & 0x0f) : (dst & 0x0f)) 119 | | ((src & 0xf0) ? (src & 0xf0) : (dst & 0xf0)); 120 | } 121 | 122 | /* Shortcut to sample metadata (sample must be nonzero) */ 123 | ubyte* POCKETMOD_SAMPLE(pocketmod_context *c, int sample) 124 | { 125 | return ((c).source + 12 + 30 * (sample)); 126 | } 127 | 128 | /* Channel dirty flags */ 129 | enum POCKETMOD_PITCH = 0x01; 130 | enum POCKETMOD_VOLUME = 0x02; 131 | 132 | /* The size of one sample in bytes */ 133 | enum int POCKETMOD_SAMPLE_SIZE = 8; 134 | 135 | /* Finetune adjustment table. Three octaves for each finetune setting. */ 136 | static immutable byte[36][16] _pocketmod_finetune = [ 137 | [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 138 | [ -6, -6, -5, -5, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -3, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0], 139 | [-12,-12,-10,-11, -8, -8, -7, -7, -6, -6, -6, -6, -6, -6, -5, -5, -4, -4, -4, -3, -3, -3, -3, -2, -3, -3, -2, -3, -3, -2, -2, -2, -2, -2, -2, -1], 140 | [-18,-17,-16,-16,-13,-12,-12,-11,-10,-10,-10, -9, -9, -9, -8, -8, -7, -6, -6, -5, -5, -5, -5, -4, -5, -4, -3, -4, -4, -3, -3, -3, -3, -2, -2, -2], 141 | [-24,-23,-21,-21,-18,-17,-16,-15,-14,-13,-13,-12,-12,-12,-11,-10, -9, -8, -8, -7, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -4, -3, -3, -3], 142 | [-30,-29,-26,-26,-23,-21,-20,-19,-18,-17,-17,-16,-15,-14,-13,-13,-11,-11,-10, -9, -9, -9, -8, -7, -8, -7, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4], 143 | [-36,-34,-32,-31,-27,-26,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14,-13,-12,-11,-11,-10,-10, -9, -9, -9, -7, -8, -7, -6, -6, -6, -6, -5, -5, -4], 144 | [-42,-40,-37,-36,-32,-30,-29,-27,-25,-24,-23,-22,-21,-20,-18,-18,-16,-15,-14,-13,-13,-12,-12,-10,-10,-10, -9, -9, -9, -8, -7, -7, -7, -6, -6, -5], 145 | [ 51, 48, 46, 42, 42, 38, 36, 34, 32, 30, 24, 27, 25, 24, 23, 21, 21, 19, 18, 17, 16, 15, 14, 14, 12, 12, 12, 10, 10, 10, 9, 8, 8, 8, 7, 7], 146 | [ 44, 42, 40, 37, 37, 35, 32, 31, 29, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 15, 14, 13, 12, 11, 10, 10, 9, 9, 9, 8, 7, 7, 7, 6, 6], 147 | [ 38, 36, 34, 32, 31, 30, 28, 27, 25, 24, 22, 21, 19, 18, 17, 16, 16, 15, 14, 13, 13, 12, 11, 11, 9, 9, 9, 8, 7, 7, 7, 6, 6, 6, 5, 5], 148 | [ 31, 30, 29, 26, 26, 25, 24, 22, 21, 20, 18, 17, 16, 15, 14, 13, 13, 12, 12, 11, 11, 10, 9, 9, 8, 7, 8, 7, 6, 6, 6, 5, 5, 5, 5, 5], 149 | [ 25, 24, 23, 21, 21, 20, 19, 18, 17, 16, 14, 14, 13, 12, 11, 10, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 4], 150 | [ 19, 18, 17, 16, 16, 15, 15, 14, 13, 12, 11, 10, 9, 9, 9, 8, 8, 18, 7, 7, 7, 6, 5, 6, 5, 4, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3], 151 | [ 12, 12, 12, 10, 11, 11, 10, 10, 9, 8, 7, 7, 6, 6, 6, 5, 6, 5, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2], 152 | [ 6, 6, 6, 5, 6, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1] 153 | ]; 154 | 155 | /* Min/max helper functions */ 156 | int _pocketmod_min(int x, int y) 157 | { 158 | return x < y ? x : y; 159 | } 160 | 161 | int _pocketmod_max(int x, int y) 162 | { 163 | return x > y ? x : y; 164 | } 165 | 166 | /* Clamp a volume value to the 0..64 range */ 167 | int _pocketmod_clamp_volume(int x) 168 | { 169 | x = _pocketmod_max(x, 0x00); 170 | x = _pocketmod_min(x, 0x40); 171 | return x; 172 | } 173 | 174 | /* Zero out a block of memory */ 175 | void _pocketmod_zero(void *data, size_t size) 176 | { 177 | char* byte_ = cast(char*) data; 178 | char* end = byte_ + size; 179 | while (byte_ != end) { *byte_++ = 0; } 180 | } 181 | 182 | /* Convert a period (at finetune = 0) to a note index in 0..35 */ 183 | int _pocketmod_period_to_note(int period) 184 | { 185 | switch (period) { 186 | case 856: return 0; case 808: return 1; case 762: return 2; 187 | case 720: return 3; case 678: return 4; case 640: return 5; 188 | case 604: return 6; case 570: return 7; case 538: return 8; 189 | case 508: return 9; case 480: return 10; case 453: return 11; 190 | case 428: return 12; case 404: return 13; case 381: return 14; 191 | case 360: return 15; case 339: return 16; case 320: return 17; 192 | case 302: return 18; case 285: return 19; case 269: return 20; 193 | case 254: return 21; case 240: return 22; case 226: return 23; 194 | case 214: return 24; case 202: return 25; case 190: return 26; 195 | case 180: return 27; case 170: return 28; case 160: return 29; 196 | case 151: return 30; case 143: return 31; case 135: return 32; 197 | case 127: return 33; case 120: return 34; case 113: return 35; 198 | default: return 0; 199 | } 200 | } 201 | 202 | /* Table-based sine wave oscillator */ 203 | int _pocketmod_sin(int step) 204 | { 205 | /* round(sin(x * pi / 32) * 255) for x in 0..15 */ 206 | static immutable ubyte[16] sin = [ 207 | 0x00, 0x19, 0x32, 0x4a, 0x62, 0x78, 0x8e, 0xa2, 208 | 0xb4, 0xc5, 0xd4, 0xe0, 0xec, 0xf4, 0xfa, 0xfe 209 | ]; 210 | int x = sin[step & 0x0f]; 211 | x = (step & 0x1f) < 0x10 ? x : 0xff - x; 212 | return step < 0x20 ? x : -x; 213 | } 214 | 215 | /* Oscillators for vibrato/tremolo effects */ 216 | int _pocketmod_lfo(pocketmod_context *c, _pocketmod_chan *ch, int step) 217 | { 218 | switch (ch.lfo_type[ch.effect == 7] & 3) { 219 | case 0: return _pocketmod_sin(step & 0x3f); /* Sine */ 220 | case 1: return 0xff - ((step & 0x3f) << 3); /* Saw */ 221 | case 2: return (step & 0x3f) < 0x20 ? 0xff : -0xff; /* Square */ 222 | case 3: return (c.lfo_rng & 0x1ff) - 0xff; /* Random */ 223 | default: return 0; /* Hush little compiler */ 224 | } 225 | } 226 | 227 | void _pocketmod_update_pitch(pocketmod_context *c, _pocketmod_chan *ch) 228 | { 229 | /* Don't do anything if the period is zero */ 230 | ch.increment = 0.0f; 231 | if (ch.period) { 232 | float period = ch.period; 233 | 234 | /* Apply vibrato (if active) */ 235 | if (ch.effect == 0x4 || ch.effect == 0x6) { 236 | int step = (ch.param4 >> 4) * ch.lfo_step; 237 | int rate = ch.param4 & 0x0f; 238 | period += _pocketmod_lfo(c, ch, step) * rate / 128.0f; 239 | 240 | /* Apply arpeggio (if active) */ 241 | } else if (ch.effect == 0x0 && ch.param) { 242 | static immutable float[16] arpeggio = [ /* 2^(X/12) for X in 0..15 */ 243 | 1.000000f, 1.059463f, 1.122462f, 1.189207f, 244 | 1.259921f, 1.334840f, 1.414214f, 1.498307f, 245 | 1.587401f, 1.681793f, 1.781797f, 1.887749f, 246 | 2.000000f, 2.118926f, 2.244924f, 2.378414f 247 | ]; 248 | int step = (ch.param >> ((2 - c.tick % 3) << 2)) & 0x0f; 249 | period /= arpeggio[step]; 250 | } 251 | 252 | /* Calculate sample buffer position increment */ 253 | ch.increment = 3546894.6f / (period * c.samples_per_second); 254 | } 255 | 256 | /* Clear the pitch dirty flag */ 257 | ch.dirty &= ~POCKETMOD_PITCH; 258 | } 259 | 260 | void _pocketmod_update_volume(pocketmod_context *c, _pocketmod_chan *ch) 261 | { 262 | int volume = ch.volume; 263 | if (ch.effect == 0x7) { 264 | int step = ch.lfo_step * (ch.param7 >> 4); 265 | volume += _pocketmod_lfo(c, ch, step) * (ch.param7 & 0x0f) >> 6; 266 | } 267 | ch.real_volume = cast(ubyte) _pocketmod_clamp_volume(volume); 268 | ch.dirty &= ~POCKETMOD_VOLUME; 269 | } 270 | 271 | void _pocketmod_pitch_slide(_pocketmod_chan *ch, int amount) 272 | { 273 | int max = 856 + _pocketmod_finetune[ch.finetune][ 0]; 274 | int min = 113 + _pocketmod_finetune[ch.finetune][35]; 275 | ch.period += amount; 276 | ch.period = cast(ushort) _pocketmod_max(ch.period, min); 277 | ch.period = cast(ushort) _pocketmod_min(ch.period, max); 278 | ch.dirty |= POCKETMOD_PITCH; 279 | } 280 | 281 | void _pocketmod_volume_slide(_pocketmod_chan *ch, int param) 282 | { 283 | /* Undocumented quirk: If both x and y are nonzero, then the value of x */ 284 | /* takes precedence. (Yes, there are songs that rely on this behavior.) */ 285 | int change = (param & 0xf0) ? (param >> 4) : -(param & 0x0f); 286 | ch.volume = cast(ubyte) _pocketmod_clamp_volume(ch.volume + change); 287 | ch.dirty |= POCKETMOD_VOLUME; 288 | } 289 | 290 | /** 291 | Returns the amount of remaining (mono) samples/frames in the 292 | currently playing pattern. 293 | */ 294 | int pocketmod_count_remaining_samples(pocketmod_context *c) { 295 | ubyte[4]* data; 296 | int i, pos; 297 | 298 | int result = 0; 299 | int lineEnd = 64; 300 | 301 | int iTicksPerLine = c.ticks_per_line; 302 | float iSamplesPerTick = c.samples_per_tick; 303 | 304 | /* For every (remaining) line in this current pattern */ 305 | for(int line = c.line; line < 64; line++) { 306 | /* Find the pattern data for the current line */ 307 | pos = (c.order[c.pattern] * 64 + line) * c.num_channels * 4; 308 | data = cast(ubyte[4]*) (c.patterns + pos); 309 | for (i = 0; i < c.num_channels; i++) { 310 | /* Decode columns */ 311 | int effect = ((data[i][2] & 0x0f) << 8) | data[i][3]; 312 | 313 | /* Memorize effect parameter values */ 314 | _pocketmod_chan *ch = &c.channels[i]; 315 | ch.effect = cast(ubyte) ( (effect >> 8) != 0xe ? (effect >> 8) : (effect >> 4) ); 316 | ch.param = (effect >> 8) != 0xe ? (effect & 0xff) : (effect & 0x0f); 317 | switch (ch.effect) { 318 | /* Dxy: Pattern break */ 319 | case 0xD: 320 | // Our pattern jumps *after* decoding the data where the break is on. 321 | return result+cast(int)(cast(float)iTicksPerLine*iSamplesPerTick); 322 | 323 | /* E6x: Pattern loop */ 324 | case 0xE6: 325 | if (ch.param) { 326 | if (!ch.loop_count) { 327 | lineEnd = ch.loop_line; 328 | } else if (--ch.loop_count) { 329 | lineEnd = ch.loop_line; 330 | } 331 | } 332 | break; 333 | 334 | /* Fxx: Set speed */ 335 | case 0xF: 336 | if (ch.param != 0) { 337 | if (ch.param < 0x20) { 338 | iTicksPerLine = ch.param; 339 | } else { 340 | float rate = c.samples_per_second; 341 | iSamplesPerTick = rate / (0.4f * ch.param); 342 | } 343 | } 344 | break; 345 | default: break; 346 | } 347 | } 348 | 349 | result += cast(int)(cast(float)iTicksPerLine*iSamplesPerTick); 350 | } 351 | return result; 352 | } 353 | 354 | void _pocketmod_next_line(pocketmod_context *c) 355 | { 356 | ubyte[4]* data; 357 | int i, pos, pattern_break = -1; 358 | 359 | /* When entering a new pattern order index, mark it as "visited" */ 360 | if (c.line == 0) 361 | { 362 | c.visited[c.pattern >> 3] |= 1 << (c.pattern & 7); 363 | } 364 | 365 | /* Move to the next pattern if this was the last line */ 366 | if (++c.line == 64) 367 | { 368 | if (++c.pattern == c.length) 369 | { 370 | c.pattern = c.reset; 371 | } 372 | c.line = 0; 373 | } 374 | 375 | /* Find the pattern data for the current line */ 376 | pos = (c.order[c.pattern] * 64 + c.line) * c.num_channels * 4; 377 | data = cast(ubyte[4]*) (c.patterns + pos); 378 | for (i = 0; i < c.num_channels; i++) 379 | { 380 | 381 | /* Decode columns */ 382 | int sample = (data[i][0] & 0xf0) | (data[i][2] >> 4); 383 | int period = ((data[i][0] & 0x0f) << 8) | data[i][1]; 384 | int effect = ((data[i][2] & 0x0f) << 8) | data[i][3]; 385 | 386 | /* Memorize effect parameter values */ 387 | _pocketmod_chan *ch = &c.channels[i]; 388 | ch.effect = cast(ubyte) ( (effect >> 8) != 0xe ? (effect >> 8) : (effect >> 4) ); 389 | ch.param = (effect >> 8) != 0xe ? (effect & 0xff) : (effect & 0x0f); 390 | 391 | /* Set sample */ 392 | if (sample) { 393 | if (sample <= POCKETMOD_MAX_SAMPLES) { 394 | ubyte *sample_data = POCKETMOD_SAMPLE(c, sample); 395 | ch.sample = cast(ubyte)sample; 396 | ch.finetune = sample_data[2] & 0x0f; 397 | ch.volume = cast(ubyte)_pocketmod_min(sample_data[3], 0x40); 398 | if (ch.effect != 0xED) { 399 | ch.dirty |= POCKETMOD_VOLUME; 400 | } 401 | } else { 402 | ch.sample = 0; 403 | } 404 | } 405 | 406 | /* Set note */ 407 | if (period) { 408 | int note = _pocketmod_period_to_note(period); 409 | period += _pocketmod_finetune[ch.finetune][note]; 410 | if (ch.effect != 0x3) { 411 | if (ch.effect != 0xED) { 412 | ch.period = cast(ushort) period; 413 | ch.dirty |= POCKETMOD_PITCH; 414 | ch.position = 0.0f; 415 | ch.lfo_step = 0; 416 | } else { 417 | ch.delayed = cast(ushort) period; 418 | } 419 | } 420 | } 421 | 422 | /* Handle pattern effects */ 423 | switch (ch.effect) { 424 | 425 | /* Memorize parameters */ 426 | case 0x3: POCKETMOD_MEM_ubyte(ch.param3, ch.param); goto case 0x5; /* Fall through */ 427 | case 0x5: POCKETMOD_MEM_ushort(ch.target, cast(ushort)period); break; 428 | case 0x4: POCKETMOD_MEM2(ch.param4, ch.param); break; 429 | case 0x7: POCKETMOD_MEM2(ch.param7, ch.param); break; 430 | case 0xE1: POCKETMOD_MEM_ubyte(ch.paramE1, ch.param); break; 431 | case 0xE2: POCKETMOD_MEM_ubyte(ch.paramE2, ch.param); break; 432 | case 0xEA: POCKETMOD_MEM_ubyte(ch.paramEA, ch.param); break; 433 | case 0xEB: POCKETMOD_MEM_ubyte(ch.paramEB, ch.param); break; 434 | 435 | /* 8xx: Set stereo balance (nonstandard) */ 436 | case 0x8: { 437 | ch.balance = ch.param; 438 | } break; 439 | 440 | /* 9xx: Set sample offset */ 441 | case 0x9: { 442 | if (period != 0 || sample != 0) { 443 | ch.param9 = ch.param ? ch.param : ch.param9; 444 | ch.position = ch.param9 << 8; 445 | } 446 | } break; 447 | 448 | /* Bxx: Jump to pattern */ 449 | case 0xB: { 450 | c.pattern = ch.param < c.length ? ch.param : 0; 451 | c.line = -1; 452 | } break; 453 | 454 | /* Cxx: Set volume */ 455 | case 0xC: { 456 | ch.volume = cast(ubyte)_pocketmod_clamp_volume(ch.param); 457 | ch.dirty |= POCKETMOD_VOLUME; 458 | } break; 459 | 460 | /* Dxy: Pattern break */ 461 | case 0xD: { 462 | pattern_break = (ch.param >> 4) * 10 + (ch.param & 15); 463 | } break; 464 | 465 | /* E4x: Set vibrato waveform */ 466 | case 0xE4: { 467 | ch.lfo_type[0] = ch.param; 468 | } break; 469 | 470 | /* E5x: Set sample finetune */ 471 | case 0xE5: { 472 | ch.finetune = ch.param; 473 | ch.dirty |= POCKETMOD_PITCH; 474 | } break; 475 | 476 | /* E6x: Pattern loop */ 477 | case 0xE6: { 478 | if (ch.param) { 479 | if (!ch.loop_count) { 480 | ch.loop_count = ch.param; 481 | c.line = ch.loop_line; 482 | } else if (--ch.loop_count) { 483 | c.line = ch.loop_line; 484 | } 485 | } else { 486 | ch.loop_line = cast(ubyte)(c.line - 1); 487 | } 488 | } break; 489 | 490 | /* E7x: Set tremolo waveform */ 491 | case 0xE7: { 492 | ch.lfo_type[1] = ch.param; 493 | } break; 494 | 495 | /* E8x: Set stereo balance (nonstandard) */ 496 | case 0xE8: { 497 | ch.balance = cast(ubyte)(ch.param << 4); 498 | } break; 499 | 500 | /* EEx: Pattern delay */ 501 | case 0xEE: { 502 | c.pattern_delay = ch.param; 503 | } break; 504 | 505 | /* Fxx: Set speed */ 506 | case 0xF: { 507 | if (ch.param != 0) { 508 | if (ch.param < 0x20) { 509 | c.ticks_per_line = ch.param; 510 | } else { 511 | float rate = c.samples_per_second; 512 | c.samples_per_tick = rate / (0.4f * ch.param); 513 | } 514 | } 515 | } break; 516 | 517 | default: break; 518 | } 519 | } 520 | 521 | /* Pattern breaks are handled here, so that only one jump happens even */ 522 | /* when multiple Dxy commands appear on the same line. (You guessed it: */ 523 | /* There are songs that rely on this behavior!) */ 524 | if (pattern_break != -1) { 525 | c.line = cast(byte)( (pattern_break < 64 ? pattern_break : 0) - 1 ); 526 | if (++c.pattern == c.length) { 527 | c.pattern = c.reset; 528 | } 529 | } 530 | } 531 | 532 | void _pocketmod_next_tick(pocketmod_context *c) 533 | { 534 | int i; 535 | 536 | /* Move to the next line if this was the last tick */ 537 | if (++c.tick == c.ticks_per_line) { 538 | if (c.pattern_delay > 0) { 539 | c.pattern_delay--; 540 | } else { 541 | _pocketmod_next_line(c); 542 | } 543 | c.tick = 0; 544 | } 545 | 546 | /* Make per-tick adjustments for all channels */ 547 | for (i = 0; i < c.num_channels; i++) { 548 | _pocketmod_chan *ch = &c.channels[i]; 549 | int param = ch.param; 550 | 551 | /* Advance the LFO random number generator */ 552 | c.lfo_rng = 0x0019660d * c.lfo_rng + 0x3c6ef35f; 553 | 554 | /* Handle effects that may happen on any tick of a line */ 555 | switch (ch.effect) { 556 | 557 | /* 0xy: Arpeggio */ 558 | case 0x0: { 559 | ch.dirty |= POCKETMOD_PITCH; 560 | } break; 561 | 562 | /* E9x: Retrigger note every x ticks */ 563 | case 0xE9: { 564 | if (!(param && c.tick % param)) { 565 | ch.position = 0.0f; 566 | ch.lfo_step = 0; 567 | } 568 | } break; 569 | 570 | /* ECx: Cut note after x ticks */ 571 | case 0xEC: { 572 | if (c.tick == param) { 573 | ch.volume = 0; 574 | ch.dirty |= POCKETMOD_VOLUME; 575 | } 576 | } break; 577 | 578 | /* EDx: Delay note for x ticks */ 579 | case 0xED: { 580 | if (c.tick == param && ch.sample) { 581 | ch.dirty |= POCKETMOD_VOLUME | POCKETMOD_PITCH; 582 | ch.period = ch.delayed; 583 | ch.position = 0.0f; 584 | ch.lfo_step = 0; 585 | } 586 | } break; 587 | 588 | default: break; 589 | } 590 | 591 | /* Handle effects that only happen on the first tick of a line */ 592 | if (c.tick == 0) { 593 | switch (ch.effect) { 594 | case 0xE1: _pocketmod_pitch_slide(ch, -cast(int)ch.paramE1); break; 595 | case 0xE2: _pocketmod_pitch_slide(ch, cast(int)ch.paramE2); break; 596 | case 0xEA: _pocketmod_volume_slide(ch, ch.paramEA << 4); break; 597 | case 0xEB: _pocketmod_volume_slide(ch, ch.paramEB & 15); break; 598 | default: break; 599 | } 600 | 601 | /* Handle effects that are not applied on the first tick of a line */ 602 | } else { 603 | switch (ch.effect) { 604 | 605 | /* 1xx: Portamento up */ 606 | case 0x1: { 607 | _pocketmod_pitch_slide(ch, -param); 608 | } break; 609 | 610 | /* 2xx: Portamento down */ 611 | case 0x2: { 612 | _pocketmod_pitch_slide(ch, +param); 613 | } break; 614 | 615 | /* 5xy: Volume slide + tone portamento */ 616 | case 0x5: { 617 | _pocketmod_volume_slide(ch, param); 618 | goto case 0x3; 619 | } 620 | 621 | /* 3xx: Tone portamento */ 622 | case 0x3: { 623 | int rate = ch.param3; 624 | int order = ch.period < ch.target; 625 | int closer = ch.period + (order ? rate : -rate); 626 | int new_order = closer < ch.target; 627 | ch.period = cast(ushort)(new_order == order ? closer : ch.target); 628 | ch.dirty |= POCKETMOD_PITCH; 629 | } break; 630 | 631 | /* 6xy: Volume slide + vibrato */ 632 | case 0x6: { 633 | _pocketmod_volume_slide(ch, param); 634 | goto case 0x4; 635 | } 636 | 637 | /* 4xy: Vibrato */ 638 | case 0x4: { 639 | ch.lfo_step++; 640 | ch.dirty |= POCKETMOD_PITCH; 641 | } break; 642 | 643 | /* 7xy: Tremolo */ 644 | case 0x7: { 645 | ch.lfo_step++; 646 | ch.dirty |= POCKETMOD_VOLUME; 647 | } break; 648 | 649 | /* Axy: Volume slide */ 650 | case 0xA: { 651 | _pocketmod_volume_slide(ch, param); 652 | } break; 653 | 654 | default: break; 655 | } 656 | } 657 | 658 | /* Update channel volume/pitch if either is out of date */ 659 | if (ch.dirty & POCKETMOD_VOLUME) { _pocketmod_update_volume(c, ch); } 660 | if (ch.dirty & POCKETMOD_PITCH) { _pocketmod_update_pitch(c, ch); } 661 | } 662 | } 663 | 664 | void _pocketmod_render_channel(pocketmod_context *c, 665 | _pocketmod_chan *chan, 666 | float *output, 667 | int samples_to_write) 668 | { 669 | /* Gather some loop data */ 670 | _pocketmod_sample *sample = &c.samples[chan.sample - 1]; 671 | ubyte *data = POCKETMOD_SAMPLE(c, chan.sample); 672 | const int loop_start = ((data[4] << 8) | data[5]) << 1; 673 | const int loop_length = ((data[6] << 8) | data[7]) << 1; 674 | const int loop_end = loop_length > 2 ? loop_start + loop_length : 0xffffff; 675 | const float sample_end = 1 + _pocketmod_min(loop_end, sample.length); 676 | 677 | /* Calculate left/right levels */ 678 | const float volume = chan.real_volume / cast(float) (128 * 64 * 4); 679 | const float level_l = volume * (1.0f - chan.balance / 255.0f); 680 | const float level_r = volume * (0.0f + chan.balance / 255.0f); 681 | 682 | /* Write samples */ 683 | int i, num; 684 | do { 685 | 686 | /* Calculate how many samples we can write in one go */ 687 | num = cast(int)( (sample_end - chan.position) / chan.increment ); 688 | num = _pocketmod_min(num, samples_to_write); 689 | 690 | /* Resample and write 'num' samples */ 691 | for (i = 0; i < num; i++) 692 | { 693 | int x0 = cast(int)(chan.position); 694 | version(AF_LINEAR) { 695 | int x1 = x0 + 1 - loop_length * (x0 + 1 >= loop_end); 696 | float t = chan.position - x0; 697 | float s = (1.0f - t) * sample.data[x0] + t * sample.data[x1]; 698 | } else { 699 | float s = sample.data[x0]; 700 | } 701 | 702 | chan.position += chan.increment; 703 | *output++ += level_l * s; 704 | *output++ += level_r * s; 705 | } 706 | 707 | /* Rewind the sample when reaching the loop point */ 708 | if (chan.position >= loop_end) 709 | { 710 | chan.position -= loop_length; 711 | /* Cut the sample if the end is reached */ 712 | } 713 | else if (chan.position >= sample.length) 714 | { 715 | chan.position = -1.0f; 716 | break; 717 | } 718 | 719 | samples_to_write -= num; 720 | } while (num > 0); 721 | } 722 | 723 | // Modification: can be called without a context, for probing file format. 724 | int _pocketmod_ident(pocketmod_context *c, ubyte *data, int size) 725 | { 726 | int i, j; 727 | 728 | /* 31-instrument files are at least 1084 bytes long */ 729 | if (size >= 1084) 730 | { 731 | 732 | /* The format tag is located at offset 1080 */ 733 | ubyte *tag = data + 1080; 734 | 735 | /* List of recognized format tags (possibly incomplete) */ 736 | static struct Format_Tag 737 | { 738 | char[5] name; 739 | char channels; 740 | } 741 | 742 | static immutable Format_Tag[40] tags = 743 | [ 744 | /* TODO: FLT8 intentionally omitted because I haven't been able */ 745 | /* to find a specimen to test its funky pattern pairing format */ 746 | Format_Tag("M.K.", 4), Format_Tag("M!K!", 4), Format_Tag("FLT4", 4), Format_Tag("4CHN", 4), 747 | Format_Tag("OKTA", 8), Format_Tag("OCTA", 8), Format_Tag("CD81", 8), Format_Tag("FA08", 8), 748 | Format_Tag("1CHN", 1), Format_Tag("2CHN", 2), Format_Tag("3CHN", 3), Format_Tag("4CHN", 4), 749 | Format_Tag("5CHN", 5), Format_Tag("6CHN", 6), Format_Tag("7CHN", 7), Format_Tag("8CHN", 8), 750 | Format_Tag("9CHN", 9), Format_Tag("10CH", 10), Format_Tag("11CH", 11), Format_Tag("12CH", 12), 751 | Format_Tag("13CH", 13), Format_Tag("14CH", 14), Format_Tag("15CH", 15), Format_Tag("16CH", 16), 752 | Format_Tag("17CH", 17), Format_Tag("18CH", 18), Format_Tag("19CH", 19), Format_Tag("20CH", 20), 753 | Format_Tag("21CH", 21), Format_Tag("22CH", 22), Format_Tag("23CH", 23), Format_Tag("24CH", 24), 754 | Format_Tag("25CH", 25), Format_Tag("26CH", 26), Format_Tag("27CH", 27), Format_Tag("28CH", 28), 755 | Format_Tag("29CH", 29), Format_Tag("30CH", 30), Format_Tag("31CH", 31), Format_Tag("32CH", 32) 756 | ]; 757 | 758 | /* Check the format tag to determine if this is a 31-sample MOD */ 759 | for (i = 0; i < cast(int) (tags.length); i++) 760 | { 761 | if (tags[i].name[0] == tag[0] && tags[i].name[1] == tag[1] 762 | && tags[i].name[2] == tag[2] && tags[i].name[3] == tag[3]) 763 | { 764 | if (c) 765 | { 766 | c.num_channels = tags[i].channels; 767 | c.length = data[950]; 768 | c.reset = data[951]; 769 | c.order = &data[952]; 770 | c.patterns = &data[1084]; 771 | c.num_samples = 31; 772 | } 773 | return 1; 774 | } 775 | } 776 | } 777 | 778 | /* A 15-instrument MOD has to be at least 600 bytes long */ 779 | if (size < 600) { 780 | return 0; 781 | } 782 | 783 | /* Check that the song title only contains ASCII bytes (or null) */ 784 | for (i = 0; i < 20; i++) { 785 | if (data[i] != '\0' && (data[i] < ' ' || data[i] > '~')) { 786 | return 0; 787 | } 788 | } 789 | 790 | /* Check that sample names only contain ASCII bytes (or null) */ 791 | for (i = 0; i < 15; i++) { 792 | for (j = 0; j < 22; j++) { 793 | char chr = data[20 + i * 30 + j]; 794 | if (chr != '\0' && (chr < ' ' || chr > '~')) { 795 | return 0; 796 | } 797 | } 798 | } 799 | 800 | /* It looks like we have an older 15-instrument MOD */ 801 | if (c) 802 | { 803 | c.length = data[470]; 804 | c.reset = data[471]; 805 | c.order = &data[472]; 806 | c.patterns = &data[600]; 807 | c.num_samples = 15; 808 | c.num_channels = 4; 809 | } 810 | return 1; 811 | } 812 | 813 | int pocketmod_init(pocketmod_context *c, const void *data, int size, int rate) 814 | { 815 | int i, remaining, header_bytes, pattern_bytes; 816 | ubyte*byte_ = cast(ubyte*) c; 817 | byte *sample_data; 818 | 819 | /* Check that arguments look more or less sane */ 820 | if (!c || !data || rate <= 0 || size <= 0) { 821 | return 0; 822 | } 823 | 824 | /* Zero out the whole context and identify the MOD type */ 825 | _pocketmod_zero(c, pocketmod_context.sizeof); 826 | c.source = cast(ubyte*) data; 827 | if (!_pocketmod_ident(c, c.source, size)) { 828 | return 0; 829 | } 830 | 831 | /* Check that we are compiled with support for enough channels */ 832 | if (c.num_channels > POCKETMOD_MAX_CHANNELS) { 833 | return 0; 834 | } 835 | 836 | /* Check that we have enough sample slots for this file */ 837 | if (POCKETMOD_MAX_SAMPLES < 31) { 838 | byte_ = cast(ubyte*) data + 20; 839 | for (i = 0; i < c.num_samples; i++) { 840 | uint length = 2 * ((byte_[22] << 8) | byte_[23]); 841 | if (i >= POCKETMOD_MAX_SAMPLES && length > 2) { 842 | return 0; /* Can't fit this sample */ 843 | } 844 | byte_ += 30; 845 | } 846 | } 847 | 848 | /* Check that the song length is in valid range (1..128) */ 849 | if (c.length == 0 || c.length > 128) { 850 | return 0; 851 | } 852 | 853 | /* Make sure that the reset pattern doesn't take us out of bounds */ 854 | if (c.reset >= c.length) { 855 | c.reset = 0; 856 | } 857 | 858 | /* Count how many patterns there are in the file */ 859 | c.num_patterns = 0; 860 | for (i = 0; i < 128 && c.order[i] < 128; i++) { 861 | c.num_patterns = cast(ubyte) _pocketmod_max(c.num_patterns, c.order[i]); 862 | } 863 | pattern_bytes = 256 * c.num_channels * ++c.num_patterns; 864 | header_bytes = cast(int) (cast(char*) c.patterns - cast(char*) data); 865 | 866 | /* Check that each pattern in the order is within file bounds */ 867 | for (i = 0; i < c.length; i++) { 868 | if (header_bytes + 256 * c.num_channels * c.order[i] > size) { 869 | return 0; /* Reading this pattern would be a buffer over-read! */ 870 | } 871 | } 872 | 873 | /* Check that the pattern data doesn't extend past the end of the file */ 874 | if (header_bytes + pattern_bytes > size) { 875 | return 0; 876 | } 877 | 878 | /* Load sample payload data, truncating ones that extend outside the file */ 879 | remaining = size - header_bytes - pattern_bytes; 880 | sample_data = cast(byte*) data + header_bytes + pattern_bytes; 881 | for (i = 0; i < c.num_samples; i++) 882 | { 883 | ubyte *data2 = POCKETMOD_SAMPLE(c, i + 1); 884 | uint length = ((data2[0] << 8) | data2[1]) << 1; 885 | _pocketmod_sample *sample = &c.samples[i]; 886 | sample.data = sample_data; 887 | sample.length = _pocketmod_min(length > 2 ? length : 0, remaining); 888 | sample_data += sample.length; 889 | remaining -= sample.length; 890 | } 891 | 892 | /* Set up ProTracker default panning for all channels */ 893 | for (i = 0; i < c.num_channels; i++) { 894 | c.channels[i].balance = 0x80 + ((((i + 1) >> 1) & 1) ? 0x20 : -0x20); 895 | } 896 | 897 | /* Prepare to render from the start */ 898 | c.ticks_per_line = 6; 899 | c.samples_per_second = rate; 900 | c.samples_per_tick = rate / 50.0f; 901 | c.lfo_rng = 0xbadc0de; 902 | c.line = -1; 903 | c.tick = cast(ushort)(c.ticks_per_line - 1); 904 | _pocketmod_next_tick(c); 905 | return 1; 906 | } 907 | 908 | int pocketmod_render(pocketmod_context *c, void *buffer, int buffer_size) 909 | { 910 | int i, samples_rendered = 0; 911 | int samples_remaining = buffer_size / POCKETMOD_SAMPLE_SIZE; 912 | if (c && buffer) 913 | { 914 | float[2]* output = cast(float[2]*) buffer; 915 | while (samples_remaining > 0) { 916 | 917 | /* Calculate the number of samples left in this tick */ 918 | int num = cast(int) (c.samples_per_tick - c.sample); 919 | num = _pocketmod_min(num + !num, samples_remaining); 920 | 921 | /* Render and mix 'num' samples from each channel */ 922 | _pocketmod_zero(output, num * POCKETMOD_SAMPLE_SIZE); 923 | for (i = 0; i < c.num_channels; i++) { 924 | _pocketmod_chan *chan = &c.channels[i]; 925 | if (chan.sample != 0 && chan.position >= 0.0f) { 926 | _pocketmod_render_channel(c, chan, (*output).ptr, num); 927 | } 928 | } 929 | samples_remaining -= num; 930 | samples_rendered += num; 931 | output += num; 932 | 933 | /* Advance song position by 'num' samples */ 934 | if ((c.sample += num) >= c.samples_per_tick) { 935 | c.sample -= c.samples_per_tick; 936 | _pocketmod_next_tick(c); 937 | 938 | /* Stop if a new pattern was reached */ 939 | if (c.line == 0 && c.tick == 0) { 940 | 941 | /* Increment loop counter as needed */ 942 | if (c.visited[c.pattern >> 3] & (1 << (c.pattern & 7))) { 943 | _pocketmod_zero(c.visited.ptr, (c.visited).sizeof); 944 | c.loop_count++; 945 | } 946 | break; 947 | } 948 | } 949 | } 950 | } 951 | return samples_rendered * POCKETMOD_SAMPLE_SIZE; 952 | } 953 | 954 | bool pocketmod_seek(pocketmod_context* c, int pattern, int row, int tick) 955 | { 956 | // NOTE: This is untested. 957 | c.line = cast(byte)row; 958 | c.pattern = cast(byte)pattern; 959 | c.tick = cast(short)tick; 960 | c.sample = 0; 961 | return true; // TODO check that the pattern exist, the row exist, and the tick exist. Else return false. 962 | } 963 | 964 | int pocketmod_loop_count(pocketmod_context *c) 965 | { 966 | return c.loop_count; 967 | } 968 | 969 | -------------------------------------------------------------------------------- /source/audioformats/qoa.d: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org 4 | SPDX-License-Identifier: MIT 5 | 6 | QOA - The "Quite OK Audio" format for fast, lossy audio compression 7 | 8 | 9 | -- Data Format 10 | 11 | A QOA file has an 8 byte file header, followed by a number of frames. Each frame 12 | consists of an 8 byte frame header, the current 16 byte en-/decoder state per 13 | channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 14 | samples of audio data. 15 | 16 | Note that the last frame of a file may contain less than 256 slices per channel. 17 | The last slice (per channel) in the last frame may contain less 20 samples, but 18 | the slice will still be 8 bytes wide, with the unused samples zeroed out. 19 | 20 | The samplerate and number of channels is only stated in the frame headers, but 21 | not in the file header. A decoder may peek into the first frame of the file to 22 | find these values. 23 | 24 | In a valid QOA file all frames have the same number of channels and the same 25 | samplerate. These restrictions may be relaxed for streaming. This remains to 26 | be decided. 27 | 28 | All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, 29 | including the headers, is 64 bit aligned, so it's possible to read files with 30 | just a read_u64() that does the byte swapping if necessary. 31 | 32 | In pseudocode, the file layout is as follows: 33 | 34 | struct { 35 | struct { 36 | char magic[4]; // magic bytes 'qoaf' 37 | uint32_t samples; // number of samples per channel in this file 38 | } file_header; // = 64 bits 39 | 40 | struct { 41 | struct { 42 | uint8_t num_channels; // number of channels 43 | uint24_t samplerate; // samplerate in hz 44 | uint16_t fsamples; // sample count per channel in this frame 45 | uint16_t fsize; // frame size (including the frame header) 46 | } frame_header; // = 64 bits 47 | 48 | struct { 49 | int16_t history[4]; // = 64 bits 50 | int16_t weights[4]; // = 64 bits 51 | } lms_state[num_channels]; 52 | 53 | qoa_slice_t slices[256][num_channels]; // = 64 bits each 54 | } frames[samples * channels / qoa_max_framesize()]; 55 | } qoa_file; 56 | 57 | Wheras the 64bit qoa_slice_t is defined as follows: 58 | 59 | .- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------. 60 | | Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] | 61 | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 | 62 | |------------+--------+--------+--------+---------+---------+-\ \--+---------| 63 | | sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 | 64 | `-------------------------------------------------------------\ \------------` 65 | 66 | `sf_index` defines the scalefactor to use for this slice as an index into the 67 | qoa_scalefactor_tab[16] 68 | 69 | `r00`--`r19` are the residuals for the individual samples, divided by the 70 | scalefactor and quantized by the qoa_quant_tab[]. 71 | 72 | In the decoder, a prediction of the next sample is computed by multiplying the 73 | state (the last four output samples) with the predictor. The residual from the 74 | slice is then dequantized using the qoa_dequant_tab[] and added to the 75 | prediction. The result is clamped to int16 to form the final output sample. 76 | 77 | */ 78 | /* 79 | MIT License 80 | 81 | Copyright (c) 2022-2023 Dominic Szablewski 82 | Copyright (c) 2023 Guillaume Piolat 83 | 84 | Permission is hereby granted, free of charge, to any person obtaining a copy 85 | of this software and associated documentation files (the "Software"), to deal 86 | in the Software without restriction, including without limitation the rights 87 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 88 | copies of the Software, and to permit persons to whom the Software is 89 | furnished to do so, subject to the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be included in all 92 | copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 95 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 96 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 97 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 98 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 99 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 100 | SOFTWARE. 101 | */ 102 | /** 103 | Note: was extended to support seeking (input only), 104 | - chunk decoding and encoding to avoid having the whole song in memory 105 | */ 106 | module audioformats.qoa; 107 | 108 | import audioformats.io; 109 | import audioformats.internals; 110 | import core.stdc.stdlib: malloc, free; 111 | alias QOA_MALLOC = malloc; 112 | alias QOA_FREE = free; 113 | 114 | nothrow @nogc private: 115 | 116 | enum int QOA_MIN_FILESIZE = 16; 117 | enum int QOA_MAX_CHANNELS = 8; 118 | enum int QOA_SLICE_LEN = 20; 119 | enum int QOA_SLICES_PER_FRAME = 256; 120 | enum int QOA_FRAME_LEN = QOA_SLICES_PER_FRAME * QOA_SLICE_LEN; 121 | enum int QOA_LMS_LEN = 4; 122 | enum uint QOA_MAGIC = 0x716f6166; /* 'qoaf' in BE*/ 123 | 124 | uint QOA_FRAME_SIZE(uint channels, uint slices) pure 125 | { 126 | return 8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels; 127 | } 128 | 129 | struct qoa_lms_t 130 | { 131 | int[QOA_LMS_LEN] history; 132 | int[QOA_LMS_LEN] weights; 133 | } 134 | 135 | public struct qoa_desc 136 | { 137 | uint channels; 138 | uint samplerate; 139 | uint samples; 140 | qoa_lms_t[QOA_MAX_CHANNELS] lms; 141 | } 142 | 143 | alias qoa_uint64_t = ulong; 144 | 145 | /* The quant_tab provides an index into the dequant_tab for residuals in the 146 | range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at 147 | the higher end. Note that the residual zero is identical to the lowest positive 148 | value. This is mostly fine, since the qoa_div() function always rounds away 149 | from zero. */ 150 | static immutable int[17] qoa_quant_tab = 151 | [ 152 | 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */ 153 | 0, /* 0 */ 154 | 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */ 155 | ]; 156 | 157 | 158 | /* We have 16 different scalefactors. Like the quantized residuals these become 159 | less accurate at the higher end. In theory, the highest scalefactor that we 160 | would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we 161 | rely on the LMS filter to predict samples accurately enough that a maximum 162 | residual of one quarter of the 16 bit range is sufficient. I.e. with the 163 | scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. 164 | 165 | The scalefactor values are computed as: 166 | scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */ 167 | 168 | static immutable int[16] qoa_scalefactor_tab = 169 | [ 170 | 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 171 | ]; 172 | 173 | 174 | /* The reciprocal_tab maps each of the 16 scalefactors to their rounded 175 | reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in 176 | the encoder with just one multiplication instead of an expensive division. We 177 | do this in .16 fixed point with integers, instead of floats. 178 | 179 | The reciprocal_tab is computed as: 180 | reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */ 181 | 182 | static immutable int[16] qoa_reciprocal_tab = 183 | [ 184 | 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 185 | ]; 186 | 187 | 188 | /* The dequant_tab maps each of the scalefactors and quantized residuals to 189 | their unscaled & dequantized version. 190 | 191 | Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 192 | instead of 1. The dequant_tab assumes the following dequantized values for each 193 | of the quant_tab indices and is computed as: 194 | float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; 195 | dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */ 196 | 197 | static immutable int[8][16] qoa_dequant_tab = 198 | [ 199 | [ 1, -1, 3, -3, 5, -5, 7, -7], 200 | [ 5, -5, 18, -18, 32, -32, 49, -49], 201 | [ 16, -16, 53, -53, 95, -95, 147, -147], 202 | [ 34, -34, 113, -113, 203, -203, 315, -315], 203 | [ 63, -63, 210, -210, 378, -378, 588, -588], 204 | [ 104, -104, 345, -345, 621, -621, 966, -966], 205 | [ 158, -158, 528, -528, 950, -950, 1477, -1477], 206 | [ 228, -228, 760, -760, 1368, -1368, 2128, -2128], 207 | [ 316, -316, 1053, -1053, 1895, -1895, 2947, -2947], 208 | [ 422, -422, 1405, -1405, 2529, -2529, 3934, -3934], 209 | [ 548, -548, 1828, -1828, 3290, -3290, 5117, -5117], 210 | [ 696, -696, 2320, -2320, 4176, -4176, 6496, -6496], 211 | [ 868, -868, 2893, -2893, 5207, -5207, 8099, -8099], 212 | [1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933], 213 | [1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005], 214 | [1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336], 215 | ]; 216 | 217 | 218 | /* The Least Mean Squares Filter is the heart of QOA. It predicts the next 219 | sample based on the previous 4 reconstructed samples. It does so by continuously 220 | adjusting 4 weights based on the residual of the previous prediction. 221 | 222 | The next sample is predicted as the sum of (weight[i] * history[i]). 223 | 224 | The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or 225 | subtracts the residual to each weight, based on the corresponding sample from 226 | the history. This, surprisingly, is sufficient to get worthwhile predictions. 227 | 228 | This is all done with fixed point integers. Hence the right-shifts when updating 229 | the weights and calculating the prediction. */ 230 | 231 | int qoa_lms_predict(qoa_lms_t *lms) pure 232 | { 233 | int prediction = 0; 234 | for (int i = 0; i < QOA_LMS_LEN; i++) 235 | { 236 | prediction += lms.weights[i] * lms.history[i]; 237 | } 238 | return prediction >> 13; 239 | } 240 | 241 | void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) pure 242 | { 243 | int delta = residual >> 4; 244 | for (int i = 0; i < QOA_LMS_LEN; i++) 245 | { 246 | lms.weights[i] += lms.history[i] < 0 ? -delta : delta; 247 | } 248 | 249 | for (int i = 0; i < QOA_LMS_LEN-1; i++) 250 | { 251 | lms.history[i] = lms.history[i+1]; 252 | } 253 | lms.history[QOA_LMS_LEN-1] = sample; 254 | } 255 | 256 | 257 | /* qoa_div() implements a rounding division, but avoids rounding to zero for 258 | small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still 259 | returns as 0, which is handled in the qoa_quant_tab[]. 260 | qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an 261 | argument, so it can do the division with a cheaper integer multiplication. */ 262 | 263 | int qoa_div(int v, int scalefactor) pure 264 | { 265 | int reciprocal = qoa_reciprocal_tab[scalefactor]; 266 | int n = (v * reciprocal + (1 << 15)) >> 16; 267 | n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */ 268 | return n; 269 | } 270 | 271 | int qoa_clamp(int v, int min, int max) pure 272 | { 273 | if (v < min) { return min; } 274 | if (v > max) { return max; } 275 | return v; 276 | } 277 | 278 | int qoa_clamp_s16(int v) pure 279 | { 280 | if (cast(uint)(v + 32768) > 65535) 281 | { 282 | if (v < -32768) { return -32768; } 283 | if (v > 32767) { return 32767; } 284 | } 285 | return v; 286 | } 287 | 288 | 289 | 290 | 291 | /* ----------------------------------------------------------------------------- 292 | Encoder */ 293 | 294 | 295 | bool qoa_encode_frame(IOCallbacks* io, 296 | void* userData, 297 | const(short)* sample_data, 298 | qoa_desc *desc, 299 | uint frame_len) 300 | { 301 | uint channels = desc.channels; 302 | 303 | uint slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN; 304 | uint frame_size = QOA_FRAME_SIZE(channels, slices); 305 | 306 | if (!io.write_ulong_BE(userData, 307 | cast(qoa_uint64_t)desc.channels << 56 | 308 | cast(qoa_uint64_t)desc.samplerate << 32 | 309 | cast(qoa_uint64_t)frame_len << 16 | 310 | cast(qoa_uint64_t)frame_size 311 | )) 312 | return false; 313 | 314 | /* Write the current LMS state */ 315 | for (int c = 0; c < channels; c++) { 316 | qoa_uint64_t weights = 0; 317 | qoa_uint64_t history = 0; 318 | for (int i = 0; i < QOA_LMS_LEN; i++) { 319 | history = (history << 16) | (desc.lms[c].history[i] & 0xffff); 320 | weights = (weights << 16) | (desc.lms[c].weights[i] & 0xffff); 321 | } 322 | if (!io.write_ulong_BE(userData, history)) 323 | return false; 324 | if (!io.write_ulong_BE(userData, weights)) 325 | return false; 326 | } 327 | 328 | /* We encode all samples with the channels interleaved on a slice level. 329 | E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/ 330 | for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) 331 | { 332 | for (int c = 0; c < channels; c++) 333 | { 334 | int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index); 335 | int slice_start = sample_index * channels + c; 336 | int slice_end = (sample_index + slice_len) * channels + c; 337 | 338 | /* Brute for search for the best scalefactor. Just go through all 339 | 16 scalefactors, encode all samples for the current slice and 340 | meassure the total squared error. */ 341 | qoa_uint64_t best_error = -1; 342 | qoa_uint64_t best_slice; 343 | qoa_lms_t best_lms; 344 | 345 | for (int scalefactor = 0; scalefactor < 16; scalefactor++) 346 | { 347 | /* We have to reset the LMS state to the last known good one 348 | before trying each scalefactor, as each pass updates the LMS 349 | state when encoding. */ 350 | qoa_lms_t lms = desc.lms[c]; 351 | qoa_uint64_t slice = scalefactor; 352 | qoa_uint64_t current_error = 0; 353 | 354 | for (int si = slice_start; si < slice_end; si += channels) 355 | { 356 | int sample = sample_data[si]; 357 | int predicted = qoa_lms_predict(&lms); 358 | 359 | int residual = sample - predicted; 360 | int scaled = qoa_div(residual, scalefactor); 361 | int clamped = qoa_clamp(scaled, -8, 8); 362 | int quantized = qoa_quant_tab[clamped + 8]; 363 | int dequantized = qoa_dequant_tab[scalefactor][quantized]; 364 | int reconstructed = qoa_clamp_s16(predicted + dequantized); 365 | 366 | long error = (sample - reconstructed); 367 | current_error += error * error; 368 | if (current_error > best_error) 369 | { 370 | break; 371 | } 372 | 373 | qoa_lms_update(&lms, reconstructed, dequantized); 374 | slice = (slice << 3) | quantized; 375 | } 376 | 377 | if (current_error < best_error) 378 | { 379 | best_error = current_error; 380 | best_slice = slice; 381 | best_lms = lms; 382 | } 383 | } 384 | 385 | desc.lms[c] = best_lms; 386 | 387 | /* If this slice was shorter than QOA_SLICE_LEN, we have to left- 388 | shift all encoded data, to ensure the rightmost bits are the empty 389 | ones. This should only happen in the last frame of a file as all 390 | slices are completely filled otherwise. */ 391 | best_slice <<= (QOA_SLICE_LEN - slice_len) * 3; 392 | 393 | if (!io.write_ulong_BE(userData, best_slice)) 394 | return false; 395 | } 396 | } 397 | 398 | return true; 399 | } 400 | 401 | 402 | 403 | 404 | /* ----------------------------------------------------------------------------- 405 | Decoder */ 406 | 407 | uint qoa_max_frame_size(qoa_desc *qoa) 408 | { 409 | return QOA_FRAME_SIZE(qoa.channels, QOA_SLICES_PER_FRAME); 410 | } 411 | 412 | // Note: was changed, qoa_desc is allocated on heap 413 | uint qoa_decode_header(IOCallbacks* io, void* userData, qoa_desc** qoadesc) 414 | { 415 | uint p = 0; 416 | if (io.remainingBytesToRead(userData) < QOA_MIN_FILESIZE) 417 | { 418 | return 0; 419 | } 420 | 421 | bool err; 422 | 423 | /* Read the file header, verify the magic number ('qoaf') and read the 424 | total number of samples. */ 425 | qoa_uint64_t file_header = io.read_ulong_BE(userData, &err); 426 | if (err) 427 | return 0; 428 | 429 | if ((file_header >> 32) != QOA_MAGIC) { 430 | return 0; 431 | } 432 | 433 | qoa_desc* desc = cast(qoa_desc*) QOA_MALLOC(qoa_desc.sizeof); 434 | *qoadesc = desc; 435 | 436 | desc.samples = file_header & 0xffffffff; 437 | if (!(desc.samples)) 438 | return 0; 439 | 440 | /* Peek into the first frame header to get the number of channels and 441 | the samplerate. */ 442 | qoa_uint64_t frame_header = io.read_ulong_BE(userData, &err); 443 | if (err) 444 | return 0; 445 | desc.channels = (frame_header >> 56) & 0x0000ff; 446 | desc.samplerate = (frame_header >> 32) & 0xffffff; 447 | 448 | if (desc.channels == 0 || desc.samples == 0 || desc.samplerate == 0) { 449 | return 0; 450 | } 451 | 452 | return 8; 453 | } 454 | 455 | uint qoa_decode_frame(IOCallbacks* io, void* userData, qoa_desc *qoa, short *sample_data, uint *frame_len) 456 | { 457 | uint p = 0; 458 | *frame_len = 0; 459 | 460 | if (io.remainingBytesToRead(userData) < 8 + QOA_LMS_LEN * 4 * qoa.channels) 461 | return 0; 462 | 463 | /* Read and verify the frame header */ 464 | bool err; 465 | qoa_uint64_t frame_header = io.read_ulong_BE(userData, &err); 466 | if (err) 467 | return 0; 468 | int channels = (frame_header >> 56) & 0x0000ff; 469 | int samplerate = (frame_header >> 32) & 0xffffff; 470 | int samples = (frame_header >> 16) & 0x00ffff; 471 | int frame_size = (frame_header ) & 0x00ffff; 472 | 473 | int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; 474 | int num_slices = data_size / 8; 475 | int max_total_samples = num_slices * QOA_SLICE_LEN; 476 | 477 | if (io.remainingBytesToRead(userData) < frame_size - 8) 478 | return 0; 479 | if ( 480 | channels != qoa.channels || 481 | samplerate != qoa.samplerate || 482 | samples * channels > max_total_samples 483 | ) 484 | { 485 | return 0; 486 | } 487 | 488 | /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */ 489 | for (int c = 0; c < channels; c++) 490 | { 491 | qoa_uint64_t history = io.read_ulong_BE(userData, &err); 492 | if (err) 493 | return 0; 494 | qoa_uint64_t weights = io.read_ulong_BE(userData, &err); 495 | if (err) 496 | return 0; 497 | for (int i = 0; i < QOA_LMS_LEN; i++) { 498 | qoa.lms[c].history[i] = (cast(short)(history >> 48)); 499 | history <<= 16; 500 | qoa.lms[c].weights[i] = (cast(short)(weights >> 48)); 501 | weights <<= 16; 502 | } 503 | } 504 | 505 | /* Decode all slices for all channels in this frame */ 506 | for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) 507 | { 508 | for (int c = 0; c < channels; c++) 509 | { 510 | qoa_uint64_t slice = io.read_ulong_BE(userData, &err); 511 | if (err) 512 | return 0; 513 | 514 | int scalefactor = (slice >> 60) & 0xf; 515 | int slice_start = sample_index * channels + c; 516 | int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c; 517 | 518 | for (int si = slice_start; si < slice_end; si += channels) { 519 | int predicted = qoa_lms_predict(&qoa.lms[c]); 520 | int quantized = (slice >> 57) & 0x7; 521 | int dequantized = qoa_dequant_tab[scalefactor][quantized]; 522 | int reconstructed = qoa_clamp_s16(predicted + dequantized); 523 | 524 | sample_data[si] = cast(short)reconstructed; 525 | slice <<= 3; 526 | 527 | qoa_lms_update(&qoa.lms[c], reconstructed, dequantized); 528 | } 529 | } 530 | } 531 | 532 | *frame_len = samples; 533 | return p; 534 | } 535 | 536 | 537 | // Streaming encoder for QOA. Queues samples until a full frame can be produced. 538 | public struct QOAEncoder 539 | { 540 | nothrow @nogc: 541 | IOCallbacks* io; 542 | void* userData; 543 | int sampleRate; 544 | int numChannels; 545 | 546 | qoa_desc* desc; 547 | 548 | short* buffer; // buffer[0..count] is the staging area before encoding 549 | int count; 550 | uint framesEncoded; 551 | 552 | void initialize(IOCallbacks* io, void* userData, int sampleRate, int numChannels, bool* err) 553 | { 554 | this.io = io; 555 | this.userData = userData; 556 | this.sampleRate = sampleRate; 557 | this.numChannels = numChannels; 558 | 559 | desc = cast(qoa_desc*) QOA_MALLOC(qoa_desc.sizeof); 560 | desc.channels = numChannels; 561 | desc.samplerate = sampleRate; 562 | desc.samples = 0; 563 | 564 | framesEncoded = 0; 565 | 566 | for (int c = 0; c < desc.channels; c++) 567 | { 568 | /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the 569 | prediction of the first few ms of a file. */ 570 | desc.lms[c].weights[0] = 0; 571 | desc.lms[c].weights[1] = 0; 572 | desc.lms[c].weights[2] = -(1<<13); 573 | desc.lms[c].weights[3] = (1<<14); 574 | 575 | /* Explicitly set the history samples to 0, as we might have some 576 | garbage in there. */ 577 | for (int i = 0; i < QOA_LMS_LEN; i++) 578 | { 579 | desc.lms[c].history[i] = 0; 580 | } 581 | } 582 | 583 | // We need a single QOA_FRAME_LEN buffer for encoding a full frame. 584 | buffer = cast(short*) QOA_MALLOC(short.sizeof * QOA_FRAME_LEN * numChannels); 585 | if (!buffer) 586 | { 587 | *err = true; 588 | return; 589 | } 590 | count = 0; 591 | 592 | if (desc.samplerate == 0 || desc.samplerate > 0xffffff || desc.channels == 0 || desc.channels > QOA_MAX_CHANNELS) 593 | { 594 | *err = true; 595 | return; 596 | } 597 | 598 | // Skip QOA header for now 599 | if (!io.write_ulong_BE(userData, 0)) 600 | { 601 | *err = true; 602 | return; 603 | } 604 | 605 | *err = false; 606 | } 607 | 608 | ~this() 609 | { 610 | QOA_FREE(buffer); 611 | buffer = null; 612 | 613 | QOA_FREE(desc); 614 | desc = null; 615 | } 616 | 617 | int writeSamples(T)(const(T)* inSamples, int frames, bool* err) 618 | { 619 | int enqueued = 0; // frames put in buffer 620 | 621 | while (enqueued < frames) 622 | { 623 | int maxToEnqueue = frames - enqueued; 624 | int storeRoom = QOA_FRAME_LEN - count; 625 | int toEnqueue = storeRoom < maxToEnqueue ? storeRoom : maxToEnqueue; 626 | 627 | for (int n = 0; n < toEnqueue; ++n) 628 | { 629 | for (int ch = 0; ch < numChannels; ++ch) 630 | { 631 | int index = n*numChannels+ch; 632 | double x = inSamples[index]; 633 | int s = cast(int)(32768.5 + x * 32767.0); 634 | s -= 32768; 635 | assert(s >= -32767 && s <= 32767); 636 | buffer[(count+n)*numChannels+ch] = cast(short)s; 637 | } 638 | } 639 | count += toEnqueue; 640 | 641 | if (count == QOA_FRAME_LEN) 642 | { 643 | bool success = outputFrame(QOA_FRAME_LEN); 644 | if (!success) 645 | { 646 | *err = true; 647 | return enqueued; // was an error 648 | } 649 | } 650 | 651 | enqueued += toEnqueue; 652 | } 653 | *err = false; 654 | return enqueued; 655 | } 656 | 657 | bool outputFrame(int frames) 658 | { 659 | assert(frames > 0); 660 | if (frames + framesEncoded < framesEncoded) // overflow, QOA too long 661 | return false; 662 | 663 | bool success = qoa_encode_frame(io, userData, buffer, desc, frames); 664 | if (!success) 665 | return false; 666 | 667 | framesEncoded += frames; 668 | count = 0; 669 | return true; 670 | } 671 | 672 | // true on success. 673 | bool finalizeEncoding() 674 | { 675 | // 1. Encode remaining queued samples. 676 | if (count > 0) 677 | { 678 | if (!outputFrame(count)) 679 | return false; 680 | } 681 | 682 | // 2. Finalize file. 683 | long end = io.tell(userData); 684 | 685 | // Overwrite `samples` value in QOA header. 686 | if (!io.seek(0, false, userData)) 687 | return false; 688 | 689 | if (!io.write_ulong_BE(userData, (cast(qoa_uint64_t)QOA_MAGIC << 32) | framesEncoded)) 690 | return false; 691 | 692 | // Put back cursor at the end. 693 | // Note: finalizeEncoding could technically be called several time, and encoding could continue. 694 | // But not supported by audio-formats API. 695 | if (!io.seek(end, false, userData)) 696 | return false; 697 | 698 | return true; 699 | } 700 | } 701 | 702 | // Streaming decoder for QOA. 703 | public struct QOADecoder 704 | { 705 | nothrow @nogc: 706 | IOCallbacks* io; 707 | void* userData; 708 | short* buffer = null; 709 | qoa_desc* desc; 710 | 711 | int numChannels; 712 | int totalFrames; 713 | float samplerate; 714 | 715 | int bufStart; // start of buffer 716 | int bufStop; // end of buffer (bufStop - bufStart) is the number of frames in buffer 717 | 718 | int currentPositionFrame = -1; 719 | 720 | bool seekPosition(int positionFrame) 721 | { 722 | if (currentPositionFrame == positionFrame) 723 | return true; 724 | 725 | // A QOA file has an 8 byte file header, followed by a number of frames. Each frame 726 | // consists of an 8 byte frame header, the current 16 byte en-/decoder state per 727 | // channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 728 | // samples of audio data. 729 | 730 | // Forget current decoding buffer content. 731 | bufStop = 0; 732 | bufStart = 0; 733 | 734 | uint sliceIndex = positionFrame / QOA_SLICE_LEN; 735 | uint frameIndex = sliceIndex / QOA_SLICES_PER_FRAME; 736 | 737 | int remain = positionFrame - frameIndex*QOA_SLICES_PER_FRAME*QOA_SLICE_LEN; 738 | assert(remain >= 0); 739 | 740 | uint byteSizeOfFullFrame = QOA_FRAME_SIZE(numChannels, QOA_SLICES_PER_FRAME); 741 | uint frameOffset = 8 + byteSizeOfFullFrame * frameIndex; 742 | 743 | // goto this frame 744 | if (!io.seek(frameOffset, false, userData)) 745 | return false; 746 | 747 | if (remain > 0) 748 | { 749 | // Read complete slice, refill buffer. 750 | uint frameLen; 751 | qoa_decode_frame(io, userData, desc, buffer, &frameLen); 752 | bufStart = 0; 753 | bufStop = frameLen; 754 | 755 | // Then read some sample to advance. 756 | bool err; 757 | int res = readSamples!float(null, remain, &err); 758 | if (res != remain || err) 759 | return false; // Note: in this case currentPositionFrame is left invalid... 760 | } 761 | 762 | currentPositionFrame = positionFrame; 763 | return true; 764 | } 765 | 766 | int tellPosition() 767 | { 768 | return currentPositionFrame; 769 | } 770 | 771 | // return true if this is a QOA. Taint io. 772 | bool initialize(IOCallbacks* io, void* userData) 773 | { 774 | this.io = io; 775 | this.userData = userData; 776 | 777 | if (qoa_decode_header(io, userData, &desc) != 8) 778 | return false; 779 | 780 | this.numChannels = desc.channels; 781 | this.totalFrames = desc.samples; 782 | this.samplerate = desc.samplerate; 783 | 784 | if (!io.seek(8, false, userData)) 785 | return false; 786 | currentPositionFrame = 0; 787 | 788 | // We need a single QOA_FRAME_LEN buffer for decoding. 789 | buffer = cast(short*) QOA_MALLOC(short.sizeof * QOA_FRAME_LEN * numChannels); 790 | 791 | bufStart = 0; // Nothing in buffer 792 | bufStop = 0; 793 | 794 | return true; // Note: we've read 16 bytes, so we seek to byte 8 (begin of first frame). 795 | } 796 | 797 | ~this() 798 | { 799 | QOA_FREE(buffer); 800 | buffer = null; 801 | 802 | QOA_FREE(desc); 803 | desc = null; 804 | } 805 | 806 | int readSamples(T)(T* outData, int frames, bool* err) 807 | { 808 | int offsetFrames = 0; 809 | while (frames > 0) 810 | { 811 | // If no more data in buffer, read a frame 812 | if (bufStop - bufStart == 0) 813 | { 814 | uint frameLen; 815 | qoa_decode_frame(io, userData, desc, buffer, &frameLen); 816 | 817 | if (frameLen == 0) 818 | return offsetFrames; 819 | 820 | bufStart = 0; 821 | bufStop = frameLen; 822 | } 823 | 824 | // How many samples we have in buffers? Take them. 825 | int inStore = bufStop - bufStart; 826 | if (inStore > frames) 827 | inStore = frames; 828 | 829 | if (outData !is null) 830 | { 831 | enum float F = 1.0f / short.max; 832 | 833 | for (int n = 0; n < inStore; ++n) 834 | { 835 | for (int ch = 0; ch < numChannels; ++ch) 836 | { 837 | int index = n*numChannels+ch; 838 | outData[offsetFrames*numChannels + index] = buffer[bufStart*numChannels + index] * F; 839 | } 840 | } 841 | } 842 | 843 | bufStart += inStore; 844 | offsetFrames += inStore; 845 | currentPositionFrame += inStore; 846 | frames -= inStore; 847 | assert(bufStart <= bufStop); 848 | } 849 | return offsetFrames; 850 | } 851 | } 852 | -------------------------------------------------------------------------------- /source/audioformats/wav.d: -------------------------------------------------------------------------------- 1 | /** 2 | Supports Microsoft WAV audio file format. 3 | 4 | Copyright: Guillaume Piolat 2015-2020. 5 | License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 | */ 7 | module audioformats.wav; 8 | 9 | import core.stdc.math: round, floor, fabs; 10 | import core.stdc.stdlib: rand, RAND_MAX; 11 | import audioformats.io; 12 | import audioformats.internals; 13 | 14 | @nogc: 15 | nothrow: 16 | 17 | version(decodeWAV) 18 | { 19 | /// Use both for scanning and decoding 20 | 21 | final class WAVDecoder 22 | { 23 | public: 24 | @nogc: 25 | nothrow: 26 | 27 | static struct WAVError 28 | { 29 | @nogc nothrow pure @safe: 30 | string reason; 31 | string file; 32 | size_t line; 33 | static WAVError none() {return WAVError("","",0);} 34 | 35 | this(string reason, string file = __FILE__, size_t line = __LINE__) 36 | { 37 | this.reason = reason; 38 | this.file = file; 39 | this.line = line; 40 | } 41 | } 42 | 43 | static immutable ubyte[16] KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 44 | [3, 0, 0, 0, 0, 0, 16, 0, 128, 0, 0, 170, 0, 56, 155, 113]; 45 | 46 | this(IOCallbacks* io, void* userData) nothrow 47 | { 48 | _io = io; 49 | _userData = userData; 50 | } 51 | 52 | // After scan, we know _sampleRate, _lengthInFrames, and _channels, and can call `readSamples` 53 | WAVError scan() 54 | { 55 | // check RIFF header 56 | { 57 | uint chunkId, chunkSize; 58 | bool err; 59 | _io.readRIFFChunkHeader(_userData, chunkId, chunkSize, &err); 60 | if (err) 61 | return WAVError("Cannot read RIFF header"); 62 | if (chunkId != RIFFChunkId!"RIFF") 63 | return WAVError("Expected RIFF chunk."); 64 | 65 | if (chunkSize < 4) 66 | return WAVError("RIFF chunk is too small to contain a format."); 67 | 68 | if (_io.read_uint_BE(_userData, &err) != RIFFChunkId!"WAVE") 69 | return WAVError("Expected WAVE format."); 70 | } 71 | 72 | bool foundFmt = false; 73 | bool foundData = false; 74 | 75 | int byteRate; 76 | int blockAlign; 77 | int bitsPerSample; 78 | 79 | while (!_io.nothingToReadAnymore(_userData)) 80 | { 81 | // Some corrupted WAV files in the wild finish with one 82 | // extra 0 byte after an AFAn chunk, very odd 83 | if (_io.remainingBytesToRead(_userData) == 1) 84 | { 85 | bool err; 86 | ubyte res = _io.peek_ubyte(_userData, &err); 87 | if (err) 88 | return WAVError("cannot read ubyte"); 89 | if (res == 0) 90 | break; 91 | } 92 | 93 | // Question: is there any reason to parse the whole WAV file? This prevents streaming. 94 | 95 | uint chunkId, chunkSize; 96 | bool err; 97 | _io.readRIFFChunkHeader(_userData, chunkId, chunkSize, &err); 98 | if (err) 99 | return WAVError("Cannot read RIFF header"); 100 | 101 | if (chunkId == RIFFChunkId!"fmt ") 102 | { 103 | if (foundFmt) 104 | return WAVError("Found several 'fmt ' chunks in RIFF file."); 105 | 106 | foundFmt = true; 107 | 108 | if (chunkSize < 16) 109 | return WAVError("Expected at least 16 bytes in 'fmt ' chunk."); // found in real-world for the moment: 16 or 40 bytes 110 | 111 | _audioFormat = _io.read_ushort_LE(_userData, &err); 112 | if (err) return WAVError("Cannot read WAV format"); 113 | bool isWFE = _audioFormat == WAVE_FORMAT_EXTENSIBLE; 114 | 115 | if (_audioFormat != LinearPCM && _audioFormat != FloatingPointIEEE && !isWFE) 116 | return WAVError("Unsupported audio format, only PCM and IEEE float and WAVE_FORMAT_EXTENSIBLE are supported."); 117 | 118 | _channels = _io.read_ushort_LE(_userData, &err); 119 | if (err) return WAVError("Cannot read number of channels"); 120 | 121 | _sampleRate = _io.read_uint_LE(_userData, &err); 122 | if (_sampleRate <= 0) 123 | return WAVError("Unsupported sample-rate."); // we do not support sample-rate higher than 2^31hz 124 | 125 | uint bytesPerSec = _io.read_uint_LE(_userData, &err); 126 | if (err) return WAVError("Cannot read bytesPerSec"); 127 | int bytesPerFrame = _io.read_ushort_LE(_userData, &err); 128 | if (err) return WAVError("Cannot read bytesPerFrame"); 129 | bitsPerSample = _io.read_ushort_LE(_userData, &err); 130 | if (err) return WAVError("Cannot read bitsPerSample"); 131 | 132 | if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32 && bitsPerSample != 64) 133 | return WAVError("Unsupported bitdepth"); 134 | 135 | if (bytesPerFrame != (bitsPerSample / 8) * _channels) 136 | return WAVError("Invalid bytes-per-second, data might be corrupted."); 137 | 138 | // Sometimes there is no cbSize 139 | if (chunkSize >= 18) 140 | { 141 | ushort cbSize = _io.read_ushort_LE(_userData, &err); 142 | if (err) return WAVError("Cannot read cbSize"); 143 | 144 | if (isWFE) 145 | { 146 | if (cbSize >= 22) 147 | { 148 | ushort wReserved = _io.read_ushort_LE(_userData, &err); 149 | if (err) return WAVError("Cannot read wReserved"); 150 | uint dwChannelMask = _io.read_uint_LE(_userData, &err); 151 | if (err) return WAVError("Cannot read dwChannelMask"); 152 | ubyte[16] SubFormat = _io.read_guid(_userData, &err); 153 | if (err) return WAVError("Cannot read SubFormat"); 154 | 155 | if (SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) 156 | { 157 | _audioFormat = FloatingPointIEEE; 158 | } 159 | else 160 | return WAVError("Unsupported GUID in WAVE_FORMAT_EXTENSIBLE."); 161 | } 162 | else 163 | return WAVError("Unsupported WAVE_FORMAT_EXTENSIBLE."); 164 | 165 | _io.skip(chunkSize - (18 + 2 + 4 + 16), _userData); 166 | } 167 | else 168 | { 169 | _io.skip(chunkSize - 18, _userData); 170 | } 171 | } 172 | else 173 | { 174 | _io.skip(chunkSize - 16, _userData); 175 | } 176 | 177 | } 178 | else if (chunkId == RIFFChunkId!"data") 179 | { 180 | if (foundData) 181 | return WAVError("Found several 'data' chunks in RIFF file."); 182 | 183 | if (!foundFmt) 184 | return WAVError("'fmt ' chunk expected before the 'data' chunk."); 185 | 186 | _bytePerSample = bitsPerSample / 8; 187 | uint frameSize = _channels * _bytePerSample; 188 | if (chunkSize % frameSize != 0) 189 | return WAVError("Remaining bytes in 'data' chunk, inconsistent with audio data type."); 190 | 191 | uint numFrames = chunkSize / frameSize; 192 | _lengthInFrames = numFrames; 193 | 194 | _samplesOffsetInFile = _io.tell(_userData); 195 | 196 | _io.skip(chunkSize, _userData); // skip, will read later 197 | foundData = true; 198 | } 199 | else 200 | { 201 | // ignore unknown chunks 202 | _io.skip(chunkSize, _userData); 203 | } 204 | } 205 | 206 | if (!foundFmt) 207 | return WAVError("'fmt ' chunk not found."); 208 | 209 | if (!foundData) 210 | return WAVError("'data' chunk not found."); 211 | 212 | // Get ready to decode 213 | _io.seek(_samplesOffsetInFile, false, _userData); 214 | _framePosition = 0; // seek to start 215 | 216 | return WAVError.none; 217 | } 218 | 219 | /// Returns: false in case of failure. 220 | bool seekPosition(int absoluteFrame) 221 | { 222 | if (absoluteFrame < 0) 223 | return false; 224 | if (absoluteFrame > _lengthInFrames) 225 | return false; 226 | uint frameSize = _channels * _bytePerSample; 227 | long pos = _samplesOffsetInFile + absoluteFrame * frameSize; 228 | _io.seek(pos, false, _userData); 229 | _framePosition = absoluteFrame; 230 | return true; 231 | } 232 | 233 | /// Returns: position in absolute number of frames since beginning. 234 | int tellPosition() 235 | { 236 | return _framePosition; 237 | } 238 | 239 | // read interleaved samples 240 | // `outData` should have enough room for frames * _channels 241 | // Returs: Frames actually read. 242 | int readSamples(T)(T* outData, int maxFrames, bool* err) 243 | { 244 | *err = false; 245 | 246 | assert(_framePosition <= _lengthInFrames); 247 | int available = _lengthInFrames - _framePosition; 248 | 249 | // How much frames can we decode? 250 | int frames = maxFrames; 251 | if (frames > available) 252 | frames = available; 253 | _framePosition += frames; 254 | 255 | int numSamples = frames * _channels; 256 | 257 | uint n = 0; 258 | 259 | 260 | if (_audioFormat == FloatingPointIEEE) 261 | { 262 | if (_bytePerSample == 4) 263 | { 264 | for (n = 0; n < numSamples; ++n) 265 | { 266 | float sample = _io.read_float_LE(_userData, err); 267 | if (*err) 268 | return 0; // could return n, but well 269 | outData[n] = sample; 270 | } 271 | } 272 | else if (_bytePerSample == 8) 273 | { 274 | for (n = 0; n < numSamples; ++n) 275 | { 276 | double sample = _io.read_double_LE(_userData, err); 277 | if (*err) 278 | return 0; // ditto 279 | outData[n] = sample; 280 | } 281 | } 282 | else 283 | { 284 | *err = true; 285 | return 0; // Unsupported bit-depth for floating point data, should be 32 or 64. 286 | } 287 | } 288 | else if (_audioFormat == LinearPCM) 289 | { 290 | if (_bytePerSample == 1) 291 | { 292 | for (n = 0; n < numSamples; ++n) 293 | { 294 | ubyte b = _io.read_ubyte(_userData, err); 295 | if (*err) 296 | return 0; // ditto 297 | outData[n] = (b - 128) / 127.0; 298 | } 299 | } 300 | else if (_bytePerSample == 2) 301 | { 302 | for (n = 0; n < numSamples; ++n) 303 | { 304 | short s = _io.read_ushort_LE(_userData, err); 305 | if (*err) 306 | return 0; // ditto 307 | outData[n] = s / 32767.0; 308 | } 309 | } 310 | else if (_bytePerSample == 3) 311 | { 312 | for (n = 0; n < numSamples; ++n) 313 | { 314 | int s = _io.read_24bits_LE(_userData, err); 315 | if (*err) 316 | return 0; // ditto 317 | // duplicate sign bit 318 | s = (s << 8) >> 8; 319 | outData[n] = s / 8388607.0; 320 | } 321 | } 322 | else if (_bytePerSample == 4) 323 | { 324 | for (n = 0; n < numSamples; ++n) 325 | { 326 | int s = _io.read_uint_LE(_userData, err); 327 | if (*err) 328 | return 0; // ditto 329 | outData[n] = s / 2147483648.0; 330 | } 331 | } 332 | else 333 | { 334 | // Unsupported bit-depth for integer PCM data, should be 8, 16, 24 or 32 bits. 335 | *err = true; 336 | return 0; 337 | } 338 | } 339 | else 340 | assert(false); // should have been handled earlier, crash 341 | 342 | // Return number of integer samples read 343 | return frames; 344 | } 345 | 346 | package: 347 | int _sampleRate; 348 | int _channels; 349 | int _audioFormat; 350 | int _bytePerSample; 351 | long _samplesOffsetInFile; 352 | uint _lengthInFrames; 353 | uint _framePosition; 354 | 355 | private: 356 | void* _userData; 357 | IOCallbacks* _io; 358 | } 359 | } 360 | 361 | 362 | version(encodeWAV) 363 | { 364 | /// Use both for scanning and decoding 365 | final class WAVEncoder 366 | { 367 | public: 368 | @nogc: 369 | nothrow: 370 | enum Format 371 | { 372 | s8, 373 | s16le, 374 | s24le, 375 | fp32le, 376 | fp64le, 377 | } 378 | 379 | static bool isFormatLinearPCM(Format fmt) 380 | { 381 | return fmt <= Format.s24le; 382 | } 383 | 384 | this(IOCallbacks* io, 385 | void* userData, 386 | int sampleRate, 387 | int numChannels, 388 | Format format, 389 | bool enableDither, 390 | bool* err) 391 | { 392 | *err = false; 393 | _io = io; 394 | _userData = userData; 395 | _channels = numChannels; 396 | _format = format; 397 | _enableDither = enableDither; 398 | 399 | // Avoids a number of edge cases. 400 | if (_channels < 0 || _channels > 1024) 401 | { 402 | // Can't save a WAV with this number of channels. 403 | *err = true; 404 | return; 405 | } 406 | 407 | // RIFF header 408 | // its size will be overwritten at finalizing 409 | _riffLengthOffset = _io.tell(_userData) + 4; 410 | if (! _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"RIFF", 0)) 411 | { 412 | *err = true; 413 | return; 414 | } 415 | if (! _io.write_uint_BE(_userData, RIFFChunkId!"WAVE")) 416 | { 417 | *err = true; 418 | return; 419 | } 420 | 421 | // 'fmt ' sub-chunk 422 | if (! _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"fmt ", 0x10)) 423 | { 424 | *err = true; 425 | return; 426 | } 427 | if (! _io.write_ushort_LE(_userData, isFormatLinearPCM(format) ? LinearPCM : FloatingPointIEEE)) 428 | { 429 | *err = true; 430 | return; 431 | } 432 | if (! _io.write_ushort_LE(_userData, cast(ushort)(_channels))) 433 | { 434 | *err = true; 435 | return; 436 | } 437 | if (! _io.write_uint_LE(_userData, sampleRate)) 438 | { 439 | *err = true; 440 | return; 441 | } 442 | 443 | size_t bytesPerSec = sampleRate * cast(size_t) frameSize(); 444 | if (!_io.write_uint_LE(_userData, cast(uint)(bytesPerSec))) 445 | { 446 | *err = true; 447 | return; 448 | } 449 | 450 | int bytesPerFrame = frameSize(); 451 | if (!_io.write_ushort_LE(_userData, cast(ushort)bytesPerFrame)) 452 | { 453 | *err = true; 454 | return; 455 | } 456 | 457 | if (!_io.write_ushort_LE(_userData, cast(ushort)(sampleSize() * 8))) 458 | { 459 | *err = true; 460 | return; 461 | } 462 | 463 | // data sub-chunk 464 | _dataLengthOffset = _io.tell(_userData) + 4; 465 | if(! _io.writeRIFFChunkHeader(_userData, RIFFChunkId!"data", 0)) // write 0 but temporarily, this will be overwritten at finalizing 466 | { 467 | *err = true; 468 | return; 469 | } 470 | _writtenFrames = 0; 471 | } 472 | 473 | // write interleaved samples 474 | // `inSamples` should have enough room for frames * _channels 475 | int writeSamples(T)(T* inSamples, int frames, bool* err) nothrow 476 | { 477 | int n = 0; 478 | int samples = frames * _channels; 479 | 480 | final switch(_format) 481 | { 482 | case Format.s8: 483 | ditherInput(inSamples, samples, 127.0f); 484 | for ( ; n < samples; ++n) 485 | { 486 | double x = _ditherBuf[n]; 487 | int b = cast(int)(128.5 + x * 127.0); 488 | if (!_io.write_byte(_userData, cast(byte)b)) 489 | { 490 | *err = true; 491 | return 0; 492 | } 493 | } 494 | break; 495 | 496 | case Format.s16le: 497 | ditherInput(inSamples, samples, 32767.0f); 498 | for ( ; n < samples; ++n) 499 | { 500 | double x = _ditherBuf[n]; 501 | int s = cast(int)(32768.5 + x * 32767.0); 502 | s -= 32768; 503 | assert(s >= -32767 && s <= 32767); 504 | if (!_io.write_short_LE(_userData, cast(short)s)) 505 | { 506 | *err = true; 507 | return 0; 508 | } 509 | } 510 | break; 511 | 512 | case Format.s24le: 513 | ditherInput(inSamples, samples, 8388607.0f); 514 | for ( ; n < samples; ++n) 515 | { 516 | double x = _ditherBuf[n]; 517 | int s = cast(int)(8388608.5 + x * 8388607.0); 518 | s -= 8388608; 519 | assert(s >= -8388607 && s <= 8388607); 520 | if (!_io.write_24bits_LE(_userData, s)) 521 | { 522 | *err = true; 523 | return 0; 524 | } 525 | } 526 | break; 527 | 528 | case Format.fp32le: 529 | for ( ; n < samples; ++n) 530 | { 531 | if (!_io.write_float_LE(_userData, inSamples[n])) 532 | { 533 | *err = true; 534 | return 0; 535 | } 536 | } 537 | break; 538 | case Format.fp64le: 539 | for ( ; n < samples; ++n) 540 | { 541 | if (!_io.write_double_LE(_userData, inSamples[n])) 542 | { 543 | *err = true; 544 | return 0; 545 | } 546 | } 547 | break; 548 | } 549 | _writtenFrames += frames; 550 | *err = false; 551 | 552 | return n; 553 | } 554 | 555 | int sampleSize() 556 | { 557 | final switch(_format) 558 | { 559 | case Format.s8: return 1; 560 | case Format.s16le: return 2; 561 | case Format.s24le: return 3; 562 | case Format.fp32le: return 4; 563 | case Format.fp64le: return 8; 564 | } 565 | } 566 | 567 | int frameSize() 568 | { 569 | return sampleSize() * _channels; 570 | } 571 | 572 | void finalizeEncoding(bool* err) 573 | { 574 | size_t bytesOfData = frameSize() * _writtenFrames; 575 | 576 | // write final number of samples for the 'RIFF' chunk 577 | { 578 | uint riffLength = cast(uint)( 4 + (4 + 4 + 16) + (4 + 4 + bytesOfData) ); 579 | if (!_io.seek(_riffLengthOffset, false, _userData)) 580 | { 581 | *err = true; 582 | return; 583 | } 584 | if (!_io.write_uint_LE(_userData, riffLength)) 585 | { 586 | *err = true; 587 | return; 588 | } 589 | } 590 | 591 | // write final number of samples for the 'data' chunk 592 | { 593 | if (!_io.seek(_dataLengthOffset, false, _userData)) 594 | { 595 | *err = true; 596 | return; 597 | } 598 | if (!_io.write_uint_LE(_userData, cast(uint)bytesOfData )) 599 | { 600 | *err = true; 601 | return; 602 | } 603 | } 604 | *err = true; 605 | } 606 | 607 | ~this() 608 | { 609 | _ditherBuf.reallocBuffer(0); 610 | } 611 | 612 | private: 613 | void* _userData; 614 | IOCallbacks* _io; 615 | Format _format; 616 | int _channels; 617 | int _writtenFrames; 618 | long _riffLengthOffset, _dataLengthOffset; 619 | 620 | bool _enableDither; 621 | double[] _ditherBuf; 622 | TPDFDither _tpdf; 623 | 624 | void ditherInput(T)(T* inSamples, int frames, double scaleFactor) 625 | { 626 | if (_ditherBuf.length < frames) 627 | _ditherBuf.reallocBuffer(frames); 628 | 629 | for (int n = 0; n < frames; ++n) 630 | { 631 | _ditherBuf[n] = inSamples[n]; 632 | } 633 | 634 | if (_enableDither) 635 | _tpdf.process(_ditherBuf.ptr, frames, scaleFactor); 636 | } 637 | } 638 | } 639 | 640 | 641 | private: 642 | 643 | // wFormatTag 644 | immutable int LinearPCM = 0x0001; 645 | immutable int FloatingPointIEEE = 0x0003; 646 | immutable int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; 647 | 648 | 649 | /+ 650 | MIT License 651 | 652 | Copyright (c) 2018 Chris Johnson 653 | 654 | Permission is hereby granted, free of charge, to any person obtaining a copy 655 | of this software and associated documentation files (the "Software"), to deal 656 | in the Software without restriction, including without limitation the rights 657 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 658 | copies of the Software, and to permit persons to whom the Software is 659 | furnished to do so, subject to the following conditions: 660 | 661 | The above copyright notice and this permission notice shall be included in all 662 | copies or substantial portions of the Software. 663 | 664 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 665 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 666 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 667 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 668 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 669 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 670 | SOFTWARE. 671 | +/ 672 | /// This is based upon TPDF Dither by Chris Johnson / AirWindows 673 | /// though the algorithm changed quite a bit, tuned on 8-bit dither by ear. 674 | public struct TPDFDither 675 | { 676 | nothrow: 677 | @nogc: 678 | 679 | void process(double* inoutSamples, int frames, double scaleFactor) 680 | { 681 | for (int n = 0; n < frames; ++n) 682 | { 683 | double x = inoutSamples[n]; 684 | 685 | x *= scaleFactor; 686 | //0-1 is now one bit, now we dither 687 | 688 | enum double TUNE0 = 0.25; // could probably be better if tuned interactively 689 | enum double TUNE1 = TUNE0*0.5; // ditto 690 | 691 | x += (0.5 - 0.5 * (TUNE0+TUNE1)); 692 | x += TUNE0 * (rand()/cast(double)RAND_MAX); 693 | x += TUNE1 * (rand()/cast(double)RAND_MAX); 694 | x = floor(x); 695 | //TPDF: two 0-1 random noises 696 | x /= scaleFactor; 697 | if (x < -1.0) x = -1.0; 698 | if (x > 1.0) x = 1.0; 699 | inoutSamples[n] = x; 700 | } 701 | } 702 | } --------------------------------------------------------------------------------