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
|
||||||
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion classifier "natives-linux"
|
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion classifier "natives-linux"
|
||||||
libraryDependencies += "org.lwjgl" % "lwjgl-opengl" % lwjglVersion classifier "natives-windows"
|
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 := {
|
assemblyMergeStrategy in assembly := {
|
||||||
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
|
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.UiHandler
|
||||||
import ocelot.desktop.ui.widget.ScreenWidget
|
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.LogManager
|
||||||
import org.apache.logging.log4j.scala.Logging
|
import org.apache.logging.log4j.scala.Logging
|
||||||
import org.lwjgl.Version
|
import org.lwjgl.Version
|
||||||
@ -97,15 +97,15 @@ object OcelotDesktop extends Logging {
|
|||||||
|
|
||||||
private def setupEventHandlers(): Unit = {
|
private def setupEventHandlers(): Unit = {
|
||||||
EventBus.listenTo(classOf[BeepEvent], { case event: BeepEvent =>
|
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 =>
|
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 =>
|
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 =>
|
EventBus.listenTo(classOf[TextBufferSetEvent], { case event: TextBufferSetEvent =>
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
package ocelot.desktop.ui
|
package ocelot.desktop.ui
|
||||||
|
|
||||||
|
import java.nio.{ByteBuffer, IntBuffer}
|
||||||
|
|
||||||
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
import ocelot.desktop.geometry.{Size2D, Vector2D}
|
||||||
import ocelot.desktop.graphics.Graphics
|
import ocelot.desktop.graphics.Graphics
|
||||||
import ocelot.desktop.ui.widget.RootWidget
|
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.apache.logging.log4j.scala.Logging
|
||||||
import org.lwjgl.glfw.{GLFW, GLFWErrorCallback}
|
import org.lwjgl.glfw.{GLFW, GLFWErrorCallback}
|
||||||
|
import org.lwjgl.openal._
|
||||||
import org.lwjgl.opengl.{GL, GL11}
|
import org.lwjgl.opengl.{GL, GL11}
|
||||||
import org.lwjgl.system.MemoryUtil.NULL
|
import org.lwjgl.system.MemoryUtil.NULL
|
||||||
|
|
||||||
@ -18,6 +21,9 @@ class UiHandler(val root: RootWidget) extends Logging {
|
|||||||
private var window: Long = _
|
private var window: Long = _
|
||||||
private var fullRedraw = true
|
private var fullRedraw = true
|
||||||
|
|
||||||
|
private var soundDevice: Long = _
|
||||||
|
private var soundContext: Long = _
|
||||||
|
|
||||||
private val fpsCalculator = new FPSCalculator
|
private val fpsCalculator = new FPSCalculator
|
||||||
|
|
||||||
def fps: Float = fpsCalculator.fps
|
def fps: Float = fpsCalculator.fps
|
||||||
@ -66,12 +72,25 @@ class UiHandler(val root: RootWidget) extends Logging {
|
|||||||
|
|
||||||
FontLoader.init()
|
FontLoader.init()
|
||||||
graphics = new Graphics
|
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 = {
|
def start(): Unit = {
|
||||||
while (!GLFW.glfwWindowShouldClose(window)) {
|
while (!GLFW.glfwWindowShouldClose(window)) {
|
||||||
graphics.setViewport(windowSize.width.asInstanceOf[Int], windowSize.height.asInstanceOf[Int])
|
graphics.setViewport(windowSize.width.asInstanceOf[Int], windowSize.height.asInstanceOf[Int])
|
||||||
|
|
||||||
|
Audio.update()
|
||||||
|
|
||||||
KeyHandler.reset()
|
KeyHandler.reset()
|
||||||
MouseHandler.reset()
|
MouseHandler.reset()
|
||||||
ScrollHandler.reset()
|
ScrollHandler.reset()
|
||||||
@ -93,6 +112,10 @@ class UiHandler(val root: RootWidget) extends Logging {
|
|||||||
def terminate(): Unit = {
|
def terminate(): Unit = {
|
||||||
GLFW.glfwTerminate()
|
GLFW.glfwTerminate()
|
||||||
GLFW.glfwSetErrorCallback(null).free()
|
GLFW.glfwSetErrorCallback(null).free()
|
||||||
|
|
||||||
|
ALC10.alcMakeContextCurrent(NULL)
|
||||||
|
ALC10.alcDestroyContext(soundContext)
|
||||||
|
ALC10.alcCloseDevice(soundDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def update(): Unit = {
|
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