hackedteam/rcs-db

View on GitHub
lib/rcs-worker/libs/speex/speex.rb

Summary

Maintainability
A
1 hr
Test Coverage

require 'rcs-common/trace'

require 'ffi'
require 'rbconfig'

module Speex
    extend FFI::Library
  extend RCS::Tracer

    class Mode < FFI::Struct
      layout :mode,       :pointer,
             :query,      :pointer,
             :modeName,   :string,
             :modeID,     :int,
             :bistream_version, :int,
             :enc_init,   :pointer,
             :enc_destroy,:pointer,
             :enc,        :pointer,
             :dec_init,   :pointer,
             :dec_destroy,:pointer,
             :dec,        :pointer,
             :enc_ctl,    :pointer,
             :dec_ctl,    :pointer
  end
    
    class Bits < FFI::Struct
      layout :chars,      :string,
             :nbBits,     :int,
             :charPtr,    :int,
             :bitPtr,     :int, 
             :owner,      :int,
             :overflow,   :int,
             :buf_size,   :int,
             :reserved1,  :int,
             :reserved2,  :int
  end
  
  MODEID_NB = 0
  MODEID_UWB = 2
  SET_ENH = 0
  GET_FRAME_SIZE = 3
  GET_VERSION_STRING = 9
  
  begin
    base_path = File.dirname(__FILE__)
    case RbConfig::CONFIG['host_os']
        when /darwin/
            ffi_lib File.join(base_path, 'macos/libspeex.1.5.0.dylib')
        when /mingw/
            ffi_lib File.join(base_path, 'win/libspeex.dll')
    end
    
    ffi_convention :stdcall

    attach_function :decoder_init, :speex_decoder_init, [:pointer], :pointer
    attach_function :decoder_destroy, :speex_decoder_destroy, [:pointer], :void
    attach_function :decoder_ctl, :speex_decoder_ctl, [:pointer, :int, :pointer], :int
    attach_function :decode, :speex_decode, [:pointer, :pointer, :pointer], :int

      attach_function :bits_init, :speex_bits_init, [:pointer], :void
    attach_function :bits_init_buffer, :speex_bits_init_buffer, [:pointer, :pointer, :int], :void
    attach_function :bits_destroy, :speex_bits_destroy, [:pointer], :void
    attach_function :bits_read_from, :speex_bits_read_from, [:pointer, :pointer, :int], :void

    attach_function :lib_get_mode, :speex_lib_get_mode, [:int], :pointer
    attach_function :lib_ctl, :speex_lib_ctl, [:int, :pointer], :int
  rescue Exception => e
    trace :fatal, "ERROR: Cannot open libspeex"
    exit!
  end

  def self.get_wav_frames(data, mode)

    decoder = Speex.decoder_init(Speex.lib_get_mode(mode))

    # enable enhancement
    enhancement_ptr = FFI::MemoryPointer.new(:int32).write_uint 1
    Speex.decoder_ctl(decoder, Speex::SET_ENH, enhancement_ptr)

    # get frame size
    frame_size_ptr = FFI::MemoryPointer.new(:int32).write_uint 0
    Speex.decoder_ctl(decoder, Speex::GET_FRAME_SIZE, frame_size_ptr)
    frame_size = frame_size_ptr.get_uint(0)

    bits = Speex::Bits.new
    Speex.bits_init(bits.pointer)

    wav_ary = []
    stream = StringIO.new data
    until stream.eof? do
      # read one chunk
      len = stream.read(4).unpack("L").shift
      chunk = stream.read(len)
      break if chunk.nil?

      if chunk.size == len
        buffer = FFI::MemoryPointer.new(:char, len)
        buffer.put_bytes(0, chunk, 0, len)

        Speex.bits_read_from(bits.pointer, buffer, len)

        output_buffer = FFI::MemoryPointer.new(:float, frame_size)
        Speex.decode(decoder, bits.pointer, output_buffer)

        # Speex outputs 32 bits float samples
        wav_ary.concat output_buffer.read_array_of_float(frame_size)
      end
    end

    Speex.bits_destroy(bits.pointer)
    Speex.decoder_destroy(decoder)

    wav_ary
  end

  def self.version
    ptr = FFI::MemoryPointer.new :pointer, 1
    Speex::lib_ctl Speex::GET_VERSION_STRING, ptr
    ptr.read_pointer.read_string
  end
end