I understand for example that a «string» is something like a one dimensional «array» [«vector»?!] of «objects» or «class»(?) called character\s (code table), represented by glyphs (graphical sub‑objects, assigned to that code table), loaded from PAM (permanent access memory) residing somewhere in RAM (random access memory).
You are right that a <string> is an <array of Char>(dyn array implementation in pascal). The Char is not an object! The Char represent a part of a character set in a codepage like ASCI, ANSI, UTF-8, UTF-16.
This codepage provides the byte length of one character. For Example: ASCII a Char has everytime the length of 1 Byte. UTF-8 a Char can be between 1 and 2 Bytes long.
That's why Mark asked you for this, because it is not easy to convert in such formats with special sets or it can be difficult.
For this reason FPC uses different Char implementatons, like a Char(synonym for AnsiChar) is only 1 Byte long and a WideChar is a UTF-16 char and is 2 Bytes long.
For clearifing the Char or WideChar specify his own simple data type and when a Char in the codepage ASCII has the value 10 then its an "A". Have a look at
https://tools.piex.at/ascii-tabelle/I assume you will write your own Font(-Family), which you will be load and show the notes like a music character set. The font in each programming language is like the visual style to show your character set, but I think you know this. For the developer means it, when you paint this characters on a control/component the Draw function will paint the character-set with the given font-set at this component/control. Each operating system have pre defined functions for this and they are cross-platform availible with FPC.
Objects and ClassesA class is structure defined by the developer. This structure is build to let the envoirment know, how much memory is needed to store all this informations.
In such a class you can define multiple data elements like the duration or a name or whatever you need and you can define methods (procedure or function) todo something with this data. As an Example: a Clear procedure to clear all the data in this class.
With this information you can create an object. The object is the pointer to the memory where all the information is stored.
Simple class example:
unit MusicNote;
{$mode ObjFPC}{$H+}
interface
// the uses refere to other classes and implementations which are usefull for us
uses
Classes, SysUtils, Generics.Collections;
type
{ TPitch }
TPitch = class(TObject)
procedure Clear;
end;
TPitchList = array of TPitch;
// we can't inherit from an array so we have todo all the work in functions and I think
// that's not really helpfull, only when we have really large lists to optimize the performance
// we define the class/structure of an object
TMusicNote = class(TObject)
public
// How to define a var in a class
//[Name]: [DataType];
Duration: Cardinal;
Name: string;
Pitch: TPitchList;
procedure Clear;
end;
// define an array of our object with an extra data type to make it easier for the future
TArrayMusicNote = array of TMusicNote;
// define a generic list of TMusicNote
TSMusicNoteList = specialize TObjectList<TMusicNote>;
//with the generic list we can inherite from it and write extra functions and more data elements if needed
TMusicNoteList = class(TSMusicNoteList)
public
// we use our clear example again
procedure ClearData;
// you can also define a methode to calc your pitch (I hope I understood it right)
procedure CalcPitch;
end;
procedure Example;
implementation
procedure Example;
var
ANoteList: TMusicNoteList;
ANote: TMusicNote;
begin
// lets create in the first an object of our music notes
// we use True as a parameter so the generic list will free the memory of our objects
// when we clear or destroy(free) it
ANoteList := TMusicNoteList.Create(True);
try
// now our list of objects is initialized and we can do something with it
// here I will show to create an element of our list and put it in the list
ANote := TMusicNote.Create;
ANote.Duration := 5;
ANote.Name := 'example_name';
ANoteList.Add(ANote);
finally
// at the end of the example we have to clear the memory to avoid memory leaks
// we don't have to free each object in the generic list, because the list
// will do it for us
FreeAndNil(ANoteList);
end;
end;
// here are the implemntation of methods of our classes
{ TPitch }
procedure TPitch.Clear;
begin
// do something usefull
end;
procedure TMusicNote.CalcPitch;
begin
// do what you need todo
end;
procedure TMusicNote.Clear;
var
i: Integer;
begin
//lets clear all data
Duration := 0;
Name := '';
// the array doesnt have a clear function so we need todo it here or define it
// in a helper class
for i := 0 to High(Pitch) do
Pitch[i].Clear;
end;
procedure TMusicNoteList.ClearData;
var
i: Integer;
begin
// we need to clear each element in the list, so we use a for loop.
// Each list will start with the element index 0 and we need to stop before the count is reached (Count - 1)
// Self is used to to show that we use our own object data. In some cases you don't need to use Self, but in other cases you have
// to use Self for this example I will use everytime Self so its easier
for i := 0 to Self.Count - 1 do
begin
// we need to call the Clear function of each object in the list Self[INDEX] will give us this object
Self[i].Clear;
end;
end;
end.
You described your pitch like a list of another class, so you have many options to create such a list.
As an <Array of TPitch> = dynamic array of a class or
as a generic list "TObjectList<TPitch>" the generic list have allready implementations of several functions which the array doesn't have and makes it easier to handle them,
but is it slower as an array. As long as you don't have more then 1000 objects in such a list you will not notice so much of a difference and you should have less to implement by your self.
I hope this will help you geting started