Merge branch 'master' into v3.0-dev
commit
ab701df552
|
|
@ -46,14 +46,14 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20251216/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20260908/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
|
||||
- name: Configure freedv-gui (unsigned)
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
mkdir build_windows
|
||||
cd build_windows
|
||||
cmake -DENABLE_LTO=1 -DPGO_INSTRUMENT=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/cross-compile/freedv-mingw-llvm-${{ matrix.arch }}.cmake ..
|
||||
|
|
@ -62,7 +62,7 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build_windows
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
make -j$(nproc) package
|
||||
|
||||
- name: Calculate installer filename
|
||||
|
|
@ -125,14 +125,14 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20251216/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20260908/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
|
||||
- name: Configure freedv-gui (unsigned, ${{ matrix.sanitizer_name }})
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
mkdir build_windows
|
||||
cd build_windows
|
||||
cmake ${{ matrix.sanitizer_flag }} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/cross-compile/freedv-mingw-llvm-${{ matrix.arch }}.cmake ..
|
||||
|
|
@ -141,7 +141,7 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build_windows
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
make -j$(nproc) package
|
||||
|
||||
- name: Calculate installer filename
|
||||
|
|
@ -401,12 +401,12 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20251216/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
wget https://github.com/mstorsjo/llvm-mingw/releases/download/20260908/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
tar xvf llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64.tar.xz
|
||||
|
||||
- name: Merge profile data
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
llvm-profdata merge -output ${{ github.workspace }}/code.profdata ${{ github.workspace }}/*.profraw
|
||||
|
||||
- name: Configure code signing tunnel
|
||||
|
|
@ -460,7 +460,7 @@ jobs:
|
|||
if: ${{ env.FREEDV_WINDOWS_CODE_SIGNING_SSH_KEY == '' }}
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:$PATH
|
||||
mkdir build_windows
|
||||
cd build_windows
|
||||
cmake -DENABLE_LTO=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPGO_USE_PROFILE=${{github.workspace}}/code.profdata -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/cross-compile/freedv-mingw-llvm-${{ matrix.arch }}.cmake ..
|
||||
|
|
@ -470,7 +470,7 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
mkdir build_windows
|
||||
cd build_windows
|
||||
cmake -DENABLE_LTO=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DPGO_USE_PROFILE=${{github.workspace}}/code.profdata -DSIGN_WINDOWS_BINARIES=1 -DPKCS11_MODULE=${{github.workspace}}/pkcs11-proxy/build/libpkcs11-proxy.so -DPKCS11_CERTIFICATE_FILE=${{github.workspace}}/freedv.cert -DPKCS11_KEY_FILE=${{github.workspace}}/freedv.key -DPKCS11_SECONDARY_CERTIFICATE_FILE=${{github.workspace}}/freedv-secondary.cert -DPKCS11_SECONDARY_KEY_FILE=${{github.workspace}}/freedv-secondary.key -DINTERMEDIATE_CERT_FILE=${{github.workspace}}/freedv.intermediatecerts -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/cross-compile/freedv-mingw-llvm-${{ matrix.arch }}.cmake ..
|
||||
|
|
@ -480,7 +480,7 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build_windows
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
make -j$(nproc) package
|
||||
|
||||
- name: Build freedv-gui (signed)
|
||||
|
|
@ -488,7 +488,7 @@ jobs:
|
|||
shell: bash
|
||||
working-directory: ${{github.workspace}}/build_windows
|
||||
run: |
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20251216-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
export PATH=${{github.workspace}}/llvm-mingw-20260908-ucrt-ubuntu-22.04-x86_64/bin:${{github.workspace}}/osslsigncode/build:$PATH
|
||||
ssh -fN -o "ServerAliveInterval 60" -p ${{ secrets.FREEDV_WINDOWS_CODE_SIGNING_SSH_PORT }} -i ${{github.workspace}}/code-sign.sshkey -L localhost:2222:127.0.0.1:2222 ${{ secrets.FREEDV_WINDOWS_CODE_SIGNING_SSH_USER }}@${{ secrets.FREEDV_WINDOWS_CODE_SIGNING_SSH_HOST }}
|
||||
make -j$(nproc) package
|
||||
killall ssh
|
||||
|
|
|
|||
|
|
@ -22,28 +22,29 @@ set(PROJECT_HOMEPAGE_URL "https://freedv.org")
|
|||
# Makes FreeDV overridden CMake platform available for ARM MinGW builds.
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
|
||||
|
||||
# NOTE: APPLE/WIN32/UNIX aren't reliably set until after the first
|
||||
# project()/enable_language() call, so the platform-specific language
|
||||
# list can't be chosen via if(APPLE) beforehand -- when cross-compiling
|
||||
# for Windows from a macOS host, APPLE would (incorrectly) reflect the
|
||||
# host platform at that point rather than the CMAKE_SYSTEM_NAME from the
|
||||
# toolchain file. Always declare C/CXX first, then add OBJCXX afterward
|
||||
# once APPLE reflects the actual target.
|
||||
project(
|
||||
${PROJECT_NAME}
|
||||
VERSION ${PROJECT_VERSION}
|
||||
DESCRIPTION ${PROJECT_DESCRIPTION}
|
||||
HOMEPAGE_URL ${PROJECT_HOMEPAGE_URL}
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
project(
|
||||
${PROJECT_NAME}
|
||||
VERSION ${PROJECT_VERSION}
|
||||
DESCRIPTION ${PROJECT_DESCRIPTION}
|
||||
HOMEPAGE_URL ${PROJECT_HOMEPAGE_URL}
|
||||
LANGUAGES C CXX OBJCXX
|
||||
)
|
||||
enable_language(OBJCXX)
|
||||
|
||||
# Workaround for Xcode 15 bug preventing FreeDV binaries from starting
|
||||
# on older versions of macOS. See https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#Linking.
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0.0.0)
|
||||
add_link_options("-Wl,-ld_classic")
|
||||
endif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0.0.0)
|
||||
else(APPLE)
|
||||
project(
|
||||
${PROJECT_NAME}
|
||||
VERSION ${PROJECT_VERSION}
|
||||
DESCRIPTION ${PROJECT_DESCRIPTION}
|
||||
HOMEPAGE_URL ${PROJECT_HOMEPAGE_URL}
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
endif(APPLE)
|
||||
|
||||
if(NOT DEFINED LINUX)
|
||||
|
|
|
|||
|
|
@ -867,8 +867,11 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
|
|||
* Fix potential equalizer bug that could introduce corrupted audio. (PR #1480)
|
||||
* Fix uninitialized value read during waterfall plot render. (PR #1481)
|
||||
* Match std::atomic memory ordering to what each atomic actually synchronises. (PR #1482)
|
||||
* Remove use of mutexes in audio path during callsign decode. (PR #1486)
|
||||
2. Other:
|
||||
* Remove use of mutexes in audio path during callsign decode. (PR #1486, #1493)
|
||||
* Tighten audio thread timings on macOS and Windows. (PR #1494)
|
||||
2. Build system:
|
||||
* Windows versions are now built with llvm-mingw 20260908 (PR #1489)
|
||||
3. Other:
|
||||
* Waterfall and other plot performance improvements. (PR #1481)
|
||||
* Windows audio thread timing improvements. (PR #1488)
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ thread_local int MacAudioDevice::CurrentCoreAudioId_ = 0;
|
|||
|
||||
// Conversion factors.
|
||||
constexpr static int MS_TO_SEC = 1000;
|
||||
constexpr static int MS_TO_NSEC = 1000000;
|
||||
|
||||
// The I/O interval time in seconds.
|
||||
constexpr static int AUDIO_SAMPLE_BLOCK_MSEC = 20;
|
||||
|
|
@ -714,7 +713,7 @@ void MacAudioDevice::setHelperRealTime()
|
|||
|
||||
// Define constants determining how much time the audio thread can
|
||||
// use in a given time quantum. All times are in milliseconds.
|
||||
const double kTimeQuantum = 60; // 60ms, 1/2 of a RADEV1 block and confirmed to be sufficient with Instruments analysis.
|
||||
const double kTimeQuantum = 60; // 60ms, calculated by AUDIO_SAMPLE_BLOCK_WIRELESS_MSEC / kGuaranteedAudioDutyCycle + a bit extra.
|
||||
|
||||
// Time guaranteed each quantum.
|
||||
const double kAudioTimeNeeded = kGuaranteedAudioDutyCycle * kTimeQuantum;
|
||||
|
|
@ -911,27 +910,32 @@ void MacAudioDevice::startRealTimeWork()
|
|||
|
||||
void MacAudioDevice::stopRealTimeWork(bool fastMode)
|
||||
{
|
||||
int64_t timeToWaitMilliseconds = ((1000 * chosenFrameSize_) / sampleRate_) >> (fastMode ? 1 : 0);
|
||||
int64_t nominalUs = ((1000000LL * chosenFrameSize_) / sampleRate_) >> (fastMode ? 1 : 0);
|
||||
|
||||
// If last cycle's total (processing + wait, measured from
|
||||
// startRealTimeWork() above) ran longer than its nominal period, shave
|
||||
// that overrun off this cycle's wait -- otherwise every cycle where
|
||||
// processing takes nonzero time makes the loop's average period longer
|
||||
// than intended, drifting later relative to real time instead of
|
||||
// self-correcting. Matches WASAPIAudioDevice/PulseAudioDevice, which
|
||||
// had this already; this device previously didn't.
|
||||
timeToWaitMilliseconds -= extraTimeMs_;
|
||||
if (timeToWaitMilliseconds <= 0)
|
||||
// Compensate for how much of the period THIS cycle's own processing
|
||||
// already used, measured directly against startTime_ (set by
|
||||
// startRealTimeWork() right before processing began) rather than a debt
|
||||
// figure copied from the *previous* cycle. The previous approach lagged
|
||||
// by one cycle: it corrected this wait for last cycle's overrun instead
|
||||
// of this cycle's own, which overcorrects/undercorrects whenever
|
||||
// processing time varies cycle to cycle instead of holding steady.
|
||||
// waitOvershootUs_ separately tracks only the wait itself running long
|
||||
// (the one thing that genuinely can't be known until after it happens),
|
||||
// so a systematic scheduling overshoot still can't accumulate into
|
||||
// drift. Matches WASAPIAudioDevice's stopRealTimeWork() fix (36db96e5).
|
||||
auto elapsedUs = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - startTime_).count();
|
||||
int64_t waitUs = nominalUs - elapsedUs - waitOvershootUs_;
|
||||
if (waitUs <= 0)
|
||||
{
|
||||
extraTimeMs_ = 0;
|
||||
waitOvershootUs_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_semaphore_wait(sem_, dispatch_time(DISPATCH_TIME_NOW, MS_TO_NSEC * timeToWaitMilliseconds));
|
||||
auto waitStartTime = std::chrono::steady_clock::now();
|
||||
dispatch_semaphore_wait(sem_, dispatch_time(DISPATCH_TIME_NOW, 1000 * waitUs));
|
||||
|
||||
auto endTime = std::chrono::steady_clock::now();
|
||||
auto durationUs = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime_).count() - (1000 * timeToWaitMilliseconds);
|
||||
extraTimeMs_ = std::max((int64_t)0, (durationUs + 500) / 1000); // round to nearest ms, floor at 0.
|
||||
auto actualWaitUs = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - waitStartTime).count();
|
||||
waitOvershootUs_ = std::max((int64_t)0, actualWaitUs - waitUs); // cap at >= 0; an early (semaphore) wake isn't overshoot.
|
||||
}
|
||||
|
||||
void MacAudioDevice::clearHelperRealTime()
|
||||
|
|
|
|||
|
|
@ -88,12 +88,12 @@ private:
|
|||
int chosenFrameSize_;
|
||||
std::atomic<int> numRealTimeWorkers_;
|
||||
|
||||
// For handling additional wakeup time after semaphore timeout, matching
|
||||
// WASAPIAudioDevice/PulseAudioDevice: if last cycle's total (processing
|
||||
// + wait) ran long, shave that overrun off this cycle's wait so the
|
||||
// average loop period stays locked to the nominal rate instead of
|
||||
// drifting later every cycle that processing takes nonzero time.
|
||||
int64_t extraTimeMs_ = 0;
|
||||
// Tracks only how long the *wait itself* overshot its requested duration
|
||||
// last cycle (microseconds), separate from processing time -- which
|
||||
// stopRealTimeWork() measures directly each cycle against startTime_
|
||||
// rather than inferring it from this. See stopRealTimeWork() for why;
|
||||
// matches WASAPIAudioDevice's equivalent fields/fix (36db96e5).
|
||||
int64_t waitOvershootUs_ = 0;
|
||||
std::chrono::time_point<std::chrono::steady_clock> startTime_;
|
||||
|
||||
void stopImpl_();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <cmath>
|
||||
#include <avrt.h>
|
||||
#include <timeapi.h>
|
||||
#include <inttypes.h>
|
||||
|
|
@ -332,7 +333,7 @@ void WASAPIAudioDevice::start()
|
|||
// Allocate temporary buffer
|
||||
tmpBuf_ = new short[sampleRate_];
|
||||
assert(tmpBuf_ != nullptr);
|
||||
memset(tmpBuf_, 0, bufferFrameCount_ * numChannels_ * sizeof(short));
|
||||
memset(tmpBuf_, 0, sizeof(short) * sampleRate_);
|
||||
|
||||
if (direction_ == IAudioEngine::AUDIO_ENGINE_OUT)
|
||||
{
|
||||
|
|
@ -575,6 +576,21 @@ void WASAPIAudioDevice::setHelperRealTime()
|
|||
if (HelperTask_ == nullptr)
|
||||
{
|
||||
log_warn("Could not increase thread priority");
|
||||
return;
|
||||
}
|
||||
|
||||
// AvSetMmThreadCharacteristics() alone only enrolls the thread in the
|
||||
// "Pro Audio" MMCSS class at that class's default (Normal) priority
|
||||
// band. This thread's wait in stopRealTimeWork() is on the critical
|
||||
// path for audio timing (it's what TxRxThread's wait/TX/RX stats
|
||||
// measure), so bump it to the top of the band to cut down on how long
|
||||
// it sits ready-but-not-running behind other MMCSS-scheduled threads
|
||||
// after the semaphore/timer wakes it -- that scheduling delay is what
|
||||
// shows up as wait jitter (stdev/max) rather than the wait target
|
||||
// itself being wrong.
|
||||
if (!AvSetMmThreadPriority(HelperTask_, AVRT_PRIORITY_CRITICAL))
|
||||
{
|
||||
log_warn("Could not raise MMCSS thread priority to critical (err = %lu)", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ void WASAPIAudioDevice::copyFloatToShort_(T* source, int numFrames)
|
|||
{
|
||||
for (int innerIndex = 0; innerIndex < numChannels_; innerIndex++)
|
||||
{
|
||||
tmpBuf_[index * numChannels_ + innerIndex] = source[index * numChannels_ + innerIndex] * std::numeric_limits<short>::max();
|
||||
tmpBuf_[index * numChannels_ + innerIndex] = source[index * numChannels_ + innerIndex] * (std::numeric_limits<short>::max());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ void WASAPIAudioDevice::copyShortToFloat_(T* dest, int numFrames)
|
|||
{
|
||||
for (int innerIndex = 0; innerIndex < numChannels_; innerIndex++)
|
||||
{
|
||||
dest[index * numChannels_ + innerIndex] = (T)tmpBuf_[index * numChannels_ + innerIndex] / std::numeric_limits<short>::max();
|
||||
dest[index * numChannels_ + innerIndex] = (T)tmpBuf_[index * numChannels_ + innerIndex] / (std::numeric_limits<short>::max());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ void FreeDVInterface::OnRadeTextRx_(rade_text_t, const char* txt_ptr, int, void*
|
|||
|
||||
char tmpBuf[RELIABLE_TEXT_FIFO_SIZE];
|
||||
memset(tmpBuf, 0, RELIABLE_TEXT_FIFO_SIZE);
|
||||
strncpy(tmpBuf, txt_ptr, RELIABLE_TEXT_FIFO_SIZE);
|
||||
strncpy(tmpBuf, txt_ptr, RELIABLE_TEXT_FIFO_SIZE - 1);
|
||||
obj->reliableTextFifo_.write(tmpBuf, RELIABLE_TEXT_FIFO_SIZE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
//==========================================================================
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include "os/os_interface.h"
|
||||
|
|
|
|||
Loading…
Reference in New Issue