Skip to content

Commit

Permalink
Karaoke mode preference impl
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-venugopal committed Jan 5, 2025
1 parent 19c5a42 commit 5a6ddfc
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 67 deletions.
Binary file not shown.
13 changes: 8 additions & 5 deletions Source/Core/Messaging/Messenger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,17 @@ class Messenger {
///
/// Unsubscribes the client from notifications with the given notification name.
///
/// - Parameter notifName: The name of the notification the client wishes to unsubscribe from.
/// - Parameter notifNames: The names of the notifications the client wishes to unsubscribe from.
///
func unsubscribe(from notifName: Notification.Name) {
func unsubscribe(from notifNames: Notification.Name...) {

if let observer = subscriptions[notifName] {
for notifName in notifNames {

notifCtr.removeObserver(observer)
subscriptions.removeValue(forKey: notifName)
if let observer = subscriptions[notifName] {

notifCtr.removeObserver(observer)
subscriptions.removeValue(forKey: notifName)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ extension Notification.Name {
static let loadFromFile = Notification.Name("lyrics_loadFromFile")
static let addLyricsFile = Notification.Name("lyrics_addLyricsFile")
static let lyricsUpdated = Notification.Name("lyrics_lyricsUpdated")
static let karaokeModePreferenceUpdated = Notification.Name("lyrics_karaokeModePreferenceUpdated")
}
}
4 changes: 4 additions & 0 deletions Source/Core/Preferences/Metadata/LyricsPreferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ class LyricsPreferences {
lazy var enableAutoScroll: UserPreference<Bool> = .init(defaultsKey: "\(Self.keyPrefix).enableAutoScroll",
defaultValue: Defaults.enableAutoSearch)

// For timed (LRC) lyrics only
lazy var enableKaraokeMode: UserPreference<Bool> = .init(defaultsKey: "\(Self.keyPrefix).enableKaraokeMode",
defaultValue: Defaults.enableKaraokeMode)

lazy var lyricsFilesDirectory: OptionalUserPreference<URL> = .init(defaultsKey: "\(Self.keyPrefix).lyricsFilesDirectory")

lazy var enableAutoSearch: UserPreference<Bool> = .init(defaultsKey: "\(Self.keyPrefix).enableAutoSearch",
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Preferences/PreferencesDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ struct PreferencesDefaults {

static let showWindowWhenPresent: Bool = true
static let enableAutoScroll: Bool = true
static let enableKaraokeMode: Bool = true

static let enableAutoSearch: Bool = true
static let showTranslations: Bool = true
Expand Down
130 changes: 79 additions & 51 deletions Source/UI/Lyrics/LyricsViewController+Timed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@

import AppKit

fileprivate var isKaraokeModeEnabled: Bool {
preferences.metadataPreferences.lyrics.enableKaraokeMode.value
}

extension LyricsViewController {

var fileOpenDialog: NSOpenPanel {DialogsAndAlerts.openLyricsFileDialog}
Expand All @@ -26,6 +30,7 @@ extension LyricsViewController {

messenger.subscribeAsync(to: .Player.playbackStateChanged, handler: playbackStateChanged)
messenger.subscribeAsync(to: .Player.seekPerformed, handler: seekPerformed)
messenger.subscribeAsync(to: .Lyrics.karaokeModePreferenceUpdated, handler: karaokeModePreferenceUpdated)
}

@IBAction func loadLyricsButtonAction(_ sender: NSButton) {
Expand Down Expand Up @@ -60,8 +65,10 @@ extension LyricsViewController {
func dismissTimedLyricsView() {

timer.pause()
messenger.unsubscribe(from: .Player.playbackStateChanged)
messenger.unsubscribe(from: .Player.seekPerformed)

messenger.unsubscribe(from: .Player.playbackStateChanged,
.Player.seekPerformed,
.Lyrics.karaokeModePreferenceUpdated)
}

private var isAutoScrollEnabled: Bool {
Expand All @@ -80,6 +87,8 @@ extension LyricsViewController {

if line.isCurrent(atPosition: seekPos) {

if !isKaraokeModeEnabled {return}

// Current line is still current. Find the current segment, if required.

if line.segments.isEmpty {return}
Expand Down Expand Up @@ -124,6 +133,18 @@ extension LyricsViewController {
func seekPerformed() {
highlightCurrentLine()
}

func karaokeModePreferenceUpdated() {

self.curSegment = nil

if isKaraokeModeEnabled {
highlightCurrentLine()

} else if let curLine {
tableView.reloadRows([curLine])
}
}
}

extension LyricsViewController: NSTableViewDataSource {
Expand Down Expand Up @@ -158,62 +179,69 @@ extension LyricsViewController: NSTableViewDelegate {

let isCurrentLine = row == self.curLine
let line = timedLyrics.lines[row]

if isCurrentLine, line.segments.isNonEmpty {

if let curSegment {

var mutStr: NSMutableAttributedString = .init(string: "")

let segment = line.segments[curSegment]
let segmentLoc = segment.range.location

if segmentLoc > 1 {

let preSegmentRange = NSMakeRange(0, segmentLoc)

let subString = line.content.substring(range: preSegmentRange.intRange)
let attrStr = subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.primarySelectedTextColor)
mutStr = mutStr + attrStr
}

let subString = line.content.substring(range: segment.range.intRange)
mutStr = mutStr + subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.activeControlColor)

let segmentLen = segment.range.length
let segmentEnd = segmentLoc + segmentLen
let contentLength = line.contentLength

if segmentEnd < contentLength {

let postSegmentRange = NSMakeRange(segmentEnd, contentLength - segmentEnd)

let subString = line.content.substring(range: postSegmentRange.intRange)
let attrStr = subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.primarySelectedTextColor)
mutStr = mutStr + attrStr
}

cell.attributedText = mutStr

} else {

// No cur segment

cell.text = line.content
cell.textFont = systemFontScheme.lyricsHighlightFont
cell.textColor = systemColorScheme.primarySelectedTextColor
}

if isCurrentLine, isKaraokeModeEnabled, line.segments.isNonEmpty {
updateCurrentLineCellInKaraokeMode(cell, line: line)
} else {

cell.text = line.content
cell.textFont = isCurrentLine ? systemFontScheme.lyricsHighlightFont : systemFontScheme.prominentFont
cell.textColor = isCurrentLine ? systemColorScheme.activeControlColor : systemColorScheme.secondaryTextColor
updateCellWithDefaultAppearance(cell, line: line, isCurrentLine: isCurrentLine)
}

cell.textField?.show()
cell.textField?.lineBreakMode = .byTruncatingTail

return cell
}

private func updateCurrentLineCellInKaraokeMode(_ cell: AuralTableCellView, line: TimedLyricsLine) {

guard let curSegment else {

// No current segment (eg. between words)

cell.text = line.content
cell.textFont = systemFontScheme.lyricsHighlightFont
cell.textColor = systemColorScheme.primarySelectedTextColor

return
}

var mutStr: NSMutableAttributedString = .init(string: "")

let segment = line.segments[curSegment]
let segmentLoc = segment.range.location

if segmentLoc > 1 {

let preSegmentRange = NSMakeRange(0, segmentLoc)

let subString = line.content.substring(range: preSegmentRange.intRange)
let attrStr = subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.primarySelectedTextColor)
mutStr = mutStr + attrStr
}

let subString = line.content.substring(range: segment.range.intRange)
mutStr = mutStr + subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.activeControlColor)

let segmentLen = segment.range.length
let segmentEnd = segmentLoc + segmentLen
let contentLength = line.contentLength

if segmentEnd < contentLength {

let postSegmentRange = NSMakeRange(segmentEnd, contentLength - segmentEnd)

let subString = line.content.substring(range: postSegmentRange.intRange)
let attrStr = subString.attributed(font: systemFontScheme.lyricsHighlightFont, color: systemColorScheme.primarySelectedTextColor)
mutStr = mutStr + attrStr
}

cell.attributedText = mutStr
}

private func updateCellWithDefaultAppearance(_ cell: AuralTableCellView, line: TimedLyricsLine, isCurrentLine: Bool) {

cell.text = line.content
cell.textFont = isCurrentLine ? systemFontScheme.lyricsHighlightFont : systemFontScheme.prominentFont
cell.textColor = isCurrentLine ? systemColorScheme.activeControlColor : systemColorScheme.secondaryTextColor
}
}
7 changes: 3 additions & 4 deletions Source/UI/Lyrics/Utils/TimedLyrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ struct TimedLyricsLine {

var segments: [TimedLyricsLineSegment] = []

print("\n---------------------------------------------")
print("For line: \(content)\n")
// print("\n---------------------------------------------")
// print("For line: \(content)\n")

for (index, tag) in timeTags.enumerated() {

Expand All @@ -102,9 +102,8 @@ struct TimedLyricsLine {
}

if startPos >= position, endPos <= maxPosition, startPos < endPos, let range {

segments.append(.init(startPos: position + tag.time, endPos: position + nextTag.time, range: range))
print("Segment \(segments.count): \(startPos), \(endPos), \(range)")
// print("Segment \(segments.count): \(startPos), \(endPos), \(range)")
}
}
}
Expand Down
47 changes: 40 additions & 7 deletions Source/UI/Preferences/Metadata/Lyrics/LyricsPreferences.xib
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<connections>
<outlet property="btnEnableAutoScroll" destination="FXN-q7-QXF" id="X7u-OC-Yjl"/>
<outlet property="btnEnableAutoShowWindow" destination="yd1-72-Yf8" id="BZT-Mf-geN"/>
<outlet property="btnEnableKaraokeMode" destination="vYe-YM-Jep" id="lTn-AI-jRN"/>
<outlet property="lblLyricsFolder" destination="B4P-Hb-LUM" id="FU4-je-o2p"/>
<outlet property="view" destination="W8Y-oq-0wu" id="YaQ-nL-8LG"/>
</connections>
Expand All @@ -21,23 +22,23 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yd1-72-Yf8" userLabel="Media key response" customClass="DialogCheckRadioButton" customModule="Aural" customModuleProvider="target">
<rect key="frame" x="26" y="243" width="515" height="32"/>
<rect key="frame" x="26" y="253" width="515" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Automatically show Lyrics window when playing a track that has lyrics" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Xav-Fp-VUg">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="13" name="Play-Regular"/>
</buttonCell>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FXN-q7-QXF" userLabel="Media key response" customClass="DialogCheckRadioButton" customModule="Aural" customModuleProvider="target">
<rect key="frame" x="26" y="200" width="515" height="32"/>
<rect key="frame" x="41" y="170" width="515" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Automatically scroll to show the current lyrics line (for synchronized lyrics only)" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="kQB-Cf-nc1">
<buttonCell key="cell" type="check" title="Automatically scroll to show the current lyrics line" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="kQB-Cf-nc1">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="13" name="Play-Regular"/>
</buttonCell>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="B4P-Hb-LUM">
<rect key="frame" x="46" y="132" width="520" height="22"/>
<rect key="frame" x="61" y="82" width="395" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingHead" truncatesLastVisibleLine="YES" selectable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="left" id="gVS-Zj-f6c" customClass="ValidatedLabelCell" customModule="Aural" customModuleProvider="target">
<font key="font" size="13" name="Play-Regular"/>
Expand All @@ -46,7 +47,7 @@
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pP1-Np-lPR">
<rect key="frame" x="44" y="160" width="399" height="20"/>
<rect key="frame" x="59" y="110" width="399" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Look for synchronized lyrics files (LRC / LRCX) in directory:" id="Ahy-EN-3fU">
<font key="font" size="13" name="Play-Regular"/>
Expand All @@ -55,9 +56,9 @@
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ryU-o6-Lar" userLabel="Btn Browse">
<rect key="frame" x="457" y="155" width="116" height="32"/>
<rect key="frame" x="472" y="105" width="116" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="Npq-Np-IAU">
<buttonCell key="cell" type="push" title="Browse" bezelStyle="rounded" image="folder" catalog="system" imagePosition="trailing" alignment="center" state="on" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="Npq-Np-IAU">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Expand All @@ -68,8 +69,40 @@ DQ
<action selector="chooseLyricsFolderAction:" target="-2" id="cbC-yq-4Nk"/>
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vYe-YM-Jep" userLabel="Media key response" customClass="DialogCheckRadioButton" customModule="Aural" customModuleProvider="target">
<rect key="frame" x="41" y="140" width="515" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Enable Karaoke mode (when time tags are available )" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="rfF-Mj-AhF">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="13" name="Play-Regular"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CBd-Of-GfX">
<rect key="frame" x="472" y="77" width="116" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Clear" bezelStyle="rounded" image="xmark.circle" catalog="system" imagePosition="trailing" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="gFz-7h-9Lv">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="clearLyricsFolderAction:" target="-2" id="x5T-m4-CKc"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6eJ-Yg-8mL">
<rect key="frame" x="26" y="212" width="281" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Synchronized lyrics" id="emH-jl-zDa">
<font key="font" size="13" name="Play-Regular"/>
<color key="textColor" white="0.70467122400000004" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="148" y="-450"/>
</view>
</objects>
<resources>
<image name="folder" catalog="system" width="18" height="14"/>
<image name="xmark.circle" catalog="system" width="15" height="15"/>
</resources>
</document>
Loading

0 comments on commit 5a6ddfc

Please sign in to comment.