Load sound from TMemoryStream

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
78 messages Options
1234
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Load sound from TMemoryStream

Phoenix
Hello!!
There a way to run a sound directly from a TMemoryStream without using the net or going to save a file.
I only saw
uos_AddFromFile (PlayerIndex1, (pchar (SoundFilename)));
but a function of the type
ms: TMemoryStream
uos_AddFromMStream (PlayerIndex1, ms);
or by passing the file extension if needed
uos_AddFromMStream (PlayerIndex1, ms, ext);
it's possible?

I have an encrypted data structure where I fetch the files and decode them. Everything happens in memory. So to me it is fundamental the use of TMemoryStream without writing anything. uos_AddFromFile () I can not use it. I saw that in "uos_libsndfile.pas" in addition to "sf_open ()" there is also "sf_open_virtual" I do not understand how to exploit it .

Thanks, any help is appreciated
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
Hello and welcome.

You may use:

uos_CreatePlayer(0); // create the player
uos_AddIntoDevOut(0); // add a output to sound card
uos_AddFromFile(0,pchar(thesound)); // add a input from file
...
uos_PlayNoFree(0); // you may play how many times without re-load it
...
uos_FreePlayer(0); // when you do not need it anymore.

I am busy to add a new method: uos_AddFromFileIntoMemory().

It will copy the data of the file into memory.
Then you may play it directly from memory.

I will release it ASAP (I am over-booked now).

PS: But maybe you want something else.

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Hello, thank you for your work !!!
I found this library a few days ago, and I'm thinking to use only this.
In this case, the real problem is that I do not have a PATH which can give uos_AddFromFile (0, pchar (???)). The data structure in question is a single file that contains different encrypted data such as sounds and more.
A very bad solution would be to save the TMemoryStream decrypted file so as to have the path to load. But this is what I want to avoid.
As well as in my case, this feature might be useful to those using a game engine combine with a data structure containing the sounds and other.

I saw the "AddFromFileIntoMemory ()" method just added but the problem is always the same that I have NO PATH. The audio files are ready to use it only in TMemoryStream memory.

Anyway thanks for your time !!
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
Phoenix wrote
I saw the "AddFromFileIntoMemory ()" method just added but the problem is always the same that I have NO PATH. The audio files are ready to use it only in TMemoryStream memory.
AddFromFileIntoMemory () does 2 things:

- Decode the file and store the data into a array.
- Create a input that will use that array.

If the array already exists, thanks, 1 job less to do ;-)

You may use FiletoBuffer() if you need to create the buffer.
Up to you to do what you want with that array.

function FiletoBuffer(Filename): TDArFloat;  // for creating the buffer

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Hello,
certainly start with available files but I can not give a multi-encrypted file structure as PATH!!

FiletoBuffer -> theplayer.AddFromFile -> "sf_open(FileName???, SFM_READ, sfInfo)"

sf_open (const char *path, int mode, SF_INFO *sfinfo) ;
The problem is that this implementation will always depend on a PATH file

But if used:

sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
From site lib:
"Opens a soundfile from a virtual file I/O context which is provided by the caller. This is usually used to interface libsndfile to a stream or buffer based system. Apart from the sfvirtual and the user_data parameters this function behaves like sf_open."

In addition to the implementation through "PATH" and can implement a solution without asking for any file path using "sf_open_virtual"?

 Thanks for your time!!
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
Hello.

Added in last commit d35cb28  AddFromMemory(...) to play from memory stream.

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Thanks for the method!

function uos_AddFromMemory (PlayerIndex: cint32; MemoryBuffer: PDArFloat; OutputIndex: cint32;
   SampleFormat: cint32; SampleRate: cint32; Channels: cint32; FramesCount: cint32): cint32;

It did not accept as a parameter an TMemoryStream but will be useful.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Hello,

I try to help by implementing a method for using sf_open_virtual that solves the problem.
This is my implementation:

Type
 PMemoryStream = ^TMemoryStream;
 sf_count_t = Int64;

 //pm_get_filelen = ^tm_get_filelen;
 tm_get_filelen =
  function (pms: PMemoryStream): sf_count_t;
 //pm_seek = ^tm_seek;
 tm_seek =
  function (offset: sf_count_t; whence: Integer; pms: PMemoryStream): sf_count_t;
 //pm_read = ^tm_read;
 tm_read =
  function (buf: Pointer; count: sf_count_t; pms: PMemoryStream): sf_count_t;
 //pm_write = ^tm_write;
 tm_write =
  function (const buf: Pointer; count: sf_count_t; pms: PMemoryStream): sf_count_t;
 //pm_tell = ^tm_tell;
 tm_tell =
  function (pms: PMemoryStream): sf_count_t;

 TSF_VIRTUAL = packed record
  sf_vio_get_filelen  : tm_get_filelen;
  sf_vio_seek         : tm_seek;
  sf_vio_read         : tm_read;
  sf_vio_write        : tm_write;
  sf_vio_tell         : tm_tell;
 end;

function m_get_filelen(pms: PMemoryStream): sf_count_t;
begin
 Result:= pms^.Size;
end;

function m_seek(offset: sf_count_t; whence: Integer; pms: PMemoryStream): sf_count_t;
Const
 SEEK_SET = 0;
 SEEK_CUR = 1;
 SEEK_END = 2;
begin
 case whence of
  SEEK_SET: Result:= pms^.Seek(offset, soFromBeginning);
  SEEK_CUR: Result:= pms^.Seek(offset, soFromCurrent);
  SEEK_END: Result:= pms^.Seek(offset, soFromEnd);
 end;
end;

function m_read(buf: Pointer; count: sf_count_t; pms: PMemoryStream): sf_count_t;
begin
 Result:= pms^.Read(buf^,count);
end;

function m_write(const buf: Pointer; count: sf_count_t; pms: PMemoryStream): sf_count_t;
begin
 Result:= pms^.Write(buf^,count);
end;

function m_tell(pms: PMemoryStream): sf_count_t;
begin
 Result:= pms^.Position;
end;

procedure TSSoundUOS.TestLibSndFile;
Var
 sfInfo: TSF_INFO;
 sfVirtual: TSF_VIRTUAL;

 procedure ShowInfo;
 Var
  sl: TStringList;
 begin
  sl:= TStringList.Create;
  try
   with sfInfo do
   begin
    sl.Add('frames: '+IntToStr(frames));
    sl.Add('samplerate: '+IntToStr(samplerate));
    sl.Add('channels: '+IntToStr(channels));
    sl.Add('format: '+IntToStr(format));
    sl.Add('sections: '+IntToStr(sections));
    sl.Add('seekable: '+IntToStr(seekable));
   end;
   ShowMessage(sl.Text);
  finally
   sl.Free;
  end;
 end;

Const
 path_test = 'c:\test.flac'; //for simulate
Var
 ms: TMemoryStream;
begin
 FillChar(sfInfo, SizeOf(sfinfo), 0);
 sf_open(path_test, SFM_READ, sfInfo);
 ShowInfo;

 with sfVirtual do
 begin
  sf_vio_get_filelen  := m_get_filelen;
  sf_vio_seek         := m_seek;
  sf_vio_read         := m_read;
  sf_vio_write        := m_write;
  sf_vio_tell         := m_tell;
 end;

 FillChar(sfInfo, SizeOf(sfinfo), 0);
 ms:= TMemoryStream.Create;
 try
  ms.LoadFromFile(path_test);
  ms.Position:= 0;
  sf_open_virtual(@sfVirtual, SFM_READ, @sfInfo, @ms);
  ShowInfo;
 finally
  ms.Free;
 end;
end;  

Now create this

function Tuos_Player.AddFromMStream(ms: TMemoryStream; OutputIndex: cint32;
  SampleFormat: cint32 ; FramesCount: cint32 ): cint32;

Replace the code that uses sf_open with sf_open_virtual

Streamin [x] .Data.HandleSt: = sf_open (FileName, SFM_READ, sfinfo);

Streamin [x] .Data.HandleSt: = sf_open_virtual(@sfVirtual, SFM_READ, @sfInfo, @ms);

is it solve for libsndfile

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
Hello.

Added in last commit 0746826 ConsolePlayMemory.pas to show how to use memory buffer as input.

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Thanks for add
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
> Thanks for add

Huh, is it useful for you ?
Did you add the buffer into ressource?

I am busy to add addFromMemoryStream(), I hope to commit it asap.

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
It is useful for UOS, so for all.
For my problem, when you have time.
Thank you
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
> For my problem, when you have time.

Did you try to convert the buffer into file, then add this to ressource, then load the file from ressource and convert it into buffer ?

In short, if you can convert the buffer into file (with a TFileStream) and TMemoryStream into a buffer, you get it ;-)

It is not very complicated, there are code for convert data of TMemoryStream into buffer in uos if you need it now.

But, I am very sorry, now I can do it, too much work. ;-(

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
It is possible by storing in the structure, the preloaded data using libsndfile (buffer and parameters). But NOT in the original format.
In practice by altering the encrypted data structure it is possible.
My goal is to use only UOS, but keeping the data structure as independent as possible.
In the future, if they will be available direct support for the TMemoryStream will be a nice addition.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
Hello.

Added procedure uos_File2File() in last commit 0746826..e96cd3d.

You may use this to save all type of audio file into a pcm file (= save the buffer into file).

For example, to save a file in format pcm:

uos_File2File(pchar(SoundFilenameIN),pchar(SoundFilenameOUT), 1, 1);

This file could be used for ressource.

Then convert it back to buffer.

PS: Only integer 32/16 is allowed for uos_File2File() (not float 32, but it will be converted by uos if needed).


Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
In reply to this post by Phoenix
Hello Phoenix ?

Did you try with TRessourceStream ?
http://lazarus-ccr.sourceforge.net/docs/rtl/classes/tresourcestream.html

I think that you have everything to do what you want:

1) uos_File2File(pchar(SoundFilenameIN),pchar(SoundFilenameOUT), 1, 1);
    (This for create the ressource)

2) TRessourceStream to load the ressource.

3) Convert the data of TRessourceStream into a buffer of float.

4) Use it with uos_AddFromMemoryBuffer()

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
> 3) Convert the data of TRessourceStream into a buffer of float.

Hello.

Here how to convert TRessourceStream into buffer:

uses
..., ctypes,...;

var
len : integer;
buffer : array of cfloat;
ARessorceStream : TRessorceStream;

...

 try
  ... // the ARessorceStream was created

  len := ARessorceStream.Size;
  SetLength(buffer, 0);
  SetLength(buffer, len div SizeOf(buffer[0]));
  ARessorceStream.ReadBuffer(buffer[0], Length(buffer) * SizeOf(buffer[0]));
  finally
    ARessorceStream.Free;
  end;                

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

Phoenix
Hello,
sorry for the wait,
I tested everything and works perfectly.
No need to use TResourceStream because it also works with TMemoryStream. It basically a bypass of linsndfile library (for the accuracy of all libraries for sound format). Using the decoded buffer and then just load only PortAudio. This is an interesting feature. But in my case, you lose the advantage of starting from files in original format as flac, mp3 .. Decrypt a large file such as .wav format is inconvenient.
It is true that sf_open_virtual can only solve for libsndfile but it would still be an additional feature (apart from a possible problem at the end of the sound seems that the solution was nearly reached "Tuos_Player.AddFromMStream").
Maybe I'm wrong to focus on sf_open_virtual but by a non-expert point of view, it may seem promising.

Thanks for your help
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
> Maybe I'm wrong to focus on sf_open_virtual

Hello.

No and excuse me if I did not speak over this.
Of course it is a great addition and I will take a look asap.
But before to commit it, I want to do the same for mpg123 and opus.
It will not be very hard because it was already done for web-streaming.
I have just to adapt the code for a independent TMemoryStream of compressed audio.

Many thanks to contribute to uos.

Fre;D
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Load sound from TMemoryStream

fredvs
Administrator
In reply to this post by Phoenix
> is it solve for libsndfile

Huh, is it working for you ?
I have try your code but it crash after +- 1 second playing.

What result do you have ?

Fre;D
1234
Loading...