class PlotImage(Qwt.QwtPlotItem): def __init__(self, logger): Qwt.QwtPlotItem.__init__(self) self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) def addData(self, freq, xyzs, logfreqscale): self.canvasscaledspectrogram.setlogfreqscale(logfreqscale) self.canvasscaledspectrogram.addData(freq, xyzs) def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.canvasscaledspectrogram.setcanvas_height(rect.height()) self.canvasscaledspectrogram.setcanvas_width(rect.width()) pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset() rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap painter.drawPixmap(rect.left(), rect.top(), pixmap, offset, 0, 0, 0) else: # draw one single line of the pixmap at a moving position painter.drawPixmap(rect.left() + offset, rect.top(), pixmap, offset - 1, 0, 1, 0) #print painter #print xMap.p1(), xMap.p2(), xMap.s1(), xMap.s2() #print yMap.p1(), yMap.p2(), yMap.s1(), yMap.s2() #print rect def settimerange(self, timerange_seconds): self.canvasscaledspectrogram.setT(timerange_seconds) def setfreqrange(self, minfreq, maxfreq): self.canvasscaledspectrogram.setfreqrange(minfreq, maxfreq) def erase(self): self.canvasscaledspectrogram.erase()
class PlotImage(Qwt.QwtPlotItem): def __init__(self, logger): Qwt.QwtPlotItem.__init__(self) self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) def addData(self, freq, xyzs, logfreqscale): self.canvasscaledspectrogram.setlogfreqscale(logfreqscale) self.canvasscaledspectrogram.addData(freq, xyzs) def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.canvasscaledspectrogram.setcanvas_height(rect.height()) self.canvasscaledspectrogram.setcanvas_width(rect.width()) pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset() rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap painter.drawPixmap(rect.left(), rect.top(), pixmap, offset, 0, 0, 0) else: # draw one single line of the pixmap at a moving position painter.drawPixmap(rect.left() + offset, rect.top(), pixmap, offset-1, 0, 1, 0) #print painter #print xMap.p1(), xMap.p2(), xMap.s1(), xMap.s2() #print yMap.p1(), yMap.p2(), yMap.s1(), yMap.s2() #print rect def settimerange(self, timerange_seconds): self.canvasscaledspectrogram.setT(timerange_seconds) def setfreqrange(self, minfreq, maxfreq): self.canvasscaledspectrogram.setfreqrange(minfreq, maxfreq) def erase(self): self.canvasscaledspectrogram.erase()
class PlotImage: def __init__(self): self.canvasscaledspectrogram = CanvasScaledSpectrogram() self.T = 0. self.dT = 1. self.jitter_s = 0. self.last_data_time = 0. self.isPlaying = True self.sfft_rate_frac = Fraction(1, 1) self.frequency_resampler = Frequency_Resampler() self.resampler = Online_Linear_2D_resampler() self.timer = QtCore.QElapsedTimer() self.timer.start() self.last_time = 0. def addData(self, freq, xyzs, logfreqscale, last_data_time): self.frequency_resampler.setlogfreqscale(logfreqscale) # Note: both the frequency and the time resampler work # only on 1D arrays, so we loop on the columns of data. # However, we reassemble the 2D output before drawing # on the widget's pixmap, because the drawing operation # seems to have a costly warmup phase, so it is better # to invoke it the fewer number of times possible. n = self.resampler.processable(xyzs.shape[1]) resampled_data = np.zeros((self.frequency_resampler.nsamples, n)) i = 0 for j in range(xyzs.shape[1]): freq_resampled_data = self.frequency_resampler.process(freq, xyzs[:, j]) data = self.resampler.process(freq_resampled_data) resampled_data[:, i:i + data.shape[1]] = data i += data.shape[1] self.canvasscaledspectrogram.addData(resampled_data) if i > 0: self.last_data_time = last_data_time def pause(self): self.isPlaying = False def restart(self): self.isPlaying = True self.last_time = AudioBackend().get_stream_time() self.timer.restart() def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.frequency_resampler.setnsamples(rect.height()) self.resampler.set_height(rect.height()) self.canvasscaledspectrogram.setcanvas_height(rect.height()) # print self.jitter_s, self.T, rect.width(), rect.width()*(1 + self.jitter_s/self.T) jitter_pix = rect.width() * self.jitter_s / self.T self.canvasscaledspectrogram.setcanvas_width(rect.width() + jitter_pix) screen_rate_frac = Fraction(rect.width(), int(self.T * 1000)) self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac) # time advance # This function is meant to be called at paintevent time, for better time sync. pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset(delay=jitter_pix / 2) if self.isPlaying: delta_t = self.timer.nsecsElapsed() * 1e-9 self.timer.restart() pixel_advance = delta_t / (self.T + self.jitter_s) * rect.width() self.canvasscaledspectrogram.addPixelAdvance(pixel_advance) time = AudioBackend().get_stream_time() time_delay = time - self.last_data_time pixel_delay = rect.width() * time_delay / self.T self.last_time = time offset += pixel_delay rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap hints = painter.renderHints() # enable bilinear pixmap transformation painter.setRenderHints(hints | QtGui.QPainter.SmoothPixmapTransform) # Note: nstead of a generic bilinear transformation, a specialized one could be more efficient, # since no transformation is needed in y, and the sampling rate is already known to be ok in x. sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(offset, 0, sw, sh) # QRectF since the offset and width may be non-integer painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) else: sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(0, 0, sw, sh) painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) def settimerange(self, timerange_seconds, dT): self.T = timerange_seconds self.dT = dT def setfreqrange(self, minfreq, maxfreq): self.frequency_resampler.setfreqrange(minfreq, maxfreq) def set_sfft_rate(self, rate_frac): self.sfft_rate_frac = rate_frac def setlogfreqscale(self, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) def erase(self): self.canvasscaledspectrogram.erase() def isOpaque(self): return True def set_jitter(self, jitter_s): self.jitter_s = jitter_s
class PlotImage(Qwt.QwtPlotItem): def __init__(self, logger, audiobackend): Qwt.QwtPlotItem.__init__(self) self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) self.T = 0. self.dT = 1. self.audiobackend = audiobackend #self.previous_time = self.audiobackend.get_stream_time() self.offset = 0 #self.audiobackend.get_stream_time()/self.dT self.sfft_rate_frac = Fraction(1, 1) self.frequency_resampler = Frequency_Resampler() self.resampler = Online_Linear_2D_resampler() def addData(self, freq, xyzs, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) # Note: both the frequency and the time resampler work # only on 1D arrays, so we loop on the columns of data. # However, we reassemble the 2D output before drawing # on the widget's pixmap, because the drawing operation # seems to have a costly warmup phase, so it is better # to invoke it the fewer number of times possible. n = self.resampler.processable(xyzs.shape[1]) resampled_data = np.zeros((self.frequency_resampler.nsamples, n)) i = 0 for j in range(xyzs.shape[1]): freq_resampled_data = self.frequency_resampler.process(freq, xyzs[:, j]) data = self.resampler.process(freq_resampled_data) resampled_data[:,i:i+data.shape[1]] = data i += data.shape[1] self.canvasscaledspectrogram.addData(resampled_data) def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.frequency_resampler.setnsamples(rect.height()) self.resampler.set_height(rect.height()) self.canvasscaledspectrogram.setcanvas_height(rect.height()) self.canvasscaledspectrogram.setcanvas_width(rect.width()) screen_rate_frac = Fraction(rect.width(), int(self.T*1000)) self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac) pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset() rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap hints = painter.renderHints() # enable bilinear pixmap transformation painter.setRenderHints(hints|QtGui.QPainter.SmoothPixmapTransform) #FIXME instead of a generic bilinear transformation, I need a specialized one # since no transformation is needed in y, and the sampling rate is already known to be ok in x sw = rect.width() sh = rect.height() # this function should be called by repaint, for better time sync # FIXME the following is wrong when the display is paused ! # and even when not paused, it does not improve the smoothness # and has a problem of offset #current_time = self.audiobackend.get_stream_time() #delay = sw*(current_time - self.previous_time)/self.T #self.previous_time = current_time #self.offset += delay #self.offset = (self.offset % sw) #offset = self.offset source_rect = QtCore.QRectF(offset, 0, sw, sh) # QRectF since the offset and width may be non-integer painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) else: sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(0, 0, sw, sh) painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) def settimerange(self, timerange_seconds, dT): self.T = timerange_seconds self.dT = dT def setfreqrange(self, minfreq, maxfreq): self.frequency_resampler.setfreqrange(minfreq, maxfreq) def set_sfft_rate(self, rate_frac): self.sfft_rate_frac = rate_frac def setlogfreqscale(self, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) def erase(self): self.canvasscaledspectrogram.erase()
class PlotImage(Qwt.QwtPlotItem): def __init__(self, logger, audiobackend): Qwt.QwtPlotItem.__init__(self) self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) self.T = 0. self.dT = 1. self.audiobackend = audiobackend #self.previous_time = self.audiobackend.get_stream_time() self.offset = 0 #self.audiobackend.get_stream_time()/self.dT self.sfft_rate_frac = Fraction(1, 1) self.frequency_resampler = Frequency_Resampler() self.resampler = Online_Linear_2D_resampler() def addData(self, freq, xyzs, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) # Note: both the frequency and the time resampler work # only on 1D arrays, so we loop on the columns of data. # However, we reassemble the 2D output before drawing # on the widget's pixmap, because the drawing operation # seems to have a costly warmup phase, so it is better # to invoke it the fewer number of times possible. n = self.resampler.processable(xyzs.shape[1]) resampled_data = np.zeros((self.frequency_resampler.nsamples, n)) i = 0 for j in range(xyzs.shape[1]): freq_resampled_data = self.frequency_resampler.process( freq, xyzs[:, j]) data = self.resampler.process(freq_resampled_data) resampled_data[:, i:i + data.shape[1]] = data i += data.shape[1] self.canvasscaledspectrogram.addData(resampled_data) def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.frequency_resampler.setnsamples(rect.height()) self.resampler.set_height(rect.height()) self.canvasscaledspectrogram.setcanvas_height(rect.height()) self.canvasscaledspectrogram.setcanvas_width(rect.width()) screen_rate_frac = Fraction(rect.width(), int(self.T * 1000)) self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac) pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset() rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap hints = painter.renderHints() # enable bilinear pixmap transformation painter.setRenderHints(hints | QtGui.QPainter.SmoothPixmapTransform) #FIXME instead of a generic bilinear transformation, I need a specialized one # since no transformation is needed in y, and the sampling rate is already known to be ok in x sw = rect.width() sh = rect.height() # this function should be called by repaint, for better time sync # FIXME the following is wrong when the display is paused ! # and even when not paused, it does not improve the smoothness # and has a problem of offset #current_time = self.audiobackend.get_stream_time() #delay = sw*(current_time - self.previous_time)/self.T #self.previous_time = current_time #self.offset += delay #self.offset = (self.offset % sw) #offset = self.offset source_rect = QtCore.QRectF(offset, 0, sw, sh) # QRectF since the offset and width may be non-integer painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) else: sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(0, 0, sw, sh) painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) def settimerange(self, timerange_seconds, dT): self.T = timerange_seconds self.dT = dT def setfreqrange(self, minfreq, maxfreq): self.frequency_resampler.setfreqrange(minfreq, maxfreq) def set_sfft_rate(self, rate_frac): self.sfft_rate_frac = rate_frac def setlogfreqscale(self, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) def erase(self): self.canvasscaledspectrogram.erase()
class PlotImage(Qwt.QwtPlotItem): def __init__(self, logger, audiobackend): Qwt.QwtPlotItem.__init__(self) self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) self.T = 0. self.dT = 1. self.audiobackend = audiobackend #self.previous_time = self.audiobackend.get_stream_time() self.offset = 0 #self.audiobackend.get_stream_time()/self.dT self.jitter_s = 0. self.isPlaying = True self.sfft_rate_frac = Fraction(1, 1) self.frequency_resampler = Frequency_Resampler() self.resampler = Online_Linear_2D_resampler() self.timer = QtCore.QElapsedTimer() self.timer.start() def addData(self, freq, xyzs, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) # Note: both the frequency and the time resampler work # only on 1D arrays, so we loop on the columns of data. # However, we reassemble the 2D output before drawing # on the widget's pixmap, because the drawing operation # seems to have a costly warmup phase, so it is better # to invoke it the fewer number of times possible. n = self.resampler.processable(xyzs.shape[1]) resampled_data = np.zeros((self.frequency_resampler.nsamples, n)) i = 0 for j in range(xyzs.shape[1]): freq_resampled_data = self.frequency_resampler.process(freq, xyzs[:, j]) data = self.resampler.process(freq_resampled_data) resampled_data[:,i:i+data.shape[1]] = data i += data.shape[1] self.canvasscaledspectrogram.addData(resampled_data) def pause(self): self.isPlaying = False def restart(self): self.isPlaying = True self.timer.restart() def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.frequency_resampler.setnsamples(rect.height()) self.resampler.set_height(rect.height()) self.canvasscaledspectrogram.setcanvas_height(rect.height()) #print self.jitter_s, self.T, rect.width(), rect.width()*(1 + self.jitter_s/self.T) jitter_pix = rect.width()*self.jitter_s/self.T self.canvasscaledspectrogram.setcanvas_width(rect.width() + jitter_pix) screen_rate_frac = Fraction(rect.width(), int(self.T*1000)) self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac) # time advance # FIXME ideally this function should be called at paintevent time, for better time sync # but I'm not sure it is... maybe qwt does some sort of double-buffering # and repaints its items outside of paintevents # solution: look at PaintEvent # FIXME there is a small bands of columns with jitter (on both sides of the spectrogram) # solution: grow the rolling-canvas by a couple of columns, # and slightly delay the spectrogram by the same number of columns if self.isPlaying: delta_t = self.timer.nsecsElapsed()*1e-9 self.timer.restart() pixel_advance = delta_t/(self.T + self.jitter_s)*rect.width() self.canvasscaledspectrogram.addPixelAdvance(pixel_advance) pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset(delay=jitter_pix/2) rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap hints = painter.renderHints() # enable bilinear pixmap transformation painter.setRenderHints(hints|QtGui.QPainter.SmoothPixmapTransform) #FIXME instead of a generic bilinear transformation, I need a specialized one # since no transformation is needed in y, and the sampling rate is already known to be ok in x sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(offset, 0, sw, sh) # QRectF since the offset and width may be non-integer painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) else: sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(0, 0, sw, sh) painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) def settimerange(self, timerange_seconds, dT): self.T = timerange_seconds self.dT = dT def setfreqrange(self, minfreq, maxfreq): self.frequency_resampler.setfreqrange(minfreq, maxfreq) def set_sfft_rate(self, rate_frac): self.sfft_rate_frac = rate_frac def setlogfreqscale(self, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) def erase(self): self.canvasscaledspectrogram.erase() def set_jitter(self, jitter_s): self.jitter_s = jitter_s
class PlotImage: def __init__(self, logger, audiobackend): self.canvasscaledspectrogram = CanvasScaledSpectrogram(logger) self.T = 0. self.dT = 1. self.audiobackend = audiobackend self.jitter_s = 0. self.last_data_time = 0. self.isPlaying = True self.sfft_rate_frac = Fraction(1, 1) self.frequency_resampler = Frequency_Resampler() self.resampler = Online_Linear_2D_resampler() self.timer = QtCore.QElapsedTimer() self.timer.start() self.last_time = 0. def addData(self, freq, xyzs, logfreqscale, last_data_time): self.frequency_resampler.setlogfreqscale(logfreqscale) # Note: both the frequency and the time resampler work # only on 1D arrays, so we loop on the columns of data. # However, we reassemble the 2D output before drawing # on the widget's pixmap, because the drawing operation # seems to have a costly warmup phase, so it is better # to invoke it the fewer number of times possible. n = self.resampler.processable(xyzs.shape[1]) resampled_data = np.zeros((self.frequency_resampler.nsamples, n)) i = 0 for j in range(xyzs.shape[1]): freq_resampled_data = self.frequency_resampler.process(freq, xyzs[:, j]) data = self.resampler.process(freq_resampled_data) resampled_data[:, i:i + data.shape[1]] = data i += data.shape[1] self.canvasscaledspectrogram.addData(resampled_data) if i > 0: self.last_data_time = last_data_time def pause(self): self.isPlaying = False def restart(self): self.isPlaying = True self.timer.restart() def draw(self, painter, xMap, yMap, rect): # update the spectrogram according to possibly new canvas dimensions self.frequency_resampler.setnsamples(rect.height()) self.resampler.set_height(rect.height()) self.canvasscaledspectrogram.setcanvas_height(rect.height()) # print self.jitter_s, self.T, rect.width(), rect.width()*(1 + self.jitter_s/self.T) jitter_pix = rect.width() * self.jitter_s / self.T self.canvasscaledspectrogram.setcanvas_width(rect.width() + jitter_pix) screen_rate_frac = Fraction(rect.width(), int(self.T * 1000)) self.resampler.set_ratio(self.sfft_rate_frac, screen_rate_frac) # time advance # This function is meant to be called at paintevent time, for better time sync. pixmap = self.canvasscaledspectrogram.getpixmap() offset = self.canvasscaledspectrogram.getpixmapoffset(delay=jitter_pix / 2) if self.isPlaying: delta_t = self.timer.nsecsElapsed() * 1e-9 self.timer.restart() pixel_advance = delta_t / (self.T + self.jitter_s) * rect.width() self.canvasscaledspectrogram.addPixelAdvance(pixel_advance) time = self.audiobackend.get_stream_time() time_delay = time - self.last_data_time pixel_delay = rect.width() * time_delay / self.T draw_delay = time - self.last_time self.last_time = time offset += pixel_delay rolling = True if rolling: # draw the whole canvas with a selected portion of the pixmap hints = painter.renderHints() # enable bilinear pixmap transformation painter.setRenderHints(hints | QtGui.QPainter.SmoothPixmapTransform) # Note: nstead of a generic bilinear transformation, a specialized one could be more efficient, # since no transformation is needed in y, and the sampling rate is already known to be ok in x. sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(offset, 0, sw, sh) # QRectF since the offset and width may be non-integer painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) else: sw = rect.width() sh = rect.height() source_rect = QtCore.QRectF(0, 0, sw, sh) painter.drawPixmap(QtCore.QRectF(rect), pixmap, source_rect) def settimerange(self, timerange_seconds, dT): self.T = timerange_seconds self.dT = dT def setfreqrange(self, minfreq, maxfreq): self.frequency_resampler.setfreqrange(minfreq, maxfreq) def set_sfft_rate(self, rate_frac): self.sfft_rate_frac = rate_frac def setlogfreqscale(self, logfreqscale): self.frequency_resampler.setlogfreqscale(logfreqscale) def erase(self): self.canvasscaledspectrogram.erase() def isOpaque(self): return True def set_jitter(self, jitter_s): self.jitter_s = jitter_s