docs: def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
                 always_2d=False, fill_value=None, out=None):
          """Return a generator for block-wise reading.
  
          By default, the generator yields blocks of the given
          `blocksize` (using a given `overlap`) until the end of the file
          is reached; `frames` can be used to stop earlier.
  
          Parameters
          ----------
          blocksize : int
              The number of frames to read per block. Either this or `out`
              must be given.
          overlap : int, optional
              The number of frames to rewind between each block.
          frames : int, optional
              The number of frames to read.
              If ``frames < 0``, the file is read until the end.
          dtype : {'float64', 'float32', 'int32', 'int16'}, optional
              See :meth:`.read`.
  
          Yields
          ------
          numpy.ndarray or type(out)
              Blocks of audio data.
              If `out` was given, and the requested frames are not an
              integer multiple of the length of `out`, and no
              `fill_value` was given, the last block will be a smaller
              view into `out`.
  
  
          Other Parameters
          ----------------
          always_2d, fill_value, out
              See :meth:`.read`.
          fill_value : float, optional
              See :meth:`.read`.
          out : numpy.ndarray or subclass, optional
              If `out` is specified, the data is written into the given
              array instead of creating a new array. In this case, the
              arguments `dtype` and `always_2d` are silently ignored!
  
          Examples
          --------
          >>> from soundfile import SoundFile
          >>> with SoundFile('stereo_file.wav') as f:
          >>>     for block in f.blocks(blocksize=1024):
          >>>         pass  # do something with 'block'
  
          """
          import numpy as np
  
          if 'r' not in self.mode and '+' not in self.mode:
              raise RuntimeError("blocks() is not allowed in write-only mode")
  
          if out is None:
              if blocksize is None:
                  raise TypeError("One of {blocksize, out} must be specified")
              out = self._create_empty_array(blocksize, always_2d, dtype)
              copy_out = True
          else:
              if blocksize is not None:
                  raise TypeError(
                      "Only one of {blocksize, out} may be specified")
              blocksize = len(out)
              copy_out = False
  
          overlap_memory = None
          frames = self._check_frames(frames, fill_value)
          while frames > 0:
              if overlap_memory is None:
                  output_offset = 0
              else:
                  output_offset = len(overlap_memory)
                  out[:output_offset] = overlap_memory
  
              toread = min(blocksize - output_offset, frames)
              self.read(toread, dtype, always_2d, fill_value, out[output_offset:])
  
              if overlap:
                  if overlap_memory is None:
                      overlap_memory = np.copy(out[-overlap:])
                  else:
                      overlap_memory[:] = out[-overlap:]
  
              if blocksize > frames + overlap and fill_value is None:
                  block = out[:frames + overlap]
              else:
                  block = out
              yield np.copy(block) if copy_out else block
              frames -= toread