removed unwanted bar now with waveforms, major updates and enhancements
This commit is contained in:
@@ -322,9 +322,9 @@ struct DJAudioDevice: Identifiable, Equatable {
|
||||
let cache = waveformDirectory,locations = referenceLocations
|
||||
generatingWaveforms = true;waveformJob = .init(total:tracks.count)
|
||||
waveformTask = Task.detached(priority:.utility) { [weak self] in
|
||||
let result = await WaveformBatch.runConcurrent(songs:tracks,locations:locations,cache:cache,force:force) { update in
|
||||
let result = await WaveformBatch.runConcurrent(songs:tracks,locations:locations,cache:cache,force:force,progress: { update in
|
||||
Task { @MainActor [weak self] in self?.waveformJob = update }
|
||||
}
|
||||
})
|
||||
await MainActor.run { [weak self] in
|
||||
guard let self else { return }
|
||||
self.waveformJob = result;self.generatingWaveforms = false;self.waveformTask = nil
|
||||
@@ -365,11 +365,6 @@ struct DJAudioDevice: Identifiable, Equatable {
|
||||
@Published var error: String?
|
||||
let root: URL
|
||||
private(set) var library: Library?
|
||||
private var appleMusicStorage: AnyObject?
|
||||
@available(macOS 14.0, *) var appleMusic: AppleMusicSession {
|
||||
if let session = appleMusicStorage as? AppleMusicSession { return session }
|
||||
let session = AppleMusicSession();appleMusicStorage = session;return session
|
||||
}
|
||||
let dragSession = UUID().uuidString
|
||||
@Published var songs = [Song]()
|
||||
@Published var playlists = [PersonPlace]()
|
||||
@@ -384,12 +379,6 @@ struct DJAudioDevice: Identifiable, Equatable {
|
||||
mediaDirectory = root.appendingPathComponent("Media");waveformDirectory = root.appendingPathComponent("Waveforms")
|
||||
a = DJChannel("A",waveformCache:root.appendingPathComponent("Waveforms"));b = DJChannel("B",waveformCache:root.appendingPathComponent("Waveforms"))
|
||||
preview.autoFade = false;preview.volume = cueLevel;refreshDevices();applyMix()
|
||||
for deck in [a,b] {
|
||||
deck.player.onWillStartPlayback = { [weak self] in
|
||||
if #available(macOS 14.0, *),let session = self?.appleMusicStorage as? AppleMusicSession { session.pause() }
|
||||
return 0
|
||||
}
|
||||
}
|
||||
Publishers.CombineLatest4(a.$bpm,b.$bpm,a.player.$tempo,b.player.$tempo).dropFirst().sink { [weak self] _ in
|
||||
Task { @MainActor [weak self] in self?.maintainSync() }
|
||||
}.store(in:&syncObservers)
|
||||
@@ -910,8 +899,8 @@ struct DJMIDIMapping: Codable, Identifiable, Equatable {
|
||||
var list = MIDIPacketList()
|
||||
withUnsafeMutablePointer(to:&list) { pointer in
|
||||
let packet = MIDIPacketListInit(pointer)
|
||||
let added = bytes.withUnsafeBufferPointer { MIDIPacketListAdd(pointer,MemoryLayout<MIDIPacketList>.size,packet,0,$0.count,$0.baseAddress!) }
|
||||
if added != nil { MIDISend(outputPort,outputDestination,pointer) }
|
||||
_ = bytes.withUnsafeBufferPointer { MIDIPacketListAdd(pointer,MemoryLayout<MIDIPacketList>.size,packet,0,$0.count,$0.baseAddress!) }
|
||||
MIDISend(outputPort,outputDestination,pointer)
|
||||
}
|
||||
}
|
||||
func remove(_ id: String) { mappings.removeAll{$0.id == id};save() }
|
||||
@@ -1018,7 +1007,6 @@ struct DJConsole: View {
|
||||
@State private var reinterpreting = false
|
||||
@State private var reinterpretIDs = Set<Int64>()
|
||||
@State private var editingTrack: Song?
|
||||
@State private var appleMusicVisible = false
|
||||
@State private var graphicEQVisible = false
|
||||
@State private var waveformDetails = false
|
||||
@State private var favouritesOnly = false
|
||||
@@ -1064,13 +1052,7 @@ struct DJConsole: View {
|
||||
VStack(spacing:10) {
|
||||
header
|
||||
DJContinuousConsole(system:system,loadA:{loadSelected("A")},loadB:{loadSelected("B")}).fixedSize(horizontal:false,vertical:true)
|
||||
if appleMusicVisible {
|
||||
if #available(macOS 14.0, *) {
|
||||
AppleMusicBrowser(session:system.appleMusic,hasPlayingDeck:{system.a.player.playing || system.b.player.playing},stopDecks:{system.automix.stop();if system.a.player.playing { system.a.player.toggle() };if system.b.player.playing { system.b.player.toggle() }})
|
||||
} else { Text("Apple Music playback requires macOS 14 or later.").frame(maxWidth:.infinity,maxHeight:.infinity) }
|
||||
} else {
|
||||
DJLibrarySnapshot(revision:libraryRevision,selection:selection,selected:selected,page:page,busy:system.generatingWaveforms || system.importing,font:system.settings.libraryFont,height:system.settings.libraryRowHeight) { browser }.equatable().frame(maxWidth:.infinity,maxHeight:.infinity)
|
||||
}
|
||||
if let progress = system.automaticWaveformProgress {
|
||||
BackgroundWaveformStatus(progress:progress,cancel:{system.automaticWaveforms.cancel()},dismiss:{system.automaticWaveformProgress = nil})
|
||||
}
|
||||
@@ -1134,21 +1116,20 @@ struct DJConsole: View {
|
||||
.sheet(isPresented:$playlistEditor) { playlistSheet }
|
||||
.alert("DJ workstation",isPresented:Binding(get:{system.error != nil},set:{if !$0 { system.error = nil }})) { Button("OK") { system.error = nil } } message: { Text(system.error ?? "") }
|
||||
.onAppear { refreshPlaylist();connectMIDI() }
|
||||
.onChange(of:selected) { _ in connectMIDI() }
|
||||
.onChange(of:selection) { ids in if selected == nil || !ids.contains(selected!) { selected = songs.first(where:{ids.contains($0.id)})?.id } }
|
||||
.onChange(of:playlist) { _ in refreshPlaylist();page = 0;selected = nil;selection = [] }
|
||||
.onChange(of:system.songs) { _ in refreshPlaylist();connectMIDI() }
|
||||
.onChange(of:system.playlists) { _ in if let playlist,!system.playlists.contains(where:{$0.id == playlist}) { self.playlist = nil };refreshPlaylist() }
|
||||
.onChange(of:query) { _ in updateFilteredTracks();page = 0 }
|
||||
.onChange(of:favouritesOnly) { _ in updateFilteredTracks();page = 0 }
|
||||
.onChange(of:system.favourites) { _ in updateFilteredTracks() }
|
||||
.onStudioChange(of:selected) { _ in connectMIDI() }
|
||||
.onStudioChange(of:selection) { ids in if selected == nil || !ids.contains(selected!) { selected = songs.first(where:{ids.contains($0.id)})?.id } }
|
||||
.onStudioChange(of:playlist) { _ in refreshPlaylist();page = 0;selected = nil;selection = [] }
|
||||
.onStudioChange(of:system.songs) { _ in refreshPlaylist();connectMIDI() }
|
||||
.onStudioChange(of:system.playlists) { _ in if let playlist,!system.playlists.contains(where:{$0.id == playlist}) { self.playlist = nil };refreshPlaylist() }
|
||||
.onStudioChange(of:query) { _ in updateFilteredTracks();page = 0 }
|
||||
.onStudioChange(of:favouritesOnly) { _ in updateFilteredTracks();page = 0 }
|
||||
.onStudioChange(of:system.favourites) { _ in updateFilteredTracks() }
|
||||
}
|
||||
private var header: some View {
|
||||
HStack {
|
||||
Image(systemName:"waveform.path").foregroundStyle(DJStyle.a)
|
||||
Text("DJ WORKSTATION").font(.system(size:12,weight:.bold,design:.rounded))
|
||||
Text("TWO DECKS").font(.system(size:9,weight:.medium)).foregroundStyle(.secondary)
|
||||
Picker("Music source",selection:$appleMusicVisible) { Text("DJ Library").tag(false);Text("Apple Music").tag(true) }.pickerStyle(.segmented).frame(width:230)
|
||||
Spacer()
|
||||
Button { graphicEQVisible = true } label: { Label("Equaliser",systemImage:"slider.vertical.3") }.tint(system.graphicEQ.enabled ? .cyan : .accentColor)
|
||||
DJQueueButton(automix:system.automix,open:{queueVisible = true})
|
||||
@@ -1218,7 +1199,7 @@ struct DJConsole: View {
|
||||
.scrollContentBackground(.hidden).background(DJStyle.panel,in:RoundedRectangle(cornerRadius:8))
|
||||
.onCommand(#selector(NSResponder.selectAll(_:))) { selectAllTracks() }
|
||||
.background(DJLibraryPageScroll(page:page) { if page < pages-1 { page += 1 } })
|
||||
.onChange(of:page) { _ in
|
||||
.onStudioChange(of:page) { _ in
|
||||
selected = visible.first?.id;selection = Set(visible.first.map{[$0.id]} ?? [])
|
||||
if let first = visible.first?.id { DispatchQueue.main.async { reader.scrollTo(first,anchor:.top) } }
|
||||
}
|
||||
@@ -2088,14 +2069,15 @@ enum DJFinderDrop {
|
||||
static func urls(_ providers: [NSItemProvider]) async throws -> [URL] {
|
||||
var urls = [URL]()
|
||||
for provider in providers {
|
||||
let data: Data? = await withCheckedContinuation { continuation in
|
||||
provider.loadDataRepresentation(forTypeIdentifier:UTType.fileURL.identifier) { data,_ in continuation.resume(returning:data) }
|
||||
}
|
||||
if let data,let url = URL(dataRepresentation:data,relativeTo:nil),url.isFileURL { urls.append(url);continue }
|
||||
let url: URL = try await withCheckedThrowingContinuation { continuation in
|
||||
provider.loadDataRepresentation(forTypeIdentifier:UTType.fileURL.identifier) { data,_ in
|
||||
if let data,let url = URL(dataRepresentation:data,relativeTo:nil),url.isFileURL { continuation.resume(returning:url);return }
|
||||
provider.loadItem(forTypeIdentifier:UTType.fileURL.identifier,options:nil) { item,error in
|
||||
if let url = item as? URL,url.isFileURL { continuation.resume(returning:url) }
|
||||
else if let data = item as? Data,let url = URL(dataRepresentation:data,relativeTo:nil),url.isFileURL { continuation.resume(returning:url) }
|
||||
else { continuation.resume(throwing:error ?? LibraryError.message("Could not read an absolute local file URL from Finder. Drop the audio file itself or import it with Import Files / Folder.")) }
|
||||
}
|
||||
provider.loadItem(forTypeIdentifier:UTType.fileURL.identifier,options:nil) { item,error in
|
||||
if let url = item as? URL,url.isFileURL { continuation.resume(returning:url) }
|
||||
else if let data = item as? Data,let url = URL(dataRepresentation:data,relativeTo:nil),url.isFileURL { continuation.resume(returning:url) }
|
||||
else { continuation.resume(throwing:error ?? LibraryError.message("Could not read an absolute local file URL from Finder. Drop the audio file itself or import it with Import Files / Folder.")) }
|
||||
}
|
||||
}
|
||||
urls.append(url)
|
||||
|
||||
Reference in New Issue
Block a user