from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * from MainWindow import Ui_MainWindow from solo_tool import SoloTool from midi_controller_launchpad_mini import MidiController POSITION_FACTOR = 100000 RATE_FACTOR = 10 UI_REFRESH_PERIOD_MS = 500 class PlaylistModel(QAbstractListModel): def __init__(self, soloTool, *args, **kwargs): super(PlaylistModel, self).__init__(*args, **kwargs) self.soloTool = soloTool def data(self, index, role): if role == Qt.DisplayRole: from pathlib import Path path = Path(self.soloTool.getSongs()[index.row()]) return path.name def rowCount(self, index): return len(self.soloTool.getSongs()) class ABListModel(QAbstractListModel): def __init__(self, soloTool, *args, **kwargs): super(ABListModel, self).__init__(*args, **kwargs) self.soloTool = soloTool def data(self, index, role): if role == Qt.DisplayRole: ab = self.soloTool.getStoredAbLimits()[index.row()] return f"{ab[0]} - {ab[1]}" def rowCount(self, index): return len(self.soloTool.getStoredAbLimits()) class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) self.timer = QTimer(self) self.timer.setInterval(UI_REFRESH_PERIOD_MS) self.timer.timeout.connect(self.timerCallback) self.soloTool = SoloTool() self.midiController = MidiController(self.soloTool) self.playlistModel = PlaylistModel(self.soloTool) self.songListView.setModel(self.playlistModel) self.songListView.selectionModel().selectionChanged.connect(self.playlistSelectionChanged) self.abListModel = ABListModel(self.soloTool) self.abListView.setModel(self.abListModel) self.abListView.selectionModel().selectionChanged.connect(self.abListSelectionChanged) self.songSlider.setMaximum(POSITION_FACTOR) self.songSlider.sliderPressed.connect(self.songSliderPressed) self.songSlider.sliderReleased.connect(self.songSliderReleased) self.aSlider.setMaximum(POSITION_FACTOR) self.aSlider.sliderReleased.connect(self.abSliderReleased) self.bSlider.setMaximum(POSITION_FACTOR) self.bSlider.sliderReleased.connect(self.abSliderReleased) self.rateSlider.setRange(int(0.5 * RATE_FACTOR), int(1.2 * RATE_FACTOR)) self.rateSlider.setSingleStep(int(0.1 * RATE_FACTOR)) self.rateSlider.setValue(int(1.0 * RATE_FACTOR)) self.rateSlider.sliderReleased.connect(self.rateSliderReleased) self.playButton.pressed.connect(self.soloTool.play) self.pauseButton.pressed.connect(self.soloTool.pause) self.storeAbButton.pressed.connect(self.storeAbLimits) self.setAButton.pressed.connect(self.setA) self.setBButton.pressed.connect(self.setB) self.saveSessionButton.pressed.connect(self.saveSession) self.loadSessionButton.pressed.connect(self.loadSession) self.addSongButton.pressed.connect(self.addSong) self.abRepeatCheckBox.clicked.connect(self.toggleAbRepeat) self.initMidiButton.pressed.connect(self.initMidi) self.timer.start() self.show() def timerCallback(self): position = self.soloTool.getPlaybackPosition() * POSITION_FACTOR self.songSlider.setValue(int(position)) self.soloTool.tick() def addSong(self): path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "mp3 Audio (*.mp3);FLAC audio (*.flac);All files (*.*)") if path: self.soloTool.addSong(path) self.playlistModel.layoutChanged.emit() def storeAbLimits(self): a = self.aSlider.value() / float(POSITION_FACTOR) b = self.bSlider.value() / float(POSITION_FACTOR) self.soloTool.storeAbLimits(a, b) self.abListModel.layoutChanged.emit() def setA(self): position = self.songSlider.value() self.aSlider.setValue(position) self.abSliderReleased() def setB(self): position = self.songSlider.value() self.bSlider.setValue(position) self.abSliderReleased() def toggleAbRepeat(self): enable = self.abRepeatCheckBox.isChecked() self.soloTool.setAbLimitEnable(enable) def saveSession(self): path, _ = QFileDialog.getSaveFileName(self, "Open file", "", "session file (*.json)") if path: self.soloTool.saveSession(path) def loadSession(self): path, _ = QFileDialog.getOpenFileName(self, "Open file", "", "session file (*.json)") if path: self.soloTool.loadSession(path) self.playlistModel.layoutChanged.emit() self.abListModel.layoutChanged.emit() def songSliderPressed(self): self.timer.stop() def songSliderReleased(self): position = self.songSlider.value() / float(POSITION_FACTOR) self.soloTool.setPlaybackPosition(position) self.timer.start() def clearAbListSelection(self): i = self.abListView.selectionModel().currentIndex() self.abListView.selectionModel().select(i, QItemSelectionModel.Deselect) def abSliderReleased(self): a = self.aSlider.value() / float(POSITION_FACTOR) b = self.bSlider.value() / float(POSITION_FACTOR) self.soloTool.setAbLimits(a, b) self.clearAbListSelection() def rateSliderReleased(self): rate = self.rateSlider.value() / float(RATE_FACTOR) self.soloTool.setPlaybackRate(rate) def playlistSelectionChanged(self, i): index = i.indexes()[0].row() self.soloTool.setSong(index) self.abListModel.layoutChanged.emit() self.clearAbListSelection() def abListSelectionChanged(self, i): if i is not None and not i.isEmpty(): index = i.indexes()[0].row() ab = self.soloTool.getStoredAbLimits()[index] self.soloTool.loadAbLimits(index) self.aSlider.setValue(int(ab[0] * POSITION_FACTOR)) self.bSlider.setValue(int(ab[1] * POSITION_FACTOR)) def initMidi(self): try: self.midiController.connect() except Exception as e: print("Error: could not connect to MIDI controller") print(e) if __name__ == '__main__': app = QApplication([]) app.setApplicationName("Solo Tool") app.setStyle("Fusion") # Fusion dark palette from https://gist.github.com/QuantumCD/6245215. palette = QPalette() palette.setColor(QPalette.Window, QColor(53, 53, 53)) palette.setColor(QPalette.WindowText, Qt.white) palette.setColor(QPalette.Base, QColor(25, 25, 25)) palette.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) palette.setColor(QPalette.ToolTipBase, Qt.white) palette.setColor(QPalette.ToolTipText, Qt.white) palette.setColor(QPalette.Text, Qt.white) palette.setColor(QPalette.Button, QColor(53, 53, 53)) palette.setColor(QPalette.ButtonText, Qt.white) palette.setColor(QPalette.BrightText, Qt.red) palette.setColor(QPalette.Link, QColor(42, 130, 218)) palette.setColor(QPalette.Highlight, QColor(42, 130, 218)) palette.setColor(QPalette.HighlightedText, Qt.black) app.setPalette(palette) app.setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }") window = MainWindow() app.exec_()