summaryrefslogtreecommitdiff
path: root/autoutils
diff options
context:
space:
mode:
Diffstat (limited to 'autoutils')
-rw-r--r--autoutils/audio.go83
-rw-r--r--autoutils/batches.go56
-rw-r--r--autoutils/colorconversion.go57
-rw-r--r--autoutils/functiongenerator.go227
4 files changed, 423 insertions, 0 deletions
diff --git a/autoutils/audio.go b/autoutils/audio.go
new file mode 100644
index 0000000..19ae5f3
--- /dev/null
+++ b/autoutils/audio.go
@@ -0,0 +1,83 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package autoutils
+
+import (
+ "io"
+ "encoding/binary"
+)
+
+// Write a header to writer. You need to decide ahead of time how many samples
+// you want.
+func WriteAudioHeader(writer io.Writer, nSamples int64, channels, sampleRate int32) error {
+ w := func (data interface{}) error {
+ return binary.Write(writer, binary.LittleEndian, data)
+ }
+ var err error
+ if err = w([]byte("RIFF")); err != nil { return err }
+ var chunkSize1 uint32 = 36 + uint32(nSamples)
+ if err = w(chunkSize1); err != nil { return err }
+ if err = w([]byte("WAVEfmt ")); err != nil { return err }
+ var subchunk1size uint32 = 16
+ if err = w(subchunk1size); err != nil { return err }
+ var audioFormat uint16 = 1
+ if err = w(audioFormat); err != nil { return err }
+ var nChannels uint16 = uint16(channels)
+ if err = w(nChannels); err != nil { return err }
+ var srate uint32 = uint32(sampleRate)
+ if err = w(srate); err != nil { return err }
+ var byteRate uint32 = srate * uint32(nChannels)
+ if err = w(byteRate); err != nil { return err }
+ var blockAlign uint16 = nChannels
+ if err = w(blockAlign); err != nil { return err }
+ var bitsPerSample uint16 = 8
+ if err = w(bitsPerSample); err != nil { return err }
+ if err = w([]byte("data")); err != nil { return err }
+ var chunkSize2 uint32 = uint32(nSamples) * uint32(nChannels)
+ if err = w(chunkSize2); err != nil { return err }
+ return nil
+}
+
+
+// Writes some samples to the writer. You will need to write a header before
+// any samples.
+func WriteAudioSamples(writer io.Writer, samples []uint8) error {
+ return binary.Write(writer, binary.LittleEndian, samples)
+}
+
+/*
+Writes audio data in WAV format to the writer. There is only support for 8-bit
+audio. If there are multiple channels, audio[0] should refer to the first sample
+for the first channel, audio[1] should refer to the first sample for the second
+channel, etc.
+WAV does not support more then 65535 channels (but what are you doing if
+you're using that many?!)
+*/
+func WriteAudio(writer io.Writer, audio []uint8, channels, sampleRate int32) error {
+
+ err := WriteAudioHeader(writer, int64(len(audio)), channels, sampleRate)
+ if err != nil {
+ return err
+ }
+ if err = WriteAudioSamples(writer, audio); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/autoutils/batches.go b/autoutils/batches.go
new file mode 100644
index 0000000..fb39fc1
--- /dev/null
+++ b/autoutils/batches.go
@@ -0,0 +1,56 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package autoutils
+
+import (
+ "fmt"
+)
+
+/*
+This function runs f in batches of batchSize. n will be the number in the
+sequence. f should send an error to errs if it wants this function to return
+that error, and otherwise should send nil when it is done. Before each batch,
+a message will be printed, starting with progress, and showing how many batches
+have been completed so far out of the total number of batches.
+*/
+const batchSize = 32
+func RunInBatches(number int64, progress string, f func (n int64, errs chan<- error)) error {
+ nBatches := number / batchSize
+ errs := make(chan error)
+ for batch := int64(0); batch <= nBatches; batch++ {
+ fmt.Println(progress, batch+1, "/", nBatches+1)
+ thisBatchSize := batchSize
+ if batch == nBatches {
+ // Deal with case of last batch
+ thisBatchSize = int(number - nBatches * batchSize)
+ }
+ for task := 0; task < thisBatchSize; task++ {
+ go f(int64(task) + batchSize * batch, errs)
+ }
+
+ for completed := 0; completed < thisBatchSize; completed++ {
+ err := <-errs
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+} \ No newline at end of file
diff --git a/autoutils/colorconversion.go b/autoutils/colorconversion.go
new file mode 100644
index 0000000..af21668
--- /dev/null
+++ b/autoutils/colorconversion.go
@@ -0,0 +1,57 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package autoutils
+import (
+ "math"
+)
+
+func HSVToRGB(h uint8, s uint8, v uint8) (uint8, uint8, uint8) {
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
+ V := float64(v) / 256
+ S := float64(s) / 256
+ C := V * S
+ H := float64(h) / (256/6)
+ X := float64(C) * (1 - math.Abs(math.Mod(H, 2) - 1))
+ var r, g, b float64
+ switch true {
+ case s == 0:
+ r, g, b = 0, 0, 0
+ case H <= 1:
+ r, g, b = C, X, 0
+ case H <= 2:
+ r, g, b = X, C, 0
+ case H <= 3:
+ r, g, b = 0, C, X
+ case H <= 4:
+ r, g, b = 0, X, C
+ case H <= 5:
+ r, g, b = X, 0, C
+ default:
+ r, g, b = C, 0, X
+ }
+ m := V - C
+ r += m
+ g += m
+ b += m
+ r *= 255
+ g *= 255
+ b *= 255
+ return uint8(r), uint8(g), uint8(b)
+}
diff --git a/autoutils/functiongenerator.go b/autoutils/functiongenerator.go
new file mode 100644
index 0000000..7acb680
--- /dev/null
+++ b/autoutils/functiongenerator.go
@@ -0,0 +1,227 @@
+/*
+Copyright (C) 2019 Leo Tenenbaum
+
+This file is part of AutoArt.
+
+AutoArt is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+AutoArt is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AutoArt. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package autoutils
+
+import (
+ "math"
+ "math/rand"
+ "fmt"
+)
+
+// Operators
+const (
+ CONST = iota
+ ADD
+ SUB
+ MUL
+ DIV
+ MIN
+ MAX
+ SQRT
+ SIN
+ COS
+ TAN
+ LOG
+ EXP
+ OPERATOR_COUNT
+)
+const FIRST_BINARY = ADD
+const FIRST_UNARY = SQRT
+const BINARY_COUNT = FIRST_UNARY - 1 // -1 for CONST
+const UNARY_COUNT = OPERATOR_COUNT - FIRST_UNARY
+const FIRST_VAR = OPERATOR_COUNT
+
+type Operator struct {
+ op int // Operator number. If operator is a variable, v, it is equal to FIRST_VAR + v
+ constant float64 // Constant (if op = CONST)
+}
+
+type Function struct {
+ nvars int
+ operators []Operator
+}
+
+// Generate a random function f with the given length (i.e. len(f.operators))
+// and the given number of variables
+func (f *Function) Generate(nvars int, length int) {
+ f.nvars = nvars
+ f.operators = make([]Operator, length)
+ nsOnStack := 0
+ i := 0
+ for nsOnStack + i < length {
+ var operator Operator
+ var optype int
+ if nsOnStack == 0 {
+ // Pick a random variable
+ optype = 0
+ } else if nsOnStack == 1 {
+ // Pick a constant/variable/unary operator
+ optype = rand.Intn(3)
+ } else {
+ // Pick a constant/variable/unary/binary operator
+ optype = rand.Intn(4)
+ }
+ switch optype {
+ case 0:
+ // variable
+ operator.op = FIRST_VAR + rand.Intn(nvars)
+ nsOnStack++
+ case 1:
+ // Constant
+ operator.op = CONST
+ operator.constant = rand.Float64()
+ nsOnStack++
+ case 2:
+ // unary
+ operator.op = rand.Intn(UNARY_COUNT) + FIRST_UNARY
+ case 3:
+ // binary
+ operator.op = rand.Intn(BINARY_COUNT) + FIRST_BINARY
+ nsOnStack--
+ }
+ f.operators[i] = operator
+ i++
+ }
+
+ if nsOnStack + i == length {
+ // Add a unary operator
+ f.operators[i].op = rand.Intn(UNARY_COUNT) + FIRST_UNARY
+ i++
+ }
+
+ // Keep adding binary operators until nsOnStack == 1
+ for nsOnStack > 1 {
+ f.operators[i].op = rand.Intn(BINARY_COUNT) + FIRST_BINARY
+ nsOnStack--
+ i++
+ }
+
+}
+
+func (f *Function) Evaluate(vars []float64) float64 {
+ var stack []float64
+ for _, op := range f.operators {
+ l := len(stack)
+ switch (op.op) {
+ case CONST:
+ stack = append(stack, op.constant)
+ case ADD:
+ stack[l-2] += stack[l-1]
+ stack = stack[:l-1]
+ case SUB:
+ stack[l-2] -= stack[l-1]
+ stack = stack[:l-1]
+ case MUL:
+ stack[l-2] *= stack[l-1]
+ stack = stack[:l-1]
+ case DIV:
+ if stack[l-1] == 0 { // Check for division by 0
+ stack[l-1] = 0.01
+ }
+ stack[l-2] /= stack[l-1]
+ stack = stack[:l-1]
+ case MIN:
+ stack[l-2] = math.Min(stack[l-2], stack[l-1])
+ stack = stack[:l-1]
+ case MAX:
+ stack[l-2] = math.Max(stack[l-2], stack[l-1])
+ stack = stack[:l-1]
+ case SQRT:
+ stack[l-1] = math.Sqrt(math.Abs(stack[l-1]))
+ case SIN:
+ stack[l-1] = math.Sin(stack[l-1])
+ case COS:
+ stack[l-1] = math.Cos(stack[l-1])
+ case TAN:
+ stack[l-1] = math.Tan(stack[l-1])
+ case LOG:
+ stack[l-1] = math.Log(math.Abs(stack[l-1]))
+ case EXP:
+ stack[l-1] = math.Exp(stack[l-1])
+ default:
+ stack = append(stack, vars[op.op - FIRST_VAR])
+ }
+ }
+ return stack[0]
+}
+
+func (f *Function) String() string {
+ var str string
+ for _, op := range f.operators {
+ switch (op.op) {
+ case CONST:
+ str += fmt.Sprintf("%v",op.constant)
+ case ADD:
+ str += "+"
+ case SUB:
+ str += "-"
+ case MUL:
+ str += "*"
+ case DIV:
+ str += "/"
+ case SQRT:
+ str += "sqrt"
+ case SIN:
+ str += "sin"
+ case COS:
+ str += "cos"
+ case TAN:
+ str += "tan"
+ default:
+ str += fmt.Sprintf("v%v", op.op - FIRST_VAR)
+ }
+ str += " "
+ }
+ return str
+}
+
+const mutationRate = 0.01
+
+func (f *Function) Mutate() {
+ for i, op := range f.operators {
+ if op.op == CONST && rand.Float64() < mutationRate {
+ f.operators[i].constant += rand.NormFloat64() / 5 // Nudge constant
+ }
+ }
+}
+
+func (f *Function) Breed(f1* Function, f2 *Function) {
+ // f(x) = (f1(x) + f2(x)) / 2
+ f.operators = make([]Operator, len(f1.operators) + len(f2.operators) + 3)
+ for i, o := range f1.operators {
+ f.operators[i] = o
+ }
+ for i, o := range f2.operators {
+ f.operators[i + len(f1.operators)] = o
+ }
+ i := len(f1.operators) + len(f2.operators)
+ f.operators[i].op = ADD
+ f.operators[i+1].op = CONST
+ f.operators[i+1].constant = 2
+ f.operators[i+2].op = DIV
+}
+
+func (f *Function) CopyFrom(other *Function) {
+ f.nvars = other.nvars
+ f.operators = make([]Operator, len(other.operators))
+ for i, op := range other.operators {
+ f.operators[i] = op
+ }
+}