Looped SFZ instruments

Link to good samples/soundfonts at http://wiki.linuxaudio.org/wiki/free_audio_data

Moderators: khz, MattKingUSA

Lyberta
Established Member
Posts: 681
Joined: Sat Nov 01, 2014 8:15 pm
Location: The Internet

Looped SFZ instruments

Postby Lyberta » Sat Sep 29, 2018 11:40 pm

So I'm trying to learn how to create looped SFZ instruments. Here it says that by default this info comes from the audio file. Does this mean it reads WAV chunks? What kind of editor can create those?

I've tried to put just loop_mode=loop_continuous but LinuxSampler refuses to loop this. Does this mean I need full info inside WAV or manually specify start and end in SFZ?

j_e_f_f_g
Established Member
Posts: 1045
Joined: Fri Aug 10, 2012 10:48 pm

Re: Looped SFZ instruments

Postby j_e_f_f_g » Sun Sep 30, 2018 3:38 am

loop_mode=continuous, without also specifying start and end, means that the looping info is embedded in your wave file.

Obviously that isn't the case.

So either include start and end...

... or get a good wave editor that allows you to set/save loop points (not Audacity -- it deletes them), with a (preferably 50%) crossfade algorithm (Waveosaur).

Lyberta
Established Member
Posts: 681
Joined: Sat Nov 01, 2014 8:15 pm
Location: The Internet

Re: Looped SFZ instruments

Postby Lyberta » Sun Sep 30, 2018 2:59 pm

j_e_f_f_g wrote:... or get a good wave editor that allows you to set/save loop points (not Audacity -- it deletes them), with a (preferably 50%) crossfade algorithm (Waveosaur).


Wavosaur is Windows only.

j_e_f_f_g
Established Member
Posts: 1045
Joined: Fri Aug 10, 2012 10:48 pm

Re: Looped SFZ instruments

Postby j_e_f_f_g » Sun Sep 30, 2018 7:04 pm

Lyberta wrote:Wavosaur is Windows only.


And it's also the single best tool for looping waves I've ever used. I'd advise you to try to get it running under Wine first, before wasting time on inferior tools.

User avatar
bhilmers
Established Member
Posts: 155
Joined: Mon Apr 23, 2012 11:44 pm

Re: Looped SFZ instruments

Postby bhilmers » Mon Oct 01, 2018 1:59 am

j_e_f_f_g wrote:
Lyberta wrote:Wavosaur is Windows only.
And it's also the single best tool for looping waves I've ever used. I'd advise you to try to get it running under Wine first, before wasting time on inferior tools.

Ditto. Wavosaur is the best free audio editor I've ever used and it's been running perfectly in WINE for several years. I do most of my editing with it. The project is on the verge of abandonware and I hope the authors release the source.

Lyberta
Established Member
Posts: 681
Joined: Sat Nov 01, 2014 8:15 pm
Location: The Internet

Re: Looped SFZ instruments

Postby Lyberta » Mon Oct 01, 2018 3:24 am

Ok, I'm not interesting in proprietary Windows software (ugh, double whammy). Can someone say what chunks are needed? I will plug them into my WAV reading/writing code.

antiesen
Established Member
Posts: 148
Joined: Sat Aug 27, 2011 3:36 pm

Re: Looped SFZ instruments

Postby antiesen » Mon Oct 01, 2018 5:26 am

I welcome the attitude not to use system-foreign programs To your first question:
Maybe a little overdone, but i would recommend polyphone. You can loop inside the programm and export wav, sf2 and sfz.
This was made to end all partys - Einstürzende Neubauten 1985

nilshi
Established Member
Posts: 286
Joined: Wed Oct 22, 2008 9:05 pm
Contact:

Re: Looped SFZ instruments

Postby nilshi » Mon Oct 01, 2018 1:50 pm

Do not use the .wav built-in loop format. It is very fragile and practically not supported.
Always use external meta data, in this case sfz loop.

Lyberta
Established Member
Posts: 681
Joined: Sat Nov 01, 2014 8:15 pm
Location: The Internet

Re: Looped SFZ instruments

Postby Lyberta » Tue Oct 02, 2018 6:04 pm

antiesen wrote:I welcome the attitude not to use system-foreign programs To your first question:
Maybe a little overdone, but i would recommend polyphone. You can loop inside the programm and export wav, sf2 and sfz.


Argh, it had Debian Stable build but it is too old for my Debian Testing.

falkTX wrote:This part of SFZero references Loop0Start and Loop0End.
https://github.com/altalogix/SFZeroModu ... le.cpp#L27


Ok, it uses JUCE to read the chucks. That's fine but at this moment I'm not very interested in reading through JUCE (there are also license problems, right?).

nilshi wrote:Do not use the .wav built-in loop format. It is very fragile and practically not supported.
Always use external meta data, in this case sfz loop.


Sure but I need to support those to fully support the SFZ format.



Ok, here's a single period of 440Hz sine wave, just 99 frames. Can someone put loop start at frame 0 and loop end at frame 99? Then I will figure out how to read this file with hex editor and C++.
You do not have the required permissions to view the files attached to this post.

antiesen
Established Member
Posts: 148
Joined: Sat Aug 27, 2011 3:36 pm

Re: Looped SFZ instruments

Postby antiesen » Tue Oct 02, 2018 7:49 pm

So here are the examples. I have set the loop end to 99, but should actually be at 100 to loop the whole thing.
Then another one with differet loop-settings an one with changed root-key and detuning.
You do not have the required permissions to view the files attached to this post.
This was made to end all partys - Einstürzende Neubauten 1985

j_e_f_f_g
Established Member
Posts: 1045
Joined: Fri Aug 10, 2012 10:48 pm

Re: Looped SFZ instruments

Postby j_e_f_f_g » Tue Oct 02, 2018 9:20 pm

Let me save you some time:

Code: Select all

#ifndef O_NOATIME
#define O_NOATIME        01000000
#endif

#pragma pack(1)
/////////////////////// WAVE File Stuff /////////////////////
// An IFF file header looks like this
typedef struct
{
   unsigned char   ID[4];   // could be {'R', 'I', 'F', 'F'} or {'F', 'O', 'R', 'M'}
   uint32_t         Length;   // Length of subsequent file (including remainder of header). This is in
                     // Intel reverse byte order if RIFF, Motorola format if FORM.
   unsigned char   Type[4];   // {'W', 'A', 'V', 'E'} or {'A', 'I', 'F', 'F'}
} FILE_head;

// An IFF chunk header looks like this
typedef struct
{
   unsigned char   ID[4];   // 4 ascii chars that is the chunk ID
   uint32_t         Length;   // Length of subsequent data within this chunk. This is in Intel reverse byte
                     // order if RIFF, Motorola format if FORM. Note: this doesn't include any
                     // extra byte needed to pad the chunk out to an even size.
} CHUNK_head;

// WAVE fmt chunk
typedef struct {
   short         wFormatTag;
   unsigned short   wChannels;
   uint32_t         dwSamplesPerSec;
   uint32_t         dwAvgBytesPerSec;
   unsigned short   wBlockAlign;
   unsigned short   wBitsPerSample;
  // Note: there may be additional fields here, depending upon wFormatTag
} FORMAT;

typedef struct {
   FILE_head      head;
   CHUNK_head      fmt;
   FORMAT         fmtdata;
} WAVESTART;

// WAVE Sample Loop struct
typedef struct
{
   int32_t         dwIdentifier;
   int32_t         dwType;
   uint32_t         dwStart;
   uint32_t         dwEnd;
   int32_t         dwFraction;
   int32_t         dwPlayCount;
} SAMPLELOOP;

// WAVE Smpl chunk
typedef struct
{
   int32_t   dwManufacturer;
   int32_t   dwProduct;
   int32_t   dwSamplePeriod;
   int32_t   dwMIDIUnityNote;
   int32_t   dwMIDIPitchFraction;
   int32_t   dwSMPTEFormat;
   int32_t   dwSMPTEOffset;
   int32_t   cSampleLoops;
   int32_t   cbSamplerData;
} SAMPLER;

typedef struct {
  int32_t         dwIdentifier;
  uint32_t         dwPosition;
  unsigned char   fccChunk[4];
  uint32_t         dwChunkStart;
  uint32_t         dwBlockStart;
  uint32_t         dwSampleOffset;
} CUELOOP;
#pragma pack()

static const unsigned char Cue[4] = { 'c', 'u', 'e', ' ' };
static const unsigned char Smpl[4] = { 's', 'm', 'p', 'l' };
static const unsigned char Data[4] = { 'd', 'a', 't', 'a' };

/********************** compareID() *********************
 * Compares the passed ID str (ie, a ptr to 4 Ascii
 * bytes) with the ID at the passed ptr. Returns TRUE if
 * a match, FALSE if not.
 */

static unsigned char compareID(const unsigned char * id, unsigned char * ptr)
{
   register unsigned char i = 4;

   while (i--)
   {
      if ( *(id)++ != *(ptr)++ ) return 0;
   }
   return 1;
}

typedef struct {
   uint32_t   DataLength;
   uint32_t   LoopStart;
   uint32_t   LoopEnd;
} MY_WAVE_INFO;

uint32_t readWave(const char * fn)
{
   WAVESTART            file;
   FILE_head            head;
   MY_WAVE_INFO         myInfo;
   uint32_t               action;
   register int         inHandle;

   // Open the WAVE
   inHandle = open(fn, O_RDONLY|O_NOATIME);

   // Read in IFF File header and fmt chunk
   read(inHandle, &file, sizeof(WAVESTART));

   // Assume no loop
   myInfo.LoopStart = myInfo.LoopEnd = (uint32_t)-1;

   myInfo.DataLength = 0;

   // Skip any extra fmt bytes
   head.Length = file.fmt.Length - sizeof(FORMAT);
   goto skip;

   while (read(inHandle, &head, sizeof(CHUNK_head)) == sizeof(CHUNK_head))
   {
      // ============================ Is it a data chunk? ===============================
      if (compareID(&Data[0], &head.ID[0]))
      {
         read(inHandle, wavePtr, head.Length);
         myInfo.DataLength = head.Length;
      }

      // ============================ Is it an smpl chunk? ===============================
      else if (compareID(&Smpl[0], &head.ID[0]))
      {
         SAMPLER               smpl;
         SAMPLELOOP            smploop;

         // Read in SAMPLER
         read(inHandle, &smpl, sizeof(SAMPLER));
         head.Length -= sizeof(SAMPLER);

         // Use only the first loop
         if (smpl.cSampleLoops)
         {
            // Read in sample loop
            read(inHandle, &smploop, sizeof(SAMPLELOOP));

            myInfo.LoopStart = smploop.dwStart;
            myInfo.LoopEnd = smploop.dwEnd;

            head.Length -= sizeof(SAMPLELOOP);
         }

         // Skip remainder of chunk
         goto skip;
      }

      // ============================ Is it a cue chunk? ===============================
      else if (compareID(&Cue[0], &head.ID[0]))
      {
         // Give preference to the smpl chunk if we found one
         if (myInfo.LoopStart == (uint32_t)-1)
         {
            CUELOOP         cueloop;

            // Read in dwCuePoints
            read(inHandle, &action, sizeof(uint32_t));
            head.Length -= sizeof(uint32_t);

            // Use only the first loop
            if (action)
            {
               // Read in cue loop
               read(inHandle, &cueloop, sizeof(CUELOOP));

               myInfo.LoopStart = cueloop.dwPosition * (file.fmtdata.wBitsPerSample == 16 ? 2 : 4);   // note: We don't care about 8-bit files
               myInfo.LoopEnd = myInfo.DataLength / file.fmtdata.wChannels;

               head.Length -= sizeof(CUELOOP);
            }
         }

         // Skip remainder of chunk
         goto skip;
      }

      // ============================ Skip this chunk ===============================
      else
      {
skip:      if (head.Length & 1) ++head.Length;  // If odd, round it up to account for pad byte
         lseek(inHandle, head.Length, SEEK_CUR);
      }
   }

   // We got the data? Note: size in bytes
   if (myInfo.DataLength && myInfo.LoopStart != (uint32_t)-1)
   {
      // Offsets in bytes
      myInfo.LoopStart *= ((file.fmtdata.wBitsPerSample == 16 ? 2 : 4) * file.fmtdata.wChannels);
      myInfo.LoopEnd *= ((file.fmtdata.wBitsPerSample == 16 ? 2 : 4) * file.fmtdata.wChannels);
      if (myInfo.LoopStart != myInfo.LoopEnd && myInfo.LoopStart < myInfo.DataLength)
      {
         if (myInfo.LoopEnd > myInfo.DataLength) myInfo.LoopEnd = myInfo.DataLength;
         if (myInfo.LoopStart > myInfo.LoopEnd)
         {
            register uint32_t   temp;

            temp = myInfo.LoopEnd;
            myInfo.LoopEnd = myInfo.LoopStart;
            myInfo.LoopStart = temp;
         }
      }
   }

   close(inHandle);
}

j_e_f_f_g
Established Member
Posts: 1045
Joined: Fri Aug 10, 2012 10:48 pm

Re: Looped SFZ instruments

Postby j_e_f_f_g » Tue Oct 02, 2018 9:36 pm

Note: I stripped out the error checking to make it more readable. Do not use in production code without all the error checks on open() and read() and lseek(), and checking chunk size against file size for corruption.

j_e_f_f_g
Established Member
Posts: 1045
Joined: Fri Aug 10, 2012 10:48 pm

Re: Looped SFZ instruments

Postby j_e_f_f_g » Wed Oct 03, 2018 7:31 pm

falkTX wrote:We cant just borrow code like that... what license is that in?
Where did it came from, did you wrote it yourself or copied from somewhere else?


I wrote it. It originally came from a Windows utility i wrote/released two decades ago called Aiff2Wav (convert between WAVE and apple AIFF formats), and has been subsequently used in many of my other projects, most recently BackupBand's ConvertWave utility. The latter is GPL. (Considering BSD going forward, because the "amateur gpl licensing police" have become more of an actual, real-world annoyance than anyone who has ever "borrowed" my code. Seriously.) But the above snippet is so utterly basic, simple, short, and limited in scope that i consider it public domain. It would be the epitome of hubris for a developer to think the above snippet should merit copyright. And i ain't one of those a-holes.

If someone wants it, use it.


Return to “Samplers & samples”

Who is online

Users browsing this forum: No registered users and 1 guest