Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Musical Phantoms

TJ Usiyan
September 16, 2016

Musical Phantoms

Slides from my NSSpain 2016 talk

TJ Usiyan

September 16, 2016
Tweet

More Decks by TJ Usiyan

Other Decks in Programming

Transcript

  1. Caveats 4 Details omitted 4 More Signals, Less Types 4

    This is all exploratory © TJ Usiyan, 2016 2
  2. This is All Exploratory 4 Problems of Interest 4 Semantics

    4 Sample Rate 4 Infinite Sequences 4 User Input © TJ Usiyan, 2016 3
  3. This is All Exploratory 1. Make it work 2. Make

    it fast 3. Make it pretty © TJ Usiyan, 2016 4
  4. This is All Exploratory 1. Make it work 2. Make

    it pretty 3. Could it be prettier? 4. Make it prettier 5. Make it fast © TJ Usiyan, 2016 5
  5. The Plan 4 Math 4 Audio 4 Types 4 Phantom

    Types © TJ Usiyan, 2016 7
  6. Hertz.swift typealias Samples = Int typealias Seconds = Double ///

    Cycles (or periods) per second typealias Hertz = Double © TJ Usiyan, 2016 11
  7. SampleRateType.swift extension SampleRateType { var frameDuration: Seconds { return 1.0

    / Self.value } func convert(samples: Samples) -> Seconds { return frameDuration * Double(samples) } func convertToSamples(seconds: Seconds) -> Samples { return Int(Self.value * seconds) } } © TJ Usiyan, 2016 13
  8. public struct SampleRate_44100 : SampleRateType { public static let hertz:

    Hertz = 44100.0 public init() {} } public struct SampleRate_50 : SampleRateType { public static let hertz: Hertz = 50.0 public init() { } } public struct SampleRate_100 : SampleRateType { public static let hertz: Hertz = 100.0 public init() { } } public struct SampleRate_500 : SampleRateType { public static let hertz: Hertz = 500.0 public init() { } } public struct SampleRate_1000 : SampleRateType { public static let hertz: Hertz = 1000.0 public init() { } } © TJ Usiyan, 2016 14
  9. public protocol SignalType : Sequence { associatedtype SampleRate : SampleRateType

    associatedtype Iterator : SignalGeneratorType } public protocol SignalGeneratorType : IteratorProtocol { associatedtype SampleRate : SampleRateType } © TJ Usiyan, 2016 16
  10. Constant.swift public struct Constant<T, SR : SampleRateType> { public let

    value:T public init(value:T) { self.value = value } } © TJ Usiyan, 2016 18
  11. Constant.swift extension Constant : SignalType { public typealias SampleRate =

    SR public typealias Iterator = ConstantGenerator<T, SR> public func makeIterator() -> Iterator { return Iterator(signal: self) } } © TJ Usiyan, 2016 19
  12. Constant.swift public struct ConstantGenerator<T, SR : SampleRateType> { fileprivate let

    value:T fileprivate init(value:T) { self.value = value } fileprivate init(signal:Constant<T, SR>) { self.init(value: signal.value) } } © TJ Usiyan, 2016 20
  13. TimeSignal.swift extension TimeSignal : SignalType { typealias SampleRate = SR

    typealias Iterator = TimeSignalGenerator<SR> func makeIterator() -> Iterator { return Iterator(index: 0) } } © TJ Usiyan, 2016 25
  14. TimeSignal.swift public struct TimeSignalGenerator<SR : SampleRateType> { fileprivate var index:Index

    internal init(index:Index) { self.index = index } } © TJ Usiyan, 2016 26
  15. TimeSignal.swift extension TimeSignalGenerator : SignalGeneratorType { public typealias SampleRate =

    SR public typealias Element = (frame:Int, seconds:Double) public typealias Index = Int public mutating func next() -> Element? { let value = (frame:index, seconds:Double(index) / SR.hertz) index += 1 return value } } © TJ Usiyan, 2016 27
  16. TimeSignal.swift extension TimeSignal : SignalType { typealias SampleRate = SR

    typealias Iterator = TimeSignalGenerator<SR> func makeIterator() -> Iterator { return Iterator(index: 0) } } © TJ Usiyan, 2016 28
  17. Oscillators are Continuous Functions of Time struct ContinuousTimeFunction<OutputType, SR :

    SampleRateType> { let function:(_ time:Double) -> OutputType init(function:@escaping (_ time:Double) -> OutputType) { self.function = function } } © TJ Usiyan, 2016 29
  18. Continuous Functions of Time public struct ContinuousTimeFunctionGenerator<OutputType, SR : SampleRateType>

    { public typealias Index = Int public typealias Function = (_ time:Double) -> OutputType fileprivate var clock:TimeSignal<SR>.Iterator fileprivate let function:Function public init(index:Index, function:@escaping Function) { self.clock = TimeSignalGenerator<SR>(index:index) self.function = function } fileprivate init(clock:TimeSignalGenerator<SR>, function:@escaping Function) { self.clock = clock self.function = function } } © TJ Usiyan, 2016 30
  19. Sine let contFnSin: ContinuousTimeDoubleSignal = { let frequency: Hertz =

    2 let period: Second = twoPi let initialPhase: Double = 0 let fn = { sample in return sin(initialPhase + sample * frequency * period) } return ContinuousTimeFunction(function: fn) }() © TJ Usiyan, 2016 31
  20. Phase class Phase<SR : SampleRateType> { var value: Seconds {

    didSet { if value >= period { value = value.truncatingRemainder(dividingBy: period) } } } let period: Seconds init(value: Seconds, period: Seconds) { self.value = fabs(value).truncatingRemainder(dividingBy: period) self.period = period } } © TJ Usiyan, 2016 32
  21. Drawbacks 4 Lock in to sample rate 4 Limitations with

    generics 4 Inference. So much inference. © TJ Usiyan, 2016 34
  22. Based on "Audio Processing and Sound Synthesis in Haskell" by

    Eric Cheng and Paul Hudak © TJ Usiyan, 2016 40