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

Choose MP3 bitrate when writing #390

Open
lminer opened this issue May 29, 2023 · 17 comments
Open

Choose MP3 bitrate when writing #390

lminer opened this issue May 29, 2023 · 17 comments

Comments

@lminer
Copy link

lminer commented May 29, 2023

Now that mp3 support has been added, it would be great to be able to choose the bitrate when writing to mp3. What is the default btw? 128?

@bastibe
Copy link
Owner

bastibe commented May 30, 2023

Does libsndfile have this feature? We can probably support it if there's an API for it.

@jimmieliu
Copy link

is bitrate now supported?

@bastibe
Copy link
Owner

bastibe commented Mar 27, 2024

Does libsndfile provide an API for it? If so, we can support it.

@sammlapp
Copy link
Contributor

sammlapp commented May 7, 2024

Couldn't find an API so I asked here:
libsndfile/libsndfile#1008

@sammlapp
Copy link
Contributor

I don't really understand the syntax but over in that libsndfile thread 1008 evpobr linked to this documentation:
https://github.com/libsndfile/libsndfile/blob/c81375f070f3c6764969a738eacded64f53a076e/docs/command.md#sfc_set_bitrate_mode

@bastibe
Copy link
Owner

bastibe commented May 14, 2024

I think that's just a flag on how the bitrate is to be interpreted. But how do we set the bitrate itself?

@sammlapp
Copy link
Contributor

Hm, I don't know in that case

@sammlapp
Copy link
Contributor

@bastibe more details provided by arthurt now libsndfile/libsndfile#1008 (comment)

it seems like exposing the SFC_SET_COMPRESSION_LEVEL in the python-soundfile API would be useful. The argument takes values 0-1.

For VBR, it translates to lame_set_VBR_qaulity(). 0.0 -> 0.0 (highest quality), 1.0 -> 10.0 (lowest quality)

behavior is different for average and constant bitrate methods, see aurthurt's comment for details

@bastibe
Copy link
Owner

bastibe commented May 31, 2024

That sounds great! Would anyone want to prepare a pull request?

@sammlapp
Copy link
Contributor

sammlapp commented May 31, 2024

I gave it a try, but did not see any effect. I first added the constant defining SFC_SET_COMPRESSION_LEVEL to soundfile_build.py:

SFC_SET_COMPRESSION_LEVEL		= 0x1301,

then running pythin setup.py install, which made this constant visible in the soundfile._snd.SFC_SET_COMPRESSION_LEVEL API.

However, with the following code, the value assigned to pointer_compression_level has no effect on the file size produced:

pointer_compression_level = _ffi.new('float *')
pointer_compression_level[0] = 0.0
with soundfile.SoundFile('./compressed.mp3','w', 44100, 1, format='MP3') as sfc:
    _snd.sf_command(sfc._file, _snd.SFC_SET_COMPRESSION_LEVEL, pointer_compression_level, _ffi.sizeof(pointer_compression_level))
    sfc.write(data) # data is numpy array of audio samples

I also tried a different syntax for setting the float value:

pointer_compression_level = _ffi.cast("float *",0.5)

but the file sizes do not change with pointer_compression_level.

I don't have any grasp on how ffi really works so I'm not sure how to proceed. I feel it would be easiest for someone more familiar with the python-soundfile library to take it from here.

I also tried with flac format and saw no difference in file size.

Documentation: https://github.com/libsndfile/libsndfile/blob/c81375f070f3c6764969a738eacded64f53a076e/docs/command.md#sfc_set_compression_level

@bastibe
Copy link
Owner

bastibe commented Jun 2, 2024

Thank you for looking into this. I think the pointer_compression_level should be a double, not float. What do you get as return value from _snd.sf_command? It should equal SF_TRUE or SF_FALSE.

Also, check if your version of libsndfile is new enough to support the SFC_SET_COMPRESSION_LEVEL API.

@sammlapp
Copy link
Contributor

sammlapp commented Jun 6, 2024

I tried 'double', no change. The returned value is 0 (not SF_TRUE or SF_FALSE). I also updated libsndfile to 1.2.2 with brew install libsndfile, no difference.

@bastibe
Copy link
Owner

bastibe commented Jun 6, 2024

If you installed the wheel, you'll need to remove _soundfile_data from your site-packages before it'll pick up the system libsndfile. Or uninstall the wheel and install the source distribution, which does not come with _soundfile_data.

@rawrbw
Copy link

rawrbw commented Sep 19, 2024

What's the status on this? When reading a 256kbs mp3 file with pysoundfile and writing it again it ends up as 32kb/s.

@sammlapp
Copy link
Contributor

I personally had no success, not familiar with the development environment here and don't have time to pursue it

@ytya
Copy link
Contributor

ytya commented Nov 7, 2024

I changed the bitrate by adding lines to _cdata_io in soundfile.py. This was sufficient for my case, but there must be a better way.

OS: Windows 11
soundfile: 0.12.1
libsndfile: 1.2.0

def _cdata_io(self, action, data, ctype, frames):
    assert ctype in _ffi_types.values()
    self._check_if_closed()
    if self.seekable():
        curr = self.tell()
+   if action == "write":
+       pointer_compression_level = _ffi.new("double *")
+       pointer_compression_level[0] = 0  # 0:low compression level  1:high compression level
+       _snd.sf_command(self._file, 0x1301, pointer_compression_level, _ffi.sizeof(pointer_compression_level))
    func = getattr(_snd, "sf_" + action + "f_" + ctype)
    frames = func(self._file, data, frames)
    _error_check(self._errorcode)
    if self.seekable():
        self.seek(curr + frames, SEEK_SET)  # Update read & write position
    return frames

@bastibe
Copy link
Owner

bastibe commented Nov 7, 2024

Cool! You got it working!

I'd be happy to merge a pull request that adds this to the rest of the command interface features.

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

6 participants