How to handle this OOP/inheritance problem?

This is a discussion on How to handle this OOP/inheritance problem? within the Pascal forums in Programming Languages category; For a hobbyist project, I'm writing a music composer, and I really want to make it object-oriented because the data really lends itself to OOP for reasons I'll explain later. The only problem is that, while I'm a competent Pascal programmer, I've never done OOP before. My environment is Turbo Pascal 7.0. Before I lay out the details, I want to stress this is not a homework assignment :-) I am not asking for code, just the normal, best-practices way to do what I want to accomplish. I thought of creating a "song" object with the ability to enter notes, ...

Go Back   Application Development Forum > Programming Languages > Pascal

Object Mix

Register FAQ Calendar Search Today's Posts Mark Forums Read
  #1  
Old 02-04-2008, 08:18 PM
Jim Leonard
Guest
 
Default How to handle this OOP/inheritance problem?

For a hobbyist project, I'm writing a music composer, and I really
want to make it object-oriented because the data really lends itself
to OOP for reasons I'll explain later. The only problem is that,
while I'm a competent Pascal programmer, I've never done OOP before.
My environment is Turbo Pascal 7.0. Before I lay out the details, I
want to stress this is not a homework assignment :-) I am not asking
for code, just the normal, best-practices way to do what I want to
accomplish.

I thought of creating a "song" object with the ability to enter notes,
save/load the song data, etc because it makes perfect sense; the
methods can validate the data and generally abstract the song. But
the song needs to play, and that's where I get stuck on the proper way
to program in OOP to access the song data. Should I:

- Make a new "player" object that inherits the song object (so that it
can directly reference the song data structures)?
- Make a new "player" object that calls the song object's methods to
pass the data back and forth?
- Have the song object pass a pointer to the note data to be accessed
by the player object? (that seems like it's defeating the whole
purpose of OOP)
- Build the playback routines directly into the song object, so that I
have a single object with a gazillion methods?

The program needs to be able to play the song, not only for one
particular output device, but several. That means the playback
routines need to be VIRTUAL so that I can replace them with methods
for additional sound devices as I code them. It is that requirement
that has me confused about the proper direction to take.

I was leaning toward this:
A song object;
a player object that inherits the song object and contains only
generic VIRTUAL playback methods;
a device-specific object that replaces the virtual methods with
methods for that device's hardware.
I figure that way I can get direct access to the song's data
structures without "exposing them to the outside", and I can keep
building on previous code as necessary.

Does that sound like the right way to go, or am I barking up the wrong
tree?
Reply With Quote
  #2  
Old 02-05-2008, 02:17 PM
Jason Burgon
Guest
 
Default Re: How to handle this OOP/inheritance problem?

----- Original Message -----
From: "Jim Leonard" <MobyGamer@gmail.com>

> Should I:
>
> - Make a new "player" object that inherits the song object (so that it
> can directly reference the song data structures)?
> - Make a new "player" object that calls the song object's methods to
> pass the data back and forth?
> - Have the song object pass a pointer to the note data to be accessed
> by the player object? (that seems like it's defeating the whole
> purpose of OOP)
> - Build the playback routines directly into the song object, so that I
> have a single object with a gazillion methods?


You should definately have different objects for the player and the song
because:

(1) The song object encapsulates a data element -TSong.
(2) The Playback object represents [an interface to] a device driver -
TPlayer.

The TPlayer should be an abstract object so that, as you say, you can
virtualize the player details. TSong might also want to be an abstract type
as well, so that different song types can be played by the same player type.

Now you have to decide what the interface ("contract") between the TSong and
the TPlayer is going to be. Ideally, only one of them at most should "know"
about the other. It might be better for both to be ignorant of each other,
and use a third object to transport the data from the TSong to the TPlayer.
Some sort of stream object comes mind for this role.

> I was leaning toward this:
> A song object;


Yes.

> a player object that inherits the song object and contains only
> generic VIRTUAL playback methods;


I wouldn't. Use a different object for the player. Ah, I think you don't
mean "inherits", but rather "contains". Yes, perhaps. But then you have a
1:1 relationship between Song and Player, so I would probalbly do it use a
stream object as described above.

> a device-specific object that replaces the virtual methods with
> methods for that device's hardware.


Yes.

> I figure that way I can get direct access to the song's data
> structures without "exposing them to the outside", and I can keep
> building on previous code as necessary.


You could also use a procedural "data-getter" method of TSong that retrieves
data from the Song and stores it in a buffer of the TPlayer. The TSong (or a
3rd party) would pass the function to the TPlayer, which would then call it
whenever it needs "feeding".

Jay
--

Jason Burgon - author of Graphic Vision
http://homepage.ntlworld.com/gvision


Reply With Quote
  #3  
Old 02-05-2008, 04:01 PM
Femme Verbeek
Guest
 
Default Re: How to handle this OOP/inheritance problem?

Jim Leonard schreef:
> For a hobbyist project, I'm writing a music composer, and I really
> want to make it object-oriented because the data really lends itself
> to OOP for reasons I'll explain later. The only problem is that,
> while I'm a competent Pascal programmer, I've never done OOP before.
> My environment is Turbo Pascal 7.0. Before I lay out the details, I
> want to stress this is not a homework assignment :-) I am not asking
> for code, just the normal, best-practices way to do what I want to
> accomplish.
>
> I thought of creating a "song" object with the ability to enter notes,
> save/load the song data, etc because it makes perfect sense; the
> methods can validate the data and generally abstract the song. But
> the song needs to play, and that's where I get stuck on the proper way
> to program in OOP to access the song data. Should I:
>
> - Make a new "player" object that inherits the song object (so that it
> can directly reference the song data structures)?
> - Make a new "player" object that calls the song object's methods to
> pass the data back and forth?
> - Have the song object pass a pointer to the note data to be accessed
> by the player object? (that seems like it's defeating the whole
> purpose of OOP)
> - Build the playback routines directly into the song object, so that I
> have a single object with a gazillion methods?
>
> The program needs to be able to play the song, not only for one
> particular output device, but several. That means the playback
> routines need to be VIRTUAL so that I can replace them with methods
> for additional sound devices as I code them. It is that requirement
> that has me confused about the proper direction to take.
>
> I was leaning toward this:
> A song object;
> a player object that inherits the song object and contains only
> generic VIRTUAL playback methods;
> a device-specific object that replaces the virtual methods with
> methods for that device's hardware.
> I figure that way I can get direct access to the song's data
> structures without "exposing them to the outside", and I can keep
> building on previous code as necessary.
>
> Does that sound like the right way to go, or am I barking up the wrong
> tree?


Interesting project. Controlling the PC sound possibilities in TP or BP
is not easy. In don't know how you would organise this. Perhaps Delphi
will give you more possibilities here. But you are very right that this
kind of program requires OOP.

If you can get your hands on a copy of Borland Pascal with objects V7.01
I would strongly recommend this instead of TP7. Try to find a legal
copy, but for educational projects no one will care if you try to find
the BP.Zip installation file somewhere

Inheritance is very handy if there are several enteties that all need to
be treated more or less in the same way.

E.g. A note and a tone have the parameter "duration" in common, but a
note has the added parameters heigth, volume, rampup and rampdown etc.
So it is logical that TNote is a descendant of TTone, which in itself
descends from Tobject. Both will have a play method that can be used,
but TTone will override the Play method of TNote.

Next is to define the Melody object, which can best be a descendant of
Tcollection. The collection can be filled with objects either being note
or tone.

The common factor between violin, piano and drum is that they are all
different kinds of musical instruments. So if you want to load them in a
player there should be an inherited common method e.g. Play.

type
PInstrument:^TInstrument;
TInstrumentbject(tobject)
melody:tmelody;
constructor init:
destructor done;
procedure play; virtual;
end;


implementation

constructor TInstrument.init;
begin
melody.init;
end;

Procedure TInstrument.play;
begin
abstract; {Always to be overridden by a descendant}
end;

destructor TInstrument.done
begin
melody.done;
end;


The abstract statement will generate an error on execution. This
procedure should always be overwritten by the actual instruments of
piano, violin and drum which are descendants of TInstrument. Each of
them holds different sound samples and different ways on how to connect
the tone and duration to the sound sample.

The player will probably have a list of instruments that can all play
simulataneously. In order to call a method of an instrument of unknown
implementation, you can simply call the abstract method of the parent.

Something like

Procedure tplayer.play
procedure plee(pinstrument);far;
begin
p^.play;
end;
begin
foreach(@plee);
end:


But the puzzle is entirely yours. There are more ways to do what you want.

Good luck

-- Femme

Reply With Quote
  #4  
Old 02-06-2008, 12:50 PM
Jim Leonard
Guest
 
Default Re: How to handle this OOP/inheritance problem?

On Feb 5, 1:17 pm, "Jason Burgon" <jayn...@ntlworld.com> wrote:
> You should definately have different objects for the player and the song
> because:
>
> (1) The song object encapsulates a data element -TSong.
> (2) The Playback object represents [an interface to] a device driver -
> TPlayer.


Actually, there are basic song handling routines that are common to
playing *any* song, such as "slide this note upward each tick for a
portamento", etc. The device-specific routines would come later, so
that's why I was leaning toward something like:

song = object
player = object(song)
playSpk = object(player)
playAdlib = object(player)
playGMIDI = object(player)

That sort of thing. If you're familiar with PC MOD players/trackers,
that's the same idea; there are song maintenance operations to be done
on every interrupt tick, but those operations are independent of the
actual output device used.

> TSong might also want to be an abstract type
> as well, so that different song types can be played by the same player type.


While that's a good design decision, it is quite out of scope for this
project The goal was to create an editor/player for very low-
resource machines, so the song data format is actually a big part of
the design -- I'll explain later in this post.

> Now you have to decide what the interface ("contract") between the TSong and
> the TPlayer is going to be. Ideally, only one of them at most should "know"
> about the other. It might be better for both to be ignorant of each other,
> and use a third object to transport the data from the TSong to the TPlayer.
> Some sort of stream object comes mind for this role.


If I didn't care about performance, I would agree this is the most
proper way to do it. However, the target platform for this project is
-- don't get mad -- an original IBM PC, which is a 4.77MHz 8088.
(Most people hit *DELETE* after reading that, but I like challenges in
my hobby...) Not only that, but the music must play at the same time
many other things are going on, so player performance is extremely
important. It's important enough that I started this project with the
song data format, because I wanted to be able to retrieve a note in a
single data access (if you're curious, each note+effects is only 2
bytes, which I plan to fetch with LODSW and then do all processing in
registers).

This is why I was trying to figure out the most efficient way to pass
data between objects -- ideally, there would be no passing at all (ie.
the generic player object would be able to read the song data
directory without passing it on the stack or through a stream). So,
with this in mind, do you think my idea (see above) is the best
compromise?
Reply With Quote
  #5  
Old 02-06-2008, 03:07 PM
Jim Leonard
Guest
 
Default Re: How to handle this OOP/inheritance problem?

On Feb 5, 3:01 pm, Femme Verbeek <fv2...@nospam.tcenl.com> wrote:
> If you can get your hands on a copy of Borland Pascal with objects V7.01
> I would strongly recommend this instead of TP7. Try to find a legal
> copy, but for educational projects no one will care if you try to find
> the BP.Zip installation file somewhere


I have owned BP7 on CDROM since the day it came out, but I must use
TP7 because of my target platform, which is... let's just say, very
small and slow :-) See my reply to Jason for more details on the
target platform, which is a driving factor in the design.

> E.g. A note and a tone have the parameter "duration" in common, but a
> note has the added parameters heigth, volume, rampup and rampdown etc.
> So it is logical that TNote is a descendant of TTone, which in itself
> descends from Tobject. Both will have a play method that can be used,
> but TTone will override the Play method of TNote.


I hadn't thought of breaking it down that granularly -- that's an
interesting idea, I'll have to think about that.

> Next is to define the Melody object, which can best be a descendant of
> Tcollection. The collection can be filled with objects either being note
> or tone.


I had definitely not thought about that at all :-) It's an
interesting idea, but unfortunately memory and speed are at a premium
on my target platform, and unless I'm terribly mistaken I don't think
tcollection is the fastest thing I can use.

Thanks for your thoughts.
Reply With Quote
  #6  
Old 02-06-2008, 08:02 PM
Femme Verbeek
Guest
 
Default Re: How to handle this OOP/inheritance problem?

Jim Leonard schreef:
> On Feb 5, 3:01 pm, Femme Verbeek <fv2...@nospam.tcenl.com> wrote:
>> If you can get your hands on a copy of Borland Pascal with objects V7.01
>> I would strongly recommend this instead of TP7. Try to find a legal
>> copy, but for educational projects no one will care if you try to find
>> the BP.Zip installation file somewhere

>
> I have owned BP7 on CDROM since the day it came out, but I must use
> TP7 because of my target platform, which is... let's just say, very
> small and slow :-) See my reply to Jason for more details on the
> target platform, which is a driving factor in the design.


In my experience you'll run quite quickly into the limits of the TP IDE,
whereas BP real mode still gioing strong. The executable will not differ
in size.
I made a complete 3d cad-cam system in BP, which compiles to less than
400kB. I can still compile and run in real mode BP, which is handy for
using the built-in debugger, but not in TP7.

>
>> E.g. A note and a tone have the parameter "duration" in common, but a
>> note has the added parameters heigth, volume, rampup and rampdown etc.
>> So it is logical that TNote is a descendant of TTone, which in itself
>> descends from Tobject. Both will have a play method that can be used,
>> but TTone will override the Play method of TNote.

>
> I hadn't thought of breaking it down that granularly -- that's an
> interesting idea, I'll have to think about that.
>

The advantage is of course that you can write clear code. Each object
handles its own data, like saving and restoring.

>> Next is to define the Melody object, which can best be a descendant of
>> Tcollection. The collection can be filled with objects either being note
>> or tone.

>
> I had definitely not thought about that at all :-) It's an
> interesting idea, but unfortunately memory and speed are at a premium
> on my target platform, and unless I'm terribly mistaken I don't think
> tcollection is the fastest thing I can use.
>

Somehow the memory management of the OOP methods is very well optimised,
at least I never noticed any difference. If you use a lot of data it is
actually faster than conventional methods using arrays.

-- Femme
Reply With Quote
  #7  
Old 02-07-2008, 12:04 AM
Jim Leonard
Guest
 
Default Re: How to handle this OOP/inheritance problem?

On Feb 6, 7:02 pm, Femme Verbeek <fv2...@nospam.tcenl.com> wrote:
> In my experience you'll run quite quickly into the limits of the TP IDE,
> whereas BP real mode still gioing strong. The executable will not differ
> in size.


If it ever becomes a problem, I can edit in my own editor and then
just compile command-line :-) My fear is that, if I don't write the
code on the target platform itself, I may get sloppy or wasteful.
Because compiling takes so long on such a platform, I find myself
thinking about my code and reviewing it much more than when I first
started out on a 386 (which is 8x the speed of what I'm coding this
for).

> > I hadn't thought of breaking it down that granularly -- that's an
> > interesting idea, I'll have to think about that.

>
> The advantage is of course that you can write clear code. Each object
> handles its own data, like saving and restoring.


That is already the goal, but you had broken it down to the individual
instrument :-)

> > I had definitely not thought about that at all :-) It's an
> > interesting idea, but unfortunately memory and speed are at a premium
> > on my target platform, and unless I'm terribly mistaken I don't think
> > tcollection is the fastest thing I can use.

>
> Somehow the memory management of the OOP methods is very well optimised,
> at least I never noticed any difference. If you use a lot of data it is
> actually faster than conventional methods using arrays.


Since an array access is essentially MOV AX,[SI+BX] I can't possibly
see how it would be faster... I am envious of EMS streams, though :-)
Reply With Quote
  #8  
Old 02-07-2008, 08:10 AM
dik
Guest
 
Default Re: How to handle this OOP/inheritance problem?

Interesting project. What sound card do you intend to use
with the 4.77 MHz 8088 machine?
Reply With Quote
  #9  
Old 02-07-2008, 10:39 AM
Jim Leonard
Guest
 
Default Re: How to handle this OOP/inheritance problem?

On Feb 7, 7:10 am, dik <quag...@yahoo.com> wrote:
> Interesting project. What sound card do you intend to use
> with the 4.77 MHz 8088 machine?


Initially, none. I will be using the built-in speaker, first with
multiplexing (arpeggios) and later with a simple state machine to mix
multiple square waves realtime.

I am dying to know how Tim Follin produced his stuff on the ZX
Spectrum... up to five voices, with multiple instrument timbres...
Reply With Quote
  #10  
Old 02-07-2008, 02:01 PM
dik
Guest
 
Default Re: How to handle this OOP/inheritance problem?

On Feb 7, 10:39 am, Jim Leonard <MobyGa...@gmail.com> wrote:

> Initially, none. I will be using the built-in speaker, first
> with multiplexing (arpeggios) and later with a simple state
> machine to mix multiple square waves realtime.


I have always wondered how this is done.

What is the user interface you envision? A Csound-like
pre-processor? Some kind of a player piano? A virtual keyboard?

Thanks - DIK
Reply With Quote
Reply


Thread Tools
Display Modes


All times are GMT -5. The time now is 12:08 AM.


Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
vB Ad Management by =RedTyger=

In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.