Browse Source

Add files

tags/1.9.4
falkTX 11 years ago
parent
commit
ca083b9a78
13 changed files with 2012 additions and 92 deletions
  1. +7
    -4
      .gitignore
  2. +61
    -0
      INSTALL.md
  3. +166
    -0
      Makefile
  4. +12
    -0
      README.md
  5. +9
    -0
      source/carla.kdev4
  6. +1098
    -0
      source/carla_backend.py
  7. +520
    -0
      source/carla_shared.py
  8. +1
    -1
      source/discovery/carla-discovery.cpp
  9. +9
    -3
      source/widgets/digitalpeakmeter.py
  10. +11
    -5
      source/widgets/ledbutton.py
  11. +26
    -1
      source/widgets/paramspinbox.py
  12. +49
    -43
      source/widgets/pixmapdial.py
  13. +43
    -35
      source/widgets/pixmapkeyboard.py

+ 7
- 4
.gitignore View File

@@ -2,6 +2,7 @@
.directory
.fuse-*
.*.kate-swp
.kdev_include_paths
.kdev4/

# Temp files
@@ -29,10 +30,12 @@ ui_*.h
# Python files
*.pyc
ui_*.py

# Resource files
src/resources_rc.py
qrc_resources*.cpp
source/digitalpeakmeter.py
source/ledbutton.py
source/paramspinbox.py
source/pixmapdial.py
source/pixmapkeyboard.py
source/resources_rc.py

# Binaries
carla-bridge-qtcreator


+ 61
- 0
INSTALL.md View File

@@ -0,0 +1,61 @@
# --- INSTALL for Carla ---

To install Carla, simply run as usual: <br/>
`$ make` <br/>
`$ [sudo] make install`

You can run it without installing, by using instead: <br/>
`$ make` <br/>
`$ python3 source/carla.py`

Packagers can make use of the 'PREFIX' and 'DESTDIR' variable during install, like this: <br/>
`$ make install PREFIX=/usr DESTDIR=./test-dir`

<br/>

===== BUILD DEPENDENCIES =====
--------------------------------
The required build dependencies are: <i>(devel packages of these)</i>

- JACK
- liblo
- Qt4
- PyQt4
- OpenGL

Optional but recommended:

- FluidSynth
- LinuxSampler

Optional for extended LV2 UIs support:

- Gtk2
- Gtk3
- Suil

Optional for native zynaddsubfx plugin:
- fftw3
- mxml

On Debian and Ubuntu, use these commands to install all dependencies: <br/>
`$ sudo apt-get install libjack-dev liblo-dev libqt4-dev libfluidsynth-dev qt4-dev-tools` <br/>
`$ sudo apt-get install libgtk2.0-dev libgtk-3-dev libsuil-dev` <br/>
`$ sudo apt-get install libfftw3-dev libmxml-dev` <br/>
`$ sudo apt-get install python3-pyqt4 pyqt4-dev-tools`

NOTE: linuxsampler is not packaged in either Debian or Ubuntu, but it's available in KXStudio. <br/>
<br/>

To run all the Carla-Control, you'll additionally need:

- python3-liblo

Optional but recommended:

- python3-rdflib (for LADSPA-RDF support)

The python version used and tested is python3.2. <br/>
After install, Carla will still work on distros with python2 as default, without any additional work.

<br/>

+ 166
- 0
Makefile View File

@@ -0,0 +1,166 @@
#!/usr/bin/make -f
# Makefile for Carla #
# ---------------------- #
# Created by falkTX
#

PREFIX = /usr/local
DESTDIR =

SED_PREFIX = $(shell echo $(PREFIX) | sed "s/\//\\\\\\\\\//g")

LINK = ln -s
PYUIC = pyuic4
PYRCC = pyrcc4 -py3


all: CPP RES UI WIDGETS


# ------------------------------------------------------------------------------------------------------------------------------------------------------

CPP: discovery

discovery:
$(MAKE) -C source/discovery

# ------------------------------------------------------------------------------------------------------------------------------------------------------

RES = source/resources_rc.py

RES: $(RES)

source/resources_rc.py: resources/resources.qrc
$(PYRCC) $< -o $@

# ------------------------------------------------------------------------------------------------------------------------------------------------------

UIs = source/ui_carla.py source/ui_carla_control.py\
source/ui_carla_about.py source/ui_carla_database.py source/ui_carla_edit.py source/ui_carla_parameter.py source/ui_carla_plugin.py source/ui_carla_refresh.py \
source/ui_inputdialog_value.py

UI: $(UIs)

source/ui_%.py: resources/ui/%.ui
$(PYUIC) $< -o $@

# ------------------------------------------------------------------------------------------------------------------------------------------------------

WIDGETS = source/digitalpeakmeter.py source/ledbutton.py source/paramspinbox.py source/pixmapdial.py source/pixmapkeyboard.py

WIDGETS: $(WIDGETS)

source/%.py: source/widgets/%.py
$(LINK) widgets/$*.py $@

# ------------------------------------------------------------------------------------------------------------------------------------------------------

debug:
$(MAKE) DEBUG=true

# doxygen:
# $(MAKE) doxygen -C source/backend

# ------------------------------------------------------------------------------------------------------------------------------------------------------

posix32:
$(MAKE) -C source/bridge posix32
$(MAKE) -C source/discovery posix32

posix64:
$(MAKE) -C source/bridge posix64
$(MAKE) -C source/discovery posix64

win32:
$(MAKE) -C source/bridge win32
$(MAKE) -C source/discovery win32

win64:
$(MAKE) -C source/bridge win64
$(MAKE) -C source/discovery win64

wine32:
$(MAKE) -C source/jackbridge wine32
$(LINK) source/libs/jackbridge/libcarla-jackbridge-win32.dll.so source/bridge/libcarla-jackbridge-win32.dll

wine64:
$(MAKE) -C source/jackbridge wine64
$(LINK) source/libs/jackbridge/libcarla-jackbridge-win64.dll.so source/bridge/libcarla-jackbridge-win64.dll

# ------------------------------------------------------------------------------------------------------------------------------------------------------

clean:
$(MAKE) clean -C source/discovery
rm -f $(RES)
rm -f $(UIs)
rm -f $(WIDGETS)
rm -f *~ source/*~ source/*.pyc
# rm -rf source/*/doxygen

install:
# Create directories
install -d $(DESTDIR)$(PREFIX)/bin/
install -d $(DESTDIR)$(PREFIX)/lib/carla/
install -d $(DESTDIR)$(PREFIX)/share/applications/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/256x256/apps/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/
install -d $(DESTDIR)$(PREFIX)/share/carla/

# Install script files and binaries
install -m 755 \
data/carla \
data/carla-control \
data/carla-standalone \
$(DESTDIR)$(PREFIX)/bin/

# Install desktop files
install -m 644 data/*.desktop $(DESTDIR)$(PREFIX)/share/applications/

# Install icons, 16x16
install -m 644 resources/16x16/carla.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/
install -m 644 resources/16x16/carla-control.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/

# Install icons, 48x48
install -m 644 resources/48x48/carla.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/
install -m 644 resources/48x48/carla-control.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/

# Install icons, 128x128
install -m 644 resources/128x128/carla.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/
install -m 644 resources/128x128/carla-control.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/

# Install icons, 256x256
install -m 644 resources/256x256/carla.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/256x256/apps/
install -m 644 resources/256x256/carla-control.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/256x256/apps/

# Install icons, scalable
install -m 644 resources/scalable/carla.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/
install -m 644 resources/scalable/carla-control.svg $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/

# Install main code
install -m 755 source/*.py $(DESTDIR)$(PREFIX)/share/carla/

install -m 755 \
source/backend/*.so \
source/bridge/carla-bridge-* \
source/discovery/carla-discovery-* \
$(DESTDIR)$(PREFIX)/lib/cadence/

# Adjust PREFIX value in script files
sed -i "s/X-PREFIX-X/$(SED_PREFIX)/" \
$(DESTDIR)$(PREFIX)/bin/carla \
$(DESTDIR)$(PREFIX)/bin/carla-control \
$(DESTDIR)$(PREFIX)/bin/carla-standalone \

uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/carla*
rm -f $(DESTDIR)$(PREFIX)/share/applications/carla.desktop
rm -f $(DESTDIR)$(PREFIX)/share/applications/carla-control.desktop
rm -f $(DESTDIR)$(PREFIX)/share/icons/hicolor/*/apps/carla.png
rm -f $(DESTDIR)$(PREFIX)/share/icons/hicolor/*/apps/carla-control.png
rm -f $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/carla.svg
rm -f $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/carla-control.svg
rm -rf $(DESTDIR)$(PREFIX)/lib/carla/
rm -rf $(DESTDIR)$(PREFIX)/share/carla/

+ 12
- 0
README.md View File

@@ -0,0 +1,12 @@
# --- README for Carla ---

Carla is an audio plugin host, with support for many audio drivers and plugin formats. <br/>
It's being developed by falkTX, using C++, Python3 and Qt4.

It has some nice features like automation of parameters via MIDI CCs (and send control outputs back to MIDI too) and full OSC control. <br/>
Currently supports LADSPA (including LRDF), DSSI, LV2, and VST plugin formats, with additional GIG, SF2 and SFZ file support via FluidSynth and LinuxSampler.
It uses JACK as the default and preferred audio driver, but also supports native system drivers by using RtAudio+RtMidi.

Carla-Control is an OSC Control GUI for Carla (you get the OSC address from the Carla's about dialog, and connect to it).<br/>
Supports controlling main UI components (Dry/Wet, Volume and Balance), and all plugins parameters. <br/>
Peak values and control outputs are displayed as well.

+ 9
- 0
source/carla.kdev4 View File

@@ -0,0 +1,9 @@
[Project]
Manager=KDevGenericManager
Name=Carla

[Filters]
Excludes=*/.*,*/*~,*/*.pyc,*/ui_*.py,*/__pycache__/

[Project]
VersionControlSupport=kdevgit

+ 1098
- 0
source/carla_backend.py
File diff suppressed because it is too large
View File


+ 520
- 0
source/carla_shared.py View File

@@ -0,0 +1,520 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Common Carla code
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

import os
#import platform
import sys
from codecs import open as codecopen
from copy import deepcopy
#from decimal import Decimal
from PyQt4.QtCore import qWarning
#pyqtSlot, qFatal, Qt, QSettings, QTimer
#from PyQt4.QtGui import QColor, QCursor, QDialog, QFontMetrics, QFrame, QGraphicsScene, QInputDialog, QLinearGradient, QMenu, QPainter, QPainterPath, QVBoxLayout, QWidget
#from PyQt4.QtXml import QDomDocument

# ------------------------------------------------------------------------------------------------------------
# Imports (Custom)

#import ui_carla_about
#import ui_carla_edit
#import ui_carla_parameter
#import ui_carla_plugin

# ------------------------------------------------------------------------------------------------------------
# Try Import Signal

try:
from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2
haveSignal = True
except:
haveSignal = False

# ------------------------------------------------------------------------------------------------------------
# Set Platform

if sys.platform == "darwin":
from PyQt4.QtGui import qt_mac_set_menubar_icons
qt_mac_set_menubar_icons(False)
HAIKU = False
LINUX = False
MACOS = True
WINDOWS = False
elif "haiku" in sys.platform:
HAIKU = True
LINUX = False
MACOS = False
WINDOWS = False
elif "linux" in sys.platform:
HAIKU = False
LINUX = True
MACOS = False
WINDOWS = False
elif sys.platform in ("win32", "win64", "cygwin"):
WINDIR = os.getenv("WINDIR")
HAIKU = False
LINUX = False
MACOS = False
WINDOWS = True
else:
HAIKU = False
LINUX = False
MACOS = False
WINDOWS = False

# ------------------------------------------------------------------------------------------------------------
# Set Version

VERSION = "0.5.0"

# ------------------------------------------------------------------------------------------------------------
# Set TMP

TMP = os.getenv("TMP")

if TMP is None:
if WINDOWS:
qWarning("TMP variable not set")
TMP = os.path.join(WINDIR, "temp")
else:
TMP = "/tmp"

# ------------------------------------------------------------------------------------------------------------
# Set HOME

HOME = os.getenv("HOME")

if HOME is None:
HOME = os.path.expanduser("~")

if LINUX or MACOS:
qWarning("HOME variable not set")

if not os.path.exists(HOME):
qWarning("HOME does not exist")
HOME = TMP

# ------------------------------------------------------------------------------------------------------------
# Set PATH

PATH = os.getenv("PATH")

if PATH is None:
qWarning("PATH variable not set")

if MACOS:
PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin")
elif WINDOWS:
PATH = (os.path.join(WINDIR, "system32"), WINDIR)
else:
PATH = ("/usr/local/bin", "/usr/bin", "/bin")

else:
PATH = PATH.split(os.pathsep)

# ------------------------------------------------------------------------------------------------------------
# 64bit check

is64bit = False #bool(platform.architecture()[0] == "64bit" and sys.maxsize > 2**32)

# ------------------------------------------------------------------------------------------------------------
# Check if a value is a number (float support)

def isNumber(value):
try:
float(value)
return True
except:
return False

# ------------------------------------------------------------------------------------------------------------
# Unicode open

def uopen(filename, mode="r"):
return codecopen(filename, encoding="utf-8", mode=mode)

# ------------------------------------------------------------------------------------------------
# Backend defines

MAX_DEFAULT_PLUGINS = 99
MAX_RACK_PLUGINS = 16
MAX_PATCHBAY_PLUGINS = 999
MAX_DEFAULT_PARAMETERS = 200

# Plugin Hints
PLUGIN_IS_BRIDGE = 0x001
PLUGIN_IS_RTSAFE = 0x002
PLUGIN_IS_SYNTH = 0x004
PLUGIN_HAS_GUI = 0x010
PLUGIN_USES_CHUNKS = 0x020
PLUGIN_USES_SINGLE_THREAD = 0x040
PLUGIN_CAN_DRYWET = 0x100
PLUGIN_CAN_VOLUME = 0x200
PLUGIN_CAN_BALANCE = 0x400
PLUGIN_CAN_FORCE_STEREO = 0x800

# Parameter Hints
PARAMETER_IS_BOOLEAN = 0x01
PARAMETER_IS_INTEGER = 0x02
PARAMETER_IS_LOGARITHMIC = 0x04
PARAMETER_IS_ENABLED = 0x08
PARAMETER_IS_AUTOMABLE = 0x10
PARAMETER_USES_SAMPLERATE = 0x20
PARAMETER_USES_SCALEPOINTS = 0x40
PARAMETER_USES_CUSTOM_TEXT = 0x80

# FIXME
# Custom Data types
CUSTOM_DATA_INVALID = None
CUSTOM_DATA_CHUNK = "http://kxstudio.sf.net/ns/carla/chunk"
CUSTOM_DATA_STRING = "http://kxstudio.sf.net/ns/carla/string"

# Binary Type
BINARY_NONE = 0
BINARY_POSIX32 = 1
BINARY_POSIX64 = 2
BINARY_WIN32 = 3
BINARY_WIN64 = 4
BINARY_OTHER = 5

# Plugin Type
PLUGIN_NONE = 0
PLUGIN_INTERNAL = 1
PLUGIN_LADSPA = 2
PLUGIN_DSSI = 3
PLUGIN_LV2 = 4
PLUGIN_VST = 5
PLUGIN_GIG = 6
PLUGIN_SF2 = 7
PLUGIN_SFZ = 8

# Plugin Category
PLUGIN_CATEGORY_NONE = 0
PLUGIN_CATEGORY_SYNTH = 1
PLUGIN_CATEGORY_DELAY = 2 # also Reverb
PLUGIN_CATEGORY_EQ = 3
PLUGIN_CATEGORY_FILTER = 4
PLUGIN_CATEGORY_DYNAMICS = 5 # Amplifier, Compressor, Gate
PLUGIN_CATEGORY_MODULATOR = 6 # Chorus, Flanger, Phaser
PLUGIN_CATEGORY_UTILITY = 7 # Analyzer, Converter, Mixer
PLUGIN_CATEGORY_OTHER = 8 # used to check if a plugin has a category

# Parameter Type
PARAMETER_UNKNOWN = 0
PARAMETER_INPUT = 1
PARAMETER_OUTPUT = 2
PARAMETER_LATENCY = 3
PARAMETER_SAMPLE_RATE = 4
PARAMETER_LV2_FREEWHEEL = 5
PARAMETER_LV2_TIME = 6

# Internal Parameters Index
PARAMETER_NULL = -1
PARAMETER_ACTIVE = -2
PARAMETER_DRYWET = -3
PARAMETER_VOLUME = -4
PARAMETER_BALANCE_LEFT = -5
PARAMETER_BALANCE_RIGHT = -6
PARAMETER_PANNING = -7

# Options Type
OPTION_PROCESS_NAME = 0
OPTION_PROCESS_MODE = 1
OPTION_PROCESS_HIGH_PRECISION = 2
OPTION_FORCE_STEREO = 3
OPTION_PREFER_PLUGIN_BRIDGES = 4
OPTION_PREFER_UI_BRIDGES = 5
OPTION_USE_DSSI_VST_CHUNKS = 6
OPTION_MAX_PARAMETERS = 7
OPTION_OSC_UI_TIMEOUT = 8
OPTION_PREFERRED_BUFFER_SIZE = 9
OPTION_PREFERRED_SAMPLE_RATE = 10
OPTION_PATH_BRIDGE_NATIVE = 11
OPTION_PATH_BRIDGE_POSIX32 = 12
OPTION_PATH_BRIDGE_POSIX64 = 13
OPTION_PATH_BRIDGE_WIN32 = 14
OPTION_PATH_BRIDGE_WIN64 = 15
OPTION_PATH_BRIDGE_LV2_GTK2 = 16
OPTION_PATH_BRIDGE_LV2_GTK3 = 17
OPTION_PATH_BRIDGE_LV2_QT4 = 18
OPTION_PATH_BRIDGE_LV2_QT5 = 19
OPTION_PATH_BRIDGE_LV2_COCOA = 20
OPTION_PATH_BRIDGE_LV2_WINDOWS = 21
OPTION_PATH_BRIDGE_LV2_X11 = 22
OPTION_PATH_BRIDGE_VST_COCOA = 23
OPTION_PATH_BRIDGE_VST_HWND = 24
OPTION_PATH_BRIDGE_VST_X11 = 25

# Callback Type
CALLBACK_DEBUG = 0
CALLBACK_PARAMETER_VALUE_CHANGED = 1
CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED = 2
CALLBACK_PARAMETER_MIDI_CC_CHANGED = 3
CALLBACK_PROGRAM_CHANGED = 4
CALLBACK_MIDI_PROGRAM_CHANGED = 5
CALLBACK_NOTE_ON = 6
CALLBACK_NOTE_OFF = 7
CALLBACK_SHOW_GUI = 8
CALLBACK_UPDATE = 9
CALLBACK_RELOAD_INFO = 10
CALLBACK_RELOAD_PARAMETERS = 11
CALLBACK_RELOAD_PROGRAMS = 12
CALLBACK_RELOAD_ALL = 13
CALLBACK_NSM_ANNOUNCE = 14
CALLBACK_NSM_OPEN1 = 15
CALLBACK_NSM_OPEN2 = 16
CALLBACK_NSM_SAVE = 17
CALLBACK_ERROR = 18
CALLBACK_QUIT = 19

# Process Mode Type
PROCESS_MODE_SINGLE_CLIENT = 0
PROCESS_MODE_MULTIPLE_CLIENTS = 1
PROCESS_MODE_CONTINUOUS_RACK = 2
PROCESS_MODE_PATCHBAY = 3

# Set BINARY_NATIVE
if HAIKU or LINUX or MACOS:
BINARY_NATIVE = BINARY_POSIX64 if is64bit else BINARY_POSIX32
elif WINDOWS:
BINARY_NATIVE = BINARY_WIN64 if is64bit else BINARY_WIN32
else:
BINARY_NATIVE = BINARY_OTHER

# ------------------------------------------------------------------------------------------------------------
# Carla Host object

class CarlaHostObject(object):
__slots__ = [
'host',
'gui',
'isControl',
'processMode',
'maxParameters'
]

Carla = CarlaHostObject()
Carla.host = None
Carla.gui = None
Carla.isControl = False
Carla.processMode = PROCESS_MODE_CONTINUOUS_RACK
Carla.maxParameters = MAX_RACK_PLUGINS

# ------------------------------------------------------------------------------------------------------------
# Carla GUI stuff

ICON_STATE_NULL = 0
ICON_STATE_WAIT = 1
ICON_STATE_OFF = 2
ICON_STATE_ON = 3

PALETTE_COLOR_NONE = 0
PALETTE_COLOR_WHITE = 1
PALETTE_COLOR_RED = 2
PALETTE_COLOR_GREEN = 3
PALETTE_COLOR_BLUE = 4
PALETTE_COLOR_YELLOW = 5
PALETTE_COLOR_ORANGE = 6
PALETTE_COLOR_BROWN = 7
PALETTE_COLOR_PINK = 8

CarlaStateParameter = {
'index': 0,
'name': "",
'symbol': "",
'value': 0.0,
'midiChannel': 1,
'midiCC': -1
}

CarlaStateCustomData = {
'type': CUSTOM_DATA_INVALID,
'key': "",
'value': ""
}

CarlaSaveState = {
'type': "",
'name': "",
'label': "",
'binary': "",
'uniqueId': 0,
'active': False,
'dryWet': 1.0,
'volume': 1.0,
'balanceLeft': -1.0,
'balanceRight': 1.0,
'pannning': 0.0,
'parameterList': [],
'currentProgramIndex': -1,
'currentProgramName': "",
'currentMidiBank': -1,
'currentMidiProgram': -1,
'customDataList': [],
'chunk': None
}

# ------------------------------------------------------------------------------------------------------------
# Carla XML helpers

def getSaveStateDictFromXML(xmlNode):
saveState = deepcopy(CarlaSaveState)

node = xmlNode.firstChild()

while not node.isNull():
# ------------------------------------------------------
# Info

if node.toElement().tagName() == "Info":
xmlInfo = node.toElement().firstChild()

while not xmlInfo.isNull():
tag = xmlInfo.toElement().tagName()
text = xmlInfo.toElement().text().strip()

if tag == "Type":
saveState["type"] = text
elif tag == "Name":
saveState["name"] = xmlSafeString(text, False)
elif tag in ("Label", "URI"):
saveState["label"] = xmlSafeString(text, False)
elif tag == "Binary":
saveState["binary"] = xmlSafeString(text, False)
elif tag == "UniqueID":
if text.isdigit(): saveState["uniqueId"] = int(text)

xmlInfo = xmlInfo.nextSibling()

# ------------------------------------------------------
# Data

elif node.toElement().tagName() == "Data":
xmlData = node.toElement().firstChild()

while not xmlData.isNull():
tag = xmlData.toElement().tagName()
text = xmlData.toElement().text().strip()

# ----------------------------------------------
# Internal Data

if tag == "Active":
saveState['active'] = bool(text == "Yes")
elif tag == "DryWet":
if isNumber(text): saveState["dryWet"] = float(text)
elif tag == "Volume":
if isNumber(text): saveState["volume"] = float(text)
elif tag == "Balance-Left":
if isNumber(text): saveState["balanceLeft"] = float(text)
elif tag == "Balance-Right":
if isNumber(text): saveState["balanceRight"] = float(text)
elif tag == "Panning":
if isNumber(text): saveState["pannning"] = float(text)

# ----------------------------------------------
# Program (current)

elif tag == "CurrentProgramIndex":
if text.isdigit(): saveState["currentProgramIndex"] = int(text)
elif tag == "CurrentProgramName":
saveState["currentProgramName"] = xmlSafeString(text, False)

# ----------------------------------------------
# Midi Program (current)

elif tag == "CurrentMidiBank":
if text.isdigit(): saveState["currentMidiBank"] = int(text)
elif tag == "CurrentMidiProgram":
if text.isdigit(): saveState["currentMidiProgram"] = int(text)

# ----------------------------------------------
# Parameters

elif tag == "Parameter":
stateParameter = deepcopy(CarlaStateParameter)

xmlSubData = xmlData.toElement().firstChild()

while not xmlSubData.isNull():
pTag = xmlSubData.toElement().tagName()
pText = xmlSubData.toElement().text().strip()

if pTag == "Index":
if pText.isdigit(): stateParameter["index"] = int(pText)
elif pTag == "Name":
stateParameter["name"] = xmlSafeString(pText, False)
elif pTag == "Symbol":
stateParameter["symbol"] = xmlSafeString(pText, False)
elif pTag == "Value":
if isNumber(pText): stateParameter["value"] = float(pText)
elif pTag == "MidiChannel":
if pText.isdigit(): stateParameter["midiChannel"] = int(pText)
elif pTag == "MidiCC":
if pText.isdigit(): stateParameter["midiCC"] = int(pText)

xmlSubData = xmlSubData.nextSibling()

saveState["parameterList"].append(stateParameter)

# ----------------------------------------------
# Custom Data

elif tag == "CustomData":
stateCustomData = deepcopy(CarlaStateCustomData)

xmlSubData = xmlData.toElement().firstChild()

while not xmlSubData.isNull():
cTag = xmlSubData.toElement().tagName()
cText = xmlSubData.toElement().text().strip()

if cTag == "Type":
stateCustomData["type"] = xmlSafeString(cText, False)
elif cTag == "Key":
stateCustomData["key"] = xmlSafeString(cText, False)
elif cTag == "Value":
stateCustomData["value"] = xmlSafeString(cText, False)

xmlSubData = xmlSubData.nextSibling()

saveState["customDataList"].append(stateCustomData)

# ----------------------------------------------
# Chunk

elif tag == "Chunk":
saveState["chunk"] = xmlSafeString(text, False)

# ----------------------------------------------

xmlData = xmlData.nextSibling()

# ------------------------------------------------------

node = node.nextSibling()

return saveState

def xmlSafeString(string, toXml):
if toXml:
return string.replace("&", "&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;")
else:
return string.replace("&amp;", "&").replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"")

+ 1
- 1
source/discovery/carla-discovery.cpp View File

@@ -188,7 +188,7 @@ intptr_t VSTCALLBACK vstHostCallback(AEffect* const effect, const int32_t opcode
break;

case audioMasterGetNumAutomatableParameters:
ret = carla_min<int32_t>(effect->numParams, MAX_PARAMETERS, 0);
ret = carla_min<int32_t>(effect->numParams, MAX_DEFAULT_PARAMETERS, 0);
break;

case audioMasterGetParameterQuantization:


+ 9
- 3
source/widgets/digitalpeakmeter.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Digital Peak Meter, a custom Qt4 widget
# Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,16 +11,20 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from PyQt4.QtCore import qCritical, Qt, QTimer, QSize
from PyQt4.QtGui import QColor, QLinearGradient, QPainter, QWidget

# ------------------------------------------------------------------------------------------------------------
# Widget Class

class DigitalPeakMeter(QWidget):
# enum Orientation
HORIZONTAL = 1
@@ -60,7 +64,7 @@ class DigitalPeakMeter(QWidget):
self.m_channelsData[meter-1] = level

def setChannels(self, channels):
if (channels < 0):
if channels < 0:
return qCritical("DigitalPeakMeter::setChannels(%i) - 'channels' must be a positive integer" % channels)

self.m_channels = channels
@@ -228,6 +232,8 @@ class DigitalPeakMeter(QWidget):
painter.setPen(QColor(110, 15, 15, 100))
painter.drawLine(2, lsmall - (lsmall * 0.96), lfull-2, lsmall - (lsmall * 0.96))

event.accept()

def resizeEvent(self, event):
self.updateSizes()
QWidget.resizeEvent(self, event)

+ 11
- 5
source/widgets/ledbutton.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Pixmap Button, a custom Qt4 widget
# Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,16 +11,20 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from PyQt4.QtCore import qCritical, QRectF
from PyQt4.QtGui import QPainter, QPixmap, QPushButton

# ------------------------------------------------------------------------------------------------------------
# Widget Class

class LEDButton(QPushButton):
BLUE = 1
GREEN = 2
@@ -32,7 +36,7 @@ class LEDButton(QPushButton):
QPushButton.__init__(self, parent)

self.m_pixmap = QPixmap()
self.m_pixmap_rect = QRectF(0, 0, 0, 0)
self.m_pixmapRect = QRectF(0, 0, 0, 0)

self.setCheckable(True)
self.setText("")
@@ -52,7 +56,7 @@ class LEDButton(QPushButton):
self.setPixmapSize(size)

def setPixmapSize(self, size):
self.m_pixmap_rect = QRectF(0, 0, size, size)
self.m_pixmapRect = QRectF(0, 0, size, size)

self.setMinimumWidth(size)
self.setMaximumWidth(size)
@@ -83,4 +87,6 @@ class LEDButton(QPushButton):
else:
return

painter.drawPixmap(self.m_pixmap_rect, self.m_pixmap, self.m_pixmap_rect)
painter.drawPixmap(self.m_pixmapRect, self.m_pixmap, self.m_pixmapRect)

event.accept()

+ 26
- 1
source/widgets/paramspinbox.py View File

@@ -1,13 +1,32 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Parameter SpinBox, a custom Qt4 widget
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from PyQt4.QtCore import pyqtSlot, Qt, QTimer, SIGNAL, SLOT
from PyQt4.QtGui import QAbstractSpinBox, QComboBox, QCursor, QDialog, QInputDialog, QMenu, QPainter, QProgressBar, QValidator
from PyQt4.QtGui import QStyleFactory
from math import isnan

# ------------------------------------------------------------------------------------------------------------
# Imports (Custom)

import ui_inputdialog_value

def fix_value(value, minimum, maximum):
@@ -25,7 +44,9 @@ def fix_value(value, minimum, maximum):

#QPlastiqueStyle = QStyleFactory.create("Plastique")

# ------------------------------------------------------------------------------------------------------------
# Custom InputDialog with Scale Points support

class CustomInputDialog(QDialog, ui_inputdialog_value.Ui_Dialog):
def __init__(self, parent, label, current, minimum, maximum, step, scalePoints):
QDialog.__init__(self, parent)
@@ -59,7 +80,9 @@ class CustomInputDialog(QDialog, ui_inputdialog_value.Ui_Dialog):
QDialog.done(self, r)
self.close()

# ------------------------------------------------------------------------------------------------------------
# Progress-Bar used for ParamSpinBox

class ParamProgressBar(QProgressBar):
def __init__(self, parent):
QProgressBar.__init__(self, parent)
@@ -138,7 +161,9 @@ class ParamProgressBar(QProgressBar):

QProgressBar.paintEvent(self, event)

# ------------------------------------------------------------------------------------------------------------
# Special SpinBox used for parameters

class ParamSpinBox(QAbstractSpinBox):
def __init__(self, parent):
QAbstractSpinBox.__init__(self, parent)
@@ -205,7 +230,7 @@ class ParamSpinBox(QAbstractSpinBox):
self._step = 0.001
else:
self._step = value
if self._step_small > value:
self._step_small = value
if self._step_large < value:


+ 49
- 43
source/widgets/pixmapdial.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Pixmap Dial, a custom Qt4 widget
# Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,17 +11,21 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from math import floor
from PyQt4.QtCore import Qt, QPointF, QRectF, QTimer, QSize, SLOT
from PyQt4.QtGui import QColor, QConicalGradient, QDial, QFontMetrics, QLinearGradient, QPainter, QPainterPath, QPen, QPixmap

# ------------------------------------------------------------------------------------------------------------
# Widget Class

class PixmapDial(QDial):
# enum Orientation
HORIZONTAL = 0
@@ -41,11 +45,11 @@ class PixmapDial(QDial):
QDial.__init__(self, parent)

self.m_pixmap = QPixmap(":/bitmaps/dial_01d.png")
self.m_pixmap_n_str = "01"
self.m_custom_paint = self.CUSTOM_PAINT_NULL
self.m_pixmapNum = "01"
self.m_customPaint = self.CUSTOM_PAINT_NULL

self.m_hovered = False
self.m_hover_step = self.HOVER_MIN
self.m_hovered = False
self.m_hoverStep = self.HOVER_MIN

if self.m_pixmap.width() > self.m_pixmap.height():
self.m_orientation = self.HORIZONTAL
@@ -53,10 +57,10 @@ class PixmapDial(QDial):
self.m_orientation = self.VERTICAL

self.m_label = ""
self.m_label_pos = QPointF(0.0, 0.0)
self.m_label_width = 0
self.m_label_height = 0
self.m_label_gradient = QLinearGradient(0, 0, 0, 1)
self.m_labelPos = QPointF(0.0, 0.0)
self.m_labelWidth = 0
self.m_labelHeight = 0
self.m_labelGradient = QLinearGradient(0, 0, 0, 1)

if self.palette().window().color().lightness() > 100:
# Light background
@@ -76,12 +80,12 @@ class PixmapDial(QDial):
return self.p_size

def setCustomPaint(self, paint):
self.m_custom_paint = paint
self.m_customPaint = paint
self.update()

def setEnabled(self, enabled):
if self.isEnabled() != enabled:
self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmap_n_str, "" if enabled else "d"))
self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmapNum, "" if enabled else "d"))
self.updateSizes()
self.update()
QDial.setEnabled(self, enabled)
@@ -89,25 +93,25 @@ class PixmapDial(QDial):
def setLabel(self, label):
self.m_label = label

self.m_label_width = QFontMetrics(self.font()).width(label)
self.m_label_height = QFontMetrics(self.font()).height()
self.m_labelWidth = QFontMetrics(self.font()).width(label)
self.m_labelHeight = QFontMetrics(self.font()).height()

self.m_label_pos.setX(float(self.p_size)/2 - float(self.m_label_width)/2)
self.m_label_pos.setY(self.p_size + self.m_label_height)
self.m_labelPos.setX(float(self.p_size)/2 - float(self.m_labelWidth)/2)
self.m_labelPos.setY(self.p_size + self.m_labelHeight)

self.m_label_gradient.setColorAt(0.0, self.m_color1)
self.m_label_gradient.setColorAt(0.6, self.m_color1)
self.m_label_gradient.setColorAt(1.0, self.m_color2)
self.m_labelGradient.setColorAt(0.0, self.m_color1)
self.m_labelGradient.setColorAt(0.6, self.m_color1)
self.m_labelGradient.setColorAt(1.0, self.m_color2)

self.m_label_gradient.setStart(0, float(self.p_size)/2)
self.m_label_gradient.setFinalStop(0, self.p_size + self.m_label_height + 5)
self.m_labelGradient.setStart(0, float(self.p_size)/2)
self.m_labelGradient.setFinalStop(0, self.p_size + self.m_labelHeight + 5)

self.m_label_gradient_rect = QRectF(float(self.p_size)/8, float(self.p_size)/2, float(self.p_size)*6/8, self.p_size+self.m_label_height+5)
self.m_labelGradient_rect = QRectF(float(self.p_size)/8, float(self.p_size)/2, float(self.p_size)*6/8, self.p_size+self.m_labelHeight+5)
self.update()

def setPixmap(self, pixmapId):
self.m_pixmap_n_str = "%02i" % pixmapId
self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmap_n_str, "" if self.isEnabled() else "d"))
self.m_pixmapNum = "%02i" % pixmapId
self.m_pixmap.load(":/bitmaps/dial_%s%s.png" % (self.m_pixmapNum, "" if self.isEnabled() else "d"))

if self.m_pixmap.width() > self.m_pixmap.height():
self.m_orientation = self.HORIZONTAL
@@ -140,19 +144,19 @@ class PixmapDial(QDial):
self.p_size = self.p_width
self.p_count = self.p_height / self.p_width

self.setMinimumSize(self.p_size, self.p_size + self.m_label_height + 5)
self.setMaximumSize(self.p_size, self.p_size + self.m_label_height + 5)
self.setMinimumSize(self.p_size, self.p_size + self.m_labelHeight + 5)
self.setMaximumSize(self.p_size, self.p_size + self.m_labelHeight + 5)

def enterEvent(self, event):
self.m_hovered = True
if self.m_hover_step == self.HOVER_MIN:
self.m_hover_step += 1
if self.m_hoverStep == self.HOVER_MIN:
self.m_hoverStep += 1
QDial.enterEvent(self, event)

def leaveEvent(self, event):
self.m_hovered = False
if self.m_hover_step == self.HOVER_MAX:
self.m_hover_step -= 1
if self.m_hoverStep == self.HOVER_MAX:
self.m_hoverStep -= 1
QDial.leaveEvent(self, event)

def paintEvent(self, event):
@@ -161,11 +165,11 @@ class PixmapDial(QDial):

if self.m_label:
painter.setPen(self.m_color2)
painter.setBrush(self.m_label_gradient)
painter.drawRect(self.m_label_gradient_rect)
painter.setBrush(self.m_labelGradient)
painter.drawRect(self.m_labelGradient_rect)

painter.setPen(self.m_colorT[0 if self.isEnabled() else 1])
painter.drawText(self.m_label_pos, self.m_label)
painter.drawText(self.m_labelPos, self.m_label)

if self.isEnabled():
current = float(self.value() - self.minimum())
@@ -190,10 +194,10 @@ class PixmapDial(QDial):
painter.drawPixmap(target, self.m_pixmap, source)

# Custom knobs (Dry/Wet and Volume)
if self.m_custom_paint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL):
if self.m_customPaint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL):
# knob color
colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.m_hover_step*7)
colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.m_hover_step*7)
colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.m_hoverStep*7)
colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.m_hoverStep*7)

# draw small circle
ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
@@ -208,7 +212,7 @@ class PixmapDial(QDial):
startAngle = 216*16
spanAngle = -252*16*value

if self.m_custom_paint == self.CUSTOM_PAINT_CARLA_WET:
if self.m_customPaint == self.CUSTOM_PAINT_CARLA_WET:
painter.setBrush(colorBlue)
painter.setPen(QPen(colorBlue, 0))
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
@@ -234,9 +238,9 @@ class PixmapDial(QDial):
painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)

# Custom knobs (L and R)
elif self.m_custom_paint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R):
elif self.m_customPaint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R):
# knob color
color = QColor(0xAD + self.m_hover_step*5, 0xD5 + self.m_hover_step*4, 0x4B + self.m_hover_step*5)
color = QColor(0xAD + self.m_hoverStep*5, 0xD5 + self.m_hoverStep*4, 0x4B + self.m_hoverStep*5)

# draw small circle
ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
@@ -252,10 +256,10 @@ class PixmapDial(QDial):
painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))

# draw arc
if self.m_custom_paint == self.CUSTOM_PAINT_CARLA_L:
if self.m_customPaint == self.CUSTOM_PAINT_CARLA_L:
startAngle = 216*16
spanAngle = -252.0*16*value
elif self.m_custom_paint == self.CUSTOM_PAINT_CARLA_R:
elif self.m_customPaint == self.CUSTOM_PAINT_CARLA_R:
startAngle = 324.0*16
spanAngle = 252.0*16*(1.0-value)
else:
@@ -264,14 +268,16 @@ class PixmapDial(QDial):
painter.setPen(QPen(color, 2))
painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle)

if self.HOVER_MIN < self.m_hover_step < self.HOVER_MAX:
self.m_hover_step += 1 if self.m_hovered else -1
if self.HOVER_MIN < self.m_hoverStep < self.HOVER_MAX:
self.m_hoverStep += 1 if self.m_hovered else -1
QTimer.singleShot(20, self, SLOT("update()"))

else:
target = QRectF(0.0, 0.0, self.p_size, self.p_size)
painter.drawPixmap(target, self.m_pixmap, target)

event.accept()

def resizeEvent(self, event):
self.updateSizes()
QDial.resizeEvent(self, event)

+ 43
- 35
source/widgets/pixmapkeyboard.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Pixmap Keyboard, a custom Qt4 widget
# Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,15 +11,19 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# ------------------------------------------------------------------------------------------------------------
# Imports (Global)

from PyQt4.QtCore import pyqtSlot, qCritical, Qt, QPointF, QRectF, QTimer, SIGNAL, SLOT
from PyQt4.QtGui import QFont, QPainter, QPixmap, QWidget

# ------------------------------------------------------------------------------------------------------------

midi_key2rect_map_horizontal = {
'0': QRectF(0, 0, 18, 64), # C
'1': QRectF(13, 0, 11, 42), # C#
@@ -79,7 +83,9 @@ midi_keyboard2key_map = {
'%i' % Qt.Key_U: 71,
}

# ------------------------------------------------------------------------------------------------------------
# MIDI Keyboard, using a pixmap for painting

class PixmapKeyboard(QWidget):
# enum Color
COLOR_CLASSIC = 0
@@ -146,15 +152,15 @@ class PixmapKeyboard(QWidget):
return self.setMode(mode)

if mode == self.HORIZONTAL:
self.m_midi_map = midi_key2rect_map_horizontal
self.m_midiMap = midi_key2rect_map_horizontal
self.m_pixmap.load(":/bitmaps/kbd_h_%s.png" % self.m_colorStr)
self.m_pixmap_mode = self.HORIZONTAL
self.m_pixmapMode = self.HORIZONTAL
self.p_width = self.m_pixmap.width()
self.p_height = self.m_pixmap.height() / 2
elif mode == self.VERTICAL:
self.m_midi_map = midi_key2rect_map_vertical
self.m_midiMap = midi_key2rect_map_vertical
self.m_pixmap.load(":/bitmaps/kbd_v_%s.png" % self.m_colorStr)
self.m_pixmap_mode = self.VERTICAL
self.m_pixmapMode = self.VERTICAL
self.p_width = self.m_pixmap.width() / 2
self.p_height = self.m_pixmap.height()
else:
@@ -170,23 +176,23 @@ class PixmapKeyboard(QWidget):
octaves = 8
self.m_octaves = octaves

if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
self.setMinimumSize(self.p_width * self.m_octaves, self.p_height)
self.setMaximumSize(self.p_width * self.m_octaves, self.p_height)
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
self.setMinimumSize(self.p_width, self.p_height * self.m_octaves)
self.setMaximumSize(self.p_width, self.p_height * self.m_octaves)

self.update()

def handleMousePos(self, pos):
if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
if pos.x() < 0 or pos.x() > self.m_octaves * 144:
return
posX = pos.x() - 1
octave = int(posX / self.p_width)
n_pos = QPointF(posX % self.p_width, pos.y())
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
if pos.y() < 0 or pos.y() > self.m_octaves * 144:
return
posY = pos.y() - 1
@@ -197,29 +203,29 @@ class PixmapKeyboard(QWidget):

octave += 3

if self.m_midi_map['1'].contains(n_pos): # C#
if self.m_midiMap['1'].contains(n_pos): # C#
note = 1
elif self.m_midi_map['3'].contains(n_pos): # D#
elif self.m_midiMap['3'].contains(n_pos): # D#
note = 3
elif self.m_midi_map['6'].contains(n_pos): # F#
elif self.m_midiMap['6'].contains(n_pos): # F#
note = 6
elif self.m_midi_map['8'].contains(n_pos): # G#
elif self.m_midiMap['8'].contains(n_pos): # G#
note = 8
elif self.m_midi_map['10'].contains(n_pos):# A#
elif self.m_midiMap['10'].contains(n_pos):# A#
note = 10
elif self.m_midi_map['0'].contains(n_pos): # C
elif self.m_midiMap['0'].contains(n_pos): # C
note = 0
elif self.m_midi_map['2'].contains(n_pos): # D
elif self.m_midiMap['2'].contains(n_pos): # D
note = 2
elif self.m_midi_map['4'].contains(n_pos): # E
elif self.m_midiMap['4'].contains(n_pos): # E
note = 4
elif self.m_midi_map['5'].contains(n_pos): # F
elif self.m_midiMap['5'].contains(n_pos): # F
note = 5
elif self.m_midi_map['7'].contains(n_pos): # G
elif self.m_midiMap['7'].contains(n_pos): # G
note = 7
elif self.m_midi_map['9'].contains(n_pos): # A
elif self.m_midiMap['9'].contains(n_pos): # A
note = 9
elif self.m_midi_map['11'].contains(n_pos):# B
elif self.m_midiMap['11'].contains(n_pos):# B
note = 11
else:
note = -1
@@ -271,9 +277,9 @@ class PixmapKeyboard(QWidget):
# Paint clean keys (as background)

for octave in range(self.m_octaves):
if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
target = QRectF(self.p_width * octave, 0, self.p_width, self.p_height)
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
target = QRectF(0, self.p_height * octave, self.p_width, self.p_height)
else:
return
@@ -316,13 +322,13 @@ class PixmapKeyboard(QWidget):
# cannot paint this note either
continue

if self.m_pixmap_mode == self.VERTICAL:
if self.m_pixmapMode == self.VERTICAL:
octave = self.m_octaves - octave - 1

if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height())
source = QRectF(pos.x(), self.p_height, pos.width(), pos.height())
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height())
source = QRectF(self.p_width, pos.y(), pos.width(), pos.height())
else:
@@ -338,10 +344,10 @@ class PixmapKeyboard(QWidget):
for octave in range(self.m_octaves):
for note in (1, 3, 6, 8, 10):
pos = self._getRectFromMidiNote(note)
if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height())
source = QRectF(pos.x(), 0, pos.width(), pos.height())
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height())
source = QRectF(0, pos.y(), pos.width(), pos.height())
else:
@@ -382,13 +388,13 @@ class PixmapKeyboard(QWidget):
# cannot paint this note either
continue

if self.m_pixmap_mode == self.VERTICAL:
if self.m_pixmapMode == self.VERTICAL:
octave = self.m_octaves - octave - 1

if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
target = QRectF(pos.x() + (self.p_width * octave), 0, pos.width(), pos.height())
source = QRectF(pos.x(), self.p_height, pos.width(), pos.height())
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
target = QRectF(pos.x(), pos.y() + (self.p_height * octave), pos.width(), pos.height())
source = QRectF(self.p_width, pos.y(), pos.width(), pos.height())
else:
@@ -401,11 +407,13 @@ class PixmapKeyboard(QWidget):
painter.setPen(Qt.black)

for i in range(self.m_octaves):
if self.m_pixmap_mode == self.HORIZONTAL:
if self.m_pixmapMode == self.HORIZONTAL:
painter.drawText(i * 144, 48, 18, 18, Qt.AlignCenter, "C%i" % int(i + 2))
elif self.m_pixmap_mode == self.VERTICAL:
elif self.m_pixmapMode == self.VERTICAL:
painter.drawText(45, (self.m_octaves * 144) - (i * 144) - 16, 18, 18, Qt.AlignCenter, "C%i" % int(i + 2))

event.accept()

@pyqtSlot()
def slot_updateOnce(self):
if self.m_needsUpdate:
@@ -417,4 +425,4 @@ class PixmapKeyboard(QWidget):
return bool(baseNote in (1, 3, 6, 8, 10))

def _getRectFromMidiNote(self, note):
return self.m_midi_map.get(str(note % 12))
return self.m_midiMap.get(str(note % 12))

Loading…
Cancel
Save