Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Help with TMemoryStream

Status
Not open for further replies.

simmo09

Programmer
Apr 22, 2009
82
GB
Hi, here is a quick introduction to what im trying to do:

load sounds (midi,mp3,wav) into a memory stream, then when called determine from the memory stream what type of file it is and play it.

im not a expert coder and am always keen to learn, code samples is easiest way for me to do this. searching for ages on the net i found this:

Code:
procedure LoadFromFileToMemory(const AFilename: String; memStream: TMemoryStream);
var
  aFileStream: TFileStream;
begin
  aFileStream:= TFileStream.Create(AFilename, fmOpenRead);
  try
   memStream.CopyFrom(aFileStream, 0);
  finally
   aFileStream.Free;
  end;
end;

{
  LoadFromFileToMemory usage example
  **********************************

  var
    memStream: TMemoryStream;

    memStream:= TMemoryStream.Create;
    try
      LoadFromFileToMemory('C\my file.mp3',memStream);
    finally
      memStream.Free;
    end;
  end;
}

i can identify the file extension if i wanted by using ExtractFileExt('C\my file.mp3'), but if the file is in memory how could i do it?

Thanks
 
ok excuse me for bad coding, ive never dealt with anything like this, its my first time trying something like this.

i have a treeview, which i add child nodes to during run-time. when a child is added a form is shown where i can load my midi, wav files, what im trying to do is get the filename to assign to the newly added child. so whenever i double click this child node, it will open the form again and show the filename that was assigned earlier on.

here is what i tried (again im not too sure what im doing!)

Code:
{ THIS IS THE MAIN FORM }

procedure TfrmMain.tvwStructureDblClick(Sender: TObject);
begin
  frmSoundProperties:= TfrmSoundProperties.Create(Application);
  frmSoundProperties.lblFileName.Caption:= 'Filename: ' + aSoundItem.FileName;
end;


{ THIS FORM IS SHOWN WHEN THE CHILD NODE FROM THE MAIN FORM
 IS DOUBLE CLICKED }

type
  TSoundItem = Object
    FileName: String; {hopefully hold the sound filename}
  end;

procedure TfrmSoundProperties.cmdLoadClick(Sender: TObject);
var
  memStream: TMemoryStream;
  aSoundItem: TSoundItem;
begin
  memStream:= TMemoryStream.Create;
  StopWAVE;

  if dlgOpen.Execute() then
  begin
    aSoundItem.FileName:= dlgOpen.FileName;
    try
      LoadFromFileToMemory(aSoundItem.FileName, memStream);
    finally
      memStream.Free;
    end;

    lblFileName.Caption:= 'Filename: ' + ExtractFileName(aSoundItem.FileName);
  end;
end;

procedure TfrmSoundProperties.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:= caFree;
end;

procedure TfrmSoundProperties.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose:= True;
end;

how can i get the filename to store with each child i add?

i would also like to store other things in memory like if the sound should loop or not - but one step at a time.

i hope some guru on here can help me out, many thanks in advance :)
 
so each time a new child is added and i load a sound, i need to store in memory the parameters, and recall them.

i need to know how to assign such data, im going along the lines of:

Code:
type
  TSoundItem = class(TObject)
    FileName: String;
    FileExtension: String;
    Loop: Boolean;
    Volumne: Integer;
  end;

but i dont really know, please help thanks
 
Okay, you typed so much here that you need to clarify what you're looking for. For my reading, I'm noticing the following:

1) How do I load a sound file into a TMemoryStream and play it?
2) How do I store information that I can associate with my TTreeView?

Am I on the right track?

Measurement is not management.
 
hi, sorry i kind of started answering my own questions.

ok i can play the wave files from memory stream, that is fine.

Code:
type
  TSoundItem = class(TObject)
    FileName: String;
    FileExtension: String;
    Loop: Boolean;
    Volume: Integer;
  end;

what i need to know how to do is associate the properties in TSoundItem with each added treenode, know how to recall this data and its associated wave from the memory stream.

for example, my treeview could like:

TreeView1
--My Sounds (parent)
----sound1 (child)
----sound2 (child)
----sound3 (child)

each child could have assigned (done at runtime) the following:

sound1:
FileName > 'c:\sound1.wav'
FileExtension > 'wav'
Loop > true
Volume > 75

sound2:
FileName > 'c:\sound2.mp3'
FileExtension > 'mp3'
Loop > false
Volume > 83

etc..


so when i then double click a child node, the sound form is opened and the corresponding properties from TSoundItem is recalled.

I appreciate any help you can give me, im not looking for full blown source code, just some help and guidance.

Thank you so much.
 
what i need to know how to do is associate the properties in TSoundItem with each added treenode, know how to recall this data and its associated wave from the memory stream.

Associating data with a control always seems like a constant problem with the VCL. From what I see about TTreeNode, the way to do it would be to create an array with the number of values you have in the TTreeView and then associate the data with AbsoluteIndex in TTreeNode.

When you save the tree node, something like:
Code:
TNode := TreeView1.Items.AddChild(WinAddr1, Sr.Name);
//save TSoundItem to array of TSoundItem using AbsoluteIndex
writeln(TNode.AbsoluteIndex);

Then when you click on a tree node, you read the absolute index and go to your array and get the data by doing something like:

Code:
mysoundarray[TNode.AbsoluteIndex];

Hope that helps you out.

Measurement is not management.
 
ok i will have a look with this,

thank you Glenn9999
 
something like this to declare it?

Code:
type
  TSoundItem = class(TObject)
    FileName: String;
    FileExtension: String;
    Loop: Boolean;
    Volume: Integer;
  end;

var
  frmSoundProperties: TfrmSoundProperties;

  SoundItemArray: Array[0..3] of TSoundItem;
 
I'd just use a record type instead of the class, at least until I made sure I got it right. But yes, that's the basic idea.

Measurement is not management.
 
well im a bit confused, here is what i have so far, how would i change it to a record type?

Code:
type
  TSoundItem = class(TObject)
    FileName: String;
    FileExtension: String;
    Loop: Boolean;
    Volume: Integer;
  end;

var
  frmSoundProperties: TfrmSoundProperties;

  SoundItemArray: Array[0..3] of TSoundItem;

procedure TfrmSoundProperties.cmdOKClick(Sender: TObject);
begin
  SoundItemArray[0].FileName:= dlgOpen.FileName; {filename}
  SoundItemArray[1].FileExtension:= ExtractFileExt(dlgOpen.FileName); {file extension}
  SoundItemArray[2].Loop:= chkLoopSound.Checked; {loop}
  SoundItemArray[3].Volume:= barVolume.Position; {volume}
  TSoundItem:= frmMain.tvwStructure.Selected.AbsoluteIndex; {errors!!}

  Self.Close;
end;

Thanks
 
Code:
TSoundItem = record

like that i guess, doh!
 
The array is going to have a record type attached to each one. Instead of what you have:

Code:
var
  TNode: TTreeNode;
  TNodeIndex: Integer;

// this is where you add the node to the TTreeView
TNode := TTreeView.Items.AddChild(...);
// now the returned TNode has an AbsoluteIndex.
TNodeIndex := TNode.AbsoluteIndex;
// store your data to the record array
SoundItemArray[TNodeIndex].FileName:= dlgOpen.FileName; {filename}  
SoundItemArray[TNodeIndex].FileExtension:= ExtractFileExt(dlgOpen.FileName); {file extension}  
SoundItemArray[TNodeIndex].Loop:= chkLoopSound.Checked; {loop}  
SoundItemArray[TNodeIndex].Volume:= barVolume.Position; {volume}

Measurement is not management.
 
ok apologies for any mixups in the thread, this is what i have now which i hope you can inspect and point me to where i might be going wrong:

Code:
type
  TSoundItem = record
    FileName: String;
    FileExtension: String;
    Loop: Boolean;
    Volume: Integer;
  end;

var
  frmSoundProperties: TfrmSoundProperties;

  SoundItemArray: Array[0..3] of TSoundItem;
  aSoundItem: TSoundItem;

implementation

Code:
procedure TfrmSoundProperties.cmdLoadClick(Sender: TObject);
var
  memStream: TMemoryStream;
begin
  memStream:= TMemoryStream.Create;
  StopWAVE;

  if dlgOpen.Execute() then
  begin
    {assign to TSoundItem}
    aSoundItem.FileName:= dlgOpen.FileName;
    aSoundItem.FileExtension:= ExtractFileExt(aSoundItem.FileName);

    try
      LoadFromFileToMemory(aSoundItem.FileName, memStream);
    finally
      memStream.Free;
    end;

    lblFileName.Caption:= 'Filename: ' + ExtractFileName(aSoundItem.FileName);
  end;
end;

procedure TfrmSoundProperties.barVolumeChange(Sender: TObject);
begin
  {assign to TSoundItem}
  aSoundItem.Volume:= barVolume.Position; {volume}
end;

procedure TfrmSoundProperties.chkLoopSoundClick(Sender: TObject);
begin
  {assign to TSoundItem}
  aSoundItem.Loop:= chkLoopSound.Checked; {loop}
end;

procedure TfrmSoundProperties.cmdOKClick(Sender: TObject);
begin
  SoundItemArray[0].FileName:= aSoundItem.FileName; {filename}
  SoundItemArray[1].FileExtension:= aSoundItem.FileExtension; {file extension}
  SoundItemArray[2].Loop:= aSoundItem.Loop; {loop}
  SoundItemArray[3].Volume:= aSoundItem.Volume; {volume}

  aSoundItem:= frmMain.tvwStructure.Selected.AbsoluteIndex; {errors - HOW TO ASSIGN THIS?}

  Self.Close;
end;
 
ok, it remembers the properties but only for one child node, whenever i add a new child the other childs properties are forgotten - most likely because im not assigning it correctly, you posted this line:

Code:
// this is where you add the node to the TTreeView
TNode := TTreeView.Items.AddChild(...);
// now the returned TNode has an AbsoluteIndex.
TNodeIndex := TNode.AbsoluteIndex;

how would i put that into this because im assigning images to the new child:

Code:
with tvwStructure.Items.Add(tvwStructure.Selected, ItemName) do
begin
  ImageIndex:= tvwSound;
  SelectedIndex:= tvwSound;
end;

or is it correct what ive done?

Thanks

PS, i will add an attachment some time later which i hope you could look at and advise on.

Thanks again
 
Hi Glenn, i have made up a quick a project attachment,

just right click the treeview and select the add child, as you can see the sound form opens. if you load a sound (i included 3) and change the volume, loop etc and then click ok, if you could repeat this say twice (add another child etc..), when you go to click each child it needs to recall the properties.

also the sound in the memory stream should be recalled.

could you look at this attachment and advise? (the password for the zip is tektips )

Thank You
 
 http://www.easy-share.com/1907841041/Assign to Treeview.zip
Sorry to break the conversation here Guys.

Glenn999 said:
Associating data with a control always seems like a constant problem with the VCL.

Strongly disagree on this matter. Though Glenn's code is perfectly valid, I want to point out there IS another way:

Simmo, if you looked at the TTreeNode object, you might have noticed is has a Data property. This IS the place where you link the object to the TreeNode. No need to mess with arrays and records (old style).

Some basic rules:
- use Objectlist to contain the objects you create.
- link the object to the related vcl item. In fact many VCL objects can be linked with data. TStringList is a nice example (it has Items.Objects property). Remember that TStringList is used in many VCL objects.

so for your treenode problem:

1. create a treenode
2. create the TSoundItem or whatever object you need
3. link the object to the TTreenode just by doing TreeNode.Data := YourObject;
4. Add the object to an objectlist
5 at application destruction you objects will automatically be freed by the ObjectList

retrieving the object is done by typecasting:

TSoundItem(TreeNode.Selected)

in case of mixed objects, you have multiple possibilities :

- create a new object that encapsulates all possible object types
- find out what object type you have and do the appropriate actions with the object. like :

Code:
 if  TObject(TreeNode.Selected) is TSoundItem then
  ...
else
 if  TObject(TreeNode.Selected) is TVideoItem then
 ....

I prefer the first method however.

Cheers,
Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
hi, feel free to input your say its much appreciated.

im going to have to redo most of it so far then i think, also the assigned data to the nodes needs to be saved to disk, so whenever i close the program, and open the saved file at a later date the assigned data can be recalled.

i need to do this in-memory first, so let me have another look..
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top