ScalaAudioFile
statement
ScalaAudioFile is a Scala library to read and write audio files. It is (C)opyright 2004–2018 by Hanns Holger Rutz. All rights reserved. ScalaAudioFile is released under the GNU Lesser General Public License v2.1+ and comes with absolutely no warranties. To contact the author, send an email to contact at sciss.de
requirements / installation
ScalaAudioFile currently compiles against Scala 2.12, 2.11 using sbt.
To use the library in your project:
"de.sciss" %% "scalaaudiofile" % v
The current version v
is "1.4.7"
contributing
Please see the file CONTRIBUTING.md
supported formats
name | read/write | notes |
---|---|---|
AIFF | R/W | |
WAVE | R/W | |
IRCAM | R/W | aka BICSF |
NeXT | R/W | aka Snd and AU |
Wave64 | R/W | |
Raw | R/W | headerless |
getting started
ScalaAudioFile currently supports reading and writing files, while still lacking a few features of the Java predecessor de.sciss.io.AudioFile
from ScissLib, such as reading and writing markers and application specific chunks.
- To open an audio file for reading:
AudioFile.openRead(aFile)
orAudioFile.openRead(anInputStream)
. TheInputStream
variant has limited functionality, e.g. you cannot seek into the file, but only read sequentially. The stream variant can be used to decode files from an HTTP connection or in-memory (ByteArrayInputStream
). - To just retrieve the specification of an existing file, to see if it can be decoded, what is length, number of channels and format are:
AudioFile.readSpec(fileOrPathName)
. - To open a file for writing:
AudioFile.openWrite(aFile, spec)
orAudioFile.openWrite(anOutputStream, spec)
To the user, frame data is always represented as de-interleaved 32-bit floating point data, so you create a user buffer through Array.ofDim[Float](numChannels, bufFrames)
, or use a convenience method such as AudioFile.buffer(...)
.
The AudioFile
implementation is currently not thread-safe, but synchronization is planned. At the moment, make sure you are not sharing an instance across threads. Alternatively, use a lock and validate the frame position before each read/write.
Here is an example of opening an existing file, reading through it to determine the maximum sample magnitude, then writing a normalized version to a new file:
import de.sciss.synth.io._
val in = AudioFile.openRead("input.aif")
// for the output, switch to AIFF 24-bit integer,
// but keep the other parameters (sample-rate and number-of-channels)
val outSpec = in.spec.copy(fileType = AudioFileType.AIFF,
sampleFormat = SampleFormat.Int24)
val out = AudioFile.openWrite("output.aif", outSpec)
// create a buffer
val bufSz = 8192 // perform operations in blocks of this size
val buf = in.buffer(bufSz)
// first pass: determine maximum magnitude
var mag = 0f
var remain = in.numFrames
while (remain > 0) {
val chunk = math.min(bufSz, remain).toInt
in.read(buf, 0, chunk)
buf.foreach { chan =>
mag = math.max(mag, math.abs(chan.maxBy(math.abs)))
}
remain -= chunk
}
println(f"Maximum magnitude detected: $mag%1.3f")
// second pass: adjust gain and write output
require(mag > 0)
val gain = 1f / mag
in.seek(0L) // start over from the beginning of the file
remain = in.numFrames
while (remain > 0) {
val chunk = math.min(bufSz, remain).toInt
in.read(buf, 0, chunk)
buf.foreach { chan =>
for (i <- 0 until chunk) {
chan(i) *= gain
}
}
out.write(buf, 0, chunk)
remain -= chunk
}
out.close()
in.close()
properties
There is a system property de.sciss.synth.io.AudioFile.DirectMemory
whose value can be set to true
in order to use direct-memory buffers (ByteBuffer.allocateDirect
). Reading in a 1 GB is around 6% faster with direct memory. Direct memory can have the disadvantage that it requires an individual VM switch to adjust the maximum available memory before throwing an out-of-memory error. The default is false.
For all further information, please refer to the API docs. They can be created with sbt doc
.