1

I am writing a Fortran90 code to read .wav audio files.

Within the .wav format there is a chunk introduced by a string 'WAVE'. Within this chunk must appear two subchunks introduced by the strings 'fmt ' and 'data'.

In the particular .wav file I am using, to test the code, after the 'WAVE' string there is a gap of 36 characters beginning with the word 'JUNK' before the subchunk beginning with 'fmt ' appears in the file (picture suppled below).

The online resources I have read do not indicate such gaps are to be expected. The expectation is 'fmt ' should appear directly after 'WAVE'.

.wav file format description

I don't want my code to collapse when it encounters untypical formatting.

There appears to be no way to predetermine where the 'fmt ' string appears in the file. My strategy is to search the file for it and then simply discard the rogue section beginning with 'JUNK'.

My initial attempts to search the file stream using SCAN or INDEX have failed because passing these intrinsic functions the open file unit number throws an error which reports the file is not a string.

It may aid clarity to read my code as it is so far.

program main
  
  use iso_fortran_env

  !=========================================================================

  !Variables for .wav header.
  character(4)     :: ChunkID = '____'
  integer  (4)     :: FileSize
  character(4)     :: Wave = 'WAVE'

  !fmt need only be charcter(4) but is extended here for illustation output.
  character(40)    :: fmt = 'fmt '
  
  !=========================================================================

  !Working variables for file handling..
  integer  (1)  :: args
  character(30) :: file
  integer :: stat

  !Exit when no file name is supplied. 
  args = command_argument_count()
  if(args.ne.1)then
     print *
     print *, 'Error. Enter .wav file name'
     print *, 'Example: cat'
     print *, "NB. The '.wav' extension is assumed. You don't need to add it."
     stop
  end if
  call GET_COMMAND_ARGUMENT(1,file)

  !Construct .wav file name.
  file =  trim(file) // '.wav'

  !Try opening .wav file with name supplied
  OPEN(UNIT=1, iostat=stat, FILE=file, &
       form='unformatted', access='stream', status='old')

  !Test file status and exit on error.
  if(stat.ne.0) then
     write(*,'(a)') 'No known file named ', file
     stop
  end if
  print *, 'File existence test: Passed'

  ! Header read.
  read(1) ChunkID, FileSize, Wave, fmt
  print *, 'ChunkID: ', ChunkID
  print *, 'FileSize: ', FileSize
  print *, '"WAVE": ', wave
  print *, '"fmt ":', fmt

END PROGRAM MAIN

The output the program produces using my downloaded trial .wav file is this:

enter image description here

The trouble starts with the unwanted text following "fmt ": ahead of fmt at the end.

My purpose is to discard this redundant string then continue reading the file from the expected string 'fmt '.

What intrinsics should I use in fortran to enable to me to absorb and discard useless file contents, retaining the parts that I need, within a file containing an assortment of data types?

Georgina Davenport
  • 189
  • 1
  • 2
  • 11
  • 1
    Please use tag [tag:fortran] for all Fortran questions. Be aware that Fortran 90 is a very old incarnation of the standard. There are many more recent ones. Also be aware that stuff like `integer(1)` and `integer(4)` is not portable between compilers, see https://stackoverflow.com/questions/3170239/fortran-integer4-vs-integer4-vs-integerkind-4 – Vladimir F Героям слава Mar 07 '21 at 16:26
  • 1
    Actually, you are using stream access. That is Fortran 2003, not 90. Also, using file unit number 1 is dangerous. Use numbers higher than 10, some numbers are pre-connected somewhere already. – Vladimir F Героям слава Mar 07 '21 at 16:30
  • Yes, I had also tried 'sequential'. I am a beginning Fortran programmer not certain of how best to open a file which contains a mixture of data types and unknown length. – Georgina Davenport Mar 07 '21 at 17:01
  • 1
    Stream is the correct thing, do not use sequential here. That uses records separated by record markers. – Vladimir F Героям слава Mar 07 '21 at 17:34

1 Answers1

1

I use this subroutine to change the file position behind the searched string str:

subroutine skip_to(str, stat)
  character(*), intent(in) :: str
  integer, intent(out) :: stat
  character :: ch
  integer :: io

  do
    read(unit, iostat=io) ch

    if (io/=0) then
      stat = 1
      return
    end if

    if (ch==str(1:1)) then
      call check(str(2:), stat)
      if (stat == 0) return
    end if

  end do
end subroutine

subroutine check(str, stat)
  character(*), intent(in) :: str
  integer, intent(out) :: stat
  character :: ch
  integer :: i, io

  stat = 1
  i = 0

  do
    i = i + 1

    read(unit, iostat=io) ch

    if (io/=0) return

    if (ch/=str(i:i)) return

    if (i==len(str)) then
      stat = 0
      return
    end if
  end do
end subroutine

It might be very inefficient because it reads one byte a time for maximum simplicity. It just reads a byte and checks whether the string might be starting there and then it checks whether the next byte is the right one and so on.


Note that I often have to search for a string in the middle of a very large vtk file (gigabytes).

If you actually have just a small header. I would read the whole header into a long string and process it in memory using string-oriented routines.

  • "You are reading fixed-length characters so no wonder the character also contains some junk". I don't understand what this means. – Georgina Davenport Mar 07 '21 at 16:52
  • Thank you Vladimir, I shall take a close look at this subroutine. In the interim I have altered my question slightly in the hope of producing a clearer question. – Georgina Davenport Mar 07 '21 at 17:00
  • 1
    Don't worry about that, I think I understand your intent. I have deleted the confusing sentence. – Vladimir F Героям слава Mar 07 '21 at 17:04
  • Thank you. Would it be correct to say fortran intrinsics do not provide a function to find a string within a stream of mixed data types, which includes non character types, and it is therefore necessary to write my own function or subroutine to do it, such as you have above? – Georgina Davenport Mar 07 '21 at 17:50
  • 1
    Probably not. The external file is a stream of bytes. It can be a stream of `character(1)` if you want to read it that way. But Fortran does not provide intrinsic for searching in external files. – Vladimir F Героям слава Mar 07 '21 at 18:46