Ejemplo n.º 1
0
 def initAUD(self):
     #
     info = QAudioDeviceInfo.defaultInputDevice()
     if (~info.isFormatSupported(self.audio.format)):
         # print("警告,设置的默认音频格式并不支持,将尝试采用最相近的支持格式")
         # 不知道这里面有什么神改动?
         self.audio.format  = info.nearestFormat(self.audio.format)
     #
     update_interval = 160
     self.audioRecorder = QAudioInput(self.audio.format)
     self.audioRecorder.setNotifyInterval(update_interval) #按毫秒ms 类似于QTimer的作用
     self.audioRecorder.notify.connect(self.processAudioData)
     self.audioRecorder_TD = QThread()
     self.audioRecorder.moveToThread(self.audioRecorder_TD)
     self.audioRecorder_TD.started.connect(self.startRecord)
     self.audioRecorder.stateChanged.connect(self.recordStopped)
     # 总结来说线程只是一个容器,里面执行的循环要是没法结束,强制退出也不好操作
     # 所以还是好好写好任务流然后发送信号比较合理
     self.audioPlayer = QAudioOutput(self.audio.format)
     self.audioPlayer.setNotifyInterval(update_interval)
     self.audioPlayer.notify.connect(self.processAudioData)
     self.audioPlayer_TD = QThread()
     self.audioPlayer.moveToThread(self.audioPlayer_TD)
     self.audioPlayer_TD.started.connect(self.startPlay)
     self.audioPlayer.stateChanged.connect(self.playStopped)
Ejemplo n.º 2
0
    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)
Ejemplo n.º 3
0
 def initAudio(self):
     self.audioformat = QAudioFormat()
     self.audioformat.setSampleRate(self.samplerate)
     self.audioformat.setChannelCount(2)
     self.audioformat.setSampleSize(32)
     self.audioformat.setCodec('audio/pcm')
     self.audioformat.setByteOrder(QAudioFormat.LittleEndian)
     self.audioformat.setSampleType(QAudioFormat.Float)
     self.audiooutput = QAudioOutput(self.audioformat)
     self.audiooutput.setVolume(1.0)
Ejemplo n.º 4
0
    def __init__(self, parent=None):
        print("Tone Widget inst")
        QWidget.__init__(self, parent)

        self.activeGen = ActiveGen(sampleRate=SAMPLE_RATE,
                                   samplePerRead=Meep.SAMPLES_PER_READ)
        self.createUI(self)

        # Meep playback format initialization
        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        # check compatibility of format with device
        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if info.isFormatSupported(format) is False:
            print(
                "Raw audio format not supported by backend, cannot play audio."
            )
            return None

        # Audio Output init
        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)
        # initialize and start the audio playback
        self.generator = Meep(format, self.activeGen, self)
        self.generator.start()
        self.output.start(self.generator)

        # Create the port reader object
        self.midiListener = MidiPortReader()
        # Create a thread which will read it
        self.listenerThread = QThread()

        # move QObjet to a new thread
        self.midiListener.moveToThread(self.listenerThread)

        # Connect the signals to slot functions
        self.midiListener.newNoteFrequency.connect(self.activeGen.setFreq)
        self.midiListener.newNotePress.connect(self.activeGen.setNote)

        # Tell Qt the function to call
        # when it starts the thread
        self.listenerThread.started.connect(self.midiListener.listener)
        # start the thread
        self.listenerThread.start()
Ejemplo n.º 5
0
    def initAudio(self):
        self.audioformat = QAudioFormat()
        self.audioformat.setSampleRate(self.samplerate)
        self.audioformat.setChannelCount(2)
        self.audioformat.setSampleSize(32)
        self.audioformat.setCodec('audio/pcm')
        self.audioformat.setByteOrder(QAudioFormat.LittleEndian)
        self.audioformat.setSampleType(QAudioFormat.Float)
        # self.audiodeviceinfo = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())

        self.audiooutput = QAudioOutput(self.audioformat)
        self.audiooutput.setVolume(self.initVolume)
Ejemplo n.º 6
0
 def ot_dev_change(self, new_index):
     if self.otput_device:
         if self.input_device:
             self.input_device.stop()
         self.otput_device.stop()
         self.otput_device = None
     audio_info = self.otput_info_list[new_index]
     preferred_format = audio_info.preferredFormat()
     self.otput_device = QAudioOutput(audio_info, preferred_format)
     self.otput_device.setVolume(self.volume.value() / 100)
     #self.otput_device.setBufferSize( 384 )
     if self.input_device:
         self.input_device.start(self.otput_device.start())
Ejemplo n.º 7
0
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(1)
        format.setSampleRate(22050)
        format.setSampleSize(16)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)
        self.output = QAudioOutput(format, self)

        self.frequency = 440
        self.volume = 0
        self.buffer = QBuffer()
        self.data = QByteArray()

        self.deviceLineEdit = QLineEdit()
        self.deviceLineEdit.setReadOnly(True)
        self.deviceLineEdit.setText(
            QAudioDeviceInfo.defaultOutputDevice().deviceName())

        self.pitchSlider = QSlider(Qt.Horizontal)
        self.pitchSlider.setMaximum(100)
        self.volumeSlider = QSlider(Qt.Horizontal)
        self.volumeSlider.setMaximum(32767)
        self.volumeSlider.setPageStep(1024)

        self.playButton = QPushButton(self.tr("&Play"))

        self.pitchSlider.valueChanged.connect(self.changeFrequency)
        self.volumeSlider.valueChanged.connect(self.changeVolume)
        self.playButton.clicked.connect(self.play)

        formLayout = QFormLayout()
        formLayout.addRow(self.tr("Device:"), self.deviceLineEdit)
        formLayout.addRow(self.tr("P&itch:"), self.pitchSlider)
        formLayout.addRow(self.tr("&Volume:"), self.volumeSlider)

        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(self.playButton)
        buttonLayout.addStretch()

        horizontalLayout = QHBoxLayout(self)
        horizontalLayout.addLayout(formLayout)
        horizontalLayout.addLayout(buttonLayout)

        self.play()
        self.createData()
    def __init__(self):
        super(JackStreamListen, self).__init__()
        self.ip = '127.0.0.1'
        self.port = '23'
        self.state = 'disconnected'
        self.channel_select = -1
        self.channel_count = -1
        # self.tsfmt = '%y%m%d-%H%M%S:'
        # self.textFgColor = 'rgb(0,0,0)'
        # self.textBgColor = 'rgb(255,255,255)'
        # self.fontstr = 'Helvetica'

        logging.debug('DBG: state = ' + self.state)

        # self.insFile = "";self.insDir=""
        # self.insSleepAmount = 0;

        self.qsock = QtNetwork.QTcpSocket(self)
        self.qsock.readyRead.connect(self.onReadyRead)

        # create None/empty variables to be created/updated later
        self.prevpkt = bytearray()  # note a QByteArray b/c reusing msgify_pkt
        self.audiodata = bytearray()
        self.clips = []
        self.rms = []
        self.audiofmt = QAudioFormat()
        self.audioout = QAudioOutput()
        self.iodevice = self.createIoDevice()

        #FIXME self.tcpSocket.error.connect(self.displayError)

        # self.font = QtGui.QFont()
        self.loadSettings()
        self.initUI()
Ejemplo n.º 9
0
    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)
Ejemplo n.º 10
0
    def create_AUDIO(self, format):
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(
            QAudioFormat.LittleEndian
        )
        format.setSampleType(
            QAudioFormat.SignedInt
        )

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(
            output_buffer_size
        )
Ejemplo n.º 11
0
 def __init__(self,parent=None):
     #UI
     QWidget.__init__(self,parent)
     self.create_UI(parent)
     #audio formatting
     format=QAudioFormat()
     format.setChannelCount(AUDIO_CHANS)
     format.setSampleRate(SAMPLE_RATE)
     format.setSampleSize(SAMPLE_SIZE)
     format.setCodec("audio/pcm")
     format.setByteOrder(
         QAudioFormat.LittleEndian
     )
     format.setSampleType(
         QAudioFormat.SignedInt
     )
     self.output=QAudioOutput(format,self)
     output_buffer_size=\
         int(2*SAMPLE_RATE \
             *CTRL_INTERVAL/1000)
     self.output.setBufferSize(
         output_buffer_size
     )
     self.generator=Generator(format,self)
     #THREADS
     self.midiListener=MidiPortReader()
     self.listenerThread=QThread()
     self.midiListener.moveToThread(
             self.listenerThread
     )
     self.listenerThread.started.connect(
             self.midiListener.listener
     )
     self.listenerThread.start()
     self.midiListener.noteOff.connect(self.generator.noteOff)
     self.midiListener.noteVelocity.connect(self.generator.noteVelocity)
     self.midiListener.noteOn.connect(self.generator.noteOn)
     self.volumeSlider.valueChanged.connect(self.generator.volSlide)
     self.qfacSlider.valueChanged.connect(self.generator.qFactor)
     self.generator.start()
     self.output.start(self.generator)
class ToneGenerator(QWidget):
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)

        self.generator = Meep(format, self)
        self.generator.start()
        self.output.start(self.generator)

        # Create the port reader object
        self.midiListener = MidiPortReader()

        # Create a thread which will read it
        self.listenerThread = QThread()

        # Take the object and move it
        # to the new thread (it isn't running yet)
        self.midiListener.moveToThread(self.listenerThread)

        self.midiListener.newNoteFrequency.connect(self.generator.changeFreq)

        # Tell Qt the function to call
        # when it starts the thread
        self.listenerThread.started.connect(self.midiListener.listener)

        # Fingers in ears, eyes tight shut...
        self.listenerThread.start()
Ejemplo n.º 13
0
    def set_data(self, mono_sig, sr):
        # if not self.format:
        self.format = QAudioFormat()
        self.format.setChannelCount(1)
        self.format.setSampleRate(sr)
        #numpy is in bites, qt in bits
        self.format.setSampleSize(mono_sig.dtype.itemsize * 8)
        self.format.setCodec("audio/pcm")
        self.format.setByteOrder(QAudioFormat.LittleEndian)
        self.format.setSampleType(QAudioFormat.Float)
        self.output = QAudioOutput(self.format, self)
        self.output.stateChanged.connect(self.audio_state_changed)
        #change the content without stopping playback
        p = self.buffer.pos()
        if self.buffer.isOpen():
            self.buffer.close()

        self.data = mono_sig.tobytes()
        self.buffer.setData(self.data)
        self.buffer.open(QIODevice.ReadWrite)
        self.buffer.seek(p)
Ejemplo n.º 14
0
    def ot_dev_change(self, new_index):
        print('index', new_index)
        # Disconnect and stop the devices if they are connected.
        self.disconnect_devices()

        self.otput_device = None  # device object goes out of scope

        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.otput_info_list[new_index]

        # Create a new QAudioOutput based on that.
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(0)  # reconnect will set correct volume

        # hook up possible debug status display
        self.otput_device.stateChanged.connect(self.ot_dev_state_change)

        # reconnect the devices if possible. Which also sets the volume.

        self.reconnect_devices()
Ejemplo n.º 15
0
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)

        self.generator = Meep(format, self)
        self.generator.start()
        self.output.start(self.generator)
Ejemplo n.º 16
0
 def ot_dev_change(self, new_index):
     if self.otput_device:
         if self.input_device:
             self.input_device.stop()
         self.otput_device.stop()
         self.otput_device = None
     audio_info = self.otput_info_list[new_index]
     preferred_format = audio_info.preferredFormat()
     self.otput_device = QAudioOutput(audio_info, preferred_format)
     self.otput_device.setVolume(self.volume.value() / 100)
     # self.otput_device.setBufferSize( 384 )
     if self.input_device:
         self.input_device.start(self.otput_device.start())
Ejemplo n.º 17
0
def get_audio_output(params):
    """
    Create and return a QAudioOutput from wav params.
    """
    enc = 'audio/pcm'
    fmt = QAudioFormat()
    fmt.setChannelCount(params.nchannels)
    fmt.setSampleRate(params.framerate)
    fmt.setSampleSize(params.sampwidth * 8)
    fmt.setCodec(enc)
    fmt.setByteOrder(QAudioFormat.LittleEndian)
    fmt.setSampleType(QAudioFormat.SignedInt)
    return QAudioOutput(fmt)
Ejemplo n.º 18
0
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
                int(2*CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)

        self.generator = Generator(format, self)
        self.midiListener = MidiPortReader()
        self.listenerThread = QThread()
        self.midiListener.moveToThread(self.listenerThread)
        self.listenerThread.started.connect(self.midiListener.listener)
        self.midiListener.addVoice.connect(self.generator.addVoice)
        self.midiListener.removeVoice.connect(self.generator.removeVoice)

        self.createUI()

        self.pslider.valueChanged.connect(self.generator.changeP)
        self.fslider.valueChanged.connect(self.generator.qCalc)
        self.semiDown.clicked.connect(self.smDown)
        self.semiUp.clicked.connect(self.smUp)
        self.octaveDown.clicked.connect(self.ovDown)
        self.octaveUp.clicked.connect(self.ovUp)

        self.listenerThread.start()
        self.generator.start()
        self.output.start(self.generator)
Ejemplo n.º 19
0
class SideToneWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Get the status bar
        self.status_bar = parent.statusBar()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        # self.input_info_list, list of QAudioInfo for inputs
        # self.cb_inputs, combox of input names in same order
        # self.otput_info_list, list of QAudioInfo for outputs
        # self.cb_otputs, combox of output names in same order
        # self.volume, volume slider
        # self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots.
        # Changes in the comboxes go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Start with the mute switch on. This triggers the above two signals.
        self.mute.setChecked(True)

    # Slot for any change in volume (or mute). If we have an output device
    # then convert level to a real and pass to the output device. If the new
    # level is 0, tell the device to stop; if nonzero, tell it to start.
    # Note we don't do anything about the input device level, it is always 1.0
    def volume_change(self, new_level):
        if self.otput_device:  # we have an output device
            self.otput_device.setVolume(self.volume.value() / 100)
            if new_level == 0:  # looks like a mute
                # tell the output device to stop just in case it
                # doesn't know about volume control.
                self.otput_device.stop()
            else:  # non-zero level, if the output is stopped, start it
                if self.otput_device.state() == QAudio.StoppedState:
                    self.otput_device.start()

    # Slot for mute switch. Note that any change to the volume slider
    # generates a signal to the volume_change slot.
    def mute_change(self, onoff):
        if onoff:
            # Mute has been clicked ON. Remember the current volume.
            # Turn the volume to zero.
            self.volume_level = self.volume.value()
            self.volume.setValue(0)
        else:
            # Mute has been clicked OFF. If we do not yet have input and
            # output devices, get them. Then reset the old volume level.
            if self.otput_device is None:
                # We are starting up and have no devices. Fake a call to
                # the checkbox-change entries thus creating devices.
                self.in_dev_change(self.cb_inputs.currentIndex())
                self.ot_dev_change(self.cb_inputs.currentIndex())
            self.volume.setValue(self.volume_level)

    # Slots for changes in the selection of the input- and output-device
    # combo boxes. On startup we have neither an input nor an output device.
    # We do not know which combox the user will fiddle with first. So either
    # has to assume that the other device may not yet exist.
    #
    # On a change of input choice: if we have an input device, get rid of it.
    # Create a new input device. Set its level to 1.0. If we
    # have an output, connect the two.

    def in_dev_change(self, new_index):
        if self.input_device:
            if self.otput_device:
                self.otput_device.stop()
            self.input_device.stop()
            self.input_device = None  # goodby object
        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]
        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)
        self.input_device.setVolume(1.0)
        self.input_device.setBufferSize(384)
        # If we have an output device, redirect it to this input. This is
        # done by asking the input device for its QIODevice, and passing that
        # to the output device's start() method.
        if self.otput_device:
            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

    # On a change in the selection of output choice: If we have an output
    # device, get rid of it. Create a new output device. If we have an input
    # device, connect the two. Set the output level from the volume slider.

    def ot_dev_change(self, new_index):
        if self.otput_device:
            if self.input_device:
                self.input_device.stop()
            self.otput_device.stop()
            self.otput_device = None
        audio_info = self.otput_info_list[new_index]
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(self.volume.value() / 100)
        #self.otput_device.setBufferSize( 384 )
        if self.input_device:
            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

    def _uic(self):
        '''
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    hooking put the signals to useful slots is the job
    of __init__. Here just make the layout.
        '''
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(':/icon.png').scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Create a combo box and populate it with names of inputs
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems(
            [audio_info.deviceName() for audio_info in self.input_info_list])
        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        self.status_bar.showMessage(
            '{} inputs {} otputs'.format(len(self.input_info_list),
                                         len(self.otput_info_list)), 2000)
        # Create a combo box and populate it with names of outputs
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems(
            [audio_info.deviceName() for audio_info in self.otput_info_list])
        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)
        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # Create a checkbox "Mute"
        self.mute = QCheckBox('Mute')
        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)
        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
Ejemplo n.º 20
0
class AudioAnalysis(QDialog):
    def __init__(self, mainwin, dir):
        super().__init__()
        self.main_Win = mainwin
        self.snd_record_ctr = 0 
        self.snd_play_ctr = 0
        self.snd_reset_ctr = 0
        self.is_snd_recording = None
        #
        self.audio = Audio(save_dir=dir)
        self.initAUD()
        self.initIF()
        self.initWaveList()

    def initIF(self):
        self.layout = QGridLayout(self)
        self.setLayout(self.layout)
        icon = QIcon()
        icon.addPixmap(QPixmap("./style/logo3.png"))
        self.setWindowIcon(icon)
        self.setWindowTitle("语音录制与分析")
        self.list_LW = QListWidget(self)
        self.list_LW.setMaximumWidth(160)
        class WaveSpectrum(QWidget):
            def __init__(self, parent=None, maindlg=None):
                super(WaveSpectrum, self).__init__(parent)
                self.main_Dlg = maindlg
                #self.pg_PL = pg.PlotWidget(enableMenu=False)
                self.audio = self.main_Dlg.audio
                self.layout = QGridLayout(self)
                self.setLayout(self.layout)
                self.pg_PL = pg.PlotWidget() #pg.plot(title="Three plot curves")
                self.pg_PL.hideButtons()
                self.layout.addWidget(self.pg_PL)               
                self.item = self.pg_PL.getPlotItem()
                self.item.hideButtons()
                self.item.setMouseEnabled(y=False)
                self.item.setYRange(0,20000)
                range = self.audio.rate/2
                self.item.setXRange(-range,range, padding=0)
                self.axis = self.item.getAxis("bottom")
                self.axis.setLabel("频率(赫兹)")
                
            def updatePlot(self):
               try:
                   data = np.fromstring(self.audio.block, 'int16')
                   #print(data)
                   T = 1.0/self.audio.rate
                   N = data.shape[0]
                   Fx = (1./N) * np.fft.fft(data) # 万一N==0
               except Exception as e:
                   print("??",e)
               else:
                   f = np.fft.fftfreq(N, T)
                   Fx = np.fft.fftshift(Fx)
                   f = np.fft.fftshift(f)
                   self.item.plot(x=f.tolist(), y=(np.absolute(Fx)).tolist(), clear=True)    
    
        self.wave_spectrum_PG = WaveSpectrum(maindlg=self)
        self.result_LB = QLabel(self)
        self.result_LB.setText("欢迎使用")
        self.running_SL = QSlider(Qt.Horizontal)
        self.running_SL.setMinimum(0)
        self.running_SL.setMaximum(100)
        self.running_SL.setStyleSheet("QSlider::handle:horizontal {background-color: #d91900;}")
        self.save_BT = QPushButton(self)
        self.save_BT.setText("保存与分析")
        self.save_BT.setMinimumSize(128,32)
        self.record_BT = QPushButton(self)
        self.record_BT.setText("开始录音")
        self.record_BT.setMinimumSize(144,32)
        self.play_BT = QPushButton(self)
        self.play_BT.setText("开始播放")
        self.play_BT.setMinimumSize(144,32)
        self.reset_BT = QPushButton(self)
        self.reset_BT.setText("停止")
        self.reset_BT.setMinimumSize(128,32)
               
        self.layout.addWidget(self.list_LW, 0,0,1,1)
        self.layout.addWidget(self.wave_spectrum_PG, 0,1, 1,3)
        self.layout.addWidget(self.result_LB, 1,0, 1,4)
        self.layout.addWidget(self.running_SL, 2,0, 1,4)
        self.layout.addWidget(self.save_BT, 3,0, 2,1)
        self.layout.addWidget(self.record_BT, 3,1, 2,1)
        self.layout.addWidget(self.play_BT, 3,2, 2,1)
        self.layout.addWidget(self.reset_BT, 3,3, 2,1)
        

        self.list_LW.itemClicked.connect(self.sel2Play)
        self.record_BT.clicked.connect(self.click2Record)
        self.running_SL.sliderReleased.connect(self.dragPosPlay) 
        # 注意这里得是用户主动的动作哟 另外如果需要点击位置定位的话还必须要重写mousePressEvent,这里就不弄了
        self.play_BT.clicked.connect(self.click2Play)
        self.reset_BT.clicked.connect(self.click2Reset)
        self.save_BT.clicked.connect(self.click2Save)
    
    def initWaveList(self):
        self.wave_dict = {"小黄":["catH1.wav",0],"小黄骚":["catH2.wav",0], "小黄又骚":["catH3.wav",0], "小黄又又骚":["catH4.wav",0]
                         ,"煤球":["catM1.wav",0],"煤球骚":["catM2.wav",0], "煤球又骚":["catM3.wav",0]
                         ,"老公":["laog.wav",0], "老婆":["laop.wav",0]}
        
        for k in self.wave_dict:
            item = QListWidgetItem()
            item.setText(k)
            item.setData(Qt.UserRole, self.wave_dict[k])
            self.list_LW.addItem(item)
            
    def initAUD(self):
        #
        info = QAudioDeviceInfo.defaultInputDevice()
        if (~info.isFormatSupported(self.audio.format)):
            # print("警告,设置的默认音频格式并不支持,将尝试采用最相近的支持格式")
            # 不知道这里面有什么神改动?
            self.audio.format  = info.nearestFormat(self.audio.format)
        #
        update_interval = 160
        self.audioRecorder = QAudioInput(self.audio.format)
        self.audioRecorder.setNotifyInterval(update_interval) #按毫秒ms 类似于QTimer的作用
        self.audioRecorder.notify.connect(self.processAudioData)
        self.audioRecorder_TD = QThread()
        self.audioRecorder.moveToThread(self.audioRecorder_TD)
        self.audioRecorder_TD.started.connect(self.startRecord)
        self.audioRecorder.stateChanged.connect(self.recordStopped)
        # 总结来说线程只是一个容器,里面执行的循环要是没法结束,强制退出也不好操作
        # 所以还是好好写好任务流然后发送信号比较合理
        self.audioPlayer = QAudioOutput(self.audio.format)
        self.audioPlayer.setNotifyInterval(update_interval)
        self.audioPlayer.notify.connect(self.processAudioData)
        self.audioPlayer_TD = QThread()
        self.audioPlayer.moveToThread(self.audioPlayer_TD)
        self.audioPlayer_TD.started.connect(self.startPlay)
        self.audioPlayer.stateChanged.connect(self.playStopped)
    
    #   
    def startRecord(self):
        self.audioRecorder.start(self.audio.record_buffer) # 独立出来主要就是为了传个参数进去
        
    def click2Record(self):
        if self.snd_play_ctr != 0:
            self.audioPlayer.suspend()
            self.audioPlayer.stop()
            self.audio.play_buffer.close()
            self.running_SL.setValue(0)
            self.audioPlayer_TD.quit()
            self.snd_play_ctr = 0
            self.play_BT.setText("开始播放")
        #
        self.is_snd_recording = True
        self.running_SL.setStyleSheet("QSlider::handle:horizontal {background-color: #d91900;}")
        self.running_SL.setValue(0)
        if self.snd_record_ctr == 0:
            self.audio.record_buffer.open(QIODevice.WriteOnly)
            self.audioRecorder_TD.start() #注意这里是分线程进行
            self.record_BT.setText("暂停录音")
            self.reset_BT.setText("停止录音")
        elif self.snd_record_ctr % 2 == 1:
            self.audioRecorder.suspend()
            self.result_LB.setText("录音暂停")
            self.record_BT.setText("继续录音")
        else: # self.snd_record_ctr % 2 == 0:
            self.audioRecorder.resume()
            self.record_BT.setText("暂停录音")
        self.snd_record_ctr += 1

    def recordStopped(self):
        if self.audioRecorder.state() == QAudio.StoppedState: #==2 #QAudio.IdleState: #==3;
            self.audioRecorder_TD.quit()

    def startPlay(self):
        self.audioPlayer.start(self.audio.play_buffer)

    def click2Play(self):
        if self.is_snd_recording == None:
            self.result_LB.setText("还没录音呢!!!")
        else:
            if self.snd_record_ctr % 2 == 1:
                self.audioRecorder.suspend()
                self.record_BT.setText("继续录音")
                self.snd_record_ctr += 1
            #
            self.is_snd_recording = False
            self.running_SL.setStyleSheet("QSlider::handle:horizontal {background-color: #007ad9;}")
            if self.snd_play_ctr == 0:
                data = self.audio.record_buffer.data()      
                self.audio.play_buffer.setData(data)
                self.audio.play_buffer.open(QIODevice.ReadOnly) # 要在关闭的情况下设置数据然后在以某种模式打开
                self.audioPlayer_TD.start()
                self.running_SL.setValue(0)
                self.start_time = 0
                # self.start_time = self.running_SL.value() / 100 * self.audio.duration
                # self.audioPlayer.setVolume(0.8)
                self.play_BT.setText("暂停播放")
                self.reset_BT.setText("停止播放")
            elif self.snd_play_ctr % 2 == 1:
                self.audioPlayer.suspend()
                self.result_LB.setText("播放暂停")
                self.play_BT.setText("继续播放")
            else:    
                self.audioPlayer.resume()
                self.play_BT.setText("暂停播放")
            self.snd_play_ctr += 1
    
    def playStopped(self):
        if self.audioPlayer.state() == QAudio.IdleState: #==3; #QAudio.StoppedState: #==2
            self.audioPlayer.stop()
            self.running_SL.setValue(0)
            self.audio.play_buffer.close()
            self.audioPlayer_TD.quit()
            self.snd_play_ctr = 0
            self.play_BT.setText("开始播放")
    
    def dragPosPlay(self):
        if self.is_snd_recording == None:
            self.running_SL.setValue(0)
        else:
            if self.is_snd_recording & (self.snd_record_ctr % 2 == 1):
                self.audioRecorder.suspend()
                self.record_BT.setText("继续录音")
                self.snd_record_ctr += 1
            if (not self.is_snd_recording) & (self.snd_play_ctr % 2 == 1):
                self.audioPlayer.suspend()
                self.play_BT.setText("继续播放")
                self.snd_play_ctr += 1
            self.is_snd_recording = False
            self.running_SL.setStyleSheet("QSlider::handle:horizontal {background-color: #007ad9;}")
            self.audioPlayer.stop()
            self.audio.play_buffer.close()
            self.audioPlayer_TD.quit()
            #
            data = self.audio.record_buffer.data()      
            self.audio.play_buffer.setData(data)
            self.audio.play_buffer.open(QIODevice.ReadOnly) # 要在关闭的情况下设置数据然后在以某种模式打开
            self.audioPlayer_TD.start()
            data_size = self.audio.record_buffer.data().size()
            sel_pcent = self.running_SL.value() / 100
            sel_size = int(sel_pcent * data_size)
            self.audio.pos = int(sel_pcent * (data_size / self.audio.chunksize)) # 重设第几个chunk开始播放
            self.start_time = int(sel_pcent * self.audio.duration) # 重设开始播放时间
            self.audio.play_buffer.seek(sel_size)
            self.snd_play_ctr = 1
            self.play_BT.setText("暂停播放")
    
    def sel2Play(self, item):
        c0 = (self.is_snd_recording == None)
        c1 = ((self.is_snd_recording == False) & (self.snd_play_ctr % 2 == 0))
        c2 = ((self.is_snd_recording == True) & (self.snd_record_ctr % 2 == 0))
        if (c0 | c1 | c2):
            self.cur_item = item
            sound_dir = "./sound/"
            #self.cur_wave = os.path.abspath(item.data(Qt.UserRole)[0])
            self.cur_wave = item.data(Qt.UserRole)[0]
            sound_path = os.path.join(sound_dir, self.cur_wave)
            with wave.open(sound_path, 'rb') as wf:
                data = wf.readframes(wf.getnframes())
                self.audio.play_buffer.setData(data)
                self.audio.play_buffer.open(QIODevice.ReadOnly)
                self.start_time = 0
                self.audio.duration = 10 # 随便给了个值,避免产生除0的问题其他没啥用
                self.audioPlayer_TD.start()           
    
    def click2Reset(self):
        if self.is_snd_recording == None:
            self.result_LB.setText("还没录音呢!!!")
        elif self.is_snd_recording:
            self.audioRecorder.stop()
            self.audio.record_buffer = QBuffer()
            self.snd_record_ctr = 0
            self.result_LB.setText("录音停止")
            self.record_BT.setText("开始录音")
        else: #not self.is_snd_recording:
            self.audioPlayer.stop()
            self.audio.pos = 0
            self.snd_play_ctr = 0
            self.result_LB.setText("播放停止")
            self.play_BT.setText("开始播放")
        self.running_SL.setValue(0)
        
    def click2Save(self):
        if self.is_snd_recording == None:
            self.result_LB.setText("还没录音呢!!!")
        elif self.is_snd_recording:
            self.audioRecorder.suspend()
        else:
            self.audioPlayer.suspend()
        #self.audio.save_path = QFileDialog().getSaveFileName(self.main_Dlg, "选个保存的地方吧", new_path)[0]
        # 注意末尾那个[0]别丢了,不然返回的是tuple类型
        self.audio.saveWave()
        self.snd_record_ctr = 0
        self.result_LB.setText("录音存于:{};刚刚应该是{}叫了:)".format(os.path.abspath(self.audio.save_path), self.getMinDist()))
        self.record_BT.setText("开始录音")
         
    def processAudioData(self):
        if self.is_snd_recording: #self.audioRecorder.state() == QAudio.ActiveState:
            self.audio.block = self.audio.record_buffer.data().right(self.audio.chunksize)
            self.audio.duration = self.audioRecorder.processedUSecs() # 注意这里是微秒!!!
            interval = 10
            self.running_SL.setValue((self.audio.duration / 1000000) % interval * (100 / interval))
            show_info = "已录制{:.1f}秒".format(self.audio.duration/1000000.0)
            self.result_LB.setText(show_info)
        else: # self.audioPlayer.state() == QAudio.ActiveState:
            # 试过chop 不过好像没有必要
            self.audio.block = self.audio.play_buffer.data().mid(self.audio.pos*self.audio.chunksize, self.audio.chunksize)
            self.audio.pos += 1
            self.running_SL.setValue((self.start_time + self.audioPlayer.processedUSecs())/self.audio.duration*100)
            show_info = "正在播放{:.1f}/{:.1f}秒".format((self.start_time + self.audioPlayer.processedUSecs())/1000000.0
                                                         ,self.audio.duration/1000000.0)
            self.result_LB.setText(show_info)
        self.wave_spectrum_PG.updatePlot()

    def getMFCC(self,path):
        (rate, sig) = scwav.read(path)
        mfcc_feature = mfcc(sig, rate)
        nmfcc = np.array(mfcc_feature)
        y, sr = librosa.load(path)
        return librosa.feature.mfcc(y, sr)
        
    def compareMFCC(self, demo_path):
        mfcc1 = self.getMFCC(self.audio.save_path)
        print(demo_path)
        mfcc2 = self.getMFCC(demo_path)
        norm = lambda x, y: nlnorm(x-y, ord=1)
        d, cost_matrix, acc_cost_matrix, path = dtw(mfcc1.T, mfcc2.T, dist=norm)
        return d
       
    def getMinDist(self):
        i = 1000000
        sound_dir = "./sound/"
        for k in self.wave_dict:
            self.wave_dict[k][1] = self.compareMFCC(os.path.join(sound_dir, self.wave_dict[k][0]))
            print("{}:{:.1f}".format(k, self.wave_dict[k][1]))
            i = min(i, self.wave_dict[k][1])
            if i == self.wave_dict[k][1]:
                min_k = k
        return min_k
Ejemplo n.º 21
0
class AudioTest(QMainWindow):

    PUSH_MODE_LABEL = "Enable push mode"
    PULL_MODE_LABEL = "Enable pull mode"
    SUSPEND_LABEL = "Suspend playback"
    RESUME_LABEL = "Resume playback"

    DurationSeconds = 1
    ToneSampleRateHz = 600
    DataSampleRateHz = 44100

    def __init__(self):
        super(AudioTest, self).__init__()

        self.m_device = QAudioDeviceInfo.defaultOutputDevice()
        self.m_output = None

        self.initializeWindow()
        self.initializeAudio()

    def initializeWindow(self):
        layout = QVBoxLayout()

        self.m_deviceBox = QComboBox(activated=self.deviceChanged)
        for deviceInfo in QAudioDeviceInfo.availableDevices(
                QAudio.AudioOutput):
            self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)

        layout.addWidget(self.m_deviceBox)

        self.m_modeButton = QPushButton(clicked=self.toggleMode)
        self.m_modeButton.setText(self.PUSH_MODE_LABEL)

        layout.addWidget(self.m_modeButton)

        self.m_suspendResumeButton = QPushButton(
            clicked=self.toggleSuspendResume)
        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

        layout.addWidget(self.m_suspendResumeButton)

        volumeBox = QHBoxLayout()
        volumeLabel = QLabel("Volume:")
        self.m_volumeSlider = QSlider(Qt.Horizontal,
                                      minimum=0,
                                      maximum=100,
                                      singleStep=10,
                                      valueChanged=self.volumeChanged)
        volumeBox.addWidget(volumeLabel)
        volumeBox.addWidget(self.m_volumeSlider)

        layout.addLayout(volumeBox)

        window = QWidget()
        window.setLayout(layout)

        self.setCentralWidget(window)

    def initializeAudio(self):
        self.m_pullTimer = QTimer(self, timeout=self.pullTimerExpired)
        self.m_pullMode = True

        self.m_format = QAudioFormat()
        self.m_format.setSampleRate(self.DataSampleRateHz)
        self.m_format.setChannelCount(1)
        self.m_format.setSampleSize(16)
        self.m_format.setCodec('audio/pcm')
        self.m_format.setByteOrder(QAudioFormat.LittleEndian)
        self.m_format.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.m_format):
            qWarning("Default format not supported - trying to use nearest")
            self.m_format = info.nearestFormat(self.m_format)

        self.m_generator = Generator(self.m_format,
                                     self.DurationSeconds * 1000000,
                                     self.ToneSampleRateHz, self)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)

    def deviceChanged(self, index):
        self.m_pullTimer.stop()
        self.m_generator.stop()
        self.m_audioOutput.stop()
        self.m_device = self.m_deviceBox.itemData(index)

        self.createAudioOutput()

    def volumeChanged(self, value):
        if self.m_audioOutput is not None:
            self.m_audioOutput.setVolume(value / 100.0)

    def notified(self):
        qWarning(
            "bytesFree = %d, elapsedUSecs = %d, processedUSecs = %d" %
            (self.m_audioOutput.bytesFree(), self.m_audioOutput.elapsedUSecs(),
             self.m_audioOutput.processedUSecs()))

    def pullTimerExpired(self):
        if self.m_audioOutput is not None and self.m_audioOutput.state(
        ) != QAudio.StoppedState:
            chunks = self.m_audioOutput.bytesFree(
            ) // self.m_audioOutput.periodSize()
            for _ in range(chunks):
                data = self.m_generator.read(self.m_audioOutput.periodSize())
                if data is None or len(
                        data) != self.m_audioOutput.periodSize():
                    break

                self.m_output.write(data)

    def toggleMode(self):
        self.m_pullTimer.stop()
        self.m_audioOutput.stop()

        if self.m_pullMode:
            self.m_modeButton.setText(self.PULL_MODE_LABEL)
            self.m_output = self.m_audioOutput.start()
            self.m_pullMode = False
            self.m_pullTimer.start(20)
        else:
            self.m_modeButton.setText(self.PUSH_MODE_LABEL)
            self.m_pullMode = True
            self.m_audioOutput.start(self.m_generator)

        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

    def toggleSuspendResume(self):
        if self.m_audioOutput.state() == QAudio.SuspendedState:
            qWarning("status: Suspended, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.ActiveState:
            qWarning("status: Active, suspend()")
            self.m_audioOutput.suspend()
            self.m_suspendResumeButton.setText(self.RESUME_LABEL)
        elif self.m_audioOutput.state() == QAudio.StoppedState:
            qWarning("status: Stopped, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.IdleState:
            qWarning("status: IdleState")

    stateMap = {
        QAudio.ActiveState: "ActiveState",
        QAudio.SuspendedState: "SuspendedState",
        QAudio.StoppedState: "StoppedState",
        QAudio.IdleState: "IdleState"
    }

    def handleStateChanged(self, state):
        qWarning("state = " + self.stateMap.get(state, "Unknown"))
Ejemplo n.º 22
0
class ToneWindow(QWidget):
    """
    Central Widget of the application.
    
    Defines the format of the audio channel and initializes the midi listener, 
    generators and modifiers. The midi listener is operated on separate thread 
    handled by a Qthread. The view components are initialized and put together 
    in a general layout using the CreateUI method.
    """
    def __init__(self, parent=None):
        print("Tone Widget inst")
        QWidget.__init__(self, parent)

        self.activeGen = ActiveGen(sampleRate=SAMPLE_RATE,
                                   samplePerRead=Meep.SAMPLES_PER_READ)
        self.createUI(self)

        # Meep playback format initialization
        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        # check compatibility of format with device
        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if info.isFormatSupported(format) is False:
            print(
                "Raw audio format not supported by backend, cannot play audio."
            )
            return None

        # Audio Output init
        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)
        # initialize and start the audio playback
        self.generator = Meep(format, self.activeGen, self)
        self.generator.start()
        self.output.start(self.generator)

        # Create the port reader object
        self.midiListener = MidiPortReader()
        # Create a thread which will read it
        self.listenerThread = QThread()

        # move QObjet to a new thread
        self.midiListener.moveToThread(self.listenerThread)

        # Connect the signals to slot functions
        self.midiListener.newNoteFrequency.connect(self.activeGen.setFreq)
        self.midiListener.newNotePress.connect(self.activeGen.setNote)

        # Tell Qt the function to call
        # when it starts the thread
        self.listenerThread.started.connect(self.midiListener.listener)
        # start the thread
        self.listenerThread.start()

    def createUI(self, parent):
        """Receives a parent QWidget and produces the view in it"""

        print("Create UI")
        slidLayout = QHBoxLayout()
        vLayout = QVBoxLayout(self)
        vLayout.setSpacing(10)

        # initialize title label and UI constraints
        self.title = QLabel(self)
        self.title.setText("Tone Generator")
        self.title.setMargin(10)
        self.title.setFixedHeight(50)
        self.title.setFixedWidth(400)
        self.title.setAlignment(Qt.AlignCenter)

        # set title style
        self.title.setStyleSheet("""
                font-family : "Lucida Console";
                font-size: 20px;
                color : #F2F2F2;
                border: 2px solid green;
                border-radius: 4px;
                border-color: #082126;
                padding: 2px;
                background-color : #0E3740 ;
        """)
        # set background color
        self.setStyleSheet(" background-color : #082126 ; ")

        # initialize the view components
        filtSlider = sliders.FiltSlider(self.activeGen,
                                        parent).createUI(parent)
        adsrSlider = sliders.ADSRSlider(self.activeGen,
                                        parent).createUI(parent)
        masterSlider = sliders.MasterSlider(self.activeGen,
                                            parent).createUI(parent)
        genSlider = sliders.GenSlider(self.activeGen, parent).createUI(parent)

        # add the two sliders and ADSR layout in a horizontal layout
        slidLayout.addStretch(1)
        slidLayout.addWidget(filtSlider)
        slidLayout.addStretch(1)
        slidLayout.addWidget(adsrSlider, Qt.AlignHCenter)
        slidLayout.addStretch(1)
        slidLayout.addWidget(masterSlider)
        slidLayout.addStretch(1)

        # put all components in the vertical layout
        vLayout.addWidget(self.title)
        vLayout.setAlignment(self.title, Qt.AlignCenter)
        vLayout.addSpacing(20)
        vLayout.addLayout(slidLayout)
        vLayout.addSpacing(20)
        vLayout.addWidget(genSlider)
        vLayout.setAlignment(genSlider, Qt.AlignCenter)
Ejemplo n.º 23
0
class MainWindow(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
                int(2*CTRL_INTERVAL/1000)
        self.output.setBufferSize(output_buffer_size)

        self.generator = Generator(format, self)
        self.midiListener = MidiPortReader()
        self.listenerThread = QThread()
        self.midiListener.moveToThread(self.listenerThread)
        self.listenerThread.started.connect(self.midiListener.listener)
        self.midiListener.addVoice.connect(self.generator.addVoice)
        self.midiListener.removeVoice.connect(self.generator.removeVoice)

        self.createUI()

        self.pslider.valueChanged.connect(self.generator.changeP)
        self.fslider.valueChanged.connect(self.generator.qCalc)
        self.semiDown.clicked.connect(self.smDown)
        self.semiUp.clicked.connect(self.smUp)
        self.octaveDown.clicked.connect(self.ovDown)
        self.octaveUp.clicked.connect(self.ovUp)

        self.listenerThread.start()
        self.generator.start()
        self.output.start(self.generator)

    def createUI(self):
        label = QLabel()
        label.setText("Pulse ammount")
        self.pslider = QSlider(Qt.Horizontal)
        self.pslider.setMinimum(0)
        self.pslider.setMaximum(100)
        self.pslider.setValue(60)
        flabel = QLabel()
        flabel.setText("Q")
        self.fslider = QSlider(Qt.Horizontal)
        self.fslider.setMinimum(10)
        self.fslider.setMaximum(10000)
        self.fslider.setValue(1000)
        self.octaveUp = QPushButton("+12")
        self.octaveDown = QPushButton("-12")
        self.semiUp = QPushButton("+1")
        self.semiDown = QPushButton("-1")
        self.transLabel = QLabel(str(self.generator.transpose))
        vl = QVBoxLayout(self)

        hl = QHBoxLayout()
        hl.addWidget(label)
        hl.addStretch(1)
        hl.addWidget(self.pslider)
        vl.addLayout(hl)

        fhl = QHBoxLayout()
        fhl.addWidget(flabel)
        fhl.addStretch(1)
        fhl.addWidget(self.fslider)
        vl.addLayout(fhl)

        bhl = QHBoxLayout()
        bhl.addWidget(self.octaveDown)
        bhl.addWidget(self.semiDown)
        bhl.addWidget(self.transLabel)
        bhl.addWidget(self.semiUp)
        bhl.addWidget(self.octaveUp)
        vl.addLayout(bhl)

    def transposeChange(self, amt):
        self.generator.transpose = self.generator.transpose + amt
        self.transLabel.setText(str(self.generator.transpose))

    @pyqtSlot()
    def ovUp(self):
        self.transposeChange(12)

    @pyqtSlot()
    def ovDown(self):
        self.transposeChange(-12)

    @pyqtSlot()
    def smUp(self):
        self.transposeChange(1)

    @pyqtSlot()
    def smDown(self):
        self.transposeChange(-1)
Ejemplo n.º 24
0
class SideToneWidget(QWidget):
    def __init__(self, parent, the_settings):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Save link to settings object
        self.settings = the_settings
        # Get the status bar
        self.status_bar = parent.statusBar()
        # just the time
        self.time = QTime()
        self.time.start()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        #   self.input_info_list, list of QAudioInfo for inputs
        #   self.cb_inputs, combox of input names in same order
        #   self.otput_info_list, list of QAudioInfo for outputs
        #   self.cb_otputs, combox of output names in same order
        #   self.volume, volume slider
        #   self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots. Up to this point, the changes that
        # _uic() made in e.g. the volume or mute, or the combobox selections,
        # raised signals that were not connected. Now connect the signals
        # so that user-changes go to our slots for handling.
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Changes in the combox selections go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Now pretend the user has made a selection of the in and out devices.
        # That should result in activating everythings.
        self.in_dev_change(self.cb_inputs.currentIndex())
        self.ot_dev_change(self.cb_otputs.currentIndex())

    # Method to disconnect the input and output devices, if they exist.
    # This is called prior to any change in device selection.
    # Note that the QWidget class has an existing method disconnect(),
    # and we do not want to override that, so that name is not used.

    def disconnect_devices(self):

        # If an output device exists, make it stop. That prevents it
        # trying to pull any data from the input device if any.
        if self.otput_device is not None:
            self.otput_device.stop()
        # If an input device exists, make it stop also. That means it
        # loses track of the output device it was formerly connected to.
        if self.input_device is not None:
            self.input_device.stop()

    # Method to connect the input and output devices, if both exist. This is
    # called after making any change in device selection.

    def reconnect_devices(self):

        if (self.input_device is not None) \
           and (self.otput_device is not None ) :

            # Connect the devices by asking the OUTput device for its
            # QIODevice, and passing that to the INput device's start()
            # method. This could equally well be done the other way,
            # by passing the input dev's IODevice to the output device.

            self.input_device.start(self.otput_device.start())
            #self.otput_device.start( self.input_device.start() )

            # In case the output device was just created, set its volume.
            self.set_volume()

    # Method to set the volume on the output device. (The input device volume
    # is always 1.0.) This is called on any change of the volume slider or
    # of the Mute button or of the output device choice.

    def set_volume(self):
        if self.mute.isChecked():
            # Mute is ON, set volume to 0 regardless of volume slider
            volume = 0.0
        else:
            # Mute is OFF, set volume to float version of volume slider
            volume = self.volume.value() / 100
        if self.otput_device:
            # an output device exists (almost always true), set it
            self.otput_device.setVolume(volume)

    # Slot entered upon any change in the volume slider widget.
    def volume_change(self, new_level):
        if self.mute.isChecked():
            # The Mute button is ON; assume the user wants it OFF, else why
            # move the slider? Note this causes a call to set_volume().
            self.mute.setChecked(False)
        else:
            # The Mute button is OFF, just change the volume.
            self.set_volume()

    # Slot entered upon toggling of the mute switch, by the user or by the
    # code calling mute.setChecked(). Make sure the volume is set appropriately.
    def mute_change(self, onoff):
        self.set_volume()

    # Slots for selection of the input and output devices. On startup we have
    # neither an input nor an output device. We do not know which combox the
    # user will fiddle with first.

    # Slot entered upon any change in the selection of the input device
    # combo box. The argument is the new index of the list of values.

    def in_dev_change(self, new_index):

        # Disconnect and stop the devices if they are connected.
        self.disconnect_devices()

        self.input_device = None  # device object goes out of scope

        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]

        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)

        # the input device volume is always 1.0, wide open.
        self.input_device.setVolume(1.0)

        # The choice of buffer size has a major impact on the lag. It needs
        # to be small or there is severe echo; but if it is too small, there
        # is a sputtering or "motor-boating" effect.
        self.input_device.setBufferSize(384)

        # hook up possible debug status display
        self.input_device.stateChanged.connect(self.in_dev_state_change)

        # reconnect the devices if possible.

        self.reconnect_devices()

    # Slot entered upon any change in the selection of output. The argument
    # is the index to the list of output devices in the combobox.

    def ot_dev_change(self, new_index):
        print('index', new_index)
        # Disconnect and stop the devices if they are connected.
        self.disconnect_devices()

        self.otput_device = None  # device object goes out of scope

        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.otput_info_list[new_index]

        # Create a new QAudioOutput based on that.
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(0)  # reconnect will set correct volume

        # hook up possible debug status display
        self.otput_device.stateChanged.connect(self.ot_dev_state_change)

        # reconnect the devices if possible. Which also sets the volume.

        self.reconnect_devices()

    # Show some text in the main-window status bar for 1 second, more or less.
    def show_status(self, text, duration=1000):
        self.status_bar.showMessage(text, duration)

    # Slots called on any "state" change of an audio device. Optionally
    # show the state in the main window status bar.
    def in_dev_state_change(self, new_state):
        #self.show_status(
        #'{} in dev state {}'.format(self.time.elapsed(),int(new_state))
        #)
        pass

    def ot_dev_state_change(self, new_state):
        #self.show_status(
        #'{} ot dev state {}'.format(self.time.elapsed(),int(new_state))
        #)
        pass

    # Close events are only received by a top-level widget. When our top-level
    # widget gets one, indicating the app is done, it calls this method.

    def closeEvent(self, event):
        # if we have devices, make them stop.
        self.disconnect_devices()

        # if the devices exist, reset them and then trash them.
        if self.otput_device is not None:
            self.otput_device.reset()
            self.otput_device = None
        if self.input_device is not None:
            self.input_device.reset()
            self.input_device = None

        # Save the current selection of the input and output combo boxes,
        # in the settings file.
        in_dev_name = self.cb_inputs.currentText()
        self.settings.setValue('in_dev_name', in_dev_name)
        ot_dev_name = self.cb_otputs.currentText()
        self.settings.setValue('ot_dev_name', ot_dev_name)

        # Save the volume setting and mute status in the settings.
        self.settings.setValue('volume', self.volume.value())
        self.settings.setValue('mute_status', int(self.mute.isChecked()))

    def _uic(self):
        '''
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    Hooking the signals to useful slots is the job
    of __init__. Here just make the layout.
        '''
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(':/icon.png').scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Make a list of the name-strings for those items.
        in_dev_names = [
            audio_info.deviceName() for audio_info in self.input_info_list
        ]
        # Create a combo box and populate it with those names
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems(in_dev_names)
        # If the in_dev_name from the previous run is in the current list,
        # make it current, otherwise pick the first item.
        in_dev_name = self.settings.value('in_dev_name', 'unknown')
        if in_dev_name in in_dev_names:
            self.cb_inputs.setCurrentIndex(in_dev_names.index(in_dev_name))
        else:
            self.cb_inputs.setCurrentIndex(0)

        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(
            QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        # Make a list of the name-strings of those things
        ot_dev_names = [
            audio_info.deviceName() for audio_info in self.otput_info_list
        ]
        # Create a combo box and populate it with those names
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems(ot_dev_names)
        # If the ot_dev_name from the previous run is in the current list,
        # make it the current choice in the box.
        ot_dev_name = self.settings.value('ot_dev_name', 'unknown')
        if ot_dev_name in ot_dev_names:
            self.cb_otputs.setCurrentIndex(ot_dev_names.index(ot_dev_name))
        else:
            self.cb_otputs.setCurrentIndex(0)

        #self.show_status(
        #'{} inputs {} otputs'.format(len(self.input_info_list),len(self.otput_info_list))
        #)
        # Create a combo box and populate it with names of outputs

        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)

        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # set the volume slider to the value from the previous run, or zero.
        self.volume.setValue(self.settings.value('volume', 0))

        # Create a checkbox "Mute"
        self.mute = QCheckBox('Mute')
        # Set it to the value at the end of the last run, or to True
        self.mute.setChecked(bool(self.settings.value('mute_status', 1)))

        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)

        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
Ejemplo n.º 25
0
class MayRenderer(QWidget):

    shouldSave = pyqtSignal()

    texsize = 512
    samplerate = 44100

    shaderHeader = '#version 130\nuniform float iTexSize;\nuniform float iBlockOffset;\nuniform float iSampleRate;\n\n'

    def __init__(self, parent):
        super().__init__()
        self.parent = parent

        self.blocksize = (self.texsize * self.texsize) / self.samplerate

        self.initState()
        self.initUI()
        self.initAudio()

    def initState(self):
        self.playing = False
        self.initVolume = 1

        self.useWatchFile = False
        self.watchFileName = ''
        self.storeCodeIfNotWatching = ''

        self.useSynDump = False
        self.synDrumName = ''
        self.synFileName = ''

    def initUI(self):
        self.mainLayout = QVBoxLayout()
        self.codeLayout = QVBoxLayout()
        self.codeButtonBar = QHBoxLayout()
        self.codeWatchFileBar = QHBoxLayout()
        self.renderBar = QHBoxLayout()
        self.playbackBar = QHBoxLayout()
        self.renderGroup = QGroupBox()
        self.renderGroupLayout = QVBoxLayout()
        self.renderGroupLayout.addLayout(self.renderBar)
        self.renderGroupLayout.addLayout(self.playbackBar)
        self.renderGroup.setLayout(self.renderGroupLayout)
        self.renderGroup.setObjectName("renderGroup")
        self.synGroupLayout = QHBoxLayout()
        self.synGroup = QGroupBox()
        self.synGroup.setLayout(self.synGroupLayout)

        self.synDumpCheckBox = QCheckBox('Dump as')
        self.synDrumNameBox = QLineEdit(self)
        self.synFileNameBox = QLineEdit(self)
        self.synFileButton = QPushButton('...')
        self.synGroupLayout.addWidget(self.synDumpCheckBox, 1)
        self.synGroupLayout.addWidget(self.synDrumNameBox, 2)
        self.synGroupLayout.addWidget(QLabel('in'), .1)
        self.synGroupLayout.addWidget(self.synFileNameBox, 5)
        self.synGroupLayout.addWidget(self.synFileButton, 0.1)
        self.synDumpCheckBox.stateChanged.connect(self.toggleSynDump)
        self.synDrumNameBox.setPlaceholderText('drumname')
        self.synDrumNameBox.textChanged.connect(self.setSynDrumName)
        self.synFileNameBox.setPlaceholderText('some aMaySyn .syn file')
        self.synFileNameBox.textChanged.connect(self.setSynFileName)
        self.synFileButton.setMaximumWidth(40)
        self.synFileButton.clicked.connect(self.chooseSynFile)

        self.codeGroup = QGroupBox()
        self.buttonCopy = QPushButton('↬ Clipboard', self)
        self.buttonCopy.clicked.connect(self.copyToClipboard)
        self.buttonPaste = QPushButton('Paste ↴', self)
        self.buttonPaste.clicked.connect(self.pasteClipboard)
        self.buttonClear = QPushButton('×', self)
        self.buttonClear.clicked.connect(self.clearEditor)
        self.codeEditor = QPlainTextEdit(self)
        self.codeEditor.setLineWrapMode(QPlainTextEdit.WidgetWidth)
        #self.codeEditor.setCenterOnScroll(True)
        #self.codeEditor.textChanged.formatEditor()) # this gives a recursion problem, but how to filter e.g. tabs?
        self.codeEditor.cursorPositionChanged.connect(self.updatePosLabel)
        self.codeEditor.setTabStopWidth(14)
        self.watchFileCheckBox = QCheckBox('watch file:', self)
        self.watchFileCheckBox.stateChanged.connect(self.toggleWatchFile)
        self.watchFileNameBox = QLineEdit(self)
        self.watchFileNameBox.setPlaceholderText(
            'use GLSL code file instead of the above editor...')
        self.watchFileNameBox.textChanged.connect(self.setWatchFileName)
        self.buttonWatchFile = QPushButton('...', self)
        self.buttonWatchFile.setMaximumWidth(40)
        self.buttonWatchFile.clicked.connect(self.chooseWatchFile)

        self.renderButton = QPushButton(self)
        self.renderButton.clicked.connect(self.pressRenderShader)
        self.renderLengthBox = QDoubleSpinBox(self)
        self.renderLengthBox.setMinimum(0)
        self.renderLengthBox.setValue(4 * self.blocksize - .01)
        self.renderLengthBox.setSingleStep(self.blocksize)
        self.renderLengthBox.setSuffix(' sec')
        self.renderLengthBox.setToolTip('render length')
        self.renderBpmBox = QSpinBox(self)
        self.renderBpmBox.setRange(1, 999)
        self.renderBpmBox.setValue(160)
        self.renderBpmBox.setPrefix('BPM ')
        self.renderBpmBox.setToolTip('--> determines SPB')
        self.playbackVolumeSlider = QSlider(Qt.Horizontal)
        self.playbackVolumeSlider.setMaximum(100)
        self.playbackVolumeSlider.setValue(self.initVolume * 100)
        self.playbackVolumeSlider.setToolTip('volume')
        self.playbackVolumeSlider.sliderMoved.connect(self.setVolume)
        self.renderBar.addWidget(self.renderButton, 60)
        self.renderBar.addWidget(self.renderBpmBox, 20)
        self.renderBar.addWidget(self.renderLengthBox, 20)

        self.progressBar = QProgressBar(self)
        self.progressBar.setEnabled(False)
        self.pauseButton = QPushButton(self)
        self.pauseButton.setEnabled(False)
        self.pauseButton.clicked.connect(self.pressPauseButton)
        self.playbackBar.addWidget(self.progressBar, 60)
        self.playbackBar.addWidget(self.playbackVolumeSlider, 20)
        self.playbackBar.addWidget(self.pauseButton, 20)

        self.codeButtonBar.addWidget(self.buttonCopy)
        self.codeButtonBar.addWidget(self.buttonPaste)
        self.codeButtonBar.addWidget(self.buttonClear)
        self.codeWatchFileBar.addWidget(self.watchFileCheckBox)
        self.codeWatchFileBar.addWidget(self.watchFileNameBox)
        self.codeWatchFileBar.addWidget(self.buttonWatchFile)
        self.codeHeader = QHBoxLayout()
        self.codePosLabel = QLabel('(0,0)')
        self.codeHeader.addWidget(QLabel('GLSL code'))
        self.codeHeader.addStretch()
        self.codeHeader.addWidget(self.codePosLabel)
        self.codeLayout.addLayout(self.codeHeader)
        self.codeLayout.addLayout(self.codeButtonBar)
        self.codeLayout.addWidget(self.codeEditor)
        self.codeLayout.addLayout(self.codeWatchFileBar)
        self.codeGroup.setLayout(self.codeLayout)

        self.mainLayout.addWidget(self.synGroup)
        self.mainLayout.addWidget(self.codeGroup)
        self.mainLayout.addWidget(self.renderGroup)

        self.updatePlayingUI()

        self.setLayout(self.mainLayout)

    def updatePlayingUI(self, keepActive=False):
        self.renderButton.setText(
            'shut the f**k up' if self.playing else 'send to hell')
        if not self.playing and not keepActive:
            self.progressBar.setValue(0)
        self.progressBar.setEnabled(self.playing if not keepActive else True)
        self.pauseButton.setEnabled(self.playing if not keepActive else True)
        self.pauseButton.setText('||' if (
            self.playing and self.audiooutput.state() != QAudio.SuspendedState
        ) else '▶')

    def initAudio(self):
        self.audioformat = QAudioFormat()
        self.audioformat.setSampleRate(self.samplerate)
        self.audioformat.setChannelCount(2)
        self.audioformat.setSampleSize(32)
        self.audioformat.setCodec('audio/pcm')
        self.audioformat.setByteOrder(QAudioFormat.LittleEndian)
        self.audioformat.setSampleType(QAudioFormat.Float)
        # self.audiodeviceinfo = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())

        self.audiooutput = QAudioOutput(self.audioformat)
        self.audiooutput.setVolume(self.initVolume)

    def paste(self, source):
        self.codeEditor.clear()
        source = source.replace(4 * ' ', '\t').replace(3 * ' ', '\t')
        self.codeEditor.insertPlainText(source)
        #self.codeEditor.setFocus() #TODO: think about whether we want this
        self.codeEditor.ensureCursorVisible()

    def pasteClipboard(self):
        self.paste(self.shaderHeader + QApplication.clipboard().text())

    def copyToClipboard(self):
        text = self.codeEditor.toPlainText().replace('\t', 4 * ' ')
        QApplication.clipboard().setText(text)

    def clearEditor(self):
        self.codeEditor.setPlainText('')
        self.codeEditor.setFocus()

    def updatePosLabel(self):
        cursor = self.codeEditor.textCursor()
        self.codePosLabel.setText(
            f'({cursor.blockNumber()},{cursor.positionInBlock()})')

#    def formatEditor(self):
#        plainText = self.codeEditor.toPlainText().replace('\t', 4*' ')
#        self.codeEditor.setPlainText(plainText)

    def toggleSynDump(self, state):
        self.useSynDump = (state == Qt.Checked)
        self.shouldSave.emit()

    def chooseSynFile(self):
        dialogResult, _ = QFileDialog.getSaveFileName(
            self, 'Choose SYN definition file', '',
            'aMaySyn definition files (*.syn);;All files (*)')
        print(dialogResult)
        self.synFileNameBox.setText(dialogResult)
        self.synDumpCheckBox.setCheckState(Qt.Checked)
        if self.synFileName == '':
            self.synFileNameBox.setFocus()
        self.shouldSave.emit()

    def setSynFileName(self):
        self.synFileName = self.synFileNameBox.text()
        self.shouldSave.emit()

    def setSynDrumName(self):
        self.synDrumName = self.synDrumNameBox.text()
        self.shouldSave.emit()

    def setSynDumpParameters(self, useSynDump, synFileName, synDrumName):
        self.synDumpCheckBox.setChecked(useSynDump)
        self.synFileNameBox.setText(synFileName)
        self.synDrumNameBox.setText(synDrumName)

    def toggleWatchFile(self, state):
        if not self.useWatchFile and state == Qt.Checked:
            self.storeCodeIfNotWatching = self.codeEditor.toPlainText()

        self.useWatchFile = (state == Qt.Checked)
        self.codeEditor.setEnabled(not self.useWatchFile)

        if self.useWatchFile:
            self.showWatchFileInfo()
        else:
            self.codeEditor.setPlainText(self.storeCodeIfNotWatching)

    def chooseWatchFile(self):
        dialogResult = QFileDialog.getOpenFileName(
            self, 'Choose file with GLSL code', '',
            'GLSL files (*.glsl);;All files (*)')
        print(dialogResult)
        self.watchFileNameBox.setText(dialogResult[0])
        self.watchFileCheckBox.setCheckState(Qt.Checked)
        self.shouldSave.emit()

    def setWatchFileName(self):
        self.watchFileName = self.watchFileNameBox.text()
        self.showWatchFileInfo()

    def showWatchFileInfo(self):
        if self.useWatchFile:
            fileInfo = QFileInfo(self.watchFileName)
            infoText = 'use code from file:\n' + self.watchFileName + '\n' + (
                '(exists)' if fileInfo.exists() else '(doesn\'t exist)')
            self.codeEditor.setPlainText(infoText)

    def pressRenderShader(self):
        self.playing = not self.playing
        if self.playing:
            self.renderShaderAndPlay()
        else:
            self.stopShader()

    def pressPauseButton(self):
        state = self.audiooutput.state()

        if state == QAudio.ActiveState:
            self.audiooutput.suspend()
        elif state == QAudio.SuspendedState:
            self.audiooutput.resume()

        self.updatePlayingUI(keepActive=True)

    def stopShader(self):
        self.audiooutput.stop()
        self.updatePlayingUI()

    def renderShaderAndPlay(self, file=None):
        self.playing = True
        self.updatePlayingUI()

        shaderSource = self.shaderHeader + """
uniform float SPB;
void main()
{
   float t = (iBlockOffset + gl_FragCoord.x + gl_FragCoord.y*iTexSize) / iSampleRate;
   t = floor(t*BITS.) / BITS.;
   vec2 s = .2 * vec2(sin(2.*3.14159*49.*t*(1.+t)*SPB*2.667)); // let's make it fun and squeaky
   vec2 v  = floor((0.5+0.5*s)*65535.0);
   vec2 vl = mod(v,256.0)/255.0;
   vec2 vh = floor(v/256.0)/255.0;
   gl_FragColor = vec4(vl.x,vh.x,vl.y,vh.y);
}
"""
        # this is the SUPER FUN BITCRUSHER for the test shader
        nr_bits = randint(128, 8192)
        shaderSource = shaderSource.replace('BITS', str(nr_bits))
        print(nr_bits, 'bits for the SUPER FUN BITCRUSHER in the test shader.')

        starttime = datetime.now()

        try:
            if self.useWatchFile:
                watchFile = QFile(self.watchFileName)
                if not watchFile.open(QFile.ReadOnly | QFile.Text):
                    QMessageBox.warning(
                        self, "Öhm... blöd.",
                        "File öffnen ging nicht. Is genügend Pfeffer drauf?")
                    self.playing = False
                    self.updatePlayingUI()
                    return
                textStream = QTextStream(watchFile)
                textStream.setCodec('utf-8')
                shaderSource = self.shaderHeader + textStream.readAll()

            else:
                code = self.codeEditor.toPlainText()
                if code:
                    shaderSource = code

        except:
            raise

        uniforms = {}

        SPB = 60 / float(self.renderBpmBox.value())
        uniforms.update({'SPB': SPB})

        print(self.renderLengthBox.value())

        try:
            duration = self.renderLengthBox.value()
        except:
            print('couldn\'t read duration field. take 10secs.')
            duration = 10

        glwidget = SFXGLWidget(self,
                               self.audioformat.sampleRate(),
                               duration,
                               self.texsize,
                               moreUniforms=uniforms)

        glwidget.show()
        log = glwidget.newShader(shaderSource)
        print(log)
        self.music = glwidget.music
        floatmusic = glwidget.floatmusic
        glwidget.hide()
        glwidget.destroy()

        if self.music == None:
            return

        self.renderLengthBox.setValue(round(glwidget.duration_real, 2) - .01)

        self.bytearray = QByteArray(self.music)

        self.audiobuffer = QBuffer(self.bytearray)
        self.audiobuffer.open(QIODevice.ReadOnly)

        endtime = datetime.now()
        el = endtime - starttime
        print("Compile time: {:.3f}s".format(el.total_seconds()))

        self.audiooutput.stop()
        self.audiooutput.start(self.audiobuffer)

        self.audiooutput.setNotifyInterval(100)
        self.audiooutput.stateChanged.connect(self.updatePlayingUI)

        self.progressBar.setMaximum(self.audiobuffer.size())
        self.audiooutput.notify.connect(self.proceedAudio)

        if file is not None:
            floatmusic_L = []
            floatmusic_R = []
            for n, sample in enumerate(floatmusic):
                if n % 2 == 0:
                    floatmusic_L.append(sample)
                else:
                    floatmusic_R.append(sample)
            floatmusic_stereo = np.transpose(
                np.array([floatmusic_L, floatmusic_R], dtype=np.float32))
            wavfile.write(file, self.samplerate, floatmusic_stereo)

    def proceedAudio(self):
        # print(self.audiobuffer.pos() / self.audioformat.sampleRate())
        self.progressBar.setValue(self.audiobuffer.pos())
        if self.audiobuffer.atEnd():
            self.audiooutput.stop()
            self.playing = False
        self.updatePlayingUI()

    def setVolume(self):
        self.audiooutput.setVolume(self.playbackVolumeSlider.value() * .01)

    def dumpInSynFile(self, drumatizeL, drumatizeR, envCode, releaseTime):
        if not self.synDumpCheckBox.isChecked():
            return
        if self.synDrumName == '':
            print("specify a valid drum name!!")
            return
        if self.synFileName == '':
            print("specify a valid .syn filename!!")
            return
        if not path.exists(self.synFileName):
            open(self.synFileName, 'a').close()

        uniqueEnv = f'_{self.synDrumName}ENV'
        drumatizeL = drumatizeL.replace('__ENV', uniqueEnv)
        drumatizeR = drumatizeR.replace('__ENV', uniqueEnv)
        envCode = envCode.replace('__ENV', uniqueEnv).replace('\n', ' ')
        parLine = f'param include src="{envCode}"\n'
        synLine = f'maindrum {self.synDrumName} src="{drumatizeL}" srcr="{drumatizeR}" release={releaseTime}\n'
        print(parLine, '\n', synLine)

        tmpSynFile = 'tmp.syn'
        copyfile(self.synFileName, tmpSynFile)
        parWritten, synWritten = False, False
        with open(tmpSynFile, 'r') as synFileHandle:
            synFileLines = synFileHandle.readlines()
        with open(self.synFileName, 'w') as synFileHandle:
            for line in synFileLines:
                parseLine = line.strip('\n').split()
                if parseLine[0:2] == ['maindrum', self.synDrumName]:
                    synFileHandle.write(synLine)
                    synWritten = True
                elif parseLine[0:2] == ['param', 'include'
                                        ] and line.find(uniqueEnv) != -1:
                    synFileHandle.write(parLine)
                    parWritten = True
                else:
                    synFileHandle.write(line)
            if not parWritten:
                synFileHandle.write('\n' + parLine)
            if not synWritten:
                synFileHandle.write('\n' + synLine)
            synFileHandle.close()
Ejemplo n.º 26
0
class AudioTest(QMainWindow):

    PUSH_MODE_LABEL = "Enable push mode"
    PULL_MODE_LABEL = "Enable pull mode"
    SUSPEND_LABEL = "Suspend playback"
    RESUME_LABEL = "Resume playback"

    DurationSeconds = 1
    ToneSampleRateHz = 600
    DataSampleRateHz = 44100

    def __init__(self):
        super(AudioTest, self).__init__()

        self.m_device = QAudioDeviceInfo.defaultOutputDevice()
        self.m_output = None

        self.initializeWindow()
        self.initializeAudio()

    def initializeWindow(self):
        layout = QVBoxLayout()

        self.m_deviceBox = QComboBox(activated=self.deviceChanged)
        for deviceInfo in QAudioDeviceInfo.availableDevices(QAudio.AudioOutput):
            self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)

        layout.addWidget(self.m_deviceBox)

        self.m_modeButton = QPushButton(clicked=self.toggleMode)
        self.m_modeButton.setText(self.PUSH_MODE_LABEL)

        layout.addWidget(self.m_modeButton)

        self.m_suspendResumeButton = QPushButton(
                clicked=self.toggleSuspendResume)
        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

        layout.addWidget(self.m_suspendResumeButton)

        volumeBox = QHBoxLayout()
        volumeLabel = QLabel("Volume:")
        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
                singleStep=10, valueChanged=self.volumeChanged)
        volumeBox.addWidget(volumeLabel)
        volumeBox.addWidget(self.m_volumeSlider)

        layout.addLayout(volumeBox)

        window = QWidget()
        window.setLayout(layout)

        self.setCentralWidget(window)

    def initializeAudio(self):
        self.m_pullTimer = QTimer(self, timeout=self.pullTimerExpired)
        self.m_pullMode = True

        self.m_format = QAudioFormat()
        self.m_format.setSampleRate(self.DataSampleRateHz)
        self.m_format.setChannelCount(1)
        self.m_format.setSampleSize(16)
        self.m_format.setCodec('audio/pcm')
        self.m_format.setByteOrder(QAudioFormat.LittleEndian)
        self.m_format.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.m_format):
            qWarning("Default format not supported - trying to use nearest")
            self.m_format = info.nearestFormat(self.m_format)

        self.m_generator = Generator(self.m_format,
                self.DurationSeconds * 1000000, self.ToneSampleRateHz, self)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)

    def deviceChanged(self, index):
        self.m_pullTimer.stop()
        self.m_generator.stop()
        self.m_audioOutput.stop()
        self.m_device = self.m_deviceBox.itemData(index)

        self.createAudioOutput()

    def volumeChanged(self, value):
        if self.m_audioOutput is not None:
            self.m_audioOutput.setVolume(value / 100.0)

    def notified(self):
        qWarning("bytesFree = %d, elapsedUSecs = %d, processedUSecs = %d" % (
                self.m_audioOutput.bytesFree(),
                self.m_audioOutput.elapsedUSecs(),
                self.m_audioOutput.processedUSecs()))

    def pullTimerExpired(self):
        if self.m_audioOutput is not None and self.m_audioOutput.state() != QAudio.StoppedState:
            chunks = self.m_audioOutput.bytesFree() // self.m_audioOutput.periodSize()
            for _ in range(chunks):
                data = self.m_generator.read(self.m_audioOutput.periodSize())
                if data is None or len(data) != self.m_audioOutput.periodSize():
                    break

                self.m_output.write(data)

    def toggleMode(self):
        self.m_pullTimer.stop()
        self.m_audioOutput.stop()

        if self.m_pullMode:
            self.m_modeButton.setText(self.PULL_MODE_LABEL)
            self.m_output = self.m_audioOutput.start()
            self.m_pullMode = False
            self.m_pullTimer.start(20)
        else:
            self.m_modeButton.setText(self.PUSH_MODE_LABEL)
            self.m_pullMode = True
            self.m_audioOutput.start(self.m_generator)

        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

    def toggleSuspendResume(self):
        if self.m_audioOutput.state() == QAudio.SuspendedState:
            qWarning("status: Suspended, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.ActiveState:
            qWarning("status: Active, suspend()")
            self.m_audioOutput.suspend()
            self.m_suspendResumeButton.setText(self.RESUME_LABEL)
        elif self.m_audioOutput.state() == QAudio.StoppedState:
            qWarning("status: Stopped, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.IdleState:
            qWarning("status: IdleState")

    stateMap = {
        QAudio.ActiveState: "ActiveState",
        QAudio.SuspendedState: "SuspendedState",
        QAudio.StoppedState: "StoppedState",
        QAudio.IdleState: "IdleState"}

    def handleStateChanged(self, state):
        qWarning("state = " + self.stateMap.get(state, "Unknown"))
Ejemplo n.º 27
0
class ToneGenerator(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        super().__init__(parent)

        # start new thread that constantly polls MIDI input
        self.create_MIDI()

        # create GUI (windows, slider, etc...)
        self.create_UI(parent)

        format = QAudioFormat()
        self.create_AUDIO(format)
        self.generator = Flute(format, self)
        self.generator.start()
        self.output.start(self.generator)


    def create_AUDIO(self, format):
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(
            QAudioFormat.LittleEndian
        )
        format.setSampleType(
            QAudioFormat.SignedInt
        )

        self.output = QAudioOutput(format, self)
        output_buffer_size = \
            int(2*SAMPLE_RATE \
                 *CTRL_INTERVAL/1000)
        self.output.setBufferSize(
            output_buffer_size
        )

    def create_MIDI(self):
        # Create the port reader object
        self.midiListener = MidiPortReader()

        # Create a thread which will read it
        self.listenerThread = QThread()

        # Take the object and move it
        # to the new thread (it isn't running yet)
        self.midiListener.moveToThread(self.listenerThread)

        # Tell Qt the function to call
        # when it starts the thread
        self.listenerThread.started.connect(self.midiListener.listener)

        # connect pyqtSignals to slots in ToneGenerator
        self.midiListener.newNoteFrequency.connect(self.on_newNoteFrequency)
        self.midiListener.newNoteVelocity.connect(self.on_newNoteVelocity)
        self.midiListener.portClosed.connect(self.on_portClosed)


        # Fingers in ears, eyes tight shut...
        self.listenerThread.start()

        # Good grief, IT WORKS!




    def create_UI(self, parent):
        # Create a slider to fine tune freq and two buttons
        #self.octaveBox = QSlider(Qt.Horizontal)
        #self.octaveBox.setMinimum(-100)
        #self.octaveBox.setMaximum(100)
        self.octaveBox = QSpinBox()
        self.octaveBox.setRange(-5,5)

        self.octaveLabel = QLabel("Octave: ")
        self.quitButton = QPushButton(self.tr('&Quit'))

        # create dropdown menu so user can choose midi device in use (populate list from data from midi listener object)
        self.MIDIMenu = QComboBox()
        self.MIDIMenu.addItems(self.midiListener.getMIDIDevices())

        # create ADSR sliders
        self.aSlider = QSlider(Qt.Vertical)
        self.aSlider.setMinimum(1)
        self.aSlider.setMaximum(50)
        self.aSlider.setSliderPosition(1)

        self.dSlider = QSlider(Qt.Vertical)
        self.dSlider.setMinimum(1)
        self.dSlider.setMaximum(50)
        self.dSlider.setSliderPosition(8)

        self.sSlider = QSlider(Qt.Vertical)
        self.sSlider.setMinimum(1)
        self.sSlider.setMaximum(100)
        self.sSlider.setSliderPosition(80)

        self.rSlider = QSlider(Qt.Vertical)
        self.rSlider.setMinimum(1)
        self.rSlider.setMaximum(100)
        self.rSlider.setSliderPosition(50)


        # No parent: we're going to add this
        # to vLayout.
        hLayout1 = QHBoxLayout()
        hLayout1.addWidget(self.octaveLabel)
        hLayout1.addWidget(self.octaveBox)

        hLayout2 = QHBoxLayout()
        hLayout2.addWidget(self.aSlider)
        hLayout2.addWidget(self.dSlider)
        hLayout2.addWidget(self.sSlider)
        hLayout2.addWidget(self.rSlider)

        hLayout3 = QHBoxLayout()
        hLayout3.addWidget(self.MIDIMenu)
        hLayout3.addWidget(self.quitButton)


        # parent = self: this is the
        # "top level" layout
        vLayout = QVBoxLayout(self)
        vLayout.addLayout(hLayout1)
        vLayout.addLayout(hLayout2)
        vLayout.addLayout(hLayout3)

        # connect qt object signals to slots

        self.quitButton.clicked.connect(self.quitClicked)
        self.octaveBox.valueChanged.connect(self.changeOctave)
        self.aSlider.valueChanged.connect(self.changeADSRParam)
        self.dSlider.valueChanged.connect(self.changeADSRParam)
        self.sSlider.valueChanged.connect(self.changeADSRParam)
        self.rSlider.valueChanged.connect(self.changeADSRParam)
        self.MIDIMenu.currentIndexChanged[str].connect(self.changeMIDIDevice)

    # this isn't a @pyqtSlot(), the text is passed directly to the function
    def changeMIDIDevice(self, dev):
        self.midiListener.setMIDIDevice(dev)


    @pyqtSlot()
    def quitClicked(self):
        self.close()

    @pyqtSlot()
    def changeOctave(self):
        #scale slider value to freq and pass to subclass to update filter
        self.generator.setOctave(self.octaveBox.value())

    @pyqtSlot(float)
    def on_newNoteFrequency(self, freq):
        # change value of filter cutoff so it sounds like we're playing a different note
        self.generator.updateFilter(freq)

    @pyqtSlot(int)
    def on_newNoteVelocity(self, value):
        # note_off message doesn't work with shit USB keyboards, so we simulate a note_off message with the velocity
        if value != 0:
            self.generator.playNote()
        else:
            self.generator.stopNote()

    @pyqtSlot()
    def on_portClosed(self):
        # when port has closed, clear drop down menu and rescan for MIDI devices
        self.MIDIMenu.clear()
        self.MIDIMenu.addItems(self.midiListener.getMIDIDevices())


    @pyqtSlot()
    def changeADSRParam(self):
        self.generator.updateADSR(self.aSlider.value()/10, self.dSlider.value()/10, self.sSlider.value()/100, self.rSlider.value()/10)
Ejemplo n.º 28
0
class Window(QWidget):
    volSlide=pyqtSignal(int)
    def __init__(self,parent=None):
        #UI
        QWidget.__init__(self,parent)
        self.create_UI(parent)
        #audio formatting
        format=QAudioFormat()
        format.setChannelCount(AUDIO_CHANS)
        format.setSampleRate(SAMPLE_RATE)
        format.setSampleSize(SAMPLE_SIZE)
        format.setCodec("audio/pcm")
        format.setByteOrder(
            QAudioFormat.LittleEndian
        )
        format.setSampleType(
            QAudioFormat.SignedInt
        )
        self.output=QAudioOutput(format,self)
        output_buffer_size=\
            int(2*SAMPLE_RATE \
                *CTRL_INTERVAL/1000)
        self.output.setBufferSize(
            output_buffer_size
        )
        self.generator=Generator(format,self)
        #THREADS
        self.midiListener=MidiPortReader()
        self.listenerThread=QThread()
        self.midiListener.moveToThread(
                self.listenerThread
        )
        self.listenerThread.started.connect(
                self.midiListener.listener
        )
        self.listenerThread.start()
        self.midiListener.noteOff.connect(self.generator.noteOff)
        self.midiListener.noteVelocity.connect(self.generator.noteVelocity)
        self.midiListener.noteOn.connect(self.generator.noteOn)
        self.volumeSlider.valueChanged.connect(self.generator.volSlide)
        self.qfacSlider.valueChanged.connect(self.generator.qFactor)
        self.generator.start()
        self.output.start(self.generator)

    def create_UI(self,parent):
        rockLabel=QLabel()
        rockLabel.setText("Let's ROCK!!!")
        volLabel=QLabel()
        volLabel.setText("Volume")
        self.volumeSlider=QSlider(Qt.Horizontal)
        self.volumeSlider.setMinimum(0)
        self.volumeSlider.setMaximum(100)
        qfacLabel=QLabel()
        qfacLabel.setText("Q-Factor")
        self.qfacSlider=QSlider(Qt.Horizontal)
        self.qfacSlider.setMinimum(0)
        self.qfacSlider.setMaximum(1000)
        self.qfacSlider.setValue(5000)
        self.quitButton=\
                QPushButton(self.tr('&Quit'))
        self.volSlide.emit(7)        
        self.lynch=QLabel()
        self.halfDown=QPushButton("+1")
        self.halfUp=QPushButton("-1")
        pixmap=QPixmap()
        pixmap.load('lynch2.jpeg')
        pixmap=pixmap.scaledToWidth(200)
        self.lynch.setPixmap(pixmap)
        vLayout=QVBoxLayout(self)
        h0Layout=QHBoxLayout()
        h0Layout.addWidget(rockLabel)
        h0Layout.addStretch(1)
        h0Layout.addWidget(self.lynch)
        vLayout.addLayout(h0Layout)
        hLayout=QHBoxLayout()
        hLayout.addWidget(volLabel)
        hLayout.addStretch(1)
        hLayout.addWidget(self.volumeSlider)
        vLayout.addLayout(hLayout)
        h2Layout=QHBoxLayout()
        h2Layout.addWidget(qfacLabel)
        h2Layout.addStretch(1)
        h2Layout.addWidget(self.qfacSlider)
        vLayout.addLayout(h2Layout)
        self.quitButton.clicked.connect(
                self.quitClicked
        )

    @pyqtSlot()
    def quitClicked(self):
        self.close()
Ejemplo n.º 29
0
class AudioWidget(QWidget):
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        self.format = None
        self.output = None
        self.buffer = QBuffer()

        self.volumeSlider = QSlider(Qt.Horizontal)
        self.volumeSlider.setMaximum(10)
        self.volumeSlider.setPageStep(1)
        self.volumeSlider.setValue(5)

        self.playButton = QPushButton()
        self.playButton.setIcon(QIcon("icons/play.png"))

        self.stopButton = QPushButton()
        self.stopButton.setIcon(QIcon("icons/stop.png"))

        self.volumeSlider.valueChanged.connect(self.change_volume)
        self.playButton.clicked.connect(self.play_pause)
        self.stopButton.clicked.connect(self.stop)

        layout = QHBoxLayout(self)
        layout.addWidget(self.playButton)
        layout.addWidget(self.stopButton)
        layout.addWidget(self.volumeSlider)
        layout.addStretch()

    def stop(self):
        if self.output:
            if self.output.state() != QAudio.StoppedState:
                self.output.stop()

    def set_data(self, mono_sig, sr):
        # if not self.format:
        self.format = QAudioFormat()
        self.format.setChannelCount(1)
        self.format.setSampleRate(sr)
        #numpy is in bites, qt in bits
        self.format.setSampleSize(mono_sig.dtype.itemsize * 8)
        self.format.setCodec("audio/pcm")
        self.format.setByteOrder(QAudioFormat.LittleEndian)
        self.format.setSampleType(QAudioFormat.Float)
        self.output = QAudioOutput(self.format, self)
        self.output.stateChanged.connect(self.audio_state_changed)
        #change the content without stopping playback
        p = self.buffer.pos()
        if self.buffer.isOpen():
            self.buffer.close()

        self.data = mono_sig.tobytes()
        self.buffer.setData(self.data)
        self.buffer.open(QIODevice.ReadWrite)
        self.buffer.seek(p)

    def audio_state_changed(self, new_state):
        #adjust the button icon
        if new_state != QAudio.ActiveState:
            self.playButton.setIcon(QIcon("icons/play.png"))
        else:
            self.playButton.setIcon(QIcon("icons/pause.png"))

    def cursor(self, t):
        #seek towards the time t
        #todo: handle EOF case
        try:
            if self.format:
                t = max(0, t)
                b = self.format.bytesForDuration(t * 1000000)
                self.buffer.seek(b)
        except:
            print("cursor error")

    def play_pause(self):
        if self.output:
            #(un)pause the audio output, keeps the buffer intact
            if self.output.state() == QAudio.ActiveState:
                self.output.suspend()
            elif self.output.state() == QAudio.SuspendedState:
                self.output.resume()
            else:
                self.buffer.seek(0)
                self.output.start(self.buffer)

    def change_volume(self, value):
        if self.output:
            #need to wrap this because slider gives not float output
            self.output.setVolume(value / 10)
Ejemplo n.º 30
0
class SleaZynth(QMainWindow):

    autoSaveFile = 'auto.save'

    texsize = 512
    samplerate = 44100

    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.show()

        self.initModelView()
        self.initSignals()

        self.initState()
        self.autoLoad()

        self.initAMaySyn()
        self.initAudio()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()

        elif event.key() == Qt.Key_F1:
            self.debugOutput()

        if event.modifiers() & Qt.ControlModifier:

            if event.key() == Qt.Key_S:
                self.autoSave()

            elif event.key() == Qt.Key_L:
                self.autoLoad()

            elif event.key() == Qt.Key_T:
                self.renderWhateverWasLast()

    def closeEvent(self, event):
        QApplication.quit()

    def initSignals(self):
        self.ui.btnChooseFilename.clicked.connect(self.loadAndImportMayson)
        self.ui.btnImport.clicked.connect(self.importMayson)
        self.ui.btnExport.clicked.connect(self.exportChangedMayson)
        self.ui.checkAutoReimport.clicked.connect(self.toggleAutoReimport)
        self.ui.checkAutoRender.clicked.connect(self.toggleAutoRender)

        self.ui.editFilename.editingFinished.connect(
            partial(self.updateStateFromUI, only='maysonFile'))
        self.ui.editBPM.editingFinished.connect(
            partial(self.updateStateFromUI, only='BPM'))
        self.ui.spinBOffset.valueChanged.connect(
            partial(self.updateStateFromUI, only='B_offset'))
        self.ui.spinBStop.valueChanged.connect(
            partial(self.updateStateFromUI, only='B_stop'))
        self.ui.spinLevelSyn.valueChanged.connect(
            partial(self.updateStateFromUI, only='level_syn'))
        self.ui.spinLevelDrum.valueChanged.connect(
            partial(self.updateStateFromUI, only='level_drum'))
        self.ui.checkWriteWAV.clicked.connect(
            partial(self.updateStateFromUI, only='writeWAV'))
        self.ui.spinTimeShift.valueChanged.connect(
            partial(self.updateStateFromUI, only='extraTimeShift'))

        self.ui.editTrackName.textChanged.connect(self.trackSetName)
        self.ui.spinTrackVolume.valueChanged.connect(self.trackSetVolume)
        self.ui.checkTrackMute.stateChanged.connect(self.trackSetMute)
        self.ui.spinModOn.valueChanged.connect(self.moduleSetModOn)
        self.ui.spinModTranspose.valueChanged.connect(self.moduleSetTranspose)
        self.ui.btnApplyPattern.clicked.connect(self.moduleSetPattern)
        self.ui.btnApplyNote.clicked.connect(self.noteApplyChanges)
        self.ui.btnTrackClone.clicked.connect(self.trackClone)
        self.ui.btnTrackDelete.clicked.connect(self.trackDelete)
        self.ui.btnRandomSynth.clicked.connect(self.trackSetRandomSynth)
        self.ui.btnRandomizeSynth.clicked.connect(self.synthRandomize)
        self.ui.btnSaveSynth.clicked.connect(self.synthHardClone)
        self.ui.btnApplySynthName.clicked.connect(self.synthChangeName)
        self.ui.btnReloadSyn.clicked.connect(self.loadSynthsFromSynFile)

        self.ui.btnRenderModule.clicked.connect(self.renderModule)
        self.ui.btnRenderTrack.clicked.connect(self.renderTrack)
        self.ui.btnRenderSong.clicked.connect(self.renderSong)
        self.ui.btnStopPlayback.clicked.connect(self.stopPlayback)

        # model/view signals
        self.ui.trackList.selectionModel().currentChanged.connect(
            self.trackLoad)
        self.ui.patternCombox.currentIndexChanged.connect(self.patternLoad)
        self.ui.moduleList.selectionModel().currentChanged.connect(
            self.moduleLoad)
        self.ui.synthList.selectionModel().currentChanged.connect(
            self.trackSetSynth)
        self.ui.noteList.selectionModel().currentChanged.connect(self.noteLoad)
        self.ui.drumList.selectionModel().currentChanged.connect(
            self.noteSetDrum)

    def initModelView(self):
        self.trackModel = TrackModel()
        self.ui.trackList.setModel(self.trackModel)
        self.moduleModel = ModuleModel()
        self.ui.moduleList.setModel(self.moduleModel)
        self.patternModel = PatternModel()
        self.ui.patternCombox.setModel(self.patternModel)
        self.noteModel = NoteModel()
        self.ui.noteList.setModel(self.noteModel)
        self.synthModel = QStringListModel()
        self.ui.synthList.setModel(self.synthModel)
        self.drumModel = QStringListModel()
        self.ui.drumList.setModel(self.drumModel)

        self.noteModel.dataChanged.connect(
            self.updateModulesWithChangedPattern)
        self.noteModel.reloadNoteParameters.connect(self.noteLoad)

    def initState(self):
        self.state = {
            'maysonFile': '',
            'autoReimport': False,
            'autoRender': False,
            'lastRendered': '',
            'writeWAV': False,
            'selectedTrack': 0,
            'selectedModule': 0,
            'extraTimeShift': 0,
        }
        self.info = {}
        self.patterns = []
        self.synths = []
        self.drumkit = []
        self.amaysyn = None
        self.fileObserver = None

    def loadAndImportMayson(self):
        name, _ = QFileDialog.getOpenFileName(
            self, 'Load MAYSON file', '', 'aMaySyn export *.mayson(*.mayson)')
        if name == '':
            return
        self.state['maysonFile'] = name
        self.state['title'], self.state[
            'synFile'] = self.getTitleAndSynFromMayson(name)
        self.autoSave()
        self.importMayson()

    def importMayson(self):
        maysonData = {}
        try:
            file = open(self.state['maysonFile'], 'r')
            maysonData = json.load(file)
        except FileNotFoundError:
            print(
                f"{self.state['maysonFile']} could not be imported. make sure that it exists, or choose another one."
            )
            self.loadAndImportMayson()
        except json.decoder.JSONDecodeError:
            print(
                f"{self.state['maysonFile']} is changing right now, pause for 1 sec..."
            )
            sleep(1)
            self.importMayson()
        finally:
            file.close()

        if maysonData == {}:
            return

        self.info = maysonData['info']
        self.info.update({'title': self.state['title']})
        if self.amaysyn is not None:
            self.amaysyn.updateState(info=self.info)

        self.trackModel.setTracks(maysonData['tracks'])
        self.patternModel.setPatterns(maysonData['patterns'])
        self.synthModel.setStringList(maysonData['synths'])
        self.drumModel.setStringList(maysonData['drumkit'])

        self.trackModel.layoutChanged.emit()
        if self.state['selectedTrack'] >= self.trackModel.rowCount():
            self.state['selectedTrack'] = 0
        self.selectIndex(self.ui.trackList, self.trackModel,
                         self.state['selectedTrack'])

        if self.state['selectedModule'] >= self.moduleModel.rowCount():
            self.state['selectedModule'] = 0
        self.selectIndex(self.ui.moduleList, self.moduleModel,
                         self.state['selectedModule'])

        self.noteModel.layoutChanged.emit()
        if self.noteModel.rowCount() > 0:
            self.selectIndex(self.ui.noteList, self.noteModel, 0)

        self.synthModel.layoutChanged.emit()

        self.drumModel.layoutChanged.emit()
        if self.drumIndex().isValid():
            self.selectIndex(self.ui.drumList, self.drumModel,
                             self.drumIndex().row())

        self.applyStateToUI()

    def exportChangedMayson(self):
        name, _ = QFileDialog.getSaveFileName(
            self, 'Export with Changes', self.state['maysonFile'],
            'aMaySyn export *.mayson(*.mayson)')
        if name == '':
            return
        data = {
            'info': self.info,
            'tracks': self.trackModel.tracks,
            'patterns': self.patternModel.patterns,
            'synths': self.synthModel.stringList(),
            'drumkit': self.drumModel.stringList(),
        }
        file = open(name, 'w')
        json.dump(data, file)
        file.close()

    def updateStateFromUI(self, only=None):
        if only is None or only == 'maysonFile':
            self.state.update({'maysonFile': self.ui.editFilename.text()})
        title, synFile = self.getTitleAndSynFromMayson(
            self.state['maysonFile'])
        self.state.update({'synFile': synFile})
        self.state.update({'title': title})
        self.info['title'] = title
        if only is None or only == 'BPM':
            self.info['BPM'] = self.ui.editBPM.text()
        if only is None or only == 'B_offset':
            self.info['B_offset'] = self.ui.spinBOffset.value()
        if only is None or only == 'B_stop':
            self.info['B_stop'] = self.ui.spinBStop.value()
        if only is None or only == 'level_syn':
            self.info['level_syn'] == self.ui.spinLevelSyn.value()
        if only is None or only == 'level_drum':
            self.info['level_drum'] == self.ui.spinLevelDrum.value()
        if only is None or only == 'writeWAV':
            self.state['writeWAV'] = self.ui.checkWriteWAV.isChecked()
        if only is None or only == 'extraTimeShift':
            self.state['extraTimeShift'] = self.ui.spinTimeShift.value()

        if self.amaysyn is not None:
            self.amaysyn.updateState(info=self.info, synFile=synFile)

    def applyStateToUI(self):
        self.ui.editFilename.setText(self.state['maysonFile'])
        # TODO: think about - do I want self.state['override']['BPM'] etc.??
        self.ui.editBPM.setText(self.info['BPM'])
        self.ui.spinBOffset.setValue(self.info['B_offset'])
        self.ui.spinBStop.setValue(self.info['B_stop'])
        self.ui.spinLevelSyn.setValue(self.info['level_syn'])
        self.ui.spinLevelDrum.setValue(self.info['level_drum'])
        self.ui.checkAutoReimport.setChecked(self.state['autoReimport'])
        self.ui.checkAutoRender.setChecked(self.state['autoRender'])
        self.ui.checkWriteWAV.setChecked(self.state['writeWAV'])
        self.ui.spinTimeShift.setValue(self.state['extraTimeShift'])

    def autoSave(self):
        file = open(self.autoSaveFile, 'w')
        json.dump(self.state, file)
        file.close()

    def autoLoad(self):
        loadState = {}
        try:
            file = open(self.autoSaveFile, 'r')
            loadState = json.load(file)
            file.close()
        except FileNotFoundError:
            pass

        for key in loadState:
            self.state[key] = loadState[key]

        if 'autoReimport' in self.state:
            self.toggleAutoReimport(self.state['autoReimport'])
        if 'maysonFile' not in self.state or self.state['maysonFile'] == '':
            self.loadAndImportMayson()
        else:
            self.importMayson()

    def toggleAutoRender(self, checked):
        self.state['autoRender'] = checked
        self.autoSave()

    def toggleAutoReimport(self, checked):
        self.state['autoReimport'] = checked
        self.autoSave()

        if self.fileObserver is not None:
            self.fileObserver.stop()
            self.fileObserver.join()
            self.fileObserver = None

        if checked:
            file = self.state['maysonFile']
            eventHandler = FileModifiedHandler(file)
            eventHandler.fileChanged.connect(
                self.importAndRender if self.state['autoRender'] else self.
                importMayson)
            self.fileObserver = Observer()
            self.fileObserver.schedule(eventHandler,
                                       path=path.dirname(file),
                                       recursive=False)
            self.fileObserver.start()

    def importAndRender(self):
        self.importMayson()
        if self.amaysyn is None:
            print(
                "You want to Reimport&Render, but why is aMaySyn not initialized? do some rendering first!"
            )
            return
        self.renderWhateverWasLast()

#################################### GENERAL HELPERS ###########################################

    def selectIndex(self, list, model, index):
        list.selectionModel().setCurrentIndex(
            model.createIndex(index, 0), QItemSelectionModel.SelectCurrent)

    def patternIndexOfName(self, name):
        patternNames = [p['name'] for p in self.patternModel.patterns]
        if name in patternNames:
            return patternNames.index(name)
        else:
            return None

    def getTitleAndSynFromMayson(self, maysonFile):
        synFile = '.'.join(maysonFile.split('.')[:-1]) + '.syn'
        title = '.'.join(path.basename(maysonFile).split('.')[:-1])
        return title, synFile

    def placeholder(self):
        print("FUNCTION NOT IMPLEMENTED. Sorrriiiiiiieee! (not sorry.)")

#################################### TRACK FUNCTIONALITY #######################################

    def track(self):
        return self.trackModel.tracks[self.trackIndex().row(
        )] if self.trackModel.rowCount() > 0 else None

    def trackIndex(self):
        return self.ui.trackList.currentIndex()

    def trackModelChanged(self):
        self.trackModel.dataChanged.emit(self.trackIndex(), self.trackIndex())

    def trackLoad(self, currentIndex):
        cTrack = self.trackModel.tracks[currentIndex.row()]
        self.ui.editTrackName.setText(cTrack['name'])
        self.ui.spinTrackVolume.setValue(100 * cTrack['par_norm'])
        self.ui.checkTrackMute.setChecked(not cTrack['mute'])
        self.moduleModel.setModules(cTrack['modules'])
        if len(cTrack['modules']) > 0:
            self.selectIndex(self.ui.moduleList, self.moduleModel,
                             cTrack['current_module'])
            self.moduleLoad()
        self.selectIndex(self.ui.synthList, self.synthModel,
                         cTrack['current_synth'])
        self.state['selectedTrack'] = currentIndex.row()

    def trackClone(self):
        self.trackModel.cloneRow(self.trackIndex().row())

    def trackDelete(self):
        self.trackModel.removeRow(self.trackIndex().row())

    def trackSetName(self, name):
        self.track()['name'] = name
        self.trackModelChanged()

    def trackSetVolume(self, value):
        self.track()['par_norm'] = round(value * .01, 3)
        self.trackModelChanged()

    def trackSetMute(self, state):
        self.track()['mute'] = (state != Qt.Checked)
        self.trackModelChanged()

    def trackSetSynth(self, index):
        self.track()['current_synth'] = self.synthModel.stringList().index(
            self.synthModel.data(index, Qt.DisplayRole))
        self.ui.editSynthName.setText(self.synthName())
        self.trackModelChanged()

        if self.synth()[0] == 'D':
            self.noteModel.useDrumkit(self.drumModel.stringList())
        else:
            self.noteModel.useDrumkit(None)

    def trackSetRandomSynth(self):
        randomIndex = self.synthModel.createIndex(
            randint(0,
                    len(self.instrumentSynths()) - 1), 0)
        self.ui.synthList.setCurrentIndex(randomIndex)

#################################### MODULE FUNCTIONALITY ######################################

    def module(self):
        return self.moduleModel.modules[self.moduleIndex().row(
        )] if self.moduleModel.rowCount() > 0 else None

    def moduleIndex(self):
        return self.ui.moduleList.currentIndex()

    def moduleModelChanged(self):
        self.moduleModel.dataChanged.emit(self.moduleIndex(),
                                          self.moduleIndex())

    def moduleLoad(self, currentIndex=None):
        if currentIndex is None:
            cModule = self.module()
        else:
            cModule = self.moduleModel.modules[currentIndex.row()]
            self.state['selectedModule'] = currentIndex.row()
        self.ui.patternCombox.setCurrentIndex(
            self.patternIndexOfName(cModule['pattern']['name']))
        self.ui.spinModOn.setValue(cModule['mod_on'])
        self.ui.spinModTranspose.setValue(cModule['transpose'])

    def moduleAssignPattern(self, pattern):
        self.module()['pattern'] = pattern  # deepcopy(pattern)

    def moduleSetPattern(self):
        self.moduleAssignPattern(self.pattern())
        self.moduleModelChanged()

    def moduleSetModOn(self, value):
        self.module()['mod_on'] = self.ui.spinModOn.value()
        self.moduleModelChanged()

    def moduleSetTranspose(self, value):
        self.module()['transpose'] = self.ui.spinModTranspose.value()
        self.moduleModelChanged()

#################################### PATTERN FUNCTIONALITY #####################################

    def pattern(self):
        return self.patternModel.patterns[
            self.patternIndex()] if self.patternModel.rowCount() > 0 else None

    def patternIndex(self):
        return self.ui.patternCombox.currentIndex()

    def patternLoad(self, currentIndex):
        cPattern = self.patternModel.patterns[currentIndex]
        self.noteModel.setNotes(cPattern['notes'])

    def updateModulesWithChangedPattern(self, rowBegin, rowEnd):
        self.moduleAssignPattern(self.pattern())
        self.moduleModelChanged()
        self.trackModel.updateModulesWithChangedPattern(self.pattern())
        self.trackModelChanged()

#################################### NOTE FUNCTIONALITY ########################################

    def note(self):
        return self.noteModel.notes[
            self.noteIndex().row()] if self.noteModel.rowCount() > 0 else None

    def noteIndex(self):
        return self.ui.noteList.currentIndex()

    def noteModelChanged(self):
        self.noteModel.dataChanged.emit(self.noteIndex(), self.noteIndex())

    def noteLoad(self, currentIndex):
        self.ui.editNote.setText(
            self.noteModel.data(currentIndex, Qt.DisplayRole))
        self.ui.editNote.setCursorPosition(0)

    def noteApplyChanges(self):
        self.noteModel.changeByString(self.noteIndex(),
                                      self.ui.editNote.text())

    def noteSetDrum(self, currentIndex):
        if self.drum() is not None:
            self.noteModel.changeDrumTo(self.noteIndex(), self.drum())

################################ SYNTH / DRUM FUNCTIONALITY ####################################

    def synth(self):
        return self.synthModel.data(self.ui.synthList.currentIndex(),
                                    Qt.DisplayRole)

    def synthName(self):
        return self.synth()[2:]

    def instrumentSynths(self):
        return [
            I_synth for I_synth in self.synthModel.stringList()
            if I_synth[0] == 'I'
        ]

    def drum(self):
        if not self.drumIndex():
            print("LOLOLOL DRUM INDEX IS NONE (should never happen)")
        if not self.drumIndex().isValid():
            print("LOLOLOL DRUM INDEX NOT VALID")
        return self.drumModel.data(self.drumIndex(), Qt.DisplayRole)

    def drumIndex(self):
        return self.ui.drumList.currentIndex()

    def synthRandomize(self):
        self.amaysyn.aMaySynatize(reshuffle_randoms=True)

    def synthHardClone(self):
        if self.synth()[0] == 'D':
            self.synthHardCloneDrum(self)
            return
        else:
            count = 0
            oldID = self.synthName()
            synths = self.instrumentSynths()
            while True:
                formID = oldID + '.' + str(count)
                print("TRYING", formID, synths)
                if 'I_' + formID not in synths: break
                count += 1

            try:
                formTemplate = next(
                    form for form in self.amaysyn.last_synatized_forms
                    if form['id'] == oldID)
                formType = formTemplate['type']
                formMode = formTemplate['mode']
                formBody = ' '.join(key + '=' + formTemplate[key]
                                    for key in formTemplate
                                    if key not in ['type', 'id', 'mode'])
                if formMode: formBody += ' mode=' + ','.join(formMode)
            except StopIteration:
                print(
                    "Current synth is not compiled yet. Do so and try again.")
                return
            except:
                print("could not CLONE HARD:", formID, formTemplate)
                raise
            else:
                with open(self.state['synFile'], mode='a') as filehandle:
                    filehandle.write('\n' + formType + 4 * ' ' + formID +
                                     4 * ' ' + formBody)
                self.loadSynthsFromSynFile()

    def synthHardDrum(self):
        print("NOT IMPLEMENTED YET")
        return
        count = 0
        oldID = self.synthName()
        synths = self.instrumentSynths()
        while True:
            formID = oldID + '.' + str(count)
            print("TRYING", formID, synths)
            if 'I_' + formID not in synths: break
            count += 1

        try:
            formTemplate = next(form
                                for form in self.amaysyn.last_synatized_forms
                                if form['id'] == oldID)
            formType = formTemplate['type']
            formMode = formTemplate['mode']
            formBody = ' '.join(key + '=' + formTemplate[key]
                                for key in formTemplate
                                if key not in ['type', 'id', 'mode'])
            if formMode: formBody += ' mode=' + ','.join(formMode)
        except StopIteration:
            print("Current synth is not compiled yet. Do so and try again.")
            return
        except:
            print("could not CLONE HARD:", formID, formTemplate)
            raise
        else:
            with open(self.state['synFile'], mode='a') as filehandle:
                filehandle.write('\n' + formType + 4 * ' ' + formID + 4 * ' ' +
                                 formBody)
            self.loadSynthsFromSynFile()

    def synthChangeName(self):
        if self.synth()[0] != 'I':
            print("Nah. Select an instrument synth (I_blabloo)")
            return
        newID = self.ui.editSynthName.text()
        if newID == '':
            return
        formID = self.synthName()
        tmpFile = self.state['synFile'] + '.tmp'
        move(self.state['synFile'], tmpFile)
        with open(tmpFile, mode='r') as tmp_handle:
            with open(self.state['synFile'], mode='w') as new_handle:
                for line in tmp_handle.readlines():
                    lineparse = line.split()
                    if len(lineparse) > 2 and lineparse[0] in [
                            'main', 'maindrum'
                    ] and lineparse[1] == formID:
                        new_handle.write(
                            line.replace(' ' + formID + ' ',
                                         ' ' + newID + ' '))
                    else:
                        new_handle.write(line)
        self.loadSynthsFromSynFile()

    def loadSynthsFromSynFile(self):
        self.amaysyn.aMaySynatize()
        self.synthModel.setStringList(self.amaysyn.synths)
        self.synthModel.dataChanged.emit(
            self.synthModel.createIndex(0, 0),
            self.synthModel.createIndex(self.synthModel.rowCount(), 0))
        self.drumModel.setStringList(self.amaysyn.drumkit)
        self.drumModel.dataChanged.emit(
            self.drumModel.createIndex(0, 0),
            self.drumModel.createIndex(self.drumModel.rowCount(), 0))
        self.trackModel.setSynthList(self.amaysyn.synths)
        self.trackModelChanged()

# TODO: function to change drumkit order / assignment?

######################################## SleaZYNTHesizer #######################################

    def initAMaySyn(self):
        self.amaysyn = aMaySynBuilder(self, self.state['synFile'], self.info)

    def initAudio(self):
        self.audioformat = QAudioFormat()
        self.audioformat.setSampleRate(self.samplerate)
        self.audioformat.setChannelCount(2)
        self.audioformat.setSampleSize(32)
        self.audioformat.setCodec('audio/pcm')
        self.audioformat.setByteOrder(QAudioFormat.LittleEndian)
        self.audioformat.setSampleType(QAudioFormat.Float)
        self.audiooutput = QAudioOutput(self.audioformat)
        self.audiooutput.setVolume(1.0)

    def stopPlayback(self):
        self.audiooutput.stop()

    def renderWhateverWasLast(self):
        if self.state['lastRendered'] == 'module':
            self.renderModule()
        elif self.state['lastRendered'] == 'track':
            self.renderTrack()
        else:
            self.renderSong()

    def renderModule(self):
        print(self.track(), self.module())
        self.state['lastRendered'] = 'module'
        restoreMute = self.track()['mute']
        self.track()['mute'] = False
        modInfo = deepcopy(self.info)
        modInfo['B_offset'] = self.module()['mod_on']
        modInfo['B_stop'] = self.module()['mod_on'] + self.module(
        )['pattern']['length']
        self.amaysyn.info = modInfo
        self.amaysyn.extra_time_shift = self.state['extraTimeShift']
        shader = self.amaysyn.build(tracks=[self.track()],
                                    patterns=[self.module()['pattern']])
        self.amaysyn.info = self.info
        self.track()['mute'] = restoreMute
        self.executeShader(shader)

    def renderTrack(self):
        self.state['lastRendered'] = 'track'
        restoreMute = self.track()['mute']
        self.track()['mute'] = False
        self.amaysyn.extra_time_shift = self.state['extraTimeShift']
        shader = self.amaysyn.build(tracks=[self.track()],
                                    patterns=self.patternModel.patterns)
        self.track()['mute'] = restoreMute
        self.executeShader(shader)

    def renderSong(self):
        self.state['lastRendered'] = 'song'
        self.amaysyn.extra_time_shift = self.state['extraTimeShift']
        shader = self.amaysyn.build(tracks=self.trackModel.tracks,
                                    patterns=self.patternModel.patterns)
        self.executeShader(shader)

    def executeShader(self, shader):
        self.ui.codeEditor.clear()
        self.ui.codeEditor.insertPlainText(
            shader.replace(4 * ' ', '\t').replace(3 * ' ', '\t'))
        self.ui.codeEditor.ensureCursorVisible()

        sequenceLength = len(
            self.amaysyn.sequence) if self.amaysyn.sequence is not None else 0
        if not self.amaysyn.useSequenceTexture and sequenceLength > pow(2, 14):
            QMessageBox.critical(
                self, "I CAN'T",
                f"Either switch to using the Sequence Texture (ask QM), or reduce the sequence size by limiting the offset/stop positions or muting tracks.\nCurrent sequence length is:\n{sequenceLength} > {pow(2,14)}"
            )
            return

        self.bytearray = self.amaysyn.executeShader(
            shader,
            self.samplerate,
            self.texsize,
            renderWAV=self.state['writeWAV'])
        self.audiobuffer = QBuffer(self.bytearray)
        self.audiobuffer.open(QIODevice.ReadOnly)
        self.audiooutput.stop()
        self.audiooutput.start(self.audiobuffer)

###################################### DEBUG STUFF #############################################

    def debugOutput(self):
        print("TRACKS:", self.trackModel.rowCount())
        print("===== TRACK ACCUMULATION =====")
        track_accumulate = 0
        for t in self.trackModel.tracks:
            delta = len(t['modules'])
            print(
                f"{t['name']:>20} {track_accumulate:>10} {track_accumulate + delta:>10}"
            )
            track_accumulate += delta
        print("END AT", track_accumulate)
        print()
        print("PATTERNS:", self.patternModel.rowCount())
        print("===== PATTERN ACCUMULATION =====")
        pattern_accumulate = 0
        for p in self.patternModel.patterns:
            delta = len(p['notes'])
            print(
                f"{p['name']:>20} {pattern_accumulate:>10} {pattern_accumulate + delta:>10}"
            )
            pattern_accumulate += delta
        print("END AT", pattern_accumulate)
        print()
Ejemplo n.º 31
0
class SideToneWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # Save link to main window
        self.main_window = parent
        # Get the status bar
        self.status_bar = parent.statusBar()
        # Slot that will point to a QAudioInput some day
        self.input_device = None
        # Slot that will point to a QAudioOutput in time
        self.otput_device = None
        # set up layout, creating:
        # self.input_info_list, list of QAudioInfo for inputs
        # self.cb_inputs, combox of input names in same order
        # self.otput_info_list, list of QAudioInfo for outputs
        # self.cb_otputs, combox of output names in same order
        # self.volume, volume slider
        # self.mute, mute checkbox
        self._uic()
        # Connect up signals to slots.
        # Changes in the comboxes go to in_device and ot_device
        self.cb_inputs.currentIndexChanged.connect(self.in_dev_change)
        self.cb_otputs.currentIndexChanged.connect(self.ot_dev_change)
        # Mute button goes to mute_change
        self.mute.stateChanged.connect(self.mute_change)
        # Change in volume goes to volume_change
        self.volume.valueChanged.connect(self.volume_change)
        # Start with the mute switch on. This triggers the above two signals.
        self.mute.setChecked(True)

    # Slot for any change in volume (or mute). If we have an output device
    # then convert level to a real and pass to the output device. If the new
    # level is 0, tell the device to stop; if nonzero, tell it to start.
    # Note we don't do anything about the input device level, it is always 1.0
    def volume_change(self, new_level):
        if self.otput_device:  # we have an output device
            self.otput_device.setVolume(self.volume.value() / 100)
            if new_level == 0:  # looks like a mute
                # tell the output device to stop just in case it
                # doesn't know about volume control.
                self.otput_device.stop()
            else:  # non-zero level, if the output is stopped, start it
                if self.otput_device.state() == QAudio.StoppedState:
                    self.otput_device.start()

    # Slot for mute switch. Note that any change to the volume slider
    # generates a signal to the volume_change slot.
    def mute_change(self, onoff):
        if onoff:
            # Mute has been clicked ON. Remember the current volume.
            # Turn the volume to zero.
            self.volume_level = self.volume.value()
            self.volume.setValue(0)
        else:
            # Mute has been clicked OFF. If we do not yet have input and
            # output devices, get them. Then reset the old volume level.
            if self.otput_device is None:
                # We are starting up and have no devices. Fake a call to
                # the checkbox-change entries thus creating devices.
                self.in_dev_change(self.cb_inputs.currentIndex())
                self.ot_dev_change(self.cb_inputs.currentIndex())
            self.volume.setValue(self.volume_level)

    # Slots for changes in the selection of the input- and output-device
    # combo boxes. On startup we have neither an input nor an output device.
    # We do not know which combox the user will fiddle with first. So either
    # has to assume that the other device may not yet exist.
    #
    # On a change of input choice: if we have an input device, get rid of it.
    # Create a new input device. Set its level to 1.0. If we
    # have an output, connect the two.

    def in_dev_change(self, new_index):
        if self.input_device:
            if self.otput_device:
                self.otput_device.stop()
            self.input_device.stop()
            self.input_device = None  # goodby object
        # Get the QAudioDeviceInfo corresponding to this index of the combox.
        audio_info = self.input_info_list[new_index]
        # Create a new QAudioInput based on that.
        preferred_format = audio_info.preferredFormat()
        self.input_device = QAudioInput(audio_info, preferred_format)
        self.input_device.setVolume(1.0)
        self.input_device.setBufferSize(384)
        # If we have an output device, redirect it to this input. This is
        # done by asking the input device for its QIODevice, and passing that
        # to the output device's start() method.
        if self.otput_device:
            self.input_device.start(self.otput_device.start())
            # self.otput_device.start( self.input_device.start() )

    # On a change in the selection of output choice: If we have an output
    # device, get rid of it. Create a new output device. If we have an input
    # device, connect the two. Set the output level from the volume slider.

    def ot_dev_change(self, new_index):
        if self.otput_device:
            if self.input_device:
                self.input_device.stop()
            self.otput_device.stop()
            self.otput_device = None
        audio_info = self.otput_info_list[new_index]
        preferred_format = audio_info.preferredFormat()
        self.otput_device = QAudioOutput(audio_info, preferred_format)
        self.otput_device.setVolume(self.volume.value() / 100)
        # self.otput_device.setBufferSize( 384 )
        if self.input_device:
            self.input_device.start(self.otput_device.start())
            # self.otput_device.start( self.input_device.start() )

    def _uic(self):
        """
    set up our layout which consists of:

                 Big Honkin' Label
        [input combobox]    [output combobox]
               [volume slider]  [x] Mute

    hooking put the signals to useful slots is the job
    of __init__. Here just make the layout.
        """
        self.setMinimumWidth(400)
        # Create the big honkin' label and logo
        icon_pixmap = QPixmap(":/icon.png").scaledToWidth(64)
        icon_label = QLabel()
        icon_label.setPixmap(icon_pixmap)
        text_label = QLabel("Sidetone!")
        hb_label = QHBoxLayout()
        hb_label.addStretch(1)
        hb_label.addWidget(icon_label, 0)
        hb_label.addWidget(text_label, 0)
        hb_label.addStretch(1)

        # Create a list of QAudioInfo objects for inputs
        self.input_info_list = QAudioDeviceInfo.availableDevices(QAudio.AudioInput)
        if 0 == len(self.input_info_list):
            self.input_info_list = [QAudioDeviceInfo.defaultInputDevice()]
        # Create a combo box and populate it with names of inputs
        self.cb_inputs = QComboBox()
        self.cb_inputs.addItems([audio_info.deviceName() for audio_info in self.input_info_list])
        # Create a list of QAudioInfo objects for outputs
        self.otput_info_list = QAudioDeviceInfo.availableDevices(QAudio.AudioOutput)
        if 0 == len(self.otput_info_list):
            self.otput_info_list = [QAudioDeviceInfo.defaultOutputDevice()]
        self.status_bar.showMessage(
            "{} inputs {} otputs".format(len(self.input_info_list), len(self.otput_info_list)), 2000
        )
        # Create a combo box and populate it with names of outputs
        self.cb_otputs = QComboBox()
        self.cb_otputs.addItems([audio_info.deviceName() for audio_info in self.otput_info_list])
        # Lay those two out aligned to the outside
        hb_combos = QHBoxLayout()
        hb_combos.addWidget(self.cb_inputs, 1)
        hb_combos.addStretch(0)
        hb_combos.addWidget(self.cb_otputs, 1)
        # Create a volume slider from 0 to 100.
        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimum(0)
        self.volume.setMaximum(100)
        self.volume.setTickInterval(10)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        # Create a checkbox "Mute"
        self.mute = QCheckBox("Mute")
        # Put those together in a row squeezed in the center
        hb_volume = QHBoxLayout()
        hb_volume.addStretch(1)
        hb_volume.addWidget(self.volume, 1)
        hb_volume.addWidget(self.mute, 0)
        hb_volume.addStretch(1)
        # Stack all those up as this widget's layout
        vlayout = QVBoxLayout()
        vlayout.addLayout(hb_label)
        vlayout.addLayout(hb_combos)
        vlayout.addLayout(hb_volume)
        self.setLayout(vlayout)
Ejemplo n.º 32
0
class Window(QWidget):
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)

        format = QAudioFormat()
        format.setChannelCount(1)
        format.setSampleRate(22050)
        format.setSampleSize(16)
        format.setCodec("audio/pcm")
        format.setByteOrder(QAudioFormat.LittleEndian)
        format.setSampleType(QAudioFormat.SignedInt)
        self.output = QAudioOutput(format, self)

        self.frequency = 440
        self.volume = 0
        self.buffer = QBuffer()
        self.data = QByteArray()

        self.deviceLineEdit = QLineEdit()
        self.deviceLineEdit.setReadOnly(True)
        self.deviceLineEdit.setText(
            QAudioDeviceInfo.defaultOutputDevice().deviceName())

        self.pitchSlider = QSlider(Qt.Horizontal)
        self.pitchSlider.setMaximum(100)
        self.volumeSlider = QSlider(Qt.Horizontal)
        self.volumeSlider.setMaximum(32767)
        self.volumeSlider.setPageStep(1024)

        self.playButton = QPushButton(self.tr("&Play"))

        self.pitchSlider.valueChanged.connect(self.changeFrequency)
        self.volumeSlider.valueChanged.connect(self.changeVolume)
        self.playButton.clicked.connect(self.play)

        formLayout = QFormLayout()
        formLayout.addRow(self.tr("Device:"), self.deviceLineEdit)
        formLayout.addRow(self.tr("P&itch:"), self.pitchSlider)
        formLayout.addRow(self.tr("&Volume:"), self.volumeSlider)

        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(self.playButton)
        buttonLayout.addStretch()

        horizontalLayout = QHBoxLayout(self)
        horizontalLayout.addLayout(formLayout)
        horizontalLayout.addLayout(buttonLayout)

        self.play()
        self.createData()

    def changeFrequency(self, value):

        self.frequency = 440 + (value * 2)
        self.createData()

    def play(self):

        if self.output.state() == QAudio.ActiveState:
            self.output.stop()

        if self.buffer.isOpen():
            self.buffer.close()

        if self.output.error() == QAudio.UnderrunError:
            self.output.reset()

        self.buffer.setData(self.data)
        self.buffer.open(QIODevice.ReadOnly)
        self.buffer.seek(0)

        self.output.start(self.buffer)

    def changeVolume(self, value):

        self.volume = value
        self.createData()

    def createData(self):

        self.data.clear()
        for i in range(2 * 22050):
            t = i / 22050.0
            value = int(self.volume * sin(2 * pi * self.frequency * t))
            self.data.append(struct.pack("<h", value))