Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FreeDV on Windows ARM64 #278

Closed
k8wu opened this issue Sep 13, 2022 · 28 comments
Closed

FreeDV on Windows ARM64 #278

k8wu opened this issue Sep 13, 2022 · 28 comments

Comments

@k8wu
Copy link

k8wu commented Sep 13, 2022

Hello,

I have a Microsoft Surface Pro X tablet with an SQ2 (ARMv8) CPU, and it runs Windows 11 Pro. Of course, this configuration is an outlier in terms of what most hams (or even most people in general) would use for a Windows machine, but it's what I bought to replace a very old Dell Latitude laptop that I used to use for Amateur Radio purposes. I've been slowly working through the open source projects that I use to get native ARM64 versions built. So far, I've been successful with Fldigi and DOSBox (which I use for an old Yagi modeling program), but of course, the more complicated the software is, the harder it is to compile for this platform.

Because there does not exist a GCC-based toolchain to target aarch64-w64-mingw32, I am instead using the LLVM-based one that is developed here. I have a Debian Bullseye installation running via WSL2, and I use a separate chroot with that installation for each project that I want to build. The aarch64-w64-mingw32 toolchain is available in each one.

I am able to manually build a somewhat-working FreeDV executable for Windows on ARM64. I build the pre-requisites (Hamlib, SpeexDSP, wxWidgets 3.1, libsndfile, libsamplerate, and PortAudio), and install them into /usr/local in the chroot, since I don't care about /usr/local getting junked up with cross-compiled libraries because I won't be running any compiled software within it. I then check out freedv-gui, then go into that directory and check out LPCNet and codec2. LPCNet is then built with automatic optimizations disabled and NEON manually enabled (which as I understand it would happen anyway because NEON optimizations are required for AArch64). A "native" build of codec2 is then performed just to get the generate_codebook executable to be available to the cross-compilation process the second time through, and LPCNet is referenced on that cross-compilation build.

At this point, we can then build freedv-gui by running this twice (for some reason, the cmake process fails the first time around): cmake -D CFLAGS="-I/usr/local/include -lws2_32" -D LDFLAGS="-L/usr/local/lib -lws2_32" -D CMAKE_C_COMPILER=aarch64-w64-mingw32-clang -D CMAKE_CXX_COMPILER=aarch64-w64-mingw32-clang++ -D CMAKE_RC_COMPILER=aarch64-w64-mingw32-windres -D CMAKE_SKIP_RPATH=1 -D UNITTEST=0 -D LPCNET_BUILD_DIR=/usr/src/freedv-gui/LPCNet/build_windows -D CODEC2_BUILD_DIR=/usr/src/freedv-gui/codec2/build_windows -D PORTAUDIO_LIBRARY=/usr/local/lib/libportaudio.dll.a -D PORTAUDIO_LIBRARY_DIR=/usr/local/lib -D LIBSAMPLERATE=/usr/local/lib/libsamplerate.dll.a -D LIBSNDFILE=/usr/local/lib/libsndfile.dll.a -D WXCONFIG=/usr/local/bin/wx-config -D wxWidgets_LIBRARIES=/usr/local/lib ..

We have to manually edit the src/CMakeFiles/freedv.dir/link.txt file as follows:
i. Before "-lm", put the following: /usr/local/lib/libwx_baseu-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_baseu_net-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_baseu_xml-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_adv-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_aui-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_core-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_gl-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_html-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_media-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_propgrid-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_qa-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_ribbon-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_richtext-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_stc-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_webview-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_xrc-3.1-aarch64-w64-mingw32.dll.a
ii. After "-lm", put -lpthread -lws2_32

We can then run make to build the program, and then copy the resulting .exe file and its dependent .dll files to a Windows-accessible directory to run.

All this is to say that things work as long as you don't need the 2020 or 2020B modes, but of course, those are why I am interested in FreeDV in the first place, so not having them is a definite deal-breaker. As I understand it by looking at the code, even if LPCNet is compiled with any kind of optimizations in place, freedv-gui uses the checkAvxSupport() function to determine whether to enable the 2020 modes, and it looks like running on Windows will always result in that function returning false. I made a local modification to cause checkAvxSupport() to return true if the platform was "aarch64" regardless of OS (i.e. removing the bit about checking for the macOS platform), and while it does enable those modes, attempting to use them results in a program crash, and I have not yet determined where it's failing since it doesn't echo anything to standard output when it happens.

The question then becomes: Is there a better way to enable these modes on Windows on ARM64?

@tmiw
Copy link
Collaborator

tmiw commented Sep 13, 2022

I actually have an ARM Mac with Parallels installed so I should be able to test this. Would you be able to provide more details on how you built the various dependencies (e.g. what you specified for ./configure for wxWidgets, etc.)?

@k8wu
Copy link
Author

k8wu commented Sep 13, 2022

Sure! Here's how we set up the chroot (I use WSL on Windows, but this can also obviously apply for setting up a virtual instance):

In Windows:
1. Install Debian GNU/Linux (or Ubuntu Linux, if you prefer) for WSL from the MS Store
2. Set up Debian (Ubuntu, whatever) in WSL and make sure it's in WSL 2 mode
3. Execute a shell in this new environment

In WSL:
1. Get some necessary tools for this system: sudo apt update && sudo apt install debootstrap rsync
2. Set up minimal Debian installation for chroot: sudo debootstrap stable /opt/aarch64-w64-mingw32
3. Go to https://github.com/mstorsjo/llvm-mingw/releases and get the package named llvm-mingw-YYYYMMDD-ucrt-ubuntu-YY.MM-aarch64.tar.xz (the latest one - at the time of this writing, it was llvm-mingw-20220323-ucrt-ubuntu-18.04-aarch64.tar.xz)
4. Unpack this package somewhere: tar -xJvf llvm-mingw-YYYYMMDD-ucrt-ubuntu-YY.MM-aarch64.tar.xz
5. Enter that directory and run this command to get the cross-compilation toolchain into the chroot: rsync -avr . ../aarch64-w64-mingw32/usr/local/cross-tools/aarch64-w64-mingw32/
6. Mount the /proc and /sys special directories:
a. Proc: mount -t proc none /opt/aarch64-w64-mingw32/proc
b. Sysfs: mount -t sysfs none /opt/aarch64-w64-mingw32/sys
7. Chroot into the chroot directory: chroot /opt/aarch64-w64-mingw32

In the chroot (setup):
1. Get some necessary tools for this system: apt update && apt install locales cmake make autoconf file python3
2. In order to get the warnings about locales to go away, run this command and select "en_US.UTF-8" or whatever your preferred locale is so it will compile that: dpkg-reconfigure locales
3. Add the cross-compilation toolchain's bin directory to the path: PATH=/usr/local/cross-tools/aarch64-w64-mingw32/bin:$PATH
4. Change directory to /usr/src

Here's how we get the prerequisites compiled:

  1. Hamlib
    a. If a systemwide GCC and/or G++ is installed in this chroot (i.e. if it got pulled in with something else that was installed up to this point), it will cause compilation to fail due to incorrectly including ioctl() calls, which are not supported on Windows. Run this command to get rid of it: apt purge gcc gcc-10 g++ g++-10
    b. Go to https://github.com/Hamlib/Hamlib/releases and download the source code for the latest version (4.4 at the time of this writing), and extract it under /usr/src
    c. Modify the configure file as follows:
    i. On the line for WINLDFLAGS, remove the string -Wl,--add-stdcall-alias
    d. Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 --without-cxx-binding && make -j4 install
  2. libsndfile
    a. Go to https://github.com/libsndfile/libsndfile/releases and download the latest stable version of libsndfile (1.1.0 at the time of this writing), and extract it under /usr/src
    b. Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 && make -j4 install
  3. libsamplerate
    a. Go to https://github.com/libsndfile/libsamplerate/releases/ and download the source code for the latest version (0.2.2 at the time of this writing), and extract it under /usr/src
    b. Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 && make -j4 install
  4. PortAudio
    a. Go to http://files.portaudio.com/download.html and download the source code for the latest version (19.7.0 at the time of this writing), and extract it under /usr/src
    b. Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 && make -j4 install
  5. SpeexDSP
    a. Go to https://www.speex.org/downloads/ and download the source code for the latest version (1.2.1 at the time of this writing), and extract it under /usr/src
    b. Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 --disable-neon && make -j4 install
  6. wxWidgets
    a. Go to https://github.com/wxWidgets/wxWidgets/releases and download the source code for the latest 3.1.x version (3.1.7 at the time of this writing), and extract it under /usr/src
    Change into the extracted directory and execute the following command to get everything built and installed: ./configure --host=aarch64-w64-mingw32 && make -j4 install

Here's how we get FreeDV (freedv-gui with LPCNet and codec2) compiled:

  1. FreeDV-GUI
    a. Change to the /usr/src directory and clone the freedv-gui repository: git clone https://github.com/drowe67/freedv-gui.git
    b. Change into the freedv-gui directory and run the following commands to clone the LPCNet and codec2 repositories: git clone https://github.com/drowe67/LPCNet.git && git clone https://github.com/drowe67/codec2.git
    c. Follow these steps to get the in-tree LPCNet and codec2 dependencies built:
    i. LPCNet
    i. Change into the LPCNet directory and underneath that, create a build_windows directory, then change into it and run this command: cmake -D CMAKE_C_COMPILER=aarch64-w64-mingw32-clang -D CMAKE_SKIP_RPATH=1 -D DISABLE_CPU_OPTIMIZATION=ON -D NEON=ON ..
    ii. Edit the src/CMakeFiles/lpcnetfreedv.dir/link.txt file and remove the part where it mentions "soname"
    iii. Run make -j4 to get the library built
    ii. codec2
    i. Change into the codec2 directory and underneath that, create a build_windows directory, then change into it and run this command to get the codebook generator built (which has to be native): cmake .. && make -j4
    ii. Run this command to set up the actual cross-compilation build: cmake -D CMAKE_C_COMPILER=aarch64-w64-mingw32-clang -D CMAKE_SKIP_RPATH=1 -D LPCNET_BUILD_DIR=/usr/src/freedv-gui/LPCNet/build_windows ..
    iii. Edit the src/CMakeFiles/codec2.dir/link.txt and src/CMakeFiles/codec2.dir/relink.txt files and remove the parts where they mention "soname"
    iv. Run this command to remove rpath references:
    for file in $(grep -r rpath | cut -d":" -f1); do sed -i -e s^-Wl ,-rpath,/usr/src/freedv-gui/codec2/build_windows/src^^g $file; done
    v. Run make -j4 to get the library built
    d. Change back up to the freedv-gui directory, create a subdirectory called "build_windows", change into it, and execute the following command twice to set up the build: cmake -D CFLAGS="-I/usr/local/include -lws2_32" -D LDFLAGS="-L/usr/local/lib -lws2_32" -D CMAKE_C_COMPILER=aarch64-w64-mingw32-clang -D CMAKE_CXX_COMPILER=aarch64-w64-mingw32-clang++ -D CMAKE_RC_COMPILER=aarch64-w64-mingw32-windres -D CMAKE_SKIP_RPATH=1 -D UNITTEST=0 -D LPCNET_BUILD_DIR=/usr/src/freedv-gui/LPCNet/build_windows -D CODEC2_BUILD_DIR=/usr/src/freedv-gui/codec2/build_windows -D PORTAUDIO_LIBRARY=/usr/local/lib/libportaudio.dll.a -D PORTAUDIO_LIBRARY_DIR=/usr/local/lib -D LIBSAMPLERATE=/usr/local/lib/libsamplerate.dll.a -D LIBSNDFILE=/usr/local/lib/libsndfile.dll.a -D WXCONFIG=/usr/local/bin/wx-config -D wxWidgets_LIBRARIES=/usr/local/lib ..
    e. Edit the src/CMakeFiles/freedv.dir/link.txt file as follows:
    i. Before -lm, put the following: /usr/local/lib/libwx_baseu-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_baseu_net-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_baseu_xml-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_adv-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_aui-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_core-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_gl-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_html-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_media-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_propgrid-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_qa-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_ribbon-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_richtext-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_stc-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_webview-3.1-aarch64-w64-mingw32.dll.a /usr/local/lib/libwx_mswu_xrc-3.1-aarch64-w64-mingw32.dll.a
    ii. After -lm, put -lpthread -lws2_32
    f. Run this command to compile the program: make -j4
    g. Copy the program to a destination directory accessible in Windows, and also copy the following DLLs (you can use find /usr -name _dllname_ to locate them if you're having trouble)
    i. libc++.dll
    ii. libcodec2.so.1.0
    iii. libhamlib-4.dll
    iv. liblpcnetfreedv.so.0.4
    v. libportaudio-2.dll
    vi. libsamplerate-0.dll
    vii. libsndfile-1.dll
    viii. libspeexdsp-1.dll
    ix. libunwind.dll
    x. libwinpthread-1.dll
    xi. wxbase317u_gcc_custom.dll
    xii. wxbase317u_net_gcc_custom.dll
    xiii. wxbase317u_xml_gcc_custom.dll
    xiv. wxmsw317u_adv_gcc_custom.dll
    xv. wxmsw317u_aui_gcc_custom.dll
    xvi. wxmsw317u_core_gcc_custom.dll
    xvii. wxmsw317u_gl_gcc_custom.dll
    xviii. wxmsw317u_html_gcc_custom.dll
    xix. wxmsw317u_media_gcc_custom.dll
    xx. wxmsw317u_propgrid_gcc_custom.dll
    xxi. wxmsw317u_qa_gcc_custom.dll
    xxii. wxmsw317u_ribbon_gcc_custom.dll
    xxiii. wxmsw317u_richtext_gcc_custom.dll
    xxiv. wxmsw317u_stc_gcc_custom.dll
    xxv. wxmsw317u_webview_gcc_custom.dll
    xxvi. wxmsw317u_xrc_gcc_custom.dll

There may be some weird issues that arise, but I think I got every step that I took to get this to even compile in its present state on this platform.

@tmiw
Copy link
Collaborator

tmiw commented Sep 14, 2022

I think I was able to generate something that works for 2020/2020B. I used your instructions all the way through the dependencies, then did the following:

  1. Create a file called mingw32.cmake inside /usr/src/freedv-gui:
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(triple aarch64-w64-mingw32)

set(CMAKE_C_COMPILER ${triple}-clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER ${triple}-clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
  1. For LPCNet: cmake -DCMAKE_TOOLCHAIN_FILE=/usr/src/freedv-gui/mingw32.cmake .. && make -j4. It wasn't necessary to remove rpaths or soname references.
  2. For codec2: cmake -DCMAKE_TOOLCHAIN_FILE=/usr/src/freedv-gui/mingw32.cmake -DLPCNET_BUILD_DIR=/usr/src/freedv-gui/LPCNet/build_windows .. Again, no need to remove rpaths or soname references.
  3. For freedv-gui:
    a. Modify the top level CMakeLists.txt to comment out set(USE_STATIC_PORTAUDIO TRUE).
    b. Modify src/CMakeLists.txt to remove the reference to usb-1.0.
    c. Run cmake -DCMAKE_TOOLCHAIN_FILE=/usr/src/freedv-gui/mingw32.cmake -DLPCNET_BUILD_DIR=/usr/src/freedv-gui/LPCNet/build_windows -DCODEC2_BUILD_DIR=/usr/src/freedv-gui/codec2/build_windows -DPORTAUDIO_LIBRARY=/usr/local/lib/libportaudio.dll.a -DPORTAUDIO_LIBRARY_DIR=/usr/local/lib .. && make -j4
  4. From here I was able to copy all required DLL files and freedv.exe off of the Debian VM onto my Windows 11 VM.

Anyway, I also made the same change to force isAvxEnabled to be true and I seem to be able to decode the sample 2020 .wav file with no problem using these steps. Can you confirm whether this set of instructions works for you too?

@k8wu
Copy link
Author

k8wu commented Sep 14, 2022

Very good! At the least, the build process has been simplified somewhat for the FreeDV-related items.

I followed your instructions and did new builds of the LPCNet, codec2, and freedv-gui projects, then copied the resultant EXE and DLL files to my Windows-accessible location and ran the program. Here is what it shows - 2020 is greyed out, but 2020B is available:

image

Upon attempting to select the 2020B mode, we get hit with an assertion error, but at least it's caught and handled without killing the program:

image

I was able to decode the sample WAV files in the LPCNet wav directory using the Windows versions of the command-line tools that were compiled along the way (c2demo.exe, in particular). I'm not sure which ones are 2020 or 2020B.

EDIT: Oops, I missed the bit about the modification in freedv-gui/src/util.cpp. I did that, and now I can select the 2020 and 2020B modes without it bombing out with an assertion failure error. I will try some on-air tests later today.

@tmiw
Copy link
Collaborator

tmiw commented Sep 14, 2022

I followed your instructions and did new builds of the LPCNet, codec2, and freedv-gui projects, then copied the resultant EXE and DLL files to my Windows-accessible location and ran the program. Here is what it shows - 2020 is greyed out, but 2020B is available:

...

Upon attempting to select the 2020B mode, we get hit with an assertion error, but at least it's caught and handled without killing the program:

Yeah, that's definitely a bug. I opened #279 to resolve that particular issue. 👍

I was able to decode the sample WAV files in the LPCNet wav directory using the Windows versions of the command-line tools that were compiled along the way (c2demo.exe, in particular). I'm not sure which ones are 2020 or 2020B.

I was testing by going to Tools->Start Play File - From Radio and selecting the files from https://github.com/drowe67/freedv-gui/tree/master/wav. CPU usage in the Parallels VM seemed to top out at 15-20% or so, FWIW.

Anyway, it may be worthwhile to figure out some alternative way to determine whether a particular non-x86 system can support 2020/2020B. That may need some thought, though.

@k8wu
Copy link
Author

k8wu commented Sep 14, 2022

After I did the modification to src/util.cpp to allow the total whitelisting of the aarch64 architecture regardless of platform, 2020 and 2020B modes are now working within freedv-gui on both transmit and receive. When transmitting, the process takes around 5% to 7% of the total CPU, peaking occasionally to 10%. Playing the all.wav file results in the same amount of CPU being used (5% or so). It may take a bit more on an SQ1 processor, or even a different one such as the version of the Qualcomm Snapdragon that Lenovo uses in their new ARM laptop, but generally speaking, it should be fine regardless of OS.

@tmiw
Copy link
Collaborator

tmiw commented Sep 15, 2022

After I did the modification to src/util.cpp to allow the total whitelisting of the aarch64 architecture regardless of platform, 2020 and 2020B modes are now working within freedv-gui on both transmit and receive. When transmitting, the process takes around 5% to 7% of the total CPU, peaking occasionally to 10%. Playing the all.wav file results in the same amount of CPU being used (5% or so). It may take a bit more on an SQ1 processor, or even a different one such as the version of the Qualcomm Snapdragon that Lenovo uses in their new ARM laptop, but generally speaking, it should be fine regardless of OS.

For those devices with enough CPU power, yep. However, there are definitely those that can't decode 2020 or 2020B in real time (for example, Raspberry Pi 4), so those modes should probably still be blocked on those platforms.

One thought I had is to do a benchmark the first time someone starts FreeDV using the sample 2020 .wav file. If that file can be decoded in Y% of real time (where Y is some sort of value >= 100% that we figure out), 2020 and 2020B get enabled. Otherwise, they're disabled. Personally I'd probably set Y to be at least 200% of real time to take into account other stuff running on a user's machine but I'm open to suggestions.

@k8wu
Copy link
Author

k8wu commented Sep 16, 2022

That's probably the best way to go about it - that way, regardless of OS or CPU architecture, the program would know for sure whether it could reliably process 2020 modes in real time with enough system resources to spare for other tasks. Let's say we have 2 seconds of 2020 mode data. If the decode operation takes 1 second or less, then 2020 and 2020B could be enabled without any further issue. If the decode operation takes between 1 and 1.75 seconds, then they could still be enabled, but a warning could be shown to the user indicating that there may be performance degradation depending on what else the system is doing while FreeDV is running. Anything more than 1.75 seconds would result in 2020/2020B being disabled, since it's almost a given at that point that the system doesn't have the necessary resources for those modes.

At that point, I don't think that CPU architecture or operating system/platform would have anything to do with it - it's all down to how fast the system can process the data.

Side note: I was thinking, with this way of compiling a Windows ARM64 version of FreeDV and its dependencies, that this process could also technically be used to compile x86 and x64 versions, since the LLVM toolchain has the ability to target i686-w64-mingw32 and x86_64-w64-mingw32 in addition to aarch64-w64-mingw32. It would probably take more CMakeLists.txt edits to make it work without breaking non-Windows builds, but it could probably be done.

@tmiw
Copy link
Collaborator

tmiw commented Sep 17, 2022

Yeah, I was actually about to try to build an ARM Windows build before you created this bug :) It might be a good idea longer term to use LLVM for Windows builds, assuming that packages eventually end up in a form that's easily included in the current Docker container.

Anyway, can you give #280 a shot? It's a first pass at an alternate way of determining whether 2020 mode will work on non-x86 machines.

@Tyrbiter
Copy link

I have merged this on my x86 system just for completeness, when I run it the debug output is:

Making sure your machine can handle 2020 mode:
Generating test audio...
Decoding modulated audio...
One second of 2020 decoded in 143 ms
2020 allowed: 1

so it appears that the code works correctly. I have all the 2020x modes compiled in and enabled with the latest locally built and installed codec2 and lpcnetfreedv libraries.

@k8wu
Copy link
Author

k8wu commented Sep 17, 2022

Yeah, I was actually about to try to build an ARM Windows build before you created this bug :) It might be a good idea longer term to use LLVM for Windows builds, assuming that packages eventually end up in a form that's easily included in the current Docker container.

Anyway, can you give #280 a shot? It's a first pass at an alternate way of determining whether 2020 mode will work on non-x86 machines.

@tmiw I followed the directions in #278 (comment) after checking out the "ms-2020-detection" branch, and it resulted in a working freedv.exe executable. I ran it with a new config so it would do the 2020 test, and it said that it was allowing the 2020 modes because it decoded 1 second of 2020 in 368 ms. Therefore, this branch, with the previous modifications from the earlier comment in this thread, results in success on Windows 11 on ARM64.

@tmiw
Copy link
Collaborator

tmiw commented Sep 17, 2022

Yeah, I was actually about to try to build an ARM Windows build before you created this bug :) It might be a good idea longer term to use LLVM for Windows builds, assuming that packages eventually end up in a form that's easily included in the current Docker container.
Anyway, can you give #280 a shot? It's a first pass at an alternate way of determining whether 2020 mode will work on non-x86 machines.

@tmiw I followed the directions in #278 (comment) after checking out the "ms-2020-detection" branch, and it resulted in a working freedv.exe executable. I ran it with a new config so it would do the 2020 test, and it said that it was allowing the 2020 modes because it decoded 1 second of 2020 in 368 ms. Therefore, this branch, with the previous modifications from the earlier comment in this thread, results in success on Windows 11 on ARM64.

I just tested on my M1 Mac, too:

  • Windows 11: 311ms
  • macOS: 149ms

I'm going to also try on my Raspberry Pi 4 at some point this weekend to make sure 2020 mode is still blocked there.

@tmiw
Copy link
Collaborator

tmiw commented Sep 17, 2022

Found a bug testing on the Raspberry Pi but after fixing:

Making sure your machine can handle 2020 mode:
Generating test audio...
Decoding modulated audio...
One second of 2020 decoded in 1124 ms
2020 allowed: 0

@k8wu
Copy link
Author

k8wu commented Nov 1, 2022

FYI - just tested the compilation with the latest (v1.8.5-20221030) version using the process outlined in this earlier comment - with the exception that you no longer have to remove the "usb-1.0" reference from the src/CMakeLists.txt file, and it looks like the "aarch64" check in src/util.cpp is now no longer necessary (which we discussed earlier in this thread). I am able to build for Windows on ARM64 still, and it allows me to use the 2020* modes.

I suppose I should try this with Ninja instead of Make, because that's one more step toward being able to use native Windows build tools for at least the parts of this process that can use CMake instead of configure scripts.

@tmiw
Copy link
Collaborator

tmiw commented Nov 26, 2022

Update: I've been doing some build system enhancements to make this easier (#305). I've gotten to the point where I can execute the following with that PR branch and it builds almost everything:

export FREEDV_DIR=`pwd`

git clone https://github.com/drowe67/LPCNet.git
cd LPCNet
mkdir build_windows
cmake -DCMAKE_TOOLCHAIN_FILE=$FREEDV_DIR/freedv-llvm-aarch64.cmake ..
make

cd $FREEDV_DIR
git clone https://github.com/drowe67/codec2.git
cd codec2
mkdir build_windows
cmake -DCMAKE_TOOLCHAIN_FILE=$FREEDV_DIR/freedv-llvm-aarch64.cmake -DLPCNET_BUILD_DIR=$FREEDV_DIR/LPCNet/build_windows ..
make

cd $FREEDV_DIR
mkdir build_windows
cd build_windows
export PATH=/Users/mooneer/Downloads/llvm-mingw-20220906-ucrt-macos-universal/bin:$PATH
cmake -DCMAKE_TOOLCHAIN_FILE=$FREEDV_DIR/freedv-llvm-aarch64.cmake -DLPCNET_BUILD_DIR=$FREEDV_DIR/LPCNet/build_windows -DCODEC2_BUILD_DIR=$FREEDV_DIR/codec2/build_windows -DBOOTSTRAP_WXWIDGETS=1 ..
make

Unfortunately it fails during linking with the following:

[100%] Linking CXX executable freedv.exe
lld: error: unable to find library -lws2_32.lib
lld: error: unable to find library -lusb-1.0.lib
lld: error: unable to find library -ldsound.lib
lld: error: unable to find library -lsetupapi.lib
lld: error: unable to find library -lwinhttp.lib
lld: error: unable to find library -lkernel32.lib
lld: error: unable to find library -luser32.lib
lld: error: unable to find library -lgdi32.lib
lld: error: unable to find library -lcomdlg32.lib
lld: error: unable to find library -lwinspool.lib
lld: error: unable to find library -lwinmm.lib
lld: error: unable to find library -lshell32.lib
lld: error: unable to find library -lshlwapi.lib
lld: error: unable to find library -lcomctl32.lib
lld: error: unable to find library -lole32.lib
lld: error: unable to find library -loleaut32.lib
lld: error: unable to find library -luuid.lib
lld: error: unable to find library -lrpcrt4.lib
lld: error: unable to find library -ladvapi32.lib
lld: error: unable to find library -lversion.lib
lld: error: too many errors emitted, stopping now

I think it's something with the toolchain file I'm using but it's not that much different from the one I suggested. (.a files exist for the above, but not .lib.) Any ideas would be appreciated.

@k8wu
Copy link
Author

k8wu commented Nov 30, 2022

You don't need to add the ".lib" extension. If you find a way to get rid of those on the ld command line, it should find those libraries as long as the compiler's bin directory is in your path (for example, on Linux, mine is currently /usr/local/cross-tools/llvm-mingw-20220323-ucrt-ubuntu-18.04-aarch64/bin (though you might be using a different build - there's a 20220906 one now).

@tmiw
Copy link
Collaborator

tmiw commented Nov 30, 2022

You don't need to add the ".lib" extension. If you find a way to get rid of those on the ld command line, it should find those libraries as long as the compiler's bin directory is in your path (for example, on Linux, mine is currently /usr/local/cross-tools/llvm-mingw-20220323-ucrt-ubuntu-18.04-aarch64/bin (though you might be using a different build - there's a 20220906 one now).

Interestingly, I'm not explicitly adding the .lib extension; something in the CMake process is doing so.

As for setup, the bin folder is indeed in my path but I see the following:

Mooneers-16-MacBook-Pro-2325:build_osx mooneer$ ls ~/Downloads/llvm-mingw-20220906-ucrt-macos-universal/aarch64-w64-mingw32/lib/lib*
/Users/mooneer/Downloads/llvm-mingw-20220906-ucrt-macos-universal/aarch64-w64-mingw32/lib/libacledit.a
/Users/mooneer/Downloads/llvm-mingw-20220906-ucrt-macos-universal/aarch64-w64-mingw32/lib/libactiveds.a
/Users/mooneer/Downloads/llvm-mingw-20220906-ucrt-macos-universal/aarch64-w64-mingw32/lib/libadsiid.a
...
Mooneers-16-MacBook-Pro-2325:build_osx mooneer$

I also tried setting CMAKE_SYSTEM_NAME to Windows-Clang and Windows-LLVM (since CMake seems to look for a file named Platform/${CMAKE_SYSTEM_NAME}.cmake to load settings from) and both of those gave me different errors. Specifically, the wxWidgets configuration seemed unable to figure out that I was building for Windows and errored out. I'm wondering if I need to specify "Clang" or "LLVM" in another variable instead?

@tmiw
Copy link
Collaborator

tmiw commented Dec 1, 2022

Good news--it looks like it was an issue with the CMake version in MacPorts. "Windows" inside the toolchain file seems to work fine on 3.25 (the version in Ubuntu 22.04). Still need to investigate why I can't produce installer packages, though.

@tmiw
Copy link
Collaborator

tmiw commented Dec 3, 2022

So yeah, if you have CMake 3.25 or greater, you can do the following to get an installer that's usable on ARM (tested on Ubuntu 22.04):

$ git clone https://github.com/drowe67/freedv-gui.git
$ cd freedv-gui
$ git checkout ms-simple-build
$ mkdir build_windows
$ cd build_windows

$ export PATH=/path/to/llvm-mingw-20220906-ucrt-ubuntu-18.04-x86_64/bin:$PATH
$ cmake -DBOOTSTRAP_WXWIDGETS=1 -DCMAKE_TOOLCHAIN_FILE=/path/to/freedv-gui/cross-compile/freedv-mingw-llvm-aarch64.cmake ..
$ make
$ make package

(note: this downloads and compiles all dependencies, so this may take a while)

Feel free to give it a try and let me know how that goes. It looks like freedv.exe is able to start and decode 2020 using the Windows 11 VM on my M1 Mac Mini, but I'm not sure how well it'll run on other devices.

@k8wu
Copy link
Author

k8wu commented Dec 3, 2022

It almost made it through. I did have to update my CMake to 3.25 (which is in Debian's bullseye-backports repos, so I upgraded it using "apt install -t bullseye-backports cmake"), but it got as far as linking freedv.exe before failing with this error:

[ 98%] Linking CXX executable freedv.exe ld.lld: error: unknown file type: rig.o ld.lld: error: unknown file type: misc.o ld.lld: error: unknown file type: register.o ld.lld: error: unknown file type: conf.o ld.lld: error: unknown file type: debug.o clang-15: error: linker command failed with exit code 1 (use -v to see invocation) make[2]: *** [src/CMakeFiles/freedv.dir/build.make:488: src/freedv.exe] Error 1 make[1]: *** [CMakeFiles/Makefile2:2717: src/CMakeFiles/freedv.dir/all] Error 2 make: *** [Makefile:156: all] Error 2

I am using the latest LLVM toolchain from /mstorsjo/llvm-mingw, and I am building on my usual Debian Bullseye (11) system under WSL2:

$ uname -a Linux K8WU-SPX 5.15.74.2-microsoft-standard-WSL2 #1 SMP Wed Nov 2 19:50:29 UTC 2022 aarch64 GNU/Linux

@tmiw
Copy link
Collaborator

tmiw commented Dec 3, 2022

Is it possible that maybe it's using the wrong lld for some reason? Try running make VERBOSE=1 and pasting what command it's using exactly for the link step?

@k8wu
Copy link
Author

k8wu commented Dec 3, 2022

Here's the relevant part of the output after running make VERBOSE=1:

[ 98%] Linking CXX executable freedv.exe
cd /home/mphipps/src-local/freedv-gui/build_windows/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/freedv.dir/link.txt --verbose=1
/usr/bin/cmake -E rm -f CMakeFiles/freedv.dir/objects.a
/usr/local/cross-tools/llvm-mingw-20220906-ucrt-ubuntu-18.04-aarch64/bin/llvm-ar qc CMakeFiles/freedv.dir/objects.a @CMakeFiles/freedv.dir/objects1
/usr/local/cross-tools/llvm-mingw-20220906-ucrt-ubuntu-18.04-aarch64/bin/aarch64-w64-mingw32-clang++ --target=aarch64-w64-mingw32  -Wall -g -mwindows -Wl,--whole-archive CMakeFiles/freedv.dir/objects.a -Wl,--no-whole-archive -o freedv.exe -Wl,--out-implib,libfreedv.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles/freedv.dir/linkLibs.rsp
ld.lld: error: unknown file type: rig.o
ld.lld: error: unknown file type: misc.o
ld.lld: error: unknown file type: register.o
ld.lld: error: unknown file type: conf.o
ld.lld: error: unknown file type: debug.o
clang-15: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [src/CMakeFiles/freedv.dir/build.make:488: src/freedv.exe] Error 1
make[2]: Leaving directory '/home/mphipps/src-local/freedv-gui/build_windows'
make[1]: *** [CMakeFiles/Makefile2:2717: src/CMakeFiles/freedv.dir/all] Error 2
make[1]: Leaving directory '/home/mphipps/src-local/freedv-gui/build_windows'
make: *** [Makefile:156: all] Error 2

@k8wu
Copy link
Author

k8wu commented Dec 3, 2022

Addendum: It seems to me like it should be calling aarch64-w64-mingw32-ar instead of llvm-ar.

@tmiw
Copy link
Collaborator

tmiw commented Dec 3, 2022

Addendum: It seems to me like it should be calling aarch64-w64-mingw32-ar instead of llvm-ar.

I pushed a commit that should hopefully help with that. Try again?

@k8wu
Copy link
Author

k8wu commented Dec 4, 2022

It still bombs when it comes time to link freedv.exe:

[ 98%] Linking CXX executable freedv.exe
cd /home/mphipps/src-local/freedv-gui/build_windows/src && /usr/bin/cmake -E cmake_link_script CMakeFiles/freedv.dir/link.txt --verbose=1
/usr/bin/cmake -E rm -f CMakeFiles/freedv.dir/objects.a
aarch64-w64-mingw32-ar qc CMakeFiles/freedv.dir/objects.a @CMakeFiles/freedv.dir/objects1
/usr/local/cross-tools/llvm-mingw-20220906-ucrt-ubuntu-18.04-aarch64/bin/aarch64-w64-mingw32-clang++ --target=aarch64-w64-mingw32  -Wall -g -mwindows -Wl,--whole-archive CMakeFiles/freedv.dir/objects.a -Wl,--no-whole-archive -o freedv.exe -Wl,--out-implib,libfreedv.dll.a -Wl,--major-image-version,0,--minor-image-version,0 @CMakeFiles/freedv.dir/linkLibs.rsp
ld.lld: error: unknown file type: rig.o
ld.lld: error: unknown file type: misc.o
ld.lld: error: unknown file type: register.o
ld.lld: error: unknown file type: conf.o
ld.lld: error: unknown file type: debug.o
clang-15: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [src/CMakeFiles/freedv.dir/build.make:488: src/freedv.exe] Error 1
make[2]: Leaving directory '/home/mphipps/src-local/freedv-gui/build_windows'
make[1]: *** [CMakeFiles/Makefile2:2717: src/CMakeFiles/freedv.dir/all] Error 2
make[1]: Leaving directory '/home/mphipps/src-local/freedv-gui/build_windows'
make: *** [Makefile:156: all] Error 2

Given that it's saying that the file type of these object files isn't recognized, I would say that something didn't get built correctly earlier in the process. Looking through the output, nothing jumps out at me, though.

@tmiw
Copy link
Collaborator

tmiw commented Dec 4, 2022

After merging the latest master into that branch, I started having linker errors as well (though not exactly the same errors you got). Turns out that I needed to add -fstack-protector to CMAKE_C_FLAGS whenever hamlib was being statically built. Considering that the files in the error you got are also hamlib related, I wonder if that will fix your errors too. Might be worth pulling down the latest to try again regardless.

@tmiw
Copy link
Collaborator

tmiw commented Dec 19, 2022

I'm off at my day job for the rest of the year, so I'm going through PRs and issues still in progress. It looks like the x86_64 installer generated using PR #305 works properly here. Once I've gone through and made sure the existing build scripts still work properly I'll merge the PR.

(The LLVM MinGW instructions have also been added to the README as experimental. Eventually we can transition over to using those for official binaries once there's more runtime.)

@tmiw
Copy link
Collaborator

tmiw commented Dec 19, 2022

All good. Thanks for your help, @k8wu!

@tmiw tmiw closed this as completed Dec 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants