Playing a simple MP3 file

I’m working on a project that has to load a MP3 file and play it along with loaded images and videos. I stumped on the fact that OPENRNDR doesn’t handle MP3 files and came up with a workaround. I don’t think this is the ideal solution, but it works.

In build.gradle.kts we should add:

plugins {
    id("org.openjfx.javafxplugin") version "0.1.0"
}

javafx {
    version = "22.0.1"
}

And here is the code for a simple program that plays the audio file. Note that as JavaFX doesn’t have native MP3 support, I’m using FFmpeg to convert the MP3 file to a WAV file and play it.

The program also checks if there is already a WAV file with the same name of the MP3 file. If there is, it plays the WAV file directly, without converting.

import org.openrndr.application
import javax.sound.sampled.*
import java.io.File
import java.io.IOException
import kotlin.concurrent.thread

class AudioPlayer(private val mp3FilePath: String) {
    private var clip: Clip? = null
    private var isPlaying = false
    private val wavFilePath: String

    init {
        val mp3File = File(mp3FilePath)
        wavFilePath = mp3File.parent + File.separator + mp3File.nameWithoutExtension + ".wav"
    }

    fun play() {
        if (!isPlaying) {
            thread {
                try {
                    if (!File(wavFilePath).exists()) {
                        convertToWav()
                    }

                    val audioInputStream = AudioSystem.getAudioInputStream(File(wavFilePath))
                    clip = AudioSystem.getClip()
                    clip?.open(audioInputStream)
                    clip?.start()
                    isPlaying = true
                    println("Playing audio: $wavFilePath")
                    clip?.addLineListener { event ->
                        if (event.type == LineEvent.Type.STOP) {
                            isPlaying = false
                            clip?.close()
                            println("Audio playback finished")
                        }
                    }
                } catch (e: Exception) {
                    println("Error playing audio: ${e.message}")
                    e.printStackTrace()
                }
            }
        }
    }

    private fun convertToWav() {
        try {
            println("Converting MP3 to WAV...")
            val process = ProcessBuilder("ffmpeg", "-i", mp3FilePath, wavFilePath)
                .redirectErrorStream(true)
                .start()
            process.inputStream.bufferedReader().use { it.readText() }
            process.waitFor()
            println("Conversion completed: $wavFilePath")
        } catch (e: IOException) {
            println("Error converting file: ${e.message}")
            e.printStackTrace()
        }
    }

    fun stop() {
        clip?.stop()
        clip?.close()
        isPlaying = false
        println("Audio playback stopped")
    }
}

fun main() = application {
    var audioPlayer: AudioPlayer? = null

    program {
        extend {
            if (audioPlayer == null) {
                audioPlayer = AudioPlayer("C:\\videos\\godgangs\\godgangs.mp3")
                audioPlayer?.play()
            }
        }

        keyboard.keyDown.listen {
            if (it.name == "q") {
                audioPlayer?.stop()
                application.exit()
            }
        }
    }
}

It’s really simple and experimental but it can lead somewhere. Hope somebody finds it useful. Remember to replace the file paths!

:slight_smile:

If you find things which are missing or not well documented in the guide, I would be thankful for new issues at Issues · openrndr/openrndr-guide · GitHub

All your recent questions in the forum are very useful.

Well, I’m glad I’m not just being annoying then. I really like OPENRNDR and if I had a better head for coding I’d be glad to help more.

Thanks again! Happy to use minim and not a clunky workaround.

1 Like

BTW. How did I find that example?

I didn’t remember how to play mp3, so I went into the orx folder and typed ag mp3 in the terminal to find any mentions on all text files. ag is the Silver Searcher.

There’s also rg (RipGrep) which I discovered recently and claims to be faster.

Nice, I will keep this in mind and use Silver Searcher

I rarely use more arguments than these:

ag foo . --context 5 --width 100 --kotlin

Searches in . (current dir and subdirs), showing 5 lines above and below the hit, maxiumum 100 characters (I use this if there’s minimized javascript to avoid huge blobs of text in the results) and --kotlin is to only search that file type.

Also useful:

ag --list-file-types

:slight_smile:

Just so you know: the code you pasted above returns this:

Unresolved reference: minim

I have uncommented "orx-minim", from the gradle.build.kts file, but it didn’t seem to resolve the issue. I have pasted the exact code from GitHub you have provided.

Is there anything else I’m missing?

Did you click the reload Gradle button after uncommenting the line?

no, I didn’t. sorry. it’s my 1st time using Gradle, Kotlin, IDEA etc. it works perfectly and I have already adapted it to my program. thanks a lot

1 Like

I added a Usage section to orx. Maybe it helps the next one :smile:

1 Like