Skip to content

Instantly share code, notes, and snippets.

@miguel-negrao
Created April 20, 2012 22:05
Show Gist options
  • Save miguel-negrao/2432229 to your computer and use it in GitHub Desktop.
Save miguel-negrao/2432229 to your computer and use it in GitHub Desktop.
My take on synthesis with lazy infinite lists
/**
* User: miguelnegrao
* Date: 20/04/12
*/
import annotation.tailrec
import javax.sound.sampled._
import math._
import scalaz._
import Scalaz._
import EphemeralStream._
object MyTestApp {
val sampleRate = 44100
val bitSize = 8
val blockSize = 128
val r = new scala.util.Random
implicit def intToSignal(a:Int):Signal = inf(a.toDouble)
implicit def doubleToSignal(a:Double):Signal = inf(a)
implicit val ephemeralStreamFunctor: Functor[EphemeralStream] = new Functor[EphemeralStream] {
def fmap[A, B](a: EphemeralStream[A], f: A => B) =
a.map(f)
}
type Signal = EphemeralStream[Double]
def repeat(a:Double, f:Double=>Double):EphemeralStream[Double] = cons(a, repeat(f(a),f) )
def inf(a:Double):Signal = cons(a, inf(a) )
def hold(stream:Signal, i:Int = 4) = stream.flatMap{ x:Double => EphemeralStream.range(0,i).map(j=>x) }
def myzip(s1:Signal, s2:Signal): EphemeralStream[(Double,Double)] =
if( s1.isEmpty || s2.isEmpty) EphemeralStream.empty else
cons( (s1.head(),s2.head()), myzip( s1.tail(), s2.tail() ) )
def myzip3(s1:Signal, s2:Signal, s3:Signal): EphemeralStream[(Double,Double,Double)] =
if( s1.isEmpty || s2.isEmpty || s3.isEmpty) EphemeralStream.empty else
cons( (s1.head(),s2.head(), s3.head()), myzip3( s1.tail(), s2.tail(), s3.tail() ) )
def time = repeat( 0.0d, { t => t + (1 / sampleRate.toDouble) } )
def saw( freq:Double ) = time.map( { t: Double => 0.5*(2*((t * freq) % 1.0)-1.0) } )
def whiteNoise = repeat( 0.0d, { t => 0.5 * ( (r.nextDouble()*2)-1.0) } )
def clipNoise0( freq:Double ) = hold( repeat( 0.0d, { t => 0.5 * ( (r.nextDouble().round.toDouble*2)-1.0) } ), (sampleRate / freq).toInt )
def sine( freq:Signal = 400.0, phase:Signal = 0.0 ) = myzip3(time, freq, phase).map( x => x match { case (t,b,p) => sin( (t * b * 2 * Pi) + p )} )
def distort1( signal:Signal ) = signal.map{ d:Double =>
if(d<0.25) {
0.0
} else {
if (d<0.5) {
0.25
} else
if (d<0.75) {
0.5
} else {
0.75
}
}
}
def main(args: Array[String]) {
//val signal = modulateSine( sine(20000).map{ x => 400 + (x*10) } )
//val stream = saw(200).map{ x => 400.0 + (x*100.0) }
//val signal = (sine(10000) |@| sine(20000)){ (x,y) => (x,y) }
//val signal = myzip(time, sine(10)).map( x => x match { case (t,b) => sin( t * 400 + (b*10) * 2 * Pi )} )
//val signal = clipNoise0(1000)
//val signal = sine( sine(1).map{ x => 400+(x*2) } )
//val signal = sine( 400 )
//val signal = myzip( sine(200), clipNoise0(4000) ).map( x => x match { case (a,b) => a+b/4 } )
val noise = whiteNoise
val signal = myzip( noise, noise.tail() ).map( x => x match { case (a,b) => (a-b)/2 } )
//val signal = sine(8000).map{ x => 8000+x }
println( signal take 100 toList)
play( signal )
}
def play(stream: EphemeralStream[Double]) {
try {
val audioFormat: AudioFormat = new AudioFormat(sampleRate, bitSize, 1, true, true)
val line: SourceDataLine = AudioSystem.getSourceDataLine(audioFormat)
line.open(audioFormat)
line.start()
process(stream, blockSize, line)
line.drain()
line.close()
}
catch {
case e: Exception => {
e.printStackTrace()
}
}
}
@tailrec def process(stream: EphemeralStream[Double], blockSize: Int, line: SourceDataLine) {
val (restOfStream, byteList) = getBlock( stream, IndexedSeq.empty[Byte], blockSize)
val byteArray = byteList.toArray
line.write(byteArray, 0, byteArray.length)
process( restOfStream, blockSize, line )
}
@tailrec def getBlock(stream: EphemeralStream[Double], byteArray:IndexedSeq[Byte], i:Int ) : (EphemeralStream[Double], IndexedSeq[Byte]) =
if(i == 0 ) {
(stream, byteArray)
} else {
getBlock(stream.tail(), byteArray :+ (stream.head() * 127d).asInstanceOf[Byte], i-1 )
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment