Class: Waveform

Inherits:
Object
  • Object
show all
Defined in:
lib/waveform.rb,
lib/waveform.rb,
lib/waveform/version.rb

Defined Under Namespace

Classes: ArgumentError, Log, RuntimeError

Constant Summary collapse

DefaultOptions =
{
  :method => :peak,
  :width => 1800,
  :height => 280,
  :background_color => "#666666",
  :color => "#00ccff",
  :force => false,
  :logger => nil
}
TransparencyMask =
"#00ff00"
TransparencyAlternate =

in case the mask is the background color!

"#ffff00"
VERSION =
"0.1.3"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#sourceObject (readonly)

Returns the value of attribute source.



24
25
26
# File 'lib/waveform.rb', line 24

def source
  @source
end

Class Method Details

.generate(source, filename, options = {}) ⇒ Object

Generate a Waveform image at the given filename with the given options.

Available options (all optional) are:

:method => The method used to read sample frames, available methods
  are peak and rms. peak is probably what you're used to seeing, it uses
  the maximum amplitude per sample to generate the waveform, so the
  waveform looks more dynamic. RMS gives a more fluid waveform and
  probably more accurately reflects what you hear, but isn't as
  pronounced (typically).

  Can be :rms or :peak
  Default is :peak.

:width => The width (in pixels) of the final waveform image.
  Default is 1800.

:height => The height (in pixels) of the final waveform image.
  Default is 280.

:auto_width => msec per pixel. This will overwrite the width of the
  final waveform image depending on the length of the audio file.
  Example:
    100 => 1 pixel per 100 msec; a one minute audio file will result in a width of 600 pixels

:background_color => Hex code of the background color of the generated
  waveform image.
  Default is #666666 (gray).

:color => Hex code of the color to draw the waveform, or can pass
  :transparent to render the waveform transparent (use w/ a solid
  color background to achieve a "cutout" effect).
  Default is #00ccff (cyan-ish).

:force => Force generation of waveform, overwriting WAV or PNG file.

:logger => IOStream to log progress to.

Example:

Waveform.generate("Kickstart My Heart.wav", "Kickstart My Heart.png")
Waveform.generate("Kickstart My Heart.wav", "Kickstart My Heart.png", :method => :rms)
Waveform.generate("Kickstart My Heart.wav", "Kickstart My Heart.png", :color => "#ff00ff", :logger => $stdout)

Raises:



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/waveform.rb', line 75

def generate(source, filename, options={})
  options = DefaultOptions.merge(options)

  raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless source
  raise ArgumentError.new("No destination filename given for waveform") unless filename
  raise RuntimeError.new("Source audio file '#{source}' not found.") unless File.exist?(source)
  raise RuntimeError.new("Destination file #{filename} exists. Use --force if you want to automatically remove it.") if File.exist?(filename) && !options[:force] === true

  @log = Log.new(options[:logger])
  @log.start!

  if options[:auto_width]
    RubyAudio::Sound.open(source) do |audio|
      options[:width] = (audio.info.length * 1000 / options[:auto_width].to_i).ceil
    end
  end

  # Frames gives the amplitudes for each channel, for our waveform we're
  # saying the "visual" amplitude is the average of the amplitude across all
  # the channels. This might be a little weird w/ the "peak" method if the
  # frames are very wide (i.e. the image width is very small) -- I *think*
  # the larger the frames are, the more "peaky" the waveform should get,
  # perhaps to the point of inaccurately reflecting the actual sound.
  samples = frames(source, options[:width], options[:method]).collect do |frame|
    frame.inject(0.0) { |sum, peak| sum + peak } / frame.size
  end

  @log.timed("\nDrawing...") do
    # Don't remove the file even if force is true until we're sure the
    # source was readable
    if File.exist?(filename) && options[:force] === true
      @log.out("Output file #{filename} encountered. Removing.")
      File.unlink(filename)
    end

    image = draw samples, options
    image.save filename
  end

  @log.done!("Generated waveform '#{filename}'")
end