The class structure that I would like to use is..
-TModulePlayer -> Component to be used on form
-TSongData -> the Parent to classes such as TModData, TItData.
-TRenderer -> The parent to TWinRenderer, or TOpAlRender.
Any Special rendering required for a specific type such as S3m instruments Should be written into the renderer. Any comments?
-Jeremy
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Right.. The component will not rely on the form in any way. It will be able to be instantiated without an owner class assigned. The only reson really to make it a component is for formed projects that will allow some published properties to be modified.
-jeremy
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I can't say that I'm too happy with this structure's naming conventions. For one, 'TModulePlayer' should be 'TModuleMixer', 'TOpAlRender' should be 'TOpenALRender' and 'TITData' to 'TITData'. Acronyms should use all caps and we should try to use the full word or name if it is simple. This will help keep the code from getting cryptic and confusing to remember/code with.
Also do you mean to make the currently dubbed "TModulePlayer" into a TComponent or TObject class decendant? TObject should give us more than enough to work with and is slighty smaller too, I believe.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I think that the command to 'Render' the next audio feed from the curently loaded module should be ran from the actual declared object that it was loaded from.
ie. When using the library I would have code set something like this:
--- Start Of Code ---
program ThirdPartyModPlayer;
uses
openmod, {etc};
var
ModMixer: TModMixer; {<-- or whatever we call it}
AudioStream: TStream;
AudioBufferSize: Integer;
OutputBufferSize: Integer;
procedure Timer.Timer();
begin
if ({sound hardware is done playing last buffer}) then
begin
{get next audio buffer output frame}
if (not ModMixer.Rendering) then
OutputBufferSize := ModMixer.Render(AudioStream, AudioBufferSize); // if -1 then nothing to render
end;
end;
begin
ModMixer := TModMixer.Init({set things like what kind of mixer to use, etc});
end;
--- End Of Code ---
This is how I see the openMOD library to function effectively. Requiring only one object to ultimately be used in the end user's code. This is what I think we should shoot for. I know I wouldn't want to use it any other way.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In what I'm thinking you will still be able to do as you suggest WILL, I was just after a more uniform model. I.E. being able to use it on the form designer or being able to use it at runtime. If you want we could start with a lower object that will be more like you suggest that will descend from TObject and then build more of a jutbox component that can be used on a form and command the player component.
-Jeremy
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
This is actually waht I believed we were doing. Was it not? We can go further later on and make an actual player component, etc if we anted, but wouldn't it be better to make a simple loader/mixer model first and can be used on it's own? I think that this will allow us to have a more functional Magic Black Box effect rather than a complex picky monster on our hands. I've seen too many of these projects get dropped because of this effect.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Pattern: array of TPattern; {the blocks a modfile consists of}
Order: array of integer; {orders what patterns to play when 0=1 1=2 2=3 3=3 4=3 5=2}
Instruments: array of TSample; {the samples}
----------------
TPattern
consists of
Channel: array of TChannel {could be 0..3 for a 4 channel mod}
---------------
TChannel
consists of
Bar: array of Tbar {could be 0..63 for a 64 bar (note positions} long pattern}
Yes I think that that is the most logical of looks here is how I thing we should start. Lemme know of any other changes any would like to see. Obviously not all items are there yet but as I said I think this is the best start.
Code begins here
---------------------------------------
unit OpenMod;
interface
Uses
SysUtils, ModData;
type
TModState = (modPlaying, modStopped, modPaused, modLoaded);
TOpenMod = class(TObject)
private
FModData: TSongData;
FModRenderer: TModRenderer;
FState: TModState;
procedure SetState(const Value: TModState);
public
function Play: boolean;
function Pause: boolean;
function Stop: boolean;
function OpenFile(FileName: String): boolean;
constructor Create(AFilename: String); overload;
constructor Create; overload;
destructor Destroy; override;
property State: TModState read FState write SetState;
end;
var
//Globals.
implementation
{ TOpenMod }
constructor TOpenMod.Create(AFilename: String);
begin
//Create and open A file
end;
constructor TOpenMod.Create;
begin
//Create Without opening a file
end;
destructor TOpenMod.Destroy;
begin
//Free and Nil all Possable instantiated sub Object
inherited;
end;
function TOpenMod.OpenFile(FileName: String): boolean;
begin
//Open file return if a Data object could be created
end;
function TOpenMod.Pause: Boolean;
begin
//Pause playback
end;
function TOpenMod.Play: Boolean;
begin
//Play Data
end;
procedure TOpenMod.SetState(const Value: TModState);
begin
FState := Value;
end;
function TOpenMod.Stop: Boolean;
begin
//Stop playback.
end;
end.
unit ModData;
interface
Uses
Sysutils, Classes;
Type
TSongData = class(TObject)
protected
FData: TMemoryStream;
public
constructor Create(AFileName: String);
destructor Destroy;
end;
TModData = class(TSongData)
private
function GetName: String;
function GetSig: String;
public
property SongName: String Read GetName;
property Signature: String Read GetSig;
end;
implementation
{ TSongData }
constructor TSongData.Create(AFileName: String);
begin
FData := TMemoryStream.Create;
FData.LoadFromFile(AFilename);
end;
destructor TSongData.Destroy;
begin
FreeAndNil(FData);
end;
{ TModData }
function TModData.GetName: String;
var
tmpStr: String;
begin
tmpStr := StringOfChar(' ', 20);
FData.Seek(0,0);
FData.Read(tmpStr, 20);
Result := tmpStr;
end;
function TModData.GetSig: String;
var
tmpStr: String;
begin
tmpStr := StringOfChar(' ', 4);
FData.Seek(1080, 0);
FData.Read(tmpStr, 4);
Result := tmpStr;
end;
end.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The main reason jto keeping them in the same unit is so that anything we decide to put into the protected portion of TSongData is still accessable by derived classes. If they are seperated out the fdata needs a public accessor method. The other reason is I'm kinda using a borland convention here where all like classes are in the same unit.
-Jeremy
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
i think it is best to use loadfromstream as the main loading part. loadfromfile would be a wrapper around loadfromstream. That way it will be easier to load a mod from a zip file. (no need to write it to disk first)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I will change the class definition a little to allow that it's not a big change. Also if you would like the current version is in cvs now. I want to finish TmodData but any one can add TwhateverData kto it if you want. I will also make the change to TSongData also but it should not effect any other changes anyone would like to make.
-Jeremy
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hmm... I think we should be continuing this in the Developers forums.
But as for the CVS, I don't see any files posted. Maybe I'm not looking in the right place? Can you double check that it submitted properly? Thanks. :)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
You'll notice that I've added the S3MData.pas unit as a test(Finally got CVS configured properly). I have to double-check that this follows our set library structure plan(I may have had a small laps and placed the code in a wrong file). Anyhow... I am ready to do some coding now commits and all.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The class structure that I would like to use is..
-TModulePlayer -> Component to be used on form
-TSongData -> the Parent to classes such as TModData, TItData.
-TRenderer -> The parent to TWinRenderer, or TOpAlRender.
Any Special rendering required for a specific type such as S3m instruments Should be written into the renderer. Any comments?
-Jeremy
TModulePlayer should also function without a form e.g. console mode.
Right.. The component will not rely on the form in any way. It will be able to be instantiated without an owner class assigned. The only reson really to make it a component is for formed projects that will allow some published properties to be modified.
-jeremy
I can't say that I'm too happy with this structure's naming conventions. For one, 'TModulePlayer' should be 'TModuleMixer', 'TOpAlRender' should be 'TOpenALRender' and 'TITData' to 'TITData'. Acronyms should use all caps and we should try to use the full word or name if it is simple. This will help keep the code from getting cryptic and confusing to remember/code with.
Also do you mean to make the currently dubbed "TModulePlayer" into a TComponent or TObject class decendant? TObject should give us more than enough to work with and is slighty smaller too, I believe.
I think that the command to 'Render' the next audio feed from the curently loaded module should be ran from the actual declared object that it was loaded from.
ie. When using the library I would have code set something like this:
--- Start Of Code ---
program ThirdPartyModPlayer;
uses
openmod, {etc};
var
ModMixer: TModMixer; {<-- or whatever we call it}
AudioStream: TStream;
AudioBufferSize: Integer;
OutputBufferSize: Integer;
procedure Timer.Timer();
begin
if ({sound hardware is done playing last buffer}) then
begin
{get next audio buffer output frame}
if (not ModMixer.Rendering) then
OutputBufferSize := ModMixer.Render(AudioStream, AudioBufferSize); // if -1 then nothing to render
end;
end;
begin
ModMixer := TModMixer.Init({set things like what kind of mixer to use, etc});
end;
--- End Of Code ---
This is how I see the openMOD library to function effectively. Requiring only one object to ultimately be used in the end user's code. This is what I think we should shoot for. I know I wouldn't want to use it any other way.
In what I'm thinking you will still be able to do as you suggest WILL, I was just after a more uniform model. I.E. being able to use it on the form designer or being able to use it at runtime. If you want we could start with a lower object that will be more like you suggest that will descend from TObject and then build more of a jutbox component that can be used on a form and command the player component.
-Jeremy
This is actually waht I believed we were doing. Was it not? We can go further later on and make an actual player component, etc if we anted, but wouldn't it be better to make a simple loader/mixer model first and can be used on it's own? I think that this will allow us to have a more functional Magic Black Box effect rather than a complex picky monster on our hands. I've seen too many of these projects get dropped because of this effect.
-k- Well then I'll start with a loader like structure. I'll post an example when I've get a sec.
-Jeremy
Some thoughts on the TModuleData class details:
TModuledata
consists of
Pattern: array of TPattern; {the blocks a modfile consists of}
Order: array of integer; {orders what patterns to play when 0=1 1=2 2=3 3=3 4=3 5=2}
Instruments: array of TSample; {the samples}
----------------
TPattern
consists of
Channel: array of TChannel {could be 0..3 for a 4 channel mod}
---------------
TChannel
consists of
Bar: array of Tbar {could be 0..63 for a 64 bar (note positions} long pattern}
---------------
TBar
consists of
Instrument: integer;
Note: integer;
Effect: integer;
Param: integer;
---------------
Access to a note would be like:
module.pattern[module.order[0]].channel[0].bar[0].note
Of course the above classes should use properties for access but the exampele above is still valid.
Yes I think that that is the most logical of looks here is how I thing we should start. Lemme know of any other changes any would like to see. Obviously not all items are there yet but as I said I think this is the best start.
Code begins here
---------------------------------------
unit OpenMod;
interface
Uses
SysUtils, ModData;
type
TModState = (modPlaying, modStopped, modPaused, modLoaded);
TOpenMod = class(TObject)
private
FModData: TSongData;
FModRenderer: TModRenderer;
FState: TModState;
procedure SetState(const Value: TModState);
public
function Play: boolean;
function Pause: boolean;
function Stop: boolean;
function OpenFile(FileName: String): boolean;
constructor Create(AFilename: String); overload;
constructor Create; overload;
destructor Destroy; override;
property State: TModState read FState write SetState;
end;
var
//Globals.
implementation
{ TOpenMod }
constructor TOpenMod.Create(AFilename: String);
begin
//Create and open A file
end;
constructor TOpenMod.Create;
begin
//Create Without opening a file
end;
destructor TOpenMod.Destroy;
begin
//Free and Nil all Possable instantiated sub Object
inherited;
end;
function TOpenMod.OpenFile(FileName: String): boolean;
begin
//Open file return if a Data object could be created
end;
function TOpenMod.Pause: Boolean;
begin
//Pause playback
end;
function TOpenMod.Play: Boolean;
begin
//Play Data
end;
procedure TOpenMod.SetState(const Value: TModState);
begin
FState := Value;
end;
function TOpenMod.Stop: Boolean;
begin
//Stop playback.
end;
end.
unit ModData;
interface
Uses
Sysutils, Classes;
Type
TSongData = class(TObject)
protected
FData: TMemoryStream;
public
constructor Create(AFileName: String);
destructor Destroy;
end;
TModData = class(TSongData)
private
function GetName: String;
function GetSig: String;
public
property SongName: String Read GetName;
property Signature: String Read GetSig;
end;
implementation
{ TSongData }
constructor TSongData.Create(AFileName: String);
begin
FData := TMemoryStream.Create;
FData.LoadFromFile(AFilename);
end;
destructor TSongData.Destroy;
begin
FreeAndNil(FData);
end;
{ TModData }
function TModData.GetName: String;
var
tmpStr: String;
begin
tmpStr := StringOfChar(' ', 20);
FData.Seek(0,0);
FData.Read(tmpStr, 20);
Result := tmpStr;
end;
function TModData.GetSig: String;
var
tmpStr: String;
begin
tmpStr := StringOfChar(' ', 4);
FData.Seek(1080, 0);
FData.Read(tmpStr, 4);
Result := tmpStr;
end;
end.
My Code was indented properly :-/.....
Ah Well
I like it. Quick, post it before I start to get picky. ;)
Actually I'd have OpenFile be LoadFromFile to match with ObjectPascal method conventions. Uh oh.. already started. ;)
I do have one question though. Would it not be best to seperate each module format into it's own source file?
ie. TSongData in fdata.pas, TS3MData in fs3mdata.pas, T669Data in f669data.pas, TITData in fitdata.pas, etc...
For CVS and development purposes it may be easier for us to share work-loads and add new formats to the project.
The main reason jto keeping them in the same unit is so that anything we decide to put into the protected portion of TSongData is still accessable by derived classes. If they are seperated out the fdata needs a public accessor method. The other reason is I'm kinda using a borland convention here where all like classes are in the same unit.
-Jeremy
Yes we can chand to loadFromFile.
i think it is best to use loadfromstream as the main loading part. loadfromfile would be a wrapper around loadfromstream. That way it will be easier to load a mod from a zip file. (no need to write it to disk first)
also take a look at this structure: https://sourceforge.net/forum/message.php?msg_id=2363669
it also above here somewhere...
I will change the class definition a little to allow that it's not a big change. Also if you would like the current version is in cvs now. I want to finish TmodData but any one can add TwhateverData kto it if you want. I will also make the change to TSongData also but it should not effect any other changes anyone would like to make.
-Jeremy
Hmm... I think we should be continuing this in the Developers forums.
But as for the CVS, I don't see any files posted. Maybe I'm not looking in the right place? Can you double check that it submitted properly? Thanks. :)
You'll notice that I've added the S3MData.pas unit as a test(Finally got CVS configured properly). I have to double-check that this follows our set library structure plan(I may have had a small laps and placed the code in a wrong file). Anyhow... I am ready to do some coding now commits and all.