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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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.
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>
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>
External artwork already supported the HEIC format, just not the correct
filename extension for the format.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
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>
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>
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>
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>
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>
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>
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>
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>
The DSPs should not be deinitialized from another thread, possibly while
they are currently processing.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Plist generator now emits the output to the temporary folder, which we
have write permission to.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Add dealloc function to close the file container, in case the caller
neglected to do so on its own.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Refine the output function a bit, including adding some minor safety
checks, in case the caller requests zero samples, or requests a format
with zero channels.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Move the Core Audio output function block to its own declarative
function, so that its block variables are isolated, and so that debug
traces show up in a more sensible place.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This code turned out to be somewhat of a mistake to employ, so it's now
being removed, and shall not be re-added, as it doesn't really work.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Add play count data collection, including first seen times for every
file first added to the playlist. Data is indexed by album, artist, and
title, or by filename, whichever matches first. Add interfaces to
AppleScript automation definition as well.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
As it doesn't seem to work properly on Intel machines, anyway. It just
leads to pointless crashes, and doesn't seem to serve any purpose.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Restrict the use of workgroup joining and workgroup intervals to macOS
Monterey or newer, as it seems the way I use it, it's completely broken
on macOS Big Sur, which was the original minimum target for the API.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Apply changes to exit the thread if workgroup initialization or joining
fails, instead of attempting to continue executing the thread.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
Add an extra step to the workgroup exit call, so that it only calls to
leave if the join token is valid, or at least initialized.
Signed-off-by: Christopher Snowhill <kode54@gmail.com>