FluidSynth / fluidsynth

Software synthesizer based on the SoundFont 2 specifications
https://www.fluidsynth.org
GNU Lesser General Public License v2.1
1.85k stars 258 forks source link

How to --fast-render= to a pipe? #208

Closed mk-pmb closed 7 years ago

mk-pmb commented 7 years ago

Spoiler: Kind-of solved by the libsndfile FAQ ("Q17: Can libsndfile read/write files from/to UNIX pipes?"). Currently "wontfix" for .wav, so render .au or .flac instead.


Hi! I'm trying to convert huge amounts of small chunks of data that start with MThd and would be valid content for a MIDI file if I were to save them to disk. They arrive over a network socket and there's no use for them after they have been rendered, so I'd prefer to not store them. Especially, I'd like to avoid:

Should be easy: Just have FluidSynth 1.1.6 (Ubuntu 14.04.5 LTS) read the MIDI data from a pipe and --fast-render= wave audio to a pipe.

The first part failed with Parameter '/dev/fd/62' not a SoundFont or MIDI file or error occurred identifying it. Luckily my project's input possibilities are limited to a few ten thousand variations, so I generated MIDI files for all of them on my big fast read-only storage and calculate the appropriate file name from the input data. Ugly but it works.

However, I can't find a solution for the output. The easy approach fails without any data:

Rendering audio to file '/dev/fd/63'..
fluidsynth: error: Failed to open audio file '/dev/fd/63' for writing

In the special case that the fast-render file is my terminal, I managed to get at least some partial output before it flinches:

fluidsynth: warning: Failed to pin the sample data to RAM; swapping is possible.
Rendering audio to file '/dev/stdout'..
RIFWAVEfmt D��dataRIFWAVEfmt D��data����fluidsynth: error: Failed to open audio file '/dev/stdout' for writing

strace log:

write(1, "Rendering audio to file '/dev/fd"..., 39) = 39
open("/dev/fd/63", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0644) = 3
gettimeofday({1505789510, 144375}, NULL) = 0
fstat64(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
close(3)                                = 0
write(2, "fluidsynth: error: Failed to ope"..., 70) = 70
munmap(0xb6ae6000, 610304)              = 0
munmap(0xb6b7b000, 921600)              = 0
munlock(0xb6566008, 5764336)            = 0
munmap(0xb6566000, 5767168)             = 0
brk(0x8c04000)                          = 0x8c04000
exit_group(0)                           = ?
+++ exited with 0 +++

Any somewhat-easy ideas how I could make v1.1.6 fast-render to a pipe?

Update: For whatever strange reason, fast-render to pipe works with --audio-file-type flac, so as a temporary work-around I'll pipe that into a flac-decoder. I'd still love to read about better ideas, for both sides (in/out).

derselbst commented 7 years ago

As far as I can tell reading midi files from a fifo isnt implemented in fluidsynth, as it always wants to make sure to find the MThd chunk in a midi file. For a fifo there is no data available at that time so it fails, right? Also fluidsynth uses the C-Lib function like fread(), never tried if they can even handle fifos.

As for the output fluidsynth uses libsndfile. So whether writing to fifos works is up to libsndfile.

I'm trying to convert huge amounts of small MIDI files.

What keeps you from specifying the midi files explicitly for fluidsynth?

 fluidsynth -F out.wav sound.sf2 midi1.mid midi2.mid ... 
mk-pmb commented 7 years ago

What keeps you from specifying the midi files explicitly for fluidsynth?

The files' data isn't stored in actual files, it arrives over a network socket. I'll add that to the original post to clarify.

Thanks for your hint about the MThd chunk. I remembered that from an earlier debug session, so I checked the input case again. Output:

Parameter '/dev/fd/63' not a SoundFont or MIDI file or error occurred identifying it.
No midi file specified!

strace (added diff margin for highlighting):

+ open("/dev/fd/63", O_RDONLY)            = 3
  fstat64(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76df000
+ read(3, "MThd\0\0\0\6\0\1\0\1\1\340MTrk\0\0\0\24\0\377\4\0\0\300\0\0\231'"..., 4096) = 42
  close(3)                                = 0
  munmap(0xb76df000, 4096)                = 0
- open("/dev/fd/63", O_RDONLY)            = 3
  fstat64(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76df000
- read(3, "", 4096)                       = 0
  close(3)                                = 0
  munmap(0xb76df000, 4096)                = 0
  write(2, "Parameter '/dev/fd/63' not a Sou"..., 86) = 86

The green lines show read() returns 42, which is the exact number of bytes that had been written to the pipe, and the preview is a prefix of those exact bytes, so it seems the problem really isn't about reading from the pipe.

To me the red lines look like fluidsynth is too eager to try and check something else (??) even after encountering valid MIDI data, as it tries to read the same file again.

Update: The first read seems to be from fluid_is_soundfont() @ src/utils/fluid_sys.c:349. Would be nice if future versions of fluidsynth would support CLI flags to allow announcing which sort of data to expect in the next filename, so it has to read them only once.

Update 2: Almost forgot, thanks for the libsndfile hint as well. I'll report the strangeness there. Update 2b: FAQ's "Q17: Can libsndfile read/write files from/to UNIX pipes?" sounds like wontfix.

derselbst commented 7 years ago

To me the red lines look like fluidsynth is too eager to try and check something else (??) even after encountering valid MIDI data,

Not sure, will have to debug that. Could you provide me the commands you're using for setting up the fifo and feeding data to it? (too long ago I last did that, cant remember)

mk-pmb commented 7 years ago

I'll make a stripped-down version of the render server if I find a free schedule slot soon, and will open a new issue about the input case then, so people searching for the fast-render pipe have less stuff to read here. After all, the original problem is kind-of solved thanks to your hint at libsndfile. :+1: If I find an easy way to make it work for WAV format audio, I'll post it here.

(Unfortunately it's a bit more complicated than "render with

  --audio-file-endian little
  --audio-file-format=s16     # 16-bit Signed Integer PCM
  --sample-rate 44100
  --audio-file-type raw

and insert a wave file header in front of that".)