Created
April 20, 2012 22:05
-
-
Save miguel-negrao/2432229 to your computer and use it in GitHub Desktop.
My take on synthesis with lazy infinite lists
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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