package go4k // FindSuperIntArray finds a small super array containing all // the subarrays passed to it. Returns the super array and indices where // the subarrays can be found. For example: // FindSuperIntArray([][]int{{4,5,6},{1,2,3},{3,4}}) // returns {1,2,3,4,5,6},{3,0,2} // Implemented using a greedy search, so does not necessarily find // the true optimal (the problem is NP-hard and analogous to traveling // salesman problem). // // Used to construct a small delay time table without unnecessary repetition // of delay times. func FindSuperIntArray(arrays [][]int) ([]int, []int) { // If we go past MAX_MERGES, the algorithm could get slow and hang the computer // So this is a safety limit: after this problem size, just merge any arrays // until we get into more manageable range const maxMerges = 1000 min := func(a int, b int) int { if a < b { return a } return b } overlap := func(a []int, b []int) (int, int) { minShift := len(a) for shift := len(a) - 1; shift >= 0; shift-- { overlapping := true for k := shift; k < min(len(a), len(b)+shift); k++ { if a[k] != b[k-shift] { overlapping = false break } } if overlapping { minShift = shift } } overlap := min(len(a)-minShift, len(b)) return overlap, minShift } sliceNumbers := make([]int, len(arrays)) startIndices := make([]int, len(arrays)) var processedArrays [][]int for i := range arrays { if len(arrays[i]) == 0 { // Zero length arrays do not need to be processed at all // They will 'start' at index 0 always as they have no length. sliceNumbers[i] = -1 } else { sliceNumbers[i] = len(processedArrays) processedArrays = append(processedArrays, arrays[i]) } } if len(processedArrays) == 0 { return []int{}, startIndices // no arrays with len>0 to process, just return empty array and all indices as 0 } for len(processedArrays) > 1 { // there's at least two candidates that could be be merged maxO, maxI, maxJ, maxS := -1, -1, -1, -1 if len(processedArrays) < maxMerges { // find the pair i,j that results in the largest overlap with array i coming first, followed by potentially overlapping array j for i := range processedArrays { for j := range processedArrays { if i == j { continue } overlap, shift := overlap(processedArrays[i], processedArrays[j]) if overlap > maxO { maxI, maxJ, maxO, maxS = i, j, overlap, shift } } } } else { // The task is daunting, we have over MAX_MERGES overlaps to test. Just merge two first ones until the task is more manageable size overlap, shift := overlap(processedArrays[0], processedArrays[1]) maxI, maxJ, maxO, maxS = 0, 1, overlap, shift } for k := range sliceNumbers { if sliceNumbers[k] == maxJ { // update slice pointers to point maxI instead of maxJ (maxJ will be appended to maxI, taking overlap into account) sliceNumbers[k] = maxI startIndices[k] += maxS // the array j starts at index maxS in array i } if sliceNumbers[k] > maxJ { // pointers maxJ reduced by 1 as maxJ will be deleted sliceNumbers[k]-- } } // if array j was not entirely included within array j if maxO < len(processedArrays[maxJ]) { // append array maxJ to array maxI, without duplicating the overlapping part processedArrays[maxI] = append(processedArrays[maxI], processedArrays[maxJ][maxO:]...) } // finally, remove element maxJ from processedArrays processedArrays = append(processedArrays[:maxJ], processedArrays[maxJ+1:]...) } return processedArrays[0], startIndices // there should be only one slice left in the arrays after the loop } // ConstructDelayTimeTable tries to construct the delay times table // abusing overlapping between different delay times tables as much // as possible. Especially: if two delay units use exactly the same // delay times, they appear in the table only once. func ConstructDelayTimeTable(patch Patch) ([]int, [][]int) { ind := make([][]int, len(patch.Instruments)) var subarrays [][]int // flatten the delay times into one array of arrays // saving the indices where they were placed for i, instr := range patch.Instruments { ind[i] = make([]int, len(instr.Units)) for j, unit := range instr.Units { // only include delay times for delays. Only delays // should use delay times if unit.Type == "delay" { ind[i][j] = len(subarrays) end := unit.Parameters["count"] if unit.Parameters["stereo"] > 0 { end *= 2 } subarrays = append(subarrays, patch.DelayTimes[unit.Parameters["delay"]:end]) } } } delayTable, indices := FindSuperIntArray(subarrays) // cancel the flattening, so unitindices can be used to // to find the index of each delay in the delay table unitindices := make([][]int, len(patch.Instruments)) for i, instr := range patch.Instruments { unitindices[i] = make([]int, len(instr.Units)) for j, unit := range instr.Units { if unit.Type == "delay" { unitindices[i][j] = indices[ind[i][j]] } } } return delayTable, unitindices } // ConstructSampleOffsetTable collects the sample offests from // all sample-based oscillators and collects them in a table, // so that they appear in the table only once. Returns the collected // table and [][]int array where element [i][j] is the index in the // table by instrument i / unit j (units other than sample oscillators // have the value 0) func ConstructSampleOffsetTable(patch Patch) ([]SampleOffset, [][]int) { unitindices := make([][]int, len(patch.Instruments)) var offsetTable []SampleOffset offsetMap := map[SampleOffset]int{} for i, instr := range patch.Instruments { unitindices[i] = make([]int, len(instr.Units)) for j, unit := range instr.Units { if unit.Type == "oscillator" && unit.Parameters["type"] == Sample { offset := SampleOffset{ Start: unit.Parameters["start"], LoopStart: unit.Parameters["loopstart"], LoopLength: unit.Parameters["looplength"], } if ind, ok := offsetMap[offset]; ok { unitindices[i][j] = ind // the sample has been already added to table, reuse the index } else { ind = len(offsetTable) unitindices[i][j] = ind offsetMap[offset] = ind offsetTable = append(offsetTable, offset) } } } } return offsetTable, unitindices }