Skip to content

Commit

Permalink
Linking Instruments
Browse files Browse the repository at this point in the history
All the instruments have been linked with the detail view.
UI improvements.
Proper engine startup and close function.

Keyboard support for drum pad added.
  • Loading branch information
Aayush9029 committed May 31, 2023
1 parent 54ba111 commit b13dc4c
Show file tree
Hide file tree
Showing 21 changed files with 411 additions and 169 deletions.
16 changes: 12 additions & 4 deletions Miano.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
45D880E32A263B5C00555693 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D880E22A263B5C00555693 /* View+Extensions.swift */; };
45D880E62A26BA1700555693 /* MiniSpectrogram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D880E52A26BA1700555693 /* MiniSpectrogram.swift */; };
45D880E92A26C9FD00555693 /* Colorful in Frameworks */ = {isa = PBXBuildFile; productRef = 45D880E82A26C9FD00555693 /* Colorful */; };
45D880EB2A26CE0400555693 /* Haptics+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D880EA2A26CE0400555693 /* Haptics+Extensions.swift */; };
45D880ED2A26DF3800555693 /* DrumsConductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D880EC2A26DF3800555693 /* DrumsConductor.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -123,6 +125,8 @@
45D880DF2A26352B00555693 /* InstrumentDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstrumentDetail.swift; sourceTree = "<group>"; };
45D880E22A263B5C00555693 /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
45D880E52A26BA1700555693 /* MiniSpectrogram.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniSpectrogram.swift; sourceTree = "<group>"; };
45D880EA2A26CE0400555693 /* Haptics+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Haptics+Extensions.swift"; sourceTree = "<group>"; };
45D880EC2A26DF3800555693 /* DrumsConductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrumsConductor.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -248,6 +252,7 @@
children = (
45D880812A25EA8600555693 /* MiniDrumPad.swift */,
45D880A72A25EFF300555693 /* SingleDrumPad.swift */,
45D880EC2A26DF3800555693 /* DrumsConductor.swift */,
);
path = MiniDrum;
sourceTree = "<group>";
Expand Down Expand Up @@ -327,14 +332,14 @@
path = "MIDI Files";
sourceTree = "<group>";
};
45D880C92A261A1200555693 /* MiniSectrogram */ = {
45D880C92A261A1200555693 /* MiniSpectrogram */ = {
isa = PBXGroup;
children = (
45D880E52A26BA1700555693 /* MiniSpectrogram.swift */,
45D880CA2A261A3400555693 /* SpectrogramView.swift */,
45D880D32A261B8E00555693 /* SpectrogramKit */,
);
path = MiniSectrogram;
path = MiniSpectrogram;
sourceTree = "<group>";
};
45D880D32A261B8E00555693 /* SpectrogramKit */ = {
Expand Down Expand Up @@ -369,19 +374,20 @@
isa = PBXGroup;
children = (
45D880E22A263B5C00555693 /* View+Extensions.swift */,
45D880EA2A26CE0400555693 /* Haptics+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
45D880E42A263BB000555693 /* Instruments */ = {
isa = PBXGroup;
children = (
45D880C92A261A1200555693 /* MiniSectrogram */,
45D880B62A26079500555693 /* SharedComponents */,
45D880C12A26148500555693 /* MidiPlayer */,
45D880A92A25F76500555693 /* MiniTuner */,
45D880C92A261A1200555693 /* MiniSpectrogram */,
45D880BC2A260FCB00555693 /* VocalTrack */,
45D880B12A26075000555693 /* NoiseGenerator */,
45D880A92A25F76500555693 /* MiniTuner */,
45D880802A25E79700555693 /* MiniDrum */,
45D8807E2A25E6CF00555693 /* MiniKeyboard */,
);
Expand Down Expand Up @@ -496,12 +502,14 @@
45D880B52A26077B00555693 /* NoiseGeneratorsConductor.swift in Sources */,
45D880E32A263B5C00555693 /* View+Extensions.swift in Sources */,
45D880CF2A261A8200555693 /* MelSpectrogram.swift in Sources */,
45D880ED2A26DF3800555693 /* DrumsConductor.swift in Sources */,
45D8807C2A25D62500555693 /* SmallKnob.swift in Sources */,
45D880B02A25F83900555693 /* MiniTuner.swift in Sources */,
45D880DE2A26350000555693 /* InstrumentsSidebar.swift in Sources */,
45D880492A25B9AB00555693 /* MiniKeyboard.swift in Sources */,
45D8807A2A25C20E00555693 /* EXSConductor.swift in Sources */,
45D880772A25C19A00555693 /* CustomKeys.swift in Sources */,
45D880EB2A26CE0400555693 /* Haptics+Extensions.swift in Sources */,
45D880A82A25EFF300555693 /* SingleDrumPad.swift in Sources */,
45D880CB2A261A3400555693 /* SpectrogramView.swift in Sources */,
45D880D02A261A8200555693 /* AudioSpectrogram.swift in Sources */,
Expand Down
17 changes: 17 additions & 0 deletions Miano/Extensions/Haptics+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// Haptics+Extensions.swift
// Miano
//
// Created by Aayush Pokharel on 2023-05-30.
//

import SwiftUI

extension View {
func performHapticFeedback(_ feedback: NSHapticFeedbackManager.FeedbackPattern = .generic) {
NSHapticFeedbackManager.defaultPerformer.perform(
feedback,
performanceTime: .now
)
}
}
123 changes: 123 additions & 0 deletions Miano/Instruments/MiniDrum/DrumsConductor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// DrumsConductor.swift
// Miano
//
// Created by Aayush Pokharel on 2023-05-30.
//

import AudioKit
import AudioKitEX
import AudioKitUI
import AVFoundation
import Combine
import SwiftUI

struct DrumSample {
var name: String
var fileName: String
var midiNote: Int
var audioFile: AVAudioFile?
var color: Color
var key: KeyEquivalent

init(_ prettyName: String, file: String, note: Int, _ drumColor: Color = Color.red, keyboardKey: KeyEquivalent) {
name = prettyName
fileName = file
midiNote = note
color = drumColor
key = keyboardKey

guard let url = Bundle.main.resourceURL?.appendingPathComponent(file) else { return }

do {
audioFile = try AVAudioFile(forReading: url)
} catch {
Log("Could not load: \(fileName)")
}
}
}

class DrumsConductor: ObservableObject, HasAudioEngine {
// Mark Published so View updates label on changes
@Published private(set) var lastPlayed: String = "None"

let engine = AudioEngine()

let drumSamples: [DrumSample] =
[
DrumSample(
"OPEN HI HAT", file: "open_hi_hat_A#1.wav",
note: 34, .pink,
keyboardKey: "t"
),
DrumSample(
"HI TOM", file: "hi_tom_D2.wav",
note: 38, .orange,
keyboardKey: "y"
),
DrumSample(
"MID TOM", file: "mid_tom_B1.wav",
note: 35, .orange,
keyboardKey: "u"
),
DrumSample(
"LO TOM", file: "lo_tom_F1.wav",
note: 29, .orange,
keyboardKey: "i"
),
DrumSample(
"HI HAT", file: "closed_hi_hat_F#1.wav",
note: 30, .pink,
keyboardKey: "g"
),
DrumSample(
"CLAP", file: "clap_D#1.wav",
note: 27, .blue,
keyboardKey: "h"
),
DrumSample(
"SNARE", file: "snare_D1.wav",
note: 26, .indigo,
keyboardKey: "j"
),
DrumSample(
"KICK", file: "bass_drum_C1.wav",
note: 24, .mint,
keyboardKey: "k"
),
]

let drums = AppleSampler()

func playPad(padNumber: Int) {
drums.play(noteNumber: MIDINoteNumber(drumSamples[padNumber].midiNote))
let fileName = drumSamples[padNumber].fileName
lastPlayed = fileName.components(separatedBy: "/").last!
}

init() {
engine.output = drums
do {
let files = drumSamples.map {
$0.audioFile!
}
try drums.loadAudioFiles(files)

} catch {
Log("Files Didn't Load")
}
}

// Refreshing the visualizer UI
@Published var running: Int = 0

func start() {
print("RUNNING INSTANCES \(running)")
running += 1
try? engine.start()
}

func stop() {
running -= 1
}
}
103 changes: 33 additions & 70 deletions Miano/Instruments/MiniDrum/MiniDrumPad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import Combine
import SwiftUI

struct MiniDrumPad: View {
@Environment(\.controlActiveState) private var controlActiveState
@StateObject var conductor = DrumsConductor()

var body: some View {
VStack {
VStack {
Expand All @@ -30,12 +32,16 @@ struct MiniDrumPad: View {
}.padding(8)
.background(.black)

.onAppear {
conductor.start()
}
.onDisappear {
.onReceive(NotificationCenter.default.publisher(for: NSWindow.willCloseNotification)) { _ in
print("close")
conductor.stop()
}

.onChange(of: controlActiveState) { phase in
if phase == .active || phase == .key {
conductor.start()
}
}
}
.ignoresSafeArea()
.frame(minWidth: 360, minHeight: 280)
Expand All @@ -49,71 +55,8 @@ struct MiniDrumPad_Previews: PreviewProvider {
}
}

struct DrumSample {
var name: String
var fileName: String
var midiNote: Int
var audioFile: AVAudioFile?
var color: Color

init(_ prettyName: String, file: String, note: Int, _ drumColor: Color = Color.red) {
name = prettyName
fileName = file
midiNote = note
color = drumColor

guard let url = Bundle.main.resourceURL?.appendingPathComponent(file) else { return }

do {
audioFile = try AVAudioFile(forReading: url)
} catch {
Log("Could not load: \(fileName)")
}
}
}

class DrumsConductor: ObservableObject, HasAudioEngine {
// Mark Published so View updates label on changes
@Published private(set) var lastPlayed: String = "None"

let engine = AudioEngine()

let drumSamples: [DrumSample] =
[
DrumSample("OPEN HI HAT", file: "open_hi_hat_A#1.wav", note: 34, .red),
DrumSample("HI TOM", file: "hi_tom_D2.wav", note: 38, .blue),
DrumSample("MID TOM", file: "mid_tom_B1.wav", note: 35, .green),
DrumSample("LO TOM", file: "lo_tom_F1.wav", note: 29, .orange),
DrumSample("HI HAT", file: "closed_hi_hat_F#1.wav", note: 30, .teal),
DrumSample("CLAP", file: "clap_D#1.wav", note: 27, .pink),
DrumSample("SNARE", file: "snare_D1.wav", note: 26, .indigo),
DrumSample("KICK", file: "bass_drum_C1.wav", note: 24, .mint),
]

let drums = AppleSampler()

func playPad(padNumber: Int) {
drums.play(noteNumber: MIDINoteNumber(drumSamples[padNumber].midiNote))
let fileName = drumSamples[padNumber].fileName
lastPlayed = fileName.components(separatedBy: "/").last!
}

init() {
engine.output = drums
do {
let files = drumSamples.map {
$0.audioFile!
}
try drums.loadAudioFiles(files)

} catch {
Log("Files Didn't Load")
}
}
}

struct PadsView: View {
var conductor: DrumsConductor
@ObservedObject var conductor: DrumsConductor

var padsAction: (_ padNumber: Int) -> Void
@State var downPads: [Int] = []
Expand All @@ -122,13 +65,29 @@ struct PadsView: View {
VStack(spacing: 0) {
NodeOutputView(conductor.drums)
.padding(.horizontal, -24)
.id(conductor.running)

ForEach(0 ..< 2, id: \.self) { row in
HStack(spacing: 0) {
ForEach(0 ..< 4, id: \.self) { column in
ZStack {
SingleDrumPad(drumpad: conductor.drumSamples.map { $0 }[getPadID(row: row, column: column)])

Button {
padsAction(getPadID(row: row, column: column))
downPads.append(row * 4 + column)
withAnimation(.easeIn(duration: 1.0)) {
downPads.removeAll(where: { $0 == row * 4 + column })
}

} label: {
ZStack {
SingleDrumPad(
playing: downPads.contains(where: { $0 == row * 4 + column }),
drumpad: conductor.drumSamples.map { $0 }[getPadID(row: row, column: column)]
)
.padding(6)
}
}
.buttonStyle(.plain)
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged { _ in
if !(downPads.contains(where: { $0 == row * 4 + column })) {
padsAction(getPadID(row: row, column: column))
Expand All @@ -137,6 +96,10 @@ struct PadsView: View {
}.onEnded { _ in
downPads.removeAll(where: { $0 == row * 4 + column })
})
.keyboardShortcut(
conductor.drumSamples.map { $0.key }[getPadID(row: row, column: column)],
modifiers: []
)
}
}
}
Expand Down
Loading

0 comments on commit b13dc4c

Please sign in to comment.