node-flac-bindings
node-flac-bindings copied to clipboard
Support for foreign metadata RIFF chunks
Wave files can contain unofficial metadata, such as Sampler Chunk - "smpl": https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl
These are used for audio looping players and samplers avoiding to loading multiple samples.
I have one such file here: https://github.com/studiorack/basic-harmonica/blob/bf42d5bab7470cc201e3c4b6dee7925b19db6bff/samples/harmonica_1.wav
and a flac file converted using the official flac command line tool:
flac harmonica_1.wav --keep-foreign-metadata
When inspecting with exiftool it shows the RIFF metadata was copied:
$ exiftool harmonica_1.flac -G1
[RIFF] Encoding : Microsoft PCM
[RIFF] Num Channels : 1
[RIFF] Sample Rate : 48000
[RIFF] Avg Bytes Per Sec : 96000
[RIFF] Bits Per Sample : 16
[RIFF] Manufacturer : 0
[RIFF] Product : 0
[RIFF] Sample Period : 20833
[RIFF] MIDI Unity Note : 64
[RIFF] MIDI Pitch Fraction : 0
[RIFF] SMPTE Format : none
[RIFF] SMPTE Offset : 00:00:00:00
[RIFF] Num Sample Loops : 1
[RIFF] Sampler Data Len : 0
[RIFF] Sampler Data : (Binary data 20 bytes, use -b option to extract)
[RIFF] Unshifted Note : 64
[RIFF] Fine Tune : 0
[RIFF] Gain : 0
[RIFF] Low Note : 0
[RIFF] High Note : 127
[RIFF] Low Velocity : 0
[RIFF] High Velocity : 127
[RIFF] Acidizer Flags : One shot
[RIFF] Root Note : High C
[RIFF] Beats : 2
[RIFF] Meter : 4/4
[RIFF] Tempo : 0
[RIFF] Comment : Recorded on 7/10/2022 in Edison.
[RIFF] Software : FL Studio 20
[Composite] Duration : 0.87 s
I have tried all the metadata types in your bindings, but none appear to support the RIFF chunks:
new flac.FileEncoder({
file: output || 'out.flac',
compressionLevel: 9,
metadata: [new metadata.ApplicationMetadata(), new metadata.PaddingMetadata(), new metadata.SeekTableMetadata(), new metadata.UnknownMetadata(), new metadata.VorbisCommentMetadata()]
})
How can I get this to work? Happy to submit a PR
Using flac-bindings v2.7.2 as I want my library to have backwards compatibility for CommonJS.
Hello! This is the first time I see this feature of the flac
tool, and I never read about it before. I will try my best to help you.
First, what you want is something you can accomplish using the available APIs (either through streams or using the metadata API level 1 or level 2). I know the metadata APIs are a bit complicated so that's why added the metadata array for the stream encoders (FileEncoder
and StreamEncoder
). I will focus on this API.
The metadata
property in the constructor just tells the encoder to write those flac metadata blocks into the output stream/file. In your example, you are adding several empty metadata blocks to the output file (not sure if libflac
sees this and removes them or something).
For that case, the idea is to read the RIFF
chunks beforehand, store each chunk in an array of buffers (the whole chunk) and then write them as application metadata with id riff
(see this). The only thing I don't know for sure if it just needs to be metadata block for all chunks or one metadata block for each chunk. My guess goes to the one for each.
Imagine that you have a library that reads all RIFF
chunks somehow using an async iterator, and puts the raw chunk in a property called raw
. The idea would look like this:
// read riff chunks
const pathToTheFile = '...'
const riffChunks = []
for await (const riffChunk of readRiffChunks(pathToTheFile)) {
const riffApplicationMetadata = new flac.metadata.ApplicationMetadata()
riffApplicationMetadata.id = 'riff'
riffApplicationMetadata.data = riffChunk.raw
riffChunks.push(riffApplicationMetadata)
}
// create encoder with the metadata block
const flacEncoder = new flac.FileEncoder({
file: output || 'out.flac',
compressionLevel: 9,
metadata: riffChunks,
})
Note that I could not test that properly, don't have anything to try this fine. This "code" gives you something like this:
If you would like to inspect further, I made a quick CodeSandbox https://codesandbox.io/p/sandbox/lively-bash-gw9jnh?file=%2Findex.js%3A76%2C38
I could not find any npm package that I liked to use for this sample (I have to say I just spend 10 minutes looking) so I made the code to read the chunks manually. Is not that complicated I have to say hehe. I hope this helps you find the solution to your issue.
On the other hand, I don't quite like the idea to have this kind of code around, which is not from flac API directly. I see the utility of it but I don't many people using it for their daily use. If you would like to keep this for your project, for me it's fine.
Anyway, I also don't mind to have it anyways if the code does not use any external dependency. Like an utils package inside the library 🤔 I would accept the PR. The only issue is I don't have time to maintain two versions for the library (2.x and 3.x), so it will only go to 3.x, sorry for that tho.
Thanks for raising the issue :)
Thanks for your thoughtful comment and useful advice/examples.
Comparing the two files you generated in your codebox:
exiftool ./test/out.flac -G1
They appear to be different metadata. I will spend some time on this over the next few days and then report back my findings!
Hello again! At least something is being read with that tool! That's something hehe
Good luck with the investigations. I am looking forward to your results. If you need some help, I may be able to assist you.
Also, take a look at flac's source code, something I did not think of yesterday to check: https://github.com/xiph/flac/blob/master/src/flac/foreign_metadata.c#L210 Maybe you see something useful in it.