Skip to content

How can we emit sound without noise? #152

@renanbastos93

Description

@renanbastos93

I'd like to reproduce sound using a speaker like the example below because I want to simulate a piano.

package main

import (
	"fmt"
	"math"
	"os"
	"os/signal"
	"time"

	"github.com/gopxl/beep"
	"github.com/gopxl/beep/speaker"
)

const sampleRate = 44100

func main() {
	duration := 1 * time.Second // Duração do som (por exemplo, 1 segundo)

	// Configuração do formato do som
	format := beep.SampleRate(sampleRate)
	speaker.Init(format, format.N(time.Second/10))

	// Mapa de frequências para as notas musicais
	noteFrequencies := map[string]float64{
		"DO":  261.63,
		"RE":  293.66,
		"MI":  329.63,
		"FA":  349.23,
		"SOL": 392.00,
		"LA":  440.00,
		"SI":  493.88,

		// DO RE MI FA FA DO RE DO RE RE DO SOL FA MI MI DO RE MI FA FA
	}

	doremifa := []string{
		"DO", "RE", "MI", "FA", "FA", "DO", "RE", "DO",
		"RE", "RE", "DO", "SOL", "FA", "MI", "MI", "DO", "RE",
		"MI", "FA", "FA",
	}

	// Tocar as notas musicais
	for _, note := range doremifa {
		playNoteWithCleanSignal(note, noteFrequencies[note], duration)
	}

	fmt.Println("Finished playing notes.")
}

// playNoteWithCleanSignal toca uma nota musical específica com sinal limpo
func playNoteWithCleanSignal(note string, frequency float64, duration time.Duration) {
	// Gerar streamer para a nota especificada
	streamer := beep.StreamerFunc(func(samples [][2]float64) (n int, ok bool) {
		for i := range samples {
			t := float64(i) / sampleRate
			// Aplicar um envelope de amplitude para suavizar o início e o fim da nota
			amplitude := math.Sin(2*math.Pi*frequency*t) * envelope(float64(i), duration.Seconds()*sampleRate)
			samples[i][0] = amplitude * 0.3 // Ajuste de volume
			samples[i][1] = samples[i][0]   // Canal estéreo
		}
		return len(samples), true
	})

	// Tocar o som
	done := make(chan bool)
	speaker.Play(beep.Seq(streamer, beep.Callback(func() {
		done <- true
	})))

	fmt.Printf("Playing note %s (%.2f Hz) for %s...\n", note, frequency, duration)

	// Esperar até que a duração expire ou até que o usuário pressione Ctrl+C
	select {
	case <-time.After(duration):
	case <-interrupt():
	}

	fmt.Printf("Note %s finished playing.\n", note)
}

func envelope(i float64, length float64) float64 {
	attack := 0.1  // duração do ataque (em porcentagem da duração total)
	decay := 0.1   // duração da queda (em porcentagem da duração total)
	sustain := 0.7 // nível de sustentação (em porcentagem do nível máximo)
	release := 0.1 // duração da liberação (em porcentagem da duração total)

	attackSamples := int(attack * length)
	decaySamples := int(decay * length)
	sustainSamples := int(sustain * length)
	releaseSamples := int(release * length)

	if i < float64(attackSamples) {
		return float64(i) / float64(attackSamples)
	} else if i < float64(attackSamples+decaySamples) {
		return 1 - (i - float64(attackSamples)/float64(decaySamples)*(1-sustain))
	} else if i < float64(attackSamples+decaySamples+sustainSamples) {
		return sustain
	} else {
		return sustain - (i-float64(attackSamples)-float64(decaySamples)-float64(sustainSamples))/(float64(releaseSamples)*sustain)
	}
}

func interrupt() chan os.Signal {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	return c
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions