Skip to content

Commit

Permalink
karaoke mode
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-venugopal committed Jan 5, 2025
1 parent 38b00dc commit 4225168
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 79 deletions.
20 changes: 16 additions & 4 deletions Aural.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,6 @@
3E10179028D7722B002C0D8E /* EffectsUnitToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E10178F28D7722B002C0D8E /* EffectsUnitToggle.swift */; };
3E1341282B6F008E009782B6 /* TrackInfoViewController+MenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E1341272B6F008E009782B6 /* TrackInfoViewController+MenuDelegate.swift */; };
3E13412A2B6F0115009782B6 /* TrackInfoViewController+Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E1341292B6F0115009782B6 /* TrackInfoViewController+Theming.swift */; };
3E143C592D25E6BB00F40A67 /* LyricsViewController+TableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E143C582D25E6BB00F40A67 /* LyricsViewController+TableViewDelegate.swift */; };
3E143C5B2D26010A00F40A67 /* LyricsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E143C5A2D26010A00F40A67 /* LyricsExtensions.swift */; };
3E154A912BFA03A100F18EE9 /* EffectsPresetsManagerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E154A902BFA03A100F18EE9 /* EffectsPresetsManagerWindowController.swift */; };
3E1572AC2C28C0C2006E9965 /* AudioUnitEditorDialogController+PresetsMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E1572AB2C28C0C2006E9965 /* AudioUnitEditorDialogController+PresetsMenu.swift */; };
Expand Down Expand Up @@ -969,6 +968,8 @@
3ED373F52C71803F00836511 /* FFmpegPacketSideData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED373F42C71803F00836511 /* FFmpegPacketSideData.swift */; };
3ED65A532C45BA3000859677 /* PlayerViewController+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED65A522C45BA3000859677 /* PlayerViewController+Actions.swift */; };
3ED65A552C45BAD000859677 /* PlayerViewController+Theming.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED65A542C45BAD000859677 /* PlayerViewController+Theming.swift */; };
3ED8831C2D296A9E00F39E68 /* CompactPlayerLyrics.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3ED8831B2D296A9E00F39E68 /* CompactPlayerLyrics.xib */; };
3ED8831E2D296AB200F39E68 /* CompactPlayerLyricsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8831D2D296AB200F39E68 /* CompactPlayerLyricsViewController.swift */; };
3ED8E74A281482C9002E313F /* TableImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8E749281482C9002E313F /* TableImageCell.swift */; };
3ED8E74F2814A312002E313F /* GeneralColorSchemeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED8E74D2814A312002E313F /* GeneralColorSchemeViewController.swift */; };
3ED8E7502814A312002E313F /* GeneralColorScheme.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3ED8E74E2814A312002E313F /* GeneralColorScheme.xib */; };
Expand Down Expand Up @@ -1543,7 +1544,6 @@
3E10178F28D7722B002C0D8E /* EffectsUnitToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsUnitToggle.swift; sourceTree = "<group>"; };
3E1341272B6F008E009782B6 /* TrackInfoViewController+MenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TrackInfoViewController+MenuDelegate.swift"; sourceTree = "<group>"; };
3E1341292B6F0115009782B6 /* TrackInfoViewController+Theming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TrackInfoViewController+Theming.swift"; sourceTree = "<group>"; };
3E143C582D25E6BB00F40A67 /* LyricsViewController+TableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LyricsViewController+TableViewDelegate.swift"; sourceTree = "<group>"; };
3E143C5A2D26010A00F40A67 /* LyricsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LyricsExtensions.swift; sourceTree = "<group>"; };
3E154A902BFA03A100F18EE9 /* EffectsPresetsManagerWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EffectsPresetsManagerWindowController.swift; sourceTree = "<group>"; };
3E1572AB2C28C0C2006E9965 /* AudioUnitEditorDialogController+PresetsMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AudioUnitEditorDialogController+PresetsMenu.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2128,6 +2128,8 @@
3ED373F42C71803F00836511 /* FFmpegPacketSideData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FFmpegPacketSideData.swift; sourceTree = "<group>"; };
3ED65A522C45BA3000859677 /* PlayerViewController+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayerViewController+Actions.swift"; sourceTree = "<group>"; };
3ED65A542C45BAD000859677 /* PlayerViewController+Theming.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PlayerViewController+Theming.swift"; sourceTree = "<group>"; };
3ED8831B2D296A9E00F39E68 /* CompactPlayerLyrics.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CompactPlayerLyrics.xib; sourceTree = "<group>"; };
3ED8831D2D296AB200F39E68 /* CompactPlayerLyricsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactPlayerLyricsViewController.swift; sourceTree = "<group>"; };
3ED8E749281482C9002E313F /* TableImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableImageCell.swift; sourceTree = "<group>"; };
3ED8E74D2814A312002E313F /* GeneralColorSchemeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralColorSchemeViewController.swift; sourceTree = "<group>"; };
3ED8E74E2814A312002E313F /* GeneralColorScheme.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GeneralColorScheme.xib; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5107,7 +5109,6 @@
3EAF727A2D25B24700E24245 /* LyricsViewController.swift */,
3E57443C2D262104004B5AE7 /* LyricsViewController+Static.swift */,
3E57443A2D261E28004B5AE7 /* LyricsViewController+Timed.swift */,
3E143C582D25E6BB00F40A67 /* LyricsViewController+TableViewDelegate.swift */,
3EA21A452D2763B3008B5B21 /* LyricsFileDropView.swift */,
3E719D8C2D28A105000A33B9 /* SheetView */,
3E719D8D2D28A12D000A33B9 /* Utils */,
Expand Down Expand Up @@ -5248,6 +5249,7 @@
3EC793842B746C6000AF7E9E /* PlayQueue */,
3E8B8C382BF004AB0025D60B /* ChaptersList */,
3E2EA6E72BD322D7001ABFEE /* TrackInfo */,
3ED8831A2D296A6700F39E68 /* Lyrics */,
);
path = CompactPlayer;
sourceTree = "<group>";
Expand Down Expand Up @@ -5503,6 +5505,15 @@
path = Utils;
sourceTree = "<group>";
};
3ED8831A2D296A6700F39E68 /* Lyrics */ = {
isa = PBXGroup;
children = (
3ED8831B2D296A9E00F39E68 /* CompactPlayerLyrics.xib */,
3ED8831D2D296AB200F39E68 /* CompactPlayerLyricsViewController.swift */,
);
path = Lyrics;
sourceTree = "<group>";
};
3ED8E74C2814A312002E313F /* General */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -5824,6 +5835,7 @@
3E6C121D25CEBDE600BF0D07 /* Preferences.xib in Resources */,
3EA3335B2AE098620041B4FA /* MenuBarPlayer.xib in Resources */,
3E2C4BD72B38E5E600FC65DD /* SearchWindow.xib in Resources */,
3ED8831C2D296A9E00F39E68 /* CompactPlayerLyrics.xib in Resources */,
3EC7937F2B745E0800AF7E9E /* EffectsSheetView.xib in Resources */,
3ED2385928207E2C0048CF8E /* FilterBandEditorDialog.xib in Resources */,
3E6C121525CEBDE500BF0D07 /* ViewPreferences.xib in Resources */,
Expand Down Expand Up @@ -6000,7 +6012,6 @@
3E0219412C23490E00865AC2 /* MusicBrainzCoverArtArchive.swift in Sources */,
3E0219842C23490E00865AC2 /* IntExtensions.swift in Sources */,
3E02193B2C23490E00865AC2 /* MetadataFormat.swift in Sources */,
3E143C592D25E6BB00F40A67 /* LyricsViewController+TableViewDelegate.swift in Sources */,
3E6C123A25CEBDF400BF0D07 /* ChaptersListWindowController.swift in Sources */,
3E0218C32C23490E00865AC2 /* SavePlaybackProfileAction.swift in Sources */,
3EAF727B2D25B24700E24245 /* LyricsViewController.swift in Sources */,
Expand Down Expand Up @@ -6315,6 +6326,7 @@
3E02195A2C23490E00865AC2 /* SearchResultLocation.swift in Sources */,
3E02199A2C23490E00865AC2 /* MenuItemMappable.swift in Sources */,
3E2000C7267CEEF7008BAB70 /* GesturesPreferencesViewController.swift in Sources */,
3ED8831E2D296AB200F39E68 /* CompactPlayerLyricsViewController.swift in Sources */,
3E0218072C23490E00865AC2 /* ReverbUnitProtocol.swift in Sources */,
3EC7425D2C41C6E300C100DE /* AVFScheduler+Gapless.swift in Sources */,
3E02191F2C23490E00865AC2 /* RemoteCommandManager.swift in Sources */,
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion Source/Core/TrackIO/Model/Track+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ extension Track {
}

var hasLyrics: Bool {
metadata.lyrics != nil || metadata.timedLyrics != nil
metadata.lyrics != nil || metadata.timedLyrics != nil || metadata.externalTimedLyrics != nil
}

// Non-essential metadata
Expand Down
14 changes: 14 additions & 0 deletions Source/Core/Utils/Extensions/StringExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ extension String {
func encodedAsURLQueryParameter() -> String {
self.replacingOccurrences(of: " ", with: "+").addingPercentEncoding(withAllowedCharacters: .queryParmCharacters) ?? self.replacingOccurrences(of: " ", with: "+")
}

///
/// Converts this ``String`` to a ``NSAttributedString`` with the given font and color as attributes.
///
func attributed(withFont font: NSFont, andColor color: NSColor) -> NSAttributedString {
return NSAttributedString(string: self, attributes: [.font: font, .foregroundColor: color])
}
}

extension Character {
Expand Down Expand Up @@ -473,3 +480,10 @@ extension NSMutableAttributedString {
return left
}
}

extension NSRange {

var intRange: Range<Int> {
location..<(location + length)
}
}
54 changes: 0 additions & 54 deletions Source/UI/Lyrics/LyricsViewController+TableViewDelegate.swift

This file was deleted.

121 changes: 118 additions & 3 deletions Source/UI/Lyrics/LyricsViewController+Timed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension LyricsViewController {
tabView.selectTabViewItem(at: 1)

curLine = nil
curSegment = nil
tableView.reloadData()

track != nil ? timer.startOrResume() : timer.pause()
Expand Down Expand Up @@ -73,10 +74,29 @@ extension LyricsViewController {

let seekPos = playbackInfoDelegate.seekPosition.timeElapsed

if let curLine, timedLyrics.lines[curLine].isCurrent(atPosition: seekPos) {
if let curLine {

// Current line is still current, do nothing.
return
let line = timedLyrics.lines[curLine]

if line.isCurrent(atPosition: seekPos) {

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

if line.segments.isEmpty {return}

if let curSegment, line.segments[curSegment].isCurrent(atPosition: seekPos) {return}

// No current segment or segment no longer current, find the new current one.
let newCurSegment = line.findCurrentSegment(at: seekPos)

if self.curSegment != newCurSegment {

self.curSegment = newCurSegment
tableView.reloadRows([curLine])
}

return
}
}

let newCurLine = timedLyrics.currentLine(at: seekPos)
Expand All @@ -85,7 +105,10 @@ extension LyricsViewController {

// Try curLine + 1 (in most cases, playback proceeds sequentially, so this is the most likely line to match)
let refreshIndices = [self.curLine, newCurLine].compactMap {$0}

self.curLine = newCurLine
self.curSegment = nil

tableView.reloadRows(refreshIndices)

if isAutoScrollEnabled, let curLine {
Expand All @@ -102,3 +125,95 @@ extension LyricsViewController {
highlightCurrentLine()
}
}

extension LyricsViewController: NSTableViewDataSource {

func numberOfRows(in tableView: NSTableView) -> Int {timedLyrics?.lines.count ?? 0}
}

extension LyricsViewController: NSTableViewDelegate {

private static let rowHeight: CGFloat = 30

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
Self.rowHeight
}

func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {

// Seek to clicked line.
if let timedLyrics, timedLyrics.lines.indices.contains(row) {
messenger.publish(.Player.jumpToTime, payload: max(0, timedLyrics.lines[row].position))
}

return false
}

// Returns a view for a single column
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {

guard let timedLyrics,
let cell = tableView.makeView(withIdentifier: .cid_lyricsLine, owner: nil) as? AuralTableCellView
else {return nil}

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
}

} else {

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

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

return cell
}
}
1 change: 1 addition & 0 deletions Source/UI/Lyrics/LyricsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class LyricsViewController: NSViewController {

var timedLyrics: TimedLyrics?
var curLine: Int?
var curSegment: Int?

lazy var messenger = Messenger(for: self)

Expand Down
5 changes: 2 additions & 3 deletions Source/UI/Lyrics/Utils/LyricsExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@

import Foundation
import LyricsCore
import LyricsXCore

extension LyricsCore.Lyrics {
extension Lyrics {

var offset: TimeInterval {

Expand Down Expand Up @@ -51,7 +50,7 @@ extension LyricsCore.Lyrics {
/// - lyrics: The lyrics to save
/// - url: The destination URL
///
private func persistLyrics(_ lyrics: LyricsCore.Lyrics, to url: URL) {
private func persistLyrics(_ lyrics: Lyrics, to url: URL) {

url.parentDir.createDirectory()

Expand Down
Loading

0 comments on commit 4225168

Please sign in to comment.