diff --git a/.periphery.yml b/.periphery.yml new file mode 100644 index 0000000..c14863f --- /dev/null +++ b/.periphery.yml @@ -0,0 +1,7 @@ +project: Miano.xcodeproj +retain_objc_accessible: true +retain_public: true +schemes: +- Miano +targets: +- Miano diff --git a/Miano.xcodeproj/project.pbxproj b/Miano.xcodeproj/project.pbxproj index 296f204..4ce647e 100644 --- a/Miano.xcodeproj/project.pbxproj +++ b/Miano.xcodeproj/project.pbxproj @@ -648,6 +648,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Miano/Miano.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -676,6 +677,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Miano/Miano.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; diff --git a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram.png b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram.png index 1936f7d..8b63cb2 100644 Binary files a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram.png and b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram.png differ diff --git a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@2x.png b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@2x.png index 97790f5..4a1f06a 100644 Binary files a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@2x.png and b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@2x.png differ diff --git a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@3x.png b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@3x.png index b1247c3..1aa3310 100644 Binary files a/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@3x.png and b/Miano/Assets.xcassets/Instruments/Mel Spectrogram.imageset/Mel Spectrogram@3x.png differ diff --git a/Miano/Instruments/MiniDrum/SingleDrumPad.swift b/Miano/Instruments/MiniDrum/SingleDrumPad.swift index 7ec04c9..fd773cd 100644 --- a/Miano/Instruments/MiniDrum/SingleDrumPad.swift +++ b/Miano/Instruments/MiniDrum/SingleDrumPad.swift @@ -8,14 +8,9 @@ import SwiftUI struct SingleDrumPad: View { - @State private var hovering: Bool = false let playing: Bool let drumpad: DrumSample - var selected: Bool { - return hovering || playing - } - var body: some View { ZStack { Rectangle() @@ -45,11 +40,6 @@ struct SingleDrumPad: View { .foregroundColor(drumpad.color) .brightness(0.5) } - .onHover { state in - withAnimation { - hovering = state - } - } } } diff --git a/Miano/Instruments/MiniKeyboard/EXSConductor.swift b/Miano/Instruments/MiniKeyboard/EXSConductor.swift index 1fd0497..7d86cc6 100644 --- a/Miano/Instruments/MiniKeyboard/EXSConductor.swift +++ b/Miano/Instruments/MiniKeyboard/EXSConductor.swift @@ -13,16 +13,19 @@ import SwiftUI import Tonic class InstrumentEXSConductor: ObservableObject, HasAudioEngine { + @Published var velocity: Float = 0.45 + var midiVelocity: MIDIVelocity { + MIDIVelocity(velocity * 200) + } + let engine = AudioEngine() var instrument = MIDISampler(name: "Instrument 1") func noteOn(pitch: Pitch, point _: CGPoint) { - print("Playing \(pitch.midiNoteNumber)") - instrument.play(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), velocity: 90, channel: 0) + instrument.play(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), velocity: midiVelocity, channel: 0) } func noteOff(pitch: Pitch) { - print("Stopping \(pitch.midiNoteNumber)") instrument.stop(noteNumber: MIDINoteNumber(pitch.midiNoteNumber), channel: 0) } diff --git a/Miano/Instruments/MiniKeyboard/UI/CustomKeys.swift b/Miano/Instruments/MiniKeyboard/UI/CustomKeys.swift index 5b34418..48ea553 100644 --- a/Miano/Instruments/MiniKeyboard/UI/CustomKeys.swift +++ b/Miano/Instruments/MiniKeyboard/UI/CustomKeys.swift @@ -16,7 +16,10 @@ struct CustomKeys: View { @EnvironmentObject var conductor: InstrumentEXSConductor @State var octaveRange = 0 - @Binding var layoutType: Int + @Binding var layoutFloat: Float // 0.1...3 (1, 2, 3) + var layoutType: Int { + Int(layoutFloat * 10) + } @Binding var customPitch: Int var lowestNote: Int { @@ -50,8 +53,8 @@ struct CustomKeys: View { struct CustomKeys_Previews: PreviewProvider { static var previews: some View { CustomKeys( - layoutType: .constant(1), - customPitch: .constant(6) + layoutFloat: .constant(0.1), + customPitch: .constant(2) ) .environmentObject(InstrumentEXSConductor()) } diff --git a/Miano/Instruments/MiniKeyboard/UI/MiniKeyboard.swift b/Miano/Instruments/MiniKeyboard/UI/MiniKeyboard.swift index c21acee..84a31fe 100644 --- a/Miano/Instruments/MiniKeyboard/UI/MiniKeyboard.swift +++ b/Miano/Instruments/MiniKeyboard/UI/MiniKeyboard.swift @@ -15,8 +15,8 @@ import Tonic struct MiniKeyboard: View { @Environment(\.controlActiveState) private var controlActiveState - @State private var layoutType: Int = 0 // 0, 1, 2 - @State private var pitch: Int = 3 // pitch * 8 = lowest note + @State private var layoutType: Float = 0 // 0, 1, 2 + @State private var octave: Int = 3 // pitch * 8 = lowest note @State private var amplitude: Float = 0.25 @State private var velocity: Float = 0.25 @@ -49,11 +49,14 @@ struct MiniKeyboard: View { Spacer() HStack { - SmallKnob(value: $amplitude) - .frame(width: 60) - .shadow(color: .white.opacity(0.125), radius: 12, y: 4) + SmallKnob( + value: $layoutType, + range: 0 ... 0.3 + ) + .frame(width: 60) + .shadow(color: .white.opacity(0.125), radius: 12, y: 4) - SmallKnob(value: $velocity) + SmallKnob(value: $conductor.velocity) .frame(width: 60) .shadow(color: .white.opacity(0.125), radius: 12, y: 4) } @@ -75,11 +78,11 @@ struct MiniKeyboard: View { } HStack { VStack(spacing: 0) { - PitchButton(type: .increase, pitch: $pitch) + OctaveButton(type: .increase, pitch: $octave) Divider().frame(width: 12) - PitchButton(type: .decrease, pitch: $pitch) + OctaveButton(type: .decrease, pitch: $octave) } .labelStyle(.iconOnly) .cornerRadius(12) @@ -89,8 +92,8 @@ struct MiniKeyboard: View { ) CustomKeys( - layoutType: $layoutType, - customPitch: $pitch + layoutFloat: $layoutType, + customPitch: $octave ) .background(.gray) @@ -126,11 +129,19 @@ struct MiniKeyboard: View { } struct KeyboardLayoutChanger: View { - @Binding var layoutType: Int + @Binding var layoutType: Float + var lt: Int { + Int(layoutType * 10) + } + let setLayout: Int + var st: Float { + Float(setLayout / 10) + } + let shortcutKey: KeyEquivalent - init(_ setLayout: Int, binded: Binding, shortcutKey: KeyEquivalent) { + init(_ setLayout: Int, binded: Binding, shortcutKey: KeyEquivalent) { self.setLayout = setLayout self._layoutType = binded self.shortcutKey = shortcutKey @@ -139,7 +150,7 @@ struct KeyboardLayoutChanger: View { var body: some View { Button { withAnimation { - layoutType = setLayout + layoutType = st } } label: { @@ -147,17 +158,17 @@ struct KeyboardLayoutChanger: View { Circle() .frame(width: 12) .foregroundColor( - layoutType == setLayout ? .green : .clear + lt == setLayout ? .green : .clear ) - .blur(radius: layoutType == setLayout ? 12 : 0) + .blur(radius: lt == setLayout ? 12 : 0) Circle() .frame(width: 6) .foregroundColor( - layoutType == setLayout ? .green : .white.opacity(0.25) + lt == setLayout ? .green : .white.opacity(0.25) ) .shadow( - color: layoutType == setLayout ? + color: lt == setLayout ? .green : .white.opacity(0.25), radius: 12 ) @@ -173,18 +184,18 @@ enum PitchType { case increase, decrease } -struct PitchButton: View { - let maxPitch = 9 - let minPitch = 0 +struct OctaveButton: View { + let maxOctave = 9 + let minOctave = 0 let type: PitchType @Binding var pitch: Int var inRange: Bool { if type == .decrease { - return pitch > minPitch + return pitch > minOctave } if type == .increase { - return pitch < maxPitch + return pitch < maxOctave } return false } @@ -205,12 +216,12 @@ struct PitchButton: View { Group { if type == .increase { Label( - "Increase Pitch", + "Increase Octave", systemImage: "triangle.fill" ) } else { Label( - "Decrease Pitch", + "Decrease Octave", systemImage: "arrowtriangle.down.fill" ) } diff --git a/Miano/Instruments/MiniSpectrogram/MiniSpectrogram.swift b/Miano/Instruments/MiniSpectrogram/MiniSpectrogram.swift index 237a8cd..c72fd35 100644 --- a/Miano/Instruments/MiniSpectrogram/MiniSpectrogram.swift +++ b/Miano/Instruments/MiniSpectrogram/MiniSpectrogram.swift @@ -17,7 +17,7 @@ struct MiniSpectrogram: View { } var body: some View { - SpectrogramView(audioSpectrogram: audioSpectrogram, .mel) + SpectrogramView(audioSpectrogram: audioSpectrogram, mode) .environmentObject(audioSpectrogram) .onAppear(perform: { audioSpectrogram.startRunning() diff --git a/Miano/Instruments/SharedComponents/SmallKnob.swift b/Miano/Instruments/SharedComponents/SmallKnob.swift index 63ae225..51dd7b1 100644 --- a/Miano/Instruments/SharedComponents/SmallKnob.swift +++ b/Miano/Instruments/SharedComponents/SmallKnob.swift @@ -11,10 +11,8 @@ import SwiftUI /// Knob in which you start by tapping in its bound and change the value by either horizontal or vertical motion public struct SmallKnob: View { @Binding var value: Float - var range: ClosedRange = 0.0 ... 1.0 - var backgroundColor: Color = .white - var foregroundColor: Color = .black.opacity(0.5) + var range: ClosedRange = 0.0 ... 1.0 var strokeColor: Color = .white.opacity(0.25) /// Initialize the knob with a bound value and range @@ -75,24 +73,6 @@ public struct SmallKnob: View { } } -public extension SmallKnob { - /// Modifier to change the background color of the knob - /// - Parameter backgroundColor: background color - func backgroundColor(_ backgroundColor: Color) -> SmallKnob { - var copy = self - copy.backgroundColor = backgroundColor - return copy - } - - /// Modifier to change the foreground color of the knob - /// - Parameter foregroundColor: foreground color - func foregroundColor(_ foregroundColor: Color) -> SmallKnob { - var copy = self - copy.foregroundColor = foregroundColor - return copy - } -} - struct SmallKnob_Previews: PreviewProvider { static var previews: some View { HStack { diff --git a/Miano/Instruments/VocalTrack/VocalTrack.swift b/Miano/Instruments/VocalTrack/VocalTrack.swift index fd408c3..90bd1bc 100644 --- a/Miano/Instruments/VocalTrack/VocalTrack.swift +++ b/Miano/Instruments/VocalTrack/VocalTrack.swift @@ -113,10 +113,6 @@ public struct ParameterRow: View { self.param = param } - func floatToDoubleRange(_ floatRange: ClosedRange) -> ClosedRange { - Double(floatRange.lowerBound) ... Double(floatRange.upperBound) - } - func getBinding() -> Binding { Binding( get: { param.value }, diff --git a/Miano/MainApp/InstrumentDetail.swift b/Miano/MainApp/InstrumentDetail.swift index 63daf05..4ac0a79 100644 --- a/Miano/MainApp/InstrumentDetail.swift +++ b/Miano/MainApp/InstrumentDetail.swift @@ -5,17 +5,20 @@ // Created by Aayush Pokharel on 2023-05-30. // +import AVFoundation import Colorful import SwiftUI struct InstrumentDetailView: View { + @State private var popoverShown: Bool = false + @Environment(\.openURL) var openURL @Environment(\.openWindow) var openWindow @State private var hovering: Bool = false let instrument: InstrumentModel var body: some View { ScrollView(.vertical, showsIndicators: false) { - ZStack { + ZStack(alignment: .topTrailing) { ZStack(alignment: .bottomLeading) { Image(instrument.image) .resizable() @@ -29,9 +32,9 @@ struct InstrumentDetailView: View { Text(instrument.emoji) Text("Launch Instrument") } - .font(hovering ? .largeTitle : .caption) + .font(hovering ? .largeTitle : .callout) .padding(.horizontal, hovering ? 0 : 6) - .frame(maxWidth: hovering ? .infinity : 128, maxHeight: hovering ? .infinity : 24) + .frame(maxWidth: hovering ? .infinity : 180, maxHeight: hovering ? .infinity : 24) } .padding(hovering ? 0 : 4) .background( @@ -61,6 +64,45 @@ struct InstrumentDetailView: View { .accessibilityIdentifier("Launch Instrument") .buttonStyle(.plain) } + + if instrument.microphoneUsed { + Label("Microphone Used", systemImage: "mic.fill") + .labelStyle(.iconOnly) + .font(.title2) + .padding(8) + .background(.ultraThinMaterial) + .clipShape( + Circle() + ) + .overlay( + Circle() + .stroke(.tertiary, lineWidth: 1) + ) + .padding(12) + .containerShape(Circle()) + .onTapGesture { + if AVCaptureDevice.authorizationStatus(for: .audio) != .authorized { + openURL(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone")!) + } + } + .onHover { state in + withAnimation { + popoverShown = state + } + } + .popover(isPresented: $popoverShown) { + VStack { + Text("This instrument uses microphone.") + if AVCaptureDevice.authorizationStatus(for: .audio) != .authorized { + Divider() + Text("Double click mic icon to open settings\nor\nGo to Settings > Privacy > Microphone > Check **Miano**") + .multilineTextAlignment(.center) + .foregroundStyle(.secondary) + } + } + .padding() + } + } } .cornerRadius(12) VStack(alignment: .leading) { diff --git a/Miano/Models/InstrumentModel.swift b/Miano/Models/InstrumentModel.swift index 3162c97..c835233 100644 --- a/Miano/Models/InstrumentModel.swift +++ b/Miano/Models/InstrumentModel.swift @@ -16,7 +16,6 @@ struct InstrumentModel: Identifiable, Equatable { let emoji: String let tags: [String] let microphoneUsed: Bool - var launched: Bool = false init( type: InstrumentType, description: String, @@ -79,11 +78,6 @@ struct InstrumentModel: Identifiable, Equatable { tags: ["relaxation", "sound therapy", "sleep aid"] ) ] - - static func getInstrument(_ type: InstrumentType) -> InstrumentModel? { - return allInstruments.filter { $0.type == type }.first - } - static let firstInstrument: InstrumentModel = .allInstruments.first! }