Click here to Skip to main content
15,895,709 members
Articles / Programming Languages / C#

SampleTagger, fast indexer for Universal Patch Finder

Rate me:
Please Sign up or sign in to vote.
4.20/5 (2 votes)
29 May 2013CPOL3 min read 16.1K   221   5  
How to index REX, RCY, RX2, AIFF, WAV audio files in Universal Patch Finder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SampleTagger.Log;
using System.Diagnostics;

namespace SampleTagger.Parsers
{
    /*
     * a RX2 file is like a WAV file with different chunk names.
     * 'RIFF' is 'CAT '
     * 'WAV ' is 'REX2'
     * 
     *  the file contain multiple chunks like HEAD,CREI,GLOB...
     *  
     * this code is based on Valek Filippov's 're-lab tools'
     * http://code.ohloh.net/file?fid=kOyQbUIgVr9uyAU4HDYff65Bejw&cid=3o-mFMH94ls&s=&browser=Default#L0
     * 
     */
    public class REXReader : BinaryFileReader
    {

        protected override void ReadContent()
        {
            ReadCAT();
        }
        public static bool isRex2File(String filename)
        {
            return filename.ToLower().EndsWith(".rx2");
        }
        private void ReadCAT()
        {
            String CAT = ReadID();
            int size = BigIndianReadInt32();
            String type = ReadID();
            if (CAT == "CAT " && type == "REX2")
            {
                for (; ; )
                {
                    bool eof = ReadChunk();
                    if (eof)
                        return;
                }
            }
        }
        private void Log(String msg)
        {
            Logger.Instance.Log(msg);
        }
        /*
        * read the next Chunk ID and parse it if
        * it is relevant for us
        */
        private bool ReadChunk()
        {
            if (Reader.BaseStream.Length == Reader.BaseStream.Position)
                return true;

            if (Reader.BaseStream.Length < Reader.BaseStream.Position)
                return true; // we should never be here if chunk size is valid


            String id = ReadID();
            if (id == null)
                return true;

            //Log(id);
            int size = BigIndianReadInt32();
            long pos = Reader.BaseStream.Position;

            // sanity check
            if (pos + size > Reader.BaseStream.Length)
            {
                int newsize = (int)(Reader.BaseStream.Length - pos);
                Log("REX bad chunk size " + id + " size:" + size + " replaced by " + newsize);
                size = newsize;
            }

            if (id == "SINF")
            {
                ReadSINF();
            }
            if (id == "HEAD")
            {
                ReadHEAD();
            }
            else if (id == "GLOB")
            {
                ReadGLOB();   
            }
            // DEVLTRSH: effects envelope
            Log("Jump over Chunk " + id + " " + size + " bytes to " + pos + size);
            try
            {
                Reader.BaseStream.Seek(pos + size, SeekOrigin.Begin);
            }
            catch (Exception)
            {
                return true;
            }

            return false;
        }

        /*
         * CHUNK HEAD
         * 
         * contain bitdepth
         */
        private void ReadHEAD()
        {
            int v1 = ReadInt8();
            int v2 = ReadInt8();
            int v3 = ReadInt8();
            int v4 = ReadInt8();
            int v5 = ReadInt8();
            int bytepersample = ReadInt8();
            info.AddField(PredefinedPatchAttribute.BITDEPTH, bytepersample * 8);
        }

        /*
         * CHUNK SINF
         * 
         * contain audio format
         * 
         */
        private void ReadSINF()
        {
            int channels = ReadInt8();
            int v2 = ReadInt8();
            int sampleRate = BigIndianReadInt32();
            int v3 = BigIndianReadInt32();
            int v4 = BigIndianReadInt32();
            int v5 = BigIndianReadInt32();
            info.AddField(PredefinedPatchAttribute.SAMPLERATE, sampleRate);
        }
        /*
         * CHUNK GLOB
         * 
         * contain loop metadata
         */
        private void ReadGLOB()
        {
            int v1 = ReadInt8();
            int v2 = ReadInt8();
            int v3 = ReadInt8();
            int v4 = ReadInt8();
            int v5 = ReadInt8();
            int bars = ReadInt8();
            int beat_length = ReadInt8();
            int fTimeSignNom = ReadInt8();
            int fTimeSignDenom = ReadInt8();
            int sensibilty = ReadInt8();
            int gate_sensibility = BigIndianReadInt16();
            /*
             *  gain ranges from -inf then -60db to +18db
             *    -inf is stored as 00 00
             *    -60db is stored as 00 01
             *    -54db is stored as 00 02
             *    -50.5db is stored as 00 03
             *    -48db is stored as 00 04
             *    -46db is stored as 00 05
             *    -44.4db is stored as 00 06
             *    -38.4db is stored as 00 0C
             *    -25.8db is stored as 00 33
             *    -20.8db is stored as 00 5B
             *    -19.5db is stored as 00 6A
             *    -17.6db is stored as 00 84
             *    -12.6db is stored as 00 EA
             *    -9.3db is stored as 01 57
             *    -3.9db is stored as 02 7D
             *    0db is stored as 03 E8 (1000)
             *    18db is stored as 1F 07 (7943)
             *
             */
            int gain = BigIndianReadInt16();
            /*
             * pitch has 2 decimal places but is stored as an integer
             * if the number displayed is 5.75, it will be stored as an integer 575
             */
            int pitch = BigIndianReadInt16();
            float tempo = BigIndianReadInt32() / 1000.0f;
            int exportMultiSamples = ReadInt8();
            int silenceSelected = ReadInt8();

            info.AddField(PredefinedPatchAttribute.TEMPO, tempo);
            info.AddField(PredefinedPatchAttribute.BARS, bars);
            info.AddField(PredefinedPatchAttribute.BEATS, beat_length);
            info.AddField(PredefinedPatchAttribute.TIME_SIGNATURE, fTimeSignNom + "/" + fTimeSignDenom);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions