Compare commits

..

735 commits

Author SHA1 Message Date
Christopher Snowhill
1a0ea3b5d3 Emergency Bug Fix: Fix loading individual tracks
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
And folders of tracks, too! The only thing that worked in 3117 was
playlist files. Hah!

Fixes #436

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-11 05:33:00 -07:00
Christopher Snowhill
7839f661a1 Sentry: Update sentry-cocoa to version 8.50.1
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-10 00:40:24 -07:00
Christopher Snowhill
30db270af3 VGMStream: Updated libvgmstream code base
Updated VGMStream to r1980-277-g72cb4b89

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-09 21:13:34 -07:00
Christopher Snowhill
ab7c0a0afd Track loading: Change file import and sorting
Files are now loaded to unique keys, and containers such as playlists
and CUE sheets maintain their file order. Deduplication now only applies
to top level files and not playlist contents. Sorting applies to top
level files, and playlist or container names, but not their contents.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-09 21:13:28 -07:00
Christopher Snowhill
69533e12c7 VGMStream: Updated libvgmstream code base
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Updated VGMStream to r1980-268-gc32951e9

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-03 01:56:34 -07:00
Christopher Snowhill
048bc7c30d Converter Node: Change volume scale observer
This should fix an exception being thrown because the observer wasn't
registered, or known to be registered. Only register it when it will be
used, and only unregister it if it was registered.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-05-03 01:56:29 -07:00
Christopher Snowhill
5753c48245 HDCD: Stop dynamically halving the volume
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Apparently, this doesn't work too well with real HDCD tracks, and causes
all sorts of weird volume issues. Just leave the volume alone, and let
HDCD decoding make tracks louder, possibly, rather than try to halve the
volume automatically on a detector that isn't terribly good.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-24 14:55:33 -07:00
Christopher Snowhill
3b4313d844 HDCD: Fix how unsigned audio may be processed
Unsigned will alter the input, so move it like the other integer sample
processors do.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-24 14:55:29 -07:00
Christopher Snowhill
a92973c6aa HDCD: Make HDCD extension processing optional
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
And disabled by default, at that. I can't actually hear the difference
of Peak Extension in the Rock track I have that claims to use it. And
Low Level Range Extension is more trouble than it's worth on tracks that
use it by mistake, or maliciously, if the case may be. I may add track
tag level control in the future.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-23 21:44:06 -07:00
Christopher Snowhill
91c4ed0c98 Maintenance: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-23 21:44:01 -07:00
Christopher Snowhill
9e889399ec VGMStream: Fix memory leak
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Thanks to upstream VGMStream for fixing this in the Audacious plugin,
which I derived this code from in the first place, which explains the
memory leak getting in.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-20 21:35:27 -07:00
Christopher Snowhill
47a749ace0 VGMStream: Add native support for 24/32 bps int
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-20 21:35:23 -07:00
Christopher Snowhill
4232fb3949 VGMStream: Updated libvgmstream code base
Updated VGMStream to r1980-242-gfccbb05f

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-20 21:35:18 -07:00
Christopher Snowhill
f96cfea2d3 Placeholder commit for Sparkle update
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-20 21:03:54 -07:00
Christopher Snowhill
1cd431019c Updated MASShortcut to fix crashes
I hope this works to fix the crashes from migration.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-20 21:00:58 -07:00
Christopher Snowhill
3f0c8d379a Path Suggester: Make font fixed size
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-07 14:14:49 -07:00
Christopher Snowhill
72608ecea0 Path Suggester: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-04-07 14:14:42 -07:00
Christopher Snowhill
ac26a28f59 Bug Fix: Fix FFmpeg reporting timestamps for DSD
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Raw DSD is counting frames in bytes, not bits/samples, so it needs to be
scaled up when dividing by the raw sample rate.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 20:26:36 -07:00
Christopher Snowhill
a19469325b MIDI: Add unstable configuration for AU players
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
It's regarded as unstable as some plugins randomly decide to overflow
the main thread queue and crash the player on the main thread check, but
only sometimes, and not always.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:51 -07:00
Christopher Snowhill
f4a382c67f Sandbox Security: Disable library validation
Apparently, this was preventing some signed plugins from being loaded
into the process, preventing their use as MIDI plugins. And this may be
needed for future AudioUnit DSP filter support, for third party plugins
there, as well.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:46 -07:00
Christopher Snowhill
25cd57a3cb MIDI: Move sample buffer from stack to class
Move the stack-based buffer, which is rather large, to the player class
instance, where it will be allocated on the heap.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:42 -07:00
Christopher Snowhill
40d9574015 MIDI: Fix Audio Unit player in several ways
Improve rendering functions of Audio Unit player, and also fix looping
for the Audio Unit player, and any other possible future players which
use blocked decoding with timestamped events.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:37 -07:00
Christopher Snowhill
8cfddb875c MIDI: Make BASSMIDI the visible default
Also hide the Apple plugins from settings so they don't get activated
unless there's no SoundFont configured.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:33 -07:00
Christopher Snowhill
54db6c17cb Dependencies: Update BASS and WavPack libraries
BASSMIDI: 2.4.15.3
BASSFLAC: 2.4.5.5
BASSOPUS: 2.4.3
BASSWV: 2.4.7.4

WavPack: 5.8.1

And updated the WavPack plugin to support threaded decoding, using up to
four worker threads, as detected from the host machine's CPU count.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 16:18:28 -07:00
Christopher Snowhill
104e959ea2 MIDI: Update BASS docs
This was the version of BASSWV previously bundled.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-28 15:26:22 -07:00
Christopher Snowhill
9a23ff9ff9 Audio Output: Optimize fader function
Now using Accelerate methods to calculate and multiply ramps per channel
and add the remainder if necessary.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-27 17:13:47 -07:00
Christopher Snowhill
6b23590ac8 Play Control: Previous track now also restarts
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
If the current track has played for more than 5 seconds, previous track
now restarts the current track, instead of jumping back to the previous
track.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-26 20:10:18 -07:00
Christopher Snowhill
2282538c42 Bug Fix: Fix inserting empty chunks on track ends
This code did not check the number of samples in a packet before adding
it to the output buffer, which apparently had the potential to cause the
output code to emit up to 512 samples of silence between tracks. This,
as one can guess, is a bad thing, and causes noticeable gapping between
tracks.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-26 20:10:13 -07:00
Christopher Snowhill
73951d6fe7 Bug Fix: Fix output logging, switch log method
Output logging, a debugging feature that is only enabled at build time
if I need to chase down some audio mixing or output bug, was not logging
anything at all. Change to use Cocoa file writing methods, and actually
implement the output writer function again.

This code is left disabled 99% of the time anyway, and especially in
release builds. Like the node logging code elsewhere, it has the
potential to be very noisy and consume massive amounts of disk space.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-26 20:10:08 -07:00
Christopher Snowhill
8c019c7302 Bug Fix: Include soxr latency in memory allocation
This should be included, for safety purposes, in case the rounding up to
the nearest multiple of 256 samples doesn't bump the buffer size enough.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-26 20:10:03 -07:00
Christopher Snowhill
f7b2c481e6 Bug Fix: Retry MP3 file a few times before failure
Some checks failed
Check if Cog buildable / Build Universal Cog.app (push) Has been cancelled
Give up after 10 tries.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-24 16:05:36 -07:00
Christopher Snowhill
2038a140ed Bug Fix: Fix minimp3 to deal with invalid files
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
If a file can't decode, there should not be a division by zero error.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-24 15:42:39 -07:00
Christopher Snowhill
18d3f76152 Bug Fix: Fix minimp3 streaming support
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
The streaming support was breaking because initial packet detection was
failing due to bit reservoir errors. Instead, detect consecutive sync
frames in the initial read buffer, then attempt to sync to a decodable
frame in the first block of data, otherwise give up.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-24 08:16:47 -07:00
Christopher Snowhill
2237863f08 FFmpeg: Fix HLS, HLS metadata, update FFmpeg
Add missing HLS MIME type: audio/mpegurl

Update FFmpeg to version 7.1.1, carrying the same patches, and one new
patch: Implementing support for HLS ID3 tags changing mid-stream.

We cannot do away with fdk-aac yet, because the USAC codec is missing
features that fdk-aac implements already.

Fixes #428

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-24 06:09:40 -07:00
Christopher Snowhill
ac93ff038e Bug Fix: Fix minimp3 seek position
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
Seek offset is scaled by the number of channels in the file. Oops again.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 20:22:27 -07:00
Christopher Snowhill
184e1f14c9 Bug Fix: Free memory buffers used by minimp3
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
This was being leaked when playing static, seekable files. Oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 20:04:00 -07:00
Christopher Snowhill
c24d914127 minimp3: Numerous buffering fixes, consolidation
Move minimp3 packet decoder state into the decoder_ex state structure,
instead of using the redundant duplicate structure. Also reduce input
buffer size for streams to 16KiB, and actually use the defined macro in
the header file to declare the streaming buffer size.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 20:03:55 -07:00
Christopher Snowhill
4b0f4a43fd minimp3: Fix seeking behavior
Some checks are pending
Check if Cog buildable / Build Universal Cog.app (push) Waiting to run
Seeking should clear the sample buffer if it contains anything, and non-
seekable files should return an error on an attempt to seek.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 16:18:44 -07:00
Christopher Snowhill
b67b2acd6a VGMStream: Update associated filename extensions
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 16:18:39 -07:00
Christopher Snowhill
90c5833fe9 VGMStream: Considerably rewrite plugin interface
It was about time to rewrite this anyway. Now adapted to the new public
interface API.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 16:18:35 -07:00
Christopher Snowhill
8b3a165d9a Bug Fix: Fixed several code signing issues
There were several issues which broke debug and possibly release
signing, and broke VGMStream in debug situations. Not sure if it was
also broken in release.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 16:18:29 -07:00
Christopher Snowhill
cbaacd35b1 VGMStream: Updated libvgmstream code base
Updated VGMStream to r1980-181-g10093db5

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 16:18:25 -07:00
Christopher Snowhill
9b683a9c56 Bug Fix: Reorder LPC scratch memory for alignment
The double members should be ordered first so they are aligned to an 8
byte boundary. The rest are fine as-is.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 00:05:19 -07:00
Christopher Snowhill
7ff653b48f MP3: Replace MAD with minimp3
libMAD had memory safety issues, possibly with corrupt files. Hopefully,
this will fix the problem.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-23 00:05:11 -07:00
Christopher Snowhill
31281197d4 Code Fix: Change visualizers to only copy FFT data
These two visualization components, SceneKit and Core Graphics based,
only use the FFT data. So now make the request drop the PCM data.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-13 19:51:27 -07:00
Christopher Snowhill
7cb010e337 Code Fix: Add nullability flags to Vis Manager
The Visualization Manager PCM/FFT copy function was already observing
these parameters for null input and only returning output to the ones
which were not null. This just makes it clear that they are both
optional parameters. This is useful for future visualization adventures,
allowing PCM copy without invoking the FFT processing, or requesting FFT
without also having to keep the PCM.

This is mostly only a compile time change, and has no noticeable effect
on the current runtime behavior, as the only consumers of the Visualizer
data currently request both PCM and FFT.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-13 19:51:22 -07:00
Christopher Snowhill
d97b1e788a Sentry: Update sentry-cocoa to version 8.47.0
This release is a bug fix against the Sentry integration from the
upstream project.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-13 19:51:16 -07:00
Christopher Snowhill
2401536e5c Crash Fix: Only selectively register observer
This affects User Defaults, but only has any effect on ChunkLists which
are being used for conversion, and only if they're processing DSD source
material. Thus, the observer should only be added on the one stream that
is converting DSD, and should definitely be removed when the object is
deallocated.

This fixes a serious crash bug that mostly appears to only affect Intel
Macs, and has no major side effects on Apple Silicon that I can tell.
It's a good thing I still own an Intel Mac or two to test on, even if
they are both trapped on older releases of macOS.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-13 19:51:12 -07:00
Christopher Snowhill
a1bbfbe3ac Bug Fix: Track advancing when Rubber Band disabled
Apparently I somehow didn't notice this situation because I still had
Rubber Band enabled, and existing users kept it enabled ever since I
introduced it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-11 14:30:09 -07:00
Christopher Snowhill
2cc7ff9c4a Core Audio: Implement proper fade on seek
Whew, what a mess! And this may pave the way for crossfading.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-10 23:09:03 -07:00
Christopher Snowhill
54c9f30e8a Bug Fix: Don't display notification on seek
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-10 23:08:57 -07:00
Christopher Snowhill
a09907ccd9 Metadata: Move encoding helper to CogAudio
Move this commonly used string decoding helper to the CogAudio framework
and import it in every plugin that uses it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-10 14:55:56 -07:00
Christopher Snowhill
5d73246926 Bug Fix: Handle invalid UTF-8 decoding errors
Apparently, stringWithUTF8String: just returns nil when the encoding is
not UTF-8, rather than throwing an exception.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-10 14:55:51 -07:00
Christopher Snowhill
532d22b9bb App Store: Add encryption attestation
Finally add this to Info.plist properties to save a few seconds on
submissions.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:50:35 -07:00
Christopher Snowhill
aee05d5503 Bug Fix: Change how pause stops unseekable files
Change the stop action slightly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:45:01 -07:00
Christopher Snowhill
d4dcf29dd4 Bug Fix: Disable seeking hotkeys when unseekable
Disable the seek forward and backward actions when the current track
doesn't support seeking.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:44:26 -07:00
Christopher Snowhill
90a62821f4 Preferences: Touched by Xcode
Xcode really wants to add focusRingType="none" to everything now upon
opening with the editor, for some reason.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:19:17 -07:00
Christopher Snowhill
f889b34b1b Bug Fix: Add more constraints to general prefs
Add more constraints so the checkboxes are properly spaced again.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:13:18 -07:00
Christopher Snowhill
2733ee95f3 Playlist Loader: Add option to skip playlists
When parsing folders, add option to skip importing playlist files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 23:01:47 -07:00
Christopher Snowhill
2042034868 Bug Fix: Remove missing outlet from preferences
This is a reference to the updates pane that doesn't exist in the App
Store version of Cog.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 22:30:51 -07:00
Christopher Snowhill
0a849fc1d9 FFmpeg: Handle multiple attached pictures properly
Now it handles multiple attached pictures and tries to pick out the one
which may be the front cover picture, or otherwise picks the first one.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 20:06:49 -07:00
Christopher Snowhill
b0fd359388 Crash Fix: Fix a serious bug in previous commit
Fixes commit 9c1c6d7130, where code was
treating a dictionary like an array.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 18:30:34 -07:00
Christopher Snowhill
318cfd71ea Bug Fix: Fix circular bind setter loop in hotkeys
MASShortcut had a potential circular loop in its bindings, let's fix
that right up.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 18:23:39 -07:00
Christopher Snowhill
9c1c6d7130 Bug Fix: Operation blocks should queue inputs
Operation blocks cannot expect their out of scope variables to be
present when the block executes, so design the block operation to pull
inputs from a queue array one at a time, like the rest of the blocks do.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 15:05:20 -07:00
Christopher Snowhill
cf77521bb7 Bug Fix: Handle possible null exceptions
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 15:05:15 -07:00
Christopher Snowhill
f8ee7e5e46 Core Audio: Fix pausing glitches
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 14:43:16 -07:00
Christopher Snowhill
e64c279e45 Disable global hotkeys again for macOS < 15.0
Clearly, NSUserDefaults bindings were not really meant to be used in
practice at all, as they do nothing but crash the app.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 14:26:07 -07:00
Christopher Snowhill
0de555c2fd Re-enable hotkeys and change preferences storage
Change hotkey storage system, hopefully this will fix the stability
issues that have been plaguing it for a while now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-08 20:24:25 -08:00
Christopher Snowhill
442dfb726b Bug Fix: Disable hotkeys on macOS older than 15.0
Apparently even touching the NSUserDefaults with MASShortcut there is
crash inducing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-08 12:34:38 -08:00
Christopher Snowhill
afeb614481 Bug Fix: Actually perform a fade in
It doesn't fade if we don't advance the sample pointer. Ugh.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-08 00:38:56 -08:00
Christopher Snowhill
63a5239346 Core Audio: Slight change to audio fade in on seek
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-08 00:38:50 -08:00
Christopher Snowhill
0570ebedea Core Audio: Increase fade duration to 125ms
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 23:44:46 -08:00
Christopher Snowhill
1d8803e6f2 Core Audio: Shut off device after fade out
And resume playback before fade in.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 23:44:24 -08:00
Christopher Snowhill
46aac2fa91 Core Audio: Add a slight fading to operations
Add 10 millisecond fade to seeking, pausing and unpausing, and stopping
on command.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 23:22:29 -08:00
Christopher Snowhill
ff5a1c6c2c Bug Fix: Prevent hangs when starting paused
This happens when the player is resumed paused, sometimes near the end
of a track.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 20:14:39 -08:00
Christopher Snowhill
d99eda15b5 Bug Fix: Disable hotkeys configuration on macOS<15
MASShortcutView is apparently buggy on older macOS versions. So everyone
there gets hard coded shortcuts and nothing else.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 17:51:17 -08:00
Christopher Snowhill
d3f99f8987 Bug Fix: Simplification of chunk duration check
This only needs to check that the chunk is empty, not its exact
duration.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 17:28:09 -08:00
Christopher Snowhill
aafe817a1f Bug Fix: Correct playback of DSD formats
DSD formats were buffering incorrectly and terminating way too soon.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 17:28:04 -08:00
Christopher Snowhill
36a2d8efd5 Bug Fix: Restart converter on format change
The converter doesn't just require an output format call, it also
requires this input format change callback to actually signal it to
reopen the converter process with a new format setup.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 05:39:17 -08:00
Christopher Snowhill
17b8647052 Bug Fix: Ensure robust output format changes
Output format mostly requires stopping and restarting the output device,
and this also prevents us from using the latency function properly,
which apparently always returns 0 for output devices anyway. These
changes also prevent the output callback from hanging when resets occur.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 05:39:12 -08:00
Christopher Snowhill
a3385d1af9 Crash Fix: Change how default shortcuts are stored
It turns out that initializing NSUserDefaultsController like this is a
really bad idea, especially on older versions of macOS. This is probably
also why the equalizer was crashing for people on first activation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 04:40:08 -08:00
Christopher Snowhill
9cc0b8be8d Cleanup: Remove unused code
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 04:40:04 -08:00
Christopher Snowhill
25d6f6ea7c Quality of Life: Make buildable with old Xcode
Make the code mostly buildable with Xcode as old as 13.2.1, for debug
testing on Big Sur.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 04:39:59 -08:00
Christopher Snowhill
d7681bda71 Translation: Missing string recently added
Translation provided.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 16:47:16 -08:00
Christopher Snowhill
355169475d Sentry: Replace deprecated SentryUserFeedback use
Replace with newer SentryFeedback.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:58:12 -08:00
Christopher Snowhill
918aa59920 Sentry: Temporarily disable app hang detection
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:51:59 -08:00
Christopher Snowhill
00d42d519e Translation: Properly support this string
Properly support translating the System Default Device name for sound
output devices. Pending a Spanish translation, but in its current state,
it's no different from where it was before this change.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:44:20 -08:00
Christopher Snowhill
b41fd74f31 Bug Fix: Default output device changes monitoring
Fix default output device logging, and also the preferences if no
default device happens to be set.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:41:28 -08:00
Christopher Snowhill
72bfe1c846 Bug Fix: Latency reporting for high latency output
Fixes visualization latency under virtual machines, at least. Not sure
which local or native systems would be reporting high latency here, but
this should fix them as well.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 13:14:41 -08:00
Christopher Snowhill
713241a94f Major Bug Fix: Volume control works again
Shouldn't have broken this again.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 10:18:13 -08:00
Christopher Snowhill
d06ee01a17 Feature: Add seeking hotkeys with defaults
Defaulting to ctrl+command+left/right arrows.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 10:18:08 -08:00
Christopher Snowhill
04db82d0ff Bug Fix: Move default hotkeys to main app startup
Instead of the Preferences plugin.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 10:18:03 -08:00
Christopher Snowhill
bb78d49f23 Sound Output: Move DSPs, restructure output buffer
Move the DSPs to the output node, so they don't get closed and reopened
across each file. Also restructure the output handler to buffer a little
on its own, to account for track switch activity.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-05 20:07:38 -08:00
Christopher Snowhill
428ffb1531 Sentry: Bump to version 8.46.0
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-05 20:07:30 -08:00
Christopher Snowhill
c89cc1cff4 Bug Fix: Add more guards to sound output block
These guards should prevent the one crash we saw logged.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-05 20:05:53 -08:00
Christopher Snowhill
4ccd1811b0 Bug Fix: Correct exception handling blocks
These should be catching NSException*, not generic `id`.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-05 20:05:48 -08:00
Christopher Snowhill
45352f9261 Bug Fix: Handle compile time warning
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:46:55 -08:00
Christopher Snowhill
68a146f6b8 Bug Fix: Handle gaplessness for headphone filter
The filter uses a pre-buffer of input audio, so extrapolate from the
actual input to fill the buffer. Fixes clicking on non-zero-crossing
track endings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:46:50 -08:00
Christopher Snowhill
ea7eff40e0 Bug Fix: Restructure Rubber Band gapless handler
Change how samples are accounted for by the filter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:46:45 -08:00
Christopher Snowhill
45cb841ec0 Bug Fix: Greatly improve audio buffer handling
Buffers were being treated as empty before they were actually processed,
due to races between the current node's end of stream marker and
actually feeding the output buffer.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:43:30 -08:00
Christopher Snowhill
e3b48bdc86 Bug Fix: Snap pitch and tempo settings to 1
Pitch and tempo weren't snapping to exactly 1.0 before, as a result of
various things. This fixes that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:43:25 -08:00
Christopher Snowhill
462a509c85 Debugging: Implement buffer chain logging code
This optional code, disabled at compile time by default, allows finding
weird issues with the sample decoding chain.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:43:20 -08:00
Christopher Snowhill
c7a160a9e7 Improvement: Hopefully improve tag loading speed
Hopefully this works for most ASCII and UTF-8 tags, and continues to
work for weird tag encodings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:43:16 -08:00
Christopher Snowhill
2f9d7fe66d Bug Fix: Fix .gitignore file
Oops, the "build" folder reference was incorrect.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:43:11 -08:00
Christopher Snowhill
cc12da5507
CI: Bump OS and Xcode versions
Bump to macOS 15 and Xcode 16.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 18:52:52 -08:00
Christopher Snowhill
b889586708 Playback: Implement Selection Follows Playback
Option to make selection follow the playback, within the lag of the
output buffer, including if Always Stop After Current or Repeat One is
enabled. Allows easily queueing up a list of tracks in Always Stop mode,
then hitting the Play button again to play the next track. Enabled by
default, but always optional to disable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 18:37:31 -08:00
Christopher Snowhill
a8902b4ee6 Playback: Implement Always Stop After Current
A new menu option under the Control menu, disabled by default, which
stops playback after the current track completes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 18:34:58 -08:00
Christopher Snowhill
5e324927c5 Miscellaneous: File touched by Xcode
Xcode sure does love to tweak the numbers randomly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 18:34:51 -08:00
Christopher Snowhill
05e830c681 Bug Fix: Wait for output to shut down first
In case stop function called on another thread, wait for it to complete
first.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-01 15:18:26 -08:00
Christopher Snowhill
8c97f075cf Bug Fix: Remove observer cleanup
Apparently, this isn't needed, and on two users reporting crashes,
actually causes exceptions to be thrown somewhere.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-01 15:18:21 -08:00
Christopher Snowhill
83ba062010 Seeking: Restart output completely on track seek
This required some minor workarounds to deal with the play time counting
that works toward play count reporting.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-28 17:56:22 -08:00
Christopher Snowhill
10f8d0c2e1 Bug Fix: Attempt to solve a next track crash
This was attempting to retrieve the NSURL host object, possibly on a
file URL where the two did not match a previous check. Now we only pass
the full scheme/host/path check if both URLs are not file URLs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-28 16:34:26 -08:00
Christopher Snowhill
9fc7c99022 Optimization: Perform container checks in queue
Perform the file container checks in an operation queue, since those are
a major bottleneck at this point, too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 19:05:22 -08:00
Christopher Snowhill
00d861efc0 Bug Fix: Unregister observer correctly
Only unregister it if it was actually registered.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 14:39:21 -08:00
Christopher Snowhill
bfa9660437 Cleanup: Remove unused code
This is no longer needed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 14:31:58 -08:00
Christopher Snowhill
24888a669b Bug Fix: Correctly set audio volume on play start
Play start was missing this somehow, after a specific commit removed
some code.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 14:31:53 -08:00
Christopher Snowhill
b05f428cde Feature: Add fractional track length tooltips
Add fractional track length tooltips, for extra verbose info goodness.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:59:06 -08:00
Christopher Snowhill
d5ca037943 Bug Fix: Playlist Loader now correctly sorts items
Playlist Loader was sorting only the non-container tracks, and not the
final track list. Move the sort operation to the end of the processing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:59:01 -08:00
Christopher Snowhill
bf5e6d85a3 Cleanup: Remove unused variable
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:58:56 -08:00
Christopher Snowhill
2be457f395 Bug Fix: Rubber Band should now flush last chunk
There is a race condition with the next Node in the chain and the End of
Stream marker, considering how tiny the buffering is for these DSPs. Set
End of Stream instead after inserting the end of stream flush chunk.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:58:51 -08:00
Christopher Snowhill
955b90280f Bug Fix: Do not perform cascading reset on DSPs
DSPs should not be performing a cascading reset when resetting just
their own buffers, for example, on init or shutdown of just that one
DSP filter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:58:47 -08:00
Christopher Snowhill
1909b26671 Bug Fix: Clear counter correctly on reset
This reset was missing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:58:43 -08:00
Christopher Snowhill
fb5193ab62 FFMPEG: Optimize file reader access
Improve handling where FFmpeg may call the provided file reader with
AVSEEK_SIZE repeatedly, when file size is not likely to change between
repeated calls. This prevents repeated seek operations that would
otherwise be required to probe the file size each time.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 21:20:38 -08:00
Christopher Snowhill
6855449550 Sentry: Replace most of old logging, add traces
Add event traces to playlist loading and metadata processing queues.
Unfortunately, most of the old non-error events should not be logged,
because Sentry gets terribly spammy with captureMessage events. They
should only be used for error events, or other uncommon events which
do not already throw exceptions or NSError objects.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 20:29:04 -08:00
Christopher Snowhill
bde8f23ab1 Sentry: Enable profiling for issue debugging
Enable processor usage profiling for the app to gather information for
potential bottleneck tracking.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 02:37:17 -08:00
Christopher Snowhill
d47a96acaa Bug Fix: Fix CI building again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 01:23:49 -08:00
Christopher Snowhill
4a0cca22b8 Feature: Replaced Crashlytics with Sentry
Crash logging is now handled by the Sentry service.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 01:17:56 -08:00
Christopher Snowhill
30d9eeec2b Cleanup: Massive code cleanup and reorganization
Cleaned up project settings to current defaults, except for the macOS
deployment version, which is still 10.13. Cleaned up a lot of headers
and such to include with angle braces instead of double quotes. Enabled
build sandbox in a lot of places. Disabled subproject signing in several
places, for libraries and frameworks which will be stripped and signed
when they are copied into place in the final build.

Also, while trying to solve compilation issues, the visualization
controller was reverted to the Objective C implementation, which is
probably faster anyway. Stupid Swift/Objective-C language mixing issues.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 01:15:03 -08:00
Christopher Snowhill
9f6134a588 Bug Fix: Attempt to make seeking more performant
Seeking should clear the buffers completely now, and will be nearly
instant, depending on how fast the input can decode.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-25 18:12:30 -08:00
Christopher Snowhill
375b019972 Bug Fix: Solve outstanding Equalizer bugs
This includes setting and unsetting the equalizer DSP chain objects on
track change and advancing on track playback end, and also bugs with
applying equalizer presets to the band configuration items when the
equalizer is disabled or when playback is stopped.

Fixes #420

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-23 21:16:46 -08:00
Christopher Snowhill
fa34d469df Bug Fix: Greatly improve seeking operations
Seeking now mutes properly, and will not leave the audio muted across
other operations. Audio output changes should also mute and destroy the
buffers of the input chain, so that the audio resets properly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-23 19:59:13 -08:00
Christopher Snowhill
7c169f9ef1 Bug Fix: Stage seeking operation on main thread
This should not interact with the Audio Player object on a background
thread, but instead the main thread queue.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-23 19:59:06 -08:00
Christopher Snowhill
c02eabcf70 Bug Fix: Fix output volume from seeking
Fixes output volume setting on seek or audio output restart on format
change. Also safeguards these setters so they don't go off if the nodes
aren't actually allocated.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-23 18:58:55 -08:00
Christopher Snowhill
c45070138a Bug Fix: Playlist item out of range
This should never happen. But apparently it did.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-23 16:40:52 -08:00
Christopher Snowhill
c1665b6ee3 Crash Fix: Change background event to main thread
Two playback event items were set to queue a playback start to a
background thread, when playback should instead be queued on the main
thread. Fix this in a simple way.

This crash was easily reproducible by skipping through tracks rapidly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-22 04:39:54 -08:00
Christopher Snowhill
e3209e8901 Bug Fix: Play Count data may be missing tags
Sometimes the play count data only includes the filenames, and thus will
fail a query for just the tags. Also, a file query may be stored without
the subsong fragment tag, which will also break the tags.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-20 01:27:16 -08:00
Christopher Snowhill
ff9bd89389 Bug Fix: Clean up input node class definition
Fix some missing items, and add nullability declarations.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-19 15:09:21 -08:00
Christopher Snowhill
e49ba8e3e0 Bug Fix: Crash fix sorting by several fields
Play Count sorting was entirely missing, and sample rate and bits per
sample sorting caused exceptions due to the capitalization of the fields
versus the column identifiers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-19 15:09:16 -08:00
Christopher Snowhill
169c798bef Bug Fix: Hopefully fix pasting a list of paths
This should hopefully fix pasting from a list of file URLs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-19 15:09:10 -08:00
Christopher Snowhill
122757b42a TagLib: Fix framework Info.plist again
The TagLib framework build process leaves several
key fields empty. This breaks App Store submission.

Fix it again. Dang.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 19:19:30 -08:00
Christopher Snowhill
0f1be5939d VGMStream: Clean up FFmpeg code somewhat
Remove deprecated functions, make use of free functions that clear the
pointers before returning, etc.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 18:33:29 -08:00
Christopher Snowhill
212ef0ffdf FFmpeg: Clean up code somewhat
Remove deprecated functions, make use of free functions that clear the
pointers before returning, etc.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 18:33:25 -08:00
Christopher Snowhill
1c92c56d75 Bug Fix: Adding tracks to playlist while in search
When adding tracks to the playlist, clear the search filter first, so
the playlist doesn't become all jumbled, or so we don't overflow the
playlist indexes.

Also add some bug fixes for reversing the arranged to disarranged index
lists, so if an arranged index is past the end of the arranged list, as
is the case for appending, we shift the indexes forward past the end of
the diarranged object list.

Extra exception handling was added as well, so these things will only
cause a failure to add playlist items at worst, instead of crashing the
player entirely.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 16:06:16 -08:00
Christopher Snowhill
2e884b9e85 TagLib: Implement preliminary writer class
This is not currently being used anywhere, but may function eventually.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 04:25:36 -08:00
Christopher Snowhill
7e23e80b24 TagLib: Update metadata readers
Update the readers to support the newly added tag fields, and also read
the supported format list from the library itself.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 04:25:30 -08:00
Christopher Snowhill
65e2da2d9e TagLib: Implement new field support
Implement the new fields into TagLib, pending contribution to upstream.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-17 04:25:24 -08:00
Christopher Snowhill
023d7e2ba7 Bug Fix: Hopefully fix flickering visualizations
Now buffer twice as much audio as would be requested for a single
visualization PCM/FFT chunk, which should hopefully prevent it from
flickering due to running out of audio because of too low latency.

Now it buffers up to two chunks at the current hard coded visualization
sample rate, which works out to about 186 milliseconds.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-16 14:20:02 -08:00
Christopher Snowhill
44b8aa0dd8 Minor Bug Fix: Handle Rubber Band buffer latency
We implement this function to return the current latency buffered,
regardless of how often this function may be called. In practice, it is
only called on track completion, to time the reporting of the next track
display. We also avoid using Rubber Band's latency function, as in most
cases, this function will be called from other threads, and also, it
currently only gets called after Rubber Band has been emptied out, so it
would otherwise calculate zero samples buffered. And thirdly, Rubber
Band's latency function doesn't account for the buffered samples already
removed from it and waiting to be fed out.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-16 14:06:30 -08:00
Christopher Snowhill
f251c91f02 Bug Fix: Rubber Band handles end of track gap
The code was polling the input chunk duration after emptying out the
chunk's samples, which resulted in an input duration account sitting at
exactly zero, so the end overrun flush would not be cut short properly,
resulting in gaps between tracks.

Correct the input sum to tabulate before emptying the input chunk, so
output remains properly gapless.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-16 14:06:25 -08:00
Christopher Snowhill
dd5be9b117 Bug Fix: Set seek position when resuming paused
And a minor reoder of seek time reset code.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 21:46:46 -08:00
Christopher Snowhill
b8698ea125 Bug Fix: Rework playlist setup again
In case playlist setup is reset or not, move the reset above the menu
setup code, so the menu is set up correctly if a reset occurs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 20:23:42 -08:00
Christopher Snowhill
f239784bf2 Bug Fix: Change how bad playlist setup is handled
Reset to defaults if no columns are visible. Also log this situation in
Firebase events, in case it becomes relevant.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 20:10:09 -08:00
Christopher Snowhill
4face7d631 Audio: Unify playback setup of the converter
This code was being duplicated across three different playback functions
which basically did most of the same things.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 19:58:18 -08:00
Christopher Snowhill
04426d755c Bug Fix: Audio chain should do more error checking
Check all audio chain elements for allocation failures, and also dispose
of all of the previous handles in reverse order, including nulling the
final node handle so the output does not attempt to poll for audio while
the chain is being rebuilt.

Also set up output node to handle the new null finalNode state, and
return an empty chunk to the caller.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 19:58:12 -08:00
Christopher Snowhill
931da40b4d Bug Fix: Safeguard play count updates
Play Count cannot be updated for tracks which have been deleted before
the update was added to them. This was another cause of a rare crash.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 17:08:10 -08:00
Christopher Snowhill
0d289e47ab Bug Fix: Add safety checks to playlist columns
Playlist column setup needed a couple of safety checks to prevent
crashes from happening.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 14:09:35 -08:00
Christopher Snowhill
abf77a70d2 Bug Fix: Do not process format change on stop
We should not be processing a potential playback restart when the chain
is being torn down for shutdown.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-15 01:33:14 -08:00
Christopher Snowhill
923179c4eb Bug Fix: Significantly rework Rubber Band DSP
This should be perfectly safe to use in all situations now. It may have
been unstable due to mishandling return values, or not supporting
requesting more sample data from the library without feeding in more
input first.

Also, still signaling the End of Stream flag on chunk reading should be
correct, as downstream processors only react to it when the buffer runs
empty.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 19:43:24 -08:00
Christopher Snowhill
abd11ebc3b Opus: Boost priority of libopusfile decoder
This should take priority over the Core Audio and FFmpeg decoders.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 19:07:48 -08:00
Christopher Snowhill
632e53510c TagLib: Re-enable some file types
These may be handled by the Core Audio input, which does not read tags
on its own.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 19:04:14 -08:00
Christopher Snowhill
cdddcaecd8 Audio: Attempt to reduce glitching from seeking
Also applies to how output format changes are handled.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 18:51:36 -08:00
Christopher Snowhill
72a4a1c245 Bug Fix: Downmixer converter should update now
The Downmixer wasn't updating its output format correctly, so it was
prone to outputting the wrong format for a while, which could confuse
the output device and produce garbage output.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 18:51:31 -08:00
Christopher Snowhill
ab62de0fa2 Crash Fix: Fix HRTF resampler delay misuse
The delay value should be scaled by the resampling ratio, similar to
how it already is when allocating the impulse buffer. This went
undetected, as it scribbled over other memory without causing immediate
crashes, but instead later heap corruption.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-14 18:51:27 -08:00
Christopher Snowhill
75441bc5fa Audio: Fix more hangs and resume playback on start
Check for paused processing state in various places, so that startup
playback works properly, and resume playback at seek offset works
properly and doesn't hang the player.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 22:25:27 -08:00
Christopher Snowhill
ae4c49ea68 Rubber Band DSP: Fix error checking for output
The samples available function returns a signed integer, so it can
apparently return negative on error, and the DSP was incorrectly casting
this to an unsigned type, and thus attempting to buffer an inordinate
number of samples and crashing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 20:58:17 -08:00
Christopher Snowhill
146dae216a Visualization: Optimize Swift code handling arrays
This looks a lot better than some ruddy for-loops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 19:58:34 -08:00
Christopher Snowhill
6470b2627f Audio: Improve buffer signaling
This should stop the deadlocks which were occurring.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 19:58:30 -08:00
Christopher Snowhill
a40fcbca37 Downmix: Move downmix to DSP chain and fix a bug
The downmix filter also had a bug related to the channel configuration
used by the HRTF filter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 14:56:28 -08:00
Christopher Snowhill
aba5b8d120 Audio: Make chunk merging abortable
The merge function should be able to tell when the caller has no audio
left to process, such as on end of stream.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 13:51:55 -08:00
Christopher Snowhill
86ce3cf69b Equalizer: Fix to function properly
This was completely broken, oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 13:39:28 -08:00
Christopher Snowhill
fd8b20db86 Audio: Increase buffering before FreeSurround
FreeSurround needs more buffering from its input, so increase buffering
of previous node to 100ms.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 06:35:42 -08:00
Christopher Snowhill
a0e68df0e2 Audio: General fixes and improvements
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 06:35:38 -08:00
Christopher Snowhill
5b8363c9ec Bug Fixes: Fix monotonically increasing timestamps
Fixes timestamps in several cases where they were being processed
incorrectly, which was causing some chunked audio files to mis-report
timestamps into the past or the future, which caused the seekbar to jump
around in an unpredictable way.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 03:27:24 -08:00
Christopher Snowhill
d2284df683 FFmpeg Input: Fix stream timestamps
Stream timestamps were correctly being converted from the monotonically
increasing frame count, but the AudioChunk parameter was being set from
the frame count rather than the converted seconds count.

Fixes #418

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 03:27:11 -08:00
Christopher Snowhill
3ed6e8a6b9 Audio Node: Revert timedWait usage
Timed wait for 500us is kind of stupid and makes the threads wake up way
too much, and use way more CPU time. Reduce this, as the semaphores are
signaled appropriately, and the waiter should not wake up constantly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 02:23:52 -08:00
Christopher Snowhill
d9a08914df Visualization: Clean up Swift code a bit
Some of this is handled in simpler ways now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 02:09:17 -08:00
Christopher Snowhill
81b7dcfc0c Visualization: Reworked buffering system
Visualization now buffers in the audio output pipeline, and uses a
container system to delay multiple buffer chains from emitting
visualization data over top of each other. This should stabilize
display output significantly, while introducing minimal lag before
DSP configuration changes take effect.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 01:13:15 -08:00
Christopher Snowhill
9701bd5421 Visualization: Do not increment latency on write
The latency should not be incremented when writing sample data to the
buffer, but rather be posted by the output.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 01:13:10 -08:00
Christopher Snowhill
3cc97b5574 Audio: General cleanup and empty chunk checking
Upstream functions which return empty chunks on error do not return nil,
so the caller should check for an empty duration instead.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 01:13:06 -08:00
Christopher Snowhill
a78933ca80 Audio Chunk: Add interface to copy chunk
This is needed if audio is to be removed from the chunk without altering
the original chunk.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 01:13:03 -08:00
Christopher Snowhill
8790df1ef0 HRTF DSP: Add gain correction to impulse resampler
Impulses should be gain scaled roughly based on the sample ratio
relative to the original impulses. Lower target sample rate means less
impulses means gain goes up, higher target sample rate means more
impulses so gain goes down. Somewhat simple, seems to work.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 21:08:33 -08:00
Christopher Snowhill
9346a4edbd Audio: No longer force output sample rate
We were forcing a resampling ratio to match the HRTF filter supplied
with the app, now we resample the HRTF to match the input audio, which
will be resampled to match the output device settings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 20:59:41 -08:00
Christopher Snowhill
b39882168b HRTF DSP: Support resampling impulses
This prepares the filter to be the same as the rest of the filters, in
that they support flexible sample rates to match the output device.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 20:57:22 -08:00
Christopher Snowhill
d17388ee95 Rubber Band DSP: Make it possible to disable it
And disable it by default in new installations, otherwise leave the
setting alone. The disablement setting is shared with the engine
setting, so the default should not really change anything, except for
new installs.

Also, the time/pitch shifting dialog disables itself and displays an
obvious notice button, which opens the Rubber Band settings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 20:13:53 -08:00
Christopher Snowhill
b913d423a2 Crashlytics: Add consent preferences defaults
These should have been defined already, but now they're the safe
defaults that should spring the dialog on startup, and doesn't grant
consent by default.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 20:11:22 -08:00
Christopher Snowhill
7543020291 Cleanup: Whitespace removal
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 19:01:29 -08:00
Christopher Snowhill
9fd393b64e Audio Output: Set higher priority on output thread
It's more like the output monitor thread, since it only monitors output,
rather than actually handing the output callbacks.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 19:01:25 -08:00
Christopher Snowhill
a82742e689 Audio Processing: Unify sample block merging code
Sample block merging code should not be duplicated across the DSPs that
require it, but instead should be a common function. Also added some
optimizations to the Float32 converter function, to bypass conversion if
the audio format needs no conversion.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 19:01:20 -08:00
Christopher Snowhill
2364a7d469 Rubber Band DSP: Process larger blocks at a time
Attempt to completely fill the input buffer of the Rubber Band library
between each call to the process function, instead of processing in
as small an increment as the source node provides. May reduce processing
power required.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 14:56:44 -08:00
Christopher Snowhill
7994929a80 Audio: Add full timestamp accounting to playback
Audio Chunks now have full timestamp accounting, including DSP playback
speed ratio for the one DSP that can change play ratio, Rubber Band.
Inputs which support looping and actually reporting the absolute play
position now do so.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 14:08:43 -08:00
Christopher Snowhill
1e1ee2fbe2 Bug Fix: Fix resume playback on startup
In case multiple playlist entries are left marked as "current" in the
playlist database, resume playback on the first one, and unmark all the
rest of them.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 23:04:01 -08:00
Christopher Snowhill
b4c8c11218 DSP: Add format change checking to FreeSurround
FreeSurround, like the Equalizer, which attempt to coalesce Audio Chunks
into larger blocks of 4096 samples, must check if the audio format has
changed between blocks, and stop stacking chunks together when a new
format is detected. They will continue processing with less sample data
than expected, as necessary.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 23:02:55 -08:00
Christopher Snowhill
26efcda71a DSP: Move Equalizer processor to DSP node chain
The last of the built-in processors is now in the threaded processing
chain, and all DSPs are marked high priority and with short buffers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 23:01:13 -08:00
Christopher Snowhill
dc0a44067a DSP: Move HRTF filter to DSP class chain
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 21:17:58 -08:00
Christopher Snowhill
7179abe8ef DSP: Move FreeSurround to DSP chain
This will no longer be in the output implementation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 19:43:54 -08:00
Christopher Snowhill
724144accd DSP: Move Rubber Band to its own DSP group
This is a project file structure change only, no code changes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 19:42:27 -08:00
Christopher Snowhill
0e608481d9 Output: Remove pointless scale value
This shouldn't have been applied, the problem with Rubber Band was the
flushing mechanism.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:51 -08:00
Christopher Snowhill
98bac743df Audio: Adjust node buffering behavior a bit
Change one remaining semaphore wait to 500us, and change the buffering
so that it can always overflow the requested duration by one chunk, so
that at least one chunk will always fit in the buffer. This also allows
the DSP nodes to flush at the end of the stream without losing their
output.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:46 -08:00
Christopher Snowhill
cddfc3d1db Rubber Band: Handle end of stream flushing better
The end of stream flushing should only request remaining samples once,
as should the rest of the process. The problem with the Rubber Band code
in this case is that it will wrap the remaining samples pointer after it
has been flushed, and emit a really huge number.

Also, add code to try to equalize the samples output with the samples
input, relative to the tempo stretching, as Rubber Band seems to flush
entirely too much data at end of stream, which can create noticeable
gaps in the output. This solves that as well.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:41 -08:00
Christopher Snowhill
cb8d873b5b Rubberband DSP: Guard non-restart config function
This should be guarded, so that no other thread tries to free the DSP
while it is potentially writing to the Rubber Band instance.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:35 -08:00
Christopher Snowhill
9b3487b6e0 DSP: Stylistic change
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:30 -08:00
Christopher Snowhill
4890ee67a6 DSP: Whitespace changes
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 18:12:26 -08:00
Christopher Snowhill
9e82e2737e Cleanup: Remove stale comment from source code
This comment was copied by accident when duplicating the original
Converter Node class for the new DSP base.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 15:10:42 -08:00
Christopher Snowhill
7d803a0211 DSP: Add thread priority control
DSP threads, such as the Rubber Band processing, and planned moves of
other processing to buffer threads, such as the Equalizer, FreeSurround,
HRTF, and Downmixing for output, because they all have small output
buffers. Since these buffers drain and fill fast, they should be
processed at a high priority. Hopefully, App Store doesn't complain
about the use of these APIs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 15:10:38 -08:00
Christopher Snowhill
d6923781ef Preferences: Fix merge error
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 13:57:19 -08:00
d8d3265016 Changed term in Spanish translation
Yes, it's mostly used untranslated now...
2025-02-11 09:48:32 -03:00
Christopher Snowhill
7fc6361d6c Rubber Band: Fix preferences disabling items
The items not applicable to Finer / R3 engine were not being disabled
properly. Change the dialog to use a transformer to disable them on the
preferences value instead of coding it, since the code didn't seem to
work.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 01:28:42 -08:00
Christopher Snowhill
266da8cc07 Rubber Band: Move default preferences
Move them to the main app instead of an external module.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 01:27:28 -08:00
Christopher Snowhill
227ed0dfa3 Rubber Band: Move everything to a DSP class
This class can more flexibly process and emit varying chunk sizes than
the previous code could, solving the problem of wide tempo changes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 01:25:26 -08:00
48d89d4496 Spanish translation for Rubber Band settings, and
for rubber band icon credits.
2025-02-10 20:07:21 -03:00
Christopher Snowhill
6f6773da09 Fix up team identifiers again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:49:00 -08:00
Christopher Snowhill
ad074ec13e Rubber Band: Implement configuration dialog
Now there's a configuration dialog for tweaking the settings
in semi-real time. Everything that can be changed without
restarting is changed without restarting, otherwise the audio
pipeline is reset, which happens quickly enough anyway.

Awaiting translation to Spanish, other languages have been
removed pending their maintainers fixing most of their
problems, which includes me being lazy and AI translating
bits so I could rush updates.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:37:07 -08:00
Christopher Snowhill
e4b70f53ae Core Audio: Fix API header
Fix a function declaration that was missing its
parameter variable in the header.
2025-02-10 14:33:28 -08:00
Christopher Snowhill
5392a5fa91 Revert "Visualization: Tweak systems a bit"
This reverts commit 6c24ad8244.
2025-02-10 14:31:22 -08:00
Christopher Snowhill
591f3ceb18 Updated VGMStream to r1980-95-g551c0787
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:30:28 -08:00
Christopher Snowhill
6e53cebf5c Remove unmaintained translations
The Polish, Russian, and Turkish translations have
no active maintainers, so I was stupidly relying on
AI translation to fill in the newer things. These
translations will sit mostly idle until I get active
maintainers for them.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:12:42 -08:00
Christopher Snowhill
0b98c0ac98 Placeholder commit for Sparkle update
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-06 20:59:59 -08:00
Christopher Snowhill
6c24ad8244 Visualization: Tweak systems a bit
This should improve performance slightly. It's
still recommended to switch off SceneKit to
save CPU usage, or switch of vis entirely.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-01 15:08:56 -08:00
Christopher Snowhill
170fb06a62 Updated credits file and Patron list
Been a while since I did this. Oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-01 15:08:50 -08:00
Christopher Snowhill
f4cfad370d TagLib: Fix up project file
Gah, how in heck did Xcode end up inserting an absolute
path? Fix that, and some other leftovers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 02:18:33 -08:00
Christopher Snowhill
dc81605343 TagLib: Fix framework Info.plist
The TagLib framework build process leaves several
key fields empty. This breaks App Store submission.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 01:45:41 -08:00
Christopher Snowhill
994d0e328e SID: Add exception handling
Exception handling was quite missing from this code
as well. Let's fix that too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 01:17:56 -08:00
Christopher Snowhill
ffa46ae2d0 OpenMPT: Update exception handling
Make exception handling more robust and thorough. Never
know what may happen, make sure to handle most cases.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 01:17:49 -08:00
Christopher Snowhill
cd95cd68f8 MIDI: Add exception handling
Both the midi_processing and the various players may
throw exceptions, so we should check for these too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 01:17:44 -08:00
Christopher Snowhill
d835c252fa Highly Complete: Add exception handling for NCSF
SSEQPlayer throws exceptions, there should be exception
handling to catch them and fail gracefully.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-30 01:17:39 -08:00
Christopher Snowhill
7777e50f03 TagLib: Add exception handling
The TagLib C++ code was missing generic try/catch handling
which could result in any generic errors throwing straight
to a full crash. Add exception handling and logging, which
will fix a logged crash regardless of whether the tags are
read correctly or not by the newer TagLib version.

Fixes #415

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-29 23:57:36 -08:00
Christopher Snowhill
b0414f4399 TagLib: Replace bundled copy with upstream 2.0.2
Include a Framework build, unmodified, RelWithDbgInfo.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-29 23:42:31 -08:00
Christopher Snowhill
a873441484 Updated VGMStream to r1980-54-g35c8283f
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-26 01:17:55 -08:00
Christopher Snowhill
dd4a0b7af3 Playlist View: Save column settings differently
You will need to reset your settings after this, but then it should
stay put for the indefinite future.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-26 01:17:47 -08:00
cfa4e078ea
Update bug_report.md 2025-01-20 19:18:40 -03:00
Christopher Snowhill
b0368a7010 Visualization: Fix race condition on first launch crashing
Due to a race condition with the visualization control racing with
the Crashlytics consent dialog, it was possible that the repaint
function would be called before the control was fully initialized,
which would cause the visualization drawing code to crash due to
division by zero error.

The fix is two-fold: First guards were added to the borrowed
code so that the draw functions won't run if they would later
divide by zero on an uninitialized width property. Secondly, the
top level visualization windows added a startup variable guard
so their drawing code will return immediately if setup has not
completed yet.

Note that this bug was only just noticed in a recent App Store
submission, but was unrelated to the recent commits to the code
base, and could have triggered much earlier in the development
cycle. Strangely, it did not.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-15 17:43:03 -08:00
Christopher Snowhill
458d0a3824 Playlist: Added play count column
It should also be possible to sort by the column, ascending or
descending. This also necessitated adding playlist row refreshing
for play count updates.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-14 23:38:22 -08:00
Christopher Snowhill
838d5ca9f7 Highly Complete: Fix crash on paths containing URL escapes
In the event of local paths containing not just UTF-8 characters,
but also un-decoded URL percent sequences, which will end up double
encoded in the player, code which reverses percent encoding should
later re-apply it.

Apparently, this whole time, since the last code overhaul, the
URL encoding was being stripped, then the file opener was converting
these paths back into URLs without re-encoding, which didn't break
until someone played an album in a folder containing a partially
decoded UTF-8 sequence. Thanks, Zophar's Domain, and whoever ripped
the Golden Sun GSF set for finding this bug!

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-14 21:25:52 -08:00
Christopher Snowhill
7e98a0398b Updated libOpenMPT to version 0.7.13
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-10 14:38:44 -08:00
Christopher Snowhill
e400fafa5d Dependencies: Updated mpg123 to 1.32.10
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-10 14:37:59 -08:00
Christopher Snowhill
8bc2d3cd38 Audio/HRTF: Make head tracking optional, add reset button
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 15:24:02 -08:00
Christopher Snowhill
68c334d96e Updated VGMStream to r1980-0-ged9a7202
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 02:31:11 -08:00
Christopher Snowhill
9f07f04ed6 Audio/extrapolator: Fix short prime length
When the input buffer has less samples in it than the LPC order,
it would crash reaching past the ends of the buffer. Now, it will
pad past the correct end of the audio with silence, while still
extrapolating a prime input minimum of the LPC order. Should fix
the last of the outstanding crashes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 02:18:35 -08:00
Christopher Snowhill
0707c4bb71 Notification: Fix album date assignment
The NSCalendar assignment should have a placeholder month and day
of January 1st, instead of the invalid month/day of 0/0. Also,
even if this somehow fails, don't attempt to assign it if it
returns nil.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 01:28:07 -08:00
Christopher Snowhill
75ef35efa6 FileTree: Fix handling of metadata with multiple values
Goody, my metadata formatting comes back to bite me.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 01:19:24 -08:00
Christopher Snowhill
d89c9c852f Chore: Update copyright dates somewhat
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-01 01:31:10 -08:00
Christopher Snowhill
eb72b73e6c FFmpeg: Move some error buffers around
Since in one case, it probably wasn't combining them into one stack
allocation, it probably blew up the stack allocation quite a bit.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-09 18:04:56 -08:00
Christopher Snowhill
9c6915ecb2 Implemented real pitch and time shifting using Rubber Band
I will implement the more complex setup of providing options for
most of the configuration that Rubber Band provides, at a later
date, when I feel like creating a complex configuration dialog
for it, and asking for help translating every option and setting.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-09 18:04:34 -08:00
Christopher Snowhill
59f3f416ba Updated VGMStream to r1951-102-gf1483e22
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-04 23:14:53 -08:00
Christopher Snowhill
7e3112efc0 Updated libOpenMPT to version 0.7.12
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-04 23:14:47 -08:00
Christopher Snowhill
afe65df6c6 Minor corrections to Polish translation
mpan told me these were broken, oops. One of them
may as well not be translated until it gets a proper
translation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-11-24 21:30:28 -08:00
Christopher Snowhill
f5be598311 Add support for custom Dock icons while running
The emoji labeled buttons will convert and save their respective
state icon to the settings folder, and refresh the current icon
as necessary.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-11-24 21:30:22 -08:00
Christopher Snowhill
aa491fe0bb Updated VGMStream to r1951-100-g73ef7c6c
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-11-24 15:09:07 -08:00
Christopher Snowhill
de43b1b226 Fix FreeSurround being broken by the speed control
It should be deriving its channel count from the file format,
since it's applied before any other filters.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-11-24 15:08:44 -08:00
Christopher Snowhill
db5b4e68c2 Updated libOpenMPT to version 0.7.11
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-10-30 23:58:46 -07:00
Christopher Snowhill
820a1dedfb Updated VGMStream to r1951-88-g02d3c3f8
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-10-30 23:58:13 -07:00
Christopher Snowhill
ef8dd958ae Reorder project file entries with a sort 2024-09-20 22:25:23 -07:00
Christopher Snowhill
eba9dc2c43 Remove unused variable 2024-09-20 22:25:17 -07:00
Christopher Snowhill
6a309a3075 Speed Control: Implement simple speed control
Implements a simple speed control using a resampler
designed for real time changes. A rubberband speed
control will be implemented at a later date.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-09-20 22:25:12 -07:00
Christopher Snowhill
2e5140a321 Visualization: Make latency animation smoother
Compensate for latency by incrementing an offset
according to animation frame rate.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-09-20 22:25:02 -07:00
Christopher Snowhill
edbdc8d98e Update Github workflow
Update checkout and upload artifacts to latest versions
2024-09-17 02:42:04 -07:00
Christopher Snowhill
a3e6636731 VGMStream: Fix fade time configuration
Fade time was reading from some completely wrong
configuration field name, so fades weren't working.
2024-09-17 02:26:07 -07:00
Christopher Snowhill
389f56e392 VGMStream: Change render API, allocate off heap
Change to the future render api, hopefully float support
will be available eventually. Also change to allocate the
sample buffers from the heap instead of the stack.
2024-09-17 02:26:01 -07:00
Christopher Snowhill
de24bd4560 Updated VGMStream to r1951-50-g1d836a36 2024-09-17 02:25:20 -07:00
Christopher Snowhill
fcddcdce61 Updated VGMStream to r1951-0-g4b2dc01c
Updated to add one new extension, too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-23 23:07:13 -07:00
Christopher Snowhill
ba91d2e5ba Downgrade SwiftPM pins format version number
Apparently Github's old ass version of Xcode can't handle a 3 instead
of a 2 here. Whatever.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-18 04:13:10 -07:00
Christopher Snowhill
b997a6d5dc Downgrade Firebase to version 10.x
Version 11.0 requires macOS 10.15, and I haven't raised the minimum
deployment version that far yet.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 15:36:31 -07:00
Christopher Snowhill
63264de462 Update Firebase SDK to version 11.0.0 minimum
This should fix the Framework packaging issues App Store noticed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 15:31:20 -07:00
Christopher Snowhill
256c99badd Update Copyright year to 2024 manually
Including extending existing starting-2023 dates to ranges.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 05:08:02 -07:00
Christopher Snowhill
d3415a3a25 Updated libOpenMPT to version 0.7.9
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:34:02 -07:00
Christopher Snowhill
03b52685bd Update PluginController.mm
Add missing definitions to the Info.plist template generator.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:23:29 -07:00
Christopher Snowhill
4d11ba266e Update Info.plist.template
Clean up duplicate definitions.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:23:22 -07:00
Christopher Snowhill
6dd01e07c1 Updated VGMStream to r1917-201-g7ab622d3
Also updated filename extensions,  and the interface plugins.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:23:08 -07:00
Christopher Snowhill
b701a4d347 Placeholder commit for Sparkle update 2024-08-07 23:17:22 -07:00
Christopher Snowhill
9f0ef52fd4 Update FFmpeg to version 7.0 and rebuilt soxr
Rebuilt libsoxr, which now removes the dependency on libavutil.
2024-08-07 23:17:04 -07:00
Christopher Snowhill
42ea824972
Fix crash on unaligned volume scale
Volume scaling would potentially crash when handling
unaligned blocks of samples, and also handled them
completely wrong. It should be counting up single
samples until the buffer is aligned to a multiple of 16
bytes, and it should not exceed the intended count.

BUG: It was not only counting the unaligned samples
backwards, it was ignoring the real sample count.

Fixes #380

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-11 20:22:42 -07:00
Christopher Snowhill
1c95771ed0
Hopefully fix memory usage during playback
Shuffle around @autoreleasepool blocks, and also add one
to the audio processing code in the playback callback, so
audio memory is released during playback instead of
accumulating.

Fixes #379

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-11 20:22:39 -07:00
Christopher Snowhill
bc330e75f6
Processing: Fix missing converter setup function
Oops, I was missing a function necessary for output format changes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-04 16:07:33 -07:00
Christopher Snowhill
87684a7974
CI: Hopefully fix this time
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-04 02:10:34 -07:00
Christopher Snowhill
5e27f084e9
Add a cache shutdown guard
This appears to maybe be necessary as the prior join call doesn't seem to
be doing what it should.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 22:59:38 -07:00
Christopher Snowhill
8997ffa030
Update libFLAC to version 1.4.3
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 22:59:34 -07:00
Christopher Snowhill
2fc42ccb73
CI: Switch to Xcode 15
Apparently, the build images are broken or something.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 22:02:52 -07:00
Christopher Snowhill
1eee2fbf11
CI: Switch to latest macOS and latest stable Xcode
Switch the CI image to the latest stable OS version, and latest stable
Xcode version, selected by another action.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 21:29:59 -07:00
Christopher Snowhill
6b2964328a
Remove redundant track end checker
This is checked inside the audio thread, it isn't needed in the watcher
thread. Remove the second check.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 21:29:54 -07:00
Christopher Snowhill
1f497ee8bb
Added Usage Description translations
Spanish translation provided by Kevin López Brante.
Polish, Russian, and Turkish translations of specifically these messages
provided by GPT-4, to be replaced by human translations if our
translators offer them, but it's been a while since I've gotten an update
out and I didn't really want to wait so long.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 20:35:50 -07:00
Christopher Snowhill
73252a8928
Reduce audio buffering slightly again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 19:46:30 -07:00
Christopher Snowhill
b07a6fd098
Visualization: Improve latency and buffering appearance
Adjust the buffering so if latency is too low, we fill the rest of
the output with silence instead of peeking at the oldest part
of the buffer. Also increase latency by half a buffer size so
that the requested sample is in the center of the buffer, which
improves the 4096 sample situation with the current low
latency output.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 19:35:20 -07:00
Christopher Snowhill
2102fc1c44
Visualization: Reset buffer on playback stop
Reset the visualization system when stopping playback.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 19:35:16 -07:00
Christopher Snowhill
09f7496d9a
Initial implementation of positional audio for macOS Sonoma
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:01:07 -07:00
Christopher Snowhill
4ced731194
Replace hard coded Pi constant with M_PI
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:01:03 -07:00
Christopher Snowhill
8c67b6c2f7
Significantly improve memory usage of loading tags
This especially helps with bad or brutal files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:59 -07:00
Christopher Snowhill
d88a90e5b0
Fix a typo
Notifcation -> Notification

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:55 -07:00
Christopher Snowhill
122b6d6a6d
Improve audio buffering situation
Buffer up to 20 seconds per stage, and buffer only up
to 2 seconds before starting the next stage.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:51 -07:00
Christopher Snowhill
fb72db74f8
Stop visualizer feed for stopped playback
A stopped instance of OutputCoreAudio should not continue to feed the
visualization system with stale audio, potentially while another instance
is already starting up and feeding its own audio output.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:47 -07:00
Christopher Snowhill
2987857b93
Fix a missing do on a do-while block
This should be looping on the condition, not sure
how the compiler missed this one.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:43 -07:00
Christopher Snowhill
df198110ed
Hopefully fix format change on end of track
This should keep the audio pipeline flowing either way.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 05:00:38 -07:00
Christopher Snowhill
4ddca15a8f
Simplify HRTF filter, change option to reflect it
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:57:11 -07:00
Christopher Snowhill
919661c9e8
Attempt to stabilize visualization flutter
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:57:06 -07:00
Christopher Snowhill
cfd5b1c6fb
Fix converter after output switchover
This was missing, too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:57:01 -07:00
Christopher Snowhill
a2e4fa17b2
Fix further bugs with output switchover
Change some things I missed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:56:56 -07:00
Christopher Snowhill
e59c7be0b8
Update BASS and friends for Xcode 15
This is needed for the Intel build target to work.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:56:44 -07:00
Christopher Snowhill
9e96bb2b75
Add missing Info.plist strings
These strings were added to the InfoPlist translations in a
previous commit, but likely need to be added to the
Info.plist file directly as well.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:56:39 -07:00
Christopher Snowhill
7ab2a8305a
Revert to previous low latency output system
This reverts usage of the AVFoundation output to use
the previous lower latency CoreAudio output, and
paves the way for a change I am cooking up soon.

Fixes several issues with playback and seeking latency.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:56:33 -07:00
Christopher Snowhill
5e36affad8
Hopefully fix crashes from rapidly skipping files
Do this by serializing the background thread actions against
the AudioPlayer object, so we don't start playback multiple
times at once.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:56:28 -07:00
Christopher Snowhill
e85b5eea34
Privacy - Added Spanish translation
Translation provided by team member, Kevin López Brante.
2023-09-03 16:51:50 -07:00
Christopher Snowhill
f3d218fd3d
Privacy: Added purpose strings
The other translations currently have placeholders, awaiting translation.
I may end up using machine translation from Deepl just for Russian,
Polish, and Turkish, until I get human translations from a contributor.
Spanish will be provided by our team later tonight.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-03 16:39:04 -07:00
Christopher Snowhill
f54dd1c7b1
Playback: Start playback and seek in the background
Perform playback start and seeking operations in the background, instead
of on the main thread, which should help prevent them from stalling the
user interface.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 22:28:05 -07:00
Christopher Snowhill
d4196a5595
Updated Translations
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:42:05 -07:00
Christopher Snowhill
09c9711b50
Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:42:00 -07:00
Christopher Snowhill
40b5bf7e13
Updated VGMStream to r1866-46-g883d796d
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:56 -07:00
Christopher Snowhill
fb208b4cd5
MainMenu: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:50 -07:00
Christopher Snowhill
57158da628
AppleScript: Implemented Composer field
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:46 -07:00
Christopher Snowhill
a01d9ee5b3
Info Inspector: Implemented Composer field
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:42 -07:00
Christopher Snowhill
9593902aff
Playlist: Implemented Composer column
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:37 -07:00
Christopher Snowhill
a3c8149612
Tags: Expose Composer tag through interfaces
Implement the composer field interface to playlist item tag
reading and writing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:33 -07:00
Christopher Snowhill
1bffcaa429
TagLib: Remove stray whitespace
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:29 -07:00
Christopher Snowhill
d62d5f1279
TagLib: Implement Composer tag support
Most of the reading was already there, it just didn't expose
it to the player.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:24 -07:00
Christopher Snowhill
11dcac6b04
ASF/WMA: Implement Album Artist tags (oops)
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:41:20 -07:00
Christopher Snowhill
5b9e5be295
Add a bitrate column to the playlist
Adds a bitrate column to the playlist view, which is fully sortable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:48:22 -07:00
Christopher Snowhill
0f6b403b43
Fix rating sorting
This apparently fixes sorting by rating.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:48:17 -07:00
Christopher Snowhill
03e510fa92
Add missing column header translations
These column header names were missing from the strings
tables. Add them based on existing translations of the same
field names by our translators.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:48:12 -07:00
Christopher Snowhill
1dbaa61313
Touched by Xcode
Miscellaneous XIB changes automatically applied by Xcode
when editing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:48:07 -07:00
Christopher Snowhill
bfe2f03fbb
Fix sorting for missing columns
This should fix the sorting for rating, sample rate and bits per sample

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:48:02 -07:00
Christopher Snowhill
702a52a93e
Attempt to make project build with Xcode 15
This is supposed to fix running on macOS 10.13, but it doesn't.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:47:58 -07:00
Christopher Snowhill
eae6c96b5e
Reduce inter-thread buffering a bit
This isn't needed so much now that the output buffers more.

Should reduce the problems of #370

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 01:37:37 -07:00
Christopher Snowhill
fc2d96831d
Don't pause streams, stop instead
Do not bother to pause streamed files, instead stop playback when pause
is requested.

Fixes #372

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 01:37:33 -07:00
Christopher Snowhill
587ccd6a0d
Fix Repeat menu being disabled, change default
Default repeat mode should now be Repeat All, and the menu items should
now all function, fixed by removing a bunch of pointless attributes from
each affected menu item.

Fixes #371

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 01:37:29 -07:00
Christopher Snowhill
a4ff143600
Touched by Xcode
Resource scripts touched by Xcode again

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 01:37:24 -07:00
Christopher Snowhill
a49456f44b
Stash a Core Audio output, kind of glitchy
This output may prove to have lower latency, but the results are too
glitchy to really be usable. Not even visualization latency is handled
correctly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 01:37:19 -07:00
Christopher Snowhill
f642a066f8
MIDI: Properly bundle the BASS Musepack plugin
This is imported by the player, though it doesn't fail when it's
missing, so that would explain why I didn't spot this error sooner.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 02:41:26 -07:00
Christopher Snowhill
fa1b632a59
MIDI: Stop linking BASS plugins directly
They'll be imported by the plugin on startup anyway, and they don't
really have any exported functions we can use.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 02:41:21 -07:00
Christopher Snowhill
d3ca6c390c
Disable dead code stripping
No idea why this was enabled, no idea if I should disable it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 02:41:14 -07:00
Christopher Snowhill
99578a333e
APL+CUE: Revert pointless change
This change served no purpose, other than to confuse me. I must need
some sleep already.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:25:11 -07:00
Christopher Snowhill
c3070e5f39
APL: Stop on the correct sample
This should stop before the specified end point, not one sample later.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:20:04 -07:00
Christopher Snowhill
dd7340d6d5
APL: Fix position handling for DSD
Correctly scale the AudioChunk frame counts. This more and more makes me
think I should be scaling this in the AudioChunk code instead, but then
code may not know about the special case of every 8 frames only being
one byte per channel.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:53 -07:00
Christopher Snowhill
e7d6ab88ce
APL: Round bits per sample to an even byte
Round this variable up.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:49 -07:00
Christopher Snowhill
16072b3af3
CUE: Fix playback position tracking for DSD
DSD wasn't tracking the correct sample count, because DSD
Audio Chunks store the byte count, rather than the bit count.
This may be changed in the future, so I'll have to remember.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:44 -07:00
Christopher Snowhill
440ba24c57
CUE: Stop on the correct sample
Stop before the end, rather than on it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:40 -07:00
Christopher Snowhill
37fc054739
CUE: Round up bits per sample
Round bits per sample to an even byte.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:36 -07:00
Christopher Snowhill
8ad84c3aed
Add status messages to updater script
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:18:31 -07:00
Christopher Snowhill
2d7a7480d9
Add an option to control halving DSD volume level
And default it to disabled. As was pointed out to me by a user, DSD is
apparently mastered to a level of -6 dB, so double its level on output
by default.

Also reorder all preferences dialog controls so they are instantiated in
display order, which should help screen readers, maybe.

Fixes #368

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-15 16:46:39 -07:00
Christopher Snowhill
09aa0d96e1
Work around rounding error with resampler flush
Resampler flush may indefinitely produce 1 sample if there is a rounding
error with the buffering calculations. Work around this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-14 05:16:34 -07:00
Christopher Snowhill
3d24168ba7
Fix clipped sample rate changing between files
When the clipped sample rate changes, the resampler needs to be
restarted. This was previously failing because the target sample rate
wasn't changing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-14 05:16:30 -07:00
Christopher Snowhill
323a554832
Fix lossless capability reporting for partial read
When reading partial chunks, and when returning partial data, it is
essential to maintain this lossless chunk status across either whole or
partial chunk reads. Otherwise, the converter chain sees the lossless
flag constantly changing on lossless files, such as PCM or DSD, and
causes the DSD decimator and/or resampler to be torn down and reset
repeatedly, causing glitches in the audio.

The glitch was not, in fact, with the decimator itself, and was
occurring to a degree without it, as it would be restarting the
resampler repeatedly as well.

Fixes #367

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-14 04:14:14 -07:00
Christopher Snowhill
ffbc571660
Correct the decimator sample latency
The latency is half of the FIFO, or half the filter size, and each byte
is 8 samples, so return the value accordingly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-14 04:11:04 -07:00
Christopher Snowhill
efd1349a59
Add an explanatory comment that got lost
This comment was in the original sample decimator code, I neglected to
include it in my port over to Cog. Doesn't really serve any functional
change, though. It would have clarified that I needed to reduce the gain
level much sooner, though.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-14 04:10:10 -07:00
Christopher Snowhill
728ae813c5
Updated libOpenMPT to version 0.7.2
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:32:01 -07:00
Christopher Snowhill
0be8dbe7c5
Updated VGMStream to r1843-92-g740a4048
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:31:49 -07:00
Christopher Snowhill
270cf79bb7
Change default open to enqueue and play
Instead of clear playlist and play. This was confusing people,
apparently. I should have changed it sooner, since this is what
I use normally anyway.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:31:34 -07:00
Christopher Snowhill
0efc3d4ad7
Add fade global shortcut
Defaulting to ctrl-cmd-O, though I may change this if
someone has any better ideas for a default.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:31:10 -07:00
Christopher Snowhill
0fb28001f9
Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:30:48 -07:00
Christopher Snowhill
207fee1a50
Change "Spam" to "Copy now playing"
Change the not-obviously-named shortcut to something more
obviously named after its purpose.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:30:03 -07:00
Christopher Snowhill
a366cbadee
Remove a saucy option
Nobody was likely to be using it, much less even know what it was.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-13 22:42:13 -07:00
Christopher Snowhill
3c91aab9d8
Updated BASS and company
Also included missing BASS_MPC, which was still being imported
even though it wasn't actually included.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-12 17:29:01 -07:00
Christopher Snowhill
3689fb51f8 Update MASShortcut for Xcode 15
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:40:44 -07:00
Christopher Snowhill
e1e5c22b8a Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:40:32 -07:00
Christopher Snowhill
cb0ae6db6c
Organya: Fix deployment target for 10.13
Oops, it was somehow still set to 13.0, from when I created it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:15:41 -07:00
Christopher Snowhill
39459b89cb
Update projects and source in prep for Xcode 15
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:14:45 -07:00
Christopher Snowhill
5aaea62b54
About Dialog: Move logo to asset catalog
Also convert it to a supported HEIF format, making it much smaller.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:14:38 -07:00
Shoh Sewell
baf9797907 Volume slider changes
Makes volume slider logarithmic when limited to 100% to allow easier changing of volume towards the bottom of the slider.
The tooltip remains as the slider location instead of the logarithmic value of the actual volume.
2023-06-08 02:10:15 -07:00
Christopher Snowhill
dd630ba394
File Tree: Reduce monitoring to limited changes
Only track new, removed, or renamed files. Tracking Xattr changes was
apparently causing the tracker dialog to crash on things like the user
changing file type associations, which either added or removed a per-
file association xattr, or when clicking Change All, removed this xattr
from all associated files tracked by Spotlight.

Fixes #361

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-04 01:37:46 -07:00
Christopher Snowhill
a640627fe1
Playlist: Add a workaround for AppleStript URLs
AppleScript is apparently such a legacy system, that when it sends URLs
to your app to open, they're in the old Carbon format. So we need to
translate these to proper URL strings for the rest of the app to deal
with them at all.

The format of these URLs is as follows:

/method/::

Followed optionally by:

username/password@

Where the slash and password are optional.

Followed by:

hostname

Followed optionally by:

/portnumber

And finally, followed by:

:path:on:server:filename.ext

So, in hostname field, we must swap slashes to colons. And in the path
field, swap colons to slashes. What a bizarre world.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-01 21:34:34 -07:00
Christopher Snowhill
5550af7e64
HTTP Input: Do not hang if transfer completes
If transfer completes quickly, do not hang waiting for it to achieve
reading state. Also add some comments indicating what we're doing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-01 21:34:29 -07:00
Christopher Snowhill
e0a1cf49e7
Metadata: Fixes metadata reading
metadataBlob may be null, so create dictionary in that case.

Fixes #360

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-01 15:15:36 -07:00
Christopher Snowhill
391b9cf6dc
Update feed updater script for R2
Update to use rclone with out of tree config to upload

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-01 00:15:03 -07:00
Christopher Snowhill
689010b714
Playlist: Fix merging dynamic metadata dictionary
Merge the existing metadata entry dictionary with new values, because
sometimes, the caller may pass us a dictionary with some fields missing.

Fixes stream metadata for many HTTP streams.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:36:04 -07:00
Christopher Snowhill
c88c9e9aa0
HTTP: Support a stream title hack
Support a stream title hack employed by some iHeartRadio streams

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:36:00 -07:00
Christopher Snowhill
90eb2ded1d
HTTP: Support much larger metadata blocks
Support icy metaint data blocks up to 4KiB in size

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:35:45 -07:00
Christopher Snowhill
7640c869de
Updated VGMStream to r1843-0-gb158e812
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:35:40 -07:00
Christopher Snowhill
1ef7853488
FFmpeg: Fix AAC streaming
Fix AAC streaming by adding the correct audio/aac MIME type.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:35:36 -07:00
Christopher Snowhill
e7692dc40b
Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:23:06 -07:00
Christopher Snowhill
b9768e8100
Updated Turkish translation to near completion
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:22:53 -07:00
Christopher Snowhill
d2adf39e04
Updated VGMStream to r1831-27-ge6883cbd
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:22:08 -07:00
Christopher Snowhill
dfa5f41984
Updated libOpenMPT to version 0.7
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:20:54 -07:00
C.W. Betts
49bfd5053f Move most image assets to Xcode assets.
AboutCog.jp2 isn't moved because Xcode/Xcode assets doesn't recognize jp2 files as images.
2023-05-03 17:59:50 -07:00
Shoh Sewell
9c5a7d0e3e
Adds decimal digits to volume slider tooltip (#356)
* Adds decimal digits to volume slider tooltip

Modifies the volume slider tooltip so that:
-If the volume slider falls below 10%, the volume tooltip will display one decimal digit of precision (e.g. 3.4%).
-Else if the volume slider falls below 1%, display one decimal digit of precision (e.g. 0.34%).
-Otherwise display the volume slider tooltip as normal.

This helps show changes in volume between 0% and 10% where a change in volume isn't shown in the UI but is heard (especially when the "Limit volume control to 100%" option is unchecked in the "Output" Preferences submenu.

* Update VolumeSlider.m

Fix variable declaration

---------

Co-authored-by: Christopher Snowhill <chris@kode54.net>
2023-05-03 17:59:24 -07:00
Christopher Snowhill
07680673f8
Translation: Added Turkish language support
This translation is mostly complete.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 22:44:43 -08:00
Christopher Snowhill
37de4dc447
Translation: Updated Spanish translation
Added the new Lyrics window strings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 22:44:38 -08:00
Christopher Snowhill
55e28973d1
Main Menu: Add new Lyrics item to strings tables
Add item to strings tables for translations of the menu item. English is
already done as the base language, awaiting Spanish at least, will await
Polish and Russian when the respective translators get to it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 18:25:46 -08:00
Christopher Snowhill
e7b0b8bbb1
Lyrics Window: Added localization templates
Awaiting localizations of the window name in currently supported
languages.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 18:18:50 -08:00
Christopher Snowhill
3347cbf1da
Lyrics Window: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 18:18:03 -08:00
Christopher Snowhill
48f339f25b
Info Window: Reorder header imports
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:32 -08:00
Christopher Snowhill
420d706979
Lyrics: Implement Lyrics window display and hotkey
Implement Lyrics window display into main app as a popup window panel,
with a main menu hotkey.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:27 -08:00
Christopher Snowhill
8eaafe8f9b
Tags: Implement unsynced lyrics in Vorbis plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:23 -08:00
Christopher Snowhill
7b98a64035
Tags: Implement unsynced lyrics in Opus plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:19 -08:00
Christopher Snowhill
403f02f961
Tags: Implement unsynced lyrics in Flac plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:15 -08:00
Christopher Snowhill
abbb2fece7
Tags: Implement unsynced lyrics in FFmpeg plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:11 -08:00
Christopher Snowhill
e66b01b6db
Tags: Implement unsynced lyrics into TagLib plugin
Implement unsynced lyrics reading into TagLib frontend plugin.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:08 -08:00
Christopher Snowhill
a21242db55
TagLib: Implement unsynced lyrics tag support
Implement unsynced lyrics tag reading and writing into TagLib.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:47:04 -08:00
Christopher Snowhill
7e2a286b83
Tags: Added unsynced lyrics tag interface
Added an unsynced lyrics tag interface to the PlaylistEntry class

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:46:59 -08:00
Christopher Snowhill
ed281eb743
Update copyright year in various places
Update these things a bit for the next release.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-05 16:46:31 -08:00
Christopher Snowhill
d5720e3944
Updated VGMStream to r1810-97-g408cada5
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 23:52:34 -08:00
Christopher Snowhill
66338e2c83
Updated libOpenMPT to version 0.6.8
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 23:51:44 -08:00
Christopher Snowhill
4617654b57
Repair SceneKit project definition
Repair the SceneKit container definition in the Xcode project file, so
that the Visualization scene gets copied properly on project build.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 23:48:04 -08:00
Christopher Snowhill
d4785e0c74
Replaced r8brain with libsoxr
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 23:47:54 -08:00
Christopher Snowhill
e1fb7b9bd7
Playback Controller: Fixed title bar updating
This change had several components. For one, the delay of the dispatch
was increased from 5 milliseconds to 50 milliseconds. Two, the post to
the notification center was included in the delayed dispatch, so that
retains the PlaylistEntry object. Finally, the playlistController's
currentEntry object is reassigned from the input PlaylistEntry object,
which facilitates all watchers which are observing that variable for
updates. This final step also retains self for the callback, which
should be fine, since it's a quick dispatch with a short delay.

Fixes #335

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-09 22:34:47 -08:00
Christopher Snowhill
9822dcc4c0
Audio Player: Only wait for unstopped input
Input thread now signals when it has stopped and is about to return, in
case the input thread returns before the BufferChain dealloc function
would be waiting for it to terminate. Somehow, even though the Semaphore
is being signaled at this point, the BufferChain still ends up waiting
the default of 2.5 seconds for the signal that apparently never comes,
delaying file stoppage. This prevents the wait action entirely. Must
have been some sort of race condition.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-09 21:17:45 -08:00
Christopher Snowhill
447a60afd9
Audio Player: Add new method of signaling stop
This new method should cause all stops to default to immediate stoppage,
and only stops that occur after an end of track signal should indicate
to play out the entire buffer.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-09 21:14:45 -08:00
Christopher Snowhill
4f942ac890
Playback Controller: Remove "stopping" status use
This should not really be necessary for proper player operation any
longer, and can safely be removed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-09 21:13:12 -08:00
Christopher Snowhill
02c5ccaae1
Sandbox: Rework several blocking actions
Several actions have been reworked to be non-blocking, as their
operation should still occur in the main thread, but should not block
the thread they are called from, as they are not required to continue
processing there.

End of secure access has also been made non-blocking, as it is usually
only called when an input is done accessing a given file or folder, so
it should be important to return quickly, as the input is likely about
to terminate, and other things are waiting for it to return.

Also remove a nested block call for the storage access, as it is within
an existing serializing block, so it shouldn't need to be nested.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-09 21:12:33 -08:00
Christopher Snowhill
e8caede273
Update script: Purge cache of manifest files
Purge the manifest files from the Bunny CDN cache before starting
the update of the site.
2022-12-05 23:18:29 -08:00
Christopher Snowhill
1c09694f1b
Decoders: Implemented Organya decoder
Based on the C++11 code by Joel Yliluoma / bisqwit, which in turn is
based on information from NX-Engine. I have also taken the liberty of
bundling the required wavetable bank and PixTone drums. Contrary to the
documentation provided with the code, my version of dou_1006.zip, as
downloaded over a decade ago, had the wavetable bank at offset 635,816
bytes into the file, not 1,115,748 bytes. Possibly a difference of
having applied the translation patch? My copy is the original version,
so I had to use a real resource parser to locate the waveforms.

The player will obey the configured sample rate, loop count, and fade
time for synthesizers, and also obey Repeat One to play indefinitely.
The code should be quite robust to minor abuses, though I can't imagine
how well it would hold up to random bad files, other than playing
outright garbage.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-04 03:03:45 -08:00
Christopher Snowhill
6023acc10e
Sandbox: Fixed another outstanding sync bug
Releasing sandbox access was incorrectly synchronizing on the object,
but still running code in the calling thread. It has been updated to
match the rest of the interface, which serializes all access through the
main thread only. This should prevent the sandbox from carrying stale
handles to already-released objects.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-03 22:13:14 -08:00
Christopher Snowhill
466d07dccb
Resampler: Update r8brain-free-src to v6.2
This should improve performance significantly for downsampling.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-12-03 22:10:50 -08:00
Christopher Snowhill
547f4ea012
Fix SceneKit visualization
Must always remember to have xcode-select set to Xcode.app and not
the Command Line Tools when archiving the app myself, as otherwise
Xcode will fail to package the SceneKit objects into the resulting
bundle.
2022-11-30 01:12:15 -08:00
Christopher Snowhill
d7d6014789
Sandbox Broker: Fixed a potential reference crash
The code which looked up Sandbox handles for a given path had a bug
where it would re-add the handle to the in-memory cache storage even if
it already had just retrieved the current handle from the cache. I hope
this will fix the crashes which have been plaguing people adding a lot
of files to the playlist all at once from a single folder.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-29 00:54:16 -08:00
Christopher Snowhill
ecd46cfa3c
Updated VGMStream to r1800-25-g599326a3
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-29 00:54:05 -08:00
Christopher Snowhill
cd55a0b08c
Dependencies: Update libVGM release and debug
Update from 0e34925..fd7da37, with the following changes:

- C140: simplify update loop, add unbanked mode
  - It resembles MAME's code more closely now.

- OKIM6258: make more tolerant against late writes

- enforce "discrete YM3438" mode for YM3438 VGMs

- Update emu2413 to v1.5.9

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-29 00:49:43 -08:00
Christopher Snowhill
56ea4a5428
Update Script: Change feed upload to Bunny CDN
Change upload script to use curl to upload to Bunny. The access
key is stored in the system keychain, with confirmation on access.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-07 00:00:19 -08:00
Christopher Snowhill
c850d7752f
Vorbis Plugin: Remove unnecessary shell script
Remove shell script that was modifying the import path of a framework,
as we are no longer using libvorbis like that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-06 00:50:01 -07:00
Christopher Snowhill
1d1babd2bb
Add display name and category to project
Add display name and category fields to project. Thanks, Apple, for
adding those to Xcode all of a sudden.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-05 00:48:57 -07:00
Christopher Snowhill
08f2dabc81
Info plist: Add newly required keys
For some reason, Xcode isn't adding these now. No idea what Apple
has done to cause this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-05 00:43:43 -07:00
Christopher Snowhill
445b1bb295
Sandbox Broker: Fix hang with synchronization
Synchronize with dispatches to the main thread instead of using
synchronization primitives. This prevents the main thread from
hanging another thread as a result of other threads entering the
sync block, then dispatching a callback to the main thread, which
also tries to lock on the sync block.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-04 21:14:19 -07:00
Christopher Snowhill
4bed0af868 Core Data Store: Handle concurrency properly
All concurrency from other threads should pass through the viewContext's
performBlock or performBlockAndWait functions, and no other way. So now,
all access to Core Data is either happening on the main thread, or by
using these code blocks, all of which will wait for their access to
proceed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-30 16:54:52 -07:00
Christopher Snowhill
e3ae28369f MIDI Plugin: Fix Secret Sauce memory leaks
Needed some autoreleasepools in there.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-28 00:17:08 -07:00
Christopher Snowhill
48056a9a2e Touched by Xcode
Xcode updated the Preferences xib.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-28 00:17:03 -07:00
Christopher Snowhill
5011f59b34 Sync commits 2022-10-28 00:02:46 -07:00
Christopher Snowhill
052a77d2cc MIDI Plugin: Add a little Secret Sauce
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-27 23:06:14 -07:00
Christopher Snowhill
bcd5ab0dd2 Amend MIDI Audio Unit player a bit
This should fix some potential initialization errors it may have had
before, but this doesn't fix the broken Sound Canvas VA plugin. Roland
says it's supposed to be broken on macOS 12+ and/or Apple silicon
anyway, so I guess there's no dodging that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-21 16:37:14 -07:00
Christopher Snowhill
5882affd3f Always create new ContentViewController for volume
This is needed to re-parent the VolumeSlider window, as there is only a
single VolumeSlider, but two different VolumeButtons and their
respective NSPopover windows. So, always recreate the view on open,
which doesn't appear to have a noticeable impact on performance.

Fixes #331

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-20 21:28:20 -07:00
Christopher Snowhill
034863ee38 Fix Spectrum View in toolbar customizer
It was referencing the old SpectrumView class, when it should have been
referencing the safe SpectrumViewCG class, which will software render a
single frame of spectrum data when the customizing dialog is opened.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-20 21:28:15 -07:00
Christopher Snowhill
b5e096180a Updated FFmpeg to n5.2-dev-1305-g3bd0bf76fb
Among other things, fixes CVE-2022-2566.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-17 03:55:49 -07:00
Christopher Snowhill
54f46d6380 Ignore unnamed audio devices on enumeration
This was crashing trying to assign a nil CFStringRef from mystery audio
devices to the NSString passed to the enumerate block function, which
was predictably crashing. Ignore such devices instead.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-16 15:23:14 -07:00
Christopher Snowhill
ab512e5086 Handle external artwork with .heic extension
External artwork already supported the HEIC format, just not the correct
filename extension for the format.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-16 15:00:06 -07:00
Christopher Snowhill
e4bc5853ad Fix a crash with embedded cue sheet handling
Tag reading can read cue sheets as either a single NSString, or an
NSArray of NSStrings, so handle either case.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-16 14:59:19 -07:00
Christopher Snowhill
cddf9fb9ba Sync commit count 2022-10-16 00:19:32 -07:00
Christopher Snowhill
ee5231f567 Only process visualizations when visible
Stop visualization processing when the host window is completely
occluded, thus reducing background CPU usage levels significantly

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-15 23:19:36 -07:00
58a3c3aeeb
Merge pull request #329 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2022-10-13 21:13:37 -03:00
The Gitter Badger
29f20c60c1 Add Gitter badge 2022-10-14 00:07:50 +00:00
Christopher Snowhill
fba0775ebd Update associated file type extensions
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:17:54 -07:00
Christopher Snowhill
3a98f3f5a1 Formatting fixes
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:09:09 -07:00
Christopher Snowhill
c9181f571f Better locking behavior for playlist storage
This should fix up potential locking issues with maintaining a copy of
the results set while certain other background actions may happen, such
as the player updating play counts while playing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:09:05 -07:00
Christopher Snowhill
e54d05a907 Updated libraries and libraries debug set
Updated:
- libFLAC from 1.3.3-235-g772efde6 to 1.4.1
- libvgm from 001ca75 to 0e34925
- libid3tag from 0.16.1 with patch to 0.16.2

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:09:01 -07:00
Christopher Snowhill
894aada3d2 Updated libOpenMPT to version 0.6.6
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:08:56 -07:00
Christopher Snowhill
929ab11738 Updated VGMStream to r1776-97-g845961bb
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:08:51 -07:00
Christopher Snowhill
7ad895b95c Updated VGMStream to r1776-46-gc0c2c3c7
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-09-06 21:57:24 -07:00
Christopher Snowhill
9a7ecd98ea Update translations
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 16:50:34 -07:00
Christopher Snowhill
dffd09e6f7 Updated VGMStream to r1776-16-g7e4f5dc6
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 16:31:05 -07:00
Christopher Snowhill
d03afd6bf8 Updated libOpenMPT to version 0.6.5 final
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 16:31:01 -07:00
Christopher Snowhill
58453a6b7d [Cog Audio] Rename Semaphore.h to CogSemaphore.h
This magically fixes the stupid header maps that were pulling the system
semaphore.h into Swift projects, when they shouldn't have been doing
that in the first place. This is the same reason that the FLAC library
has its assert.h renamed to FLAC_assert.h.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-05 22:18:40 -07:00
Christopher Snowhill
be87e5433c [Cog Audio] Make the Swift Vis Controller work
And this is the actual meat of getting it to work properly, the changes
the Swift code needed to actually be fully functional.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-05 21:38:58 -07:00
Christopher Snowhill
e630b34981 [Cog Audio] Add a Swift bridging header
This makes the Swift version of the Visualization Controller usable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-05 21:38:54 -07:00
Christopher Snowhill
8db5386053 [Cog Audio] Change a couple of imports
These imports needed to be changed so that Swift bridging didn't import
the system's semaphore.h instead of CogAudio's Semaphore.h, which is a
completely different thing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-05 21:38:50 -07:00
Christopher Snowhill
cbeba3fa0e First module converted to swift, but broken 2022-08-05 21:38:44 -07:00
Christopher Snowhill
a8fea3f657 [GME Input] Correct old comment in the code
There has been an API in GME to detect tracks ending for quite some time
now, and this just adds a little bit to the existing comment, which
previously noted that there was no way to detect if a track had ended,
which may have been true several major versions of GME long past.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-04 18:03:04 -07:00
Christopher Snowhill
5ac279e289 [GME Input] Fix decoder output sample count
The output has been assigning twice as many samples as it was supposed
to ever since commit 8d851e5bda, which
ended up generating the correct 1024 samples (2048 per GME parameter),
but assigned 2048 to the AudioChunk, which resulted in over-reading the
audio buffer, and thankfully not crashing, but instead causing an awful
sound distortion effect as random memory contents were played as PCM
audio.

Fixes #320

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-04 18:03:00 -07:00
Christopher Snowhill
4480eeefbe [CUE Sheet Container] Allow other containers
Allow .mp3 and such to fall back to the FFmpeg container handler, in
case there are chapters in a renamed file.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-03 21:21:22 -07:00
Christopher Snowhill
4d1d799f98 [MAD Decoder] Do not close source ourselves
The input isn't supposed to close its own sources, as it did not open
them itself, and they should be cleaned up automatically when they are
released to zero reference count.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-03 21:21:18 -07:00
Christopher Snowhill
ad8c59be2a [MAD Decoder] Drop RIFF files to the next input
Let the FFmpeg decoder handle RIFF files, if they happen to be named
.mp3 and not something like .wav.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-03 21:21:14 -07:00
Christopher Snowhill
545385da59 [Update Script] Add new JSON generator
Source is included here:

https://github.com/losnoco/sparkle-to-JSON
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-03 21:21:11 -07:00
Christopher Snowhill
59ab4ccdb5 Fix update script
This fixes the update URL and parameter handling. Seems there
was an extraneous newline returned by the security command.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 23:44:12 -07:00
Christopher Snowhill
4d3274362c [AdPlug Input] Fixed seeking
Looks like I never tested this, meh.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 22:52:13 -07:00
Christopher Snowhill
233dc424cb Update update_feed.rb with new parameter
New parameter for update title for the site generator.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 21:51:25 -07:00
Christopher Snowhill
3a64cdd5cc Updated AdPlug with a crash fix for RAD2
Fixed RAD2 files referencing instruments not present in the
file, which caused the player to reference uninitialized memory
and usually crash.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 21:51:21 -07:00
Christopher Snowhill
0bae420e6b Updated libbinio
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 21:51:16 -07:00
Christopher Snowhill
a4327579fa [Playlist Menu] Disable actions on empty selection
These actions should not be invoked when there is no selection.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-29 01:43:04 -07:00
Christopher Snowhill
ff1afdb63c [Playlist Queue] Save queue state change to disk
Save queue state changes to disk, rather than leaving it for later.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-29 01:43:00 -07:00
Christopher Snowhill
e0c1e338e4 [Playlist Queue] Hopefully prevent a crash state
This removal was causing crashes for some people. It should not get this
way, however.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-29 01:42:56 -07:00
Christopher Snowhill
b60a5462b7 [SID Input] Add static initializer for residfp
The SID builder needs a static initializer, otherwise multiple instances
created simultaneously, such as during populating info on adding a lot
of tracks, will race and crash the player.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-27 21:38:33 -07:00
Christopher Snowhill
3ff9d079d7 [File Tree] Only free Smart Folder query if used
Only free the query if it was successfully allocated.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-27 06:35:45 -07:00
Christopher Snowhill
9113051840 [File Tree] Ask Sandbox for access to Smart Folder
Ask for access to Smart Folder dictionary file, to read its query.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-27 06:35:04 -07:00
Christopher Snowhill
a2dacc067b [Playlist View] Prevent assigning nil textField
Prevent somehow assigning nil textField contents, as well as the
tooltip text.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-25 19:55:54 -07:00
Christopher Snowhill
03c498c0fc [Playlist Loader] Extend deduplication to CUEs
CUEs will now deduplicate playlist entries based on their dependencies,
and prevent loading redundant tracks if you add an entire directory, or
use the option to add a directory when adding single files from it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-25 19:35:11 -07:00
Christopher Snowhill
59ac5769ad [Sandbox] Add Sandbox grants to places missing it
The subdirectory parser, the CUEsheet reader, and the legacy XML
playlist reader were missing grants for Sandbox access.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-25 19:33:51 -07:00
Christopher Snowhill
9752cb725e Spanish translation of new option.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:23:28 -07:00
Christopher Snowhill
f36ec30cb3 Clarified folder add message to correct button
Clarified the button name to "Open", which is what the button actually
says, not "OK". Also used double quotes around the other button name.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:23:24 -07:00
Christopher Snowhill
77b3d74948 [Sandbox Paths] Automatically clean up old paths
Clean up redundant paths automatically, and on startup. Also refresh the
preferences dialog path list every time it is opened.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:14:52 -07:00
Christopher Snowhill
7e06aef932 [Playlist Loader] Deduplicate loaded items
Deduplicate loaded tracks, to prevent duplicate items when adding a
folder that happens to contain playlists or CUE sheets referencing the
very same files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:13:03 -07:00
Christopher Snowhill
e6bafa0b38 [Playlist Loader] Add option to load more files
Add option to load every file in a folder when opening just one or more
files in that folder.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:12:11 -07:00
Christopher Snowhill
72350a8b4c [Playlist View] Change truncation behavior a bit
Change the truncation behavior to only truncate if the length exceeds
1024 code points.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 19:35:57 -07:00
Christopher Snowhill
5f87475f2d [Playlist View] Add Sample Rate and BPS fields
Add Sample Rate and Bits Per Sample columns, hidden by default.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 19:35:10 -07:00
Christopher Snowhill
92b02c20db [Sandbox Dialog] Fix removing newly added paths
Newly added paths weren't adding all of the necessary data to the list
storage to make it possible to remove them without restarting the
player. Oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 19:17:04 -07:00
Christopher Snowhill
ddbc38c7fe Move most large stack using buffers to the heap
This should solve most potential future stack overflows.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 18:32:47 -07:00
Christopher Snowhill
eec8bf9f1c Enable warnings to track stack overuse
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 17:50:28 -07:00
Christopher Snowhill
699bdbada3 [Sandbox Notification] Add Spanish translation
Add the Spanish translation of the new dialog.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:39:26 -07:00
Christopher Snowhill
d4ce000dc3 [Sandbox Notice] Change single to double quotes
Change the quotes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:39:22 -07:00
Christopher Snowhill
de710ef402 [Libraries] Fixed OpusFile debug library
It was linking to the wrong filename for libogg.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:39:17 -07:00
Christopher Snowhill
df17e1c788 Updated VGMStream to r1745-79-g449bb5e0
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:39:13 -07:00
Christopher Snowhill
5c3b876c04 [File Permissions] Add warning dialog
Added a warning dialog to notify the user of the purpose of the add
folder dialog that will pop up after it. Otherwise, they may get the
idea that the dialog is a glitch and should be cancelled.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:04:13 -07:00
Christopher Snowhill
f9aa56b18a [Playlist Loader] Only ask permission for local
Only ask permission for container folders if the container has local
files, and not for purely remote files, such as stream playlists.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:00:18 -07:00
Christopher Snowhill
9ced34a87c [Playlist Insert] Add a further bodge fix
I wish people would stop adding files to the playlist while there's a
search filter in place.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-21 04:16:36 -07:00
Christopher Snowhill
8f7b26cb5d [FFmpeg Input] Buffer up to 5ms each read call
Buffer up to 5 milliseconds of audio, or at minimum 1024 samples, each
call. Also pre-allocate the buffer, rather than using a stack buffer.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-21 04:03:21 -07:00
Christopher Snowhill
bb307a64b6 [FFmpeg Input] Do not subtract first block length
This is an unnecessary step, and results in the offset being off by the
duration of the first pre-read block. This is incorrect.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-21 03:54:08 -07:00
Christopher Snowhill
59d54f3190 [Inputs] Severely reduce metadata update intervals
The Vorbis, Opus, MAD MPEG, and especially the FFmpeg inputs needed to
have their metadata update intervals severely reduced, to reduce CPU
usage, especially on files with lots of tags. Interval reduced to only
once per second.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-21 03:53:14 -07:00
Christopher Snowhill
0b8a850086 [Chunk List Converter] Fix repeated initialization
Oops, this compare blunder resulted in DSD decimation breaking every
1024 samples or so, owing to block sizes, and caused ticking sounds as a
result. It would also cause HDCD decoding to break completely.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 23:05:40 -07:00
Christopher Snowhill
7e03f423c3 [Chunk List Converter] Minor changes
Neither of these two changes is really important, but they do simplify
things, and the division on that one function makes the non-decimating
DSD support actually functional, as the caller expects a specific number
of samples, and that was otherwise octupling the input sample count.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 23:04:25 -07:00
Christopher Snowhill
d9a09bff40 [Audio Output] Eliminated another stack buffer
Another large stack buffer was at play here. Consolidated it into an
existing buffer that can perform double duty here, since neither place
it's used conflicts with each other.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 23:03:00 -07:00
Christopher Snowhill
afa992189d Add a lock around access to output PTS variable
This locking should help, but I don't know why visualization jumps
around now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 07:31:04 -07:00
Christopher Snowhill
5314eda5ab Reconfigure default toolbar layout
Now things are a little more understandable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 07:29:38 -07:00
Christopher Snowhill
8ae271c511 [FLAC Decoder] Fix reading CUESHEET tags
It already supported reading the CUESHEET metadata block, but I managed
to break reading and processing CUESHEET Vorbis comments, which broke
CUE tagging, as well as files that didn't have both tags.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 04:53:46 -07:00
Christopher Snowhill
5280cd3aa3 [Playlist Pasteboard] Rewrite row pasteboard
Playlist View pasteboard copier function should only be generating URLs,
and it should verify that the entry has a valid URL to begin with.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-16 06:10:16 -07:00
Christopher Snowhill
7f4459c41c [Repeat Album] Add a safety test to repeat list
In case the current track isn't part of an album, or is otherwise not
matching any albums in the playlist. Though the Album filter predicate
wasn't working for a while due to changes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-16 05:59:27 -07:00
Christopher Snowhill
0f003fcefd Update all localizations
This includes the base English strings, Spanish, Polish, and Russian.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-16 05:46:34 -07:00
Christopher Snowhill
72ed975d56 [Audio Output] Remove renderer from synchronizer
Remove the renderer from the synchronizer on stop, before releasing the
objects, if possible.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 22:52:46 -07:00
Christopher Snowhill
76d612faf4 [Audio Output] Only unregister listener if used
Only unregister the listener if it actually has been registered, and
clear the handle upon doing so.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 22:51:59 -07:00
Christopher Snowhill
dc536eef3d [MAD Decoder] Don't crash on bad files
The local and seekable file scanner could crash on bad MPEG files if
they failed to decode any frames and broke due to either end of file or
other unrecoverable errors, due to a division by zero error attempting
to calculate the file bitrate. Now correctly return error state if this
occurs, bailing early.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 22:51:04 -07:00
Christopher Snowhill
47258b02b1 [Core Data] Add access locking
Apparently we need this to prevent Core Data from stomping on itself
when another thread accesses it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 07:00:11 -07:00
Christopher Snowhill
7d46ac42b6 [Playlist Storage] Properly force migration
Old version users needed this, but it wasn't performed correctly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 06:18:39 -07:00
Christopher Snowhill
498d85a0f4 [Dependencies] Fix libogg version number
Apparently, the autotools package uses a different versioning scheme
than the CMake build. Also rebuilt and re-versioned libvorbis and
libvorbisfile.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 06:18:28 -07:00
Christopher Snowhill
187ee602d6 [libid3tag] Updated to avoid crash bug
Already updated to 0.16.1, but this fixes a crash bug in 0.16.1.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 06:18:18 -07:00
Christopher Snowhill
de9b09251d [Audio Output] Greatly improve sample rate changes
Sample rate changes will now occur on exact sample boundaries, like they
are supposed to. Also, FreeSurround accounts for its output latency.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 06:18:08 -07:00
Christopher Snowhill
4df50c7fc3 Fix the Spotlight search panel
It was previously crashing horribly on adding search results. This makes
it actually functional, and renders it using a view-based table instead.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-15 06:18:01 -07:00
Christopher Snowhill
96304dbb17 Significantly reduce stack memory usage
Oops, there were a lot of large local buffers in use here.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 17:28:24 -07:00
Christopher Snowhill
b60646a1fc Remove obsolete helpbook document
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 16:12:04 -07:00
LennyLip
ed6f0d8f52 ru FreeSurround pref string 2022-07-14 16:11:58 -07:00
Christopher Snowhill
b742fb6659 Sync with sparkle branch 2022-07-14 16:06:20 -07:00
Christopher Snowhill
d417d245f4 [Audio Output] Make toggling DSPs safe
The DSPs should not be deinitialized from another thread, possibly while
they are currently processing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 03:16:21 -07:00
Christopher Snowhill
dd58a540a8 [FreeSurround] Add configuration option to enable
It now needs translation of the new string.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 03:02:08 -07:00
Christopher Snowhill
10272ca7a4 [Audio Processing] Increase thread stack size
Apparently, all these new changes with FreeSurround have pushed the
default 512KB thread stack size to the limit. And I'm not even using
stack variables, really, except for maybe the autoreleasepools.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 02:42:21 -07:00
Christopher Snowhill
7de0792b97 [FreeSurround] Actually make it work
Apparently, the LFE channel is not being initialized at all if bass
redirection isn't enabled, and even if it is, it's uninitialized for a
great portion of the spectrum. Clear it all on every iteration.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 02:41:06 -07:00
Christopher Snowhill
eb0c1a8463 [Audio Output / Debugging] Fix sample logging
Fix the sample logging function that is optionally compiled into debug
versions.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 02:39:25 -07:00
Christopher Snowhill
38832d9ce9 [Audio Processing] Update for new API
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 02:38:04 -07:00
Christopher Snowhill
b0bef40386 [FreeSurround] Change another variable to const
This should be const anyway, as it's not written to.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:47:02 -07:00
Christopher Snowhill
8b418004eb [FreeSurround] Further improvements
Still not working, though.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:47:02 -07:00
Christopher Snowhill
60429490b3 [FreeSurround] Fix surround block size
The output implementation has a block size of 4096, so the class
implementation should also use that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:47:02 -07:00
Christopher Snowhill
11413ef3db [FreeSurround] The rest of the implementation
This is the code that actually needs to be added to make it process
audio. This insertion makes the whole app crash when processing audio at
all. Weirdly, simply reverting these two files makes the audio code work
again. I can't explain it.

Also, commenting out CMAudioFormatDescriptionCreate makes it work, too.
There's something weird going on with that function.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:47:02 -07:00
Christopher Snowhill
85db95befd [FreeSurround] Experimental implementation code
This is a working implementation of FreeSurround, but I can't get it to
work in the Cog code base, as the whole project crashes head over heels
if this code is inserted into the output chain.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:47:01 -07:00
Christopher Snowhill
04d394c65c [Audio Processing] Move float32 converter
Move the Float32 converter to a different location, for any future plans
to support decoding audio files to common data for any other purpose.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:45:49 -07:00
Christopher Snowhill
58be4a40fc [FLAC Decoder] Change maximum buffer size
This should be more correct, especially considering that the library can
handle 32 bit files now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:43:33 -07:00
Christopher Snowhill
f4bbeaec77 [libFLAC] Remove debug overlay
There's a bug in the debug version of the library which does not occur
in the release build.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 01:42:57 -07:00
Christopher Snowhill
27a1153b91 [FLAC Decoder] Correctly handle zero length frames
Apparently, the decoder is capable of returning zero length frames
without having hit the end of the stream.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 00:01:59 -07:00
Christopher Snowhill
32ec11a42e Updated the help document a bit
Oops, it's been a while since I've touched this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 04:56:03 -07:00
Christopher Snowhill
30bff0b52d [Translations] Tweaked the Preferences dialog
Now the Preferences panels are 110 points wider, and most things are
shifted around in ways to make the current set of translations fit into
the dialogs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 04:37:16 -07:00
Christopher Snowhill
f17fc3708d [Polish translation] Fixed total time formatter
Please do not translate the token names, they are used by the code to
look up which value to insert into the string.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 03:50:34 -07:00
Christopher Snowhill
a9002a616e [Polish Translation] Fix the MainMenu strings file
The MainMenu.strings file had several mistyped quotation marks, and
several strings outside of quotation marks or comments, breaking the
menu translation entirely. This makes the translation actually work.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 03:43:08 -07:00
Christopher Snowhill
fc5c3c5441 [Polish translation] Activate localized strings
Template was missing from the project declaration. Oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 03:42:08 -07:00
Christopher Snowhill
3adc30477d XIB touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 03:41:27 -07:00
Christopher Snowhill
c8c2bb909a [Ogg Vorbis/Opus] Fix tag clobber on play
Fixed the tags being overwritten by an "update" on non-streaming files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 00:12:35 -07:00
Christopher Snowhill
0fa2d0110c [Ogg Vorbis/Opus] Fix picture metadata handling
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 00:11:51 -07:00
Christopher Snowhill
13fb6bb141 [Playlist Insert] Add a special case for filtered
Insertions which occur when the playlist is filtered can try to add past
the end of the playlist. Let's try to dodge that.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-12 23:47:03 -07:00
Christopher Snowhill
8d231d34d4 Update several of the dependencies
- Updated libFLAC to the latest Git commit, post 1.3.4.
- Updated libid3tag to 0.16.1.
- Updated libopus to the latest Git commit.
- Updated my FFmpeg libfdk-aac patch. Previously was overwriting
  memory when it was supposed to be skipping samples.

Also added debug versions of several of the libraries, and changed
the library extractor script to unpack the debug libraries over the
release set to add the particular matching debug versions when
building a debug build.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-12 23:40:53 -07:00
LennyLip
4a5d21836a
Russian translation: new strings (#310)
* Update MainMenu.strings

* Update Preferences.strings
2022-07-11 19:58:58 -07:00
Christopher Snowhill
67f05bad27 [MAD Decoder] Fix streamed MP3s not working
Due to a change designed to stop playback when the end of the file is
reached, which should not be checked for unseekable files, which are
web streams that only stop when the connection drops, or when the user
stops playback manually.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 19:44:19 -07:00
Christopher Snowhill
fc1a226791 [HTTP Reader] Fix reading small static files
The reader was previously returning a failure state on open if the read
completed and fit entirely into the read buffer, which broke most remote
M3U or PLS playlists.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 19:44:19 -07:00
Christopher Snowhill
32940d3277 [Sandbox Broker] Only pop suggester for local URLs
Only pop up the path suggester and check on local file URLs, not remote
URLs, which shouldn't be checked, since they don't require sandbox
permission grants or bookmarks.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 19:44:19 -07:00
Christopher Snowhill
749e92694e Update About Window logo
The previous version was from an old render. Also apply some minor
tweaks to the scene.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 19:44:19 -07:00
Christopher Snowhill
a81ed64915 [Shuffle] Fix Shuffle Album mode
Oops, I should have remembered that the data structure changes would
break this search predicate. Now apply the search predicate to the
playlist representation, which allows searching against the data blocks
using the PlaylistEntry property implementation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 19:44:19 -07:00
70db08cf07
Update README.md
Third's the charm.
2022-07-11 19:10:50 -04:00
2619eff6ad
Update README.md 2022-07-11 19:09:46 -04:00
847f96c13e
Update README.md 2022-07-11 19:09:13 -04:00
af8cfc1c02
Added Lokalise logo and instructions. 2022-07-11 19:08:25 -04:00
Christopher Snowhill
7763269af8 Add missing strings to new translations
Still in need of translation from their respective authors.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 15:26:25 -07:00
LennyLip
7504300d9e Russian lang fixes 2022-07-11 15:19:25 -07:00
LennyLip
0ac17fe543 Russian translation 2022-07-11 15:19:20 -07:00
Christopher Snowhill
26f903ab77 Fix missing Polish declarations in project
There were still missing things.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 15:03:19 -07:00
Christopher Snowhill
8244eb0bb8 Activate Polish translation
The declarations for the translation were missing from the project files
so that it wasn't being used. Also added the missing strings to the
files that were already added to the translation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 14:40:32 -07:00
Christopher Snowhill
18824e0e18 [Visualization Controller] Minor guard check
Guard check in case visualization controller is called before any data
is posted to it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 14:39:23 -07:00
Christopher Snowhill
9f03b72b69 [Headphone Filter] Minor changes
Change a variable type, to avoid a warning.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 14:38:52 -07:00
Christopher Snowhill
8d8b508d09 [Audio Converter] Minor change for format changes
This should also seal up any potential hole for problems if there's an
audio format change and no audio buffered.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 16:36:31 -07:00
Christopher Snowhill
f8a8a57cf0 [Audio API] Repair the damage to the input chain
The input chain could hang up indefinitely, and MAD decoder didn't
indicate end of file properly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 16:24:21 -07:00
pax
e595dbd525 first(really basic) open url panel translation. 2022-07-10 15:23:45 -07:00
pax
f5aa7401b6 translated equalizer strings, as well as the spotlight thing. 2022-07-10 15:23:45 -07:00
Christopher Snowhill
3c351f6968 [Input API] Change input readAudio method
readAudio now returns an AudioChunk object directly, and all inputs have
been changed to accomodate this. Also, input and converter processing
have been altered to better work with this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 15:14:47 -07:00
Christopher Snowhill
8bc9738ccb [Downmixer] Only downmix to stereo if not stereo
When downmixing to mono, only downmix to stereo first if the source is
not already stereo.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 15:08:56 -07:00
Christopher Snowhill
000a2e0cb6 [Project Files] Change most to enable modules
Most projects needed to be changed to enable C or Objective C modules.
Hopefully, this improves debugging.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 15:07:39 -07:00
Christopher Snowhill
abd557943c [Cuesheet Input] Don't repeatedly open file
The input file has already been opened for decoding by an earlier step
in the testing process, reuse the decoder from that. Spares a decoder
open cycle on all embedded cuesheet supporting formats.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 14:57:22 -07:00
Christopher Snowhill
39d3f7edfe [HRTF] Force filtering of odd channel formats
Apparently, Apple's Spatial Audio processor doesn't really support weird
configurations like this. So we need to filter them down to stereo.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 18:42:38 -07:00
Christopher Snowhill
57719a038c [HRTF] Reverse Z axis of speakers above listener
Apparently, positive elevation is above, negative is below.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 18:42:32 -07:00
Christopher Snowhill
0e1bf0abf2 Update some strings from Polish translation
Update missing strings, including one which was translated already in
the comments.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 16:22:06 -07:00
f2b9d9d84e [Spanish Translation] New strings
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-09 16:16:55 -07:00
bfd363df5d [Translation] Privacy policy URL can now be loaded from strings files
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-09 16:16:55 -07:00
Christopher Snowhill
30789c608e Add two missing strings from the Sparkle branch
Oops, those were missing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 16:16:55 -07:00
a7290aedee [Spanish Translation] Updated string for new HRTF filter. 2022-07-09 16:16:55 -07:00
Christopher Snowhill
349ab57afe Implemented all new HRTF filter
This filter replaces the old one, and uses OpenAL Soft presets. Since
there aren't that many of those, I've left off configuration for now,
except to turn it on or off.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 16:16:55 -07:00
pax
85d46bc1b3 The Polish translation is mostly ready. 2022-07-09 15:56:02 -07:00
pax
f682378cf7 *added Polish translation, not fully ready, but there ya go. 2022-07-09 15:56:02 -07:00
Christopher Snowhill
291b1e1253 Truncate text in playlist to a reasonable length
1024 characters aught to be enough for any playlist view.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:34:50 -07:00
Christopher Snowhill
02f49093a3 Remove the meta string cache
It wasn't helping anyway.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:34:50 -07:00
564a8739d0 [Spanish Translation] Added strings for new Info Inspector fields 2022-07-08 16:34:50 -07:00
Christopher Snowhill
dad9275a30 Ditch the data compression
It just wasn't working out.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:34:50 -07:00
Christopher Snowhill
6222e25adc Experimental tag support redesign
This redesign completely changes how tags are stored in memory. Now all
arbitrary tag names are supported, where possible. Some extra work will
be needed to support arbitrary tags with TagLib, such as replacing it
with a different library.

Translation pending for a couple of strings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:34:50 -07:00
Christopher Snowhill
bea7d8e651 [Playlist] Increase default font size to Regular
Regular control size ends up being 13 points, rather than the previous
default of Small control size, which ended up being 11 points.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-07 17:56:47 -07:00
Christopher Snowhill
39ab0eaf45 Updated VGMStream to r1745-58-g828c6b09
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-07 16:44:01 -07:00
Christopher Snowhill
b29d19966f [Path Suggester] Only process local file URLs
Playlist entries for non-file URLs should be ignored.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-07 16:38:56 -07:00
Christopher Snowhill
63ca187aa7 [Table Views] Add a safety check to cell creation
Cell creation may create some other type of view, somehow. No idea how.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-07 16:38:48 -07:00
Christopher Snowhill
da053d7282 [File Tree] Significantly improve the watcher
- Switch to fine grained folder and file watching responses
- Navigate the PathNode tree using a fast dictionary of path components
- Quickly refresh the file tree by locating parent nodes to refresh

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 22:39:47 -07:00
Christopher Snowhill
1c3717eb84 [File Association] Correctly play files on open
When opening files from external association, such as opening files, or
opening folders with Cog, correctly obey the configured clear and play
or enqueue and play actions, by playing the new additions.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 17:04:15 -07:00
Christopher Snowhill
a03e519c61 [Playlist Loader] Fix Clear and Play action
Clear and Play was broken by the previous update. This fixes it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 16:04:03 -07:00
Christopher Snowhill
2754a9fca3 [CI Scripts] Update to use command -v
Instead of `which`.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 14:44:36 -07:00
Christopher Snowhill
b4e3c05c87 [Playlist Loader] Load files in the background
Load new playlist entries in the background, asynchronously.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 14:28:14 -07:00
Christopher Snowhill
c54062f4bf Attempt for one last time to fix Xcode Cloud
This should hammer fix it. That'll show them for forcing a shallow
commit on me.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 04:37:59 -07:00
Christopher Snowhill
016bc9a45b Attempt to fix Xcode Cloud again
This time, run the git fetch tags and genversion in the pre
xcodebuild script.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 04:22:26 -07:00
Christopher Snowhill
df74f5ebe7 Fix Xcode Cloud CI script to fetch tags
The script needs to fetch repository tags to function properly.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 04:14:24 -07:00
Christopher Snowhill
333392f284 Change Crashlytics symbol upload script
Script should not fail an otherwise successful build.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 03:38:42 -07:00
Christopher Snowhill
c48d55ba9a [Playback] Prevent erroneous file from repeating
Prevent Repeat Single from locking up the player on an unplayable file.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 02:20:05 -07:00
Christopher Snowhill
6ee0d8a267 [Sandbox Broker] Greatly speed up path resolving
The fragment remover need not detect whether the given path is a folder
or a file, as it is only removing hash marked fragments, not actually
removing the entire filename if it's only a file and not a folder like
the old versions used to. This greatly speeds up access, especially on
network shares.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 00:04:58 -07:00
Christopher Snowhill
d834819f79 [Path Config] Allow multiple selection
This more easily allows removing multiple paths at once.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 00:04:52 -07:00
Christopher Snowhill
60964a00b2 [Path Config] Remove exact items by token id
Should use the exact token object to remove them, rather than doing a
path comparison.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 00:04:45 -07:00
Christopher Snowhill
d03fb11f4c [Path Config] Properly prune the database
When cleaning up the path list, actually remove the pruned entries from
the Core Data storage, so they don't end up resolving to broken
bookmarks in the player, breaking playback on migrated configurations.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 23:47:05 -07:00
Christopher Snowhill
0e2bfeb671 [Path Config] Properly report broken bookmarks
Broken bookmarks weren't reporting as isStale, but rather, were failing
to resolve at all, and without this change, they were impossible to
detect in a migrated configuration.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 23:45:46 -07:00
Christopher Snowhill
ab285417ae [Album Art] macOS Ventura natively supports AVIF
Disable the compiled in AVIF support there, as the OS supports it
natively. Keep the libraries for older OSes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 23:44:35 -07:00
Christopher Snowhill
02e1276df8 [FFmpeg Decoder] Further improve Matroska tags
Matroska defaults the date field to "date_recorded".

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 18:11:45 -07:00
Christopher Snowhill
a8b13748d6 [FFmpeg Decoder] Better handle Matroska tags
Matroska files use the "TITLE" field for the album when there are
chapters. Also, Matroska container uses shorter gain field names for
album and track gain, differentiating them by either being global or
specific to each chapter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 17:40:57 -07:00
Christopher Snowhill
fadebd39ea [FFmpeg Decoder] Enable Metroska and WebM videos
Enable playback of video file extensions. Like other video formats
handled by the FFmpeg decoder, video streams are dropped in decode and
only the first audio stream is played.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 14:25:49 -07:00
6439c82eb6 [Spanish Translation] Updated strings in Appearance preferences
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-05 16:48:48 -04:00
Christopher Snowhill
472272129b [FLAC Decoder] Safety decoding for tag reader
Use tag string encoding guessing for tag decoding, just in case there
are invalid files with non-standard encoded strings inside the tags, or
if there are streams with such tags. We don't want any crashes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 13:44:44 -07:00
Christopher Snowhill
d4b434a68f [Spectrum] Enable switching style at runtime
It is now possible to switch the display style at runtime, while the
views are open.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 13:43:21 -07:00
Christopher Snowhill
f4926cf569 Interface builder touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 13:40:54 -07:00
Christopher Snowhill
d3256f3c54 Reformat spaces to tabs
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 12:52:53 -07:00
Dzmitry Neviadomski
0dc31808e7 Add preference to choose between SceneKit and DDB spectrum. 2022-07-05 12:49:09 -07:00
Christopher Snowhill
45ec7b6263 Greatly improve tag reading performance
Improve tag reading performance for Ogg, Opus, FLAC, TTA, and TAK, by
eliminating TagLib from the equation in those cases and just using the
respective file inputs to do the tag reading, which is apparently a lot
faster anyway.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 00:55:48 -07:00
Christopher Snowhill
22cd16bb46 [Audio Player] Eliminate an avenue of lockups
Prevent the player from locking up in certain circumstances, by not
locking chainQueue the entire time this function is processing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 00:55:41 -07:00
Christopher Snowhill
42b2142ab7 Remove unnecessary files from build and copy steps
Remove a single .inc include from CogAudio build phase, as it's included
but not compiled as Pascal like Xcode thinks. Also remove a bunch of
files from being copied into the resulting .framework and .bundle files
during link stage, as we don't need to distribute that stuff.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-04 23:58:06 -07:00
Christopher Snowhill
2c698304ca [Playlist Loader] Revert background loader
This reverts most of 802a86a3d8, since it
didn't work anyway.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-04 18:09:01 -07:00
Christopher Snowhill
af82399f37 [Playlist Loader] Fix background queue post action
Post action now returns the files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-04 17:37:32 -07:00
Christopher Snowhill
1145bca042 [Equalizer] Fix support for arbitrary channels
The deinterleaved format was being specified incorrectly. Now it asks
for the correct format, which is deinterleaved, and the bytes per frame
or packet sizes are relative to a single channel's buffer, not all
buffers. Oops, that could have been more clear in the documentation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-03 22:40:40 -07:00
Christopher Snowhill
e6c3fcaa0d [Equalizer] Remove unnecessary code
This code is obsolete, remove it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-03 22:40:33 -07:00
Christopher Snowhill
3c86758cb5 [Sandbox] Ask for permission for container folders
Ask for permission to access the folders containing container files,
such as .CUE sheets, or .M3U or .PLS playlists.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-03 22:40:26 -07:00
Christopher Snowhill
b3e0da6fe9 [M3U Playlist] Reformulate safety checks
Apparently someone managed to crash this with their playlists. No idea
how. Added more safety checks.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-03 14:30:29 -07:00
Christopher Snowhill
7f1c337ee8 [Tag Reading] Moved external cover art reader
Moved external cover art reader to a place where it can be used for any
format, even formats unsupported by Metadata Reader interfaces.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 14:59:47 -07:00
Christopher Snowhill
b72d5a4c26 Update Info.plist.template
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:37:32 -07:00
Christopher Snowhill
a32a8718d7 [Plugin Loader] Unregister loader callback
This callback should be unregistered when plugin loading completes,
otherwise we could end up processing bundles loaded by external stuff,
like Audio Units loading for MIDI playback.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:31:22 -07:00
Christopher Snowhill
9839f51ee1 [FFmpeg Input] Add .m4b and .m4r extensions
Add support for more file name extensions, so we don't fall back on
Core Audio Input for these files.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:22:34 -07:00
Christopher Snowhill
74bb2527a9 [FFmpeg Input] Implement SoundCheck tag support
Implement support for the Sound Check tag format.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:21:28 -07:00
Christopher Snowhill
cfcb7c041d [TagLib] Disable MP4 tag reader, as it can crash
This MP4 tag reader is buggy. Disable it in favor of FFmpeg decoder's
metadata reader.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:20:01 -07:00
Christopher Snowhill
a847cbde3b [MIDI Input] No longer crash when seeking to the end
When seeking to the end of a file, no longer crash due to out
of range std::vector access, because it was using at() with an
offset of the array size. Instead, offset from the begin()
iterator return value, which allows offsetting to end().

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 03:19:24 -07:00
321e3382f4 [Spanish Translation] Added new strings for synthesis settings
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-02 01:25:24 -07:00
Christopher Snowhill
b025a21e13 [Synthesizers] Implement default overrides
Default time, fade, loop count, and sample rate may now be overridden.

Synchronized preferences strings tables. Spanish translation of new
options pending, new releases won't be pushed until they're complete.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 01:25:24 -07:00
Christopher Snowhill
34513f3b38 [Translation] Widen the Path Suggester column
Widen the enable column, for the Spanish description of the column
header.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 14:31:16 -07:00
Christopher Snowhill
cb007d62a1 [Translation] Fix translation of Path Suggester
Also fix the fact that the XIB wasn't embedding the XIB/NIB in the app
as a result of the translation move.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 14:31:10 -07:00
Christopher Snowhill
a53a8ecb82 [Audio Output] Fix equalizer support
Equalizer was copying the output of the equalizer repeatedly to the
first output channel, instead of copying each channel correctly. This
had the effect of making the equalizer output adjusted audio to only the
left channel in stereo output, and possibly render the stream sounding
weird.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 14:11:46 -07:00
Christopher Snowhill
70b01a7cf1 [Plugin Controller] Add Cue sheet safety check
If somehow a plugin doesn't load, skip cuesheet should skip it anyway,
as we don't want any recursive loops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 13:39:06 -07:00
Christopher Snowhill
99f15679a6 [Audio Output] Remove unnecessary variables
These variables weren't being used anyway, so remove them.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 12:42:21 -07:00
dd20073a9b [Translation] The Big Translation Commit
- Plugs the Total duration text to macOS's localization technology
- Adds a proper Spanish translation
- Adapts certain dialogs to make them more suitable for translation

Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-01 12:37:30 -07:00
Christopher Snowhill
189999b7c2 [libOpenMPT] Remove unnecessary compile option
ENABLE_ASM isn't even used anywhere in the library any more.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 01:03:13 -07:00
Christopher Snowhill
904ce1c4cd Move all dialog XIBs for translation
Make it easier to translate the relevant dialogs now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 00:38:25 -07:00
Christopher Snowhill
c69433bf0b Updated credits file with Patron preference
Oops, missed that Patreon message, because Patreon did not email me.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 00:17:55 -07:00
Christopher Snowhill
23fa3c5e8e Update Credits.html in Spanish placeholder
Spanish translation really needs doing some time soon, maybe.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 00:12:03 -07:00
Christopher Snowhill
731e52c440 Build libOpenMPT from source once again
Bundle libOpenMPT as a dynamic framework, which should be safe once
again, now that there is only one version to bundle. Also, now it is
using the versions of libvorbisfile and libmpg123 that are bundled with
the player, instead of compiling minimp3 and stbvorbis.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 22:57:30 -07:00
Christopher Snowhill
919497148a Updated libOpenMPT to version 0.6.5-pre.1+r17609
This allows us to eliminate the requirement to continue bundling version
0.5.x of libOpenMPT for compatibility with macOS 10.13 through 10.14.x.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 21:28:28 -07:00
Christopher Snowhill
0832a8ea34 Restore the File Tree, now with a chooser button
Revert "Remove the file tree, as Sandbox does not permit"

This reverts commit 02ec735687.

This also changes how the File Tree choosing works.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 16:57:51 -07:00
Christopher Snowhill
830d3bf3be [Info Inspector] Improve formatting of sample rate
Sample rate now has a locale independent formatting, and no longer uses
scientific notation for large numbers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 00:28:03 -07:00
Christopher Snowhill
ff56b757e6 [Play Counts] Fix reporting play counts
Play counts are guaranteed to be reported on the correct track now.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 23:26:30 -07:00
Christopher Snowhill
608483ae60 Update libVGM and BASSMIDI, SF3 support
BASSMIDI now includes SF3 support, as well as several other changes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 23:25:59 -07:00
Christopher Snowhill
ee5b6be4bb [Sandbox] Synchronize write accesses to storage
Synchronize writing to the bookmark storage to the main thread.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 19:41:24 -07:00
Christopher Snowhill
ad7574be59 [Sandbox] Fix URL fragment removal function
This should be deleting from the #, including the #.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 19:41:20 -07:00
Christopher Snowhill
14a1157508 [Context Menu] Hook up Reset Play Counts item
Actually hook up the Reset Play Counts menu item so it actually does
something instead of just sitting there looking pretty.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 12:41:46 -07:00
Christopher Snowhill
7e2dda750e [Sandbox Config] Correctly test paths for files
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 12:11:22 -07:00
Christopher Snowhill
170310d7e5 [Sandbox] Remove startup folder consent prompt
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 12:02:59 -07:00
Christopher Snowhill
82179a5f10 [Sandbox] Support bookmarking individual files
Individually added files, directly opened by the user, may now store
bookmarks in settings.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 11:56:50 -07:00
Christopher Snowhill
02ec735687 Remove the file tree, as Sandbox does not permit
The Sandbox does not permit such controls to exist.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 11:56:01 -07:00
06739eb001 [Info Inspector] Made fields selectable, and fixed blending issue with
album art

Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-29 14:14:27 -04:00
bd8cfd300a [About Window] Fixed appearance for systems without Dark Mode
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-29 14:13:40 -04:00
Christopher Snowhill
3ef6a4204f [Sandbox] Handle file tree path config better
Handle the configuration better, by adding the path to the grants list
if it is newly configured.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 00:31:34 -07:00
Christopher Snowhill
f5bde75eda [Sandbox] Automatically save folder bookmarks
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 23:13:42 -07:00
Christopher Snowhill
6f126f4857 [Sandbox] Reduce entitlements granted by default
Since App Store approval decided these suddenly matter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 22:58:57 -07:00
Christopher Snowhill
22085d94f1 [Sandbox] Show grant dialog on launch if empty
If there are no configured paths, show the grant page on every startup.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 22:58:13 -07:00
Christopher Snowhill
de7afad3d4 [Playlist Loading] Process messages while loading
Process main queue messages by handling the loading in a background
queue, and sync it to the main thread periodically, while pausing to
wait for the results. This allows the file open dialog to return
immediately, and display loading progress on the status bar.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 20:26:55 -07:00
Christopher Snowhill
bf6a28a95e [Playlist View] Change ratings column to variable
The ratings column needs to be made variable width, for variable font
sizes. If anyone knows how to force the width to fit the current text,
I'm open to suggestions.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 20:23:43 -07:00
Christopher Snowhill
e491de0a93 [Crash Handling] Enable exceptions for debugging
Debug builds should have exceptions enabled, rather than crashing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 20:22:51 -07:00
Christopher Snowhill
f5412fbfec [About Dialog] Add needed WebKit framework
This is needed for macOS older than 11.0? 10.15? to open the About
dialog without crashing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 04:28:33 -07:00
Christopher Snowhill
44fb1efc73 [Play Info] Implement track rating system
The track ratings are stored in the same stats table as the play counts.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:59:36 -07:00
Christopher Snowhill
2d7c3b51c7 [Audio Output] Restart correct track
When restarting playback on the current track, restart the correct
track, in case restarting near the end of it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:59:23 -07:00
Christopher Snowhill
8fc981cb92 [Play Counts] Add option to (mass) reset counts
Add option to reset counts for all selected tracks on the playlist.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:59:10 -07:00
Christopher Snowhill
de43192453 [Play Counts] Commit play count edits to storage
Was calling commitEditing rather than commitPersistentStore, whoops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:59:00 -07:00
Christopher Snowhill
0515a63645 Resource templates touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:58:31 -07:00
Christopher Snowhill
3b1eefaabb [Play Counts] Track play counts of correct track
Track play counts for the correct track, even on short tracks. Also
correctly track the play count of the last played item in the play queue
which stops with bufferChain set to nil, so the previous iteration was
not tracking it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 01:57:00 -07:00
Christopher Snowhill
f1dd55b29e [File Tree] Pop permission grant on setting root
Setting the root path should now pop up a permission grant dialog.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-27 16:17:54 -07:00
Christopher Snowhill
9411e7bd45 [Sandbox Broker] Synchronize full access operation
Full access should be synchronized, otherwise rapid access to the same
path from different threads will cause crashes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-27 01:00:56 -07:00
Christopher Snowhill
dc02404184 [Metadata Cache] Actually run cleanup thread
Previously, the cleanup thread was not being run. Also, only reset the
metadata deduplication store when the cache is first emptied.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-27 01:00:48 -07:00
Christopher Snowhill
bb11567948 [Audio Output] Change converter back to Obj-C
Change converter source file back from Objective-C++ to Objective-C.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-27 01:00:42 -07:00
Christopher Snowhill
cacdc13d15 [Sandbox Broker] Bypass entitled paths
Include entitlement granted user folders in the permission check, so
that if the file or folder is nested under one of them, it allocates a
static permission object, rather than querying the list of configured
paths every time. This also prevents the player from popping open the
path grant / suggester dialog every time a default path is in the file
set listed, which should provide some relief to most users.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 23:53:06 -07:00
Christopher Snowhill
824648321c Attempt to fix Xcode Cloud CI
Try to generate the Info.plist before xcodebuild runs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 23:00:18 -07:00
Christopher Snowhill
ee79935a8a Update versioning setup
Versioning now happens before building Cog itself, and goes
into the Info.plist in the project directory. The original
file became a template file which is altered any time a
build occurs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 22:10:17 -07:00
Christopher Snowhill
b3591867e7
Update debug.yml
Switch to building on macos-12
2022-06-26 21:12:27 -07:00
Christopher Snowhill
bf03119373 Add Package.resolved back to repo
Guess we can't use Github actions now, because this file breaks those.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 20:27:09 -07:00
Christopher Snowhill
fc296bda77 Add CI scripts for Xcode Cloud
Add a post clone script for Xcode Cloud

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 20:23:40 -07:00
Christopher Snowhill
cc45750c0c Automatically unpack libraries before building
This required adding the included script in every project that links to
one of the bundled libraries. The script is designed to sleep for a
while if another thread is already extracting the libraries. The script
uses a temporary file as an extraction step lock, so other instances
sleep, and then detect the libraries.updated file, which is created
before the lock is removed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 20:12:06 -07:00
Christopher Snowhill
05960b1da1 Updated VGMStream to r1745-47-gfa55119d
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 15:12:00 -07:00
Christopher Snowhill
f516658a5e [Equalizer] Prevent crash on stop
Wait for the equalizer to be shut down properly by the main thread
before destroying it. Otherwise, the main thread could crash on stop,
due to accessing the equalizer handle while it's being torn down in the
output thread.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 14:48:02 -07:00
Christopher Snowhill
db181bde4d [Visualization System] Change API a bit
Now the API makes both PCM and FFT data optional, and will do nothing if
neither are requested. Also, it now supports a latency offset in seconds
with floating point precision. The two built-in visualizations currently
request zero larency. Increasing the latency asks for even older samples
while specifying a negative count requests samples from the "future"
relative to what the listener is hearing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 05:40:06 -07:00
Christopher Snowhill
8068a506b9 [Play Events] Don't bug on end of playlist
Don't bug out on end of playlist, when didBeginStream will receive a nil
track pointer, which should result in unsetting the current track in the
player, and not send a DidBegin notification to everything, including
the visualization views' event handlers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 05:40:00 -07:00
Christopher Snowhill
5c4cfb4053 [Play Counts] Fix counts for tracks with subsongs
Fix counts for tracks with subsongs from piling all the counts onto the
first subsong seen, by using the URL fragment in the filename check and
storage.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 04:38:18 -07:00
Christopher Snowhill
f04761ff42 [Audio Output] Fix for previous commit
This fixes the problem caused by the following commit:

050aaaf852

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 03:58:10 -07:00
Christopher Snowhill
adbf8aa7d1 Fix compilation
Oops, that last suggester change broke compilation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 03:08:39 -07:00
Christopher Snowhill
1e03df2aab [Sandbox] Don't try to grant access to container
Do not try to grant access to the app's container folder when searching
for paths to add.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 03:00:00 -07:00
Christopher Snowhill
0e4ff8a55c [Sandbox] Compare to the actual user paths
Remove the sandbox reference, because the user will add folders outside
the sandbox, and we have entitlements to access these folders.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 02:59:49 -07:00
Christopher Snowhill
bc309fe725 [Sandbox] Suggest URLs that are contained in CUEs
Cuesheets can now expose which URLs they contain, which may help with
sandbox path configuration. That is, if the CUE sheets are already
readable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 02:59:37 -07:00
Christopher Snowhill
378f710cee [Visualization] Resample more audio if present
If upsampling the audio by a significant factor, it may be necessary to
process more than one buffer at a time, rather than lose input.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 02:59:27 -07:00
Christopher Snowhill
aa3673ed33 [Audio Output] Better handle latency oddities
The visualization buffer now holds up to 45 seconds of loop, and the
latency measurement code now caps this at 30 seconds, and restarts the
output if latency exceeds 30 seconds, such as if a sound output is
reset.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 02:59:20 -07:00
Christopher Snowhill
c489d7ca91 [Audio Output] Play last track and stop correctly
Play last track up until it actually ends, and stop on command.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 06:43:16 -07:00
Christopher Snowhill
99fad892f0 [FFmpeg Decoder] Don't post redundant meta event
Don't post a metadata event on open, because inputs will relay it to the
player as an early notification bubble, which is unwanted.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 06:05:35 -07:00
Christopher Snowhill
8d10aa7e80 [Audio Output] Fix serious memory leakage
For one thing, the example code I followed was Swift and handled auto
releasing handles in the background, while Objective-C requires manual
handle reference management.

For two, there was no autoreleasepool around the block handling the
input audio chunks, which need to be released as they are pulled out and
disposed of. This also contributed to memory leakage.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 06:00:34 -07:00
Christopher Snowhill
f2dee9e0a7 [fdkaac] Update libfdk-aac to 2.0.2 with patches
Update fdk-aac library in dependencies package.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 05:14:09 -07:00
Christopher Snowhill
a1a084aabb [Audio Output] Fix serious deadlock issue
There was a serious deadlock issue. Now it is fixed. Whew.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 05:14:04 -07:00
Christopher Snowhill
cca447ca5e [InputNode] Syntax code fix
This code was misformatted.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 05:13:57 -07:00
Christopher Snowhill
4bc4b38489 [Play Count Info] Tabulate first seen info later
Tabulate first seen information when loading the metadata, rather than
when first adding the tracks to the playlist. This should fix first seen
information when metadata is available, as the information will be
useless without track titles.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 02:40:49 -07:00
Christopher Snowhill
ed8c8acc89 [Cuesheet] Greatly improve loading performance
Cuesheets were invoking a seek operation on open, rather than on first
playback, and this has a heavy toll on FFmpeg audio formats, apparently.
Defer the initial seek to the first readAudio call, and do not invoke it
if a seek was already called on that input session.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 02:40:43 -07:00
Christopher Snowhill
b3be8f9d11 [FFmpeg] Fix chapter handling and seeking
Fix chapter startup, and chapter seeking.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 02:40:36 -07:00
Christopher Snowhill
3852fab64b [FFmpeg] Seek including skip samples
This is essential for chapters, as otherwise, we would be skipping an
awful lot of samples every chapter, or every seek within a chapter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 01:43:57 -07:00
Christopher Snowhill
65fd9c71c6 [FFmpeg] Support files with chapters
Support file chapters, including metadata reading for each chapter.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 01:35:34 -07:00
Christopher Snowhill
43c709cd9f [FFmpeg] Deduce the length from the container
Determine the length of the file from the container, rather than the
individual audio stream. The former is more likely to be set than the
latter is.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:07:54 -07:00
Christopher Snowhill
a6b6c8120b [FFmpeg] Update FFmpeg library and decoder plugin
Update based on newest changes from upstream.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:07:41 -07:00
Christopher Snowhill
d79275b285 [Audio Output] Correctly configure WAVE layouts
Correctly configure AVFoundation with the channel layouts supported by
WAVEFORMATEXTENSIBLE speaker position flags, which includes varied
formats supported by FFmpeg and Core Audio inputs.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:07:31 -07:00
Christopher Snowhill
27924e246d [Audio Output] Correctly delay layout updates
Channel layout updates should be delayed when resampling, just like
sample format changes are.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:07:20 -07:00
Christopher Snowhill
94de049184 [FFmpeg] Update minimum platform for x86_64
Update minimum platform version to macOS 10.13.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:07:04 -07:00
Christopher Snowhill
43180640f2 [FFmpeg] Enable TrueHD decoder and demuxer
Oops, somehow I didn't enable TrueHD support.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:06:54 -07:00
Christopher Snowhill
67b6a3606f [FFmpeg] Update libfdk-aac fixed point patch
Update this patch to the latest FFmpeg master source, and update to use
fmtconvert instead of a naive for loop.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 00:06:48 -07:00
Christopher Snowhill
d563617998 [Audio Output] Stop immediately, and fix deadlocks
Stop output when requested, except on natural completion of the last
track in the play queue. Also fix deadlocks with stopping and
restarting.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 19:14:48 -07:00
Christopher Snowhill
6592144584 [Vorbis / Opus] Do not assume text encoding
Stream metadata encoding may not be UTF-8, even though the Vorbis
Comment specification clearly calls for this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 19:12:38 -07:00
Christopher Snowhill
26ac2b09fb Move static and dynamic libaries to archive
Please remember to unpack the archive before building, and
if it is updated by a future version.
2022-06-24 17:09:29 -07:00
Christopher Snowhill
156e25ed6a [Sandbox] Change preference dialog descriptions
Make the descriptions more apt to what they do.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:09:25 -07:00
Christopher Snowhill
f9490c1329 [OpenMPT / OpenMPT Legacy] Fix include paths
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:09:21 -07:00
Christopher Snowhill
93736565cb [mpg123] Fix include paths 2022-06-24 17:09:15 -07:00
Christopher Snowhill
431849414f [Sandbox Broker] Fix deadlock and crash
The crash was because we weren't copying the results array before
iterating over it, and the deadlock was because this was forced to go
through the main thread, rather than going through its calling thread,
which could lock up if the main thread was busy working with the Sandbox
Broker object.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:09:11 -07:00
Christopher Snowhill
ec91f61403 [Metadata] Commit first seen date for whole batch
Commit only once the entire batch is loaded and processed. Also commit
using the correct function.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:09:06 -07:00
Christopher Snowhill
32a595222d [OpenMPT / vgmstream] Made libraries pre-built
Made the OpenMPT / legacy OpenMPT and mpg123 libraries pre-built.
Changed the OpenMPT and vgmstream plugins to import the libraries as
they are now. Made mpg123 embedded and imported by the main binary,
since it's now shared by two plugins.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:09:02 -07:00
Christopher Snowhill
4540b88094 [Sandbox Broker] Copy results array
Hopefully this heads off a crash elsewhere.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:08:57 -07:00
Christopher Snowhill
0cc084b58b [Audio Output] Properly handle end of playlist
Handle audio on the end of the playlist, flushing playback until all
output stops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 03:47:41 -07:00
Christopher Snowhill
858f446597 [Audio Output] Synchronize access, report latency
Report resampler latency properly, and synchronize access to the
resampler objects.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 03:47:33 -07:00
Christopher Snowhill
3a9db65b53 [Visualization] Resample all visualizer audio
Visualizer audio is now resampled to 44100 Hz, for consistency across
the system.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 03:47:27 -07:00
Christopher Snowhill
2e45deb8d3 [Audio Output] Resample unsupported sample rates
These rates are too high for Apple's output routines, for some reason.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 03:47:19 -07:00
Christopher Snowhill
6d772562ca [Core Data Store] Fix startup playlist pruning
The playlist was being pruned of entries marked for deletion, but they
were not being pruned from the set that was then added to the player.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 00:52:33 -07:00
Christopher Snowhill
ba33218ddf [Path Suggester] Automatically pop where required
The Path Suggester will now automatically open when new files are added
to the playlist and a given path is not in the sandbox settings. It will
also pop for both the File Tree and MIDI SoundFont path configuration
settings being changed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 00:52:03 -07:00
Christopher Snowhill
b6621da410 [Sandbox Broker] Synchronize storage access
Synchronize storage access to main thread only, to prevent enumeration
from hitting a case of the main thread writing to the storage.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 00:46:45 -07:00
280b58dfd8 [Sandbox] Fixed path suggester window
Added a title to the window, make the table view resize properly, and
remove the font size inheritance from the main window.

Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-23 23:25:29 -07:00
Christopher Snowhill
dccb7f8b47 Replace Core Audio output with Core Media runtime
The output now uses AVSampleBufferAudioRenderer to play all formats, and
uses that to resample. It also supports Spatial Audio on macOS 12.0 or
newer. Note that there are some outstanding bugs with Spatial Audio
support. Namely that it appears to be limited to only 192 kHz at mono or
stereo, or 352800 Hz at surround configurations. This breaks DSD64
playback at stereo formats, as well as possibly other things. This is
entirely an Apple bug. I have reported it to Apple with reference code
FB10441301 for reference, in case anyone else wants to complain that it
isn't fixed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-23 23:23:13 -07:00
Christopher Snowhill
2c2a058126 Cog now requires macOS 10.13 as a minimum version
All optional fallback code for older versions has also been removed, and
everything now assumes 10.13.0 or newer. Some cases are still included
for point releases, such as 10.13.2.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-23 23:23:07 -07:00
Christopher Snowhill
27b2e8584d Updated VGMStream to r1745-37-g776c4d8c
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-22 19:33:06 -07:00
Christopher Snowhill
b2c354a32c Serialize persistent store update to main thread
This needs to be called on the main thread, as something may or may not
be enumerating over the data while this thread decides to call it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-22 19:11:06 -07:00
Christopher Snowhill
6867ef1e9a [Playlist Info Loader] Do not clear if loading
Do not clear the progress indicator if a loading task is already running
in the background, but instead return without doing anything.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-22 19:06:10 -07:00
Christopher Snowhill
71d8cb6246 Comply with App Store policies
Remove the stale Updates check preferences pane, and remove the donation
option from the menu, as it is considered In App Purchases.

Consider adding actual App Store In App Purchase options of "Buy me a
coffee" or similar, with no guarantee of return for the purchase, other
than furthering development. Make them repeatable purchases.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-22 16:27:51 -07:00
5e6c0a4c52
[About Window] Fix
Pull request #281 by @nevack.
2022-06-22 18:39:36 -04:00
e46dc12efc [About Window] Reorganized credits and added @nevack and myself in them
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-22 18:38:14 -04:00
Dzmitry Neviadomski
45345b810c Fix runtime warnings in Window/AboutWindowController.xib
Fix typo in File Owner class name and remove absent outlet.
2022-06-23 01:34:17 +03:00
9c371b8181
Merge pull request #280 from losnoco/nevack/about-window
AboutWindow adjustments
2022-06-22 18:28:54 -04:00
Dzmitry Neviadomski
43467b6978 AboutWindow adjustments
Allow opening links in default browser
Close window on Esc
Add rounded corners
2022-06-22 19:01:42 +03:00
Christopher Snowhill
05eb1c38a0 Changed updater script to handle new version strings
New version strings are in a different place, and Sparkle will no longer
be including the Git hash in the CFBundleVersion query, so we must get
it from the ZIP filename.
2022-06-22 01:17:02 -07:00
Christopher Snowhill
90b064fd3a Empty commit to sync version number 2022-06-21 23:52:21 -07:00
Christopher Snowhill
5cd39b919b Update README to add App Store information
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 23:51:50 -07:00
Christopher Snowhill
4db0fc1f5f One last attempt to fix CI
This should fix building. I don't know how I missed those.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 23:50:12 -07:00
Christopher Snowhill
73ad7901b0 Assign blank development team in project files
Hopefully this blank assignment will spare these files from being
touched by Xcode again in the future, when the variable in question is
imported from a developer supplied configuration file.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 23:29:17 -07:00
Christopher Snowhill
d3e069ddc0 Remove developer supplied configuration file
This file should not be referenced directly by projects, otherwise it
will be expected to exist, even in CI.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 23:29:07 -07:00
Christopher Snowhill
4a0d4bb093 [Volume Control] Only initialize view once
Only initialize viewController once, the first time the volume control
is opened. Re-initializing it can cause an error assigning it as first
responder to the volume slider popover view.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 22:52:52 -07:00
Christopher Snowhill
60523b985a Completely overhaul code signing practices
Redesign the code signing from the ground up. Now all bundles and their
embedded frameworks import the Shared.xcconfig file and enable its
settings, so they may be signed with Apple Development instead of sign
to run locally. This apparently isn't necessary for frameworks which are
embedded in the main app bundle directly, only for the bundles and their
frameworks.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 22:43:04 -07:00
Christopher Snowhill
82a1a400d9 Remove deep forced code signing option
This option should no longer be needed for anything.
2022-06-21 19:39:36 -07:00
d78b31d34e [Main Menu] Added Privacy Policy link in App Menu
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-21 22:30:30 -04:00
0679b146a3 [About Dialog] Switched to WebView for credits
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-21 22:20:18 -04:00
Christopher Snowhill
2821cb36b5 [Crashlytics] Require asking user consent
Require asking user consent for data transmission on first launch, or
otherwise disable sending crash reports by default.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 19:03:37 -07:00
Christopher Snowhill
eadac4c033 [Info Plist] Auto format XML escapes
Automatically format any XML escapes of file type association names.
Adjust Info.plist to account for this change.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-21 18:59:57 -07:00
535 changed files with 23700 additions and 43269 deletions

6
.gitignore vendored
View file

@ -53,8 +53,4 @@ Xcode-config/SENTRY_SETTINGS.xcconfig
/ThirdParty/vorbis/lib/libvorbisfile.3.dylib /ThirdParty/vorbis/lib/libvorbisfile.3.dylib
/ThirdParty/vorbis/lib/libvorbis.0.dylib /ThirdParty/vorbis/lib/libvorbis.0.dylib
/ThirdParty/soxr/lib/libsoxr.0.dylib /ThirdParty/soxr/lib/libsoxr.0.dylib
/ThirdParty/libprojectM/lib/libprojectM-4.4.dylib /ThirdParty/WavPack/lib/libwavpack.a
/ThirdParty/libprojectM/lib/libprojectM-4-playlist.4.dylib
/ThirdParty/libprojectM/lib/libboost_system.dylib
/ThirdParty/libprojectM/lib/libboost_atomic.dylib
/ThirdParty/libprojectM/lib/libboost_filesystem.dylib

6
.gitmodules vendored
View file

@ -19,9 +19,3 @@
[submodule "Frameworks/libsidplayfp/sidplayfp"] [submodule "Frameworks/libsidplayfp/sidplayfp"]
path = Frameworks/libsidplayfp/sidplayfp path = Frameworks/libsidplayfp/sidplayfp
url = https://github.com/kode54/libsidplayfp.git url = https://github.com/kode54/libsidplayfp.git
[submodule "ThirdParty/libprojectM/subprojects/presets-milkdrop-texture-pack"]
path = ThirdParty/libprojectM/subprojects/presets-milkdrop-texture-pack
url = https://github.com/projectM-visualizer/presets-milkdrop-texture-pack.git
[submodule "ThirdParty/libprojectM/subprojects/presets/presets-cream-of-the-crop"]
path = ThirdParty/libprojectM/subprojects/presets/presets-cream-of-the-crop
url = https://github.com/projectM-visualizer/presets-cream-of-the-crop.git

View file

@ -67,11 +67,6 @@
- (IBAction)delEntries:(id)sender; - (IBAction)delEntries:(id)sender;
- (IBAction)savePlaylist:(id)sender; - (IBAction)savePlaylist:(id)sender;
- (IBAction)openLiberapayPage:(id)sender;
- (IBAction)openPaypalPage:(id)sender;
- (IBAction)openKofiPage:(id)sender;
- (IBAction)openPatreonPage:(id)sender;
- (IBAction)privacyPolicy:(id)sender; - (IBAction)privacyPolicy:(id)sender;
- (IBAction)feedback:(id)sender; - (IBAction)feedback:(id)sender;
@ -113,8 +108,6 @@
- (IBAction)showRubberbandSettings:(id)sender; - (IBAction)showRubberbandSettings:(id)sender;
+ (void)globalShowRubberbandSettings; + (void)globalShowRubberbandSettings;
- (IBAction)checkForUpdates:(id)sender;
@property NSWindow *mainWindow; @property NSWindow *mainWindow;
@property NSWindow *miniWindow; @property NSWindow *miniWindow;

View file

@ -30,8 +30,6 @@
#import <MASShortcut/Shortcut.h> #import <MASShortcut/Shortcut.h>
#import <MASShortcut/MASDictionaryTransformer.h> #import <MASShortcut/MASDictionaryTransformer.h>
#import <Sparkle/Sparkle.h>
#import "PreferencesController.h" #import "PreferencesController.h"
#import "FeedbackController.h" #import "FeedbackController.h"
@ -44,22 +42,6 @@ BOOL kAppControllerShuttingDown = NO;
static AppController *kAppController = nil; static AppController *kAppController = nil;
@interface SparkleBridge : NSObject
+ (SPUStandardUpdaterController *)sharedStandardUpdaterController;
@end
@implementation SparkleBridge
+ (SPUStandardUpdaterController *)sharedStandardUpdaterController {
static SPUStandardUpdaterController *sharedStandardUpdaterController_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedStandardUpdaterController_ = [[SPUStandardUpdaterController alloc] initWithUpdaterDelegate: nil userDriverDelegate: nil];
});
return sharedStandardUpdaterController_;
}
@end
@implementation AppController { @implementation AppController {
BOOL _isFullToolbarStyle; BOOL _isFullToolbarStyle;
} }
@ -195,12 +177,6 @@ static BOOL consentLastEnabled = NO;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.sentryConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.sentryConsented" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kAppControllerContext];
#ifdef DEBUG
// Prevent updates automatically in debug builds
[[[SparkleBridge sharedStandardUpdaterController] updater] setAutomaticallyChecksForUpdates:NO];
#endif
[[[SparkleBridge sharedStandardUpdaterController] updater] setUpdateCheckInterval:3600];
[[totalTimeField cell] setBackgroundStyle:NSBackgroundStyleRaised]; [[totalTimeField cell] setBackgroundStyle:NSBackgroundStyleRaised];
[self.infoButton setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")]; [self.infoButton setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")];
@ -617,22 +593,6 @@ static BOOL consentLastEnabled = NO;
[theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; [theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
} }
- (IBAction)openLiberapayPage:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://liberapay.com/kode54"]];
}
- (IBAction)openPaypalPage:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/paypalme/kode54"]];
}
- (IBAction)openKofiPage:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://ko-fi.com/kode54"]];
}
- (IBAction)openPatreonPage:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.patreon.com/kode54"]];
}
- (IBAction)privacyPolicy:(id)sender { - (IBAction)privacyPolicy:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"PrivacyPolicyURL", @"Privacy policy URL from Iubenda.")]]; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"PrivacyPolicyURL", @"Privacy policy URL from Iubenda.")]];
} }
@ -664,6 +624,9 @@ static BOOL consentLastEnabled = NO;
NSNumber *fontSize = @(fFontSize); NSNumber *fontSize = @(fFontSize);
[userDefaultsValuesDict setObject:fontSize forKey:@"fontSize"]; [userDefaultsValuesDict setObject:fontSize forKey:@"fontSize"];
NSString *feedURLdefault = @"https://cogcdn.cog.losno.co/mercury.xml";
[userDefaultsValuesDict setObject:feedURLdefault forKey:@"SUFeedURL"];
[userDefaultsValuesDict setObject:@"enqueueAndPlay" forKey:@"openingFilesBehavior"]; [userDefaultsValuesDict setObject:@"enqueueAndPlay" forKey:@"openingFilesBehavior"];
[userDefaultsValuesDict setObject:@"enqueue" forKey:@"openingFilesAlteredBehavior"]; [userDefaultsValuesDict setObject:@"enqueue" forKey:@"openingFilesAlteredBehavior"];
@ -673,7 +636,7 @@ static BOOL consentLastEnabled = NO;
[userDefaultsValuesDict setObject:@(CogStatusStopped) forKey:@"lastPlaybackStatus"]; [userDefaultsValuesDict setObject:@(CogStatusStopped) forKey:@"lastPlaybackStatus"];
[userDefaultsValuesDict setObject:@"dls appl" forKey:@"midiPlugin"]; [userDefaultsValuesDict setObject:@"BASSMIDI" forKey:@"midiPlugin"];
[userDefaultsValuesDict setObject:@"default" forKey:@"midi.flavor"]; [userDefaultsValuesDict setObject:@"default" forKey:@"midi.flavor"];
@ -705,6 +668,18 @@ static BOOL consentLastEnabled = NO;
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict]; [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
[[NSUserDefaults standardUserDefaults] synchronize]; [[NSUserDefaults standardUserDefaults] synchronize];
// And if the existing feed URL is broken due to my ineptitude with the above defaults, fix it
NSSet<NSString *> *brokenFeedURLs = [NSSet setWithObjects:
@"https://kode54.net/cog/stable.xml",
@"https://kode54.net/cog/mercury.xml"
@"https://www.kode54.net/cog/mercury.xml",
@"https://f.losno.co/cog/mercury.xml",
nil];
NSString *feedURL = [[NSUserDefaults standardUserDefaults] stringForKey:@"SUFeedURL"];
if([brokenFeedURLs containsObject:feedURL]) {
[[NSUserDefaults standardUserDefaults] setValue:feedURLdefault forKey:@"SUFeedURL"];
}
NSString *oldMidiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"]; NSString *oldMidiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"];
if(oldMidiPlugin) { if(oldMidiPlugin) {
[[NSUserDefaults standardUserDefaults] setValue:oldMidiPlugin forKey:@"midiPlugin"]; [[NSUserDefaults standardUserDefaults] setValue:oldMidiPlugin forKey:@"midiPlugin"];
@ -717,6 +692,11 @@ static BOOL consentLastEnabled = NO;
if([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"FluidSynth"]) { if([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"FluidSynth"]) {
[[NSUserDefaults standardUserDefaults] setValue:@"BASSMIDI" forKey:@"midiPlugin"]; [[NSUserDefaults standardUserDefaults] setValue:@"BASSMIDI" forKey:@"midiPlugin"];
} }
NSString *midiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"];
if([midiPlugin length] == 8 && [[midiPlugin substringFromIndex:4] isEqualToString:@"appl"]) {
[[NSUserDefaults standardUserDefaults] setObject:@"BASSMIDI" forKey:@"midiPlugin"];
}
} }
MASShortcut *shortcutWithMigration(NSString *oldKeyCodePrefName, MASShortcut *shortcutWithMigration(NSString *oldKeyCodePrefName,
@ -987,10 +967,6 @@ static NSDictionary *shortcutDefaults = nil;
[kAppController showRubberbandSettings:kAppController]; [kAppController showRubberbandSettings:kAppController];
} }
- (IBAction)checkForUpdates:(id)sender {
[[SparkleBridge sharedStandardUpdaterController] checkForUpdates:[[NSApplication sharedApplication] delegate]];
}
- (void)selectTrack:(id)sender { - (void)selectTrack:(id)sender {
PlaylistEntry *pe = (PlaylistEntry *)sender; PlaylistEntry *pe = (PlaylistEntry *)sender;
@try { @try {

View file

@ -129,6 +129,7 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
@"volumeLimit": @(YES), @"volumeLimit": @(YES),
@"enableHrtf": @(NO), @"enableHrtf": @(NO),
@"enableHeadTracking": @(NO), @"enableHeadTracking": @(NO),
@"enableHDCD": @(NO),
/*@"rubberbandEngine": @"faster",*/ /*@"rubberbandEngine": @"faster",*/
@"rubberbandTransients": @"crisp", @"rubberbandTransients": @"crisp",
@"rubberbandDetector": @"compound", @"rubberbandDetector": @"compound",
@ -356,8 +357,12 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
} }
- (IBAction)prev:(id)sender { - (IBAction)prev:(id)sender {
double pos = [audioPlayer amountPlayed];
if(pos < 5.0) {
if([playlistController prev] == NO) if([playlistController prev] == NO)
return; return;
}
[self playEntry:[playlistController currentEntry]]; [self playEntry:[playlistController currentEntry]];
} }

View file

@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
BOOL observersRegistered; BOOL observersRegistered;
BOOL halveDSDVolume; BOOL halveDSDVolume;
BOOL enableHDCD;
void *hdcd_decoder; void *hdcd_decoder;
BOOL formatRead; BOOL formatRead;

View file

@ -408,8 +408,10 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
- (void)addObservers { - (void)addObservers {
if(!observersRegistered) { if(!observersRegistered) {
halveDSDVolume = NO; halveDSDVolume = NO;
enableHDCD = NO;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.halveDSDVolume" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kChunkListContext]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.halveDSDVolume" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kChunkListContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHDCD" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kChunkListContext];
observersRegistered = YES; observersRegistered = YES;
} }
@ -418,6 +420,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
- (void)removeObservers { - (void)removeObservers {
if(observersRegistered) { if(observersRegistered) {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.halveDSDVolume" context:kChunkListContext]; [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.halveDSDVolume" context:kChunkListContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHDCD" context:kChunkListContext];
observersRegistered = NO; observersRegistered = NO;
} }
@ -456,6 +459,8 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
if([keyPath isEqualToString:@"values.halveDSDVolume"]) { if([keyPath isEqualToString:@"values.halveDSDVolume"]) {
halveDSDVolume = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"halveDSDVolume"]; halveDSDVolume = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"halveDSDVolume"];
} else if([keyPath isEqualToString:@"values.enableHDCD"]) {
enableHDCD = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableHDCD"];
} }
} }
@ -693,6 +698,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
inputFormat.mChannelsPerFrame == 2 && inputFormat.mChannelsPerFrame == 2 &&
inputFormat.mSampleRate == 44100) { inputFormat.mSampleRate == 44100) {
// possibly HDCD, run through decoder // possibly HDCD, run through decoder
[self addObservers];
if(hdcd_decoder) { if(hdcd_decoder) {
free(hdcd_decoder); free(hdcd_decoder);
hdcd_decoder = NULL; hdcd_decoder = NULL;
@ -831,21 +837,35 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes
if(hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale if(hdcd_decoder) { // implied bits per sample is 16, produces 32 bit int scale
samplesRead = bytesReadFromInput / 2; samplesRead = bytesReadFromInput / 2;
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0; const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
if(isUnsigned) if(isUnsigned) {
if(!inputChanged) {
memcpy(&tempData[buffer_adder], inputBuffer, samplesRead * 2);
inputBuffer = &tempData[buffer_adder];
inputChanged = YES;
}
convert_u16_to_s16((int16_t *)inputBuffer, samplesRead); convert_u16_to_s16((int16_t *)inputBuffer, samplesRead);
convert_s16_to_hdcd_input((int32_t *)(&tempData[buffer_adder]), (int16_t *)inputBuffer, samplesRead); isUnsigned = NO;
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)(&tempData[buffer_adder]), (int)(samplesRead / 2)); }
const size_t buffer_adder2 = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
convert_s16_to_hdcd_input((int32_t *)(&tempData[buffer_adder2]), (int16_t *)inputBuffer, samplesRead);
hdcd_process_stereo((hdcd_state_stereo_t *)hdcd_decoder, (int32_t *)(&tempData[buffer_adder2]), (int)(samplesRead / 2));
if(((hdcd_state_stereo_t *)hdcd_decoder)->channel[0].sustain && if(((hdcd_state_stereo_t *)hdcd_decoder)->channel[0].sustain &&
((hdcd_state_stereo_t *)hdcd_decoder)->channel[1].sustain) { ((hdcd_state_stereo_t *)hdcd_decoder)->channel[1].sustain) {
hdcdSustained = YES; hdcdSustained = YES;
} }
if(enableHDCD) {
gain = 2.0; gain = 2.0;
bitsPerSample = 32; bitsPerSample = 32;
bytesReadFromInput = samplesRead * 4; bytesReadFromInput = samplesRead * 4;
isUnsigned = NO; isUnsigned = NO;
inputBuffer = &tempData[buffer_adder]; inputBuffer = &tempData[buffer_adder2];
inputChanged = YES; inputChanged = YES;
} else {
// Discard the output of the decoder and process again
goto process16bit;
}
} else if(bitsPerSample <= 16) { } else if(bitsPerSample <= 16) {
process16bit:
samplesRead = bytesReadFromInput / 2; samplesRead = bytesReadFromInput / 2;
const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0; const size_t buffer_adder = (inputBuffer == &tempData[0]) ? buffer_adder_base : 0;
if(isUnsigned) { if(isUnsigned) {

View file

@ -45,6 +45,8 @@
double sampleRatio; double sampleRatio;
BOOL observersAdded;
float volumeScale; float volumeScale;
void *floatBuffer; void *floatBuffer;

View file

@ -66,8 +66,6 @@ static void *kConverterNodeContext = &kConverterNodeContext;
extrapolateBuffer = NULL; extrapolateBuffer = NULL;
extrapolateBufferSize = 0; extrapolateBufferSize = 0;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:kConverterNodeContext];
#ifdef LOG_CHAINS #ifdef LOG_CHAINS
[self initLogFiles]; [self initLogFiles];
#endif #endif
@ -76,6 +74,20 @@ static void *kConverterNodeContext = &kConverterNodeContext;
return self; return self;
} }
- (void)addObservers {
if(!observersAdded) {
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:(NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew) context:kConverterNodeContext];
observersAdded = YES;
}
}
- (void)removeObservers {
if(observersAdded) {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext];
observersAdded = NO;
}
}
void scale_by_volume(float *buffer, size_t count, float volume) { void scale_by_volume(float *buffer, size_t count, float volume) {
if(volume != 1.0) { if(volume != 1.0) {
size_t unaligned = (uintptr_t)buffer & 15; size_t unaligned = (uintptr_t)buffer & 15;
@ -269,6 +281,7 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
size_t inputSamples = ioNumberPackets / floatFormat.mBytesPerPacket; size_t inputSamples = ioNumberPackets / floatFormat.mBytesPerPacket;
ioNumberPackets = (UInt32)inputSamples; ioNumberPackets = (UInt32)inputSamples;
ioNumberPackets = (UInt32)ceil((float)ioNumberPackets * sampleRatio); ioNumberPackets = (UInt32)ceil((float)ioNumberPackets * sampleRatio);
ioNumberPackets += soxr_delay(soxr);
ioNumberPackets = (ioNumberPackets + 255) & ~255; ioNumberPackets = (ioNumberPackets + 255) & ~255;
size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket; size_t newSize = ioNumberPackets * floatFormat.mBytesPerPacket;
@ -285,7 +298,6 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
size_t outputDone = 0; size_t outputDone = 0;
if(!skipResampler) { if(!skipResampler) {
ioNumberPackets += soxr_delay(soxr);
soxr_process(soxr, (float *)(((uint8_t *)inputBuffer) + inpOffset), inputSamples, &inputDone, floatBuffer, ioNumberPackets, &outputDone); soxr_process(soxr, (float *)(((uint8_t *)inputBuffer) + inpOffset), inputSamples, &inputDone, floatBuffer, ioNumberPackets, &outputDone);
if(latencyEatenPost) { if(latencyEatenPost) {
@ -334,6 +346,7 @@ void scale_by_volume(float *buffer, size_t count, float volume) {
if(nodeChannelConfig) { if(nodeChannelConfig) {
[chunk setChannelConfig:nodeChannelConfig]; [chunk setChannelConfig:nodeChannelConfig];
} }
[self addObservers];
scale_by_volume(floatBuffer, ioNumberPackets / sizeof(float), volumeScale); scale_by_volume(floatBuffer, ioNumberPackets / sizeof(float), volumeScale);
[chunk setStreamTimestamp:streamTimestamp]; [chunk setStreamTimestamp:streamTimestamp];
[chunk setStreamTimeRatio:streamTimeRatio]; [chunk setStreamTimeRatio:streamTimeRatio];
@ -488,7 +501,7 @@ static float db_to_scale(float db) {
- (void)dealloc { - (void)dealloc {
DLog(@"Converter dealloc"); DLog(@"Converter dealloc");
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling" context:kConverterNodeContext]; [self removeObservers];
paused = NO; paused = NO;
[self cleanUp]; [self cleanUp];

View file

@ -28,9 +28,6 @@ using std::atomic_long;
#import <CogAudio/HeadphoneFilter.h> #import <CogAudio/HeadphoneFilter.h>
//#define OUTPUT_LOG //#define OUTPUT_LOG
#ifdef OUTPUT_LOG
#import <stdio.h>
#endif
@class OutputNode; @class OutputNode;
@ -102,7 +99,7 @@ using std::atomic_long;
ChunkList *outputBuffer; ChunkList *outputBuffer;
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
FILE *_logFile; NSFileHandle *_logFile;
#endif #endif
} }

View file

@ -19,6 +19,10 @@
#import <CogAudio/VisualizationController.h> #import <CogAudio/VisualizationController.h>
#ifdef OUTPUT_LOG
#import <NSFileHandle+CreateFile.h>
#endif
extern void scale_by_volume(float *buffer, size_t count, float volume); extern void scale_by_volume(float *buffer, size_t count, float volume);
static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation"; static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation";
@ -27,23 +31,19 @@ static BOOL fadeAudio(const float *inSamples, float *outSamples, size_t channels
float _fadeLevel = *fadeLevel; float _fadeLevel = *fadeLevel;
BOOL towardZero = fadeStep < 0.0; BOOL towardZero = fadeStep < 0.0;
BOOL stopping = NO; BOOL stopping = NO;
for(size_t i = 0; i < count; ++i) { size_t maxCount = (size_t)floor(fabs(fadeTarget - _fadeLevel) / fabs(fadeStep));
for(size_t j = 0; j < channels; ++j) { if(maxCount) {
outSamples[j] += inSamples[j] * _fadeLevel; size_t countToDo = MIN(count, maxCount);
for(size_t i = 0; i < channels; ++i) {
_fadeLevel = *fadeLevel;
vDSP_vrampmuladd(&inSamples[i], channels, &_fadeLevel, &fadeStep, &outSamples[i], channels, countToDo);
} }
inSamples += channels;
outSamples += channels;
_fadeLevel += fadeStep;
if(towardZero && _fadeLevel <= fadeTarget) {
_fadeLevel = fadeTarget;
fadeStep = 0.0;
stopping = YES;
break;
} else if(!towardZero && _fadeLevel >= fadeTarget) {
_fadeLevel = fadeTarget;
fadeStep = 0.0;
stopping = YES;
} }
if(maxCount <= count) {
if(!towardZero && maxCount < count) {
vDSP_vadd(&inSamples[maxCount * channels], 1, &outSamples[maxCount * channels], 1, &outSamples[maxCount * channels], 1, (count - maxCount) * channels);
}
stopping = YES;
} }
*fadeLevel = _fadeLevel; *fadeLevel = _fadeLevel;
return stopping; return stopping;
@ -146,7 +146,7 @@ static void *kOutputCoreAudioContext = &kOutputCoreAudioContext;
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
NSString *logName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"CogAudioLog.raw"]; NSString *logName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"CogAudioLog.raw"];
_logFile = fopen([logName UTF8String], "wb"); _logFile = [NSFileHandle fileHandleForWritingAtPath:logName createFile:YES];
#endif #endif
} }
@ -621,9 +621,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
AudioChunk *chunk = [self renderInput:512]; AudioChunk *chunk = [self renderInput:512];
size_t frameCount = 0; size_t frameCount = 0;
if(chunk) { if(chunk && (frameCount = [chunk frameCount])) {
frameCount = [chunk frameCount];
[outputLock lock]; [outputLock lock];
[outputBuffer addChunk:chunk]; [outputBuffer addChunk:chunk];
[outputLock unlock]; [outputLock unlock];
@ -648,7 +646,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
__block NSMutableArray *faders = self->fadedBuffers; __block NSMutableArray *faders = self->fadedBuffers;
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
__block FILE *logFile = _logFile; __block NSFileHandle *logFile = _logFile;
#endif #endif
_au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags *_Nonnull actionFlags, const AudioTimeStamp *_Nonnull timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList *_Nonnull inputData) { _au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags *_Nonnull actionFlags, const AudioTimeStamp *_Nonnull timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList *_Nonnull inputData) {
@ -715,17 +713,6 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
} }
double secondsRendered = (double)renderedSamples / format->mSampleRate; double secondsRendered = (double)renderedSamples / format->mSampleRate;
float volumeScale = 1.0;
double sustained;
sustained = _self->secondsHdcdSustained;
if(sustained > 0) {
if(sustained < secondsRendered) {
_self->secondsHdcdSustained = 0.0;
} else {
_self->secondsHdcdSustained = sustained - secondsRendered;
volumeScale = 0.5;
}
}
[fadersLock lock]; [fadersLock lock];
for(size_t i = 0; i < [faders count];) { for(size_t i = 0; i < [faders count];) {
@ -739,9 +726,14 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
} }
[fadersLock unlock]; [fadersLock unlock];
scale_by_volume(outSamples, frameCount * channels, volumeScale * _self->volume); scale_by_volume(outSamples, frameCount * channels, _self->volume);
[_self updateLatency:secondsRendered]; [_self updateLatency:secondsRendered];
#ifdef OUTPUT_LOG
NSData *outData = [NSData dataWithBytes:outSamples length:frameCount * format->mBytesPerPacket];
[logFile writeData:outData];
#endif
} }
#ifdef _DEBUG #ifdef _DEBUG
@ -924,7 +916,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
} }
#ifdef OUTPUT_LOG #ifdef OUTPUT_LOG
if(_logFile) { if(_logFile) {
fclose(_logFile); [_logFile closeFile];
_logFile = NULL; _logFile = NULL;
} }
#endif #endif

View file

@ -155,12 +155,13 @@ void lpc_extrapolate2(float *const data, const size_t data_len, const int nch, c
*extrapolate_buffer_size = new_size; *extrapolate_buffer_size = new_size;
} }
float *tdata = (float *)(*extrapolate_buffer); // for 1 channel only double *aut = (double *)(*extrapolate_buffer);
double *lpc = (double *)(*extrapolate_buffer + aut_size);
double *aut = (double *)(*extrapolate_buffer + tdata_size); float *tdata = (float *)(*extrapolate_buffer + aut_size + lpc_size); // for 1 channel only
double *lpc = (double *)(*extrapolate_buffer + tdata_size + aut_size);
float *lpci = (float *)(*extrapolate_buffer + tdata_size + aut_size + lpc_size); float *lpci = (float *)(*extrapolate_buffer + aut_size + lpc_size + tdata_size);
float *work = (float *)(*extrapolate_buffer + tdata_size + aut_size + lpc_size + lpci_size); float *work = (float *)(*extrapolate_buffer + aut_size + lpc_size + tdata_size + lpci_size);
for(int c = 0; c < nch; c++) { for(int c = 0; c < nch; c++) {
if(extra_bkwd) { if(extra_bkwd) {

View file

@ -1531,11 +1531,6 @@
<action selector="showWindow:" target="Hd4-Wy-Rfl" id="xfd-8T-SL4"/> <action selector="showWindow:" target="Hd4-Wy-Rfl" id="xfd-8T-SL4"/>
</connections> </connections>
</menuItem> </menuItem>
<menuItem title="Check for Updates..." id="302">
<connections>
<action selector="checkForUpdates:" target="226" id="jEY-i9-qwZ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="1100"> <menuItem isSeparatorItem="YES" id="1100">
<modifierMask key="keyEquivalentModifierMask" command="YES"/> <modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem> </menuItem>
@ -1546,45 +1541,6 @@
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="0ig-xg-gkg"/> <menuItem isSeparatorItem="YES" id="0ig-xg-gkg"/>
<menuItem title="Donate" id="751">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Donate" id="kue-p2-G0Y">
<items>
<menuItem title="LiberaPay" id="nyW-nI-abw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="openLiberapayPage:" target="226" id="pS6-Hj-tIm"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Mbf-yW-WGC"/>
<menuItem title="One time" enabled="NO" id="wLp-NA-5u2">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="PayPal" id="xcs-tx-Viz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="openPaypalPage:" target="226" id="oxr-P1-35O"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="txC-Jd-Gez"/>
<menuItem title="Recurring" enabled="NO" id="iRb-7e-iMC">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Ko-fi" id="CVM-rp-UJe">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="openKofiPage:" target="226" id="gax-6q-SuW"/>
</connections>
</menuItem>
<menuItem title="Patreon" id="NXj-oA-q3F">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="openPatreonPage:" target="226" id="LFz-4J-b6o"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Send Feedback..." id="303"> <menuItem title="Send Feedback..." id="303">
<connections> <connections>
<action selector="feedback:" target="226" id="GSH-G5-qM1"/> <action selector="feedback:" target="226" id="GSH-G5-qM1"/>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22113.1" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22113.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>

View file

@ -2,13 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>
</array>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-jit</key> <key>com.apple.security.cs.allow-jit</key>
<true/> <true/>
<key>com.apple.security.files.user-selected.read-write</key> <key>com.apple.security.files.user-selected.read-write</key>

View file

@ -97,8 +97,6 @@
830C37A127B95E3000E02BB0 /* Equalizer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 830C379F27B95E3000E02BB0 /* Equalizer.xib */; }; 830C37A127B95E3000E02BB0 /* Equalizer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 830C379F27B95E3000E02BB0 /* Equalizer.xib */; };
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; }; 830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; };
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; }; 830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; };
830EDD1B2D813DEA00988EA8 /* libprojectM-4.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E9EEBA2D80634600F1D22D /* libprojectM-4.4.dylib */; };
830EDD1C2D813DF600988EA8 /* libprojectM-4-playlist.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E9EEB92D80634600F1D22D /* libprojectM-4-playlist.4.dylib */; };
831B99BF27C23E88005A969B /* Cog.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 831B99BE27C23E88005A969B /* Cog.sdef */; }; 831B99BF27C23E88005A969B /* Cog.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 831B99BE27C23E88005A969B /* Cog.sdef */; };
83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; }; 83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; };
83256B69286661FC0036D9C0 /* libmpg123.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83256B69286661FC0036D9C0 /* libmpg123.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@ -138,7 +136,6 @@
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D73C277419F700245CE0 /* SQLiteStore.m */; }; 8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D73C277419F700245CE0 /* SQLiteStore.m */; };
8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */; }; 8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */; };
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8372C93027C785BE00E250C9 /* MAD.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */; }; 8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */; };
8377C6B927B900F000E8BC0F /* SpectrumItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C6B827B900F000E8BC0F /* SpectrumItem.m */; }; 8377C6B927B900F000E8BC0F /* SpectrumItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C6B827B900F000E8BC0F /* SpectrumItem.m */; };
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 837DC92A285B05710005C58A /* CoreData.framework */; }; 837DC92B285B05710005C58A /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 837DC92A285B05710005C58A /* CoreData.framework */; };
@ -153,8 +150,6 @@
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33802D06CF4100D0D770 /* SpectrumViewCG.m */; }; 838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33802D06CF4100D0D770 /* SpectrumViewCG.m */; };
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33822D06CF4100D0D770 /* SpectrumWindowController.m */; }; 838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33822D06CF4100D0D770 /* SpectrumWindowController.m */; };
838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33862D06CFCA00D0D770 /* SpeedButton.m */; }; 838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33862D06CFCA00D0D770 /* SpeedButton.m */; };
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; };
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838F851D256B4E5E00C3E614 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83922FB6286B1AA900A0B039 /* WebKit.framework */; }; 83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83922FB6286B1AA900A0B039 /* WebKit.framework */; };
839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; }; 839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; };
839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; }; 839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; };
@ -174,6 +169,7 @@
83B61E2429A8296500CD0580 /* LyricsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83B61E2229A8296500CD0580 /* LyricsWindow.xib */; }; 83B61E2429A8296500CD0580 /* LyricsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83B61E2229A8296500CD0580 /* LyricsWindow.xib */; };
83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B61E2729A82A0200CD0580 /* LyricsWindowController.m */; }; 83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B61E2729A82A0200CD0580 /* LyricsWindowController.m */; };
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B72E2A279044F6006007A3 /* libfdk-aac.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B72E2A279044F6006007A3 /* libfdk-aac.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B73B602D8FC05A00A57F08 /* minimp3.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BC5AB020E4C87100631CD4 /* DualWindow.m */; }; 83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BC5AB020E4C87100631CD4 /* DualWindow.m */; };
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; }; 83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; };
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; }; 83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; };
@ -183,17 +179,6 @@
83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B1DA0F6330D400694C57 /* Feedback.xib */; }; 83BC5AC420E4CE9000631CD4 /* Feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B1DA0F6330D400694C57 /* Feedback.xib */; };
83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */; }; 83D0380F24A40DFB004CF90F /* CogAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */; };
83E9A0D02D804A1800F1D22D /* ProjectMView.m in Sources */ = {isa = PBXBuildFile; fileRef = 83E9A0CF2D804A1800F1D22D /* ProjectMView.m */; };
83E9EEB32D805C5300F1D22D /* textures in Copy Files */ = {isa = PBXBuildFile; fileRef = 83E9EEB22D805C5300F1D22D /* textures */; };
83E9EEB52D805C7B00F1D22D /* presets in Copy Files */ = {isa = PBXBuildFile; fileRef = 83E9EEB42D805C7B00F1D22D /* presets */; };
83E9EEBB2D80634600F1D22D /* libboost_atomic.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E9EEB62D80634600F1D22D /* libboost_atomic.dylib */; };
83E9EEBC2D80634600F1D22D /* libboost_system.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E9EEB82D80634600F1D22D /* libboost_system.dylib */; };
83E9EEBE2D80634600F1D22D /* libboost_filesystem.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83E9EEB72D80634600F1D22D /* libboost_filesystem.dylib */; };
83E9EEC02D80639E00F1D22D /* libprojectM-4.4.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E9EEBA2D80634600F1D22D /* libprojectM-4.4.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83E9EEC12D8063A100F1D22D /* libboost_system.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E9EEB82D80634600F1D22D /* libboost_system.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83E9EEC22D8063A400F1D22D /* libboost_atomic.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E9EEB62D80634600F1D22D /* libboost_atomic.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83E9EEC32D8063A700F1D22D /* libboost_filesystem.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E9EEB72D80634600F1D22D /* libboost_filesystem.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83E9EEC42D8063AD00F1D22D /* libprojectM-4-playlist.4.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83E9EEB92D80634600F1D22D /* libprojectM-4-playlist.4.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 83F9FFEE2D6EB75B00026576 /* Sentry */; }; 83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 83F9FFEE2D6EB75B00026576 /* Sentry */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
@ -526,20 +511,6 @@
remoteGlobalIDString = 836FB52C1820538700B3AD2D; remoteGlobalIDString = 836FB52C1820538700B3AD2D;
remoteInfo = Hively; remoteInfo = Hively;
}; };
8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8372C92327C785BD00E250C9;
remoteInfo = MAD;
};
8372C93B27C7893100E250C9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8372C92227C785BD00E250C9;
remoteInfo = MAD;
};
8375B36117FFEF010092A79F /* PBXContainerItemProxy */ = { 8375B36117FFEF010092A79F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */; containerPortal = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */;
@ -561,6 +532,20 @@
remoteGlobalIDString = 83B06686180D5668008E3612; remoteGlobalIDString = 83B06686180D5668008E3612;
remoteInfo = MIDI; remoteInfo = MIDI;
}; };
83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8372C92327C785BD00E250C9;
remoteInfo = minimp3;
};
83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8372C92227C785BD00E250C9;
remoteInfo = minimp3;
};
83BB13C120E4E38E00723731 /* PBXContainerItemProxy */ = { 83BB13C120E4E38E00723731 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */; containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */;
@ -682,8 +667,6 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 7; dstSubfolderSpec = 7;
files = ( files = (
83E9EEB52D805C7B00F1D22D /* presets in Copy Files */,
83E9EEB32D805C5300F1D22D /* textures in Copy Files */,
ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */, ED69CA3B25BE2A390090B90D /* Preferences.preferencePane in Copy Files */,
); );
name = "Copy Files"; name = "Copy Files";
@ -695,8 +678,8 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 13; dstSubfolderSpec = 13;
files = ( files = (
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */,
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */, 8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */,
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */,
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */, 83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */,
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */, 834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */,
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */, 834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */,
@ -732,11 +715,6 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
83E9EEC32D8063A700F1D22D /* libboost_filesystem.dylib in CopyFiles */,
83E9EEC22D8063A400F1D22D /* libboost_atomic.dylib in CopyFiles */,
83E9EEC12D8063A100F1D22D /* libboost_system.dylib in CopyFiles */,
83E9EEC42D8063AD00F1D22D /* libprojectM-4-playlist.4.dylib in CopyFiles */,
83E9EEC02D80639E00F1D22D /* libprojectM-4.4.dylib in CopyFiles */,
836DF617298F6F1700CD0580 /* libsoxr.0.dylib in CopyFiles */, 836DF617298F6F1700CD0580 /* libsoxr.0.dylib in CopyFiles */,
836EF0E127BB98AB00BF35B2 /* libopusfile.0.dylib in CopyFiles */, 836EF0E127BB98AB00BF35B2 /* libopusfile.0.dylib in CopyFiles */,
836EF0E027BB98A800BF35B2 /* libopus.0.dylib in CopyFiles */, 836EF0E027BB98A800BF35B2 /* libopus.0.dylib in CopyFiles */,
@ -753,7 +731,6 @@
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */, 83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */,
8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */, 8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */,
ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */, ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */,
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */,
17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */, 17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -978,7 +955,7 @@
8370D739277419D200245CE0 /* SQLiteStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteStore.h; sourceTree = "<group>"; }; 8370D739277419D200245CE0 /* SQLiteStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteStore.h; sourceTree = "<group>"; };
8370D73C277419F700245CE0 /* SQLiteStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLiteStore.m; sourceTree = "<group>"; }; 8370D73C277419F700245CE0 /* SQLiteStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLiteStore.m; sourceTree = "<group>"; };
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MAD.xcodeproj; path = Plugins/MAD/MAD.xcodeproj; sourceTree = "<group>"; }; 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = minimp3.xcodeproj; path = Plugins/minimp3/minimp3.xcodeproj; sourceTree = "<group>"; };
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; }; 8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewSK.m; path = Visualization/SpectrumViewSK.m; sourceTree = "<group>"; }; 8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewSK.m; path = Visualization/SpectrumViewSK.m; sourceTree = "<group>"; };
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpectrumViewSK.h; path = Visualization/SpectrumViewSK.h; sourceTree = "<group>"; }; 8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpectrumViewSK.h; path = Visualization/SpectrumViewSK.h; sourceTree = "<group>"; };
@ -1008,7 +985,6 @@
838EE79E29A8556000CD0580 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LyricsWindow.strings; sourceTree = "<group>"; }; 838EE79E29A8556000CD0580 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
838EE7A029A8556500CD0580 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LyricsWindow.strings; sourceTree = "<group>"; }; 838EE7A029A8556500CD0580 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = "<group>"; }; 838F84FF25687C5C00C3E614 /* Cog-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Cog-Bridging-Header.h"; sourceTree = "<group>"; };
838F851D256B4E5E00C3E614 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; sourceTree = "<group>"; };
83922FB6286B1AA900A0B039 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 83922FB6286B1AA900A0B039 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
839614A1286ED97200D3EEDB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutWindowController.xib; sourceTree = "<group>"; }; 839614A1286ED97200D3EEDB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutWindowController.xib; sourceTree = "<group>"; };
839614A4286ED98600D3EEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AboutWindowController.strings; sourceTree = "<group>"; }; 839614A4286ED98600D3EEDB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AboutWindowController.strings; sourceTree = "<group>"; };
@ -1064,15 +1040,6 @@
83D0380E24A40DF2004CF90F /* CogAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = CogAssets.xcassets; sourceTree = "<group>"; }; 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = CogAssets.xcassets; sourceTree = "<group>"; };
83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AdPlug.xcodeproj; path = Plugins/AdPlug/AdPlug.xcodeproj; sourceTree = "<group>"; }; 83D3C5FC201C674D005564CB /* AdPlug.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AdPlug.xcodeproj; path = Plugins/AdPlug/AdPlug.xcodeproj; sourceTree = "<group>"; };
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = Plugins/OpenMPT/OpenMPT.xcodeproj; sourceTree = "<group>"; }; 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPT.xcodeproj; path = Plugins/OpenMPT/OpenMPT.xcodeproj; sourceTree = "<group>"; };
83E9A0CE2D8049FA00F1D22D /* ProjectMView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ProjectMView.h; path = Visualization/ProjectMView.h; sourceTree = "<group>"; };
83E9A0CF2D804A1800F1D22D /* ProjectMView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ProjectMView.m; path = Visualization/ProjectMView.m; sourceTree = "<group>"; };
83E9EEB22D805C5300F1D22D /* textures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = textures; path = "ThirdParty/libprojectM/subprojects/presets-milkdrop-texture-pack/textures"; sourceTree = "<group>"; };
83E9EEB42D805C7B00F1D22D /* presets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = presets; path = ThirdParty/libprojectM/subprojects/presets; sourceTree = "<group>"; };
83E9EEB62D80634600F1D22D /* libboost_atomic.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_atomic.dylib; path = ThirdParty/libprojectM/lib/libboost_atomic.dylib; sourceTree = "<group>"; };
83E9EEB72D80634600F1D22D /* libboost_filesystem.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_filesystem.dylib; path = ThirdParty/libprojectM/lib/libboost_filesystem.dylib; sourceTree = "<group>"; };
83E9EEB82D80634600F1D22D /* libboost_system.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libboost_system.dylib; path = ThirdParty/libprojectM/lib/libboost_system.dylib; sourceTree = "<group>"; };
83E9EEB92D80634600F1D22D /* libprojectM-4-playlist.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libprojectM-4-playlist.4.dylib"; path = "ThirdParty/libprojectM/lib/libprojectM-4-playlist.4.dylib"; sourceTree = "<group>"; };
83E9EEBA2D80634600F1D22D /* libprojectM-4.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libprojectM-4.4.dylib"; path = "ThirdParty/libprojectM/lib/libprojectM-4.4.dylib"; sourceTree = "<group>"; };
83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = "<group>"; }; 83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; }; 8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; };
@ -1109,11 +1076,6 @@
8355D6B8180613FB00D05687 /* Security.framework in Frameworks */, 8355D6B8180613FB00D05687 /* Security.framework in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */, 83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */,
830EDD1C2D813DF600988EA8 /* libprojectM-4-playlist.4.dylib in Frameworks */,
830EDD1B2D813DEA00988EA8 /* libprojectM-4.4.dylib in Frameworks */,
83E9EEBB2D80634600F1D22D /* libboost_atomic.dylib in Frameworks */,
83E9EEBC2D80634600F1D22D /* libboost_system.dylib in Frameworks */,
83E9EEBE2D80634600F1D22D /* libboost_filesystem.dylib in Frameworks */,
17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */, 17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */,
835FAC7F27BCDF5B00BA8562 /* libavif.a in Frameworks */, 835FAC7F27BCDF5B00BA8562 /* libavif.a in Frameworks */,
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */, 83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */,
@ -1122,7 +1084,6 @@
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */, 838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */,
17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */, 17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */,
17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */, 17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */,
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */,
83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */, 83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */,
17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */, 17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */,
17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */, 17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */,
@ -1168,7 +1129,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */, ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */,
838F851D256B4E5E00C3E614 /* Sparkle.framework */,
17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */, 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
); );
@ -1178,11 +1138,6 @@
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
83E9EEB62D80634600F1D22D /* libboost_atomic.dylib */,
83E9EEB72D80634600F1D22D /* libboost_filesystem.dylib */,
83E9EEB82D80634600F1D22D /* libboost_system.dylib */,
83E9EEB92D80634600F1D22D /* libprojectM-4-playlist.4.dylib */,
83E9EEBA2D80634600F1D22D /* libprojectM-4.4.dylib */,
838A33732D06A9B100D0D770 /* librubberband.3.dylib */, 838A33732D06A9B100D0D770 /* librubberband.3.dylib */,
836DF616298F6EC400CD0580 /* libsoxr.0.dylib */, 836DF616298F6EC400CD0580 /* libsoxr.0.dylib */,
83256B672866617F0036D9C0 /* libmpg123.0.dylib */, 83256B672866617F0036D9C0 /* libmpg123.0.dylib */,
@ -1315,7 +1270,7 @@
17C808830C3BD181005707C4 /* HTTPSource.xcodeproj */, 17C808830C3BD181005707C4 /* HTTPSource.xcodeproj */,
83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */, 83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */,
8E8D40820CBB036600135C1B /* M3u.xcodeproj */, 8E8D40820CBB036600135C1B /* M3u.xcodeproj */,
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */, 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */,
83B0669C180D5668008E3612 /* MIDI.xcodeproj */, 83B0669C180D5668008E3612 /* MIDI.xcodeproj */,
17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */, 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */,
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */, 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */,
@ -1507,8 +1462,6 @@
29B97314FDCFA39411CA2CEA /* Cog */ = { 29B97314FDCFA39411CA2CEA /* Cog */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
83E9EEB42D805C7B00F1D22D /* presets */,
83E9EEB22D805C5300F1D22D /* textures */,
0A1B412E286F6301008A6A44 /* Localizable.stringsdict */, 0A1B412E286F6301008A6A44 /* Localizable.stringsdict */,
83D0380E24A40DF2004CF90F /* CogAssets.xcassets */, 83D0380E24A40DF2004CF90F /* CogAssets.xcassets */,
83859520234FEB35004E9946 /* Cog.entitlements */, 83859520234FEB35004E9946 /* Cog.entitlements */,
@ -1820,14 +1773,6 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
8372C92B27C785BD00E250C9 /* Products */ = {
isa = PBXGroup;
children = (
8372C93027C785BE00E250C9 /* MAD.bundle */,
);
name = Products;
sourceTree = "<group>";
};
8377C66027B8CF2300E8BC0F /* Visualization */ = { 8377C66027B8CF2300E8BC0F /* Visualization */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1841,8 +1786,6 @@
838A33812D06CF4100D0D770 /* SpectrumWindowController.h */, 838A33812D06CF4100D0D770 /* SpectrumWindowController.h */,
838A33822D06CF4100D0D770 /* SpectrumWindowController.m */, 838A33822D06CF4100D0D770 /* SpectrumWindowController.m */,
8377C66427B8CF7A00E8BC0F /* VisualizationController.h */, 8377C66427B8CF7A00E8BC0F /* VisualizationController.h */,
83E9A0CE2D8049FA00F1D22D /* ProjectMView.h */,
83E9A0CF2D804A1800F1D22D /* ProjectMView.m */,
); );
name = Visualization; name = Visualization;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1873,6 +1816,14 @@
path = LyricsWindow; path = LyricsWindow;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
83B73B5C2D8FC05A00A57F08 /* Products */ = {
isa = PBXGroup;
children = (
83B73B602D8FC05A00A57F08 /* minimp3.bundle */,
);
name = Products;
sourceTree = "<group>";
};
83BB13AE20E4E38E00723731 /* Products */ = { 83BB13AE20E4E38E00723731 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2033,8 +1984,8 @@
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
83B73B642D8FD74000A57F08 /* PBXTargetDependency */,
8327DBA8293CAD0A00CD0580 /* PBXTargetDependency */, 8327DBA8293CAD0A00CD0580 /* PBXTargetDependency */,
8372C93C27C7893100E250C9 /* PBXTargetDependency */,
83489C6A2782F76900BDCEA2 /* PBXTargetDependency */, 83489C6A2782F76900BDCEA2 /* PBXTargetDependency */,
ED69CBC625BE32B40090B90D /* PBXTargetDependency */, ED69CBC625BE32B40090B90D /* PBXTargetDependency */,
834D793E20E4EFD200C4A5CC /* PBXTargetDependency */, 834D793E20E4EFD200C4A5CC /* PBXTargetDependency */,
@ -2179,10 +2130,6 @@
ProductGroup = 8E8D40830CBB036600135C1B /* Products */; ProductGroup = 8E8D40830CBB036600135C1B /* Products */;
ProjectRef = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */; ProjectRef = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */;
}, },
{
ProductGroup = 8372C92B27C785BD00E250C9 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
},
{ {
ProductGroup = ED69CBB925BE328C0090B90D /* Products */; ProductGroup = ED69CBB925BE328C0090B90D /* Products */;
ProjectRef = ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */; ProjectRef = ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */;
@ -2191,6 +2138,10 @@
ProductGroup = 83B0669D180D5668008E3612 /* Products */; ProductGroup = 83B0669D180D5668008E3612 /* Products */;
ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */; ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */;
}, },
{
ProductGroup = 83B73B5C2D8FC05A00A57F08 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
},
{ {
ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */; ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */;
ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */; ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */;
@ -2406,13 +2357,6 @@
remoteRef = 836FB5461820538800B3AD2D /* PBXContainerItemProxy */; remoteRef = 836FB5461820538800B3AD2D /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
8372C93027C785BE00E250C9 /* MAD.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = MAD.bundle;
remoteRef = 8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83B066A1180D5669008E3612 /* MIDI.bundle */ = { 83B066A1180D5669008E3612 /* MIDI.bundle */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = wrapper.cfbundle; fileType = wrapper.cfbundle;
@ -2420,6 +2364,13 @@
remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */; remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
83B73B602D8FC05A00A57F08 /* minimp3.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = minimp3.bundle;
remoteRef = 83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83BB13C220E4E38E00723731 /* vgmstream.bundle */ = { 83BB13C220E4E38E00723731 /* vgmstream.bundle */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = wrapper.cfbundle; fileType = wrapper.cfbundle;
@ -2588,7 +2539,6 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
83E9A0D02D804A1800F1D22D /* ProjectMView.m in Sources */,
83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */, 83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */,
8D11072D0486CEB800E47090 /* main.m in Sources */, 8D11072D0486CEB800E47090 /* main.m in Sources */,
8E75757109F31D5A0080F1EE /* DNDArrayController.m in Sources */, 8E75757109F31D5A0080F1EE /* DNDArrayController.m in Sources */,
@ -2798,11 +2748,6 @@
name = Hively; name = Hively;
targetProxy = 836FB5A518206F1500B3AD2D /* PBXContainerItemProxy */; targetProxy = 836FB5A518206F1500B3AD2D /* PBXContainerItemProxy */;
}; };
8372C93C27C7893100E250C9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MAD;
targetProxy = 8372C93B27C7893100E250C9 /* PBXContainerItemProxy */;
};
8375B36217FFEF010092A79F /* PBXTargetDependency */ = { 8375B36217FFEF010092A79F /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
name = ArchiveSource; name = ArchiveSource;
@ -2813,6 +2758,11 @@
name = MIDI; name = MIDI;
targetProxy = 83B06702180D5776008E3612 /* PBXContainerItemProxy */; targetProxy = 83B06702180D5776008E3612 /* PBXContainerItemProxy */;
}; };
83B73B642D8FD74000A57F08 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = minimp3;
targetProxy = 83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */;
};
83BCB8D917FC96F800760340 /* PBXTargetDependency */ = { 83BCB8D917FC96F800760340 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
name = HighlyComplete; name = HighlyComplete;
@ -3000,10 +2950,7 @@
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = ThirdParty/avif/include;
ThirdParty/avif/include,
ThirdParty/libprojectM/include,
);
IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES; IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES; IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
@ -3022,7 +2969,6 @@
"$(PROJECT_DIR)/ThirdParty/soxr/lib", "$(PROJECT_DIR)/ThirdParty/soxr/lib",
"$(PROJECT_DIR)", "$(PROJECT_DIR)",
"$(PROJECT_DIR)/ThirdParty/rubberband/lib", "$(PROJECT_DIR)/ThirdParty/rubberband/lib",
"$(PROJECT_DIR)/ThirdParty/libprojectM/lib",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-D__MACOSX__", "-D__MACOSX__",
@ -3063,10 +3009,7 @@
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/ThirdParty/Frameworks"; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/ThirdParty/Frameworks";
GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = ThirdParty/avif/include;
ThirdParty/avif/include,
ThirdParty/libprojectM/include,
);
IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES; IBC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES; IBSC_COMPILER_USE_NIBKEYEDARCHIVER_FOR_MACOS = YES;
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
@ -3085,7 +3028,6 @@
"$(PROJECT_DIR)/ThirdParty/soxr/lib", "$(PROJECT_DIR)/ThirdParty/soxr/lib",
"$(PROJECT_DIR)", "$(PROJECT_DIR)",
"$(PROJECT_DIR)/ThirdParty/rubberband/lib", "$(PROJECT_DIR)/ThirdParty/rubberband/lib",
"$(PROJECT_DIR)/ThirdParty/libprojectM/lib",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-D__MACOSX__", "-D__MACOSX__",

View file

@ -6,10 +6,10 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa.git", "location" : "https://github.com/getsentry/sentry-cocoa.git",
"state" : { "state" : {
"revision" : "21223d1c864db0561d91f48d80f269a363a1625d", "revision" : "6c81e671154e63464dd6749b7ba3279dd390a146",
"version" : "8.47.0" "version" : "8.50.1"
} }
} }
], ],
"version" : 2 "version" : 3
} }

View file

@ -1,11 +0,0 @@
David Bryant <david@wavpack.com>
Sebastian Dröge <slomo@circular-chaos.org>
Joachim Henke <j-o@users.sourceforge.net>
Joël R. Langlois <joel.r.langlois@gmail.com>
Alexis Ballier <aballier@gentoo.org>
Stephen <stephengroat@users.noreply.github.com>
Phil Eichinger <phil@zankapfel.net>
Sebastian Ramacher <sramacher@debian.org>
luxagen <hello@luxagen.com>
Martin Koegler <martin.koegler@chello.at>
nu774 <honeycomb77@gmail.com>

View file

@ -1,366 +0,0 @@
2019-12-14 David Bryant <david@wavpack.com>
* cli/wvunpack.c
-s option: add "5.1 surround side" and "7.1 surround" to reported channel configurations
2019-12-13 David Bryant <david@wavpack.com>
* cli/riff[_write].c, cli/wave64[_write].c, cli/caff[_write].c, cli/dsf[_write].c, cli/dsdiff[_write].c:
split header readers & writers so that wvunpack doesn't link libwavpack encoder
2019-12-12 David Bryant <david@wavpack.com>
* cli/riff.c, cli/wave64.c, cli/caff.c:
-i option: display warning when dropping PCM samples from end of file
2019-12-11 David Bryant <david@wavpack.com>
* cli/wavpack.c:
fix a WAV header if user specified -i (to ignore length) and we can make it valid
2019-12-08 David Bryant <david@wavpack.com>
* fuzzing/fuzzer.cc, fuzzing/fuzzer_seed_corpus.zip, etc...:
add fuzzing directory with corpus and other files for oss-fuzz
2019-12-08 David Bryant <david@wavpack.com>
* src/open_utils.c:
fix possible memory leak on opening corrupted files
2019-12-08 David Bryant <david@wavpack.com>
* src/common_utils.c, src/pack_dsd.c, src/unpack_dsd.c, src/wavpack_local.h:
- fix potential memory leak when seeking in DSD files
- simplify DSD fast mode lookup buffer allocations
2019-12-08 David Bryant <david@wavpack.com>
* src/unpack.c, src/unpack_dsd.c, src/unpack_seek.c:
seeking fixes:
- fix crash during seek to corrupted block
- check header size before malloc()
- fix overlapping memcpy()
2019-11-30 David Bryant <david@wavpack.com>
* src/pack.c:
- provide more configuration sanity checks to aid application debugging
- force max_blocksize even so bitstream buffer overflow detection works
2019-04-09 David Bryant <david@wavpack.com>
* cli/import_id3.c:
issue #69: add TPUB (Publisher) to accepted ID3v2 tag fields
2019-03-05 David Bryant <david@wavpack.com>
* cli/wave64.c:
issue #68: clear WaveHeader at start to prevent uninitialized read
2019-03-05 David Bryant <david@wavpack.com>
* cli/dsdiff.c:
issue #67: make sure sample rate is specified and non-zero in DFF files
2019-03-04 David Bryant <david@wavpack.com>
* cli/caff.c:
issue #66: make sure CAF files have a "desc" chunk
2019-03-02 David Bryant <david@wavpack.com>
* cli/dsdiff.c:
issue #65: makre sure DSDIFF files have a valid channel count
2018-12-23 evpobr <evpobr@gmail.com>
* include/wavpack.h src/wavpack_local.h:
remove duplication so that wavpack_local.h can include wavpack.h
2018-12-16 evpobr <evpobr@gmail.com>
* Makefile.am, CMakeLists.txt
add CMake project
2018-12-09 orbea <orbea@fredslev.dk>
* cli/Makefile.am:
fix command-line builds with slibtool
2018-12-08 Ørjan Malde <foxyred333@gmail.com>
* src/extra[12].c, src/pack.c, src/pack_x64.S, src/unpack.c, src/unpack_x64.S, src/wavpack_local.h:
x64 ASM support for midipix
2018-11-29 David Bryant <david@wavpack.com>
* src/pack_utils.c:
issue #53: error on zero sample rate
- CVE-2018-19840
2018-11-29 David Bryant <david@wavpack.com>
* src/open_utils.c:
issue #54: fix potential out-of-bounds heap read
- CVE-2018-19841
2018-11-29 David Bryant <david@wavpack.com>
* src/open_filename.c:
Windows only: use wvc file when verifying encode when source is stdin
2018-09-03 Mike Tzou <Chocobo1@users.noreply.github.com>
* cli/import_id3.c, cli/wvgain.c, cli/open_raw.c, cli/wvparser.c, cli/wvunpack.c, winamp/in_wv.c:
printf() format specifiers
memory leaks
2018-08-26 David Bryant <david@wavpack.com>
* cli/dsdiff.c, cli/dsf.c, cli/caff.c:
issue #41 issue #42 issue #43: sanitize input files to prevent crashes
2018-06-02 David Bryant <david@wavpack.com>
* src/unpack_armv7.S:
fix thumb interworking on ARM by adding .type for assembly functions
2018-04-30 David Bryant <david@wavpack.com>
* cli/import_id3.c, cli/wavpack.c:
allow ID3v2.3 tag import from any file type (not just DSF)
2018-04-29 David Bryant <david@wavpack.com>
* cli/import_id3.c:
handle ID3v2.3 TXXX tags using description for APEv2 item name (w/ case formatting)
2018-04-24 David Bryant <david@wavpack.com>
* cli/riff.c, cli/wave64.c:
issue #30 issue #31 issue #32: no multiple format chunks in WAV or W64
- CVE-2018-10536
- CVE-2018-10537
* cli/dsdiff.c, cli/riff.c, cli/wave64.c:
issue #33, sanitize size of unknown chunks before malloc()
- CVE-2018-10538
- CVE-2018-10539
- CVE-2018-10540
2018-04-17 David Bryant <david@wavpack.com>
* cli/import_id3.c:
add a bunch more ID3v2.3 tag entries
make ImportID3v2() more robust (e.g. always set bytes_used)
2018-04-08 David Bryant <david@wavpack.com>
* src/common_utils.c:
fix memory leaks
2018-02-11 David Bryant <david@wavpack.com>
* cli/caff.c:
issue #26, fix buffer overflows and bad allocs on corrupt CAF files
- CVE-2018-7254
2018-02-10 David Bryant <david@wavpack.com>
* cli/dsdiff.c:
issue #28, do not overwrite heap on corrupt DSDIFF file
- CVE-2018-7253
2018-02-04 David Bryant <david@wavpack.com>
* cli/riff.c:
issue #27, do not overwrite stack on corrupt RF64 file
- CVE-2018-6767
2017-10-29 David Bryant <david@wavpack.com>
* src/read_words.c:
issue #24, another C++ compiler fix, this time for _BitScanForward()
2017-10-28 David Bryant <david@wavpack.com>
* Makefile.am:
add README.md to extra distribution files
2017-10-20 Joël R. Langlois <joel.r.langlois@gmail.com>
* README, README.md:
Updated README to Markdown format.
2017-10-12 Joël R. Langlois <joel.r.langlois@gmail.com>
* src/decorr_utils.c, src/entropy_utils.c, src/open_legacy.c,
src/open_utils.c, src/tag_utils.c, src/tags.c, src/unpack3.c,
src/unpack3_open.c, src/unpack_dsd.c, src/unpack_seek.c,
src/unpack_utils.c:
Fixed errors when compiling using a C++ compiler.
2017-09-30 David Bryant <david@wavpack.com>
* cli/import_id3.c:
experimental fix to handle ID3v2.3 tags that [incorrectly] use synchsafe for the frame size
2017-08-31 David Bryant <david@wavpack.com>
* cli/wavpack.c
briefly describe other utilities in help displays for wavpack
2017-07-24 David Bryant <david@wavpack.com>
* cli/md5.h
do not try to use libcrypto on OS X
2017-07-23 David Bryant <david@wavpack.com>
* cli/md5.c, cli/md5.h, cli/wavpack.c, cli/wvtest.c, cli/wvunpack.c, configure.ac:
use Alexander Peslyak's MD5 implementation (or libcrypto if present) to fix
unaligned access coredump on OpenBSD/sparc64 (reported on openbsd-ports)
2017-03-19 David Bryant <david@wavpack.com>
* src/write_words.c:
improve quality of scan_word() results on very short blocks (via multiple passes)
2017-03-01 David Bryant <david@wavpack.com>
* cli/wavpack.c, cli/wvgain.c, cli/wvtag.c, cli/wvunpack.c:
add required parens to correct precedence error/warning
2017-02-26 David Bryant <david@wavpack.com>
* cli/wavpack.c, cli/wvgain.c, cli/wvtag.c, cli/wvunpack.c:
refactor debug logging mode so that we can turn on a forced arg dump
* src/common_utils.c, src/wavpack_local.h:
provide for a "close" callback to be installed for dumping accumulated statistics
* configure.ac, src/unpack_armv7.S:
SSAT instruction required armv6, now we should work on all ARMs using a pair of shifts instead
2017-02-18 Alexis Ballier <aballier@gentoo.org>
* configure.ac:
configure: Restrict arm assembly to armv7 only.
ARM assembly in wavpack is armv7 only it seems.
I have reports this causes build failures on armv5: https://bugs.gentoo.org/show_bug.cgi?id=609168
2017-02-16 David Bryant <david@wavpack.com>
* cli/import_id3.c, cli/wvtag.c:
fix GitHub issue #19 (new dependency on wchar_t) by removing dependency
2017-01-22 David Bryant <david@wavpack.com>
* .travis.yml:
do more exhaustive testing for Travis (but should be faster)
2017-01-22 Stephen <stephengroat@users.noreply.github.com>
* .travis.yml:
enable travis ci build and testing (#17)
Create .travis.yml
fix for running tests
limit to smaller test suite
add quotes to get spaces in arg
remove linux clang builds
move to trusty for clang
2017-01-18 David Bryant <david@wavpack.com>
* ChangeLog:
refine change log and add updated plugins
* audition/cool_wv4.c, audition/readme.odt, audition/readme.pdf:
update Cool Edit / Audition filter to 3.1
* COPYING, license.txt, winamp/in_wv.c, winamp/installer/WavPackPlugin1.nsi:
update winamp to 2.8.0.3 and license dates
2017-01-17 David Bryant <david@wavpack.com>
* ChangeLog:
first pass at 5.1.0 changelog
* cli/Makefile.am, cli/import_id3.c, cli/wavpack.c:
fix Darwin build (iconv) and ptr warnings
improve --import-id3 console messaging
2017-01-16 David Bryant <david@wavpack.com>
* wavpackdll/wavpackdll.rc, wavpackexe/wavpack.vcproj, winamp/in_wv.c:
bump DLL version and fix MSVC build
* cli/utils.h, cli/wavpack.c, cli/wvgain.c, cli/wvtag.c, cli/wvunpack.c,
configure.ac, src/wavpack_version.h:
update version to 5.1.0 and bump some copyright dates
* src/pack.c:
do not write data in NEW_CONFIG_BLOCK for "do not care" bits in qmode
* src/unpack_dsd.c:
shorter DSD decimation filter with less HF rolloff and lower CPU use
2017-01-15 David Bryant <david@wavpack.com>
* doc/wavpack_doc.html:
update user manual for 5.1.0 and wvtag
2017-01-14 David Bryant <david@wavpack.com>
* cli/wvtag.c:
allow multiple files on Windows, update "help"
* man/Makefile.am, man/wavpack.1, man/wavpack.xml, man/wvgain.1, man/wvgain.xml,
man/wvtag.1, man/wvtag.xml, man/wvunpack.1, man/wvunpack.xml:
add man page for wvtag and update the other man pages (--import-id3)
2017-01-13 David Bryant <david@wavpack.com>
* cli/Makefile.am, cli/wavpack.c:
add --import-id3 option to wavpack executable
(works with original DSF files and when transcoding)
* cli/import_id3.c, cli/wvtag.c:
refactor ID3 import code to calculate the total number of bytes being imported
- allow total size and item count to be returned even on dry runs
- plug a memory leak in the dry run
2017-01-11 David Bryant <david@wavpack.com>
* src/pack.c, src/unpack.c:
fix issue where noise-shaping falsely triggers lossy muting
- only in very rare cases (detected with pathological testing)
- also fix (again) macro that disables lossy muting
2017-01-08 David Bryant <david@wavpack.com>
* src/pack_utils.c:
fix regression causing non-byte-aligned audio (e.g., 12-bit)
to lose the actual reduced bit-depth indication (although
there was no effect on integrity or compression ratio)
2017-01-07 David Bryant <david@wavpack.com>
* cli/import_id3.c, cli/wvtag.c, wavpack.sln, wvtagexe/wvtag.vcproj:
add wvtag to MSVC build and fix warnings (and one mistake)
* src/open_filename.c:
fix MSVC build (broken by portability enhancements...sigh)
2017-01-06 David Bryant <david@wavpack.com>
* cli/Makefile.am, cli/import_id3.c, cli/wvtag.c:
new cli tool "wvtag" to manipulate APEv2 tags on existing WavPack files
(includes new facility to import ID3v2.3 tag items from Sony DSF files)
* cli/wavpack.c:
add --pre-quantize-round to settings tag
* cli/wvgain.c, cli/wvunpack.c:
copy TextToUTF8() BOM fix into other modules that use it for filename lists

View file

@ -1,717 +0,0 @@
---------------------------------
Release 5.2.0 - December 15, 2019
---------------------------------
WavPack Library Source Code - 5.2.0
wavpack.exe (command-line encoder) - 5.2.0
wvunpack.exe (command-line decoder) - 5.2.0
wvgain.exe (command-line ReplayGain scanner) - 5.2.0
wvtag.exe (command-line tagging utility) - 5.2.0
----------------------------------------------------
fixed: potential security issues including the following CVEs:
CVE-2018-19840 CVE-2018-19841 CVE-2018-10536
CVE-2018-10537 CVE-2018-10538 CVE-2018-10539
CVE-2018-10540 CVE-2018-7254 CVE-2018-7253
CVE-2018-6767
added: support for CMake, Travis CI, and Google's OSS-fuzz
fixed: use correction file for encode verify (pipe input, Windows)
fixed: correct WAV header with actual length (pipe input, -i option)
fixed: thumb interworking and not needing v6 architecture (ARM asm)
added: handle more ID3v2.3 tag items and from all file types
fixed: coredump on Sparc64 (changed MD5 implementation)
fixed: handle invalid ID3v2.3 tags from sacd-ripper
fixed: several corner-case memory leaks
--------------------------------
Release 5.1.0 - January 18, 2017
--------------------------------
WavPack Library Source Code - 5.1.0
wavpack.exe (command-line encoder) - 5.1.0
wvunpack.exe (command-line decoder) - 5.1.0
wvgain.exe (command-line ReplayGain scanner) - 5.1.0
wvtag.exe (command-line tagging utility) - 5.1.0
----------------------------------------------------
added: all new command-line tagging utility (wvtag)
added: option to import ID3v2.3 tags from Sony DSF files
fixed: fuzz test failures from AFL reported on SourceForge
improved: DSD decimation filter (less HF rolloff & CPU use)
fixed: non-byte audio depths (12-bit, 20-bit) not showing
fixed: rare case of noise-shaping triggering a lossy mute
fixed: recognize UTF-8 BOM when reading text files
fixed: a few portability issues
in_wv.dll (winamp plugin) - 2.8.0.3
CoreWavPack DirectShow Filters - 1.5.1.0
AmioWavpack.amio (Adobe Audition Plugins) - 2.1
cool_wv4.flt (Cool Edit / Audition filter) - 3.1
------------------------------------------------
updated: see 5.1.0 library changes
--------------------------------
Release 5.0.0 - December 6, 2016
--------------------------------
WavPack Library Source Code - 5.0.0
wavpack.exe (command-line encoder) - 5.0.0
wvunpack.exe (command-line decoder) - 5.0.0
wvgain.exe (command-line ReplayGain scanner) - 5.0.0
----------------------------------------------------
added: multiple input formats, including RF64, Wave64, and CAF
added: lossless DSD audio in Philips DSDIFF and Sony DSF files
fixed: seeking in > 2GB WavPack files (new stream reader)
fixed: accept > 4GB source audio files (all formats)
improved: increase maximum samples from 2^32 to 2^40
added: block checksums for robustness to corruption
added: support for non-standard channel identities
removed: support for legacy WavPack files (< 4.0)
added: block decoder for streaming applications
fixed: many small fixes and improvements
added: all new pdf documentation
AmioWavpack.amio (Adobe Audition Plugins) - 2.0
-----------------------------------------------
improved: all new dialog for WavPack settings
fixed: handle unlimited audio file size
fixed: save all Amio channel identities
added: save/restore APEv2 tags
in_wv.dll (winamp plugin) - 2.8.0.2
CoreWavPack DirectShow Filters - 1.5.0.0
cool_wv4.flt (Cool Edit / Audition filter) - 3.0
------------------------------------------------
updated: see 5.0.0 library changes
-------------------------------
Release 4.80.0 - March 28, 2016
-------------------------------
WavPack Library Source Code - 4.80.0
wavpack.exe (command-line encoder) - 4.80.0
wvunpack.exe (command-line decoder) - 4.80.0
wvgain.exe (command-line ReplayGain scanner) - 4.80.0
-----------------------------------------------------
added: full Unicode support on Windows platform
added: new option --pre-quantize to truncate high-resolution files
to a reasonable depth (e.g., 20-bit) for better compression
fixed: Debian bug #793320 (executable stack)
fixed: LargeAddressAware problem reported on HA
fixed: several "fuzz test" failures reported on GitHub
fixed: repack blocks after possible arithmetic overflows
improved: faster assembly code for mono packing
improved: portability for various platforms
wvtest.exe (command-line libwavpack test suite) - 4.80.0
--------------------------------------------------------
added: exhaustive test for WavpackSeekSample() API
in_wv.dll (winamp plugin) - 2.8.0.1
CoreWavPack DirectShow Filters - 1.3.0.0
AmioWavpack.amio (Adobe Audition Plugins) - 1.5
cool_wv4.flt (CoolEdit / Audition filter) - 2.14
------------------------------------------------
updated: see 4.80.0 library changes
--------------------------------
Release 4.75.2 - October 1, 2015
--------------------------------
WavPack Library Source Code - 4.75.2
------------------------------------
fixed: corrupt mono or multichannel files created with assembly code (rare)
fixed: building on Clang systems like Darwin and FreeBSD (req. Clang 3.5+)
fixed: explicitly sign-extend audio data (< 4-byte) to avoid corrupt files
fixed: rare decoding errors due to integer overflow (ARM assembly code)
added: assembly optimizations for "extra" mode on mono or multichannel
wvtest.exe (command-line libwavpack test suite) - 4.75.2
--------------------------------------------------------
all new program to stress-test libwavpack (requires Pthreads)
wavpack.exe (command-line encoder) - 4.75.2
wvunpack.exe (command-line decoder) - 4.75.2
wvgain.exe (command-line ReplayGain scanner) - 4.75.2
-----------------------------------------------------
fixed: corrupt mono or multichannel files created with assembly code (rare)
added: assembly optimizations for "extra" mode on mono or multichannel
improved: flush stderr after all writes
cool_wv4.flt (CoolEdit / Audition filter) - 2.13
AmioWavpack.amio (Adobe Audition Plugins) - 1.4
------------------------------------------------
fixed: corrupt mono or multichannel files (rare)
-----------------------------
Release 4.75.0 - May 25, 2015
-----------------------------
WavPack Library Source Code - 4.75.0
------------------------------------
improved: reorganization for modularity and to improve linking
added: assembly optimizations for encode/decode on x86 and x64
added: assembly optimizations for decoding on ARMv7 (Linux)
improved: several minor speed optimizations using intrinsics
fixed: wavpack.pc.in not working correctly on some Linux distros
fixed: memcpy() issue causing abort() on OpenBSD
wavpack.exe (command-line encoder) - 4.75.0
wvunpack.exe (command-line decoder) - 4.75.0
wvgain.exe (command-line ReplayGain scanner) - 4.75.0
-----------------------------------------------------
changed: writing to console title default is off (Linux only, -z1 to enable)
fixed: wvgain crashes on bad file arguments (Debian bug #716478)
cool_wv4.flt (CoolEdit / Audition filter) - 2.12
------------------------------------------------
improved: performance (from assembly optimizations)
-------------------------
Update - December 7, 2013
-------------------------
CoreWavPack DirectShow Filters - 1.2.0.2
----------------------------------------
imported: latest filter sources from Christophe Paris and CoreCodec
updated: port to VS 2008 and add 64-bit build platform with installer
added: decode streams with full headers (tested with LAV splitter)
fixed: issues with 7.1 and non-standard channel configurations
fixed: problems with 12-bit, 20-bit, and 32-bit integer audio
fixed: crashing bug related to hybrid files with DNS
fixed: custom sampling rates being ignored
---------------------------------
Release 4.70.0 - October 19, 2013
---------------------------------
wavpack.exe (command-line encoder) - 4.70.0
-------------------------------------------
added: transcoding from existing WavPack files (with tag copy)
added: option to verify WavPack file integrity on creation (-v)
added: use temporary files for safer overwriting
added: detect UTF-16LE encoding for tag text files (mostly a Windows thing)
added: --version command to write machine-parsable value
added: option to allow up to 16 MB APEv2 tag data (--allow-huge-tags)
added: allow channel-order specification on WAV files with zeroed channel mask
added: several Windows features to Linux (clean ^C handling, console title)
added: 4GB file support on 32-bit Linux targets
WavPack Library Source Code - 4.70.0
------------------------------------
fixed: seeking to last block failure (after finishing file)
fixed: memcpy() not always used correctly (Linux targets)
fixed: unsigned char issue (ARM targets)
fixed: add binary tag functions to Windows DLL exports (forgot on 4.60)
added: read-only access to APEv2 tags that come at the beginning of files
improved: switched to Microsoft Visual Studio 2008 (win32 only)
wvunpack.exe (command-line decoder) - 4.70.0
--------------------------------------------
added: use temporary files for safer overwriting
added: --version command to write machine-parsable value
added: new command (-f) for getting machine-parsable WavPack file info
added: option (-n) to suppress audio decoding (useful for extracting only tags)
wvgain.exe (command-line ReplayGain scanner) - 4.70.0
-----------------------------------------------------
fixed: the -q (quiet) option would cause the -c (clean) option to fail
added: version command (-v) to write machine-parsable value
in_wv.dll (winamp plugin) - 2.8
-------------------------------
fixed: settings could not be saved on newer Windows versions (7 & 8)
fixed: installation issue caused by including manifest in build
added: dialog to installer suggesting "Winamp Essentials Pack"
AmioWavpack.amio (Adobe Audition Plugin) - 1.0
----------------------------------------------
all new plugin for Audition 4.0 (CS5.5) and later (including Audition CC)
--------------------------
Update - December 23, 2009
--------------------------
in_wv.dll (winamp plugin) - 2.8a
--------------------------------
fixed: crashes in winamp 5.57 when playing tracks that have "genre" tag
----------------------------------
Release 4.60.1 - November 29, 2009
----------------------------------
WavPack Library Source Code - 4.60.1
------------------------------------
fixed: filename specs in tag extractions failed in batch operations
fixed: prevent creation of APEv2 tags > 1 MB (which we can't read)
fixed: crash when decoding old WavPack files (pre version 4.0)
added: man pages to build system and updated with newer options
added: versioning info to Windows DLL
improved: build compatibility (eliminated uchar, ushort types)
wavpack.exe (command-line encoder) - 4.60.1
-------------------------------------------
fixed: don't allow user to attempt to place over 1 MB into APEv2 tags
in_wv.dll (winamp plugin) - 2.7
-------------------------------
added: read-only support for displaying cover art (thanks Benski!)
wvunpack.exe (command-line decoder) - 4.60.1
wvgain.exe (command-line ReplayGain scanner) - 4.60.1
cool_wv4.flt (CoolEdit / Audition filter) - 2.11
-----------------------------------------------------
(see library changes)
---------------------------------
Release 4.60 - September 27, 2009
---------------------------------
WavPack Library Source Code - 4.60
----------------------------------
added: API for reading & writing binary fields in APEv2 tags
fixed: recognize APEv2 tags with footers but no headers
fixed: playback of files with 8 streams (15-16 channels)
fixed: playback and seeking failed on certain rare correction files
fixed: handle case where library makes RIFF header but app adds RIFF trailer
improved: channel count limit now virtually unlimited (tested to 256)
improved: move all tag functions into new module (tags.c)
wavpack.exe (command-line encoder) - 4.60
-----------------------------------------
added: --write-binary-tag command for embedded cover art
added: --no-utf8-convert command to skip Unicode character conversions
added: --raw-pcm command to specify raw PCM data (samplerate, bitdepth, num chans)
added: --channel-order accepts "..." to specify unassigned channels
added: --pair-unassigned-chans command to put unassigned channels into stereo pairs
wvunpack.exe (command-line decoder) - 4.60
------------------------------------------
added: -x (and -xx) commands for extracting arbitrary tag fields to stdout (and files)
added: --no-utf8-convert command to skip Unicode character conversions
changed: -ss command no longer dumps multiline tags (use -x instead)
improved: formatting of -ss command, also shows information on binary tags
wvgain.exe (command-line ReplayGain scanner) - 4.60
---------------------------------------------------
added: -n option for processing new files only (those without ReplayGain info)
improved: increase maximum gain value generated from +24 to +64 dB
in_wv.dll (winamp plugin) - 2.6
cool_wv4.flt (CoolEdit / Audition filter) - 2.10
------------------------------------------------
(see library changes)
-------------------------
Update - January 23, 2009
-------------------------
in_wv.dll (winamp plugin) - 2.6b
--------------------------------
added: "lossless" and "category" to metadata keywords that we handle in winamp plugin
added: internationalization support to facilitate inclusion in Winamp Essentials Pack
-----------------------------
Release 4.50.1 - July 3, 2008
-----------------------------
WavPack Library Source Code - 4.50.1
------------------------------------
fixed: alignment fault when manipulating APEv2 tags (non-x86 only)
fixed: build on UNIX via elimination of non-standard strnlen()
wavpack.exe (command-line encoder) - 4.50.1
wvunpack.exe (command-line decoder) - 4.50.1
--------------------------------------------
fixed: checking return value of iconv_open() prevents core dump on Solaris
----------------------------
Release 4.50 - June 13, 2008
----------------------------
WavPack Library Source Code - 4.50
----------------------------------
added: dynamic noise shaping for improved hybrid quality
added: option to merge blocks of similar redundancy
added: ability to store and retrieve extra mode level
fixed: alignment fault on some big-endian machines
fixed: compiling with enable-mmx on gcc 4.3.x (thanks Joachim)
improved: allow bitrate to be calculated for files down to 1/10 second
improved: decoding of corrupt files (prevents heap overrun crashes)
wavpack.exe (command-line encoder) - 4.50
-----------------------------------------
added: dynamic noise shaping for improved hybrid quality
added: --channel-order option to reorder nonconforming multichannel files
added: --merge-blocks option to optimize storage of LossyWAV output files
added: ignore -o on Windows for compatibility with Linux version
fixed: alignment fault on some big-endian machines
improved: reformatted and expanded --help display
wvunpack.exe (command-line decoder) - 4.50
------------------------------------------
fixed: don't ignore fractions of seconds in --skip option
added: show extra level and dns status for newer files (-s command)
added: ignore -o on Windows for compatibility with Linux version
improved: decoding of corrupt files (prevents heap overrun crashes)
improved: display bitrate for files down to 1/10 second
in_wv.dll (winamp plugin) - 2.5
-------------------------------
added: transcoding API (allows CD burning, format conversion, ReplayGain calc, etc.)
added: metadata writing API (for Auto-Tag, etc.)
added: full Unicode support for info box (older Winamps) and media library
added: standard Winamp metadata display & edit for newer Winamps
added: option to pass multichannel audio
added: option to pass all audio as 16-bit (for better compatibility)
added: option to output 24-bit audio when ReplayGain is active
added: genre display to info box (older Winamps)
fixed: seek bar sometimes vacillates when moved
fixed: crash when winamp is opened with files in playlist moved or deleted
improved: hi-res audio now output as 24-bit (not 32-bit) for better compatibility (EQ, etc.)
improved: performance of adding tracks to library, especially from network drives
improved: decoding of corrupt files (prevents heap overrun crashes)
cool_wv4.flt (CoolEdit / Audition filter) - 2.9
-----------------------------------------------
added: about box
added: dynamic noise shaping for improved hybrid quality
improved: display bitrate for files as short as 1/10 second
improved: decoding of corrupt files (prevents heap overrun crashes)
improved: replace "extra processing" switch with a slider (0-6)
--------------------------
Release 4.41 - May 6, 2007
--------------------------
WavPack Library Source Code - 4.41
----------------------------------
added: create wavpackdll.dll for Windows (not used yet)
fixed: corrupt floating-point audio on big-endian machines
fixed: put MSVC projects in their own subdir (fixed build problems)
fixed: limit RIFF data buffering to 16 MB to prevent out-of-memory crash
improved: attempt to mute errors when decoding corrupt legacy WavPack files
improved: overall performance enhancements of 10% to 30% (depending on mode)
added: MMX intrinsics for 24-bit (and higher) stereo encoding (thanks to
Joachim Henke)
wavpack.exe (command-line encoder) - 4.41
-----------------------------------------
fixed: corrupt floating-point audio on big-endian machines
improved: refuse to encode WAV files over 4 GB or with over 16 MB RIFF data
improved: overall performance enhancements of 10% to 30% (depending on mode)
added: MMX intrinsics for 24-bit (and higher) stereo encoding (thanks to
Joachim Henke)
wvunpack.exe (command-line decoder) - 4.41
------------------------------------------
fixed: corrupt floating-point audio on big-endian machines
fixed: restore files mistakenly encoded with huge RIFF chunks
improved: attempt to mute errors when decoding corrupt legacy WavPack files
improved: overall performance enhancements of 10% to 30% (depending on mode)
added: --skip and --until commands to unpack specified range of audio data
added: MMX intrinsics for 24-bit (and higher) stereo encoding (thanks to
Joachim Henke)
wvgain.exe (command-line ReplayGain scanner) - 4.41
---------------------------------------------------
improved: overall performance enhancements of 10% to 30% (depending on mode)
added: MMX intrinsics for 24-bit (and higher) stereo encoding (thanks to
Joachim Henke)
cool_wv4.flt (CoolEdit / Audition filter) - 2.8
-----------------------------------------------
fixed: read all RIFF metadata from files created in other applications
improved: attempt to mute errors when decoding corrupt legacy WavPack files
improved: overall performance enhancements of 10% to 30% (depending on mode)
added: MMX intrinsics for 24-bit (and higher) stereo encoding (thanks to
Joachim Henke)
-------------------------------
Release 4.40 - December 3, 2006
-------------------------------
WavPack Library Source Code - 4.40
----------------------------------
added: new hardware-friendly "high" mode that compresses almost as well as
old "high" mode but decodes significantly faster; old "high" mode
now available as "very high"
added: option added to improve compression of mono material in stereo files
(requires at least version 4.3 decoder)
added: function to obtain channel mapping information on decoding
added: function to get trailing wrapper info (RIFF) without decoding file
improved: "extra" mode levels 1-3 completely revamped, fast enough for use
improved: reorganized to create a standard library that should more easily
integrate into other applications; eliminated namespace issues
improved: more robust handling of corrupt files
wavpack.exe (command-line encoder) - 4.40
-----------------------------------------
added: accepts long option names including --help for full usage info
added: new hardware-friendly "high" mode that compresses almost as well as
old "high" mode but decodes significantly faster; old "high" mode
now available as "very high" (-hh)
added: --optimize-mono option added to improve compression of mono material
in stereo files (requires at least version 4.3 decoder)
improved: "extra" mode levels 1-3 completely revamped, fast enough for use
improved: switched to Microsoft Visual Studio 2005 (win32 only)
removed: support for Windows 95
wvunpack.exe (command-line decoder) - 4.40
------------------------------------------
added: cuesheet extraction (to .cue file or stdout)
added: wav header generation on decode for files with missing RIFF
information, or forced with -w option
added: more summary info (wrapper info + channel assignments)
improved: more robust handling of corrupt files
improved: separate options for raw (-r) and blind stream decoding (-b)
improved: switched to Microsoft Visual Studio 2005 (win32 only)
removed: support for Windows 95
wvgain.exe (command-line ReplayGain scanner) - 4.40
---------------------------------------------------
improved: switched to Microsoft Visual Studio 2005 (win32 only)
removed: support for Windows 95
wvselfx.exe (self-extraction stub) - 4.40
------------------------------------------
added: automatic cuesheet extraction (if present in APEv2 tag)
in_wv.dll (winamp plugin) - 2.4
-------------------------------
fixed: quietly skips deleted files in playlist
improved: more robust handling of corrupt files
improved: APEv2 tags are read even if followed by ID3v1 tag
cool_wv4.flt (CoolEdit / Audition filter) - 2.7
-----------------------------------------------
added: new hardware-friendly "high" mode that compresses almost as well as
old "high" mode but decodes significantly faster; old "high" mode
now available as "v. high"
improved: more robust handling of corrupt files
----------------------
Update - April 5, 2006
----------------------
WavPack Library Source Code - 4.32
wavpack.exe (command-line encoder) - 4.32
-----------------------------------------
fixed: generating RIFF headers on big-endian machines caused crash
--------------------------
Update - December 10, 2005
--------------------------
wavpack.exe (command-line encoder) - 4.31
wvunpack.exe (command-line decoder) - 4.31
------------------------------------------
fixed: detect debug mode in all cases (win32 only)
improved: use latest service pack and SDK for building (win32 only)
improved: better directory choice for logging file (win32 only)
improved: allow shell to expand wildcards (*nix only)
added: option (-o) to specify output directory or path (*nix only)
added: option (-t) to copy timestamp (*nix only)
wvgain.exe (command-line ReplayGain scanner) - 4.31
---------------------------------------------------
new
WavPack Library Source Code - 4.31
----------------------------------
fixed: failing seek with some files that had been played to the end
fixed: small memory leak when opening hybrid lossless files
improved: signed characters no longer must be default
improved: APEv2 tags are read even if followed by ID3v1 tag
improved: limited APEv2 tag editing capability
------------------------------
Release 4.3 - November 1, 2005
------------------------------
wavpack.exe (command-line encoder) - 4.3
----------------------------------------
fixed: bug causing termination error with very wide screen widths
added: command-line option (-l) to use low priority for batch operation
added: command-line option (-r) to generate a fresh RIFF header
added: debug mode (rename to wavpack_debug.exe)
added: automatically detect lower resolution data even without -x1
added: src and dst dirs are searched also for tag source files (handy for EAC)
added: wildcard accepted for tag source files (handy for EAC)
added: handle non-standard sampling rates
improved: returns error status for any error
improved: use longer blocks in multichannel files (better "high" compression)
wvunpack.exe (command-line decoder) - 4.3
-----------------------------------------
fixed: very rare decoding bug causing overflow with hi-res files
fixed: bug causing termination error with very wide screen widths
fixed: formatting error in duration display
added: command-line option (-ss) to include tags in summary dump
added: command-line option (-l) to use low priority for batch operation
added: debug mode (rename to wvunpack_debug.exe)
improved: returns error status for any error
improved: more robust decoding of damaged (or invalid) files
in_wv.dll (winamp plugin) - 2.3
nxWavPack.dll (Nero plugin) - 1.2
WavPack_Apollo.dll (Apollo plugin) - 1.3
cool_wv4.flt (CoolEdit / Audition filter) - 2.6
-----------------------------------------------
fixed: very rare decoding bug causing overflow with hi-res files
improved: handle ID3v1.1 tags (now includes track number)
improved: more robust decoding of damaged (or invalid) files
added: handle non-standard sampling rates
foo_wavpack.dll (foobar plugin) - 2.3
-----------------------------------------------
fixed: any error during WavPack file open caused crash if wvc file present
fixed: very rare decoding bug causing overflow with hi-res files
improved: more robust decoding of damaged (or invalid) files
added: handle non-standard sampling rates
WavPack Library Source Code - 4.3
---------------------------------
fixed: very rare decoding bug causing overflow with hi-res files
added: automatic generation of RIFF wav header during encoding
added: new functions to access tags by index (instead of item name)
added: automatically detect lower resolution data during encoding
added: handle non-standard sampling rates
improved: more robust decoding of damaged (or invalid) files
improved: use longer blocks in multichannel files (better "high" compression)
improved: two structures renamed to avoid namespace conflict
removed: legacy code for Borland compiler
--------------------------
Update - September 1, 2005
--------------------------
wavpack.exe (command-line encoder) - 4.22
cool_wv4.flt (CoolEdit / Audition filter) - 2.5
-----------------------------------------------
fixed: possible corrupt files written (24 or 32-bit + "extra" mode)
---------------------------
Release 4.2 - April 2, 2005
---------------------------
wavpack.exe (command-line encoder) - 4.2
----------------------------------------
fixed: handling of wav files larger than 2 gig
improved: stereo lossless encoding speed (including "extra" mode)
added: -i option to ignore length specified in wav header
added: -w option to write APEv2 tags directly from command line
wvunpack.exe (command-line decoder) - 4.2
-----------------------------------------
improved: decoding speed
in_wv.dll (winamp plugin) - 2.2
-------------------------------
added: winamp media library support
improved: decoding speed
foo_wavpack.dll (foobar plugin) - 2.2
-------------------------------------
improved: decoding speed
nxWavPack.dll (Nero plugin) - 1.1
Cool_wv4.flt (CoolEdit / Audition filter) - 2.4
-----------------------------------------------
fixed: handling of wav files larger than 2 gig
improved: encoding and decoding speed
WavPack Library Source Code - 4.2
---------------------------------
improved: encoding and decoding speed
fixed: works correctly with 64-bit compilers
added: mode bit to open files in "streaming" mode
--------------------------
Update - December 12, 2004
--------------------------
WavPack_Apollo.dll (Apollo plugin) - 1.2
----------------------------------------
fixed: crash when Apollo opened and WavPack plugin can't find config file
--------------------------------
Release 4.1 - September 14, 2004
--------------------------------
wavpack.exe (command-line encoder) - 4.1
----------------------------------------
fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files
fixed: mono or multichannel files causing crash (no corruption possible)
added: third name specification for "correction" file (EAC specific)
added: -t option to preserve timestamps
added: error summary for batch mode
wvunpack.exe (command-line decoder) - 4.1
-----------------------------------------
fixed: hybrid mode decoding bugs (very obscure situations)
added: -s option to dump file summary to stdout
added: -t option to preserve timestamps
added: error summary for batch mode
wvselfx.exe (self-extraction stub) - 4.1
----------------------------------------
fixed: hybrid mode decoding bugs (very obscure situations)
in_wv.dll (winamp plugin) - 2.1
-------------------------------
fixed: international characters in tags display properly (UTF-8 to Ansi)
added: maximum tag data field width changed from 64 chars to 128 chars
added: new infobox items including encoder version & modes, track #, md5
foo_wavpack.dll (foobar plugin) - 2.1
-------------------------------------
added: new database items including encoder version & modes and md5
WavPack_Apollo.dll (Apollo plugin) - 1.1
----------------------------------------
fixed: international characters in tags display properly (UTF-8 to Ansi)
Cool_wv4.flt (CoolEdit / Audition filter) - 2.2
-----------------------------------------------
fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files
fixed: saving mono file causing crash (no corruption possible)
fixed: hybrid mode decoding bugs (very obscure situations)
fixed: partial saves (with "Cancel") have incorrect RIFF header if unpacked
nxWavPack.dll (Nero plugin) - 1.0
---------------------------------
new
WavPack Library Source Code - 4.1
---------------------------------
fixed: hybrid mode + "extra" mode + very low bitrates making corrupt files
fixed: mono or multichannel files causing crash (no corruption possible)
fixed: hybrid mode decoding bugs (very obscure situations)
added: mode bits for determining additional encode info (extra, sfx)
added: function to return total compressed file length (including wvc)
added: function to return encoder version (1, 2, 3, or 4)
added: ability to obtain MD5 sum before decoding file (requires seek to end)
added: mode bit for determining tag type (for proper character translation)
added: ability to encode WavPack files without knowing length in advance
added: option for small "information only" version of library

View file

@ -1,136 +0,0 @@
<img src="http://www.rarewares.org/wavpack/logos/wavpacklogo.png" width="250"></img>
Hybrid Lossless Wavefile Compressor
Copyright (c) 1998 - 2019 David Bryant.
All Rights Reserved.
Distributed under the [BSD Software License](https://github.com/dbry/WavPack/blob/master/license.txt).
---
This [repository](https://github.com/dbry/WavPack) contains all of the source code required to build the WavPack library (_libwavpack_), and any associated command-line programs.
Additional references:
* [Official website](http://wavpack.com/)
* [Binaries](http://wavpack.com/downloads.html#binaries)
* [Other sources](http://wavpack.com/downloads.html#sources)
* [Documentation](http://wavpack.com/downloads.html#documentation)
* [Test suite](http://www.rarewares.org/wavpack/test_suite.zip)
* [Logos](http://wavpack.com/downloads.html#logos)
---
## Build Status
| Branch | Status |
|----------------|-------------------------------------------------------------------------------------------------------------------|
| `master` | [![Build Status](https://travis-ci.org/dbry/WavPack.svg?branch=master)](https://travis-ci.org/dbry/WavPack) |
Branches [actively built](https://travis-ci.org/dbry/WavPack/branches) by TravisCI.
---
## Building
### Windows
There are solution and project files for Visual Studio 2008, and additional source code to build the [CoolEdit/Audition](https://github.com/dbry/WavPack/tree/master/audition) plugin and the [Winamp](https://github.com/dbry/WavPack/tree/master/winamp) plugin.
The CoolEdit/Audition plugin provides a good example for using the library to both read and write WavPack files, and the Winamp plugin makes extensive use of APEv2 tag reading and writing.
Both 32-bit and 64-bit platforms are provided.
Visual Studio 2008 does not support projects with x64 assembly very well. I have provided a copy of the edited `masm.rules` file that works for me, but I can't provide support if your build does not work. Please make a copy of your `masm.rules` file first.
On my system it lives here: `C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\VCProjectDefaults`
### Linux
To build everything on Linux, type:
1. `./configure`
* `--disable-asm`
* `--enable-man`
* `--enable-rpath`
* `--enable-tests`
* `--disable-apps`
* `--disable-dsd`
* `--enable-legacy`
2. `make`
* Optionally, `make install`, to install into `/usr/local/bin`
If you are using the code directly from Git (rather than a distribution) then you will need to do a ./autogen.sh instead of the configure step. If assembly optimizations are available for your processor they will be automatically enabled, but if there is a problem with them then use the `--disable-asm` option to revert to pure C.
For Clang-based build systems (Darwin, FreeBSD, etc.), Clang version 3.5 or higher is required.
If you get a WARNING about unexpected _libwavpack_ version when you run the command-line programs, you might try using `--enable-rpath` to hardcode the library location in the executables, or simply force static linking with `--disable-shared`.
There is now a CLI program to do a full suite of stress tests for _libwavpack_, and this is particularly useful for packagers to make sure that the assembly language optimizations are working correctly on various platforms. It is built with the configure option `--enable-tests` and requires Pthreads (it worked out-of-the-box on all the platforms I tried it on). There are lots of options, but the default test suite (consisting of 192 tests) is executed with `wvtest --default`. There is also a seeking test. On Windows a third-party Pthreads library is required, so I am not including this in the build for now.
---
## Assembly
Assembly language optimizations are provided for x86 and x86-64 (AMD64) processors (encoding and decoding) and ARMv7 (decoding only).
The x86 assembly code includes a runtime check for MMX capability, so it will work on legacy i386 processors.
## Documentation
There are four documentation files contained in the distribution:
| File | Description |
|------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [doc/wavpack_doc.html](https://github.com/dbry/WavPack/blob/master/doc/wavpack_doc.html) | Contains user-targeted documentation for the command-line programs. |
| [doc/WavPack5PortingGuide.pdf](https://github.com/dbry/WavPack/blob/master/doc/WavPack5PortingGuide.pdf) | This document is targeted at developers who are migrating to WavPack 5, and it provides a short description of the major improvements and how to utilize them. |
| [doc/WavPack5LibraryDoc.pdf](https://github.com/dbry/WavPack/blob/master/doc/WavPack5LibraryDoc.pdf) | Contains a detailed description of the API provided by WavPack library appropriate for reading and writing WavPack files and manipulating APEv2 tags. |
| [doc/WavPack5FileFormat.pdf](https://github.com/dbry/WavPack/blob/master/doc/WavPack5FileFormat.pdf) | Contains a description of the WavPack file format, including details needed for parsing WavPack, blocks, and interpreting the block header and flags. |
There is also a description of the WavPack algorithms in the forth edition of David Salomon's book "Data Compression: The Complete Reference". This section can be found here: www.wavpack.com/WavPack.pdf
## Portability
This code is designed to be easy to port to other platforms.
It is endian-agnostic and usually uses callbacks for I/O, although there's a convenience function for reading files that accepts filename strings and automatically handles correction files.
On Windows, there is now an option to select UTF-8 instead of ANSI.
To maintain compatibility on various platforms, the following conventions are used:
* `char` must be 8-bits (`signed` or `unsigned`).
* `short` must be 16-bits.
* `int` and `long` must be at least 32-bits.
## Design
The code's modules are organized in such a way that if major chunks of the functionality are not referenced (for example, creating WavPack files) then link-time dependency resolution should provide optimum binary sizes.
However, some functionality could not be easily excluded in this way and so there are additional macros that may be used to further reduce the size of the binary. Note that these must be defined for all modules:
| Macros | Description |
|-----------------|------------------------------------------------------------------------------------------------------------|
| `NO_SEEKING` | To not allow seeking to a specific sample index (for applications that always read entire files). |
| `NO_TAGS` | To not read specified fields from ID3v1 and APEv2 tags, and not create or edit APEv2 tags. |
| `ENABLE_LEGACY` | Include support for Wavpack files from before version 4.0. This was eliminated by default with WavPack 5. |
| `ENABLE_DSD` | Include support for DSD audio. New for WavPack 5 and the default, but obviously not universally required. |
Note that this has been tested on many platforms.
## Tiny Decoder
There are alternate versions of this library available specifically designed for resource limited CPUs, and hardware encoding and decoding.
There is the _Tiny Decoder_ library which works with less than 32k of code and less than 4k of data, and has assembly language optimizations for the ARM and Freescale ColdFire CPUs.
The _Tiny Decoder_ is also designed for embedded use and handles the pure lossless, lossy, and hybrid lossless modes.
Neither of these versions use any memory allocation functions, nor do they require floating-point arithmetic support.
---
Questions or comments should be directed to david@wavpack.com.
You may also find David on GitHub as [dbry](https://github.com/dbry).

View file

@ -1,788 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// common_utils.c
// This module provides a lot of the trivial WavPack API functions and several
// functions that are common to both reading and writing WavPack files (like
// WavpackCloseFile()). Functions here are restricted to those that have few
// external dependencies and this is done so that applications that statically
// link to the WavPack library (like the command-line utilities on Windows)
// do not need to include the entire library image if they only use a subset
// of it. This module will be loaded for ANY WavPack application.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "wavpack_local.h"
#ifndef LIBWAVPACK_VERSION_STRING
#include "wavpack_version.h"
#endif
///////////////////////////// local table storage ////////////////////////////
const uint32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
///////////////////////////// executable code ////////////////////////////////
// This function obtains general information about an open input file and
// returns a mask with the following bit values:
// MODE_WVC: a .wvc file has been found and will be used for lossless
// MODE_LOSSLESS: file is lossless (either pure or hybrid)
// MODE_HYBRID: file is hybrid mode (either lossy or lossless)
// MODE_FLOAT: audio data is 32-bit ieee floating point
// MODE_VALID_TAG: file contains a valid ID3v1 or APEv2 tag
// MODE_HIGH: file was created in "high" mode (information only)
// MODE_FAST: file was created in "fast" mode (information only)
// MODE_EXTRA: file was created using "extra" mode (information only)
// MODE_APETAG: file contains a valid APEv2 tag
// MODE_SFX: file was created as a "self-extracting" executable
// MODE_VERY_HIGH: file was created in the "very high" mode (or in
// the "high" mode prior to 4.4)
// MODE_MD5: file contains an MD5 checksum
// MODE_XMODE: level used for extra mode (1-6, 0=unknown)
// MODE_DNS: dynamic noise shaping
int WavpackGetMode (WavpackContext *wpc)
{
int mode = 0;
if (wpc) {
if (wpc->config.flags & CONFIG_HYBRID_FLAG)
mode |= MODE_HYBRID;
else if (!(wpc->config.flags & CONFIG_LOSSY_MODE))
mode |= MODE_LOSSLESS;
if (wpc->wvc_flag)
mode |= (MODE_LOSSLESS | MODE_WVC);
if (wpc->lossy_blocks)
mode &= ~MODE_LOSSLESS;
if (wpc->config.flags & CONFIG_FLOAT_DATA)
mode |= MODE_FLOAT;
if (wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG)) {
mode |= MODE_HIGH;
if ((wpc->config.flags & CONFIG_VERY_HIGH_FLAG) ||
(wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version < 0x405))
mode |= MODE_VERY_HIGH;
}
if (wpc->config.flags & CONFIG_FAST_FLAG)
mode |= MODE_FAST;
if (wpc->config.flags & CONFIG_EXTRA_MODE)
mode |= (MODE_EXTRA | (wpc->config.xmode << 12));
if (wpc->config.flags & CONFIG_CREATE_EXE)
mode |= MODE_SFX;
if (wpc->config.flags & CONFIG_MD5_CHECKSUM)
mode |= MODE_MD5;
if ((wpc->config.flags & CONFIG_HYBRID_FLAG) && (wpc->config.flags & CONFIG_DYNAMIC_SHAPING) &&
wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version >= 0x407)
mode |= MODE_DNS;
#ifndef NO_TAGS
if (valid_tag (&wpc->m_tag)) {
mode |= MODE_VALID_TAG;
if (valid_tag (&wpc->m_tag) == 'A')
mode |= MODE_APETAG;
}
#endif
mode |= (wpc->config.qmode << 16) & 0xFF0000;
}
return mode;
}
// This function obtains information about specific file features that were
// added for version 5.0, specifically qualifications added to support CAF
// and DSD files. Except for indicating the presence of DSD data, these
// bits are meant to simply indicate the format of the data in the original
// source file and do NOT indicate how the library will return the data to
// the appication (which is always the same). This means that in general an
// application that simply wants to play or process the audio data need not
// be concerned about these. If the file is DSD audio, then either of the
// QMDOE_DSD_LSB_FIRST or QMODE_DSD_MSB_FIRST bits will be set (but the
// DSD audio is always returned to the caller MSB first).
// QMODE_BIG_ENDIAN 0x1 // big-endian data format (opposite of WAV format)
// QMODE_SIGNED_BYTES 0x2 // 8-bit audio data is signed (opposite of WAV format)
// QMODE_UNSIGNED_WORDS 0x4 // audio data (other than 8-bit) is unsigned (opposite of WAV format)
// QMODE_REORDERED_CHANS 0x8 // source channels were not Microsoft order, so they were reordered
// QMODE_DSD_LSB_FIRST 0x10 // DSD bytes, LSB first (most Sony .dsf files)
// QMODE_DSD_MSB_FIRST 0x20 // DSD bytes, MSB first (Philips .dff files)
// QMODE_DSD_IN_BLOCKS 0x40 // DSD data is blocked by channels (Sony .dsf only)
int WavpackGetQualifyMode (WavpackContext *wpc)
{
return wpc->config.qmode & 0xFF;
}
// This function returns a pointer to a string describing the last error
// generated by WavPack.
char *WavpackGetErrorMessage (WavpackContext *wpc)
{
return wpc->error_message;
}
// Get total number of samples contained in the WavPack file, or -1 if unknown
uint32_t WavpackGetNumSamples (WavpackContext *wpc)
{
return (uint32_t) WavpackGetNumSamples64 (wpc);
}
int64_t WavpackGetNumSamples64 (WavpackContext *wpc)
{
return wpc ? wpc->total_samples : -1;
}
// Get the current sample index position, or -1 if unknown
uint32_t WavpackGetSampleIndex (WavpackContext *wpc)
{
return (uint32_t) WavpackGetSampleIndex64 (wpc);
}
int64_t WavpackGetSampleIndex64 (WavpackContext *wpc)
{
if (wpc) {
#ifdef ENABLE_LEGACY
if (wpc->stream3)
return get_sample_index3 (wpc);
else if (wpc->streams && wpc->streams [0])
return wpc->streams [0]->sample_index;
#else
if (wpc->streams && wpc->streams [0])
return wpc->streams [0]->sample_index;
#endif
}
return -1;
}
// Get the number of errors encountered so far
int WavpackGetNumErrors (WavpackContext *wpc)
{
return wpc ? wpc->crc_errors : 0;
}
// return TRUE if any uncorrected lossy blocks were actually written or read
int WavpackLossyBlocks (WavpackContext *wpc)
{
return wpc ? wpc->lossy_blocks : 0;
}
// Calculate the progress through the file as a double from 0.0 (for begin)
// to 1.0 (for done). A return value of -1.0 indicates that the progress is
// unknown.
double WavpackGetProgress (WavpackContext *wpc)
{
if (wpc && wpc->total_samples != -1 && wpc->total_samples != 0)
return (double) WavpackGetSampleIndex64 (wpc) / wpc->total_samples;
else
return -1.0;
}
// Return the total size of the WavPack file(s) in bytes.
uint32_t WavpackGetFileSize (WavpackContext *wpc)
{
return (uint32_t) (wpc ? wpc->filelen + wpc->file2len : 0);
}
int64_t WavpackGetFileSize64 (WavpackContext *wpc)
{
return wpc ? wpc->filelen + wpc->file2len : 0;
}
// Calculate the ratio of the specified WavPack file size to the size of the
// original audio data as a double greater than 0.0 and (usually) smaller than
// 1.0. A value greater than 1.0 represents "negative" compression and a
// return value of 0.0 indicates that the ratio cannot be determined.
double WavpackGetRatio (WavpackContext *wpc)
{
if (wpc && wpc->total_samples != -1 && wpc->filelen) {
double output_size = (double) wpc->total_samples * wpc->config.num_channels *
wpc->config.bytes_per_sample;
double input_size = (double) wpc->filelen + wpc->file2len;
if (output_size >= 1.0 && input_size >= 1.0)
return input_size / output_size;
}
return 0.0;
}
// Calculate the average bitrate of the WavPack file in bits per second. A
// return of 0.0 indicates that the bitrate cannot be determined. An option is
// provided to use (or not use) any attendant .wvc file.
double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc)
{
if (wpc && wpc->total_samples != -1 && wpc->filelen) {
double output_time = (double) wpc->total_samples / WavpackGetSampleRate (wpc);
double input_size = (double) wpc->filelen + (count_wvc ? wpc->file2len : 0);
if (output_time >= 0.1 && input_size >= 1.0)
return input_size * 8.0 / output_time;
}
return 0.0;
}
// Calculate the bitrate of the current WavPack file block in bits per second.
// This can be used for an "instant" bit display and gets updated from about
// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot
// be determined.
double WavpackGetInstantBitrate (WavpackContext *wpc)
{
if (wpc && wpc->stream3)
return WavpackGetAverageBitrate (wpc, TRUE);
if (wpc && wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.block_samples) {
double output_time = (double) wpc->streams [0]->wphdr.block_samples / WavpackGetSampleRate (wpc);
double input_size = 0;
int si;
for (si = 0; si < wpc->num_streams; ++si) {
if (wpc->streams [si]->blockbuff)
input_size += ((WavpackHeader *) wpc->streams [si]->blockbuff)->ckSize;
if (wpc->streams [si]->block2buff)
input_size += ((WavpackHeader *) wpc->streams [si]->block2buff)->ckSize;
}
if (output_time > 0.0 && input_size >= 1.0)
return input_size * 8.0 / output_time;
}
return 0.0;
}
// This function allows retrieving the Core Audio File channel layout, many of which do not
// conform to the Microsoft ordering standard that WavPack requires internally (at least for
// those channels present in the "channel mask"). In addition to the layout tag, this function
// returns the reordering string (if stored in the file) to allow the unpacker to reorder the
// channels back to the specified layout (if it wants to restore the CAF order). The number of
// channels in the layout is determined from the lower nybble of the layout word (and should
// probably match the number of channels in the file), and if a reorder string is requested
// then that much space must be allocated. Note that all the reordering is actually done
// outside of this library, and that if reordering is done then the appropriate qmode bit
// will be set.
//
// Note: Normally this function would not be used by an application unless it specifically
// wanted to restore a non-standard channel order (to check an MD5, for example) or obtain
// the Core Audio channel layout ID. For simple file decoding for playback, the channel_mask
// should provide all the information required unless there are non-Microsoft channels
// involved, in which case WavpackGetChannelIdentities() will provide the identities of
// the other channels (if they are known).
uint32_t WavpackGetChannelLayout (WavpackContext *wpc, unsigned char *reorder)
{
if ((wpc->channel_layout & 0xff) && wpc->channel_reordering && reorder)
memcpy (reorder, wpc->channel_reordering, wpc->channel_layout & 0xff);
return wpc->channel_layout;
}
// This function provides the identities of ALL the channels in the file, including the
// standard Microsoft channels (which come first, in order, and are numbered 1-18) and also
// any non-Microsoft channels (which can be in any order and have values from 33-254). The
// value 0x00 is invalid and 0xFF indicates an "unknown" or "unnassigned" channel. The
// string is NULL terminated so the caller must supply enough space for the number
// of channels indicated by WavpackGetNumChannels(), plus one.
//
// Note that this function returns the actual order of the channels in the Wavpack file
// (i.e., the order returned by WavpackUnpackSamples()). If the file includes a "reordering"
// string because the source file was not in Microsoft order that is NOT taken into account
// here and really only needs to be considered if doing an MD5 verification or if it's
// required to restore the original order/file (like wvunpack does).
void WavpackGetChannelIdentities (WavpackContext *wpc, unsigned char *identities)
{
int num_channels = wpc->config.num_channels, index = 1;
uint32_t channel_mask = wpc->config.channel_mask;
unsigned char *src = wpc->channel_identities;
while (num_channels--) {
if (channel_mask) {
while (!(channel_mask & 1)) {
channel_mask >>= 1;
index++;
}
*identities++ = index++;
channel_mask >>= 1;
}
else if (src && *src)
*identities++ = *src++;
else
*identities++ = 0xff;
}
*identities = 0;
}
// For local use only. Install a callback to be executed when WavpackCloseFile() is called,
// usually used to dump some statistics accumulated during encode or decode.
void install_close_callback (WavpackContext *wpc, void cb_func (void *wpc))
{
wpc->close_callback = cb_func;
}
// Close the specified WavPack file and release all resources used by it.
// Returns NULL.
WavpackContext *WavpackCloseFile (WavpackContext *wpc)
{
if (wpc->close_callback)
wpc->close_callback (wpc);
if (wpc->streams) {
free_streams (wpc);
if (wpc->streams [0])
free (wpc->streams [0]);
free (wpc->streams);
}
#ifdef ENABLE_LEGACY
if (wpc->stream3)
free_stream3 (wpc);
#endif
if (wpc->reader && wpc->reader->close && wpc->wv_in)
wpc->reader->close (wpc->wv_in);
if (wpc->reader && wpc->reader->close && wpc->wvc_in)
wpc->reader->close (wpc->wvc_in);
WavpackFreeWrapper (wpc);
if (wpc->metadata) {
int i;
for (i = 0; i < wpc->metacount; ++i)
if (wpc->metadata [i].data)
free (wpc->metadata [i].data);
free (wpc->metadata);
}
if (wpc->channel_identities)
free (wpc->channel_identities);
if (wpc->channel_reordering)
free (wpc->channel_reordering);
#ifndef NO_TAGS
free_tag (&wpc->m_tag);
#endif
#ifdef ENABLE_DSD
if (wpc->decimation_context)
decimate_dsd_destroy (wpc->decimation_context);
#endif
free (wpc);
return NULL;
}
// These routines are used to access (and free) header and trailer data that
// was retrieved from the Wavpack file. The header will be available before
// the samples are decoded and the trailer will be available after all samples
// have been read.
uint32_t WavpackGetWrapperBytes (WavpackContext *wpc)
{
return wpc ? wpc->wrapper_bytes : 0;
}
unsigned char *WavpackGetWrapperData (WavpackContext *wpc)
{
return wpc ? wpc->wrapper_data : NULL;
}
void WavpackFreeWrapper (WavpackContext *wpc)
{
if (wpc && wpc->wrapper_data) {
free (wpc->wrapper_data);
wpc->wrapper_data = NULL;
wpc->wrapper_bytes = 0;
}
}
// Returns the sample rate of the specified WavPack file
uint32_t WavpackGetSampleRate (WavpackContext *wpc)
{
return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier : wpc->config.sample_rate) : 44100;
}
// Returns the native sample rate of the specified WavPack file
// (provides the native rate for DSD files rather than the "byte" rate that's used for
// seeking, duration, etc. and would generally be used just for user facing reports)
uint32_t WavpackGetNativeSampleRate (WavpackContext *wpc)
{
return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier * 8 : wpc->config.sample_rate) : 44100;
}
// Returns the number of channels of the specified WavPack file. Note that
// this is the actual number of channels contained in the file even if the
// OPEN_2CH_MAX flag was specified when the file was opened.
int WavpackGetNumChannels (WavpackContext *wpc)
{
return wpc ? wpc->config.num_channels : 2;
}
// Returns the standard Microsoft channel mask for the specified WavPack
// file. A value of zero indicates that there is no speaker assignment
// information.
int WavpackGetChannelMask (WavpackContext *wpc)
{
return wpc ? wpc->config.channel_mask : 0;
}
// Return the normalization value for floating point data (valid only
// if floating point data is present). A value of 127 indicates that
// the floating point range is +/- 1.0. Higher values indicate a
// larger floating point range.
int WavpackGetFloatNormExp (WavpackContext *wpc)
{
return wpc->config.float_norm_exp;
}
// Returns the actual number of valid bits per sample contained in the
// original file, which may or may not be a multiple of 8. Floating data
// always has 32 bits, integers may be from 1 to 32 bits each. When this
// value is not a multiple of 8, then the "extra" bits are located in the
// LSBs of the results. That is, values are right justified when unpacked
// into ints, but are left justified in the number of bytes used by the
// original data.
int WavpackGetBitsPerSample (WavpackContext *wpc)
{
return wpc ? wpc->config.bits_per_sample : 16;
}
// Returns the number of bytes used for each sample (1 to 4) in the original
// file. This is required information for the user of this module because the
// audio data is returned in the LOWER bytes of the long buffer and must be
// left-shifted 8, 16, or 24 bits if normalized longs are required.
int WavpackGetBytesPerSample (WavpackContext *wpc)
{
return wpc ? wpc->config.bytes_per_sample : 2;
}
// If the OPEN_2CH_MAX flag is specified when opening the file, this function
// will return the actual number of channels decoded from the file (which may
// or may not be less than the actual number of channels, but will always be
// 1 or 2). Normally, this will be the front left and right channels of a
// multichannel file.
int WavpackGetReducedChannels (WavpackContext *wpc)
{
if (wpc)
return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels;
else
return 2;
}
// Free all memory allocated for raw WavPack blocks (for all allocated streams)
// and free all additional streams. This does not free the default stream ([0])
// which is always kept around.
void free_streams (WavpackContext *wpc)
{
int si = wpc->num_streams;
while (si--) {
if (wpc->streams [si]->blockbuff) {
free (wpc->streams [si]->blockbuff);
wpc->streams [si]->blockbuff = NULL;
}
if (wpc->streams [si]->block2buff) {
free (wpc->streams [si]->block2buff);
wpc->streams [si]->block2buff = NULL;
}
if (wpc->streams [si]->sample_buffer) {
free (wpc->streams [si]->sample_buffer);
wpc->streams [si]->sample_buffer = NULL;
}
if (wpc->streams [si]->dc.shaping_data) {
free (wpc->streams [si]->dc.shaping_data);
wpc->streams [si]->dc.shaping_data = NULL;
}
#ifdef ENABLE_DSD
free_dsd_tables (wpc->streams [si]);
#endif
if (si) {
wpc->num_streams--;
free (wpc->streams [si]);
wpc->streams [si] = NULL;
}
}
wpc->current_stream = 0;
}
void free_dsd_tables (WavpackStream *wps)
{
if (wps->dsd.probabilities) {
free (wps->dsd.probabilities);
wps->dsd.probabilities = NULL;
}
if (wps->dsd.summed_probabilities) {
free (wps->dsd.summed_probabilities);
wps->dsd.summed_probabilities = NULL;
}
if (wps->dsd.lookup_buffer) {
free (wps->dsd.lookup_buffer);
wps->dsd.lookup_buffer = NULL;
}
if (wps->dsd.value_lookup) {
free (wps->dsd.value_lookup);
wps->dsd.value_lookup = NULL;
}
if (wps->dsd.ptable) {
free (wps->dsd.ptable);
wps->dsd.ptable = NULL;
}
}
void WavpackFloatNormalize (int32_t *values, int32_t num_values, int delta_exp)
{
f32 *fvalues = (f32 *) values;
int exp;
if (!delta_exp)
return;
while (num_values--) {
if ((exp = get_exponent (*fvalues)) == 0 || exp + delta_exp <= 0)
*fvalues = 0;
else if (exp == 255 || (exp += delta_exp) >= 255) {
set_exponent (*fvalues, 255);
set_mantissa (*fvalues, 0);
}
else
set_exponent (*fvalues, exp);
fvalues++;
}
}
void WavpackLittleEndianToNative (void *data, char *format)
{
unsigned char *cp = (unsigned char *) data;
int64_t temp;
while (*format) {
switch (*format) {
case 'D':
temp = cp [0] + ((int64_t) cp [1] << 8) + ((int64_t) cp [2] << 16) + ((int64_t) cp [3] << 24) +
((int64_t) cp [4] << 32) + ((int64_t) cp [5] << 40) + ((int64_t) cp [6] << 48) + ((int64_t) cp [7] << 56);
* (int64_t *) cp = temp;
cp += 8;
break;
case 'L':
temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24);
* (int32_t *) cp = (int32_t) temp;
cp += 4;
break;
case 'S':
temp = cp [0] + (cp [1] << 8);
* (int16_t *) cp = (int16_t) temp;
cp += 2;
break;
default:
if (isdigit (*format))
cp += *format - '0';
break;
}
format++;
}
}
void WavpackNativeToLittleEndian (void *data, char *format)
{
unsigned char *cp = (unsigned char *) data;
int64_t temp;
while (*format) {
switch (*format) {
case 'D':
temp = * (int64_t *) cp;
*cp++ = (unsigned char) temp;
*cp++ = (unsigned char) (temp >> 8);
*cp++ = (unsigned char) (temp >> 16);
*cp++ = (unsigned char) (temp >> 24);
*cp++ = (unsigned char) (temp >> 32);
*cp++ = (unsigned char) (temp >> 40);
*cp++ = (unsigned char) (temp >> 48);
*cp++ = (unsigned char) (temp >> 56);
break;
case 'L':
temp = * (int32_t *) cp;
*cp++ = (unsigned char) temp;
*cp++ = (unsigned char) (temp >> 8);
*cp++ = (unsigned char) (temp >> 16);
*cp++ = (unsigned char) (temp >> 24);
break;
case 'S':
temp = * (int16_t *) cp;
*cp++ = (unsigned char) temp;
*cp++ = (unsigned char) (temp >> 8);
break;
default:
if (isdigit (*format))
cp += *format - '0';
break;
}
format++;
}
}
void WavpackBigEndianToNative (void *data, char *format)
{
unsigned char *cp = (unsigned char *) data;
int64_t temp;
while (*format) {
switch (*format) {
case 'D':
temp = cp [7] + ((int64_t) cp [6] << 8) + ((int64_t) cp [5] << 16) + ((int64_t) cp [4] << 24) +
((int64_t) cp [3] << 32) + ((int64_t) cp [2] << 40) + ((int64_t) cp [1] << 48) + ((int64_t) cp [0] << 56);
* (int64_t *) cp = temp;
cp += 8;
break;
case 'L':
temp = cp [3] + ((int32_t) cp [2] << 8) + ((int32_t) cp [1] << 16) + ((int32_t) cp [0] << 24);
* (int32_t *) cp = (int32_t) temp;
cp += 4;
break;
case 'S':
temp = cp [1] + (cp [0] << 8);
* (int16_t *) cp = (int16_t) temp;
cp += 2;
break;
default:
if (isdigit (*format))
cp += *format - '0';
break;
}
format++;
}
}
void WavpackNativeToBigEndian (void *data, char *format)
{
unsigned char *cp = (unsigned char *) data;
int64_t temp;
while (*format) {
switch (*format) {
case 'D':
temp = * (int64_t *) cp;
*cp++ = (unsigned char) (temp >> 56);
*cp++ = (unsigned char) (temp >> 48);
*cp++ = (unsigned char) (temp >> 40);
*cp++ = (unsigned char) (temp >> 32);
*cp++ = (unsigned char) (temp >> 24);
*cp++ = (unsigned char) (temp >> 16);
*cp++ = (unsigned char) (temp >> 8);
*cp++ = (unsigned char) temp;
break;
case 'L':
temp = * (int32_t *) cp;
*cp++ = (unsigned char) (temp >> 24);
*cp++ = (unsigned char) (temp >> 16);
*cp++ = (unsigned char) (temp >> 8);
*cp++ = (unsigned char) temp;
break;
case 'S':
temp = * (int16_t *) cp;
*cp++ = (unsigned char) (temp >> 8);
*cp++ = (unsigned char) temp;
break;
default:
if (isdigit (*format))
cp += *format - '0';
break;
}
format++;
}
}
uint32_t WavpackGetLibraryVersion (void)
{
return (LIBWAVPACK_MAJOR<<16)
|(LIBWAVPACK_MINOR<<8)
|(LIBWAVPACK_MICRO<<0);
}
const char *WavpackGetLibraryVersionString (void)
{
return LIBWAVPACK_VERSION_STRING;
}

File diff suppressed because it is too large Load diff

View file

@ -1,204 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// decorr_utils.c
// This module contains the functions that process metadata blocks that are
// specific to the decorrelator. These would be called any time a WavPack
// block was parsed. These are in a module separate from the actual unpack
// decorrelation code (unpack.c) so that if an application just wants to get
// information from WavPack files (rather than actually decoding audio) then
// less code needs to be linked.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
// Read decorrelation terms from specified metadata block into the
// decorr_passes array. The terms range from -3 to 8, plus 17 & 18;
// other values are reserved and generate errors for now. The delta
// ranges from 0 to 7 with all values valid. Note that the terms are
// stored in the opposite order in the decorr_passes array compared
// to packing.
int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd)
{
int termcnt = wpmd->byte_length;
unsigned char *byteptr = (unsigned char *)wpmd->data;
struct decorr_pass *dpp;
if (termcnt > MAX_NTERMS)
return FALSE;
wps->num_terms = termcnt;
for (dpp = wps->decorr_passes + termcnt - 1; termcnt--; dpp--) {
dpp->term = (int)(*byteptr & 0x1f) - 5;
dpp->delta = (*byteptr++ >> 5) & 0x7;
if (!dpp->term || dpp->term < -3 || (dpp->term > MAX_TERM && dpp->term < 17) || dpp->term > 18 ||
((wps->wphdr.flags & MONO_DATA) && dpp->term < 0))
return FALSE;
}
return TRUE;
}
// Read decorrelation weights from specified metadata block into the
// decorr_passes array. The weights range +/-1024, but are rounded and
// truncated to fit in signed chars for metadata storage. Weights are
// separate for the two channels and are specified from the "last" term
// (first during encode). Unspecified weights are set to zero.
int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd)
{
int termcnt = wpmd->byte_length, tcount;
char *byteptr = (char *)wpmd->data;
struct decorr_pass *dpp;
if (!(wps->wphdr.flags & MONO_DATA))
termcnt /= 2;
if (termcnt > wps->num_terms)
return FALSE;
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
dpp->weight_A = dpp->weight_B = 0;
while (--dpp >= wps->decorr_passes && termcnt--) {
dpp->weight_A = restore_weight (*byteptr++);
if (!(wps->wphdr.flags & MONO_DATA))
dpp->weight_B = restore_weight (*byteptr++);
}
return TRUE;
}
// Read decorrelation samples from specified metadata block into the
// decorr_passes array. The samples are signed 32-bit values, but are
// converted to signed log2 values for storage in metadata. Values are
// stored for both channels and are specified from the "last" term
// (first during encode) with unspecified samples set to zero. The
// number of samples stored varies with the actual term value, so
// those must obviously come first in the metadata.
int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char *byteptr = (unsigned char *)wpmd->data;
unsigned char *endptr = byteptr + wpmd->byte_length;
struct decorr_pass *dpp;
int tcount;
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
CLEAR (dpp->samples_A);
CLEAR (dpp->samples_B);
}
if (wps->wphdr.version == 0x402 && (wps->wphdr.flags & HYBRID_FLAG)) {
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr)
return FALSE;
wps->dc.error [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->dc.error [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
}
}
while (dpp-- > wps->decorr_passes && byteptr < endptr)
if (dpp->term > MAX_TERM) {
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 4 : 8) > endptr)
return FALSE;
dpp->samples_A [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
dpp->samples_A [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
byteptr += 4;
if (!(wps->wphdr.flags & MONO_DATA)) {
dpp->samples_B [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
dpp->samples_B [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
byteptr += 4;
}
}
else if (dpp->term < 0) {
if (byteptr + 4 > endptr)
return FALSE;
dpp->samples_A [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
dpp->samples_B [0] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
byteptr += 4;
}
else {
int m = 0, cnt = dpp->term;
while (cnt--) {
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr)
return FALSE;
dpp->samples_A [m] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
if (!(wps->wphdr.flags & MONO_DATA)) {
dpp->samples_B [m] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
}
m++;
}
}
return byteptr == endptr;
}
// Read the shaping weights from specified metadata block into the
// WavpackStream structure. Note that there must be two values (even
// for mono streams) and that the values are stored in the same
// manner as decorrelation weights. These would normally be read from
// the "correction" file and are used for lossless reconstruction of
// hybrid data.
int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd)
{
if (wpmd->byte_length == 2) {
char *byteptr = (char *)wpmd->data;
wps->dc.shaping_acc [0] = (int32_t) restore_weight (*byteptr++) << 16;
wps->dc.shaping_acc [1] = (int32_t) restore_weight (*byteptr++) << 16;
return TRUE;
}
else if (wpmd->byte_length >= (wps->wphdr.flags & MONO_DATA ? 4 : 8)) {
unsigned char *byteptr = (unsigned char *)wpmd->data;
wps->dc.error [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
wps->dc.shaping_acc [0] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
byteptr += 4;
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->dc.error [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
wps->dc.shaping_acc [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
byteptr += 4;
}
if (wpmd->byte_length == (wps->wphdr.flags & MONO_DATA ? 6 : 12)) {
wps->dc.shaping_delta [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
if (!(wps->wphdr.flags & MONO_DATA))
wps->dc.shaping_delta [1] = wp_exp2s ((int16_t)(byteptr [2] + (byteptr [3] << 8)));
}
return TRUE;
}
return FALSE;
}

View file

@ -1,378 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// entropy_utils.c
// This module contains the functions that process metadata blocks that are
// specific to the entropy decoder; these would be called any time a WavPack
// block was parsed. Additionally, it contains tables and functions that are
// common to both entropy coding and decoding. These are in a module separate
// from the actual entropy encoder (write_words.c) and decoder (read_words.c)
// so that if applications that just do a subset of the full WavPack reading
// and writing can link with a subset of the library.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
///////////////////////////// local table storage ////////////////////////////
const uint32_t bitset [] = {
1L << 0, 1L << 1, 1L << 2, 1L << 3,
1L << 4, 1L << 5, 1L << 6, 1L << 7,
1L << 8, 1L << 9, 1L << 10, 1L << 11,
1L << 12, 1L << 13, 1L << 14, 1L << 15,
1L << 16, 1L << 17, 1L << 18, 1L << 19,
1L << 20, 1L << 21, 1L << 22, 1L << 23,
1L << 24, 1L << 25, 1L << 26, 1L << 27,
1L << 28, 1L << 29, 1L << 30, 1L << 31
};
const uint32_t bitmask [] = {
(1L << 0) - 1, (1L << 1) - 1, (1L << 2) - 1, (1L << 3) - 1,
(1L << 4) - 1, (1L << 5) - 1, (1L << 6) - 1, (1L << 7) - 1,
(1L << 8) - 1, (1L << 9) - 1, (1L << 10) - 1, (1L << 11) - 1,
(1L << 12) - 1, (1L << 13) - 1, (1L << 14) - 1, (1L << 15) - 1,
(1L << 16) - 1, (1L << 17) - 1, (1L << 18) - 1, (1L << 19) - 1,
(1L << 20) - 1, (1L << 21) - 1, (1L << 22) - 1, (1L << 23) - 1,
(1L << 24) - 1, (1L << 25) - 1, (1L << 26) - 1, (1L << 27) - 1,
(1L << 28) - 1, (1L << 29) - 1, (1L << 30) - 1, 0x7fffffff
};
const char nbits_table [] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, // 0 - 15
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 16 - 31
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 32 - 47
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // 48 - 63
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 64 - 79
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 95
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 96 - 111
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 112 - 127
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 128 - 143
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 144 - 159
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 160 - 175
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 176 - 191
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 192 - 207
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 208 - 223
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // 224 - 239
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 // 240 - 255
};
static const unsigned char log2_table [] = {
0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a,
0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2,
0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0,
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce,
0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb,
0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7,
0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4,
0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff
};
static const unsigned char exp2_table [] = {
0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b,
0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16,
0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23,
0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d,
0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4,
0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9,
0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};
///////////////////////////// executable code ////////////////////////////////
// Read the median log2 values from the specified metadata structure, convert
// them back to 32-bit unsigned values and store them. If length is not
// exactly correct then we flag and return an error.
int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char *byteptr = (unsigned char *)wpmd->data;
if (wpmd->byte_length != ((wps->wphdr.flags & MONO_DATA) ? 6 : 12))
return FALSE;
wps->w.c [0].median [0] = wp_exp2s (byteptr [0] + (byteptr [1] << 8));
wps->w.c [0].median [1] = wp_exp2s (byteptr [2] + (byteptr [3] << 8));
wps->w.c [0].median [2] = wp_exp2s (byteptr [4] + (byteptr [5] << 8));
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->w.c [1].median [0] = wp_exp2s (byteptr [6] + (byteptr [7] << 8));
wps->w.c [1].median [1] = wp_exp2s (byteptr [8] + (byteptr [9] << 8));
wps->w.c [1].median [2] = wp_exp2s (byteptr [10] + (byteptr [11] << 8));
}
return TRUE;
}
// Read the hybrid related values from the specified metadata structure, convert
// them back to their internal formats and store them. The extended profile
// stuff is not implemented yet, so return an error if we get more data than
// we know what to do with.
int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char *byteptr = (unsigned char *)wpmd->data;
unsigned char *endptr = byteptr + wpmd->byte_length;
if (wps->wphdr.flags & HYBRID_BITRATE) {
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr)
return FALSE;
wps->w.c [0].slow_level = wp_exp2s (byteptr [0] + (byteptr [1] << 8));
byteptr += 2;
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->w.c [1].slow_level = wp_exp2s (byteptr [0] + (byteptr [1] << 8));
byteptr += 2;
}
}
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr)
return FALSE;
wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
byteptr += 2;
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->w.bitrate_acc [1] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
byteptr += 2;
}
if (byteptr < endptr) {
if (byteptr + (wps->wphdr.flags & MONO_DATA ? 2 : 4) > endptr)
return FALSE;
wps->w.bitrate_delta [0] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
if (!(wps->wphdr.flags & MONO_DATA)) {
wps->w.bitrate_delta [1] = wp_exp2s ((int16_t)(byteptr [0] + (byteptr [1] << 8)));
byteptr += 2;
}
if (byteptr < endptr)
return FALSE;
}
else
wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0;
return TRUE;
}
// This function is called during both encoding and decoding of hybrid data to
// update the "error_limit" variable which determines the maximum sample error
// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only
// currently implemented) this is calculated from the slow_level values and the
// bitrate accumulators. Note that the bitrate accumulators can be changing.
void update_error_limit (WavpackStream *wps)
{
int bitrate_0 = (wps->w.bitrate_acc [0] += wps->w.bitrate_delta [0]) >> 16;
if (wps->wphdr.flags & MONO_DATA) {
if (wps->wphdr.flags & HYBRID_BITRATE) {
int slow_log_0 = (wps->w.c [0].slow_level + SLO) >> SLS;
if (slow_log_0 - bitrate_0 > -0x100)
wps->w.c [0].error_limit = wp_exp2s (slow_log_0 - bitrate_0 + 0x100);
else
wps->w.c [0].error_limit = 0;
}
else
wps->w.c [0].error_limit = wp_exp2s (bitrate_0);
}
else {
int bitrate_1 = (wps->w.bitrate_acc [1] += wps->w.bitrate_delta [1]) >> 16;
if (wps->wphdr.flags & HYBRID_BITRATE) {
int slow_log_0 = (wps->w.c [0].slow_level + SLO) >> SLS;
int slow_log_1 = (wps->w.c [1].slow_level + SLO) >> SLS;
if (wps->wphdr.flags & HYBRID_BALANCE) {
int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1;
if (balance > bitrate_0) {
bitrate_1 = bitrate_0 * 2;
bitrate_0 = 0;
}
else if (-balance > bitrate_0) {
bitrate_0 = bitrate_0 * 2;
bitrate_1 = 0;
}
else {
bitrate_1 = bitrate_0 + balance;
bitrate_0 = bitrate_0 - balance;
}
}
if (slow_log_0 - bitrate_0 > -0x100)
wps->w.c [0].error_limit = wp_exp2s (slow_log_0 - bitrate_0 + 0x100);
else
wps->w.c [0].error_limit = 0;
if (slow_log_1 - bitrate_1 > -0x100)
wps->w.c [1].error_limit = wp_exp2s (slow_log_1 - bitrate_1 + 0x100);
else
wps->w.c [1].error_limit = 0;
}
else {
wps->w.c [0].error_limit = wp_exp2s (bitrate_0);
wps->w.c [1].error_limit = wp_exp2s (bitrate_1);
}
}
}
// The concept of a base 2 logarithm is used in many parts of WavPack. It is
// a way of sufficiently accurately representing 32-bit signed and unsigned
// values storing only 16 bits (actually fewer). It is also used in the hybrid
// mode for quickly comparing the relative magnitude of large values (i.e.
// division) and providing smooth exponentials using only addition.
// These are not strict logarithms in that they become linear around zero and
// can therefore represent both zero and negative values. They have 8 bits
// of precision and in "roundtrip" conversions the total error never exceeds 1
// part in 225 except for the cases of +/-115 and +/-195 (which error by 1).
// This function returns the log2 for the specified 32-bit unsigned value.
// The maximum value allowed is about 0xff800000 and returns 8447.
int FASTCALL wp_log2 (uint32_t avalue)
{
int dbits;
if ((avalue += avalue >> 9) < (1 << 8)) {
dbits = nbits_table [avalue];
return (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
}
else {
if (avalue < (1L << 16))
dbits = nbits_table [avalue >> 8] + 8;
else if (avalue < (1L << 24))
dbits = nbits_table [avalue >> 16] + 16;
else
dbits = nbits_table [avalue >> 24] + 24;
return (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
}
}
// This function scans a buffer of longs and accumulates the total log2 value
// of all the samples. This is useful for determining maximum compression
// because the bitstream storage required for entropy coding is proportional
// to the base 2 log of the samples. On some platforms there is an assembly
// version of this.
#if !defined(OPT_ASM_X86) && !defined(OPT_ASM_X64)
uint32_t log2buffer (int32_t *samples, uint32_t num_samples, int limit)
{
uint32_t result = 0, avalue;
int dbits;
while (num_samples--) {
avalue = abs (*samples++);
if ((avalue += avalue >> 9) < (1 << 8)) {
dbits = nbits_table [avalue];
result += (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
}
else {
if (avalue < (1L << 16))
dbits = nbits_table [avalue >> 8] + 8;
else if (avalue < (1L << 24))
dbits = nbits_table [avalue >> 16] + 16;
else
dbits = nbits_table [avalue >> 24] + 24;
result += dbits = (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
if (limit && dbits >= limit)
return (uint32_t) -1;
}
}
return result;
}
#endif
// This function returns the log2 for the specified 32-bit signed value.
// All input values are valid and the return values are in the range of
// +/- 8192.
int wp_log2s (int32_t value)
{
return (value < 0) ? -wp_log2 (-value) : wp_log2 (value);
}
// This function returns the original integer represented by the supplied
// logarithm (at least within the provided accuracy). The log is signed,
// but since a full 32-bit value is returned this can be used for unsigned
// conversions as well (i.e. the input range is -8192 to +8447).
int32_t wp_exp2s (int log)
{
uint32_t value;
if (log < 0)
return -wp_exp2s (-log);
value = exp2_table [log & 0xff] | 0x100;
if ((log >>= 8) <= 9)
return value >> (9 - log);
else
return value << (log - 9);
}
// These two functions convert internal weights (which are normally +/-1024)
// to and from an 8-bit signed character version for storage in metadata. The
// weights are clipped here in the case that they are outside that range.
signed char store_weight (int weight)
{
if (weight > 1024)
weight = 1024;
else if (weight < -1024)
weight = -1024;
if (weight > 0)
weight -= (weight + 64) >> 7;
return (weight + 4) >> 3;
}
int restore_weight (signed char weight)
{
int result;
if ((result = (int) weight << 3) > 0)
result += (result + 64) >> 7;
return result;
}

View file

@ -1,704 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// extra1.c
// This module handles the "extra" mode for mono files.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
// This flag causes this module to take into account the size of the header
// (which grows with more decorrelation passes) when making decisions about
// adding additional passes (as opposed to just considering the resulting
// magnitude of the residuals). With really long blocks it seems to actually
// hurt compression (for reasons I cannot explain), but with short blocks it
// works okay, so we're enabling it for now.
#define USE_OVERHEAD
// If the log2 value of any sample in a buffer being scanned exceeds this value,
// we abandon that configuration. This prevents us from going down paths that
// are wildly unstable.
#define LOG_LIMIT 6912
//#define EXTRA_DUMP // dump generated filter data error_line()
#ifdef OPT_ASM_X86
#define PACK_DECORR_MONO_PASS_CONT pack_decorr_mono_pass_cont_x86
#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__) || defined(__midipix__))
#define PACK_DECORR_MONO_PASS_CONT pack_decorr_mono_pass_cont_x64win
#elif defined(OPT_ASM_X64)
#define PACK_DECORR_MONO_PASS_CONT pack_decorr_mono_pass_cont_x64
#endif
#ifdef PACK_DECORR_MONO_PASS_CONT
void PACK_DECORR_MONO_PASS_CONT (int32_t *out_buffer, int32_t *in_buffer, struct decorr_pass *dpp, int32_t sample_count);
#endif
typedef struct {
int32_t *sampleptrs [MAX_NTERMS+2];
struct decorr_pass dps [MAX_NTERMS];
int nterms, log_limit;
uint32_t best_bits;
} WavpackExtraInfo;
static void decorr_mono_pass (int32_t *in_samples, int32_t *out_samples, uint32_t num_samples, struct decorr_pass *dpp, int dir)
{
int32_t cont_samples = 0;
int m = 0, i;
#ifdef PACK_DECORR_MONO_PASS_CONT
if (num_samples > 16 && dir > 0) {
int32_t pre_samples = (dpp->term > MAX_TERM) ? 2 : dpp->term;
cont_samples = num_samples - pre_samples;
num_samples = pre_samples;
}
#endif
dpp->sum_A = 0;
if (dir < 0) {
out_samples += (num_samples + cont_samples - 1);
in_samples += (num_samples + cont_samples - 1);
dir = -1;
}
else
dir = 1;
dpp->weight_A = restore_weight (store_weight (dpp->weight_A));
for (i = 0; i < 8; ++i)
dpp->samples_A [i] = wp_exp2s (wp_log2s (dpp->samples_A [i]));
if (dpp->term > MAX_TERM) {
while (num_samples--) {
int32_t left, sam_A;
if (dpp->term & 1)
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
else
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_A [0] = left = in_samples [0];
left -= apply_weight (dpp->weight_A, sam_A);
update_weight (dpp->weight_A, dpp->delta, sam_A, left);
dpp->sum_A += dpp->weight_A;
out_samples [0] = left;
in_samples += dir;
out_samples += dir;
}
}
else if (dpp->term > 0) {
while (num_samples--) {
int k = (m + dpp->term) & (MAX_TERM - 1);
int32_t left, sam_A;
sam_A = dpp->samples_A [m];
dpp->samples_A [k] = left = in_samples [0];
m = (m + 1) & (MAX_TERM - 1);
left -= apply_weight (dpp->weight_A, sam_A);
update_weight (dpp->weight_A, dpp->delta, sam_A, left);
dpp->sum_A += dpp->weight_A;
out_samples [0] = left;
in_samples += dir;
out_samples += dir;
}
}
if (m && dpp->term > 0 && dpp->term <= MAX_TERM) {
int32_t temp_A [MAX_TERM];
int k;
memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
for (k = 0; k < MAX_TERM; k++) {
dpp->samples_A [k] = temp_A [m];
m = (m + 1) & (MAX_TERM - 1);
}
}
#ifdef PACK_DECORR_MONO_PASS_CONT
if (cont_samples)
PACK_DECORR_MONO_PASS_CONT (out_samples, in_samples, dpp, cont_samples);
#endif
}
static void reverse_mono_decorr (struct decorr_pass *dpp)
{
if (dpp->term > MAX_TERM) {
int32_t sam_A;
if (dpp->term & 1)
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
else
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_A [0] = sam_A;
if (dpp->term & 1)
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
else
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
dpp->samples_A [1] = sam_A;
}
else if (dpp->term > 1) {
int i = 0, j = dpp->term - 1, cnt = dpp->term / 2;
while (cnt--) {
i &= (MAX_TERM - 1);
j &= (MAX_TERM - 1);
dpp->samples_A [i] ^= dpp->samples_A [j];
dpp->samples_A [j] ^= dpp->samples_A [i];
dpp->samples_A [i++] ^= dpp->samples_A [j--];
}
// CLEAR (dpp->samples_A);
}
}
static void decorr_mono_buffer (int32_t *samples, int32_t *outsamples, uint32_t num_samples, struct decorr_pass *dpp, int tindex)
{
struct decorr_pass dp, *dppi = dpp + tindex;
int delta = dppi->delta, pre_delta, term = dppi->term;
if (delta == 7)
pre_delta = 7;
else if (delta < 2)
pre_delta = 3;
else
pre_delta = delta + 1;
CLEAR (dp);
dp.term = term;
dp.delta = pre_delta;
decorr_mono_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1);
dp.delta = delta;
if (tindex == 0)
reverse_mono_decorr (&dp);
else
CLEAR (dp.samples_A);
memcpy (dppi->samples_A, dp.samples_A, sizeof (dp.samples_A));
dppi->weight_A = dp.weight_A;
if (delta == 0) {
dp.delta = 1;
decorr_mono_pass (samples, outsamples, num_samples, &dp, 1);
dp.delta = 0;
memcpy (dp.samples_A, dppi->samples_A, sizeof (dp.samples_A));
dppi->weight_A = dp.weight_A = dp.sum_A / num_samples;
}
// if (memcmp (dppi, &dp, sizeof (dp)))
// error_line ("decorr_passes don't match, delta = %d", delta);
decorr_mono_pass (samples, outsamples, num_samples, &dp, 1);
}
static int log2overhead (int first_term, int num_terms)
{
#ifdef USE_OVERHEAD
if (first_term > MAX_TERM)
return (4 + num_terms * 2) << 11;
else
return (2 + num_terms * 2) << 11;
#else
return 0;
#endif
}
static void recurse_mono (WavpackContext *wpc, WavpackExtraInfo *info, int depth, int delta, uint32_t input_bits)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth;
int32_t *samples, *outsamples;
uint32_t term_bits [22], bits;
if (branches < 1 || depth + 1 == info->nterms)
branches = 1;
CLEAR (term_bits);
samples = info->sampleptrs [depth];
outsamples = info->sampleptrs [depth + 1];
for (term = 1; term <= 18; ++term) {
if (term == 17 && branches == 1 && depth + 1 < info->nterms)
continue;
if (term > 8 && term < 17)
continue;
if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term > 4 && term < 17))
continue;
info->dps [depth].term = term;
info->dps [depth].delta = delta;
decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, info->dps, depth);
bits = LOG2BUFFER (outsamples, wps->wphdr.block_samples, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (info->dps [0].term, depth + 1);
if (bits < info->best_bits) {
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * (depth + 1));
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [depth + 1], wps->wphdr.block_samples * 4);
}
term_bits [term + 3] = bits;
}
while (depth + 1 < info->nterms && branches--) {
uint32_t local_best_bits = input_bits;
int best_term = 0, i;
for (i = 0; i < 22; ++i)
if (term_bits [i] && term_bits [i] < local_best_bits) {
local_best_bits = term_bits [i];
// term_bits [i] = 0;
best_term = i - 3;
}
if (!best_term)
break;
term_bits [best_term + 3] = 0;
info->dps [depth].term = best_term;
info->dps [depth].delta = delta;
decorr_mono_buffer (samples, outsamples, wps->wphdr.block_samples, info->dps, depth);
// if (log2buffer (outsamples, wps->wphdr.block_samples * 2, 0) != local_best_bits)
// error_line ("data doesn't match!");
recurse_mono (wpc, info, depth + 1, delta, local_best_bits);
}
}
static void delta_mono (WavpackContext *wpc, WavpackExtraInfo *info)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int lower = FALSE, delta, d;
uint32_t bits;
if (wps->decorr_passes [0].term)
delta = wps->decorr_passes [0].delta;
else
return;
for (d = delta - 1; d >= 0; --d) {
int i;
if (!d && (wps->wphdr.flags & HYBRID_FLAG))
break;
for (i = 0; i < info->nterms && wps->decorr_passes [i].term; ++i) {
info->dps [i].term = wps->decorr_passes [i].term;
info->dps [i].delta = d;
decorr_mono_buffer (info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, info->dps, i);
}
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
lower = TRUE;
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 4);
}
else
break;
}
for (d = delta + 1; !lower && d <= 7; ++d) {
int i;
for (i = 0; i < info->nterms && wps->decorr_passes [i].term; ++i) {
info->dps [i].term = wps->decorr_passes [i].term;
info->dps [i].delta = d;
decorr_mono_buffer (info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, info->dps, i);
}
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 4);
}
else
break;
}
}
static void sort_mono (WavpackContext *wpc, WavpackExtraInfo *info)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int reversed = TRUE;
uint32_t bits;
while (reversed) {
int ri, i;
memcpy (info->dps, wps->decorr_passes, sizeof (wps->decorr_passes));
reversed = FALSE;
for (ri = 0; ri < info->nterms && wps->decorr_passes [ri].term; ++ri) {
if (ri + 1 >= info->nterms || !wps->decorr_passes [ri+1].term)
break;
if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) {
decorr_mono_buffer (info->sampleptrs [ri], info->sampleptrs [ri+1], wps->wphdr.block_samples, info->dps, ri);
continue;
}
info->dps [ri] = wps->decorr_passes [ri+1];
info->dps [ri+1] = wps->decorr_passes [ri];
for (i = ri; i < info->nterms && wps->decorr_passes [i].term; ++i)
decorr_mono_buffer (info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, info->dps, i);
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
reversed = TRUE;
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 4);
}
else {
info->dps [ri] = wps->decorr_passes [ri];
info->dps [ri+1] = wps->decorr_passes [ri+1];
decorr_mono_buffer (info->sampleptrs [ri], info->sampleptrs [ri+1], wps->wphdr.block_samples, info->dps, ri);
}
}
}
}
static const uint32_t xtable [] = { 91, 123, 187, 251 };
static void analyze_mono (WavpackContext *wpc, int32_t *samples, int do_samples)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
WavpackExtraInfo info;
int i;
#ifdef LOG_LIMIT
info.log_limit = (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) + 4) * 256;
if (info.log_limit > LOG_LIMIT)
info.log_limit = LOG_LIMIT;
#else
info.log_limit = 0;
#endif
if (wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG))
wpc->config.extra_flags = xtable [wpc->config.xmode - 4];
else
wpc->config.extra_flags = xtable [wpc->config.xmode - 3];
info.nterms = wps->num_terms;
for (i = 0; i < info.nterms + 2; ++i)
info.sampleptrs [i] = malloc (wps->wphdr.block_samples * 4);
memcpy (info.dps, wps->decorr_passes, sizeof (info.dps));
memcpy (info.sampleptrs [0], samples, wps->wphdr.block_samples * 4);
for (i = 0; i < info.nterms && info.dps [i].term; ++i)
decorr_mono_pass (info.sampleptrs [i], info.sampleptrs [i + 1], wps->wphdr.block_samples, info.dps + i, 1);
info.best_bits = LOG2BUFFER (info.sampleptrs [info.nterms], wps->wphdr.block_samples, 0) * 1;
info.best_bits += log2overhead (info.dps [0].term, i);
memcpy (info.sampleptrs [info.nterms + 1], info.sampleptrs [i], wps->wphdr.block_samples * 4);
if (wpc->config.extra_flags & EXTRA_BRANCHES)
recurse_mono (wpc, &info, 0, (int) floor (wps->delta_decay + 0.5),
LOG2BUFFER (info.sampleptrs [0], wps->wphdr.block_samples, 0));
if (wpc->config.extra_flags & EXTRA_SORT_FIRST)
sort_mono (wpc, &info);
if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) {
delta_mono (wpc, &info);
if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term)
wps->delta_decay = (float)((wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0);
else
wps->delta_decay = 2.0;
}
if (wpc->config.extra_flags & EXTRA_SORT_LAST)
sort_mono (wpc, &info);
if (do_samples)
memcpy (samples, info.sampleptrs [info.nterms + 1], wps->wphdr.block_samples * 4);
for (i = 0; i < info.nterms; ++i)
if (!wps->decorr_passes [i].term)
break;
wps->num_terms = i;
for (i = 0; i < info.nterms + 2; ++i)
free (info.sampleptrs [i]);
}
static void mono_add_noise (WavpackStream *wps, int32_t *lptr, int32_t *rptr)
{
int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING;
short *shaping_array = wps->dc.shaping_array;
int32_t error = 0, temp, cnt;
scan_word (wps, rptr, wps->wphdr.block_samples, -1);
cnt = wps->wphdr.block_samples;
if (wps->wphdr.flags & HYBRID_SHAPE) {
while (cnt--) {
if (shaping_array)
shaping_weight = *shaping_array++;
else
shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
temp = -apply_weight (shaping_weight, error);
if (new && shaping_weight < 0 && temp) {
if (temp == error)
temp = (temp < 0) ? temp + 1 : temp - 1;
lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0] + temp);
}
else
lptr [0] += (error = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp;
lptr++;
rptr++;
}
if (!shaping_array)
wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples;
}
else
while (cnt--) {
lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0];
lptr++;
rptr++;
}
}
void execute_mono (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples)
{
int32_t *temp_buffer [2], *best_buffer, *noisy_buffer = NULL;
struct decorr_pass temp_decorr_pass, save_decorr_passes [MAX_NTERMS];
WavpackStream *wps = wpc->streams [wpc->current_stream];
int32_t num_samples = wps->wphdr.block_samples;
int32_t buf_size = sizeof (int32_t) * num_samples;
uint32_t best_size = (uint32_t) -1, size;
int log_limit, pi, i;
#ifdef SKIP_DECORRELATION
CLEAR (wps->decorr_passes);
wps->num_terms = 0;
return;
#endif
for (i = 0; i < num_samples; ++i)
if (samples [i])
break;
if (i == num_samples) {
CLEAR (wps->decorr_passes);
wps->num_terms = 0;
init_words (wps);
return;
}
#ifdef LOG_LIMIT
log_limit = (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) + 4) * 256;
if (log_limit > LOG_LIMIT)
log_limit = LOG_LIMIT;
#else
log_limit = 0;
#endif
CLEAR (save_decorr_passes);
temp_buffer [0] = malloc (buf_size);
temp_buffer [1] = malloc (buf_size);
best_buffer = malloc (buf_size);
if (wps->num_passes > 1 && (wps->wphdr.flags & HYBRID_FLAG)) {
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = 2;
temp_decorr_pass.term = 18;
decorr_mono_pass (samples, temp_buffer [0],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
reverse_mono_decorr (&temp_decorr_pass);
decorr_mono_pass (samples, temp_buffer [0], num_samples, &temp_decorr_pass, 1);
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = 2;
temp_decorr_pass.term = 17;
decorr_mono_pass (temp_buffer [0], temp_buffer [1],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
decorr_mono_pass (temp_buffer [0], temp_buffer [1], num_samples, &temp_decorr_pass, 1);
noisy_buffer = malloc (buf_size);
memcpy (noisy_buffer, samples, buf_size);
mono_add_noise (wps, noisy_buffer, temp_buffer [1]);
no_history = 1;
}
if (no_history || wps->num_passes >= 7)
wps->best_decorr = wps->mask_decorr = 0;
for (pi = 0; pi < wps->num_passes;) {
const WavpackDecorrSpec *wpds;
int nterms, c, j;
if (!pi)
c = wps->best_decorr;
else {
if (wps->mask_decorr == 0)
c = 0;
else
c = (wps->best_decorr & (wps->mask_decorr - 1)) | wps->mask_decorr;
if (c == wps->best_decorr) {
wps->mask_decorr = wps->mask_decorr ? ((wps->mask_decorr << 1) & (wps->num_decorrs - 1)) : 1;
continue;
}
}
wpds = &wps->decorr_specs [c];
nterms = (int) strlen ((char *) wpds->terms);
while (1) {
memcpy (temp_buffer [0], noisy_buffer ? noisy_buffer : samples, buf_size);
CLEAR (save_decorr_passes);
for (j = 0; j < nterms; ++j) {
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = wpds->delta;
temp_decorr_pass.term = wpds->terms [j];
if (temp_decorr_pass.term < 0)
temp_decorr_pass.term = 1;
decorr_mono_pass (temp_buffer [j&1], temp_buffer [~j&1],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
if (j) {
CLEAR (temp_decorr_pass.samples_A);
}
else
reverse_mono_decorr (&temp_decorr_pass);
memcpy (save_decorr_passes + j, &temp_decorr_pass, sizeof (struct decorr_pass));
decorr_mono_pass (temp_buffer [j&1], temp_buffer [~j&1], num_samples, &temp_decorr_pass, 1);
}
size = LOG2BUFFER (temp_buffer [j&1], num_samples, log_limit);
if (size == (uint32_t) -1 && nterms)
nterms >>= 1;
else
break;
}
size += log2overhead (wpds->terms [0], nterms);
if (size < best_size) {
memcpy (best_buffer, temp_buffer [j&1], buf_size);
memcpy (wps->decorr_passes, save_decorr_passes, sizeof (struct decorr_pass) * MAX_NTERMS);
wps->num_terms = nterms;
wps->best_decorr = c;
best_size = size;
}
if (pi++)
wps->mask_decorr = wps->mask_decorr ? ((wps->mask_decorr << 1) & (wps->num_decorrs - 1)) : 1;
}
if (wpc->config.xmode > 3) {
if (noisy_buffer) {
analyze_mono (wpc, noisy_buffer, do_samples);
if (do_samples)
memcpy (samples, noisy_buffer, buf_size);
}
else
analyze_mono (wpc, samples, do_samples);
}
else if (do_samples)
memcpy (samples, best_buffer, buf_size);
if (no_history || wpc->config.xmode > 3)
scan_word (wps, best_buffer, num_samples, -1);
if (noisy_buffer)
free (noisy_buffer);
free (temp_buffer [1]);
free (temp_buffer [0]);
free (best_buffer);
#ifdef EXTRA_DUMP
if (1) {
char string [256], substring [20];
int i;
sprintf (string, "M: terms =");
for (i = 0; i < wps->num_terms; ++i) {
if (wps->decorr_passes [i].term) {
if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta)
sprintf (substring, " %d", wps->decorr_passes [i].term);
else
sprintf (substring, " %d->%d", wps->decorr_passes [i].term,
wps->decorr_passes [i].delta);
}
else
sprintf (substring, " *");
strcat (string, substring);
}
error_line (string);
}
#endif
}

View file

@ -1,929 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// MMX optimizations (c) 2006 Joachim Henke //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// extra2.c
// This module handles the "extra" mode for stereo files.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
// This flag causes this module to take into account the size of the header
// (which grows with more decorrelation passes) when making decisions about
// adding additional passes (as opposed to just considering the resulting
// magnitude of the residuals). With really long blocks it seems to actually
// hurt compression (for reasons I cannot explain), but with short blocks it
// works okay, so we're enabling it for now.
#define USE_OVERHEAD
// If the log2 value of any sample in a buffer being scanned exceeds this value,
// we abandon that configuration. This prevents us from going down paths that
// are wildly unstable.
#define LOG_LIMIT 6912
//#define EXTRA_DUMP // dump generated filter data to error_line()
#ifdef OPT_ASM_X86
#define PACK_DECORR_STEREO_PASS_CONT pack_decorr_stereo_pass_cont_x86
#define PACK_DECORR_STEREO_PASS_CONT_REV pack_decorr_stereo_pass_cont_rev_x86
#define PACK_DECORR_STEREO_PASS_CONT_AVAILABLE pack_cpu_has_feature_x86(CPU_FEATURE_MMX)
#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__) || defined(__midipix__))
#define PACK_DECORR_STEREO_PASS_CONT pack_decorr_stereo_pass_cont_x64win
#define PACK_DECORR_STEREO_PASS_CONT_REV pack_decorr_stereo_pass_cont_rev_x64win
#define PACK_DECORR_STEREO_PASS_CONT_AVAILABLE 1
#elif defined(OPT_ASM_X64)
#define PACK_DECORR_STEREO_PASS_CONT pack_decorr_stereo_pass_cont_x64
#define PACK_DECORR_STEREO_PASS_CONT_REV pack_decorr_stereo_pass_cont_rev_x64
#define PACK_DECORR_STEREO_PASS_CONT_AVAILABLE 1
#endif
#ifdef PACK_DECORR_STEREO_PASS_CONT
void PACK_DECORR_STEREO_PASS_CONT (struct decorr_pass *dpp, int32_t *in_buffer, int32_t *out_buffer, int32_t sample_count);
void PACK_DECORR_STEREO_PASS_CONT_REV (struct decorr_pass *dpp, int32_t *in_buffer, int32_t *out_buffer, int32_t sample_count);
#endif
typedef struct {
int32_t *sampleptrs [MAX_NTERMS+2];
struct decorr_pass dps [MAX_NTERMS];
int nterms, log_limit;
uint32_t best_bits;
} WavpackExtraInfo;
static void decorr_stereo_pass (int32_t *in_samples, int32_t *out_samples, int32_t num_samples, struct decorr_pass *dpp, int dir)
{
int32_t cont_samples = 0;
int m = 0, i;
#ifdef PACK_DECORR_STEREO_PASS_CONT
if (num_samples > 16 && PACK_DECORR_STEREO_PASS_CONT_AVAILABLE) {
int32_t pre_samples = (dpp->term < 0 || dpp->term > MAX_TERM) ? 2 : dpp->term;
cont_samples = num_samples - pre_samples;
num_samples = pre_samples;
}
#endif
dpp->sum_A = dpp->sum_B = 0;
if (dir < 0) {
out_samples += (num_samples + cont_samples - 1) * 2;
in_samples += (num_samples + cont_samples - 1) * 2;
dir = -2;
}
else
dir = 2;
dpp->weight_A = restore_weight (store_weight (dpp->weight_A));
dpp->weight_B = restore_weight (store_weight (dpp->weight_B));
for (i = 0; i < 8; ++i) {
dpp->samples_A [i] = wp_exp2s (wp_log2s (dpp->samples_A [i]));
dpp->samples_B [i] = wp_exp2s (wp_log2s (dpp->samples_B [i]));
}
switch (dpp->term) {
case 2:
while (num_samples--) {
int32_t sam, tmp;
sam = dpp->samples_A [0];
dpp->samples_A [0] = dpp->samples_A [1];
out_samples [0] = tmp = (dpp->samples_A [1] = in_samples [0]) - apply_weight (dpp->weight_A, sam);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
dpp->sum_A += dpp->weight_A;
sam = dpp->samples_B [0];
dpp->samples_B [0] = dpp->samples_B [1];
out_samples [1] = tmp = (dpp->samples_B [1] = in_samples [1]) - apply_weight (dpp->weight_B, sam);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
dpp->sum_B += dpp->weight_B;
in_samples += dir;
out_samples += dir;
}
break;
case 17:
while (num_samples--) {
int32_t sam, tmp;
sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
dpp->samples_A [1] = dpp->samples_A [0];
out_samples [0] = tmp = (dpp->samples_A [0] = in_samples [0]) - apply_weight (dpp->weight_A, sam);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
dpp->sum_A += dpp->weight_A;
sam = 2 * dpp->samples_B [0] - dpp->samples_B [1];
dpp->samples_B [1] = dpp->samples_B [0];
out_samples [1] = tmp = (dpp->samples_B [0] = in_samples [1]) - apply_weight (dpp->weight_B, sam);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
dpp->sum_B += dpp->weight_B;
in_samples += dir;
out_samples += dir;
}
break;
case 18:
while (num_samples--) {
int32_t sam, tmp;
sam = dpp->samples_A [0] + ((dpp->samples_A [0] - dpp->samples_A [1]) >> 1);
dpp->samples_A [1] = dpp->samples_A [0];
out_samples [0] = tmp = (dpp->samples_A [0] = in_samples [0]) - apply_weight (dpp->weight_A, sam);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
dpp->sum_A += dpp->weight_A;
sam = dpp->samples_B [0] + ((dpp->samples_B [0] - dpp->samples_B [1]) >> 1);
dpp->samples_B [1] = dpp->samples_B [0];
out_samples [1] = tmp = (dpp->samples_B [0] = in_samples [1]) - apply_weight (dpp->weight_B, sam);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
dpp->sum_B += dpp->weight_B;
in_samples += dir;
out_samples += dir;
}
break;
default: {
int k = dpp->term & (MAX_TERM - 1);
while (num_samples--) {
int32_t sam, tmp;
sam = dpp->samples_A [m];
out_samples [0] = tmp = (dpp->samples_A [k] = in_samples [0]) - apply_weight (dpp->weight_A, sam);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
dpp->sum_A += dpp->weight_A;
sam = dpp->samples_B [m];
out_samples [1] = tmp = (dpp->samples_B [k] = in_samples [1]) - apply_weight (dpp->weight_B, sam);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
dpp->sum_B += dpp->weight_B;
in_samples += dir;
out_samples += dir;
m = (m + 1) & (MAX_TERM - 1);
k = (k + 1) & (MAX_TERM - 1);
}
if (m) {
int32_t temp_A [MAX_TERM], temp_B [MAX_TERM];
int k;
memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B));
for (k = 0; k < MAX_TERM; k++) {
dpp->samples_A [k] = temp_A [m];
dpp->samples_B [k] = temp_B [m];
m = (m + 1) & (MAX_TERM - 1);
}
}
break;
}
case -1:
while (num_samples--) {
int32_t sam_A, sam_B, tmp;
sam_A = dpp->samples_A [0];
out_samples [0] = tmp = (sam_B = in_samples [0]) - apply_weight (dpp->weight_A, sam_A);
update_weight_clip (dpp->weight_A, dpp->delta, sam_A, tmp);
dpp->sum_A += dpp->weight_A;
out_samples [1] = tmp = (dpp->samples_A [0] = in_samples [1]) - apply_weight (dpp->weight_B, sam_B);
update_weight_clip (dpp->weight_B, dpp->delta, sam_B, tmp);
dpp->sum_B += dpp->weight_B;
in_samples += dir;
out_samples += dir;
}
break;
case -2:
while (num_samples--) {
int32_t sam_A, sam_B, tmp;
sam_B = dpp->samples_B [0];
out_samples [1] = tmp = (sam_A = in_samples [1]) - apply_weight (dpp->weight_B, sam_B);
update_weight_clip (dpp->weight_B, dpp->delta, sam_B, tmp);
dpp->sum_B += dpp->weight_B;
out_samples [0] = tmp = (dpp->samples_B [0] = in_samples [0]) - apply_weight (dpp->weight_A, sam_A);
update_weight_clip (dpp->weight_A, dpp->delta, sam_A, tmp);
dpp->sum_A += dpp->weight_A;
in_samples += dir;
out_samples += dir;
}
break;
case -3:
while (num_samples--) {
int32_t sam_A, sam_B, tmp;
sam_A = dpp->samples_A [0];
sam_B = dpp->samples_B [0];
dpp->samples_A [0] = tmp = in_samples [1];
out_samples [1] = tmp -= apply_weight (dpp->weight_B, sam_B);
update_weight_clip (dpp->weight_B, dpp->delta, sam_B, tmp);
dpp->sum_B += dpp->weight_B;
dpp->samples_B [0] = tmp = in_samples [0];
out_samples [0] = tmp -= apply_weight (dpp->weight_A, sam_A);
update_weight_clip (dpp->weight_A, dpp->delta, sam_A, tmp);
dpp->sum_A += dpp->weight_A;
in_samples += dir;
out_samples += dir;
}
break;
}
#ifdef PACK_DECORR_STEREO_PASS_CONT
if (cont_samples) {
if (dir < 0)
PACK_DECORR_STEREO_PASS_CONT_REV (dpp, in_samples, out_samples, cont_samples);
else
PACK_DECORR_STEREO_PASS_CONT (dpp, in_samples, out_samples, cont_samples);
}
#endif
}
static void reverse_decorr (struct decorr_pass *dpp)
{
if (dpp->term > MAX_TERM) {
int32_t sam_A, sam_B;
if (dpp->term & 1) {
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
}
else {
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
}
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_B [1] = dpp->samples_B [0];
dpp->samples_A [0] = sam_A;
dpp->samples_B [0] = sam_B;
if (dpp->term & 1) {
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
}
else {
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
}
dpp->samples_A [1] = sam_A;
dpp->samples_B [1] = sam_B;
}
else if (dpp->term > 1) {
int i = 0, j = dpp->term - 1, cnt = dpp->term / 2;
while (cnt--) {
i &= (MAX_TERM - 1);
j &= (MAX_TERM - 1);
dpp->samples_A [i] ^= dpp->samples_A [j];
dpp->samples_A [j] ^= dpp->samples_A [i];
dpp->samples_A [i] ^= dpp->samples_A [j];
dpp->samples_B [i] ^= dpp->samples_B [j];
dpp->samples_B [j] ^= dpp->samples_B [i];
dpp->samples_B [i++] ^= dpp->samples_B [j--];
}
}
else if (dpp->term == -1) {
}
else if (dpp->term == -2) {
}
else if (dpp->term == -3) {
}
}
static void decorr_stereo_buffer (WavpackExtraInfo *info, int32_t *samples, int32_t *outsamples, int32_t num_samples, int tindex)
{
struct decorr_pass dp, *dppi = info->dps + tindex;
int delta = dppi->delta, pre_delta;
int term = dppi->term;
if (delta == 7)
pre_delta = 7;
else if (delta < 2)
pre_delta = 3;
else
pre_delta = delta + 1;
CLEAR (dp);
dp.term = term;
dp.delta = pre_delta;
decorr_stereo_pass (samples, outsamples, num_samples > 2048 ? 2048 : num_samples, &dp, -1);
dp.delta = delta;
if (tindex == 0)
reverse_decorr (&dp);
else {
CLEAR (dp.samples_A);
CLEAR (dp.samples_B);
}
memcpy (dppi->samples_A, dp.samples_A, sizeof (dp.samples_A));
memcpy (dppi->samples_B, dp.samples_B, sizeof (dp.samples_B));
dppi->weight_A = dp.weight_A;
dppi->weight_B = dp.weight_B;
if (delta == 0) {
dp.delta = 1;
decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1);
dp.delta = 0;
memcpy (dp.samples_A, dppi->samples_A, sizeof (dp.samples_A));
memcpy (dp.samples_B, dppi->samples_B, sizeof (dp.samples_B));
dppi->weight_A = dp.weight_A = dp.sum_A / num_samples;
dppi->weight_B = dp.weight_B = dp.sum_B / num_samples;
}
// if (memcmp (dppi, &dp, sizeof (dp)))
// error_line ("decorr_passes don't match, delta = %d", delta);
decorr_stereo_pass (samples, outsamples, num_samples, &dp, 1);
}
static int log2overhead (int first_term, int num_terms)
{
#ifdef USE_OVERHEAD
if (first_term > MAX_TERM)
return (8 + num_terms * 3) << 11;
else
return (4 + num_terms * 3) << 11;
#else
return 0;
#endif
}
static void recurse_stereo (WavpackContext *wpc, WavpackExtraInfo *info, int depth, int delta, uint32_t input_bits)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int term, branches = ((wpc->config.extra_flags & EXTRA_BRANCHES) >> 6) - depth;
int32_t *samples, *outsamples;
uint32_t term_bits [22], bits;
if (branches < 1 || depth + 1 == info->nterms)
branches = 1;
CLEAR (term_bits);
samples = info->sampleptrs [depth];
outsamples = info->sampleptrs [depth + 1];
for (term = -3; term <= 18; ++term) {
if (!term || (term > 8 && term < 17))
continue;
if (term == 17 && branches == 1 && depth + 1 < info->nterms)
continue;
if (term == -1 || term == -2)
if (!(wps->wphdr.flags & CROSS_DECORR))
continue;
if ((wpc->config.flags & CONFIG_FAST_FLAG) && (term > 4 && term < 17))
continue;
info->dps [depth].term = term;
info->dps [depth].delta = delta;
decorr_stereo_buffer (info, samples, outsamples, wps->wphdr.block_samples, depth);
bits = LOG2BUFFER (outsamples, wps->wphdr.block_samples * 2, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (info->dps [0].term, depth + 1);
if (bits < info->best_bits) {
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * (depth + 1));
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [depth + 1], wps->wphdr.block_samples * 8);
}
term_bits [term + 3] = bits;
}
while (depth + 1 < info->nterms && branches--) {
uint32_t local_best_bits = input_bits;
int best_term = 0, i;
for (i = 0; i < 22; ++i)
if (term_bits [i] && term_bits [i] < local_best_bits) {
local_best_bits = term_bits [i];
// term_bits [i] = 0;
best_term = i - 3;
}
if (!best_term)
break;
term_bits [best_term + 3] = 0;
info->dps [depth].term = best_term;
info->dps [depth].delta = delta;
decorr_stereo_buffer (info, samples, outsamples, wps->wphdr.block_samples, depth);
// if (log2buffer (outsamples, wps->wphdr.block_samples * 2, 0) != local_best_bits)
// error_line ("data doesn't match!");
recurse_stereo (wpc, info, depth + 1, delta, local_best_bits);
}
}
static void delta_stereo (WavpackContext *wpc, WavpackExtraInfo *info)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int lower = FALSE;
int delta, d;
uint32_t bits;
if (wps->decorr_passes [0].term)
delta = wps->decorr_passes [0].delta;
else
return;
for (d = delta - 1; d >= 0; --d) {
int i;
if (!d && (wps->wphdr.flags & HYBRID_FLAG))
break;
for (i = 0; i < info->nterms && wps->decorr_passes [i].term; ++i) {
info->dps [i].term = wps->decorr_passes [i].term;
info->dps [i].delta = d;
decorr_stereo_buffer (info, info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, i);
}
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples * 2, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
lower = TRUE;
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 8);
}
else
break;
}
for (d = delta + 1; !lower && d <= 7; ++d) {
int i;
for (i = 0; i < info->nterms && wps->decorr_passes [i].term; ++i) {
info->dps [i].term = wps->decorr_passes [i].term;
info->dps [i].delta = d;
decorr_stereo_buffer (info, info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, i);
}
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples * 2, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 8);
}
else
break;
}
}
static void sort_stereo (WavpackContext *wpc, WavpackExtraInfo *info)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int reversed = TRUE;
uint32_t bits;
while (reversed) {
int ri, i;
memcpy (info->dps, wps->decorr_passes, sizeof (wps->decorr_passes));
reversed = FALSE;
for (ri = 0; ri < info->nterms && wps->decorr_passes [ri].term; ++ri) {
if (ri + 1 >= info->nterms || !wps->decorr_passes [ri+1].term)
break;
if (wps->decorr_passes [ri].term == wps->decorr_passes [ri+1].term) {
decorr_stereo_buffer (info, info->sampleptrs [ri], info->sampleptrs [ri+1], wps->wphdr.block_samples, ri);
continue;
}
info->dps [ri] = wps->decorr_passes [ri+1];
info->dps [ri+1] = wps->decorr_passes [ri];
for (i = ri; i < info->nterms && wps->decorr_passes [i].term; ++i)
decorr_stereo_buffer (info, info->sampleptrs [i], info->sampleptrs [i+1], wps->wphdr.block_samples, i);
bits = LOG2BUFFER (info->sampleptrs [i], wps->wphdr.block_samples * 2, info->log_limit);
if (bits != (uint32_t) -1)
bits += log2overhead (wps->decorr_passes [0].term, i);
if (bits < info->best_bits) {
reversed = TRUE;
info->best_bits = bits;
CLEAR (wps->decorr_passes);
memcpy (wps->decorr_passes, info->dps, sizeof (info->dps [0]) * i);
memcpy (info->sampleptrs [info->nterms + 1], info->sampleptrs [i], wps->wphdr.block_samples * 8);
}
else {
info->dps [ri] = wps->decorr_passes [ri];
info->dps [ri+1] = wps->decorr_passes [ri+1];
decorr_stereo_buffer (info, info->sampleptrs [ri], info->sampleptrs [ri+1], wps->wphdr.block_samples, ri);
}
}
}
}
static const uint32_t xtable [] = { 91, 123, 187, 251 };
static void analyze_stereo (WavpackContext *wpc, int32_t *samples, int do_samples)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
WavpackExtraInfo info;
int i;
#ifdef LOG_LIMIT
info.log_limit = (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) + 4) * 256;
if (info.log_limit > LOG_LIMIT)
info.log_limit = LOG_LIMIT;
#else
info.log_limit = 0;
#endif
if (wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG))
wpc->config.extra_flags = xtable [wpc->config.xmode - 4];
else
wpc->config.extra_flags = xtable [wpc->config.xmode - 3];
info.nterms = wps->num_terms;
for (i = 0; i < info.nterms + 2; ++i)
info.sampleptrs [i] = malloc (wps->wphdr.block_samples * 8);
memcpy (info.dps, wps->decorr_passes, sizeof (info.dps));
memcpy (info.sampleptrs [0], samples, wps->wphdr.block_samples * 8);
for (i = 0; i < info.nterms && info.dps [i].term; ++i)
decorr_stereo_pass (info.sampleptrs [i], info.sampleptrs [i + 1], wps->wphdr.block_samples, info.dps + i, 1);
info.best_bits = LOG2BUFFER (info.sampleptrs [info.nterms], wps->wphdr.block_samples * 2, 0) * 1;
info.best_bits += log2overhead (info.dps [0].term, i);
memcpy (info.sampleptrs [info.nterms + 1], info.sampleptrs [i], wps->wphdr.block_samples * 8);
if (wpc->config.extra_flags & EXTRA_BRANCHES)
recurse_stereo (wpc, &info, 0, (int) floor (wps->delta_decay + 0.5),
LOG2BUFFER (info.sampleptrs [0], wps->wphdr.block_samples * 2, 0));
if (wpc->config.extra_flags & EXTRA_SORT_FIRST)
sort_stereo (wpc, &info);
if (wpc->config.extra_flags & EXTRA_TRY_DELTAS) {
delta_stereo (wpc, &info);
if ((wpc->config.extra_flags & EXTRA_ADJUST_DELTAS) && wps->decorr_passes [0].term)
wps->delta_decay = (float)((wps->delta_decay * 2.0 + wps->decorr_passes [0].delta) / 3.0);
else
wps->delta_decay = 2.0;
}
if (wpc->config.extra_flags & EXTRA_SORT_LAST)
sort_stereo (wpc, &info);
if (do_samples)
memcpy (samples, info.sampleptrs [info.nterms + 1], wps->wphdr.block_samples * 8);
for (i = 0; i < info.nterms; ++i)
if (!wps->decorr_passes [i].term)
break;
wps->num_terms = i;
for (i = 0; i < info.nterms + 2; ++i)
free (info.sampleptrs [i]);
}
static void stereo_add_noise (WavpackStream *wps, int32_t *lptr, int32_t *rptr)
{
int shaping_weight, new = wps->wphdr.flags & NEW_SHAPING;
short *shaping_array = wps->dc.shaping_array;
int32_t error [2], temp, cnt;
scan_word (wps, rptr, wps->wphdr.block_samples, -1);
cnt = wps->wphdr.block_samples;
CLEAR (error);
if (wps->wphdr.flags & HYBRID_SHAPE) {
while (cnt--) {
if (shaping_array)
shaping_weight = *shaping_array++;
else
shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
temp = -apply_weight (shaping_weight, error [0]);
if (new && shaping_weight < 0 && temp) {
if (temp == error [0])
temp = (temp < 0) ? temp + 1 : temp - 1;
lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0] + temp);
}
else
lptr [0] += (error [0] = nosend_word (wps, rptr [0], 0) - rptr [0]) + temp;
if (!shaping_array)
shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16;
temp = -apply_weight (shaping_weight, error [1]);
if (new && shaping_weight < 0 && temp) {
if (temp == error [1])
temp = (temp < 0) ? temp + 1 : temp - 1;
lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1] + temp);
}
else
lptr [1] += (error [1] = nosend_word (wps, rptr [1], 1) - rptr [1]) + temp;
lptr += 2;
rptr += 2;
}
if (!shaping_array) {
wps->dc.shaping_acc [0] -= wps->dc.shaping_delta [0] * wps->wphdr.block_samples;
wps->dc.shaping_acc [1] -= wps->dc.shaping_delta [1] * wps->wphdr.block_samples;
}
}
else
while (cnt--) {
lptr [0] += nosend_word (wps, rptr [0], 0) - rptr [0];
lptr [1] += nosend_word (wps, rptr [1], 1) - rptr [1];
lptr += 2;
rptr += 2;
}
}
void execute_stereo (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples)
{
int32_t *temp_buffer [2], *best_buffer, *noisy_buffer = NULL, *js_buffer = NULL;
struct decorr_pass temp_decorr_pass, save_decorr_passes [MAX_NTERMS];
WavpackStream *wps = wpc->streams [wpc->current_stream];
int32_t num_samples = wps->wphdr.block_samples;
int32_t buf_size = sizeof (int32_t) * num_samples * 2;
uint32_t best_size = (uint32_t) -1, size;
int log_limit, force_js = 0, force_ts = 0, pi, i;
#ifdef SKIP_DECORRELATION
CLEAR (wps->decorr_passes);
wps->num_terms = 0;
return;
#endif
for (i = 0; i < num_samples * 2; ++i)
if (samples [i])
break;
if (i == num_samples * 2) {
wps->wphdr.flags &= ~((uint32_t) JOINT_STEREO);
CLEAR (wps->decorr_passes);
wps->num_terms = 0;
init_words (wps);
return;
}
#ifdef LOG_LIMIT
log_limit = (((wps->wphdr.flags & MAG_MASK) >> MAG_LSB) + 4) * 256;
if (log_limit > LOG_LIMIT)
log_limit = LOG_LIMIT;
#else
log_limit = 0;
#endif
if (wpc->config.flags & CONFIG_JOINT_OVERRIDE) {
if (wps->wphdr.flags & JOINT_STEREO)
force_js = 1;
else
force_ts = 1;
}
CLEAR (save_decorr_passes);
temp_buffer [0] = malloc (buf_size);
temp_buffer [1] = malloc (buf_size);
best_buffer = malloc (buf_size);
if (wps->num_passes > 1 && (wps->wphdr.flags & HYBRID_FLAG)) {
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = 2;
temp_decorr_pass.term = 18;
decorr_stereo_pass (samples, temp_buffer [0],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
reverse_decorr (&temp_decorr_pass);
decorr_stereo_pass (samples, temp_buffer [0], num_samples, &temp_decorr_pass, 1);
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = 2;
temp_decorr_pass.term = 17;
decorr_stereo_pass (temp_buffer [0], temp_buffer [1],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
decorr_stereo_pass (temp_buffer [0], temp_buffer [1], num_samples, &temp_decorr_pass, 1);
noisy_buffer = malloc (buf_size);
memcpy (noisy_buffer, samples, buf_size);
stereo_add_noise (wps, noisy_buffer, temp_buffer [1]);
no_history = 1;
}
if (no_history || wps->num_passes >= 7)
wps->best_decorr = wps->mask_decorr = 0;
for (pi = 0; pi < wps->num_passes;) {
const WavpackDecorrSpec *wpds;
int nterms, c, j;
if (!pi)
c = wps->best_decorr;
else {
if (wps->mask_decorr == 0)
c = 0;
else
c = (wps->best_decorr & (wps->mask_decorr - 1)) | wps->mask_decorr;
if (c == wps->best_decorr) {
wps->mask_decorr = wps->mask_decorr ? ((wps->mask_decorr << 1) & (wps->num_decorrs - 1)) : 1;
continue;
}
}
wpds = &wps->decorr_specs [c];
nterms = (int) strlen ((char *) wpds->terms);
while (1) {
if (force_js || (wpds->joint_stereo && !force_ts)) {
if (!js_buffer) {
int32_t *lptr, cnt = num_samples;
lptr = js_buffer = malloc (buf_size);
memcpy (js_buffer, noisy_buffer ? noisy_buffer : samples, buf_size);
while (cnt--) {
lptr [1] += ((lptr [0] -= lptr [1]) >> 1);
lptr += 2;
}
}
memcpy (temp_buffer [0], js_buffer, buf_size);
}
else
memcpy (temp_buffer [0], noisy_buffer ? noisy_buffer : samples, buf_size);
CLEAR (save_decorr_passes);
for (j = 0; j < nterms; ++j) {
CLEAR (temp_decorr_pass);
temp_decorr_pass.delta = wpds->delta;
temp_decorr_pass.term = wpds->terms [j];
if (temp_decorr_pass.term < 0 && !(wps->wphdr.flags & CROSS_DECORR))
temp_decorr_pass.term = -3;
decorr_stereo_pass (temp_buffer [j&1], temp_buffer [~j&1],
num_samples > 2048 ? 2048 : num_samples, &temp_decorr_pass, -1);
if (j) {
CLEAR (temp_decorr_pass.samples_A);
CLEAR (temp_decorr_pass.samples_B);
}
else
reverse_decorr (&temp_decorr_pass);
memcpy (save_decorr_passes + j, &temp_decorr_pass, sizeof (struct decorr_pass));
decorr_stereo_pass (temp_buffer [j&1], temp_buffer [~j&1], num_samples, &temp_decorr_pass, 1);
}
size = LOG2BUFFER (temp_buffer [j&1], num_samples * 2, log_limit);
if (size == (uint32_t) -1 && nterms)
nterms >>= 1;
else
break;
}
size += log2overhead (wpds->terms [0], nterms);
if (size < best_size) {
memcpy (best_buffer, temp_buffer [j&1], buf_size);
memcpy (wps->decorr_passes, save_decorr_passes, sizeof (struct decorr_pass) * MAX_NTERMS);
wps->num_terms = nterms;
wps->best_decorr = c;
best_size = size;
}
if (pi++)
wps->mask_decorr = wps->mask_decorr ? ((wps->mask_decorr << 1) & (wps->num_decorrs - 1)) : 1;
}
if (force_js || (wps->decorr_specs [wps->best_decorr].joint_stereo && !force_ts))
wps->wphdr.flags |= JOINT_STEREO;
else
wps->wphdr.flags &= ~((uint32_t) JOINT_STEREO);
if (wpc->config.xmode > 3) {
if (wps->wphdr.flags & JOINT_STEREO) {
analyze_stereo (wpc, js_buffer, do_samples);
if (do_samples)
memcpy (samples, js_buffer, buf_size);
}
else if (noisy_buffer) {
analyze_stereo (wpc, noisy_buffer, do_samples);
if (do_samples)
memcpy (samples, noisy_buffer, buf_size);
}
else
analyze_stereo (wpc, samples, do_samples);
}
else if (do_samples)
memcpy (samples, best_buffer, buf_size);
if (wpc->config.xmode > 3 || no_history || wps->joint_stereo != wps->decorr_specs [wps->best_decorr].joint_stereo) {
wps->joint_stereo = wps->decorr_specs [wps->best_decorr].joint_stereo;
scan_word (wps, best_buffer, num_samples, -1);
}
if (noisy_buffer)
free (noisy_buffer);
if (js_buffer)
free (js_buffer);
free (temp_buffer [1]);
free (temp_buffer [0]);
free (best_buffer);
#ifdef EXTRA_DUMP
if (1) {
char string [256], substring [20];
int i;
sprintf (string, "%s: terms =",
(wps->wphdr.flags & JOINT_STEREO) ? "JS" : "TS");
for (i = 0; i < wps->num_terms; ++i) {
if (wps->decorr_passes [i].term) {
if (i && wps->decorr_passes [i-1].delta == wps->decorr_passes [i].delta)
sprintf (substring, " %d", wps->decorr_passes [i].term);
else
sprintf (substring, " %d->%d", wps->decorr_passes [i].term,
wps->decorr_passes [i].delta);
}
else
sprintf (substring, " *");
strcat (string, substring);
}
error_line (string);
}
#endif
}

View file

@ -1,291 +0,0 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* (This is a heavily cut-down "BSD license".)
*
* This differs from Colin Plumb's older public domain implementation in that
* no exactly 32-bit integer data type is required (any 32-bit or wider
* unsigned integer data type will do), there's no compile-time endianness
* configuration, and the function prototypes match OpenSSL's. No code from
* Colin Plumb's implementation has been reused; this comment merely compares
* the properties of the two independent implementations.
*
* The primary goals of this implementation are portability and ease of use.
* It is meant to be fast, but not as fast as possible. Some known
* optimizations are not included to reduce source code size and avoid
* compile-time configuration.
*/
#ifndef HAVE_LIBCRYPTO
#include <string.h>
#include "md5.h"
/*
* The basic MD5 functions.
*
* F and G are optimized compared to their RFC 1321 definitions for
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
* implementation.
*/
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) (((x) ^ (y)) ^ (z))
#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
/*
* The MD5 transformation for all four rounds.
*/
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
/*
* SET reads 4 input bytes in little-endian byte order and stores them in a
* properly aligned word in host byte order.
*
* The check for little-endian architectures that tolerate unaligned memory
* accesses is just an optimization. Nothing will break if it fails to detect
* a suitable architecture.
*
* Unfortunately, this optimization may be a C strict aliasing rules violation
* if the caller's data buffer has effective type that cannot be aliased by
* MD5_u32plus. In practice, this problem may occur if these MD5 routines are
* inlined into a calling function, or with future and dangerously advanced
* link-time optimizations. For the time being, keeping these MD5 routines in
* their own translation unit avoids the problem.
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define SET(n) \
(*(MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
#else
#define SET(n) \
(ctx->block[(n)] = \
(MD5_u32plus)ptr[(n) * 4] | \
((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \
((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \
((MD5_u32plus)ptr[(n) * 4 + 3] << 24))
#define GET(n) \
(ctx->block[(n)])
#endif
/*
* This processes one or more 64-byte data blocks, but does NOT update the bit
* counters. There are no alignment requirements.
*/
static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
{
const unsigned char *ptr;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
ptr = (const unsigned char *)data;
a = ctx->a;
b = ctx->b;
c = ctx->c;
d = ctx->d;
do {
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
/* Round 2 */
STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
/* Round 3 */
STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
/* Round 4 */
STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx->a = a;
ctx->b = b;
ctx->c = c;
ctx->d = d;
return ptr;
}
void MD5_Init(MD5_CTX *ctx)
{
ctx->a = 0x67452301;
ctx->b = 0xefcdab89;
ctx->c = 0x98badcfe;
ctx->d = 0x10325476;
ctx->lo = 0;
ctx->hi = 0;
}
void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
{
MD5_u32plus saved_lo;
unsigned long used, available;
saved_lo = ctx->lo;
if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
ctx->hi++;
ctx->hi += size >> 29;
used = saved_lo & 0x3f;
if (used) {
available = 64 - used;
if (size < available) {
memcpy(&ctx->buffer[used], data, size);
return;
}
memcpy(&ctx->buffer[used], data, available);
data = (const unsigned char *)data + available;
size -= available;
body(ctx, ctx->buffer, 64);
}
if (size >= 64) {
data = body(ctx, data, size & ~(unsigned long)0x3f);
size &= 0x3f;
}
memcpy(ctx->buffer, data, size);
}
#define OUT(dst, src) \
(dst)[0] = (unsigned char)(src); \
(dst)[1] = (unsigned char)((src) >> 8); \
(dst)[2] = (unsigned char)((src) >> 16); \
(dst)[3] = (unsigned char)((src) >> 24);
void MD5_Final(unsigned char *result, MD5_CTX *ctx)
{
unsigned long used, available;
used = ctx->lo & 0x3f;
ctx->buffer[used++] = 0x80;
available = 64 - used;
if (available < 8) {
memset(&ctx->buffer[used], 0, available);
body(ctx, ctx->buffer, 64);
used = 0;
available = 64;
}
memset(&ctx->buffer[used], 0, available - 8);
ctx->lo <<= 3;
OUT(&ctx->buffer[56], ctx->lo)
OUT(&ctx->buffer[60], ctx->hi)
body(ctx, ctx->buffer, 64);
OUT(&result[0], ctx->a)
OUT(&result[4], ctx->b)
OUT(&result[8], ctx->c)
OUT(&result[12], ctx->d)
memset(ctx, 0, sizeof(*ctx));
}
#endif

View file

@ -1,45 +0,0 @@
/*
* This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
* MD5 Message-Digest Algorithm (RFC 1321).
*
* Homepage:
* http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
*
* Author:
* Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See md5.c for more information.
*/
#ifdef HAVE_LIBCRYPTO
#include <openssl/md5.h>
#elif !defined(_MD5_H)
#define _MD5_H
/* Any 32-bit or wider unsigned integer data type will do */
typedef unsigned int MD5_u32plus;
typedef struct {
MD5_u32plus lo, hi;
MD5_u32plus a, b, c, d;
unsigned char buffer[64];
MD5_u32plus block[16];
} MD5_CTX;
extern void MD5_Init(MD5_CTX *ctx);
extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
#endif

View file

@ -1,304 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// open_filename.c
// This module provides all the code required to open an existing WavPack
// file, by filename, for reading. It does not contain the actual code to
// unpack audio data and this was done so that programs that just want to
// query WavPack files for information (like, for example, taggers) don't
// need to link in a lot of unnecessary code.
//
// To allow opening files by filename, this code provides an interface
// between the reader callback mechanism that WavPack uses internally and
// the standard fstream C library. Note that in applications that do not
// require opening files by filename, this module can be omitted (which
// might make building easier).
//
// For Unicode support on Windows, a flag has been added (OPEN_FILE_UTF8)
// that forces the filename string to be assumed UTF-8 and converted to
// a widechar string suitable for _wfopen(). Without this flag we revert
// to the previous behavior of simply calling fopen() and hoping that the
// local character set works. This is ignored on non-Windows platforms
// (which is okay because they are probably UTF-8 anyway).
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#include <fcntl.h>
#include <sys/stat.h>
#if (defined(__GNUC__) || defined(__sun)) && !defined(_WIN32)
#include <unistd.h>
#endif
#ifdef __OS2__
#include <io.h>
#endif
#ifdef _WIN32
#define fileno _fileno
static FILE *fopen_utf8 (const char *filename_utf8, const char *mode_utf8);
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#endif
#ifdef HAVE_FSEEKO
#define fseek fseeko
#define ftell ftello
#endif
static int32_t read_bytes (void *id, void *data, int32_t bcount)
{
return (int32_t) fread (data, 1, bcount, (FILE*) id);
}
static int64_t get_pos (void *id)
{
#ifdef _WIN32
return _ftelli64 ((FILE*) id);
#else
return ftell ((FILE*) id);
#endif
}
static int set_pos_abs (void *id, int64_t pos)
{
#ifdef _WIN32
return _fseeki64 (id, pos, SEEK_SET);
#else
return fseek (id, pos, SEEK_SET);
#endif
}
static int set_pos_rel (void *id, int64_t delta, int mode)
{
#ifdef _WIN32
return _fseeki64 (id, delta, mode);
#else
return fseek (id, delta, mode);
#endif
}
static int push_back_byte (void *id, int c)
{
return ungetc (c, id);
}
#ifdef _WIN32
static int64_t get_length (void *id)
{
LARGE_INTEGER Size;
HANDLE fHandle;
if (id == NULL)
return 0;
fHandle = (HANDLE)_get_osfhandle(_fileno((FILE*) id));
if (fHandle == INVALID_HANDLE_VALUE)
return 0;
Size.u.LowPart = GetFileSize(fHandle, &Size.u.HighPart);
if (Size.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
return 0;
return (int64_t)Size.QuadPart;
}
#else
static int64_t get_length (void *id)
{
FILE *file = id;
struct stat statbuf;
if (!file || fstat (fileno (file), &statbuf) || !S_ISREG(statbuf.st_mode))
return 0;
return statbuf.st_size;
}
#endif
static int can_seek (void *id)
{
FILE *file = id;
struct stat statbuf;
return file && !fstat (fileno (file), &statbuf) && S_ISREG(statbuf.st_mode);
}
static int32_t write_bytes (void *id, void *data, int32_t bcount)
{
return (int32_t) fwrite (data, 1, bcount, (FILE*) id);
}
#ifdef _WIN32
static int truncate_here (void *id)
{
FILE *file = id;
int64_t curr_pos = _ftelli64 (file);
return _chsize_s (fileno (file), curr_pos);
}
#else
static int truncate_here (void *id)
{
FILE *file = id;
off_t curr_pos = ftell (file);
return ftruncate (fileno (file), curr_pos);
}
#endif
static int close_stream (void *id)
{
return fclose ((FILE*) id);
}
// int32_t (*read_bytes)(void *id, void *data, int32_t bcount);
// int32_t (*write_bytes)(void *id, void *data, int32_t bcount);
// int64_t (*get_pos)(void *id); // new signature for large files
// int (*set_pos_abs)(void *id, int64_t pos); // new signature for large files
// int (*set_pos_rel)(void *id, int64_t delta, int mode); // new signature for large files
// int (*push_back_byte)(void *id, int c);
// int64_t (*get_length)(void *id); // new signature for large files
// int (*can_seek)(void *id);
// int (*truncate_here)(void *id); // new function to truncate file at current position
// int (*close)(void *id); // new function to close file
static WavpackStreamReader64 freader = {
read_bytes, write_bytes, get_pos, set_pos_abs, set_pos_rel,
push_back_byte, get_length, can_seek, truncate_here, close_stream
};
// This function attempts to open the specified WavPack file for reading. If
// this fails for any reason then an appropriate message is copied to "error"
// (which must accept 80 characters) and NULL is returned, otherwise a
// pointer to a WavpackContext structure is returned (which is used to call
// all other functions in this module). A filename beginning with "-" is
// assumed to be stdin. The "flags" argument has the following bit mask
// values to specify details of the open operation:
// OPEN_WVC: attempt to open/read "correction" file
// OPEN_TAGS: attempt to read ID3v1 / APEv2 tags (requires seekable file)
// OPEN_WRAPPER: make audio wrapper available (i.e. RIFF) to caller
// OPEN_2CH_MAX: open only first stream of multichannel file (usually L/R)
// OPEN_NORMALIZE: normalize floating point data to +/- 1.0 (w/ offset exp)
// OPEN_STREAMING: blindly unpacks blocks w/o regard to header file position
// OPEN_EDIT_TAGS: allow editing of tags (file must be writable)
// OPEN_FILE_UTF8: assume infilename is UTF-8 encoded (Windows only)
// Version 4.2 of the WavPack library adds the OPEN_STREAMING flag. This is
// essentially a "raw" mode where the library will simply decode any blocks
// fed it through the reader callback, regardless of where those blocks came
// from in a stream. The only requirement is that complete WavPack blocks are
// fed to the decoder (and this may require multiple blocks in multichannel
// mode) and that complete blocks are decoded (even if all samples are not
// actually required). All the blocks must contain the same number of channels
// and bit resolution, and the correction data must be either present or not.
// All other parameters may change from block to block (like lossy/lossless).
// Obviously, in this mode any seeking must be performed by the application
// (and again, decoding must start at the beginning of the block containing
// the seek sample).
WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset)
{
char *file_mode = (flags & OPEN_EDIT_TAGS) ? "r+b" : "rb";
FILE *(*fopen_func)(const char *, const char *) = fopen;
FILE *wv_id, *wvc_id;
#ifdef _WIN32
if (flags & OPEN_FILE_UTF8)
fopen_func = fopen_utf8;
#endif
if (*infilename == '-') {
wv_id = stdin;
#if defined(_WIN32)
_setmode (fileno (stdin), O_BINARY);
#endif
#if defined(__OS2__)
setmode (fileno (stdin), O_BINARY);
#endif
}
else if ((wv_id = fopen_func (infilename, file_mode)) == NULL) {
if (error) strcpy (error, (flags & OPEN_EDIT_TAGS) ? "can't open file for editing" : "can't open file");
return NULL;
}
if (*infilename != '-' && (flags & OPEN_WVC)) {
char *in2filename = malloc (strlen (infilename) + 10);
strcpy (in2filename, infilename);
strcat (in2filename, "c");
wvc_id = fopen_func (in2filename, "rb");
free (in2filename);
}
else
wvc_id = NULL;
return WavpackOpenFileInputEx64 (&freader, wv_id, wvc_id, error, flags, norm_offset);
}
#ifdef _WIN32
// The following code Copyright (c) 2004-2012 LoRd_MuldeR <mulder2@gmx.de>
// (see cli/win32_unicode_support.c for full license)
static wchar_t *utf8_to_utf16(const char *input)
{
wchar_t *Buffer;
int BuffSize = 0, Result = 0;
BuffSize = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
Buffer = (wchar_t*) malloc(sizeof(wchar_t) * BuffSize);
if(Buffer)
{
Result = MultiByteToWideChar(CP_UTF8, 0, input, -1, Buffer, BuffSize);
}
return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
}
static FILE *fopen_utf8(const char *filename_utf8, const char *mode_utf8)
{
FILE *ret = NULL;
wchar_t *filename_utf16 = utf8_to_utf16(filename_utf8);
wchar_t *mode_utf16 = utf8_to_utf16(mode_utf8);
if(filename_utf16 && mode_utf16)
{
ret = _wfopen(filename_utf16, mode_utf16);
}
if(filename_utf16) free(filename_utf16);
if(mode_utf16) free(mode_utf16);
return ret;
}
#endif

View file

@ -1,114 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2019 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// open_legacy.c
// This code provides an interface between the new reader callback mechanism that
// WavPack uses internally and the old reader callback functions that did not
// provide large file support.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
typedef struct {
WavpackStreamReader *reader;
void *id;
} WavpackReaderTranslator;
static int32_t trans_read_bytes (void *id, void *data, int32_t bcount)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->read_bytes (trans->id, data, bcount);
}
static int32_t trans_write_bytes (void *id, void *data, int32_t bcount)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->write_bytes (trans->id, data, bcount);
}
static int64_t trans_get_pos (void *id)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->get_pos (trans->id);
}
static int trans_set_pos_abs (void *id, int64_t pos)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->set_pos_abs (trans->id, (uint32_t) pos);
}
static int trans_set_pos_rel (void *id, int64_t delta, int mode)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->set_pos_rel (trans->id, (int32_t) delta, mode);
}
static int trans_push_back_byte (void *id, int c)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->push_back_byte (trans->id, c);
}
static int64_t trans_get_length (void *id)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->get_length (trans->id);
}
static int trans_can_seek (void *id)
{
WavpackReaderTranslator *trans = (WavpackReaderTranslator *)id;
return trans->reader->can_seek (trans->id);
}
static int trans_close_stream (void *id)
{
free (id);
return 0;
}
static WavpackStreamReader64 trans_reader = {
trans_read_bytes, trans_write_bytes, trans_get_pos, trans_set_pos_abs, trans_set_pos_rel,
trans_push_back_byte, trans_get_length, trans_can_seek, NULL, trans_close_stream
};
// This function is identical to WavpackOpenFileInput64() except that instead
// of providing the new 64-bit reader callbacks, the old reader callbacks are
// utilized and a translation layer is employed. It is provided as a compatibility
// function for existing applications. To ensure that streaming applications using
// this function continue to work, the OPEN_NO_CHECKSUM flag is forced on when
// the OPEN_STREAMING flag is set.
WavpackContext *WavpackOpenFileInputEx (WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset)
{
WavpackReaderTranslator *trans_wv = NULL, *trans_wvc = NULL;
// this prevents existing streaming applications from failing if they try to pass
// in blocks that have been modified from the original (e.g., Matroska blocks)
if (flags & OPEN_STREAMING)
flags |= OPEN_NO_CHECKSUM;
if (wv_id) {
trans_wv = (WavpackReaderTranslator *)malloc (sizeof (WavpackReaderTranslator));
trans_wv->reader = reader;
trans_wv->id = wv_id;
}
if (wvc_id) {
trans_wvc = (WavpackReaderTranslator *)malloc (sizeof (WavpackReaderTranslator));
trans_wvc->reader = reader;
trans_wvc->id = wvc_id;
}
return WavpackOpenFileInputEx64 (&trans_reader, trans_wv, trans_wvc, error, flags, norm_offset);
}

View file

@ -1,315 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2019 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// open_raw.c
// This code provides the ability to decode WavPack frames directly from
// memory for use in a streaming application. It can handle full blocks
// or the headerless block data provided by Matroska and the DirectShow
// WavPack splitter. For information about how Matroska stores WavPack,
// see: https://www.matroska.org/technical/specs/codecid/wavpack.html
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
typedef struct {
unsigned char *sptr, *dptr, *eptr, free_required;
} RawSegment;
typedef struct {
RawSegment *segments;
int num_segments, curr_segment;
unsigned char ungetc_char, ungetc_flag;
} WavpackRawContext;
static int32_t raw_read_bytes (void *id, void *data, int32_t bcount)
{
WavpackRawContext *rcxt = id;
unsigned char *outptr = data;
while (bcount) {
if (rcxt->ungetc_flag) {
*outptr++ = rcxt->ungetc_char;
rcxt->ungetc_flag = 0;
bcount--;
}
else if (rcxt->curr_segment < rcxt->num_segments) {
RawSegment *segptr = rcxt->segments + rcxt->curr_segment;
int bytes_to_copy = (int)(segptr->eptr - segptr->dptr);
if (bytes_to_copy > bcount)
bytes_to_copy = bcount;
memcpy (outptr, segptr->dptr, bytes_to_copy);
outptr += bytes_to_copy;
bcount -= bytes_to_copy;
if ((segptr->dptr += bytes_to_copy) == segptr->eptr)
rcxt->curr_segment++;
}
else
break;
}
return (int32_t)(outptr - (unsigned char *) data);
}
static int32_t raw_write_bytes (void *id, void *data, int32_t bcount)
{
return 0;
}
static int64_t raw_get_pos (void *id)
{
return 0;
}
static int raw_set_pos_abs (void *id, int64_t pos)
{
return 0;
}
static int raw_set_pos_rel (void *id, int64_t delta, int mode)
{
return 0;
}
static int raw_push_back_byte (void *id, int c)
{
WavpackRawContext *rcxt = id;
rcxt->ungetc_char = c;
rcxt->ungetc_flag = 1;
return c;
}
static int64_t raw_get_length (void *id)
{
return 0;
}
static int raw_can_seek (void *id)
{
return 0;
}
static int raw_close_stream (void *id)
{
WavpackRawContext *rcxt = id;
int i;
if (rcxt) {
for (i = 0; i < rcxt->num_segments; ++i)
if (rcxt->segments [i].sptr && rcxt->segments [i].free_required)
free (rcxt->segments [i].sptr);
if (rcxt->segments) free (rcxt->segments);
free (rcxt);
}
return 0;
}
static WavpackStreamReader64 raw_reader = {
raw_read_bytes, raw_write_bytes, raw_get_pos, raw_set_pos_abs, raw_set_pos_rel,
raw_push_back_byte, raw_get_length, raw_can_seek, NULL, raw_close_stream
};
// This function is similar to WavpackOpenFileInput() except that instead of
// providing a filename to open, the caller provides pointers to buffered
// WavPack frames (both standard and, optionally, correction data). It
// decodes only a single frame. Note that in this context, a "frame" is a
// collection of WavPack blocks that represent all the channels present. In
// the case of mono or [most] stereo streams, this is the same thing, but
// for multichannel streams each frame consists of several WavPack blocks
// (which can contain only 1 or 2 channels).
WavpackContext *WavpackOpenRawDecoder (
void *main_data, int32_t main_size,
void *corr_data, int32_t corr_size,
int16_t version, char *error, int flags, int norm_offset)
{
WavpackRawContext *raw_wv = NULL, *raw_wvc = NULL;
// if the WavPack data does not contain headers we assume Matroska-style storage
// and recreate the missing headers
if (strncmp (main_data, "wvpk", 4)) {
uint32_t multiple_blocks = 0, block_size, block_samples = 0, wphdr_flags, crc;
uint32_t main_bytes = main_size, corr_bytes = corr_size;
unsigned char *mcp = main_data;
unsigned char *ccp = corr_data;
int msi = 0, csi = 0;
raw_wv = malloc (sizeof (WavpackRawContext));
memset (raw_wv, 0, sizeof (WavpackRawContext));
if (corr_data && corr_size) {
raw_wvc = malloc (sizeof (WavpackRawContext));
memset (raw_wvc, 0, sizeof (WavpackRawContext));
}
while (main_bytes >= 12) {
if (!msi) {
block_samples = *mcp++;
block_samples += *mcp++ << 8;
block_samples += *mcp++ << 16;
block_samples += *mcp++ << 24;
main_bytes -= 4;
}
wphdr_flags = *mcp++;
wphdr_flags += *mcp++ << 8;
wphdr_flags += *mcp++ << 16;
wphdr_flags += *mcp++ << 24;
main_bytes -= 4;
// if the first block does not have the FINAL_BLOCK flag set,
// then there are multiple blocks
if (!msi && !(wphdr_flags & FINAL_BLOCK))
multiple_blocks = 1;
crc = *mcp++;
crc += *mcp++ << 8;
crc += *mcp++ << 16;
crc += *mcp++ << 24;
main_bytes -= 4;
if (multiple_blocks) {
block_size = *mcp++;
block_size += *mcp++ << 8;
block_size += *mcp++ << 16;
block_size += *mcp++ << 24;
main_bytes -= 4;
}
else
block_size = main_bytes;
if (block_size > main_bytes) {
if (error) strcpy (error, "main block overran available data!");
raw_close_stream (raw_wv);
raw_close_stream (raw_wvc);
return NULL;
}
else {
WavpackHeader *wphdr = malloc (sizeof (WavpackHeader));
memset (wphdr, 0, sizeof (WavpackHeader));
memcpy (wphdr->ckID, "wvpk", 4);
wphdr->ckSize = sizeof (WavpackHeader) - 8 + block_size;
SET_TOTAL_SAMPLES (*wphdr, block_samples);
wphdr->block_samples = block_samples;
wphdr->version = version;
wphdr->flags = wphdr_flags;
wphdr->crc = crc;
WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat);
raw_wv->num_segments += 2;
raw_wv->segments = realloc (raw_wv->segments, sizeof (RawSegment) * raw_wv->num_segments);
raw_wv->segments [msi].dptr = raw_wv->segments [msi].sptr = (unsigned char *) wphdr;
raw_wv->segments [msi].eptr = raw_wv->segments [msi].dptr + sizeof (WavpackHeader);
raw_wv->segments [msi++].free_required = 1;
raw_wv->segments [msi].dptr = raw_wv->segments [msi].sptr = mcp;
raw_wv->segments [msi].eptr = raw_wv->segments [msi].dptr + block_size;
raw_wv->segments [msi++].free_required = 0;
main_bytes -= block_size;
mcp += block_size;
}
if (corr_data && corr_bytes >= 4) {
crc = *ccp++;
crc += *ccp++ << 8;
crc += *ccp++ << 16;
crc += *ccp++ << 24;
corr_bytes -= 4;
if (multiple_blocks) {
block_size = *ccp++;
block_size += *ccp++ << 8;
block_size += *ccp++ << 16;
block_size += *ccp++ << 24;
corr_bytes -= 4;
}
else
block_size = corr_bytes;
if (block_size > corr_bytes) {
if (error) strcpy (error, "correction block overran available data!");
raw_close_stream (raw_wv);
raw_close_stream (raw_wvc);
return NULL;
}
else {
WavpackHeader *wphdr = malloc (sizeof (WavpackHeader));
memset (wphdr, 0, sizeof (WavpackHeader));
memcpy (wphdr->ckID, "wvpk", 4);
wphdr->ckSize = sizeof (WavpackHeader) - 8 + block_size;
SET_TOTAL_SAMPLES (*wphdr, block_samples);
wphdr->block_samples = block_samples;
wphdr->version = version;
wphdr->flags = wphdr_flags;
wphdr->crc = crc;
WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat);
raw_wvc->num_segments += 2;
raw_wvc->segments = realloc (raw_wvc->segments, sizeof (RawSegment) * raw_wvc->num_segments);
raw_wvc->segments [csi].dptr = raw_wvc->segments [csi].sptr = (unsigned char *) wphdr;
raw_wvc->segments [csi].eptr = raw_wvc->segments [csi].dptr + sizeof (WavpackHeader);
raw_wvc->segments [csi++].free_required = 1;
raw_wvc->segments [csi].dptr = raw_wvc->segments [csi].sptr = ccp;
raw_wvc->segments [csi].eptr = raw_wvc->segments [csi].dptr + block_size;
raw_wvc->segments [csi++].free_required = 0;
corr_bytes -= block_size;
ccp += block_size;
}
}
}
if (main_bytes || (corr_data && corr_bytes)) {
if (error) strcpy (error, "leftover multiblock data!");
raw_close_stream (raw_wv);
raw_close_stream (raw_wvc);
return NULL;
}
}
else { // the case of WavPack blocks with headers is much easier...
if (main_data) {
raw_wv = malloc (sizeof (WavpackRawContext));
memset (raw_wv, 0, sizeof (WavpackRawContext));
raw_wv->num_segments = 1;
raw_wv->segments = malloc (sizeof (RawSegment) * raw_wv->num_segments);
raw_wv->segments [0].dptr = raw_wv->segments [0].sptr = main_data;
raw_wv->segments [0].eptr = raw_wv->segments [0].dptr + main_size;
raw_wv->segments [0].free_required = 0;
}
if (corr_data && corr_size) {
raw_wvc = malloc (sizeof (WavpackRawContext));
memset (raw_wvc, 0, sizeof (WavpackRawContext));
raw_wvc->num_segments = 1;
raw_wvc->segments = malloc (sizeof (RawSegment) * raw_wvc->num_segments);
raw_wvc->segments [0].dptr = raw_wvc->segments [0].sptr = corr_data;
raw_wvc->segments [0].eptr = raw_wvc->segments [0].dptr + corr_size;
raw_wvc->segments [0].free_required = 0;
}
}
return WavpackOpenFileInputEx64 (&raw_reader, raw_wv, raw_wvc, error, flags | OPEN_STREAMING | OPEN_NO_CHECKSUM, norm_offset);
}
// Return the number of samples represented by the current (and in the raw case, only) frame.
uint32_t WavpackGetNumSamplesInFrame (WavpackContext *wpc)
{
if (wpc && wpc->streams && wpc->streams [0])
return wpc->streams [0]->wphdr.block_samples;
else
return -1;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,191 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// pack_dns.c
// This module handles the implementation of "dynamic noise shaping" which is
// designed to move the spectrum of the quantization noise introduced by lossy
// compression up or down in frequency so that it is more likely to be masked
// by the source material.
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
static void best_floating_line (short *values, int num_values, double *initial_y, double *final_y, short *max_error);
void dynamic_noise_shaping (WavpackContext *wpc, int32_t *buffer, int shortening_allowed)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int32_t sample_count = wps->wphdr.block_samples;
struct decorr_pass *ap = &wps->analysis_pass;
uint32_t flags = wps->wphdr.flags;
int32_t *bptr, temp, sam;
short *swptr;
int sc;
if (!wps->num_terms && sample_count > 8) {
if (flags & MONO_DATA)
for (bptr = buffer + sample_count - 3, sc = sample_count - 2; sc--;) {
sam = (3 * bptr [1] - bptr [2]) >> 1;
temp = *bptr-- - apply_weight (ap->weight_A, sam);
update_weight (ap->weight_A, 2, sam, temp);
}
else
for (bptr = buffer + (sample_count - 3) * 2 + 1, sc = sample_count - 2; sc--;) {
sam = (3 * bptr [2] - bptr [4]) >> 1;
temp = *bptr-- - apply_weight (ap->weight_B, sam);
update_weight (ap->weight_B, 2, sam, temp);
sam = (3 * bptr [2] - bptr [4]) >> 1;
temp = *bptr-- - apply_weight (ap->weight_A, sam);
update_weight (ap->weight_A, 2, sam, temp);
}
}
if (sample_count > wps->dc.shaping_samples) {
sc = sample_count - wps->dc.shaping_samples;
swptr = wps->dc.shaping_data + wps->dc.shaping_samples;
bptr = buffer + wps->dc.shaping_samples * ((flags & MONO_DATA) ? 1 : 2);
if (flags & MONO_DATA)
while (sc--) {
sam = (3 * ap->samples_A [0] - ap->samples_A [1]) >> 1;
temp = *bptr - apply_weight (ap->weight_A, sam);
update_weight (ap->weight_A, 2, sam, temp);
ap->samples_A [1] = ap->samples_A [0];
ap->samples_A [0] = *bptr++;
*swptr++ = (ap->weight_A < 256) ? 1024 : 1536 - ap->weight_A * 2;
}
else
while (sc--) {
sam = (3 * ap->samples_A [0] - ap->samples_A [1]) >> 1;
temp = *bptr - apply_weight (ap->weight_A, sam);
update_weight (ap->weight_A, 2, sam, temp);
ap->samples_A [1] = ap->samples_A [0];
ap->samples_A [0] = *bptr++;
sam = (3 * ap->samples_B [0] - ap->samples_B [1]) >> 1;
temp = *bptr - apply_weight (ap->weight_B, sam);
update_weight (ap->weight_B, 2, sam, temp);
ap->samples_B [1] = ap->samples_B [0];
ap->samples_B [0] = *bptr++;
*swptr++ = (ap->weight_A + ap->weight_B < 512) ? 1024 : 1536 - ap->weight_A - ap->weight_B;
}
wps->dc.shaping_samples = sample_count;
}
if (wpc->wvc_flag) {
int max_allowed_error = 1000000 / wpc->ave_block_samples;
short max_error, trial_max_error;
double initial_y, final_y;
if (max_allowed_error < 128)
max_allowed_error = 128;
best_floating_line (wps->dc.shaping_data, sample_count, &initial_y, &final_y, &max_error);
if (shortening_allowed && max_error > max_allowed_error) {
int min_samples = 0, max_samples = sample_count, trial_count;
double trial_initial_y, trial_final_y;
while (1) {
trial_count = (min_samples + max_samples) / 2;
best_floating_line (wps->dc.shaping_data, trial_count, &trial_initial_y,
&trial_final_y, &trial_max_error);
if (trial_max_error < max_allowed_error) {
max_error = trial_max_error;
min_samples = trial_count;
initial_y = trial_initial_y;
final_y = trial_final_y;
}
else
max_samples = trial_count;
if (min_samples > 10000 || max_samples - min_samples < 2)
break;
}
sample_count = min_samples;
}
if (initial_y < -512) initial_y = -512;
else if (initial_y > 1024) initial_y = 1024;
if (final_y < -512) final_y = -512;
else if (final_y > 1024) final_y = 1024;
#if 0
error_line ("%.2f sec, sample count = %5d, max error = %3d, range = %5d, %5d, actual = %5d, %5d",
(double) wps->sample_index / wpc->config.sample_rate, sample_count, max_error,
(int) floor (initial_y), (int) floor (final_y),
wps->dc.shaping_data [0], wps->dc.shaping_data [sample_count-1]);
#endif
if (sample_count != wps->wphdr.block_samples)
wps->wphdr.block_samples = sample_count;
if (wpc->wvc_flag) {
wps->dc.shaping_acc [0] = wps->dc.shaping_acc [1] = (int32_t) floor (initial_y * 65536.0 + 0.5);
wps->dc.shaping_delta [0] = wps->dc.shaping_delta [1] =
(int32_t) floor ((final_y - initial_y) / (sample_count - 1) * 65536.0 + 0.5);
wps->dc.shaping_array = NULL;
}
else
wps->dc.shaping_array = wps->dc.shaping_data;
}
else
wps->dc.shaping_array = wps->dc.shaping_data;
}
// Given an array of integer data (in shorts), find the linear function that most closely
// represents it (based on minimum sum of absolute errors). This is returned as the double
// precision initial & final Y values of the best-fit line. The function can also optionally
// compute and return a maximum error value (as a short). Note that the ends of the resulting
// line may fall way outside the range of input values, so some sort of clipping may be
// needed.
static void best_floating_line (short *values, int num_values, double *initial_y, double *final_y, short *max_error)
{
double left_sum = 0.0, right_sum = 0.0, center_x = (num_values - 1) / 2.0, center_y, m;
int i;
for (i = 0; i < num_values >> 1; ++i) {
right_sum += values [num_values - i - 1];
left_sum += values [i];
}
if (num_values & 1) {
right_sum += values [num_values >> 1] * 0.5;
left_sum += values [num_values >> 1] * 0.5;
}
center_y = (right_sum + left_sum) / num_values;
m = (right_sum - left_sum) / ((double) num_values * num_values) * 4.0;
if (initial_y)
*initial_y = center_y - m * center_x;
if (final_y)
*final_y = center_y + m * center_x;
if (max_error) {
double max = 0.0;
for (i = 0; i < num_values; ++i)
if (fabs (values [i] - (center_y + (i - center_x) * m)) > max)
max = fabs (values [i] - (center_y + (i - center_x) * m));
*max_error = (short) floor (max + 0.5);
}
}

View file

@ -1,668 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** DSDPACK **** //
// Lossless DSD (Direct Stream Digital) Audio Compressor //
// Copyright (c) 2013 - 2016 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// pack_dsd.c
// This module actually handles the compression of the DSD audio data.
#ifdef ENABLE_DSD
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
// This function initializes everything required to pack WavPack DSD bitstreams
// and must be called BEFORE any other function in this module.
void pack_dsd_init (WavpackContext *wpc)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
wps->sample_index = 0;
}
// Pack an entire block of samples (either mono or stereo) into a completed
// WavPack block. This function is actually a shell for pack_samples() and
// performs tasks like handling any shift required by the format, preprocessing
// of floating point data or integer data over 24 bits wide, and implementing
// the "extra" mode (via the extra?.c modules). It is assumed that there is
// sufficient space for the completed block at "wps->blockbuff" and that
// "wps->blockend" points to the end of the available space. A return value of
// FALSE indicates an error.
// Pack an entire block of samples (either mono or stereo) into a completed
// WavPack block. It is assumed that there is sufficient space for the
// completed block at "wps->blockbuff" and that "wps->blockend" points to the
// end of the available space. A return value of FALSE indicates an error.
// Any unsent metadata is transmitted first, then required metadata for this
// block is sent, and finally the compressed integer data is sent. If a "wpx"
// stream is required for floating point data or large integer data, then this
// must be handled outside this function. To find out how much data was written
// the caller must look at the ckSize field of the written WavpackHeader, NOT
// the one in the WavpackStream.
static int encode_buffer_high (WavpackStream *wps, int32_t *buffer, int num_samples, unsigned char *destination);
static int encode_buffer_fast (WavpackStream *wps, int32_t *buffer, int num_samples, unsigned char *destination);
int pack_dsd_block (WavpackContext *wpc, int32_t *buffer)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
uint32_t flags = wps->wphdr.flags, mult = wpc->dsd_multiplier, data_count;
uint32_t sample_count = wps->wphdr.block_samples;
unsigned char *dsd_encoding, dsd_power = 0;
int32_t res;
// This code scans stereo data to check whether it can be stored as mono data
// (i.e., all L/R samples identical).
if (!(flags & MONO_FLAG)) {
int32_t *sptr, *dptr, i;
for (sptr = buffer, i = 0; i < (int32_t) sample_count; sptr += 2, i++)
if ((sptr [0] ^ sptr [1]) & 0xff)
break;
if (i == sample_count) {
wps->wphdr.flags = flags |= FALSE_STEREO;
dptr = buffer;
sptr = buffer;
for (i = sample_count; i--; sptr++)
*dptr++ = *sptr++;
}
else
wps->wphdr.flags = flags &= ~FALSE_STEREO;
}
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
if (wpc->metacount) {
WavpackMetadata *wpmdp = wpc->metadata;
while (wpc->metacount) {
copy_metadata (wpmdp, wps->blockbuff, wps->blockend);
wpc->metabytes -= wpmdp->byte_length;
free_metadata (wpmdp++);
wpc->metacount--;
}
free (wpc->metadata);
wpc->metadata = NULL;
}
if (!sample_count)
return TRUE;
send_general_metadata (wpc);
memcpy (&wps->wphdr, wps->blockbuff, sizeof (WavpackHeader));
dsd_encoding = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 12;
while (mult >>= 1)
dsd_power++;
*dsd_encoding++ = dsd_power;
if (wpc->config.flags & CONFIG_HIGH_FLAG) {
int fast_res = encode_buffer_fast (wps, buffer, sample_count, dsd_encoding);
res = encode_buffer_high (wps, buffer, sample_count, dsd_encoding);
if ((fast_res != -1) && (res == -1 || res > fast_res))
res = encode_buffer_fast (wps, buffer, sample_count, dsd_encoding);
}
else
res = encode_buffer_fast (wps, buffer, sample_count, dsd_encoding);
if (res == -1) {
int num_samples = sample_count * ((flags & MONO_DATA) ? 1 : 2);
uint32_t crc = 0xffffffff;
*dsd_encoding++ = 0;
data_count = num_samples + 2;
while (num_samples--)
crc += (crc << 1) + (*dsd_encoding++ = *buffer++);
((WavpackHeader *) wps->blockbuff)->crc = crc;
}
else
data_count = res + 1;
if (data_count) {
unsigned char *cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8;
if (data_count & 1) {
cptr [data_count + 4] = 0;
*cptr++ = ID_DSD_BLOCK | ID_LARGE | ID_ODD_SIZE;
data_count++;
}
else
*cptr++ = ID_DSD_BLOCK | ID_LARGE;
*cptr++ = data_count >> 1;
*cptr++ = data_count >> 9;
*cptr++ = data_count >> 17;
((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4;
}
wps->sample_index += sample_count;
return TRUE;
}
/*------------------------------------------------------------------------------------------------------------------------*/
// #define DSD_BYTE_READY(low,high) (((low) >> 24) == ((high) >> 24))
// #define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) >> 24))
#define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) & 0xff000000))
#define MAX_PROBABILITY 0xa0 // set to 0xff to disable RLE encoding for probabilities table
#if (MAX_PROBABILITY < 0xff)
static int rle_encode (unsigned char *src, int bcount, unsigned char *destination)
{
int max_rle_zeros = 0xff - MAX_PROBABILITY;
unsigned char *dp = destination;
int zcount = 0;
while (bcount--) {
if (*src) {
while (zcount) {
*dp++ = MAX_PROBABILITY + (zcount > max_rle_zeros ? max_rle_zeros : zcount);
zcount -= (zcount > max_rle_zeros ? max_rle_zeros : zcount);
}
*dp++ = *src++;
}
else {
zcount++;
src++;
}
}
while (zcount) {
*dp++ = MAX_PROBABILITY + (zcount > max_rle_zeros ? max_rle_zeros : zcount);
zcount -= (zcount > max_rle_zeros ? max_rle_zeros : zcount);
}
*dp++ = 0;
return (int)(dp - destination);
}
#endif
static void calculate_probabilities (int hist [256], unsigned char probs [256], unsigned short prob_sums [256])
{
int divisor, min_value, max_value, sum_values;
int min_hits = 0x7fffffff, max_hits = 0, i;
for (i = 0; i < 256; ++i) {
if (hist [i] < min_hits) min_hits = hist [i];
if (hist [i] > max_hits) max_hits = hist [i];
}
if (max_hits == 0) {
memset (probs, 0, sizeof (*probs) * 256);
memset (prob_sums, 0, sizeof (*prob_sums) * 256);
return;
}
// fprintf (stderr, "process_histogram(): hits = %d to %d\n", min_hits, max_hits);
if (max_hits > MAX_PROBABILITY)
divisor = ((max_hits << 8) + (MAX_PROBABILITY >> 1)) / MAX_PROBABILITY;
else
divisor = 0;
while (1) {
min_value = 0x7fffffff; max_value = 0; sum_values = 0;
for (i = 0; i < 256; ++i) {
int value;
if (hist [i]) {
if (divisor) {
if (!(value = ((hist [i] << 8) + (divisor >> 1)) / divisor))
value = 1;
}
else
value = hist [i];
if (value < min_value) min_value = value;
if (value > max_value) max_value = value;
}
else
value = 0;
prob_sums [i] = sum_values += value;
probs [i] = value;
}
if (max_value > MAX_PROBABILITY) {
divisor++;
continue;
}
#if 0 // this code reduces probability values when they are completely redundant (i.e., common divisor), but
// this doesn't really happen often enough to make it worthwhile
if (min_value > 1) {
for (i = 0; i < 256; ++i)
if (probs [i] % min_value)
break;
if (i == 256) {
for (i = 0; i < 256; ++i) {
prob_sums [i] /= min_value;
probs [i] /= min_value;
}
// fprintf (stderr, "fixed min_value = %d, divisor = %d, probs_sum = %d\n", min_value, divisor, prob_sums [255]);
}
}
#endif
break;
}
}
static int encode_buffer_fast (WavpackStream *wps, int32_t *buffer, int num_samples, unsigned char *destination)
{
uint32_t flags = wps->wphdr.flags, crc = 0xffffffff;
unsigned int low = 0, high = 0xffffffff, mult;
unsigned short (*summed_probabilities) [256];
unsigned char (*probabilities) [256];
unsigned char *dp = destination, *ep;
int history_bins, bc, p0 = 0, p1 = 0;
int total_summed_probabilities = 0;
int (*histogram) [256];
int32_t *bp = buffer;
char history_bits;
if (!(flags & MONO_DATA))
num_samples *= 2;
if (num_samples < 280)
return -1;
else if (num_samples < 560)
history_bits = 0;
else if (num_samples < 1725)
history_bits = 1;
else if (num_samples < 5000)
history_bits = 2;
else if (num_samples < 14000)
history_bits = 3;
else if (num_samples < 28000)
history_bits = 4;
else if (num_samples < 76000)
history_bits = 5;
else if (num_samples < 130000)
history_bits = 6;
else if (num_samples < 300000)
history_bits = 7;
else
history_bits = 8;
if (history_bits > MAX_HISTORY_BITS)
history_bits = MAX_HISTORY_BITS;
history_bins = 1 << history_bits;
histogram = malloc (sizeof (*histogram) * history_bins);
memset (histogram, 0, sizeof (*histogram) * history_bins);
probabilities = malloc (sizeof (*probabilities) * history_bins);
summed_probabilities = malloc (sizeof (*summed_probabilities) * history_bins);
bc = num_samples;
if (flags & MONO_DATA)
while (bc--) {
crc += (crc << 1) + (*bp & 0xff);
histogram [p0] [*bp & 0xff]++;
p0 = *bp++ & (history_bins-1);
}
else
while (bc--) {
crc += (crc << 1) + (*bp & 0xff);
histogram [p0] [*bp & 0xff]++;
p0 = p1;
p1 = *bp++ & (history_bins-1);
}
for (p0 = 0; p0 < history_bins; p0++) {
calculate_probabilities (histogram [p0], probabilities [p0], summed_probabilities [p0]);
total_summed_probabilities += summed_probabilities [p0] [255];
}
((WavpackHeader *) wps->blockbuff)->crc = crc;
// This code detects the case where the required value lookup tables grow silly big and cuts them back down. This would
// normally only happen with large blocks or poorly compressible data. The target is to guarantee that the total memory
// required for all three decode tables will be 2K bytes per history bin.
while (total_summed_probabilities > history_bins * MAX_BYTES_PER_BIN) {
int max_sum = 0, sum_values = 0, largest_bin = 0;
for (p0 = 0; p0 < history_bins; ++p0)
if (summed_probabilities [p0] [255] > max_sum) {
max_sum = summed_probabilities [p0] [255];
largest_bin = p0;
}
total_summed_probabilities -= max_sum;
p0 = largest_bin;
for (p1 = 0; p1 < 256; ++p1)
summed_probabilities [p0] [p1] = sum_values += probabilities [p0] [p1] = (probabilities [p0] [p1] + 1) >> 1;
total_summed_probabilities += summed_probabilities [p0] [255];
// fprintf (stderr, "processed bin 0x%02x, bin: %d --> %d, new sum = %d\n",
// p0, max_sum, summed_probabilities [p0] [255], total_summed_probabilities);
}
free (histogram);
bp = buffer;
bc = num_samples;
*dp++ = 1;
*dp++ = history_bits;
*dp++ = MAX_PROBABILITY;
ep = destination + num_samples - 10;
#if (MAX_PROBABILITY < 0xff)
dp += rle_encode ((unsigned char *) probabilities, sizeof (*probabilities) * history_bins, dp);
#else
memcpy (dp, probabilities, sizeof (*probabilities) * history_bins);
dp += sizeof (*probabilities) * history_bins;
#endif
p0 = p1 = 0;
while (dp < ep && bc--) {
mult = (high - low) / summed_probabilities [p0] [255];
if (!mult) {
high = low;
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
mult = (high - low) / summed_probabilities [p0] [255];
}
if (*bp & 0xff)
low += summed_probabilities [p0] [(*bp & 0xff)-1] * mult;
high = low + probabilities [p0] [*bp & 0xff] * mult - 1;
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
if (flags & MONO_DATA)
p0 = *bp++ & (history_bins-1);
else {
p0 = p1;
p1 = *bp++ & (history_bins-1);
}
}
high = low;
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
free (summed_probabilities);
free (probabilities);
if (dp < ep)
return (int)(dp - destination);
else
return -1;
}
/*------------------------------------------------------------------------------------------------------------------------*/
#define PTABLE_BITS 8
#define PTABLE_BINS (1<<PTABLE_BITS)
#define PTABLE_MASK (PTABLE_BINS-1)
#define INITIAL_TERM (1536/PTABLE_BINS)
#define UP 0x010000fe
#define DOWN 0x00010000
#define DECAY 8
#define PRECISION 20
#define VALUE_ONE (1 << PRECISION)
#define PRECISION_USE 12
#define RATE_S 20
static void init_ptable (int *table, int rate_i, int rate_s)
{
int value = 0x808000, rate = rate_i << 8, c, i;
for (c = (rate + 128) >> 8; c--;)
value += (DOWN - value) >> DECAY;
for (i = 0; i < PTABLE_BINS/2; ++i) {
table [i] = value;
table [PTABLE_BINS-1-i] = 0x100ffff - value;
if (value > 0x010000) {
rate += (rate * rate_s + 128) >> 8;
for (c = (rate + 64) >> 7; c--;)
value += (DOWN - value) >> DECAY;
}
}
}
static int normalize_ptable (int *ptable)
{
int rate = 0, min_error, error_sum, i;
int ntable [PTABLE_BINS];
init_ptable (ntable, rate, RATE_S);
for (min_error = i = 0; i < PTABLE_BINS; ++i)
min_error += abs (ptable [i] - ntable [i]) >> 8;
while (1) {
init_ptable (ntable, ++rate, RATE_S);
for (error_sum = i = 0; i < PTABLE_BINS; ++i)
error_sum += abs (ptable [i] - ntable [i]) >> 8;
if (error_sum < min_error)
min_error = error_sum;
else
break;
}
return rate - 1;
}
static int encode_buffer_high (WavpackStream *wps, int32_t *buffer, int num_samples, unsigned char *destination)
{
int channel, stereo = (wps->wphdr.flags & MONO_DATA) ? 0 : 1;
uint32_t crc = 0xffffffff, high = 0xffffffff, low = 0;
unsigned char *dp = destination, *ep;
DSDfilters *sp;
if (num_samples * (stereo + 1) < 280)
return -1;
*dp++ = 3;
ep = destination + num_samples * (stereo + 1) - 10;
if (!wps->sample_index) {
if (!wps->dsd.ptable)
wps->dsd.ptable = malloc (PTABLE_BINS * sizeof (*wps->dsd.ptable));
init_ptable (wps->dsd.ptable, INITIAL_TERM, RATE_S);
for (channel = 0; channel < 2; ++channel) {
sp = wps->dsd.filters + channel;
sp->filter1 = sp->filter2 = sp->filter3 = sp->filter4 = sp->filter5 = VALUE_ONE / 2;
sp->filter6 = sp->factor = 0;
}
*dp++ = INITIAL_TERM;
*dp++ = RATE_S;
}
else {
int rate = normalize_ptable (wps->dsd.ptable);
init_ptable (wps->dsd.ptable, rate, RATE_S);
*dp++ = rate;
*dp++ = RATE_S;
}
for (channel = 0; channel <= stereo; ++channel) {
sp = wps->dsd.filters + channel;
*dp = sp->filter1 >> (PRECISION - 8);
sp->filter1 = *dp++ << (PRECISION - 8);
*dp = sp->filter2 >> (PRECISION - 8);
sp->filter2 = *dp++ << (PRECISION - 8);
*dp = sp->filter3 >> (PRECISION - 8);
sp->filter3 = *dp++ << (PRECISION - 8);
*dp = sp->filter4 >> (PRECISION - 8);
sp->filter4 = *dp++ << (PRECISION - 8);
*dp = sp->filter5 >> (PRECISION - 8);
sp->filter5 = *dp++ << (PRECISION - 8);
*dp++ = sp->factor;
*dp++ = sp->factor >> 8;
sp->filter6 = 0;
sp->factor = (sp->factor << 16) >> 16;
}
sp = wps->dsd.filters;
while (dp < ep && num_samples--) {
int bitcount = 8;
crc += (crc << 1) + (sp->byte = *buffer++ & 0xff);
sp [0].value = sp [0].filter1 - sp [0].filter5 + ((sp [0].filter6 * sp [0].factor) >> 2);
if (stereo) {
crc += (crc << 1) + (sp [1].byte = *buffer++ & 0xff);
sp [1].value = sp [1].filter1 - sp [1].filter5 + ((sp [1].filter6 * sp [1].factor) >> 2);
}
while (bitcount--) {
int32_t *pp = wps->dsd.ptable + ((sp [0].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK);
if (sp [0].byte & 0x80) {
high = low + ((high - low) >> 8) * (*pp >> 16);
*pp += (UP - *pp) >> DECAY;
sp [0].filter0 = -1;
}
else {
low += 1 + ((high - low) >> 8) * (*pp >> 16);
*pp += (DOWN - *pp) >> DECAY;
sp [0].filter0 = 0;
}
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
sp [0].value += sp [0].filter6 << 3;
sp [0].factor += (((sp [0].value ^ sp [0].filter0) >> 31) | 1) & ((sp [0].value ^ (sp [0].value - (sp [0].filter6 << 4))) >> 31);
sp [0].filter1 += ((sp [0].filter0 & VALUE_ONE) - sp [0].filter1) >> 6;
sp [0].filter2 += ((sp [0].filter0 & VALUE_ONE) - sp [0].filter2) >> 4;
sp [0].filter3 += (sp [0].filter2 - sp [0].filter3) >> 4;
sp [0].filter4 += (sp [0].filter3 - sp [0].filter4) >> 4;
sp [0].value = (sp [0].filter4 - sp [0].filter5) >> 4;
sp [0].filter5 += sp [0].value;
sp [0].filter6 += (sp [0].value - sp [0].filter6) >> 3;
sp [0].value = sp [0].filter1 - sp [0].filter5 + ((sp [0].filter6 * sp [0].factor) >> 2);
sp [0].byte <<= 1;
if (!stereo)
continue;
pp = wps->dsd.ptable + ((sp [1].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK);
if (sp [1].byte & 0x80) {
high = low + ((high - low) >> 8) * (*pp >> 16);
*pp += (UP - *pp) >> DECAY;
sp [1].filter0 = -1;
}
else {
low += 1 + ((high - low) >> 8) * (*pp >> 16);
*pp += (DOWN - *pp) >> DECAY;
sp [1].filter0 = 0;
}
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
sp [1].value += sp [1].filter6 << 3;
sp [1].factor += (((sp [1].value ^ sp [1].filter0) >> 31) | 1) & ((sp [1].value ^ (sp [1].value - (sp [1].filter6 << 4))) >> 31);
sp [1].filter1 += ((sp [1].filter0 & VALUE_ONE) - sp [1].filter1) >> 6;
sp [1].filter2 += ((sp [1].filter0 & VALUE_ONE) - sp [1].filter2) >> 4;
sp [1].filter3 += (sp [1].filter2 - sp [1].filter3) >> 4;
sp [1].filter4 += (sp [1].filter3 - sp [1].filter4) >> 4;
sp [1].value = (sp [1].filter4 - sp [1].filter5) >> 4;
sp [1].filter5 += sp [1].value;
sp [1].filter6 += (sp [1].value - sp [1].filter6) >> 3;
sp [1].value = sp [1].filter1 - sp [1].filter5 + ((sp [1].filter6 * sp [1].factor) >> 2);
sp [1].byte <<= 1;
}
sp [0].factor -= (sp->factor + 512) >> 10;
if (stereo)
sp [1].factor -= (sp [1].factor + 512) >> 10;
}
((WavpackHeader *) wps->blockbuff)->crc = crc;
high = low;
while (DSD_BYTE_READY (high, low)) {
*dp++ = high >> 24;
high = (high << 8) | 0xff;
low <<= 8;
}
if (dp < ep)
return (int)(dp - destination);
else
return -1;
}
#endif // ENABLE_DSD

View file

@ -1,270 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// pack_floats.c
// This module deals with the compression of floating-point data. Note that no
// floating point math is involved here...the values are only processed with
// the macros that directly access the mantissa, exponent, and sign fields.
// That's why we use the f32 type instead of the built-in float type.
#include <stdlib.h>
#include "wavpack_local.h"
//#define DISPLAY_DIAGNOSTICS
// Scan the provided buffer of floating-point values and (1) convert the
// significant portion of the data to integers for compression using the
// regular WavPack algorithms (which only operate on integers) and (2)
// determine whether the data requires a second stream for lossless
// storage (which will usually be the case except when the floating-point
// data was originally integer data). The converted integers are returned
// "in-place" and a return value of TRUE indicates that a second stream
// is required.
int scan_float_data (WavpackStream *wps, f32 *values, int32_t num_values)
{
int32_t shifted_ones = 0, shifted_zeros = 0, shifted_both = 0;
int32_t false_zeros = 0, neg_zeros = 0;
#ifdef DISPLAY_DIAGNOSTICS
int32_t true_zeros = 0, denormals = 0, exceptions = 0;
#endif
uint32_t ordata = 0, crc = 0xffffffff;
int32_t count, value, shift_count;
int max_mag = 0, max_exp = 0;
f32 *dp;
wps->float_shift = wps->float_flags = 0;
// First loop goes through all the data and (1) calculates the CRC and (2) finds the
// max magnitude that does not have an exponent of 255 (reserved for +/-inf and NaN).
for (dp = values, count = num_values; count--; dp++) {
crc = crc * 27 + get_mantissa (*dp) * 9 + get_exponent (*dp) * 3 + get_sign (*dp);
if (get_exponent (*dp) < 255 && get_magnitude (*dp) > max_mag)
max_mag = get_magnitude (*dp);
}
wps->crc_x = crc;
// round up the magnitude so that when we convert the floating-point values to integers,
// they will be (at most) just over 24-bits signed precision
if (get_exponent (max_mag))
max_exp = get_exponent (max_mag + 0x7F0000);
for (dp = values, count = num_values; count--; dp++) {
// Exponent of 255 is reserved for +/-inf (mantissa = 0) or NaN (mantissa != 0).
// we use a value one greater than 24-bits unsigned for this.
if (get_exponent (*dp) == 255) {
#ifdef DISPLAY_DIAGNOSTICS
exceptions++;
#endif
wps->float_flags |= FLOAT_EXCEPTIONS;
value = 0x1000000;
shift_count = 0;
}
// This is the regular case. We generate a 24-bit unsigned value with the implied
// '1' MSB set and calculate a shift that will make it line up with the biggest
// samples in this block (although that shift would obviously shift out real data).
else if (get_exponent (*dp)) {
shift_count = max_exp - get_exponent (*dp);
value = 0x800000 + get_mantissa (*dp);
}
// Zero exponent means either +/- zero (mantissa = 0) or denormals (mantissa != 0).
// shift_count is set so that denormals (without an implied '1') will line up with
// regular values (with their implied '1' added at bit 23). Trust me. We don't care
// about the shift with zero.
else {
shift_count = max_exp ? max_exp - 1 : 0;
value = get_mantissa (*dp);
#ifdef DISPLAY_DIAGNOSTICS
if (get_mantissa (*dp))
denormals++;
#endif
}
if (shift_count < 25)
value >>= shift_count; // perform the shift if there could be anything left
else
value = 0; // else just zero the value
// If we are going to encode an integer zero, then this might be a "false zero" which
// means that there are significant bits but they're completely shifted out, or a
// "negative zero" which is simply a floating point value that we have to encode
// (and converting it to a positive zero would be an error).
if (!value) {
if (get_exponent (*dp) || get_mantissa (*dp))
++false_zeros;
else if (get_sign (*dp))
++neg_zeros;
#ifdef DISPLAY_DIAGNOSTICS
else
++true_zeros;
#endif
}
// If we are going to shift something (but not everything) out of our integer before
// encoding, then we generate a mask corresponding to the bits that will be shifted
// out and increment the counter for the 3 possible cases of (1) all zeros, (2) all
// ones, and (3) a mix of ones and zeros.
else if (shift_count) {
int32_t mask = (1 << shift_count) - 1;
if (!(get_mantissa (*dp) & mask))
shifted_zeros++;
else if ((get_mantissa (*dp) & mask) == mask)
shifted_ones++;
else
shifted_both++;
}
// "or" all the integer values together, and store the final integer with applied sign
ordata |= value;
* (int32_t *) dp = (get_sign (*dp)) ? -value : value;
}
wps->float_max_exp = max_exp; // on decode, we use this to calculate actual exponent
// Now, based on our various counts, we determine the scheme required to encode the bits
// shifted out. Usually these will simply have to be sent literally, but in some rare cases
// we can get away with always assuming ones shifted out, or assuming all the bits shifted
// out in each value are the same (which means we only have to send a single bit).
if (shifted_both)
wps->float_flags |= FLOAT_SHIFT_SENT;
else if (shifted_ones && !shifted_zeros)
wps->float_flags |= FLOAT_SHIFT_ONES;
else if (shifted_ones && shifted_zeros)
wps->float_flags |= FLOAT_SHIFT_SAME;
// Another case is that we only shift out zeros (or maybe nothing), and in that case we
// check to see if our data actually has less than 24 or 25 bits of resolution, which means
// that we reduce can the magnitude of the integers we are encoding (which saves all those
// bits). The number of bits of reduced resolution is stored in float_shift.
else if (ordata && !(ordata & 1)) {
while (!(ordata & 1)) {
wps->float_shift++;
ordata >>= 1;
}
// here we shift out all those zeros in the integer data we will encode
for (dp = values, count = num_values; count--; dp++)
* (int32_t *) dp >>= wps->float_shift;
}
// Here we calculate the actual magnitude used by our integer data, although this is just
// used for informational purposes during encode/decode to possibly use faster math.
wps->wphdr.flags &= ~MAG_MASK;
while (ordata) {
wps->wphdr.flags += 1 << MAG_LSB;
ordata >>= 1;
}
// Finally, we have to set some flags that guide how we encode various types of "zeros".
// If none of these are set (which is the most common situation), then every integer
// zero in the decoded data will simply become a floating-point zero.
if (false_zeros || neg_zeros)
wps->float_flags |= FLOAT_ZEROS_SENT;
if (neg_zeros)
wps->float_flags |= FLOAT_NEG_ZEROS;
#ifdef DISPLAY_DIAGNOSTICS
{
int32_t *ip, min = 0x7fffffff, max = 0x80000000;
for (ip = (int32_t *) values, count = num_values; count--; ip++) {
if (*ip < min) min = *ip;
if (*ip > max) max = *ip;
}
fprintf (stderr, "integer range = %d to %d\n", min, max);
}
fprintf (stderr, "samples = %d, max exp = %d, pre-shift = %d, denormals = %d, exceptions = %d, max_mag = %x\n",
num_values, max_exp, wps->float_shift, denormals, exceptions, max_mag);
fprintf (stderr, "shifted ones/zeros/both = %d/%d/%d, true/neg/false zeros = %d/%d/%d\n",
shifted_ones, shifted_zeros, shifted_both, true_zeros, neg_zeros, false_zeros);
#endif
return wps->float_flags & (FLOAT_EXCEPTIONS | FLOAT_ZEROS_SENT | FLOAT_SHIFT_SENT | FLOAT_SHIFT_SAME);
}
// Given a buffer of float data, convert the data to integers (which is what the WavPack compression
// algorithms require) and write the other data required for lossless compression (which includes
// significant bits shifted out of the integers, plus information about +/- zeros and exceptions
// like NaN and +/- infinities) into the wvxbits stream (which is assumed to be opened). Note that
// for this work correctly, scan_float_data() must have been called on the original data to set
// the appropriate flags in float_flags and max_exp.
void send_float_data (WavpackStream *wps, f32 *values, int32_t num_values)
{
int max_exp = wps->float_max_exp;
int32_t count, value, shift_count;
f32 *dp;
for (dp = values, count = num_values; count--; dp++) {
if (get_exponent (*dp) == 255) {
if (get_mantissa (*dp)) {
putbit_1 (&wps->wvxbits);
putbits (get_mantissa (*dp), 23, &wps->wvxbits);
}
else {
putbit_0 (&wps->wvxbits);
}
value = 0x1000000;
shift_count = 0;
}
else if (get_exponent (*dp)) {
shift_count = max_exp - get_exponent (*dp);
value = 0x800000 + get_mantissa (*dp);
}
else {
shift_count = max_exp ? max_exp - 1 : 0;
value = get_mantissa (*dp);
}
if (shift_count < 25)
value >>= shift_count;
else
value = 0;
if (!value) {
if (wps->float_flags & FLOAT_ZEROS_SENT) {
if (get_exponent (*dp) || get_mantissa (*dp)) {
putbit_1 (&wps->wvxbits);
putbits (get_mantissa (*dp), 23, &wps->wvxbits);
if (max_exp >= 25) {
putbits (get_exponent (*dp), 8, &wps->wvxbits);
}
putbit (get_sign (*dp), &wps->wvxbits);
}
else {
putbit_0 (&wps->wvxbits);
if (wps->float_flags & FLOAT_NEG_ZEROS)
putbit (get_sign (*dp), &wps->wvxbits);
}
}
}
else if (shift_count) {
if (wps->float_flags & FLOAT_SHIFT_SENT) {
int32_t data = get_mantissa (*dp) & ((1 << shift_count) - 1);
putbits (data, shift_count, &wps->wvxbits);
}
else if (wps->float_flags & FLOAT_SHIFT_SAME) {
putbit (get_mantissa (*dp) & 1, &wps->wvxbits);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,614 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// read_words.c
// This module provides entropy word decoding functions using
// a variation on the Rice method. This was introduced in version 3.93
// because it allows splitting the data into a "lossy" stream and a
// "correction" stream in a very efficient manner and is therefore ideal
// for the "hybrid" mode. For 4.0, the efficiency of this method was
// significantly improved by moving away from the normal Rice restriction of
// using powers of two for the modulus divisions and now the method can be
// used for both hybrid and pure lossless encoding.
// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%),
// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the
// previous. Using standard Rice coding on this data would result in 1.4
// bits per sample average (not counting sign bit). However, there is a
// very simple encoding that is over 99% efficient with this data and
// results in about 1.22 bits per sample.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#if defined (HAVE___BUILTIN_CTZ) || defined (_WIN64)
#define USE_CTZ_OPTIMIZATION // use ctz intrinsic (or Windows equivalent) to count trailing ones
#else
#define USE_NEXT8_OPTIMIZATION // optimization using a table to count trailing ones
#endif
#define USE_BITMASK_TABLES // use tables instead of shifting for certain masking operations
///////////////////////////// local table storage ////////////////////////////
#ifdef USE_NEXT8_OPTIMIZATION
static const char ones_count_table [] = {
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8
};
#endif
///////////////////////////// executable code ////////////////////////////////
static uint32_t __inline read_code (Bitstream *bs, uint32_t maxcode);
// Read the next word from the bitstream "wvbits" and return the value. This
// function can be used for hybrid or lossless streams, but since an
// optimized version is available for lossless this function would normally
// be used for hybrid only. If a hybrid lossless stream is being read then
// the "correction" offset is written at the specified pointer. A return value
// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or
// some other error occurred.
int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction)
{
struct entropy_data *c = wps->w.c + chan;
uint32_t ones_count, low, mid, high;
int32_t value;
int sign;
if (!wps->wvbits.ptr)
return WORD_EOF;
if (correction)
*correction = 0;
if (!(wps->w.c [0].median [0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.c [1].median [0] & ~1)) {
uint32_t mask;
int cbits;
if (wps->w.zeros_acc) {
if (--wps->w.zeros_acc) {
c->slow_level -= (c->slow_level + SLO) >> SLS;
return 0;
}
}
else {
for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
if (cbits == 33)
return WORD_EOF;
if (cbits < 2)
wps->w.zeros_acc = cbits;
else {
for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1)
if (getbit (&wps->wvbits))
wps->w.zeros_acc |= mask;
wps->w.zeros_acc |= mask;
}
if (wps->w.zeros_acc) {
c->slow_level -= (c->slow_level + SLO) >> SLS;
CLEAR (wps->w.c [0].median);
CLEAR (wps->w.c [1].median);
return 0;
}
}
}
if (wps->w.holding_zero)
ones_count = wps->w.holding_zero = 0;
else {
#ifdef USE_CTZ_OPTIMIZATION
while (wps->wvbits.bc < LIMIT_ONES) {
if (++(wps->wvbits.ptr) == wps->wvbits.end)
wps->wvbits.wrap (&wps->wvbits);
wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc;
wps->wvbits.bc += sizeof (*(wps->wvbits.ptr)) * 8;
}
#ifdef _MSC_VER
{ unsigned long res; _BitScanForward (&res, (unsigned long)~wps->wvbits.sr); ones_count = (uint32_t) res; }
#else
ones_count = __builtin_ctz (~wps->wvbits.sr);
#endif
if (ones_count >= LIMIT_ONES) {
wps->wvbits.bc -= ones_count;
wps->wvbits.sr >>= ones_count;
for (; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count);
if (ones_count == (LIMIT_ONES + 1))
return WORD_EOF;
if (ones_count == LIMIT_ONES) {
uint32_t mask;
int cbits;
for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
if (cbits == 33)
return WORD_EOF;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (&wps->wvbits))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
}
else {
wps->wvbits.bc -= ones_count + 1;
wps->wvbits.sr >>= ones_count + 1;
}
#elif defined (USE_NEXT8_OPTIMIZATION)
int next8;
if (wps->wvbits.bc < 8) {
if (++(wps->wvbits.ptr) == wps->wvbits.end)
wps->wvbits.wrap (&wps->wvbits);
next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff;
wps->wvbits.bc += sizeof (*(wps->wvbits.ptr)) * 8;
}
else
next8 = wps->wvbits.sr & 0xff;
if (next8 == 0xff) {
wps->wvbits.bc -= 8;
wps->wvbits.sr >>= 8;
for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count);
if (ones_count == (LIMIT_ONES + 1))
return WORD_EOF;
if (ones_count == LIMIT_ONES) {
uint32_t mask;
int cbits;
for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
if (cbits == 33)
return WORD_EOF;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (&wps->wvbits))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
}
else {
wps->wvbits.bc -= (ones_count = ones_count_table [next8]) + 1;
wps->wvbits.sr >>= ones_count + 1;
}
#else
for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count);
if (ones_count >= LIMIT_ONES) {
uint32_t mask;
int cbits;
if (ones_count == (LIMIT_ONES + 1))
return WORD_EOF;
for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits);
if (cbits == 33)
return WORD_EOF;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (&wps->wvbits))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
#endif
if (wps->w.holding_one) {
wps->w.holding_one = ones_count & 1;
ones_count = (ones_count >> 1) + 1;
}
else {
wps->w.holding_one = ones_count & 1;
ones_count >>= 1;
}
wps->w.holding_zero = ~wps->w.holding_one & 1;
}
if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
update_error_limit (wps);
if (ones_count == 0) {
low = 0;
high = GET_MED (0) - 1;
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (ones_count == 1) {
high = low + GET_MED (1) - 1;
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (ones_count == 2) {
high = low + GET_MED (2) - 1;
DEC_MED2 ();
}
else {
low += (ones_count - 2) * GET_MED (2);
high = low + GET_MED (2) - 1;
INC_MED2 ();
}
}
}
low &= 0x7fffffff;
high &= 0x7fffffff;
if (low > high) // make sure high and low make sense
high = low;
mid = (high + low + 1) >> 1;
if (!c->error_limit)
mid = read_code (&wps->wvbits, high - low) + low;
else while (high - low > c->error_limit) {
if (getbit (&wps->wvbits))
mid = (high + (low = mid) + 1) >> 1;
else
mid = ((high = mid - 1) + low + 1) >> 1;
}
sign = getbit (&wps->wvbits);
if (bs_is_open (&wps->wvcbits) && c->error_limit) {
value = read_code (&wps->wvcbits, high - low) + low;
if (correction)
*correction = sign ? (mid - value) : (value - mid);
}
if (wps->wphdr.flags & HYBRID_BITRATE) {
c->slow_level -= (c->slow_level + SLO) >> SLS;
c->slow_level += wp_log2 (mid);
}
return sign ? ~mid : mid;
}
// This is an optimized version of get_word() that is used for lossless only
// (error_limit == 0). Also, rather than obtaining a single sample, it can be
// used to obtain an entire buffer of either mono or stereo samples.
int32_t get_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples)
{
struct entropy_data *c = wps->w.c;
uint32_t ones_count, low, high;
Bitstream *bs = &wps->wvbits;
int32_t csamples;
#ifdef USE_NEXT8_OPTIMIZATION
int32_t next8;
#endif
if (nsamples && !bs->ptr) {
memset (buffer, 0, (wps->wphdr.flags & MONO_DATA) ? nsamples * 4 : nsamples * 8);
return nsamples;
}
if (!(wps->wphdr.flags & MONO_DATA))
nsamples *= 2;
for (csamples = 0; csamples < nsamples; ++csamples) {
if (!(wps->wphdr.flags & MONO_DATA))
c = wps->w.c + (csamples & 1);
if (wps->w.holding_zero) {
wps->w.holding_zero = 0;
low = read_code (bs, GET_MED (0) - 1);
DEC_MED0 ();
buffer [csamples] = (getbit (bs)) ? ~low : low;
if (++csamples == nsamples)
break;
if (!(wps->wphdr.flags & MONO_DATA))
c = wps->w.c + (csamples & 1);
}
if (wps->w.c [0].median [0] < 2 && !wps->w.holding_one && wps->w.c [1].median [0] < 2) {
uint32_t mask;
int cbits;
if (wps->w.zeros_acc) {
if (--wps->w.zeros_acc) {
buffer [csamples] = 0;
continue;
}
}
else {
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
if (cbits == 33)
break;
if (cbits < 2)
wps->w.zeros_acc = cbits;
else {
for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1)
if (getbit (bs))
wps->w.zeros_acc |= mask;
wps->w.zeros_acc |= mask;
}
if (wps->w.zeros_acc) {
CLEAR (wps->w.c [0].median);
CLEAR (wps->w.c [1].median);
buffer [csamples] = 0;
continue;
}
}
}
#ifdef USE_CTZ_OPTIMIZATION
while (bs->bc < LIMIT_ONES) {
if (++(bs->ptr) == bs->end)
bs->wrap (bs);
bs->sr |= *(bs->ptr) << bs->bc;
bs->bc += sizeof (*(bs->ptr)) * 8;
}
#ifdef _MSC_VER
{ unsigned long res; _BitScanForward (&res, (unsigned long)~wps->wvbits.sr); ones_count = (uint32_t) res; }
#else
ones_count = __builtin_ctz (~wps->wvbits.sr);
#endif
if (ones_count >= LIMIT_ONES) {
bs->bc -= ones_count;
bs->sr >>= ones_count;
for (; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count);
if (ones_count == (LIMIT_ONES + 1))
break;
if (ones_count == LIMIT_ONES) {
uint32_t mask;
int cbits;
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
if (cbits == 33)
break;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (bs))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
}
else {
bs->bc -= ones_count + 1;
bs->sr >>= ones_count + 1;
}
#elif defined (USE_NEXT8_OPTIMIZATION)
if (bs->bc < 8) {
if (++(bs->ptr) == bs->end)
bs->wrap (bs);
next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff;
bs->bc += sizeof (*(bs->ptr)) * 8;
}
else
next8 = bs->sr & 0xff;
if (next8 == 0xff) {
bs->bc -= 8;
bs->sr >>= 8;
for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count);
if (ones_count == (LIMIT_ONES + 1))
break;
if (ones_count == LIMIT_ONES) {
uint32_t mask;
int cbits;
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
if (cbits == 33)
break;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (bs))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
}
else {
bs->bc -= (ones_count = ones_count_table [next8]) + 1;
bs->sr >>= ones_count + 1;
}
#else
for (ones_count = 0; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count);
if (ones_count >= LIMIT_ONES) {
uint32_t mask;
int cbits;
if (ones_count == (LIMIT_ONES + 1))
break;
for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);
if (cbits == 33)
break;
if (cbits < 2)
ones_count = cbits;
else {
for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
if (getbit (bs))
ones_count |= mask;
ones_count |= mask;
}
ones_count += LIMIT_ONES;
}
#endif
low = wps->w.holding_one;
wps->w.holding_one = ones_count & 1;
wps->w.holding_zero = ~ones_count & 1;
ones_count = (ones_count >> 1) + low;
if (ones_count == 0) {
low = 0;
high = GET_MED (0) - 1;
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (ones_count == 1) {
high = low + GET_MED (1) - 1;
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (ones_count == 2) {
high = low + GET_MED (2) - 1;
DEC_MED2 ();
}
else {
low += (ones_count - 2) * GET_MED (2);
high = low + GET_MED (2) - 1;
INC_MED2 ();
}
}
}
low += read_code (bs, high - low);
buffer [csamples] = (getbit (bs)) ? ~low : low;
}
return (wps->wphdr.flags & MONO_DATA) ? csamples : (csamples / 2);
}
// Read a single unsigned value from the specified bitstream with a value
// from 0 to maxcode. If there are exactly a power of two number of possible
// codes then this will read a fixed number of bits; otherwise it reads the
// minimum number of bits and then determines whether another bit is needed
// to define the code.
static uint32_t __inline read_code (Bitstream *bs, uint32_t maxcode)
{
unsigned long local_sr;
uint32_t extras, code;
int bitcount;
if (maxcode < 2)
return maxcode ? getbit (bs) : 0;
bitcount = count_bits (maxcode);
#ifdef USE_BITMASK_TABLES
extras = bitset [bitcount] - maxcode - 1;
#else
extras = (1 << bitcount) - maxcode - 1;
#endif
local_sr = bs->sr;
while (bs->bc < bitcount) {
if (++(bs->ptr) == bs->end)
bs->wrap (bs);
local_sr |= (long)*(bs->ptr) << bs->bc;
bs->bc += sizeof (*(bs->ptr)) * 8;
}
#ifdef USE_BITMASK_TABLES
if ((code = local_sr & bitmask [bitcount - 1]) >= extras)
#else
if ((code = local_sr & ((1 << (bitcount - 1)) - 1)) >= extras)
#endif
code = (code << 1) - extras + ((local_sr >> (bitcount - 1)) & 1);
else
bitcount--;
if (sizeof (local_sr) < 8 && bs->bc > sizeof (local_sr) * 8) {
bs->bc -= bitcount;
bs->sr = *(bs->ptr) >> (sizeof (*(bs->ptr)) * 8 - bs->bc);
}
else {
bs->bc -= bitcount;
bs->sr = local_sr >> bitcount;
}
return code;
}

View file

@ -1,597 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// tag_utils.c
// This module provides the high-level API for creating, reading and editing
// APEv2 tags on WavPack files. Read-only support is also provided for ID3v1
// tags, but their use is not recommended.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#ifdef _WIN32
#define stricmp(x,y) _stricmp(x,y)
#else
#define stricmp strcasecmp
#endif
static int get_ape_tag_item (M_Tag *m_tag, const char *item, char *value, int size, int type);
static int get_id3_tag_item (M_Tag *m_tag, const char *item, char *value, int size);
static int get_ape_tag_item_indexed (M_Tag *m_tag, int index, char *item, int size, int type);
static int get_id3_tag_item_indexed (M_Tag *m_tag, int index, char *item, int size);
static int append_ape_tag_item (WavpackContext *wpc, const char *item, const char *value, int vsize, int type);
static int write_tag_blockout (WavpackContext *wpc);
static int write_tag_reader (WavpackContext *wpc);
static void tagcpy (char *dest, char *src, int tag_size);
static int tagdata (char *src, int tag_size);
//////////////////// Global functions part of external API /////////////////////////
// Count and return the total number of tag items in the specified file.
int WavpackGetNumTagItems (WavpackContext *wpc)
{
int i = 0;
while (WavpackGetTagItemIndexed (wpc, i, NULL, 0))
++i;
return i;
}
// Count and return the total number of binary tag items in the specified file. This applies
// only to APEv2 tags and was implemented as a separate function to avoid breaking the old API.
int WavpackGetNumBinaryTagItems (WavpackContext *wpc)
{
int i = 0;
while (WavpackGetBinaryTagItemIndexed (wpc, i, NULL, 0))
++i;
return i;
}
// Attempt to get the specified item from the specified file's ID3v1 or APEv2
// tag. The "size" parameter specifies the amount of space available at "value",
// if the desired item will not fit in this space then ellipses (...) will
// be appended and the string terminated. Only text data are supported. The
// actual length of the string is returned (or 0 if no matching value found).
// Note that with APEv2 tags the length might not be the same as the number of
// characters because UTF-8 encoding is used. Also, APEv2 tags can have multiple
// (NULL separated) strings for a single value (this is why the length is
// returned). If this function is called with a NULL "value" pointer (or a
// zero "length") then only the actual length of the value data is returned
// (not counting the terminating NULL). This can be used to determine the
// actual memory to be allocated beforehand.
int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size)
{
M_Tag *m_tag = &wpc->m_tag;
if (value && size)
*value = 0;
if (m_tag->ape_tag_hdr.ID [0] == 'A')
return get_ape_tag_item (m_tag, item, value, size, APE_TAG_TYPE_TEXT);
else if (m_tag->id3_tag.tag_id [0] == 'T')
return get_id3_tag_item (m_tag, item, value, size);
else
return 0;
}
// Attempt to get the specified binary item from the specified file's APEv2
// tag. The "size" parameter specifies the amount of space available at "value".
// If the desired item will not fit in this space then nothing will be copied
// and 0 will be returned, otherwise the actual size will be returned. If this
// function is called with a NULL "value" pointer (or a zero "length") then only
// the actual length of the value data is returned and can be used to determine
// the actual memory to be allocated beforehand.
int WavpackGetBinaryTagItem (WavpackContext *wpc, const char *item, char *value, int size)
{
M_Tag *m_tag = &wpc->m_tag;
if (value && size)
*value = 0;
if (m_tag->ape_tag_hdr.ID [0] == 'A')
return get_ape_tag_item (m_tag, item, value, size, APE_TAG_TYPE_BINARY);
else
return 0;
}
// This function looks up the tag item name by index and is used when the
// application wants to access all the items in the file's ID3v1 or APEv2 tag.
// Note that this function accesses only the item's name; WavpackGetTagItem()
// still must be called to get the actual value. The "size" parameter specifies
// the amount of space available at "item", if the desired item will not fit in
// this space then ellipses (...) will be appended and the string terminated.
// The actual length of the string is returned (or 0 if no item exists for
// index). If this function is called with a NULL "value" pointer (or a
// zero "length") then only the actual length of the item name is returned
// (not counting the terminating NULL). This can be used to determine the
// actual memory to be allocated beforehand. For binary tag values use the
// otherwise identical WavpackGetBinaryTagItemIndexed ();
int WavpackGetTagItemIndexed (WavpackContext *wpc, int index, char *item, int size)
{
M_Tag *m_tag = &wpc->m_tag;
if (item && size)
*item = 0;
if (m_tag->ape_tag_hdr.ID [0] == 'A')
return get_ape_tag_item_indexed (m_tag, index, item, size, APE_TAG_TYPE_TEXT);
else if (m_tag->id3_tag.tag_id [0] == 'T')
return get_id3_tag_item_indexed (m_tag, index, item, size);
else
return 0;
}
int WavpackGetBinaryTagItemIndexed (WavpackContext *wpc, int index, char *item, int size)
{
M_Tag *m_tag = &wpc->m_tag;
if (item && size)
*item = 0;
if (m_tag->ape_tag_hdr.ID [0] == 'A')
return get_ape_tag_item_indexed (m_tag, index, item, size, APE_TAG_TYPE_BINARY);
else
return 0;
}
// These two functions are used to append APEv2 tags to WavPack files; one is
// for text values (UTF-8 encoded) and the other is for binary values. If no tag
// has been started, then an empty one will be allocated first. When finished,
// use WavpackWriteTag() to write the completed tag to the file. The purpose of
// the passed size parameter is obvious for binary values, but might not be for
// text values. Keep in mind that APEv2 text values can have multiple values
// that are NULL separated, so the size is required to know the extent of the
// value (although the final terminating NULL is not included in the passed
// size). If the specified item already exists, it will be replaced with the
// new value. ID3v1 tags are not supported.
int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize)
{
while (WavpackDeleteTagItem (wpc, item));
return append_ape_tag_item (wpc, item, value, vsize, APE_TAG_TYPE_TEXT);
}
int WavpackAppendBinaryTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize)
{
while (WavpackDeleteTagItem (wpc, item));
return append_ape_tag_item (wpc, item, value, vsize, APE_TAG_TYPE_BINARY);
}
// Delete the specified tag item from the APEv2 tag on the specified WavPack file
// (fields cannot be deleted from ID3v1 tags). A return value of TRUE indicates
// that the item was found and successfully deleted.
int WavpackDeleteTagItem (WavpackContext *wpc, const char *item)
{
M_Tag *m_tag = &wpc->m_tag;
if (m_tag->ape_tag_hdr.ID [0] == 'A') {
unsigned char *p = m_tag->ape_tag_data;
unsigned char *q = p + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr);
int i;
for (i = 0; i < m_tag->ape_tag_hdr.item_count; ++i) {
int vsize, isize;
vsize = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); p += 8; // skip flags because we don't need them
for (isize = 0; p[isize] && p + isize < q; ++isize);
if (vsize < 0 || vsize > m_tag->ape_tag_hdr.length || p + isize + vsize + 1 > q)
break;
if (isize && vsize && !stricmp (item, (char *) p)) {
unsigned char *d = p - 8;
p += isize + vsize + 1;
while (p < q)
*d++ = *p++;
m_tag->ape_tag_hdr.length = (int32_t)(d - m_tag->ape_tag_data) + sizeof (APE_Tag_Hdr);
m_tag->ape_tag_hdr.item_count--;
return 1;
}
else
p += isize + vsize + 1;
}
}
return 0;
}
// Once a APEv2 tag has been created with WavpackAppendTag(), this function is
// used to write the completed tag to the end of the WavPack file. Note that
// this function uses the same "blockout" function that is used to write
// regular WavPack blocks, although that's where the similarity ends. It is also
// used to write tags that have been edited on existing files.
int WavpackWriteTag (WavpackContext *wpc)
{
if (wpc->blockout) // this is the case for creating fresh WavPack files
return write_tag_blockout (wpc);
else // otherwise we are editing existing tags (OPEN_EDIT_TAGS)
return write_tag_reader (wpc);
}
////////////////////////// local static functions /////////////////////////////
static int get_ape_tag_item (M_Tag *m_tag, const char *item, char *value, int size, int type)
{
unsigned char *p = m_tag->ape_tag_data;
unsigned char *q = p + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr);
int i;
for (i = 0; i < m_tag->ape_tag_hdr.item_count && q - p > 8; ++i) {
int vsize, flags, isize;
vsize = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); p += 4;
flags = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); p += 4;
for (isize = 0; p[isize] && p + isize < q; ++isize);
if (vsize < 0 || vsize > m_tag->ape_tag_hdr.length || p + isize + vsize + 1 > q)
break;
if (isize && vsize && !stricmp (item, (char *) p) && ((flags & 6) >> 1) == type) {
if (!value || !size)
return vsize;
if (type == APE_TAG_TYPE_BINARY) {
if (vsize <= size) {
memcpy (value, p + isize + 1, vsize);
return vsize;
}
else
return 0;
}
else if (vsize < size) {
memcpy (value, p + isize + 1, vsize);
value [vsize] = 0;
return vsize;
}
else if (size >= 4) {
memcpy (value, p + isize + 1, size - 1);
value [size - 4] = value [size - 3] = value [size - 2] = '.';
value [size - 1] = 0;
return size - 1;
}
else
return 0;
}
else
p += isize + vsize + 1;
}
return 0;
}
static int get_id3_tag_item (M_Tag *m_tag, const char *item, char *value, int size)
{
char lvalue [64];
int len;
lvalue [0] = 0;
if (!stricmp (item, "title"))
tagcpy (lvalue, m_tag->id3_tag.title, sizeof (m_tag->id3_tag.title));
else if (!stricmp (item, "artist"))
tagcpy (lvalue, m_tag->id3_tag.artist, sizeof (m_tag->id3_tag.artist));
else if (!stricmp (item, "album"))
tagcpy (lvalue, m_tag->id3_tag.album, sizeof (m_tag->id3_tag.album));
else if (!stricmp (item, "year"))
tagcpy (lvalue, m_tag->id3_tag.year, sizeof (m_tag->id3_tag.year));
else if (!stricmp (item, "comment"))
tagcpy (lvalue, m_tag->id3_tag.comment, sizeof (m_tag->id3_tag.comment));
else if (!stricmp (item, "track") && m_tag->id3_tag.comment [29] && !m_tag->id3_tag.comment [28])
sprintf (lvalue, "%d", m_tag->id3_tag.comment [29]);
else
return 0;
len = (int) strlen (lvalue);
if (!value || !size)
return len;
if (len < size) {
strcpy (value, lvalue);
return len;
}
else if (size >= 4) {
strncpy (value, lvalue, size - 1);
value [size - 4] = value [size - 3] = value [size - 2] = '.';
value [size - 1] = 0;
return size - 1;
}
else
return 0;
}
static int get_ape_tag_item_indexed (M_Tag *m_tag, int index, char *item, int size, int type)
{
unsigned char *p = m_tag->ape_tag_data;
unsigned char *q = p + m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr);
int i;
for (i = 0; i < m_tag->ape_tag_hdr.item_count && index >= 0 && q - p > 8; ++i) {
int vsize, flags, isize;
vsize = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); p += 4;
flags = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); p += 4;
for (isize = 0; p[isize] && p + isize < q; ++isize);
if (vsize < 0 || vsize > m_tag->ape_tag_hdr.length || p + isize + vsize + 1 > q)
break;
if (isize && vsize && ((flags & 6) >> 1) == type && !index--) {
if (!item || !size)
return isize;
if (isize < size) {
memcpy (item, p, isize);
item [isize] = 0;
return isize;
}
else if (size >= 4) {
memcpy (item, p, size - 1);
item [size - 4] = item [size - 3] = item [size - 2] = '.';
item [size - 1] = 0;
return size - 1;
}
else
return 0;
}
else
p += isize + vsize + 1;
}
return 0;
}
static int get_id3_tag_item_indexed (M_Tag *m_tag, int index, char *item, int size)
{
char lvalue [16];
int len;
lvalue [0] = 0;
if (tagdata (m_tag->id3_tag.title, sizeof (m_tag->id3_tag.title)) && !index--)
strcpy (lvalue, "Title");
else if (tagdata (m_tag->id3_tag.artist, sizeof (m_tag->id3_tag.artist)) && !index--)
strcpy (lvalue, "Artist");
else if (tagdata (m_tag->id3_tag.album, sizeof (m_tag->id3_tag.album)) && !index--)
strcpy (lvalue, "Album");
else if (tagdata (m_tag->id3_tag.year, sizeof (m_tag->id3_tag.year)) && !index--)
strcpy (lvalue, "Year");
else if (tagdata (m_tag->id3_tag.comment, sizeof (m_tag->id3_tag.comment)) && !index--)
strcpy (lvalue, "Comment");
else if (m_tag->id3_tag.comment [29] && !m_tag->id3_tag.comment [28] && !index--)
strcpy (lvalue, "Track");
else
return 0;
len = (int) strlen (lvalue);
if (!item || !size)
return len;
if (len < size) {
strcpy (item, lvalue);
return len;
}
else if (size >= 4) {
strncpy (item, lvalue, size - 1);
item [size - 4] = item [size - 3] = item [size - 2] = '.';
item [size - 1] = 0;
return size - 1;
}
else
return 0;
}
static int append_ape_tag_item (WavpackContext *wpc, const char *item, const char *value, int vsize, int type)
{
M_Tag *m_tag = &wpc->m_tag;
int isize = (int) strlen (item);
if (!m_tag->ape_tag_hdr.ID [0]) {
memcpy (m_tag->ape_tag_hdr.ID, "APETAGEX", sizeof (m_tag->ape_tag_hdr.ID));
m_tag->ape_tag_hdr.version = 2000;
m_tag->ape_tag_hdr.length = sizeof (m_tag->ape_tag_hdr);
m_tag->ape_tag_hdr.item_count = 0;
m_tag->ape_tag_hdr.flags = APE_TAG_CONTAINS_HEADER; // we will include header on tags we originate
}
if (m_tag->ape_tag_hdr.ID [0] == 'A') {
int new_item_len = vsize + isize + 9, flags = type << 1;
unsigned char *p;
if (m_tag->ape_tag_hdr.length + new_item_len > APE_TAG_MAX_LENGTH) {
strcpy (wpc->error_message, "APEv2 tag exceeds maximum allowed length!");
return FALSE;
}
m_tag->ape_tag_hdr.item_count++;
m_tag->ape_tag_hdr.length += new_item_len;
p = m_tag->ape_tag_data = (unsigned char*)realloc (m_tag->ape_tag_data, m_tag->ape_tag_hdr.length);
p += m_tag->ape_tag_hdr.length - sizeof (APE_Tag_Hdr) - new_item_len;
*p++ = (unsigned char) vsize;
*p++ = (unsigned char) (vsize >> 8);
*p++ = (unsigned char) (vsize >> 16);
*p++ = (unsigned char) (vsize >> 24);
*p++ = (unsigned char) flags;
*p++ = (unsigned char) (flags >> 8);
*p++ = (unsigned char) (flags >> 16);
*p++ = (unsigned char) (flags >> 24);
strcpy ((char *) p, item);
p += isize + 1;
memcpy (p, value, vsize);
return TRUE;
}
else
return FALSE;
}
// Append the stored APEv2 tag to the file being created using the "blockout" function callback.
static int write_tag_blockout (WavpackContext *wpc)
{
M_Tag *m_tag = &wpc->m_tag;
int result = TRUE;
if (m_tag->ape_tag_hdr.ID [0] == 'A' && m_tag->ape_tag_hdr.item_count) {
// only write header if it's specified in the flags
if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) {
m_tag->ape_tag_hdr.flags |= APE_TAG_THIS_IS_HEADER;
WavpackNativeToLittleEndian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
result = wpc->blockout (wpc->wv_out, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr));
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
}
if (m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr))
result = wpc->blockout (wpc->wv_out, m_tag->ape_tag_data, m_tag->ape_tag_hdr.length - sizeof (m_tag->ape_tag_hdr));
m_tag->ape_tag_hdr.flags &= ~APE_TAG_THIS_IS_HEADER; // this is NOT header
WavpackNativeToLittleEndian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
result = wpc->blockout (wpc->wv_out, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr));
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
}
if (!result)
strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
return result;
}
// Write the [potentially] edited tag to the existing WavPack file using the reader callback functions.
static int write_tag_reader (WavpackContext *wpc)
{
M_Tag *m_tag = &wpc->m_tag;
int32_t tag_size = 0;
int result;
// before we write an edited (or new) tag into an existing file, make sure it's safe and possible
if (m_tag->tag_begins_file) {
strcpy (wpc->error_message, "can't edit tags located at the beginning of files!");
return FALSE;
}
if (!wpc->reader->can_seek (wpc->wv_in)) {
strcpy (wpc->error_message, "can't edit tags on pipes or unseekable files!");
return FALSE;
}
if (!(wpc->open_flags & OPEN_EDIT_TAGS)) {
strcpy (wpc->error_message, "can't edit tags without OPEN_EDIT_TAGS flag!");
return FALSE;
}
if (m_tag->ape_tag_hdr.ID [0] == 'A' && m_tag->ape_tag_hdr.item_count &&
m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr))
tag_size = m_tag->ape_tag_hdr.length;
// only write header if it's specified in the flags
if (tag_size && (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER))
tag_size += sizeof (m_tag->ape_tag_hdr);
result = !wpc->reader->set_pos_rel (wpc->wv_in, m_tag->tag_file_pos, SEEK_END);
if (result && tag_size < -m_tag->tag_file_pos && !wpc->reader->truncate_here) {
int nullcnt = (int) (-m_tag->tag_file_pos - tag_size);
char zero [1] = { 0 };
while (nullcnt--)
wpc->reader->write_bytes (wpc->wv_in, &zero, 1);
}
if (result && tag_size) {
if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) {
m_tag->ape_tag_hdr.flags |= APE_TAG_THIS_IS_HEADER;
WavpackNativeToLittleEndian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
result = (wpc->reader->write_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr)) == sizeof (m_tag->ape_tag_hdr));
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
}
result = (wpc->reader->write_bytes (wpc->wv_in, m_tag->ape_tag_data, m_tag->ape_tag_hdr.length - sizeof (m_tag->ape_tag_hdr)) == sizeof (m_tag->ape_tag_hdr));
m_tag->ape_tag_hdr.flags &= ~APE_TAG_THIS_IS_HEADER; // this is NOT header
WavpackNativeToLittleEndian (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
result = (wpc->reader->write_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (m_tag->ape_tag_hdr)) == sizeof (m_tag->ape_tag_hdr));
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
}
if (result && tag_size < -m_tag->tag_file_pos && wpc->reader->truncate_here)
result = !wpc->reader->truncate_here (wpc->wv_in);
if (!result)
strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
return result;
}
// Copy the specified ID3v1 tag value (with specified field size) from the
// source pointer to the destination, eliminating leading spaces and trailing
// spaces and nulls.
static void tagcpy (char *dest, char *src, int tag_size)
{
char *s1 = src, *s2 = src + tag_size - 1;
if (*s2 && !s2 [-1])
s2--;
while (s1 <= s2)
if (*s1 == ' ')
++s1;
else if (!*s2 || *s2 == ' ')
--s2;
else
break;
while (*s1 && s1 <= s2)
*dest++ = *s1++;
*dest = 0;
}
static int tagdata (char *src, int tag_size)
{
char *s1 = src, *s2 = src + tag_size - 1;
if (*s2 && !s2 [-1])
s2--;
while (s1 <= s2)
if (*s1 == ' ')
++s1;
else if (!*s2 || *s2 == ' ')
--s2;
else
break;
return (*s1 && s1 <= s2);
}

View file

@ -1,179 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// tags.c
// This module provides support for reading metadata tags (either ID3v1 or
// APEv2) from WavPack files. No actual creation or manipulation of the tags
// is done in this module; this is just internal code to load the tags into
// memory. The high-level API functions are in the tag_utils.c module.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
// This function attempts to load an ID3v1 or APEv2 tag from the specified
// file into the specified M_Tag structure. The ID3 tag fits in completely,
// but an APEv2 tag is variable length and so space must be allocated here
// to accommodate the data, and this will need to be freed later. A return
// value of TRUE indicates a valid tag was found and loaded. Note that the
// file pointer is undefined when this function exits.
int load_tag (WavpackContext *wpc)
{
int ape_tag_length, ape_tag_items;
M_Tag *m_tag = &wpc->m_tag;
CLEAR (*m_tag);
// This is a loop because we can try up to three times to look for an APEv2 tag. In order, we look:
//
// 1. At the end of the file for a APEv2 footer (this is the preferred location)
// 2. If there's instead an ID3v1 tag at the end of the file, try looking for an APEv2 footer right before that
// 3. If all else fails, look for an APEv2 header the the beginning of the file (use is strongly discouraged)
while (1) {
// seek based on specific location that we are looking for tag (see above list)
if (m_tag->tag_begins_file) // case #3
wpc->reader->set_pos_abs (wpc->wv_in, 0);
else if (m_tag->id3_tag.tag_id [0] == 'T') // case #2
wpc->reader->set_pos_rel (wpc->wv_in, -(int32_t)(sizeof (APE_Tag_Hdr) + sizeof (ID3_Tag)), SEEK_END);
else // case #1
wpc->reader->set_pos_rel (wpc->wv_in, -(int32_t)sizeof (APE_Tag_Hdr), SEEK_END);
// read a possible APEv2 tag header/footer and see if there's one there...
if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) == sizeof (APE_Tag_Hdr) &&
!strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) {
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
if (m_tag->ape_tag_hdr.version == 2000 && m_tag->ape_tag_hdr.item_count &&
m_tag->ape_tag_hdr.length > sizeof (m_tag->ape_tag_hdr) &&
m_tag->ape_tag_hdr.length <= APE_TAG_MAX_LENGTH &&
(m_tag->ape_tag_data = (unsigned char *)malloc (m_tag->ape_tag_hdr.length)) != NULL) {
ape_tag_items = m_tag->ape_tag_hdr.item_count;
ape_tag_length = m_tag->ape_tag_hdr.length;
// If this is a APEv2 footer (which is normal if we are searching at the end of the file)...
if (!(m_tag->ape_tag_hdr.flags & APE_TAG_THIS_IS_HEADER)) {
if (m_tag->id3_tag.tag_id [0] == 'T')
m_tag->tag_file_pos = -(int32_t)sizeof (ID3_Tag);
else
m_tag->tag_file_pos = 0;
m_tag->tag_file_pos -= ape_tag_length;
// if the footer claims there is a header present also, we will read that and use it
// instead of the footer (after verifying it, of course) for enhanced robustness
if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER)
m_tag->tag_file_pos -= sizeof (APE_Tag_Hdr);
wpc->reader->set_pos_rel (wpc->wv_in, m_tag->tag_file_pos, SEEK_END);
if (m_tag->ape_tag_hdr.flags & APE_TAG_CONTAINS_HEADER) {
if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->ape_tag_hdr, sizeof (APE_Tag_Hdr)) !=
sizeof (APE_Tag_Hdr) || strncmp (m_tag->ape_tag_hdr.ID, "APETAGEX", 8)) {
free (m_tag->ape_tag_data);
CLEAR (*m_tag);
return FALSE; // something's wrong...
}
WavpackLittleEndianToNative (&m_tag->ape_tag_hdr, APE_Tag_Hdr_Format);
if (m_tag->ape_tag_hdr.version != 2000 || m_tag->ape_tag_hdr.item_count != ape_tag_items ||
m_tag->ape_tag_hdr.length != ape_tag_length) {
free (m_tag->ape_tag_data);
CLEAR (*m_tag);
return FALSE; // something's wrong...
}
}
}
if (wpc->reader->read_bytes (wpc->wv_in, m_tag->ape_tag_data,
ape_tag_length - sizeof (APE_Tag_Hdr)) != ape_tag_length - sizeof (APE_Tag_Hdr)) {
free (m_tag->ape_tag_data);
CLEAR (*m_tag);
return FALSE; // something's wrong...
}
else {
CLEAR (m_tag->id3_tag); // ignore ID3v1 tag if we found APEv2 tag
return TRUE;
}
}
}
// we come here if the search for the APEv2 tag failed (otherwise we would have returned with it)
if (m_tag->id3_tag.tag_id [0] == 'T') { // settle for the ID3v1 tag that we found
CLEAR (m_tag->ape_tag_hdr);
return TRUE;
}
// if this was the search for the APEv2 tag at the beginning of the file (which is our
// last resort) then we have nothing, so return failure
if (m_tag->tag_begins_file) {
CLEAR (*m_tag);
return FALSE;
}
// If we get here, then we have failed the first APEv2 tag search (at end of file) and so now we
// look for an ID3v1 tag at the same position. If that succeeds, then we'll loop back and look for
// an APEv2 tag immediately before the ID3v1 tag, otherwise our last resort is to look for an
// APEv2 tag at the beginning of the file. These are strongly discouraged (and not editable) but
// they have been seen in the wild so we attempt to handle them here (at least well enough to
// allow a proper transcoding).
m_tag->tag_file_pos = -(int32_t)sizeof (ID3_Tag);
wpc->reader->set_pos_rel (wpc->wv_in, m_tag->tag_file_pos, SEEK_END);
if (wpc->reader->read_bytes (wpc->wv_in, &m_tag->id3_tag, sizeof (ID3_Tag)) != sizeof (ID3_Tag) ||
strncmp (m_tag->id3_tag.tag_id, "TAG", 3)) {
m_tag->tag_begins_file = 1; // failed ID3v1, so look for APEv2 at beginning of file
CLEAR (m_tag->id3_tag);
}
}
}
// Return TRUE is a valid ID3v1 or APEv2 tag has been loaded.
int valid_tag (M_Tag *m_tag)
{
if (m_tag->ape_tag_hdr.ID [0] == 'A')
return 'A';
else if (m_tag->id3_tag.tag_id [0] == 'T')
return 'T';
else
return 0;
}
// Return FALSE if a valid APEv2 tag was only found at the beginning of the file (these are read-only
// because they cannot be edited without possibly shifting the entire file)
int editable_tag (M_Tag *m_tag)
{
return !m_tag->tag_begins_file;
}
// Free the data for any APEv2 tag that was allocated.
void free_tag (M_Tag *m_tag)
{
if (m_tag->ape_tag_data) {
free (m_tag->ape_tag_data);
m_tag->ape_tag_data = NULL;
}
}

View file

@ -1,817 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack.c
// This module actually handles the decompression of the audio data, except for
// the entropy decoding which is handled by the read_words.c module. For better
// efficiency, the conversion is isolated to tight loops that handle an entire
// buffer.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#ifdef OPT_ASM_X86
#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x86
#define DECORR_STEREO_PASS_CONT_AVAILABLE unpack_cpu_has_feature_x86(CPU_FEATURE_MMX)
#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x86
#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__) || defined(__midipix__))
#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64win
#define DECORR_STEREO_PASS_CONT_AVAILABLE 1
#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64win
#elif defined(OPT_ASM_X64)
#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_x64
#define DECORR_STEREO_PASS_CONT_AVAILABLE 1
#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_x64
#elif defined(OPT_ASM_ARM)
#define DECORR_STEREO_PASS_CONT unpack_decorr_stereo_pass_cont_armv7
#define DECORR_STEREO_PASS_CONT_AVAILABLE 1
#define DECORR_MONO_PASS_CONT unpack_decorr_mono_pass_cont_armv7
#endif
#ifdef DECORR_STEREO_PASS_CONT
extern void DECORR_STEREO_PASS_CONT (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math);
extern void DECORR_MONO_PASS_CONT (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count, int32_t long_math);
#endif
// This flag provides the functionality of terminating the decoding and muting
// the output when a lossy sample appears to be corrupt. This is automatic
// for lossless files because a corrupt sample is unambigious, but for lossy
// data it might be possible for this to falsely trigger (although I have never
// seen it).
#define LOSSY_MUTE
///////////////////////////// executable code ////////////////////////////////
// This monster actually unpacks the WavPack bitstream(s) into the specified
// buffer as 32-bit integers or floats (depending on original data). Lossy
// samples will be clipped to their original limits (i.e. 8-bit samples are
// clipped to -128/+127) but are still returned in longs. It is up to the
// caller to potentially reformat this for the final output including any
// multichannel distribution, block alignment or endian compensation. The
// function unpack_init() must have been called and the entire WavPack block
// must still be visible (although wps->blockbuff will not be accessed again).
// For maximum clarity, the function is broken up into segments that handle
// various modes. This makes for a few extra infrequent flag checks, but
// makes the code easier to follow because the nesting does not become so
// deep. For maximum efficiency, the conversion is isolated to tight loops
// that handle an entire buffer. The function returns the total number of
// samples unpacked, which can be less than the number requested if an error
// occurs or the end of the block is reached.
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count);
static void fixup_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count);
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
uint32_t flags = wps->wphdr.flags, crc = wps->crc, i;
int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2;
int32_t correction [2], read_word, *bptr;
struct decorr_pass *dpp;
int tcount, m = 0;
// don't attempt to decode past the end of the block, but watch out for overflow!
if (wps->sample_index + sample_count > GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples &&
(uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index) < sample_count)
sample_count = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index);
if (GET_BLOCK_INDEX (wps->wphdr) > wps->sample_index || wps->wphdr.block_samples < sample_count)
wps->mute_error = TRUE;
if (wps->mute_error) {
if (wpc->reduced_channels == 1 || wpc->config.num_channels == 1 || (flags & MONO_FLAG))
memset (buffer, 0, sample_count * 4);
else
memset (buffer, 0, sample_count * 8);
wps->sample_index += sample_count;
return sample_count;
}
if ((flags & HYBRID_FLAG) && !wps->block2buff)
mute_limit = (mute_limit * 2) + 128;
//////////////// handle lossless or hybrid lossy mono data /////////////////
if (!wps->block2buff && (flags & MONO_DATA)) {
int32_t *eptr = buffer + sample_count;
if (flags & HYBRID_FLAG) {
i = sample_count;
for (bptr = buffer; bptr < eptr;)
if ((*bptr++ = get_word (wps, 0, NULL)) == WORD_EOF) {
i = (uint32_t)(bptr - buffer);
break;
}
}
else
i = get_words_lossless (wps, buffer, sample_count);
#ifdef DECORR_MONO_PASS_CONT
if (sample_count < 16)
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
decorr_mono_pass (dpp, buffer, sample_count);
else
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
int pre_samples = (dpp->term > MAX_TERM) ? 2 : dpp->term;
decorr_mono_pass (dpp, buffer, pre_samples);
DECORR_MONO_PASS_CONT (dpp, buffer + pre_samples, sample_count - pre_samples,
((flags & MAG_MASK) >> MAG_LSB) > 15);
}
#else
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
decorr_mono_pass (dpp, buffer, sample_count);
#endif
#ifndef LOSSY_MUTE
if (!(flags & HYBRID_FLAG))
#endif
for (bptr = buffer; bptr < eptr; ++bptr) {
if (labs (bptr [0]) > mute_limit) {
i = (uint32_t)(bptr - buffer);
break;
}
crc = crc * 3 + bptr [0];
}
#ifndef LOSSY_MUTE
else
for (bptr = buffer; bptr < eptr; ++bptr)
crc = crc * 3 + bptr [0];
#endif
}
/////////////// handle lossless or hybrid lossy stereo data ///////////////
else if (!wps->block2buff && !(flags & MONO_DATA)) {
int32_t *eptr = buffer + (sample_count * 2);
if (flags & HYBRID_FLAG) {
i = sample_count;
for (bptr = buffer; bptr < eptr; bptr += 2)
if ((bptr [0] = get_word (wps, 0, NULL)) == WORD_EOF ||
(bptr [1] = get_word (wps, 1, NULL)) == WORD_EOF) {
i = (uint32_t)(bptr - buffer) / 2;
break;
}
}
else
i = get_words_lossless (wps, buffer, sample_count);
#ifdef DECORR_STEREO_PASS_CONT
if (sample_count < 16 || !DECORR_STEREO_PASS_CONT_AVAILABLE) {
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
decorr_stereo_pass (dpp, buffer, sample_count);
m = sample_count & (MAX_TERM - 1);
}
else
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
int pre_samples = (dpp->term < 0 || dpp->term > MAX_TERM) ? 2 : dpp->term;
decorr_stereo_pass (dpp, buffer, pre_samples);
DECORR_STEREO_PASS_CONT (dpp, buffer + pre_samples * 2, sample_count - pre_samples,
((flags & MAG_MASK) >> MAG_LSB) >= 16);
}
#else
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
decorr_stereo_pass (dpp, buffer, sample_count);
m = sample_count & (MAX_TERM - 1);
#endif
if (flags & JOINT_STEREO)
for (bptr = buffer; bptr < eptr; bptr += 2) {
bptr [0] += (bptr [1] -= (bptr [0] >> 1));
crc += (crc << 3) + (bptr [0] << 1) + bptr [0] + bptr [1];
}
else
for (bptr = buffer; bptr < eptr; bptr += 2)
crc += (crc << 3) + (bptr [0] << 1) + bptr [0] + bptr [1];
#ifndef LOSSY_MUTE
if (!(flags & HYBRID_FLAG))
#endif
for (bptr = buffer; bptr < eptr; bptr += 16)
if (labs (bptr [0]) > mute_limit || labs (bptr [1]) > mute_limit) {
i = (uint32_t)(bptr - buffer) / 2;
break;
}
}
/////////////////// handle hybrid lossless mono data ////////////////////
else if ((flags & HYBRID_FLAG) && (flags & MONO_DATA))
for (bptr = buffer, i = 0; i < sample_count; ++i) {
if ((read_word = get_word (wps, 0, correction)) == WORD_EOF)
break;
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
int32_t sam, temp;
int k;
if (dpp->term > MAX_TERM) {
if (dpp->term & 1)
sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
else
sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
dpp->samples_A [1] = dpp->samples_A [0];
k = 0;
}
else {
sam = dpp->samples_A [m];
k = (m + dpp->term) & (MAX_TERM - 1);
}
temp = apply_weight (dpp->weight_A, sam) + read_word;
update_weight (dpp->weight_A, dpp->delta, sam, read_word);
dpp->samples_A [k] = read_word = temp;
}
m = (m + 1) & (MAX_TERM - 1);
if (flags & HYBRID_SHAPE) {
int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
int32_t temp = -apply_weight (shaping_weight, wps->dc.error [0]);
if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
if (temp == wps->dc.error [0])
temp = (temp < 0) ? temp + 1 : temp - 1;
wps->dc.error [0] = temp - correction [0];
}
else
wps->dc.error [0] = -correction [0];
read_word += correction [0] - temp;
}
else
read_word += correction [0];
crc += (crc << 1) + read_word;
if (labs (read_word) > mute_limit)
break;
*bptr++ = read_word;
}
//////////////////// handle hybrid lossless stereo data ///////////////////
else if (wps->block2buff && !(flags & MONO_DATA))
for (bptr = buffer, i = 0; i < sample_count; ++i) {
int32_t left, right, left2, right2;
int32_t left_c = 0, right_c = 0;
if ((left = get_word (wps, 0, correction)) == WORD_EOF ||
(right = get_word (wps, 1, correction + 1)) == WORD_EOF)
break;
if (flags & CROSS_DECORR) {
left_c = left + correction [0];
right_c = right + correction [1];
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
int32_t sam_A, sam_B;
if (dpp->term > 0) {
if (dpp->term > MAX_TERM) {
if (dpp->term & 1) {
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
}
else {
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
}
}
else {
sam_A = dpp->samples_A [m];
sam_B = dpp->samples_B [m];
}
left_c += apply_weight (dpp->weight_A, sam_A);
right_c += apply_weight (dpp->weight_B, sam_B);
}
else if (dpp->term == -1) {
left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]);
right_c += apply_weight (dpp->weight_B, left_c);
}
else {
right_c += apply_weight (dpp->weight_B, dpp->samples_B [0]);
if (dpp->term == -3)
left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]);
else
left_c += apply_weight (dpp->weight_A, right_c);
}
}
if (flags & JOINT_STEREO)
left_c += (right_c -= (left_c >> 1));
}
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
int32_t sam_A, sam_B;
if (dpp->term > 0) {
int k;
if (dpp->term > MAX_TERM) {
if (dpp->term & 1) {
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1];
}
else {
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1;
}
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_B [1] = dpp->samples_B [0];
k = 0;
}
else {
sam_A = dpp->samples_A [m];
sam_B = dpp->samples_B [m];
k = (m + dpp->term) & (MAX_TERM - 1);
}
left2 = apply_weight (dpp->weight_A, sam_A) + left;
right2 = apply_weight (dpp->weight_B, sam_B) + right;
update_weight (dpp->weight_A, dpp->delta, sam_A, left);
update_weight (dpp->weight_B, dpp->delta, sam_B, right);
dpp->samples_A [k] = left = left2;
dpp->samples_B [k] = right = right2;
}
else if (dpp->term == -1) {
left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]);
update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left);
left = left2;
right2 = right + apply_weight (dpp->weight_B, left2);
update_weight_clip (dpp->weight_B, dpp->delta, left2, right);
dpp->samples_A [0] = right = right2;
}
else {
right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]);
update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right);
right = right2;
if (dpp->term == -3) {
right2 = dpp->samples_A [0];
dpp->samples_A [0] = right;
}
left2 = left + apply_weight (dpp->weight_A, right2);
update_weight_clip (dpp->weight_A, dpp->delta, right2, left);
dpp->samples_B [0] = left = left2;
}
}
m = (m + 1) & (MAX_TERM - 1);
if (!(flags & CROSS_DECORR)) {
left_c = left + correction [0];
right_c = right + correction [1];
if (flags & JOINT_STEREO)
left_c += (right_c -= (left_c >> 1));
}
if (flags & JOINT_STEREO)
left += (right -= (left >> 1));
if (flags & HYBRID_SHAPE) {
int shaping_weight;
int32_t temp;
correction [0] = left_c - left;
shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16;
temp = -apply_weight (shaping_weight, wps->dc.error [0]);
if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
if (temp == wps->dc.error [0])
temp = (temp < 0) ? temp + 1 : temp - 1;
wps->dc.error [0] = temp - correction [0];
}
else
wps->dc.error [0] = -correction [0];
left = left_c - temp;
correction [1] = right_c - right;
shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16;
temp = -apply_weight (shaping_weight, wps->dc.error [1]);
if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) {
if (temp == wps->dc.error [1])
temp = (temp < 0) ? temp + 1 : temp - 1;
wps->dc.error [1] = temp - correction [1];
}
else
wps->dc.error [1] = -correction [1];
right = right_c - temp;
}
else {
left = left_c;
right = right_c;
}
if (labs (left) > mute_limit || labs (right) > mute_limit)
break;
crc += (crc << 3) + (left << 1) + left + right;
*bptr++ = left;
*bptr++ = right;
}
else
i = 0; /* this line can't execute, but suppresses compiler warning */
if (i != sample_count) {
memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8));
wps->mute_error = TRUE;
i = sample_count;
if (bs_is_open (&wps->wvxbits))
bs_close_read (&wps->wvxbits);
}
if (m)
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++)
if (dpp->term > 0 && dpp->term <= MAX_TERM) {
int32_t temp_A [MAX_TERM], temp_B [MAX_TERM];
int k;
memcpy (temp_A, dpp->samples_A, sizeof (dpp->samples_A));
memcpy (temp_B, dpp->samples_B, sizeof (dpp->samples_B));
for (k = 0; k < MAX_TERM; k++) {
dpp->samples_A [k] = temp_A [m];
dpp->samples_B [k] = temp_B [m];
m = (m + 1) & (MAX_TERM - 1);
}
}
fixup_samples (wpc, buffer, i);
if ((flags & FLOAT_DATA) && (wpc->open_flags & OPEN_NORMALIZE))
WavpackFloatNormalize (buffer, (flags & MONO_DATA) ? i : i * 2,
127 - wps->float_norm_exp + wpc->norm_offset);
if (flags & FALSE_STEREO) {
int32_t *dptr = buffer + i * 2;
int32_t *sptr = buffer + i;
int32_t c = i;
while (c--) {
*--dptr = *--sptr;
*--dptr = *sptr;
}
}
wps->sample_index += i;
wps->crc = crc;
return i;
}
// General function to perform mono decorrelation pass on specified buffer
// (although since this is the reverse function it might technically be called
// "correlation" instead). This version handles all sample resolutions and
// weight deltas. The dpp->samples_X[] data is returned normalized for term
// values 1-8.
static void decorr_mono_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count)
{
int32_t delta = dpp->delta, weight_A = dpp->weight_A;
int32_t *bptr, *eptr = buffer + sample_count, sam_A;
int m, k;
switch (dpp->term) {
case 17:
for (bptr = buffer; bptr < eptr; bptr++) {
sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1];
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
update_weight (weight_A, delta, sam_A, bptr [0]);
bptr [0] = dpp->samples_A [0];
}
break;
case 18:
for (bptr = buffer; bptr < eptr; bptr++) {
sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1;
dpp->samples_A [1] = dpp->samples_A [0];
dpp->samples_A [0] = apply_weight (weight_A, sam_A) + bptr [0];
update_weight (weight_A, delta, sam_A, bptr [0]);
bptr [0] = dpp->samples_A [0];
}
break;
default:
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr++) {
sam_A = dpp->samples_A [m];
dpp->samples_A [k] = apply_weight (weight_A, sam_A) + bptr [0];
update_weight (weight_A, delta, sam_A, bptr [0]);
bptr [0] = dpp->samples_A [k];
m = (m + 1) & (MAX_TERM - 1);
k = (k + 1) & (MAX_TERM - 1);
}
if (m) {
int32_t temp_samples [MAX_TERM];
memcpy (temp_samples, dpp->samples_A, sizeof (dpp->samples_A));
for (k = 0; k < MAX_TERM; k++, m++)
dpp->samples_A [k] = temp_samples [m & (MAX_TERM - 1)];
}
break;
}
dpp->weight_A = weight_A;
}
// General function to perform stereo decorrelation pass on specified buffer
// (although since this is the reverse function it might technically be called
// "correlation" instead). This version handles all sample resolutions and
// weight deltas. The dpp->samples_X[] data is *not* returned normalized for
// term values 1-8, so it should be normalized if it is going to be used to
// call this function again.
static void decorr_stereo_pass (struct decorr_pass *dpp, int32_t *buffer, int32_t sample_count)
{
int32_t *bptr, *eptr = buffer + (sample_count * 2);
int m, k;
switch (dpp->term) {
case 17:
for (bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam, tmp;
sam = 2 * dpp->samples_A [0] - dpp->samples_A [1];
dpp->samples_A [1] = dpp->samples_A [0];
bptr [0] = dpp->samples_A [0] = apply_weight (dpp->weight_A, sam) + (tmp = bptr [0]);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
sam = 2 * dpp->samples_B [0] - dpp->samples_B [1];
dpp->samples_B [1] = dpp->samples_B [0];
bptr [1] = dpp->samples_B [0] = apply_weight (dpp->weight_B, sam) + (tmp = bptr [1]);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
}
break;
case 18:
for (bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam, tmp;
sam = dpp->samples_A [0] + ((dpp->samples_A [0] - dpp->samples_A [1]) >> 1);
dpp->samples_A [1] = dpp->samples_A [0];
bptr [0] = dpp->samples_A [0] = apply_weight (dpp->weight_A, sam) + (tmp = bptr [0]);
update_weight (dpp->weight_A, dpp->delta, sam, tmp);
sam = dpp->samples_B [0] + ((dpp->samples_B [0] - dpp->samples_B [1]) >> 1);
dpp->samples_B [1] = dpp->samples_B [0];
bptr [1] = dpp->samples_B [0] = apply_weight (dpp->weight_B, sam) + (tmp = bptr [1]);
update_weight (dpp->weight_B, dpp->delta, sam, tmp);
}
break;
default:
for (m = 0, k = dpp->term & (MAX_TERM - 1), bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam;
sam = dpp->samples_A [m];
dpp->samples_A [k] = apply_weight (dpp->weight_A, sam) + bptr [0];
update_weight (dpp->weight_A, dpp->delta, sam, bptr [0]);
bptr [0] = dpp->samples_A [k];
sam = dpp->samples_B [m];
dpp->samples_B [k] = apply_weight (dpp->weight_B, sam) + bptr [1];
update_weight (dpp->weight_B, dpp->delta, sam, bptr [1]);
bptr [1] = dpp->samples_B [k];
m = (m + 1) & (MAX_TERM - 1);
k = (k + 1) & (MAX_TERM - 1);
}
break;
case -1:
for (bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam;
sam = bptr [0] + apply_weight (dpp->weight_A, dpp->samples_A [0]);
update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]);
bptr [0] = sam;
dpp->samples_A [0] = bptr [1] + apply_weight (dpp->weight_B, sam);
update_weight_clip (dpp->weight_B, dpp->delta, sam, bptr [1]);
bptr [1] = dpp->samples_A [0];
}
break;
case -2:
for (bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam;
sam = bptr [1] + apply_weight (dpp->weight_B, dpp->samples_B [0]);
update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]);
bptr [1] = sam;
dpp->samples_B [0] = bptr [0] + apply_weight (dpp->weight_A, sam);
update_weight_clip (dpp->weight_A, dpp->delta, sam, bptr [0]);
bptr [0] = dpp->samples_B [0];
}
break;
case -3:
for (bptr = buffer; bptr < eptr; bptr += 2) {
int32_t sam_A, sam_B;
sam_A = bptr [0] + apply_weight (dpp->weight_A, dpp->samples_A [0]);
update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], bptr [0]);
sam_B = bptr [1] + apply_weight (dpp->weight_B, dpp->samples_B [0]);
update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], bptr [1]);
bptr [0] = dpp->samples_B [0] = sam_A;
bptr [1] = dpp->samples_A [0] = sam_B;
}
break;
}
}
// This is a helper function for unpack_samples() that applies several final
// operations. First, if the data is 32-bit float data, then that conversion
// is done in the float.c module (whether lossy or lossless) and we return.
// Otherwise, if the extended integer data applies, then that operation is
// executed first. If the unpacked data is lossy (and not corrected) then
// it is clipped and shifted in a single operation. Otherwise, if it's
// lossless then the last step is to apply the final shift (if any).
static void fixup_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
uint32_t flags = wps->wphdr.flags;
int lossy_flag = (flags & HYBRID_FLAG) && !wps->block2buff;
int shift = (flags & SHIFT_MASK) >> SHIFT_LSB;
if (flags & FLOAT_DATA) {
float_values (wps, buffer, (flags & MONO_DATA) ? sample_count : sample_count * 2);
return;
}
if (flags & INT32_DATA) {
uint32_t count = (flags & MONO_DATA) ? sample_count : sample_count * 2;
int sent_bits = wps->int32_sent_bits, zeros = wps->int32_zeros;
int ones = wps->int32_ones, dups = wps->int32_dups;
uint32_t data, mask = (1 << sent_bits) - 1;
int32_t *dptr = buffer;
if (bs_is_open (&wps->wvxbits)) {
uint32_t crc = wps->crc_x;
while (count--) {
// if (sent_bits) {
getbits (&data, sent_bits, &wps->wvxbits);
*dptr = (*dptr << sent_bits) | (data & mask);
// }
if (zeros)
*dptr <<= zeros;
else if (ones)
*dptr = ((*dptr + 1) << ones) - 1;
else if (dups)
*dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1);
crc = crc * 9 + (*dptr & 0xffff) * 3 + ((*dptr >> 16) & 0xffff);
dptr++;
}
wps->crc_x = crc;
}
else if (!sent_bits && (zeros + ones + dups)) {
while (lossy_flag && (flags & BYTES_STORED) == 3 && shift < 8) {
if (zeros)
zeros--;
else if (ones)
ones--;
else if (dups)
dups--;
else
break;
shift++;
}
while (count--) {
if (zeros)
*dptr <<= zeros;
else if (ones)
*dptr = ((*dptr + 1) << ones) - 1;
else if (dups)
*dptr = ((*dptr + (*dptr & 1)) << dups) - (*dptr & 1);
dptr++;
}
}
else
shift += zeros + sent_bits + ones + dups;
}
if (lossy_flag) {
int32_t min_value, max_value, min_shifted, max_shifted;
switch (flags & BYTES_STORED) {
case 0:
min_shifted = (min_value = -128 >> shift) << shift;
max_shifted = (max_value = 127 >> shift) << shift;
break;
case 1:
min_shifted = (min_value = -32768 >> shift) << shift;
max_shifted = (max_value = 32767 >> shift) << shift;
break;
case 2:
min_shifted = (min_value = -8388608 >> shift) << shift;
max_shifted = (max_value = 8388607 >> shift) << shift;
break;
case 3: default: /* "default" suppresses compiler warning */
min_shifted = (min_value = (int32_t) 0x80000000 >> shift) << shift;
max_shifted = (max_value = (int32_t) 0x7fffffff >> shift) << shift;
break;
}
if (!(flags & MONO_DATA))
sample_count *= 2;
while (sample_count--) {
if (*buffer < min_value)
*buffer++ = min_shifted;
else if (*buffer > max_value)
*buffer++ = max_shifted;
else
*buffer++ <<= shift;
}
}
else if (shift) {
if (!(flags & MONO_DATA))
sample_count *= 2;
while (sample_count--)
*buffer++ <<= shift;
}
}
// This function checks the crc value(s) for an unpacked block, returning the
// number of actual crc errors detected for the block. The block must be
// completely unpacked before this test is valid. For losslessly unpacked
// blocks of float or extended integer data the extended crc is also checked.
// Note that WavPack's crc is not a CCITT approved polynomial algorithm, but
// is a much simpler method that is virtually as robust for real world data.
int check_crc_error (WavpackContext *wpc)
{
int result = 0, stream;
for (stream = 0; stream < wpc->num_streams; stream++) {
WavpackStream *wps = wpc->streams [stream];
if (wps->crc != wps->wphdr.crc)
++result;
else if (bs_is_open (&wps->wvxbits) && wps->crc_x != wps->crc_wvx)
++result;
}
return result;
}

File diff suppressed because it is too large Load diff

View file

@ -1,119 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2006 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// wavpack3.h
// This header file contains all the additional definitions required for
// decoding old (versions 1, 2 & 3) WavPack files.
typedef struct {
uint16_t FormatTag, NumChannels;
uint32_t SampleRate, BytesPerSecond;
uint16_t BlockAlign, BitsPerSample;
} WaveHeader3;
#define WaveHeader3Format "SSLLSS"
typedef struct {
char ckID [4];
int32_t ckSize;
int16_t version;
int16_t bits; // added for version 2.00
int16_t flags, shift; // added for version 3.00
int32_t total_samples, crc, crc2;
char extension [4], extra_bc, extras [3];
} WavpackHeader3;
#define WavpackHeader3Format "4LSSSSLLL4L"
// these flags added for version 3
#undef MONO_FLAG // these definitions changed for WavPack 4.0
#undef CROSS_DECORR
#undef JOINT_STEREO
#define MONO_FLAG 1 // not stereo
#define FAST_FLAG 2 // non-adaptive predictor and stereo mode
#define RAW_FLAG 4 // raw mode (no .wav header)
#define CALC_NOISE 8 // calc noise in lossy mode (no longer stored)
#define HIGH_FLAG 0x10 // high quality mode (all modes)
#define BYTES_3 0x20 // files have 3-byte samples
#define OVER_20 0x40 // samples are over 20 bits
#define WVC_FLAG 0x80 // create/use .wvc (no longer stored)
#define LOSSY_SHAPE 0x100 // noise shape (lossy mode only)
#define VERY_FAST_FLAG 0x200 // double fast (no longer stored)
#define NEW_HIGH_FLAG 0x400 // new high quality mode (lossless only)
#define CANCEL_EXTREME 0x800 // cancel EXTREME_DECORR
#define CROSS_DECORR 0x1000 // decorrelate chans (with EXTREME_DECORR flag)
#define NEW_DECORR_FLAG 0x2000 // new high-mode decorrelator
#define JOINT_STEREO 0x4000 // joint stereo (lossy and high lossless)
#define EXTREME_DECORR 0x8000 // extra decorrelation (+ enables other flags)
#define STORED_FLAGS 0xfd77 // these are only flags that affect unpacking
#define NOT_STORED_FLAGS (~STORED_FLAGS & 0xffff)
// BitStream stuff (bits.c)
typedef struct bs3 {
void (*wrap)(struct bs3 *bs);
unsigned char *buf, *end, *ptr;
uint32_t bufsiz, sr;
int64_t fpos;
WavpackStreamReader64 *reader;
int error, bc;
void *id;
} Bitstream3;
#define K_DEPTH 3
#define MAX_NTERMS3 18
typedef struct {
WavpackHeader3 wphdr;
Bitstream3 wvbits, wvcbits;
uint32_t sample_index;
int num_terms;
#ifndef NO_SEEKING
struct index_point {
char saved;
uint32_t sample_index;
} index_points [256];
unsigned char *unpack_data;
uint32_t unpack_size;
#endif
struct {
int32_t sum_level, left_level, right_level, diff_level;
int last_extra_bits, extra_bits_count, m;
int32_t error [2], crc;
int32_t sample [2] [2];
int weight [2] [1];
} dc;
struct decorr_pass decorr_passes [MAX_NTERMS3];
struct {
unsigned int index [2], k_value [2], ave_k [2];
uint32_t zeros_acc, ave_level [K_DEPTH] [2];
} w1;
struct { int last_dbits [2], last_delta_sign [2], bit_limit; } w2;
struct { int ave_dbits [2], bit_limit; } w3;
struct {
uint32_t fast_level [2], slow_level [2];
int bits_acc [2], bitrate;
} w4;
} WavpackStream3;
#define SAVE(destin, item) { memcpy (destin, &item, sizeof (item)); destin = (char *) destin + sizeof (item); }
#define RESTORE(item, source) { memcpy (&item, source, sizeof (item)); source = (char *) source + sizeof (item); }
void unpack_init3 (WavpackStream3 *wps);

View file

@ -1,289 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack3_open.c
// This module provides an extension to the open_utils.c module for handling
// WavPack files prior to version 4.0, not including "raw" files. As these
// modes are all obsolete and are no longer written, this code will not be
// fully documented other than the global functions. However, full documentation
// is provided in the version 3.97 source code. Note that this module only
// provides the functionality of opening the files and obtaining information
// from them; the actual audio decoding is located in the unpack3.c module.
#ifdef ENABLE_LEGACY
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#include "unpack3.h"
#define ATTEMPT_ERROR_MUTING
// This provides an extension to the WavpackOpenFileRead () function contained
// in the wputils.c module. It is assumed that an 'R' had been read as the
// first character of the file/stream (indicating a non-raw pre version 4.0
// WavPack file) and had been pushed back onto the stream (or simply seeked
// back to).
WavpackContext *open_file3 (WavpackContext *wpc, char *error)
{
RiffChunkHeader RiffChunkHeader;
ChunkHeader ChunkHeader;
WavpackHeader3 wphdr;
WavpackStream3 *wps;
WaveHeader3 wavhdr;
CLEAR (wavhdr);
wpc->stream3 = wps = (WavpackStream3 *) malloc (sizeof (WavpackStream3));
CLEAR (*wps);
if (wpc->reader->read_bytes (wpc->wv_in, &RiffChunkHeader, sizeof (RiffChunkHeader)) !=
sizeof (RiffChunkHeader)) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (!strncmp (RiffChunkHeader.ckID, "RIFF", 4) && !strncmp (RiffChunkHeader.formType, "WAVE", 4)) {
if (wpc->open_flags & OPEN_WRAPPER) {
wpc->wrapper_data = (unsigned char *)malloc (wpc->wrapper_bytes = sizeof (RiffChunkHeader));
memcpy (wpc->wrapper_data, &RiffChunkHeader, sizeof (RiffChunkHeader));
}
// If the first chunk is a wave RIFF header, then read the various chunks
// until we get to the "data" chunk (and WavPack header should follow). If
// the first chunk is not a RIFF, then we assume a "raw" WavPack file and
// the WavPack header must be first.
while (1) {
if (wpc->reader->read_bytes (wpc->wv_in, &ChunkHeader, sizeof (ChunkHeader)) !=
sizeof (ChunkHeader)) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
else {
if (wpc->open_flags & OPEN_WRAPPER) {
wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (ChunkHeader));
memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &ChunkHeader, sizeof (ChunkHeader));
wpc->wrapper_bytes += sizeof (ChunkHeader);
}
WavpackLittleEndianToNative (&ChunkHeader, ChunkHeaderFormat);
if (!strncmp (ChunkHeader.ckID, "fmt ", 4)) {
if (ChunkHeader.ckSize < sizeof (wavhdr) ||
wpc->reader->read_bytes (wpc->wv_in, &wavhdr, sizeof (wavhdr)) != sizeof (wavhdr)) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
else if (wpc->open_flags & OPEN_WRAPPER) {
wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (wavhdr));
memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &wavhdr, sizeof (wavhdr));
wpc->wrapper_bytes += sizeof (wavhdr);
}
WavpackLittleEndianToNative (&wavhdr, WaveHeader3Format);
if (ChunkHeader.ckSize > sizeof (wavhdr)) {
uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1 - sizeof (wavhdr)) & ~1L;
if (bytes_to_skip > 1024 * 1024) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (wpc->open_flags & OPEN_WRAPPER) {
wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip);
wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip);
wpc->wrapper_bytes += bytes_to_skip;
}
else {
unsigned char *temp = (unsigned char *)malloc (bytes_to_skip);
wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip);
free (temp);
}
}
}
else if (!strncmp (ChunkHeader.ckID, "data", 4))
break;
else if ((ChunkHeader.ckSize + 1) & ~1L) {
uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1) & ~1L;
if (bytes_to_skip > 1024 * 1024) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (wpc->open_flags & OPEN_WRAPPER) {
wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip);
wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip);
wpc->wrapper_bytes += bytes_to_skip;
}
else {
unsigned char *temp = (unsigned char *)malloc (bytes_to_skip);
wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip);
free (temp);
}
}
}
}
}
else {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (wavhdr.FormatTag != 1 || !wavhdr.NumChannels || wavhdr.NumChannels > 2 ||
!wavhdr.SampleRate || wavhdr.BitsPerSample < 16 || wavhdr.BitsPerSample > 24 ||
wavhdr.BlockAlign / wavhdr.NumChannels > 3 || wavhdr.BlockAlign % wavhdr.NumChannels ||
wavhdr.BlockAlign / wavhdr.NumChannels < (wavhdr.BitsPerSample + 7) / 8) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
wpc->total_samples = ChunkHeader.ckSize / wavhdr.NumChannels /
((wavhdr.BitsPerSample > 16) ? 3 : 2);
if (wpc->reader->read_bytes (wpc->wv_in, &wphdr, 10) != 10) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (((char *) &wphdr) [8] == 2 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10, 2) != 2)) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
else if (((char *) &wphdr) [8] == 3 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10,
sizeof (wphdr) - 10) != sizeof (wphdr) - 10)) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
WavpackLittleEndianToNative (&wphdr, WavpackHeader3Format);
// make sure this is a version we know about
if (strncmp (wphdr.ckID, "wvpk", 4) || wphdr.version < 1 || wphdr.version > 3) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
// Because I ran out of flag bits in the WavPack header, an amazingly ugly
// kludge was forced upon me! This code takes care of preparing the flags
// field for internal use and checking for unknown formats we can't decode
if (wphdr.version == 3) {
if (wphdr.flags & EXTREME_DECORR) {
if ((wphdr.flags & NOT_STORED_FLAGS) ||
((wphdr.bits) &&
(((wphdr.flags & NEW_HIGH_FLAG) &&
(wphdr.flags & (FAST_FLAG | HIGH_FLAG))) ||
(wphdr.flags & CROSS_DECORR)))) {
if (error) strcpy (error, "not a valid WavPack file!");
return WavpackCloseFile (wpc);
}
if (wphdr.flags & CANCEL_EXTREME)
wphdr.flags &= ~(EXTREME_DECORR | CANCEL_EXTREME);
}
else
wphdr.flags &= ~CROSS_DECORR;
}
// check to see if we should look for a "correction" file, and if so try
// to open it for reading, then set WVC_FLAG accordingly
if (wpc->wvc_in && wphdr.version == 3 && wphdr.bits && (wphdr.flags & NEW_HIGH_FLAG)) {
wpc->file2len = wpc->reader->get_length (wpc->wvc_in);
wphdr.flags |= WVC_FLAG;
wpc->wvc_flag = TRUE;
}
else
wphdr.flags &= ~WVC_FLAG;
// check WavPack version to handle special requirements of versions
// before 3.0 that had smaller headers
if (wphdr.version < 3) {
wphdr.total_samples = (int32_t) wpc->total_samples;
wphdr.flags = wavhdr.NumChannels == 1 ? MONO_FLAG : 0;
wphdr.shift = 16 - wavhdr.BitsPerSample;
if (wphdr.version == 1)
wphdr.bits = 0;
}
wpc->config.sample_rate = wavhdr.SampleRate;
wpc->config.num_channels = wavhdr.NumChannels;
wpc->config.channel_mask = 5 - wavhdr.NumChannels;
if (wphdr.flags & MONO_FLAG)
wpc->config.flags |= CONFIG_MONO_FLAG;
if (wphdr.flags & EXTREME_DECORR)
wpc->config.flags |= CONFIG_HIGH_FLAG;
if (wphdr.bits) {
if (wphdr.flags & NEW_HIGH_FLAG)
wpc->config.flags |= CONFIG_HYBRID_FLAG;
else
wpc->config.flags |= CONFIG_LOSSY_MODE;
}
else if (!(wphdr.flags & HIGH_FLAG))
wpc->config.flags |= CONFIG_FAST_FLAG;
wpc->config.bytes_per_sample = (wphdr.flags & BYTES_3) ? 3 : 2;
wpc->config.bits_per_sample = wavhdr.BitsPerSample;
memcpy (&wps->wphdr, &wphdr, sizeof (wphdr));
wps->wvbits.bufsiz = wps->wvcbits.bufsiz = 1024 * 1024;
return wpc;
}
// return currently decoded sample index
uint32_t get_sample_index3 (WavpackContext *wpc)
{
WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
return (wps) ? wps->sample_index : (uint32_t) -1;
}
int get_version3 (WavpackContext *wpc)
{
WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
return (wps) ? wps->wphdr.version : 0;
}
void free_stream3 (WavpackContext *wpc)
{
WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
if (wps) {
#ifndef NO_SEEKING
if (wps->unpack_data)
free (wps->unpack_data);
#endif
if ((wps->wphdr.flags & WVC_FLAG) && wps->wvcbits.buf)
free (wps->wvcbits.buf);
if (wps->wvbits.buf)
free (wps->wvbits.buf);
free (wps);
}
}
#endif // ENABLE_LEGACY

View file

@ -1,212 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack3_seek.c
// This module provides seeking support for WavPack files prior to version 4.0.
#ifdef ENABLE_LEGACY
#ifndef NO_SEEKING
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
#include "unpack3.h"
static void *unpack_restore (WavpackStream3 *wps, void *source, int keep_resources);
static void bs_restore3 (Bitstream3 *bs);
// This is an extension for WavpackSeekSample (). Note that because WavPack
// files created prior to version 4.0 are not inherently seekable, this
// function could take a long time if a forward seek is requested to an
// area that has not been played (or seeked through) yet.
int seek_sample3 (WavpackContext *wpc, uint32_t desired_index)
{
int points_index = desired_index / (((uint32_t) wpc->total_samples >> 8) + 1);
WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
if (desired_index >= wpc->total_samples)
return FALSE;
while (points_index)
if (wps->index_points [points_index].saved &&
wps->index_points [points_index].sample_index <= desired_index)
break;
else
points_index--;
if (wps->index_points [points_index].saved)
if (wps->index_points [points_index].sample_index > wps->sample_index ||
wps->sample_index > desired_index) {
wps->sample_index = wps->index_points [points_index].sample_index;
unpack_restore (wps, wps->unpack_data + points_index * wps->unpack_size, TRUE);
}
if (desired_index > wps->sample_index) {
int32_t *buffer = (int32_t *) malloc (1024 * (wps->wphdr.flags & MONO_FLAG ? 4 : 8));
uint32_t samples_to_skip = desired_index - wps->sample_index;
while (1) {
if (samples_to_skip > 1024) {
if (unpack_samples3 (wpc, buffer, 1024) == 1024)
samples_to_skip -= 1024;
else
break;
}
else {
samples_to_skip -= unpack_samples3 (wpc, buffer, samples_to_skip);
break;
}
}
free (buffer);
if (samples_to_skip)
return FALSE;
}
return TRUE;
}
// This function restores the unpacking context from the specified pointer
// and returns the updated pointer. After this call, unpack_samples() will
// continue where it left off immediately before unpack_save() was called.
// If the WavPack files and bitstreams might have been closed and reopened,
// then the "keep_resources" flag should be set to avoid using the "old"
// resources that were originally saved (and are probably now invalid).
static void *unpack_restore (WavpackStream3 *wps, void *source, int keep_resources)
{
int flags = wps->wphdr.flags, tcount;
struct decorr_pass *dpp;
FILE *temp_file;
unsigned char *temp_buf;
unpack_init3 (wps);
temp_file = wps->wvbits.id;
temp_buf = wps->wvbits.buf;
RESTORE (wps->wvbits, source);
if (keep_resources) {
wps->wvbits.id = temp_file;
wps->wvbits.ptr += temp_buf - wps->wvbits.buf;
wps->wvbits.end += temp_buf - wps->wvbits.buf;
wps->wvbits.buf = temp_buf;
}
bs_restore3 (&wps->wvbits);
if (flags & WVC_FLAG) {
temp_file = wps->wvcbits.id;
temp_buf = wps->wvcbits.buf;
RESTORE (wps->wvcbits, source);
if (keep_resources) {
wps->wvcbits.id = temp_file;
wps->wvcbits.ptr += temp_buf - wps->wvcbits.buf;
wps->wvcbits.end += temp_buf - wps->wvcbits.buf;
wps->wvcbits.buf = temp_buf;
}
bs_restore3 (&wps->wvcbits);
}
if (wps->wphdr.version == 3) {
if (wps->wphdr.bits) {
RESTORE (wps->w4, source);
}
else {
RESTORE (wps->w1, source);
}
RESTORE (wps->w3, source);
RESTORE (wps->dc.crc, source);
}
else
RESTORE (wps->w2, source);
if (wps->wphdr.bits) {
RESTORE (wps->dc.error, source);
}
else {
RESTORE (wps->dc.sum_level, source);
RESTORE (wps->dc.left_level, source);
RESTORE (wps->dc.right_level, source);
RESTORE (wps->dc.diff_level, source);
}
if (flags & OVER_20) {
RESTORE (wps->dc.last_extra_bits, source);
RESTORE (wps->dc.extra_bits_count, source);
}
if (!(flags & EXTREME_DECORR)) {
RESTORE (wps->dc.sample, source);
RESTORE (wps->dc.weight, source);
}
if (flags & (HIGH_FLAG | NEW_HIGH_FLAG))
for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) {
if (dpp->term > 0) {
int count = dpp->term;
int index = wps->dc.m;
RESTORE (dpp->weight_A, source);
while (count--) {
RESTORE (dpp->samples_A [index], source);
index = (index + 1) & (MAX_TERM - 1);
}
if (!(flags & MONO_FLAG)) {
count = dpp->term;
index = wps->dc.m;
RESTORE (dpp->weight_B, source);
while (count--) {
RESTORE (dpp->samples_B [index], source);
index = (index + 1) & (MAX_TERM - 1);
}
}
}
else {
RESTORE (dpp->weight_A, source);
RESTORE (dpp->weight_B, source);
RESTORE (dpp->samples_A [0], source);
RESTORE (dpp->samples_B [0], source);
}
}
return source;
}
// This function is called after a call to unpack_restore() has restored
// the BitStream structure to a previous state and causes any required data
// to be read from the file. This function is NOT supported for overlapped
// operation.
static void bs_restore3 (Bitstream3 *bs)
{
uint32_t bytes_to_read = (uint32_t)(bs->end - bs->ptr - 1), bytes_read;
bs->reader->set_pos_abs (bs->id, bs->fpos - bytes_to_read);
if (bytes_to_read > 0) {
bytes_read = bs->reader->read_bytes (bs->id, bs->ptr + 1, bytes_to_read);
if (bytes_to_read != bytes_read)
bs->end = bs->ptr + 1 + bytes_read;
}
}
#endif // NO_SEEKING
#endif // ENABLE_LEGACY

View file

@ -1,620 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** DSDPACK **** //
// Lossless DSD (Direct Stream Digital) Audio Compressor //
// Copyright (c) 2013 - 2016 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack_dsd.c
// This module actually handles the uncompression of the DSD audio data.
#ifdef ENABLE_DSD
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
// This function initializes the main range-encoded data for DSD audio samples
static int init_dsd_block_fast (WavpackStream *wps, WavpackMetadata *wpmd);
static int init_dsd_block_high (WavpackStream *wps, WavpackMetadata *wpmd);
static int decode_fast (WavpackStream *wps, int32_t *output, int sample_count);
static int decode_high (WavpackStream *wps, int32_t *output, int sample_count);
int init_dsd_block (WavpackContext *wpc, WavpackMetadata *wpmd)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
if (wpmd->byte_length < 2)
return FALSE;
wps->dsd.byteptr = (unsigned char *)wpmd->data;
wps->dsd.endptr = wps->dsd.byteptr + wpmd->byte_length;
wpc->dsd_multiplier = 1 << *wps->dsd.byteptr++;
wps->dsd.mode = *wps->dsd.byteptr++;
if (!wps->dsd.mode) {
if (wps->dsd.endptr - wps->dsd.byteptr != wps->wphdr.block_samples * (wps->wphdr.flags & MONO_DATA ? 1 : 2)) {
return FALSE;
}
wps->dsd.ready = 1;
return TRUE;
}
if (wps->dsd.mode == 1)
return init_dsd_block_fast (wps, wpmd);
else if (wps->dsd.mode == 3)
return init_dsd_block_high (wps, wpmd);
else
return FALSE;
}
int32_t unpack_dsd_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
uint32_t flags = wps->wphdr.flags;
// don't attempt to decode past the end of the block, but watch out for overflow!
if (wps->sample_index + sample_count > GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples &&
(uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index) < sample_count)
sample_count = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index);
if (GET_BLOCK_INDEX (wps->wphdr) > wps->sample_index || wps->wphdr.block_samples < sample_count)
wps->mute_error = TRUE;
if (!wps->mute_error) {
if (!wps->dsd.mode) {
int total_samples = sample_count * ((flags & MONO_DATA) ? 1 : 2);
int32_t *bptr = buffer;
if (wps->dsd.endptr - wps->dsd.byteptr < total_samples)
total_samples = (int)(wps->dsd.endptr - wps->dsd.byteptr);
while (total_samples--)
wps->crc += (wps->crc << 1) + (*bptr++ = *wps->dsd.byteptr++);
}
else if (wps->dsd.mode == 1) {
if (!decode_fast (wps, buffer, sample_count))
wps->mute_error = TRUE;
}
else if (!decode_high (wps, buffer, sample_count))
wps->mute_error = TRUE;
}
if (wps->mute_error) {
int samples_to_null;
if (wpc->reduced_channels == 1 || wpc->config.num_channels == 1 || (flags & MONO_FLAG))
samples_to_null = sample_count;
else
samples_to_null = sample_count * 2;
while (samples_to_null--)
*buffer++ = 0x55;
wps->sample_index += sample_count;
return sample_count;
}
if (flags & FALSE_STEREO) {
int32_t *dptr = buffer + sample_count * 2;
int32_t *sptr = buffer + sample_count;
int32_t c = sample_count;
while (c--) {
*--dptr = *--sptr;
*--dptr = *sptr;
}
}
wps->sample_index += sample_count;
return sample_count;
}
/*------------------------------------------------------------------------------------------------------------------------*/
// #define DSD_BYTE_READY(low,high) (((low) >> 24) == ((high) >> 24))
// #define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) >> 24))
#define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) & 0xff000000))
static int init_dsd_block_fast (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char history_bits, max_probability, *lb_ptr;
int total_summed_probabilities = 0, bi, i;
if (wps->dsd.byteptr == wps->dsd.endptr)
return FALSE;
history_bits = *wps->dsd.byteptr++;
if (wps->dsd.byteptr == wps->dsd.endptr || history_bits > MAX_HISTORY_BITS)
return FALSE;
wps->dsd.history_bins = 1 << history_bits;
free_dsd_tables (wps);
lb_ptr = wps->dsd.lookup_buffer = (unsigned char *)malloc (wps->dsd.history_bins * MAX_BYTES_PER_BIN);
wps->dsd.value_lookup = (unsigned char **)malloc (sizeof (*wps->dsd.value_lookup) * wps->dsd.history_bins);
memset (wps->dsd.value_lookup, 0, sizeof (*wps->dsd.value_lookup) * wps->dsd.history_bins);
wps->dsd.summed_probabilities = (int16_t (*)[256])malloc (sizeof (*wps->dsd.summed_probabilities) * wps->dsd.history_bins);
wps->dsd.probabilities = (unsigned char (*)[256])malloc (sizeof (*wps->dsd.probabilities) * wps->dsd.history_bins);
max_probability = *wps->dsd.byteptr++;
if (max_probability < 0xff) {
unsigned char *outptr = (unsigned char *) wps->dsd.probabilities;
unsigned char *outend = outptr + sizeof (*wps->dsd.probabilities) * wps->dsd.history_bins;
while (outptr < outend && wps->dsd.byteptr < wps->dsd.endptr) {
int code = *wps->dsd.byteptr++;
if (code > max_probability) {
int zcount = code - max_probability;
while (outptr < outend && zcount--)
*outptr++ = 0;
}
else if (code)
*outptr++ = code;
else
break;
}
if (outptr < outend || (wps->dsd.byteptr < wps->dsd.endptr && *wps->dsd.byteptr++))
return FALSE;
}
else if (wps->dsd.endptr - wps->dsd.byteptr > (int) sizeof (*wps->dsd.probabilities) * wps->dsd.history_bins) {
memcpy (wps->dsd.probabilities, wps->dsd.byteptr, sizeof (*wps->dsd.probabilities) * wps->dsd.history_bins);
wps->dsd.byteptr += sizeof (*wps->dsd.probabilities) * wps->dsd.history_bins;
}
else
return FALSE;
for (bi = 0; bi < wps->dsd.history_bins; ++bi) {
int32_t sum_values;
for (sum_values = i = 0; i < 256; ++i)
wps->dsd.summed_probabilities [bi] [i] = sum_values += wps->dsd.probabilities [bi] [i];
if (sum_values) {
if ((total_summed_probabilities += sum_values) > wps->dsd.history_bins * MAX_BYTES_PER_BIN)
return FALSE;
wps->dsd.value_lookup [bi] = lb_ptr;
for (i = 0; i < 256; i++) {
int c = wps->dsd.probabilities [bi] [i];
while (c--)
*lb_ptr++ = i;
}
}
}
if (wps->dsd.endptr - wps->dsd.byteptr < 4 || total_summed_probabilities > wps->dsd.history_bins * MAX_BYTES_PER_BIN)
return FALSE;
for (i = 4; i--;)
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.p0 = wps->dsd.p1 = 0;
wps->dsd.low = 0; wps->dsd.high = 0xffffffff;
wps->dsd.ready = 1;
return TRUE;
}
static int decode_fast (WavpackStream *wps, int32_t *output, int sample_count)
{
int total_samples = sample_count;
if (!(wps->wphdr.flags & MONO_DATA))
total_samples *= 2;
while (total_samples--) {
int mult, index, code, i;
if (!wps->dsd.summed_probabilities [wps->dsd.p0] [255])
return 0;
mult = (wps->dsd.high - wps->dsd.low) / wps->dsd.summed_probabilities [wps->dsd.p0] [255];
if (!mult) {
if (wps->dsd.endptr - wps->dsd.byteptr >= 4)
for (i = 4; i--;)
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.low = 0;
wps->dsd.high = 0xffffffff;
mult = wps->dsd.high / wps->dsd.summed_probabilities [wps->dsd.p0] [255];
if (!mult)
return 0;
}
index = (wps->dsd.value - wps->dsd.low) / mult;
if (index >= wps->dsd.summed_probabilities [wps->dsd.p0] [255])
return 0;
if ((*output++ = code = wps->dsd.value_lookup [wps->dsd.p0] [index]))
wps->dsd.low += wps->dsd.summed_probabilities [wps->dsd.p0] [code-1] * mult;
wps->dsd.high = wps->dsd.low + wps->dsd.probabilities [wps->dsd.p0] [code] * mult - 1;
wps->crc += (wps->crc << 1) + code;
if (wps->wphdr.flags & MONO_DATA)
wps->dsd.p0 = code & (wps->dsd.history_bins-1);
else {
wps->dsd.p0 = wps->dsd.p1;
wps->dsd.p1 = code & (wps->dsd.history_bins-1);
}
while (DSD_BYTE_READY (wps->dsd.high, wps->dsd.low) && wps->dsd.byteptr < wps->dsd.endptr) {
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.high = (wps->dsd.high << 8) | 0xff;
wps->dsd.low <<= 8;
}
}
return sample_count;
}
/*------------------------------------------------------------------------------------------------------------------------*/
#define PTABLE_BITS 8
#define PTABLE_BINS (1<<PTABLE_BITS)
#define PTABLE_MASK (PTABLE_BINS-1)
#define UP 0x010000fe
#define DOWN 0x00010000
#define DECAY 8
#define PRECISION 20
#define VALUE_ONE (1 << PRECISION)
#define PRECISION_USE 12
#define RATE_S 20
static void init_ptable (int *table, int rate_i, int rate_s)
{
int value = 0x808000, rate = rate_i << 8, c, i;
for (c = (rate + 128) >> 8; c--;)
value += (DOWN - value) >> DECAY;
for (i = 0; i < PTABLE_BINS/2; ++i) {
table [i] = value;
table [PTABLE_BINS-1-i] = 0x100ffff - value;
if (value > 0x010000) {
rate += (rate * rate_s + 128) >> 8;
for (c = (rate + 64) >> 7; c--;)
value += (DOWN - value) >> DECAY;
}
}
}
static int init_dsd_block_high (WavpackStream *wps, WavpackMetadata *wpmd)
{
uint32_t flags = wps->wphdr.flags;
int channel, rate_i, rate_s, i;
if (wps->dsd.endptr - wps->dsd.byteptr < ((flags & MONO_DATA) ? 13 : 20))
return FALSE;
rate_i = *wps->dsd.byteptr++;
rate_s = *wps->dsd.byteptr++;
if (rate_s != RATE_S)
return FALSE;
if (!wps->dsd.ptable)
wps->dsd.ptable = (int32_t *)malloc (PTABLE_BINS * sizeof (*wps->dsd.ptable));
init_ptable (wps->dsd.ptable, rate_i, rate_s);
for (channel = 0; channel < ((flags & MONO_DATA) ? 1 : 2); ++channel) {
DSDfilters *sp = wps->dsd.filters + channel;
sp->filter1 = *wps->dsd.byteptr++ << (PRECISION - 8);
sp->filter2 = *wps->dsd.byteptr++ << (PRECISION - 8);
sp->filter3 = *wps->dsd.byteptr++ << (PRECISION - 8);
sp->filter4 = *wps->dsd.byteptr++ << (PRECISION - 8);
sp->filter5 = *wps->dsd.byteptr++ << (PRECISION - 8);
sp->filter6 = 0;
sp->factor = *wps->dsd.byteptr++ & 0xff;
sp->factor |= (*wps->dsd.byteptr++ << 8) & 0xff00;
sp->factor = (sp->factor << 16) >> 16;
}
wps->dsd.high = 0xffffffff;
wps->dsd.low = 0x0;
for (i = 4; i--;)
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.ready = 1;
return TRUE;
}
static int decode_high (WavpackStream *wps, int32_t *output, int sample_count)
{
int total_samples = sample_count, stereo = (wps->wphdr.flags & MONO_DATA) ? 0 : 1;
DSDfilters *sp = wps->dsd.filters;
while (total_samples--) {
int bitcount = 8;
sp [0].value = sp [0].filter1 - sp [0].filter5 + ((sp [0].filter6 * sp [0].factor) >> 2);
if (stereo)
sp [1].value = sp [1].filter1 - sp [1].filter5 + ((sp [1].filter6 * sp [1].factor) >> 2);
while (bitcount--) {
int32_t *pp = wps->dsd.ptable + ((sp [0].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK);
uint32_t split = wps->dsd.low + ((wps->dsd.high - wps->dsd.low) >> 8) * (*pp >> 16);
if (wps->dsd.value <= split) {
wps->dsd.high = split;
*pp += (UP - *pp) >> DECAY;
sp [0].filter0 = -1;
}
else {
wps->dsd.low = split + 1;
*pp += (DOWN - *pp) >> DECAY;
sp [0].filter0 = 0;
}
while (DSD_BYTE_READY (wps->dsd.high, wps->dsd.low) && wps->dsd.byteptr < wps->dsd.endptr) {
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.high = (wps->dsd.high << 8) | 0xff;
wps->dsd.low <<= 8;
}
sp [0].value += sp [0].filter6 << 3;
sp [0].byte = (sp [0].byte << 1) | (sp [0].filter0 & 1);
sp [0].factor += (((sp [0].value ^ sp [0].filter0) >> 31) | 1) & ((sp [0].value ^ (sp [0].value - (sp [0].filter6 << 4))) >> 31);
sp [0].filter1 += ((sp [0].filter0 & VALUE_ONE) - sp [0].filter1) >> 6;
sp [0].filter2 += ((sp [0].filter0 & VALUE_ONE) - sp [0].filter2) >> 4;
sp [0].filter3 += (sp [0].filter2 - sp [0].filter3) >> 4;
sp [0].filter4 += (sp [0].filter3 - sp [0].filter4) >> 4;
sp [0].value = (sp [0].filter4 - sp [0].filter5) >> 4;
sp [0].filter5 += sp [0].value;
sp [0].filter6 += (sp [0].value - sp [0].filter6) >> 3;
sp [0].value = sp [0].filter1 - sp [0].filter5 + ((sp [0].filter6 * sp [0].factor) >> 2);
if (!stereo)
continue;
pp = wps->dsd.ptable + ((sp [1].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK);
split = wps->dsd.low + ((wps->dsd.high - wps->dsd.low) >> 8) * (*pp >> 16);
if (wps->dsd.value <= split) {
wps->dsd.high = split;
*pp += (UP - *pp) >> DECAY;
sp [1].filter0 = -1;
}
else {
wps->dsd.low = split + 1;
*pp += (DOWN - *pp) >> DECAY;
sp [1].filter0 = 0;
}
while (DSD_BYTE_READY (wps->dsd.high, wps->dsd.low) && wps->dsd.byteptr < wps->dsd.endptr) {
wps->dsd.value = (wps->dsd.value << 8) | *wps->dsd.byteptr++;
wps->dsd.high = (wps->dsd.high << 8) | 0xff;
wps->dsd.low <<= 8;
}
sp [1].value += sp [1].filter6 << 3;
sp [1].byte = (sp [1].byte << 1) | (sp [1].filter0 & 1);
sp [1].factor += (((sp [1].value ^ sp [1].filter0) >> 31) | 1) & ((sp [1].value ^ (sp [1].value - (sp [1].filter6 << 4))) >> 31);
sp [1].filter1 += ((sp [1].filter0 & VALUE_ONE) - sp [1].filter1) >> 6;
sp [1].filter2 += ((sp [1].filter0 & VALUE_ONE) - sp [1].filter2) >> 4;
sp [1].filter3 += (sp [1].filter2 - sp [1].filter3) >> 4;
sp [1].filter4 += (sp [1].filter3 - sp [1].filter4) >> 4;
sp [1].value = (sp [1].filter4 - sp [1].filter5) >> 4;
sp [1].filter5 += sp [1].value;
sp [1].filter6 += (sp [1].value - sp [1].filter6) >> 3;
sp [1].value = sp [1].filter1 - sp [1].filter5 + ((sp [1].filter6 * sp [1].factor) >> 2);
}
wps->crc += (wps->crc << 1) + (*output++ = sp [0].byte & 0xff);
sp [0].factor -= (sp [0].factor + 512) >> 10;
if (stereo) {
wps->crc += (wps->crc << 1) + (*output++ = wps->dsd.filters [1].byte & 0xff);
wps->dsd.filters [1].factor -= (wps->dsd.filters [1].factor + 512) >> 10;
}
}
return sample_count;
}
/*------------------------------------------------------------------------------------------------------------------------*/
#if 0
// 80 term DSD decimation filter
// < 1 dB down at 20 kHz
// > 108 dB stopband attenuation (fs/16)
static const int32_t decm_filter [] = {
4, 17, 56, 147, 336, 693, 1320, 2359,
4003, 6502, 10170, 15392, 22623, 32389, 45275, 61920,
82994, 109174, 141119, 179431, 224621, 277068, 336983, 404373,
479004, 560384, 647741, 740025, 835917, 933849, 1032042, 1128551,
1221329, 1308290, 1387386, 1456680, 1514425, 1559128, 1589610, 1605059,
1605059, 1589610, 1559128, 1514425, 1456680, 1387386, 1308290, 1221329,
1128551, 1032042, 933849, 835917, 740025, 647741, 560384, 479004,
404373, 336983, 277068, 224621, 179431, 141119, 109174, 82994,
61920, 45275, 32389, 22623, 15392, 10170, 6502, 4003,
2359, 1320, 693, 336, 147, 56, 17, 4,
};
#define NUM_FILTER_TERMS 80
#else
// 56 term decimation filter
// < 0.5 dB down at 20 kHz
// > 100 dB stopband attenuation (fs/12)
static const int32_t decm_filter [] = {
4, 17, 56, 147, 336, 692, 1315, 2337,
3926, 6281, 9631, 14216, 20275, 28021, 37619, 49155,
62616, 77870, 94649, 112551, 131049, 149507, 167220, 183448,
197472, 208636, 216402, 220385, 220385, 216402, 208636, 197472,
183448, 167220, 149507, 131049, 112551, 94649, 77870, 62616,
49155, 37619, 28021, 20275, 14216, 9631, 6281, 3926,
2337, 1315, 692, 336, 147, 56, 17, 4,
};
#define NUM_FILTER_TERMS 56
#endif
#define HISTORY_BYTES ((NUM_FILTER_TERMS+7)/8)
typedef struct {
unsigned char delay [HISTORY_BYTES];
} DecimationChannel;
typedef struct {
int32_t conv_tables [HISTORY_BYTES] [256];
DecimationChannel *chans;
int num_channels;
} DecimationContext;
void *decimate_dsd_init (int num_channels)
{
DecimationContext *context = (DecimationContext *)malloc (sizeof (DecimationContext));
double filter_sum = 0, filter_scale;
int skipped_terms, i, j;
if (!context)
return context;
memset (context, 0, sizeof (*context));
context->num_channels = num_channels;
context->chans = (DecimationChannel *)malloc (num_channels * sizeof (DecimationChannel));
if (!context->chans) {
free (context);
return NULL;
}
for (i = 0; i < NUM_FILTER_TERMS; ++i)
filter_sum += decm_filter [i];
filter_scale = ((1 << 23) - 1) / filter_sum * 16.0;
// fprintf (stderr, "convolution, %d terms, %f sum, %f scale\n", NUM_FILTER_TERMS, filter_sum, filter_scale);
for (skipped_terms = i = 0; i < NUM_FILTER_TERMS; ++i) {
int scaled_term = (int) floor (decm_filter [i] * filter_scale + 0.5);
if (scaled_term) {
for (j = 0; j < 256; ++j)
if (j & (0x80 >> (i & 0x7)))
context->conv_tables [i >> 3] [j] += scaled_term;
else
context->conv_tables [i >> 3] [j] -= scaled_term;
}
else
skipped_terms++;
}
// fprintf (stderr, "%d terms skipped\n", skipped_terms);
decimate_dsd_reset (context);
return context;
}
void decimate_dsd_reset (void *decimate_context)
{
DecimationContext *context = (DecimationContext *) decimate_context;
int chan = 0, i;
if (!context)
return;
for (chan = 0; chan < context->num_channels; ++chan)
for (i = 0; i < HISTORY_BYTES; ++i)
context->chans [chan].delay [i] = 0x55;
}
void decimate_dsd_run (void *decimate_context, int32_t *samples, int num_samples)
{
DecimationContext *context = (DecimationContext *) decimate_context;
int chan = 0;
if (!context)
return;
while (num_samples) {
DecimationChannel *sp = context->chans + chan;
int sum = 0;
#if (HISTORY_BYTES == 10)
sum += context->conv_tables [0] [sp->delay [0] = sp->delay [1]];
sum += context->conv_tables [1] [sp->delay [1] = sp->delay [2]];
sum += context->conv_tables [2] [sp->delay [2] = sp->delay [3]];
sum += context->conv_tables [3] [sp->delay [3] = sp->delay [4]];
sum += context->conv_tables [4] [sp->delay [4] = sp->delay [5]];
sum += context->conv_tables [5] [sp->delay [5] = sp->delay [6]];
sum += context->conv_tables [6] [sp->delay [6] = sp->delay [7]];
sum += context->conv_tables [7] [sp->delay [7] = sp->delay [8]];
sum += context->conv_tables [8] [sp->delay [8] = sp->delay [9]];
sum += context->conv_tables [9] [sp->delay [9] = *samples];
#elif (HISTORY_BYTES == 7)
sum += context->conv_tables [0] [sp->delay [0] = sp->delay [1]];
sum += context->conv_tables [1] [sp->delay [1] = sp->delay [2]];
sum += context->conv_tables [2] [sp->delay [2] = sp->delay [3]];
sum += context->conv_tables [3] [sp->delay [3] = sp->delay [4]];
sum += context->conv_tables [4] [sp->delay [4] = sp->delay [5]];
sum += context->conv_tables [5] [sp->delay [5] = sp->delay [6]];
sum += context->conv_tables [6] [sp->delay [6] = *samples];
#else
int i;
for (i = 0; i < HISTORY_BYTES-1; ++i)
sum += context->conv_tables [i] [sp->delay [i] = sp->delay [i+1]];
sum += context->conv_tables [i] [sp->delay [i] = *samples];
#endif
*samples++ = sum >> 4;
if (++chan == context->num_channels) {
num_samples--;
chan = 0;
}
}
}
void decimate_dsd_destroy (void *decimate_context)
{
DecimationContext *context = (DecimationContext *) decimate_context;
if (!context)
return;
if (context->chans)
free (context->chans);
free (context);
}
#endif // ENABLE_DSD

View file

@ -1,134 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack_floats.c
// This module deals with the restoration of floating-point data. Note that no
// floating point math is involved here...the values are only processed with
// the macros that directly access the mantissa, exponent, and sign fields.
// That's why we use the f32 type instead of the built-in float type.
#include <stdlib.h>
#include "wavpack_local.h"
static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values);
void float_values (WavpackStream *wps, int32_t *values, int32_t num_values)
{
uint32_t crc = wps->crc_x;
if (!bs_is_open (&wps->wvxbits)) {
float_values_nowvx (wps, values, num_values);
return;
}
while (num_values--) {
int shift_count = 0, exp = wps->float_max_exp;
f32 outval = 0;
uint32_t temp;
if (*values == 0) {
if (wps->float_flags & FLOAT_ZEROS_SENT) {
if (getbit (&wps->wvxbits)) {
getbits (&temp, 23, &wps->wvxbits);
set_mantissa (outval, temp);
if (exp >= 25) {
getbits (&temp, 8, &wps->wvxbits);
set_exponent (outval, temp);
}
set_sign (outval, getbit (&wps->wvxbits));
}
else if (wps->float_flags & FLOAT_NEG_ZEROS)
set_sign (outval, getbit (&wps->wvxbits));
}
}
else {
*values <<= wps->float_shift;
if (*values < 0) {
*values = -*values;
set_sign (outval, 1);
}
if (*values == 0x1000000) {
if (getbit (&wps->wvxbits)) {
getbits (&temp, 23, &wps->wvxbits);
set_mantissa (outval, temp);
}
set_exponent (outval, 255);
}
else {
if (exp)
while (!(*values & 0x800000) && --exp) {
shift_count++;
*values <<= 1;
}
if (shift_count) {
if ((wps->float_flags & FLOAT_SHIFT_ONES) ||
((wps->float_flags & FLOAT_SHIFT_SAME) && getbit (&wps->wvxbits)))
*values |= ((1 << shift_count) - 1);
else if (wps->float_flags & FLOAT_SHIFT_SENT) {
getbits (&temp, shift_count, &wps->wvxbits);
*values |= temp & ((1 << shift_count) - 1);
}
}
set_mantissa (outval, *values);
set_exponent (outval, exp);
}
}
crc = crc * 27 + get_mantissa (outval) * 9 + get_exponent (outval) * 3 + get_sign (outval);
* (f32 *) values++ = outval;
}
wps->crc_x = crc;
}
static void float_values_nowvx (WavpackStream *wps, int32_t *values, int32_t num_values)
{
while (num_values--) {
int shift_count = 0, exp = wps->float_max_exp;
f32 outval = 0;
if (*values) {
*values <<= wps->float_shift;
if (*values < 0) {
*values = -*values;
set_sign (outval, 1);
}
if (*values >= 0x1000000) {
while (*values & 0xf000000) {
*values >>= 1;
++exp;
}
}
else if (exp) {
while (!(*values & 0x800000) && --exp) {
shift_count++;
*values <<= 1;
}
if (shift_count && (wps->float_flags & FLOAT_SHIFT_ONES))
*values |= ((1 << shift_count) - 1);
}
set_mantissa (outval, *values);
set_exponent (outval, exp);
}
* (f32 *) values++ = outval;
}
}

View file

@ -1,387 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack_seek.c
// This module provides the high-level API for unpacking audio data from
// a specific sample index (i.e., seeking).
#ifndef NO_SEEKING
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample);
// Seek to the specified sample index, returning TRUE on success. Note that
// files generated with version 4.0 or newer will seek almost immediately.
// Older files can take quite long if required to seek through unplayed
// portions of the file, but will create a seek map so that reverse seeks
// (or forward seeks to already scanned areas) will be very fast. After a
// FALSE return the file should not be accessed again (other than to close
// it); this is a fatal error.
int WavpackSeekSample (WavpackContext *wpc, uint32_t sample)
{
return WavpackSeekSample64 (wpc, sample);
}
int WavpackSeekSample64 (WavpackContext *wpc, int64_t sample)
{
WavpackStream *wps = wpc->streams ? wpc->streams [wpc->current_stream = 0] : NULL;
uint32_t bcount, samples_to_skip, samples_to_decode = 0;
int32_t *buffer;
if (wpc->total_samples == -1 || sample >= wpc->total_samples ||
!wpc->reader->can_seek (wpc->wv_in) || (wpc->open_flags & OPEN_STREAMING) ||
(wpc->wvc_flag && !wpc->reader->can_seek (wpc->wvc_in)))
return FALSE;
#ifdef ENABLE_LEGACY
if (wpc->stream3)
return seek_sample3 (wpc, (uint32_t) sample);
#endif
#ifdef ENABLE_DSD
if (wpc->decimation_context) { // the decimation code needs some context to be sample accurate
if (sample < 16) {
samples_to_decode = (uint32_t) sample;
sample = 0;
}
else {
samples_to_decode = 16;
sample -= 16;
}
}
#endif
if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < GET_BLOCK_INDEX (wps->wphdr) ||
sample >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) {
free_streams (wpc);
wpc->filepos = find_sample (wpc, wpc->wv_in, wpc->filepos, sample);
if (wpc->filepos == -1)
return FALSE;
if (wpc->wvc_flag) {
wpc->file2pos = find_sample (wpc, wpc->wvc_in, 0, sample);
if (wpc->file2pos == -1)
return FALSE;
}
}
if (!wps->blockbuff) {
wpc->reader->set_pos_abs (wpc->wv_in, wpc->filepos);
wpc->reader->read_bytes (wpc->wv_in, &wps->wphdr, sizeof (WavpackHeader));
WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat);
if ((wps->wphdr.ckSize & 1) || wps->wphdr.ckSize < 24 || wps->wphdr.ckSize >= 1024 * 1024) {
free_streams (wpc);
return FALSE;
}
wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
wps->wphdr.ckSize - 24) {
free_streams (wpc);
return FALSE;
}
// render corrupt blocks harmless
if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
wps->wphdr.block_samples = 0;
memcpy (wps->blockbuff, &wps->wphdr, 32);
}
SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
wps->init_done = FALSE;
if (wpc->wvc_flag) {
wpc->reader->set_pos_abs (wpc->wvc_in, wpc->file2pos);
wpc->reader->read_bytes (wpc->wvc_in, &wps->wphdr, sizeof (WavpackHeader));
WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat);
if ((wps->wphdr.ckSize & 1) || wps->wphdr.ckSize < 24 || wps->wphdr.ckSize >= 1024 * 1024) {
free_streams (wpc);
return FALSE;
}
wps->block2buff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
wps->wphdr.ckSize - 24) {
free_streams (wpc);
return FALSE;
}
// render corrupt blocks harmless
if (!WavpackVerifySingleBlock (wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
wps->wphdr.block_samples = 0;
memcpy (wps->block2buff, &wps->wphdr, 32);
}
SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
}
if (!wps->init_done && !unpack_init (wpc)) {
free_streams (wpc);
return FALSE;
}
wps->init_done = TRUE;
}
while (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) {
if (++wpc->current_stream == wpc->num_streams) {
if (wpc->num_streams == wpc->max_streams) {
free_streams (wpc);
return FALSE;
}
wpc->streams = (WavpackStream **)realloc (wpc->streams, (wpc->num_streams + 1) * sizeof (wpc->streams [0]));
wps = wpc->streams [wpc->num_streams++] = (WavpackStream *)malloc (sizeof (WavpackStream));
CLEAR (*wps);
bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
if (bcount == (uint32_t) -1) {
free_streams (wpc);
return FALSE;
}
wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
memcpy (wps->blockbuff, &wps->wphdr, 32);
if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
wps->wphdr.ckSize - 24) {
free_streams (wpc);
return FALSE;
}
// render corrupt blocks harmless
if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
wps->wphdr.block_samples = 0;
memcpy (wps->blockbuff, &wps->wphdr, 32);
}
wps->init_done = FALSE;
if (wpc->wvc_flag && !read_wvc_block (wpc)) {
free_streams (wpc);
return FALSE;
}
if (!wps->init_done && !unpack_init (wpc)) {
free_streams (wpc);
return FALSE;
}
wps->init_done = TRUE;
}
else
wps = wpc->streams [wpc->current_stream];
}
if (sample < wps->sample_index) {
for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
if (!unpack_init (wpc))
return FALSE;
else
wpc->streams [wpc->current_stream]->init_done = TRUE;
}
samples_to_skip = (uint32_t) (sample - wps->sample_index);
if (samples_to_skip > 131072) {
free_streams (wpc);
return FALSE;
}
if (samples_to_skip) {
buffer = (int32_t *)malloc (samples_to_skip * 8);
for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
#ifdef ENABLE_DSD
if (wpc->streams [wpc->current_stream]->wphdr.flags & DSD_FLAG)
unpack_dsd_samples (wpc, buffer, samples_to_skip);
else
#endif
unpack_samples (wpc, buffer, samples_to_skip);
free (buffer);
}
wpc->current_stream = 0;
#ifdef ENABLE_DSD
if (wpc->decimation_context)
decimate_dsd_reset (wpc->decimation_context);
if (samples_to_decode) {
buffer = (int32_t *)malloc (samples_to_decode * wpc->config.num_channels * 4);
if (buffer) {
WavpackUnpackSamples (wpc, buffer, samples_to_decode);
free (buffer);
}
}
#endif
return TRUE;
}
// Find a valid WavPack header, searching either from the current file position
// (or from the specified position if not -1) and store it (endian corrected)
// at the specified pointer. The return value is the exact file position of the
// header, although we may have actually read past it. Because this function
// is used for seeking to a specific audio sample, it only considers blocks
// that contain audio samples for the initial stream to be valid.
#define BUFSIZE 4096
static int64_t find_header (WavpackStreamReader64 *reader, void *id, int64_t filepos, WavpackHeader *wphdr)
{
unsigned char *buffer = (unsigned char *)malloc (BUFSIZE), *sp = buffer, *ep = buffer;
if (filepos != (uint32_t) -1 && reader->set_pos_abs (id, filepos)) {
free (buffer);
return -1;
}
while (1) {
int bleft;
if (sp < ep) {
bleft = (int)(ep - sp);
memmove (buffer, sp, bleft);
ep -= (sp - buffer);
sp = buffer;
}
else {
if (sp > ep)
if (reader->set_pos_rel (id, (int32_t)(sp - ep), SEEK_CUR)) {
free (buffer);
return -1;
}
sp = ep = buffer;
bleft = 0;
}
ep += reader->read_bytes (id, ep, BUFSIZE - bleft);
if (ep - sp < 32) {
free (buffer);
return -1;
}
while (sp + 32 <= ep)
if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' &&
!(*++sp & 1) && sp [2] < 16 && !sp [3] && (sp [2] || sp [1] || *sp >= 24) && sp [5] == 4 &&
sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff) && sp [18] < 3 && !sp [19]) {
memcpy (wphdr, sp - 4, sizeof (*wphdr));
WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat);
if (wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) {
free (buffer);
return reader->get_pos (id) - (ep - sp + 4);
}
if (wphdr->ckSize > 1024)
sp += wphdr->ckSize - 1024;
}
}
}
// Find the WavPack block that contains the specified sample. If "header_pos"
// is zero, then no information is assumed except the total number of samples
// in the file and its size in bytes. If "header_pos" is non-zero then we
// assume that it is the file position of the valid header image contained in
// the first stream and we can limit our search to either the portion above
// or below that point. If a .wvc file is being used, then this must be called
// for that file also.
static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample)
{
WavpackStream *wps = wpc->streams [wpc->current_stream];
int64_t file_pos1 = 0, file_pos2 = wpc->reader->get_length (infile);
int64_t sample_pos1 = 0, sample_pos2 = wpc->total_samples;
double ratio = 0.96;
int file_skip = 0;
if (sample >= wpc->total_samples)
return -1;
if (header_pos && wps->wphdr.block_samples) {
if (GET_BLOCK_INDEX (wps->wphdr) > sample) {
sample_pos2 = GET_BLOCK_INDEX (wps->wphdr);
file_pos2 = header_pos;
}
else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) {
sample_pos1 = GET_BLOCK_INDEX (wps->wphdr);
file_pos1 = header_pos;
}
else
return header_pos;
}
while (1) {
double bytes_per_sample;
int64_t seek_pos;
bytes_per_sample = (double) file_pos2 - file_pos1;
bytes_per_sample /= sample_pos2 - sample_pos1;
seek_pos = file_pos1 + (file_skip ? 32 : 0);
seek_pos += (int64_t)(bytes_per_sample * (sample - sample_pos1) * ratio);
seek_pos = find_header (wpc->reader, infile, seek_pos, &wps->wphdr);
if (seek_pos != (int64_t) -1)
SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
if (seek_pos == (int64_t) -1 || seek_pos >= file_pos2) {
if (ratio > 0.0) {
if ((ratio -= 0.24) < 0.0)
ratio = 0.0;
}
else
return -1;
}
else if (GET_BLOCK_INDEX (wps->wphdr) > sample) {
sample_pos2 = GET_BLOCK_INDEX (wps->wphdr);
file_pos2 = seek_pos;
}
else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) {
if (seek_pos == file_pos1)
file_skip = 1;
else {
sample_pos1 = GET_BLOCK_INDEX (wps->wphdr);
file_pos1 = seek_pos;
}
}
else
return seek_pos;
}
}
#endif

View file

@ -1,411 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// unpack_utils.c
// This module provides the high-level API for unpacking audio data from
// WavPack files. It manages the buffers used to interleave the data passed
// back to the application from the individual streams. The actual audio
// stream decompression is handled in the unpack.c module.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
// Unpack the specified number of samples from the current file position.
// Note that "samples" here refers to "complete" samples, which would be
// 2 longs for stereo files or even more for multichannel files, so the
// required memory at "buffer" is 4 * samples * num_channels bytes. The
// audio data is returned right-justified in 32-bit longs in the endian
// mode native to the executing processor. So, if the original data was
// 16-bit, then the values returned would be +/-32k. Floating point data
// can also be returned if the source was floating point data (and this
// can be optionally normalized to +/-1.0 by using the appropriate flag
// in the call to WavpackOpenFileInput ()). The actual number of samples
// unpacked is returned, which should be equal to the number requested unless
// the end of fle is encountered or an error occurs. After all samples have
// been unpacked then 0 will be returned.
uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples)
{
WavpackStream *wps = wpc->streams ? wpc->streams [wpc->current_stream = 0] : NULL;
int num_channels = wpc->config.num_channels, file_done = FALSE;
uint32_t bcount, samples_unpacked = 0, samples_to_unpack;
int32_t *bptr = buffer;
#ifdef ENABLE_LEGACY
if (wpc->stream3)
return unpack_samples3 (wpc, buffer, samples);
#endif
while (samples) {
// if the current block has no audio, or it's not the first block of a multichannel
// sequence, or the sample we're on is past the last sample in this block...we need
// to free up the streams and read the next block
if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
wps->sample_index >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) {
int64_t nexthdrpos;
if (wpc->wrapper_bytes >= MAX_WRAPPER_BYTES)
break;
free_streams (wpc);
nexthdrpos = wpc->reader->get_pos (wpc->wv_in);
bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
if (bcount == (uint32_t) -1)
break;
wpc->filepos = nexthdrpos + bcount;
// allocate the memory for the entire raw block and read it in
wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
if (!wps->blockbuff)
break;
memcpy (wps->blockbuff, &wps->wphdr, 32);
if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
wps->wphdr.ckSize - 24) {
strcpy (wpc->error_message, "can't read all of last block!");
wps->wphdr.block_samples = 0;
wps->wphdr.ckSize = 24;
break;
}
// render corrupt blocks harmless
if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
wps->wphdr.block_samples = 0;
memcpy (wps->blockbuff, &wps->wphdr, 32);
}
// potentially adjusting block_index must be done AFTER verifying block
if (wpc->open_flags & OPEN_STREAMING)
SET_BLOCK_INDEX (wps->wphdr, wps->sample_index = 0);
else
SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
memcpy (wps->blockbuff, &wps->wphdr, 32);
wps->init_done = FALSE; // we have not yet called unpack_init() for this block
// if this block has audio, but not the sample index we were expecting, flag an error
if (wps->wphdr.block_samples && wps->sample_index != GET_BLOCK_INDEX (wps->wphdr))
wpc->crc_errors++;
// if this block has audio, and we're in hybrid lossless mode, read the matching wvc block
if (wps->wphdr.block_samples && wpc->wvc_flag)
read_wvc_block (wpc);
// if the block does NOT have any audio, call unpack_init() to process non-audio stuff
if (!wps->wphdr.block_samples) {
if (!wps->init_done && !unpack_init (wpc))
wpc->crc_errors++;
wps->init_done = TRUE;
}
}
// if the current block has no audio, or it's not the first block of a multichannel
// sequence, or the sample we're on is past the last sample in this block...we need
// to loop back and read the next block
if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
wps->sample_index >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples)
continue;
// There seems to be some missing data, like a block was corrupted or something.
// If it's not too much data, just fill in with silence here and loop back.
if (wps->sample_index < GET_BLOCK_INDEX (wps->wphdr)) {
int32_t zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0;
samples_to_unpack = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) - wps->sample_index);
if (!samples_to_unpack || samples_to_unpack > 262144) {
strcpy (wpc->error_message, "discontinuity found, aborting file!");
wps->wphdr.block_samples = 0;
wps->wphdr.ckSize = 24;
break;
}
if (samples_to_unpack > samples)
samples_to_unpack = samples;
wps->sample_index += samples_to_unpack;
samples_unpacked += samples_to_unpack;
samples -= samples_to_unpack;
samples_to_unpack *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels);
while (samples_to_unpack--)
*bptr++ = zvalue;
continue;
}
// calculate number of samples to process from this block, then initialize the decoder for
// this block if we haven't already
samples_to_unpack = (uint32_t) (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples - wps->sample_index);
if (samples_to_unpack > samples)
samples_to_unpack = samples;
if (!wps->init_done && !unpack_init (wpc))
wpc->crc_errors++;
wps->init_done = TRUE;
// if this block is not the final block of a multichannel sequence (and we're not truncating
// to stereo), then enter this conditional block...otherwise we just unpack the samples directly
if (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) {
int32_t *temp_buffer = (int32_t *)malloc (samples_to_unpack * 8), *src, *dst;
int offset = 0; // offset to next channel in sequence (0 to num_channels - 1)
uint32_t samcnt;
// since we are getting samples from multiple bocks in a multichannel sequence, we must
// allocate a temporary buffer to unpack to so that we can re-interleave the samples
if (!temp_buffer)
break;
// loop through all the streams...
while (1) {
// if the stream has not been allocated and corresponding block read, do that here...
if (wpc->current_stream == wpc->num_streams) {
wpc->streams = (WavpackStream **)realloc (wpc->streams, (wpc->num_streams + 1) * sizeof (wpc->streams [0]));
if (!wpc->streams)
break;
wps = wpc->streams [wpc->num_streams++] = (WavpackStream *)malloc (sizeof (WavpackStream));
if (!wps)
break;
CLEAR (*wps);
bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
if (bcount == (uint32_t) -1) {
wpc->streams [0]->wphdr.block_samples = 0;
wpc->streams [0]->wphdr.ckSize = 24;
file_done = TRUE;
break;
}
wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
if (!wps->blockbuff)
break;
memcpy (wps->blockbuff, &wps->wphdr, 32);
if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
wps->wphdr.ckSize - 24) {
wpc->streams [0]->wphdr.block_samples = 0;
wpc->streams [0]->wphdr.ckSize = 24;
file_done = TRUE;
break;
}
// render corrupt blocks harmless
if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
wps->wphdr.block_samples = 0;
memcpy (wps->blockbuff, &wps->wphdr, 32);
}
// potentially adjusting block_index must be done AFTER verifying block
if (wpc->open_flags & OPEN_STREAMING)
SET_BLOCK_INDEX (wps->wphdr, wps->sample_index = 0);
else
SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
memcpy (wps->blockbuff, &wps->wphdr, 32);
// if this block has audio, and we're in hybrid lossless mode, read the matching wvc block
if (wpc->wvc_flag)
read_wvc_block (wpc);
// initialize the unpacker for this block
if (!unpack_init (wpc))
wpc->crc_errors++;
wps->init_done = TRUE;
}
else
wps = wpc->streams [wpc->current_stream];
// unpack the correct number of samples (either mono or stereo) into the temp buffer
#ifdef ENABLE_DSD
if (wps->wphdr.flags & DSD_FLAG)
unpack_dsd_samples (wpc, src = temp_buffer, samples_to_unpack);
else
#endif
unpack_samples (wpc, src = temp_buffer, samples_to_unpack);
samcnt = samples_to_unpack;
dst = bptr + offset;
// if the block is mono, copy the samples from the single channel into the destination
// using num_channels as the stride
if (wps->wphdr.flags & MONO_FLAG) {
while (samcnt--) {
dst [0] = *src++;
dst += num_channels;
}
offset++;
}
// if the block is stereo, and we don't have room for two more channels, just copy one
// and flag an error
else if (offset == num_channels - 1) {
while (samcnt--) {
dst [0] = src [0];
dst += num_channels;
src += 2;
}
wpc->crc_errors++;
offset++;
}
// otherwise copy the stereo samples into the destination
else {
while (samcnt--) {
dst [0] = *src++;
dst [1] = *src++;
dst += num_channels;
}
offset += 2;
}
// check several clues that we're done with this set of blocks and exit if we are; else do next stream
if ((wps->wphdr.flags & FINAL_BLOCK) || wpc->current_stream == wpc->max_streams - 1 || offset == num_channels)
break;
else
wpc->current_stream++;
}
// if we didn't get all the channels we expected, mute the buffer and flag an error
if (offset != num_channels) {
if (wps->wphdr.flags & DSD_FLAG) {
int samples_to_zero = samples_to_unpack * num_channels;
int32_t *zptr = bptr;
while (samples_to_zero--)
*zptr++ = 0x55;
}
else
memset (bptr, 0, samples_to_unpack * num_channels * 4);
wpc->crc_errors++;
}
// go back to the first stream (we're going to leave them all loaded for now because they might have more samples)
// and free the temp buffer
wps = wpc->streams [wpc->current_stream = 0];
free (temp_buffer);
}
// catch the error situation where we have only one channel but run into a stereo block
// (this avoids overwriting the caller's buffer)
else if (!(wps->wphdr.flags & MONO_FLAG) && (num_channels == 1 || wpc->reduced_channels == 1)) {
memset (bptr, 0, samples_to_unpack * sizeof (*bptr));
wps->sample_index += samples_to_unpack;
wpc->crc_errors++;
}
#ifdef ENABLE_DSD
else if (wps->wphdr.flags & DSD_FLAG)
unpack_dsd_samples (wpc, bptr, samples_to_unpack);
#endif
else
unpack_samples (wpc, bptr, samples_to_unpack);
if (file_done) {
strcpy (wpc->error_message, "can't read all of last block!");
break;
}
if (wpc->reduced_channels)
bptr += samples_to_unpack * wpc->reduced_channels;
else
bptr += samples_to_unpack * num_channels;
samples_unpacked += samples_to_unpack;
samples -= samples_to_unpack;
// if we just finished a block, check for a calculated crc error
// (and back up the streams a little if possible in case we passed a header)
if (wps->sample_index == GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) {
if (check_crc_error (wpc)) {
int32_t *zptr = bptr, zvalue = (wps->wphdr.flags & DSD_FLAG) ? 0x55 : 0;
uint32_t samples_to_zero = wps->wphdr.block_samples;
if (samples_to_zero > samples_to_unpack)
samples_to_zero = samples_to_unpack;
samples_to_zero *= (wpc->reduced_channels ? wpc->reduced_channels : num_channels);
while (samples_to_zero--)
*--zptr = zvalue;
if (wps->blockbuff && wpc->reader->can_seek (wpc->wv_in)) {
int32_t rseek = ((WavpackHeader *) wps->blockbuff)->ckSize / 3;
wpc->reader->set_pos_rel (wpc->wv_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR);
}
if (wpc->wvc_flag && wps->block2buff && wpc->reader->can_seek (wpc->wvc_in)) {
int32_t rseek = ((WavpackHeader *) wps->block2buff)->ckSize / 3;
wpc->reader->set_pos_rel (wpc->wvc_in, (rseek > 16384) ? -16384 : -rseek, SEEK_CUR);
}
wpc->crc_errors++;
}
}
if (wpc->total_samples != -1 && wps->sample_index == wpc->total_samples)
break;
}
#ifdef ENABLE_DSD
if (wpc->decimation_context)
decimate_dsd_run (wpc->decimation_context, buffer, samples_unpacked);
#endif
return samples_unpacked;
}

View file

@ -1,770 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2006 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// utils.c
// This module provides general purpose utilities for the WavPack command-line
// utilities and the self-extraction module.
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#include <conio.h>
#include <shlobj.h>
#elif defined(__GNUC__) || defined(__sun)
#include <glob.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include "wavpack.h"
#include "utils.h"
#ifdef _WIN32
#include "win32_unicode_support.h"
#define fprintf fprintf_utf8
#define fputs fputs_utf8
#define remove(f) unlink_utf8(f)
#endif
#ifdef _WIN32
int copy_timestamp (const char *src_filename, const char *dst_filename)
{
wchar_t *src_filename_utf16 = utf8_to_utf16(src_filename);
wchar_t *dst_filename_utf16 = utf8_to_utf16(dst_filename);
FILETIME last_modified;
HANDLE src, dst;
int res = TRUE;
if (*src_filename == '-' || *dst_filename == '-')
return res;
if (!src_filename_utf16 || !dst_filename_utf16)
return FALSE;
src = CreateFileW (src_filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
dst = CreateFileW (dst_filename_utf16, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (src == INVALID_HANDLE_VALUE || dst == INVALID_HANDLE_VALUE ||
!GetFileTime (src, NULL, NULL, &last_modified) ||
!SetFileTime (dst, NULL, NULL, &last_modified))
res = FALSE;
if (src != INVALID_HANDLE_VALUE)
CloseHandle (src);
if (dst != INVALID_HANDLE_VALUE)
CloseHandle (dst);
free (src_filename_utf16);
free (dst_filename_utf16);
return res;
}
#else
#include <sys/time.h>
#include <sys/types.h>
int copy_timestamp(const char *src_filename, const char *dst_filename)
{
struct stat fileinfo;
struct timeval times[2];
if (strcmp(src_filename, "-") == 0 || strcmp(dst_filename, "-") == 0)
return TRUE;
if (stat(src_filename, &fileinfo))
return FALSE; /* stat failed */
times[0].tv_sec = fileinfo.st_atime;
times[0].tv_usec = 0;
times[1].tv_sec = fileinfo.st_mtime;
times[1].tv_usec = 0;
if (utimes(dst_filename, times))
return FALSE; /* utimes failed */
return TRUE;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// This function parses a filename (with or without full path) and returns //
// a pointer to the extension (including the "."). If no extension is found //
// then NULL is returned. Extensions with more than 4 letters don't count. //
//////////////////////////////////////////////////////////////////////////////
#if defined(_WIN32)
char *filespec_ext (char *filespec)
{
char *cp = filespec + strlen (filespec);
while (--cp >= filespec) {
if (*cp == '\\' || *cp == ':')
return NULL;
if (*cp == '.') {
if (strlen (cp+1) && strlen (cp+1) <= 4)
return cp;
else
return NULL;
}
}
return NULL;
}
#else
char *filespec_ext (char *filespec)
{
char *cp = filespec + strlen (filespec);
while (--cp >= filespec) {
if (*cp == '/')
return NULL;
if (*cp == '.') {
if (strlen (cp+1) && strlen (cp+1) <= 4)
return cp;
else
return NULL;
}
}
return NULL;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// This function determines if the specified filespec is a valid pathname. //
// If not, NULL is returned. If it is in the format of a pathname, then the //
// original pointer is returned. If the format is ambiguous, then a lookup //
// is performed to determine if it is in fact a valid path, and if so a "\" //
// is appended so that the pathname can be used and the original pointer is //
// returned. //
//////////////////////////////////////////////////////////////////////////////
#if (defined(__GNUC__) || defined(__sun)) && !defined(_WIN32)
char *filespec_path (char *filespec)
{
char *cp = filespec + strlen (filespec);
glob_t globs;
struct stat fstats;
if (cp == filespec || filespec_wild (filespec))
return NULL;
if (*--cp == '/')
return filespec;
if (*cp == '.' && cp == filespec)
return strcat (filespec, "/");
if (glob (filespec, GLOB_MARK|GLOB_NOSORT, NULL, &globs) == 0 &&
globs.gl_pathc > 0)
{
/* test if the file is a directory */
if (stat(globs.gl_pathv[0], &fstats) == 0 && (fstats.st_mode & S_IFDIR)) {
filespec[0] = '\0';
strcat (filespec, globs.gl_pathv[0]);
globfree(&globs);
return filespec;
}
}
globfree(&globs);
return NULL;
}
#else
char *filespec_path (char *filespec)
{
char *cp = filespec + strlen (filespec);
struct _wfinddata_t wfinddata;
wchar_t *filespec_utf16;
intptr_t file;
if (cp == filespec || filespec_wild (filespec))
return NULL;
--cp;
if (*cp == '\\' || *cp == ':')
return filespec;
if (*cp == '.' && cp == filespec)
return strcat (filespec, "\\");
filespec_utf16 = utf8_to_utf16(filespec);
if (!filespec_utf16)
return NULL;
if ((file = _wfindfirst (filespec_utf16, &wfinddata)) != (intptr_t) -1 &&
(wfinddata.attrib & _A_SUBDIR)) {
_findclose (file);
free (filespec_utf16);
return strcat (filespec, "\\");
}
if (file != -1L)
_findclose(file);
free (filespec_utf16);
return NULL;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// This function returns non-NULL if the specified filename spec has any //
// wildcard characters. //
//////////////////////////////////////////////////////////////////////////////
char *filespec_wild (char *filespec)
{
return strpbrk (filespec, "*?");
}
//////////////////////////////////////////////////////////////////////////////
// This function parses a filename (with or without full path) and returns //
// a pointer to the actual filename, or NULL if no filename can be found. //
//////////////////////////////////////////////////////////////////////////////
#if defined(_WIN32)
char *filespec_name (char *filespec)
{
char *cp = filespec + strlen (filespec);
while (--cp >= filespec) {
if (*cp == '\\' || *cp == ':')
break;
}
if (strlen (cp + 1))
return cp + 1;
else
return NULL;
}
#else
char *filespec_name (char *filespec)
{
char *cp = filespec + strlen (filespec);
while (--cp >= filespec)
if (*cp == '/')
break;
if (strlen (cp + 1))
return cp + 1;
else
return NULL;
}
#endif
//////////////////////////////////////////////////////////////////////////////
// This function allows the user to type 'y', 'n', or 'a' (with Enter) in //
// response to a system query. The return value is the key typed as //
// lowercase (regardless of the typed case). //
//////////////////////////////////////////////////////////////////////////////
static int waiting_input;
char yna (void)
{
char choice = 0;
int key;
waiting_input = 1;
while (1) {
#if defined(_WIN32)
key = _getch ();
#else
key = fgetc(stdin);
#endif
if (key == 3) {
fprintf (stderr, "^C\n");
exit (1);
}
else if (key == EOF) {
fprintf (stderr, "\r\n");
exit (1);
}
else if (key == '\r' || key == '\n') {
if (choice) {
fprintf (stderr, "\r\n");
fflush (stderr);
break;
}
else {
fprintf (stderr, "%c", 7);
fflush (stderr);
}
}
else if (key == 'Y' || key == 'y') {
#ifdef _WIN32
fprintf (stderr, "%c\b", key);
fflush (stderr);
#endif
choice = 'y';
}
else if (key == 'N' || key == 'n') {
#ifdef _WIN32
fprintf (stderr, "%c\b", key);
fflush (stderr);
#endif
choice = 'n';
}
else if (key == 'A' || key == 'a') {
#ifdef _WIN32
fprintf (stderr, "%c\b", key);
fflush (stderr);
#endif
choice = 'a';
}
else {
fprintf (stderr, "%c", 7);
fflush (stderr);
}
}
waiting_input = 0;
return choice;
}
//////////////////////////////////////////////////////////////////////////////
// Display the specified message on the console through stderr. Note that //
// the cursor may start anywhere in the line and all text already on the //
// line is erased. A terminating newline is not needed and function works //
// with printf strings and args. //
//////////////////////////////////////////////////////////////////////////////
extern int debug_logging_mode;
#ifdef _WIN32
int get_app_path (char *app_path)
{
static char file_path [MAX_PATH], tried, result;
HINSTANCE hinstLib;
FARPROC ProcAdd;
if (tried) {
if (result)
strcpy (app_path, file_path);
return result;
}
tried = TRUE;
hinstLib = LoadLibrary ("shell32.dll");
if (hinstLib) {
ProcAdd = GetProcAddress (hinstLib, "SHGetFolderPathA");
if (ProcAdd && SUCCEEDED ((ProcAdd) (NULL, CSIDL_APPDATA | 0x8000, NULL, 0, file_path)))
result = TRUE;
if (!result) {
ProcAdd = GetProcAddress (hinstLib, "SHGetSpecialFolderPathA");
if (ProcAdd && SUCCEEDED ((ProcAdd) (NULL, file_path, CSIDL_APPDATA, TRUE)))
result = TRUE;
}
FreeLibrary (hinstLib);
}
if (!result) {
hinstLib = LoadLibrary ("shfolder.dll");
if (hinstLib) {
ProcAdd = GetProcAddress (hinstLib, "SHGetFolderPathA");
if (ProcAdd && SUCCEEDED ((ProcAdd) (NULL, CSIDL_APPDATA | 0x8000, NULL, 0, file_path)))
result = TRUE;
FreeLibrary (hinstLib);
}
}
if (result)
strcpy (app_path, file_path);
return result;
}
void error_line (char *error, ...)
{
char error_msg [512];
va_list argptr;
error_msg [0] = '\r';
va_start (argptr, error);
vsprintf (error_msg + 1, error, argptr);
va_end (argptr);
fputs (error_msg, stderr);
finish_line ();
if (debug_logging_mode) {
char file_path [MAX_PATH];
FILE *error_log = NULL;
if (get_app_path (file_path)) {
strcat (file_path, "\\WavPack\\wavpack.log");
error_log = fopen (file_path, "a+");
if (!error_log) {
get_app_path (file_path);
strcat (file_path, "\\WavPack");
if (CreateDirectory (file_path, NULL)) {
strcat (file_path, "\\wavpack.log");
error_log = fopen (file_path, "a+");
}
}
}
if (!error_log)
error_log = fopen ("c:\\wavpack.log", "a+");
if (error_log) {
fputs (error_msg + 1, error_log);
fputc ('\n', error_log);
fclose (error_log);
}
}
}
#else
void error_line (char *error, ...)
{
char error_msg [512];
va_list argptr;
error_msg [0] = '\r';
va_start (argptr, error);
vsprintf (error_msg + 1, error, argptr);
va_end (argptr);
fputs (error_msg, stderr);
finish_line ();
}
#endif
//////////////////////////////////////////////////////////////////////////////
// Function to intercept ^C or ^Break typed at the console. //
//////////////////////////////////////////////////////////////////////////////
#if defined(_WIN32)
static int break_flag;
BOOL WINAPI ctrl_handler (DWORD ctrl)
{
if (ctrl == CTRL_C_EVENT) {
break_flag = TRUE;
return TRUE;
}
if (ctrl == CTRL_BREAK_EVENT) {
if (waiting_input) {
return FALSE;
}
else {
break_flag = TRUE;
return TRUE;
}
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// Function to initialize console for intercepting ^C and ^Break. //
//////////////////////////////////////////////////////////////////////////////
void setup_break (void)
{
HANDLE hConIn = GetStdHandle (STD_INPUT_HANDLE);
SetConsoleMode (hConIn, ENABLE_PROCESSED_INPUT);
FlushConsoleInputBuffer (hConIn);
SetConsoleCtrlHandler (ctrl_handler, TRUE);
break_flag = 0;
}
//////////////////////////////////////////////////////////////////////////////
// Function to determine whether ^C or ^Break has been issued by user. //
//////////////////////////////////////////////////////////////////////////////
int check_break (void)
{
return break_flag;
}
//////////////////////////////////////////////////////////////////////////////
// Function to clear the stderr console to the end of the current line (and //
// go to the beginning next line). //
//////////////////////////////////////////////////////////////////////////////
void finish_line (void)
{
HANDLE hConIn = GetStdHandle (STD_ERROR_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO coninfo;
if (hConIn && GetConsoleScreenBufferInfo (hConIn, &coninfo) &&
(coninfo.dwCursorPosition.X || coninfo.dwCursorPosition.Y)) {
unsigned char spaces = coninfo.dwSize.X - coninfo.dwCursorPosition.X;
while (spaces--)
fputc (' ', stderr);
}
else
fprintf (stderr, " \n");
fflush (stderr);
}
#else
//////////////////////////////////////////////////////////////////////////////
// Function to clear the stderr console to the end of the current line (and //
// go to the beginning next line). //
//////////////////////////////////////////////////////////////////////////////
void finish_line (void)
{
fprintf (stderr, " \n");
fflush (stderr);
}
//////////////////////////////////////////////////////////////////////////////
// Function to initialize console for intercepting ^C and ^Break. //
//////////////////////////////////////////////////////////////////////////////
#include <signal.h>
static int break_flag;
static void int_handler(int s)
{
break_flag = 1;
}
void setup_break (void)
{
struct sigaction sigIntHandler;
break_flag = 0;
sigIntHandler.sa_handler = int_handler;
sigemptyset (&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction (SIGINT, &sigIntHandler, NULL);
}
//////////////////////////////////////////////////////////////////////////////
// Function to determine whether ^C or ^Break has been issued by user. //
//////////////////////////////////////////////////////////////////////////////
int check_break (void)
{
return break_flag;
}
#endif
//////////////////////////// File I/O Wrapper ////////////////////////////////
int DoReadFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, uint32_t *lpNumberOfBytesRead)
{
uint32_t bcount;
*lpNumberOfBytesRead = 0;
while (nNumberOfBytesToRead) {
bcount = (uint32_t) fread ((unsigned char *) lpBuffer + *lpNumberOfBytesRead, 1, nNumberOfBytesToRead, hFile);
if (bcount) {
*lpNumberOfBytesRead += bcount;
nNumberOfBytesToRead -= bcount;
}
else
break;
}
return !ferror (hFile);
}
int DoWriteFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten)
{
uint32_t bcount;
*lpNumberOfBytesWritten = 0;
while (nNumberOfBytesToWrite) {
bcount = (uint32_t) fwrite ((unsigned char *) lpBuffer + *lpNumberOfBytesWritten, 1, nNumberOfBytesToWrite, hFile);
if (bcount) {
*lpNumberOfBytesWritten += bcount;
nNumberOfBytesToWrite -= bcount;
}
else
break;
}
return !ferror (hFile);
}
#ifdef _WIN32
int64_t DoGetFileSize (FILE *hFile)
{
LARGE_INTEGER Size;
HANDLE fHandle;
if (hFile == NULL)
return 0;
fHandle = (HANDLE)_get_osfhandle(_fileno(hFile));
if (fHandle == INVALID_HANDLE_VALUE)
return 0;
Size.u.LowPart = GetFileSize(fHandle, &Size.u.HighPart);
if (Size.u.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
return 0;
return (int64_t)Size.QuadPart;
}
int64_t DoGetFilePosition (FILE *hFile)
{
return _ftelli64 (hFile);
}
int DoSetFilePositionAbsolute (FILE *hFile, int64_t pos)
{
return _fseeki64 (hFile, pos, SEEK_SET);
}
int DoSetFilePositionRelative (FILE *hFile, int64_t pos, int mode)
{
return _fseeki64 (hFile, pos, mode);
}
#else
int64_t DoGetFileSize (FILE *hFile)
{
struct stat statbuf;
if (!hFile || fstat (fileno (hFile), &statbuf) || !S_ISREG(statbuf.st_mode))
return 0;
return (int64_t) statbuf.st_size;
}
int64_t DoGetFilePosition (FILE *hFile)
{
return ftell (hFile);
}
int DoSetFilePositionAbsolute (FILE *hFile, int64_t pos)
{
return fseek (hFile, pos, SEEK_SET);
}
int DoSetFilePositionRelative (FILE *hFile, int64_t pos, int mode)
{
return fseek (hFile, pos, mode);
}
#endif
// if ungetc() is not available, a seek of -1 is fine also because we do not
// change the byte read.
int DoUngetc (int c, FILE *hFile)
{
return ungetc (c, hFile);
}
int DoCloseHandle (FILE *hFile)
{
return hFile ? !fclose (hFile) : 0;
}
int DoTruncateFile (FILE *hFile)
{
if (hFile) {
fflush (hFile);
#if defined(_WIN32)
return !_chsize (_fileno (hFile), 0);
#else
return !ftruncate(fileno (hFile), 0);
#endif
}
return 0;
}
int DoDeleteFile (char *filename)
{
return filename ? !remove (filename) : 0;
}
/////////////////////////////////////////////////////////////////////////////////
// Function to set the name of the console window. This is very handy for //
// displaying progress of batch operations with the console window minimized. //
/////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
void DoSetConsoleTitle (char *text)
{
SetConsoleTitle (text);
}
#else
void DoSetConsoleTitle (char *text)
{
fprintf (stderr, "\033]0;%s\007", text);
fflush (stderr);
}
#endif

View file

@ -1,61 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2006 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// utils.h
#ifndef UTILS_H
#define UTILS_H
#ifndef PATH_MAX
#ifdef MAX_PATH
#define PATH_MAX MAX_PATH
#elif defined (MAXPATHLEN)
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif
#if defined(_WIN32)
#undef VERSION_OS
#ifdef _WIN64
#define VERSION_OS "Win64"
#else
#define VERSION_OS "Win32"
#endif
#define PACKAGE_VERSION "5.2.0"
#endif
#define FALSE 0
#define TRUE 1
#define CLEAR(destin) memset (&destin, 0, sizeof (destin));
int copy_timestamp (const char *src_filename, const char *dst_filename);
char *filespec_ext (char *filespec), *filespec_path (char *filespec);
char *filespec_name (char *filespec), *filespec_wild (char *filespec);
void error_line (char *error, ...);
void setup_break (void), finish_line (void);
int check_break (void);
char yna (void);
int DoReadFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, uint32_t *lpNumberOfBytesRead);
int DoWriteFile (FILE *hFile, void *lpBuffer, uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten);
int64_t DoGetFileSize (FILE *hFile);
int64_t DoGetFilePosition (FILE *hFile);
int DoSetFilePositionAbsolute (FILE *hFile, int64_t pos);
int DoSetFilePositionRelative (FILE *hFile, int64_t pos, int mode);
int DoUngetc (int c, FILE *hFile);
int DoCloseHandle (FILE *hFile);
int DoTruncateFile (FILE *hFile);
int DoDeleteFile (char *filename);
void DoSetConsoleTitle (char *text);
#define FN_FIT(fn) ((strlen (fn) > 30) ? filespec_name (fn) : fn)
#endif

View file

@ -1,707 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2019 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// wavpack_local.h
#ifndef WAVPACK_LOCAL_H
#define WAVPACK_LOCAL_H
#include "wavpack.h"
#if defined(_WIN32)
#define strdup(x) _strdup(x)
#define FASTCALL __fastcall
#else
#define FASTCALL
#endif
#if defined(_WIN32) || \
(defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN)) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#define BITSTREAM_SHORTS // use 16-bit "shorts" for reading/writing bitstreams (instead of chars)
// (only works on little-endian machines)
#endif
#include <sys/types.h>
// This header file contains all the definitions required by WavPack.
#if defined(_MSC_VER) && _MSC_VER < 1600
#include <stdlib.h>
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
typedef __int64 int64_t;
typedef __int32 int32_t;
typedef __int16 int16_t;
typedef __int8 int8_t;
#else
#include <stdint.h>
#endif
// Because the C99 specification states that "The order of allocation of
// bit-fields within a unit (high-order to low-order or low-order to
// high-order) is implementation-defined" (6.7.2.1), I decided to change
// the representation of floating-point values from a structure of
// bit-fields to a 32-bit integer with access macros. Note that the WavPack
// library doesn't use any floating-point math to implement compression of
// floating-point data (although a little floating-point math is used in
// high-level functions unrelated to the codec).
typedef int32_t f32;
#define get_mantissa(f) ((f) & 0x7fffff)
#define get_magnitude(f) ((f) & 0x7fffffff)
#define get_exponent(f) (((f) >> 23) & 0xff)
#define get_sign(f) (((f) >> 31) & 0x1)
#define set_mantissa(f,v) (f) ^= (((f) ^ (v)) & 0x7fffff)
#define set_exponent(f,v) (f) ^= (((f) ^ ((v) << 23)) & 0x7f800000)
#define set_sign(f,v) (f) ^= (((f) ^ ((v) << 31)) & 0x80000000)
#include <stdio.h>
#define FALSE 0
#define TRUE 1
// ID3v1 and APEv2 TAG formats (may occur at the end of WavPack files)
typedef struct {
char tag_id [3], title [30], artist [30], album [30];
char year [4], comment [30], genre;
} ID3_Tag;
typedef struct {
char ID [8];
int32_t version, length, item_count, flags;
char res [8];
} APE_Tag_Hdr;
#define APE_Tag_Hdr_Format "8LLLL"
#define APE_TAG_TYPE_TEXT 0x0
#define APE_TAG_TYPE_BINARY 0x1
#define APE_TAG_THIS_IS_HEADER 0x20000000
#define APE_TAG_CONTAINS_HEADER 0x80000000
#define APE_TAG_MAX_LENGTH (1024 * 1024 * 16)
typedef struct {
int64_t tag_file_pos;
int tag_begins_file;
ID3_Tag id3_tag;
APE_Tag_Hdr ape_tag_hdr;
unsigned char *ape_tag_data;
} M_Tag;
// or-values for "flags"
#define CUR_STREAM_VERS 0x407 // universally compatible stream version
//////////////////////////// WavPack Metadata /////////////////////////////////
// This is an internal representation of metadata.
typedef struct {
int32_t byte_length;
void *data;
unsigned char id;
} WavpackMetadata;
#define ID_UNIQUE 0x3f
#define ID_OPTIONAL_DATA 0x20
#define ID_ODD_SIZE 0x40
#define ID_LARGE 0x80
#define ID_DUMMY 0x0
#define ID_ENCODER_INFO 0x1
#define ID_DECORR_TERMS 0x2
#define ID_DECORR_WEIGHTS 0x3
#define ID_DECORR_SAMPLES 0x4
#define ID_ENTROPY_VARS 0x5
#define ID_HYBRID_PROFILE 0x6
#define ID_SHAPING_WEIGHTS 0x7
#define ID_FLOAT_INFO 0x8
#define ID_INT32_INFO 0x9
#define ID_WV_BITSTREAM 0xa
#define ID_WVC_BITSTREAM 0xb
#define ID_WVX_BITSTREAM 0xc
#define ID_CHANNEL_INFO 0xd
#define ID_DSD_BLOCK 0xe
#define ID_RIFF_HEADER (ID_OPTIONAL_DATA | 0x1)
#define ID_RIFF_TRAILER (ID_OPTIONAL_DATA | 0x2)
#define ID_ALT_HEADER (ID_OPTIONAL_DATA | 0x3)
#define ID_ALT_TRAILER (ID_OPTIONAL_DATA | 0x4)
#define ID_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0x5)
#define ID_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x6)
#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7)
#define ID_ALT_EXTENSION (ID_OPTIONAL_DATA | 0x8)
#define ID_ALT_MD5_CHECKSUM (ID_OPTIONAL_DATA | 0x9)
#define ID_NEW_CONFIG_BLOCK (ID_OPTIONAL_DATA | 0xa)
#define ID_CHANNEL_IDENTITIES (ID_OPTIONAL_DATA | 0xb)
#define ID_BLOCK_CHECKSUM (ID_OPTIONAL_DATA | 0xf)
#define CONFIG_BYTES_STORED 3 // 1-4 bytes/sample
#define CONFIG_MONO_FLAG 4 // not stereo
#define CONFIG_FLOAT_DATA 0x80 // ieee 32-bit floating point data
#define CONFIG_AUTO_SHAPING 0x4000 // automatic noise shaping
#define CONFIG_LOSSY_MODE 0x1000000 // obsolete (for information)
/*
* These config flags were never actually used, or are no longer used, or are
* used for something else now. They may be used in the future for what they
* say, or for something else. WavPack files in the wild *may* have some of
* these bit set in their config flags (with these older meanings), but only
* if the stream version is 0x410 or less than 0x407. Of course, this is not
* very important because once the file has been encoded, the config bits are
* just for information purposes (i.e., they do not affect decoding),
*
#define CONFIG_ADOBE_MODE 0x100 // "adobe" mode for 32-bit floats
#define CONFIG_VERY_FAST_FLAG 0x400 // double fast
#define CONFIG_COPY_TIME 0x20000 // copy file-time from source
#define CONFIG_QUALITY_MODE 0x200000 // psychoacoustic quality mode
#define CONFIG_RAW_FLAG 0x400000 // raw mode (not implemented yet)
#define CONFIG_QUIET_MODE 0x10000000 // don't report progress %
#define CONFIG_IGNORE_LENGTH 0x20000000 // ignore length in wav header
#define CONFIG_NEW_RIFF_HEADER 0x40000000 // generate new RIFF wav header
*
*/
#define EXTRA_SCAN_ONLY 1
#define EXTRA_STEREO_MODES 2
#define EXTRA_TRY_DELTAS 8
#define EXTRA_ADJUST_DELTAS 16
#define EXTRA_SORT_FIRST 32
#define EXTRA_BRANCHES 0x1c0
#define EXTRA_SKIP_8TO16 512
#define EXTRA_TERMS 0x3c00
#define EXTRA_DUMP_TERMS 16384
#define EXTRA_SORT_LAST 32768
//////////////////////////////// WavPack Stream ///////////////////////////////
// This internal structure contains everything required to handle a WavPack
// "stream", which is defined as a stereo or mono stream of audio samples. For
// multichannel audio several of these would be required. Each stream contains
// pointers to hold a complete allocated block of WavPack data, although it's
// possible to decode WavPack blocks without buffering an entire block.
typedef struct bs {
#ifdef BITSTREAM_SHORTS
uint16_t *buf, *end, *ptr;
#else
unsigned char *buf, *end, *ptr;
#endif
void (*wrap)(struct bs *bs);
int error, bc;
uint32_t sr;
} Bitstream;
#define MAX_WRAPPER_BYTES 16777216
#define NEW_MAX_STREAMS 4096
#define OLD_MAX_STREAMS 8
#define MAX_NTERMS 16
#define MAX_TERM 8
// DSD-specific definitions
#define MAX_HISTORY_BITS 5 // maximum number of history bits in DSD "fast" mode
// note that 5 history bits requires 32 history bins
#define MAX_BYTES_PER_BIN 1280 // maximum bytes for the value lookup array (per bin)
// such that the total storage per bin = 2K (also
// counting probabilities and summed_probabilities)
// Note that this structure is directly accessed in assembly files, so modify with care
struct decorr_pass {
int32_t term, delta, weight_A, weight_B;
int32_t samples_A [MAX_TERM], samples_B [MAX_TERM];
int32_t aweight_A, aweight_B;
int32_t sum_A, sum_B;
};
typedef struct {
signed char joint_stereo, delta, terms [MAX_NTERMS+1];
} WavpackDecorrSpec;
struct entropy_data {
uint32_t median [3], slow_level, error_limit;
};
struct words_data {
uint32_t bitrate_delta [2], bitrate_acc [2];
uint32_t pend_data, holding_one, zeros_acc;
int holding_zero, pend_count;
struct entropy_data c [2];
};
typedef struct {
int32_t value, filter0, filter1, filter2, filter3, filter4, filter5, filter6, factor, byte;
} DSDfilters;
typedef struct {
WavpackHeader wphdr;
struct words_data w;
unsigned char *blockbuff, *blockend;
unsigned char *block2buff, *block2end;
int32_t *sample_buffer;
int64_t sample_index;
int bits, num_terms, mute_error, joint_stereo, false_stereo, shift;
int num_decorrs, num_passes, best_decorr, mask_decorr;
uint32_t crc, crc_x, crc_wvx;
Bitstream wvbits, wvcbits, wvxbits;
int init_done, wvc_skip;
float delta_decay;
unsigned char int32_sent_bits, int32_zeros, int32_ones, int32_dups;
unsigned char float_flags, float_shift, float_max_exp, float_norm_exp;
struct {
int32_t shaping_acc [2], shaping_delta [2], error [2];
double noise_sum, noise_ave, noise_max;
int16_t *shaping_data, *shaping_array;
int32_t shaping_samples;
} dc;
struct decorr_pass decorr_passes [MAX_NTERMS], analysis_pass;
const WavpackDecorrSpec *decorr_specs;
struct {
unsigned char *byteptr, *endptr, (*probabilities) [256], *lookup_buffer, **value_lookup, mode, ready;
int history_bins, p0, p1;
int16_t (*summed_probabilities) [256];
uint32_t low, high, value;
DSDfilters filters [2];
int32_t *ptable;
} dsd;
} WavpackStream;
// flags for float_flags:
#define FLOAT_SHIFT_ONES 1 // bits left-shifted into float = '1'
#define FLOAT_SHIFT_SAME 2 // bits left-shifted into float are the same
#define FLOAT_SHIFT_SENT 4 // bits shifted into float are sent literally
#define FLOAT_ZEROS_SENT 8 // "zeros" are not all real zeros
#define FLOAT_NEG_ZEROS 0x10 // contains negative zeros
#define FLOAT_EXCEPTIONS 0x20 // contains exceptions (inf, nan, etc.)
/////////////////////////////// WavPack Context ///////////////////////////////
// This internal structure holds everything required to encode or decode WavPack
// files. It is recommended that direct access to this structure be minimized
// and the provided utilities used instead.
struct WavpackContext {
WavpackConfig config;
WavpackMetadata *metadata;
uint32_t metabytes;
int metacount;
unsigned char *wrapper_data;
uint32_t wrapper_bytes;
WavpackBlockOutput blockout;
void *wv_out, *wvc_out;
WavpackStreamReader64 *reader;
void *wv_in, *wvc_in;
int64_t filelen, file2len, filepos, file2pos, total_samples, initial_index;
uint32_t crc_errors, first_flags;
int wvc_flag, open_flags, norm_offset, reduced_channels, lossy_blocks, version_five;
uint32_t block_samples, ave_block_samples, block_boundary, max_samples, acc_samples, riff_trailer_bytes;
int riff_header_added, riff_header_created;
M_Tag m_tag;
int current_stream, num_streams, max_streams, stream_version;
WavpackStream **streams;
void *stream3;
// these items were added in 5.0 to support alternate file types (especially CAF & DSD)
unsigned char file_format, *channel_reordering, *channel_identities;
uint32_t channel_layout, dsd_multiplier;
void *decimation_context;
char file_extension [8];
void (*close_callback)(void *wpc);
char error_message [80];
};
//////////////////////// function prototypes and macros //////////////////////
#define CLEAR(destin) memset (&destin, 0, sizeof (destin));
//////////////////////////////// decorrelation //////////////////////////////
// modules: pack.c, unpack.c, unpack_floats.c, extra1.c, extra2.c
// #define SKIP_DECORRELATION // experimental switch to disable all decorrelation on encode
// These macros implement the weight application and update operations
// that are at the heart of the decorrelation loops. Note that there are
// sometimes two and even three versions of each macro. Theses should be
// equivalent and produce identical results, but some may perform better
// or worse on a given architecture.
#if 1 // PERFCOND - apply decorrelation weight when no 32-bit overflow possible
#define apply_weight_i(weight, sample) ((weight * sample + 512) >> 10)
#else
#define apply_weight_i(weight, sample) ((((weight * sample) >> 8) + 2) >> 2)
#endif
#if 1 // PERFCOND - apply decorrelation weight when 32-bit overflow is possible
#define apply_weight_f(weight, sample) (((((sample & 0xffff) * weight) >> 9) + \
(((sample & ~0xffff) >> 9) * weight) + 1) >> 1)
#elif 1
#define apply_weight_f(weight, sample) ((int32_t)((weight * (int64_t) sample + 512) >> 10))
#else
#define apply_weight_f(weight, sample) ((int32_t)floor(((double) weight * sample + 512.0) / 1024.0))
#endif
#if 1 // PERFCOND - universal version that checks input magnitude or always uses long version
#define apply_weight(weight, sample) (sample != (int16_t) sample ? \
apply_weight_f (weight, sample) : apply_weight_i (weight, sample))
#else
#define apply_weight(weight, sample) (apply_weight_f (weight, sample))
#endif
#if 1 // PERFCOND
#define update_weight(weight, delta, source, result) \
if (source && result) { int32_t s = (int32_t) (source ^ result) >> 31; weight = (delta ^ s) + (weight - s); }
#elif 1
#define update_weight(weight, delta, source, result) \
if (source && result) weight += (((source ^ result) >> 30) | 1) * delta;
#else
#define update_weight(weight, delta, source, result) \
if (source && result) (source ^ result) < 0 ? (weight -= delta) : (weight += delta);
#endif
#define update_weight_clip(weight, delta, source, result) \
if (source && result) { \
const int32_t s = (source ^ result) >> 31; \
if ((weight = (weight ^ s) + (delta - s)) > 1024) weight = 1024; \
weight = (weight ^ s) - s; \
}
void pack_init (WavpackContext *wpc);
int pack_block (WavpackContext *wpc, int32_t *buffer);
void send_general_metadata (WavpackContext *wpc);
void free_metadata (WavpackMetadata *wpmd);
int copy_metadata (WavpackMetadata *wpmd, unsigned char *buffer_start, unsigned char *buffer_end);
double WavpackGetEncodedNoise (WavpackContext *wpc, double *peak);
int unpack_init (WavpackContext *wpc);
int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd);
int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd);
int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd);
int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd);
int32_t unpack_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count);
int check_crc_error (WavpackContext *wpc);
int scan_float_data (WavpackStream *wps, f32 *values, int32_t num_values);
void send_float_data (WavpackStream *wps, f32 *values, int32_t num_values);
void float_values (WavpackStream *wps, int32_t *values, int32_t num_values);
void dynamic_noise_shaping (WavpackContext *wpc, int32_t *buffer, int shortening_allowed);
void execute_stereo (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples);
void execute_mono (WavpackContext *wpc, int32_t *samples, int no_history, int do_samples);
////////////////////////// DSD related (including decimation) //////////////////////////
// modules: pack_dsd.c unpack_dsd.c
void pack_dsd_init (WavpackContext *wpc);
int pack_dsd_block (WavpackContext *wpc, int32_t *buffer);
int init_dsd_block (WavpackContext *wpc, WavpackMetadata *wpmd);
int32_t unpack_dsd_samples (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count);
void *decimate_dsd_init (int num_channels);
void decimate_dsd_reset (void *decimate_context);
void decimate_dsd_run (void *decimate_context, int32_t *samples, int num_samples);
void decimate_dsd_destroy (void *decimate_context);
///////////////////////////////// CPU feature detection ////////////////////////////////
int unpack_cpu_has_feature_x86 (int findex), pack_cpu_has_feature_x86 (int findex);
#define CPU_FEATURE_MMX 23
///////////////////////////// pre-4.0 version decoding ////////////////////////////
// modules: unpack3.c, unpack3_open.c, unpack3_seek.c
WavpackContext *open_file3 (WavpackContext *wpc, char *error);
int32_t unpack_samples3 (WavpackContext *wpc, int32_t *buffer, uint32_t sample_count);
int seek_sample3 (WavpackContext *wpc, uint32_t desired_index);
uint32_t get_sample_index3 (WavpackContext *wpc);
void free_stream3 (WavpackContext *wpc);
int get_version3 (WavpackContext *wpc);
////////////////////////////// bitstream macros & functions /////////////////////////////
#define bs_is_open(bs) ((bs)->ptr != NULL)
uint32_t bs_close_read (Bitstream *bs);
#define getbit(bs) ( \
(((bs)->bc) ? \
((bs)->bc--, (bs)->sr & 1) : \
(((++((bs)->ptr) != (bs)->end) ? (void) 0 : (bs)->wrap (bs)), (bs)->bc = sizeof (*((bs)->ptr)) * 8 - 1, ((bs)->sr = *((bs)->ptr)) & 1) \
) ? \
((bs)->sr >>= 1, 1) : \
((bs)->sr >>= 1, 0) \
)
#define getbits(value, nbits, bs) do { \
while ((nbits) > (bs)->bc) { \
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
(bs)->sr |= (int32_t)*((bs)->ptr) << (bs)->bc; \
(bs)->bc += sizeof (*((bs)->ptr)) * 8; \
} \
*(value) = (bs)->sr; \
if ((bs)->bc > 32) { \
(bs)->bc -= (nbits); \
(bs)->sr = *((bs)->ptr) >> (sizeof (*((bs)->ptr)) * 8 - (bs)->bc); \
} \
else { \
(bs)->bc -= (nbits); \
(bs)->sr >>= (nbits); \
} \
} while (0)
#define putbit(bit, bs) do { if (bit) (bs)->sr |= (1 << (bs)->bc); \
if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \
*((bs)->ptr) = (bs)->sr; \
(bs)->sr = (bs)->bc = 0; \
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
}} while (0)
#define putbit_0(bs) do { \
if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \
*((bs)->ptr) = (bs)->sr; \
(bs)->sr = (bs)->bc = 0; \
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
}} while (0)
#define putbit_1(bs) do { (bs)->sr |= (1 << (bs)->bc); \
if (++((bs)->bc) == sizeof (*((bs)->ptr)) * 8) { \
*((bs)->ptr) = (bs)->sr; \
(bs)->sr = (bs)->bc = 0; \
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
}} while (0)
#define putbits(value, nbits, bs) do { \
(bs)->sr |= (int32_t)(value) << (bs)->bc; \
if (((bs)->bc += (nbits)) >= sizeof (*((bs)->ptr)) * 8) \
do { \
*((bs)->ptr) = (bs)->sr; \
(bs)->sr >>= sizeof (*((bs)->ptr)) * 8; \
if (((bs)->bc -= sizeof (*((bs)->ptr)) * 8) > 32 - sizeof (*((bs)->ptr)) * 8) \
(bs)->sr |= ((value) >> ((nbits) - (bs)->bc)); \
if (++((bs)->ptr) == (bs)->end) (bs)->wrap (bs); \
} while ((bs)->bc >= sizeof (*((bs)->ptr)) * 8); \
} while (0)
///////////////////////////// entropy encoder / decoder ////////////////////////////
// modules: entropy_utils.c, read_words.c, write_words.c
// these control the time constant "slow_level" which is used for hybrid mode
// that controls bitrate as a function of residual level (HYBRID_BITRATE).
#define SLS 8
#define SLO ((1 << (SLS - 1)))
#define LIMIT_ONES 16 // maximum consecutive 1s sent for "div" data
// these control the time constant of the 3 median level breakpoints
#define DIV0 128 // 5/7 of samples
#define DIV1 64 // 10/49 of samples
#define DIV2 32 // 20/343 of samples
// this macro retrieves the specified median breakpoint (without frac; min = 1)
#define GET_MED(med) (((c->median [med]) >> 4) + 1)
// These macros update the specified median breakpoints. Note that the median
// is incremented when the sample is higher than the median, else decremented.
// They are designed so that the median will never drop below 1 and the value
// is essentially stationary if there are 2 increments for every 5 decrements.
#define INC_MED0() (c->median [0] += ((c->median [0] + DIV0) / DIV0) * 5)
#define DEC_MED0() (c->median [0] -= ((c->median [0] + (DIV0-2)) / DIV0) * 2)
#define INC_MED1() (c->median [1] += ((c->median [1] + DIV1) / DIV1) * 5)
#define DEC_MED1() (c->median [1] -= ((c->median [1] + (DIV1-2)) / DIV1) * 2)
#define INC_MED2() (c->median [2] += ((c->median [2] + DIV2) / DIV2) * 5)
#define DEC_MED2() (c->median [2] -= ((c->median [2] + (DIV2-2)) / DIV2) * 2)
#ifdef HAVE___BUILTIN_CLZ
#define count_bits(av) ((av) ? 32 - __builtin_clz (av) : 0)
#elif defined (_WIN64)
static __inline int count_bits (uint32_t av) { unsigned long res; return _BitScanReverse (&res, av) ? (int)(res + 1) : 0; }
#else
#define count_bits(av) ( \
(av) < (1 << 8) ? nbits_table [av] : \
( \
(av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \
((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \
) \
)
#endif
void init_words (WavpackStream *wps);
void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd);
void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd);
int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd);
int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd);
int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan);
void send_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples);
int32_t FASTCALL get_word (WavpackStream *wps, int chan, int32_t *correction);
int32_t get_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples);
void flush_word (WavpackStream *wps);
int32_t nosend_word (WavpackStream *wps, int32_t value, int chan);
void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir);
void update_error_limit (WavpackStream *wps);
extern const uint32_t bitset [32];
extern const uint32_t bitmask [32];
extern const char nbits_table [256];
int wp_log2s (int32_t value);
int32_t wp_exp2s (int log);
int FASTCALL wp_log2 (uint32_t avalue);
#ifdef OPT_ASM_X86
#define LOG2BUFFER log2buffer_x86
#elif defined(OPT_ASM_X64) && (defined (_WIN64) || defined(__CYGWIN__) || defined(__MINGW64__) || defined(__midipix__))
#define LOG2BUFFER log2buffer_x64win
#elif defined(OPT_ASM_X64)
#define LOG2BUFFER log2buffer_x64
#else
#define LOG2BUFFER log2buffer
#endif
uint32_t LOG2BUFFER (int32_t *samples, uint32_t num_samples, int limit);
signed char store_weight (int weight);
int restore_weight (signed char weight);
#define WORD_EOF ((int32_t)(1L << 31))
void WavpackFloatNormalize (int32_t *values, int32_t num_values, int delta_exp);
/////////////////////////// high-level unpacking API and support ////////////////////////////
// modules: open_utils.c, unpack_utils.c, unpack_seek.c, unpack_floats.c
WavpackContext *WavpackOpenFileInputEx64 (WavpackStreamReader64 *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset);
WavpackContext *WavpackOpenFileInputEx (WavpackStreamReader *reader, void *wv_id, void *wvc_id, char *error, int flags, int norm_offset);
WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int flags, int norm_offset);
#define OPEN_WVC 0x1 // open/read "correction" file
#define OPEN_TAGS 0x2 // read ID3v1 / APEv2 tags (seekable file)
#define OPEN_WRAPPER 0x4 // make audio wrapper available (i.e. RIFF)
#define OPEN_2CH_MAX 0x8 // open multichannel as stereo (no downmix)
#define OPEN_NORMALIZE 0x10 // normalize floating point data to +/- 1.0
#define OPEN_STREAMING 0x20 // "streaming" mode blindly unpacks blocks
// w/o regard to header file position info
#define OPEN_EDIT_TAGS 0x40 // allow editing of tags
#define OPEN_FILE_UTF8 0x80 // assume filenames are UTF-8 encoded, not ANSI (Windows only)
// new for version 5
#define OPEN_DSD_NATIVE 0x100 // open DSD files as bitstreams
// (returned as 8-bit "samples" stored in 32-bit words)
#define OPEN_DSD_AS_PCM 0x200 // open DSD files as 24-bit PCM (decimated 8x)
#define OPEN_ALT_TYPES 0x400 // application is aware of alternate file types & qmode
// (just affects retrieving wrappers & MD5 checksums)
#define OPEN_NO_CHECKSUM 0x800 // don't verify block checksums before decoding
int WavpackGetMode (WavpackContext *wpc);
int WavpackGetQualifyMode (WavpackContext *wpc);
int WavpackGetVersion (WavpackContext *wpc);
uint32_t WavpackUnpackSamples (WavpackContext *wpc, int32_t *buffer, uint32_t samples);
int WavpackSeekSample (WavpackContext *wpc, uint32_t sample);
int WavpackSeekSample64 (WavpackContext *wpc, int64_t sample);
int WavpackGetMD5Sum (WavpackContext *wpc, unsigned char data [16]);
int WavpackVerifySingleBlock (unsigned char *buffer, int verify_checksum);
uint32_t read_next_header (WavpackStreamReader64 *reader, void *id, WavpackHeader *wphdr);
int read_wvc_block (WavpackContext *wpc);
/////////////////////////// high-level packing API and support ////////////////////////////
// modules: pack_utils.c, pack_floats.c
WavpackContext *WavpackOpenFileOutput (WavpackBlockOutput blockout, void *wv_id, void *wvc_id);
int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples);
int WavpackSetConfiguration64 (WavpackContext *wpc, WavpackConfig *config, int64_t total_samples, const unsigned char *chan_ids);
int WavpackPackInit (WavpackContext *wpc);
int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount);
int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count);
int WavpackFlushSamples (WavpackContext *wpc);
int WavpackStoreMD5Sum (WavpackContext *wpc, unsigned char data [16]);
void WavpackSeekTrailingWrapper (WavpackContext *wpc);
void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block);
void *WavpackGetWrapperLocation (void *first_block, uint32_t *size);
/////////////////////////////////// common utilities ////////////////////////////////////
// module: common_utils.c
extern const uint32_t sample_rates [16];
uint32_t WavpackGetLibraryVersion (void);
const char *WavpackGetLibraryVersionString (void);
uint32_t WavpackGetSampleRate (WavpackContext *wpc);
int WavpackGetBitsPerSample (WavpackContext *wpc);
int WavpackGetBytesPerSample (WavpackContext *wpc);
int WavpackGetNumChannels (WavpackContext *wpc);
int WavpackGetChannelMask (WavpackContext *wpc);
int WavpackGetReducedChannels (WavpackContext *wpc);
int WavpackGetFloatNormExp (WavpackContext *wpc);
uint32_t WavpackGetNumSamples (WavpackContext *wpc);
int64_t WavpackGetNumSamples64 (WavpackContext *wpc);
uint32_t WavpackGetSampleIndex (WavpackContext *wpc);
int64_t WavpackGetSampleIndex64 (WavpackContext *wpc);
char *WavpackGetErrorMessage (WavpackContext *wpc);
int WavpackGetNumErrors (WavpackContext *wpc);
int WavpackLossyBlocks (WavpackContext *wpc);
uint32_t WavpackGetWrapperBytes (WavpackContext *wpc);
unsigned char *WavpackGetWrapperData (WavpackContext *wpc);
void WavpackFreeWrapper (WavpackContext *wpc);
double WavpackGetProgress (WavpackContext *wpc);
uint32_t WavpackGetFileSize (WavpackContext *wpc);
int64_t WavpackGetFileSize64 (WavpackContext *wpc);
double WavpackGetRatio (WavpackContext *wpc);
double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc);
double WavpackGetInstantBitrate (WavpackContext *wpc);
WavpackContext *WavpackCloseFile (WavpackContext *wpc);
void WavpackLittleEndianToNative (void *data, char *format);
void WavpackNativeToLittleEndian (void *data, char *format);
void WavpackBigEndianToNative (void *data, char *format);
void WavpackNativeToBigEndian (void *data, char *format);
void install_close_callback (WavpackContext *wpc, void cb_func (void *wpc));
void free_dsd_tables (WavpackStream *wps);
void free_streams (WavpackContext *wpc);
/////////////////////////////////// tag utilities ////////////////////////////////////
// modules: tags.c, tag_utils.c
int WavpackGetNumTagItems (WavpackContext *wpc);
int WavpackGetTagItem (WavpackContext *wpc, const char *item, char *value, int size);
int WavpackGetTagItemIndexed (WavpackContext *wpc, int index, char *item, int size);
int WavpackGetNumBinaryTagItems (WavpackContext *wpc);
int WavpackGetBinaryTagItem (WavpackContext *wpc, const char *item, char *value, int size);
int WavpackGetBinaryTagItemIndexed (WavpackContext *wpc, int index, char *item, int size);
int WavpackAppendTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize);
int WavpackAppendBinaryTagItem (WavpackContext *wpc, const char *item, const char *value, int vsize);
int WavpackDeleteTagItem (WavpackContext *wpc, const char *item);
int WavpackWriteTag (WavpackContext *wpc);
int load_tag (WavpackContext *wpc);
void free_tag (M_Tag *m_tag);
int valid_tag (M_Tag *m_tag);
int editable_tag (M_Tag *m_tag);
#endif

View file

@ -1,19 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2019 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// wavpack_version.h
#ifndef WAVPACK_VERSION_H
#define WAVPACK_VERSION_H
#define LIBWAVPACK_MAJOR 5
#define LIBWAVPACK_MINOR 2
#define LIBWAVPACK_MICRO 0
#define LIBWAVPACK_VERSION_STRING "5.2.0"
#endif

View file

@ -1,688 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2013 Conifer Software. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// write_words.c
// This module provides entropy word encoding functions using
// a variation on the Rice method. This was introduced in version 3.93
// because it allows splitting the data into a "lossy" stream and a
// "correction" stream in a very efficient manner and is therefore ideal
// for the "hybrid" mode. For 4.0, the efficiency of this method was
// significantly improved by moving away from the normal Rice restriction of
// using powers of two for the modulus divisions and now the method can be
// used for both hybrid and pure lossless encoding.
// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%),
// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the
// previous. Using standard Rice coding on this data would result in 1.4
// bits per sample average (not counting sign bit). However, there is a
// very simple encoding that is over 99% efficient with this data and
// results in about 1.22 bits per sample.
#include <stdlib.h>
#include <string.h>
#include "wavpack_local.h"
///////////////////////////// executable code ////////////////////////////////
// Initialize entropy encoder for the specified stream. In lossless mode there
// are no parameters to select; in hybrid mode the bitrate mode and value need
// be initialized.
static void word_set_bitrate (WavpackStream *wps);
void init_words (WavpackStream *wps)
{
CLEAR (wps->w);
if (wps->wphdr.flags & HYBRID_FLAG)
word_set_bitrate (wps);
}
// Set up parameters for hybrid mode based on header flags and "bits" field.
// This is currently only set up for the HYBRID_BITRATE mode in which the
// allowed error varies with the residual level (from "slow_level"). The
// simpler mode (which is not used yet) has the error level directly
// controlled from the metadata.
static void word_set_bitrate (WavpackStream *wps)
{
int bitrate_0, bitrate_1;
if (wps->wphdr.flags & HYBRID_BITRATE) {
if (wps->wphdr.flags & FALSE_STEREO)
bitrate_0 = (wps->bits * 2 - 512) < 568 ? 0 : (wps->bits * 2 - 512) - 568;
else
bitrate_0 = wps->bits < 568 ? 0 : wps->bits - 568;
if (!(wps->wphdr.flags & MONO_DATA)) {
if (wps->wphdr.flags & HYBRID_BALANCE)
bitrate_1 = (wps->wphdr.flags & JOINT_STEREO) ? 256 : 0;
else {
bitrate_1 = bitrate_0;
if (wps->wphdr.flags & JOINT_STEREO) {
if (bitrate_0 < 128) {
bitrate_1 += bitrate_0;
bitrate_0 = 0;
}
else {
bitrate_0 -= 128;
bitrate_1 += 128;
}
}
}
}
else
bitrate_1 = 0;
}
else
bitrate_0 = bitrate_1 = 0;
wps->w.bitrate_acc [0] = (int32_t) bitrate_0 << 16;
wps->w.bitrate_acc [1] = (int32_t) bitrate_1 << 16;
}
// Allocates the correct space in the metadata structure and writes the
// current median values to it. Values are converted from 32-bit unsigned
// to our internal 16-bit wp_log2 values, and read_entropy_vars () is called
// to read the values back because we must compensate for the loss through
// the log function.
void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char *byteptr;
int temp;
byteptr = wpmd->data = malloc (12);
wpmd->id = ID_ENTROPY_VARS;
*byteptr++ = temp = wp_log2 (wps->w.c [0].median [0]);
*byteptr++ = temp >> 8;
*byteptr++ = temp = wp_log2 (wps->w.c [0].median [1]);
*byteptr++ = temp >> 8;
*byteptr++ = temp = wp_log2 (wps->w.c [0].median [2]);
*byteptr++ = temp >> 8;
if (!(wps->wphdr.flags & MONO_DATA)) {
*byteptr++ = temp = wp_log2 (wps->w.c [1].median [0]);
*byteptr++ = temp >> 8;
*byteptr++ = temp = wp_log2 (wps->w.c [1].median [1]);
*byteptr++ = temp >> 8;
*byteptr++ = temp = wp_log2 (wps->w.c [1].median [2]);
*byteptr++ = temp >> 8;
}
wpmd->byte_length = (int32_t)(byteptr - (unsigned char *) wpmd->data);
read_entropy_vars (wps, wpmd);
}
// Allocates enough space in the metadata structure and writes the current
// high word of the bitrate accumulator and the slow_level values to it. The
// slow_level values are converted from 32-bit unsigned to our internal 16-bit
// wp_log2 values. Afterward, read_entropy_vars () is called to read the values
// back because we must compensate for the loss through the log function and
// the truncation of the bitrate.
void write_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
{
unsigned char *byteptr;
int temp;
word_set_bitrate (wps);
byteptr = wpmd->data = malloc (512);
wpmd->id = ID_HYBRID_PROFILE;
if (wps->wphdr.flags & HYBRID_BITRATE) {
*byteptr++ = temp = wp_log2s (wps->w.c [0].slow_level);
*byteptr++ = temp >> 8;
if (!(wps->wphdr.flags & MONO_DATA)) {
*byteptr++ = temp = wp_log2s (wps->w.c [1].slow_level);
*byteptr++ = temp >> 8;
}
}
*byteptr++ = temp = wps->w.bitrate_acc [0] >> 16;
*byteptr++ = temp >> 8;
if (!(wps->wphdr.flags & MONO_DATA)) {
*byteptr++ = temp = wps->w.bitrate_acc [1] >> 16;
*byteptr++ = temp >> 8;
}
if (wps->w.bitrate_delta [0] | wps->w.bitrate_delta [1]) {
*byteptr++ = temp = wp_log2s (wps->w.bitrate_delta [0]);
*byteptr++ = temp >> 8;
if (!(wps->wphdr.flags & MONO_DATA)) {
*byteptr++ = temp = wp_log2s (wps->w.bitrate_delta [1]);
*byteptr++ = temp >> 8;
}
}
wpmd->byte_length = (int32_t)(byteptr - (unsigned char *) wpmd->data);
read_hybrid_profile (wps, wpmd);
}
// This function writes the specified word to the open bitstream "wvbits" and,
// if the bitstream "wvcbits" is open, writes any correction data there. This
// function will work for either lossless or hybrid but because a version
// optimized for lossless exits below, it would normally be used for the hybrid
// mode only. The return value is the actual value stored to the stream (even
// if a correction file is being created) and is used as feedback to the
// predictor.
int32_t FASTCALL send_word (WavpackStream *wps, int32_t value, int chan)
{
struct entropy_data *c = wps->w.c + chan;
uint32_t ones_count, low, mid, high;
int sign = (value < 0) ? 1 : 0;
if (wps->w.c [0].median [0] < 2 && !wps->w.holding_zero && wps->w.c [1].median [0] < 2) {
if (wps->w.zeros_acc) {
if (value)
flush_word (wps);
else {
c->slow_level -= (c->slow_level + SLO) >> SLS;
wps->w.zeros_acc++;
return 0;
}
}
else if (value)
putbit_0 (&wps->wvbits);
else {
c->slow_level -= (c->slow_level + SLO) >> SLS;
CLEAR (wps->w.c [0].median);
CLEAR (wps->w.c [1].median);
wps->w.zeros_acc = 1;
return 0;
}
}
if (sign)
value = ~value;
if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
update_error_limit (wps);
if (value < (int32_t) GET_MED (0)) {
ones_count = low = 0;
high = GET_MED (0) - 1;
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (value - low < GET_MED (1)) {
ones_count = 1;
high = low + GET_MED (1) - 1;
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (value - low < GET_MED (2)) {
ones_count = 2;
high = low + GET_MED (2) - 1;
DEC_MED2 ();
}
else {
ones_count = 2 + (value - low) / GET_MED (2);
low += (ones_count - 2) * GET_MED (2);
high = low + GET_MED (2) - 1;
INC_MED2 ();
}
}
}
mid = (high + low + 1) >> 1;
if (wps->w.holding_zero) {
if (ones_count)
wps->w.holding_one++;
flush_word (wps);
if (ones_count) {
wps->w.holding_zero = 1;
ones_count--;
}
else
wps->w.holding_zero = 0;
}
else
wps->w.holding_zero = 1;
wps->w.holding_one = ones_count * 2;
if (!c->error_limit) {
if (high != low) {
uint32_t maxcode = high - low, code = value - low;
int bitcount = count_bits (maxcode);
uint32_t extras = bitset [bitcount] - maxcode - 1;
if (code < extras) {
wps->w.pend_data |= code << wps->w.pend_count;
wps->w.pend_count += bitcount - 1;
}
else {
wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count;
wps->w.pend_count += bitcount - 1;
wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++;
}
}
mid = value;
}
else
while (high - low > c->error_limit)
if (value < (int32_t) mid) {
mid = ((high = mid - 1) + low + 1) >> 1;
wps->w.pend_count++;
}
else {
mid = (high + (low = mid) + 1) >> 1;
wps->w.pend_data |= bitset [wps->w.pend_count++];
}
wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++);
if (!wps->w.holding_zero)
flush_word (wps);
if (bs_is_open (&wps->wvcbits) && c->error_limit) {
uint32_t code = value - low, maxcode = high - low;
int bitcount = count_bits (maxcode);
uint32_t extras = bitset [bitcount] - maxcode - 1;
if (bitcount) {
if (code < extras)
putbits (code, bitcount - 1, &wps->wvcbits);
else {
putbits ((code + extras) >> 1, bitcount - 1, &wps->wvcbits);
putbit ((code + extras) & 1, &wps->wvcbits);
}
}
}
if (wps->wphdr.flags & HYBRID_BITRATE) {
c->slow_level -= (c->slow_level + SLO) >> SLS;
c->slow_level += wp_log2 (mid);
}
return sign ? ~mid : mid;
}
// This function is an optimized version of send_word() that only handles
// lossless (error_limit == 0) and sends an entire buffer of either mono or
// stereo data rather than a single sample. Unlike the generalized
// send_word(), it does not return values because it always encodes
// the exact value passed.
void send_words_lossless (WavpackStream *wps, int32_t *buffer, int32_t nsamples)
{
struct entropy_data *c = wps->w.c;
int32_t value, csamples;
if (!(wps->wphdr.flags & MONO_DATA))
nsamples *= 2;
for (csamples = 0; csamples < nsamples; ++csamples) {
int sign = ((value = *buffer++) < 0) ? 1 : 0;
uint32_t ones_count, low, high;
if (!(wps->wphdr.flags & MONO_DATA))
c = wps->w.c + (csamples & 1);
if (wps->w.c [0].median [0] < 2 && !wps->w.holding_zero && wps->w.c [1].median [0] < 2) {
if (wps->w.zeros_acc) {
if (value)
flush_word (wps);
else {
wps->w.zeros_acc++;
continue;
}
}
else if (value)
putbit_0 (&wps->wvbits);
else {
CLEAR (wps->w.c [0].median);
CLEAR (wps->w.c [1].median);
wps->w.zeros_acc = 1;
continue;
}
}
if (sign)
value = ~value;
if (value < (int32_t) GET_MED (0)) {
ones_count = low = 0;
high = GET_MED (0) - 1;
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (value - low < GET_MED (1)) {
ones_count = 1;
high = low + GET_MED (1) - 1;
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (value - low < GET_MED (2)) {
ones_count = 2;
high = low + GET_MED (2) - 1;
DEC_MED2 ();
}
else {
ones_count = 2 + (value - low) / GET_MED (2);
low += (ones_count - 2) * GET_MED (2);
high = low + GET_MED (2) - 1;
INC_MED2 ();
}
}
}
if (wps->w.holding_zero) {
if (ones_count)
wps->w.holding_one++;
flush_word (wps);
if (ones_count) {
wps->w.holding_zero = 1;
ones_count--;
}
else
wps->w.holding_zero = 0;
}
else
wps->w.holding_zero = 1;
wps->w.holding_one = ones_count * 2;
if (high != low) {
uint32_t maxcode = high - low, code = value - low;
int bitcount = count_bits (maxcode);
uint32_t extras = bitset [bitcount] - maxcode - 1;
if (code < extras) {
wps->w.pend_data |= code << wps->w.pend_count;
wps->w.pend_count += bitcount - 1;
}
else {
wps->w.pend_data |= ((code + extras) >> 1) << wps->w.pend_count;
wps->w.pend_count += bitcount - 1;
wps->w.pend_data |= ((code + extras) & 1) << wps->w.pend_count++;
}
}
wps->w.pend_data |= ((int32_t) sign << wps->w.pend_count++);
if (!wps->w.holding_zero)
flush_word (wps);
}
}
// Used by send_word() and send_word_lossless() to actually send most the
// accumulated data onto the bitstream. This is also called directly from
// clients when all words have been sent.
void flush_word (WavpackStream *wps)
{
if (wps->w.zeros_acc) {
int cbits = count_bits (wps->w.zeros_acc);
while (cbits--)
putbit_1 (&wps->wvbits);
putbit_0 (&wps->wvbits);
while (wps->w.zeros_acc > 1) {
putbit (wps->w.zeros_acc & 1, &wps->wvbits);
wps->w.zeros_acc >>= 1;
}
wps->w.zeros_acc = 0;
}
if (wps->w.holding_one) {
#ifdef LIMIT_ONES
if (wps->w.holding_one >= LIMIT_ONES) {
int cbits;
putbits ((1L << LIMIT_ONES) - 1, LIMIT_ONES + 1, &wps->wvbits);
wps->w.holding_one -= LIMIT_ONES;
cbits = count_bits (wps->w.holding_one);
while (cbits--)
putbit_1 (&wps->wvbits);
putbit_0 (&wps->wvbits);
while (wps->w.holding_one > 1) {
putbit (wps->w.holding_one & 1, &wps->wvbits);
wps->w.holding_one >>= 1;
}
wps->w.holding_zero = 0;
}
else
putbits (bitmask [wps->w.holding_one], wps->w.holding_one, &wps->wvbits);
wps->w.holding_one = 0;
#else
do {
putbit_1 (&wps->wvbits);
} while (--wps->w.holding_one);
#endif
}
if (wps->w.holding_zero) {
putbit_0 (&wps->wvbits);
wps->w.holding_zero = 0;
}
if (wps->w.pend_count) {
putbits (wps->w.pend_data, wps->w.pend_count, &wps->wvbits);
wps->w.pend_data = wps->w.pend_count = 0;
}
}
// This function is similar to send_word() except that no data is actually
// written to any stream, but it does return the value that would have been
// sent to a hybrid stream. It is used to determine beforehand how much noise
// will be added to samples.
int32_t nosend_word (WavpackStream *wps, int32_t value, int chan)
{
struct entropy_data *c = wps->w.c + chan;
uint32_t ones_count, low, mid, high;
int sign = (value < 0) ? 1 : 0;
if (sign)
value = ~value;
if ((wps->wphdr.flags & HYBRID_FLAG) && !chan)
update_error_limit (wps);
if (value < (int32_t) GET_MED (0)) {
low = 0;
high = GET_MED (0) - 1;
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (value - low < GET_MED (1)) {
high = low + GET_MED (1) - 1;
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (value - low < GET_MED (2)) {
high = low + GET_MED (2) - 1;
DEC_MED2 ();
}
else {
ones_count = 2 + (value - low) / GET_MED (2);
low += (ones_count - 2) * GET_MED (2);
high = low + GET_MED (2) - 1;
INC_MED2 ();
}
}
}
mid = (high + low + 1) >> 1;
if (!c->error_limit)
mid = value;
else
while (high - low > c->error_limit)
if (value < (int32_t) mid)
mid = ((high = mid - 1) + low + 1) >> 1;
else
mid = (high + (low = mid) + 1) >> 1;
c->slow_level -= (c->slow_level + SLO) >> SLS;
c->slow_level += wp_log2 (mid);
return sign ? ~mid : mid;
}
// This function is used to scan some number of samples to set the variables
// "slow_level" and the "median" array. In pure symmetrical encoding mode this
// would not be needed because these values would simply be continued from the
// previous block. However, in the -X modes and the 32-bit modes we cannot do
// this because parameters may change between blocks and the variables might
// not apply. This function can work in mono or stereo and can scan a block
// in either direction.
static void scan_word_pass (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir)
{
uint32_t flags = wps->wphdr.flags, value, low;
struct entropy_data *c = wps->w.c;
int chan;
if (flags & MONO_DATA) {
if (dir < 0) {
samples += (num_samples - 1);
dir = -1;
}
else
dir = 1;
}
else {
if (dir < 0) {
samples += (num_samples - 1) * 2;
dir = -2;
}
else
dir = 2;
}
while (num_samples--) {
value = labs (samples [chan = 0]);
if (flags & HYBRID_BITRATE) {
wps->w.c [0].slow_level -= (wps->w.c [0].slow_level + SLO) >> SLS;
wps->w.c [0].slow_level += wp_log2 (value);
}
if (value < GET_MED (0)) {
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (value - low < GET_MED (1)) {
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (value - low < GET_MED (2)) {
DEC_MED2 ();
}
else {
INC_MED2 ();
}
}
}
if (!(flags & MONO_DATA)) {
value = labs (samples [chan = 1]);
c++;
if (wps->wphdr.flags & HYBRID_BITRATE) {
wps->w.c [1].slow_level -= (wps->w.c [1].slow_level + SLO) >> SLS;
wps->w.c [1].slow_level += wp_log2 (value);
}
if (value < GET_MED (0)) {
DEC_MED0 ();
}
else {
low = GET_MED (0);
INC_MED0 ();
if (value - low < GET_MED (1)) {
DEC_MED1 ();
}
else {
low += GET_MED (1);
INC_MED1 ();
if (value - low < GET_MED (2)) {
DEC_MED2 ();
}
else {
INC_MED2 ();
}
}
}
c--;
}
samples += dir;
}
}
// Wrapper for scan_word_pass() than ensures that at least 2048 samples are processed by
// potentially making multiple passes through the data. See description of scan_word_pass()
// for more details.
void scan_word (WavpackStream *wps, int32_t *samples, uint32_t num_samples, int dir)
{
init_words (wps);
if (num_samples) {
int passes = (2048 + num_samples - 1) / num_samples; // i.e., ceil (2048.0 / num_samples)
while (passes--)
scan_word_pass (wps, samples, num_samples, dir);
}
}

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View file

@ -1,554 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
8310BA351D7377850055CEC5 /* common_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA281D7377850055CEC5 /* common_utils.c */; };
8310BA361D7377850055CEC5 /* decorr_tables.h in Headers */ = {isa = PBXBuildFile; fileRef = 8310BA291D7377850055CEC5 /* decorr_tables.h */; };
8310BA371D7377850055CEC5 /* decorr_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2A1D7377850055CEC5 /* decorr_utils.c */; };
8310BA381D7377850055CEC5 /* entropy_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2B1D7377850055CEC5 /* entropy_utils.c */; };
8310BA391D7377850055CEC5 /* open_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2C1D7377850055CEC5 /* open_utils.c */; };
8310BA3A1D7377850055CEC5 /* read_words.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2D1D7377850055CEC5 /* read_words.c */; };
8310BA3B1D7377850055CEC5 /* tag_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2E1D7377850055CEC5 /* tag_utils.c */; };
8310BA3C1D7377850055CEC5 /* unpack_dsd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA2F1D7377850055CEC5 /* unpack_dsd.c */; };
8310BA3D1D7377850055CEC5 /* unpack_floats.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA301D7377850055CEC5 /* unpack_floats.c */; };
8310BA3E1D7377850055CEC5 /* unpack_seek.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA311D7377850055CEC5 /* unpack_seek.c */; };
8310BA3F1D7377850055CEC5 /* unpack_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA321D7377850055CEC5 /* unpack_utils.c */; };
8310BA401D7377850055CEC5 /* unpack3_open.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA331D7377850055CEC5 /* unpack3_open.c */; };
8310BA411D7377850055CEC5 /* unpack3_seek.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA341D7377850055CEC5 /* unpack3_seek.c */; };
8310BA431D7377B80055CEC5 /* write_words.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA421D7377B80055CEC5 /* write_words.c */; };
8310BA4A1D7377DC0055CEC5 /* open_filename.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA441D7377DC0055CEC5 /* open_filename.c */; };
8310BA4B1D7377DC0055CEC5 /* open_legacy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA451D7377DC0055CEC5 /* open_legacy.c */; };
8310BA4C1D7377DC0055CEC5 /* pack_dns.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA461D7377DC0055CEC5 /* pack_dns.c */; };
8310BA4D1D7377DC0055CEC5 /* pack_dsd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA471D7377DC0055CEC5 /* pack_dsd.c */; };
8310BA4E1D7377DC0055CEC5 /* pack_floats.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA481D7377DC0055CEC5 /* pack_floats.c */; };
8310BA4F1D7377DC0055CEC5 /* pack_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8310BA491D7377DC0055CEC5 /* pack_utils.c */; };
83DD1DD017FA038A00249519 /* utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DD1DCD17FA038A00249519 /* utils.h */; settings = {ATTRIBUTES = (Public, ); }; };
83DD1DD117FA038A00249519 /* wavpack_local.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DD1DCE17FA038A00249519 /* wavpack_local.h */; };
83DD1DD217FA038A00249519 /* wavpack_version.h in Headers */ = {isa = PBXBuildFile; fileRef = 83DD1DCF17FA038A00249519 /* wavpack_version.h */; };
83DD1DD417FA03F900249519 /* tags.c in Sources */ = {isa = PBXBuildFile; fileRef = 83DD1DD317FA03F900249519 /* tags.c */; };
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
8E7574B309F31BB90080F1EE /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E7574AF09F31BB90080F1EE /* md5.h */; settings = {ATTRIBUTES = (Public, ); }; };
8E7574B409F31BB90080F1EE /* unpack3.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E7574B009F31BB90080F1EE /* unpack3.h */; settings = {ATTRIBUTES = (Public, ); }; };
8E7574B509F31BB90080F1EE /* wavpack.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E7574B109F31BB90080F1EE /* wavpack.h */; settings = {ATTRIBUTES = (Public, ); }; };
8E7574C609F31BD50080F1EE /* extra1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574B809F31BD50080F1EE /* extra1.c */; };
8E7574C709F31BD50080F1EE /* extra2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574B909F31BD50080F1EE /* extra2.c */; };
8E7574C909F31BD50080F1EE /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574BB09F31BD50080F1EE /* md5.c */; };
8E7574CB09F31BD50080F1EE /* pack.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574BD09F31BD50080F1EE /* pack.c */; };
8E7574CC09F31BD50080F1EE /* unpack.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574BE09F31BD50080F1EE /* unpack.c */; };
8E7574CD09F31BD50080F1EE /* unpack3.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574BF09F31BD50080F1EE /* unpack3.c */; };
8E7574CE09F31BD50080F1EE /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 8E7574C009F31BD50080F1EE /* utils.c */; };
8E7574F509F31C7D0080F1EE /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E7574F409F31C7D0080F1EE /* libiconv.dylib */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
8310BA281D7377850055CEC5 /* common_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = common_utils.c; path = Files/common_utils.c; sourceTree = "<group>"; };
8310BA291D7377850055CEC5 /* decorr_tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = decorr_tables.h; path = Files/decorr_tables.h; sourceTree = "<group>"; };
8310BA2A1D7377850055CEC5 /* decorr_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = decorr_utils.c; path = Files/decorr_utils.c; sourceTree = "<group>"; };
8310BA2B1D7377850055CEC5 /* entropy_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = entropy_utils.c; path = Files/entropy_utils.c; sourceTree = "<group>"; };
8310BA2C1D7377850055CEC5 /* open_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = open_utils.c; path = Files/open_utils.c; sourceTree = "<group>"; };
8310BA2D1D7377850055CEC5 /* read_words.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = read_words.c; path = Files/read_words.c; sourceTree = "<group>"; };
8310BA2E1D7377850055CEC5 /* tag_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tag_utils.c; path = Files/tag_utils.c; sourceTree = "<group>"; };
8310BA2F1D7377850055CEC5 /* unpack_dsd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack_dsd.c; path = Files/unpack_dsd.c; sourceTree = "<group>"; };
8310BA301D7377850055CEC5 /* unpack_floats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack_floats.c; path = Files/unpack_floats.c; sourceTree = "<group>"; };
8310BA311D7377850055CEC5 /* unpack_seek.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack_seek.c; path = Files/unpack_seek.c; sourceTree = "<group>"; };
8310BA321D7377850055CEC5 /* unpack_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack_utils.c; path = Files/unpack_utils.c; sourceTree = "<group>"; };
8310BA331D7377850055CEC5 /* unpack3_open.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack3_open.c; path = Files/unpack3_open.c; sourceTree = "<group>"; };
8310BA341D7377850055CEC5 /* unpack3_seek.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unpack3_seek.c; path = Files/unpack3_seek.c; sourceTree = "<group>"; };
8310BA421D7377B80055CEC5 /* write_words.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = write_words.c; path = Files/write_words.c; sourceTree = "<group>"; };
8310BA441D7377DC0055CEC5 /* open_filename.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = open_filename.c; path = Files/open_filename.c; sourceTree = "<group>"; };
8310BA451D7377DC0055CEC5 /* open_legacy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = open_legacy.c; path = Files/open_legacy.c; sourceTree = "<group>"; };
8310BA461D7377DC0055CEC5 /* pack_dns.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pack_dns.c; path = Files/pack_dns.c; sourceTree = "<group>"; };
8310BA471D7377DC0055CEC5 /* pack_dsd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pack_dsd.c; path = Files/pack_dsd.c; sourceTree = "<group>"; };
8310BA481D7377DC0055CEC5 /* pack_floats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pack_floats.c; path = Files/pack_floats.c; sourceTree = "<group>"; };
8310BA491D7377DC0055CEC5 /* pack_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pack_utils.c; path = Files/pack_utils.c; sourceTree = "<group>"; };
833F683A1CDBCAB200AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
835C889B22CC188A001B4B3F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83747BDC2862D5C50021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
838EE8D329A8600D00CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83DD1DCD17FA038A00249519 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utils.h; path = Files/utils.h; sourceTree = "<group>"; };
83DD1DCE17FA038A00249519 /* wavpack_local.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wavpack_local.h; path = Files/wavpack_local.h; sourceTree = "<group>"; };
83DD1DCF17FA038A00249519 /* wavpack_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wavpack_version.h; path = Files/wavpack_version.h; sourceTree = "<group>"; };
83DD1DD317FA03F900249519 /* tags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tags.c; path = Files/tags.c; sourceTree = "<group>"; };
83F0E6CD287CAB4400D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* WavPack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WavPack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8E7574AF09F31BB90080F1EE /* md5.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = md5.h; path = Files/md5.h; sourceTree = "<group>"; };
8E7574B009F31BB90080F1EE /* unpack3.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = unpack3.h; path = Files/unpack3.h; sourceTree = "<group>"; };
8E7574B109F31BB90080F1EE /* wavpack.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = wavpack.h; path = Files/wavpack.h; sourceTree = "<group>"; };
8E7574B809F31BD50080F1EE /* extra1.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = extra1.c; path = Files/extra1.c; sourceTree = "<group>"; };
8E7574B909F31BD50080F1EE /* extra2.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = extra2.c; path = Files/extra2.c; sourceTree = "<group>"; };
8E7574BB09F31BD50080F1EE /* md5.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = md5.c; path = Files/md5.c; sourceTree = "<group>"; };
8E7574BD09F31BD50080F1EE /* pack.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = pack.c; path = Files/pack.c; sourceTree = "<group>"; };
8E7574BE09F31BD50080F1EE /* unpack.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = unpack.c; path = Files/unpack.c; sourceTree = "<group>"; };
8E7574BF09F31BD50080F1EE /* unpack3.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = unpack3.c; path = Files/unpack3.c; sourceTree = "<group>"; };
8E7574C009F31BD50080F1EE /* utils.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = utils.c; path = Files/utils.c; sourceTree = "<group>"; };
8E7574F409F31C7D0080F1EE /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = /usr/lib/libiconv.dylib; sourceTree = "<absolute>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DC2EF560486A6940098B216 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8E7574F509F31C7D0080F1EE /* libiconv.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
034768DFFF38A50411DB9C8B /* Products */ = {
isa = PBXGroup;
children = (
8DC2EF5B0486A6940098B216 /* WavPack.framework */,
);
name = Products;
sourceTree = "<group>";
};
0867D691FE84028FC02AAC07 /* WavPack */ = {
isa = PBXGroup;
children = (
83747BDB2862D5C50021245F /* Xcode-config */,
8E7574A909F31B9A0080F1EE /* Headers */,
8E7574A809F31B940080F1EE /* Source */,
089C1665FE841158C02AAC07 /* Resources */,
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
034768DFFF38A50411DB9C8B /* Products */,
);
name = WavPack;
sourceTree = "<group>";
};
0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = {
isa = PBXGroup;
children = (
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */,
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */,
);
name = "External Frameworks and Libraries";
sourceTree = "<group>";
};
089C1665FE841158C02AAC07 /* Resources */ = {
isa = PBXGroup;
children = (
8DC2EF5A0486A6940098B216 /* Info.plist */,
089C1666FE841158C02AAC07 /* InfoPlist.strings */,
);
name = Resources;
sourceTree = "<group>";
};
1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
8E7574F409F31C7D0080F1EE /* libiconv.dylib */,
);
name = "Linked Frameworks";
sourceTree = "<group>";
};
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
);
name = "Other Frameworks";
sourceTree = "<group>";
};
83747BDB2862D5C50021245F /* Xcode-config */ = {
isa = PBXGroup;
children = (
83747BDC2862D5C50021245F /* Shared.xcconfig */,
);
name = "Xcode-config";
path = "../../Xcode-config";
sourceTree = "<group>";
};
8E7574A809F31B940080F1EE /* Source */ = {
isa = PBXGroup;
children = (
8310BA441D7377DC0055CEC5 /* open_filename.c */,
8310BA451D7377DC0055CEC5 /* open_legacy.c */,
8310BA461D7377DC0055CEC5 /* pack_dns.c */,
8310BA471D7377DC0055CEC5 /* pack_dsd.c */,
8310BA481D7377DC0055CEC5 /* pack_floats.c */,
8310BA491D7377DC0055CEC5 /* pack_utils.c */,
8310BA421D7377B80055CEC5 /* write_words.c */,
8310BA281D7377850055CEC5 /* common_utils.c */,
8310BA2A1D7377850055CEC5 /* decorr_utils.c */,
8310BA2B1D7377850055CEC5 /* entropy_utils.c */,
8310BA2C1D7377850055CEC5 /* open_utils.c */,
8310BA2D1D7377850055CEC5 /* read_words.c */,
8310BA2E1D7377850055CEC5 /* tag_utils.c */,
8310BA2F1D7377850055CEC5 /* unpack_dsd.c */,
8310BA301D7377850055CEC5 /* unpack_floats.c */,
8310BA311D7377850055CEC5 /* unpack_seek.c */,
8310BA321D7377850055CEC5 /* unpack_utils.c */,
8310BA331D7377850055CEC5 /* unpack3_open.c */,
8310BA341D7377850055CEC5 /* unpack3_seek.c */,
83DD1DD317FA03F900249519 /* tags.c */,
8E7574B809F31BD50080F1EE /* extra1.c */,
8E7574B909F31BD50080F1EE /* extra2.c */,
8E7574BB09F31BD50080F1EE /* md5.c */,
8E7574BD09F31BD50080F1EE /* pack.c */,
8E7574BE09F31BD50080F1EE /* unpack.c */,
8E7574BF09F31BD50080F1EE /* unpack3.c */,
8E7574C009F31BD50080F1EE /* utils.c */,
);
name = Source;
sourceTree = "<group>";
};
8E7574A909F31B9A0080F1EE /* Headers */ = {
isa = PBXGroup;
children = (
8310BA291D7377850055CEC5 /* decorr_tables.h */,
83DD1DCD17FA038A00249519 /* utils.h */,
83DD1DCE17FA038A00249519 /* wavpack_local.h */,
83DD1DCF17FA038A00249519 /* wavpack_version.h */,
8E7574AF09F31BB90080F1EE /* md5.h */,
8E7574B009F31BB90080F1EE /* unpack3.h */,
8E7574B109F31BB90080F1EE /* wavpack.h */,
);
name = Headers;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
8DC2EF500486A6940098B216 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
8E7574B309F31BB90080F1EE /* md5.h in Headers */,
83DD1DD117FA038A00249519 /* wavpack_local.h in Headers */,
8E7574B409F31BB90080F1EE /* unpack3.h in Headers */,
83DD1DD017FA038A00249519 /* utils.h in Headers */,
83DD1DD217FA038A00249519 /* wavpack_version.h in Headers */,
8310BA361D7377850055CEC5 /* decorr_tables.h in Headers */,
8E7574B509F31BB90080F1EE /* wavpack.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
8DC2EF4F0486A6940098B216 /* WavPack Framework */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "WavPack Framework" */;
buildPhases = (
8DC2EF500486A6940098B216 /* Headers */,
8DC2EF520486A6940098B216 /* Resources */,
8DC2EF540486A6940098B216 /* Sources */,
8DC2EF560486A6940098B216 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = "WavPack Framework";
productInstallPath = "$(HOME)/Library/Frameworks";
productName = WavPack;
productReference = 8DC2EF5B0486A6940098B216 /* WavPack.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1620;
TargetAttributes = {
8DC2EF4F0486A6940098B216 = {
DevelopmentTeam = "";
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WavPack" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = en;
hasScannedForEncodings = 1;
knownRegions = (
en,
es,
Base,
pl,
tr,
);
mainGroup = 0867D691FE84028FC02AAC07 /* WavPack */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
8DC2EF4F0486A6940098B216 /* WavPack Framework */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
8DC2EF520486A6940098B216 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8DC2EF540486A6940098B216 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8310BA4A1D7377DC0055CEC5 /* open_filename.c in Sources */,
8310BA4B1D7377DC0055CEC5 /* open_legacy.c in Sources */,
8310BA4C1D7377DC0055CEC5 /* pack_dns.c in Sources */,
8310BA4D1D7377DC0055CEC5 /* pack_dsd.c in Sources */,
8310BA4E1D7377DC0055CEC5 /* pack_floats.c in Sources */,
8310BA4F1D7377DC0055CEC5 /* pack_utils.c in Sources */,
8310BA431D7377B80055CEC5 /* write_words.c in Sources */,
8310BA351D7377850055CEC5 /* common_utils.c in Sources */,
8310BA371D7377850055CEC5 /* decorr_utils.c in Sources */,
8310BA381D7377850055CEC5 /* entropy_utils.c in Sources */,
8310BA391D7377850055CEC5 /* open_utils.c in Sources */,
8310BA3A1D7377850055CEC5 /* read_words.c in Sources */,
8310BA3B1D7377850055CEC5 /* tag_utils.c in Sources */,
8310BA3C1D7377850055CEC5 /* unpack_dsd.c in Sources */,
8310BA3D1D7377850055CEC5 /* unpack_floats.c in Sources */,
8310BA3E1D7377850055CEC5 /* unpack_seek.c in Sources */,
8310BA3F1D7377850055CEC5 /* unpack_utils.c in Sources */,
8310BA401D7377850055CEC5 /* unpack3_open.c in Sources */,
8310BA411D7377850055CEC5 /* unpack3_seek.c in Sources */,
83DD1DD417FA03F900249519 /* tags.c in Sources */,
8E7574C609F31BD50080F1EE /* extra1.c in Sources */,
8E7574C709F31BD50080F1EE /* extra2.c in Sources */,
8E7574C909F31BD50080F1EE /* md5.c in Sources */,
8E7574CB09F31BD50080F1EE /* pack.c in Sources */,
8E7574CC09F31BD50080F1EE /* unpack.c in Sources */,
8E7574CD09F31BD50080F1EE /* unpack3.c in Sources */,
8E7574CE09F31BD50080F1EE /* utils.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
833F683A1CDBCAB200AFB9F0 /* es */,
835C889B22CC188A001B4B3F /* en */,
83F0E6CD287CAB4400D84594 /* pl */,
838EE8D329A8600D00CD0580 /* tr */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
1DEB91AE08733DA50010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
ENABLE_DSD,
ENABLE_LEGACY,
PACK,
UNPACK,
USE_FSTREAMS,
TAGS,
SEEKING,
VER3,
"PACKAGE_NAME='\"wavpack\"'",
"PACKAGE_TARNAME='\"wavpack\"'",
"PACKAGE_VERSION='\"5.2.0\"'",
"PACKAGE_STRING='\"wavpack 5.2.0\"'",
"PACKAGE_BUGREPORT='\"bryant@wavpack.com\"'",
"VERSION_OS='\"Darwin\"'",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.wavpack;
PRODUCT_NAME = WavPack;
SDKROOT = macosx;
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
ZERO_LINK = YES;
};
name = Debug;
};
1DEB91AF08733DA50010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
GCC_PREPROCESSOR_DEFINITIONS = (
ENABLE_DSD,
ENABLE_LEGACY,
PACK,
UNPACK,
USE_FSTREAMS,
TAGS,
SEEKING,
VER3,
"PACKAGE_NAME='\"wavpack\"'",
"PACKAGE_TARNAME='\"wavpack\"'",
"PACKAGE_VERSION='\"4.70.0\"'",
"PACKAGE_STRING='\"wavpack 4.70.0\"'",
"PACKAGE_BUGREPORT='\"bryant@wavpack.com\"'",
"VERSION_OS='\"Darwin\"'",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.lib.wavpack;
PRODUCT_NAME = WavPack;
SDKROOT = macosx;
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
};
name = Release;
};
1DEB91B208733DA50010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 83747BDC2862D5C50021245F /* Shared.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-Wframe-larger-than=4000";
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
SDKROOT = macosx;
SYMROOT = ../../build;
};
name = Debug;
};
1DEB91B308733DA50010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 83747BDC2862D5C50021245F /* Shared.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_INHIBIT_ALL_WARNINGS = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.13;
OTHER_CFLAGS = "-Wframe-larger-than=4000";
OTHER_CPLUSPLUSFLAGS = "-Wframe-larger-than=16000";
SDKROOT = macosx;
SYMROOT = ../../build;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "WavPack Framework" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB91AE08733DA50010E9CD /* Debug */,
1DEB91AF08733DA50010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WavPack" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB91B208733DA50010E9CD /* Debug */,
1DEB91B308733DA50010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
}

View file

@ -1,76 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "WavPack.framework"
BlueprintName = "WavPack Framework"
ReferencedContainer = "container:WavPack.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "WavPack.framework"
BlueprintName = "WavPack Framework"
ReferencedContainer = "container:WavPack.xcodeproj">
</BuildableReference>
</MacroExpansion>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8DC2EF4F0486A6940098B216"
BuildableName = "WavPack.framework"
BlueprintName = "WavPack Framework"
ReferencedContainer = "container:WavPack.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -1,3 +0,0 @@
/* Localized versions of Info.plist keys */
NSHumanReadableCopyright = "© __MyCompanyName__, 2006";

View file

@ -232,12 +232,6 @@
BuildIndependentTargetsInParallel = YES; BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1620; LastUpgradeCheck = 1620;
ORGANIZATIONNAME = "Christopher Snowhill"; ORGANIZATIONNAME = "Christopher Snowhill";
TargetAttributes = {
83D7311B1A74968900CA1366 = {
DevelopmentTeam = "";
ProvisioningStyle = Automatic;
};
};
}; };
buildConfigurationList = 83D731161A74968900CA1366 /* Build configuration list for PBXProject "g719" */; buildConfigurationList = 83D731161A74968900CA1366 /* Build configuration list for PBXProject "g719" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
@ -422,9 +416,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -449,6 +445,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Debug; name = Debug;
@ -457,9 +454,11 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_IDENTITY = "-"; CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -481,6 +480,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11";
PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cogx.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Release; name = Release;

View file

@ -376,7 +376,7 @@
835FC6B923F61BF0006960FA /* Debug */ = { 835FC6B923F61BF0006960FA /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
@ -402,7 +402,7 @@
835FC6BA23F61BF0006960FA /* Release */ = { 835FC6BA23F61BF0006960FA /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;

View file

@ -550,9 +550,10 @@
83D6762926539A7100252130 /* Debug */ = { 83D6762926539A7100252130 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -561,6 +562,7 @@
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0061"; PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0061";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Debug; name = Debug;
@ -568,9 +570,10 @@
83D6762A26539A7100252130 /* Release */ = { 83D6762A26539A7100252130 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -579,6 +582,7 @@
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0061"; PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0061";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Release; name = Release;

View file

@ -538,9 +538,10 @@
83D676B726539F4E00252130 /* Debug */ = { 83D676B726539F4E00252130 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -549,6 +550,7 @@
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0110"; PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0110";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Debug; name = Debug;
@ -556,9 +558,10 @@
83D676B826539F4E00252130 /* Release */ = { 83D676B826539F4E00252130 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1; DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks"; DYLIB_INSTALL_NAME_BASE = "@loader_path/Frameworks";
@ -567,6 +570,7 @@
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0110"; PRODUCT_BUNDLE_IDENTIFIER = "net.kode54.libcelt-0110";
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
name = Release; name = Release;

@ -1 +1 @@
Subproject commit e5e82dcef993dfbae9ac26917b2bb8d4bd000932 Subproject commit 40e944362ebc349522a04600180d64f6b6eeb80d

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,23 @@
#include "api_internal.h" #include "api_internal.h"
#include "mixing.h" #include "mixing.h"
#define INTERNAL_BUF_SAMPLES 1024
LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void) { LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void) {
return (LIBVGMSTREAM_API_VERSION_MAJOR << 24) | (LIBVGMSTREAM_API_VERSION_MINOR << 16) | (LIBVGMSTREAM_API_VERSION_PATCH << 0); return (LIBVGMSTREAM_API_VERSION_MAJOR << 24) | (LIBVGMSTREAM_API_VERSION_MINOR << 16) | (LIBVGMSTREAM_API_VERSION_PATCH << 0);
} }
LIBVGMSTREAM_API libvgmstream_t* libvgmstream_create(libstreamfile_t* libsf, int subsong, libvgmstream_config_t* vcfg) {
libvgmstream_t* vgmstream = libvgmstream_init();
libvgmstream_setup(vgmstream, vcfg);
int err = libvgmstream_open_stream(vgmstream, libsf, subsong);
if (err < 0) {
libvgmstream_free(vgmstream);
return NULL;
}
return vgmstream;
}
LIBVGMSTREAM_API libvgmstream_t* libvgmstream_init(void) { LIBVGMSTREAM_API libvgmstream_t* libvgmstream_init(void) {
libvgmstream_t* lib = NULL; libvgmstream_t* lib = NULL;
@ -48,57 +58,85 @@ LIBVGMSTREAM_API void libvgmstream_free(libvgmstream_t* lib) {
free(lib); free(lib);
} }
// TODO: allow calling after load
LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg) { LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg) {
if (!lib || !lib->priv) if (!lib || !lib->priv)
return; return;
libvgmstream_priv_t* priv = lib->priv; libvgmstream_priv_t* priv = lib->priv;
// Can only apply once b/c some options modify the internal mixing chain and there is no clean way to
// reset the stream when txtp also manipulates it (maybe could add some flag per mixing item)
if (priv->setup_done)
return;
// allow overwritting current config, though will only apply to next load
//if (priv->config_loaded)
// return;
if (!cfg) { if (!cfg) {
memset(&priv->cfg , 0, sizeof(libvgmstream_config_t)); memset(&priv->cfg , 0, sizeof(libvgmstream_config_t));
priv->cfg.loop_count = 1; //TODO: loop 0 means no loop (improve detection) priv->config_loaded = false;
} }
else { else {
priv->cfg = *cfg; priv->cfg = *cfg;
priv->config_loaded = true;
} }
//TODO validate, etc //TODO validate, etc (for now most incorrect values are ignored)
// apply now if possible to update format info
if (priv->vgmstream) {
api_apply_config(priv);
}
} }
void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool reset_buf) { void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool full) {
//memset(&priv->cfg, 0, sizeof(libvgmstream_config_t)); //config is always valid //memset(&priv->cfg, 0, sizeof(libvgmstream_config_t)); //config is always valid
memset(&priv->fmt, 0, sizeof(libvgmstream_format_t));
memset(&priv->dec, 0, sizeof(libvgmstream_decoder_t));
//memset(&priv->pos, 0, sizeof(libvgmstream_priv_position_t)); //position info is updated on open //memset(&priv->pos, 0, sizeof(libvgmstream_priv_position_t)); //position info is updated on open
memset(&priv->dec, 0, sizeof(libvgmstream_decoder_t));
if (reset_buf) { if (full) {
free(priv->buf.data); free(priv->buf.data); //TODO
memset(&priv->buf, 0, sizeof(libvgmstream_priv_buf_t)); memset(&priv->buf, 0, sizeof(libvgmstream_priv_buf_t));
memset(&priv->fmt, 0, sizeof(libvgmstream_format_t));
}
else {
priv->buf.consumed = priv->buf.samples;
} }
priv->pos.current = 0; priv->pos.current = 0;
priv->decode_done = false; priv->decode_done = false;
} }
libvgmstream_sample_t api_get_output_sample_type(libvgmstream_priv_t* priv) { libvgmstream_sfmt_t api_get_output_sample_type(libvgmstream_priv_t* priv) {
VGMSTREAM* v = priv->vgmstream; VGMSTREAM* v = priv->vgmstream;
sfmt_t format = mixing_get_output_sample_type(v); sfmt_t format = mixing_get_output_sample_type(v);
switch(format) { switch(format) {
case SFMT_S16: return LIBVGMSTREAM_SAMPLE_PCM16; case SFMT_S16: return LIBVGMSTREAM_SFMT_PCM16;
case SFMT_FLT: return LIBVGMSTREAM_SAMPLE_FLOAT; case SFMT_FLT: return LIBVGMSTREAM_SFMT_FLOAT;
case SFMT_S32: return LIBVGMSTREAM_SFMT_PCM32;
case SFMT_O24: return LIBVGMSTREAM_SFMT_PCM24;
// internal use only, shouldn't happen (misconfigured, see prepare_mixing)
case SFMT_S24:
case SFMT_F16:
default: default:
return 0x00; //??? return 0x00;
} }
} }
int api_get_sample_size(libvgmstream_sample_t sample_type) { int api_get_sample_size(libvgmstream_sfmt_t sample_format) {
switch(sample_type) { switch(sample_format) {
case LIBVGMSTREAM_SAMPLE_PCM24: case LIBVGMSTREAM_SFMT_FLOAT:
case LIBVGMSTREAM_SAMPLE_PCM32: case LIBVGMSTREAM_SFMT_PCM32:
case LIBVGMSTREAM_SAMPLE_FLOAT:
return 0x04; return 0x04;
case LIBVGMSTREAM_SAMPLE_PCM16: case LIBVGMSTREAM_SFMT_PCM24:
return 0x03;
case LIBVGMSTREAM_SFMT_PCM16:
default: default:
return 0x02; return 0x02;
} }

View file

@ -3,18 +3,6 @@
#include "mixing.h" #include "mixing.h"
static void load_vgmstream(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
STREAMFILE* sf_api = open_api_streamfile(opt->libsf);
if (!sf_api)
return;
//TODO: handle internal format_id
sf_api->stream_index = opt->subsong_index;
priv->vgmstream = init_vgmstream_from_STREAMFILE(sf_api);
close_streamfile(sf_api);
}
static void apply_config(libvgmstream_priv_t* priv) { static void apply_config(libvgmstream_priv_t* priv) {
libvgmstream_config_t* cfg = &priv->cfg; libvgmstream_config_t* cfg = &priv->cfg;
@ -36,23 +24,52 @@ static void apply_config(libvgmstream_priv_t* priv) {
if (!vcfg.allow_play_forever) if (!vcfg.allow_play_forever)
vcfg.play_forever = 0; vcfg.play_forever = 0;
// Traditionally in CLI loop_count = 0 removes loops but this is pretty odd for a lib
// (calling _setup with nothing set would remove most audio).
// For now loop_count 0 is set to 1, and loop_count <0 is assumed to be same 0
if (vcfg.loop_count == 0) {
vcfg.loop_count = 1;
} else if (vcfg.loop_count < 0)
vcfg.loop_count = 0;
vgmstream_apply_config(priv->vgmstream, &vcfg); vgmstream_apply_config(priv->vgmstream, &vcfg);
} }
static void prepare_mixing(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) { static void prepare_mixing(libvgmstream_priv_t* priv) {
libvgmstream_config_t* cfg = &priv->cfg;
/* enable after config but before outbuf */ /* enable after config but before outbuf */
if (priv->cfg.auto_downmix_channels) { if (cfg->auto_downmix_channels) {
vgmstream_mixing_autodownmix(priv->vgmstream, priv->cfg.auto_downmix_channels); vgmstream_mixing_autodownmix(priv->vgmstream, cfg->auto_downmix_channels);
} }
else if (opt && opt->stereo_track >= 1) { else if (cfg->stereo_track >= 1) {
vgmstream_mixing_stereo_only(priv->vgmstream, opt->stereo_track - 1); vgmstream_mixing_stereo_only(priv->vgmstream, cfg->stereo_track - 1);
} }
if (priv->cfg.force_pcm16) { if (cfg->force_sfmt) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_S16); // external force
sfmt_t force_sfmt = SFMT_NONE;
switch(cfg->force_sfmt) {
case LIBVGMSTREAM_SFMT_PCM16: force_sfmt = SFMT_S16; break;
case LIBVGMSTREAM_SFMT_FLOAT: force_sfmt = SFMT_FLT; break;
case LIBVGMSTREAM_SFMT_PCM24: force_sfmt = SFMT_O24; break;
case LIBVGMSTREAM_SFMT_PCM32: force_sfmt = SFMT_S32; break;
default: break;
} }
else if (priv->cfg.force_float) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_FLT); mixing_macro_output_sample_format(priv->vgmstream, force_sfmt);
}
else {
// internal force, swap certain internal bufs into standard output
sfmt_t force_sfmt = SFMT_NONE;
sfmt_t input_sfmt = mixing_get_input_sample_type(priv->vgmstream);
switch(input_sfmt) {
case SFMT_F16: force_sfmt = SFMT_FLT; break;
case SFMT_S24: force_sfmt = SFMT_O24; break;
default: break;
}
mixing_macro_output_sample_format(priv->vgmstream, force_sfmt);
} }
vgmstream_mixing_enable(priv->vgmstream, INTERNAL_BUF_SAMPLES, NULL /*&input_channels*/, NULL /*&output_channels*/); vgmstream_mixing_enable(priv->vgmstream, INTERNAL_BUF_SAMPLES, NULL /*&input_channels*/, NULL /*&output_channels*/);
@ -76,11 +93,11 @@ static void update_format_info(libvgmstream_priv_t* priv) {
fmt->channels = v->channels; fmt->channels = v->channels;
fmt->input_channels = 0; fmt->input_channels = 0;
vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels); vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels); //query
fmt->channel_layout = v->channel_layout; fmt->channel_layout = v->channel_layout;
fmt->sample_type = api_get_output_sample_type(priv); fmt->sample_format = api_get_output_sample_type(priv);
fmt->sample_size = api_get_sample_size(fmt->sample_type); fmt->sample_size = api_get_sample_size(fmt->sample_format);
fmt->sample_rate = v->sample_rate; fmt->sample_rate = v->sample_rate;
@ -105,26 +122,58 @@ static void update_format_info(libvgmstream_priv_t* priv) {
} }
} }
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* opt) { // apply config if data + config is loaded and not already loaded
if (!lib ||!lib->priv) void api_apply_config(libvgmstream_priv_t* priv) {
return LIBVGMSTREAM_ERROR_GENERIC; if (priv->setup_done)
if (!opt || !opt->libsf || opt->subsong_index < 0) return;
if (!priv->vgmstream)
return;
apply_config(priv);
prepare_mixing(priv);
update_position(priv);
update_format_info(priv);
priv->setup_done = true;
}
static void load_vgmstream(libvgmstream_priv_t* priv, libstreamfile_t* libsf, int subsong_index) {
STREAMFILE* sf_api = open_api_streamfile(libsf);
if (!sf_api)
return;
//TODO: handle format_id
sf_api->stream_index = subsong_index;
priv->vgmstream = init_vgmstream_from_STREAMFILE(sf_api);
close_streamfile(sf_api);
}
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libstreamfile_t* libsf, int subsong_index) {
if (!lib ||!lib->priv || !libsf)
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
// close loaded song if any + reset // close loaded song if any + reset
libvgmstream_close_stream(lib); libvgmstream_close_stream(lib);
libvgmstream_priv_t* priv = lib->priv; libvgmstream_priv_t* priv = lib->priv;
if (subsong_index < 0)
return LIBVGMSTREAM_ERROR_GENERIC;
load_vgmstream(priv, opt); load_vgmstream(priv, libsf, subsong_index);
if (!priv->vgmstream) if (!priv->vgmstream)
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
apply_config(priv);
prepare_mixing(priv, opt); // apply now if possible to update format info
if (priv->config_loaded) {
api_apply_config(priv);
}
else {
// no config: just update info (apply_config will be called later)
update_position(priv); update_position(priv);
update_format_info(priv); update_format_info(priv);
}
return LIBVGMSTREAM_OK; return LIBVGMSTREAM_OK;
} }
@ -138,6 +187,8 @@ LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib) {
close_vgmstream(priv->vgmstream); close_vgmstream(priv->vgmstream);
priv->vgmstream = NULL; priv->vgmstream = NULL;
priv->setup_done = false;
//priv->config_loaded = false; // loaded config still applies (_close is also called on _open)
libvgmstream_priv_reset(priv, true); libvgmstream_priv_reset(priv, true);
} }

View file

@ -1,6 +1,7 @@
#include "api_internal.h" #include "api_internal.h"
#include "mixing.h" #include "mixing.h"
#include "render.h" #include "render.h"
#include "../util/log.h"
static bool reset_buf(libvgmstream_priv_t* priv) { static bool reset_buf(libvgmstream_priv_t* priv) {
@ -46,15 +47,16 @@ static void update_buf(libvgmstream_priv_t* priv, int samples_done) {
priv->buf.bytes = samples_done * priv->buf.sample_size * priv->buf.channels; priv->buf.bytes = samples_done * priv->buf.sample_size * priv->buf.channels;
//priv->buf.consumed = 0; //external //priv->buf.consumed = 0; //external
// mark done if this buf reached EOF
if (!priv->pos.play_forever) { if (!priv->pos.play_forever) {
priv->decode_done = (priv->pos.current >= priv->pos.play_samples);
priv->pos.current += samples_done; priv->pos.current += samples_done;
priv->decode_done = (priv->pos.current >= priv->pos.play_samples);
} }
} }
// update decoder info based on last render, though at the moment it's all fixed // update decoder info based on last render, though at the moment it's all fixed
static void update_decoder_info(libvgmstream_priv_t* priv, int samples_done) { static void update_decoder_info(libvgmstream_priv_t* priv) {
// output copy // output copy
priv->dec.buf = priv->buf.data; priv->dec.buf = priv->buf.data;
@ -68,6 +70,13 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
libvgmstream_priv_t* priv = lib->priv; libvgmstream_priv_t* priv = lib->priv;
// setup if not called (mainly to make sure mixing is enabled) //TODO: handle internally
// (for cases where _open_stream is called but not _setup)
if (!priv->setup_done) {
api_apply_config(priv);
}
if (priv->decode_done) if (priv->decode_done)
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
@ -84,7 +93,7 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
int decoded = render_main(&ssrc, priv->vgmstream); int decoded = render_main(&ssrc, priv->vgmstream);
update_buf(priv, decoded); update_buf(priv, decoded);
update_decoder_info(priv, decoded); update_decoder_info(priv);
return LIBVGMSTREAM_OK; return LIBVGMSTREAM_OK;
} }
@ -128,6 +137,11 @@ LIBVGMSTREAM_API int libvgmstream_fill(libvgmstream_t* lib, void* buf, int buf_s
buf_copied += copy_samples; buf_copied += copy_samples;
} }
// detect EOF, to avoid another call to _fill that returns with 0 samples
if (!done && priv->decode_done && priv->buf.consumed >= priv->buf.samples) {
done = true;
}
// TODO improve // TODO improve
priv->dec.buf = buf; priv->dec.buf = buf;
priv->dec.buf_samples = buf_copied; priv->dec.buf_samples = buf_copied;
@ -161,6 +175,10 @@ LIBVGMSTREAM_API void libvgmstream_seek(libvgmstream_t* lib, int64_t sample) {
seek_vgmstream(priv->vgmstream, sample); seek_vgmstream(priv->vgmstream, sample);
priv->pos.current = priv->vgmstream->pstate.play_position; priv->pos.current = priv->vgmstream->pstate.play_position;
// update flags just in case
update_buf(priv, 0);
update_decoder_info(priv);
} }

View file

@ -12,28 +12,33 @@ static int get_internal_log_level(libvgmstream_loglevel_t level) {
} }
} }
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_log_t* cfg) { LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_loglevel_t level, void (*callback)(int level, const char* str)) {
if (!cfg) int ilevel = get_internal_log_level(level);
return; if (callback) {
vgm_log_set_callback(NULL, ilevel, 0, callback);
int ilevel = get_internal_log_level(cfg->level);
if (cfg->stdout_callback) {
//vgmstream_set_log_stdout(ilevel);
vgm_log_set_callback(NULL, ilevel, 1, NULL);
} }
else { else {
//vgmstream_set_log_callback(ilevel, cfg->callback); vgm_log_set_callback(NULL, ilevel, 1, NULL);
vgm_log_set_callback(NULL, ilevel, 0, cfg->callback);
} }
} }
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(size_t* size) { LIBVGMSTREAM_API const char** libvgmstream_get_extensions(int* size) {
return vgmstream_get_formats(size); if (!size)
return NULL;
size_t tmp = 0;
const char** list = vgmstream_get_formats(&tmp);
*size = tmp;
return list;
} }
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size) { LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(int* size) {
return vgmstream_get_common_formats(size); if (!size)
return NULL;
size_t tmp = 0;
const char** list = vgmstream_get_common_formats(&tmp);
*size = tmp;
return list;
} }
@ -69,11 +74,11 @@ LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_v
LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_title_t* cfg, char* buf, int buf_len) { LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_title_t* cfg, char* buf, int buf_len) {
if (!buf || !buf_len || !cfg) if (!buf || !buf_len)
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
buf[0] = '\0'; buf[0] = '\0';
if (!lib || !lib->priv) if (!lib || !lib->priv || !cfg)
return LIBVGMSTREAM_ERROR_GENERIC; return LIBVGMSTREAM_ERROR_GENERIC;
libvgmstream_priv_t* priv = lib->priv; libvgmstream_priv_t* priv = lib->priv;

View file

@ -33,31 +33,36 @@ typedef struct {
} libvgmstream_priv_buf_t; } libvgmstream_priv_buf_t;
// used to calculate and stop stream (to be removed once VGMSTREAM can stop on its own)
typedef struct { typedef struct {
int64_t play_forever; int64_t play_forever;
int64_t play_samples; int64_t play_samples;
int64_t current; int64_t current;
} libvgmstream_priv_position_t; } libvgmstream_priv_position_t;
/* vgmstream context/handle */ // vgmstream context/handle
typedef struct { typedef struct {
libvgmstream_format_t fmt; // externally exposed // externally exposed to API
libvgmstream_decoder_t dec; // externally exposed libvgmstream_format_t fmt;
libvgmstream_decoder_t dec;
libvgmstream_config_t cfg; // internal copy // internals
libvgmstream_config_t cfg;
VGMSTREAM* vgmstream; VGMSTREAM* vgmstream;
libvgmstream_priv_buf_t buf; libvgmstream_priv_buf_t buf;
libvgmstream_priv_position_t pos; libvgmstream_priv_position_t pos;
bool config_loaded;
bool setup_done;
bool decode_done; bool decode_done;
} libvgmstream_priv_t; } libvgmstream_priv_t;
void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool reset_buf); void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool full);
libvgmstream_sample_t api_get_output_sample_type(libvgmstream_priv_t* priv); libvgmstream_sfmt_t api_get_output_sample_type(libvgmstream_priv_t* priv);
int api_get_sample_size(libvgmstream_sample_t sample_type); int api_get_sample_size(libvgmstream_sfmt_t sample_format);
void api_apply_config(libvgmstream_priv_t* priv);
STREAMFILE* open_api_streamfile(libstreamfile_t* libsf); STREAMFILE* open_api_streamfile(libstreamfile_t* libsf);

View file

@ -5,63 +5,23 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
/* libstreamfile_t for external use, as a default implementation calling some internal SF */ /* libstreamfile_t for external use, as a default implementation calling some internal SF */
typedef struct { typedef struct {
int64_t offset;
int64_t size; int64_t size;
STREAMFILE* sf; STREAMFILE* sf;
char name[PATH_LIMIT]; char name[PATH_LIMIT];
} libsf_priv_t; } libsf_priv_t;
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) { static int libsf_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
libsf_priv_t* priv = user_data; libsf_priv_t* priv = user_data;
if (!priv || !dst) return priv->sf->read(priv->sf, dst, offset, length);
return 0;
int bytes = priv->sf->read(priv->sf, dst, priv->offset, dst_size);
priv->offset += bytes;
return bytes;
}
static int64_t libsf_seek(void* user_data, int64_t offset, int whence) {
libsf_priv_t* priv = user_data;
if (!priv)
return -1;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += priv->size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > priv->size)
offset = priv->size;
else if (offset < 0)
offset = 0;
/* main seek */
priv->offset = offset;
return 0;
} }
static int64_t libsf_get_size(void* user_data) { static int64_t libsf_get_size(void* user_data) {
libsf_priv_t* priv = user_data; libsf_priv_t* priv = user_data;
if (!priv) return priv->sf->get_size(priv->sf);
return 0;
return priv->size;
} }
static const char* libsf_get_name(void* user_data) { static const char* libsf_get_name(void* user_data) {
libsf_priv_t* priv = user_data; libsf_priv_t* priv = user_data;
if (!priv)
return NULL;
if (priv->name[0] == '\0') { if (priv->name[0] == '\0') {
priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name)); priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name));
@ -72,7 +32,8 @@ static const char* libsf_get_name(void* user_data) {
static libstreamfile_t* libsf_open(void* user_data, const char* filename) { static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
libsf_priv_t* priv = user_data; libsf_priv_t* priv = user_data;
if (!priv || !priv->sf || !filename)
if (!filename)
return NULL; return NULL;
STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0); STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0);
@ -104,14 +65,11 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
if (!sf) if (!sf)
return NULL; return NULL;
libstreamfile_t* libsf = NULL;
libsf_priv_t* priv = NULL; libsf_priv_t* priv = NULL;
libstreamfile_t* libsf = calloc(1, sizeof(libstreamfile_t));
libsf = calloc(1, sizeof(libstreamfile_t));
if (!libsf) goto fail; if (!libsf) goto fail;
libsf->read = libsf_read; libsf->read = libsf_read;
libsf->seek = libsf_seek;
libsf->get_size = libsf_get_size; libsf->get_size = libsf_get_size;
libsf->get_name = libsf_get_name; libsf->get_name = libsf_get_name;
libsf->open = libsf_open; libsf->open = libsf_open;

View file

@ -18,10 +18,10 @@ typedef struct {
char name[PATH_LIMIT]; char name[PATH_LIMIT];
} cache_priv_t; } cache_priv_t;
static int cache_read(void* user_data, uint8_t* dst, int dst_size) { static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
cache_priv_t* priv = user_data; cache_priv_t* priv = user_data;
size_t read_total = 0; size_t read_total = 0;
if (!dst || dst_size <= 0) if (!dst || length <= 0 || offset < 0)
return 0; return 0;
/* is the part of the requested length in the buffer? */ /* is the part of the requested length in the buffer? */
@ -30,19 +30,22 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
int buf_into = (int)(priv->offset - priv->buf_offset); int buf_into = (int)(priv->offset - priv->buf_offset);
buf_limit = priv->valid_size - buf_into; buf_limit = priv->valid_size - buf_into;
if (buf_limit > dst_size) if (buf_limit > length)
buf_limit = dst_size; buf_limit = length;
memcpy(dst, priv->buf + buf_into, buf_limit); memcpy(dst, priv->buf + buf_into, buf_limit);
read_total += buf_limit; read_total += buf_limit;
dst_size -= buf_limit; length -= buf_limit;
priv->offset += buf_limit; priv->offset += buf_limit;
dst += buf_limit; dst += buf_limit;
} }
/* possible if all data was copied to buf and FD closed */
if (!priv->libsf)
return read_total;
/* read the rest of the requested length */ /* read the rest of the requested length */
while (dst_size > 0) { while (length > 0) {
size_t buf_limit; size_t buf_limit;
/* ignore requests at EOF */ /* ignore requests at EOF */
@ -52,18 +55,15 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
break; break;
} }
/* position to new offset */
priv->libsf->seek(priv, priv->offset, 0);
/* fill the buffer (offset now is beyond buf_offset) */ /* fill the buffer (offset now is beyond buf_offset) */
priv->buf_offset = priv->offset; priv->buf_offset = priv->offset;
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_size); priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_offset, priv->buf_size);
/* decide how much must be read this time */ /* decide how much must be read this time */
if (dst_size > priv->buf_size) if (length > priv->buf_size)
buf_limit = priv->buf_size; buf_limit = priv->buf_size;
else else
buf_limit = dst_size; buf_limit = length;
/* give up on partial reads (EOF) */ /* give up on partial reads (EOF) */
if (priv->valid_size < buf_limit) { if (priv->valid_size < buf_limit) {
@ -77,40 +77,14 @@ static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
memcpy(dst, priv->buf, buf_limit); memcpy(dst, priv->buf, buf_limit);
priv->offset += buf_limit; priv->offset += buf_limit;
read_total += buf_limit; read_total += buf_limit;
dst_size -= buf_limit; length -= buf_limit;
dst += buf_limit; dst += buf_limit;
} }
priv->offset = offset; /* last fread offset */
return read_total; return read_total;
} }
static int64_t cache_seek(void* user_data, int64_t offset, int whence) {
cache_priv_t* priv = user_data;
switch (whence) {
case LIBSTREAMFILE_SEEK_SET: /* absolute */
break;
case LIBSTREAMFILE_SEEK_CUR: /* relative to current */
offset += priv->offset;
break;
case LIBSTREAMFILE_SEEK_END: /* relative to file end (should be negative) */
offset += priv->file_size;
break;
default:
break;
}
/* clamp offset like fseek */
if (offset > priv->file_size)
offset = priv->file_size;
else if (offset < 0)
offset = 0;
/* main seek */
priv->offset = offset;
return 0;
}
static int64_t cache_get_size(void* user_data) { static int64_t cache_get_size(void* user_data) {
cache_priv_t* priv = user_data; cache_priv_t* priv = user_data;
return priv->file_size; return priv->file_size;
@ -168,7 +142,6 @@ LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* e
if (!libsf) goto fail; if (!libsf) goto fail;
libsf->read = cache_read; libsf->read = cache_read;
libsf->seek = cache_seek;
libsf->get_size = cache_get_size; libsf->get_size = cache_get_size;
libsf->get_name = cache_get_name; libsf->get_name = cache_get_name;
libsf->open = cache_open; libsf->open = cache_open;

View file

@ -0,0 +1,91 @@
#include "codec_info.h"
//TODO: move to root folder?
extern const codec_info_t ka1a_decoder;
extern const codec_info_t ubimpeg_decoder;
extern const codec_info_t hca_decoder;
#ifdef VGM_USE_VORBIS
extern const codec_info_t ogg_vorbis_decoder;
extern const codec_info_t vorbis_custom_decoder;
#endif
extern const codec_info_t tac_decoder;
extern const codec_info_t compresswave_decoder;
extern const codec_info_t speex_decoder;
extern const codec_info_t imuse_decoder;
extern const codec_info_t mio_decoder;
extern const codec_info_t pcm32_decoder;
extern const codec_info_t pcm24_decoder;
extern const codec_info_t pcmfloat_decoder;
#ifdef VGM_USE_FFMPEG
extern const codec_info_t ffmpeg_decoder;
#endif
#ifdef VGM_USE_ATRAC9
extern const codec_info_t atrac9_decoder;
#endif
#ifdef VGM_USE_CELT
extern const codec_info_t celt_fsb_decoder;
#endif
#ifdef VGM_USE_MPEG
extern const codec_info_t mpeg_decoder;
#endif
extern const codec_info_t relic_decoder;
const codec_info_t* codec_get_info(VGMSTREAM* v) {
switch(v->coding_type) {
case coding_CRI_HCA:
return &hca_decoder;
case coding_KA1A:
return &ka1a_decoder;
case coding_UBI_MPEG:
return &ubimpeg_decoder;
#ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS:
return &ogg_vorbis_decoder;
case coding_VORBIS_custom:
return &vorbis_custom_decoder;
#endif
case coding_TAC:
return &tac_decoder;
case coding_COMPRESSWAVE:
return &compresswave_decoder;
#ifdef VGM_USE_SPEEX
case coding_SPEEX:
return &speex_decoder;
#endif
case coding_IMUSE:
return &imuse_decoder;
case coding_MIO:
return &mio_decoder;
case coding_PCM32LE:
return &pcm32_decoder;
case coding_PCM24LE:
case coding_PCM24BE:
return &pcm24_decoder;
case coding_PCMFLOAT:
return &pcmfloat_decoder;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
return &ffmpeg_decoder;
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return &atrac9_decoder;
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return &celt_fsb_decoder;
#endif
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
return &mpeg_decoder;
#endif
case coding_RELIC:
return &relic_decoder;
default:
return NULL;
}
}

View file

@ -0,0 +1,35 @@
#ifndef _CODEC_INFO_H
#define _CODEC_INFO_H
#include <stdint.h>
#include <stdbool.h>
#include "../vgmstream.h"
#include "../base/sbuf.h"
/* Class-like definition for codecs.
*/
typedef struct {
//int (*init)();
sfmt_t sample_type; // fixed for most cases; if not set will be assumed to be PCM16
sfmt_t (*get_sample_type)(VGMSTREAM* v); //variable for codecs with variations depending on data
bool (*decode_frame)(VGMSTREAM* v);
void (*free)(void* codec_data);
void (*reset)(void* codec_data);
void (*seek)(VGMSTREAM* v, int32_t num_sample);
bool (*decode_buf)(VGMSTREAM* v, sbuf_t* sdst); // alternate decoding for codecs that don't provide their own buffer
// info for vgmstream
//uint32_t flags;
// alloc size of effect's private data (don't set to manage manually in init/free)
//int priv_size;
//int sample_type;
//int get_sample_type();
} codec_info_t;
const codec_info_t* codec_get_info(VGMSTREAM* v);
#endif

View file

@ -5,6 +5,7 @@
#include "mixing.h" #include "mixing.h"
#include "plugins.h" #include "plugins.h"
#include "sbuf.h" #include "sbuf.h"
#include "codec_info.h"
#include "../util/log.h" #include "../util/log.h"
#include "decode_state.h" #include "decode_state.h"
@ -39,32 +40,16 @@ void decode_free(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data) if (!vgmstream->codec_data)
return; return;
#ifdef VGM_USE_VORBIS const codec_info_t* codec_info = codec_get_info(vgmstream);
if (vgmstream->coding_type == coding_OGG_VORBIS) { if (codec_info) {
free_ogg_vorbis(vgmstream->codec_data); codec_info->free(vgmstream->codec_data);
return;
} }
if (vgmstream->coding_type == coding_VORBIS_custom) {
free_vorbis_custom(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_CIRCUS_VQ) { if (vgmstream->coding_type == coding_CIRCUS_VQ) {
free_circus_vq(vgmstream->codec_data); free_circus_vq(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_RELIC) {
free_relic(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_CRI_HCA) {
free_hca(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_TAC) {
free_tac(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ICE_RANGE || if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) { vgmstream->coding_type == coding_ICE_DCT) {
free_ice(vgmstream->codec_data); free_ice(vgmstream->codec_data);
@ -74,48 +59,20 @@ void decode_free(VGMSTREAM* vgmstream) {
free_ubi_adpcm(vgmstream->codec_data); free_ubi_adpcm(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_IMUSE) {
free_imuse(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) { if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) {
free_ongakukan_adp(vgmstream->codec_data); free_ongakukan_adp(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
free_compresswave(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
free_ea_mt(vgmstream->codec_data, vgmstream->channels); free_ea_mt(vgmstream->codec_data, vgmstream->channels);
} }
if (vgmstream->coding_type == coding_KA1A) {
free_ka1a(vgmstream->codec_data);
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
free_ffmpeg(vgmstream->codec_data);
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) { if (vgmstream->coding_type == coding_MP4_AAC) {
free_mp4_aac(vgmstream->codec_data); free_mp4_aac(vgmstream->codec_data);
} }
#endif #endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
free_mpeg(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_G7221 #ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) { if (vgmstream->coding_type == coding_G7221C) {
free_g7221(vgmstream->codec_data); free_g7221(vgmstream->codec_data);
@ -128,24 +85,6 @@ void decode_free(VGMSTREAM* vgmstream) {
} }
#endif #endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
free_atrac9(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
free_celt_fsb(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_SPEEX
if (vgmstream->coding_type == coding_SPEEX) {
free_speex(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_ACM) { if (vgmstream->coding_type == coding_ACM) {
free_acm(vgmstream->codec_data); free_acm(vgmstream->codec_data);
} }
@ -162,22 +101,16 @@ void decode_seek(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data) if (!vgmstream->codec_data)
return; return;
const codec_info_t* codec_info = codec_get_info(vgmstream);
if (codec_info) {
codec_info->seek(vgmstream, vgmstream->loop_current_sample);
return;
}
if (vgmstream->coding_type == coding_CIRCUS_VQ) { if (vgmstream->coding_type == coding_CIRCUS_VQ) {
seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample); seek_circus_vq(vgmstream->codec_data, vgmstream->loop_current_sample);
} }
if (vgmstream->coding_type == coding_RELIC) {
seek_relic(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_CRI_HCA) {
loop_hca(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_TAC) {
seek_tac(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_ICE_RANGE || if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) { vgmstream->coding_type == coding_ICE_DCT) {
seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample); seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample);
@ -187,76 +120,20 @@ void decode_seek(VGMSTREAM* vgmstream) {
seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample); seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample);
} }
if (vgmstream->coding_type == coding_IMUSE) {
seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) { if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) {
seek_ongakukan_adp(vgmstream->codec_data, vgmstream->loop_current_sample); seek_ongakukan_adp(vgmstream->codec_data, vgmstream->loop_current_sample);
} }
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
seek_compresswave(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
seek_ea_mt(vgmstream, vgmstream->loop_current_sample); seek_ea_mt(vgmstream, vgmstream->loop_current_sample);
} }
if (vgmstream->coding_type == coding_KA1A) {
seek_ka1a(vgmstream, vgmstream->loop_current_sample);
}
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
seek_ogg_vorbis(vgmstream->codec_data, vgmstream->loop_current_sample);
}
if (vgmstream->coding_type == coding_VORBIS_custom) {
seek_vorbis_custom(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
seek_ffmpeg(vgmstream->codec_data, vgmstream->loop_current_sample);
}
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) { if (vgmstream->coding_type == coding_MP4_AAC) {
seek_mp4_aac(vgmstream, vgmstream->loop_current_sample); seek_mp4_aac(vgmstream, vgmstream->loop_current_sample);
} }
#endif #endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
seek_atrac9(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
seek_celt_fsb(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_SPEEX
if (vgmstream->coding_type == coding_SPEEX) {
seek_speex(vgmstream, vgmstream->loop_current_sample);
}
#endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
seek_mpeg(vgmstream, vgmstream->loop_current_sample);
}
#endif
if (vgmstream->coding_type == coding_NWA) { if (vgmstream->coding_type == coding_NWA) {
seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample); seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample);
} }
@ -269,32 +146,16 @@ void decode_reset(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data) if (!vgmstream->codec_data)
return; return;
#ifdef VGM_USE_VORBIS const codec_info_t* codec_info = codec_get_info(vgmstream);
if (vgmstream->coding_type == coding_OGG_VORBIS) { if (codec_info) {
reset_ogg_vorbis(vgmstream->codec_data); codec_info->reset(vgmstream->codec_data);
return;
} }
if (vgmstream->coding_type == coding_VORBIS_custom) {
reset_vorbis_custom(vgmstream);
}
#endif
if (vgmstream->coding_type == coding_CIRCUS_VQ) { if (vgmstream->coding_type == coding_CIRCUS_VQ) {
reset_circus_vq(vgmstream->codec_data); reset_circus_vq(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_RELIC) {
reset_relic(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_CRI_HCA) {
reset_hca(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_TAC) {
reset_tac(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ICE_RANGE || if (vgmstream->coding_type == coding_ICE_RANGE ||
vgmstream->coding_type == coding_ICE_DCT) { vgmstream->coding_type == coding_ICE_DCT) {
reset_ice(vgmstream->codec_data); reset_ice(vgmstream->codec_data);
@ -304,42 +165,20 @@ void decode_reset(VGMSTREAM* vgmstream) {
reset_ubi_adpcm(vgmstream->codec_data); reset_ubi_adpcm(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_IMUSE) {
reset_imuse(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) { if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) {
reset_ongakukan_adp(vgmstream->codec_data); reset_ongakukan_adp(vgmstream->codec_data);
} }
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
reset_compresswave(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_EA_MT) { if (vgmstream->coding_type == coding_EA_MT) {
reset_ea_mt(vgmstream); reset_ea_mt(vgmstream);
} }
if (vgmstream->coding_type == coding_KA1A) {
reset_ka1a(vgmstream->codec_data);
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type == coding_MP4_AAC) { if (vgmstream->coding_type == coding_MP4_AAC) {
reset_mp4_aac(vgmstream); reset_mp4_aac(vgmstream);
} }
#endif #endif
#ifdef VGM_USE_MPEG
if (vgmstream->coding_type == coding_MPEG_custom ||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
vgmstream->coding_type == coding_MPEG_layer1 ||
vgmstream->coding_type == coding_MPEG_layer2 ||
vgmstream->coding_type == coding_MPEG_layer3) {
reset_mpeg(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_G7221 #ifdef VGM_USE_G7221
if (vgmstream->coding_type == coding_G7221C) { if (vgmstream->coding_type == coding_G7221C) {
reset_g7221(vgmstream->codec_data); reset_g7221(vgmstream->codec_data);
@ -352,30 +191,6 @@ void decode_reset(VGMSTREAM* vgmstream) {
} }
#endif #endif
#ifdef VGM_USE_ATRAC9
if (vgmstream->coding_type == coding_ATRAC9) {
reset_atrac9(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_CELT
if (vgmstream->coding_type == coding_CELT_FSB) {
reset_celt_fsb(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_SPEEX
if (vgmstream->coding_type == coding_SPEEX) {
reset_speex(vgmstream->codec_data);
}
#endif
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type == coding_FFmpeg) {
reset_ffmpeg(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_ACM) { if (vgmstream->coding_type == coding_ACM) {
reset_acm(vgmstream->codec_data); reset_acm(vgmstream->codec_data);
} }
@ -431,17 +246,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_PCM24BE: case coding_PCM24BE:
case coding_PCM32LE: case coding_PCM32LE:
return 1; return 1;
#ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS:
case coding_VORBIS_custom:
#endif
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
#endif
case coding_SDX2: case coding_SDX2:
case coding_SDX2_int: case coding_SDX2_int:
case coding_CBD2: case coding_CBD2:
@ -557,10 +361,6 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
#ifdef VGM_USE_G719 #ifdef VGM_USE_G719
case coding_G719: case coding_G719:
return 48000/50; return 48000/50;
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
return 0;
#endif #endif
case coding_MTAF: case coding_MTAF:
return 128*2; return 128*2;
@ -586,36 +386,16 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
return 0; /* varies per frame */ return 0; /* varies per frame */
case coding_ONGAKUKAN_ADPCM: case coding_ONGAKUKAN_ADPCM:
return 0; /* actually 1. */ return 0; /* actually 1. */
case coding_COMPRESSWAVE:
return 0; /* multiple of 2 */
case coding_EA_MT: case coding_EA_MT:
return 0; /* 432, but variable in looped files */ return 0; /* 432, but variable in looped files */
case coding_CIRCUS_VQ: case coding_CIRCUS_VQ:
return 0; return 0;
case coding_RELIC:
return 0; /* 512 */
case coding_CRI_HCA:
return 0; /* 1024 - delay/padding (which can be bigger than 1024) */
case coding_TAC:
return 0; /* 1024 - delay/padding */
case coding_ICE_RANGE: case coding_ICE_RANGE:
case coding_ICE_DCT: case coding_ICE_DCT:
return 0; /* ~100 (range), ~16 (DCT) */ return 0; /* ~100 (range), ~16 (DCT) */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC: case coding_MP4_AAC:
return mp4_get_samples_per_frame(vgmstream->codec_data); return mp4_get_samples_per_frame(vgmstream->codec_data);
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
return 0; /* varies with config data, usually 256 or 1024 */
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
return 0; /* 512? */
#endif
#ifdef VGM_USE_SPEEX
case coding_SPEEX:
return 0;
#endif #endif
default: default:
return 0; return 0;
@ -782,9 +562,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
#endif #endif
#ifdef VGM_USE_G719 #ifdef VGM_USE_G719
case coding_G719: case coding_G719:
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif #endif
case coding_MTAF: case coding_MTAF:
return vgmstream->interleave_block_size; return vgmstream->interleave_block_size;
@ -807,12 +584,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
/* UBI_ADPCM: varies per mode? */ /* UBI_ADPCM: varies per mode? */
/* IMUSE: VBR */ /* IMUSE: VBR */
/* EA_MT: VBR, frames of bit counts or PCM frames */ /* EA_MT: VBR, frames of bit counts or PCM frames */
/* COMPRESSWAVE: VBR/huffman bits */
/* ATRAC9: CBR around 0x100-200 */
/* CELT FSB: varies, usually 0x80-100 */ /* CELT FSB: varies, usually 0x80-100 */
/* SPEEX: varies, usually 0x40-60 */
/* TAC: VBR around ~0x200-300 */ /* TAC: VBR around ~0x200-300 */
/* Vorbis, MPEG, ACM, etc: varies */
default: /* (VBR or managed by decoder) */ default: /* (VBR or managed by decoder) */
return 0; return 0;
} }
@ -869,12 +642,23 @@ bool decode_uses_internal_offset_updates(VGMSTREAM* vgmstream) {
// decode frames for decoders which decode frame by frame and have their own sample buffer // decode frames for decoders which decode frame by frame and have their own sample buffer
static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) { static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
const int max_empty = 1000; const int max_empty = 1000;
int num_empty = 0; int num_empty = 0;
decode_state_t* ds = vgmstream->decode_state; decode_state_t* ds = vgmstream->decode_state;
sbuf_t* ssrc = &ds->sbuf; sbuf_t* ssrc = &ds->sbuf;
const codec_info_t* codec_info = codec_get_info(vgmstream);
ds->samples_left = samples_to_do; //sdst->samples; // TODO this can be slow for interleaved decoders
// old-style decoding
if (codec_info && codec_info->decode_buf) {
bool ok = codec_info->decode_buf(vgmstream, sdst);
if (!ok) goto decode_fail;
sdst->filled += ds->samples_left;
return;
}
// fill the external buf by decoding N times; may read partially that buf // fill the external buf by decoding N times; may read partially that buf
while (sdst->filled < sdst->samples) { while (sdst->filled < sdst->samples) {
@ -882,11 +666,11 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) {
// decode new frame if prev one was consumed // decode new frame if prev one was consumed
if (ssrc->filled == 0) { if (ssrc->filled == 0) {
bool ok = false; bool ok = false;
switch (vgmstream->coding_type) {
case coding_KA1A: if (codec_info) {
ok = decode_ka1a_frame(vgmstream); ok = codec_info->decode_frame(vgmstream);
break; }
default: else {
goto decode_fail; goto decode_fail;
} }
@ -902,6 +686,9 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) {
goto decode_fail; goto decode_fail;
} }
} }
else {
num_empty = 0; //reset for discard loops
}
if (ds->discard) { if (ds->discard) {
// decoder may signal that samples need to be discarded (ex. encoder delay or during loops) // decoder may signal that samples need to be discarded (ex. encoder delay or during loops)
@ -919,6 +706,8 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) {
sbuf_copy_segments(sdst, ssrc, samples_copy); sbuf_copy_segments(sdst, ssrc, samples_copy);
sbuf_consume(ssrc, samples_copy); sbuf_consume(ssrc, samples_copy);
ds->samples_left -= samples_copy;
} }
} }
@ -945,7 +734,7 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
switch (vgmstream->coding_type) { switch (vgmstream->coding_type) {
case coding_SILENCE: case coding_SILENCE:
sbuf_silence_s16(buffer, samples_to_do, vgmstream->channels, 0); sbuf_silence_rest(sdst);
break; break;
case coding_CRI_ADX: case coding_CRI_ADX:
@ -1053,35 +842,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
vgmstream->channels, vgmstream->samples_into_block, samples_to_do); vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
} }
break; break;
case coding_PCMFLOAT:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcmfloat(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do,
vgmstream->codec_endian);
}
break;
case coding_PCM24LE:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm24le(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_PCM24BE:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm24be(&vgmstream->ch[ch], buffer + ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_PCM32LE:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_pcm32le(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_NDS_IMA: case coding_NDS_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
decode_nds_ima(&vgmstream->ch[ch], buffer+ch, decode_nds_ima(&vgmstream->ch[ch], buffer+ch,
@ -1229,36 +989,13 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
} }
break; break;
#ifdef VGM_USE_VORBIS
case coding_OGG_VORBIS:
decode_ogg_vorbis(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
break;
case coding_VORBIS_custom:
decode_vorbis_custom(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
case coding_CIRCUS_VQ: case coding_CIRCUS_VQ:
decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
break; break;
case coding_RELIC:
decode_relic(&vgmstream->ch[0], vgmstream->codec_data, buffer, samples_to_do);
break;
case coding_CRI_HCA:
decode_hca(vgmstream->codec_data, buffer, samples_to_do);
break;
case coding_TAC:
decode_tac(vgmstream, buffer, samples_to_do);
break;
case coding_ICE_RANGE: case coding_ICE_RANGE:
case coding_ICE_DCT: case coding_ICE_DCT:
decode_ice(vgmstream->codec_data, buffer, samples_to_do); decode_ice(vgmstream->codec_data, buffer, samples_to_do);
break; break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC: case coding_MP4_AAC:
decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
@ -1450,15 +1187,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
} }
break; break;
#ifdef VGM_USE_MPEG
case coding_MPEG_custom:
case coding_MPEG_ealayer3:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
decode_mpeg(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#ifdef VGM_USE_G7221 #ifdef VGM_USE_G7221
case coding_G7221C: case coding_G7221C:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
@ -1472,21 +1200,6 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
} }
break; break;
#endif
#ifdef VGM_USE_ATRAC9
case coding_ATRAC9:
decode_atrac9(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#ifdef VGM_USE_CELT
case coding_CELT_FSB:
decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels);
break;
#endif
#ifdef VGM_USE_SPEEX
case coding_SPEEX:
decode_speex(vgmstream, buffer, samples_to_do);
break;
#endif #endif
case coding_ACM: case coding_ACM:
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels); decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
@ -1656,18 +1369,10 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
decode_ubi_adpcm(vgmstream, buffer, samples_to_do); decode_ubi_adpcm(vgmstream, buffer, samples_to_do);
break; break;
case coding_IMUSE:
decode_imuse(vgmstream, buffer, samples_to_do);
break;
case coding_ONGAKUKAN_ADPCM: case coding_ONGAKUKAN_ADPCM:
decode_ongakukan_adp(vgmstream, buffer, samples_to_do); decode_ongakukan_adp(vgmstream, buffer, samples_to_do);
break; break;
case coding_COMPRESSWAVE:
decode_compresswave(vgmstream->codec_data, buffer, samples_to_do);
break;
case coding_EA_MT: case coding_EA_MT:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch); decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
@ -1678,7 +1383,7 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
sbuf_t stmp = *sdst; sbuf_t stmp = *sdst;
stmp.samples = stmp.filled + samples_to_do; //TODO improve stmp.samples = stmp.filled + samples_to_do; //TODO improve
decode_frames(&stmp, vgmstream); decode_frames(&stmp, vgmstream, samples_to_do);
break; break;
} }
} }

View file

@ -6,6 +6,7 @@
typedef struct { typedef struct {
int discard; int discard;
sbuf_t sbuf; sbuf_t sbuf;
int samples_left; //info for some decoders
} decode_state_t; } decode_state_t;
#endif #endif

View file

@ -176,8 +176,11 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
const char* sfmt_desc; const char* sfmt_desc;
switch(sfmt) { switch(sfmt) {
case SFMT_FLT: sfmt_desc = "float"; break; case SFMT_FLT: sfmt_desc = "float"; break;
case SFMT_F32: sfmt_desc = "float32"; break; case SFMT_F16: sfmt_desc = "float16"; break;
case SFMT_S16: sfmt_desc = "pcm16"; break; case SFMT_S16: sfmt_desc = "pcm16"; break;
case SFMT_S24: sfmt_desc = "pcm24"; break;
case SFMT_S32: sfmt_desc = "pcm32"; break;
case SFMT_O24: sfmt_desc = "pcm24"; break;
default: sfmt_desc = "???"; default: sfmt_desc = "???";
} }
@ -330,7 +333,7 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
} }
if (vgmstream->coding_type == coding_COMPRESSWAVE) { if (vgmstream->coding_type == coding_COMPRESSWAVE) {
return compresswave_get_streamfile(vgmstream->codec_data); return compresswave_get_streamfile(vgmstream);
} }
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS

View file

@ -74,11 +74,12 @@ bool mixer_is_active(mixer_t* mixer) {
static void setup_mixbuf(mixer_t* mixer, sbuf_t* sbuf) { static void setup_mixbuf(mixer_t* mixer, sbuf_t* sbuf) {
sbuf_t* smix = &mixer->smix; sbuf_t* smix = &mixer->smix;
// mixbuf can be interpreted as FLT or F32; try to use src's to keep buf as-is (less rounding errors) // mixbuf (float) can be interpreted as F16, for 1:1 mapping with PCM16 (and possibly less rounding errors with mixops)
if (sbuf->fmt == SFMT_F32 || sbuf->fmt == SFMT_FLT) // for PCM24 regular float seems ok and 1:1 as well
sbuf_init(smix, sbuf->fmt, mixer->mixbuf, sbuf->filled, sbuf->channels); //mixer->input_channels if (sbuf->fmt == SFMT_S16)
sbuf_init(smix, SFMT_F16, mixer->mixbuf, sbuf->filled, sbuf->channels);
else else
sbuf_init(smix, SFMT_F32, mixer->mixbuf, sbuf->filled, sbuf->channels); sbuf_init(smix, sbuf->fmt, mixer->mixbuf, sbuf->filled, sbuf->channels);
// remix to temp buf (somehow using float buf rather than int32 is faster?) // remix to temp buf (somehow using float buf rather than int32 is faster?)
sbuf_copy_segments(smix, sbuf, sbuf->filled); sbuf_copy_segments(smix, sbuf, sbuf->filled);
@ -100,12 +101,20 @@ static void setup_outbuf(mixer_t* mixer, sbuf_t* sbuf) {
void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) { void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) {
/* external */ // external
//if (!mixer_is_active(mixer)) //if (!mixer_is_active(mixer))
// return; // return;
/* try to skip if no fades apply (set but does nothing yet) + only has fades #if 0
* (could be done in mix op but avoids upgrading bufs in some cases) */ // TODO: not possible to copy from src to dst directly at the moment, since src doubles as dst
// optimize copy only ops to skip temp buffer
if (mixer->chain_count == 0 && mixer->force_type != SFMT_NONE) {
...
}
#endif
// try to skip if no fades apply (set but does nothing yet) + only has fades
// (could be done in mix op but avoids upgrading bufs in some cases)
if (mixer->has_fade) { if (mixer->has_fade) {
//;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count)); //;VGM_LOG("MIX: fade test %i, %i\n", data->has_non_fade, mixer_op_fade_is_active(data, current_pos, current_pos + sample_count));
if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sbuf->filled)) if (!mixer->has_non_fade && !mixer_op_fade_is_active(mixer, current_pos, current_pos + sbuf->filled))
@ -116,7 +125,7 @@ void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) {
setup_mixbuf(mixer, sbuf); setup_mixbuf(mixer, sbuf);
// apply mixing ops in order. channesl in mixersmix may increase or decrease per op // apply mixing ops in order. channels in mixers may increase or decrease per op (set in sbuf)
// - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2 // - 2ch w/ "1+2,1u" = ch1+ch2, ch1(add and push rest) = 3ch: ch1' ch1+ch2 ch2
// - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once) // - 2ch w/ "1u" = downmix to 1ch (current_channels decreases once)
for (int m = 0; m < mixer->chain_count; m++) { for (int m = 0; m < mixer->chain_count; m++) {

View file

@ -125,8 +125,11 @@ void mixer_op_fade(mixer_t* mixer, mix_op_t* mix) {
//TODO optimize for case 0? //TODO optimize for case 0?
for (int s = 0; s < smix->filled; s++) { for (int s = 0; s < smix->filled; s++) {
bool fade_applies = get_fade_gain(mix, &new_gain, current_subpos); bool fade_applies = get_fade_gain(mix, &new_gain, current_subpos);
if (!fade_applies) //TODO optimize? if (!fade_applies) { //TODO optimize?
dst += channels;
current_subpos++;
continue; continue;
}
if (mix->ch_dst < 0) { if (mix->ch_dst < 0) {
for (int ch = 0; ch < channels; ch++) { for (int ch = 0; ch < channels; ch++) {

View file

@ -7,6 +7,7 @@
#include "mixer.h" #include "mixer.h"
#include "mixer_priv.h" #include "mixer_priv.h"
#include "sbuf.h" #include "sbuf.h"
#include "codec_info.h"
/* Wrapper/helpers for vgmstream's "mixer", which does main sample buffer transformations */ /* Wrapper/helpers for vgmstream's "mixer", which does main sample buffer transformations */
@ -143,9 +144,32 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan
} }
sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) { sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) {
if (vgmstream->layout_type == layout_layered) {
layered_layout_data* data = vgmstream->layout_data;
if (data)
return data->fmt;
}
if (vgmstream->layout_type == layout_segmented) {
segmented_layout_data* data = vgmstream->layout_data;
if (data)
return data->fmt;
}
const codec_info_t* codec_info = codec_get_info(vgmstream);
if (codec_info) {
if (codec_info->sample_type)
return codec_info->sample_type;
if (codec_info->get_sample_type)
return codec_info->get_sample_type(vgmstream);
}
// TODO: on layered/segments, detect biggest value and use that (ex. if one of the layers uses flt > flt) // TODO: on layered/segments, detect biggest value and use that (ex. if one of the layers uses flt > flt)
switch(vgmstream->coding_type) { switch(vgmstream->coding_type) {
case coding_KA1A: #ifdef VGM_USE_VORBIS
case coding_VORBIS_custom:
#endif
return SFMT_FLT; return SFMT_FLT;
default: default:
return SFMT_S16; return SFMT_S16;

View file

@ -569,7 +569,7 @@ void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_map
void mixing_macro_output_sample_format(VGMSTREAM* vgmstream, sfmt_t type) { void mixing_macro_output_sample_format(VGMSTREAM* vgmstream, sfmt_t type) {
mixer_t* mixer = vgmstream->mixer; mixer_t* mixer = vgmstream->mixer;
if (!mixer) if (!mixer || !type)
return; return;
// optimization (may skip initializing mixer) // optimization (may skip initializing mixer)
@ -577,4 +577,5 @@ void mixing_macro_output_sample_format(VGMSTREAM* vgmstream, sfmt_t type) {
if (input_fmt == type) if (input_fmt == type)
return; return;
mixer->force_type = type; mixer->force_type = type;
mixer->has_non_fade = true;
} }

View file

@ -107,8 +107,8 @@ int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream) {
case layout_blocked_dec: case layout_blocked_dec:
case layout_blocked_vs_mh: case layout_blocked_vs_mh:
case layout_blocked_mul: case layout_blocked_mul:
case layout_blocked_gsb: case layout_blocked_gsnd:
case layout_blocked_xvas: case layout_blocked_vas_kceo:
case layout_blocked_thp: case layout_blocked_thp:
case layout_blocked_filp: case layout_blocked_filp:
case layout_blocked_rage_aud: case layout_blocked_rage_aud:

View file

@ -1,10 +1,129 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//#include <math.h>
#include "../util.h" #include "../util.h"
#include "sbuf.h" #include "sbuf.h"
#include "../util/log.h" #include "../util/log.h"
// float-to-int modes
//#define PCM16_ROUNDING_LRINT // rounding down, potentially faster in some systems/compilers and much slower in others (also affects rounding)
//#define PCM16_ROUNDING_HALF // rounding half + down (vorbis-style), more 'accurate' but slower
#if defined(PCM16_ROUNDING_HALF) || defined(PCM16_ROUNDING_LRINT)
#include <math.h>
#endif
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32767.5)) - 32767)
* - etc
* but since +-1 isn't really audible we'll just cast, as it's the fastest
*
* Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE
* float requirements, but C99 adds some faster-but-less-precise casting functions.
* MSVC added this in VS2015 (_MSC_VER 1900) but doesn't seem inlined and is very slow.
* It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions.
*/
static inline int float_to_int(float val) {
#ifdef PCM16_ROUNDING_LRINT
return lrintf(val);
#elif defined(_MSC_VER)
return (int)val;
#else
return (int)val;
#endif
}
#if 0
static inline int double_to_int(double val) {
#ifdef PCM16_ROUNDING_LRINT
return lrint(val);
#elif defined(_MSC_VER)
return (int)val;
#else
return (int)val;
#endif
}
static inline float double_to_float(double val) {
return (float)val;
}
#endif
static inline int64_t float_to_i64(float val) {
return (int64_t)val;
}
// TO-DO: investigate if BE machines need BE 24-bit
static inline int32_t get_s24ne(const uint8_t* p) {
#if PCM24_BIG_ENDIAN
return (((int32_t)p[0]<<24) | ((int32_t)p[1]<<16) | ((int32_t)p[2]<<8)) >> 8; //force signedness
#else
return (((int32_t)p[0]<<8) | ((int32_t)p[1]<<16) | ((int32_t)p[2]<<24)) >> 8; //force signedness
#endif
}
static void put_u24ne(uint8_t* buf, uint32_t v) {
#if PCM24_BIG_ENDIAN
buf[0] = (uint8_t)((v >> 16) & 0xFF);
buf[1] = (uint8_t)((v >> 8) & 0xFF);
buf[2] = (uint8_t)((v >> 0) & 0xFF);
#else
buf[0] = (uint8_t)((v >> 0) & 0xFF);
buf[1] = (uint8_t)((v >> 8) & 0xFF);
buf[2] = (uint8_t)((v >> 16) & 0xFF);
#endif
}
static inline int clamp_pcm16(int32_t val) {
return clamp16(val);
}
static inline int clamp_pcm24(int32_t val) {
if (val > 8388607) return 8388607;
else if (val < -8388608) return -8388608;
else return val;
}
static inline int clamp_pcm32(int64_t val) {
if (val > 2147483647) return 2147483647;
else if (val < -2147483648) return -2147483648;
else return val;
}
//TODO float can't fully represent s32, but since it mainly affects lower bytes (noise) it may be ok
#define CONV_NOOP(x) (x)
#define CONV_S16_FLT(x) (x * (1.0f / 32767.0f))
#define CONV_S16_S24(x) (x << 8)
#define CONV_S16_S32(x) (x << 16)
#define CONV_F16_S16(x) (clamp_pcm16(float_to_int(x)))
#define CONV_F16_FLT(x) (x * (1.0f / 32767.0f))
#define CONV_F16_S24(x) (clamp_pcm16(float_to_int(x)) << 8)
#define CONV_F16_S32(x) (clamp_pcm16(float_to_int(x)) << 16)
#ifdef PCM16_ROUNDING_HALF
#define CONV_FLT_S16(x) (clamp_pcm16(float_to_int( floor(x * 32767.0f + 0.5f) )))
#else
#define CONV_FLT_S16(x) (clamp_pcm16(float_to_int(x * 32767.0f)))
#endif
#define CONV_FLT_F16(x) (x * 32767.0f)
#define CONV_FLT_S24(x) (clamp_pcm24(float_to_int(x * 8388607.0f)))
#define CONV_FLT_S32(x) (clamp_pcm32(float_to_i64(x * 2147483647)))
#define CONV_S24_S16(x) (x >> 8)
#define CONV_S24_F16(x) (x >> 8)
#define CONV_S24_S32(x) (x << 8)
#define CONV_S24_FLT(x) (x * (1.0f / 8388607.0f))
#define CONV_S32_S16(x) (x >> 16)
#define CONV_S32_F16(x) (x >> 16)
#define CONV_S32_FLT(x) (x * (1.0f / 2147483647.0f))
#define CONV_S32_S24(x) (x >> 8)
void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels) { void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels) {
memset(sbuf, 0, sizeof(sbuf_t)); memset(sbuf, 0, sizeof(sbuf_t));
@ -18,8 +137,8 @@ void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels) {
sbuf_init(sbuf, SFMT_S16, buf, samples, channels); sbuf_init(sbuf, SFMT_S16, buf, samples, channels);
} }
void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels) { void sbuf_init_f16(sbuf_t* sbuf, float* buf, int samples, int channels) {
sbuf_init(sbuf, SFMT_F32, buf, samples, channels); sbuf_init(sbuf, SFMT_F16, buf, samples, channels);
} }
void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) { void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) {
@ -29,13 +148,18 @@ void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) {
int sfmt_get_sample_size(sfmt_t fmt) { int sfmt_get_sample_size(sfmt_t fmt) {
switch(fmt) { switch(fmt) {
case SFMT_F32: case SFMT_F16:
case SFMT_FLT: case SFMT_FLT:
case SFMT_S24:
case SFMT_S32:
return 0x04; return 0x04;
case SFMT_S16: case SFMT_S16:
return 0x02; return 0x02;
case SFMT_O24:
return 0x03;
default: default:
return 0; VGM_LOG("SBUF: undefined sample format %i found\n", fmt);
return 0; //TODO return 4 to avoid crashes?
} }
} }
@ -48,6 +172,8 @@ void* sbuf_get_filled_buf(sbuf_t* sbuf) {
} }
void sbuf_consume(sbuf_t* sbuf, int samples) { void sbuf_consume(sbuf_t* sbuf, int samples) {
if (samples == 0) //some discards
return;
int sample_size = sfmt_get_sample_size(sbuf->fmt); int sample_size = sfmt_get_sample_size(sbuf->fmt);
if (sample_size <= 0) //??? if (sample_size <= 0) //???
return; return;
@ -62,102 +188,6 @@ void sbuf_consume(sbuf_t* sbuf, int samples) {
sbuf->samples -= samples; sbuf->samples -= samples;
} }
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast, as it's the fastest
*
* Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE
* float requirements, but C99 adds some faster-but-less-precise casting functions.
* MSVC added this in VS2015 (_MSC_VER 1900) but doesn't seem inlined and is very slow.
* It's slightly faster (~5%) but causes fuzzy PCM<>float<>PCM conversions.
*/
static inline int float_to_int(float val) {
#if 1
return (int)val;
#elif defined(_MSC_VER)
return (int)val;
#else
return lrintf(val);
#endif
}
static inline int double_to_int(double val) {
#if 1
return (int)val;
#elif defined(_MSC_VER)
return (int)val;
#else
return lrint(val);
#endif
}
static inline float double_to_float(double val) {
return (float)val;
}
//TODO decide if using float 1.0 style or 32767 style (fuzzy PCM when doing that)
//TODO: maybe use macro-style templating (but kinda ugly)
void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf) {
switch(sbuf->fmt) {
case SFMT_S16: {
int16_t* src = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = (float)src[s]; // / 32767.0f
}
break;
}
case SFMT_F32: {
float* src = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = src[s];
}
break;
}
case SFMT_FLT: {
float* src = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = src[s] * 32768.0f;
}
break;
}
default:
break;
}
}
void sbuf_copy_from_f32(sbuf_t* sbuf, float* src) {
switch(sbuf->fmt) {
case SFMT_S16: {
int16_t* dst = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = clamp16(float_to_int(src[s]));
}
break;
}
case SFMT_F32: {
float* dst = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = src[s];
}
break;
}
case SFMT_FLT: {
float* dst = sbuf->buf;
for (int s = 0; s < sbuf->filled * sbuf->channels; s++) {
dst[s] = src[s] / 32768.0f;
}
break;
}
default:
break;
}
}
// max samples to copy from ssrc to sdst, considering that dst may be partially filled // max samples to copy from ssrc to sdst, considering that dst may be partially filled
int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) { int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) {
@ -168,209 +198,6 @@ int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) {
return samples_copy; return samples_copy;
} }
/* ugly thing to avoid repeating functions */
#define sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max) \
while (src_pos < src_max) { \
dst[dst_pos++] = src[src_pos++]; \
}
#define sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, value) \
while (src_pos < src_max) { \
dst[dst_pos++] = clamp16(float_to_int(src[src_pos++] * value)); \
}
#define sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, value) \
while (src_pos < src_max) { \
dst[dst_pos++] = float_to_int(src[src_pos++] * value); \
}
// copy N samples from ssrc into dst (should be clamped externally)
void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples_copy) {
if (ssrc->channels != sdst->channels) {
// 0'd other channels first (uncommon so probably fine albeit slower-ish)
sbuf_silence_part(sdst, sdst->filled, samples_copy);
sbuf_copy_layers(sdst, ssrc, 0, ssrc->filled);
#if 0
// "faster" but lots of extra ifs per sample format, not worth it
while (src_pos < src_max) {
for (int ch = 0; ch < dst_channels; ch++) {
dst[dst_pos++] = ch >= src_channels ? 0 : src[src_pos++];
}
}
#endif
//TODO: may want to handle externally?
sdst->filled += samples_copy;
return;
}
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels;
int src_max = samples_copy * ssrc->channels;
// define all posible combos, probably there is a better way to handle this but...
if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_S16) {
int16_t* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
else if ((sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_F32) || (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_FLT)) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
// to s16
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_F32) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, 1.0f);
}
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_FLT) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, 32768.0f);
}
// to f32
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_FLT) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, 32768.0f);
}
// to flt
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, (1/32768.0f));
}
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_F32) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, (1/32768.0f));
}
//TODO: may want to handle externally?
sdst->filled += samples_copy;
}
//TODO fix missing ->channels
/* ugly thing to avoid repeating functions */
#define sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = src[src_pos++]; \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
// float +-1.0 <> pcm +-32768.0
#define sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, value) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = clamp16(float_to_int(src[src_pos++] * value)); \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
// float +-1.0 <> pcm +-32768.0
#define sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, value) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = float_to_int(src[src_pos++] * value); \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
/* copy interleaving: dst ch1 ch2 ch3 ch4 w/ src ch1 ch2 ch1 ch2 = only fill dst ch1 ch2 */
// dst_channels == src_channels isn't likely so ignore that optimization
// sometimes one layer has less samples than others and need to 0-fill rest
void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int dst_expected) {
int src_filled = ssrc->filled;
int src_channels = ssrc->channels;
int dst_ch_step = (sdst->channels - ssrc->channels); \
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels + dst_ch_start;
// define all posible combos, probably there is a better way to handle this but...
// 1:1
if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_S16) {
int16_t* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
else if ((sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_F32) || (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_FLT)) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
// to s16
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_F32) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 1.0f);
}
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_FLT) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 32768.0f);
}
// to f32
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_FLT) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 32768.0f);
}
// to flt
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, (1/32768.0f));
}
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_F32) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, (1/32768.0f));
}
}
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled) {
memset(dst + filled * channels, 0, (samples - filled) * channels * sizeof(sample_t));
}
void sbuf_silence_part(sbuf_t* sbuf, int from, int count) { void sbuf_silence_part(sbuf_t* sbuf, int from, int count) {
int sample_size = sfmt_get_sample_size(sbuf->fmt); int sample_size = sfmt_get_sample_size(sbuf->fmt);
@ -383,44 +210,295 @@ void sbuf_silence_rest(sbuf_t* sbuf) {
sbuf_silence_part(sbuf, sbuf->filled, sbuf->samples - sbuf->filled); sbuf_silence_part(sbuf, sbuf->filled, sbuf->samples - sbuf->filled);
} }
typedef void (*sbuf_copy_t)(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_max);
// Ugly generic copy function definition with different parameters, to avoid repeating code.
// Must define various functions below, to be set in the copy matrix.
// Uses void params to allow callbacks.
#define DEFINE_SBUF_COPY(suffix, srctype, dsttype, func) \
static void sbuf_copy_##suffix(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_max) { \
srctype* src = vsrc; \
dsttype* dst = vdst; \
while (src_pos < src_max) { \
dst[dst_pos++] = func(src[src_pos++]); \
} \
}
#define DEFINE_SBUF_CP24(suffix, srctype, dsttype, func) \
static void sbuf_copy_##suffix(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_max) { \
srctype* src = vsrc; \
dsttype* dst = vdst; \
while (src_pos < src_max) { \
put_u24ne(dst + dst_pos, func(src[src_pos++]) ); \
dst_pos += 3; \
} \
}
DEFINE_SBUF_COPY(s16_s16, int16_t, int16_t, CONV_NOOP);
DEFINE_SBUF_COPY(s16_f16, int16_t, float, CONV_NOOP);
DEFINE_SBUF_COPY(s16_flt, int16_t, float, CONV_S16_FLT);
DEFINE_SBUF_COPY(s16_s24, int16_t, int32_t, CONV_S16_S24);
DEFINE_SBUF_COPY(s16_s32, int16_t, int32_t, CONV_S16_S32);
DEFINE_SBUF_CP24(s16_o24, int16_t, uint8_t, CONV_S16_S24);
DEFINE_SBUF_COPY(f16_s16, float, int16_t, CONV_F16_S16);
DEFINE_SBUF_COPY(f16_f16, float, float, CONV_NOOP);
DEFINE_SBUF_COPY(f16_flt, float, float, CONV_F16_FLT);
DEFINE_SBUF_COPY(f16_s24, float, int32_t, CONV_F16_S24);
DEFINE_SBUF_COPY(f16_s32, float, int32_t, CONV_F16_S32);
DEFINE_SBUF_CP24(f16_o24, float, uint8_t, CONV_F16_S24);
DEFINE_SBUF_COPY(flt_s16, float, int16_t, CONV_FLT_S16);
DEFINE_SBUF_COPY(flt_f16, float, float, CONV_FLT_F16);
DEFINE_SBUF_COPY(flt_flt, float, float, CONV_NOOP);
DEFINE_SBUF_COPY(flt_s24, float, int32_t, CONV_FLT_S24);
DEFINE_SBUF_COPY(flt_s32, float, int32_t, CONV_FLT_S32);
DEFINE_SBUF_CP24(flt_o24, float, uint8_t, CONV_FLT_S24);
DEFINE_SBUF_COPY(s24_s16, int32_t, int16_t, CONV_S24_S16);
DEFINE_SBUF_COPY(s24_f16, int32_t, float, CONV_S24_F16);
DEFINE_SBUF_COPY(s24_flt, int32_t, float, CONV_S24_FLT);
DEFINE_SBUF_COPY(s24_s24, int32_t, int32_t, CONV_NOOP);
DEFINE_SBUF_COPY(s24_s32, int32_t, int32_t, CONV_S24_S32);
DEFINE_SBUF_CP24(s24_o24, int32_t, uint8_t, CONV_NOOP);
DEFINE_SBUF_COPY(s32_s16, int32_t, int16_t, CONV_S32_S16);
DEFINE_SBUF_COPY(s32_f16, int32_t, float, CONV_S32_F16);
DEFINE_SBUF_COPY(s32_flt, int32_t, float, CONV_S32_FLT);
DEFINE_SBUF_COPY(s32_s24, int32_t, int32_t, CONV_S32_S24);
DEFINE_SBUF_COPY(s32_s32, int32_t, int32_t, CONV_NOOP);
DEFINE_SBUF_CP24(s32_o24, int32_t, uint8_t, CONV_S32_S24);
static sbuf_copy_t copy_matrix[SFMT_MAX][SFMT_MAX] = {
{ NULL, NULL, NULL, NULL, NULL }, //NONE
{ NULL, sbuf_copy_s16_s16, sbuf_copy_s16_f16, sbuf_copy_s16_flt, sbuf_copy_s16_s24, sbuf_copy_s16_s32, sbuf_copy_s16_o24 },
{ NULL, sbuf_copy_f16_s16, sbuf_copy_f16_f16, sbuf_copy_f16_flt, sbuf_copy_f16_s24, sbuf_copy_f16_s32, sbuf_copy_f16_o24 },
{ NULL, sbuf_copy_flt_s16, sbuf_copy_flt_f16, sbuf_copy_flt_flt, sbuf_copy_flt_s24, sbuf_copy_flt_s32, sbuf_copy_flt_o24 },
{ NULL, sbuf_copy_s24_s16, sbuf_copy_s24_f16, sbuf_copy_s24_flt, sbuf_copy_s24_s24, sbuf_copy_s24_s32, sbuf_copy_s24_o24 },
{ NULL, sbuf_copy_s32_s16, sbuf_copy_s32_f16, sbuf_copy_s32_flt, sbuf_copy_s32_s24, sbuf_copy_s32_s32, sbuf_copy_s32_o24 },
{ NULL, NULL, NULL, NULL, NULL }, //O24
};
// copy N samples from ssrc into dst (should be clamped externally)
//TODO: may want to handle sdst->flled + samples externally?
void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples) {
// rarely when decoding with empty frames, may not setup ssrc
if (samples == 0)
return;
if (ssrc->channels != sdst->channels) {
// 0'd other channels first (uncommon so probably fine albeit slower-ish)
sbuf_silence_part(sdst, sdst->filled, samples);
sbuf_copy_layers(sdst, ssrc, 0, samples);
#if 0
// "faster" but lots of extra ifs per sample format, not worth it
while (src_pos < src_max) {
for (int ch = 0; ch < dst_channels; ch++) {
dst[dst_pos++] = ch >= src_channels ? 0 : src[src_pos++];
}
}
#endif
sdst->filled += samples;
return;
}
sbuf_copy_t sbuf_copy_src_dst = copy_matrix[ssrc->fmt][sdst->fmt];
if (!sbuf_copy_src_dst) {
VGM_LOG("SBUF: undefined copy function sfmt %i to %i\n", ssrc->fmt, sdst->fmt);
sdst->filled += samples;
return;
}
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels;
int src_max = samples * ssrc->channels;
sbuf_copy_src_dst(ssrc->buf, sdst->buf, src_pos, dst_pos, src_max);
sdst->filled += samples;
}
typedef void (*sbuf_layer_t)(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_max, int dst_expected, int src_channels, int dst_channels);
// See above
#define DEFINE_SBUF_LAYER(suffix, srctype, dsttype, func) \
static void sbuf_layer_##suffix(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_filled, int dst_expected, int src_channels, int dst_channels) { \
srctype* src = vsrc; \
dsttype* dst = vdst; \
int dst_ch_step = (dst_channels - src_channels); \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = func(src[src_pos++]); \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
} \
}
#define DEFINE_SBUF_LYR24(suffix, srctype, dsttype, func) \
static void sbuf_layer_##suffix(void* vsrc, void* vdst, int src_pos, int dst_pos, int src_filled, int dst_expected, int src_channels, int dst_channels) { \
srctype* src = vsrc; \
dsttype* dst = vdst; \
int dst_ch_step = (dst_channels - src_channels); \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
put_u24ne(dst + dst_pos, func(src[src_pos++]) ); \
dst_pos += 3; \
} \
dst_pos += dst_ch_step * 3; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
put_u24ne(dst + dst_pos, 0); \
dst_pos += 3; \
} \
dst_pos += dst_ch_step * 3; \
} \
}
DEFINE_SBUF_LAYER(s16_s16, int16_t, int16_t, CONV_NOOP);
DEFINE_SBUF_LAYER(s16_f16, int16_t, float, CONV_NOOP);
DEFINE_SBUF_LAYER(s16_flt, int16_t, float, CONV_S16_FLT);
DEFINE_SBUF_LAYER(s16_s24, int16_t, int32_t, CONV_S16_S24);
DEFINE_SBUF_LAYER(s16_s32, int16_t, int32_t, CONV_S16_S32);
DEFINE_SBUF_LYR24(s16_o24, int16_t, uint8_t, CONV_S16_S24);
DEFINE_SBUF_LAYER(f16_s16, float, int16_t, CONV_F16_S16);
DEFINE_SBUF_LAYER(f16_f16, float, float, CONV_NOOP);
DEFINE_SBUF_LAYER(f16_flt, float, float, CONV_F16_FLT);
DEFINE_SBUF_LAYER(f16_s24, float, int32_t, CONV_F16_S24);
DEFINE_SBUF_LAYER(f16_s32, float, int32_t, CONV_F16_S32);
DEFINE_SBUF_LYR24(f16_o24, float, uint8_t, CONV_F16_S24);
DEFINE_SBUF_LAYER(flt_s16, float, int16_t, CONV_FLT_S16);
DEFINE_SBUF_LAYER(flt_f16, float, float, CONV_FLT_F16);
DEFINE_SBUF_LAYER(flt_flt, float, float, CONV_NOOP);
DEFINE_SBUF_LAYER(flt_s24, float, int32_t, CONV_FLT_S24);
DEFINE_SBUF_LAYER(flt_s32, float, int32_t, CONV_FLT_S32);
DEFINE_SBUF_LYR24(flt_o24, float, uint8_t, CONV_FLT_S24);
DEFINE_SBUF_LAYER(s24_s16, int32_t, int16_t, CONV_S24_S16);
DEFINE_SBUF_LAYER(s24_f16, int32_t, float, CONV_S24_F16);
DEFINE_SBUF_LAYER(s24_flt, int32_t, float, CONV_S24_FLT);
DEFINE_SBUF_LAYER(s24_s24, int32_t, int32_t, CONV_NOOP);
DEFINE_SBUF_LAYER(s24_s32, int32_t, int32_t, CONV_S24_S32);
DEFINE_SBUF_LYR24(s24_o24, int32_t, uint8_t, CONV_NOOP);
DEFINE_SBUF_LAYER(s32_s16, int32_t, int16_t, CONV_S32_S16);
DEFINE_SBUF_LAYER(s32_f16, int32_t, float, CONV_S32_F16);
DEFINE_SBUF_LAYER(s32_flt, int32_t, float, CONV_S32_FLT);
DEFINE_SBUF_LAYER(s32_s24, int32_t, int32_t, CONV_S32_S24);
DEFINE_SBUF_LAYER(s32_s32, int32_t, int32_t, CONV_NOOP);
DEFINE_SBUF_LYR24(s32_o24, int32_t, uint8_t, CONV_NOOP);
static sbuf_layer_t layer_matrix[SFMT_MAX][SFMT_MAX] = {
{ NULL, NULL, NULL, NULL, NULL }, //NONE
{ NULL, sbuf_layer_s16_s16, sbuf_layer_s16_f16, sbuf_layer_s16_flt, sbuf_layer_s16_s24, sbuf_layer_s16_s32, sbuf_layer_s16_o24 },
{ NULL, sbuf_layer_f16_s16, sbuf_layer_f16_f16, sbuf_layer_f16_flt, sbuf_layer_f16_s24, sbuf_layer_f16_s32, sbuf_layer_f16_o24 },
{ NULL, sbuf_layer_flt_s16, sbuf_layer_flt_f16, sbuf_layer_flt_flt, sbuf_layer_flt_s24, sbuf_layer_flt_s32, sbuf_layer_flt_o24 },
{ NULL, sbuf_layer_s24_s16, sbuf_layer_s24_f16, sbuf_layer_s24_flt, sbuf_layer_s24_s24, sbuf_layer_s24_s32, sbuf_layer_s24_o24 },
{ NULL, sbuf_layer_s32_s16, sbuf_layer_s32_f16, sbuf_layer_s32_flt, sbuf_layer_s32_s24, sbuf_layer_s32_s32, sbuf_layer_s32_o24 },
{ NULL, NULL, NULL, NULL, NULL }, //O32
};
// copy interleaving: dst ch1 ch2 ch3 ch4 w/ src ch1 ch2 ch1 ch2 = only fill dst ch1 ch2
// dst_channels == src_channels isn't likely so ignore that optimization (dst must be >= than src).
// dst_ch_start indicates it should write to dst's chN,chN+1,etc
// sometimes one layer has less samples than others and need to 0-fill rest up to dst_max
void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int dst_max) {
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels + dst_ch_start;
int src_copy = dst_max;
if (src_copy > ssrc->filled)
src_copy = ssrc->filled;
if (ssrc->channels > sdst->channels) {
VGM_LOG("SBUF: src channels bigger than dst\n");
return;
}
sbuf_layer_t sbuf_layer_src_dst = layer_matrix[ssrc->fmt][sdst->fmt];
if (!sbuf_layer_src_dst) {
VGM_LOG("SBUF: undefined layer function sfmt %i to %i\n", ssrc->fmt, sdst->fmt);
return;
}
sbuf_layer_src_dst(ssrc->buf, sdst->buf, src_pos, dst_pos, src_copy, dst_max, ssrc->channels, sdst->channels);
}
typedef void (*sbuf_fade_t)(void* vsrc, int start, int to_do, int fade_pos, int fade_duration);
#define DEFINE_SBUF_FADE(suffix, buftype, func) \
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
buftype* buf = sbuf->buf; \
int s = start * sbuf->channels; \
int s_end = (start + to_do) * sbuf->channels; \
while (s < s_end) { \
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
for (int i = 0; i < sbuf->channels; i++) { \
buf[s] = func(buf[s] * fadedness); \
s++; \
} \
fade_pos++; \
} \
}
#define DEFINE_SBUF_FD24(suffix, buftype, func) \
static void sbuf_fade_##suffix(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { \
buftype* buf = sbuf->buf; \
int s = start * sbuf->channels; \
int s_end = (start + to_do) * sbuf->channels; \
while (s < s_end) { \
float fadedness = (float)(fade_duration - fade_pos) / fade_duration; \
for (int i = 0; i < sbuf->channels; i++) { \
put_u24ne(buf + s * 3, func(get_s24ne(buf + s * 3) * fadedness) ); \
s++; \
} \
fade_pos++; \
} \
}
// no need to clamp in fade outs
#define CONV_FADE_FLT(x) (x)
#define CONV_FADE_PCM(x) float_to_int(x)
DEFINE_SBUF_FADE(i16, int16_t, CONV_FADE_PCM);
DEFINE_SBUF_FADE(i32, int32_t, CONV_FADE_PCM);
DEFINE_SBUF_FADE(flt, float, CONV_FADE_FLT);
DEFINE_SBUF_FD24(o24, uint8_t, CONV_FADE_PCM);
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) { void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) {
//TODO: use interpolated fadedness to improve performance? //TODO: use interpolated fadedness to improve performance?
//TODO: use float fadedness? //TODO: use float fadedness?
int s = start * sbuf->channels;
int s_end = (start + to_do) * sbuf->channels;
switch(sbuf->fmt) { switch(sbuf->fmt) {
case SFMT_S16: { case SFMT_S16:
int16_t* buf = sbuf->buf; sbuf_fade_i16(sbuf, start, to_do, fade_pos, fade_duration);
while (s < s_end) { break;
double fadedness = (double)(fade_duration - fade_pos) / fade_duration; case SFMT_S24:
fade_pos++; case SFMT_S32:
sbuf_fade_i32(sbuf, start, to_do, fade_pos, fade_duration);
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_int(buf[s] * fadedness);
s++;
}
}
break; break;
}
case SFMT_FLT: case SFMT_FLT:
case SFMT_F32: { case SFMT_F16:
float* buf = sbuf->buf; sbuf_fade_flt(sbuf, start, to_do, fade_pos, fade_duration);
while (s < s_end) { break;
double fadedness = (double)(fade_duration - fade_pos) / fade_duration; case SFMT_O24:
fade_pos++; sbuf_fade_o24(sbuf, start, to_do, fade_pos, fade_duration);
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_float(buf[s] * fadedness);
s++;
}
}
break; break;
}
default: default:
VGM_LOG("SBUF: missing fade for fmt=%i\n", sbuf->fmt);
break; break;
} }
@ -428,3 +506,72 @@ void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_dur
int count = sbuf->filled - (start + to_do); int count = sbuf->filled - (start + to_do);
sbuf_silence_part(sbuf, start + to_do, count); sbuf_silence_part(sbuf, start + to_do, count);
} }
void sbuf_interleave(sbuf_t* sbuf, float** ibuf) {
if (sbuf->fmt != SFMT_FLT)
return;
// copy multidimensional buf (pcm[0]=[ch0,ch0,...], pcm[1]=[ch1,ch1,...])
// to interleaved buf (buf[0]=ch0, sbuf[1]=ch1, sbuf[2]=ch0, sbuf[3]=ch1, ...)
for (int ch = 0; ch < sbuf->channels; ch++) {
/* channels should be in standard order unlike Ogg Vorbis (at least in FSB) */
float* ptr = sbuf->buf;
float* channel = ibuf[ch];
ptr += ch;
for (int s = 0; s < sbuf->filled; s++) {
float val = channel[s];
#if 0 //to pcm16 //from vorbis)
int val = (int)floor(channel[s] * 32767.0f + 0.5f);
if (val > 32767) val = 32767;
else if (val < -32768) val = -32768;
#endif
*ptr = val;
ptr += sbuf->channels;
}
}
}
/* vorbis encodes channels in non-standard order, so we remap during conversion to fix this oddity.
* (feels a bit weird as one would think you could leave as-is and set the player's output order,
* but that isn't possible and remapping like this is what FFmpeg and every other plugin does). */
static const int xiph_channel_map[8][8] = {
{ 0 }, // 1ch: FC > same
{ 0, 1 }, // 2ch: FL FR > same
{ 0, 2, 1 }, // 3ch: FL FC FR > FL FR FC
{ 0, 1, 2, 3 }, // 4ch: FL FR BL BR > same
{ 0, 2, 1, 3, 4 }, // 5ch: FL FC FR BL BR > FL FR FC BL BR
{ 0, 2, 1, 5, 3, 4 }, // 6ch: FL FC FR BL BR LFE > FL FR FC LFE BL BR
{ 0, 2, 1, 6, 5, 3, 4 }, // 7ch: FL FC FR SL SR BC LFE > FL FR FC LFE BC SL SR
{ 0, 2, 1, 7, 5, 6, 3, 4 }, // 8ch: FL FC FR SL SR BL BR LFE > FL FR FC LFE BL BR SL SR
};
// converts from internal Vorbis format to standard PCM and remaps (mostly from Xiph's decoder_example.c)
void sbuf_interleave_vorbis(sbuf_t* sbuf, float** src) {
if (sbuf->fmt != SFMT_FLT)
return;
int channels = sbuf->channels;
/* convert float PCM (multichannel float array, with pcm[0]=ch0, pcm[1]=ch1, pcm[2]=ch0, etc)
* to 16 bit signed PCM ints (host order) and interleave + fix clipping */
for (int ch = 0; ch < channels; ch++) {
int ch_map = (channels > 8) ? ch : xiph_channel_map[channels - 1][ch]; // put Vorbis' ch to other outbuf's ch
float* ptr = sbuf->buf;
float* channel = src[ch_map];
ptr += ch;
for (int s = 0; s < sbuf->filled; s++) {
float val = channel[s];
#if 0 //to pcm16 from vorbis
int val = (int)floor(channel[s] * 32767.0f + 0.5f);
if (val > 32767) val = 32767;
else if (val < -32768) val = -32768;
#endif
*ptr = val;
ptr += channels;
}
}
}

View file

@ -11,29 +11,33 @@
* rather than planar (buffer per channel = [ch][s] = c1 c1 c1 c1 ... c2 c2 c2 c2 ...) */ * rather than planar (buffer per channel = [ch][s] = c1 c1 c1 c1 ... c2 c2 c2 c2 ...) */
typedef enum { typedef enum {
SFMT_NONE, SFMT_NONE,
SFMT_S16, /* standard PCM16 */ SFMT_S16, // PCM16
//SFMT_S24, SFMT_F16, // PCM16-like float (+-32767.0f), for internal use (simpler s16 <> f16, plus some decoders use it)
//SFMT_S32, SFMT_FLT, // standard float (+-1.0), for external players
SFMT_F32, /* pcm-like float (+-32768), for internal use (simpler pcm > f32 plus some decoders use this) */ SFMT_S24, // PCM24 for internal use (32-bit buffers)
SFMT_FLT, /* standard float (+-1.0), for external players */ SFMT_S32, // PCM32
SFMT_O24, // PCM24 LE for output (24-bit buffers), for external use only (can't handle as a regular buf internally)
SFMT_MAX,
} sfmt_t; } sfmt_t;
/* simple buffer info to pass around, for internal mixing /* simple buffer info to pass around, for internal mixing
* meant to held existing sound buffer pointers rather than alloc'ing directly (some ops will swap/move its internals) */ * meant to held existing sound buffer pointers rather than alloc'ing directly (some ops will swap/move its internals) */
typedef struct { typedef struct {
void* buf; /* current sample buffer */ void* buf; // current sample buffer
sfmt_t fmt; /* buffer type */ sfmt_t fmt; // buffer type
int channels; /* interleaved step or planar buffers */ int channels; // interleaved step or planar buffers
int samples; /* max samples */ int samples; // max samples
int filled; /* samples in buffer */ int filled; // samples in buffer
} sbuf_t; } sbuf_t;
/* it's probably slightly faster to make some function inline'd, but aren't called that often to matter (given big enough total samples) */ /* it's probably slightly faster to make some function inline'd, but aren't called that often to matter (given big enough total samples) */
void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels); void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels);
void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels); void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels);
void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels); void sbuf_init_f16(sbuf_t* sbuf, float* buf, int samples, int channels);
void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels); void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels);
int sfmt_get_sample_size(sfmt_t fmt); int sfmt_get_sample_size(sfmt_t fmt);
@ -46,15 +50,15 @@ void sbuf_consume(sbuf_t* sbuf, int count);
/* helpers to copy between buffers; note they assume dst and src aren't the same buf */ /* helpers to copy between buffers; note they assume dst and src aren't the same buf */
int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc); int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc);
void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf);
void sbuf_copy_from_f32(sbuf_t* sbuf, float* src);
void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples_copy); void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc, int samples_copy);
void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int expected); void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int expected);
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled);
void sbuf_silence_rest(sbuf_t* sbuf); void sbuf_silence_rest(sbuf_t* sbuf);
void sbuf_silence_part(sbuf_t* sbuf, int from, int count); void sbuf_silence_part(sbuf_t* sbuf, int from, int count);
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration); void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration);
void sbuf_interleave(sbuf_t* sbuf, float** ibuf);
void sbuf_interleave_vorbis(sbuf_t* sbuf, float** ibuf);
#endif #endif

View file

@ -15,8 +15,7 @@ typedef struct {
static size_t api_read(API_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) { static size_t api_read(API_STREAMFILE* sf, uint8_t* dst, offv_t offset, size_t length) {
void* user_data = sf->libsf->user_data; void* user_data = sf->libsf->user_data;
sf->libsf->seek(sf->libsf->user_data, offset, LIBSTREAMFILE_SEEK_SET); return sf->libsf->read(user_data, dst, offset, length);
return sf->libsf->read(user_data, dst, length);
} }
static size_t api_get_size(API_STREAMFILE* sf) { static size_t api_get_size(API_STREAMFILE* sf) {

View file

@ -1,28 +1,35 @@
#include "coding.h"
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libatrac9/libatrac9.h" #include "libatrac9/libatrac9.h"
/* opaque struct */ /* opaque struct */
struct atrac9_codec_data { typedef struct {
uint8_t* data_buffer; uint8_t* buf;
size_t data_buffer_size; int buf_size;
sample_t* sample_buffer; int16_t* sbuf;
size_t samples_filled; /* number of samples in the buffer */ int discard;
size_t samples_used; /* number of samples extracted from the buffer */
int samples_to_discard;
atrac9_config config; atrac9_config config;
Atrac9CodecInfo info;
void* handle; /* decoder handle */ void* handle;
Atrac9CodecInfo info; /* decoder info */ } atrac9_codec_data;
};
atrac9_codec_data* init_atrac9(atrac9_config* cfg) { static void free_atrac9(void* priv_data) {
atrac9_codec_data* data = priv_data;
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->buf);
free(data->sbuf);
free(data);
}
void* init_atrac9(atrac9_config* cfg) {
int status; int status;
uint8_t config_data[4]; uint8_t config_data[4];
atrac9_codec_data* data = NULL; atrac9_codec_data* data = NULL;
@ -47,16 +54,18 @@ atrac9_codec_data* init_atrac9(atrac9_config* cfg) {
} }
/* must hold at least one superframe and its samples */ // must hold at least one superframe and its samples
data->data_buffer_size = data->info.superframeSize; data->buf_size = data->info.superframeSize;
/* extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though) */
data->data_buffer = calloc(data->data_buffer_size + 0x10, sizeof(uint8_t));
if (!data->data_buffer) goto fail;
/* while ATRAC9 uses float internally, Sony's API only returns PCM16 */
data->sample_buffer = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t));
if (!data->sample_buffer) goto fail;
data->samples_to_discard = cfg->encoder_delay; // extra leeway as Atrac9Decode seems to overread ~2 bytes (doesn't affect decoding though)
data->buf = calloc(data->buf_size + 0x10, sizeof(uint8_t));
if (!data->buf) goto fail;
// while ATRAC9 uses float internally, Sony's API only returns PCM16
data->sbuf = calloc(data->info.channels * data->info.frameSamples * data->info.framesInSuperframe, sizeof(sample_t));
if (!data->sbuf) goto fail;
data->discard = cfg->encoder_delay;
memcpy(&data->config, cfg, sizeof(atrac9_config)); memcpy(&data->config, cfg, sizeof(atrac9_config));
@ -67,162 +76,132 @@ fail:
return NULL; return NULL;
} }
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
atrac9_codec_data* data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) { // read one raw block (superframe) and advance offsets; CBR and around 0x100-200 bytes
static bool read_frame(VGMSTREAM* v) {
VGMSTREAMCHANNEL* vs = &v->ch[0];
atrac9_codec_data* data = v->codec_data;
if (data->samples_filled) { /* consume samples */ int to_read = data->info.superframeSize;
int samples_to_get = data->samples_filled; int bytes = read_streamfile(data->buf, vs->offset, to_read, vs->streamfile);
if (data->samples_to_discard) { vs->offset += bytes;
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels, return (bytes == to_read);
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample_t));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int iframe, status;
int bytes_used = 0;
uint8_t *buffer = data->data_buffer;
size_t bytes;
data->samples_used = 0;
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
/* read one raw block (superframe) and advance offsets */
bytes = read_streamfile(data->data_buffer,stream->offset, data->info.superframeSize,stream->streamfile);
if (bytes != data->data_buffer_size) goto decode_fail;
stream->offset += bytes;
/* decode all frames in the superframe block */
for (iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
if (status < 0) goto decode_fail;
buffer += bytes_used;
data->samples_filled += data->info.frameSamples;
}
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("ATRAC9: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels);
} }
void reset_atrac9(atrac9_codec_data* data) { static int decode(VGMSTREAM* v) {
if (!data) return; int channels = v->channels;
atrac9_codec_data* data = v->codec_data;
if (!data->handle) uint8_t* buf = data->buf;
goto fail; int16_t* sbuf = data->sbuf;
// decode all frames in the superframe block
int samples = 0;
for (int iframe = 0; iframe < data->info.framesInSuperframe; iframe++) {
int bytes_used = 0;
int status = Atrac9Decode(data->handle, buf, sbuf, &bytes_used);
if (status < 0) {
VGM_LOG("ATRAC): decode error %i\n", status);
return false;
}
buf += bytes_used;
sbuf += data->info.frameSamples * channels;
samples += data->info.frameSamples;
}
return samples;
}
// ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
// superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples).
static bool decode_frame_atrac9(VGMSTREAM* v) {
bool ok = read_frame(v);
if (!ok)
return false;
int samples = decode(v);
if (samples <= 0)
return false;
decode_state_t* ds = v->decode_state;
atrac9_codec_data* data = v->codec_data;
sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
if (data->discard) {
ds->discard += data->discard;
data->discard = 0;
}
return true;
}
void reset_atrac9(void* priv_data) {
atrac9_codec_data* data = priv_data;
if (!data || !data->handle)
return;
data->discard = data->config.encoder_delay;
#if 0 #if 0
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */ /* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
{ {
int status;
uint8_t config_data[4];
Atrac9ReleaseHandle(data->handle); Atrac9ReleaseHandle(data->handle);
data->handle = Atrac9GetHandle(); data->handle = Atrac9GetHandle();
if (!data->handle) goto fail; if (!data->handle) return;
uint8_t config_data[4];
put_u32be(config_data, data->config.config_data); put_u32be(config_data, data->config.config_data);
status = Atrac9InitDecoder(data->handle, config_data);
int status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail; if (status < 0) goto fail;
} }
#endif #endif
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = data->config.encoder_delay;
return;
fail:
return; /* decode calls should fail... */
} }
void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) { void seek_atrac9(VGMSTREAM* v, int32_t num_sample) {
atrac9_codec_data* data = vgmstream->codec_data; atrac9_codec_data* data = v->codec_data;
if (!data) return; if (!data) return;
reset_atrac9(data); reset_atrac9(data);
/* find closest offset to desired sample, and samples to discard after that offset to reach loop */ // find closest offset to desired sample, and samples to discard after that offset to reach loop
{
int32_t seek_sample = data->config.encoder_delay + num_sample; int32_t seek_sample = data->config.encoder_delay + num_sample;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe; int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
superframe_number = (seek_sample / superframe_samples); /* closest */ // decoded frames affect each other slightly, so move offset back to make PCM stable
// and equivalent to a full discard loop
/* decoded frames affect each other slightly, so move offset back to make PCM stable int superframe_number = (seek_sample / superframe_samples); // closest
* and equivalent to a full discard loop */ int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe)
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
if (superframe_back > superframe_number) if (superframe_back > superframe_number)
superframe_back = superframe_number; superframe_back = superframe_number;
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples); int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize; off_t seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
data->samples_to_discard = seek_discard; /* already includes encoder delay */ data->discard = seek_discard; // already includes encoder delay
if (vgmstream->loop_ch) if (v->loop_ch) {
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset; v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset;
} }
#if 0 #if 0
//old full discard loop //old full discard loop
{ data->discard = num_sample;
data->samples_to_discard = num_sample; data->discard += data->config.encoder_delay;
data->samples_to_discard += data->config.encoder_delay;
/* loop offsets are set during decode; force them to stream start so discard works */ // loop offsets are set during decode; force them to stream start so discard works
if (vgmstream->loop_ch) if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset; vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
#endif #endif
} }
void free_atrac9(atrac9_codec_data* data) {
if (!data) return;
if (data->handle) Atrac9ReleaseHandle(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
}
static int atrac9_parse_config(uint32_t config_data, int* p_sample_rate, int* p_channels, size_t* p_frame_size, size_t* p_samples_per_frame) { static int atrac9_parse_config(uint32_t config_data, int* p_sample_rate, int* p_channels, size_t* p_frame_size, size_t* p_samples_per_frame) {
static const int sample_rate_table[16] = { static const int sample_rate_table[16] = {
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@ -266,7 +245,8 @@ fail:
return 0; return 0;
} }
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) { size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data) {
atrac9_codec_data* data = priv_data;
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe); return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
} }
@ -276,4 +256,12 @@ size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data) {
return 0; return 0;
return bytes / frame_size * samples_per_frame; return bytes / frame_size * samples_per_frame;
} }
const codec_info_t atrac9_decoder = {
.sample_type = SFMT_S16, //TODO: decoder doesn't return float (to match Sony's lib apparently)
.decode_frame = decode_frame_atrac9,
.free = free_atrac9,
.reset = reset_atrac9,
.seek = seek_atrac9,
};
#endif #endif

View file

@ -1,6 +1,7 @@
#include "coding.h"
#ifdef VGM_USE_CELT #ifdef VGM_USE_CELT
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "celt/celt_fsb.h" #include "celt/celt_fsb.h"
#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */ #define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */
@ -9,204 +10,25 @@
#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100 #define FSB_CELT_INTERNAL_SAMPLE_RATE 44100
#define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */ #define FSB_CELT_MAX_DATA_SIZE 0x200 /* from 0x2e~0x172/1d0, all files are CBR though */
typedef enum { CELT_0_06_1, CELT_0_11_0} celt_lib_t;
/* opaque struct */ /* opaque struct */
struct celt_codec_data { typedef struct {
sample_t* buffer; uint8_t buf[FSB_CELT_MAX_DATA_SIZE];
int frame_size; // current size
sample_t* sample_buffer; int16_t* sbuf;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int samples_to_discard; int discard;
int channel_mode; int channel_mode;
celt_lib_t version; celt_lib_t version;
void* mode_handle; void* mode_handle;
void* decoder_handle; void* decoder_handle;
}; } celt_codec_data;
static void free_celt_fsb(void* priv_data) {
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs). celt_codec_data* data = priv_data;
* FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */
celt_codec_data* init_celt_fsb(int channels, celt_lib_t version) {
int error = 0, lib_version = 0;
celt_codec_data* data = NULL;
data = calloc(1, sizeof(celt_codec_data));
if (!data) goto fail;
data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */
data->version = version;
switch(data->version) {
case CELT_0_06_1: /* older FSB4 (FMOD ~4.33) */
data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error);
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0: /* newer FSB4 (FMOD ~4.34), FSB5 */
data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error);
if (!data->decoder_handle || error != CELT_OK) goto fail;
break;
default:
goto fail;
}
data->sample_buffer = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(sample_t));
if (!data->sample_buffer) goto fail;
/* there is ~128 samples of encoder delay, but FMOD DLLs don't discard it? */
return data;
fail:
free_celt_fsb(data);
return NULL;
}
void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
celt_codec_data* data = vgmstream->codec_data;
int samples_done = 0;
while (samples_done < samples_to_do) {
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
if (data->samples_to_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample_t));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_used += samples_to_get;
data->samples_filled -= samples_to_get;
}
else { /* decode data */
int status;
uint8_t data_buffer[FSB_CELT_MAX_DATA_SIZE] = {0};
size_t bytes, frame_size;
data->samples_used = 0;
/* FSB DLLs do seem to check this fixed value */
if (read_32bitBE(stream->offset+0x00,stream->streamfile) != 0x17C30DF3) {
goto decode_fail;
}
frame_size = read_32bitLE(stream->offset+0x04,stream->streamfile);
if (frame_size > FSB_CELT_MAX_DATA_SIZE) {
goto decode_fail;
}
/* read and decode one raw block and advance offsets */
bytes = read_streamfile(data_buffer,stream->offset+0x08, frame_size,stream->streamfile);
if (bytes != frame_size) goto decode_fail;
switch(data->version) {
case CELT_0_06_1:
status = celt_decode_0061(data->decoder_handle, data_buffer,bytes, data->sample_buffer);
break;
case CELT_0_11_0:
status = celt_decode_0110(data->decoder_handle, data_buffer,bytes, data->sample_buffer, FSB_CELT_SAMPLES_PER_FRAME);
break;
default:
goto decode_fail;
}
if (status != CELT_OK) goto decode_fail;
stream->offset += 0x04+0x04+frame_size;
data->samples_filled += FSB_CELT_SAMPLES_PER_FRAME;
}
}
return;
decode_fail:
/* on error just put some 0 samples */
VGM_LOG("CELT: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, (samples_to_do - samples_done));
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample_t) * channels);
}
void reset_celt_fsb(celt_codec_data* data) {
if (!data) return;
/* recreate decoder (mode should not change) */
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle) celt_decoder_destroy_0061(data->decoder_handle);
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0:
if (data->decoder_handle) celt_decoder_destroy_0110(data->decoder_handle);
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL);
if (!data->decoder_handle) goto fail;
break;
default:
goto fail;
}
data->samples_used = 0;
data->samples_filled = 0;
data->samples_to_discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_celt_fsb(VGMSTREAM *vgmstream, int32_t num_sample) {
celt_codec_data* data = vgmstream->codec_data;
if (!data) return;
reset_celt_fsb(data);
data->samples_to_discard = num_sample;
/* loop offsets are set during decode; force them to stream start so discard works */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
}
void free_celt_fsb(celt_codec_data* data) {
if (!data) return; if (!data) return;
switch(data->version) { switch(data->version) {
@ -224,7 +46,189 @@ void free_celt_fsb(celt_codec_data* data) {
break; break;
} }
free(data->sample_buffer); free(data->sbuf);
free(data); free(data);
} }
/* FSB CELT, frames with custom header and standard data (API info from FMOD DLLs).
* FMOD used various libcelt versions, thus some tweaks are needed for them to coexist. */
static void* init_celt_fsb(int channels, celt_lib_t version) {
int error = 0, lib_version = 0;
celt_codec_data* data = NULL;
data = calloc(1, sizeof(celt_codec_data));
if (!data) goto fail;
data->channel_mode = channels; /* should be 1/2, or rejected by libcelt */
data->version = version;
switch(data->version) {
case CELT_0_06_1: // older FSB4 (FMOD ~4.33)
data->mode_handle = celt_mode_create_0061(FSB_CELT_INTERNAL_SAMPLE_RATE, data->channel_mode, FSB_CELT_SAMPLES_PER_FRAME, &error);
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0061(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_06_1_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0: // newer FSB4 (FMOD ~4.34), FSB5
data->mode_handle = celt_mode_create_0110(FSB_CELT_INTERNAL_SAMPLE_RATE, FSB_CELT_SAMPLES_PER_FRAME, &error); /* "custom" and not ok? */
if (!data->mode_handle || error != CELT_OK) goto fail;
error = celt_mode_info_0110(data->mode_handle, CELT_GET_BITSTREAM_VERSION, &lib_version);
if (error != CELT_OK || lib_version != FSB_CELT_0_11_0_VERSION) goto fail;
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, &error);
if (!data->decoder_handle || error != CELT_OK) goto fail;
break;
default:
goto fail;
}
data->sbuf = calloc(data->channel_mode * FSB_CELT_SAMPLES_PER_FRAME, sizeof(int16_t));
if (!data->sbuf) goto fail;
// ~128 samples of encoder delay, but FMOD DLLs don't discard them?
return data;
fail:
free_celt_fsb(data);
return NULL;
}
void* init_celt_fsb_v1(int channels) {
return init_celt_fsb(channels, CELT_0_06_1);
}
void* init_celt_fsb_v2(int channels) {
return init_celt_fsb(channels, CELT_0_11_0);
}
// read and decode one raw block and advance offsets
static bool read_frame(VGMSTREAM* v) {
VGMSTREAMCHANNEL* vs = &v->ch[0];
celt_codec_data* data = v->codec_data;
// FSB DLLs do seem to check this fixed value
if (read_u32be(vs->offset + 0x00, vs->streamfile) != 0x17C30DF3)
return false;
data->frame_size = read_u32le(vs->offset + 0x04, vs->streamfile);
if (data->frame_size > FSB_CELT_MAX_DATA_SIZE)
return false;
int bytes = read_streamfile(data->buf, vs->offset+0x08, data->frame_size, vs->streamfile);
vs->offset += 0x04 + 0x04 + data->frame_size;
return (bytes == data->frame_size);
}
static int decode(VGMSTREAM* v) {
celt_codec_data* data = v->codec_data;
int samples = FSB_CELT_SAMPLES_PER_FRAME;
int status;
switch(data->version) {
case CELT_0_06_1:
status = celt_decode_0061(data->decoder_handle, data->buf, data->frame_size, data->sbuf);
if (status != CELT_OK)
return -1;
return samples;
case CELT_0_11_0:
status = celt_decode_0110(data->decoder_handle, data->buf, data->frame_size, data->sbuf, samples);
if (status != CELT_OK)
return -1;
return samples;
default:
return -1;
}
}
static bool decode_frame_celt_fsb(VGMSTREAM* v) {
bool ok = read_frame(v);
if (!ok)
return false;
int samples = decode(v);
if (samples <= 0)
return false;
decode_state_t* ds = v->decode_state;
celt_codec_data* data = v->codec_data;
sbuf_init_s16(&ds->sbuf, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
if (data->discard) {
ds->discard += data->discard;
data->discard = 0;
}
return true;
}
void reset_celt_fsb(void* priv_data) {
celt_codec_data* data = priv_data;
if (!data) return;
// recreate decoder (mode should not change)
switch(data->version) {
case CELT_0_06_1:
if (data->decoder_handle)
celt_decoder_destroy_0061(data->decoder_handle);
data->decoder_handle = celt_decoder_create_0061(data->mode_handle);
if (!data->decoder_handle) goto fail;
break;
case CELT_0_11_0:
if (data->decoder_handle)
celt_decoder_destroy_0110(data->decoder_handle);
data->decoder_handle = celt_decoder_create_custom_0110(data->mode_handle, data->channel_mode, NULL);
if (!data->decoder_handle) goto fail;
break;
default:
goto fail;
}
data->discard = 0;
return;
fail:
return; /* decode calls should fail... */
}
void seek_celt_fsb(VGMSTREAM* v, int32_t num_sample) {
celt_codec_data* data = v->codec_data;
if (!data) return;
reset_celt_fsb(data);
data->discard = num_sample;
/* loop offsets are set during decode; force them to stream start so discard works */
if (v->loop_ch)
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset;
}
const codec_info_t celt_fsb_decoder = {
.sample_type = SFMT_S16, // decoder doesn't return float
.decode_frame = decode_frame_celt_fsb,
.free = free_celt_fsb,
.reset = reset_celt_fsb,
.seek = seek_celt_fsb,
};
#endif #endif

View file

@ -92,18 +92,15 @@ void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int ch
void decode_pcm8_sb(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_pcm8_sb(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm4(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_pcm4(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_pcm4_unsigned(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_pcm4_unsigned(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ulaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ulaw_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcmfloat(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
void decode_pcm24le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm24be(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_pcm32le(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample); int32_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
int32_t pcm24_bytes_to_samples(size_t bytes, int channels); int32_t pcm24_bytes_to_samples(size_t bytes, int channels);
int32_t pcm16_bytes_to_samples(size_t bytes, int channels); int32_t pcm16_bytes_to_samples(size_t bytes, int channels);
int32_t pcm8_bytes_to_samples(size_t bytes, int channels); int32_t pcm8_bytes_to_samples(size_t bytes, int channels);
/* pcm_decoder */
void decode_ulaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ulaw_int(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_alaw(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* psx_decoder */ /* psx_decoder */
void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config); void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config);
@ -288,13 +285,8 @@ int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data);
/* imuse_decoder */ /* imuse_decoder */
typedef struct imuse_codec_data imuse_codec_data; void* init_imuse_mcomp(STREAMFILE* sf, int channels);
void* init_imuse_aifc(STREAMFILE* sf, uint32_t start_offset, int channels);
imuse_codec_data* init_imuse(STREAMFILE* sf, int channels);
void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
void reset_imuse(imuse_codec_data* data);
void seek_imuse(imuse_codec_data* data, int32_t num_sample);
void free_imuse(imuse_codec_data* data);
/* ongakukan_adp_decoder */ /* ongakukan_adp_decoder */
typedef struct ongakukan_adp_data ongakukan_adp_data; typedef struct ongakukan_adp_data ongakukan_adp_data;
@ -308,14 +300,8 @@ void free_ongakukan_adp(ongakukan_adp_data* data);
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data); int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data);
/* compresswave_decoder */ /* compresswave_decoder */
typedef struct compresswave_codec_data compresswave_codec_data; void* init_compresswave(STREAMFILE* sf);
STREAMFILE* compresswave_get_streamfile(VGMSTREAM* v);
compresswave_codec_data* init_compresswave(STREAMFILE* sf);
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
void reset_compresswave(compresswave_codec_data* data);
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample);
void free_compresswave(compresswave_codec_data* data);
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
@ -332,13 +318,7 @@ void free_ea_mt(ea_mt_codec_data* data, int channels);
/* relic_decoder */ /* relic_decoder */
typedef struct relic_codec_data relic_codec_data; void* init_relic(int channels, int bitrate, int codec_rate);
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate);
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
void reset_relic(relic_codec_data* data);
void seek_relic(relic_codec_data* data, int32_t num_sample);
void free_relic(relic_codec_data* data);
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate); int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
@ -346,10 +326,8 @@ int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
typedef struct hca_codec_data hca_codec_data; typedef struct hca_codec_data hca_codec_data;
hca_codec_data* init_hca(STREAMFILE* sf); hca_codec_data* init_hca(STREAMFILE* sf);
void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do); void free_hca(void* data);
void reset_hca(hca_codec_data* data);
void loop_hca(hca_codec_data* data, int32_t num_sample);
void free_hca(hca_codec_data* data);
clHCA_stInfo* hca_get_info(hca_codec_data* data); clHCA_stInfo* hca_get_info(hca_codec_data* data);
typedef struct { typedef struct {
@ -369,13 +347,7 @@ STREAMFILE* hca_get_streamfile(hca_codec_data* data);
/* tac_decoder */ /* tac_decoder */
typedef struct tac_codec_data tac_codec_data; void* init_tac(STREAMFILE* sf);
tac_codec_data* init_tac(STREAMFILE* sf);
void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
void reset_tac(tac_codec_data* data);
void seek_tac(tac_codec_data* data, int32_t num_sample);
void free_tac(tac_codec_data* data);
/* ice_decoder */ /* ice_decoder */
@ -389,18 +361,19 @@ void free_ice(ice_codec_data* data);
/* ka1a_decoder */ /* ka1a_decoder */
typedef struct ka1a_codec_data ka1a_codec_data; void* init_ka1a(int bitrate_mode, int channels_tracks);
ka1a_codec_data* init_ka1a(int bitrate_mode, int channels_tracks); /* ubimpeg_decoder */
void free_ka1a(ka1a_codec_data* data); void* init_ubimpeg(uint32_t mode);
void reset_ka1a(ka1a_codec_data* data);
bool decode_ka1a_frame(VGMSTREAM* vgmstream); /* mio_decoder */
void seek_ka1a(VGMSTREAM* v, int32_t num_sample); void* init_mio(STREAMFILE* sf, int* p_loop_point);
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
/* ogg_vorbis_decoder */ /* ogg_vorbis_decoder */
typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data;
typedef struct { //todo simplify typedef struct { //todo simplify
STREAMFILE *streamfile; STREAMFILE *streamfile;
int64_t start; /* file offset where the Ogg starts */ int64_t start; /* file offset where the Ogg starts */
@ -415,16 +388,12 @@ typedef struct { //todo simplify
} ogg_vorbis_io; } ogg_vorbis_io;
ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io); ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io);
void decode_ogg_vorbis(ogg_vorbis_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_ogg_vorbis(ogg_vorbis_codec_data* data);
void seek_ogg_vorbis(ogg_vorbis_codec_data* data, int32_t num_sample);
void free_ogg_vorbis(ogg_vorbis_codec_data* data);
int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment); int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment);
void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate); void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate);
void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples); void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples);
void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set); void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, bool set);
void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, int set); void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, bool set);
STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data); STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data);
@ -438,6 +407,7 @@ typedef enum {
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */ VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */ VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */
VORBIS_AWC, /* Rockstar AWC: custom packet blocks/headers */ VORBIS_AWC, /* Rockstar AWC: custom packet blocks/headers */
VORBIS_OOR, /* Age .OOR: custom bitpacked pages (custom header + setup) */
} vorbis_custom_t; } vorbis_custom_t;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */ /* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -466,25 +436,15 @@ typedef struct {
/* output (kinda ugly here but to simplify) */ /* output (kinda ugly here but to simplify) */
off_t data_start_offset; off_t data_start_offset;
int64_t last_granule;
} vorbis_custom_config; } vorbis_custom_config;
vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config); vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config);
void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); void free_vorbis_custom(void* data);
void reset_vorbis_custom(VGMSTREAM* vgmstream); int32_t vorbis_custom_get_samples(VGMSTREAM* v);
void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample);
void free_vorbis_custom(vorbis_custom_codec_data* data);
#endif #endif
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
/* mpeg_decoder */ /* mpeg_decoder */
@ -531,19 +491,27 @@ typedef struct {
mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels); mpeg_codec_data* init_mpeg(STREAMFILE* sf, off_t start_offset, coding_t *coding_type, int channels);
mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config* config); mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config* config);
void decode_mpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_mpeg(mpeg_codec_data* data);
void seek_mpeg(VGMSTREAM* vgmstream, int32_t num_sample);
void free_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data); int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header); uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header);
bool test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey);
#endif
typedef struct {
int version;
int layer;
int bit_rate;
int sample_rate;
int frame_samples;
int frame_size; /* bytes */
int channels;
} mpeg_frame_info;
bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info); bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info); bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info);
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey); size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
#endif int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
#ifdef VGM_USE_G7221 #ifdef VGM_USE_G7221
@ -595,41 +563,23 @@ typedef struct {
uint32_t config_data; /* ATRAC9 config header */ uint32_t config_data; /* ATRAC9 config header */
int encoder_delay; /* initial samples to discard */ int encoder_delay; /* initial samples to discard */
} atrac9_config; } atrac9_config;
typedef struct atrac9_codec_data atrac9_codec_data;
atrac9_codec_data* init_atrac9(atrac9_config* cfg); void* init_atrac9(atrac9_config* cfg);
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data);
void reset_atrac9(atrac9_codec_data* data);
void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample);
void free_atrac9(atrac9_codec_data* data);
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data);
size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data); size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data);
#endif #endif
#ifdef VGM_USE_CELT #ifdef VGM_USE_CELT
/* celt_fsb_decoder */ void* init_celt_fsb_v1(int channels);
typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t; void* init_celt_fsb_v2(int channels);
typedef struct celt_codec_data celt_codec_data;
celt_codec_data* init_celt_fsb(int channels, celt_lib_t version);
void decode_celt_fsb(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_celt_fsb(celt_codec_data* data);
void seek_celt_fsb(VGMSTREAM* vgmstream, int32_t num_sample);
void free_celt_fsb(celt_codec_data* data);
#endif #endif
#ifdef VGM_USE_SPEEX #ifdef VGM_USE_SPEEX
/* speex_decoder */ /* speex_decoder */
typedef struct speex_codec_data speex_codec_data; void* init_speex_ea(int channels);
void* init_speex_torus(int channels);
speex_codec_data* init_speex_ea(int channels);
speex_codec_data* init_speex_torus(int channels);
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
void reset_speex(speex_codec_data* data);
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample);
void free_speex(speex_codec_data* data);
#endif #endif
@ -641,10 +591,7 @@ ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t s
ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size); ffmpeg_codec_data* init_ffmpeg_header_offset(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size);
ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong); ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels); void free_ffmpeg(void* data);
void reset_ffmpeg(ffmpeg_codec_data* data);
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample);
void free_ffmpeg(ffmpeg_codec_data* data);
void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples); void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples);
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data); uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
@ -652,6 +599,7 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap);
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data); const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data);
void ffmpeg_set_force_seek(ffmpeg_codec_data* data); void ffmpeg_set_force_seek(ffmpeg_codec_data* data);
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data); void ffmpeg_set_invert_floats(ffmpeg_codec_data* data);
void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data);
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key); const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data); int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
@ -724,7 +672,7 @@ typedef struct {
int frame_samples; int frame_samples;
} mp4_custom_t; } mp4_custom_t;
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4); ffmpeg_codec_data* init_ffmpeg_mp4_custom_ktac(STREAMFILE* sf, mp4_custom_t* mp4);
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4); ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4);
#endif #endif
@ -770,8 +718,6 @@ size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align);
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align); size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels); size_t ac3_bytes_to_samples(size_t bytes, int full_block_align, int channels);
size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes); size_t aac_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes);
int32_t mpeg_get_samples_clean(STREAMFILE* sf, off_t start, size_t size, uint32_t* p_loop_start, uint32_t* p_loop_end, int is_vbr);
int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay); int mpc_get_samples(STREAMFILE* sf, off_t offset, int32_t* p_samples, int32_t* p_delay);

View file

@ -1,60 +0,0 @@
#ifndef _CODING_UTILS_SAMPLES_
#define _CODING_UTILS_SAMPLES_
/* sample helpers */
//TODO maybe move to .c
// (as .h can be inlined but these probably aren't called enough times that there is a notable boost)
typedef struct {
int16_t* samples; /* current samples (pointer is moved once consumed) */
int filled; /* samples left */
int channels; /* max channels sample buf handles */
//TODO may be more useful with filled+consumed and not moving *samples?
} s16buf_t;
static inline void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) {
int samples_silence;
samples_silence = *p_samples_silence;
memset(*p_outbuf, 0, samples_silence * channels * sizeof(int16_t));
*p_outbuf += samples_silence * channels;
*p_samples_silence -= samples_silence;
}
static inline void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) {
int samples_discard;
samples_discard = *p_samples_discard;
if (samples_discard > sbuf->filled)
samples_discard = sbuf->filled;
/* just ignore part of samples */
sbuf->samples += samples_discard * sbuf->channels;
sbuf->filled -= samples_discard;
*p_samples_discard -= samples_discard;
}
/* copy, move and mark consumed samples */
static inline void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) {
int samples_consume;
samples_consume = *p_samples_consume;
if (samples_consume > sbuf->filled)
samples_consume = sbuf->filled;
/* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */
memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t));
sbuf->samples += samples_consume * sbuf->channels;
sbuf->filled -= samples_consume;
*p_outbuf += samples_consume * sbuf->channels;
*p_samples_consume -= samples_consume;
}
#endif /* _CODING_UTILS_SAMPLES_ */

View file

@ -1,27 +1,45 @@
#include "coding.h" #include "coding.h"
#include "coding_utils_samples.h" #include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libs/compresswave_lib.h" #include "libs/compresswave_lib.h"
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */ #define COMPRESSWAVE_MAX_FRAME_SAMPLES 512 // arbitrary, but should be multiple of 2 for 22050 mode
#define COMPRESSWAVE_MAX_CHANNELS 2
/* opaque struct */ typedef struct {
struct compresswave_codec_data {
/* config */
STREAMFILE* sf; STREAMFILE* sf;
TCompressWaveData* cw; TCompressWaveData* handle;
/* frame state */ int16_t pbuf[COMPRESSWAVE_MAX_FRAME_SAMPLES * COMPRESSWAVE_MAX_CHANNELS];
int16_t* samples; int discard;
int frame_samples; } compresswave_codec_data;
/* frame state */ static void reset_compresswave(void* priv_data) {
s16buf_t sbuf; compresswave_codec_data* data = priv_data;
int samples_discard; if (!data) return;
};
/* actual way to reset internal flags */
TCompressWaveData_Stop(data->handle);
TCompressWaveData_Play(data->handle, 0);
compresswave_codec_data* init_compresswave(STREAMFILE* sf) { data->discard = 0;
return;
}
static void free_compresswave(void* priv_data) {
compresswave_codec_data* data = priv_data;
if (!data)
return;
TCompressWaveData_Free(data->handle);
close_streamfile(data->sf);
free(data);
}
void* init_compresswave(STREAMFILE* sf) {
compresswave_codec_data* data = NULL; compresswave_codec_data* data = NULL;
data = calloc(1, sizeof(compresswave_codec_data)); data = calloc(1, sizeof(compresswave_codec_data));
@ -30,15 +48,10 @@ compresswave_codec_data* init_compresswave(STREAMFILE* sf) {
data->sf = reopen_streamfile(sf, 0); data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail; if (!data->sf) goto fail;
data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES; data->handle = TCompressWaveData_Create();
data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */ if (!data->handle) goto fail;
if (!data->samples) goto fail;
TCompressWaveData_LoadFromStream(data->handle, data->sf);
data->cw = TCompressWaveData_Create();
if (!data->cw) goto fail;
TCompressWaveData_LoadFromStream(data->cw, data->sf);
reset_compresswave(data); reset_compresswave(data);
@ -49,90 +62,46 @@ fail:
} }
static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) { static bool decode_frame_compresswave(VGMSTREAM* v) {
uint32_t Len; compresswave_codec_data* data = v->codec_data;
int ok; decode_state_t* ds = v->decode_state;
data->sbuf.samples = data->samples; int samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
data->sbuf.channels = 2; //if (samples % 2 && samples > 1)
data->sbuf.filled = 0; // samples -= 1; /* 22khz does 2 samples at once */
if (samples_to_do > data->frame_samples)
samples_to_do = data->frame_samples;
if (samples_to_do % 2 && samples_to_do > 1)
samples_to_do -= 1; /* 22khz does 2 samples at once */
Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */ uint32_t len = samples * sizeof(int16_t) * 2; /* forced stereo */
ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len); int ok = TCompressWaveData_Rendering(data->handle, data->pbuf, len);
if (!ok) goto fail; if (!ok) return false;
data->sbuf.filled = samples_to_do; sbuf_init_s16(&ds->sbuf, data->pbuf, samples, v->channels);
ds->sbuf.filled = ds->sbuf.samples;
return 1; return true;
fail:
return 0;
} }
void seek_compresswave(VGMSTREAM* v, int32_t num_sample) {
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { compresswave_codec_data* data = v->codec_data;
int ok;
while (samples_to_do > 0) {
s16buf_t* sbuf = &data->sbuf;
if (sbuf->filled <= 0) {
ok = decode_frame(data, samples_to_do);
if (!ok) goto fail;
}
if (data->samples_discard)
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
else
s16buf_consume(&outbuf, sbuf, &samples_to_do);
}
return;
fail:
VGM_LOG("COMPRESSWAVE: decode fail, missing %i samples\n", samples_to_do);
s16buf_silence(&outbuf, &samples_to_do, 2);
}
void reset_compresswave(compresswave_codec_data* data) {
if (!data) return;
/* actual way to reset internal flags */
TCompressWaveData_Stop(data->cw);
TCompressWaveData_Play(data->cw, 0);
data->sbuf.filled = 0;
data->samples_discard = 0;
return;
}
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample) {
if (!data) return; if (!data) return;
reset_compresswave(data); reset_compresswave(data);
data->samples_discard += num_sample; data->discard += num_sample;
} }
void free_compresswave(compresswave_codec_data* data) { STREAMFILE* compresswave_get_streamfile(VGMSTREAM* v) {
if (!data) compresswave_codec_data* data = v->codec_data;
return;
TCompressWaveData_Free(data->cw);
close_streamfile(data->sf);
free(data->samples);
free(data);
}
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data) {
if (!data) return NULL; if (!data) return NULL;
return data->sf; return data->sf;
} }
const codec_info_t compresswave_decoder = {
.sample_type = SFMT_S16,
.decode_frame = decode_frame_compresswave,
.free = free_compresswave,
.reset = reset_compresswave,
.seek = seek_compresswave,
//.frame_samples = 2-4 (lib handles arbitrary calls)
//.frame_size = VBR / huffman codes
};

View file

@ -5,6 +5,8 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libswresample/swresample.h> #include <libswresample/swresample.h>
#include "../base/decode_state.h"
#include "../base/codec_info.h"
/* opaque struct */ /* opaque struct */
struct ffmpeg_codec_data { struct ffmpeg_codec_data {
@ -26,32 +28,34 @@ struct ffmpeg_codec_data {
int stream_index; int stream_index;
int64_t total_samples; /* may be 0 and innacurate */ int64_t total_samples; /* may be 0 and innacurate */
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */ int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
int channel_remap_set; bool channel_remap_set;
int channel_remap[32]; /* map of channel > new position */ int channel_remap[32]; /* map of channel > new position */
int invert_floats_set; bool invert_floats_set;
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */ bool skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
int force_seek; /* flags for special seeking in faulty formats */ bool force_seek; /* flags for special seeking in faulty formats */
int bad_init; bool bad_init;
// FFmpeg context used for metadata // FFmpeg context used for metadata
const AVCodec* codec; const AVCodec* codec;
/* FFmpeg decoder state */ /* FFmpeg decoder state */
unsigned char* buffer; uint8_t* buffer;
AVIOContext* ioCtx; AVIOContext* ioCtx;
AVFormatContext* formatCtx; AVFormatContext* formatCtx;
AVCodecContext* codecCtx; AVCodecContext* codecCtx;
AVFrame* frame; /* last decoded frame */ AVFrame* frame; /* last decoded frame */
AVPacket* packet; /* last read data packet */ AVPacket* packet; /* last read data packet */
int read_packet; bool read_packet;
int end_of_stream; bool end_of_stream;
int end_of_audio; bool end_of_audio;
/* sample state */ /* other state */
int32_t samples_discard; int samples_discard;
int32_t samples_consumed;
int32_t samples_filled; sfmt_t fmt;
void* sbuf;
int sbuf_samples;
}; };
@ -82,25 +86,53 @@ static void g_init_ffmpeg(void) {
} }
} }
static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) { static void remap_audio_flt(float* outbuf, int sample_count, int channels, int* channel_mappings) {
int ch_from,ch_to,s; for (int s = 0; s < sample_count; s++) {
sample_t temp; for (int ch_from = 0; ch_from < channels; ch_from++) {
for (s = 0; s < sample_count; s++) {
for (ch_from = 0; ch_from < channels; ch_from++) {
if (ch_from > 32) if (ch_from > 32)
continue; continue;
ch_to = channel_mappings[ch_from]; int ch_to = channel_mappings[ch_from];
if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to) if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to)
continue; continue;
temp = outbuf[s*channels + ch_from]; float temp = outbuf[s*channels + ch_from];
outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to]; outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to];
outbuf[s*channels + ch_to] = temp; outbuf[s*channels + ch_to] = temp;
} }
} }
} }
static sfmt_t convert_sample_type(ffmpeg_codec_data* data) {
switch (data->codecCtx->sample_fmt) {
// used?
case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_U8:
// common
case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S16:
return SFMT_S16;
// PCM32 and FLAC 24-bit (upscaled)
case AV_SAMPLE_FMT_S32P:
case AV_SAMPLE_FMT_S32:
return SFMT_S32;
// transform-based codecs (ATRAC3, AAC, etc)
case AV_SAMPLE_FMT_FLTP:
case AV_SAMPLE_FMT_FLT:
return SFMT_FLT;
// seemingly not used by any codec, only filters
case AV_SAMPLE_FMT_DBLP:
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_S64P:
case AV_SAMPLE_FMT_S64:
default:
return SFMT_NONE;
}
}
/** /**
* Special patching for FFmpeg's buggy seek code. * Special patching for FFmpeg's buggy seek code.
* *
@ -198,7 +230,6 @@ test_seek:
avcodec_flush_buffers(data->codecCtx); avcodec_flush_buffers(data->codecCtx);
return 0; return 0;
fail: fail:
return -1; return -1;
} }
@ -357,7 +388,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
if (errcode < 0) goto fail; if (errcode < 0) goto fail;
/* reset non-zero values */ /* reset non-zero values */
data->read_packet = 1; data->read_packet = true;
/* setup other values */ /* setup other values */
{ {
@ -377,6 +408,10 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0); data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
#endif #endif
data->fmt = convert_sample_type(data);
if (data->fmt == SFMT_NONE)
goto fail;
/* try to guess frames/samples (duration isn't always set) */ /* try to guess frames/samples (duration isn't always set) */
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb); data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->total_samples < 0) if (data->total_samples < 0)
@ -545,23 +580,23 @@ fail:
return -1; return -1;
} }
/* decodes a new frame to internal data */ /* decodes a new frame to internal data */
static int decode_ffmpeg_frame(ffmpeg_codec_data* data) { static int decode_frame_internal(ffmpeg_codec_data* data) {
int errcode; if (data->bad_init)
int frame_error = 0; return -1;
if (data->bad_init) {
goto fail;
}
/* ignore once file is done (but not on EOF as FFmpeg can output samples until end_of_audio) */ /* ignore once file is done (but not on EOF as FFmpeg can output samples until end_of_audio) */
if (/*data->end_of_stream ||*/ data->end_of_audio) { if (/*data->end_of_stream ||*/ data->end_of_audio) {
VGM_LOG("FFMPEG: decode after end of audio\n"); VGM_LOG("FFMPEG: decode after end of audio\n");
goto fail; return -1;
} }
bool frame_error = false;
/* read data packets until valid is found */ /* read data packets until valid is found */
while (data->read_packet && !data->end_of_audio) { while (data->read_packet && !data->end_of_audio) {
if (!data->end_of_stream) { if (!data->end_of_stream) {
@ -569,19 +604,19 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
av_packet_unref(data->packet); av_packet_unref(data->packet);
/* read encoded data from demuxer into packet */ /* read encoded data from demuxer into packet */
errcode = av_read_frame(data->formatCtx, data->packet); int errcode = av_read_frame(data->formatCtx, data->packet);
if (errcode < 0) { if (errcode < 0) {
if (errcode == AVERROR_EOF) { if (errcode == AVERROR_EOF) {
data->end_of_stream = 1; /* no more data to read (but may "drain" samples) */ data->end_of_stream = true; // no more data to read (but may "drain" samples)
} }
else { else {
VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode); VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode);
frame_error = 1; //goto fail; frame_error = true; //goto fail;
} }
if (data->formatCtx->pb && data->formatCtx->pb->error) { if (data->formatCtx->pb && data->formatCtx->pb->error) {
VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error); VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error);
frame_error = 1; //goto fail; frame_error = true; //goto fail;
} }
} }
@ -591,31 +626,31 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
} }
/* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */ /* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */
errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet); int errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
if (errcode < 0) { if (errcode < 0) {
if (errcode != AVERROR(EAGAIN)) { if (errcode != AVERROR(EAGAIN)) {
VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode); VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode);
frame_error = 1; //goto fail; frame_error = true; //goto fail;
} }
} }
data->read_packet = 0; /* got data */ data->read_packet = false; // got data
} }
/* decode frame samples from sent packet or "drain" samples*/ /* decode frame samples from sent packet or "drain" samples*/
if (!frame_error) { if (!frame_error) {
/* receive uncompressed sample data from decoded frame */ /* receive uncompressed sample data from decoded frame */
errcode = avcodec_receive_frame(data->codecCtx, data->frame); int errcode = avcodec_receive_frame(data->codecCtx, data->frame);
if (errcode < 0) { if (errcode < 0) {
if (errcode == AVERROR_EOF) { if (errcode == AVERROR_EOF) {
data->end_of_audio = 1; /* no more audio, file is fully decoded */ data->end_of_audio = true; // no more audio, file is fully decoded
} }
else if (errcode == AVERROR(EAGAIN)) { else if (errcode == AVERROR(EAGAIN)) {
data->read_packet = 1; /* 0 samples, request more encoded data */ data->read_packet = true; // 0 samples, request more encoded data
} }
else { else {
VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode); VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode);
frame_error = 1;//goto fail; frame_error = true; //goto fail;
} }
} }
} }
@ -623,155 +658,83 @@ static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
/* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output /* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output
* (ex. BlazBlue X360 022_btl_az.xwb) */ * (ex. BlazBlue X360 022_btl_az.xwb) */
return data->frame->nb_samples;
data->samples_consumed = 0;
data->samples_filled = data->frame->nb_samples;
return 1;
fail:
return 0;
}
/* When casting float to int value is simply truncated:
* - 0.0000518798828125 * 32768.0f = 1.7f, (int)1.7 = 1, (int)-1.7 = -1
* (instead of 1.7 = 2, -1.7 = -2)
*
* Alts for more accurate rounding could be:
* - (int)floor(f32 * 32768.0) //not quite ok negatives
* - (int)floor(f32 * 32768.0f + 0.5f) //Xiph Vorbis style
* - (int)(f32 < 0 ? f32 - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest.
*
* Regular C float-to-int casting ("int i = (int)f") is somewhat slow due to IEEE
* float requirements, but C99 adds some faster-but-less-precise casting functions
* we try to use (returning "long", though). They work ok without "fast float math" compiler
* flags, but probably should be enabled anyway to ensure no extra IEEE checks are needed.
* MSVC added this in VS2015 (_MSC_VER 1900) but don't seem correctly optimized and is very slow.
*/
static inline int float_to_int(float val) {
#if defined(_MSC_VER)
return (int)val;
#else
return lrintf(val);
#endif
}
static inline int double_to_int(double val) {
#if defined(_MSC_VER)
return (int)val;
#else
return lrint(val); /* returns long tho */
#endif
} }
/* sample copy helpers, using different functions to minimize branches. /* sample copy helpers, using different functions to minimize branches.
*
* in theory, small optimizations like *outbuf++ vs outbuf[i] or alt clamping
* would matter for performance, but in practice aren't very noticeable;
* keep it simple for now until more tests are done.
* *
* in normal (interleaved) formats samples are laid out straight * in normal (interleaved) formats samples are laid out straight
* (ibuf[s*chs+ch], ex. 4ch with 4s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3) * (ibuf[s*chs+ch], ex. 4ch with 4s: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3)
* in "p" (planar) formats samples are in planes per channel * in "p" (planar) formats samples are in planes per channel
* (ibuf[ch][s], ex. 4ch with 4s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3) * (ibuf[ch][s], ex. 4ch with 4s: 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3)
*
* alt float clamping:
* clamp_float(f32)
* int s16 = (int)(f32 * 32768.0f);
* if ((unsigned)(s16 + 0x8000) & 0xFFFF0000)
* s16 = (s16 >> 31) ^ 0x7FFF;
*/ */
static void samples_silence_s16(sample_t* obuf, int ochs, int samples) { static void samples_u8_to_s16(int16_t* obuf, uint8_t* ibuf, int ichs, int samples) {
int s, total_samples = samples * ochs; for (int s = 0; s < samples * ichs; s++) {
for (s = 0; s < total_samples; s++) { obuf[s] = ((int)ibuf[s] - 0x80) << 8;
obuf[s] = 0; /* memset'd */
} }
} }
static void samples_u8p_to_s16(int16_t* obuf, uint8_t** ibuf, int ichs, int samples) {
static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) { for (int ch = 0; ch < ichs; ch++) {
int s, total_samples = samples * ichs; for (int s = 0; s < samples; s++) {
for (s = 0; s < total_samples; s++) { obuf[s*ichs + ch] = ((int)ibuf[ch][s] - 0x80) << 8;
obuf[s] = ((int)ibuf[skip*ichs + s] - 0x80) << 8;
}
}
static void samples_u8p_to_s16(sample_t* obuf, uint8_t** ibuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ((int)ibuf[ch][skip + s] - 0x80) << 8;
} }
} }
} }
static void samples_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) { static void samples_s16_to_s16(int16_t* obuf, int16_t* ibuf, int ichs, int samples) {
int s, total_samples = samples * ichs; for (int s = 0; s < samples * ichs; s++) {
for (s = 0; s < total_samples; s++) { obuf[s] = ibuf[s];
obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */
} }
} }
static void samples_s16p_to_s16(sample_t* obuf, int16_t** ibuf, int ichs, int samples, int skip) { static void samples_s16p_to_s16(int16_t* obuf, int16_t** ibuf, int ichs, int samples) {
int s, ch; for (int ch = 0; ch < ichs; ch++) {
for (ch = 0; ch < ichs; ch++) { for (int s = 0; s < samples; s++) {
for (s = 0; s < samples; s++) { obuf[s*ichs + ch] = ibuf[ch][s];
obuf[s*ichs + ch] = ibuf[ch][skip + s];
} }
} }
} }
static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) { static void samples_s32_to_s32(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
int s, total_samples = samples * ichs; for (int s = 0; s < samples * ichs; s++) {
for (s = 0; s < total_samples; s++) { obuf[s] = ibuf[ichs + s];
obuf[s] = ibuf[skip*ichs + s] >> 16;
} }
} }
static void samples_s32p_to_s16(sample_t* obuf, int32_t** ibuf, int ichs, int samples, int skip) { static void samples_s32p_to_s32(int32_t* obuf, int32_t** ibuf, int ichs, int samples) {
int s, ch; for (int ch = 0; ch < ichs; ch++) {
for (ch = 0; ch < ichs; ch++) { for (int s = 0; s < samples; s++) {
for (s = 0; s < samples; s++) { obuf[s*ichs + ch] = ibuf[ch][s];
obuf[s*ichs + ch] = ibuf[ch][skip + s] >> 16;
} }
} }
} }
static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) { static void samples_flt_to_flt(float* obuf, float* ibuf, int ichs, int samples, bool invert) {
int s, total_samples = samples * ichs; float scale = invert ? -1.0f : 1.0;
float scale = invert ? -32768.0f : 32768.0f; for (int s = 0; s < samples * ichs; s++) {
for (s = 0; s < total_samples; s++) { obuf[s] = ibuf[s] * scale;
obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale));
} }
} }
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) { static void samples_fltp_to_flt(float* obuf, float** ibuf, int ichs, int samples, bool invert) {
int s, ch; float scale = invert ? -1.0f : 1.0;
float scale = invert ? -32768.0f : 32768.0f; for (int ch = 0; ch < ichs; ch++) {
for (ch = 0; ch < ichs; ch++) { for (int s = 0; s < samples; s++) {
for (s = 0; s < samples; s++) { obuf[s*ichs + ch] = ibuf[ch][s] * scale;
obuf[s*ichs + ch] = clamp16(float_to_int(ibuf[ch][skip + s] * scale));
}
}
}
static void samples_dbl_to_s16(sample_t* obuf, double* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(double_to_int(ibuf[skip*ichs + s] * 32768.0));
}
}
static void samples_dblp_to_s16(sample_t* obuf, double** inbuf, int ichs, int samples, int skip) {
int s, ch;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
obuf[s*ichs + ch] = clamp16(double_to_int(inbuf[ch][skip + s] * 32768.0));
} }
} }
} }
static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do) { static void samples_s32_to_s24(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s] >> 8;
}
}
static void copy_samples(ffmpeg_codec_data* data, void* sbuf, int samples_to_do, int max_channels) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels; int channels = data->codecCtx->channels;
#else #else
int channels = data->codecCtx->ch_layout.nb_channels; int channels = data->codecCtx->ch_layout.nb_channels;
#endif #endif
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1); int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
void* ibuf;
void* ibuf;
if (is_planar) { if (is_planar) {
ibuf = data->frame->extended_data; ibuf = data->frame->extended_data;
} }
@ -779,82 +742,107 @@ static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_
ibuf = data->frame->data[0]; ibuf = data->frame->data[0];
} }
// decoder may return more channels than expected in rare/buggy cases
if (channels > max_channels) {
VGM_LOG_ONCE("FFMPEG: buggy channels\n");
channels = max_channels;
}
switch (data->codecCtx->sample_fmt) { switch (data->codecCtx->sample_fmt) {
/* unused? */ case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_U8P: if (is_planar) { samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } case AV_SAMPLE_FMT_U8:
// fall through if (is_planar)
case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; samples_u8p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_u8_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* common */ case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S16P: if (is_planar) { samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } case AV_SAMPLE_FMT_S16:
// fall through if (is_planar)
case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; samples_s16p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_s16_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* possibly FLAC and other lossless codecs */ case AV_SAMPLE_FMT_S32P:
case AV_SAMPLE_FMT_S32P: if (is_planar) { samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } case AV_SAMPLE_FMT_S32:
// fall through if (is_planar)
case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; samples_s32p_to_s32(sbuf, ibuf, channels, samples_to_do);
else
samples_s32_to_s32(sbuf, ibuf, channels, samples_to_do);
break;
/* mainly MDCT-like codecs (Ogg, AAC, etc) */ case AV_SAMPLE_FMT_FLTP:
case AV_SAMPLE_FMT_FLTP: if (is_planar) { samples_fltp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break; } case AV_SAMPLE_FMT_FLT:
// fall through if (is_planar)
case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break; samples_fltp_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
else
/* possibly PCM64 only (not enabled) */ samples_flt_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
case AV_SAMPLE_FMT_DBLP: if (is_planar) { samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; } break;
// fall through
case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
default: default:
break; break;
} }
if (data->channel_remap_set) // FFmpeg can't do PCM24 and upsamples to PCM32, if needed
remap_audio(outbuf, samples_to_do, channels, data->channel_remap); if (data->fmt == SFMT_S24) {
samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do);
}
} }
/* decode samples of any kind of FFmpeg format */ void remap_audio(ffmpeg_codec_data* data, sbuf_t* sbuf) {
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) { if (!data->channel_remap_set)
ffmpeg_codec_data* data = vgmstream->codec_data;
while (samples_to_do > 0) {
if (data->samples_consumed < data->samples_filled) {
/* consume samples */
int samples_to_get = (data->samples_filled - data->samples_consumed);
if (data->samples_discard) {
/* discard samples for looping */
if (samples_to_get > data->samples_discard)
samples_to_get = data->samples_discard;
data->samples_discard -= samples_to_get;
}
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do)
samples_to_get = samples_to_do;
copy_samples(data, outbuf, samples_to_get);
samples_to_do -= samples_to_get;
outbuf += samples_to_get * channels;
}
/* mark consumed samples */
data->samples_consumed += samples_to_get;
}
else {
int ok = decode_ffmpeg_frame(data);
if (!ok) goto decode_fail;
}
}
return; return;
decode_fail: switch(sbuf->fmt) {
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do); case SFMT_FLT:
samples_silence_s16(outbuf, channels, samples_to_do); remap_audio_flt(sbuf->buf, sbuf->filled, sbuf->channels, data->channel_remap);
break;
default: // trivial to add but not used
VGM_LOG("FFMPEG: unsupported channel remapping found (implement)\n");
break;
}
}
// ensure output buffer can handle samples, since in theory it could change per call
static bool prepare_sbuf(ffmpeg_codec_data* data, int samples, int channels) {
if (!data->sbuf && data->sbuf_samples >= samples)
return true;
free(data->sbuf);
data->sbuf_samples = samples * 2;
data->sbuf = malloc(data->sbuf_samples * channels * sizeof(float));
if (!data->sbuf) return false;
return true;
}
bool decode_frame_ffmpeg(VGMSTREAM* v) {
decode_state_t* ds = v->decode_state;
ffmpeg_codec_data* data = v->codec_data;
int samples = decode_frame_internal(data);
if (samples < 0)
return false;
if (!prepare_sbuf(data, samples, v->channels))
return false;
copy_samples(data, data->sbuf, samples, v->channels);
sbuf_init(&ds->sbuf, data->fmt, data->sbuf, samples, v->channels);
ds->sbuf.filled = samples;
remap_audio(data, &ds->sbuf);
if (data->samples_discard) {
ds->discard = data->samples_discard;
data->samples_discard = 0;
}
return true;
} }
@ -862,11 +850,7 @@ decode_fail:
/* UTILS */ /* UTILS */
/* ******************************************** */ /* ******************************************** */
void reset_ffmpeg(ffmpeg_codec_data* data) { static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
seek_ffmpeg(data, 0);
}
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
if (!data) return; if (!data) return;
/* Start from 0 and discard samples until sample (slower but not too noticeable). /* Start from 0 and discard samples until sample (slower but not too noticeable).
@ -891,13 +875,11 @@ void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
avcodec_flush_buffers(data->codecCtx); avcodec_flush_buffers(data->codecCtx);
} }
data->samples_consumed = 0;
data->samples_filled = 0;
data->samples_discard = num_sample; data->samples_discard = num_sample;
data->read_packet = 1; data->read_packet = true;
data->end_of_stream = 0; data->end_of_stream = false;
data->end_of_audio = 0; data->end_of_audio = false;
/* consider skip samples (encoder delay), if manually set */ /* consider skip samples (encoder delay), if manually set */
if (data->skip_samples_set) { if (data->skip_samples_set) {
@ -912,6 +894,15 @@ fail:
} }
static void seek_ffmpeg(VGMSTREAM* v, int32_t num_sample) {
seek_ffmpeg_internal(v->codec_data, num_sample);
}
static void reset_ffmpeg(void* priv_data) {
seek_ffmpeg_internal(priv_data, 0);
}
static void free_ffmpeg_config(ffmpeg_codec_data* data) { static void free_ffmpeg_config(ffmpeg_codec_data* data) {
if (data == NULL) if (data == NULL)
return; return;
@ -940,10 +931,11 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) {
av_freep(&(data->buffer)); av_freep(&(data->buffer));
} }
//todo avformat_find_stream_info may cause some Win Handle leaks? related to certain option //TODO: avformat_find_stream_info may cause some Win Handle leaks? related to certain option
} }
void free_ffmpeg(ffmpeg_codec_data* data) { void free_ffmpeg(void* priv_data) {
ffmpeg_codec_data* data = priv_data;
if (data == NULL) if (data == NULL)
return; return;
@ -997,9 +989,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
#endif #endif
/* set skip samples with our internal discard */ /* set skip samples with our internal discard */
data->skip_samples_set = 1; data->skip_samples_set = true;
data->samples_discard = skip_samples;
data->skip_samples = skip_samples; data->skip_samples = skip_samples;
data->samples_discard = skip_samples;
} }
/* returns channel layout if set */ /* returns channel layout if set */
@ -1026,8 +1018,7 @@ uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data) {
/* yet another hack to fix codecs that encode channels in different order and reorder on decoder /* yet another hack to fix codecs that encode channels in different order and reorder on decoder
* but FFmpeg doesn't do it automatically * but FFmpeg doesn't do it automatically
* (maybe should be done via mixing, but could clash with other stuff?) */ * (maybe should be done via mixing, but could clash with other stuff?) */
void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) { void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
int i;
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100) #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels; int channels = data->codecCtx->channels;
#else #else
@ -1037,10 +1028,12 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int *channel_remap) {
if (channels > 32) if (channels > 32)
return; return;
for (i = 0; i < channels; i++) { for (int i = 0; i < channels; i++) {
data->channel_remap[i] = channel_remap[i]; data->channel_remap[i] = channel_remap[i];
} }
data->channel_remap_set = 1;
VGM_LOG("FFMPEG: channel remapping set\n");
data->channel_remap_set = true;
} }
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) { const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
@ -1059,7 +1052,7 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
/* some formats like Smacker are so buggy that any seeking is impossible (even on video players), /* some formats like Smacker are so buggy that any seeking is impossible (even on video players),
* or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset). * or MPC with an incorrectly parsed seek table (using as 0 some non-0 seek offset).
* whatever, we'll just kill and reconstruct FFmpeg's config every time */ * whatever, we'll just kill and reconstruct FFmpeg's config every time */
data->force_seek = 1; data->force_seek = true;
reset_ffmpeg(data); /* reset state from trying to seek */ reset_ffmpeg(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->stream_index]; //stream = data->formatCtx->streams[data->stream_index];
} }
@ -1067,7 +1060,16 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) { void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
if (!data) if (!data)
return; return;
data->invert_floats_set = 1; data->invert_floats_set = true;
}
// for flac 24-bit, since FFMpeg upsamples to PCM32 (ignored if flac 16-bit)
void ffmpeg_set_allow_pcm24(ffmpeg_codec_data* data) {
if (!data)
return;
if (data->fmt != SFMT_S32)
return;
data->fmt = SFMT_S24;
} }
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) { const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
@ -1125,4 +1127,22 @@ STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
if (!data) return NULL; if (!data) return NULL;
return data->sf; return data->sf;
} }
static sfmt_t get_sample_type_ffmpeg(VGMSTREAM* v) {
ffmpeg_codec_data* data = v->codec_data;
if (!data)
return SFMT_NONE;
return data->fmt;
}
const codec_info_t ffmpeg_decoder = {
.get_sample_type = get_sample_type_ffmpeg,
.decode_frame = decode_frame_ffmpeg,
.free = free_ffmpeg,
.reset = reset_ffmpeg,
.seek = seek_ffmpeg,
};
#endif #endif

View file

@ -4,7 +4,7 @@
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
typedef enum { MP4_STD, MP4_LYN } mp4_type_t; typedef enum { MP4_KTAC, MP4_LYN } mp4_type_t;
/** /**
* Makes a MP4 header for MP4 raw data with a separate frame table, simulating a real MP4 that * Makes a MP4 header for MP4 raw data with a separate frame table, simulating a real MP4 that
@ -54,7 +54,7 @@ static void add_u16b(m4a_header_t* h, uint16_t value) {
h->bytes += 0x02; h->bytes += 0x02;
} }
static void add_u8(m4a_header_t* h, uint32_t value) { static void add_u8b(m4a_header_t* h, uint32_t value) {
put_u8(h->out, value); put_u8(h->out, value);
h->out += 0x01; h->out += 0x01;
h->bytes += 0x01; h->bytes += 0x01;
@ -77,7 +77,7 @@ static void save_atom(m4a_header_t* h, m4a_state_t* s) {
s->bytes = h->bytes; s->bytes = h->bytes;
} }
static void load_atom(m4a_header_t* h, m4a_state_t* s) { static void mend_atom(m4a_header_t* h, m4a_state_t* s) {
put_u32be(s->out, h->bytes - s->bytes); put_u32be(s->out, h->bytes - s->bytes);
} }
@ -172,6 +172,7 @@ static void add_stts(m4a_header_t* h) {
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */ /* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
static const int m4a_sample_rates[16] = { static const int m4a_sample_rates[16] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
// index 15 means sample rate is stored in 24-bit after index
}; };
static const uint8_t m4a_channels[14] = { static const uint8_t m4a_channels[14] = {
0, 0,
@ -191,64 +192,88 @@ static const uint8_t m4a_channels[14] = {
}; };
static void add_esds(m4a_header_t* h) { static void add_esds(m4a_header_t* h) {
uint16_t config = 0;
/* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding /* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding
- config (channels/rate/etc), other atoms with the same stuff is just info * config (channels/rate/etc), other atoms with the same stuff is just info
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */ * - see ISO/IEC 14496-3:2001 > 1.6.2. Syntax (AudioSpecificConfig + GASpecificConfig) */
uint16_t config = 0;
{ uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC, etc */
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
uint8_t sr_index = 0; uint8_t sr_index = 0;
uint8_t ch_index = 0; uint8_t ch_index = 0;
uint8_t unknown = 0; uint8_t extra = 0;
int i; for (int i = 0; i < 16; i++) {
for (i = 0; i < 16; i++) {
if (m4a_sample_rates[i] == h->mp4->sample_rate) { if (m4a_sample_rates[i] == h->mp4->sample_rate) {
sr_index = i; sr_index = i;
break; break;
} }
} }
for (i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
if (m4a_channels[i] == h->mp4->channels) { if (m4a_channels[i] == h->mp4->channels) {
ch_index = i; ch_index = i;
break; break;
} }
} }
extra = 0 ; // frameLength (1b) + dependsOnCoreCoder (1b) + extensionFlag (1b)
// KTAC uses 'quad' (2/2) rather than standard 4.0 (3/1) [Winning Post 9 2022 (PC)]
if (h->mp4->channels == 4 && h->type == MP4_KTAC) {
ch_index = 0;
}
config |= (object_type & 0x1F) << 11; /* 5b */ config |= (object_type & 0x1F) << 11; /* 5b */
config |= (sr_index & 0x0F) << 7; /* 4b */ config |= (sr_index & 0x0F) << 7; /* 4b */
config |= (ch_index & 0x0F) << 3; /* 4b */ config |= (ch_index & 0x0F) << 3; /* 4b */
config |= (unknown & 0x07) << 0; /* 3b */ config |= (extra & 0x07) << 0; /* 3b */
}
add_atom(h, "esds", 0x33); uint8_t slcfg_size = 0x01;
uint8_t config_size = 0x02 + (ch_index == 0 ? 0x07 : 0x00);
uint8_t deccfg_size = 0x14;
uint8_t descr_size = 0x03 + 0x08 + deccfg_size + config_size + slcfg_size;
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "esds", 0x00);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u8 (h, 0x03); /* ES_DescrTag */ add_u8b (h, 0x03); /* ES_DescrTag */
add_u32b(h, 0x80808022); /* size 0x22 */ add_u32b(h, 0x80808000 + descr_size); /* tag size (all subtags) */
add_u16b(h, 0x0000); /* stream Id */ add_u16b(h, 0x0000); /* stream Id */
add_u8 (h, 0x00); /* flags */ add_u8b (h, 0x00); /* flags */
add_u8 (h, 0x04); /* DecoderConfigDescrTag */ add_u8b (h, 0x04); /* DecoderConfigDescrTag */
add_u32b(h, 0x80808014); /* size 0x14 */ add_u32b(h, 0x80808000 + deccfg_size); /* subtag size */
add_u8 (h, 0x40); /* object type (0x40=audio) */ add_u8b (h, 0x40); /* object type (0x40=audio) */
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */ add_u8b (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
add_u24b(h, 0x000000); /* buffer size */ add_u24b(h, 0x000000); /* buffer size */
add_u32b(h, 0); /* max bitrate (256000?)*/ add_u32b(h, 0); /* max bitrate (256000?)*/
add_u32b(h, 0); /* average bitrate (256000?) */ add_u32b(h, 0); /* average bitrate (256000?) */
add_u8 (h, 0x05); /* DecSpecificInfoTag */ add_u8b (h, 0x05); /* DecSpecificInfoTag */
add_u32b(h, 0x80808002); /* size 0x02 */ add_u32b(h, 0x80808000 + config_size); /* subtag size */
add_u16b(h, config); /* actual decoder info */ add_u16b(h, config); /* actual decoder info */
add_u8 (h, 0x06); /* SLConfigDescrTag */ // config for quad, abridged (see spec's program_config_element)
add_u32b(h, 0x80808001); /* size 0x01 */ if (ch_index == 0 && h->mp4->channels == 4) {
add_u8 (h, 0x02); /* predefined (2=default) */ uint16_t ch_config1 = 0x0004 | (object_type << 10) | (sr_index << 6); // config + channel info (part 1)
uint32_t ch_config2 = 0x04002110; // channel info (part 2)
uint8_t comment_len = 0x00;
add_u16b(h, ch_config1);
add_u32b(h, ch_config2);
add_u8b (h, comment_len);
}
add_u8b (h, 0x06); /* SLConfigDescrTag */
add_u32b(h, 0x80808000 + slcfg_size); /* tag size */
add_u8b (h, 0x02); /* predefined (2=default) */
mend_atom(h, &s);
} }
static void add_mp4a(m4a_header_t* h) { static void add_mp4a(m4a_header_t* h) {
add_atom(h, "mp4a", 0x57); m4a_state_t s;
save_atom(h, &s);
add_atom(h, "mp4a", 0x00);
add_u32b(h, 0); /* ? */ add_u32b(h, 0); /* ? */
add_u32b(h, 1); /* Data reference index */ add_u32b(h, 1); /* Data reference index */
add_u32b(h, 0); /* Reserved */ add_u32b(h, 0); /* Reserved */
@ -259,13 +284,18 @@ static void add_mp4a(m4a_header_t* h) {
add_u16b(h, h->mp4->sample_rate); /* Sample rate */ add_u16b(h, h->mp4->sample_rate); /* Sample rate */
add_u16b(h, 0); /* ? */ add_u16b(h, 0); /* ? */
add_esds(h); /* elementary stream descriptor */ add_esds(h); /* elementary stream descriptor */
mend_atom(h, &s);
} }
static void add_stsd(m4a_header_t* h) { static void add_stsd(m4a_header_t* h) {
add_atom(h, "stsd", 0x67); m4a_state_t s;
save_atom(h, &s);
add_atom(h, "stsd", 0x00);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */ add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */ add_u32b(h, 1); /* Number of entries */
add_mp4a(h); add_mp4a(h);
mend_atom(h, &s);
} }
static void add_stbl(m4a_header_t* h) { static void add_stbl(m4a_header_t* h) {
@ -278,7 +308,7 @@ static void add_stbl(m4a_header_t* h) {
add_stsc(h); /* Sample-to-chunk */ add_stsc(h); /* Sample-to-chunk */
add_stsz(h); /* Sample size */ add_stsz(h); /* Sample size */
add_stco(h); /* Chunk offset */ add_stco(h); /* Chunk offset */
load_atom(h, &s); mend_atom(h, &s);
} }
static void add_dinf(m4a_header_t* h) { static void add_dinf(m4a_header_t* h) {
@ -305,7 +335,7 @@ static void add_minf(m4a_header_t* h) {
add_smhd(h); add_smhd(h);
add_dinf(h); add_dinf(h);
add_stbl(h); add_stbl(h);
load_atom(h, &s); mend_atom(h, &s);
} }
static void add_hdlr(m4a_header_t* h) { static void add_hdlr(m4a_header_t* h) {
@ -338,7 +368,7 @@ static void add_mdia(m4a_header_t* h) {
add_mdhd(h); add_mdhd(h);
add_hdlr(h); add_hdlr(h);
add_minf(h); add_minf(h);
load_atom(h, &s); mend_atom(h, &s);
} }
static void add_tkhd(m4a_header_t* h) { static void add_tkhd(m4a_header_t* h) {
@ -375,7 +405,7 @@ static void add_trak(m4a_header_t* h) {
add_atom(h, "trak", 0x00); add_atom(h, "trak", 0x00);
add_tkhd(h); add_tkhd(h);
add_mdia(h); add_mdia(h);
load_atom(h, &s); mend_atom(h, &s);
} }
static void add_mvhd(m4a_header_t* h) { static void add_mvhd(m4a_header_t* h) {
@ -416,7 +446,7 @@ static void add_moov(m4a_header_t* h) {
add_mvhd(h); add_mvhd(h);
add_trak(h); add_trak(h);
//add_udta(h); //add_udta(h);
load_atom(h, &s); mend_atom(h, &s);
} }
/* *** */ /* *** */
@ -494,7 +524,7 @@ static ffmpeg_codec_data* init_ffmpeg_mp4_custom(STREAMFILE* sf, mp4_custom_t* m
bytes = make_m4a_header(buf, buf_len, mp4, sf, type); /* before changing stream_offset/size */ bytes = make_m4a_header(buf, buf_len, mp4, sf, type); /* before changing stream_offset/size */
switch(type) { switch(type) {
case MP4_STD: /* regular raw data */ case MP4_KTAC: /* regular raw data */
temp_sf = sf; temp_sf = sf;
break; break;
case MP4_LYN: /* frames have size before them, but also a seek table */ case MP4_LYN: /* frames have size before them, but also a seek table */
@ -523,8 +553,8 @@ fail:
return NULL; return NULL;
} }
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4) { ffmpeg_codec_data* init_ffmpeg_mp4_custom_ktac(STREAMFILE* sf, mp4_custom_t* mp4) {
return init_ffmpeg_mp4_custom(sf, mp4, MP4_STD); return init_ffmpeg_mp4_custom(sf, mp4, MP4_KTAC);
} }
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4) { ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4) {

View file

@ -1,26 +1,44 @@
#include "coding.h" #include "coding.h"
#include "../base/decode_state.h"
#include "libs/clhca.h" #include "libs/clhca.h"
#include "../base/codec_info.h"
struct hca_codec_data { struct hca_codec_data {
STREAMFILE* sf; STREAMFILE* sf;
clHCA_stInfo info; clHCA_stInfo info;
signed short* sample_buffer; void* buf;
size_t samples_filled; float* fbuf;
size_t samples_consumed; int current_delay;
size_t samples_to_discard;
void* data_buffer;
unsigned int current_block; unsigned int current_block;
void* handle; void* handle;
}; };
static void reset_hca(void* priv) {
hca_codec_data* data = priv;
clHCA_DecodeReset(data->handle);
data->current_block = 0;
data->current_delay = data->info.encoderDelay;
}
void free_hca(void* priv) {
hca_codec_data* data = priv;
if (!data) return;
close_streamfile(data->sf);
clHCA_done(data->handle);
free(data->handle);
free(data->buf);
free(data->fbuf);
free(data);
}
/* init a HCA stream; STREAMFILE will be duplicated for internal use. */ /* init a HCA stream; STREAMFILE will be duplicated for internal use. */
hca_codec_data* init_hca(STREAMFILE* sf) { hca_codec_data* init_hca(STREAMFILE* sf) {
uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */ uint8_t header_buffer[0x1000]; /* hca header buffer data (probable max ~0x400) */
hca_codec_data* data = NULL; /* vgmstream HCA context */ hca_codec_data* data = NULL; /* vgmstream HCA context */
int header_size; int header_size;
int status; int status;
@ -51,11 +69,11 @@ hca_codec_data* init_hca(STREAMFILE* sf) {
status = clHCA_getInfo(data->handle, &data->info); /* extract header info */ status = clHCA_getInfo(data->handle, &data->info); /* extract header info */
if (status < 0) goto fail; if (status < 0) goto fail;
data->data_buffer = malloc(data->info.blockSize); data->buf = malloc(data->info.blockSize);
if (!data->data_buffer) goto fail; if (!data->buf) goto fail;
data->sample_buffer = malloc(sizeof(signed short) * data->info.channelCount * data->info.samplesPerBlock); data->fbuf = malloc(sizeof(float) * data->info.channelCount * data->info.samplesPerBlock);
if (!data->sample_buffer) goto fail; if (!data->fbuf) goto fail;
/* load streamfile for reads */ /* load streamfile for reads */
data->sf = reopen_streamfile(sf, 0); data->sf = reopen_streamfile(sf, 0);
@ -71,86 +89,64 @@ fail:
return NULL; return NULL;
} }
void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { static bool read_packet(VGMSTREAM* v) {
int samples_done = 0; hca_codec_data* data = v->codec_data;
const unsigned int channels = data->info.channelCount;
const unsigned int blockSize = data->info.blockSize;
// EOF/error
if (data->current_block >= data->info.blockCount)
return false;
while (samples_done < samples_to_do) { // single block of frames
const unsigned int block_size = data->info.blockSize;
//VGMSTREAMCHANNEL* vs = &v->ch[0];
off_t offset = data->info.headerSize + data->current_block * block_size; //vs->offset
if (data->samples_filled) { int bytes = read_streamfile(data->buf, offset, block_size, data->sf);
int samples_to_get = data->samples_filled; if (bytes != block_size) {
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, block_size, (uint32_t)offset);
if (data->samples_to_discard) { return false;
/* discard samples for looping */
if (samples_to_get > data->samples_to_discard)
samples_to_get = data->samples_to_discard;
data->samples_to_discard -= samples_to_get;
} }
else {
/* get max samples and copy */
if (samples_to_get > samples_to_do - samples_done)
samples_to_get = samples_to_do - samples_done;
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_consumed*channels,
samples_to_get*channels * sizeof(sample_t));
samples_done += samples_to_get;
}
/* mark consumed samples */
data->samples_consumed += samples_to_get;
data->samples_filled -= samples_to_get;
}
else {
off_t offset = data->info.headerSize + data->current_block * blockSize;
int status;
size_t bytes;
/* EOF/error */
if (data->current_block >= data->info.blockCount) {
memset(outbuf, 0, (samples_to_do - samples_done) * channels * sizeof(sample_t));
break;
}
/* read frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->sf);
if (bytes != blockSize) {
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, (uint32_t)offset);
break;
}
data->current_block++; data->current_block++;
return true;
}
bool decode_frame_hca(VGMSTREAM* v) {
bool ok = read_packet(v);
if (!ok)
return false;
decode_state_t* ds = v->decode_state;
hca_codec_data* data = v->codec_data;
const unsigned int block_size = data->info.blockSize;
/* decode frame */ /* decode frame */
status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize); int status = clHCA_DecodeBlock(data->handle, data->buf, block_size);
if (status < 0) { if (status < 0) {
VGM_LOG("HCA: decode fail at %x, code=%i\n", (uint32_t)offset, status); VGM_LOG("HCA: decode fail, code=%i\n", status);
break; return false;
} }
/* extract samples */ clHCA_ReadSamples(data->handle, data->fbuf);
clHCA_ReadSamples16(data->handle, data->sample_buffer);
data->samples_consumed = 0; int samples = data->info.samplesPerBlock;
data->samples_filled += data->info.samplesPerBlock; sbuf_init_flt(&ds->sbuf, data->fbuf, samples, v->channels);
} ds->sbuf.filled = samples;
if (data->current_delay) {
ds->discard += data->current_delay;
data->current_delay = 0;
} }
return true;
} }
void reset_hca(hca_codec_data* data) { void seek_hca(VGMSTREAM* v, int32_t num_sample) {
if (!data) return; hca_codec_data* data = v->codec_data;
//decode_state_t* ds = v->decode_state;
clHCA_DecodeReset(data->handle); //TODO handle arbitrary seek points to block N
data->current_block = 0;
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_to_discard = data->info.encoderDelay;
}
void loop_hca(hca_codec_data* data, int32_t num_sample) {
if (!data) return;
/* manually calc loop values if not set (should only happen with installed/forced looping, /* manually calc loop values if not set (should only happen with installed/forced looping,
* as actual files usually pad encoder delay so earliest loopStartBlock becomes 1-2, * as actual files usually pad encoder delay so earliest loopStartBlock becomes 1-2,
@ -163,20 +159,9 @@ void loop_hca(hca_codec_data* data, int32_t num_sample) {
} }
data->current_block = data->info.loopStartBlock; data->current_block = data->info.loopStartBlock;
data->samples_filled = 0; data->current_delay = data->info.loopStartDelay;
data->samples_consumed = 0; //ds->discard = data->info.loopStartDelay //overwritten on decode
data->samples_to_discard = data->info.loopStartDelay;
}
void free_hca(hca_codec_data* data) {
if (!data) return;
close_streamfile(data->sf);
clHCA_done(data->handle);
free(data->handle);
free(data->data_buffer);
free(data->sample_buffer);
free(data);
} }
clHCA_stInfo* hca_get_info(hca_codec_data* data) { clHCA_stInfo* hca_get_info(hca_codec_data* data) {
@ -235,14 +220,14 @@ static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk) {
size_t bytes; size_t bytes;
/* read and test frame */ /* read and test frame */
bytes = read_streamfile(data->data_buffer, offset, block_size, data->sf); bytes = read_streamfile(data->buf, offset, block_size, data->sf);
if (bytes != block_size) { if (bytes != block_size) {
/* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */ /* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */
//total_score = -1; //total_score = -1;
break; break;
} }
score = clHCA_TestBlock(data->handle, data->data_buffer, block_size); score = clHCA_TestBlock(data->handle, data->buf, block_size);
/* get first non-blank frame */ /* get first non-blank frame */
if (!hk->start_offset && score != 0) { if (!hk->start_offset && score != 0) {
@ -315,3 +300,13 @@ void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode, uint64_t sub
} }
clHCA_SetKey(data->handle, (unsigned long long)keycode); clHCA_SetKey(data->handle, (unsigned long long)keycode);
} }
const codec_info_t hca_decoder = {
.sample_type = SFMT_FLT,
.decode_frame = decode_frame_hca,
.free = free_hca,
.reset = reset_hca,
.seek = seek_hca,
// frame_samples: 1024 + discard
// frame_size: variable
};

File diff suppressed because it is too large Load diff

View file

@ -1,19 +1,31 @@
#include "coding.h" #include "coding.h"
#include "../base/decode_state.h" #include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libs/ka1a_dec.h" #include "libs/ka1a_dec.h"
/* opaque struct */ /* opaque struct */
struct ka1a_codec_data { typedef struct {
uint8_t* buf; uint8_t* buf;
float* fbuf; float* fbuf;
int frame_size; int frame_size;
void* handle; void* handle;
}; } ka1a_codec_data;
ka1a_codec_data* init_ka1a(int bitrate_mode, int channels_tracks) { static void free_ka1a(void* priv_data) {
ka1a_codec_data* data = priv_data;
if (!data) return;
if (data->handle)
ka1a_free(data->handle);
free(data->buf);
free(data->fbuf);
free(data);
}
void* init_ka1a(int bitrate_mode, int channels_tracks) {
ka1a_codec_data* data = NULL; ka1a_codec_data* data = NULL;
int buf_size; int buf_size;
@ -39,7 +51,7 @@ fail:
return NULL; return NULL;
} }
static bool read_ka1a_frame(VGMSTREAM* v) { static bool read_frame(VGMSTREAM* v) {
ka1a_codec_data* data = v->codec_data; ka1a_codec_data* data = v->codec_data;
int bytes; int bytes;
@ -72,8 +84,8 @@ static bool read_ka1a_frame(VGMSTREAM* v) {
return true; return true;
} }
bool decode_ka1a_frame(VGMSTREAM* v) { static bool decode_frame_ka1a(VGMSTREAM* v) {
bool ok = read_ka1a_frame(v); bool ok = read_frame(v);
if (!ok) if (!ok)
return false; return false;
@ -90,13 +102,14 @@ bool decode_ka1a_frame(VGMSTREAM* v) {
return true; return true;
} }
void reset_ka1a(ka1a_codec_data* data) { static void reset_ka1a(void* priv_data) {
ka1a_codec_data* data = priv_data;
if (!data || !data->handle) return; if (!data || !data->handle) return;
ka1a_reset(data->handle); ka1a_reset(data->handle);
} }
void seek_ka1a(VGMSTREAM* v, int32_t num_sample) { static void seek_ka1a(VGMSTREAM* v, int32_t num_sample) {
ka1a_codec_data* data = v->codec_data; ka1a_codec_data* data = v->codec_data;
decode_state_t* ds = v->decode_state; decode_state_t* ds = v->decode_state;
if (!data) return; if (!data) return;
@ -135,12 +148,10 @@ void seek_ka1a(VGMSTREAM* v, int32_t num_sample) {
#endif #endif
} }
void free_ka1a(ka1a_codec_data* data) { const codec_info_t ka1a_decoder = {
if (!data) return; .sample_type = SFMT_FLT,
.decode_frame = decode_frame_ka1a,
if (data->handle) .free = free_ka1a,
ka1a_free(data->handle); .reset = reset_ka1a,
free(data->buf); .seek = seek_ka1a,
free(data->fbuf); };
free(data);
}

View file

@ -329,19 +329,16 @@ int clHCA_getInfo(clHCA* hca, clHCA_stInfo *info) {
} }
//HCADecoder_DecodeBlockInt32 //HCADecoder_DecodeBlockInt32
void clHCA_ReadSamples16(clHCA* hca, signed short *samples) { void clHCA_ReadSamples16(clHCA* hca, short* samples) {
const float scale_f = 32768.0f; const float scale_f = 32768.0f;
float f;
signed int s;
unsigned int i, j, k;
/* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */ /* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */
for (i = 0; i < HCA_SUBFRAMES; i++) { for (int i = 0; i < HCA_SUBFRAMES; i++) {
for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) { for (int j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) {
for (k = 0; k < hca->channels; k++) { for (int k = 0; k < hca->channels; k++) {
f = hca->channel[k].wave[i][j]; float f = hca->channel[k].wave[i][j];
//f = f * hca->rva_volume; /* rare, won't apply for now */ //f = f * hca->rva_volume; /* rare, won't apply for now */
s = (signed int)(f * scale_f); int s = (signed int)(f * scale_f);
if (s > 32767) if (s > 32767)
s = 32767; s = 32767;
else if (s < -32768) else if (s < -32768)
@ -352,6 +349,20 @@ void clHCA_ReadSamples16(clHCA* hca, signed short *samples) {
} }
} }
void clHCA_ReadSamples(clHCA* hca, float* samples) {
/* interleave output */
for (int i = 0; i < HCA_SUBFRAMES; i++) {
for (int j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) {
for (int k = 0; k < hca->channels; k++) {
float f = hca->channel[k].wave[i][j];
//f = f * hca->rva_volume; /* rare, won't apply for now */
*samples++ = f;
}
}
}
}
//-------------------------------------------------- //--------------------------------------------------
// Allocation and creation // Allocation and creation

View file

@ -64,7 +64,9 @@ int clHCA_DecodeBlock(clHCA* hca, void* data, unsigned int size);
/* Extracts signed and clipped 16 bit samples into sample buffer. /* Extracts signed and clipped 16 bit samples into sample buffer.
* May be called after clHCA_DecodeBlock, and will return the same data until * May be called after clHCA_DecodeBlock, and will return the same data until
* next decode. Buffer must be at least (samplesPerBlock*channels) long. */ * next decode. Buffer must be at least (samplesPerBlock*channels) long. */
void clHCA_ReadSamples16(clHCA* hca, short* outSamples); void clHCA_ReadSamples16(clHCA* hca, short* samples);
void clHCA_ReadSamples(clHCA* hca, float* samples);
/* Sets a 64 bit encryption key, to properly decode blocks. This may be called /* Sets a 64 bit encryption key, to properly decode blocks. This may be called
* multiple times to change the key, before or after clHCA_DecodeHeader. * multiple times to change the key, before or after clHCA_DecodeHeader.

Some files were not shown because too many files have changed in this diff Show more