It is currently Mon Dec 17, 2018 11:46 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: [solved] wave-files (merge)
PostPosted: Sun Aug 12, 2018 11:11 am 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
Hello.
I need to merge several sound files into one. Assume that all files have the same format. I read this topic and want to use code written by infratec. Here is a non-working example(very dirty), but it shows what I want to do (based on code by infratec):
Code:
Structure RIFFStructure
  Riff.a[4]
  Length.l
  Wave.a[4]
EndStructure

Structure chunkStructure
  Signature.a[4]
  Length.l
EndStructure

Structure fmtStructure
  fmt.a[4]
  Length.l
  Format.u
  Channels.u
  SampleRate.l
  BytesPerSecond.l
  BlockAlign.u
  BitsPerSample.u
EndStructure

Structure dataStructure
  dataChunk.a[4]
  Length.l
 
  dataBytes.a[0]
EndStructure


Structure properties
  begin_position.q
  size.q
EndStructure


#HeaderSize = 44 ; standard size for wav-file header
Global OutputFile.s = "c:\base.wav" ; result file
Global size_of_data.q
Global NewList DATABase.properties()


Procedure.i LoadSoundWav(Filename$)
  Protected.i File, FileSize
  Protected Chunk$
  Protected *ReadBuffer, *WAVBuffer
  Protected *RiffPtr.RIFFStructure
  Protected *chunkPtr.chunkStructure
  Protected *fmtPtr.fmtStructure
  Protected *dataPtr.dataStructure
 
 
  File = ReadFile(#PB_Any, Filename$)
  If File
   
    FileSize = Lof(File)
    *ReadBuffer = AllocateMemory(FileSize)
    If *ReadBuffer

      *WAVBuffer = AllocateMemory(FileSize)
      If *WAVBuffer
       
        If ReadData(File, *ReadBuffer, FileSize) = FileSize
         
          *RiffPtr = *ReadBuffer
          If PeekS(@*RiffPtr\Riff, 4, #PB_Ascii) = "RIFF" And *RiffPtr\Length = FileSize - 8 And PeekS(@*RiffPtr\Wave, 4, #PB_Ascii) = "WAVE"
            ;Debug "Header Ok"
           
            CopyMemory(*RiffPtr, *WAVBuffer, SizeOf(RIFFStructure))
           
           
            *chunkPtr = *ReadBuffer + SizeOf(RIFFStructure)
           
            Repeat
              ; check for data chunk
           
              Chunk$ = PeekS(@*chunkPtr\Signature, 4, #PB_Ascii)
           
              ;Debug "Chunk: " + Chunk$
             
              Select Chunk$
                Case "fmt "
                  *fmtPtr = *chunkPtr
                  CopyMemory(*fmtPtr, *WAVBuffer + SizeOf(RIFFStructure), *fmtPtr\Length + 8)
                 
                Case "data"
                  *dataPtr = *chunkPtr
                  CopyMemory(*dataPtr, *WAVBuffer + SizeOf(RIFFStructure) + *fmtPtr\Length + 8, *dataPtr\Length + 8)
                 
              EndSelect
             
              *chunkPtr + 8 + *chunkPtr\Length
             
            Until *chunkPtr >= *ReadBuffer + *RiffPtr\Length
           
           
            *RiffPtr = *WAVBuffer
            PokeL(@*RiffPtr\Length, SizeOf(RIFFStructure) - 8 + *fmtPtr\Length + 8 + *dataPtr\Length + 8)
           
           
            resultfile_size.q = FileSize(OutputFile)
            If resultfile_size =-1
              CreateFile(99, OutputFile)
              WriteData(99, *WAVBuffer, #HeaderSize) ; write original header
;              CloseFile(99)
            Else
              OpenFile(99, OutputFile)
              FileSeek(99, 40)
              WriteData(99, @size_of_data, 4) ; change size of data (position: 4 Bytes after 'data')
              FileSeek(99, Lof(99)) ; the same as #PB_File_Append
            EndIf

            size_of_data + PeekL(*WAVBuffer+#HeaderSize-4) ; how to sum bytes here ???
            WriteData(99, *WAVBuffer + #HeaderSize, *RiffPtr\Length + 8 - #HeaderSize)
            CloseFile(99)
            AddElement(DATABase())
            If resultfile_size
              DATABase()\begin_position = resultfile_size
            Else
              DATABase()\begin_position = #HeaderSize
            EndIf
            DATABase()\size = *RiffPtr\Length + 8 - #HeaderSize
           
          EndIf
         
        EndIf
       
        FreeMemory(*WAVBuffer)
      EndIf
     
      FreeMemory(*ReadBuffer)
    EndIf
   
  EndIf
EndProcedure




For i=1 To 3
  LoadSoundWav("c:\"+Str(i)+".wav")
Next i


; now I want to get a second sound from the base
SelectElement(DATABase(), 1)
OpenFile(99, OutputFile)

*Header = AllocateMemory(#HeaderSize)
ReadData(99, *Header, #HeaderSize)

FileSeek(99, DATABase()\begin_position)

_DATA = DATABase()\size
*ReadBuffer = AllocateMemory(_DATA)
ReadData(99, *ReadBuffer, _DATA)
CloseFile(99)


; create a new file
OpenFile(99, "c:\taste.wav")
WriteData(99, *Header, #HeaderSize-4)
WriteData(99, @_DATA, 4) ; ??? hex()
WriteData(99, *ReadBuffer, _DATA)

CloseFile(99)
FreeMemory(*Header)
FreeMemory(*ReadBuffer)



Last edited by ZX80 on Thu Aug 16, 2018 7:22 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Sun Aug 12, 2018 7:17 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Thu Sep 17, 2015 3:39 pm
Posts: 127
hello :D
tested with this file :arrow: http://www.mediafire.com/file/kskn01k96zcru8a/1.wav/file

Code:
Structure wavfileheader
  ;// RIFF Header
  riff_header.l; // Contains "RIFF"
  wav_size.l   ; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
  wave_header.l; // Contains "WAVE"
 
  ;// Format Header
  fmt_header.l; // Contains "fmt " (includes trailing space)
  fmt_chunk_size.l; // Should be 16 for PCM
  audio_format.w  ; // Should be 1 for PCM. 3 for IEEE Float
  num_channels.w  ;
  sample_rate.l   ;
  byte_rate.l     ; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
  sample_alignment.w; // num_channels * Bytes Per Sample
  bit_depth.w       ; // Number of bits per sample
 
  ;// Data
  data_header.l; // Contains "data"
  data_bytes.l ; // Number of bytes in data. Number of samples * num_channels * sample byte size
               ;// uint8_t bytes[]; // Remainder of wave file is bytes
EndStructure

swavfileheader.wavfileheader
swavfileheader2.wavfileheader
ReadFile(0,"1.wav",#PB_File_SharedRead)
ReadData(0,swavfileheader,SizeOf(wavfileheader))
ReadFile(1,"1.wav",#PB_File_SharedRead)
ReadData(1,swavfileheader2,SizeOf(wavfileheader))

siz = swavfileheader\data_bytes
p = AllocateMemory(siz)
ReadData(0,p,siz)

CreateFile(2, "3.wav")
swavfileheader\wav_size + swavfileheader2\data_bytes
swavfileheader\data_bytes + swavfileheader2\data_bytes
WriteData(2,swavfileheader,SizeOf(wavfileheader))
WriteData(2,p,siz)
p = AllocateMemory(swavfileheader2\data_bytes)
ReadData(1,p,swavfileheader2\data_bytes)
WriteData(2,p,swavfileheader2\data_bytes)
CloseFile(2)

_________________
interested in Cybersecurity..


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Sun Aug 12, 2018 8:44 pm 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
CELTIC88, thanks a lot!!!
It does. Tested with 16-bit wav-files. With 24-bit sample - not work (don't need, only for info). Comment in structure //Should be 16 for PCM.
If I need merge much than two files, then for each of files need declare new copy of wavfileheader-structure?
For example, I have directory contains about 50-files. Enumerate files - not a problem, but I want create one big file (database of sounds). This file must have only one wave-header. But... I also want to create list-file(during create database-file) with follow description(bias from beginning of database-file and size of data/raw). In the end, I need to create a sentence from some words (using data from list-file). Please, comment my question about new copy of wavfileheader-structure for each of files.
Until you answered I looked ffmpeg for this task.
Your version is better than additional dlls of ffmpeg.
Thanks again.


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Mon Aug 13, 2018 9:47 am 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3268
Location: Netherlands
@CELTIC88
It's not safe to assume the "data" chunk fill immediately follow the "fmt " chunk.
A LIST INFO chunk could be in between with information about title, artist etc.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Mon Aug 13, 2018 3:03 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Thu Sep 17, 2015 3:39 pm
Posts: 127
hi Dr @wilbert :D

i know :P , you can skip the "LIST INFO" with finding magic number "data" :

Code:
While swavfileheader\data_header <> $61746164 ; "data"
  FileSeek(0,swavfileheader\data_bytes,#PB_Relative)
  If ReadData(0,@swavfileheader\data_header,8) = 0 :End 1 : EndIf
Wend


thank you for your comment


hi @ZX80

Quote:
..With 24-bit sample - not work (don't need, only for info)

not tested :p,but should be work!

Quote:
..then for each of files need declare new copy of wavfileheader-structure?

No!. look my example ,after reading the header my file is pointed to wav data

thank .

_________________
interested in Cybersecurity..


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Mon Aug 13, 2018 6:27 pm 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
Hi, CELTIC88
Very glad to hear you again. Sorry for my stupid previous question and post without code. Today I was busy a whole time. Without access to PC and Internet. But today I have a fresh brain and eyes. And I have some idea how to do this. I'll try to write this, and you'll check, okay? I can not say exactly what it will be today. Please wait.


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Tue Aug 14, 2018 1:04 am 
Offline
New User
New User

Joined: Tue Aug 14, 2018 12:31 am
Posts: 1
ZX80 wrote:
Hi, CELTIC88
Very glad to hear you again. Sorry for my stupid previous question and post without code. Today I was busy a whole time. Without access to PC and Internet. But today I have a fresh brain and eyes. Many people have this issue, where they need a fresh day to start over. And I have seen some information here which shows how to do it and how to deal with all the problems. So, I think it should be doable. I have some idea how to do this. I'll try to write this, and you'll check, okay? I can not say exactly what it will be today. Please wait.


hello,

are you going to post it today?


Last edited by tatne on Fri Aug 24, 2018 7:28 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Tue Aug 14, 2018 8:34 am 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
Quote:
are you going to post it today?

Hi.
Yes, but ... something wrong :(
Please, check it.
create_db.pb:
Code:
Structure wavfileheader
  ;// RIFF Header
  riff_header.l; // Contains "RIFF"
  wav_size.l   ; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
  wave_header.l; // Contains "WAVE"
 
  ;// Format Header
  fmt_header.l; // Contains "fmt " (includes trailing space)
  fmt_chunk_size.l; // Should be 16 for PCM
  audio_format.w  ; // Should be 1 for PCM. 3 for IEEE Float
  num_channels.w  ;
  sample_rate.l   ;
  byte_rate.l     ; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
  sample_alignment.w; // num_channels * Bytes Per Sample
  bit_depth.w       ; // Number of bits per sample
 
  ;// Data
  data_header.l; // Contains "data"
  data_bytes.l ; // Number of bytes in data. Number of samples * num_channels * sample byte size
               ;// uint8_t bytes[]; // Remainder of wave file is bytes
EndStructure


Structure Properties
  _bias.i
  _size.i
EndStructure


Global NewList MyFiles$()

Procedure Direct(Directory.s, maska.s)
If Right(Directory.s,1)<>"\":Directory.s + "\":EndIf
   z=ExamineDirectory(#PB_Any, Directory.s, "*.*") 
  If z
    While NextDirectoryEntry(z)
     EntryName.s=DirectoryEntryName(z)

      If EntryName = "." Or EntryName = ".."
       Continue
      EndIf

      Type=DirectoryEntryType(z)
      If Type = #PB_DirectoryEntry_Directory
        Direct(Directory.s+EntryName, maska.s) 
      ElseIf Type = #PB_DirectoryEntry_File
     
      FileName.s= Directory.s+EntryName
          Ext$ = LCase(Right(FileName.s,4))
           If  Ext$ = maska
             AddElement(MyFiles$())
             MyFiles$()= FileName   
           EndIf
      EndIf
    Wend
    FinishDirectory(z)
  EndIf
EndProcedure

; Scan folder
InitialPath$ = "C:\"
Path$ = PathRequester("Please choose dir with wavs", InitialPath$)
If Path$ = ""
  MessageRequester("Information", "The requester was canceled.")
  End
EndIf

Direct(Path$,".wav")
If ListSize(MyFiles$())=0
  MessageRequester("Information", "wavs not found.")
  End
EndIf

*mem = AllocateMemory(10000000) ; MAX size for wav-file = 10 MBytes
If *mem = 0
  MessageRequester("Information", "memory cannot be allocated")
  End
EndIf

NewList SoundsDB.Properties()
bias = SizeOf(wavfileheader) ; pointer (start position)
headersize = bias
database.s = "SoundsDB.wav"
header.wavfileheader

; create blank
If CreateFile(1, database) = 0
  MessageRequester("Information", "can't create db-file")
  End
EndIf


; and now read content of wavs
ForEach MyFiles$()
  If ReadFile(0, MyFiles$(), #PB_File_SharedRead)
    If ReadData(0, header, headersize)
      bias + size
      size = header\data_bytes  ; size of raw-data
      ReadData(0, *mem, size)
      CloseFile(0)
      If ListSize(SoundsDB())=0
        WriteData(1, header, headersize)
      EndIf
      AddElement(SoundsDB())
      SoundsDB()\_bias = bias
      SoundsDB()\_size = size
      WriteData(1, *mem, size)
    EndIf
  EndIf
Next
FileSeek(1, 0)
SelectElement(SoundsDB(), 0)
header\data_bytes = bias + SoundsDB()\_size
WriteData(1, header, headersize)
CloseFile(1)
FreeMemory(*mem)
If ListSize(SoundsDB())=0
  MessageRequester("Information", "Something wrong :(")
  End
Else
  If CreateFile(1, "list.txt") = 0
    MessageRequester("Information", "can't create list-file")
    End
  Else
    ForEach SoundsDB()
      WriteStringN(1, Str(SoundsDB()\_bias) + "|" + Str(SoundsDB()\_size))
    Next
  EndIf
EndIf

and get_from_db.pb:
Code:
Structure wavfileheader
  ;// RIFF Header
  riff_header.l; // Contains "RIFF"
  wav_size.l   ; // Size of the wav portion of the file, which follows the first 8 bytes. File size - 8
  wave_header.l; // Contains "WAVE"
 
  ;// Format Header
  fmt_header.l; // Contains "fmt " (includes trailing space)
  fmt_chunk_size.l; // Should be 16 for PCM
  audio_format.w  ; // Should be 1 for PCM. 3 for IEEE Float
  num_channels.w  ;
  sample_rate.l   ;
  byte_rate.l     ; // Number of bytes per second. sample_rate * num_channels * Bytes Per Sample
  sample_alignment.w; // num_channels * Bytes Per Sample
  bit_depth.w       ; // Number of bits per sample
 
  ;// Data
  data_header.l; // Contains "data"
  data_bytes.l ; // Number of bytes in data. Number of samples * num_channels * sample byte size
               ;// uint8_t bytes[]; // Remainder of wave file is bytes
EndStructure

Structure Properties
  _bias.i
  _size.i
EndStructure

NewList SoundsDB.Properties()
database.s = "SoundsDB.wav"
headersize = SizeOf(wavfileheader)
header.wavfileheader

*buffer = AllocateMemory(10000000)
If *buffer = 0
  MessageRequester("Information", "memory cannot be allocated")
  End
EndIf

If FileSize(database)=-1
  MessageRequester("Information", "db not found")
  End
EndIf
If FileSize("list.txt") = -1
  MessageRequester("Information", "list-file not found")
  End
EndIf


If ReadFile(0, "list.txt")
  While Eof(0) = 0
    tmp$ = ReadString(0)
    If FindString(tmp$, "|")
      AddElement(SoundsDB())
      SoundsDB()\_bias = Val(StringField(tmp$, 1, "|"))
      SoundsDB()\_size = Val(StringField(tmp$, 2, "|"))
    EndIf
  Wend
  CloseFile(0)
Else
  MessageRequester("Information","Couldn't open the db-file!")
EndIf

;get sound1 + sound4
If CreateFile(1, "output.wav")=0
  MessageRequester("Information", "can't create output-file")
  End
EndIf
If ReadFile(0, database, #PB_File_SharedRead) = 0
  MessageRequester("Information", "can't read db-file")
  End
EndIf
If ReadData(0, header, headersize) = 0
  MessageRequester("Information", "readdata err")
  End
EndIf

; create wav-header with correct size of raw-data
SelectElement(SoundsDB(), 0)
size = SoundsDB()\_size
SelectElement(SoundsDB(), 3)
size + SoundsDB()\_size
header\data_bytes = size
WriteData(1, header, headersize)

; get and write raw-data of sound 1
SelectElement(SoundsDB(), 0)
FileSeek(0, SoundsDB()\_bias)
ReadData(0, *buffer, SoundsDB()\_size)
WriteData(1, *buffer, SoundsDB()\_size)

; get and write raw-data of sound 4
SelectElement(SoundsDB(), 3)
FileSeek(0, SoundsDB()\_bias)
ReadData(0, *buffer, SoundsDB()\_size)
WriteData(1, *buffer, SoundsDB()\_size)

CloseFile(0)
CloseFile(1)
FreeMemory(*buffer)


p.s.
It seems this is because incorrectly calculated the bias in "create_db.pb".

p.s. 2
Previous code fixed. Try again.


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 7:30 am 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
CELTIC88, wilbert, is it right or not?


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 1:10 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3268
Location: Netherlands
What's your goal with it ?
If it's for your own personal use and it is working, it is fine.
If you want others to use it as well, you should do it differently and scan all chunks until you have found both the 'fmt ' and 'data' chunks.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 2:02 pm 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
to wilbert
Quote:
What's your goal with it ?

I already have a parser program for meteorological telegrams. Now I want to voice this program (without use SAPI). Like this or this.
Quote:
If it's for your own personal use...

This is for personal use (one side), because no one will change the once created database of sounds (otherwise everything will be corrupted). However, a few people will use final vesion of program (another side).


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 2:28 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3268
Location: Netherlands
That makes sense :)
For playback I wouldn't create wav files.
It's faster and easier to allocate memory, combine things in memory and use CatchSound / PlaySound.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 2:40 pm 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
wilbert, in other words, you suggest keeping a directory containing 100-200 separate wav-files and playback them sequentially? It seemed to me that it would be better to create one output-file and send it to Windows Media Player. Anyway, thanks for advice.

upd:
Ooops. It seems I inattentively read your previous post. :oops:


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 2:52 pm 
Offline
PureBasic Expert
PureBasic Expert

Joined: Sun Aug 08, 2004 5:21 am
Posts: 3268
Location: Netherlands
ZX80 wrote:
wilbert, in other words, you suggest keeping a directory containing 100-200 separate wav-files and playback them sequentially? It seemed to me that it would be better to create one output-file and send it to Windows Media Player. Anyway, thanks for advice.

No, that's not what I'm suggesting.
Combining all those files into one SoundsDB is a good idea :)

What I'm suggesting is that if you need to create a sentence from samples 5, 8, 2 and 7, you allocate memory for the total raw bytes and header.
You copy the header and raw bytes of those samples to the allocated memory, use CatchSound to catch the wav from memory and PlaySound to play it back.
I was assuming you would use PlaySound to play back the sentence.
But you are talking about sending it to Windows Media Player. If you play it back using an external application my suggestion of course won't work. In that case you need to create a wav file like you were already doing.

_________________
macOS 10.14 Mojave, PB 5.62 x64


Top
 Profile  
Reply with quote  
 Post subject: Re: wave-files (merge)
PostPosted: Wed Aug 15, 2018 3:12 pm 
Offline
User
User

Joined: Mon Dec 12, 2016 1:37 pm
Posts: 62
Quote:
play it back using an external application

Yes.
reason: result wav-file will take up a lot of space in memory. I afraid that program will crashed. Even if we assume that the memory will be free after merge all fragments(catchsound) and playback(PlaySound -> FreeSound) or after export to disk(whole immediately). So I chose way write file to disk "step by step" ("byte by byte").


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 19 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye