Implement beep

This commit is contained in:
LeshaInc 2019-01-08 14:18:01 +02:00
parent 1dee04c246
commit 4a711ebe7d
No known key found for this signature in database
GPG Key ID: B4855290FC36DE72
4 changed files with 100 additions and 5 deletions

View File

@ -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

View File

@ -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 =>

View File

@ -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 = {

View 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
}
})
}
}