diff --git a/main.go b/main.go index 38121b4..ad86992 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,10 @@ package main import ( "encoding/binary" + "flag" + "fmt" + "io" + "log" "math" "os" "time" @@ -10,21 +14,60 @@ import ( ) func main() { - - simpleChord() - flourish() + err := run() + if err != nil { + log.Fatal(err) + } } -func simpleChord() { +func run() error { + sf2Path := flag.String("sf2", "", "Sound font file location") + outPath := flag.String("o", "out.pcm", "file to write pcm file to") + midiPath := flag.String("midi", "", `midi file to synth, or "chord" for example`) + flag.Parse() + + if len(*sf2Path) == 0 { + flag.PrintDefaults() + return fmt.Errorf("missing sf2 path") + } + if len(*outPath) == 0 { + flag.PrintDefaults() + return fmt.Errorf("missing output path") + } + if len(*midiPath) == 0 { + flag.PrintDefaults() + return fmt.Errorf("missing midi path") + } - // Load the SoundFont. - sf2, _ := os.Open("TimGM6mb.sf2") - soundFont, _ := meltysynth.NewSoundFont(sf2) + sf2, err := os.Open(*sf2Path) + if err != nil { + return err + } + soundFont, err := meltysynth.NewSoundFont(sf2) sf2.Close() + if err != nil { + return err + } + + switch *midiPath { + default: + err = midi(soundFont, *midiPath, *outPath) + case "chord": + err = simpleChord(soundFont, *outPath) + } + if err != nil { + return err + } + return nil +} +func simpleChord(soundFont *meltysynth.SoundFont, outputFile string) error { // Create the synthesizer. settings := meltysynth.NewSynthesizerSettings(44100) - synthesizer, _ := meltysynth.NewSynthesizer(soundFont, settings) + synthesizer, err := meltysynth.NewSynthesizer(soundFont, settings) + if err != nil { + return err + } // Play some notes (middle C, E, G). synthesizer.NoteOn(0, 60, 100) @@ -39,24 +82,27 @@ func simpleChord() { // Render the waveform. synthesizer.Render(left, right) - writePcmInterleavedInt16(left, right, "simpleChord.pcm") + return writeFile(left, right, outputFile) } -func flourish() { - - // Load the SoundFont. - sf2, _ := os.Open("TimGM6mb.sf2") - soundFont, _ := meltysynth.NewSoundFont(sf2) - sf2.Close() - +func midi(soundFont *meltysynth.SoundFont, midiFilePath string, outputFile string) error { // Create the synthesizer. settings := meltysynth.NewSynthesizerSettings(44100) - synthesizer, _ := meltysynth.NewSynthesizer(soundFont, settings) + synthesizer, err := meltysynth.NewSynthesizer(soundFont, settings) + if err != nil { + return err + } // Load the MIDI file. - mid, _ := os.Open("C:\\Windows\\Media\\flourish.mid") - midiFile, _ := meltysynth.NewMidiFile(mid) + mid, err := os.Open(midiFilePath) + if err != nil { + return err + } + midiFile, err := meltysynth.NewMidiFile(mid) mid.Close() + if err != nil { + return err + } // Create the MIDI sequencer. sequencer := meltysynth.NewMidiFileSequencer(synthesizer) @@ -70,14 +116,22 @@ func flourish() { // Render the waveform. sequencer.Render(left, right) - writePcmInterleavedInt16(left, right, "flourish.pcm") + return writeFile(left, right, outputFile) } -func writePcmInterleavedInt16(left []float32, right []float32, path string) { +func writeFile(left []float32, right []float32, filename string) error { + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() - length := len(left) + return writePCMInterleavedInt16(left, right, f) +} - max := 0.0 +func writePCMInterleavedInt16(left []float32, right []float32, pcm io.Writer) error { + length := len(left) + var max float64 for i := 0; i < length; i++ { absLeft := math.Abs(float64(left[i])) @@ -99,7 +153,5 @@ func writePcmInterleavedInt16(left []float32, right []float32, path string) { data[2*i+1] = int16(a * right[i]) } - pcm, _ := os.Create(path) - binary.Write(pcm, binary.LittleEndian, data) - pcm.Close() + return binary.Write(pcm, binary.LittleEndian, data) } diff --git a/meltysynth/array_math.go b/meltysynth/array_math.go index 54711fa..8af2bf7 100644 --- a/meltysynth/array_math.go +++ b/meltysynth/array_math.go @@ -1,7 +1,6 @@ package meltysynth func arrayMultiplyAdd(a float32, x []float32, dst []float32) { - dstLen := len(dst) for i := 0; i < dstLen; i++ { dst[i] += a * x[i] @@ -9,7 +8,6 @@ func arrayMultiplyAdd(a float32, x []float32, dst []float32) { } func arrayMultiplyAddSlope(a float32, step float32, x []float32, dst []float32) { - dstLen := len(dst) for i := 0; i < dstLen; i++ { dst[i] += a * x[i] diff --git a/meltysynth/binaryreader_ex.go b/meltysynth/binaryreader_ex.go index fdd6d57..c6fce2a 100644 --- a/meltysynth/binaryreader_ex.go +++ b/meltysynth/binaryreader_ex.go @@ -7,7 +7,6 @@ import ( ) func readFourCC(r io.Reader) (string, error) { - var data [4]byte n, err := r.Read(data[:]) if err != nil { @@ -28,7 +27,6 @@ func readFourCC(r io.Reader) (string, error) { } func readFixedLengthString(r io.Reader, length int32) (string, error) { - var data []byte = make([]byte, length) n, err := r.Read(data[:]) if err != nil { @@ -49,8 +47,7 @@ func readFixedLengthString(r io.Reader, length int32) (string, error) { } func readIntVariableLength(r io.Reader) (int32, error) { - - acc := int32(0) + var acc int32 count := 0 for { var value byte diff --git a/meltysynth/biquad_filter.go b/meltysynth/biquad_filter.go index 38f6615..41eff61 100644 --- a/meltysynth/biquad_filter.go +++ b/meltysynth/biquad_filter.go @@ -34,42 +34,35 @@ func (bf *biQuadFilter) clearBuffer() { } func (bf *biQuadFilter) setLowPassFilter(cutoffFrequency float32, resonance float32) { + if cutoffFrequency >= 0.499*float32(bf.synthesizer.SampleRate) { + bf.active = false + return + } + bf.active = true - if cutoffFrequency < 0.499*float32(bf.synthesizer.SampleRate) { - - bf.active = true - - // This equation gives the Q value which makes the desired resonance peak. - // The error of the resultant peak height is less than 3%. - q := resonance - resonancePeakOffset/(1+6*(resonance-1)) - - w := 2 * math.Pi * float64(cutoffFrequency) / float64(bf.synthesizer.SampleRate) - cosw := math.Cos(w) - alpha := math.Sin(w) / float64(2*q) - - b0 := (1 - cosw) / 2 - b1 := 1 - cosw - b2 := (1 - cosw) / 2 - a0 := 1 + alpha - a1 := -2 * cosw - a2 := 1 - alpha + // This equation gives the Q value which makes the desired resonance peak. + // The error of the resultant peak height is less than 3%. + q := resonance - resonancePeakOffset/(1+6*(resonance-1)) - bf.setCoefficients(float32(a0), float32(a1), float32(a2), float32(b0), float32(b1), float32(b2)) + w := 2 * math.Pi * float64(cutoffFrequency) / float64(bf.synthesizer.SampleRate) + cosw := math.Cos(w) + alpha := math.Sin(w) / float64(2*q) - } else { + b0 := (1 - cosw) / 2 + b1 := 1 - cosw + b2 := (1 - cosw) / 2 + a0 := 1 + alpha + a1 := -2 * cosw + a2 := 1 - alpha - bf.active = false - } + bf.setCoefficients(float32(a0), float32(a1), float32(a2), float32(b0), float32(b1), float32(b2)) } func (bf *biQuadFilter) process(block []float32) { - blockLength := len(block) if bf.active { - for t := 0; t < blockLength; t++ { - input := block[t] output := bf.a0*input + bf.a1*bf.x1 + bf.a2*bf.x2 - bf.a3*bf.y1 - bf.a4*bf.y2 @@ -80,9 +73,7 @@ func (bf *biQuadFilter) process(block []float32) { block[t] = output } - } else { - bf.x2 = block[blockLength-2] bf.x1 = block[blockLength-1] bf.y2 = bf.x2 diff --git a/meltysynth/channel.go b/meltysynth/channel.go index 6a92a7f..c59e363 100644 --- a/meltysynth/channel.go +++ b/meltysynth/channel.go @@ -25,7 +25,6 @@ type channel struct { } func newChannel(s *Synthesizer, isPercussionChannel bool) *channel { - result := new(channel) result.synthesizer = s @@ -37,7 +36,6 @@ func newChannel(s *Synthesizer, isPercussionChannel bool) *channel { } func (ch *channel) reset() { - if ch.isPercussionChannel { ch.bankNumber = 128 } else { @@ -64,7 +62,6 @@ func (ch *channel) reset() { } func (ch *channel) resetAllControllers() { - ch.modulation = 0 ch.expression = 127 << 7 ch.holdPedal = false @@ -75,7 +72,6 @@ func (ch *channel) resetAllControllers() { } func (ch *channel) setBank(value int32) { - ch.bankNumber = value if ch.isPercussionChannel { diff --git a/meltysynth/generator.go b/meltysynth/generator.go index 531dfe0..8af354a 100644 --- a/meltysynth/generator.go +++ b/meltysynth/generator.go @@ -12,7 +12,6 @@ type generator struct { } func readGeneratorsFromChunk(r io.Reader, size int32) ([]generator, error) { - var n int var err error @@ -21,11 +20,9 @@ func readGeneratorsFromChunk(r io.Reader, size int32) ([]generator, error) { } count := size/4 - 1 - generators := make([]generator, count) for i := int32(0); i < count; i++ { - var gen generator var generatorType uint16 diff --git a/meltysynth/instrument.go b/meltysynth/instrument.go index b5a6c78..b1b6984 100644 --- a/meltysynth/instrument.go +++ b/meltysynth/instrument.go @@ -10,11 +10,9 @@ type Instrument struct { } func createInstrument(info *instrumentInfo, zones []*zone, samples []*SampleHeader) (*Instrument, error) { - var err error result := new(Instrument) - result.Name = info.name zoneCount := info.zoneEndIndex - info.zoneStartIndex + 1 diff --git a/meltysynth/instrument_info.go b/meltysynth/instrument_info.go index 7eeb917..ec4febc 100644 --- a/meltysynth/instrument_info.go +++ b/meltysynth/instrument_info.go @@ -13,7 +13,6 @@ type instrumentInfo struct { } func readInstrumentsFromChunk(r io.Reader, size int32) ([]*instrumentInfo, error) { - var err error if size%22 != 0 { @@ -21,11 +20,9 @@ func readInstrumentsFromChunk(r io.Reader, size int32) ([]*instrumentInfo, error } count := size / 22 - instruments := make([]*instrumentInfo, count) for i := int32(0); i < count; i++ { - instrument := new(instrumentInfo) instrument.name, err = readFixedLengthString(r, 20) diff --git a/meltysynth/instrument_region.go b/meltysynth/instrument_region.go index 4825925..5f79933 100644 --- a/meltysynth/instrument_region.go +++ b/meltysynth/instrument_region.go @@ -11,7 +11,6 @@ type InstrumentRegion struct { } func createInstrumentRegion(inst *Instrument, global *zone, local *zone, samples []*SampleHeader) (*InstrumentRegion, error) { - result := new(InstrumentRegion) result.gs[gen_InitialFilterCutoffFrequency] = 13500 @@ -52,7 +51,6 @@ func createInstrumentRegion(inst *Instrument, global *zone, local *zone, samples } func createInstrumentRegions(inst *Instrument, zones []*zone, samples []*SampleHeader) ([]*InstrumentRegion, error) { - var err error // Is the first one the global zone? @@ -71,9 +69,7 @@ func createInstrumentRegions(inst *Instrument, zones []*zone, samples []*SampleH } } return regions, nil - } else { - // No global zone. count := len(zones) regions := make([]*InstrumentRegion, count) @@ -88,7 +84,6 @@ func createInstrumentRegions(inst *Instrument, zones []*zone, samples []*SampleH } func (region *InstrumentRegion) setParameter(param generator) { - index := param.generatorType // Unknown generators should be ignored. diff --git a/meltysynth_test/instrument_region_test.go b/meltysynth/instrument_region_test.go similarity index 99% rename from meltysynth_test/instrument_region_test.go rename to meltysynth/instrument_region_test.go index 9b0fc21..7db41d0 100644 --- a/meltysynth_test/instrument_region_test.go +++ b/meltysynth/instrument_region_test.go @@ -1,14 +1,10 @@ -package meltysynth_test +package meltysynth import ( - "os" "testing" - - "github.com/sinshu/go-meltysynth/meltysynth" ) func TestTimGM6mb_InstrumentRegion(t *testing.T) { - var reference = make([][][]float64, 210, 210) //============================================================ @@ -5187,9 +5183,7 @@ func TestTimGM6mb_InstrumentRegion(t *testing.T) { // ensstringsc7 reference[209][8] = []float64{2712865, 2727420, 2720001, 2727420, 0, 0, 0, 0, 0, 0, 0, 19913, 0, 0, 0, -2, 0, 42.4, 7.8, 0.24699, 7.8113, 0.0099978, 8.176, 0.00097656, 0.00097656, 0.00097656, 0.00097656, 0, 39.993, 0, 0, 0.00097656, 0.061997, 0.00097656, 1.9196, 8.1, 0.94007, 0, 0, 96, 120, 0, 127, 5.9, 0, -5, 1, 100, 0, 96} - file, _ := os.Open("TimGM6mb.sf2") - soundFont, _ := meltysynth.NewSoundFont(file) - + soundFont := loadGM(t) instrumentCount := len(soundFont.Instruments) for inst := 0; inst < instrumentCount; inst++ { instrument := soundFont.Instruments[inst] @@ -5203,7 +5197,6 @@ func TestTimGM6mb_InstrumentRegion(t *testing.T) { } func TestMuseScore_InstrumentRegion(t *testing.T) { - var reference = make([][][]float64, 310, 310) //============================================================ @@ -10580,8 +10573,7 @@ func TestMuseScore_InstrumentRegion(t *testing.T) { // Helicopter reference[309][0] = []float64{15500156, 15513066, 15500159, 15513058, 0, 0, 0, 0, 0, -8, 94, 2593.3, 0, 562, 3037, 0.1, 0, 0, 0, 0.65406, 0.08404, 0.00097656, 0.08404, 0.00097656, 2.1772, 0.00097656, 0.00097656, 0, 32.767, 0, 0, 0.00097656, 4.9474, 0.00097656, 0.00097656, 0, 1.5583, 0, 0, 0, 108, 0, 127, 4.5, -13, 2, 1, 50, 0, 60} - file, _ := os.Open("GeneralUser GS MuseScore v1.442.sf2") - soundFont, _ := meltysynth.NewSoundFont(file) + soundFont := loadGS(t) instrumentCount := len(soundFont.Instruments) for inst := 0; inst < instrumentCount; inst++ { diff --git a/meltysynth/lfo.go b/meltysynth/lfo.go index 6314a91..13cdaa4 100644 --- a/meltysynth/lfo.go +++ b/meltysynth/lfo.go @@ -18,9 +18,7 @@ func newLfo(s *Synthesizer) *lfo { } func (lfo *lfo) start(delay float32, frequency float32) { - if frequency > 1.0e-3 { - lfo.active = true lfo.delay = float64(delay) @@ -28,38 +26,32 @@ func (lfo *lfo) start(delay float32, frequency float32) { lfo.processedSampleCount = 0 lfo.value = 0 - - } else { - - lfo.active = false - lfo.value = 0 + return } + lfo.active = false + lfo.value = 0 } func (lfo *lfo) process() { - if !lfo.active { return } lfo.processedSampleCount += lfo.synthesizer.BlockSize - currentTime := float64(lfo.processedSampleCount) / float64(lfo.synthesizer.SampleRate) if currentTime < lfo.delay { - lfo.value = 0 - - } else { - - phase := math.Mod(currentTime-lfo.delay, lfo.period) / lfo.period - - if phase < 0.25 { - lfo.value = float32(4 * phase) - } else if phase < 0.75 { - lfo.value = float32(4 * (0.5 - phase)) - } else { - lfo.value = float32(4 * (phase - 1.0)) - } + return + } + phase := math.Mod(currentTime-lfo.delay, lfo.period) / lfo.period + + switch { + case phase < 0.25: + lfo.value = float32(4 * phase) + case phase < 0.75: + lfo.value = float32(4 * (0.5 - phase)) + default: + lfo.value = float32(4 * (phase - 1.0)) } } diff --git a/meltysynth/midifile.go b/meltysynth/midifile.go index 25bf7d5..b2f3114 100644 --- a/meltysynth/midifile.go +++ b/meltysynth/midifile.go @@ -44,14 +44,12 @@ func common2b(status byte, data1 byte) message { } func common3b(status byte, data1 byte, data2 byte) message { - channel := status & 0x0F command := status & 0xF0 return newMessage(channel, command, data1, data2) } func tempoChange(tempo int32) message { - command := byte(tempo >> 16) data1 := byte(tempo >> 8) data2 := byte(tempo) @@ -78,7 +76,6 @@ func (message message) getTempo() float64 { } func NewMidiFile(r io.Reader) (*MidiFile, error) { - var err error chunkType, err := readFourCC(r) @@ -140,7 +137,6 @@ func NewMidiFile(r io.Reader) (*MidiFile, error) { } func readTrack(r io.Reader) ([]message, []int32, error) { - var n int var err error @@ -157,8 +153,8 @@ func readTrack(r io.Reader) ([]message, []int32, error) { messages := make([]message, 0, 300) ticks := make([]int32, 0, 300) - var tick int32 = 0 - var lastStatus byte = 0 + var tick int32 + var lastStatus byte for { delta, err := readIntVariableLength(r) @@ -271,7 +267,6 @@ func readTrack(r io.Reader) ([]message, []int32, error) { } func mergeTracks(messageLists [][]message, tickLists [][]int32, resolution int16) ([]message, []time.Duration) { - mergedMessages := make([]message, 0, 1000) mergedTimes := make([]time.Duration, 0, 1000) @@ -280,7 +275,7 @@ func mergeTracks(messageLists [][]message, tickLists [][]int32, resolution int16 currentTick := int32(0) currentTime := time.Duration(0) - var tempo = float64(120) + tempo := float64(120) for { minTick := int32(math.MaxInt32) @@ -322,7 +317,6 @@ func mergeTracks(messageLists [][]message, tickLists [][]int32, resolution int16 } func readTempo(r io.Reader) (int32, error) { - size, err := readIntVariableLength(r) if err != nil { return 0, err @@ -347,7 +341,6 @@ func readTempo(r io.Reader) (int32, error) { } func discardData(r io.Reader) error { - size, err := readIntVariableLength(r) if err != nil { return err diff --git a/meltysynth/midifile_sequencer.go b/meltysynth/midifile_sequencer.go index 2aad4ef..0f770f7 100644 --- a/meltysynth/midifile_sequencer.go +++ b/meltysynth/midifile_sequencer.go @@ -22,7 +22,6 @@ func NewMidiFileSequencer(s *Synthesizer) *MidiFileSequencer { } func (seq *MidiFileSequencer) Play(midiFile *MidiFile, loop bool) { - seq.midiFile = midiFile seq.loop = loop @@ -36,15 +35,13 @@ func (seq *MidiFileSequencer) Play(midiFile *MidiFile, loop bool) { } func (seq *MidiFileSequencer) Stop() { - seq.midiFile = nil seq.synthesizer.Reset() } func (seq *MidiFileSequencer) Render(left []float32, right []float32) { - - wrote := int32(0) + var wrote int32 length := int32(len(left)) for wrote < length { if seq.blockWrote == seq.synthesizer.BlockSize { @@ -65,7 +62,6 @@ func (seq *MidiFileSequencer) Render(left []float32, right []float32) { } func (seq *MidiFileSequencer) processEvents() { - if seq.midiFile == nil { return } diff --git a/meltysynth/modulation_envelope.go b/meltysynth/modulation_envelope.go index e18f7e1..00787c9 100644 --- a/meltysynth/modulation_envelope.go +++ b/meltysynth/modulation_envelope.go @@ -26,7 +26,6 @@ func newModulationEnvelope(s *Synthesizer) *modulationEnvelope { } func (env *modulationEnvelope) start(delay float32, attack float32, hold float32, decay float32, sustain float32, release float32) { - env.attackSlope = 1 / float64(attack) env.decaySlope = 1 / float64(decay) env.releaseSlope = 1 / float64(release) @@ -49,65 +48,50 @@ func (env *modulationEnvelope) start(delay float32, attack float32, hold float32 } func (env *modulationEnvelope) release() { - env.stage = env_Release env.releaseEndTime += float64(env.processedSampleCount) / float64(env.synthesizer.SampleRate) env.releaseLevel = env.value } func (env *modulationEnvelope) process(sampleCount int32) bool { - env.processedSampleCount += sampleCount - currentTime := float64(env.processedSampleCount) / float64(env.synthesizer.SampleRate) for env.stage <= env_Hold { - var endTime float64 switch env.stage { - case env_Delay: endTime = env.attackStartTime - case env_Attack: endTime = env.holdStartTime - case env_Hold: endTime = env.decayStartTime - default: panic("invalid envelope stage") } if currentTime < endTime { break - } else { - env.stage++ } + env.stage++ } switch env.stage { - case env_Delay: env.value = 0 return true - case env_Attack: env.value = float32(env.attackSlope * (currentTime - env.attackStartTime)) return true - case env_Hold: env.value = 1 return true - case env_Decay: env.value = float32(math.Max(env.decaySlope*(env.decayEndTime-currentTime), float64(env.sustainLevel))) return env.value > nonAudible - case env_Release: env.value = float32(math.Max(float64(env.releaseLevel)*float64(env.releaseSlope)*(env.releaseEndTime-currentTime), 0)) return env.value > nonAudible - default: panic("invalid envelope stage.") } diff --git a/meltysynth/modulator.go b/meltysynth/modulator.go index 1a9d594..bd0a99d 100644 --- a/meltysynth/modulator.go +++ b/meltysynth/modulator.go @@ -7,7 +7,6 @@ import ( // Since modulators will not be supported, we discard the data. func discardModulatorData(r io.Reader, size int32) error { - if size%10 != 0 { return errors.New("the modulator list is invalid") } diff --git a/meltysynth/oscillator.go b/meltysynth/oscillator.go index 7f77b0d..5155589 100644 --- a/meltysynth/oscillator.go +++ b/meltysynth/oscillator.go @@ -37,7 +37,6 @@ func newOscillator(s *Synthesizer) *oscillator { } func (o *oscillator) start(data []int16, loopMode int32, sampleRate int32, start int32, end int32, startLoop int32, endLoop int32, rootKey int32, coarseTune int32, fineTune int32, scaleTuning int32) { - o.data = data o.loopMode = loopMode o.sampleRate = sampleRate @@ -61,21 +60,18 @@ func (o *oscillator) start(data []int16, loopMode int32, sampleRate int32, start } func (o *oscillator) release() { - if o.loopMode == loop_LoopUntilNoteOff { o.looping = false } } func (o *oscillator) process(block []float32, pitch float32) bool { - pitchChange := o.pitchChangeScale*(pitch-float32(o.rootKey)) + o.tune pitchRatio := float64(o.sampleRateRatio) * math.Pow(float64(2), float64(pitchChange)/float64(12)) return o.fillBlock(block, pitchRatio) } func (o *oscillator) fillBlock(block []float32, pitchRatio float64) bool { - pitchRatio_fp := int64(float64(fracUnit) * pitchRatio) if o.looping { @@ -86,22 +82,18 @@ func (o *oscillator) fillBlock(block []float32, pitchRatio float64) bool { } func (o *oscillator) fillBlock_NoLoop(block []float32, pitchRatio_fp int64) bool { - blockLength := len(block) for t := 0; t < blockLength; t++ { - index := int32(o.position_fp >> fracBits) - if index >= o.sampleEnd { if t > 0 { for i := t; i < blockLength; i++ { block[i] = 0 } return true - } else { - return false } + return false } x1 := int64(o.data[index]) @@ -116,7 +108,6 @@ func (o *oscillator) fillBlock_NoLoop(block []float32, pitchRatio_fp int64) bool } func (o *oscillator) fillBlock_Continuous(block []float32, pitchRatio_fp int64) bool { - blockLength := len(block) endLoop_fp := int64(o.endLoop) << fracBits @@ -125,7 +116,6 @@ func (o *oscillator) fillBlock_Continuous(block []float32, pitchRatio_fp int64) loopLength_fp := int64(loopLength) << fracBits for t := 0; t < blockLength; t++ { - if o.position_fp >= endLoop_fp { o.position_fp -= loopLength_fp } diff --git a/meltysynth/preset.go b/meltysynth/preset.go index d53b8e5..6505601 100644 --- a/meltysynth/preset.go +++ b/meltysynth/preset.go @@ -15,7 +15,6 @@ type Preset struct { } func createPreset(info *presetInfo, zones []*zone, instruments []*Instrument) (*Preset, error) { - var err error result := new(Preset) diff --git a/meltysynth/preset_info.go b/meltysynth/preset_info.go index 78aea17..2028bfa 100644 --- a/meltysynth/preset_info.go +++ b/meltysynth/preset_info.go @@ -18,7 +18,6 @@ type presetInfo struct { } func readPresetsFromChunk(r io.Reader, size int32) ([]*presetInfo, error) { - var err error if size%38 != 0 { @@ -26,11 +25,9 @@ func readPresetsFromChunk(r io.Reader, size int32) ([]*presetInfo, error) { } count := size / 38 - presets := make([]*presetInfo, count) for i := int32(0); i < count; i++ { - preset := new(presetInfo) preset.name, err = readFixedLengthString(r, 20) diff --git a/meltysynth/preset_region.go b/meltysynth/preset_region.go index 154bee7..eef6dad 100644 --- a/meltysynth/preset_region.go +++ b/meltysynth/preset_region.go @@ -11,7 +11,6 @@ type PresetRegion struct { } func createPresetRegion(preset *Preset, global *zone, local *zone, instruments []*Instrument) (*PresetRegion, error) { - result := new(PresetRegion) result.gs[gen_KeyRange] = 0x7F00 diff --git a/meltysynth_test/preset_region_test.go b/meltysynth/preset_region_test.go similarity index 99% rename from meltysynth_test/preset_region_test.go rename to meltysynth/preset_region_test.go index 3950757..c767cb2 100644 --- a/meltysynth_test/preset_region_test.go +++ b/meltysynth/preset_region_test.go @@ -1,14 +1,10 @@ -package meltysynth_test +package meltysynth import ( - "os" "testing" - - "github.com/sinshu/go-meltysynth/meltysynth" ) func TestTimGM6mb_PresetRegion(t *testing.T) { - var reference = make([][][]float64, 136, 136) //============================================================ @@ -1111,8 +1107,7 @@ func TestTimGM6mb_PresetRegion(t *testing.T) { // Strings (Tremelo)1 reference[135][1] = []float64{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 127, 0, 127, 0, 0, 0, 0} - file, _ := os.Open("TimGM6mb.sf2") - soundFont, _ := meltysynth.NewSoundFont(file) + soundFont := loadGM(t) presetCount := len(soundFont.Presets) for pre := 0; pre < presetCount; pre++ { @@ -1127,7 +1122,6 @@ func TestTimGM6mb_PresetRegion(t *testing.T) { } func TestMuseScore_PresetRegion(t *testing.T) { - var reference = make([][][]float64, 270, 270) //============================================================ @@ -7374,8 +7368,7 @@ func TestMuseScore_PresetRegion(t *testing.T) { // Helicopter reference[269][0] = []float64{0, 0, 0, 1, 0, 0, 0, 0, 7, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 3.5004, 0, 0, 0, 127, 0, 127, 0, 0, 0, 0} - file, _ := os.Open("GeneralUser GS MuseScore v1.442.sf2") - soundFont, _ := meltysynth.NewSoundFont(file) + soundFont := loadGS(t) presetCount := len(soundFont.Presets) for pre := 0; pre < presetCount; pre++ { diff --git a/meltysynth/region_ex.go b/meltysynth/region_ex.go index c39620b..5c2406c 100644 --- a/meltysynth/region_ex.go +++ b/meltysynth/region_ex.go @@ -3,7 +3,6 @@ package meltysynth import "math" func (o *oscillator) startByRegion(data []int16, region regionPair) { - sampleRate := region.instrument.Sample.SampleRate loopMode := region.GetSampleModes() sampleStart := region.GetSampleStart() @@ -19,7 +18,6 @@ func (o *oscillator) startByRegion(data []int16, region regionPair) { } func (env *volumeEnvelope) startByRegion(region regionPair, key int32, velocity int32) { - // If the release time is shorter than 10 ms, it will be clamped to 10 ms to avoid pop noise. delay := region.GetDelayVolumeEnvelope() @@ -33,7 +31,6 @@ func (env *volumeEnvelope) startByRegion(region regionPair, key int32, velocity } func (env *modulationEnvelope) startByRegion(region regionPair, key int32, velocity int32) { - // According to the implementation of TinySoundFont, the attack time should be adjusted by the velocity. delay := region.GetDelayModulationEnvelope() @@ -47,11 +44,9 @@ func (env *modulationEnvelope) startByRegion(region regionPair, key int32, veloc } func (lfo *lfo) startVibrato(region regionPair, key int32, velocity int32) { - lfo.start(region.GetDelayVibratoLfo(), region.GetFrequencyVibratoLfo()) } func (lfo *lfo) startModulation(region regionPair, key int32, velocity int32) { - lfo.start(region.GetDelayModulationLfo(), region.GetFrequencyModulationLfo()) } diff --git a/meltysynth/region_pair.go b/meltysynth/region_pair.go index 94e7ab2..0fb6b45 100644 --- a/meltysynth/region_pair.go +++ b/meltysynth/region_pair.go @@ -6,7 +6,6 @@ type regionPair struct { } func newRegionPair(preset *PresetRegion, inst *InstrumentRegion) regionPair { - var result regionPair result.preset = preset diff --git a/meltysynth/sample_header.go b/meltysynth/sample_header.go index 3322194..2170a3c 100644 --- a/meltysynth/sample_header.go +++ b/meltysynth/sample_header.go @@ -20,7 +20,6 @@ type SampleHeader struct { } func readSampleHeadersFromChunk(r io.Reader, size int32) ([]*SampleHeader, error) { - var n int var err error @@ -29,11 +28,9 @@ func readSampleHeadersFromChunk(r io.Reader, size int32) ([]*SampleHeader, error } count := size/46 - 1 - headers := make([]*SampleHeader, count) for i := int32(0); i < count; i++ { - header := new(SampleHeader) header.Name, err = readFixedLengthString(r, 20) diff --git a/meltysynth/soundfont.go b/meltysynth/soundfont.go index 718e323..5bdafc6 100644 --- a/meltysynth/soundfont.go +++ b/meltysynth/soundfont.go @@ -3,6 +3,7 @@ package meltysynth import ( "encoding/binary" "errors" + "fmt" "io" ) @@ -16,11 +17,7 @@ type SoundFont struct { } func NewSoundFont(r io.Reader) (*SoundFont, error) { - - var err error - - var chunkId string - chunkId, err = readFourCC(r) + chunkId, err := readFourCC(r) if err != nil { return nil, err } @@ -40,10 +37,10 @@ func NewSoundFont(r io.Reader) (*SoundFont, error) { return nil, err } if formType != "sfbk" { - return nil, errors.New("the type of the riff chunk must be 'sfbk', but was '" + formType + "'") + return nil, fmt.Errorf("the type of the riff chunk must be 'sfbk', but was %q'", formType) } - result := new(SoundFont) + result := &SoundFont{} result.Info, err = newSoundFontInfo(r) if err != nil { diff --git a/meltysynth/soundfont_info.go b/meltysynth/soundfont_info.go index 90f0c25..a0583b2 100644 --- a/meltysynth/soundfont_info.go +++ b/meltysynth/soundfont_info.go @@ -21,7 +21,6 @@ type SoundFontInfo struct { } func newSoundFontInfo(r io.Reader) (*SoundFontInfo, error) { - var err error var chunkId string @@ -49,11 +48,9 @@ func newSoundFontInfo(r io.Reader) (*SoundFontInfo, error) { return nil, errors.New("the type of the list chunk must be 'INFO', but was '" + listType + "'") } pos += 4 - result := new(SoundFontInfo) for pos < end { - var id string id, err = readFourCC(r) if err != nil { @@ -69,7 +66,6 @@ func newSoundFontInfo(r io.Reader) (*SoundFontInfo, error) { pos += 4 switch id { - case "ifil": result.Version, err = newSoundFontVersion(r) case "isng": diff --git a/meltysynth/soundfont_math.go b/meltysynth/soundfont_math.go index e589568..69eb8d9 100644 --- a/meltysynth/soundfont_math.go +++ b/meltysynth/soundfont_math.go @@ -4,9 +4,10 @@ import ( "math" ) -const halfPi float32 = math.Pi - -const nonAudible float32 = 1.0e-3 +const ( + halfPi float32 = math.Pi // TODO: Shouls this be math.Pi / 2? + nonAudible float32 = 1.0e-3 +) var logNonAudible float32 = float32(math.Log(1.0e-3)) @@ -43,11 +44,12 @@ func calcExpCutoff(x float64) float64 { } func calcClamp(value float32, min float32, max float32) float32 { - if value < min { + switch { + case value < min: return min - } else if value > max { + case value > max: return max - } else { + default: return value } } diff --git a/meltysynth/soundfont_parameters.go b/meltysynth/soundfont_parameters.go index d098efc..73e42b1 100644 --- a/meltysynth/soundfont_parameters.go +++ b/meltysynth/soundfont_parameters.go @@ -13,11 +13,7 @@ type soundFontParameters struct { } func newSoundFontParameters(r io.Reader) (*soundFontParameters, error) { - - var err error - - var chunkId string - chunkId, err = readFourCC(r) + chunkId, err := readFourCC(r) if err != nil { return nil, err } @@ -25,15 +21,13 @@ func newSoundFontParameters(r io.Reader) (*soundFontParameters, error) { return nil, errors.New("the list chunk was not found") } - var pos int32 = 0 - var end int32 + var pos, end int32 err = binary.Read(r, binary.LittleEndian, &end) if err != nil { return nil, err } - var listType string - listType, err = readFourCC(r) + listType, err := readFourCC(r) if err != nil { return nil, err } @@ -51,7 +45,6 @@ func newSoundFontParameters(r io.Reader) (*soundFontParameters, error) { var sampleHeaders []*SampleHeader for pos < end { - var id string id, err = readFourCC(r) if err != nil { @@ -67,7 +60,6 @@ func newSoundFontParameters(r io.Reader) (*soundFontParameters, error) { pos += 4 switch id { - case "phdr": presetInfos, err = readPresetsFromChunk(r, size) case "pbag": diff --git a/meltysynth/soundfont_sampledata.go b/meltysynth/soundfont_sampledata.go index 79f2537..a4bccb7 100644 --- a/meltysynth/soundfont_sampledata.go +++ b/meltysynth/soundfont_sampledata.go @@ -12,12 +12,8 @@ type soundFontSampleData struct { } func newSoundFontSampleData(r io.Reader) (*soundFontSampleData, error) { - var n int - var err error - - var chunkId string - chunkId, err = readFourCC(r) + chunkId, err := readFourCC(r) if err != nil { return nil, err } @@ -25,15 +21,14 @@ func newSoundFontSampleData(r io.Reader) (*soundFontSampleData, error) { return nil, errors.New("the list chunk was not found") } - var pos int32 = 0 + var pos int32 var end int32 err = binary.Read(r, binary.LittleEndian, &end) if err != nil { return nil, err } - var listType string - listType, err = readFourCC(r) + listType, err := readFourCC(r) if err != nil { return nil, err } @@ -45,7 +40,6 @@ func newSoundFontSampleData(r io.Reader) (*soundFontSampleData, error) { result := new(soundFontSampleData) for pos < end { - var id string id, err = readFourCC(r) if err != nil { @@ -61,23 +55,19 @@ func newSoundFontSampleData(r io.Reader) (*soundFontSampleData, error) { pos += 4 switch id { - case "smpl": result.bitsPerSample = 16 result.samples = make([]int16, size/2) err = binary.Read(r, binary.LittleEndian, result.samples) - case "sm24": // 24 bit audio is not supported. n, err = r.Read(make([]byte, size)) if n != int(size) { return nil, errors.New("failed to read the 24 bit audio data") } - default: return nil, errors.New("the info list contains an unknown id '" + id + "'") } - if err != nil { return nil, err } diff --git a/meltysynth_test/soundfont_test.go b/meltysynth/soundfont_test.go similarity index 82% rename from meltysynth_test/soundfont_test.go rename to meltysynth/soundfont_test.go index c46037a..eed367a 100644 --- a/meltysynth_test/soundfont_test.go +++ b/meltysynth/soundfont_test.go @@ -1,23 +1,17 @@ -package meltysynth_test +package meltysynth import ( - "os" "testing" - - "github.com/sinshu/go-meltysynth/meltysynth" ) func TestTimGM6mb_SoundFont(t *testing.T) { - - file, _ := os.Open("TimGM6mb.sf2") - soundFont, _ := meltysynth.NewSoundFont(file) + soundFont := loadGM(t) TimGM6mb_SoundFontInfo(t, soundFont) TimGM6mb_SoundFontSampleData(t, soundFont) } -func TimGM6mb_SoundFontInfo(t *testing.T, soundFont *meltysynth.SoundFont) { - +func TimGM6mb_SoundFontInfo(t *testing.T, soundFont *SoundFont) { if soundFont.Info.Version.Major != 2 { t.Fail() } @@ -71,8 +65,7 @@ func TimGM6mb_SoundFontInfo(t *testing.T, soundFont *meltysynth.SoundFont) { } } -func TimGM6mb_SoundFontSampleData(t *testing.T, soundFont *meltysynth.SoundFont) { - +func TimGM6mb_SoundFontSampleData(t *testing.T, soundFont *SoundFont) { if soundFont.BitsPerSample != 16 { t.Fail() } diff --git a/meltysynth/soundfont_version.go b/meltysynth/soundfont_version.go index 2115379..ebc5f2c 100644 --- a/meltysynth/soundfont_version.go +++ b/meltysynth/soundfont_version.go @@ -11,7 +11,6 @@ type SoundFontVersion struct { } func newSoundFontVersion(r io.Reader) (SoundFontVersion, error) { - var result SoundFontVersion var err error diff --git a/meltysynth/synthesizer.go b/meltysynth/synthesizer.go index 6efbcd5..68894cb 100644 --- a/meltysynth/synthesizer.go +++ b/meltysynth/synthesizer.go @@ -36,7 +36,6 @@ type Synthesizer struct { } func NewSynthesizer(sf *SoundFont, settings *SynthesizerSettings) (*Synthesizer, error) { - err := settings.validate() if err != nil { return nil, err @@ -92,7 +91,6 @@ func NewSynthesizer(sf *SoundFont, settings *SynthesizerSettings) (*Synthesizer, } func (s *Synthesizer) ProcessMidiMessage(channel int32, command int32, data1 int32, data2 int32) { - if !(0 <= channel && int(channel) < len(s.channels)) { return } @@ -175,7 +173,6 @@ func (s *Synthesizer) ProcessMidiMessage(channel int32, command int32, data1 int } func (s *Synthesizer) NoteOff(channel int32, key int32) { - if !(0 <= channel && int(channel) < len(s.channels)) { return } @@ -189,7 +186,6 @@ func (s *Synthesizer) NoteOff(channel int32, key int32) { } func (s *Synthesizer) NoteOn(channel int32, key int32, velocity int32) { - if velocity == 0 { s.NoteOff(channel, key) return @@ -200,7 +196,6 @@ func (s *Synthesizer) NoteOn(channel int32, key int32, velocity int32) { } channelInfo := s.channels[channel] - presetId := (channelInfo.bankNumber << 16) | channelInfo.patchNumber preset, found := s.presetLookup[presetId] @@ -243,7 +238,6 @@ func (s *Synthesizer) NoteOn(channel int32, key int32, velocity int32) { } func (s *Synthesizer) NoteOffAll(immediate bool) { - if immediate { s.voices.clear() } else { @@ -254,24 +248,22 @@ func (s *Synthesizer) NoteOffAll(immediate bool) { } func (s *Synthesizer) NoteOffAllChannel(channel int32, immediate bool) { - if immediate { for i := 0; i < int(s.voices.activeVoiceCount); i++ { if s.voices.voices[i].channel == channel { s.voices.voices[i].kill() } } - } else { - for i := 0; i < int(s.voices.activeVoiceCount); i++ { - if s.voices.voices[i].channel == channel { - s.voices.voices[i].end() - } + return + } + for i := 0; i < int(s.voices.activeVoiceCount); i++ { + if s.voices.voices[i].channel == channel { + s.voices.voices[i].end() } } } func (s *Synthesizer) ResetAllControllers() { - channelCount := len(s.channels) for i := 0; i < channelCount; i++ { s.channels[i].resetAllControllers() @@ -279,7 +271,6 @@ func (s *Synthesizer) ResetAllControllers() { } func (s *Synthesizer) ResetAllControllersChannel(channel int32) { - if !(0 <= channel && int(channel) < len(s.channels)) { return } @@ -288,7 +279,6 @@ func (s *Synthesizer) ResetAllControllersChannel(channel int32) { } func (s *Synthesizer) Reset() { - s.voices.clear() channelCount := len(s.channels) @@ -300,8 +290,7 @@ func (s *Synthesizer) Reset() { } func (s *Synthesizer) Render(left []float32, right []float32) { - - wrote := int32(0) + var wrote int32 length := int32(len(left)) for wrote < length { if s.blockRead == s.BlockSize { @@ -324,7 +313,6 @@ func (s *Synthesizer) Render(left []float32, right []float32) { } func (s *Synthesizer) renderBlock() { - s.voices.process() for i := 0; i < int(s.BlockSize); i++ { @@ -344,7 +332,6 @@ func (s *Synthesizer) renderBlock() { } func (s *Synthesizer) writeBlock(previousGain float32, currentGain float32, source []float32, destination []float32) { - if math.Max(float64(previousGain), float64(currentGain)) < float64(nonAudible) { return } diff --git a/meltysynth/synthesizer_settings.go b/meltysynth/synthesizer_settings.go index 7d1b80d..0d43df7 100644 --- a/meltysynth/synthesizer_settings.go +++ b/meltysynth/synthesizer_settings.go @@ -16,7 +16,6 @@ type SynthesizerSettings struct { } func NewSynthesizerSettings(sampleRate int32) *SynthesizerSettings { - result := new(SynthesizerSettings) result.SampleRate = sampleRate @@ -28,7 +27,6 @@ func NewSynthesizerSettings(sampleRate int32) *SynthesizerSettings { } func (settings *SynthesizerSettings) validate() error { - if !(16000 <= settings.SampleRate && settings.SampleRate <= 192000) { return errors.New("the sample rate must be between 16000 and 192000") } diff --git a/meltysynth/testing_test.go b/meltysynth/testing_test.go new file mode 100644 index 0000000..0c6dc1f --- /dev/null +++ b/meltysynth/testing_test.go @@ -0,0 +1,50 @@ +package meltysynth + +import ( + "errors" + "os" + "testing" +) + +const ( + envGS = "MELTYSYNTH_GS" + envGM = "MELTYSYNTH_GM" + + defaultPathGS = "GeneralUser GS MuseScore v1.442.sf2" + defaultPathGM = "TimGM6mb.sf2" +) + +func loadGS(t *testing.T) *SoundFont { + return loadSoundFont(t, envGS, defaultPathGS) +} + +func loadGM(t *testing.T) *SoundFont { + return loadSoundFont(t, envGS, defaultPathGM) +} + +func loadSoundFont(t *testing.T, env, defaultPath string) *SoundFont { + var useDefault bool + p := os.Getenv(env) + if len(p) == 0 { + useDefault = true + p = defaultPath + // t.Skipf("missing environment variable %q to load soundfont", env) + } + f, err := os.Open(p) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + if useDefault { + t.Skipf("missing environment variable %q to load soundfont, default path %q not found", env, defaultPath) + } + t.Fatalf("envionrment variable %q set to %q, but file does not exist", env, p) + } + t.Fatal(err) + } + sf, err := NewSoundFont(f) + f.Close() + if err != nil { + t.Fatal(err) + } + + return sf +} diff --git a/meltysynth_test/utils_test.go b/meltysynth/utils_test.go similarity index 96% rename from meltysynth_test/utils_test.go rename to meltysynth/utils_test.go index 73e04ff..c13d718 100644 --- a/meltysynth_test/utils_test.go +++ b/meltysynth/utils_test.go @@ -1,10 +1,8 @@ -package meltysynth_test +package meltysynth import ( "math" "testing" - - "github.com/sinshu/go-meltysynth/meltysynth" ) func areEqual(t *testing.T, x float64, y float64) { @@ -16,8 +14,7 @@ func areEqual(t *testing.T, x float64, y float64) { } } -func checkInstrumentRegion(t *testing.T, region *meltysynth.InstrumentRegion, values []float64) { - +func checkInstrumentRegion(t *testing.T, region *InstrumentRegion, values []float64) { areEqual(t, float64(region.GetSampleStart()), values[0]) areEqual(t, float64(region.GetSampleEnd()), values[1]) areEqual(t, float64(region.GetSampleStartLoop()), values[2]) @@ -70,7 +67,7 @@ func checkInstrumentRegion(t *testing.T, region *meltysynth.InstrumentRegion, va areEqual(t, float64(region.GetRootKey()), values[49]) } -func checkPresetRegion(t *testing.T, region *meltysynth.PresetRegion, values []float64) { +func checkPresetRegion(t *testing.T, region *PresetRegion, values []float64) { areEqual(t, float64(region.GetModulationLfoToPitch()), values[0]) areEqual(t, float64(region.GetVibratoLfoToPitch()), values[1]) diff --git a/meltysynth/voice.go b/meltysynth/voice.go index ae6af6c..596c365 100644 --- a/meltysynth/voice.go +++ b/meltysynth/voice.go @@ -71,27 +71,19 @@ type voice struct { } func newVoice(s *Synthesizer) *voice { - - result := new(voice) - - result.synthesizer = s - - result.volEnv = newVolumeEnvelope(s) - result.modEnv = newModulationEnvelope(s) - - result.vibLfo = newLfo(s) - result.modLfo = newLfo(s) - - result.oscillator = newOscillator(s) - result.filter = newBiQuadFilter(s) - - result.block = make([]float32, s.BlockSize) - - return result + return &voice{ + synthesizer: s, + volEnv: newVolumeEnvelope(s), + modEnv: newModulationEnvelope(s), + vibLfo: newLfo(s), + modLfo: newLfo(s), + oscillator: newOscillator(s), + filter: newBiQuadFilter(s), + block: make([]float32, s.BlockSize), + } } func (v *voice) start(region regionPair, channel int32, key int32, velocity int32) { - v.exclusiveClass = region.GetExclusiveClass() v.channel = channel v.key = key @@ -151,7 +143,6 @@ func (v *voice) kill() { } func (v *voice) process() bool { - if v.noteGain < nonAudible { return false } @@ -212,13 +203,14 @@ func (v *voice) process() bool { } angle := float32(math.Pi/200) * (channelInfo.getPan() + v.instrumentPan + 50) - if angle <= 0 { + switch { + case angle <= 0: v.currentMixGainLeft = mixGain v.currentMixGainRight = 0 - } else if angle >= halfPi { + case angle >= halfPi: v.currentMixGainLeft = 0 v.currentMixGainRight = mixGain - } else { + default: v.currentMixGainLeft = mixGain * float32(math.Cos(float64(angle))) v.currentMixGainRight = mixGain * float32(math.Sin(float64(angle))) } @@ -239,7 +231,6 @@ func (v *voice) process() bool { } func (v *voice) releaseIfNecessary(channelInfo *channel) { - if v.voiceLength < v.synthesizer.minimumVoiceDuration { return } @@ -254,10 +245,8 @@ func (v *voice) releaseIfNecessary(channelInfo *channel) { } func (v *voice) getPriority() float32 { - if v.noteGain < nonAudible { return 0 - } else { - return v.volEnv.priority } + return v.volEnv.priority } diff --git a/meltysynth/voice_collection.go b/meltysynth/voice_collection.go index a579950..3af459e 100644 --- a/meltysynth/voice_collection.go +++ b/meltysynth/voice_collection.go @@ -9,23 +9,19 @@ type voiceCollection struct { } func newVoiceCollection(s *Synthesizer, maxActiveVoiceCount int32) *voiceCollection { - - result := new(voiceCollection) - - result.synthesizer = s - - result.voices = make([]*voice, maxActiveVoiceCount) + result := &voiceCollection{ + synthesizer: s, + voices: make([]*voice, maxActiveVoiceCount), + } for i := 0; i < len(result.voices); i++ { result.voices[i] = newVoice(s) } - result.activeVoiceCount = 0 return result } func (vc *voiceCollection) requestNew(region *InstrumentRegion, channel int32) *voice { - // If an exclusive class is assigned to the region, find a voice with the same class. // If found, reuse it to avoid playing multiple voices with the same class at a time. exclusiveClass := region.GetExclusiveClass() @@ -67,8 +63,7 @@ func (vc *voiceCollection) requestNew(region *InstrumentRegion, channel int32) * } func (vc *voiceCollection) process() { - - var i int32 = 0 + var i int32 for { if i == vc.activeVoiceCount { diff --git a/meltysynth/volume_envelope.go b/meltysynth/volume_envelope.go index 45a3fc0..c8d030d 100644 --- a/meltysynth/volume_envelope.go +++ b/meltysynth/volume_envelope.go @@ -26,7 +26,6 @@ func newVolumeEnvelope(s *Synthesizer) *volumeEnvelope { } func (env *volumeEnvelope) start(delay float32, attack float32, hold float32, decay float32, sustain float32, release float32) { - env.attackSlope = 1 / float64(attack) env.decaySlope = -9.226 / float64(decay) env.releaseSlope = -9.226 / float64(release) @@ -47,70 +46,56 @@ func (env *volumeEnvelope) start(delay float32, attack float32, hold float32, de } func (env *volumeEnvelope) release() { - env.stage = env_Release env.releaseStartTime = float64(env.processedSampleCount) / float64(env.synthesizer.SampleRate) env.releaseLevel = env.value } func (env *volumeEnvelope) process(sampleCount int32) bool { - env.processedSampleCount += sampleCount currentTime := float64(env.processedSampleCount) / float64(env.synthesizer.SampleRate) for env.stage <= env_Hold { - var endTime float64 switch env.stage { - case env_Delay: endTime = env.attackStartTime - case env_Attack: endTime = env.holdStartTime - case env_Hold: endTime = env.decayStartTime - default: panic("invalid envelope stage") } if currentTime < endTime { break - } else { - env.stage++ } + env.stage++ } switch env.stage { - case env_Delay: env.value = 0 env.priority = 4 + env.value return true - case env_Attack: env.value = float32(env.attackSlope * (currentTime - env.attackStartTime)) env.priority = 3 + env.value return true - case env_Hold: env.value = 1 env.priority = 2 + env.value return true - case env_Decay: env.value = float32(math.Max(calcExpCutoff(env.decaySlope*(currentTime-env.decayStartTime)), float64(env.sustainLevel))) env.priority = 1 + env.value return env.value > nonAudible - case env_Release: env.value = float32(float64(env.releaseLevel) * calcExpCutoff(env.releaseSlope*(currentTime-env.releaseStartTime))) env.priority = env.value return env.value > nonAudible - default: panic("invalid envelope stage") } diff --git a/meltysynth/zone.go b/meltysynth/zone.go index 1264455..44f121b 100644 --- a/meltysynth/zone.go +++ b/meltysynth/zone.go @@ -9,18 +9,15 @@ type zone struct { } func createZones(infos []*zoneInfo, generators []generator) ([]*zone, error) { - if len(infos) <= 1 { return nil, errors.New("no valid zone was found") } // The last one is the terminator. count := len(infos) - 1 - zones := make([]*zone, count) for i := 0; i < count; i++ { - info := infos[i] zo := new(zone) @@ -36,7 +33,6 @@ func createZones(infos []*zoneInfo, generators []generator) ([]*zone, error) { } func createEmptyZone() *zone { - result := new(zone) result.generators = make([]generator, 0) return result diff --git a/meltysynth/zone_info.go b/meltysynth/zone_info.go index 5fcd39f..0f662b0 100644 --- a/meltysynth/zone_info.go +++ b/meltysynth/zone_info.go @@ -14,7 +14,6 @@ type zoneInfo struct { } func readZonesFromChunk(r io.Reader, size int32) ([]*zoneInfo, error) { - var err error if size%4 != 0 { @@ -22,11 +21,9 @@ func readZonesFromChunk(r io.Reader, size int32) ([]*zoneInfo, error) { } count := size / 4 - zones := make([]*zoneInfo, count) for i := int32(0); i < count; i++ { - zone := new(zoneInfo) var generatorIndex uint16