Rename everything to Stemify, and add a README

- Python package stemroller/ -> stemify/, Android package com.stemroller.mobile ->
  com.stemify.mobile, app names, QML module, icons, desktop entries, MPRIS and Plex
  product names
- Done-markers are now .stemify.json; stemd renames the old ones once at startup so
  songs separated before the rename aren't separated again
- A top-level README covering the server, the Android and Android Auto app, the EQ,
  Stem Now and the desktop apps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClericalErrorandClaude Opus 5.5 committed 2026-10-14 21:24:56 -07:00
1 parent 1845f03c41
commit 45bc0eefe0
159 files changed
+1136 -542

No files matched your search

+147
View File
@@ -0,0 +1,147 @@
import QtQuick
import QtQuick.Controls.Basic
// One lane per stem: color strip, solo / mute / gain / export, waveform texture.
Item {
id: lane
required property int index
required property var modelData
property string jobId: ""
property real viewStart: 0
property real viewEnd: 1
property int waveW: 100
property int waveH: 60
property real waveX: 0
property bool anySolo: false
property bool solo: false
property bool mute: false
property real gainDb: 0
signal soloToggled(bool on)
readonly property string stem: modelData.name
readonly property color tone: modelData.color
readonly property bool audible: anySolo ? solo : !mute
height: 92
Card {
anchors.fill: parent
clip: false
// color strip with soft glow
Rectangle {
x: 0
width: 4
height: parent.height
topLeftRadius: 8
bottomLeftRadius: 8
color: lane.tone
}
Rectangle {
x: 4
width: 24
height: parent.height
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0; color: Theme.alpha(lane.tone, 0.14) }
GradientStop { position: 1; color: Theme.alpha(lane.tone, 0.0) }
}
}
Column {
id: controls
x: 18
width: lane.waveX - 30
anchors.verticalCenter: parent.verticalCenter
spacing: 6
Row {
spacing: 6
width: parent.width
Txt {
width: parent.width - 3 * 30 - 12
text: lane.stem.charAt(0).toUpperCase() + lane.stem.slice(1)
font.pixelSize: Theme.fs16
font.weight: Font.DemiBold
anchors.verticalCenter: parent.verticalCenter
}
IconButton {
size: 30; iconSize: 16
iconName: "headphones"
checkable: true
checked: lane.solo
activeColor: Theme.warning
tip: "Solo"
onClicked: lane.soloToggled(checked)
}
IconButton {
size: 30; iconSize: 16
iconName: lane.mute ? "volume_off" : "volume"
checkable: true
checked: lane.mute
activeColor: Theme.error
tip: "Mute"
onClicked: { lane.mute = checked; audio.setMute(lane.index, checked) }
}
IconButton {
size: 30; iconSize: 16
iconName: "download"
tip: "Export " + lane.stem
onClicked: app.exportStem(lane.stem)
}
}
Row {
spacing: 8
width: parent.width
AeroSlider {
id: gain
width: parent.width - 52
from: -24
to: 12
stepSize: 0.5
value: lane.gainDb
tone: lane.tone
onMoved: { lane.gainDb = value; audio.setGain(lane.index, value) }
// double-click resets to unity
TapHandler { onDoubleTapped: { lane.gainDb = 0; audio.setGain(lane.index, 0) } }
}
Txt {
width: 44
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignRight
text: (lane.gainDb > 0 ? "+" : "") + lane.gainDb.toFixed(1) + " dB"
font.pixelSize: 11
font.features: { "tnum": 1 }
color: lane.gainDb === 0 ? Theme.textLo : Theme.textHi
}
}
}
Rectangle { // waveform well
x: lane.waveX
y: 8
width: lane.waveW
height: parent.height - 16
radius: 6
color: Qt.rgba(0, 0, 0, 0.22)
border.color: Qt.rgba(1, 1, 1, 0.04)
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: parent.width
height: 1
color: Theme.alpha(lane.tone, 0.18)
}
Image {
anchors.fill: parent
asynchronous: true
retainWhileLoading: true
cache: false
smooth: true
opacity: lane.audible ? 1 : 0.28
Behavior on opacity { NumberAnimation { duration: Theme.med } }
source: lane.jobId && lane.waveW > 4
? "image://wave/" + lane.jobId + "/" + lane.stem + "/"
+ Math.round(lane.waveW * Screen.devicePixelRatio) + "x" + Math.round((lane.height - 16) * Screen.devicePixelRatio)
+ "/" + lane.viewStart.toFixed(3) + "/" + lane.viewEnd.toFixed(3) + "/" + Theme.hex(lane.tone)
: ""
}
}
}
}