Compare commits

..

692 commits
main ... emusc

Author SHA1 Message Date
Christopher Snowhill
e233a95c2a EmuSC experiment goes here
This is mostly working, but kind of buggy.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-15 00:21:43 -07:00
Christopher Snowhill
e7779278bd 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:50:35 -07:00
Christopher Snowhill
ff66c8e1a9 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:47:35 -07:00
Christopher Snowhill
fb97fbd202 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:44:40 -07:00
Christopher Snowhill
9aaf6d1c2d 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:43:36 -07:00
Christopher Snowhill
fdd0244067 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:29:49 -07:00
Christopher Snowhill
15eaa877b1 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:08:49 -07:00
Christopher Snowhill
9b973a4b53 Bug Fix: Don't display notification on seek
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-10 23:03:47 -07:00
Christopher Snowhill
691d07ad5a 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:41 -07:00
Christopher Snowhill
2b52d2a766 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:39:38 -07:00
Christopher Snowhill
845b33e422 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:24 -07:00
Christopher Snowhill
cdc35c7cae 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:19 -07:00
Christopher Snowhill
b34e1b5c6d 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:45:11 -07:00
Christopher Snowhill
60ef6c873c 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:09 -07:00
Christopher Snowhill
a192ccf875 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:10 -07:00
Christopher Snowhill
13f67cf0f4 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:40 -07:00
Christopher Snowhill
bd0358b3c6 Sync with main branch
This empty commit pairs up with a XIB fix to the main branch.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 22:31:32 -07:00
Christopher Snowhill
2ddbdb953d 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:41 -07:00
Christopher Snowhill
dbee64e755 Crash Fix: Fix a serious bug in previous commit
Fixes commit 3874d65ec2, where code was
treating a dictionary like an array.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 18:30:06 -07:00
Christopher Snowhill
a3268e6a95 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:22:48 -07:00
Christopher Snowhill
3874d65ec2 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:03 -07:00
Christopher Snowhill
14a8a35dac Bug Fix: Handle possible null exceptions
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 15:03:31 -07:00
Christopher Snowhill
4c073efbfd Core Audio: Fix pausing glitches
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-09 14:43:06 -07:00
Christopher Snowhill
05f2434462 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:33 -07:00
Christopher Snowhill
6036000214 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:21:39 -08:00
Christopher Snowhill
e9df18c067 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:25 -08:00
Christopher Snowhill
3f4e35ed17 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:38 -08:00
Christopher Snowhill
3dbde22f61 Core Audio: Slight change to audio fade in on seek
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-08 00:38:00 -08:00
Christopher Snowhill
653d143c03 Core Audio: Increase fade duration to 125ms
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 23:45:02 -08:00
Christopher Snowhill
b08de34bf0 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:55 -08:00
Christopher Snowhill
5e48382774 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:16 -08:00
Christopher Snowhill
fab4d3705e 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:29 -08:00
Christopher Snowhill
fd57ed12bb 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:49:47 -08:00
Christopher Snowhill
c89e7396cd 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:27:54 -08:00
Christopher Snowhill
216cc52719 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:26:58 -08:00
Christopher Snowhill
267948350a 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:38:59 -08:00
Christopher Snowhill
780d9ae759 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:37:04 -08:00
Christopher Snowhill
694de375ea 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:39:44 -08:00
Christopher Snowhill
d19c2f1e95 Cleanup: Remove unused code
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-07 04:38:04 -08:00
Christopher Snowhill
d25d84c2be 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:37:32 -08:00
Christopher Snowhill
9a4695a80d Translation: Missing string recently added
Translation provided.

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

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:58:04 -08:00
Christopher Snowhill
fccaf31e16 Sentry: Temporarily disable app hang detection
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-06 14:51:49 -08:00
Christopher Snowhill
5eaa4c7774 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:11 -08:00
Christopher Snowhill
6eaa4b28c2 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:42:10 -08:00
Christopher Snowhill
6c3af4b2a4 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:27 -08:00
Christopher Snowhill
143010f91c 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:17:48 -08:00
Christopher Snowhill
40c4f1b780 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:16:59 -08:00
Christopher Snowhill
c48b74d52d 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 03:17:19 -08:00
Christopher Snowhill
0b1482b3c6 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:05:33 -08:00
Christopher Snowhill
fcb2639d01 Sentry: Bump to version 8.46.0
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-05 15:26:03 -08:00
Christopher Snowhill
00e18da683 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 15:24:54 -08:00
Christopher Snowhill
3d574ba187 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 15:24:10 -08:00
Christopher Snowhill
93bec8ca63 Bug Fix: Handle compile time warning
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-04 00:45:53 -08:00
Christopher Snowhill
a7e65d3a85 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:45:31 -08:00
Christopher Snowhill
019bdd7a36 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:44:59 -08:00
Christopher Snowhill
bce00aff2e 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:15:47 -08:00
Christopher Snowhill
915e212ae5 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:12:44 -08:00
Christopher Snowhill
001f3e53ea 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-03 22:24:17 -08:00
Christopher Snowhill
b631cf803e 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-03 22:22:02 -08:00
Christopher Snowhill
b4b0deebd2 Bug Fix: Fix .gitignore file
Oops, the "build" folder reference was incorrect.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 22:21:19 -08:00
Christopher Snowhill
81dac451b2 CI: Bump OS and Xcode versions
Bump to macOS 15 and Xcode 16.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-03-03 19:01:22 -08:00
Christopher Snowhill
7719ccf864 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:34:40 -08:00
Christopher Snowhill
8d848bc745 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:32:01 -08:00
Christopher Snowhill
cee604c63c 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:28:42 -08:00
Christopher Snowhill
68076ec855 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:10 -08:00
Christopher Snowhill
62010394ef 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:17:30 -08:00
Christopher Snowhill
b8580cf193 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:10 -08:00
Christopher Snowhill
07a36873e3 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:33:50 -08:00
Christopher Snowhill
ba52c69a5a 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:07 -08:00
Christopher Snowhill
6f269dd689 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:10 -08:00
Christopher Snowhill
970f472436 Cleanup: Remove unused code
This is no longer needed.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 14:31:44 -08:00
Christopher Snowhill
d253a59ee6 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:07 -08:00
Christopher Snowhill
631e8a2c23 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:58:18 -08:00
Christopher Snowhill
c1b888a21c 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:56:58 -08:00
Christopher Snowhill
55d738cbe8 Cleanup: Remove unused variable
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:55:43 -08:00
Christopher Snowhill
e1a3e3d2dc 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:55:18 -08:00
Christopher Snowhill
7b702d23a6 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:53:50 -08:00
Christopher Snowhill
4b13ca5be1 Bug Fix: Clear counter correctly on reset
This reset was missing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-27 00:51:47 -08:00
Christopher Snowhill
814f65f830 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:27 -08:00
Christopher Snowhill
d6cd240de6 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:28:51 -08:00
Christopher Snowhill
440d2254be 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:08 -08:00
Christopher Snowhill
6ddacf6e9d Bug Fix: Fix CI building again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-26 01:23:38 -08:00
Christopher Snowhill
fd774d17a5 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:11:55 -08:00
Christopher Snowhill
0c8f072deb 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-25 23:56:31 -08:00
Christopher Snowhill
4ed4ea906b 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:17 -08:00
Christopher Snowhill
53291b570d 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:57 -08:00
Christopher Snowhill
e76defbfd4 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:58:56 -08:00
Christopher Snowhill
a7f1fd9d6c 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:57:03 -08:00
Christopher Snowhill
9e24d60805 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:44 -08:00
Christopher Snowhill
a744963548 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:40 -08:00
Christopher Snowhill
ced4d73fd6 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:43 -08:00
Christopher Snowhill
03cce1b004 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:25:44 -08:00
Christopher Snowhill
e97b96b3e9 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:02:44 -08:00
Christopher Snowhill
c85c149ceb 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 14:54:51 -08:00
Christopher Snowhill
959fdf69a3 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 14:53:47 -08:00
Christopher Snowhill
293a159116 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:00 -08:00
Christopher Snowhill
0fd4c327e4 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:13 -08:00
Christopher Snowhill
b571c3f62a 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:31:39 -08:00
Christopher Snowhill
80e909fbbe 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:08 -08:00
Christopher Snowhill
c74423c32d 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:15 -08:00
Christopher Snowhill
73161fdc12 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:23:29 -08:00
Christopher Snowhill
03bf4b36fe 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:21:32 -08:00
Christopher Snowhill
c4ed14aa53 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:19:43 -08:00
Christopher Snowhill
43b6a504b8 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:15 -08:00
Christopher Snowhill
0262df7c53 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:03:27 -08:00
Christopher Snowhill
7bb6070350 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:57 -08:00
Christopher Snowhill
3f424cf5b0 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:29 -08:00
Christopher Snowhill
df74a068dc 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:09:59 -08:00
Christopher Snowhill
cd83dfa87c 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:03 -08:00
Christopher Snowhill
40b2214c88 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:56:05 -08:00
Christopher Snowhill
6fee16eb82 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:02 -08:00
Christopher Snowhill
b480453886 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:27 -08:00
Christopher Snowhill
be0ccaffa2 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:06 -08:00
Christopher Snowhill
660d2b25be 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:14 -08:00
Christopher Snowhill
0dee6e05ab 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:31 -08:00
Christopher Snowhill
de6063780b 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 18:59:00 -08:00
Christopher Snowhill
82438fca04 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:50:50 -08:00
Christopher Snowhill
7088aae2e9 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:46:47 -08:00
Christopher Snowhill
d2970e593d 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:45:18 -08:00
Christopher Snowhill
5424e18f27 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:36 -08:00
Christopher Snowhill
94fcb68563 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:07 -08:00
Christopher Snowhill
2ba8ec04a2 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:20 -08:00
Christopher Snowhill
b0e6ec98a9 Audio: Improve buffer signaling
This should stop the deadlocks which were occurring.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 19:55:39 -08:00
Christopher Snowhill
4cd5cb8fa7 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:18 -08:00
Christopher Snowhill
d3778f92fc 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:46 -08:00
Christopher Snowhill
f3132e0061 Equalizer: Fix to function properly
This was completely broken, oops.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 13:39:16 -08:00
Christopher Snowhill
8647f76a46 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:26 -08:00
Christopher Snowhill
c3af7c3bdc Audio: General fixes and improvements
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-13 06:34:21 -08:00
Christopher Snowhill
c48a52cda3 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:26:59 -08:00
Christopher Snowhill
96a79d3ff2 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:25:43 -08:00
Christopher Snowhill
061eefee29 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:44 -08:00
Christopher Snowhill
fb5c23461d 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:08 -08:00
Christopher Snowhill
9dcc434992 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:12:53 -08:00
Christopher Snowhill
08539a81e4 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:06:07 -08:00
Christopher Snowhill
b53ebc08fe 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:05:15 -08:00
Christopher Snowhill
3eec6d7700 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:01:52 -08:00
Christopher Snowhill
139ff3a2b8 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:25 -08:00
Christopher Snowhill
de29e33714 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:59 -08:00
Christopher Snowhill
be177617d3 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:59:55 -08:00
Christopher Snowhill
afd2ca2e2a 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:11:05 -08:00
Christopher Snowhill
5e4dd125dd 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:04:45 -08:00
Christopher Snowhill
7101527585 Cleanup: Whitespace removal
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-12 19:01:10 -08:00
Christopher Snowhill
9589a08724 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:00:52 -08:00
Christopher Snowhill
eaaabafdd2 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 18:54:38 -08:00
Christopher Snowhill
6f6c08dc5b 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:32 -08:00
Christopher Snowhill
ee7aae922d 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:17 -08:00
Christopher Snowhill
b858a48032 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:35 -08:00
Christopher Snowhill
92dbe351af 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:04:31 -08:00
Christopher Snowhill
ce2bedf478 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:04:26 -08:00
Christopher Snowhill
8769869aee DSP: Move HRTF filter to DSP class chain
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 23:04:22 -08:00
Christopher Snowhill
d8802bc0da 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 23:04:18 -08:00
Christopher Snowhill
c19de448dc 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 23:04:13 -08:00
Christopher Snowhill
d6b7ed467e 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:06 -08:00
Christopher Snowhill
8f1ef5eb6b 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:11:26 -08:00
Christopher Snowhill
108186450f 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:09:04 -08:00
Christopher Snowhill
08b991a7c3 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 17:50:07 -08:00
Christopher Snowhill
ec7d936289 DSP: Stylistic change
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 17:45:05 -08:00
Christopher Snowhill
7e5c9c8f7c DSP: Whitespace changes
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 17:44:07 -08:00
Christopher Snowhill
8a468c08ce 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:30 -08:00
Christopher Snowhill
9ef21d8185 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:06:59 -08:00
Christopher Snowhill
2b3d622685 Preferences: Fix merge error
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-11 13:57:45 -08:00
56cf509670 Changed term in Spanish translation
Yes, it's mostly used untranslated now...
2025-02-11 13:48:10 -08:00
Christopher Snowhill
1efdea953c 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:59 -08:00
Christopher Snowhill
9df263e87b 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:28:54 -08:00
Christopher Snowhill
4fefdc7ea3 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:23:06 -08:00
24f9ca9214 Spanish translation for Rubber Band settings, and
for rubber band icon credits.
2025-02-11 00:55:48 -08:00
Christopher Snowhill
76d42f2c08 Fix up team identifiers again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:48:17 -08:00
Christopher Snowhill
0498eb5f81 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:42:41 -08:00
Christopher Snowhill
37f1be354f Core Audio: Fix API header
Fix a function declaration that was missing its
parameter variable in the header.
2025-02-10 14:40:20 -08:00
Christopher Snowhill
47b4d19f8f Revert "Visualization: Tweak systems a bit"
This reverts commit 88f370ed91.
2025-02-10 14:40:08 -08:00
Christopher Snowhill
ce91cc5d6c Updated VGMStream to r1980-95-g551c0787
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-10 14:38:48 -08:00
Christopher Snowhill
b1deca3fcf 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:38:19 -08:00
Christopher Snowhill
e9883f38af Updated Sparkle to version 2.6.4
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-02-06 20:58:52 -08:00
Christopher Snowhill
88f370ed91 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:25 -08:00
Christopher Snowhill
d8c9236b2f 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 14:50:01 -08:00
Christopher Snowhill
8786f2b21e 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:16 -08:00
Christopher Snowhill
f3d3a5ca4e 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:24 -08:00
Christopher Snowhill
034b0d20b8 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:16:58 -08:00
Christopher Snowhill
8eddb7bf40 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:02:32 -08:00
Christopher Snowhill
fd3e9e2b24 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 00:52:26 -08:00
Christopher Snowhill
8f91a49bca 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 00:38:21 -08:00
Christopher Snowhill
e9a17a8ba5 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:12 -08:00
Christopher Snowhill
61165a022a 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:41:00 -08:00
Christopher Snowhill
6bbec09b8d Updated VGMStream to r1980-54-g35c8283f
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-26 01:16:52 -08:00
Christopher Snowhill
0a298e1d71 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 00:52:58 -08:00
ad4672b150 Update bug_report.md 2025-01-20 14:20:10 -08:00
Christopher Snowhill
d2bb1458cc 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:47:49 -08:00
Christopher Snowhill
f33e0138e4 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:37:17 -08:00
Christopher Snowhill
8102731228 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:22:26 -08:00
Christopher Snowhill
9ed4e4e8d9 Updated libOpenMPT to version 0.7.13
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-10 14:39:38 -08:00
Christopher Snowhill
4b19c15354 Dependencies: Updated mpg123 to 1.32.10
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-10 14:39:29 -08:00
Christopher Snowhill
ae71a6a2bc Audio/HRTF: Make head tracking optional, add reset button
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 15:23:02 -08:00
Christopher Snowhill
160c7e43b7 Updated VGMStream to r1980-0-ged9a7202
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-03 02:30:56 -08:00
Christopher Snowhill
cc79342c5b 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:20:56 -08:00
Christopher Snowhill
91b31255e6 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 02:20:49 -08:00
Christopher Snowhill
925a3502fa 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 02:20:42 -08:00
Christopher Snowhill
0b39b57f61 Chore: Update copyright dates somewhat
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2025-01-01 01:30:48 -08:00
Christopher Snowhill
408b8047bf 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 17:58:58 -08:00
Christopher Snowhill
e5eeb987fa 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 00:44:43 -08:00
Christopher Snowhill
fb0aae1dea Updated VGMStream to r1951-102-gf1483e22
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-04 23:09:43 -08:00
Christopher Snowhill
75a4f68feb Updated libOpenMPT to version 0.7.12
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-12-04 23:04:11 -08:00
Christopher Snowhill
18fe6c0563 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:29:43 -08:00
Christopher Snowhill
ab798fd86a 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:28:34 -08:00
Christopher Snowhill
78fa70a770 Updated VGMStream to r1951-100-g73ef7c6c
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-11-24 15:08:17 -08:00
Christopher Snowhill
95c9f91120 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:07:04 -08:00
Christopher Snowhill
58cbda594a Updated libOpenMPT to version 0.7.11
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-10-30 23:57:33 -07:00
Christopher Snowhill
6b8f5df721 Updated VGMStream to r1951-88-g02d3c3f8
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-10-30 23:56:21 -07:00
Christopher Snowhill
2ac35a0b87 Reorder project file entries with a sort 2024-09-20 22:24:37 -07:00
Christopher Snowhill
bd457f91e9 Remove unused variable 2024-09-20 22:24:14 -07:00
Christopher Snowhill
27c5e50633 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:23:59 -07:00
Christopher Snowhill
42a7232fda 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:19:36 -07:00
Christopher Snowhill
26bdd7fcc0 Update Github workflow
Update checkout and upload artifacts to latest versions
2024-09-17 02:41:49 -07:00
Christopher Snowhill
6c339bb9f9 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:24:13 -07:00
Christopher Snowhill
9fa9274ce5 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:23:36 -07:00
Christopher Snowhill
5c8441eb22 Updated VGMStream to r1951-50-g1d836a36 2024-09-17 02:19:58 -07:00
Christopher Snowhill
8498bba881 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:06:59 -07:00
Christopher Snowhill
ee98898dbc 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:11:54 -07:00
Christopher Snowhill
50b3649ce6 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:35:24 -07:00
Christopher Snowhill
952ca1b42d 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:30:08 -07:00
Christopher Snowhill
62ab217c28 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:06:58 -07:00
Christopher Snowhill
e03d857f86 Updated libOpenMPT to version 0.7.9
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:33:43 -07:00
Christopher Snowhill
681cd18145 Update PluginController.mm
Add missing definitions to the Info.plist template generator.

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

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2024-08-17 03:21:48 -07:00
Christopher Snowhill
626a9e2205 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:20:11 -07:00
Christopher Snowhill
7b2fcf7c94 Updated Sparkle to version 2.6.0 2024-08-07 23:16:47 -07:00
Christopher Snowhill
b2db8d3bac Update FFmpeg to version 7.0 and rebuilt soxr
Rebuilt libsoxr, which now removes the dependency on libavutil.
2024-08-07 23:15:51 -07:00
Christopher Snowhill
0f5f11a5a4
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:17 -07:00
Christopher Snowhill
587b7900b5
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:14 -07:00
Christopher Snowhill
f52de150a7
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:20 -07:00
Christopher Snowhill
dc1f78fe4f
CI: Hopefully fix this time
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-04 02:10:53 -07:00
Christopher Snowhill
7634283717
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:23 -07:00
Christopher Snowhill
ba8a10f494
Update libFLAC to version 1.4.3
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 22:58:49 -07:00
Christopher Snowhill
7ee6359056
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:01:30 -07:00
Christopher Snowhill
17c598878c
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:41 -07:00
Christopher Snowhill
c4df174ee6
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:28:44 -07:00
Christopher Snowhill
38dfd2f4a2
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:35 -07:00
Christopher Snowhill
a37026dec2
Reduce audio buffering slightly again
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 19:46:11 -07:00
Christopher Snowhill
8e90da6292 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:34:42 -07:00
Christopher Snowhill
416e77d220 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:32:40 -07:00
Christopher Snowhill
0d682fef37 Initial implementation of positional audio for macOS Sonoma
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 04:59:54 -07:00
Christopher Snowhill
fbde40212c Replace hard coded Pi constant with M_PI
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 04:55:44 -07:00
Christopher Snowhill
a1bd2e0d44 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 04:55:09 -07:00
Christopher Snowhill
34edc003db Fix a typo
Notifcation -> Notification

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-03 04:53:26 -07:00
Christopher Snowhill
d221300fb6 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 04:48:57 -07:00
Christopher Snowhill
791c9f9b17
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-02 19:15:58 -07:00
Christopher Snowhill
73738aa185 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-02 19:14:36 -07:00
Christopher Snowhill
e9e4fd3aa4
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-02 19:13:27 -07:00
Christopher Snowhill
4d0123e13d
Simplify HRTF filter, change option to reflect it
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:58:13 -07:00
Christopher Snowhill
2eabd72491
Attempt to stabilize visualization flutter
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:58:09 -07:00
Christopher Snowhill
f4f46942ec
Fix converter after output switchover
This was missing, too.

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

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-10-02 10:58:00 -07:00
Christopher Snowhill
d364a7ef10
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:57:55 -07:00
Christopher Snowhill
661f047e1b
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:57:50 -07:00
Christopher Snowhill
7ac32284ff
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:57:47 -07:00
Christopher Snowhill
ca1f9381b5
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:57:42 -07:00
Christopher Snowhill
cda6ce608c
Privacy - Added Spanish translation
Translation provided by team member, Kevin López Brante.
2023-09-03 16:52:40 -07:00
Christopher Snowhill
d3a31da1a6
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:38:53 -07:00
Christopher Snowhill
8d2425b06a
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:15 -07:00
Christopher Snowhill
d069aa0361
Updated Translations
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:40:53 -07:00
Christopher Snowhill
f345f1ddf0
Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:40:36 -07:00
Christopher Snowhill
08c904c201
Updated VGMStream to r1866-46-g883d796d
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:18:22 -07:00
Christopher Snowhill
c802dbfa45
MainMenu: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:17:59 -07:00
Christopher Snowhill
b64e00449e
AppleScript: Implemented Composer field
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:17:31 -07:00
Christopher Snowhill
4c9895d208
Info Inspector: Implemented Composer field
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:00:25 -07:00
Christopher Snowhill
750fae6712
Playlist: Implemented Composer column
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:00:23 -07:00
Christopher Snowhill
4ac4881e4c
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:00:20 -07:00
Christopher Snowhill
4bb59c1c3e
TagLib: Remove stray whitespace
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:00:18 -07:00
Christopher Snowhill
248718c967
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:00:15 -07:00
Christopher Snowhill
b9121be6e8
ASF/WMA: Implement Album Artist tags (oops)
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-09-02 21:00:13 -07:00
Christopher Snowhill
7ea87f0502 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:47:37 -07:00
Christopher Snowhill
16c1cf4b65 Fix rating sorting
This apparently fixes sorting by rating.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:46:00 -07:00
Christopher Snowhill
f62a7c5fed 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:36:42 -07:00
Christopher Snowhill
59123f98d5 Touched by Xcode
Miscellaneous XIB changes automatically applied by Xcode
when editing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-08-20 23:35:09 -07:00
Christopher Snowhill
158fbed081 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:16:47 -07:00
Christopher Snowhill
0b75967d64
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:15:38 -07:00
Christopher Snowhill
545a1e9632
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:31:26 -07:00
Christopher Snowhill
0c934886f4
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 00:56:16 -07:00
Christopher Snowhill
20531b2d04
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 00:55:14 -07:00
Christopher Snowhill
d606790ae3
Touched by Xcode
Resource scripts touched by Xcode again

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-29 00:49:01 -07:00
Christopher Snowhill
056bb2bd26
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 00:23:58 -07:00
Christopher Snowhill
38ad84e86c
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:01 -07:00
Christopher Snowhill
c2fa2dcaf6
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:40:09 -07:00
Christopher Snowhill
5239c1a5c8
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:38:53 -07:00
Christopher Snowhill
a81f3f05d9
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:24:59 -07:00
Christopher Snowhill
cc0f55e3c4
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:19:52 -07:00
Christopher Snowhill
6cda7696f3
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:20 -07:00
Christopher Snowhill
7f0fe33f9d
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:14:31 -07:00
Christopher Snowhill
503ca803e9 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:12:43 -07:00
Christopher Snowhill
bb21da6ed1 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:11:12 -07:00
Christopher Snowhill
2ac9741ef2 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:10:13 -07:00
Christopher Snowhill
c9a5bf26f9
Add status messages to updater script
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-07-21 00:06:40 -07:00
Christopher Snowhill
28b36dda66
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:30 -07:00
Christopher Snowhill
3d00e8dd2d
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:12 -07:00
Christopher Snowhill
a6ecd7eed9
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:15:14 -07:00
Christopher Snowhill
c9ed3c4817
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:35 -07:00
Christopher Snowhill
24a3209682
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:14:30 -07:00
Christopher Snowhill
555b9f248d
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:14:26 -07:00
Christopher Snowhill
b2657700eb Updated libOpenMPT to version 0.7.2
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:28:42 -07:00
Christopher Snowhill
69251314c4 Updated VGMStream to r1843-92-g740a4048
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 23:17:55 -07:00
Christopher Snowhill
d103d78fc3 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 22:14:43 -07:00
Christopher Snowhill
26fa9496de 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 22:13:44 -07:00
Christopher Snowhill
696f060ffc Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-30 22:10:31 -07:00
Christopher Snowhill
a4f7fa68db 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 22:09:33 -07:00
Christopher Snowhill
ad9af1640c
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:43:40 -07:00
Christopher Snowhill
a1caefa014
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:27:56 -07:00
Christopher Snowhill
42b91c6f9d Update MASShortcut for Xcode 15
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:40:24 -07:00
Christopher Snowhill
da2c2eb2a8 Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:30:25 -07:00
Christopher Snowhill
2dbe524f20
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:13:21 -07:00
Christopher Snowhill
eb26ab8be4
Update projects and source in prep for Xcode 15
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-06-08 04:12:29 -07:00
Christopher Snowhill
8443464b54
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 03:44:24 -07:00
Shoh Sewell
f64ec07b43
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 03:40:04 -07:00
Christopher Snowhill
1cd3a3230c
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:31 -07:00
Christopher Snowhill
6c0db041e3
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:16 -07:00
Christopher Snowhill
22cdf0aa4f
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:29:37 -07:00
Christopher Snowhill
10f22d137c
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:52 -07:00
Christopher Snowhill
877660b4cc
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:14:48 -07:00
C.W. Betts
fa373b9ff1
Move most image assets to Xcode assets.
AboutCog.jp2 isn't moved because Xcode/Xcode assets doesn't recognize jp2 files as images.

# Conflicts:
#	Cog.xcodeproj/project.pbxproj
2023-06-01 00:13:50 -07:00
Shoh Sewell
55cb51c330
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-06-01 00:11:16 -07:00
Christopher Snowhill
db338f929a
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:35:06 -07:00
Christopher Snowhill
13254f25b1
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:33:50 -07:00
Christopher Snowhill
1115e9651d
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:33:15 -07:00
Christopher Snowhill
836147b94b
Updated VGMStream to r1843-0-gb158e812
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-31 22:31:44 -07:00
Christopher Snowhill
b47d2154b4
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 21:26:11 -07:00
Christopher Snowhill
9e54885b97
Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:23:39 -07:00
Christopher Snowhill
11ffdcb1c1
Updated Turkish translation to near completion
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:23:35 -07:00
Christopher Snowhill
4d106c40ce
Updated VGMStream to r1831-27-ge6883cbd
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:23:31 -07:00
Christopher Snowhill
9d3089462e
Updated libOpenMPT to version 0.7
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-05-04 18:23:25 -07:00
Christopher Snowhill
3e2286683a
Translation: Added Turkish language support
This translation is mostly complete.

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

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 22:42:39 -08:00
Christopher Snowhill
549f426e65
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:33 -08:00
Christopher Snowhill
c68b4b9585
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:19:05 -08:00
Christopher Snowhill
0b82160512
Lyrics Window: Touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 18:19:01 -08:00
Christopher Snowhill
ed38e4f8d0
Info Window: Reorder header imports
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:46:22 -08:00
Christopher Snowhill
799299df3a
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:45:59 -08:00
Christopher Snowhill
57a0ea6e87
Tags: Implement unsynced lyrics in Vorbis plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:45:05 -08:00
Christopher Snowhill
de974548de
Tags: Implement unsynced lyrics in Opus plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:44:45 -08:00
Christopher Snowhill
b0c718003b
Tags: Implement unsynced lyrics in Flac plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:44:19 -08:00
Christopher Snowhill
593d7d155a
Tags: Implement unsynced lyrics in FFmpeg plugin
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-23 17:44:01 -08:00
Christopher Snowhill
190d6959fd
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:41:50 -08:00
Christopher Snowhill
790eb5508b
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:41:12 -08:00
Christopher Snowhill
c34d869b99
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:39:52 -08:00
Christopher Snowhill
5743652879
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:56 -08:00
Christopher Snowhill
4131d4eae4
Updated VGMStream to r1810-97-g408cada5
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 23:46:16 -08:00
Christopher Snowhill
9bfbeaadb5
Updated libOpenMPT to version 0.6.8
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 22:13:01 -08:00
Christopher Snowhill
26242673d1
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 22:12:31 -08:00
Christopher Snowhill
57c2adf946
Replaced r8brain with libsoxr
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2023-02-04 22:10:22 -08:00
Christopher Snowhill
01c38c9440
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:35:01 -08:00
Christopher Snowhill
72ee38ad14
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:18:56 -08:00
Christopher Snowhill
6daa0de425
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:18:47 -08:00
Christopher Snowhill
1b9f460538
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:18:07 -08:00
Christopher Snowhill
1de501a64a
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:18:03 -08:00
Christopher Snowhill
4b37ffebee
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:16:50 -08:00
Christopher Snowhill
5e0a3308c0
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:09:52 -08:00
Christopher Snowhill
85283b99a1
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:54 -08:00
Christopher Snowhill
63bd6b29d7
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:13:48 -08:00
Christopher Snowhill
fadeec1827
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:10:39 -08:00
Christopher Snowhill
9c67eb78fc
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:49:27 -08:00
Christopher Snowhill
d8f0a19524
Updated VGMStream to r1800-25-g599326a3
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-11-29 00:47:14 -08:00
Christopher Snowhill
5cd5fe1c5e
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:27:44 -08:00
Christopher Snowhill
0c5c905af3
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-06 23:58:59 -08:00
Christopher Snowhill
364f13338d
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:49:50 -07:00
Christopher Snowhill
c627a9fc58
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:47:53 -07:00
Christopher Snowhill
c40b25b571
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:42:12 -07:00
Christopher Snowhill
66e1d67b32
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:12:06 -07:00
Christopher Snowhill
3663140064 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:33 -07:00
Christopher Snowhill
31ddbbec29 MIDI Plugin: Fix Secret Sauce memory leaks
Needed some autoreleasepools in there.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-28 00:16:11 -07:00
Christopher Snowhill
7fb894d721 Touched by Xcode
Xcode updated the Preferences xib.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-28 00:15:37 -07:00
Christopher Snowhill
504ddcf82b Sparkle: Update API a bit
This updates the API interface calls a bit, and borrows about 20 lines
of code from WireShark.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-28 00:02:20 -07:00
Christopher Snowhill
02a7fe84cb MIDI Plugin: Add a little Secret Sauce
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-27 23:06:02 -07:00
Christopher Snowhill
b88bee3f4a 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:01 -07:00
Christopher Snowhill
0d7fd92c82 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:02 -07:00
Christopher Snowhill
bc66220b36 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:26:06 -07:00
Christopher Snowhill
b1f97f3399 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:39 -07:00
Christopher Snowhill
e7aec3547d 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:06 -07:00
Christopher Snowhill
a9dc4b564c 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:28 -07:00
Christopher Snowhill
e25cfbf22c 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 15:00:24 -07:00
Christopher Snowhill
695a03d9e8 Updated Sparkle framework to version 2.3.0
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-16 00:18:57 -07:00
Christopher Snowhill
066ee806dc 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:22 -07:00
2f90baf4bf Merge pull request #329 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2022-10-13 17:17:02 -07:00
The Gitter Badger
2ead57412b Add Gitter badge 2022-10-13 17:16:07 -07:00
Christopher Snowhill
309bd3bfef Update associated file type extensions
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:17:43 -07:00
Christopher Snowhill
323bc8f0df Formatting fixes
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 23:00:08 -07:00
Christopher Snowhill
ce723fd44e 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 22:59:46 -07:00
Christopher Snowhill
bc7331cf8c 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 22:52:41 -07:00
Christopher Snowhill
fc372ef8b4 Updated libOpenMPT to version 0.6.6
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 22:47:02 -07:00
Christopher Snowhill
55882c0380 Updated VGMStream to r1776-97-g845961bb
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-10-11 22:45:07 -07:00
Christopher Snowhill
37065adf8a Updated VGMStream to r1776-46-gc0c2c3c7
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-09-06 21:56:51 -07:00
Christopher Snowhill
514374019b Update translations
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 16:49:56 -07:00
Christopher Snowhill
3a42f8896b Updated VGMStream to r1776-16-g7e4f5dc6
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 16:17:51 -07:00
Christopher Snowhill
fadb3e9ee2 Updated libOpenMPT to version 0.6.5 final
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-08-21 15:56:54 -07:00
Christopher Snowhill
d7418c3b33 [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:30 -07:00
Christopher Snowhill
41a04760eb [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:13 -07:00
Christopher Snowhill
aed52840ca [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:36:39 -07:00
Christopher Snowhill
7e267f06cb [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:35:50 -07:00
Christopher Snowhill
e5aa4287d1 First module converted to swift, but broken 2022-08-05 17:39:19 -07:00
Christopher Snowhill
06b4fc3ccc [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:02:49 -07:00
Christopher Snowhill
5019c6b4f6 [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:01:09 -07:00
Christopher Snowhill
93e3dd7aa6 [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:20:42 -07:00
Christopher Snowhill
f9c7e85e72 [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:19:51 -07:00
Christopher Snowhill
fbe232f791 [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:19:02 -07:00
Christopher Snowhill
ea3d38bcae [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:18:09 -07:00
Christopher Snowhill
49f88ae37f 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:38:07 -07:00
Christopher Snowhill
c2a880fa52 [AdPlug Input] Fixed seeking
Looks like I never tested this, meh.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 22:52:03 -07:00
Christopher Snowhill
700cb962a3 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:50:05 -07:00
Christopher Snowhill
9560edf53d 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:47:58 -07:00
Christopher Snowhill
f54b6c2c9a Updated libbinio
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-31 21:47:25 -07:00
Christopher Snowhill
3f7b375bfb [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:42:45 -07:00
Christopher Snowhill
af453816a0 [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:41:49 -07:00
Christopher Snowhill
daa0c3fc61 [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:41:10 -07:00
Christopher Snowhill
660cb1bab1 [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:22 -07:00
Christopher Snowhill
4ec2146549 [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:59 -07:00
Christopher Snowhill
7cb22cfeb0 [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:55 -07:00
Christopher Snowhill
86dfe8b518 [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:45 -07:00
Christopher Snowhill
b04be78f20 [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:36 -07:00
Christopher Snowhill
177f055910 [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:35:32 -07:00
Christopher Snowhill
9f84a8bff5 Spanish translation of new option.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 23:23:02 -07:00
Christopher Snowhill
37cc0b4d30 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:22:48 -07:00
Christopher Snowhill
8809c0d257 [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:15:10 -07:00
Christopher Snowhill
8bc94e9a48 [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:15:06 -07:00
Christopher Snowhill
14eb923529 [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:15:02 -07:00
Christopher Snowhill
4e24c5b829 [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:41:34 -07:00
Christopher Snowhill
360464ceec [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:41:30 -07:00
Christopher Snowhill
9d2d29d0f4 [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:41:24 -07:00
Christopher Snowhill
c612994cb2 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:41:50 -07:00
Christopher Snowhill
e330c64f43 Enable warnings to track stack overuse
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-24 18:41:45 -07:00
Christopher Snowhill
e796e23afd [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:38:54 -07:00
Christopher Snowhill
e2d228bbc0 [Sandbox Notice] Change single to double quotes
Change the quotes.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:38:29 -07:00
Christopher Snowhill
f62a897f7d [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:37:40 -07:00
Christopher Snowhill
85a18f9a3e Updated VGMStream to r1745-79-g449bb5e0
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-23 18:36:45 -07:00
Christopher Snowhill
1c04d5e664 [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:32 -07:00
Christopher Snowhill
35dd0d38b3 [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:04:24 -07:00
Christopher Snowhill
9c4d9ebb2e [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:08 -07:00
Christopher Snowhill
e14c630034 [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:40 -07:00
Christopher Snowhill
28d5849505 [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 04:03:36 -07:00
Christopher Snowhill
81d31dbe58 [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 04:03:33 -07:00
Christopher Snowhill
41e87e3830 [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:59 -07:00
Christopher Snowhill
716170dcad [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:05:55 -07:00
Christopher Snowhill
533c36a745 [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:05:50 -07:00
Christopher Snowhill
4c4f479fb6 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:20 -07:00
Christopher Snowhill
b3d10bdd4d Reconfigure default toolbar layout
Now things are a little more understandable.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-19 07:31:15 -07:00
Christopher Snowhill
2a8aba1cf2 [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:59 -07:00
Christopher Snowhill
8bd37943aa [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:07:53 -07:00
Christopher Snowhill
7bf1bd85b8 [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 06:00:09 -07:00
Christopher Snowhill
6fe7883ed2 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:05 -07:00
Christopher Snowhill
b5f6e0ec20 [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:56:46 -07:00
Christopher Snowhill
3f212f0cfb [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:56:38 -07:00
Christopher Snowhill
804e7652a8 [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:56:30 -07:00
Christopher Snowhill
051b86cbaf [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:29 -07:00
Christopher Snowhill
faa546bc49 [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:12:57 -07:00
Christopher Snowhill
a5e6988af6 [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:11:59 -07:00
Christopher Snowhill
97707e9b8f [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 05:10:58 -07:00
Christopher Snowhill
647c754311 [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 03:34:10 -07:00
Christopher Snowhill
96acc738e3 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 03:02:41 -07:00
Christopher Snowhill
838c0d08e8 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:46 -07:00
Christopher Snowhill
193af27e7e Remove obsolete helpbook document
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 16:11:20 -07:00
LennyLip
b62d2237f8 ru FreeSurround pref string 2022-07-14 16:08:43 -07:00
Christopher Snowhill
64fc906f40 Widen the Updates setting dialog
Oops, forgot to deal with this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 16:05:33 -07:00
Christopher Snowhill
af64f93e99 [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:35:48 -07:00
Christopher Snowhill
c708e30d8c [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:35:43 -07:00
Christopher Snowhill
b95cb59a61 [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 03:35:38 -07:00
Christopher Snowhill
1a0ab6723a [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 03:35:32 -07:00
Christopher Snowhill
08e76bc9f3 [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 03:35:27 -07:00
Christopher Snowhill
4044646280 [Audio Processing] Update for new API
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 03:35:21 -07:00
Christopher Snowhill
7cb0054d77 [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 03:35:15 -07:00
Christopher Snowhill
8034054d72 [FreeSurround] Further improvements
Still not working, though.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-14 03:35:09 -07:00
Christopher Snowhill
f05bf71320 [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 03:34:48 -07:00
Christopher Snowhill
ad9b0df8ed [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 03:34:41 -07:00
Christopher Snowhill
eb9d642192 [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 03:34:36 -07:00
Christopher Snowhill
34884d825a [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:46:54 -07:00
Christopher Snowhill
1713e0df7c [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:46:47 -07:00
Christopher Snowhill
273cdef6b9 [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:46:37 -07:00
Christopher Snowhill
68d323545b [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 01:46:28 -07:00
Christopher Snowhill
c4e975319a 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:45 -07:00
Christopher Snowhill
2c0c77dee2 [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:40 -07:00
Christopher Snowhill
9fdefbf88a [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:52:10 -07:00
Christopher Snowhill
32dcc5725b [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:52:01 -07:00
Christopher Snowhill
0518f99aaf [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:51:53 -07:00
Christopher Snowhill
6a5fa23807 XIB touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 03:51:47 -07:00
Christopher Snowhill
29dfe593f1 [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:14:17 -07:00
Christopher Snowhill
8c4f9a7123 [Ogg Vorbis/Opus] Fix picture metadata handling
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-13 00:14:11 -07:00
Christopher Snowhill
5238965534 [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-13 00:14:06 -07:00
Christopher Snowhill
8c0abf5fab 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-13 00:13:07 -07:00
LennyLip
40cc36d8df Russian translation: new strings (#310)
* Update MainMenu.strings

* Update Preferences.strings
2022-07-11 19:59:51 -07:00
Christopher Snowhill
9462e9fb70 [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:50:27 -07:00
Christopher Snowhill
7adaeb4dd0 [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:50:21 -07:00
Christopher Snowhill
824675ae59 [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:50:16 -07:00
Christopher Snowhill
7a3d571492 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:50:12 -07:00
Christopher Snowhill
25b90f300f [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:50:07 -07:00
a217bbec1e Update README.md
Third's the charm.
2022-07-11 19:50:02 -07:00
8cf84e5b90 Update README.md 2022-07-11 19:49:55 -07:00
fc6d454432 Update README.md 2022-07-11 19:49:50 -07:00
ff16c583a0 Added Lokalise logo and instructions. 2022-07-11 19:49:45 -07:00
Christopher Snowhill
a92b857161 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:11 -07:00
LennyLip
1ed85a1c84 Russian lang fixes 2022-07-11 15:21:41 -07:00
LennyLip
2ecf110632 Russian translation 2022-07-11 15:21:35 -07:00
Christopher Snowhill
1ad6c78d83 Fix missing Polish declarations in project
There were still missing things.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 15:03:35 -07:00
Christopher Snowhill
6134cc47fe 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:41:15 -07:00
Christopher Snowhill
33e1086842 [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:41:09 -07:00
Christopher Snowhill
e6908ac945 [Headphone Filter] Minor changes
Change a variable type, to avoid a warning.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-11 14:41:02 -07:00
Christopher Snowhill
833e298d3d [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:43 -07:00
Christopher Snowhill
ac9e404b23 [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:08 -07:00
pax
36040582ce first(really basic) open url panel translation. 2022-07-10 15:41:42 -07:00
pax
5e09d8f4ef translated equalizer strings, as well as the spotlight thing. 2022-07-10 15:41:34 -07:00
Christopher Snowhill
8d851e5bda [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:22:04 -07:00
Christopher Snowhill
c43ebba424 [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:21:57 -07:00
Christopher Snowhill
c32d14a048 [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:21:48 -07:00
Christopher Snowhill
cbc1c85a71 [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 15:21:42 -07:00
Christopher Snowhill
f150065194 [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:03 -07:00
Christopher Snowhill
1c6db85555 [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:41:07 -07:00
Christopher Snowhill
54238d29e4 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:24:39 -07:00
02f488c7bf [Spanish Translation] New strings
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-09 16:24:26 -07:00
c735c8f387 [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:24:16 -07:00
Christopher Snowhill
2251650b6e Add two missing strings from the Sparkle branch
Oops, those were missing.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-09 16:24:05 -07:00
079301025c [Spanish Translation] Updated string for new HRTF filter. 2022-07-09 16:23:54 -07:00
Christopher Snowhill
f1381b11fd 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:23:43 -07:00
pax
b0ec50e96e The Polish translation is mostly ready. 2022-07-09 16:00:12 -07:00
pax
7c3500a925 *added Polish translation, not fully ready, but there ya go. 2022-07-09 16:00:04 -07:00
Christopher Snowhill
413ec69ee4 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:36:11 -07:00
Christopher Snowhill
ef0dd921ab Remove the meta string cache
It wasn't helping anyway.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:36:01 -07:00
375eae4be2 [Spanish Translation] Added strings for new Info Inspector fields 2022-07-08 16:35:52 -07:00
Christopher Snowhill
1d3bc8045c Ditch the data compression
It just wasn't working out.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-08 16:35:44 -07:00
Christopher Snowhill
8ee4a04f3b 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:35:34 -07:00
Christopher Snowhill
44e1fc5c49 [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:25 -07:00
Christopher Snowhill
7d8c2c53a0 Updated VGMStream to r1745-58-g828c6b09
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-07 16:44:15 -07:00
Christopher Snowhill
3a16a53bd1 [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:23 -07:00
Christopher Snowhill
812da2e331 [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:37:42 -07:00
Christopher Snowhill
3d1be2ca0d [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:41:38 -07:00
Christopher Snowhill
a0621b2537 [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:02 -07:00
Christopher Snowhill
38beb9e930 [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:17 -07:00
Christopher Snowhill
0732b176fd [CI Scripts] Update to use command -v
Instead of `which`.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-06 14:44:05 -07:00
Christopher Snowhill
7e516f8cfe [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:34 -07:00
Christopher Snowhill
36d8fa5ba5 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:38:54 -07:00
Christopher Snowhill
6984ce326c 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:23:15 -07:00
Christopher Snowhill
4956569206 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:13:30 -07:00
Christopher Snowhill
0c6e69015f 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:37:52 -07:00
Christopher Snowhill
adc159eb05 [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:20 -07:00
Christopher Snowhill
8f4fb4a44c [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:28 -07:00
Christopher Snowhill
9bf5bbba80 [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:03:07 -07:00
Christopher Snowhill
aa7eb52231 [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:02:34 -07:00
Christopher Snowhill
1fb636ffd2 [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:47 -07:00
Christopher Snowhill
933b06d5dd [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:47:41 -07:00
Christopher Snowhill
fceee35896 [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:47:34 -07:00
Christopher Snowhill
3958af0670 [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:15:34 -07:00
Christopher Snowhill
de72631ea5 [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:41:18 -07:00
Christopher Snowhill
a474b469fa [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:26:04 -07:00
ebe301a9b8 [Spanish Translation] Updated strings in Appearance preferences
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-05 13:54:57 -07:00
Christopher Snowhill
aba75e2184 [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:54:45 -07:00
Christopher Snowhill
c3ca29db0d [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:54:35 -07:00
Christopher Snowhill
b0d1533b43 Interface builder touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 13:54:24 -07:00
Christopher Snowhill
c4790af7c0 Reformat spaces to tabs
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-05 13:54:17 -07:00
Dzmitry Neviadomski
81c98736fa Add preference to choose between SceneKit and DDB spectrum. 2022-07-05 13:54:10 -07:00
Christopher Snowhill
cd45941a93 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:21 -07:00
Christopher Snowhill
a1ea668a41 [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:53:55 -07:00
Christopher Snowhill
e37d1d15b1 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:58 -07:00
Christopher Snowhill
511f1a1937 [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:08:45 -07:00
Christopher Snowhill
92bce537d1 [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:50:29 -07:00
Christopher Snowhill
aeee143de2 [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:39:43 -07:00
Christopher Snowhill
9e66838b9b [Equalizer] Remove unnecessary code
This code is obsolete, remove it.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-03 22:37:52 -07:00
Christopher Snowhill
740613b95a [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:37:11 -07:00
Christopher Snowhill
969bf4f502 [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:32:58 -07:00
Christopher Snowhill
6a46389310 [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:19 -07:00
Christopher Snowhill
e41f4e8556 Update Info.plist.template
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-02 04:38:59 -07:00
Christopher Snowhill
4450f13a8e [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:38:51 -07:00
Christopher Snowhill
310a6d44f9 [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:38:43 -07:00
Christopher Snowhill
e9f580cfbc [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:38:35 -07:00
Christopher Snowhill
bf1afd1923 [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:38:29 -07:00
Christopher Snowhill
fce21785c2 [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 04:38:23 -07:00
baec7ed72d [Spanish Translation] Added new strings for synthesis settings
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-07-02 01:27:06 -07:00
Christopher Snowhill
5d7a9798fe [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:27:01 -07:00
Christopher Snowhill
33a24d4d3e [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:30:37 -07:00
Christopher Snowhill
18a8baf93b [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:29:54 -07:00
Christopher Snowhill
01ef37c565 [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:27 -07:00
Christopher Snowhill
a4f2664ca4 [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:38:49 -07:00
Christopher Snowhill
d9f111d735 [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:43:25 -07:00
b701fa712e [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:43:18 -07:00
Christopher Snowhill
1b0e765d38 [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:02:17 -07:00
Christopher Snowhill
59bf8c6cf9 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:39:37 -07:00
Christopher Snowhill
d4b440d6a5 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:01 -07:00
Christopher Snowhill
9e02492dc2 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:56 -07:00
Christopher Snowhill
da1973bcd9 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:56:52 -07:00
Christopher Snowhill
8b8fbad6d9 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:44 -07:00
Christopher Snowhill
099588b7bd Restore the File Tree, now with a chooser button
Revert "Remove the file tree, as Sandbox does not permit"

This reverts commit 35400e1320.

This also changes how the File Tree choosing works.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-30 16:59:35 -07:00
Christopher Snowhill
73c4360b1d [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:30 -07:00
Christopher Snowhill
da21cd7341 [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:28:11 -07:00
Christopher Snowhill
27478e5df2 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:27:28 -07:00
Christopher Snowhill
d739e68e8e [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:03 -07:00
Christopher Snowhill
4ce180fb2a [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:39:47 -07:00
Christopher Snowhill
3c0ccd9d46 [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:42:12 -07:00
Christopher Snowhill
b24b9744c1 [Sandbox Config] Correctly test paths for files
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 12:11:01 -07:00
Christopher Snowhill
61778b7165 [Sandbox] Remove startup folder consent prompt
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-29 12:03:18 -07:00
Christopher Snowhill
7d26150c26 [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 12:00:25 -07:00
Christopher Snowhill
35400e1320 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 12:00:12 -07:00
3d94978f82 [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 11:57:32 -07:00
fe7c424843 [About Window] Fixed appearance for systems without Dark Mode
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-29 11:57:26 -07:00
Christopher Snowhill
1a4c140708 [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:54 -07:00
Christopher Snowhill
29c070a616 [Sandbox] Automatically save folder bookmarks
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-28 23:15:08 -07:00
Christopher Snowhill
8b7418857d [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 23:14:53 -07:00
Christopher Snowhill
a35459719d [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 23:14:21 -07:00
Christopher Snowhill
802a86a3d8 [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:28:10 -07:00
Christopher Snowhill
f8d2837c4e [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:27:57 -07:00
Christopher Snowhill
112366c850 [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:27:43 -07:00
Christopher Snowhill
a1a8607a84 [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:26:55 -07:00
Christopher Snowhill
690153f561 [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:55:30 -07:00
Christopher Snowhill
b33e3ff6b3 [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-27 22:03:02 -07:00
Christopher Snowhill
bedfac4e33 [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-27 21:50:14 -07:00
Christopher Snowhill
66102a6cda [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-27 21:49:32 -07:00
Christopher Snowhill
b36ebfe740 Resource templates touched by Xcode
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-27 21:48:43 -07:00
Christopher Snowhill
2ed78a0639 [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-27 21:46:36 -07:00
Christopher Snowhill
ae019409c5 [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:19 -07:00
Christopher Snowhill
66262c2a71 [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:11 -07:00
Christopher Snowhill
f567750d56 [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 00:58:56 -07:00
Christopher Snowhill
2a99bb076f [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 00:34:19 -07:00
Christopher Snowhill
dd65665990 [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:52:38 -07:00
Christopher Snowhill
c477fbf553 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 22:59:31 -07:00
Christopher Snowhill
03b3b43cfe 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:08:42 -07:00
Christopher Snowhill
9d8e278a57 Update debug.yml
Switch to building on macos-12
2022-06-26 22:07:12 -07:00
Christopher Snowhill
ed9e352543 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:25:41 -07:00
Christopher Snowhill
16fdc1de6a 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:22:54 -07:00
Christopher Snowhill
fc37e96099 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:11:52 -07:00
Christopher Snowhill
03a2c0c16e Updated VGMStream to r1745-47-gfa55119d
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 15:11:23 -07:00
Christopher Snowhill
206a3e42e7 [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:47:34 -07:00
Christopher Snowhill
6f6b5d6986 [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:39:24 -07:00
Christopher Snowhill
038b0b8067 [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:28:43 -07:00
Christopher Snowhill
a57827f4da [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:37:41 -07:00
Christopher Snowhill
39be3ab962 [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:57:19 -07:00
Christopher Snowhill
17df6cde4f Fix compilation
Oops, that last suggester change broke compilation.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-26 03:08:20 -07:00
Christopher Snowhill
2945de085d [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 02:58:25 -07:00
Christopher Snowhill
c2ef7d0e61 [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:56:44 -07:00
Christopher Snowhill
96a7255779 [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:54:19 -07:00
Christopher Snowhill
050aaaf852 [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 01:09:55 -07:00
Christopher Snowhill
5f52a4be81 [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 01:09:01 -07:00
Christopher Snowhill
5f2335b796 [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:42:56 -07:00
Christopher Snowhill
d33475953e [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:03 -07:00
Christopher Snowhill
7a56447271 [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:11 -07:00
Christopher Snowhill
b86ec3340f [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:13:22 -07:00
Christopher Snowhill
36c82a61e7 [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:12:43 -07:00
Christopher Snowhill
ab13b66755 [InputNode] Syntax code fix
This code was misformatted.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 05:10:33 -07:00
Christopher Snowhill
86de03a1ab [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:05 -07:00
Christopher Snowhill
0f923e6072 [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:38:17 -07:00
Christopher Snowhill
cb2ce5675a [FFmpeg] Fix chapter handling and seeking
Fix chapter startup, and chapter seeking.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-25 02:36:14 -07:00
Christopher Snowhill
1f56e5ef5a [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:36 -07:00
Christopher Snowhill
2663b5007d [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:07 -07:00
Christopher Snowhill
72572c9c7f [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:05:57 -07:00
Christopher Snowhill
3de7a34eb8 [FFmpeg] Update FFmpeg library and decoder plugin
Update based on newest changes from upstream.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 23:51:12 -07:00
Christopher Snowhill
86d8f04966 [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-24 23:29:32 -07:00
Christopher Snowhill
b55955ef1c [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-24 23:29:18 -07:00
Christopher Snowhill
8e1175bbd4 [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-24 22:33:50 -07:00
Christopher Snowhill
62e2880b49 [FFmpeg] Enable TrueHD decoder and demuxer
Oops, somehow I didn't enable TrueHD support.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 22:22:29 -07:00
Christopher Snowhill
1ac3e5cd22 [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-24 22:18:25 -07:00
Christopher Snowhill
d2eb4af3d5 [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:15:20 -07:00
Christopher Snowhill
50b7390181 [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:15:15 -07:00
Christopher Snowhill
abf80c19ac 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:04:57 -07:00
Christopher Snowhill
b21a02fe1b [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:04:52 -07:00
Christopher Snowhill
dd35639174 [OpenMPT / OpenMPT Legacy] Fix include paths
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:04:48 -07:00
Christopher Snowhill
43433c244e [mpg123] Fix include paths 2022-06-24 17:04:44 -07:00
Christopher Snowhill
870a5afed7 [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:04:39 -07:00
Christopher Snowhill
b9ef5853d6 [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:04:35 -07:00
Christopher Snowhill
1a9c73d166 [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:04:30 -07:00
Christopher Snowhill
ec393d186a [Sandbox Broker] Copy results array
Hopefully this heads off a crash elsewhere.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-24 17:04:16 -07:00
Christopher Snowhill
dfb773e9cb [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:46:01 -07:00
Christopher Snowhill
438b142558 [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:45:16 -07:00
Christopher Snowhill
26a63e85b7 [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:43:50 -07:00
Christopher Snowhill
ccbeaf16dc [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 02:46:23 -07:00
Christopher Snowhill
cc5de69e9f [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:34:30 -07:00
Christopher Snowhill
80adb85b36 [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:29:50 -07:00
Christopher Snowhill
f3f3d436ba [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-23 23:35:26 -07:00
be6453e048 [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:26:16 -07:00
Christopher Snowhill
8af32e8d2e 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:22:41 -07:00
Christopher Snowhill
5b6dacd29c 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-22 22:54:32 -07:00
Christopher Snowhill
ff44bc4d34 Updated VGMStream to r1745-37-g776c4d8c
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-06-22 19:33:32 -07:00
Christopher Snowhill
62824a94bd 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:32 -07:00
Christopher Snowhill
903bc9cba5 [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:05:27 -07:00
Christopher Snowhill
f274a8ef73 Sync version number with main branch 2022-06-22 16:28:08 -07:00
0317f2a649 [About Window] Fix
Pull request #281 by @nevack.
2022-06-22 16:11:59 -07:00
b484a0be44 [About Window] Reorganized credits and added @nevack and myself in them
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-22 16:11:38 -07:00
Dzmitry Neviadomski
36a9411b14 Fix runtime warnings in Window/AboutWindowController.xib
Fix typo in File Owner class name and remove absent outlet.
2022-06-22 16:11:24 -07:00
64fefce18d Merge pull request #280 from losnoco/nevack/about-window
AboutWindow adjustments
2022-06-22 16:07:16 -07:00
Dzmitry Neviadomski
3a6e41cabd AboutWindow adjustments
Allow opening links in default browser
Close window on Esc
Add rounded corners
2022-06-22 16:06:26 -07:00
Christopher Snowhill
632ba36f13 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:18:43 -07:00
Christopher Snowhill
271b9b34d0 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:51:05 -07:00
Christopher Snowhill
8d031f394b 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:27:53 -07:00
Christopher Snowhill
f2c6ae39c3 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:26:51 -07:00
Christopher Snowhill
bb95270747 [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:05 -07:00
Christopher Snowhill
aa36e3ce10 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:42:33 -07:00
Christopher Snowhill
da8a4dffdf Remove deep forced code signing option
This option should no longer be needed for anything.
2022-06-21 19:40:15 -07:00
a4692b80a4 [Main Menu] Added Privacy Policy link in App Menu
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-21 19:31:06 -07:00
de5cce8351 [About Dialog] Switched to WebView for credits
Signed-off-by: Kevin López Brante <kevin@kddlb.cl>
2022-06-21 19:30:58 -07:00
Christopher Snowhill
05da7450da [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:16:23 -07:00
Christopher Snowhill
2b8156e86c [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 19:14:00 -07:00
Christopher Snowhill
d59b5335e9 Revert "Removed Sparkle"
This reverts commit b54ee58ec3.
2022-06-21 18:00:30 -07:00
Christopher Snowhill
bc9e7b5d67 Revert "Remove stray entitlement from Sparkle"
This reverts commit 5ea6c9dde7.
2022-06-21 18:00:09 -07:00
514 changed files with 42395 additions and 23886 deletions

1
.gitignore vendored
View file

@ -53,4 +53,3 @@ Xcode-config/SENTRY_SETTINGS.xcconfig
/ThirdParty/vorbis/lib/libvorbisfile.3.dylib
/ThirdParty/vorbis/lib/libvorbis.0.dylib
/ThirdParty/soxr/lib/libsoxr.0.dylib
/ThirdParty/WavPack/lib/libwavpack.a

View file

@ -67,6 +67,11 @@
- (IBAction)delEntries:(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)feedback:(id)sender;
@ -108,6 +113,8 @@
- (IBAction)showRubberbandSettings:(id)sender;
+ (void)globalShowRubberbandSettings;
- (IBAction)checkForUpdates:(id)sender;
@property NSWindow *mainWindow;
@property NSWindow *miniWindow;

View file

@ -30,6 +30,8 @@
#import <MASShortcut/Shortcut.h>
#import <MASShortcut/MASDictionaryTransformer.h>
#import <Sparkle/Sparkle.h>
#import "PreferencesController.h"
#import "FeedbackController.h"
@ -42,6 +44,22 @@ BOOL kAppControllerShuttingDown = NO;
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 {
BOOL _isFullToolbarStyle;
}
@ -177,6 +195,12 @@ static BOOL consentLastEnabled = NO;
[[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];
[self.infoButton setToolTip:NSLocalizedString(@"InfoButtonTooltip", @"")];
@ -593,6 +617,22 @@ static BOOL consentLastEnabled = NO;
[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 {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:NSLocalizedString(@"PrivacyPolicyURL", @"Privacy policy URL from Iubenda.")]];
}
@ -624,9 +664,6 @@ static BOOL consentLastEnabled = NO;
NSNumber *fontSize = @(fFontSize);
[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:@"enqueue" forKey:@"openingFilesAlteredBehavior"];
@ -636,7 +673,7 @@ static BOOL consentLastEnabled = NO;
[userDefaultsValuesDict setObject:@(CogStatusStopped) forKey:@"lastPlaybackStatus"];
[userDefaultsValuesDict setObject:@"BASSMIDI" forKey:@"midiPlugin"];
[userDefaultsValuesDict setObject:@"dls appl" forKey:@"midiPlugin"];
[userDefaultsValuesDict setObject:@"default" forKey:@"midi.flavor"];
@ -668,18 +705,6 @@ static BOOL consentLastEnabled = NO;
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
[[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"];
if(oldMidiPlugin) {
[[NSUserDefaults standardUserDefaults] setValue:oldMidiPlugin forKey:@"midiPlugin"];
@ -692,11 +717,6 @@ static BOOL consentLastEnabled = NO;
if([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"FluidSynth"]) {
[[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,
@ -967,6 +987,10 @@ static NSDictionary *shortcutDefaults = nil;
[kAppController showRubberbandSettings:kAppController];
}
- (IBAction)checkForUpdates:(id)sender {
[[SparkleBridge sharedStandardUpdaterController] checkForUpdates:[[NSApplication sharedApplication] delegate]];
}
- (void)selectTrack:(id)sender {
PlaylistEntry *pe = (PlaylistEntry *)sender;
@try {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1531,6 +1531,11 @@
<action selector="showWindow:" target="Hd4-Wy-Rfl" id="xfd-8T-SL4"/>
</connections>
</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">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
@ -1541,6 +1546,45 @@
</connections>
</menuItem>
<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">
<connections>
<action selector="feedback:" target="226" id="GSH-G5-qM1"/>

View file

@ -2,10 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<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>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>

View file

@ -136,6 +136,7 @@
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 */; };
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 */; };
8377C6B927B900F000E8BC0F /* SpectrumItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C6B827B900F000E8BC0F /* SpectrumItem.m */; };
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 837DC92A285B05710005C58A /* CoreData.framework */; };
@ -150,6 +151,8 @@
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33802D06CF4100D0D770 /* SpectrumViewCG.m */; };
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33822D06CF4100D0D770 /* SpectrumWindowController.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 */; };
839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; };
839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; };
@ -169,7 +172,6 @@
83B61E2429A8296500CD0580 /* LyricsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83B61E2229A8296500CD0580 /* LyricsWindow.xib */; };
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, ); }; };
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 */; };
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; };
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; };
@ -511,6 +513,20 @@
remoteGlobalIDString = 836FB52C1820538700B3AD2D;
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 */ = {
isa = PBXContainerItemProxy;
containerPortal = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */;
@ -532,20 +548,6 @@
remoteGlobalIDString = 83B06686180D5668008E3612;
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 */ = {
isa = PBXContainerItemProxy;
containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */;
@ -678,8 +680,8 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */,
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */,
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */,
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */,
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */,
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */,
@ -731,6 +733,7 @@
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */,
8305963C277F013200EBFAAE /* File_Extractor.framework in CopyFiles */,
ED69CBCA25BE32E80090B90D /* MASShortcut.framework in CopyFiles */,
838F851F256B4E8B00C3E614 /* Sparkle.framework in CopyFiles */,
17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -955,7 +958,7 @@
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>"; };
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = minimp3.xcodeproj; path = Plugins/minimp3/minimp3.xcodeproj; sourceTree = "<group>"; };
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MAD.xcodeproj; path = Plugins/MAD/MAD.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>"; };
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpectrumViewSK.h; path = Visualization/SpectrumViewSK.h; sourceTree = "<group>"; };
@ -985,6 +988,7 @@
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>"; };
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; };
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>"; };
@ -1084,6 +1088,7 @@
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */,
17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */,
17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */,
838F851E256B4E5E00C3E614 /* Sparkle.framework in Frameworks */,
83F9FFEF2D6EB75B00026576 /* Sentry in Frameworks */,
17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */,
17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */,
@ -1129,6 +1134,7 @@
isa = PBXGroup;
children = (
ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */,
838F851D256B4E5E00C3E614 /* Sparkle.framework */,
17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
);
@ -1270,7 +1276,7 @@
17C808830C3BD181005707C4 /* HTTPSource.xcodeproj */,
83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */,
8E8D40820CBB036600135C1B /* M3u.xcodeproj */,
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */,
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */,
83B0669C180D5668008E3612 /* MIDI.xcodeproj */,
17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */,
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */,
@ -1773,6 +1779,14 @@
name = Products;
sourceTree = "<group>";
};
8372C92B27C785BD00E250C9 /* Products */ = {
isa = PBXGroup;
children = (
8372C93027C785BE00E250C9 /* MAD.bundle */,
);
name = Products;
sourceTree = "<group>";
};
8377C66027B8CF2300E8BC0F /* Visualization */ = {
isa = PBXGroup;
children = (
@ -1816,14 +1830,6 @@
path = LyricsWindow;
sourceTree = "<group>";
};
83B73B5C2D8FC05A00A57F08 /* Products */ = {
isa = PBXGroup;
children = (
83B73B602D8FC05A00A57F08 /* minimp3.bundle */,
);
name = Products;
sourceTree = "<group>";
};
83BB13AE20E4E38E00723731 /* Products */ = {
isa = PBXGroup;
children = (
@ -1984,8 +1990,8 @@
buildRules = (
);
dependencies = (
83B73B642D8FD74000A57F08 /* PBXTargetDependency */,
8327DBA8293CAD0A00CD0580 /* PBXTargetDependency */,
8372C93C27C7893100E250C9 /* PBXTargetDependency */,
83489C6A2782F76900BDCEA2 /* PBXTargetDependency */,
ED69CBC625BE32B40090B90D /* PBXTargetDependency */,
834D793E20E4EFD200C4A5CC /* PBXTargetDependency */,
@ -2130,6 +2136,10 @@
ProductGroup = 8E8D40830CBB036600135C1B /* Products */;
ProjectRef = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */;
},
{
ProductGroup = 8372C92B27C785BD00E250C9 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
},
{
ProductGroup = ED69CBB925BE328C0090B90D /* Products */;
ProjectRef = ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */;
@ -2138,10 +2148,6 @@
ProductGroup = 83B0669D180D5668008E3612 /* Products */;
ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */;
},
{
ProductGroup = 83B73B5C2D8FC05A00A57F08 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
},
{
ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */;
ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */;
@ -2357,6 +2363,13 @@
remoteRef = 836FB5461820538800B3AD2D /* PBXContainerItemProxy */;
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 */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
@ -2364,13 +2377,6 @@
remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */;
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 */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
@ -2748,6 +2754,11 @@
name = Hively;
targetProxy = 836FB5A518206F1500B3AD2D /* PBXContainerItemProxy */;
};
8372C93C27C7893100E250C9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MAD;
targetProxy = 8372C93B27C7893100E250C9 /* PBXContainerItemProxy */;
};
8375B36217FFEF010092A79F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = ArchiveSource;
@ -2758,11 +2769,6 @@
name = MIDI;
targetProxy = 83B06702180D5776008E3612 /* PBXContainerItemProxy */;
};
83B73B642D8FD74000A57F08 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = minimp3;
targetProxy = 83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */;
};
83BCB8D917FC96F800760340 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = HighlyComplete;

View file

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

View file

@ -0,0 +1,11 @@
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

@ -0,0 +1,366 @@
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

@ -0,0 +1,717 @@
---------------------------------
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

@ -0,0 +1,136 @@
<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

@ -0,0 +1,788 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,204 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,378 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,704 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,929 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,291 @@
/*
* 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

@ -0,0 +1,45 @@
/*
* 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

@ -0,0 +1,304 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,114 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,315 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,191 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,668 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,270 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,614 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,597 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,179 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,817 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,119 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,289 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,212 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,620 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,134 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,387 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,411 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,770 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,61 @@
////////////////////////////////////////////////////////////////////////////
// **** 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,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2024 David Bryant. //
// Copyright (c) 1998 - 2019 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
@ -146,15 +146,6 @@ typedef struct {
#define MIN_STREAM_VERS 0x402 // lowest stream version we'll decode
#define MAX_STREAM_VERS 0x410 // highest stream version we'll decode or encode
#define WAVPACK_MAX_CHANS 4096 // max channels handled by WavPack format & library
// This sets the maximum number of channels that the current WavPack CLI applications
// accept. It's somewhat arbitrary because the actual WavPack format and library can
// handle up to 4096 channels. However, anything beyond 256 channels is obviously
// a niche case and is not well tested, so this lower limit is defined for now.
#define WAVPACK_MAX_CLI_CHANS 256
// These are the mask bit definitions for the metadata chunk id byte (see format.txt)
#define ID_UNIQUE 0x3f
@ -176,7 +167,6 @@ typedef struct {
#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)
@ -188,8 +178,6 @@ typedef struct {
#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_WVX_NEW_BITSTREAM (ID_OPTIONAL_DATA | ID_WVX_BITSTREAM)
#define ID_BLOCK_CHECKSUM (ID_OPTIONAL_DATA | 0xf)
///////////////////////// WavPack Configuration ///////////////////////////////
@ -202,7 +190,7 @@ typedef struct {
float bitrate, shaping_weight;
int bits_per_sample, bytes_per_sample;
int qmode, flags, xmode, num_channels, float_norm_exp;
int32_t block_samples, worker_threads, sample_rate, channel_mask;
int32_t block_samples, extra_flags, sample_rate, channel_mask;
unsigned char md5_checksum [16], md5_read;
int num_tag_strings; // this field is not used
char **tag_strings; // this field is not used
@ -221,7 +209,7 @@ typedef struct {
#define CONFIG_DYNAMIC_SHAPING 0x20000 // dynamic noise shaping
#define CONFIG_CREATE_EXE 0x40000 // create executable
#define CONFIG_CREATE_WVC 0x80000 // create correction file
#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize hybrid compression
#define CONFIG_OPTIMIZE_WVC 0x100000 // maximize bybrid compression
#define CONFIG_COMPATIBLE_WRITE 0x400000 // write files for decoders < 4.3
#define CONFIG_CALC_NOISE 0x800000 // calc noise in hybrid mode
#define CONFIG_EXTRA_MODE 0x2000000 // extra processing mode
@ -229,7 +217,6 @@ typedef struct {
#define CONFIG_MD5_CHECKSUM 0x8000000 // store MD5 signature
#define CONFIG_MERGE_BLOCKS 0x10000000 // merge blocks of equal redundancy (for lossyWAV)
#define CONFIG_PAIR_UNDEF_CHANS 0x20000000 // encode undefined channels in stereo pairs
#define CONFIG_OPTIMIZE_32BIT 0x40000000 // new optimizations for 32-bit integer files
#define CONFIG_OPTIMIZE_MONO 0x80000000 // optimize for mono streams posing as stereo
// The lower 8 bits of qmode indicate the use of new features in version 5 that (presently)
@ -257,7 +244,6 @@ typedef struct {
#define QMODE_CHANS_UNASSIGNED 0x400 // user specified "..." in --channel-order option
#define QMODE_IGNORE_LENGTH 0x800 // user specified to ignore length in file header
#define QMODE_RAW_PCM 0x1000 // user specified raw PCM format (no header present)
#define QMODE_EVEN_BYTE_DEPTH 0x2000 // user specified to force even byte bit-depth
////////////// Callbacks used for reading & writing WavPack streams //////////
@ -330,11 +316,6 @@ WavpackContext *WavpackOpenFileInput (const char *infilename, char *error, int f
// (just affects retrieving wrappers & MD5 checksums)
#define OPEN_NO_CHECKSUM 0x800 // don't verify block checksums before decoding
// new for multithreaded
#define OPEN_THREADS_SHFT 12 // specify number of additional worker threads here for
#define OPEN_THREADS_MASK 0xF000 // decode; 0 to disable, otherwise 1-15 added threads
int WavpackGetMode (WavpackContext *wpc);
#define MODE_WVC 0x1
@ -404,12 +385,11 @@ int WavpackWriteTag (WavpackContext *wpc);
WavpackContext *WavpackOpenFileOutput (WavpackBlockOutput blockout, void *wv_id, void *wvc_id);
void WavpackSetFileInformation (WavpackContext *wpc, char *file_extension, unsigned char file_format);
#define WP_FORMAT_WAV 0 // Microsoft RIFF, including BWF and RF64 variants
#define WP_FORMAT_WAV 0 // Microsoft RIFF, including BWF and RF64 varients
#define WP_FORMAT_W64 1 // Sony Wave64
#define WP_FORMAT_CAF 2 // Apple CoreAudio
#define WP_FORMAT_DFF 3 // Philips DSDIFF
#define WP_FORMAT_DSF 4 // Sony DSD Format
#define WP_FORMAT_AIF 5 // Apple AIFF
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);

View file

@ -0,0 +1,707 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,19 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,688 @@
////////////////////////////////////////////////////////////////////////////
// **** 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

@ -0,0 +1,26 @@
<?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

@ -0,0 +1,554 @@
// !$*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

@ -0,0 +1,76 @@
<?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>

Binary file not shown.

View file

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

Binary file not shown.

Binary file not shown.

View file

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

View file

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

View file

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

View file

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

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

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,13 @@
#include "api_internal.h"
#include "mixing.h"
#define INTERNAL_BUF_SAMPLES 1024
LIBVGMSTREAM_API uint32_t libvgmstream_get_version(void) {
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_t* lib = NULL;
@ -58,85 +48,57 @@ LIBVGMSTREAM_API void libvgmstream_free(libvgmstream_t* lib) {
free(lib);
}
// TODO: allow calling after load
LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg) {
if (!lib || !lib->priv)
return;
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) {
memset(&priv->cfg , 0, sizeof(libvgmstream_config_t));
priv->config_loaded = false;
priv->cfg.loop_count = 1; //TODO: loop 0 means no loop (improve detection)
}
else {
priv->cfg = *cfg;
priv->config_loaded = true;
}
//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);
}
//TODO validate, etc
}
void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool full) {
void libvgmstream_priv_reset(libvgmstream_priv_t* priv, bool reset_buf) {
//memset(&priv->cfg, 0, sizeof(libvgmstream_config_t)); //config is always valid
//memset(&priv->pos, 0, sizeof(libvgmstream_priv_position_t)); //position info is updated on open
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
if (full) {
free(priv->buf.data); //TODO
if (reset_buf) {
free(priv->buf.data);
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->decode_done = false;
}
libvgmstream_sfmt_t api_get_output_sample_type(libvgmstream_priv_t* priv) {
libvgmstream_sample_t api_get_output_sample_type(libvgmstream_priv_t* priv) {
VGMSTREAM* v = priv->vgmstream;
sfmt_t format = mixing_get_output_sample_type(v);
switch(format) {
case SFMT_S16: return LIBVGMSTREAM_SFMT_PCM16;
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:
case SFMT_S16: return LIBVGMSTREAM_SAMPLE_PCM16;
case SFMT_FLT: return LIBVGMSTREAM_SAMPLE_FLOAT;
default:
return 0x00;
return 0x00; //???
}
}
int api_get_sample_size(libvgmstream_sfmt_t sample_format) {
switch(sample_format) {
case LIBVGMSTREAM_SFMT_FLOAT:
case LIBVGMSTREAM_SFMT_PCM32:
int api_get_sample_size(libvgmstream_sample_t sample_type) {
switch(sample_type) {
case LIBVGMSTREAM_SAMPLE_PCM24:
case LIBVGMSTREAM_SAMPLE_PCM32:
case LIBVGMSTREAM_SAMPLE_FLOAT:
return 0x04;
case LIBVGMSTREAM_SFMT_PCM24:
return 0x03;
case LIBVGMSTREAM_SFMT_PCM16:
case LIBVGMSTREAM_SAMPLE_PCM16:
default:
return 0x02;
}

View file

@ -3,6 +3,18 @@
#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) {
libvgmstream_config_t* cfg = &priv->cfg;
@ -24,52 +36,23 @@ static void apply_config(libvgmstream_priv_t* priv) {
if (!vcfg.allow_play_forever)
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);
}
static void prepare_mixing(libvgmstream_priv_t* priv) {
libvgmstream_config_t* cfg = &priv->cfg;
static void prepare_mixing(libvgmstream_priv_t* priv, libvgmstream_options_t* opt) {
/* enable after config but before outbuf */
if (cfg->auto_downmix_channels) {
vgmstream_mixing_autodownmix(priv->vgmstream, cfg->auto_downmix_channels);
if (priv->cfg.auto_downmix_channels) {
vgmstream_mixing_autodownmix(priv->vgmstream, priv->cfg.auto_downmix_channels);
}
else if (cfg->stereo_track >= 1) {
vgmstream_mixing_stereo_only(priv->vgmstream, cfg->stereo_track - 1);
else if (opt && opt->stereo_track >= 1) {
vgmstream_mixing_stereo_only(priv->vgmstream, opt->stereo_track - 1);
}
if (cfg->force_sfmt) {
// 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;
}
mixing_macro_output_sample_format(priv->vgmstream, force_sfmt);
if (priv->cfg.force_pcm16) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_S16);
}
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);
else if (priv->cfg.force_float) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_FLT);
}
vgmstream_mixing_enable(priv->vgmstream, INTERNAL_BUF_SAMPLES, NULL /*&input_channels*/, NULL /*&output_channels*/);
@ -93,11 +76,11 @@ static void update_format_info(libvgmstream_priv_t* priv) {
fmt->channels = v->channels;
fmt->input_channels = 0;
vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels); //query
vgmstream_mixing_enable(v, 0, &fmt->input_channels, &fmt->channels);
fmt->channel_layout = v->channel_layout;
fmt->sample_format = api_get_output_sample_type(priv);
fmt->sample_size = api_get_sample_size(fmt->sample_format);
fmt->sample_type = api_get_output_sample_type(priv);
fmt->sample_size = api_get_sample_size(fmt->sample_type);
fmt->sample_rate = v->sample_rate;
@ -122,58 +105,26 @@ static void update_format_info(libvgmstream_priv_t* priv) {
}
}
// apply config if data + config is loaded and not already loaded
void api_apply_config(libvgmstream_priv_t* priv) {
if (priv->setup_done)
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)
LIBVGMSTREAM_API int libvgmstream_open_stream(libvgmstream_t* lib, libvgmstream_options_t* opt) {
if (!lib ||!lib->priv)
return LIBVGMSTREAM_ERROR_GENERIC;
if (!opt || !opt->libsf || opt->subsong_index < 0)
return LIBVGMSTREAM_ERROR_GENERIC;
// close loaded song if any + reset
libvgmstream_close_stream(lib);
libvgmstream_priv_t* priv = lib->priv;
if (subsong_index < 0)
return LIBVGMSTREAM_ERROR_GENERIC;
load_vgmstream(priv, libsf, subsong_index);
load_vgmstream(priv, opt);
if (!priv->vgmstream)
return LIBVGMSTREAM_ERROR_GENERIC;
apply_config(priv);
prepare_mixing(priv, opt);
update_position(priv);
update_format_info(priv);
// 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_format_info(priv);
}
return LIBVGMSTREAM_OK;
}
@ -187,8 +138,6 @@ LIBVGMSTREAM_API void libvgmstream_close_stream(libvgmstream_t* lib) {
close_vgmstream(priv->vgmstream);
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);
}

View file

@ -1,7 +1,6 @@
#include "api_internal.h"
#include "mixing.h"
#include "render.h"
#include "../util/log.h"
static bool reset_buf(libvgmstream_priv_t* priv) {
@ -47,16 +46,15 @@ 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.consumed = 0; //external
// mark done if this buf reached EOF
if (!priv->pos.play_forever) {
priv->pos.current += samples_done;
priv->decode_done = (priv->pos.current >= priv->pos.play_samples);
priv->pos.current += samples_done;
}
}
// update decoder info based on last render, though at the moment it's all fixed
static void update_decoder_info(libvgmstream_priv_t* priv) {
static void update_decoder_info(libvgmstream_priv_t* priv, int samples_done) {
// output copy
priv->dec.buf = priv->buf.data;
@ -70,13 +68,6 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
return LIBVGMSTREAM_ERROR_GENERIC;
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)
return LIBVGMSTREAM_ERROR_GENERIC;
@ -93,7 +84,7 @@ LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
int decoded = render_main(&ssrc, priv->vgmstream);
update_buf(priv, decoded);
update_decoder_info(priv);
update_decoder_info(priv, decoded);
return LIBVGMSTREAM_OK;
}
@ -137,11 +128,6 @@ LIBVGMSTREAM_API int libvgmstream_fill(libvgmstream_t* lib, void* buf, int buf_s
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
priv->dec.buf = buf;
priv->dec.buf_samples = buf_copied;
@ -175,10 +161,6 @@ LIBVGMSTREAM_API void libvgmstream_seek(libvgmstream_t* lib, int64_t sample) {
seek_vgmstream(priv->vgmstream, sample);
priv->pos.current = priv->vgmstream->pstate.play_position;
// update flags just in case
update_buf(priv, 0);
update_decoder_info(priv);
}

View file

@ -12,33 +12,28 @@ static int get_internal_log_level(libvgmstream_loglevel_t level) {
}
}
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_loglevel_t level, void (*callback)(int level, const char* str)) {
int ilevel = get_internal_log_level(level);
if (callback) {
vgm_log_set_callback(NULL, ilevel, 0, callback);
}
else {
LIBVGMSTREAM_API void libvgmstream_set_log(libvgmstream_log_t* cfg) {
if (!cfg)
return;
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 {
//vgmstream_set_log_callback(ilevel, cfg->callback);
vgm_log_set_callback(NULL, ilevel, 0, cfg->callback);
}
}
LIBVGMSTREAM_API const char** libvgmstream_get_extensions(int* 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_extensions(size_t* size) {
return vgmstream_get_formats(size);
}
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(int* size) {
if (!size)
return NULL;
size_t tmp = 0;
const char** list = vgmstream_get_common_formats(&tmp);
*size = tmp;
return list;
LIBVGMSTREAM_API const char** libvgmstream_get_common_extensions(size_t* size) {
return vgmstream_get_common_formats(size);
}
@ -74,11 +69,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) {
if (!buf || !buf_len)
if (!buf || !buf_len || !cfg)
return LIBVGMSTREAM_ERROR_GENERIC;
buf[0] = '\0';
if (!lib || !lib->priv || !cfg)
if (!lib || !lib->priv)
return LIBVGMSTREAM_ERROR_GENERIC;
libvgmstream_priv_t* priv = lib->priv;

View file

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

View file

@ -5,23 +5,63 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf);
/* libstreamfile_t for external use, as a default implementation calling some internal SF */
typedef struct {
int64_t offset;
int64_t size;
STREAMFILE* sf;
char name[PATH_LIMIT];
} libsf_priv_t;
static int libsf_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
static int libsf_read(void* user_data, uint8_t* dst, int dst_size) {
libsf_priv_t* priv = user_data;
return priv->sf->read(priv->sf, dst, offset, length);
if (!priv || !dst)
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) {
libsf_priv_t* priv = user_data;
return priv->sf->get_size(priv->sf);
if (!priv)
return 0;
return priv->size;
}
static const char* libsf_get_name(void* user_data) {
libsf_priv_t* priv = user_data;
if (!priv)
return NULL;
if (priv->name[0] == '\0') {
priv->sf->get_name(priv->sf, priv->name, sizeof(priv->name));
@ -32,8 +72,7 @@ static const char* libsf_get_name(void* user_data) {
static libstreamfile_t* libsf_open(void* user_data, const char* filename) {
libsf_priv_t* priv = user_data;
if (!filename)
if (!priv || !priv->sf || !filename)
return NULL;
STREAMFILE* sf = priv->sf->open(priv->sf, filename, 0);
@ -65,11 +104,14 @@ static libstreamfile_t* libstreamfile_from_streamfile(STREAMFILE* sf) {
if (!sf)
return NULL;
libstreamfile_t* libsf = NULL;
libsf_priv_t* priv = NULL;
libstreamfile_t* libsf = calloc(1, sizeof(libstreamfile_t));
libsf = calloc(1, sizeof(libstreamfile_t));
if (!libsf) goto fail;
libsf->read = libsf_read;
libsf->seek = libsf_seek;
libsf->get_size = libsf_get_size;
libsf->get_name = libsf_get_name;
libsf->open = libsf_open;

View file

@ -18,10 +18,10 @@ typedef struct {
char name[PATH_LIMIT];
} cache_priv_t;
static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length) {
static int cache_read(void* user_data, uint8_t* dst, int dst_size) {
cache_priv_t* priv = user_data;
size_t read_total = 0;
if (!dst || length <= 0 || offset < 0)
if (!dst || dst_size <= 0)
return 0;
/* is the part of the requested length in the buffer? */
@ -30,22 +30,19 @@ static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length)
int buf_into = (int)(priv->offset - priv->buf_offset);
buf_limit = priv->valid_size - buf_into;
if (buf_limit > length)
buf_limit = length;
if (buf_limit > dst_size)
buf_limit = dst_size;
memcpy(dst, priv->buf + buf_into, buf_limit);
read_total += buf_limit;
length -= buf_limit;
dst_size -= buf_limit;
priv->offset += 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 */
while (length > 0) {
while (dst_size > 0) {
size_t buf_limit;
/* ignore requests at EOF */
@ -55,15 +52,18 @@ static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length)
break;
}
/* position to new offset */
priv->libsf->seek(priv, priv->offset, 0);
/* fill the buffer (offset now is beyond buf_offset) */
priv->buf_offset = priv->offset;
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_offset, priv->buf_size);
priv->valid_size = priv->libsf->read(priv, priv->buf, priv->buf_size);
/* decide how much must be read this time */
if (length > priv->buf_size)
if (dst_size > priv->buf_size)
buf_limit = priv->buf_size;
else
buf_limit = length;
buf_limit = dst_size;
/* give up on partial reads (EOF) */
if (priv->valid_size < buf_limit) {
@ -77,14 +77,40 @@ static int cache_read(void* user_data, uint8_t* dst, int64_t offset, int length)
memcpy(dst, priv->buf, buf_limit);
priv->offset += buf_limit;
read_total += buf_limit;
length -= buf_limit;
dst_size -= buf_limit;
dst += buf_limit;
}
priv->offset = offset; /* last fread offset */
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) {
cache_priv_t* priv = user_data;
return priv->file_size;
@ -142,6 +168,7 @@ LIBVGMSTREAM_API libstreamfile_t* libstreamfile_open_buffered(libstreamfile_t* e
if (!libsf) goto fail;
libsf->read = cache_read;
libsf->seek = cache_seek;
libsf->get_size = cache_get_size;
libsf->get_name = cache_get_name;
libsf->open = cache_open;

View file

@ -1,91 +0,0 @@
#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

@ -1,35 +0,0 @@
#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,7 +5,6 @@
#include "mixing.h"
#include "plugins.h"
#include "sbuf.h"
#include "codec_info.h"
#include "../util/log.h"
#include "decode_state.h"
@ -40,16 +39,32 @@ void decode_free(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
const codec_info_t* codec_info = codec_get_info(vgmstream);
if (codec_info) {
codec_info->free(vgmstream->codec_data);
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
free_ogg_vorbis(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_VORBIS_custom) {
free_vorbis_custom(vgmstream->codec_data);
}
#endif
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
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 ||
vgmstream->coding_type == coding_ICE_DCT) {
free_ice(vgmstream->codec_data);
@ -59,20 +74,48 @@ void decode_free(VGMSTREAM* vgmstream) {
free_ubi_adpcm(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_IMUSE) {
free_imuse(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) {
free_ongakukan_adp(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
free_compresswave(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_EA_MT) {
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 (vgmstream->coding_type == coding_MP4_AAC) {
free_mp4_aac(vgmstream->codec_data);
}
#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
if (vgmstream->coding_type == coding_G7221C) {
free_g7221(vgmstream->codec_data);
@ -85,6 +128,24 @@ void decode_free(VGMSTREAM* vgmstream) {
}
#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) {
free_acm(vgmstream->codec_data);
}
@ -101,16 +162,22 @@ void decode_seek(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
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) {
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 ||
vgmstream->coding_type == coding_ICE_DCT) {
seek_ice(vgmstream->codec_data, vgmstream->loop_current_sample);
@ -120,20 +187,76 @@ void decode_seek(VGMSTREAM* vgmstream) {
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) {
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) {
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 (vgmstream->coding_type == coding_MP4_AAC) {
seek_mp4_aac(vgmstream, vgmstream->loop_current_sample);
}
#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) {
seek_nwa(vgmstream->codec_data, vgmstream->loop_current_sample);
}
@ -146,16 +269,32 @@ void decode_reset(VGMSTREAM* vgmstream) {
if (!vgmstream->codec_data)
return;
const codec_info_t* codec_info = codec_get_info(vgmstream);
if (codec_info) {
codec_info->reset(vgmstream->codec_data);
return;
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type == coding_OGG_VORBIS) {
reset_ogg_vorbis(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_VORBIS_custom) {
reset_vorbis_custom(vgmstream);
}
#endif
if (vgmstream->coding_type == coding_CIRCUS_VQ) {
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 ||
vgmstream->coding_type == coding_ICE_DCT) {
reset_ice(vgmstream->codec_data);
@ -165,20 +304,42 @@ void decode_reset(VGMSTREAM* vgmstream) {
reset_ubi_adpcm(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_IMUSE) {
reset_imuse(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_ONGAKUKAN_ADPCM) {
reset_ongakukan_adp(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
reset_compresswave(vgmstream->codec_data);
}
if (vgmstream->coding_type == coding_EA_MT) {
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 (vgmstream->coding_type == coding_MP4_AAC) {
reset_mp4_aac(vgmstream);
}
#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
if (vgmstream->coding_type == coding_G7221C) {
reset_g7221(vgmstream->codec_data);
@ -191,6 +352,30 @@ void decode_reset(VGMSTREAM* vgmstream) {
}
#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) {
reset_acm(vgmstream->codec_data);
}
@ -246,6 +431,17 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_PCM24BE:
case coding_PCM32LE:
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_int:
case coding_CBD2:
@ -361,6 +557,10 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
#ifdef VGM_USE_G719
case coding_G719:
return 48000/50;
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
return 0;
#endif
case coding_MTAF:
return 128*2;
@ -386,16 +586,36 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
return 0; /* varies per frame */
case coding_ONGAKUKAN_ADPCM:
return 0; /* actually 1. */
case coding_COMPRESSWAVE:
return 0; /* multiple of 2 */
case coding_EA_MT:
return 0; /* 432, but variable in looped files */
case coding_CIRCUS_VQ:
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_DCT:
return 0; /* ~100 (range), ~16 (DCT) */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
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
default:
return 0;
@ -562,6 +782,9 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
#endif
#ifdef VGM_USE_G719
case coding_G719:
#endif
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg:
#endif
case coding_MTAF:
return vgmstream->interleave_block_size;
@ -584,8 +807,12 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
/* UBI_ADPCM: varies per mode? */
/* IMUSE: VBR */
/* 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 */
/* SPEEX: varies, usually 0x40-60 */
/* TAC: VBR around ~0x200-300 */
/* Vorbis, MPEG, ACM, etc: varies */
default: /* (VBR or managed by decoder) */
return 0;
}
@ -642,23 +869,12 @@ bool decode_uses_internal_offset_updates(VGMSTREAM* vgmstream) {
// decode frames for decoders which decode frame by frame and have their own sample buffer
static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream) {
const int max_empty = 1000;
int num_empty = 0;
decode_state_t* ds = vgmstream->decode_state;
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
while (sdst->filled < sdst->samples) {
@ -666,12 +882,12 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do)
// decode new frame if prev one was consumed
if (ssrc->filled == 0) {
bool ok = false;
if (codec_info) {
ok = codec_info->decode_frame(vgmstream);
}
else {
goto decode_fail;
switch (vgmstream->coding_type) {
case coding_KA1A:
ok = decode_ka1a_frame(vgmstream);
break;
default:
goto decode_fail;
}
if (!ok)
@ -686,9 +902,6 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do)
goto decode_fail;
}
}
else {
num_empty = 0; //reset for discard loops
}
if (ds->discard) {
// decoder may signal that samples need to be discarded (ex. encoder delay or during loops)
@ -706,8 +919,6 @@ static void decode_frames(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do)
sbuf_copy_segments(sdst, ssrc, samples_copy);
sbuf_consume(ssrc, samples_copy);
ds->samples_left -= samples_copy;
}
}
@ -734,7 +945,7 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
switch (vgmstream->coding_type) {
case coding_SILENCE:
sbuf_silence_rest(sdst);
sbuf_silence_s16(buffer, samples_to_do, vgmstream->channels, 0);
break;
case coding_CRI_ADX:
@ -842,6 +1053,35 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
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:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_nds_ima(&vgmstream->ch[ch], buffer+ch,
@ -989,13 +1229,36 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
}
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:
decode_circus_vq(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
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_DCT:
decode_ice(vgmstream->codec_data, buffer, samples_to_do);
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)
case coding_MP4_AAC:
decode_mp4_aac(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
@ -1187,6 +1450,15 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
}
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
case coding_G7221C:
for (ch = 0; ch < vgmstream->channels; ch++) {
@ -1200,6 +1472,21 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
decode_g719(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
}
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
case coding_ACM:
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
@ -1369,10 +1656,18 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
decode_ubi_adpcm(vgmstream, buffer, samples_to_do);
break;
case coding_IMUSE:
decode_imuse(vgmstream, buffer, samples_to_do);
break;
case coding_ONGAKUKAN_ADPCM:
decode_ongakukan_adp(vgmstream, buffer, samples_to_do);
break;
case coding_COMPRESSWAVE:
decode_compresswave(vgmstream->codec_data, buffer, samples_to_do);
break;
case coding_EA_MT:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
@ -1383,7 +1678,7 @@ void decode_vgmstream(sbuf_t* sdst, VGMSTREAM* vgmstream, int samples_to_do) {
sbuf_t stmp = *sdst;
stmp.samples = stmp.filled + samples_to_do; //TODO improve
decode_frames(&stmp, vgmstream, samples_to_do);
decode_frames(&stmp, vgmstream);
break;
}
}

View file

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

View file

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

View file

@ -74,12 +74,11 @@ bool mixer_is_active(mixer_t* mixer) {
static void setup_mixbuf(mixer_t* mixer, sbuf_t* sbuf) {
sbuf_t* smix = &mixer->smix;
// mixbuf (float) can be interpreted as F16, for 1:1 mapping with PCM16 (and possibly less rounding errors with mixops)
// for PCM24 regular float seems ok and 1:1 as well
if (sbuf->fmt == SFMT_S16)
sbuf_init(smix, SFMT_F16, mixer->mixbuf, sbuf->filled, sbuf->channels);
// mixbuf can be interpreted as FLT or F32; try to use src's to keep buf as-is (less rounding errors)
if (sbuf->fmt == SFMT_F32 || sbuf->fmt == SFMT_FLT)
sbuf_init(smix, sbuf->fmt, mixer->mixbuf, sbuf->filled, sbuf->channels); //mixer->input_channels
else
sbuf_init(smix, sbuf->fmt, mixer->mixbuf, sbuf->filled, sbuf->channels);
sbuf_init(smix, SFMT_F32, mixer->mixbuf, sbuf->filled, sbuf->channels);
// remix to temp buf (somehow using float buf rather than int32 is faster?)
sbuf_copy_segments(smix, sbuf, sbuf->filled);
@ -101,20 +100,12 @@ static void setup_outbuf(mixer_t* mixer, sbuf_t* sbuf) {
void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) {
// external
/* external */
//if (!mixer_is_active(mixer))
// return;
#if 0
// 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)
/* 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) {
//;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))
@ -125,7 +116,7 @@ void mixer_process(mixer_t* mixer, sbuf_t* sbuf, int32_t current_pos) {
setup_mixbuf(mixer, sbuf);
// apply mixing ops in order. channels in mixers may increase or decrease per op (set in sbuf)
// apply mixing ops in order. channesl in mixersmix may increase or decrease per op
// - 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)
for (int m = 0; m < mixer->chain_count; m++) {

View file

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

View file

@ -7,7 +7,6 @@
#include "mixer.h"
#include "mixer_priv.h"
#include "sbuf.h"
#include "codec_info.h"
/* Wrapper/helpers for vgmstream's "mixer", which does main sample buffer transformations */
@ -144,32 +143,9 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan
}
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)
switch(vgmstream->coding_type) {
#ifdef VGM_USE_VORBIS
case coding_VORBIS_custom:
#endif
case coding_KA1A:
return SFMT_FLT;
default:
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) {
mixer_t* mixer = vgmstream->mixer;
if (!mixer || !type)
if (!mixer)
return;
// optimization (may skip initializing mixer)
@ -577,5 +577,4 @@ void mixing_macro_output_sample_format(VGMSTREAM* vgmstream, sfmt_t type) {
if (input_fmt == type)
return;
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_vs_mh:
case layout_blocked_mul:
case layout_blocked_gsnd:
case layout_blocked_vas_kceo:
case layout_blocked_gsb:
case layout_blocked_xvas:
case layout_blocked_thp:
case layout_blocked_filp:
case layout_blocked_rage_aud:

View file

@ -1,129 +1,10 @@
#include <stdlib.h>
#include <string.h>
//#include <math.h>
#include "../util.h"
#include "sbuf.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) {
memset(sbuf, 0, sizeof(sbuf_t));
@ -137,8 +18,8 @@ void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels) {
sbuf_init(sbuf, SFMT_S16, buf, samples, channels);
}
void sbuf_init_f16(sbuf_t* sbuf, float* buf, int samples, int channels) {
sbuf_init(sbuf, SFMT_F16, buf, samples, channels);
void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels) {
sbuf_init(sbuf, SFMT_F32, buf, samples, channels);
}
void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) {
@ -148,18 +29,13 @@ void sbuf_init_flt(sbuf_t* sbuf, float* buf, int samples, int channels) {
int sfmt_get_sample_size(sfmt_t fmt) {
switch(fmt) {
case SFMT_F16:
case SFMT_F32:
case SFMT_FLT:
case SFMT_S24:
case SFMT_S32:
return 0x04;
case SFMT_S16:
return 0x02;
case SFMT_O24:
return 0x03;
default:
VGM_LOG("SBUF: undefined sample format %i found\n", fmt);
return 0; //TODO return 4 to avoid crashes?
return 0;
}
}
@ -172,8 +48,6 @@ void* sbuf_get_filled_buf(sbuf_t* sbuf) {
}
void sbuf_consume(sbuf_t* sbuf, int samples) {
if (samples == 0) //some discards
return;
int sample_size = sfmt_get_sample_size(sbuf->fmt);
if (sample_size <= 0) //???
return;
@ -188,6 +62,102 @@ void sbuf_consume(sbuf_t* sbuf, int 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
int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) {
@ -198,6 +168,209 @@ int sbuf_get_copy_max(sbuf_t* sdst, sbuf_t* ssrc) {
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) {
int sample_size = sfmt_get_sample_size(sbuf->fmt);
@ -210,295 +383,44 @@ void sbuf_silence_rest(sbuf_t* sbuf) {
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) {
//TODO: use interpolated fadedness to improve performance?
//TODO: use float fadedness?
int s = start * sbuf->channels;
int s_end = (start + to_do) * sbuf->channels;
switch(sbuf->fmt) {
case SFMT_S16:
sbuf_fade_i16(sbuf, start, to_do, fade_pos, fade_duration);
break;
case SFMT_S24:
case SFMT_S32:
sbuf_fade_i32(sbuf, start, to_do, fade_pos, fade_duration);
case SFMT_S16: {
int16_t* buf = sbuf->buf;
while (s < s_end) {
double fadedness = (double)(fade_duration - fade_pos) / fade_duration;
fade_pos++;
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_int(buf[s] * fadedness);
s++;
}
}
break;
}
case SFMT_FLT:
case SFMT_F16:
sbuf_fade_flt(sbuf, start, to_do, fade_pos, fade_duration);
break;
case SFMT_O24:
sbuf_fade_o24(sbuf, start, to_do, fade_pos, fade_duration);
case SFMT_F32: {
float* buf = sbuf->buf;
while (s < s_end) {
double fadedness = (double)(fade_duration - fade_pos) / fade_duration;
fade_pos++;
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_float(buf[s] * fadedness);
s++;
}
}
break;
}
default:
VGM_LOG("SBUF: missing fade for fmt=%i\n", sbuf->fmt);
break;
}
@ -506,72 +428,3 @@ void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_dur
int count = sbuf->filled - (start + to_do);
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,33 +11,29 @@
* rather than planar (buffer per channel = [ch][s] = c1 c1 c1 c1 ... c2 c2 c2 c2 ...) */
typedef enum {
SFMT_NONE,
SFMT_S16, // PCM16
SFMT_F16, // PCM16-like float (+-32767.0f), for internal use (simpler s16 <> f16, plus some decoders use it)
SFMT_FLT, // standard float (+-1.0), for external players
SFMT_S24, // PCM24 for internal use (32-bit buffers)
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_S16, /* standard PCM16 */
//SFMT_S24,
//SFMT_S32,
SFMT_F32, /* pcm-like float (+-32768), for internal use (simpler pcm > f32 plus some decoders use this) */
SFMT_FLT, /* standard float (+-1.0), for external players */
} sfmt_t;
/* 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) */
typedef struct {
void* buf; // current sample buffer
sfmt_t fmt; // buffer type
int channels; // interleaved step or planar buffers
int samples; // max samples
int filled; // samples in buffer
void* buf; /* current sample buffer */
sfmt_t fmt; /* buffer type */
int channels; /* interleaved step or planar buffers */
int samples; /* max samples */
int filled; /* samples in buffer */
} 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) */
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_f16(sbuf_t* sbuf, float* buf, int samples, int channels);
void sbuf_init_f32(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);
@ -50,15 +46,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 */
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_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_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_interleave(sbuf_t* sbuf, float** ibuf);
void sbuf_interleave_vorbis(sbuf_t* sbuf, float** ibuf);
#endif

View file

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

View file

@ -1,35 +1,28 @@
#ifdef VGM_USE_ATRAC9
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#ifdef VGM_USE_ATRAC9
#include "libatrac9/libatrac9.h"
/* opaque struct */
typedef struct {
uint8_t* buf;
int buf_size;
struct atrac9_codec_data {
uint8_t* data_buffer;
size_t data_buffer_size;
int16_t* sbuf;
int discard;
sample_t* sample_buffer;
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;
atrac9_config config;
Atrac9CodecInfo info;
void* handle;
} atrac9_codec_data;
void* handle; /* decoder handle */
Atrac9CodecInfo info; /* decoder info */
};
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) {
atrac9_codec_data* init_atrac9(atrac9_config* cfg) {
int status;
uint8_t config_data[4];
atrac9_codec_data* data = NULL;
@ -54,18 +47,16 @@ void* init_atrac9(atrac9_config* cfg) {
}
// must hold at least one superframe and its samples
data->buf_size = data->info.superframeSize;
/* must hold at least one superframe and its samples */
data->data_buffer_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;
// 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;
data->samples_to_discard = cfg->encoder_delay;
memcpy(&data->config, cfg, sizeof(atrac9_config));
@ -76,132 +67,162 @@ fail:
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;
// 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;
while (samples_done < samples_to_do) {
int to_read = data->info.superframeSize;
int bytes = read_streamfile(data->buf, vs->offset, to_read, vs->streamfile);
if (data->samples_filled) { /* consume samples */
int samples_to_get = data->samples_filled;
vs->offset += bytes;
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;
return (bytes == to_read);
}
memcpy(outbuf + samples_done*channels,
data->sample_buffer + data->samples_used*channels,
samples_to_get*channels * sizeof(sample_t));
static int decode(VGMSTREAM* v) {
int channels = v->channels;
atrac9_codec_data* data = v->codec_data;
samples_done += samples_to_get;
}
uint8_t* buf = data->buf;
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;
/* 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;
buf += bytes_used;
sbuf += data->info.frameSamples * channels;
samples += data->info.frameSamples;
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 samples;
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);
}
// 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;
void reset_atrac9(atrac9_codec_data* data) {
if (!data) return;
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 (!data->handle)
goto fail;
#if 0
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
{
int status;
uint8_t config_data[4];
Atrac9ReleaseHandle(data->handle);
data->handle = Atrac9GetHandle();
if (!data->handle) return;
if (!data->handle) goto fail;
uint8_t config_data[4];
put_u32be(config_data, data->config.config_data);
int status = Atrac9InitDecoder(data->handle, config_data);
status = Atrac9InitDecoder(data->handle, config_data);
if (status < 0) goto fail;
}
#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* v, int32_t num_sample) {
atrac9_codec_data* data = v->codec_data;
void seek_atrac9(VGMSTREAM* vgmstream, int32_t num_sample) {
atrac9_codec_data* data = vgmstream->codec_data;
if (!data) return;
reset_atrac9(data);
// 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 superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
/* 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;
off_t seek_offset;
int32_t seek_discard;
int32_t superframe_samples = data->info.frameSamples * data->info.framesInSuperframe;
size_t superframe_number, superframe_back;
// decoded frames affect each other slightly, so move offset back to make PCM stable
// and equivalent to a full discard loop
int superframe_number = (seek_sample / superframe_samples); // closest
int superframe_back = 1; // 1 seems enough (even when only 1 subframe in superframe)
if (superframe_back > superframe_number)
superframe_back = superframe_number;
superframe_number = (seek_sample / superframe_samples); /* closest */
int32_t seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
off_t seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
/* decoded frames affect each other slightly, so move offset back to make PCM stable
* and equivalent to a full discard loop */
superframe_back = 1; /* 1 seems enough (even when only 1 subframe in superframe) */
if (superframe_back > superframe_number)
superframe_back = superframe_number;
data->discard = seek_discard; // already includes encoder delay
seek_discard = (seek_sample % superframe_samples) + (superframe_back * superframe_samples);
seek_offset = (superframe_number - superframe_back) * data->info.superframeSize;
if (v->loop_ch) {
v->loop_ch[0].offset = v->loop_ch[0].channel_start_offset + seek_offset;
data->samples_to_discard = seek_discard; /* already includes encoder delay */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + seek_offset;
}
#if 0
//old full discard loop
data->discard = num_sample;
data->discard += data->config.encoder_delay;
{
data->samples_to_discard = num_sample;
data->samples_to_discard += data->config.encoder_delay;
// 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;
/* 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;
}
#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 const int sample_rate_table[16] = {
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
@ -245,8 +266,7 @@ fail:
return 0;
}
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data) {
atrac9_codec_data* data = priv_data;
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data* data) {
return bytes / data->info.superframeSize * (data->info.frameSamples * data->info.framesInSuperframe);
}
@ -256,12 +276,4 @@ size_t atrac9_bytes_to_samples_cfg(size_t bytes, uint32_t config_data) {
return 0;
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

View file

@ -1,7 +1,6 @@
#ifdef VGM_USE_CELT
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#ifdef VGM_USE_CELT
#include "celt/celt_fsb.h"
#define FSB_CELT_0_06_1_VERSION 0x80000009 /* libcelt-0.6.1 */
@ -10,25 +9,204 @@
#define FSB_CELT_INTERNAL_SAMPLE_RATE 44100
#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 */
typedef struct {
uint8_t buf[FSB_CELT_MAX_DATA_SIZE];
int frame_size; // current size
struct celt_codec_data {
sample_t* buffer;
int16_t* sbuf;
sample_t* sample_buffer;
size_t samples_filled; /* number of samples in the buffer */
size_t samples_used; /* number of samples extracted from the buffer */
int discard;
int samples_to_discard;
int channel_mode;
celt_lib_t version;
void* mode_handle;
void* decoder_handle;
} celt_codec_data;
};
static void free_celt_fsb(void* priv_data) {
celt_codec_data* data = priv_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. */
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;
switch(data->version) {
@ -46,189 +224,7 @@ static void free_celt_fsb(void* priv_data) {
break;
}
free(data->sbuf);
free(data->sample_buffer);
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

View file

@ -92,15 +92,18 @@ 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_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_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 pcm24_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);
/* 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 */
void decode_psx(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int is_badflags, int config);
@ -285,8 +288,13 @@ int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data);
/* imuse_decoder */
void* init_imuse_mcomp(STREAMFILE* sf, int channels);
void* init_imuse_aifc(STREAMFILE* sf, uint32_t start_offset, int channels);
typedef struct imuse_codec_data imuse_codec_data;
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 */
typedef struct ongakukan_adp_data ongakukan_adp_data;
@ -300,8 +308,14 @@ void free_ongakukan_adp(ongakukan_adp_data* data);
int32_t ongakukan_adp_get_samples(ongakukan_adp_data* data);
/* compresswave_decoder */
void* init_compresswave(STREAMFILE* sf);
STREAMFILE* compresswave_get_streamfile(VGMSTREAM* v);
typedef struct compresswave_codec_data compresswave_codec_data;
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*/
@ -318,7 +332,13 @@ void free_ea_mt(ea_mt_codec_data* data, int channels);
/* relic_decoder */
void* init_relic(int channels, int bitrate, int codec_rate);
typedef struct relic_codec_data relic_codec_data;
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);
@ -326,8 +346,10 @@ int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
typedef struct hca_codec_data hca_codec_data;
hca_codec_data* init_hca(STREAMFILE* sf);
void free_hca(void* data);
void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
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);
typedef struct {
@ -347,7 +369,13 @@ STREAMFILE* hca_get_streamfile(hca_codec_data* data);
/* tac_decoder */
void* init_tac(STREAMFILE* sf);
typedef struct tac_codec_data tac_codec_data;
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 */
@ -361,19 +389,18 @@ void free_ice(ice_codec_data* data);
/* ka1a_decoder */
void* init_ka1a(int bitrate_mode, int channels_tracks);
typedef struct ka1a_codec_data ka1a_codec_data;
/* ubimpeg_decoder */
void* init_ubimpeg(uint32_t mode);
/* mio_decoder */
void* init_mio(STREAMFILE* sf, int* p_loop_point);
ka1a_codec_data* init_ka1a(int bitrate_mode, int channels_tracks);
void free_ka1a(ka1a_codec_data* data);
void reset_ka1a(ka1a_codec_data* data);
bool decode_ka1a_frame(VGMSTREAM* vgmstream);
void seek_ka1a(VGMSTREAM* v, int32_t num_sample);
#ifdef VGM_USE_VORBIS
/* ogg_vorbis_decoder */
typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data;
typedef struct { //todo simplify
STREAMFILE *streamfile;
int64_t start; /* file offset where the Ogg starts */
@ -388,12 +415,16 @@ typedef struct { //todo simplify
} ogg_vorbis_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);
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_set_disable_reordering(ogg_vorbis_codec_data* data, bool set);
void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, bool set);
void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set);
void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, int set);
STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data);
@ -407,7 +438,6 @@ typedef enum {
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
VORBIS_VID1, /* Neversoft VID1: 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;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -436,15 +466,25 @@ typedef struct {
/* output (kinda ugly here but to simplify) */
off_t data_start_offset;
int64_t last_granule;
} vorbis_custom_config;
vorbis_custom_codec_data* init_vorbis_custom(STREAMFILE* sf, off_t start_offset, vorbis_custom_t type, vorbis_custom_config* config);
void free_vorbis_custom(void* data);
int32_t vorbis_custom_get_samples(VGMSTREAM* v);
void decode_vorbis_custom(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
void reset_vorbis_custom(VGMSTREAM* vgmstream);
void seek_vorbis_custom(VGMSTREAM* vgmstream, int32_t num_sample);
void free_vorbis_custom(vorbis_custom_codec_data* data);
#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
/* mpeg_decoder */
@ -491,27 +531,19 @@ typedef struct {
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);
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);
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);
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_h(uint32_t header, mpeg_frame_info* info);
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 test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey);
#endif
#ifdef VGM_USE_G7221
@ -563,23 +595,41 @@ typedef struct {
uint32_t config_data; /* ATRAC9 config header */
int encoder_delay; /* initial samples to discard */
} atrac9_config;
typedef struct atrac9_codec_data atrac9_codec_data;
void* init_atrac9(atrac9_config* cfg);
size_t atrac9_bytes_to_samples(size_t bytes, void* priv_data);
atrac9_codec_data* init_atrac9(atrac9_config* cfg);
void decode_atrac9(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
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);
#endif
#ifdef VGM_USE_CELT
void* init_celt_fsb_v1(int channels);
void* init_celt_fsb_v2(int channels);
/* celt_fsb_decoder */
typedef enum { CELT_0_06_1,CELT_0_11_0} celt_lib_t;
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
#ifdef VGM_USE_SPEEX
/* speex_decoder */
void* init_speex_ea(int channels);
void* init_speex_torus(int channels);
typedef struct speex_codec_data speex_codec_data;
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
@ -591,7 +641,10 @@ 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_subsong(STREAMFILE* sf, uint8_t* header, uint64_t header_size, uint64_t start, uint64_t size, int target_subsong);
void free_ffmpeg(void* data);
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels);
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);
uint32_t ffmpeg_get_channel_layout(ffmpeg_codec_data* data);
@ -599,7 +652,6 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channels_remap);
const char* ffmpeg_get_codec_name(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_allow_pcm24(ffmpeg_codec_data* data);
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key);
int32_t ffmpeg_get_samples(ffmpeg_codec_data* data);
@ -672,7 +724,7 @@ typedef struct {
int frame_samples;
} mp4_custom_t;
ffmpeg_codec_data* init_ffmpeg_mp4_custom_ktac(STREAMFILE* sf, mp4_custom_t* mp4);
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4);
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4);
#endif
@ -718,6 +770,8 @@ 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 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 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);

View file

@ -0,0 +1,60 @@
#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,45 +1,27 @@
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "coding_utils_samples.h"
#include "libs/compresswave_lib.h"
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 512 // arbitrary, but should be multiple of 2 for 22050 mode
#define COMPRESSWAVE_MAX_CHANNELS 2
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */
typedef struct {
/* opaque struct */
struct compresswave_codec_data {
/* config */
STREAMFILE* sf;
TCompressWaveData* handle;
TCompressWaveData* cw;
int16_t pbuf[COMPRESSWAVE_MAX_FRAME_SAMPLES * COMPRESSWAVE_MAX_CHANNELS];
int discard;
} compresswave_codec_data;
/* frame state */
int16_t* samples;
int frame_samples;
static void reset_compresswave(void* priv_data) {
compresswave_codec_data* data = priv_data;
if (!data) return;
/* frame state */
s16buf_t sbuf;
int samples_discard;
};
/* actual way to reset internal flags */
TCompressWaveData_Stop(data->handle);
TCompressWaveData_Play(data->handle, 0);
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* init_compresswave(STREAMFILE* sf) {
compresswave_codec_data* data = NULL;
data = calloc(1, sizeof(compresswave_codec_data));
@ -48,10 +30,15 @@ void* init_compresswave(STREAMFILE* sf) {
data->sf = reopen_streamfile(sf, 0);
if (!data->sf) goto fail;
data->handle = TCompressWaveData_Create();
if (!data->handle) goto fail;
data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */
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);
@ -62,46 +49,90 @@ fail:
}
static bool decode_frame_compresswave(VGMSTREAM* v) {
compresswave_codec_data* data = v->codec_data;
decode_state_t* ds = v->decode_state;
static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) {
uint32_t Len;
int ok;
int samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
//if (samples % 2 && samples > 1)
// samples -= 1; /* 22khz does 2 samples at once */
data->sbuf.samples = data->samples;
data->sbuf.channels = 2;
data->sbuf.filled = 0;
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 */
uint32_t len = samples * sizeof(int16_t) * 2; /* forced stereo */
Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */
int ok = TCompressWaveData_Rendering(data->handle, data->pbuf, len);
if (!ok) return false;
ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len);
if (!ok) goto fail;
sbuf_init_s16(&ds->sbuf, data->pbuf, samples, v->channels);
ds->sbuf.filled = ds->sbuf.samples;
data->sbuf.filled = samples_to_do;
return true;
return 1;
fail:
return 0;
}
void seek_compresswave(VGMSTREAM* v, int32_t num_sample) {
compresswave_codec_data* data = v->codec_data;
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
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;
reset_compresswave(data);
data->discard += num_sample;
data->samples_discard += num_sample;
}
STREAMFILE* compresswave_get_streamfile(VGMSTREAM* v) {
compresswave_codec_data* data = v->codec_data;
void free_compresswave(compresswave_codec_data* data) {
if (!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;
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,8 +5,6 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include "../base/decode_state.h"
#include "../base/codec_info.h"
/* opaque struct */
struct ffmpeg_codec_data {
@ -28,34 +26,32 @@ struct ffmpeg_codec_data {
int stream_index;
int64_t total_samples; /* may be 0 and innacurate */
int64_t skip_samples; /* number of start samples that will be skipped (encoder delay) */
bool channel_remap_set;
int channel_remap_set;
int channel_remap[32]; /* map of channel > new position */
bool invert_floats_set;
bool skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
bool force_seek; /* flags for special seeking in faulty formats */
bool bad_init;
int invert_floats_set;
int skip_samples_set; /* flag to know skip samples were manually added from vgmstream */
int force_seek; /* flags for special seeking in faulty formats */
int bad_init;
// FFmpeg context used for metadata
const AVCodec* codec;
/* FFmpeg decoder state */
uint8_t* buffer;
unsigned char* buffer;
AVIOContext* ioCtx;
AVFormatContext* formatCtx;
AVCodecContext* codecCtx;
AVFrame* frame; /* last decoded frame */
AVPacket* packet; /* last read data packet */
bool read_packet;
bool end_of_stream;
bool end_of_audio;
int read_packet;
int end_of_stream;
int end_of_audio;
/* other state */
int samples_discard;
sfmt_t fmt;
void* sbuf;
int sbuf_samples;
/* sample state */
int32_t samples_discard;
int32_t samples_consumed;
int32_t samples_filled;
};
@ -86,53 +82,25 @@ static void g_init_ffmpeg(void) {
}
}
static void remap_audio_flt(float* outbuf, int sample_count, int channels, int* channel_mappings) {
for (int s = 0; s < sample_count; s++) {
for (int ch_from = 0; ch_from < channels; ch_from++) {
static void remap_audio(sample_t* outbuf, int sample_count, int channels, int* channel_mappings) {
int ch_from,ch_to,s;
sample_t temp;
for (s = 0; s < sample_count; s++) {
for (ch_from = 0; ch_from < channels; ch_from++) {
if (ch_from > 32)
continue;
int ch_to = channel_mappings[ch_from];
ch_to = channel_mappings[ch_from];
if (ch_to < 1 || ch_to > 32 || ch_to > channels-1 || ch_from == ch_to)
continue;
float temp = outbuf[s*channels + ch_from];
temp = outbuf[s*channels + ch_from];
outbuf[s*channels + ch_from] = outbuf[s*channels + ch_to];
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.
*
@ -230,6 +198,7 @@ test_seek:
avcodec_flush_buffers(data->codecCtx);
return 0;
fail:
return -1;
}
@ -388,7 +357,7 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
if (errcode < 0) goto fail;
/* reset non-zero values */
data->read_packet = true;
data->read_packet = 1;
/* setup other values */
{
@ -408,10 +377,6 @@ ffmpeg_codec_data* init_ffmpeg_header_offset_subsong(STREAMFILE* sf, uint8_t* he
data->frameSize = av_get_audio_frame_duration(data->codecCtx,0);
#endif
data->fmt = convert_sample_type(data);
if (data->fmt == SFMT_NONE)
goto fail;
/* try to guess frames/samples (duration isn't always set) */
data->total_samples = av_rescale_q(stream->duration, stream->time_base, tb);
if (data->total_samples < 0)
@ -580,23 +545,23 @@ fail:
return -1;
}
/* decodes a new frame to internal data */
static int decode_frame_internal(ffmpeg_codec_data* data) {
if (data->bad_init)
return -1;
static int decode_ffmpeg_frame(ffmpeg_codec_data* data) {
int errcode;
int frame_error = 0;
if (data->bad_init) {
goto fail;
}
/* 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) {
VGM_LOG("FFMPEG: decode after end of audio\n");
return -1;
goto fail;
}
bool frame_error = false;
/* read data packets until valid is found */
while (data->read_packet && !data->end_of_audio) {
if (!data->end_of_stream) {
@ -604,19 +569,19 @@ static int decode_frame_internal(ffmpeg_codec_data* data) {
av_packet_unref(data->packet);
/* read encoded data from demuxer into packet */
int errcode = av_read_frame(data->formatCtx, data->packet);
errcode = av_read_frame(data->formatCtx, data->packet);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
data->end_of_stream = true; // no more data to read (but may "drain" samples)
data->end_of_stream = 1; /* no more data to read (but may "drain" samples) */
}
else {
VGM_LOG("FFMPEG: av_read_frame errcode=%i\n", errcode);
frame_error = true; //goto fail;
frame_error = 1; //goto fail;
}
if (data->formatCtx->pb && data->formatCtx->pb->error) {
VGM_LOG("FFMPEG: pb error=%i\n", data->formatCtx->pb->error);
frame_error = true; //goto fail;
frame_error = 1; //goto fail;
}
}
@ -626,31 +591,31 @@ static int decode_frame_internal(ffmpeg_codec_data* data) {
}
/* send encoded data to frame decoder (NULL at EOF to "drain" samples below) */
int errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
errcode = avcodec_send_packet(data->codecCtx, data->end_of_stream ? NULL : data->packet);
if (errcode < 0) {
if (errcode != AVERROR(EAGAIN)) {
VGM_LOG("FFMPEG: avcodec_send_packet errcode=%i\n", errcode);
frame_error = true; //goto fail;
frame_error = 1; //goto fail;
}
}
data->read_packet = false; // got data
data->read_packet = 0; /* got data */
}
/* decode frame samples from sent packet or "drain" samples*/
if (!frame_error) {
/* receive uncompressed sample data from decoded frame */
int errcode = avcodec_receive_frame(data->codecCtx, data->frame);
errcode = avcodec_receive_frame(data->codecCtx, data->frame);
if (errcode < 0) {
if (errcode == AVERROR_EOF) {
data->end_of_audio = true; // no more audio, file is fully decoded
data->end_of_audio = 1; /* no more audio, file is fully decoded */
}
else if (errcode == AVERROR(EAGAIN)) {
data->read_packet = true; // 0 samples, request more encoded data
data->read_packet = 1; /* 0 samples, request more encoded data */
}
else {
VGM_LOG("FFMPEG: avcodec_receive_frame errcode=%i\n", errcode);
frame_error = true; //goto fail;
frame_error = 1;//goto fail;
}
}
}
@ -658,83 +623,155 @@ static int decode_frame_internal(ffmpeg_codec_data* data) {
/* on frame_error simply uses current frame (possibly with nb_samples=0), which mirrors ffmpeg's output
* (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.
*
* 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
* (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
* (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_u8_to_s16(int16_t* obuf, uint8_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ((int)ibuf[s] - 0x80) << 8;
static void samples_silence_s16(sample_t* obuf, int ochs, int samples) {
int s, total_samples = samples * ochs;
for (s = 0; s < total_samples; s++) {
obuf[s] = 0; /* memset'd */
}
}
static void samples_u8p_to_s16(int16_t* obuf, uint8_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ((int)ibuf[ch][s] - 0x80) << 8;
static void samples_u8_to_s16(sample_t* obuf, uint8_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
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(int16_t* obuf, int16_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s];
static void samples_s16_to_s16(sample_t* obuf, int16_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = ibuf[skip*ichs + s]; /* maybe should mempcy */
}
}
static void samples_s16p_to_s16(int16_t* obuf, int16_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s];
static void samples_s16p_to_s16(sample_t* obuf, int16_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] = ibuf[ch][skip + s];
}
}
}
static void samples_s32_to_s32(int32_t* obuf, int32_t* ibuf, int ichs, int samples) {
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[ichs + s];
static void samples_s32_to_s16(sample_t* obuf, int32_t* ibuf, int ichs, int samples, int skip) {
int s, total_samples = samples * ichs;
for (s = 0; s < total_samples; s++) {
obuf[s] = ibuf[skip*ichs + s] >> 16;
}
}
static void samples_s32p_to_s32(int32_t* obuf, int32_t** ibuf, int ichs, int samples) {
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s];
static void samples_s32p_to_s16(sample_t* obuf, int32_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] = ibuf[ch][skip + s] >> 16;
}
}
}
static void samples_flt_to_flt(float* obuf, float* ibuf, int ichs, int samples, bool invert) {
float scale = invert ? -1.0f : 1.0;
for (int s = 0; s < samples * ichs; s++) {
obuf[s] = ibuf[s] * scale;
static void samples_flt_to_s16(sample_t* obuf, float* ibuf, int ichs, int samples, int skip, int invert) {
int s, total_samples = samples * ichs;
float scale = invert ? -32768.0f : 32768.0f;
for (s = 0; s < total_samples; s++) {
obuf[s] = clamp16(float_to_int(ibuf[skip*ichs + s] * scale));
}
}
static void samples_fltp_to_flt(float* obuf, float** ibuf, int ichs, int samples, bool invert) {
float scale = invert ? -1.0f : 1.0;
for (int ch = 0; ch < ichs; ch++) {
for (int s = 0; s < samples; s++) {
obuf[s*ichs + ch] = ibuf[ch][s] * scale;
static void samples_fltp_to_s16(sample_t* obuf, float** ibuf, int ichs, int samples, int skip, int invert) {
int s, ch;
float scale = invert ? -32768.0f : 32768.0f;
for (ch = 0; ch < ichs; ch++) {
for (s = 0; s < samples; s++) {
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 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) {
static void copy_samples(ffmpeg_codec_data* data, sample_t* outbuf, int samples_to_do) {
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 24, 100)
int channels = data->codecCtx->channels;
#else
int channels = data->codecCtx->ch_layout.nb_channels;
#endif
int is_planar = av_sample_fmt_is_planar(data->codecCtx->sample_fmt) && (channels > 1);
void* ibuf;
if (is_planar) {
ibuf = data->frame->extended_data;
}
@ -742,107 +779,82 @@ static void copy_samples(ffmpeg_codec_data* data, void* sbuf, int samples_to_do,
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) {
case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_U8:
if (is_planar)
samples_u8p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_u8_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* unused? */
case AV_SAMPLE_FMT_U8P: if (is_planar) { samples_u8p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_U8: samples_u8_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_S16P:
case AV_SAMPLE_FMT_S16:
if (is_planar)
samples_s16p_to_s16(sbuf, ibuf, channels, samples_to_do);
else
samples_s16_to_s16(sbuf, ibuf, channels, samples_to_do);
break;
/* common */
case AV_SAMPLE_FMT_S16P: if (is_planar) { samples_s16p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_S16: samples_s16_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_S32P:
case AV_SAMPLE_FMT_S32:
if (is_planar)
samples_s32p_to_s32(sbuf, ibuf, channels, samples_to_do);
else
samples_s32_to_s32(sbuf, ibuf, channels, samples_to_do);
break;
/* possibly FLAC and other lossless codecs */
case AV_SAMPLE_FMT_S32P: if (is_planar) { samples_s32p_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_S32: samples_s32_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
case AV_SAMPLE_FMT_FLTP:
case AV_SAMPLE_FMT_FLT:
if (is_planar)
samples_fltp_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
else
samples_flt_to_flt(sbuf, ibuf, channels, samples_to_do, data->invert_floats_set);
break;
/* mainly MDCT-like codecs (Ogg, AAC, etc) */
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; }
// fall through
case AV_SAMPLE_FMT_FLT: samples_flt_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed, data->invert_floats_set); break;
/* possibly PCM64 only (not enabled) */
case AV_SAMPLE_FMT_DBLP: if (is_planar) { samples_dblp_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break; }
// fall through
case AV_SAMPLE_FMT_DBL: samples_dbl_to_s16(outbuf, ibuf, channels, samples_to_do, data->samples_consumed); break;
default:
break;
}
// FFmpeg can't do PCM24 and upsamples to PCM32, if needed
if (data->fmt == SFMT_S24) {
samples_s32_to_s24(sbuf, ibuf, channels, samples_to_do);
}
if (data->channel_remap_set)
remap_audio(outbuf, samples_to_do, channels, data->channel_remap);
}
void remap_audio(ffmpeg_codec_data* data, sbuf_t* sbuf) {
if (!data->channel_remap_set)
return;
/* decode samples of any kind of FFmpeg format */
void decode_ffmpeg(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do, int channels) {
ffmpeg_codec_data* data = vgmstream->codec_data;
switch(sbuf->fmt) {
case SFMT_FLT:
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;
while (samples_to_do > 0) {
free(data->sbuf);
if (data->samples_consumed < data->samples_filled) {
/* consume samples */
int samples_to_get = (data->samples_filled - data->samples_consumed);
data->sbuf_samples = samples * 2;
data->sbuf = malloc(data->sbuf_samples * channels * sizeof(float));
if (!data->sbuf) return false;
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;
return true;
}
copy_samples(data, outbuf, samples_to_get);
bool decode_frame_ffmpeg(VGMSTREAM* v) {
decode_state_t* ds = v->decode_state;
ffmpeg_codec_data* data = v->codec_data;
samples_to_do -= samples_to_get;
outbuf += samples_to_get * channels;
}
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;
/* mark consumed samples */
data->samples_consumed += samples_to_get;
}
else {
int ok = decode_ffmpeg_frame(data);
if (!ok) goto decode_fail;
}
}
return true;
return;
decode_fail:
VGM_LOG("FFMPEG: decode fail, missing %i samples\n", samples_to_do);
samples_silence_s16(outbuf, channels, samples_to_do);
}
@ -850,7 +862,11 @@ bool decode_frame_ffmpeg(VGMSTREAM* v) {
/* UTILS */
/* ******************************************** */
static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
void reset_ffmpeg(ffmpeg_codec_data* data) {
seek_ffmpeg(data, 0);
}
void seek_ffmpeg(ffmpeg_codec_data* data, int32_t num_sample) {
if (!data) return;
/* Start from 0 and discard samples until sample (slower but not too noticeable).
@ -875,11 +891,13 @@ static void seek_ffmpeg_internal(ffmpeg_codec_data* data, int32_t num_sample) {
avcodec_flush_buffers(data->codecCtx);
}
data->samples_consumed = 0;
data->samples_filled = 0;
data->samples_discard = num_sample;
data->read_packet = true;
data->end_of_stream = false;
data->end_of_audio = false;
data->read_packet = 1;
data->end_of_stream = 0;
data->end_of_audio = 0;
/* consider skip samples (encoder delay), if manually set */
if (data->skip_samples_set) {
@ -894,15 +912,6 @@ 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) {
if (data == NULL)
return;
@ -931,11 +940,10 @@ static void free_ffmpeg_config(ffmpeg_codec_data* data) {
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(void* priv_data) {
ffmpeg_codec_data* data = priv_data;
void free_ffmpeg(ffmpeg_codec_data* data) {
if (data == NULL)
return;
@ -989,9 +997,9 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data* data, int skip_samples) {
#endif
/* set skip samples with our internal discard */
data->skip_samples_set = true;
data->skip_samples = skip_samples;
data->skip_samples_set = 1;
data->samples_discard = skip_samples;
data->skip_samples = skip_samples;
}
/* returns channel layout if set */
@ -1018,7 +1026,8 @@ 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
* but FFmpeg doesn't do it automatically
* (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)
int channels = data->codecCtx->channels;
#else
@ -1028,12 +1037,10 @@ void ffmpeg_set_channel_remapping(ffmpeg_codec_data* data, int* channel_remap) {
if (channels > 32)
return;
for (int i = 0; i < channels; i++) {
for (i = 0; i < channels; i++) {
data->channel_remap[i] = channel_remap[i];
}
VGM_LOG("FFMPEG: channel remapping set\n");
data->channel_remap_set = true;
data->channel_remap_set = 1;
}
const char* ffmpeg_get_codec_name(ffmpeg_codec_data* data) {
@ -1052,7 +1059,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),
* 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 */
data->force_seek = true;
data->force_seek = 1;
reset_ffmpeg(data); /* reset state from trying to seek */
//stream = data->formatCtx->streams[data->stream_index];
}
@ -1060,16 +1067,7 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) {
void ffmpeg_set_invert_floats(ffmpeg_codec_data* data) {
if (!data)
return;
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;
data->invert_floats_set = 1;
}
const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) {
@ -1127,22 +1125,4 @@ STREAMFILE* ffmpeg_get_streamfile(ffmpeg_codec_data* data) {
if (!data) return NULL;
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

View file

@ -4,7 +4,7 @@
#ifdef VGM_USE_FFMPEG
typedef enum { MP4_KTAC, MP4_LYN } mp4_type_t;
typedef enum { MP4_STD, MP4_LYN } mp4_type_t;
/**
* 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;
}
static void add_u8b(m4a_header_t* h, uint32_t value) {
static void add_u8(m4a_header_t* h, uint32_t value) {
put_u8(h->out, value);
h->out += 0x01;
h->bytes += 0x01;
@ -77,7 +77,7 @@ static void save_atom(m4a_header_t* h, m4a_state_t* s) {
s->bytes = h->bytes;
}
static void mend_atom(m4a_header_t* h, m4a_state_t* s) {
static void load_atom(m4a_header_t* h, m4a_state_t* s) {
put_u32be(s->out, h->bytes - s->bytes);
}
@ -172,7 +172,6 @@ static void add_stts(m4a_header_t* h) {
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
static const int m4a_sample_rates[16] = {
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] = {
0,
@ -192,88 +191,64 @@ static const uint8_t m4a_channels[14] = {
};
static void add_esds(m4a_header_t* h) {
/* 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
* - 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 sr_index = 0;
uint8_t ch_index = 0;
uint8_t extra = 0;
for (int i = 0; i < 16; i++) {
if (m4a_sample_rates[i] == h->mp4->sample_rate) {
sr_index = i;
break;
/* 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
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */
{
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
uint8_t sr_index = 0;
uint8_t ch_index = 0;
uint8_t unknown = 0;
int i;
for (i = 0; i < 16; i++) {
if (m4a_sample_rates[i] == h->mp4->sample_rate) {
sr_index = i;
break;
}
}
}
for (int i = 0; i < 8; i++) {
if (m4a_channels[i] == h->mp4->channels) {
ch_index = i;
break;
for (i = 0; i < 8; i++) {
if (m4a_channels[i] == h->mp4->channels) {
ch_index = i;
break;
}
}
config |= (object_type & 0x1F) << 11; /* 5b */
config |= (sr_index & 0x0F) << 7; /* 4b */
config |= (ch_index & 0x0F) << 3; /* 4b */
config |= (unknown & 0x07) << 0; /* 3b */
}
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 |= (sr_index & 0x0F) << 7; /* 4b */
config |= (ch_index & 0x0F) << 3; /* 4b */
config |= (extra & 0x07) << 0; /* 3b */
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_atom(h, "esds", 0x33);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u8b (h, 0x03); /* ES_DescrTag */
add_u32b(h, 0x80808000 + descr_size); /* tag size (all subtags) */
add_u8 (h, 0x03); /* ES_DescrTag */
add_u32b(h, 0x80808022); /* size 0x22 */
add_u16b(h, 0x0000); /* stream Id */
add_u8b (h, 0x00); /* flags */
add_u8 (h, 0x00); /* flags */
add_u8b (h, 0x04); /* DecoderConfigDescrTag */
add_u32b(h, 0x80808000 + deccfg_size); /* subtag size */
add_u8b (h, 0x40); /* object type (0x40=audio) */
add_u8b (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
add_u8 (h, 0x04); /* DecoderConfigDescrTag */
add_u32b(h, 0x80808014); /* size 0x14 */
add_u8 (h, 0x40); /* object type (0x40=audio) */
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
add_u24b(h, 0x000000); /* buffer size */
add_u32b(h, 0); /* max bitrate (256000?)*/
add_u32b(h, 0); /* average bitrate (256000?) */
add_u8b (h, 0x05); /* DecSpecificInfoTag */
add_u32b(h, 0x80808000 + config_size); /* subtag size */
add_u8 (h, 0x05); /* DecSpecificInfoTag */
add_u32b(h, 0x80808002); /* size 0x02 */
add_u16b(h, config); /* actual decoder info */
// config for quad, abridged (see spec's program_config_element)
if (ch_index == 0 && h->mp4->channels == 4) {
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);
add_u8 (h, 0x06); /* SLConfigDescrTag */
add_u32b(h, 0x80808001); /* size 0x01 */
add_u8 (h, 0x02); /* predefined (2=default) */
}
static void add_mp4a(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "mp4a", 0x00);
add_atom(h, "mp4a", 0x57);
add_u32b(h, 0); /* ? */
add_u32b(h, 1); /* Data reference index */
add_u32b(h, 0); /* Reserved */
@ -284,18 +259,13 @@ static void add_mp4a(m4a_header_t* h) {
add_u16b(h, h->mp4->sample_rate); /* Sample rate */
add_u16b(h, 0); /* ? */
add_esds(h); /* elementary stream descriptor */
mend_atom(h, &s);
}
static void add_stsd(m4a_header_t* h) {
m4a_state_t s;
save_atom(h, &s);
add_atom(h, "stsd", 0x00);
add_atom(h, "stsd", 0x67);
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
add_u32b(h, 1); /* Number of entries */
add_mp4a(h);
mend_atom(h, &s);
}
static void add_stbl(m4a_header_t* h) {
@ -308,7 +278,7 @@ static void add_stbl(m4a_header_t* h) {
add_stsc(h); /* Sample-to-chunk */
add_stsz(h); /* Sample size */
add_stco(h); /* Chunk offset */
mend_atom(h, &s);
load_atom(h, &s);
}
static void add_dinf(m4a_header_t* h) {
@ -335,7 +305,7 @@ static void add_minf(m4a_header_t* h) {
add_smhd(h);
add_dinf(h);
add_stbl(h);
mend_atom(h, &s);
load_atom(h, &s);
}
static void add_hdlr(m4a_header_t* h) {
@ -368,7 +338,7 @@ static void add_mdia(m4a_header_t* h) {
add_mdhd(h);
add_hdlr(h);
add_minf(h);
mend_atom(h, &s);
load_atom(h, &s);
}
static void add_tkhd(m4a_header_t* h) {
@ -405,7 +375,7 @@ static void add_trak(m4a_header_t* h) {
add_atom(h, "trak", 0x00);
add_tkhd(h);
add_mdia(h);
mend_atom(h, &s);
load_atom(h, &s);
}
static void add_mvhd(m4a_header_t* h) {
@ -446,7 +416,7 @@ static void add_moov(m4a_header_t* h) {
add_mvhd(h);
add_trak(h);
//add_udta(h);
mend_atom(h, &s);
load_atom(h, &s);
}
/* *** */
@ -524,7 +494,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 */
switch(type) {
case MP4_KTAC: /* regular raw data */
case MP4_STD: /* regular raw data */
temp_sf = sf;
break;
case MP4_LYN: /* frames have size before them, but also a seek table */
@ -553,8 +523,8 @@ fail:
return NULL;
}
ffmpeg_codec_data* init_ffmpeg_mp4_custom_ktac(STREAMFILE* sf, mp4_custom_t* mp4) {
return init_ffmpeg_mp4_custom(sf, mp4, MP4_KTAC);
ffmpeg_codec_data* init_ffmpeg_mp4_custom_std(STREAMFILE* sf, mp4_custom_t* mp4) {
return init_ffmpeg_mp4_custom(sf, mp4, MP4_STD);
}
ffmpeg_codec_data* init_ffmpeg_mp4_custom_lyn(STREAMFILE* sf, mp4_custom_t* mp4) {

View file

@ -1,44 +1,26 @@
#include "coding.h"
#include "../base/decode_state.h"
#include "libs/clhca.h"
#include "../base/codec_info.h"
struct hca_codec_data {
STREAMFILE* sf;
clHCA_stInfo info;
void* buf;
float* fbuf;
int current_delay;
signed short* sample_buffer;
size_t samples_filled;
size_t samples_consumed;
size_t samples_to_discard;
void* data_buffer;
unsigned int current_block;
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. */
hca_codec_data* init_hca(STREAMFILE* sf) {
uint8_t header_buffer[0x1000]; /* hca header buffer data (probable max ~0x400) */
uint8_t header_buffer[0x2000]; /* hca header buffer data (probable max ~0x400) */
hca_codec_data* data = NULL; /* vgmstream HCA context */
int header_size;
int status;
@ -69,11 +51,11 @@ hca_codec_data* init_hca(STREAMFILE* sf) {
status = clHCA_getInfo(data->handle, &data->info); /* extract header info */
if (status < 0) goto fail;
data->buf = malloc(data->info.blockSize);
if (!data->buf) goto fail;
data->data_buffer = malloc(data->info.blockSize);
if (!data->data_buffer) goto fail;
data->fbuf = malloc(sizeof(float) * data->info.channelCount * data->info.samplesPerBlock);
if (!data->fbuf) goto fail;
data->sample_buffer = malloc(sizeof(signed short) * data->info.channelCount * data->info.samplesPerBlock);
if (!data->sample_buffer) goto fail;
/* load streamfile for reads */
data->sf = reopen_streamfile(sf, 0);
@ -89,64 +71,86 @@ fail:
return NULL;
}
static bool read_packet(VGMSTREAM* v) {
hca_codec_data* data = v->codec_data;
void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
int samples_done = 0;
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;
// 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
while (samples_done < samples_to_do) {
int bytes = read_streamfile(data->buf, offset, block_size, data->sf);
if (bytes != block_size) {
VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, block_size, (uint32_t)offset);
return false;
if (data->samples_filled) {
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_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++;
/* decode frame */
status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize);
if (status < 0) {
VGM_LOG("HCA: decode fail at %x, code=%i\n", (uint32_t)offset, status);
break;
}
/* extract samples */
clHCA_ReadSamples16(data->handle, data->sample_buffer);
data->samples_consumed = 0;
data->samples_filled += data->info.samplesPerBlock;
}
}
data->current_block++;
return true;
}
bool decode_frame_hca(VGMSTREAM* v) {
bool ok = read_packet(v);
if (!ok)
return false;
void reset_hca(hca_codec_data* data) {
if (!data) return;
decode_state_t* ds = v->decode_state;
hca_codec_data* data = v->codec_data;
const unsigned int block_size = data->info.blockSize;
/* decode frame */
int status = clHCA_DecodeBlock(data->handle, data->buf, block_size);
if (status < 0) {
VGM_LOG("HCA: decode fail, code=%i\n", status);
return false;
}
clHCA_ReadSamples(data->handle, data->fbuf);
int samples = 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;
clHCA_DecodeReset(data->handle);
data->current_block = 0;
data->samples_filled = 0;
data->samples_consumed = 0;
data->samples_to_discard = data->info.encoderDelay;
}
void seek_hca(VGMSTREAM* v, int32_t num_sample) {
hca_codec_data* data = v->codec_data;
//decode_state_t* ds = v->decode_state;
//TODO handle arbitrary seek points to block N
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,
* as actual files usually pad encoder delay so earliest loopStartBlock becomes 1-2,
@ -159,9 +163,20 @@ void seek_hca(VGMSTREAM* v, int32_t num_sample) {
}
data->current_block = data->info.loopStartBlock;
data->current_delay = data->info.loopStartDelay;
//ds->discard = data->info.loopStartDelay //overwritten on decode
data->samples_filled = 0;
data->samples_consumed = 0;
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) {
@ -220,14 +235,14 @@ static int test_hca_score(hca_codec_data* data, hca_keytest_t* hk) {
size_t bytes;
/* read and test frame */
bytes = read_streamfile(data->buf, offset, block_size, data->sf);
bytes = read_streamfile(data->data_buffer, offset, block_size, data->sf);
if (bytes != block_size) {
/* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */
//total_score = -1;
break;
}
score = clHCA_TestBlock(data->handle, data->buf, block_size);
score = clHCA_TestBlock(data->handle, data->data_buffer, block_size);
/* get first non-blank frame */
if (!hk->start_offset && score != 0) {
@ -300,13 +315,3 @@ void hca_set_encryption_key(hca_codec_data* data, uint64_t keycode, uint64_t sub
}
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,31 +1,19 @@
#include "coding.h"
#include "../base/decode_state.h"
#include "../base/codec_info.h"
#include "libs/ka1a_dec.h"
/* opaque struct */
typedef struct {
struct ka1a_codec_data {
uint8_t* buf;
float* fbuf;
int frame_size;
void* handle;
} ka1a_codec_data;
};
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* init_ka1a(int bitrate_mode, int channels_tracks) {
ka1a_codec_data* data = NULL;
int buf_size;
@ -51,7 +39,7 @@ fail:
return NULL;
}
static bool read_frame(VGMSTREAM* v) {
static bool read_ka1a_frame(VGMSTREAM* v) {
ka1a_codec_data* data = v->codec_data;
int bytes;
@ -84,8 +72,8 @@ static bool read_frame(VGMSTREAM* v) {
return true;
}
static bool decode_frame_ka1a(VGMSTREAM* v) {
bool ok = read_frame(v);
bool decode_ka1a_frame(VGMSTREAM* v) {
bool ok = read_ka1a_frame(v);
if (!ok)
return false;
@ -102,14 +90,13 @@ static bool decode_frame_ka1a(VGMSTREAM* v) {
return true;
}
static void reset_ka1a(void* priv_data) {
ka1a_codec_data* data = priv_data;
void reset_ka1a(ka1a_codec_data* data) {
if (!data || !data->handle) return;
ka1a_reset(data->handle);
}
static void seek_ka1a(VGMSTREAM* v, int32_t num_sample) {
void seek_ka1a(VGMSTREAM* v, int32_t num_sample) {
ka1a_codec_data* data = v->codec_data;
decode_state_t* ds = v->decode_state;
if (!data) return;
@ -148,10 +135,12 @@ static void seek_ka1a(VGMSTREAM* v, int32_t num_sample) {
#endif
}
const codec_info_t ka1a_decoder = {
.sample_type = SFMT_FLT,
.decode_frame = decode_frame_ka1a,
.free = free_ka1a,
.reset = reset_ka1a,
.seek = seek_ka1a,
};
void free_ka1a(ka1a_codec_data* data) {
if (!data) return;
if (data->handle)
ka1a_free(data->handle);
free(data->buf);
free(data->fbuf);
free(data);
}

View file

@ -329,16 +329,19 @@ int clHCA_getInfo(clHCA* hca, clHCA_stInfo *info) {
}
//HCADecoder_DecodeBlockInt32
void clHCA_ReadSamples16(clHCA* hca, short* samples) {
void clHCA_ReadSamples16(clHCA* hca, signed short *samples) {
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 */
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];
for (i = 0; i < HCA_SUBFRAMES; i++) {
for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) {
for (k = 0; k < hca->channels; k++) {
f = hca->channel[k].wave[i][j];
//f = f * hca->rva_volume; /* rare, won't apply for now */
int s = (signed int)(f * scale_f);
s = (signed int)(f * scale_f);
if (s > 32767)
s = 32767;
else if (s < -32768)
@ -349,20 +352,6 @@ void clHCA_ReadSamples16(clHCA* hca, 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

View file

@ -64,9 +64,7 @@ int clHCA_DecodeBlock(clHCA* hca, void* data, unsigned int size);
/* Extracts signed and clipped 16 bit samples into sample buffer.
* May be called after clHCA_DecodeBlock, and will return the same data until
* next decode. Buffer must be at least (samplesPerBlock*channels) long. */
void clHCA_ReadSamples16(clHCA* hca, short* samples);
void clHCA_ReadSamples(clHCA* hca, float* samples);
void clHCA_ReadSamples16(clHCA* hca, short* outSamples);
/* 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.

View file

@ -695,10 +695,7 @@ int TCompressWaveData_Rendering(TCompressWaveData* self, int16_t* buf, uint32_t
}
else { //in case of playback without loop
self->FPlay = CW_FALSE;
//return result; //exit
// OG lib returns error if requested more than avaiable, return partial buf here
break;
return result; //exit
}
}
@ -764,10 +761,10 @@ int TCompressWaveData_Rendering(TCompressWaveData* self, int16_t* buf, uint32_t
self->FWavePosition += WaveStep;
}
// remainder calcs
// depending on buffer length remainder may happen
// example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG
// in that case it appears as noise
//remainder calcs
//depending on buffer lenght remainder may happen
//example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG
// in that case appear as noise
if (Len % 8 == 4) {
TCompressWaveData_Rendering_WriteWave(self, &buf1, RVol, LVol);
}

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