Music Generation and Removal OS Apps

Published: Dec 26, 2024 by Isaac Johnson

About a year ago I demo’ed NoiseDash which is a simply white noise generator (project page). I recently found another Noise Generator that is similar, but basically marries some ‘channels’ of lo-fi YouTube beats with an overlay of noise generators. We’ll checkout NextBeats and how we can use it and even add our own video(s) to the mix.

What about removing music? Fast Music Remover is a great way to remove some background noise or music in a dockerized app. The backend is based on python and while not the fastest, it does do the job. We’ll launch that into Kubernetes and give it a go on some of my older YouTube videos.

Next Beats

MariusHosting clued me into this back in November and thus it got on my To-Do list to checkout.

The portainer setup he used was:

services:
  nextbeats:
    image: peppershade/nextbeats
    container_name: NextBeats
    healthcheck:
      test: ["CMD-SHELL", "nc -z 127.0.0.1 3000 || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 90s
    ports:
      - 6431:3000
    restart: on-failure:5

While the source repo doesn’t include an image, it would seem the dockerhub image from peppershade is someone’s build.

Call me paranoid, but I’m not as keen on using images not from the author.

Building our own image

I’ll clone

builder@builder-T100:~$ git clone https://github.com/btahir/next-beats.git
Cloning into 'next-beats'...
remote: Enumerating objects: 239, done.
remote: Counting objects: 100% (131/131), done.
remote: Compressing objects: 100% (95/95), done.
remote: Total 239 (delta 69), reused 88 (delta 34), pack-reused 108 (from 1)
Receiving objects: 100% (239/239), 40.31 MiB | 22.62 MiB/s, done.
Resolving deltas: 100% (99/99), done.
builder@builder-T100:~$ cd next-beats/

Then build

[+] Building 51.6s (12/12) FINISHED                                                                          
 => [internal] load build definition from Dockerfile                                                    0.0s
 => => transferring dockerfile: 382B                                                                    0.0s
 => [internal] load metadata for docker.io/library/node:20-alpine                                       1.1s
 => [auth] library/node:pull token for registry-1.docker.io                                             0.0s
 => [internal] load .dockerignore                                                                       0.0s
 => => transferring context: 171B                                                                       0.0s
 => [1/6] FROM docker.io/library/node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb  2.7s
 => => resolve docker.io/library/node:20-alpine@sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb  0.0s
 => => sha256:426f843809ae05f324883afceebaa2b9cab9cb697097dbb1a2a7a41c5701de72 7.67kB / 7.67kB          0.0s
 => => sha256:effc1ee6b93c4db0aab5729f3bc25d9df841e9b88ec3c4683d5a948a37553244 1.72kB / 1.72kB          0.0s
 => => sha256:2215267afb33d93392d8d16b41c461a569bf52a442e5af4c4add1371b73f26e3 6.18kB / 6.18kB          0.0s
 => => sha256:38a8310d387e375e0ec6fabe047a9149e8eb214073db9f461fee6251fd936a75 3.64MB / 3.64MB          0.3s
 => => sha256:65052e355180ad17acd4b61613338090c587b931124cd2946b9dcf0e945b76cb 42.54MB / 42.54MB        1.1s
 => => sha256:9cf3157062e6e87bb84921361434e0c930e8f73584eb06114cf7563178355253 1.26MB / 1.26MB          0.4s
 => => extracting sha256:38a8310d387e375e0ec6fabe047a9149e8eb214073db9f461fee6251fd936a75               0.2s
 => => sha256:dcfb660a4e70b11e73b64689666db5db05706d6d6a9bb950d9fea8593712af3c 446B / 446B              0.4s
 => => extracting sha256:65052e355180ad17acd4b61613338090c587b931124cd2946b9dcf0e945b76cb               1.0s
 => => extracting sha256:9cf3157062e6e87bb84921361434e0c930e8f73584eb06114cf7563178355253               0.1s
 => => extracting sha256:dcfb660a4e70b11e73b64689666db5db05706d6d6a9bb950d9fea8593712af3c               0.0s
 => [internal] load build context                                                                       0.1s
 => => transferring context: 7.41MB                                                                     0.1s
 => [2/6] WORKDIR /app                                                                                  0.6s
 => [3/6] COPY package*.json ./                                                                         0.0s
 => [4/6] RUN npm install                                                                              12.3s
 => [5/6] COPY . .                                                                                      0.1s
 => [6/6] RUN npm run build                                                                            29.3s 
 => exporting to image                                                                                  5.4s 
 => => exporting layers                                                                                 5.4s 
 => => writing image sha256:62c9ebc3e072477887e3d1610f1645fe680a2253bbb6c0e49269025be044d8d5            0.0s 
 => => naming to harbor.freshbrewed.science/freshbrewedprivate/nextbeats:latest                         0.0s 

Here is where I love the Nexterm we installed a week ago as it proves quite handy

/content/images/2024/12/nextbeats-01.png

I can then push to my Harbor CR

builder@builder-T100:~/next-beats$ docker push harbor.freshbrewed.science/freshbrewedprivate/nextbeats:latest
The push refers to repository [harbor.freshbrewed.science/freshbrewedprivate/nextbeats]
255da4eecdc5: Pushed 
e206a4b8143b: Pushed 
64c3ab40a1db: Pushed 
896f9312bfab: Pushed 
416cf80a51ee: Pushed 
9fd30ff9d315: Pushed 
875b8fb676b2: Pushed 
8c0ba6a3f014: Pushed 
3e01818d79cd: Pushed 
latest: digest: sha256:dd5116f3ba681b99dad085abb3b0203c2e7708a756a67c176be844aa1f65b3bb size: 2209
builder@builder-T100:~/next-beats$ 

I can have Gemini Code Assist help turn that into a YAML Manifest for me

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nextbeats
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nextbeats
  template:
    metadata:
      labels:
        app: nextbeats
    spec:
      containers:
        - name: nextbeats
          image: harbor.freshbrewed.science/freshbrewedprivate/nextbeats:latest # Using your built image
          ports:
            - containerPort: 3000
          livenessProbe:
            exec:
              command: ["nc", "-z", "localhost", "3000"]
            initialDelaySeconds: 90
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe: # Added readinessProbe for better control
            exec:
              command: ["nc", "-z", "localhost", "3000"]
            initialDelaySeconds: 90
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
      imagePullSecrets:
        - name: myharborreg
---
apiVersion: v1
kind: Service
metadata:
  name: nextbeats
spec:
  selector:
    app: nextbeats
  ports:
    - protocol: TCP
      port: 6431 # External port
      targetPort: 3000 # Port the container exposes
  type: ClusterIP # Or NodePort, depending on your needs

Then apply it

$ kubectl apply -f ./lofi.yaml
deployment.apps/nextbeats created
service/nextbeats created

I ran for a bit but it took a bit over 2m to come up to running

Every 2.0s: kubectl get pods -l app=nextbeats                                            LuiGi: Wed Dec 18 19:18:58 2024

NAME                         READY   STATUS    RESTARTS   AGE
nextbeats-675f7d7557-ck4hm   1/1     Running   0          2m39s

I did a port-forward

$ kubectl port-forward svc/nextbeats 6431:6431
Forwarding from 127.0.0.1:6431 -> 3000
Forwarding from [::1]:6431 -> 3000
Handling connection for 6431
Handling connection for 6431
Handling connection for 6431

And found a pretty nice interface. Ironically the loaded YouTube was one I had listened to before and I was listening to a very similar channel as I was writing this

/content/images/2024/12/nextbeats-02.png

As I’m somewhere loud and am using my phone, I think I need to create an ingress so I can test on the mobile.

I’ll fire up a quick Azure DNS A Record

$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 75.73.224.240 -n nextbeats
{
  "ARecords": [
    {
      "ipv4Address": "75.73.224.240"
    }
  ],
  "TTL": 3600,
  "etag": "62262f35-5453-45ed-a21a-73428e39b125",
  "fqdn": "nextbeats.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/nextbeats",
  "name": "nextbeats",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}

Then we can create an Ingress YAML to use it

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: nextbeats
  name: nextbeatsingress
spec:
  rules:
  - host: nextbeats.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: nextbeats
            port:
              number: 6431
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - nextbeats.tpk.pw
    secretName: mynextbeats-tls

I can then apply

$ kubectl apply -f ./nextbeats.ingress.yaml
ingress.networking.k8s.io/nextbeatsingress created

And wait for the cert to be satisified

$ kubectl get cert mynextbeats-tls
NAME              READY   SECRET            AGE
mynextbeats-tls   True    mynextbeats-tls   84s

It loaded just great

/content/images/2024/12/nextbeats-03.png

and we can use the ambience sliders to add extra noise like rain or wind to the YouTube video.

I can add a new Channel (such as the one I was listening to)

/content/images/2024/12/nextbeats-04.png

It now shows as Channel 7

/content/images/2024/12/nextbeats-05.png

I presently am not getting any adverts but I don’t know if its how the video is pulled or if YouTube just picked up my account (which has YouTube premium).

Music Remover

On the topic of music, Marius turned me on to Fast Music Remover

From the Github page, we can see it’s a pretty straightforward docker app

docker run -p 8080:8080 ghcr.io/omeryusufyagci/fast-music-remover:latest

I can make that a Kubernetes manifest with a Deployment and Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fast-music-remover
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fast-music-remover
  template:
    metadata:
      labels:
        app: fast-music-remover
    spec:
      containers:
      - name: fast-music-remover
        image: ghcr.io/omeryusufyagci/fast-music-remover:latest
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: fast-music-remover-service
spec:
  type: ClusterIP
  selector:
    app: fast-music-remover
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080

Then apply

$ kubectl apply -f ../musicremover.yaml
deployment.apps/fast-music-remover created
service/fast-music-remover-service created

Let’s port-forward

$ kubectl port-forward svc/fast-music-remover-service 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

And we can see it running

/content/images/2024/12/musicremover-01.png

Let’s take a video of mine from a couple years back which has music in there.

It’s taking a fair amount of time but seems to be processing

/content/images/2024/12/musicremover-02.png

The resulting video didn’t seem to play

/content/images/2024/12/musicremover-03.png

and I could see some errors in my forwarding output

$ kubectl port-forward svc/fast-music-remover-service 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
E1220 05:50:22.626626   54939 portforward.go:381] error copying from remote stream to local connection: readfrom tcp6 [::1]:8080->[::1]:57962: write tcp6 [::1]:8080->[::1]:57962: write: broken pipe
Handling connection for 8080
E1220 05:50:52.628170   54939 portforward.go:347] error creating error stream for port 8080 -> 8080: Timeout occurred
Handling connection for 8080
E1220 05:51:22.945613   54939 portforward.go:347] error creating error stream for port 8080 -> 8080: Timeout occurred
Handling connection for 8080

It seems to get stuck

/content/images/2024/12/musicremover-04.png

I’m going to move on to exposing via the ingress to see if it helps

$ gcloud dns --project=myanthosproject2 record-sets create fms.steeped.space --zone="steepedspace" --type="A" --ttl="300" --rrdatas="75.73.224.240"
NAME                TYPE  TTL  DATA
fms.steeped.space.  A     300  75.73.224.240

I’ll make an ingress YAML

$ cat ./fastmusic.ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: gcpleprod2
    ingress.kubernetes.io/proxy-body-size: "0"
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    nginx.org/websocket-services: fast-music-remover-service
  name: fast-music-remover-service
  namespace: default
spec:
  rules:
  - host: fms.steeped.space
    http:
      paths:
      - backend:
          service:
            name: fast-music-remover-service
            port:
              number: 8080
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - fms.steeped.space
    secretName: fast-music-remover-servicegcp-tls

And apply it

$ kubectl apply -f ./fastmusic.ingress.yaml
ingress.networking.k8s.io/fast-music-remover-service created

Once the cert is ready:

$ kubectl get cert fast-music-remover-servicegcp-tls
NAME                                READY   SECRET                              AGE
fast-music-remover-servicegcp-tls   True    fast-music-remover-servicegcp-tls   74s

With the new URL, I’ll try again

/content/images/2024/12/musicremover-05.png

This worked better. but i still get some clipping (as you can see here)

/content/images/2024/12/musicremover-06.png

I can see this as useful, however, as there are times really old videos of mine are wrecked due to copyright strikes or old (now enforced) music issues.

I can recall a couple of video tapes and old DVD videos of my brothers I couldn’t put on YouTube because of background music.

Summary

Today we reviewed a couple fun and useful apps related to music. NextBeats is a fantastic little lo-fi background player one can use to just chill out or concentrate on homework or work. I demo’ed it to my eldest daughter the night I was writing that block and she just kept it playing the whole drive home. While I got a withering look when i asked if it was ‘fire’, she did give me a thumbs up, which is high praise from a teenager.

The background noise remover will be awesome if I record more tech presentation videos. The hallway-based quick presentations/tech talks are always the noisiest. Even if I’m recording someone else’s, that could be useful to trim out the distracting background chatter. I’ll leave this up for now and see if it get’s used.

You can try some of the apps as I’m hosting them locally:

OpenSource FastMusicRemover NextBeats Kubernetes

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes