Category talk:Wren-sound

From Rosetta Code

Source code

/* Module "sound.wren" */

import "io" for File

/* Wav enables the creation of simple .wav files for playing by external utiliies. */
class Wav {
    // Private method to convert a 16-bit unsigned integer 'x'
    // to a list of 2 bytes in little-endian format.
    static fromInt16LE_(x) {
        var bytes = List.filled(2, 0)
        bytes[0] = x         & 255
        bytes[1] = (x >> 8)  & 255
        return bytes
    }

    // Private method to convert a 32-bit unsigned integer 'x'
    // to a list of 4 bytes in little-endian format.
    static fromInt32LE_(x) {
        var bytes = List.filled(4, 0)
        bytes[0] = x         & 255
        bytes[1] = (x >> 8)  & 255
        bytes[2] = (x >> 16) & 255
        bytes[3] = (x >> 24) & 255
        return bytes
    }

    // Creates or overwrites an existing .wav file on disk using the given sample rate.
    // Such files are always single channel and use the PCM format.
    static create(filePath, data, sampleRate) {
        if (filePath.type != String) Fiber.abort("File path must be a string.")
        if (data.type != List || data.count == 0 || data[0].type != Num || !data[0].isInteger) {
            Fiber.abort("Data must be a non-empty list of whdr.")
        }
        if (sampleRate.type != Num || !sampleRate.isInteger || sampleRate < 1) {
            Fiber.abort("Sample rate must be a positive integer.")
        }
        var dataLength = data.count
        var hdrSize = 44
        var fileLen = dataLength + hdrSize - 8  // file size - 8

        // buffers
        var buf2 = List.filled(2, 0)
        var buf4 = List.filled(4, 0)

        // WAV header
        var whdr = []
        whdr.addAll("RIFF".bytes)
        whdr.addAll(fromInt32LE_(fileLen))    // fileLen
        whdr.addAll("WAVE".bytes)
        whdr.addAll("fmt ".bytes)
        whdr.addAll(fromInt32LE_(16))         // length of format data (= 16)
        var temp = fromInt16LE_(1)
        whdr.addAll(temp)                     // type of format (= 1 (PCM))
        whdr.addAll(temp)                     // number of channels (= 1)
        var temp2 = fromInt32LE_(sampleRate)
        whdr.addAll(temp2)                    // sample rate (= sr)
        whdr.addAll(temp2)                    // sr * bps(8) * channels(1) / 8 (= sr)
        whdr.addAll(temp)                     // bps(8) * channels(1) / 8 (= 1)
        whdr.addAll(fromInt16LE_(8))          // bits per sample (= 8)
        whdr.addAll("data".bytes)
        whdr.addAll(fromInt32LE_(dataLength)) // size of data section

        // write header and data to file
        File.create(filePath) { |file|
            for (b in whdr) file.writeBytes(String.fromByte(b))
            for (b in data) file.writeBytes(String.fromByte(b))            
        }
    }
}