mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
refactor(song): Remove song length from Song and assume the user knows MAX_SAMPLES
Trying to force a specific song length other than the default never quite worked, so we'll only support the default MAX_SAMPLES & will calculate it for the user in the user in the exported .h header file.
This commit is contained in:
parent
e2c6d4b70c
commit
a1e7e82d6d
@ -158,7 +158,7 @@ func DeserializeAsm(asmcode string) (*Song, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s := Song{BPM: bpm, Patterns: patterns, Tracks: tracks, Patch: patch, SongLength: -1}
|
s := Song{BPM: bpm, Patterns: patterns, Tracks: tracks, Patch: patch}
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
go4k/song.go
35
go4k/song.go
@ -1,13 +1,15 @@
|
|||||||
package go4k
|
package go4k
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
type Song struct {
|
type Song struct {
|
||||||
BPM int
|
BPM int
|
||||||
Patterns [][]byte
|
Patterns [][]byte
|
||||||
Tracks []Track
|
Tracks []Track
|
||||||
SongLength int // in samples, 0 means calculate automatically from BPM and Track lengths, but can also set manually
|
Patch Patch
|
||||||
Patch Patch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Song) PatternRows() int {
|
func (s *Song) PatternRows() int {
|
||||||
@ -74,13 +76,9 @@ func Play(synth Synth, song Song) ([]float32, error) {
|
|||||||
for i := range curVoices {
|
for i := range curVoices {
|
||||||
curVoices[i] = song.FirstTrackVoice(i)
|
curVoices[i] = song.FirstTrackVoice(i)
|
||||||
}
|
}
|
||||||
samples := song.SongLength
|
initialCapacity := song.TotalRows() * song.SamplesPerRow() * 2
|
||||||
if samples <= 0 {
|
buffer := make([]float32, 0, initialCapacity)
|
||||||
samples = song.TotalRows() * song.SamplesPerRow()
|
rowbuffer := make([]float32, song.SamplesPerRow()*2)
|
||||||
}
|
|
||||||
buffer := make([]float32, samples*2)
|
|
||||||
totaln := 0
|
|
||||||
rowtime := song.SamplesPerRow()
|
|
||||||
for row := 0; row < song.TotalRows(); row++ {
|
for row := 0; row < song.TotalRows(); row++ {
|
||||||
patternRow := row % song.PatternRows()
|
patternRow := row % song.PatternRows()
|
||||||
pattern := row / song.PatternRows()
|
pattern := row / song.PatternRows()
|
||||||
@ -100,8 +98,15 @@ func Play(synth Synth, song Song) ([]float32, error) {
|
|||||||
synth.Trigger(curVoices[t], note)
|
synth.Trigger(curVoices[t], note)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
samples, _, _ := synth.Render(buffer[2*totaln:], rowtime)
|
tries := 0
|
||||||
totaln += samples
|
for rowtime := 0; rowtime < song.SamplesPerRow(); {
|
||||||
|
samples, time, _ := synth.Render(rowbuffer, song.SamplesPerRow()-rowtime)
|
||||||
|
rowtime += time
|
||||||
|
buffer = append(buffer, rowbuffer[:samples*2]...)
|
||||||
|
if tries > 100 {
|
||||||
|
return nil, fmt.Errorf("Song speed modulation likely so slow that row never advances; error at pattern %v, row %v", pattern, patternRow)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buffer, nil
|
return buffer, nil
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/vsariola/sointu/go4k"
|
"github.com/vsariola/sointu/go4k"
|
||||||
)
|
)
|
||||||
|
|
||||||
const expectedMarshaled = `{"BPM":100,"Patterns":["QABEACAAAABLAE4AAAAAAA=="],"Tracks":[{"NumVoices":1,"Sequence":"AA=="}],"SongLength":0,"Patch":{"Instruments":[{"NumVoices":1,"Units":[{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":96,"detune":64,"flags":64,"gain":128,"phase":0,"shape":64,"stereo":0,"transpose":64}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":64,"detune":64,"flags":64,"gain":128,"phase":64,"shape":96,"stereo":0,"transpose":72}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"out","Parameters":{"gain":128,"stereo":1}}]}],"DelayTimes":[],"SampleOffsets":[]}}`
|
const expectedMarshaled = `{"BPM":100,"Patterns":["QABEACAAAABLAE4AAAAAAA=="],"Tracks":[{"NumVoices":1,"Sequence":"AA=="}],"Patch":{"Instruments":[{"NumVoices":1,"Units":[{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":96,"detune":64,"flags":64,"gain":128,"phase":0,"shape":64,"stereo":0,"transpose":64}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"envelope","Parameters":{"attack":32,"decay":32,"gain":128,"release":64,"stereo":0,"sustain":64}},{"Type":"oscillator","Parameters":{"color":64,"detune":64,"flags":64,"gain":128,"phase":64,"shape":96,"stereo":0,"transpose":72}},{"Type":"mulp","Parameters":{"stereo":0}},{"Type":"out","Parameters":{"gain":128,"stereo":1}}]}],"DelayTimes":[],"SampleOffsets":[]}}`
|
||||||
|
|
||||||
var testSong = go4k.Song{
|
var testSong = go4k.Song{
|
||||||
BPM: 100,
|
BPM: 100,
|
||||||
@ -16,7 +16,6 @@ var testSong = go4k.Song{
|
|||||||
Tracks: []go4k.Track{
|
Tracks: []go4k.Track{
|
||||||
{NumVoices: 1, Sequence: []byte{0}},
|
{NumVoices: 1, Sequence: []byte{0}},
|
||||||
},
|
},
|
||||||
SongLength: 0,
|
|
||||||
Patch: go4k.Patch{
|
Patch: go4k.Patch{
|
||||||
Instruments: []go4k.Instrument{{NumVoices: 1, Units: []go4k.Unit{
|
Instruments: []go4k.Instrument{{NumVoices: 1, Units: []go4k.Unit{
|
||||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||||
|
@ -37,7 +37,7 @@ func TestPlayer(t *testing.T) {
|
|||||||
SampleOffsets: []go4k.SampleOffset{}}
|
SampleOffsets: []go4k.SampleOffset{}}
|
||||||
patterns := [][]byte{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}
|
patterns := [][]byte{{64, 0, 68, 0, 32, 0, 0, 0, 75, 0, 78, 0, 0, 0, 0, 0}}
|
||||||
tracks := []go4k.Track{go4k.Track{1, []byte{0}}}
|
tracks := []go4k.Track{go4k.Track{1, []byte{0}}}
|
||||||
song := go4k.Song{100, patterns, tracks, 0, patch}
|
song := go4k.Song{100, patterns, tracks, patch}
|
||||||
synth, err := bridge.Synth(patch)
|
synth, err := bridge.Synth(patch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Compiling patch failed: %v", err)
|
t.Fatalf("Compiling patch failed: %v", err)
|
||||||
|
@ -12,7 +12,6 @@ var defaultSong = go4k.Song{
|
|||||||
{NumVoices: 1, Sequence: []byte{0}},
|
{NumVoices: 1, Sequence: []byte{0}},
|
||||||
{NumVoices: 1, Sequence: []byte{1}},
|
{NumVoices: 1, Sequence: []byte{1}},
|
||||||
},
|
},
|
||||||
SongLength: 0,
|
|
||||||
Patch: go4k.Patch{
|
Patch: go4k.Patch{
|
||||||
Instruments: []go4k.Instrument{{NumVoices: 2, Units: []go4k.Unit{
|
Instruments: []go4k.Instrument{{NumVoices: 2, Units: []go4k.Unit{
|
||||||
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
{"envelope", map[string]int{"stereo": 0, "attack": 32, "decay": 32, "sustain": 64, "release": 64, "gain": 128}},
|
||||||
|
@ -115,7 +115,9 @@ section .text ; yasm throws section redeclaration warnings if strucs are defined
|
|||||||
|
|
||||||
%define TOTAL_ROWS (MAX_PATTERNS*PATTERN_SIZE)
|
%define TOTAL_ROWS (MAX_PATTERNS*PATTERN_SIZE)
|
||||||
%define SAMPLES_PER_ROW (SAMPLE_RATE*4*60/(BPM*16))
|
%define SAMPLES_PER_ROW (SAMPLE_RATE*4*60/(BPM*16))
|
||||||
%define MAX_SAMPLES (SAMPLES_PER_ROW*TOTAL_ROWS)
|
%ifndef MAX_SAMPLES
|
||||||
|
%define MAX_SAMPLES (SAMPLES_PER_ROW*TOTAL_ROWS)
|
||||||
|
%endif
|
||||||
|
|
||||||
%macro BEGIN_PATCH 0
|
%macro BEGIN_PATCH 0
|
||||||
SECT_DATA(params)
|
SECT_DATA(params)
|
||||||
|
@ -159,6 +159,7 @@ target_compile_definitions(test_envelope_16bit PUBLIC SU_USE_16BIT_OUTPUT)
|
|||||||
regression_test(test_polyphony "ENVELOPE;VCO_SINE")
|
regression_test(test_polyphony "ENVELOPE;VCO_SINE")
|
||||||
regression_test(test_chords "ENVELOPE;VCO_SINE")
|
regression_test(test_chords "ENVELOPE;VCO_SINE")
|
||||||
regression_test(test_speed "ENVELOPE;VCO_SINE")
|
regression_test(test_speed "ENVELOPE;VCO_SINE")
|
||||||
|
target_compile_definitions(test_speed PUBLIC MAX_SAMPLES=211592)
|
||||||
|
|
||||||
regression_test(test_render_samples ENVELOPE "" test_render_samples.c)
|
regression_test(test_render_samples ENVELOPE "" test_render_samples.c)
|
||||||
target_link_libraries(test_render_samples ${STATICLIB})
|
target_link_libraries(test_render_samples ${STATICLIB})
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user