Skip to content

Commit

Permalink
Merge pull request #3 from zachgrayio/feature/write-to-file
Browse files Browse the repository at this point in the history
feat: write audio to wav file(s)
  • Loading branch information
zachgrayio authored Jan 11, 2020
2 parents 498a3cf + 34d6e1f commit e1f68d9
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 12 deletions.
4 changes: 2 additions & 2 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pinned_maven_install()

git_repository(
name = "zachgrayio_scalaudio",
commit = "6d7dfabd71392a6b15ff3eea8b86945b12ad20f5",
commit = "a0aa2daccdfee2fc78b60c1a898249c0427b6a7a",
remote = "http://github.com/zachgrayio/scalaudio.git",
shallow_since = "1578573315 +1100"
shallow_since = "1578711564 +1100"
)
23 changes: 21 additions & 2 deletions cli/src/main/scala/io/zachgray/binauralBeats/cli/BinauralApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ object BinauralApp extends App {
*/
override def main(args: Array[String]): Unit = {
parseBinauralBeatsConfiguration(args) match {
case Some(config) => BinauralEngine.play(config)
// valid config
case Some(config) => {
config.fileName match {
// filename option is provided, output to file
case Some(str) => BinauralEngine.writeToFile(config)
// no filename flag so invoke playback on default audio device
case None => BinauralEngine.play(config)
}
}
// invalid config
case _ =>
}
}
Expand Down Expand Up @@ -67,7 +76,17 @@ object BinauralApp extends App {

opt[Int]('d', "duration")
.action((x, c) => c.copy(duration = x))
.text("the duration for which to play the binaural audio - an integer property")
.text("the duration for which to play the binaural audio - an integer property"),

opt[String]('f', "file")
.optional()
.action((x, c) => c.copy(fileName = Some(x)))
.text("the output filename - a string property"),

opt[Unit]('s', "separate_files")
.optional()
.action((_, c) => c.copy(separateFiles = true))
.text("split left and right audio into separate files - a boolean property")
)
}
OParser.parse(optionsParser, args, BinauralBeatsConfiguration())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package io.zachgray.binauralBeats.engine

case class BinauralBeatsConfiguration(pitch: Double = 300, binauralPitch: Double = 10, duration: Int = 30)
case class BinauralBeatsConfiguration(
pitch: Double = 300,
binauralPitch: Double = 10,
duration: Int = 30,
fileName: Option[String] = null,
separateFiles: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package io.zachgray.binauralBeats.engine

import io.zachgray.binauralBeats.engine.BinauralEngine.{createBinauralFrameFunction, createDualBinauralFrameFunctions}
import scalaudio.core.types.{Frame, Pitch}
import scalaudio.core.{AudioContext, ScalaudioConfig}
import scalaudio.core.{AudioContext, DefaultAudioContext, ScalaudioConfig}
import scalaudio.units.AmpSyntax
import scalaudio.units.filter.{GainFilter, StereoPanner}
import scalaudio.units.ugen.{OscState, Sine}
import scalaz.Scalaz._

import scala.concurrent.duration._

object BinauralEngine extends AmpSyntax {
object BinauralEngine extends AmpSyntax with DefaultAudioContext {

/**
* Creates a stereo audio context and invokes playback for the specified configuration.
*
* @param binauralBeatConfiguration the configuration of the binaural beats which should be generated.
*/
def play(binauralBeatConfiguration: BinauralBeatsConfiguration): Unit = {
implicit val audioContext: AudioContext = AudioContext(ScalaudioConfig(nOutChannels = 2))
playback(
frameFunc = createBinauralFrameFunction(
pitch = binauralBeatConfiguration.pitch,
Expand All @@ -27,6 +27,37 @@ object BinauralEngine extends AmpSyntax {
)
}

/**
* Creates a stereo audio context and invokes `record` for the specified configuration.
*
* @param config the configuration of the binaural beats which should be generated.
*/
def writeToFile(config: BinauralBeatsConfiguration): Unit = {
if (config.separateFiles) {
writeToSeparateFiles(config)
} else {
record(
fileName = config.fileName.get,
frameFunc = createBinauralFrameFunction(
pitch = config.pitch,
binauralPitch = config.binauralPitch
),
duration = config.duration seconds
)
}
}

private def writeToSeparateFiles(config: BinauralBeatsConfiguration): Unit = {
val ffs = createDualBinauralFrameFunctions(config.pitch, config.binauralPitch)
for((ff, index) <- ffs.zipWithIndex) {
record(
fileName = config.fileName.get + "_" + index,
frameFunc = ff,
duration = config.duration seconds
)
}
}

/**
* Creates a sine frame function with the specified parameters
*
Expand Down Expand Up @@ -60,14 +91,25 @@ object BinauralEngine extends AmpSyntax {
* @return the binaural frame function
*/
def createBinauralFrameFunction(pitch: Double, binauralPitch: Double)(implicit audioContext: AudioContext): () => Frame = {
val frameFunctions = Seq(
createSineWaveFrameFunction(pitch, 0, 1),
createSineWaveFrameFunction(pitch + binauralPitch, 1, 1)
)
val frameFunctions = createDualBinauralFrameFunctions(pitch, binauralPitch)
var interleave = false
() => {
interleave = !interleave
frameFunctions(interleave.compare(false))()
}
}

/**
*
* @param pitch The baseline pitch (in Hz) of the binaural beat
* @param binauralPitch The pitch of the binaural frequency which should be generated
* @param audioContext The audio context
* @return the binaural frame functions
*/
def createDualBinauralFrameFunctions(pitch: Double, binauralPitch: Double)(implicit audioContext: AudioContext): Seq[() => Frame] = {
Seq(
createSineWaveFrameFunction(pitch, 0, 1),
createSineWaveFrameFunction(pitch + binauralPitch, 1, 1)
)
}
}

0 comments on commit e1f68d9

Please sign in to comment.