class STFTSpectrogram::GUI
User interface
Constants
- MIN_WINDOW_HEIGHT
- MIN_WINDOW_WIDTH
- PLOT_TYPE_SPECTROGRAM
- PLOT_TYPE_TOP_MAGNITUDES
Public Class Methods
new(app)
click to toggle source
Calls superclass method
# File lib/gui/gui.rb, line 17 def initialize(app) super(app, 'STFT Spectrogram generator', width: 1280, height: 720, opts: DECOR_ALL & ~DECOR_SHRINKABLE) @plot = Plot.new @audio = nil @spectrogram = nil vframe1 = FXVerticalFrame.new(self, width: 1280, opts: LAYOUT_FILL) hframe1 = FXHorizontalFrame.new(vframe1, opts: LAYOUT_FILL | FRAME_SUNKEN) @img_spectrum = FXImageView.new(hframe1, opts: LAYOUT_FILL) @current_spec_img = nil hframe2 = FXHorizontalFrame.new(vframe1) vframe_window = FXVerticalFrame.new(hframe2, opts: LAYOUT_FILL) hframe_window_size = FXHorizontalFrame.new(vframe_window, opts: LAYOUT_RIGHT) hframe_window_overlap = FXHorizontalFrame.new(vframe_window, opts: LAYOUT_RIGHT) FXLabel.new(hframe_window_size, 'Window size:') @spin_window_size = FXSpinner.new(hframe_window_size, 10) @spin_window_size.range = 1..(2**31 - 1) @spin_window_size.value = 1 FXLabel.new(hframe_window_size, 'ms') FXLabel.new(hframe_window_overlap, 'Window overlap:') @spin_window_overlap = FXSpinner.new(hframe_window_overlap, 10) @spin_window_overlap.range = 0..(2**31 - 1) FXLabel.new(hframe_window_overlap, 'ms') FXVerticalSeparator.new(hframe2) vframe_freq = FXVerticalFrame.new(hframe2, opts: LAYOUT_FILL) hframe_freq_low = FXHorizontalFrame.new(vframe_freq, opts: LAYOUT_RIGHT) hframe_freq_high = FXHorizontalFrame.new(vframe_freq, opts: LAYOUT_RIGHT) FXLabel.new(hframe_freq_low, 'Low pass:') @spin_freq_low = FXSpinner.new(hframe_freq_low, 10) @spin_freq_low.range = 0..(2**31 - 1) FXLabel.new(hframe_freq_low, 'Hz') FXLabel.new(hframe_freq_high, 'High pass:') @spin_freq_high = FXSpinner.new(hframe_freq_high, 10) @spin_freq_high.range = 0..(2**31 - 1) FXLabel.new(hframe_freq_high, 'Hz') FXVerticalSeparator.new(hframe2) vframe_wav = FXVerticalFrame.new(hframe2, opts: LAYOUT_FILL) hframe_wav_txt = FXHorizontalFrame.new(vframe_wav, opts: LAYOUT_RIGHT) hframe_wav_btn = FXHorizontalFrame.new(vframe_wav, opts: LAYOUT_RIGHT) FXLabel.new(hframe_wav_txt, 'Loaded wave file:') @txt_wav_file = FXTextField.new(hframe_wav_txt, 60, opts: TEXTFIELD_READONLY) btn_wav_open = FXButton.new(hframe_wav_btn, 'Select WAV file') btn_wav_open.connect(SEL_COMMAND) { open_wav_file } FXVerticalSeparator.new(hframe2) vframe_plot = FXVerticalFrame.new(hframe2, opts: LAYOUT_FILL) hframe_plot_type = FXHorizontalFrame.new(vframe_plot, opts: LAYOUT_RIGHT) hframe_gen_plot = FXHorizontalFrame.new(vframe_plot, opts: LAYOUT_RIGHT) @combo_plot_type = FXComboBox.new(hframe_plot_type, 25, opts: COMBOBOX_STATIC) @combo_plot_type.appendItem('Spectrogram', PLOT_TYPE_SPECTROGRAM) @combo_plot_type.appendItem('Most significant frequencies only', PLOT_TYPE_TOP_MAGNITUDES) btn_plot = FXButton.new(hframe_gen_plot, 'Generate plot') btn_plot.connect(SEL_COMMAND) { on_btnplot_pressed } FXVerticalSeparator.new(hframe2) hframe_save_img = FXHorizontalFrame.new(hframe2, opts: LAYOUT_RIGHT) btn_save_img = FXButton.new(hframe_save_img, 'Save graph') btn_save_img.connect(SEL_COMMAND) { on_save_graph } @spin_window_size.connect(SEL_COMMAND) { on_window_changed } @spin_window_overlap.connect(SEL_COMMAND) { on_window_changed } end
Public Instance Methods
create()
click to toggle source
Calls superclass method
# File lib/gui/gui.rb, line 100 def create super show(PLACEMENT_SCREEN) end
Private Instance Methods
create_spectrogram()
click to toggle source
# File lib/gui/gui.rb, line 161 def create_spectrogram return nil if @audio.nil? return @spectrogram unless @spectrogram.nil? spectrogram = nil begin spectrogram = Spectrogram.new(@audio, @spin_window_size.value, @spin_window_overlap.value) rescue ArgumentError => e FXMessageBox.error(self, MBOX_OK, 'Bad arguments!', e.message) return nil end spectrogram end
load_data(path)
click to toggle source
# File lib/gui/gui.rb, line 147 def load_data(path) audio = nil begin audio = AudioFile.new(path) rescue IOError => e FXMessageBox.error(self, MBOX_OK, 'Non-existing file!', e.message) return nil rescue WaveFile::FormatError => e FXMessageBox.error(self, MBOX_OK, 'Bad format!', e.message) return nil end audio end
load_generated_image(path)
click to toggle source
# File lib/gui/gui.rb, line 197 def load_generated_image(path) @current_spec_img = FXPNGImage.new(getApp, nil, IMAGE_KEEP | IMAGE_OWNED | IMAGE_SHMP | IMAGE_SHMI) getApp.beginWaitCursor do FXFileStream.open(path, FXStreamLoad) do |stream| @current_spec_img.loadPixels(stream) end @current_spec_img.create @img_spectrum.image = @current_spec_img end end
no_data?(data)
click to toggle source
# File lib/gui/gui.rb, line 210 def no_data?(data) if data.empty? FXMessageBox.error(self, MBOX_OK, 'No data!', 'Selected slice contains no data!') return true end false end
on_btnplot_pressed()
click to toggle source
# File lib/gui/gui.rb, line 179 def on_btnplot_pressed @spectrogram = create_spectrogram return if @spectrogram.nil? @spectrogram.transform! unless @spectrogram.transformed? @spectrogram.filter(@spin_freq_low.value, @spin_freq_high.value) index = @combo_plot_type.currentItem return if index < 0 type = @combo_plot_type.getItemData(index) if type == PLOT_TYPE_SPECTROGRAM plot_spectrogram elsif type == PLOT_TYPE_TOP_MAGNITUDES plot_top_freqs end end
on_save_graph()
click to toggle source
# File lib/gui/gui.rb, line 126 def on_save_graph if @current_spec_img.nil? FXMessageBox.error(self, MBOX_OK, 'No image!', 'Generate the graph first!') return end dialog = FXFileDialog.new(self, 'Save Graph') dialog.patternList = ['PNG Files (*.png)'] dialog.filename = '*.png' unless dialog.execute.zero? if File.exist? dialog.filename overwrite = FXMessageBox.question(self, MBOX_YES_NO, 'File exists!', 'Overwrite file?') return 1 if overwrite == MBOX_CLICKED_NO end save_graph(dialog.filename) end 1 end
on_window_changed()
click to toggle source
# File lib/gui/gui.rb, line 175 def on_window_changed @spectrogram = nil end
open_wav_file()
click to toggle source
# File lib/gui/gui.rb, line 107 def open_wav_file dialog = FXFileDialog.new(self, 'Open WAV File') dialog.patternList = ['WAV Files (*.wav)'] dialog.selectMode = SELECTFILE_EXISTING return if dialog.execute.zero? @txt_wav_file.text = '' @spectrogram = nil @audio = load_data(dialog.filename) @txt_wav_file.text = dialog.filename unless @audio.nil? end
plot_spectrogram()
click to toggle source
# File lib/gui/gui.rb, line 230 def plot_spectrogram freqs = @spectrogram.freqs return if no_data? freqs data = @spectrogram.windows.map { |w| w.spectrum.values } x = @spectrogram.windows.map { |w| w.time.to_i }.uniq len = data.length / x.length xmap = x.map.with_index do |t, i| '"' + t.to_s + '" ' + (i * len).to_s end len = x.length / [x.length, 15].min xmap = (len - 1).step(xmap.size - 1, len).map { |i| xmap[i] } data = data.transpose len = data.length / freqs.length ymap = freqs.map.with_index do |f, i| '"' + (f / 1000.0).to_s + '" ' + (i * len).to_s end len = freqs.length / [freqs.length, 20].min ymap = (len - 1).step(ymap.size - 1, len).map { |i| ymap[i] } @plot.xtics = '(' + xmap.join(', ') + ')' @plot.ytics = '(' + ymap.join(', ') + ')' @plot.plot_matrix(data, @img_spectrum.width, @img_spectrum.height) load_generated_image(@plot.imgfile.path) end
plot_top_freqs()
click to toggle source
# File lib/gui/gui.rb, line 219 def plot_top_freqs freqs = @spectrogram.freqs return if no_data? freqs y = @spectrogram.windows.map { |w| w.strongest_freq[0] / 1000.0 } x = @spectrogram.windows.map(&:time) @plot.xtics = 'auto' @plot.ytics = 'auto' @plot.plot(x, y, @img_spectrum.width, @img_spectrum.height) load_generated_image(@plot.imgfile.path) end
save_graph(path)
click to toggle source
# File lib/gui/gui.rb, line 118 def save_graph(path) getApp.beginWaitCursor do FXFileStream.open(path, FXStreamSave) do |stream| @current_spec_img.savePixels(stream) end end end