Skip to content

Commit 34d6e1f

Browse files
committed
feat: 🎸 write audio to wav file(s)
use of "-f ~/somefile" flag will write the binaural beat audio to file. use of "-s" with this flag will create separate output files for later usage. ✅ Closes: #2
1 parent 498a3cf commit 34d6e1f

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

‎WORKSPACE

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pinned_maven_install()
8080

8181
git_repository(
8282
name = "zachgrayio_scalaudio",
83-
commit = "6d7dfabd71392a6b15ff3eea8b86945b12ad20f5",
83+
commit = "a0aa2daccdfee2fc78b60c1a898249c0427b6a7a",
8484
remote = "http://github.com/zachgrayio/scalaudio.git",
85-
shallow_since = "1578573315 +1100"
85+
shallow_since = "1578711564 +1100"
8686
)

‎cli/src/main/scala/io/zachgray/binauralBeats/cli/BinauralApp.scala

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@ object BinauralApp extends App {
1212
*/
1313
override def main(args: Array[String]): Unit = {
1414
parseBinauralBeatsConfiguration(args) match {
15-
case Some(config) => BinauralEngine.play(config)
15+
// valid config
16+
case Some(config) => {
17+
config.fileName match {
18+
// filename option is provided, output to file
19+
case Some(str) => BinauralEngine.writeToFile(config)
20+
// no filename flag so invoke playback on default audio device
21+
case None => BinauralEngine.play(config)
22+
}
23+
}
24+
// invalid config
1625
case _ =>
1726
}
1827
}
@@ -67,7 +76,17 @@ object BinauralApp extends App {
6776

6877
opt[Int]('d', "duration")
6978
.action((x, c) => c.copy(duration = x))
70-
.text("the duration for which to play the binaural audio - an integer property")
79+
.text("the duration for which to play the binaural audio - an integer property"),
80+
81+
opt[String]('f', "file")
82+
.optional()
83+
.action((x, c) => c.copy(fileName = Some(x)))
84+
.text("the output filename - a string property"),
85+
86+
opt[Unit]('s', "separate_files")
87+
.optional()
88+
.action((_, c) => c.copy(separateFiles = true))
89+
.text("split left and right audio into separate files - a boolean property")
7190
)
7291
}
7392
OParser.parse(optionsParser, args, BinauralBeatsConfiguration())
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
package io.zachgray.binauralBeats.engine
22

3-
case class BinauralBeatsConfiguration(pitch: Double = 300, binauralPitch: Double = 10, duration: Int = 30)
3+
case class BinauralBeatsConfiguration(
4+
pitch: Double = 300,
5+
binauralPitch: Double = 10,
6+
duration: Int = 30,
7+
fileName: Option[String] = null,
8+
separateFiles: Boolean = false
9+
)

‎engine/src/main/scala/io/zachgray/binauralBeats/engine/BinauralEngine.scala

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package io.zachgray.binauralBeats.engine
22

3+
import io.zachgray.binauralBeats.engine.BinauralEngine.{createBinauralFrameFunction, createDualBinauralFrameFunctions}
34
import scalaudio.core.types.{Frame, Pitch}
4-
import scalaudio.core.{AudioContext, ScalaudioConfig}
5+
import scalaudio.core.{AudioContext, DefaultAudioContext, ScalaudioConfig}
56
import scalaudio.units.AmpSyntax
67
import scalaudio.units.filter.{GainFilter, StereoPanner}
78
import scalaudio.units.ugen.{OscState, Sine}
89
import scalaz.Scalaz._
910

1011
import scala.concurrent.duration._
1112

12-
object BinauralEngine extends AmpSyntax {
13+
object BinauralEngine extends AmpSyntax with DefaultAudioContext {
1314

1415
/**
1516
* Creates a stereo audio context and invokes playback for the specified configuration.
1617
*
1718
* @param binauralBeatConfiguration the configuration of the binaural beats which should be generated.
1819
*/
1920
def play(binauralBeatConfiguration: BinauralBeatsConfiguration): Unit = {
20-
implicit val audioContext: AudioContext = AudioContext(ScalaudioConfig(nOutChannels = 2))
2121
playback(
2222
frameFunc = createBinauralFrameFunction(
2323
pitch = binauralBeatConfiguration.pitch,
@@ -27,6 +27,37 @@ object BinauralEngine extends AmpSyntax {
2727
)
2828
}
2929

30+
/**
31+
* Creates a stereo audio context and invokes `record` for the specified configuration.
32+
*
33+
* @param config the configuration of the binaural beats which should be generated.
34+
*/
35+
def writeToFile(config: BinauralBeatsConfiguration): Unit = {
36+
if (config.separateFiles) {
37+
writeToSeparateFiles(config)
38+
} else {
39+
record(
40+
fileName = config.fileName.get,
41+
frameFunc = createBinauralFrameFunction(
42+
pitch = config.pitch,
43+
binauralPitch = config.binauralPitch
44+
),
45+
duration = config.duration seconds
46+
)
47+
}
48+
}
49+
50+
private def writeToSeparateFiles(config: BinauralBeatsConfiguration): Unit = {
51+
val ffs = createDualBinauralFrameFunctions(config.pitch, config.binauralPitch)
52+
for((ff, index) <- ffs.zipWithIndex) {
53+
record(
54+
fileName = config.fileName.get + "_" + index,
55+
frameFunc = ff,
56+
duration = config.duration seconds
57+
)
58+
}
59+
}
60+
3061
/**
3162
* Creates a sine frame function with the specified parameters
3263
*
@@ -60,14 +91,25 @@ object BinauralEngine extends AmpSyntax {
6091
* @return the binaural frame function
6192
*/
6293
def createBinauralFrameFunction(pitch: Double, binauralPitch: Double)(implicit audioContext: AudioContext): () => Frame = {
63-
val frameFunctions = Seq(
64-
createSineWaveFrameFunction(pitch, 0, 1),
65-
createSineWaveFrameFunction(pitch + binauralPitch, 1, 1)
66-
)
94+
val frameFunctions = createDualBinauralFrameFunctions(pitch, binauralPitch)
6795
var interleave = false
6896
() => {
6997
interleave = !interleave
7098
frameFunctions(interleave.compare(false))()
7199
}
72100
}
101+
102+
/**
103+
*
104+
* @param pitch The baseline pitch (in Hz) of the binaural beat
105+
* @param binauralPitch The pitch of the binaural frequency which should be generated
106+
* @param audioContext The audio context
107+
* @return the binaural frame functions
108+
*/
109+
def createDualBinauralFrameFunctions(pitch: Double, binauralPitch: Double)(implicit audioContext: AudioContext): Seq[() => Frame] = {
110+
Seq(
111+
createSineWaveFrameFunction(pitch, 0, 1),
112+
createSineWaveFrameFunction(pitch + binauralPitch, 1, 1)
113+
)
114+
}
73115
}

0 commit comments

Comments
 (0)