molke-productions / qloud

Tool to measure loudspeaker frequency and step responses and distortions
GNU General Public License v2.0
28 stars 6 forks source link

Add PortAudio as audio backend #19

Closed mincequi closed 2 years ago

mincequi commented 2 years ago

This PR adds support for PortAudio as backend (as alternative to JACK). Also macOS support is added.

Please tell me what you think. Thanks :)

molke-productions commented 2 years ago

Thank you for taking the time to implementing this. Note that I understand almost no C++ at all.

What was your motive for a different audio backend? Since I use exclusively Jack on Linux, I don’t really need one, but if it can be useful to you or anyone else, it can certainly be added.

mincequi commented 2 years ago

Thank you for taking the time to implementing this. Note that I understand almost no C++ at all.

What was your motive for a different audio backend? Since I use exclusively Jack on Linux, I don’t really need one, but if it can be useful to you or anyone else, it can certainly be added.

Phew, not understanding C++ is not a good precondition for maintaining a C++ project ;) However, you will learn. ;)

Well, the point is, that most people (and me) do not have jack running. I also wanted to make that App portable to be able to run on macOS and Windows as well.

mincequi commented 2 years ago

Is there an update on this? I would have some more things to come... :)

molke-productions commented 2 years ago

Since the computation in question std::max(this->maxResponse, fabs(this->capBuf[i])) hasn’t changed, I still cannot compile your version. With my unsufficient C++ knowledge, it seems like gcc doesn’t like to compare different types (float and double)?

mincequi commented 2 years ago

Yes, the computation did not change. However, i included algorithm with a recent force push to the existing PR, which should/could fix the issue. Within Capture.h, i do not see a type mismatch between maxResponse and capBuf[]. Both are of type float.

The commit in question is 7aa13ee. Maybe you find the time to test that again :)

Of course, my PR should at least run on your machine.

molke-productions commented 2 years ago

Maybe, I made some stupid mistake circumventing all the nice git functions (which I would have to look up), but if I download a zip file here, I still get the same error.

Doesn’t fabs return double?

mincequi commented 2 years ago

Hmm, still strange, though. What's your environment? Distro? GCC Version?

However, i pushed another update. Hopefully, this fixes it finally.

molke-productions commented 2 years ago

gcc (GCC) 11.1.0 on Arch Linux

molke-productions commented 2 years ago

This fails with: Capture.cpp:86:70: error: ‘fabsf’ is not a member of ‘std’; did you mean ‘fabs’?

molke-productions commented 2 years ago

Maybe, this is related to this as opposed to <cmath>?

mincequi commented 2 years ago

GCC 11 is pretty recent.

I am using clang on macOS. Let me try this tomorrow with GCC on linux. I will keep you posted.

molke-productions commented 2 years ago

That’s probably the biggest advantage and, at the same time, disadvantage of Arch Linux. Take your time.

mincequi commented 2 years ago

Ok, this time verified with GCC11. Should work now :)

molke-productions commented 2 years ago

Unfortunately, not on my system (same in line 128): JackWrap.cpp:127:14: error: ‘memcpy’ is not a member of ‘std’; did you mean ‘wmemcpy’?

mincequi commented 2 years ago

ok, i did another force push. This time verified with a debian machine and GCC10.

molke-productions commented 2 years ago

Now this compiles at least without error. But if I start the application, I get

ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map

If I change the directory (project files), I get

pure virtual method called
terminate called without an active exception
Aborted (core dumped)
mincequi commented 2 years ago

Pushed another fix. Thanks for your patience ;)

molke-productions commented 2 years ago

Changing the directory now works. However, capturing (a simple loop-back) with a generated signal of 5 s will not stop after, say, 30 s. When I cancel the measurement after 30 s, there is no response.wav, so the IR calculation fails.

molke-productions commented 2 years ago

I might be patient, but I don’t see much use in testing every step. Please make sure that at least basic functionality works before pushing another update.

mincequi commented 2 years ago

Well, except for that directory change, everything is thoroughly tested. For me the loop-back capture also works on macOS and on linux. Is there any valuable output on console?

Btw. on start-up there will be some output generated because of PortAudio (which in turn pulls in ALSA, which cannot be entirely silenced). I also see this for other audio apps like Audacity.

molke-productions commented 2 years ago

The directory change might be the actual problem. After retesting with several attempts, I managed to get a working measurement. Not terminating before might have also been related to not properly updating values (my last excitation signal length could have been 40 s).

  1. If I start the program, change to a new (empty) directory, generate an excitation signal, …, then I get the above error.
  2. If I close and start the program again (with the new directory), measurement works as expected.
  3. If I delete the new directory and start the program again, I rightfully get an error notice that the last used directory does not exist, so the program changes to some default ($HOME in my case). If I then change to some existing directory within the program and start a recording, the error is “Failed to open $HOME/excitation.wav!”, which is certainly not the displayed working directory.

There is some console output (apart from ALSA) [EDIT: this happens with the unmodified version too]

qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 2580, resource id: 11278209, major code: 40 (TranslateCoords), minor code: 0
qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 2777, resource id: 11294804, major code: 40 (TranslateCoords), minor code: 0

As for the ALSA warnings: as mentioned before, I don’t use Portaudio (I don’t even have it installed), so these errors/warnings related to Portaudio’s ALSA management are at least misleading.

mincequi commented 2 years ago

Now, I guess, I understood (all) the use-cases with that working directory and fixed this accordingly. I also followed your test approach and did some further checking. Works now on my machine.

The point with PortAudio is: if you link against it (and initialize it), it will do some device probing, which cause these console logs. Apparently, there are ways to silence this, however, those are a little hacky and i do not recommend doing this.

molke-productions commented 2 years ago

As far as I can tell from quick testing, measuring now works as expected. However, if I select Portaudio (despite running Jack all the time and not having Portaudio installed), I get neither a warning nor an error; moreover, I can still record endlessly (well over 40 s) without any actual result.

When Jack is set up with a sample rate different from 44100 Hz and I select Portaudio, at least the button to start a recording is inactive, so there is still no other sample rate supported with the new Portaudio backend. To me, this seems to impose a rather arbitrary restriction. Although I don’t know if that’s still relevant today, I seem to remember that some AC'97 chips even had/have a fixed internal sample rate of 48 or 96 kHz.

Oh, and if Jack is running at 96 kHz and I switch between backends I have to click “Generate” twice to be able to record.

I can’t comment on the actual code itself. Maybe, @be1 can have a look.

mincequi commented 2 years ago

Ok, we are getting closer 👍

As far as I can tell from quick testing, measuring now works as expected. However, if I select Portaudio (despite running Jack all the time and not having Portaudio installed), I get neither a warning nor an error; moreover, I can still record endlessly (well over 40 s) without any actual result.

PortAudio is not an external application. It is a library and actually you have it installed, because otherwise you won't be able to compile this PR at all. I added some comboBoxes to the UI to check, whether there are inputs/ouputs available on your machine. However, this endless capture window still is mysterious to me....

When Jack is set up with a sample rate different from 44100 Hz and I select Portaudio, at least the button to start a recording is inactive, so there is still no other sample rate supported with the new Portaudio backend. To me, this seems to impose a rather arbitrary restriction. Although I don’t know if that’s still relevant today, I seem to remember that some AC'97 chips even had/have a fixed internal sample rate of 48 or 96 kHz.

True, this is an arbitrary restriction i introduced for now. Real sample rate selection will come up in a separate PR, since this means some further drastic changes (from UI point of view, this looks easy, but i need to do device probing to offer all possible sample rates from the output device). As of now, i changed the fixed sample rate from 44100 to 48000.

Oh, and if Jack is running at 96 kHz and I switch between backends I have to click “Generate” twice to be able to record.

Ok, checked that too. This is fixed.

I can’t comment on the actual code itself. Maybe, @be1 can have a look.

Since this is a little tedious to do, we might think of developing a test plan, like "click this and that and then expect this and that".

molke-productions commented 2 years ago

It is a library and actually you have it installed, because otherwise you won't be able to compile this PR at all.

You’re right. I somehow missed the line while checking my system for Portaudio. Does that imply that Portaudio would become a ‘hard’ dependency, or is this configurable somewhere when compiling?

I added some comboBoxes to the UI to check, whether there are inputs/ouputs available on your machine.

The combo boxes don’t make any sense for the Jack backend if it’s not possible to select ports different than the predefined ones.

However, this endless capture window still is mysterious to me....

portaudio-capture-with-jack

Probably not related to this, but I manged to crash the program pretty consistently with canceling an export of a plot.

mincequi commented 2 years ago

You’re right. I somehow missed the line while checking my system for Portaudio. Does that imply that Portaudio would become a ‘hard’ dependency, or is this configurable somewhere when compiling?

Yes, currently, this is a hard dependency. However, portaudio is so common, that almost any linux installation already has it installed. Actually, it is quite more popular than jack.

The combo boxes don’t make any sense for the Jack backend if it’s not possible to select ports different than the predefined ones.

Ok, those combo boxes will now be hidden for JACK.

portaudio-capture-with-jack

I added some console logging to track that down to the point, where it gets stuck. Unfortunately, there is no other possibility (except for debugging, if you know, how to do that?).

Probably not related to this, but I manged to crash the program pretty consistently with canceling an export of a plot.

True, this is not related to my change. However, i could also not reproduce that.

mincequi commented 2 years ago

@molke-productions , I pushed another update. The output device is now opened in stereo mode. Maybe that was causing issues on your system.

Btw. do you hear any sound, when doing capturing with PortAudio?

molke-productions commented 2 years ago

That’s the console output with one Jack measurement and starting one Portaudio measurement, which still doesn’t stop:

(qloud:16356): dbind-WARNING **: 23:09:01.076: Couldn't connect to accessibility bus: Failed to connect to socket /tmp/dbus-tB6IldWGHq: No such file or directory
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:877:(find_matching_chmap) Found no matching channel map
qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 754, resource id: 12322480, major code: 40 (TranslateCoords), minor code: 0
max response:  3.0756e-05
write file:  "/home/tobias/dokumente/src/qloud-portaudio/test/response.wav"
max response:  0.501137
write file:  "/home/tobias/dokumente/src/qloud-portaudio/test/response.wav"
qt.qpa.xcb: QXcbConnection: XCB error: 3 (BadWindow), sequence: 3256, resource id: 12344150, major code: 40 (TranslateCoords), minor code: 0
Process  192000  audio samples at rate:  48000
Open stream
Start stream

The counter just keeps running after that. I don’t know if that’s any help. And no, I don’t hear any sound when capturing with Portaudio (probably related to the fact that the only sound device connected to speakers is occupied by Jack).

After closing, there is additionally a core dump:

QThread: Destroyed while thread is still running
Aborted (core dumped)