mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Implement beep
This commit is contained in:
parent
1dee04c246
commit
4a711ebe7d
@ -23,6 +23,9 @@ libraryDependencies += "org.lwjgl" % "lwjgl-glfw" % lwjglVersion classifier "nat
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion classifier "natives-linux"
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion classifier "natives-windows"
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-openal" % lwjglVersion
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-openal" % lwjglVersion classifier "natives-linux"
|
||||
libraryDependencies += "org.lwjgl" % "lwjgl-openal" % lwjglVersion classifier "natives-windows"
|
||||
|
||||
assemblyMergeStrategy in assembly := {
|
||||
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
|
||||
|
||||
@ -4,7 +4,7 @@ import java.io.{PrintWriter, StringWriter}
|
||||
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.widget.ScreenWidget
|
||||
import ocelot.desktop.util.ResourceManager
|
||||
import ocelot.desktop.util.{Audio, ResourceManager}
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.scala.Logging
|
||||
import org.lwjgl.Version
|
||||
@ -97,15 +97,15 @@ object OcelotDesktop extends Logging {
|
||||
|
||||
private def setupEventHandlers(): Unit = {
|
||||
EventBus.listenTo(classOf[BeepEvent], { case event: BeepEvent =>
|
||||
println(s"[EVENT] Beep (address = ${event.address}, frequency = ${event.frequency}, duration = ${event.duration})")
|
||||
Audio.beep(event.frequency, event.duration)
|
||||
})
|
||||
|
||||
EventBus.listenTo(classOf[BeepPatternEvent], { case event: BeepPatternEvent =>
|
||||
println(s"[EVENT] Beep (address = ${event.address}, pattern = ${event.pattern})")
|
||||
logger.info(s"[EVENT] Beep (address = ${event.address}, pattern = ${event.pattern})")
|
||||
})
|
||||
|
||||
EventBus.listenTo(classOf[MachineCrashEvent], { case event: MachineCrashEvent =>
|
||||
println(s"[EVENT] Machine crash! (address = ${event.address}, ${event.message})")
|
||||
logger.info(s"[EVENT] Machine crash! (address = ${event.address}, ${event.message})")
|
||||
})
|
||||
|
||||
EventBus.listenTo(classOf[TextBufferSetEvent], { case event: TextBufferSetEvent =>
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package ocelot.desktop.ui
|
||||
|
||||
import java.nio.{ByteBuffer, IntBuffer}
|
||||
|
||||
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
||||
import ocelot.desktop.graphics.Graphics
|
||||
import ocelot.desktop.ui.widget.RootWidget
|
||||
import ocelot.desktop.util.{FPSCalculator, FontLoader}
|
||||
import ocelot.desktop.util.{Audio, FPSCalculator, FontLoader}
|
||||
import org.apache.logging.log4j.scala.Logging
|
||||
import org.lwjgl.glfw.{GLFW, GLFWErrorCallback}
|
||||
import org.lwjgl.openal._
|
||||
import org.lwjgl.opengl.{GL, GL11}
|
||||
import org.lwjgl.system.MemoryUtil.NULL
|
||||
|
||||
@ -18,6 +21,9 @@ class UiHandler(val root: RootWidget) extends Logging {
|
||||
private var window: Long = _
|
||||
private var fullRedraw = true
|
||||
|
||||
private var soundDevice: Long = _
|
||||
private var soundContext: Long = _
|
||||
|
||||
private val fpsCalculator = new FPSCalculator
|
||||
|
||||
def fps: Float = fpsCalculator.fps
|
||||
@ -66,12 +72,25 @@ class UiHandler(val root: RootWidget) extends Logging {
|
||||
|
||||
FontLoader.init()
|
||||
graphics = new Graphics
|
||||
|
||||
soundDevice = ALC10.alcOpenDevice(null.asInstanceOf[ByteBuffer])
|
||||
if (soundDevice == 0) throw new IllegalStateException("Unable to create OpenAL device")
|
||||
|
||||
logger.info(s"OpenAL device: ${ALC10.alcGetString(soundDevice, ALC10.ALC_DEVICE_SPECIFIER)}")
|
||||
|
||||
soundContext = ALC10.alcCreateContext(soundDevice, null.asInstanceOf[IntBuffer])
|
||||
if (!ALC10.alcMakeContextCurrent(soundContext)) throw new IllegalStateException("Failed to make OpenAL context current")
|
||||
|
||||
val alcCapabilities = ALC.createCapabilities(soundDevice)
|
||||
AL.createCapabilities(alcCapabilities)
|
||||
}
|
||||
|
||||
def start(): Unit = {
|
||||
while (!GLFW.glfwWindowShouldClose(window)) {
|
||||
graphics.setViewport(windowSize.width.asInstanceOf[Int], windowSize.height.asInstanceOf[Int])
|
||||
|
||||
Audio.update()
|
||||
|
||||
KeyHandler.reset()
|
||||
MouseHandler.reset()
|
||||
ScrollHandler.reset()
|
||||
@ -93,6 +112,10 @@ class UiHandler(val root: RootWidget) extends Logging {
|
||||
def terminate(): Unit = {
|
||||
GLFW.glfwTerminate()
|
||||
GLFW.glfwSetErrorCallback(null).free()
|
||||
|
||||
ALC10.alcMakeContextCurrent(NULL)
|
||||
ALC10.alcDestroyContext(soundContext)
|
||||
ALC10.alcCloseDevice(soundDevice)
|
||||
}
|
||||
|
||||
private def update(): Unit = {
|
||||
|
||||
69
src/main/scala/ocelot/desktop/util/Audio.scala
Normal file
69
src/main/scala/ocelot/desktop/util/Audio.scala
Normal file
@ -0,0 +1,69 @@
|
||||
package ocelot.desktop.util
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
import org.apache.logging.log4j.scala.Logging
|
||||
import org.lwjgl.openal.AL10
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
object Audio extends Logging {
|
||||
private val sampleRate: Int = 44100
|
||||
private val amplitude: Int = 32
|
||||
|
||||
private val sources = new mutable.HashMap[Int, Int]()
|
||||
private val scheduled = new ArrayBuffer[(Short, Short)]()
|
||||
|
||||
def beep(frequency: Short, duration: Short): Unit = {
|
||||
scheduled += ((frequency, duration))
|
||||
}
|
||||
|
||||
private def _beep(frequency: Short, duration: Short): Unit = {
|
||||
val source = AL10.alGenSources()
|
||||
AL10.alSourcef(source, AL10.AL_PITCH, 1)
|
||||
AL10.alSourcef(source, AL10.AL_GAIN, 0.3f)
|
||||
AL10.alSource3f(source, AL10.AL_POSITION, 0, 0, 0)
|
||||
AL10.alSourcei(source, AL10.AL_LOOPING, AL10.AL_FALSE)
|
||||
|
||||
val samples = duration * sampleRate / 1000
|
||||
val data = ByteBuffer.allocateDirect(samples)
|
||||
val step = frequency / sampleRate.toFloat
|
||||
var offset = 1f
|
||||
|
||||
for (_ <- 0 until samples) {
|
||||
val angle = 2 * math.Pi * offset
|
||||
val value = (math.signum(math.sin(angle)) * amplitude).toByte ^ 0x80
|
||||
|
||||
offset += step
|
||||
if (offset > 1) offset -= 1
|
||||
|
||||
data.put(value.toByte)
|
||||
}
|
||||
|
||||
data.rewind()
|
||||
|
||||
val buffer = AL10.alGenBuffers()
|
||||
AL10.alBufferData(buffer, AL10.AL_FORMAT_MONO8, data, sampleRate)
|
||||
|
||||
AL10.alSourceQueueBuffers(source, buffer)
|
||||
AL10.alSourcePlay(source)
|
||||
|
||||
sources += (source -> buffer)
|
||||
}
|
||||
|
||||
def update(): Unit = {
|
||||
for ((frequency, duration) <- scheduled)
|
||||
_beep(frequency, duration)
|
||||
|
||||
scheduled.clear()
|
||||
|
||||
sources.retain((source, buffer) => {
|
||||
AL10.alGetSourcei(source, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING && {
|
||||
AL10.alDeleteSources(source)
|
||||
AL10.alDeleteBuffers(buffer)
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user