Modifying and writing out a soundfont

You're new to the LinuxSampler world? You don't know where to start and nothing works? Here's the place to ask for help.
Post Reply
wearyhacker
Newbie
Posts: 2
Joined: Thu Jun 27, 2024 4:30 pm

Modifying and writing out a soundfont

Post by wearyhacker » Sun Jun 30, 2024 9:00 pm

Hi,
I am trying to write a program using libgig-4.4.1. The idea is to make some modifications to the sample data and the write the modified soundfont out to a new file. I have got so far as being able access specific individual raw sample points. It took me a little while to work out that you could not use the sample offsets in the soundfont metadata to access sample points in buffer returned from ReadSampleData, they need to have the Start offset subtracted from them.

When I have finished working on the raw data it will be shorter. I want to effectively shorten the ReadSampleData buffer. So that when I write out the new soundfont file using the Save function the file is shorter.

This is where I am stuck and need help.

I need to write out new sample metadata offsets (Start, End, StartLoop, EndLoop) and the modified raw sample data.

All help gratefully accepted. Here is the code so far.

Code: Select all

#include "../SF.h"

// abort compilation here if libsndfile not available
#if !HAVE_SNDFILE
#error "libsndfile not available!"
#error "(HAVE_SNDFILE is false)"
#endif

#include <sndfile.h>

using namespace std;

string GetSampleType(uint16_t type)
{
    switch (type)
    {
    case sf2::Sample::MONO_SAMPLE:
        return "Mono Sample";
    case sf2::Sample::RIGHT_SAMPLE:
        return "Right Sample";
    case sf2::Sample::LEFT_SAMPLE:
        return "Left Sample";
    case sf2::Sample::LINKED_SAMPLE:
        return "Linked Sample";
    case sf2::Sample::ROM_MONO_SAMPLE:
        return "ROM Mono Sample";
    case sf2::Sample::ROM_RIGHT_SAMPLE:
        return "ROM Right Sample";
    case sf2::Sample::ROM_LEFT_SAMPLE:
        return "ROM Left Sample";
    case sf2::Sample::ROM_LINKED_SAMPLE:
        return "ROM Linked Sample";
    default:
        return "Unknown";
    }
}

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        return EXIT_FAILURE;
    }

    try
    {
        RIFF::File *riff = new RIFF::File(argv[1]);
        sf2::File *sf = new sf2::File(riff);
        for (int i = 0; i < sf->GetSampleCount(); i++)
        {
            sf2::Sample *s = sf->GetSample(i);
            uint depth = (s->GetFrameSize() / s->GetChannelCount()) * 8;
            cout << std::dec << "Processing sample " << i << endl;            cout << "\t" << s->Name << " (Depth: " << depth;
            cout << ", SampleRate: " << s->SampleRate;
            cout << ", Pitch: " << ((int)s->OriginalPitch);
            cout << ", Pitch Correction: " << ((int)s->PitchCorrection) << endl;
            cout << "\t\tStart: " << s->Start << ", End: " << s->End;
            cout << ", Start Loop: " << s->StartLoop << ", End Loop: " << s->EndLoop << endl;
            cout << "\t\tSample Type: " << GetSampleType(s->SampleType) << ", Sample Link: " << s->SampleLink << ")" << endl;

            // Correct the sample offsets LoadSampleData loads the sample into the start of the buffer.
            int StartLoop = s->StartLoop - s->Start;
            int EndLoop = s->EndLoop - s->Start;
            int End = s->End - s->Start;

            // Get the sample data
            sf2::Sample::buffer_t sample_data = s->LoadSampleData();
            long frame_count = s->GetTotalFrameCount();


            // Only handle 16 bit samples for now
            if (16 != depth)
            {
                cout << "Sample is not 16 bit" << endl;
                continue;
            }

            uint16_t* samples16bit = (uint16_t*)sample_data.pStart;

            // Switching to hex output
            cout << std::hex;

            /*
                Ideal loop layout
                =================

                1. 8 samples before loop start sample, the last 4 samples of this section
                should be identical to the 4 samples before the loop end sample.

                2. loop start sample

                3. loop samples

                5. loop end sample

                6. 8 samples after loop end sample, the first 4 samples of this section
                should be identical to the 4 samples after the loop start sample.

                7. 24 null samples to accomodate differential algorithms.
            */

            // Print 8 samples around the loop start
            cout << "Samples surrounding loop start" << endl;
            for (int i = 0; i < 8 ; i++)
            {
                uint16_t* loopptr = &samples16bit[StartLoop - 4 + i];
                if (StartLoop - 4 + i > frame_count)
                {
                    cout << "frame_count exceeded" << endl;
                    break;
                }
                cout << samples16bit[StartLoop - 4 + i] << " " << endl;
                //uint16_t sample = *loopptr;
            }
            cout << endl;

            // Print 8 samples around the loop end
            cout << "Samples surrounding loop end" << endl;
            for (int i = 0; i < 8; i++)
            {
                if (EndLoop - 4 + i > frame_count)
                {
                    cout << "frame_count exceeded" << endl;
                    break;
                }
                cout << samples16bit[EndLoop - 4 + i] << " " << endl;
            }
            cout << endl;

            s->ReleaseSampleData();

        }

        // Write out the modified file
        sf->GetRiffFile()->Save("test.sf2");

        delete sf;
        delete riff;
    }
    catch (RIFF::Exception e)
    {
        e.PrintMessage();
        return EXIT_FAILURE;
    }
    catch (...)
    {
        cout << "Unknown exception while trying to parse file." << endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
roger@dragon:~$

wearyhacker
Newbie
Posts: 2
Joined: Thu Jun 27, 2024 4:30 pm

Re: Modifying and writing out a soundfont

Post by wearyhacker » Fri Jul 05, 2024 11:12 am

I have sorted out how to modify and save the sample data. But I am now struggling to save the modified sample header data.

I have tried modifying the sf2::Sample:Start, sf2::Sample::End, sf2::Sample:StartLoop and sf2::Sample::EndLoop properties of the sf2:Sample object. Then writing out the file. This does not work.

What is the correct way to force this data to be written out when the file is saved?
Do I have to create a copy of the SHDR subchunk, copy the the old data into it, modify it, then replace the original in the PDTA list.?

Thanks.

Post Reply