Skip to content

Commit

Permalink
Merge pull request #16 from thiagokokada/implement-notification-sound-2
Browse files Browse the repository at this point in the history
Implement notification sound for when the notification is cancelled
  • Loading branch information
thiagokokada authored Jan 16, 2024
2 parents 3f94ac8 + f0c9dca commit ad2e112
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 30 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ $ direnv allow

## Credits

[Notification sound](https://bigsoundbank.com/sound-1111-message-1.html) by
[Notification sound 1](https://bigsoundbank.com/sound-1111-message-1.html) and
[Notification sound 2](https://bigsoundbank.com/sound-1112-message-2.html) by
Joseph SARDIN - https://bigsoundbank.com.

Eye open extracted from Font Awesome, in SVG format from
[Wikipedia](https://en.m.wikipedia.org/wiki/File:Eye_open_font_awesome.svg).

[1]: https://www.allaboutvision.com/conditions/refractive-errors/what-is-20-20-20-rule/
[2]: https://modernod.com/articles/2023-july-aug/myth-busting-the-202020-rule
File renamed without changes.
Binary file added assets/notification_2.ogg
Binary file not shown.
17 changes: 12 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ func sendNotification(
return nil
}
if notificationSound {
<-playNotificationSound()
playSendNotificationSound()
}
return notification
}

func cancelNotificationAfter(notification notify.Notification, after time.Duration) {
func cancelNotificationAfter(
notification notify.Notification,
after time.Duration,
notificationSound bool,
) {
if notification == nil {
return
}
Expand All @@ -80,6 +84,9 @@ func cancelNotificationAfter(notification notify.Notification, after time.Durati
if err != nil {
fmt.Printf("Error while cancelling notification: %v\n", err)
}
if notificationSound {
playCancelNotificationSound()
}
}

func twentyTwentyTwenty(
Expand All @@ -99,7 +106,7 @@ func twentyTwentyTwenty(
fmt.Sprintf("Look at 20 feet (~6 meters) away for %.f seconds", duration.Seconds()),
notificationSound,
)
go cancelNotificationAfter(notification, duration)
go cancelNotificationAfter(notification, duration, notificationSound)
}()
}
}
Expand All @@ -118,7 +125,7 @@ func main() {
// only init Beep if notification sound is enabled, otherwise we will cause
// unnecessary noise in the speakers (and also increased memory usage)
if notificationSound {
err := initBeep()
err := initNotification()
if err != nil {
log.Fatalf("Error while initialising sound: %v\n", err)
}
Expand All @@ -138,7 +145,7 @@ func main() {
if notification == nil {
log.Fatalf("Test notification failed, exiting...")
}
go cancelNotificationAfter(notification, duration)
go cancelNotificationAfter(notification, duration, notificationSound)

if notificationSoundEnabled {
fmt.Printf(
Expand Down
15 changes: 11 additions & 4 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ import (
// is not run in CI.

func TestPlayNotificationSound(t *testing.T) {
err := initBeep()
err := initNotification()
if err != nil {
t.Fatalf("Error while initialising sound: %v\n", err)
}
const wait = 10

log.Println("You should listen to a sound!")
<-playNotificationSound()
log.Println("Waiting 5 seconds to ensure that the sound is finished")
time.Sleep(5 * time.Second)
playSendNotificationSound()
log.Printf("Waiting %d seconds to ensure that the sound is finished", wait)
time.Sleep(wait * time.Second)

log.Println("You should listen to another sound!")
playCancelNotificationSound()
log.Printf("Waiting %d seconds to ensure that the sound is finished", wait)
time.Sleep(wait * time.Second)
}

func TestSendNotification(t *testing.T) {
Expand Down
58 changes: 43 additions & 15 deletions notification_sound.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,63 @@ import (
const notificationSoundEnabled bool = true

var (
//go:embed assets/notification.ogg
NotificationSound embed.FS
Buffer *beep.Buffer
buffer1 *beep.Buffer
buffer2 *beep.Buffer
//go:embed assets/notification_1.ogg
notification1 embed.FS
//go:embed assets/notification_2.ogg
notification2 embed.FS
)

func playNotificationSound() chan bool {
func playSendNotificationSound() {
done := make(chan bool)
speaker.Play(
beep.Seq(Buffer.Streamer(0, Buffer.Len())),
beep.Seq(buffer1.Streamer(0, buffer1.Len())),
beep.Callback(func() { done <- true }),
)
return done
<-done
}

func initBeep() error {
f, err := NotificationSound.Open("assets/notification.ogg")
func playCancelNotificationSound() {
done := make(chan bool)
speaker.Play(
beep.Seq(buffer2.Streamer(0, buffer2.Len())),
beep.Callback(func() { done <- true }),
)
<-done
}

func initNotification() error {
loadNotification := func(notification embed.FS, file string) (*beep.Buffer, beep.Format, error) {
f, err := notification.Open(file)
if err != nil {
return nil, beep.Format{}, fmt.Errorf("load notification %s sound: %w", file, err)
}
streamer, format, err := vorbis.Decode(f)
if err != nil {
return nil, beep.Format{}, fmt.Errorf("decode notification %s sound: %w", file, err)
}
buffer := beep.NewBuffer(format)
buffer.Append(streamer)
return buffer, format, nil
}

var format beep.Format
var err error

buffer1, format, err = loadNotification(notification1, "assets/notification_1.ogg")
if err != nil {
return fmt.Errorf("failed to load notification sound: %w", err)
return fmt.Errorf("notification 1 sound failed: %w", err)
}

streamer, format, err := vorbis.Decode(f)
// ignoring format since all audio files should have the same format
buffer2, _, err = loadNotification(notification2, "assets/notification_2.ogg")
if err != nil {
return fmt.Errorf("failed to decode the notification sound: %w", err)
return fmt.Errorf("notification 2 sound failed: %w", err)
}
Buffer = beep.NewBuffer(format)
Buffer.Append(streamer)

// 1s/4 = 250ms of lag, good enough for this use case
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/4))
// 1s/8 = 125ms of maximum lag, good enough for this use case
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/8))

return nil
}
14 changes: 9 additions & 5 deletions notification_sound_disabled.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ package main

const notificationSoundEnabled bool = false

func playNotificationSound() chan bool {
c := make(chan bool)
c <- false
return c
func playSendNotificationSound() {
panic("Not implemented")
}

func initBeep() error { return nil }
func playCancelNotificationSound() {
panic("Not implemented")
}

func initNotification() error {
panic("Not implemented")
}

0 comments on commit ad2e112

Please sign in to comment.