Link to home
Start Free TrialLog in
Avatar of PeterBaileyUk
PeterBaileyUk

asked on

unravel C++ function

I have a C++ function and want to convert it to Max but to do that I need to understand what its doing, I know its creating a special textual file for a piece of hardware.

I need help understand the file writing for want of a better word encoder.

#include "otwriter.h"

const unsigned char header_bytes[] = {0x46,0x4F,0x52,0x4D,0x00,0x00,0x00,0x00,0x44,0x50,0x53,0x31,0x53,0x4D,0x50,0x41};
const unsigned char unknown_bytes[] = {0x00,0x00,0x00,0x00,0x00,0x02,0x00};

OTWriter::OTWriter(const QString fileName, const int sampleRate, const Loop_t loopSetting, const Stretch_t strechSetting, const TrigQuant_t trigQuantSetting, int gain, int tempo)
{
    this->fileName = fileName;
    std::fill_n((char*)&otData, sizeof(otData), 0);
    memcpy(&otData, header_bytes, 16);
    memcpy(&otData.unknown, unknown_bytes, 7);

    this->tempo = tempo;
    this->sampleRate = sampleRate;
    this->gain = gain;
    this->loopSetting = loopSetting;
    this->stretchSetting = strechSetting;
    this->trigQuantSetting = trigQuantSetting;
}

void OTWriter::updateData()
{
    otData.tempo = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(tempo*6) : tempo*6;
    // calculate bars from BPM, sampleRate and audioLength and round off to closest .25
    int bars = ((tempo*totalSampleCount)/(sampleRate*60.0*4)) + 0.5;
    bars *= 25;
    otData.trimLen = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(bars) : bars;
    otData.loopLen = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(bars) : bars;
    otData.loop = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(loopSetting) : loopSetting;
    otData.stretch = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(stretchSetting) : stretchSetting;
    otData.quantize = trigQuantSetting;
    otData.gain = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ushort(gain + 48) : gain + 48;

    if (tempSlices.count() > 1)
    {
        for (int i = 0; i < tempSlices.count(); i++)
        {
            otData.slices[i].startPoint = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(tempSlices[i].startPoint) : tempSlices[i].startPoint;
            otData.slices[i].endPoint = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(tempSlices[i].endPoint) : tempSlices[i].endPoint;
            otData.slices[i].loopPoint = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(tempSlices[i].loopPoint) : tempSlices[i].loopPoint;
        }
        otData.sliceCount = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(tempSlices.count()) : tempSlices.count();
    }
    else
        otData.sliceCount = 0;
    otData.trimEnd = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ulong(totalSampleCount) : totalSampleCount;
    setChecksum();
}

void OTWriter::write(uint32_t totalSamples)
{
    this->totalSampleCount = totalSamples;
    updateData();
    QFile file(fileName);
    file.open(QIODevice::WriteOnly);
    QDataStream outStream(&file);

    outStream.writeRawData((char*)&otData.header, sizeof(otData.header));
    outStream.writeRawData((char*)&otData.unknown, sizeof(otData.unknown));
    outStream.writeRawData((char*)&otData.tempo, sizeof(otData.tempo));
    outStream.writeRawData((char*)&otData.trimLen, sizeof(otData.trimLen));
    outStream.writeRawData((char*)&otData.loopLen, sizeof(otData.loopLen));
    outStream.writeRawData((char*)&otData.stretch, sizeof(otData.stretch));
    outStream.writeRawData((char*)&otData.loop, sizeof(otData.loop));
    outStream.writeRawData((char*)&otData.gain, sizeof(otData.gain));
    outStream.writeRawData((char*)&otData.quantize, sizeof(otData.quantize));
    outStream.writeRawData((char*)&otData.trimStart, sizeof(otData.trimStart));
    outStream.writeRawData((char*)&otData.trimEnd, sizeof(otData.trimEnd));
    outStream.writeRawData((char*)&otData.loopPoint, sizeof(otData.loopPoint));
    outStream.writeRawData((char*)&otData.slices, sizeof(Slice)*64);
    outStream.writeRawData((char*)&otData.sliceCount, sizeof(otData.sliceCount));
    outStream.writeRawData((char*)&otData.checkSum, sizeof(otData.checkSum));
    file.close();
}

void OTWriter::setChecksum()
{
    uint16_t value = 0;
    unsigned char* bytePos = reinterpret_cast<unsigned char*>(&otData);
    for (int i = 16; i < sizeof(otData) - 2; i++)
        value += bytePos[i];

    otData.checkSum = SYSTEM_USE_LITTLE_ENDIAN ? _byteswap_ushort(value) : value;
}

void OTWriter::addSlice(uint32_t start, uint32_t end)
{
    Slice slice;
    slice.loopPoint = 0xFFFFFFFF;
    slice.startPoint = start;
    slice.endPoint = end;
    this->tempSlices.append(slice);
}

'*************************************************************

#ifndef OTWRITER_H
#define OTWRITER_H

#include <QString>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <stdint.h>
#include <intrin.h>

#define OTFILESIZE 832
#define SYSTEM_USE_LITTLE_ENDIAN 1

enum Loop_t { NoLoop = 0, Loop = 1, PIPO = 2 };
enum Stretch_t { NoStrech = 0, Normal = 2, Beat = 3 };
enum TrigQuant_t { Direct = 0xFF, Pattern = 0,S_1=1,S_2=2,S_3=3,S_4=4,S_6=5,S_8=6,S_12=7,S_16=8,S_24=9,S_32=10,S_48=11,S_64=12,S_96=13,S_128=14,S_192=15,S_256=16};

#pragma pack(push)
#pragma pack(1)
struct Slice
{
    uint32_t startPoint;
    uint32_t endPoint;
    uint32_t loopPoint;
};

struct OTData {
    uint8_t header[16];  // 0x00
    uint8_t unknown[7];  // 0x10 (All blank except 0x15 = 2)
    uint32_t tempo;      // 0x17 (BPM*24)
    uint32_t trimLen;    // 0x1B (value*100)
    uint32_t loopLen;    // 0x1F (value*100)
    uint32_t stretch;    // 0x23 (0ff = 0, Normal = 2, Beat = 3)
    uint32_t loop;       // 0x27 (Off = 0, Normal = 1, PingPong = 2)
    uint16_t gain;       // 0x2B (0x30 = 0, 0x60 = 24 (max), 0x00 = -24 (min))
    uint8_t quantize;    // 0x2D (0x00 = Pattern length, 0xFF = Direct, 1-10 = 1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256)
    uint32_t trimStart;  // 0x2E
    uint32_t trimEnd;    // 0x32
    uint32_t loopPoint;  // 0x36
    Slice slices[64];    // 0x3A - 0x339
    uint32_t sliceCount; // 0x33A
    uint16_t checkSum;   // 0x33E
};
#pragma pack(pop)

class OTWriter {

public:
    OTWriter(const QString fileName, const int sampleRate, const Loop_t loopSetting, const Stretch_t strechSetting, const TrigQuant_t trigQuantSetting, int gain, int tempo);
    void write(uint32_t totalSamples);
    void addSlice(uint32_t start, uint32_t end);
private:
    void updateData();
    void setChecksum();

    uint32_t totalSampleCount;
    QString fileName;
    int sampleRate;
    int tempo;
    int gain;
    Loop_t loopSetting;
    Stretch_t stretchSetting;
    TrigQuant_t trigQuantSetting;
    QVector<Slice> tempSlices;
    OTData otData;
};


#endif // OTWRITER_H

Open in new window

Avatar of NerdsOfTech
NerdsOfTech
Flag of United States of America image

This looks like this has music related functions

Looks like the Write function in particular outputs a music data file of type, tempo, loop segments, and other various attributes of the underlying music.
Hi Peter, Do you want a line by line explanation of code, or is there some particular function in the code pasted above you want us to explain? Along with C++, it is using QT libraries and data types as well for file writing. I have tried explaining each line of the write function below.

void OTWriter::write(uint32_t totalSamples)
{
    this->totalSampleCount = totalSamples; // This set the class member variable totalSampleCount to input passed.
    updateData();// Update the OTDATA which is what is written to your file
    QFile file(fileName);//declaring a QT File variable with file name required
    file.open(QIODevice::WriteOnly); // Open the file in write mode (access)
    QDataStream outStream(&file); // (Create a output stream and associate it to the file so that data written to stream is file sent to file)

    //Below lines of code write all the required member variables of OTDATA to te file by pass the address and size of each variable of OTData
    outStream.writeRawData((char*)&otData.header, sizeof(otData.header));
    outStream.writeRawData((char*)&otData.unknown, sizeof(otData.unknown));
    outStream.writeRawData((char*)&otData.tempo, sizeof(otData.tempo));
    outStream.writeRawData((char*)&otData.trimLen, sizeof(otData.trimLen));
    outStream.writeRawData((char*)&otData.loopLen, sizeof(otData.loopLen));
    outStream.writeRawData((char*)&otData.stretch, sizeof(otData.stretch));
    outStream.writeRawData((char*)&otData.loop, sizeof(otData.loop));
    outStream.writeRawData((char*)&otData.gain, sizeof(otData.gain));
    outStream.writeRawData((char*)&otData.quantize, sizeof(otData.quantize));
    outStream.writeRawData((char*)&otData.trimStart, sizeof(otData.trimStart));
    outStream.writeRawData((char*)&otData.trimEnd, sizeof(otData.trimEnd));
    outStream.writeRawData((char*)&otData.loopPoint, sizeof(otData.loopPoint));
    outStream.writeRawData((char*)&otData.slices, sizeof(Slice)*64);
    outStream.writeRawData((char*)&otData.sliceCount, sizeof(otData.sliceCount));
    outStream.writeRawData((char*)&otData.checkSum, sizeof(otData.checkSum));
    file.close();//Closes the file
}

Open in new window

Avatar of PeterBaileyUk
PeterBaileyUk

ASKER

It was more around the nature of the textfile itself so i can replicate what its doing in Cycling74 Max 7.

I know its creating slice point data relating to a .wav.
Ive made an waveform editor in Max and want to be able to create that textfile myself but not in C++ as I dont know C++ but yes your correct its creating a complimentary file .OT that goes alongside a sample so when the sampler sees the sample it can set the slice points.
Well, are you in a position to compile and execute this code and run with some sample data, which would in return generate the text file which you can view? Is this possible for you?
I have plenty of these files wait i send, normally I do this in my octatrack(this takes too long) but the code i posted joins wav files and then slices them and creates the file (i didnt write that application). This is not the action i want, I want to grab a bunch of wavs and set slice points and create the OT file.

Ive attached a .OT file

this was created by the octatrack.
its 120 BPM and has 16 slices

not sure if that helps, the guy who wrote the c++ app took a file like this, then amended to 8 slices then worked out the slicing algorhytm and then wrote the C++
BS2BassC1C2P35--2-.zip
ive linked to the app here, i dont think it breaks any ee rules..

https://drive.google.com/file/d/0B0N9blUzO0wuN0FJd3lMaVg2VEE/view?usp=sharing
Hi peter, I have got to compile and build this code, using the code in your first post (OTWriter.cpp and h), will you be in a position to give the following input values (to the functions below) so that I can generate the file?

1> OTWriter::OTWriter(const QString fileName, const int sampleRate, const Loop_t loopSetting, const Stretch_t strechSetting, const TrigQuant_t trigQuantSetting, int gain, int tempo)
2> void OTWriter::addSlice(uint32_t start, uint32_t end)
3> void OTWriter::write(uint32_t totalSamples)
here's a link to a wav file
https://drive.google.com/file/d/0B0N9blUzO0wuRTBubkpQYW5ia1k/view?usp=sharing

its name is as is in this wav
44100 for sample rate
tempo:120
gain:0
loop off i guess thats a 0
Timestretch off
trigquantdirect 0
16 for number of slices
slicemode steps
the wav in the link matches the .ot file that i sent earlier
Hi Peter,

Here are the two text files that I generated using the OTWRITER.CPP and H file that you shared, apologies for the delay, I was stuck with some other work. One sample -v1 is with addslice arguments start and end as 0 and 15, while -v2 is with addslice arguments start and end as 1 and 16. Hope they are of some use, I couldn't view any data as text in it, hopefully it is in some format that you can view or make some sense of :-), I am also sharing C++ code that I used to compile using C++ code blocks compiler which is free for use. In case you need the code that I used to generate this file. (https://drive.google.com/folderview?id=0B-PlEmGQASrTdG53dlpCbVUyS00&usp=sharing)

Please let me know if I could be of any further help.
Thanks,
Karrtik
BS2BassC1C2P351_v2.txt
BS2BassC1C2P351_v1.txt
I will load the wav and the ot file in the octatrack and I am sure itll be fine, I am almost not quite at a point where I need to understand the function themselves.

did you call the routine from your own main?

maybe I could replicates that first in visual studio? Ive just installed that but not sure how to set that up maybe thats a way for me to go so i can run through each step?
the delay is fine other people have their own lives too.
Yes I called from my own main, you can see the code which I have uploaded in google drive.
You can use the same code for a visual studio project as well. Once you have set up, it is pretty easy to use, I can even create a visual studio project for you to directly use and open in visual studio. Which version of visual studio are you targeting?
Thanks,
Karrtik
it says community 2015
Its starting with this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

Open in new window

What you are trying is a C# (.net)  console app, instead go for new project of C++ console application type.
The sampler didnt add any "slices" so I think the way to go is for me to set it up in my visual studio.
its starting like this as a console app

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}
here's my choices
ee.PNG
Go for visual C++ on left tree and select console application. Currently you have selected visual c# in left tree.
it wants to update stuff and its many gig so will be a while
ASKER CERTIFIED SOLUTION
Avatar of Karrtik Iyer
Karrtik Iyer
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial