How to play an MP3 file stored in resources folder in Kotlin Multiplatform with Jetpack Compose for Desktop?

huangapple go评论80阅读模式
英文:

How to play an MP3 file stored in resources folder in Kotlin Multiplatform with Jetpack Compose for Desktop?

问题

我正在尝试在我的Compose for Desktop项目中按下按钮时播放声音。我的MP3文件存储在资源文件夹中(./src/jvmMain/resources/beep.mp3)。

我一直在尝试使用useResource函数,如下所示(在@Composable Button的onClick参数内部):

  1. scope.launch {
  2. useResource("beep.mp3") {
  3. val clip = AudioSystem.getClip()
  4. val audioInputStream = AudioSystem.getAudioInputStream(it)
  5. clip.open(audioInputStream)
  6. clip.start()
  7. }
  8. }

但我遇到了一个错误:Exception in thread "AWT-EventQueue-0" java.io.IOException: mark/reset not supported。如果我不使用scope,我也会得到相同的错误,我在我的可组合顶层定义如下:

  1. val scope = rememberCoroutineScope()

任何帮助将不胜感激!

英文:

I'm trying to play a sound in my Compose for Desktop project when pressing a button. My MP3 file is stored in the resources folder (./src/jvmMain/resources/beep.mp3).

I've been trying using the useResource function as follows (inside onClick parameter of a @Composable Button):

  1. scope.launch {
  2. useResource("beep.mp3") {
  3. val clip = AudioSystem.getClip()
  4. val audioInputStream = AudioSystem.getAudioInputStream(
  5. it
  6. )
  7. clip.open(audioInputStream)
  8. clip.start()
  9. }
  10. }

but I get an error: Exception in thread "AWT-EventQueue-0" java.io.IOException: mark/reset not supported. I got the same error if I don't use the scope, which I define at the top level of my composable as:

  1. val scope = rememberCoroutineScope()

Any help will be appreciated!

答案1

得分: 1

需要一个库来解码MP3文件。这是关键。您可以添加以下Maven依赖项:

  1. implementation("com.googlecode.soundlibs:mp3spi:1.9.5.4")

使用最初的Java Sound API,您可以使用以下代码:

  1. import java.io.File;
  2. import javax.sound.sampled.AudioFormat;
  3. import javax.sound.sampled.AudioInputStream;
  4. import javax.sound.sampled.AudioSystem;
  5. import javax.sound.sampled.AudioSystem.getAudioInputStream;
  6. import javax.sound.sampled.DataLine.Info;
  7. import javax.sound.sampled.SourceDataLine;
  8. class AudioPlayer {
  9. public void play(String path) {
  10. File file = new File(path);
  11. getAudioInputStream(file).use(in -> {
  12. AudioFormat outFormat = getOutFormat(in.getFormat());
  13. Info info = new Info(SourceDataLine.class, outFormat);
  14. AudioSystem.getLine(info).use(line -> {
  15. if (line instanceof SourceDataLine) {
  16. SourceDataLine sourceLine = (SourceDataLine) line;
  17. sourceLine.open(outFormat);
  18. sourceLine.start();
  19. stream(getAudioInputStream(outFormat, in), sourceLine);
  20. sourceLine.drain();
  21. sourceLine.stop();
  22. }
  23. });
  24. });
  25. }
  26. private AudioFormat getOutFormat(AudioFormat inFormat) {
  27. int ch = inFormat.getChannels();
  28. float rate = inFormat.getSampleRate();
  29. return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false);
  30. }
  31. private void stream(AudioInputStream in, SourceDataLine line) {
  32. byte[] buffer = new byte[65536];
  33. int n;
  34. while ((n = in.read(buffer, 0, buffer.length)) != -1) {
  35. line.write(buffer, 0, n);
  36. }
  37. }
  38. }

然后,从非UI线程/协程调用play方法:

  1. new AudioPlayer().play(fullPathToMP3File);

原始工作由oldo完成,参考链接:https://odoepner.wordpress.com/2013/07/19/play-mp3-or-ogg-using-javax-sound-sampled-mp3spi-vorbisspi/

  1. <details>
  2. <summary>英文:</summary>
  3. A library is needed to decode MP3 files. This is the key. You may add this maven dependency:

implementation("com.googlecode.soundlibs:mp3spi:1.9.5.4")

  1. Using Java Sound APIs you originally used:

import java.io.File
import javax.sound.sampled.AudioFormat
import javax.sound.sampled.AudioInputStream
import javax.sound.sampled.AudioSystem
import javax.sound.sampled.AudioSystem.getAudioInputStream
import javax.sound.sampled.DataLine.Info
import javax.sound.sampled.SourceDataLine

class AudioPlayer {
fun play(path: String) {
val file = File(path)
getAudioInputStream(file).use { in ->
val outFormat = getOutFormat(in.format)
val info = Info(SourceDataLine::class.java, outFormat)
AudioSystem.getLine(info).use { line ->
(line as? SourceDataLine)?.let { l ->
l.open(outFormat)
l.start()
stream(getAudioInputStream(outFormat, in), l)
l.drain()
l.stop()
}
}
}
}

  1. private fun getOutFormat(inFormat: AudioFormat): AudioFormat {
  2. val ch = inFormat.channels
  3. val rate = inFormat.sampleRate
  4. return AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch * 2, rate, false)
  5. }
  6. private fun stream(`in`: AudioInputStream, line: SourceDataLine) {
  7. val buffer = ByteArray(65536)
  8. var n = 0
  9. while (n != -1) {
  10. line.write(buffer, 0, n)
  11. n = `in`.read(buffer, 0, buffer.size)
  12. }
  13. }

}

  1. And call the `play` method from a non-UI thread / coroutine:

AudioPlayer().play(fullPathToMP3File)

  1. Credit to original work by oldo: https://odoepner.wordpress.com/2013/07/19/play-mp3-or-ogg-using-javax-sound-sampled-mp3spi-vorbisspi/
  2. </details>

huangapple
  • 本文由 发表于 2023年7月7日 01:49:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76631383.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定