Animation Audio Subtask
Open, NormalPublic

Description

One of our goals for 2020 should be to get animation audio ready for non-experimental support on all supported platforms.

In the event that we can't get QtMultimedia to work, I think we should focus on using a simple, cross platform, low-level audio API instead of one focused on media playback. As of now, the two best options in that category seem to be SDL2 and PortAudio. Both are relatively low level and simple, and both allow us to pass a simple buffer of audio samples to an audio device using either "pull" (asynchronous callbacks) or "push" (synchronous blocking functions) I/O methods.

QtMultimedia

QtMultimedia Audio Overview

What we're already using, and part of Qt. Has options for both high-level (QMediaPlayer) and low-level audio (QAudioDecoder + QIODevice).
Apparently we've got issues between AppImage and GStreamer on Linux--we need to see if those can be fixed or whether we can run an alternative backend before committing to a library switch.
We should also consider switching from the high-level API to the low-level one, as it will give us better control of exactly how audio we pass audio to the device and allows for greater possibilities (mixing, stretching, dsp, etc.).

SDL2

SDL Audio Header

SDL2 is a bigger library and contains a lot of functionality that we don't need, but subsystems can be initialized and used individually, allowing us to use it for audio file I/O and streaming only. (https://wiki.libsdl.org/SDL_Init). SDL2 supports Windows, MacOSX, and Linux, as well as Android and iOS.

SDL uses the zlib license.

PortAudio

PortAudio API Overview

PortAudio is a smaller library that's focused only on providing a simple, cross-platform interface for streaming audio; opening audio streams and pulling/pushing sample buffers to them. We would probably need to use Qt for audio file I/O. While PortAudio supports Windows, MacOSX and Linux, another potential drawback is a lack of support for iOS and Android--platforms which we don't fully support yet, but are potential areas of expansion.

PortAudio uses the MIT license.

Other Considerations

Q: "Pull" vs "Push" streaming?

Many low-level audio libraries allow for "pull" and "push" methods of streaming. In the "pull" method, the library invokes an asynchronous callback function whenever the audio stream is ready to receive a new buffer of samples, while with the "push" method audio buffers are provided to a stream using a synchronous/blocking function call.

For our needs, which requires us to synchronize audio buffers to animation frames both in and out of order at uneven intervals (when scrubbing), I think that the "push" method might be a better fit. Each time we change frames, via scrubbing or playback, we would queue a buffer of N stereo audio samples. (Where N is secondsPerAnimationFrame * audioSampleRate).

Q: Latency Compensation?

There is always some latency involved in audio playback, which varies depending on the efficiency of the OS's host API. In order to keep our visual animation in sync with our audio, it may become necessary to compensate by adding L milliseconds of delay time to the video playback. (Where L is determined by the one-way trip latency of a buffer of N samples through a given host API, and would probably end up being less than 1s.)

Q: Waveform Visualizer?

It would be really helpful to see some kind of visual preview of the audio track on the Timeline Docker, so that the animator can see exactly where the transients and peaks lie in relation to the framerate of their animation. We could render this once, when an audio file is opened.

Q: Playback Speed?

We should preserve the users ability to playback their animation at different speeds, audio stretching included.
Right now audio shifts pitch with speed (repitch), worth considering other stretching options?

Q: User Interface?

We need to take a hard look at how an audio-synced animation workflow feels in Krita.
Everything from adding an audio track, to navigating the timeline, to placing keyframes in time with the sound should feel good and easy to use.

emmetoneill triaged this task as Normal priority.
emmetoneill updated the task description. (Show Details)Mar 23 2020, 2:28 AM

For the waveform visualizer UX, we could do what audacity does, so dropping this link for reference.

Hi, @emmetoneill!

Just notes:

  1. The only requirement I had when searching for the audio libraries was the ability to change playback speed. In Krita we allow to set playback speed to, say, 35%. In such a case the library should be able to slow down audio as well. Afair, QtMultimedia supports that fine.
  1. One major problem we have is the lack of audio support in AppImage. The tricky thing is, we might not be able to solve this problem with just changing library. The problem is that AppImage, which has embedded audio library, should somehow connect to the host's audio API. QtMultimedia uses GStreamer and therefore has some troubles connecting to the host's GStreamer. I personally never tried to fix it. Perhaps we can fix it without replacing QtMultimedia... I don't know.
  1. Can you check what audio APIs these libraries use on each platform? It could help us with the selection process.
  1. Do you have a list of all audio-related bugs we have? Perhaps we can just fix them without switching the library?

Hey @dkazakov . Thanks for the input.

  1. That shouldn't be a problem.
  1. Yeah. I was lead to believe that QtMultimedia was going to be a dead end due to AppImage/GStreamer. Eoin and I will look into it, and if we can make it work then we'll stick with QtMultimedia for sure.
  1. Here's what I've found:

SDL2 uses DirectSound/XAudio2 (Win), CoreAudio (OSX/iOS), ALSA/OSS/PulseAudio (Linux), JNI Audio (Android). https://wiki.libsdl.org/Introduction

PortAudio uses most of those on supported platforms, plus a couple of the low-latency ones like ASIO (Win) and Jack (Linux). http://portaudio.com/docs/v19-doxydocs/api_overview.html

When it comes to QtMultimedia, it seems like it may be possible to use a non-GStreamer backend plugin, but I haven't really researched this yet. This could be another option if GStreamer can't be made to work. https://wiki.qt.io/Qt_5.4_Multimedia_Backends

  1. Not yet. Right now I've only had a couple short conversations about it on IRC and tested the audio features myself. I'll make sure that we have a solid plan before writing any code.

So...
"Plan A" is to get GStreamer working with AppImages then fix bugs and make improvements to our existing system.
"Plan B" is to try to use QtMultimedia with a non-gstreamer backend.
"Plan C" is to port to a different cross-platform library.

emmetoneill updated the task description. (Show Details)Mar 25 2020, 8:18 PM
emmetoneill updated the task description. (Show Details)Mar 25 2020, 8:24 PM

Hi, @emmetoneill and @eoinoneill!

About your question about building an AppImage with SDL2. You have two options:

Option 1: install SDL2 inside the docker environment (./bin/sudoenter; apt install sdl2-blah-blah). After that you should run either ~/bin/build_krita_appimage.sh to build the AppImage for Krita, or just run ~/tools/bin/linuxdeployqt with proper arguments for your custom testing app. You can check an example of arguments in ~/persistent/krita/packaging/linux/appimage/build-image.sh.

Option 2: build AppImage in the host system, where you already have a test app. I have never tried this approach, but, in theory, it should look like that:

  1. Install linuxdeployqt on your host system (or copy right from the container?)
  2. Modify script packaging/linux/appimage/build-image.sh so that APPDIR and DEPS_INSTALL_PREFIX would pooint to proper directories in your host system (Krita's install prefix and probably /usr/?)
  3. Run this script and fill all the issues it finds :)
  4. Please take it into account that the script modifies the files in $APPDIR. Therefore, after every failed attempt to build the appimage, you should clear Krita's install prefix, and reinstall it using make install/fast.
emmetoneill added a comment.EditedJun 24 2020, 3:55 AM

@dkazakov @rempt

Eoin and I have done some more testing of various audio options recently and have come up with a few ideas about how we might proceed.
(We don't need anybody to do anything here, we just want to give an update and overview of the state of Krita's animation audio support.)

QtMultimedia + GStreamer:

  • Good news! QtMultimedia + GStreamer can be made to work inside an AppImage, given the appropriate attention to environment variables and dependencies.
  • QtMultimedia needs to be added to the cmake arguments inside of the 3rdparty/ext_qt (iirc), building them on the build factory so we can wget during docker setup.
  • linuxdeployqt cannot automatically/bundle GStreamer or any of our GStreamer plugin dependencies. We need to install those via dockerfile, and then (inside the docker) install them into our AppDir/ manually. Also, we need to add a couple of AppDir-relative GStreamer path environment variables inside Krita's main.cc.
  • For whatever reason, we're having difficulty getting linuxdeployqt to automatically bundle the required mediaservice qt plugin even when it's manually added to the AppDir. To workaround this, we've removed linuxdeployqt's -appimage argument, which causes it to simply wrangle our deps into the AppDir, and then we're building the appimage as a separate step using the appimagetool program. We're not exactly sure why this is the case, it may just be a bug with linuxdeployqt but the workaround isn't so bad. (It's also not as complicated as I'm making it sound.)

SDL2 + SDL_Sound:

  • SDL2 can be easily added to Krita and made to work in an AppImage. SDL2 needed to be properly added to cmakelists.txt, used, and then installed into the docker via dockerfile. It would then be automatically detected and added to appiamge by linuxdeployqt.
  • However, SDL2 cannot be used as a drop-in replacement for our current audio implementation, due to the "high-level" way that we play audio (using QMediaPlayer). Would require at least some refactoring of our audio system.
  • On top of that, SDL2's core library does not contain audio decoders and requires the use of other libraries (like SDL2_mixer, SDL_sound or some other audio format decoder library). SDL2_mixer could work, with minor refactoring, but carries some inherent limitations (high-level interface, no speed adjustment/stretching, single-track limit, etc.). Initial tests with SDL_sound were promising, and it sounds decent on paper, but we stopped short of getting it fully working due to the larger necessary refactors to Krita's current system, so we can't really come to a conclusion.

Anyway, here's our takeaway!

  • In the short term, the obvious path forward is to simply fix QtMultimedia + GStreamer in our AppImage. We now have a good idea of what needs to be done and a working proof of concept, and it shouldn't take too long to reach parity with the way audio currently works in outside of appimages. We already have an AppImage with working audio, and we just need to clean up a few things and make sure various common formats are supported with the appropriate GStreamer plugins.
  • In the long term, the answer feels a bit more complicated... We both feel that Krita's current audio system is a bit burdened with "technical debt", as boud sometimes puts it. The high-level way that we currently handle audio playback is a convenient-yet-flawed implementation, since it leaves us with very little control over how how the audio is played and kept in sync with the animation itself. With no insult to anybody involved, it's essentially the equivalent of hitting the play button on a tape player while flipping through a flipbook, occasionally correcting for timing errors above a certain threshold by seeking the audio back to roughly the correct spot. As such, there are real, inherent limitations to what we can do and how well we can keep things synced up, and we both feel that the Krita's audio will eventually need to be refactored anyway if we want to make significant improvements or add new features. (I believe this type of refactor would be possible in either QtMultimedia or SDL2, but since QtMultimedia seems to be working now it only makes sense to stick with it.)
rempt added a comment.Jun 24 2020, 7:48 AM

Good news! About linuxdeployqt -- #appimage is a busy irc channel where theassassin is usually around and can help getting answers.