class Playback(): def __init__(self, main): self.main = main self.audio = PlayAudio() self.create_actions() def create_actions(self): # Playback self.main.actionPlay = QtGui.QAction('Play data chunk', self.main) self.main.actionPlay.setShortcut(Qt.Qt.Key_P) self.main.actionPlay.triggered.connect(self.play) self.main.addAction(self.main.actionPlay) def play(self): os.close(sys.stderr.fileno()) # block error messages data = self.main.data.get_data() / self.main.data.get_data().max() rate = self.main.data.audio.samplerate self.audio.play(data, rate, blocking=False) os.open(sys.stderr.fileno()) # unblock error messages def close(self): self.audio.close()
class SignalPlot: def __init__(self, data, samplingrate, unit, filename, channel, verbose, cfg): self.filename = filename self.channel = channel self.samplerate = samplingrate self.data = data self.unit = unit self.cfg = cfg self.verbose = verbose self.tmax = (len(self.data)-1)/self.samplerate self.toffset = 0.0 self.twindow = 8.0 if self.twindow > self.tmax: self.twindow = np.round(2 ** (np.floor(np.log(self.tmax) / np.log(2.0)) + 1.0)) self.ymin = -1.0 self.ymax = +1.0 self.trace_artist = None self.spectrogram_artist = None self.fmin = 0.0 self.fmax = 0.0 self.decibel = True self.fresolution = self.cfg.value('frequencyResolution') self.deltaf = 1.0 self.mains_freq = self.cfg.value('mainsFreq') self.power_label = None self.all_peaks_artis = None self.good_peaks_artist = None self.power_artist = None self.power_frequency_label = None self.peak_artists = [] self.legend = True self.legendhandle = None self.help = self.cfg.value('displayHelp') self.helptext = [] self.allpeaks = [] self.fishlist = [] self.mains = [] self.peak_specmarker = [] self.peak_annotation = [] self.min_clip = self.cfg.value('minClipAmplitude') self.max_clip = self.cfg.value('maxClipAmplitude') self.colorrange, self.markerrange = colors_markers() # audio output: self.audio = PlayAudio() # set key bindings: plt.rcParams['keymap.fullscreen'] = 'ctrl+f' plt.rcParams['keymap.pan'] = 'ctrl+m' plt.rcParams['keymap.quit'] = 'ctrl+w, alt+q, q' plt.rcParams['keymap.yscale'] = '' plt.rcParams['keymap.xscale'] = '' plt.rcParams['keymap.grid'] = '' plt.rcParams['keymap.all_axes'] = '' # the figure: self.fig = plt.figure(figsize=(15, 9)) self.fig.canvas.set_window_title(self.filename + ' channel {0:d}'.format(self.channel)) self.fig.canvas.mpl_connect('key_press_event', self.keypress) self.fig.canvas.mpl_connect('button_press_event', self.buttonpress) self.fig.canvas.mpl_connect('pick_event', self.onpick) self.fig.canvas.mpl_connect('resize_event', self.resize) # trace plot: self.axt = self.fig.add_axes([0.1, 0.7, 0.87, 0.25]) self.axt.set_ylabel('Amplitude [{:s}]'.format(self.unit)) ht = self.axt.text(0.98, 0.05, '(ctrl+) page and arrow up, down, home, end: scroll', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.15, '+, -, X, x: zoom in/out', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.25, 'y,Y,v,V: zoom amplitudes', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.35, 'p,P: play audio (display,all)', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.45, 'ctrl-f: full screen', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.55, 'w: plot waveform into png file', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.65, 's: save figure', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.75, 'S: save audiosegment', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.85, 'q: quit', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.95, 'h: toggle this help', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) self.axt.set_xticklabels([]) # spectrogram: self.axs = self.fig.add_axes([0.1, 0.45, 0.87, 0.25]) self.axs.set_xlabel('Time [seconds]') self.axs.set_ylabel('Frequency [Hz]') # power spectrum: self.axp = self.fig.add_axes([0.1, 0.1, 0.87, 0.25]) ht = self.axp.text(0.98, 0.9, 'r, R: frequency resolution', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.8, 'f, F: zoom', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.7, '(ctrl+) left, right: move', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.6, 'l: toggle legend', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.5, 'd: toggle decibel', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.4, 'm: toggle mains filter', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.3, 'left mouse: show peak properties', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.2, 'shift/ctrl + left/right mouse: goto previous/next harmonic', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) # plot: for ht in self.helptext: ht.set_visible(self.help) self.update_plots(False) plt.show() def __del(self): self.audio.close() def remove_peak_annotation(self): for fm in self.peak_specmarker: fm.remove() self.peak_specmarker = [] for fa in self.peak_annotation: fa.remove() self.peak_annotation = [] def annotate_peak(self, peak, harmonics=-1, inx=-1): # marker: if inx >= 0: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color=self.colorrange[inx % len(self.colorrange)], marker=self.markerrange[inx], ms=10.0, mec=None, mew=0.0, zorder=2) else: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color='k', marker='o', ms=10.0, mec=None, mew=0.0, zorder=2) self.peak_specmarker.append(m) # annotation: fwidth = self.fmax - self.fmin pl = [] if self.cfg.value('labelFrequency'): pl.append(r'$f=${:.1f} Hz'.format(peak[0])) if self.cfg.value('labelHarmonic') and harmonics >= 0: pl.append(r'$h=${:d}'.format(harmonics)) if self.cfg.value('labelPower'): pl.append(r'$p=${:g}'.format(peak[1])) if self.cfg.value('labelWidth'): pl.append(r'$\Delta f=${:.2f} Hz'.format(peak[3])) if self.cfg.value('labelDoubleUse'): pl.append(r'dc={:.0f}'.format(peak[4])) self.peak_annotation.append(self.axp.annotate('\n'.join(pl), xy=(peak[0], peak[1]), xytext=(peak[0] + 0.03 * fwidth, peak[1]), bbox=dict(boxstyle='round', facecolor='white'), arrowprops=dict(arrowstyle='-'))) def annotate_fish(self, fish, inx=-1): self.remove_peak_annotation() for harmonic, freq in enumerate(fish[:, 0]): peak = self.allpeaks[np.abs(self.allpeaks[:, 0] - freq) < 0.8 * self.deltaf, :] if len(peak) > 0: self.annotate_peak(peak[0, :], harmonic, inx) self.fig.canvas.draw() def update_plots(self, draw=True): self.remove_peak_annotation() # trace: self.axt.set_xlim(self.toffset, self.toffset + self.twindow) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.trace_artist == None: self.trace_artist, = self.axt.plot(time, self.data[t0:t1]) else: self.trace_artist.set_data(time, self.data[t0:t1]) self.axt.set_ylim(self.ymin, self.ymax) # compute power spectrum: nfft, noverlap = nfft_noverlap(self.fresolution, self.samplerate, 0.5, 16) t00 = t0 t11 = t1 w = t11 - t00 minw = nfft * (self.cfg.value('minPSDAverages') + 1) // 2 if t11 - t00 < minw: w = minw t11 = t00 + w if t11 >= len(self.data): t11 = len(self.data) t00 = t11 - w if t00 < 0: t00 = 0 t11 = w power, freqs = ml.psd(self.data[t00:t11], NFFT=nfft, noverlap=noverlap, Fs=self.samplerate, detrend=ml.detrend_mean) power = np.squeeze(power) # squeeze is necessary when nfft is to large with respect to the data self.deltaf = freqs[1] - freqs[0] # detect fish: h_kwargs = psd_peak_detection_args(self.cfg) h_kwargs.update(harmonic_groups_args(self.cfg)) self.fishlist, fzero_harmonics, self.mains, self.allpeaks, peaks, lowth, highth, center = harmonic_groups(freqs, power, verbose=self.verbose, **h_kwargs) highth = center + highth - 0.5 * lowth lowth = center + 0.5 * lowth # spectrogram: t2 = t1 + nfft specpower, freqs, bins = ml.specgram(self.data[t0:t2], NFFT=nfft, Fs=self.samplerate, noverlap=nfft // 2, detrend=ml.detrend_mean) z = decibel(specpower) z = np.flipud(z) extent = self.toffset, self.toffset + np.amax(bins), freqs[0], freqs[-1] self.axs.set_xlim(self.toffset, self.toffset + self.twindow) if self.spectrogram_artist == None: self.fmax = np.round((freqs[-1] / 4.0) / 100.0) * 100.0 min = highth min = np.percentile(z, 70.0) max = np.percentile(z, 99.9) + 30.0 # cm = plt.get_cmap( 'hot_r' ) cm = plt.get_cmap('jet') self.spectrogram_artist = self.axs.imshow(z, aspect='auto', extent=extent, vmin=min, vmax=max, cmap=cm, zorder=1) else: self.spectrogram_artist.set_data(z) self.spectrogram_artist.set_extent(extent) self.axs.set_ylim(self.fmin, self.fmax) # power spectrum: self.axp.set_xlim(self.fmin, self.fmax) if self.deltaf >= 1000.0: dfs = '%.3gkHz' % 0.001 * self.deltaf else: dfs = '%.3gHz' % self.deltaf tw = float(w) / self.samplerate if tw < 1.0: tws = '%.3gms' % (1000.0 * tw) else: tws = '%.3gs' % (tw) a = 2 * w // nfft - 1 # number of ffts m = '' if self.cfg.value('mainsFreq') > 0.0: m = ', mains=%.0fHz' % self.cfg.value('mainsFreq') if self.power_frequency_label == None: self.power_frequency_label = self.axp.set_xlabel( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(nfft, dfs, tws, a, m)) else: self.power_frequency_label.set_text( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(nfft, dfs, tws, a, m)) self.axp.set_xlim(self.fmin, self.fmax) if self.power_label == None: self.power_label = self.axp.set_ylabel('Power') if self.decibel: if len(self.allpeaks) > 0: self.allpeaks[:, 1] = decibel(self.allpeaks[:, 1]) power = decibel(power) pmin = np.min(power[freqs < self.fmax]) pmin = np.floor(pmin / 10.0) * 10.0 pmax = np.max(power[freqs < self.fmax]) pmax = np.ceil(pmax / 10.0) * 10.0 doty = pmax - 5.0 self.power_label.set_text('Power [dB]') self.axp.set_ylim(pmin, pmax) else: pmax = np.max(power[freqs < self.fmax]) doty = pmax pmax *= 1.1 self.power_label.set_text('Power') self.axp.set_ylim(0.0, pmax) if self.all_peaks_artis == None: self.all_peaks_artis, = self.axp.plot(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty, 'o', color='#ffffff') self.good_peaks_artist, = self.axp.plot(peaks, np.zeros(len(peaks)) + doty, 'o', color='#888888') else: self.all_peaks_artis.set_data(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty) self.good_peaks_artist.set_data(peaks, np.zeros(len(peaks)) + doty) labels = [] fsizes = [np.sqrt(np.sum(self.fishlist[k][:, 1])) for k in range(len(self.fishlist))] fmaxsize = np.max(fsizes) if len(fsizes) > 0 else 1.0 for k in range(len(self.peak_artists)): self.peak_artists[k].remove() self.peak_artists = [] for k in range(len(self.fishlist)): if k >= len(self.markerrange): break fpeaks = self.fishlist[k][:, 0] fpeakinx = [int(np.round(fp / self.deltaf)) for fp in fpeaks if fp < freqs[-1]] fsize = 7.0 + 10.0 * (fsizes[k] / fmaxsize) ** 0.5 fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', color=self.colorrange[k % len(self.colorrange)], marker=self.markerrange[k], ms=fsize, mec=None, mew=0.0, zorder=1) self.peak_artists.append(fishpoints) if self.deltaf < 0.1: labels.append('%4.2f Hz' % fpeaks[0]) elif self.deltaf < 1.0: labels.append('%4.1f Hz' % fpeaks[0]) else: labels.append('%4.0f Hz' % fpeaks[0]) if len(self.mains) > 0: fpeaks = self.mains[:, 0] fpeakinx = np.array([np.round(fp / self.deltaf) for fp in fpeaks if fp < freqs[-1]], dtype=np.int) fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', marker='.', color='k', ms=10, mec=None, mew=0.0, zorder=2) self.peak_artists.append(fishpoints) labels.append('%3.0f Hz mains' % self.cfg.value('mainsFreq')) ncol = (len(labels)-1) // 8 + 1 self.legendhandle = self.axs.legend(self.peak_artists[:len(labels)], labels, loc='upper right', ncol=ncol) self.legenddict = dict() for legpoints, (finx, fish) in zip(self.legendhandle.get_lines(), enumerate(self.fishlist)): legpoints.set_picker(8) self.legenddict[legpoints] = [finx, fish] self.legendhandle.set_visible(self.legend) if self.power_artist == None: self.power_artist, = self.axp.plot(freqs, power, 'b', zorder=3) else: self.power_artist.set_data(freqs, power) if draw: self.fig.canvas.draw() def keypress(self, event): # print('pressed', event.key) if event.key in '+=X': if self.twindow * self.samplerate > 20: self.twindow *= 0.5 self.update_plots() elif event.key in '-x': if self.twindow < len(self.data) / self.samplerate: self.twindow *= 2.0 self.update_plots() elif event.key == 'pagedown': if self.toffset + 0.5 * self.twindow < len(self.data) / self.samplerate: self.toffset += 0.5 * self.twindow self.update_plots() elif event.key == 'pageup': if self.toffset > 0: self.toffset -= 0.5 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'a': if self.min_clip == 0.0 or self.max_clip == 0.0: self.min_clip, self.max_clip = clip_amplitudes( self.data, **clip_args(self.cfg, self.samplerate)) try: idx0, idx1, clipped = best_window_indices( self.data, self.samplerate, min_clip=self.min_clip, max_clip=self.max_clip, **best_window_args(self.cfg)) if idx1 > 0: self.toffset = idx0 / self.samplerate self.twindow = (idx1 - idx0) / self.samplerate self.twindow *= 2.0/(self.cfg.value('numberPSDWindows')+1.0) self.update_plots() except UserWarning as e: if self.verbose > 0: print(str(e)) elif event.key == 'ctrl+pagedown': if self.toffset + 5.0 * self.twindow < len(self.data) / self.samplerate: self.toffset += 5.0 * self.twindow self.update_plots() elif event.key == 'ctrl+pageup': if self.toffset > 0: self.toffset -= 5.0 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'down': if self.toffset + self.twindow < len(self.data) / self.samplerate: self.toffset += 0.05 * self.twindow self.update_plots() elif event.key == 'up': if self.toffset > 0.0: self.toffset -= 0.05 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'home': if self.toffset > 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'end': toffs = np.floor(len(self.data) / self.samplerate / self.twindow) * self.twindow if self.toffset < toffs: self.toffset = toffs self.update_plots() elif event.key == 'y': h = self.ymax - self.ymin c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'Y': h = 0.25 * (self.ymax - self.ymin) c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'v': t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) min = np.min(self.data[t0:t1]) max = np.max(self.data[t0:t1]) h = 0.5 * (max - min) c = 0.5 * (max + min) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'V': self.ymin = -1.0 self.ymax = +1.0 self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin -= 0.5 * fwidth self.fmax -= 0.5 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin self.fmin += 0.5 * fwidth self.fmax += 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin fm = 0.5 * self.samplerate self.fmax = np.ceil(fm / fwidth) * fwidth self.fmin = self.fmax - fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'f': if self.fmax < 0.5 * self.samplerate or self.fmin > 0.0: fwidth = self.fmax - self.fmin if self.fmax < 0.5 * self.samplerate: self.fmax = self.fmin + 2.0 * fwidth elif self.fmin > 0.0: self.fmin = self.fmax - 2.0 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = 2.0 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'F': if self.fmax - self.fmin > 1.0: fwidth = self.fmax - self.fmin self.fmax = self.fmin + 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'r': if self.fresolution < 1000.0: self.fresolution *= 2.0 self.update_plots() elif event.key in 'R': if 1.0 / self.fresolution < self.tmax: self.fresolution *= 0.5 self.update_plots() elif event.key in 'd': self.decibel = not self.decibel self.update_plots() elif event.key in 'm': if self.cfg.value('mainsFreq') == 0.0: self.cfg.set('mainsFreq', self.mains_freq) else: self.cfg.set('mainsFreq', 0.0) self.update_plots() elif event.key in 't': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') - 0.1) if self.cfg.value('lowThresholdFactor') < 0.1: self.cfg.set('lowThresholdFactor', 0.1) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key in 'T': t_diff = self.cfg.value('highThresholdFactor') - self.cfg.value('lowThresholdFactor') self.cfg.set('lowThresholdFactor', self.cfg.value('lowThresholdFactor') + 0.1) if self.cfg.value('lowThresholdFactor') > 20.0: self.cfg.set('lowThresholdFactor', 20.0) self.cfg.set('highThresholdFactor', self.cfg.value('lowThresholdFactor') + t_diff) print('lowThresholdFactor =', self.cfg.value('lowThresholdFactor')) self.update_plots() elif event.key == 'escape': self.remove_peak_annotation() self.fig.canvas.draw() elif event.key in 'h': self.help = not self.help for ht in self.helptext: ht.set_visible(self.help) self.fig.canvas.draw() elif event.key in 'l': self.legend = not self.legend self.legendhandle.set_visible(self.legend) self.fig.canvas.draw() elif event.key in 'w': self.plot_waveform() elif event.key in 'p': self.play_segment() elif event.key in 'P': self.play_all() elif event.key in '1' : self.play_tone('c3') elif event.key in '2' : self.play_tone('a3') elif event.key in '3' : self.play_tone('e4') elif event.key in '4' : self.play_tone('a4') elif event.key in '5' : self.play_tone('c5') elif event.key in '6' : self.play_tone('e5') elif event.key in '7' : self.play_tone('g5') elif event.key in '8' : self.play_tone('a5') elif event.key in '9' : self.play_tone('c6') elif event.key in 'S': self.save_segment() def buttonpress( self, event ) : # print('mouse pressed', event.button, event.key, event.step) if event.inaxes == self.axp: if event.key == 'shift' or event.key == 'control': # show next or previous harmonic: if event.key == 'shift': if event.button == 1: ftarget = event.xdata / 2.0 elif event.button == 3: ftarget = event.xdata * 2.0 else: if event.button == 1: ftarget = event.xdata / 1.5 elif event.button == 3: ftarget = event.xdata * 1.5 foffs = event.xdata - self.fmin fwidth = self.fmax - self.fmin self.fmin = ftarget - foffs self.fmax = self.fmin + fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() else: # put label on peak self.remove_peak_annotation() # find closest peak: fwidth = self.fmax - self.fmin peakdist = np.abs(self.allpeaks[:, 0] - event.xdata) inx = np.argmin(peakdist) if peakdist[inx] < 0.005 * fwidth: peak = self.allpeaks[inx, :] # find fish: foundfish = False for finx, fish in enumerate(self.fishlist): if np.min(np.abs(fish[:, 0] - peak[0])) < 0.8 * self.deltaf: self.annotate_fish(fish, finx) foundfish = True break if not foundfish: self.annotate_peak(peak) self.fig.canvas.draw() else: self.fig.canvas.draw() def onpick(self, event): # print('pick') legendpoint = event.artist finx, fish = self.legenddict[legendpoint] self.annotate_fish(fish, finx) def resize(self, event): # print('resized', event.width, event.height) leftpixel = 80.0 rightpixel = 20.0 xaxispixel = 50.0 toppixel = 20.0 timeaxis = 0.42 left = leftpixel / event.width width = 1.0 - left - rightpixel / event.width xaxis = xaxispixel / event.height top = toppixel / event.height height = (1.0 - timeaxis - top) / 2.0 if left < 0.5 and width < 1.0 and xaxis < 0.3 and top < 0.2: self.axt.set_position([left, timeaxis + height, width, height]) self.axs.set_position([left, timeaxis, width, height]) self.axp.set_position([left, xaxis, width, timeaxis - 2.0 * xaxis]) def plot_waveform(self): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) name = self.filename.split('.')[0] if self.channel > 0: ax.set_title('{filename} channel={channel:d}'.format( filename=self.filename, channel=self.channel)) figfile = '{name}-{channel:d}-{time:.4g}s-waveform.png'.format( name=name, channel=self.channel, time=self.toffset) else: ax.set_title(self.filename) figfile = '{name}-{time:.4g}s-waveform.png'.format( name=name, time=self.toffset) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if t1>len(self.data): t1 = len(self.data) time = np.arange(t0, t1) / self.samplerate if self.twindow < 1.0: ax.set_xlabel('Time [ms]') ax.set_xlim(1000.0 * self.toffset, 1000.0 * (self.toffset + self.twindow)) ax.plot(1000.0 * time, self.data[t0:t1]) else: ax.set_xlabel('Time [s]') ax.set_xlim(self.toffset, self.toffset + self.twindow) ax.plot(time, self.data[t0:t1]) ax.set_ylabel('Amplitude [{:s}]'.format(self.unit)) fig.tight_layout() fig.savefig(figfile) fig.clear() plt.close(fig) print('saved waveform figure to', figfile) def play_segment(self): t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) playdata = 1.0 * self.data[t0:t1] fade(playdata, self.samplerate, 0.1) self.audio.play(playdata, self.samplerate, blocking=False) def save_segment(self): t0s = int(np.round(self.toffset)) t1s = int(np.round(self.toffset + self.twindow)) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) savedata = 1.0 * self.data[t0:t1] filename = self.filename.split('.')[0] segmentfilename = '{name}-{time0:.4g}s-{time1:.4g}s.wav'.format( name=filename, time0=t0s, time1 = t1s) write_audio(segmentfilename, savedata, self.data.samplerate) print('saved segment to: ' , segmentfilename) def play_all(self): self.audio.play(self.data[:], self.samplerate, blocking=False) def play_tone( self, frequency ) : self.audio.beep(1.0, frequency)
class SignalPlot : def __init__(self, samplingrate, data, fdata, env, slowenv, envrate, threshs, onsets, offsets, unit, filename, path, cfg) : self.filepath = '' if platform.system() == 'Windows' : self.filepath = path self.filename = filename self.rate = samplingrate self.data = data self.unit = unit self.time = np.arange( 0.0, self.data.shape[0] )/self.rate self.toffset = 0.0 self.twindow = 60.0 if self.twindow > self.time[-1] : self.twindow = np.round( 2**(np.floor(np.log(self.time[-1]) / np.log(2.0)) + 1.0) ) self.lowpassfreq = cfg.value('lowpassfreq') self.highpassfreq = cfg.value('highpassfreq') self.fdata = fdata self.channels = data.shape[1] self.envelopecutofffreq = cfg.value('envelopecutofffreq') self.envelopefilter = cfg.value('envelopefilter') self.envelope = env self.slowenvelope = slowenv self.envrate = envrate self.thresholdfac = cfg.value('thresholdfactor') self.thresholds = threshs self.min_duration = cfg.value('minduration') self.songonsets = onsets self.songoffsets = offsets self.trace_artists = [] self.filtered_trace_artists = [] self.envelope_artists = [] self.slowenvelope_artists = [] self.threshold_artists = [] self.songonset_artists = [] self.songoffset_artists = [] self.highpass_label = None self.lowpass_label = None self.envelope_label = None self.max_pixel = cfg.value('maxpixel') self.show_traces = cfg.value('displayTraces') self.show_filtered_traces = cfg.value('displayFilteredTraces') self.show_envelope = cfg.value('displayEnvelope') self.show_slowenvelope = cfg.value('displaySlowEnvelope') self.show_thresholds = True self.show_songonsets = True self.show_songoffsets = True self.help = cfg.value('displayHelp') self.helptext = [] self.analysis_file = None # audio output: self.audio = PlayAudio() # set key bindings: #plt.rcParams['keymap.fullscreen'] = 'ctrl+f' plt.rcParams['keymap.fullscreen'] = '' plt.rcParams['keymap.pan'] = 'ctrl+m' plt.rcParams['keymap.quit'] = 'ctrl+w, alt+q, q' plt.rcParams['keymap.save'] = 'alt+s' plt.rcParams['keymap.yscale'] = '' plt.rcParams['keymap.xscale'] = '' plt.rcParams['keymap.grid'] = '' plt.rcParams['keymap.all_axes'] = '' # the figure: self.fig = plt.figure( figsize=( 15, 9 ) ) self.fig.canvas.set_window_title( 'SongDetector: ' + self.filename ) self.fig.canvas.mpl_connect( 'key_press_event', self.keypress ) # trace plots: ph = 0.9/self.channels self.axt = [] self.ymin = [] self.ymax = [] for c in range(self.channels): self.ymin.append( -1.0 ) self.ymax.append( +1.0 ) if np.min(self.data[:, c]) < -1.0 or np.max(self.data[:, c]) > +1.0 : self.ymin[c] = -10.0 self.ymax[c] = +10.0 if c == 0 : self.axt.append( self.fig.add_axes( [ 0.08, 0.06+(self.channels-c-1)*ph, 0.89, ph ] ) ) self.highpass_label = self.axt[0].text( 0.05, 0.9, 'highpass=%.1fkHz' % (0.001*self.highpassfreq), transform=self.axt[0].transAxes ) self.lowpass_label = self.axt[0].text( 0.2, 0.9, 'lowpass=%.1fkHz' % (0.001*self.lowpassfreq), transform=self.axt[0].transAxes ) self.envelope_label = self.axt[0].text( 0.35, 0.9, 'envelope=%.0fHz' % self.envelopecutofffreq, transform=self.axt[0].transAxes ) else: self.axt.append( self.fig.add_axes( [ 0.08, 0.08+(self.channels-c-1)*ph, 0.89, ph ], sharex=self.axt[0] ) ) self.axt[-1].set_ylabel( 'Amplitude [{:s}]'.format( self.unit ) ) self.axt[-1].set_xlabel( 'Time [s]' ) """ ht = self.axt.text( 0.98, 0.05, '(ctrl+) page and arrow up, down, home, end: scroll', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.15, '+, -, X, x: zoom in/out', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.25, 'y,Y,v,V: zoom amplitudes', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.35, 'p,P: play audio (display,all)', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.45, 'ctrl-f: full screen', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.65, 's: save figure', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.75, 'q: quit', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) ht = self.axt.text( 0.98, 0.85, 'h: toggle this help', ha='right', transform=self.axt.transAxes ) self.helptext.append( ht ) # plot: for ht in self.helptext : ht.set_visible( self.help ) """ self.update_plots( False ) plt.show() def __del( self ) : if self.analysis_file != None : self.analysis_file.close() if self.audio is not None: self.audio.close() def update_plots( self, draw=True ) : # time window: t0 = int(np.round(self.toffset*self.rate)) t1 = int(np.round((self.toffset+self.twindow)*self.rate)) tstep = 1 if self.max_pixel > 0 : tstep = int((t1-t0)//self.max_pixel) if tstep < 1 : tstep = 1 for c in range(self.channels) : self.axt[c].set_xlim( self.toffset, self.toffset+self.twindow ) self.axt[c].set_ylim( self.ymin[c], self.ymax[c] ) # raw trace: if self.show_traces : append = len(self.trace_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot( self.time[t0:t1:tstep], self.data[t0:t1:tstep, c], 'b', zorder=0 ) self.trace_artists.append( ta ) else : self.trace_artists[c].set_data( self.time[t0:t1:tstep], self.data[t0:t1:tstep, c] ) self.trace_artists[c].set_visible(True) else : for c in range(len(self.trace_artists)) : self.trace_artists[c].set_visible(False) # filtered trace: if self.show_filtered_traces : append = len(self.filtered_trace_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot( self.time[t0:t1:tstep], self.fdata[t0:t1:tstep, c], 'g', zorder=1 ) self.filtered_trace_artists.append( ta ) else : self.filtered_trace_artists[c].set_data( self.time[t0:t1:tstep], self.fdata[t0:t1:tstep, c] ) self.filtered_trace_artists[c].set_visible(True) else : for c in range(len(self.filtered_trace_artists)) : self.filtered_trace_artists[c].set_visible(False) # fast envelope: if self.show_envelope : et0 = int(np.round(self.toffset*self.envrate)) et1 = int(np.round((self.toffset+self.twindow)*self.envrate)) etstep = int(np.round(tstep*self.rate/self.envrate)) etime = self.time[t0:t1+etstep:etstep][:len(self.envelope[et0:et1:tstep, c])] append = len(self.envelope_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot( etime, self.envelope[et0:et1:tstep, c], 'r', lw=2, zorder=2 ) self.envelope_artists.append( ta ) else : self.envelope_artists[c].set_data( etime, self.envelope[et0:et1:tstep, c] ) self.envelope_artists[c].set_visible(True) else : for c in range(len(self.envelope_artists)) : self.envelope_artists[c].set_visible(False) # slow envelope: if self.show_slowenvelope : et0 = int(np.round(self.toffset*self.envrate)) et1 = int(np.round((self.toffset+self.twindow)*self.envrate)) etstep = int(np.round(tstep*self.rate/self.envrate)) etime = self.time[t0:t1+etstep:etstep][:len(self.slowenvelope[et0:et1:tstep, c])] append = len(self.slowenvelope_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot( etime, self.slowenvelope[et0:et1:tstep, c], 'c', lw=2, zorder=3 ) self.slowenvelope_artists.append( ta ) else : self.slowenvelope_artists[c].set_data( etime, self.slowenvelope[et0:et1:tstep, c] ) self.slowenvelope_artists[c].set_visible(True) else : for c in range(len(self.slowenvelope_artists)) : self.slowenvelope_artists[c].set_visible(False) # thresholds: if self.show_thresholds : append = len(self.threshold_artists) == 0 for c in range(self.channels) : tm = t1 if tm >= len(self.time): tm = len(self.time)-1 if append : ta, = self.axt[c].plot( [self.time[t0], self.time[tm]], [self.thresholds[c], self.thresholds[c]], 'k', zorder=4 ) self.threshold_artists.append( ta ) else : self.threshold_artists[c].set_data( [self.time[t0], self.time[tm]], [self.thresholds[c], self.thresholds[c]] ) self.threshold_artists[c].set_visible(True) else : for c in range(len(self.threshold_artists)) : self.threshold_artists[c].set_visible(False) # song onsets: if self.show_songonsets : append = len(self.songonset_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot(self.songonsets[c]/self.envrate, self.thresholds[c]*np.ones(len(self.songonsets[c])), '.b', ms=10, zorder=5) self.songonset_artists.append(ta) else : self.songonset_artists[c].set_data(self.songonsets[c]/self.envrate, self.thresholds[c]*np.ones(len(self.songonsets[c]))) self.songonset_artists[c].set_visible(True) else : for c in range(len(self.songonset_artists)) : self.songonset_artists[c].set_visible(False) # song offsets: if self.show_songoffsets : append = len(self.songoffset_artists) == 0 for c in range(self.channels) : if append : ta, = self.axt[c].plot(self.songoffsets[c]/self.envrate, self.thresholds[c]*np.ones(len(self.songoffsets[c])), '.b', ms=10, zorder=6) self.songoffset_artists.append(ta) else : self.songoffset_artists[c].set_data(self.songoffsets[c]/self.envrate, self.thresholds[c]*np.ones(len(self.songoffsets[c]))) self.songoffset_artists[c].set_visible(True) else : for c in range(len(self.songoffset_artists)) : self.songoffset_artists[c].set_visible(False) if draw : self.fig.canvas.draw() def keypress( self, event ) : if event.key in '+=X' : if self.twindow*self.rate > 20 : self.twindow *= 0.5 self.update_plots() elif event.key in '-x' : if self.twindow < len( self.data )/self.rate : self.twindow *= 2.0 self.update_plots() elif event.key == 'pagedown' : if self.toffset + 0.5*self.twindow < len( self.data )/self.rate : self.toffset += 0.5*self.twindow self.update_plots() elif event.key == 'pageup' : if self.toffset > 0 : self.toffset -= 0.5*self.twindow if self.toffset < 0.0 : self.toffset = 0.0 self.update_plots() elif event.key == 'ctrl+pagedown' : if self.toffset + 5.0*self.twindow < len( self.data )/self.rate : self.toffset += 5.0*self.twindow self.update_plots() elif event.key == 'ctrl+pageup' : if self.toffset > 0 : self.toffset -= 5.0*self.twindow if self.toffset < 0.0 : self.toffset = 0.0 self.update_plots() elif event.key == 'down' : if self.toffset + self.twindow < len( self.data )/self.rate : self.toffset += 0.05*self.twindow self.update_plots() elif event.key == 'up' : if self.toffset > 0.0 : self.toffset -= 0.05*self.twindow if self.toffset < 0.0 : self.toffset = 0.0 self.update_plots() elif event.key == 'home': if self.toffset > 0.0 : self.toffset = 0.0 self.update_plots() elif event.key == 'end': toffs = np.floor( len( self.data )/self.rate / self.twindow ) * self.twindow if self.toffset < toffs : self.toffset = toffs self.update_plots() elif event.key == 'y': for c in range(self.channels): h = self.ymax[c] - self.ymin[c] v = 0.5*(self.ymax[c] + self.ymin[c]) self.ymin[c] = v-h self.ymax[c] = v+h self.axt[c].set_ylim( self.ymin[c], self.ymax[c] ) self.fig.canvas.draw() elif event.key == 'Y': for c in range(self.channels): h = 0.25*(self.ymax[c] - self.ymin[c]) v = 0.5*(self.ymax[c] + self.ymin[c]) self.ymin[c] = v-h self.ymax[c] = v+h self.axt[c].set_ylim( self.ymin[c], self.ymax[c] ) self.fig.canvas.draw() elif event.key == 'v': for c in range(self.channels): fdmin = np.min( self.fdata[:, c] ) fdmax = np.max( self.fdata[:, c] ) #h = 0.5*(fdmax - fdmin) #v = 0.5*(fdmax + fdmin) #self.ymin[c] = v-h #self.ymax[c] = v+h m = max(-fdmin, fdmax) self.ymin[c] = -m self.ymax[c] = m self.axt[c].set_ylim( self.ymin[c], self.ymax[c] ) self.fig.canvas.draw() elif event.key == 'V': for c in range(self.channels): self.ymin[c] = -1.0 self.ymax[c] = +1.0 self.axt[c].set_ylim( self.ymin[c], self.ymax[c] ) self.fig.canvas.draw() elif event.key == 'ctrl+t' : self.show_traces = not self.show_traces if len(self.trace_artists) > 0 : for c in range(self.channels) : self.trace_artists[c].set_visible( self.show_traces ) self.fig.canvas.draw() else: self.update_plots() elif event.key == 'ctrl+f' : self.show_filtered_traces = not self.show_filtered_traces if len(self.filtered_trace_artists) > 0 : for c in range(self.channels) : self.filtered_trace_artists[c].set_visible( self.show_filtered_traces ) self.fig.canvas.draw() else: self.update_plots() elif event.key == 'ctrl+e' : self.show_envelope = not self.show_envelope if len(self.envelope_artists) > 0 : for c in range(self.channels) : self.envelope_artists[c].set_visible( self.show_envelope ) self.fig.canvas.draw() else: self.update_plots() elif event.key == 'h' : self.highpassfreq /= 1.5 self.highpass_label.set_text('highpass=%.1fkHz' % (0.001*self.highpassfreq)) self.fdata = bandpass_filter(self.data, self.rate, self.highpassfreq, self.lowpassfreq) self.update_plots() elif event.key == 'H' : self.highpassfreq * 1.5 self.highpass_label.set_text('highpass=%.1fkHz' % (0.001*self.highpassfreq)) self.fdata = bandpass_filter(self.data, self.rate, self.highpassfreq, self.lowpassfreq) self.update_plots() elif event.key == 'l' : self.lowpassfreq /= 1.5 self.lowpass_label.set_text('lowpass=%.1fkHz' % (0.001*self.lowpassfreq)) self.fdata = bandpass_filter(self.data, self.rate, self.highpassfreq, self.lowpassfreq) self.update_plots() elif event.key == 'L' : self.lowpassfreq * 1.5 self.lowpass_label.set_text('lowpass=%.1fkHz' % (0.001*self.lowpassfreq)) self.fdata = bandpass_filter(self.data, self.rate, self.highpassfreq, self.lowpassfreq) self.update_plots() elif event.key == 'e' : self.envelopecutofffreq /= 1.5 self.envelope_label.set_text('envelope=%.0fHz' % self.envelopecutofffreq) self.envelope, self.envrate = envelope(self.fdata, self.rate, self.envelopecutofffreq) self.songonsets, self.songoffsets = detect_songs(self.slowenvelope, self.envrate, self.thresholds, min_duration=self.min_duration) self.songonsets, self.songoffsets = refine_detection(self.songonsets, self.songoffsets, self.envelope, self.envrate, self.thresholds, min_duration=self.min_duration, envelope_use_freq=self.envelopeusefreq) self.update_plots() elif event.key == 'E' : self.envelopecutofffreq *= 1.5 self.envelope_label.set_text('envelope=%.0fHz' % self.envelopecutofffreq) self.envelope, self.envrate = envelope(self.fdata, self.rate, self.envelopecutofffreq) self.songonsets, self.songoffsets = detect_songs(self.slowenvelope, self.envrate, self.thresholds) self.songonsets, self.songoffsets = refine_detection(self.songonsets, self.songoffsets, self.envelope, self.envrate, self.thresholds, min_duration=self.min_duration, envelope_use_freq=self.envelopeusefreq) self.update_plots() # elif event.key in 'h' : # self.help = not self.help # for ht in self.helptext : # ht.set_visible( self.help ) # self.fig.canvas.draw() elif event.key in 'w' : self.plot_waveform() elif event.key in 'p' : self.play_segment(self.fdata) elif event.key in 'P' : self.play_segment(self.data) #self.play_all() def plot_waveform( self ) : fig = plt.figure() ax = fig.add_subplot( 1, 1, 1 ) name = os.path.splitext( self.filename )[0] ax.set_title( self.filename ) figfile = '{name}-{time:.4g}s-waveform.png'.format( name=name, time=self.toffset ) t0 = int(np.round(self.toffset*self.rate)) t1 = int(np.round((self.toffset+self.twindow)*self.rate)) if self.twindow < 1.0 : ax.set_xlabel( 'Time [ms]' ) ax.set_xlim( 1000.0*self.toffset, 1000.0*(self.toffset+self.twindow) ) ax.plot( 1000.0*self.time[t0:t1], self.data[t0:t1], 'b' ) if self.show_envelope : ax.plot( 1000.0*self.time[t0:t1], self.envelope[t0:t1], 'r' ) else : ax.set_xlabel( 'Time [s]' ) ax.set_xlim( self.toffset, self.toffset+self.twindow ) ax.plot( self.time[t0:t1], self.data[t0:t1], 'b' ) if self.show_envelope : ax.plot( self.time[t0:t1], self.envelope[t0:t1], 'r' ) ax.set_ylabel( 'Amplitude [{:s}]'.format( self.unit ) ) fig.tight_layout() # on linux the following is not what you want! You want to save this into the current working directory!!! fig.savefig( os.path.join( self.filepath, figfile ) ) fig.clear() plt.close( fig ) print('saved waveform figure to %s' % figfile) def play_segment(self, data) : t0 = int(np.round(self.toffset*self.rate)) t1 = int(np.round((self.toffset+self.twindow)*self.rate)) playdata = 1.0*np.mean(data[t0:t1,:], axis=1) playdata -= np.mean(playdata) fade(playdata, self.rate, 0.1) self.audio.play(playdata, self.rate, blocking=False) def play_all(self) : playdata = np.mean(self.fdata, axis=1) playdata -= np.mean(playdata) self.audio.play(np.mean(self.data, axis=1), self.rate, blocking=False)
class SignalPlot: def __init__(self, data, samplingrate, unit, filename, channel, cfg): self.filename = filename self.channel = channel self.samplerate = samplingrate self.data = data self.unit = unit self.cfg = cfg self.verbose = self.cfg['verboseLevel'][0] self.time = np.arange(0.0, len(self.data)) / self.samplerate self.toffset = 0.0 self.twindow = 8.0 if self.twindow > self.time[-1]: self.twindow = np.round(2 ** (np.floor(np.log(self.time[-1]) / np.log(2.0)) + 1.0)) self.ymin = -1.0 self.ymax = +1.0 self.trace_artist = None self.spectrogram_artist = None self.fmin = 0.0 self.fmax = 0.0 self.decibel = True self.fresolution = self.cfg['initialFrequencyResolution'][0] self.deltaf = 1.0 self.mains_freq = self.cfg['mainsFreq'][0] self.power_label = None self.all_peaks_artis = None self.good_peaks_artist = None self.power_artist = None self.power_frequency_label = None self.peak_artists = [] self.legend = True self.legendhandle = None self.help = self.cfg['displayHelp'][0] self.helptext = [] self.allpeaks = [] self.fishlist = [] self.mains = [] self.peak_specmarker = [] self.peak_annotation = [] self.min_clip = self.cfg['minClipAmplitude'][0] self.max_clip = self.cfg['maxClipAmplitude'][0] self.generate_color_range() # audio output: self.audio = PlayAudio() # set key bindings: plt.rcParams['keymap.fullscreen'] = 'ctrl+f' plt.rcParams['keymap.pan'] = 'ctrl+m' plt.rcParams['keymap.quit'] = 'ctrl+w, alt+q, q' plt.rcParams['keymap.yscale'] = '' plt.rcParams['keymap.xscale'] = '' plt.rcParams['keymap.grid'] = '' plt.rcParams['keymap.all_axes'] = '' # the figure: self.fig = plt.figure(figsize=(15, 9)) self.fig.canvas.set_window_title(self.filename + ' channel {0:d}'.format(self.channel)) self.fig.canvas.mpl_connect('key_press_event', self.keypress) self.fig.canvas.mpl_connect('button_press_event', self.buttonpress) self.fig.canvas.mpl_connect('pick_event', self.onpick) self.fig.canvas.mpl_connect('resize_event', self.resize) # trace plot: self.axt = self.fig.add_axes([0.1, 0.7, 0.87, 0.25]) self.axt.set_ylabel('Amplitude [{:s}]'.format(self.unit)) ht = self.axt.text(0.98, 0.05, '(ctrl+) page and arrow up, down, home, end: scroll', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.15, '+, -, X, x: zoom in/out', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.25, 'y,Y,v,V: zoom amplitudes', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.35, 'p,P: play audio (display,all)', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.45, 'ctrl-f: full screen', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.55, 'w: plot waveform into png file', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.65, 's: save figure', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.75, 'q: quit', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) ht = self.axt.text(0.98, 0.85, 'h: toggle this help', ha='right', transform=self.axt.transAxes) self.helptext.append(ht) self.axt.set_xticklabels([]) # spectrogram: self.axs = self.fig.add_axes([0.1, 0.45, 0.87, 0.25]) self.axs.set_xlabel('Time [seconds]') self.axs.set_ylabel('Frequency [Hz]') # power spectrum: self.axp = self.fig.add_axes([0.1, 0.1, 0.87, 0.25]) ht = self.axp.text(0.98, 0.9, 'r, R: frequency resolution', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.8, 'f, F: zoom', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.7, '(ctrl+) left, right: move', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.6, 'l: toggle legend', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.5, 'd: toggle decibel', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.4, 'm: toggle mains filter', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.3, 'left mouse: show peak properties', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) ht = self.axp.text(0.98, 0.2, 'shift/ctrl + left/right mouse: goto previous/next harmonic', ha='right', transform=self.axp.transAxes) self.helptext.append(ht) # plot: for ht in self.helptext: ht.set_visible(self.help) self.update_plots(False) plt.show() def __del(self): self.audio.close() def generate_color_range(self): # color and marker range: self.colorrange = [] self.markerrange = [] mr2 = [] # first color range: cc0 = plt.cm.gist_rainbow(np.linspace(0.0, 1.0, 8.0)) # shuffle it: for k in range((len(cc0) + 1) // 2): self.colorrange.extend(cc0[k::(len(cc0) + 1) // 2]) self.markerrange.extend(len(cc0) * 'o') mr2.extend(len(cc0) * 'v') # second darker color range: cc1 = plt.cm.gist_rainbow(np.linspace(0.33 / 7.0, 1.0, 7.0)) cc1 = mc.hsv_to_rgb(mc.rgb_to_hsv(np.array([cc1])) * np.array([1.0, 0.9, 0.7, 0.0]))[0] cc1[:, 3] = 1.0 # shuffle it: for k in range((len(cc1) + 1) // 2): self.colorrange.extend(cc1[k::(len(cc1) + 1) // 2]) self.markerrange.extend(len(cc1) * '^') mr2.extend(len(cc1) * '*') # third lighter color range: cc2 = plt.cm.gist_rainbow(np.linspace(0.67 / 6.0, 1.0, 6.0)) cc2 = mc.hsv_to_rgb(mc.rgb_to_hsv(np.array([cc2])) * np.array([1.0, 0.5, 1.0, 0.0]))[0] cc2[:, 3] = 1.0 # shuffle it: for k in range((len(cc2) + 1) // 2): self.colorrange.extend(cc2[k::(len(cc2) + 1) // 2]) self.markerrange.extend(len(cc2) * 'D') mr2.extend(len(cc2) * 'x') self.markerrange.extend(mr2) def remove_peak_annotation(self): for fm in self.peak_specmarker: fm.remove() self.peak_specmarker = [] for fa in self.peak_annotation: fa.remove() self.peak_annotation = [] def annotate_peak(self, peak, harmonics=-1, inx=-1): # marker: if inx >= 0: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color=self.colorrange[inx % len(self.colorrange)], marker=self.markerrange[inx], ms=10.0, mec=None, mew=0.0, zorder=2) else: m, = self.axs.plot([self.toffset + 0.01 * self.twindow], [peak[0]], linestyle='None', color='k', marker='o', ms=10.0, mec=None, mew=0.0, zorder=2) self.peak_specmarker.append(m) # annotation: fwidth = self.fmax - self.fmin pl = [] if self.cfg['labelFrequency'][0]: pl.append(r'$f=${:.1f} Hz'.format(peak[0])) if self.cfg['labelHarmonic'][0] and harmonics >= 0: pl.append(r'$h=${:d}'.format(harmonics)) if self.cfg['labelPower'][0]: pl.append(r'$p=${:g}'.format(peak[1])) if self.cfg['labelWidth'][0]: pl.append(r'$\Delta f=${:.2f} Hz'.format(peak[3])) if self.cfg['labelDoubleUse'][0]: pl.append(r'dc={:.0f}'.format(peak[4])) self.peak_annotation.append(self.axp.annotate('\n'.join(pl), xy=(peak[0], peak[1]), xytext=(peak[0] + 0.03 * fwidth, peak[1]), bbox=dict(boxstyle='round', facecolor='white'), arrowprops=dict(arrowstyle='-'))) def annotate_fish(self, fish, inx=-1): self.remove_peak_annotation() for harmonic, freq in enumerate(fish[:, 0]): peak = self.allpeaks[np.abs(self.allpeaks[:, 0] - freq) < 0.8 * self.deltaf, :] if len(peak) > 0: self.annotate_peak(peak[0, :], harmonic, inx) self.fig.canvas.draw() def update_plots(self, draw=True): self.remove_peak_annotation() # trace: self.axt.set_xlim(self.toffset, self.toffset + self.twindow) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if self.trace_artist == None: self.trace_artist, = self.axt.plot(self.time[t0:t1], self.data[t0:t1]) else: self.trace_artist.set_data(self.time[t0:t1], self.data[t0:t1]) self.axt.set_ylim(self.ymin, self.ymax) # compute power spectrum: nfft = int(np.round(2 ** (np.floor(np.log(self.samplerate / self.fresolution) / np.log(2.0)) + 1.0))) if nfft < 16: nfft = 16 nfft, noverlap = nfft_noverlap(self.fresolution, self.samplerate, 0.5, 16) t00 = t0 t11 = t1 w = t11 - t00 minw = nfft * (self.cfg['minPSDAverages'][0] + 1) // 2 if t11 - t00 < minw: w = minw t11 = t00 + w if t11 >= len(self.data): t11 = len(self.data) t00 = t11 - w if t00 < 0: t00 = 0 t11 = w power, freqs = ml.psd(self.data[t00:t11], NFFT=nfft, noverlap=noverlap, Fs=self.samplerate, detrend=ml.detrend_mean) self.deltaf = freqs[1] - freqs[0] # detect fish: h_kwargs = psd_peak_detection_args(self.cfg) h_kwargs.update(harmonic_groups_args(self.cfg)) self.fishlist, fzero_harmonics, self.mains, self.allpeaks, peaks, lowth, highth, center = harmonic_groups(freqs, power, verbose=self.verbose, **h_kwargs) highth = center + highth - 0.5 * lowth lowth = center + 0.5 * lowth # spectrogram: t2 = t1 + nfft specpower, freqs, bins = ml.specgram(self.data[t0:t2], NFFT=nfft, Fs=self.samplerate, noverlap=nfft // 2, detrend=ml.detrend_mean) z = 10. * np.log10(specpower) z = np.flipud(z) extent = self.toffset, self.toffset + np.amax(bins), freqs[0], freqs[-1] self.axs.set_xlim(self.toffset, self.toffset + self.twindow) if self.spectrogram_artist == None: self.fmax = np.round((freqs[-1] / 4.0) / 100.0) * 100.0 min = highth min = np.percentile(z, 70.0) max = np.percentile(z, 99.9) + 30.0 # cm = plt.get_cmap( 'hot_r' ) cm = plt.get_cmap('jet') self.spectrogram_artist = self.axs.imshow(z, aspect='auto', extent=extent, vmin=min, vmax=max, cmap=cm, zorder=1) else: self.spectrogram_artist.set_data(z) self.spectrogram_artist.set_extent(extent) self.axs.set_ylim(self.fmin, self.fmax) # power spectrum: self.axp.set_xlim(self.fmin, self.fmax) if self.deltaf >= 1000.0: dfs = '%.3gkHz' % 0.001 * self.deltaf else: dfs = '%.3gHz' % self.deltaf tw = float(w) / self.samplerate if tw < 1.0: tws = '%.3gms' % (1000.0 * tw) else: tws = '%.3gs' % (tw) a = 2 * w // nfft - 1 # number of ffts m = '' if self.cfg['mainsFreq'][0] > 0.0: m = ', mains=%.0fHz' % self.cfg['mainsFreq'][0] if self.power_frequency_label == None: self.power_frequency_label = self.axp.set_xlabel( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(nfft, dfs, tws, a, m)) else: self.power_frequency_label.set_text( r'Frequency [Hz] (nfft={:d}, $\Delta f$={:s}: T={:s}/{:d}{:s})'.format(nfft, dfs, tws, a, m)) self.axp.set_xlim(self.fmin, self.fmax) if self.power_label == None: self.power_label = self.axp.set_ylabel('Power') if self.decibel: if len(self.allpeaks) > 0: self.allpeaks[:, 1] = 10.0 * np.log10(self.allpeaks[:, 1]) power = 10.0 * np.log10(power) pmin = np.min(power[freqs < self.fmax]) pmin = np.floor(pmin / 10.0) * 10.0 pmax = np.max(power[freqs < self.fmax]) pmax = np.ceil(pmax / 10.0) * 10.0 doty = pmax - 5.0 self.power_label.set_text('Power [dB]') self.axp.set_ylim(pmin, pmax) else: pmax = np.max(power[freqs < self.fmax]) doty = pmax pmax *= 1.1 self.power_label.set_text('Power') self.axp.set_ylim(0.0, pmax) if self.all_peaks_artis == None: self.all_peaks_artis, = self.axp.plot(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty, 'o', color='#ffffff') self.good_peaks_artist, = self.axp.plot(peaks, np.zeros(len(peaks)) + doty, 'o', color='#888888') else: self.all_peaks_artis.set_data(self.allpeaks[:, 0], np.zeros(len(self.allpeaks[:, 0])) + doty) self.good_peaks_artist.set_data(peaks, np.zeros(len(peaks)) + doty) labels = [] fsizes = [np.sqrt(np.sum(self.fishlist[k][:, 1])) for k in range(len(self.fishlist))] fmaxsize = np.max(fsizes) if len(fsizes) > 0 else 1.0 self.axp.set_color_cycle(self.colorrange) for k in range(len(self.peak_artists)): self.peak_artists[k].remove() self.peak_artists = [] for k in range(len(self.fishlist)): if k >= len(self.markerrange): break fpeaks = self.fishlist[k][:, 0] fpeakinx = [int(np.round(fp / self.deltaf)) for fp in fpeaks if fp < freqs[-1]] fsize = 7.0 + 10.0 * (fsizes[k] / fmaxsize) ** 0.5 fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', marker=self.markerrange[k], ms=fsize, mec=None, mew=0.0, zorder=1) self.peak_artists.append(fishpoints) if self.deltaf < 0.1: labels.append('%4.2f Hz' % fpeaks[0]) elif self.deltaf < 1.0: labels.append('%4.1f Hz' % fpeaks[0]) else: labels.append('%4.0f Hz' % fpeaks[0]) if len(self.mains) > 0: fpeaks = self.mains[:, 0] fpeakinx = [np.round(fp / self.deltaf) for fp in fpeaks if fp < freqs[-1]] fishpoints, = self.axp.plot(fpeaks[:len(fpeakinx)], power[fpeakinx], linestyle='None', marker='.', color='k', ms=10, mec=None, mew=0.0, zorder=2) self.peak_artists.append(fishpoints) labels.append('%3.0f Hz mains' % self.cfg['mainsFreq'][0]) ncol = len(labels) // 8 + 1 self.legendhandle = self.axs.legend(self.peak_artists[:len(labels)], labels, loc='upper right', ncol=ncol) self.legenddict = dict() for legpoints, (finx, fish) in zip(self.legendhandle.get_lines(), enumerate(self.fishlist)): legpoints.set_picker(8) self.legenddict[legpoints] = [finx, fish] self.legendhandle.set_visible(self.legend) if self.power_artist == None: self.power_artist, = self.axp.plot(freqs, power, 'b', zorder=3) else: self.power_artist.set_data(freqs, power) if draw: self.fig.canvas.draw() def keypress(self, event): # print('pressed', event.key) if event.key in '+=X': if self.twindow * self.samplerate > 20: self.twindow *= 0.5 self.update_plots() elif event.key in '-x': if self.twindow < len(self.data) / self.samplerate: self.twindow *= 2.0 self.update_plots() elif event.key == 'pagedown': if self.toffset + 0.5 * self.twindow < len(self.data) / self.samplerate: self.toffset += 0.5 * self.twindow self.update_plots() elif event.key == 'pageup': if self.toffset > 0: self.toffset -= 0.5 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'a': if self.min_clip == 0.0 or self.max_clip == 0.0: self.min_clip, self.max_clip = clip_amplitudes( self.data, **clip_args(self.cfg, self.samplerate)) try: idx0, idx1, clipped = best_window_indices( self.data, self.samplerate, min_clip=self.min_clip, max_clip=self.max_clip, **best_window_args(self.cfg)) if idx1 > 0: self.toffset = idx0 / self.samplerate self.twindow = (idx1 - idx0) / self.samplerate self.update_plots() except UserWarning as e: if self.verbose > 0: print(str(e)) elif event.key == 'ctrl+pagedown': if self.toffset + 5.0 * self.twindow < len(self.data) / self.samplerate: self.toffset += 5.0 * self.twindow self.update_plots() elif event.key == 'ctrl+pageup': if self.toffset > 0: self.toffset -= 5.0 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'down': if self.toffset + self.twindow < len(self.data) / self.samplerate: self.toffset += 0.05 * self.twindow self.update_plots() elif event.key == 'up': if self.toffset > 0.0: self.toffset -= 0.05 * self.twindow if self.toffset < 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'home': if self.toffset > 0.0: self.toffset = 0.0 self.update_plots() elif event.key == 'end': toffs = np.floor(len(self.data) / self.samplerate / self.twindow) * self.twindow if self.toffset < toffs: self.toffset = toffs self.update_plots() elif event.key == 'y': h = self.ymax - self.ymin c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'Y': h = 0.25 * (self.ymax - self.ymin) c = 0.5 * (self.ymax + self.ymin) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'v': t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) min = np.min(self.data[t0:t1]) max = np.max(self.data[t0:t1]) h = 0.5 * (max - min) c = 0.5 * (max + min) self.ymin = c - h self.ymax = c + h self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'V': self.ymin = -1.0 self.ymax = +1.0 self.axt.set_ylim(self.ymin, self.ymax) self.fig.canvas.draw() elif event.key == 'left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin -= 0.5 * fwidth self.fmax -= 0.5 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin self.fmin += 0.5 * fwidth self.fmax += 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+left': if self.fmin > 0.0: fwidth = self.fmax - self.fmin self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key == 'ctrl+right': if self.fmax < 0.5 * self.samplerate: fwidth = self.fmax - self.fmin fm = 0.5 * self.samplerate self.fmax = np.ceil(fm / fwidth) * fwidth self.fmin = self.fmax - fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'f': if self.fmax < 0.5 * self.samplerate or self.fmin > 0.0: fwidth = self.fmax - self.fmin if self.fmax < 0.5 * self.samplerate: self.fmax = self.fmin + 2.0 * fwidth elif self.fmin > 0.0: self.fmin = self.fmax - 2.0 * fwidth if self.fmin < 0.0: self.fmin = 0.0 self.fmax = 2.0 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'F': if self.fmax - self.fmin > 1.0: fwidth = self.fmax - self.fmin self.fmax = self.fmin + 0.5 * fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() elif event.key in 'r': if self.fresolution < 1000.0: self.fresolution *= 2.0 self.update_plots() elif event.key in 'R': if 1.0 / self.fresolution < self.time[-1]: self.fresolution *= 0.5 self.update_plots() elif event.key in 'd': self.decibel = not self.decibel self.update_plots() elif event.key in 'm': if self.cfg['mainsFreq'][0] == 0.0: self.cfg['mainsFreq'][0] = self.mains_freq else: self.cfg['mainsFreq'][0] = 0.0 self.update_plots() elif event.key in 't': self.cfg['peakFactor'][0] -= 0.1 if self.cfg['peakFactor'][0] < -5.0: self.cfg['peakFactor'][0] = -5.0 print('peakFactor =', self.cfg['peakFactor'][0]) self.update_plots() elif event.key in 'T': self.cfg['peakFactor'][0] += 0.1 if self.cfg['peakFactor'][0] > 5.0: self.cfg['peakFactor'][0] = 5.0 print('peakFactor =', self.cfg['peakFactor'][0]) self.update_plots() elif event.key == 'escape': self.remove_peak_annotation() self.fig.canvas.draw() elif event.key in 'h': self.help = not self.help for ht in self.helptext: ht.set_visible(self.help) self.fig.canvas.draw() elif event.key in 'l': self.legend = not self.legend self.legendhandle.set_visible(self.legend) self.fig.canvas.draw() elif event.key in 'w': self.plot_waveform() elif event.key in 'p': self.play_segment() elif event.key in 'P': self.play_all() elif event.key in '1' : self.play_tone('c3') elif event.key in '2' : self.play_tone('a3') elif event.key in '3' : self.play_tone('e4') elif event.key in '4' : self.play_tone('a4') elif event.key in '5' : self.play_tone('c5') elif event.key in '6' : self.play_tone('e5') elif event.key in '7' : self.play_tone('g5') elif event.key in '8' : self.play_tone('a5') elif event.key in '9' : self.play_tone('c6') def buttonpress( self, event ) : # print('mouse pressed', event.button, event.key, event.step) if event.inaxes == self.axp: if event.key == 'shift' or event.key == 'control': # show next or previous harmonic: if event.key == 'shift': if event.button == 1: ftarget = event.xdata / 2.0 elif event.button == 3: ftarget = event.xdata * 2.0 else: if event.button == 1: ftarget = event.xdata / 1.5 elif event.button == 3: ftarget = event.xdata * 1.5 foffs = event.xdata - self.fmin fwidth = self.fmax - self.fmin self.fmin = ftarget - foffs self.fmax = self.fmin + fwidth self.axs.set_ylim(self.fmin, self.fmax) self.axp.set_xlim(self.fmin, self.fmax) self.fig.canvas.draw() else: # put label on peak self.remove_peak_annotation() # find closest peak: fwidth = self.fmax - self.fmin peakdist = np.abs(self.allpeaks[:, 0] - event.xdata) inx = np.argmin(peakdist) if peakdist[inx] < 0.005 * fwidth: peak = self.allpeaks[inx, :] # find fish: foundfish = False for finx, fish in enumerate(self.fishlist): if np.min(np.abs(fish[:, 0] - peak[0])) < 0.8 * self.deltaf: self.annotate_fish(fish, finx) foundfish = True break if not foundfish: self.annotate_peak(peak) self.fig.canvas.draw() else: self.fig.canvas.draw() def onpick(self, event): # print('pick') legendpoint = event.artist finx, fish = self.legenddict[legendpoint] self.annotate_fish(fish, finx) def resize(self, event): # print('resized', event.width, event.height) leftpixel = 80.0 rightpixel = 20.0 xaxispixel = 50.0 toppixel = 20.0 timeaxis = 0.42 left = leftpixel / event.width width = 1.0 - left - rightpixel / event.width xaxis = xaxispixel / event.height top = toppixel / event.height height = (1.0 - timeaxis - top) / 2.0 if left < 0.5 and width < 1.0 and xaxis < 0.3 and top < 0.2: self.axt.set_position([left, timeaxis + height, width, height]) self.axs.set_position([left, timeaxis, width, height]) self.axp.set_position([left, xaxis, width, timeaxis - 2.0 * xaxis]) def plot_waveform(self): fig = plt.figure() ax = fig.add_subplot(1, 1, 1) name = self.filename.split('.')[0] if self.channel > 0: ax.set_title('{filename} channel={channel:d}'.format( filename=self.filename, channel=self.channel)) figfile = '{name}-{channel:d}-{time:.4g}s-waveform.png'.format( name=name, channel=self.channel, time=self.toffset) else: ax.set_title(self.filename) figfile = '{name}-{time:.4g}s-waveform.png'.format( name=name, time=self.toffset) t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) if self.twindow < 1.0: ax.set_xlabel('Time [ms]') ax.set_xlim(1000.0 * self.toffset, 1000.0 * (self.toffset + self.twindow)) ax.plot(1000.0 * self.time[t0:t1], self.data[t0:t1]) else: ax.set_xlabel('Time [s]') ax.set_xlim(self.toffset, self.toffset + self.twindow) ax.plot(self.time[t0:t1], self.data[t0:t1]) ax.set_ylabel('Amplitude [{:s}]'.format(self.unit)) fig.tight_layout() fig.savefig(figfile) fig.clear() plt.close(fig) print('saved waveform figure to', figfile) def play_segment(self): t0 = int(np.round(self.toffset * self.samplerate)) t1 = int(np.round((self.toffset + self.twindow) * self.samplerate)) playdata = 1.0 * self.data[t0:t1] fade(playdata, self.samplerate, 0.1) self.audio.play(playdata, self.samplerate, blocking=False) def play_all(self): self.audio.play(self.data[:], self.samplerate, blocking=False) def play_tone( self, frequency ) : self.audio.beep(1.0, frequency)