spotify / pedalboard

🎛 🔊 A Python library for audio.
https://spotify.github.io/pedalboard
GNU General Public License v3.0
4.96k stars 249 forks source link

Add channel mapping support #305

Open dodeca-6-tope opened 3 months ago

dodeca-6-tope commented 3 months ago

Map channels to their respective speakers for supported audio formats. This change solves an issue where receivers can't determine how to map channels for multi channel audio files.

dodeca-6-tope commented 3 months ago

@psobot a more flexible approach would be to take an optional layout and fallback to the changes I introduced here if not provided. Notice that channel count -> layout is a one-to-many relationship.

psobot commented 3 months ago

Thanks @dodeca-6-tope! This looks great - is there a plugin I can test this with before merging?

dodeca-6-tope commented 3 months ago

@psobot not that I know of, but you can verify this change by writing and playing a multichannel track on your receiver if you have one lying around. Audacity also fills up the channels with the correct signals unlike before. The first thing I did is to look for a visualizer but I haven't found any.

psobot commented 3 months ago

Whoops, my apologies - I misread this as applying to VST3Plugin rather than AudioFile. This is much easier to test if it's just a metadata change in the output files. 👍🏻

psobot commented 3 months ago

After digging a bit deeper, I'm not sure this will actually change anything when writing audio files. From what I can tell, only the WavAudioFormat writer behaves differently when provided an AudioChannelSet - and it already calls canonicalWavChannelSet in its constructor:

    return createWriterFor (out, sampleRate, WavFileHelpers::canonicalWavChannelSet (static_cast<int> (numChannels)),
                            bitsPerSample, metadataValues, qualityOptionIndex);

...which does the same thing as namedChannelSet:

    inline AudioChannelSet canonicalWavChannelSet (int numChannels)
    {
        if (numChannels == 1)  return AudioChannelSet::mono();
        if (numChannels == 2)  return AudioChannelSet::stereo();
        if (numChannels == 3)  return AudioChannelSet::createLCR();
        if (numChannels == 4)  return AudioChannelSet::quadraphonic();
        if (numChannels == 5)  return AudioChannelSet::create5point0();
        if (numChannels == 6)  return AudioChannelSet::create5point1();
        if (numChannels == 7)  return AudioChannelSet::create7point0SDDS();
        if (numChannels == 8)  return AudioChannelSet::create7point1SDDS();

        return AudioChannelSet::discreteChannels (numChannels);
    }

Have you tested this change locally? All of my tests with different channel counts and audio formats seem to produce identical files before and after this change (except for Ogg Vorbis, for which the encoding process is not 100% deterministic).

dodeca-6-tope commented 3 months ago

Tested on a receiver that initially mapped a down-mixed version (to mono) of the file to all channels equally and after this change it mapped as expected.

dodeca-6-tope commented 3 months ago

Might be an issue with the receiver, will try again tomorrow with another device.