mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Add sound card
This commit is contained in:
parent
e5b2e5c37c
commit
9643dc05c1
@ -1 +1 @@
|
|||||||
Subproject commit e7b477cf4d369d91132275237e72c80ad5440e40
|
Subproject commit 7b2f4ce827d79a7c231bf70c76c28469b705bb9e
|
||||||
BIN
sprites/items/SoundCard.png
Normal file
BIN
sprites/items/SoundCard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 899 B |
Binary file not shown.
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 67 KiB |
@ -1,113 +1,114 @@
|
|||||||
BackgroundPattern 0 0 304 304
|
BackgroundPattern 0 0 304 304
|
||||||
BarSegment 493 105 16 4
|
BarSegment 488 51 16 4
|
||||||
ComputerMotherboard 305 0 79 70
|
ComputerMotherboard 305 0 79 70
|
||||||
Empty 9 316 1 1
|
Empty 9 316 1 1
|
||||||
EmptySlot 441 147 18 18
|
EmptySlot 458 147 18 18
|
||||||
Knob 385 0 50 50
|
Knob 385 0 50 50
|
||||||
KnobCenter 436 0 50 50
|
KnobCenter 436 0 50 50
|
||||||
KnobLimits 305 71 50 50
|
KnobLimits 305 71 50 50
|
||||||
ShadowBorder 0 305 1 24
|
ShadowBorder 0 305 1 24
|
||||||
ShadowCorner 424 122 24 24
|
ShadowCorner 441 122 24 24
|
||||||
TabArrow 458 105 8 14
|
TabArrow 502 122 8 14
|
||||||
buttons/BottomDrawerClose 460 147 18 18
|
buttons/BottomDrawerClose 477 147 18 18
|
||||||
buttons/BottomDrawerOpen 479 147 18 18
|
buttons/BottomDrawerOpen 441 180 18 18
|
||||||
buttons/PowerOff 424 180 18 18
|
buttons/PowerOff 460 180 18 18
|
||||||
buttons/PowerOn 443 180 18 18
|
buttons/PowerOn 479 180 18 18
|
||||||
icons/ButtonCheck 424 199 17 17
|
icons/ButtonCheck 466 122 17 17
|
||||||
icons/ButtonClipboard 442 199 17 17
|
icons/ButtonClipboard 484 122 17 17
|
||||||
icons/ButtonRandomize 460 199 17 17
|
icons/ButtonRandomize 373 219 17 17
|
||||||
icons/CPU 478 199 16 16
|
icons/CPU 391 219 16 16
|
||||||
icons/Card 495 199 16 16
|
icons/Card 408 219 16 16
|
||||||
icons/ComponentBus 449 122 16 16
|
icons/ComponentBus 425 219 16 16
|
||||||
icons/DragLMB 462 180 21 14
|
icons/DragLMB 441 199 21 14
|
||||||
icons/DragRMB 484 180 21 14
|
icons/DragRMB 463 199 21 14
|
||||||
icons/EEPROM 466 122 16 16
|
icons/EEPROM 442 219 16 16
|
||||||
icons/Floppy 483 122 16 16
|
icons/Floppy 459 219 16 16
|
||||||
icons/HDD 356 219 16 16
|
icons/HDD 476 219 16 16
|
||||||
icons/LMB 500 0 11 14
|
icons/LMB 402 51 11 14
|
||||||
icons/Memory 373 219 16 16
|
icons/Memory 493 219 16 16
|
||||||
icons/NA 390 219 16 16
|
icons/NA 305 251 16 16
|
||||||
icons/NotificationError 499 18 11 11
|
icons/NotificationError 426 51 11 11
|
||||||
icons/NotificationInfo 487 33 11 11
|
icons/NotificationInfo 438 51 11 11
|
||||||
icons/NotificationWarning 499 33 11 11
|
icons/NotificationWarning 450 51 11 11
|
||||||
icons/RMB 487 18 11 14
|
icons/RMB 414 51 11 14
|
||||||
icons/RackMountable 407 219 16 16
|
icons/RackMountable 322 251 16 16
|
||||||
icons/SettingsSound 498 147 12 17
|
icons/SettingsSound 498 180 12 17
|
||||||
icons/SettingsUI 487 0 12 17
|
icons/SettingsUI 496 147 12 17
|
||||||
icons/Tier0 424 219 16 16
|
icons/Tier0 339 251 16 16
|
||||||
icons/Tier1 441 219 16 16
|
icons/Tier1 356 251 16 16
|
||||||
icons/Tier2 458 219 16 16
|
icons/Tier2 373 251 16 16
|
||||||
items/APU0 356 122 16 96
|
items/APU0 373 122 16 96
|
||||||
items/APU1 373 122 16 96
|
items/APU1 390 122 16 96
|
||||||
items/APU2 390 122 16 96
|
items/APU2 407 122 16 96
|
||||||
items/CPU0 475 219 16 16
|
items/CPU0 390 251 16 16
|
||||||
items/CPU1 492 219 16 16
|
items/CPU1 407 251 16 16
|
||||||
items/CPU2 305 251 16 16
|
items/CPU2 424 251 16 16
|
||||||
items/CardBase 322 251 16 16
|
items/CardBase 441 251 16 16
|
||||||
items/CircuitBoard 339 251 16 16
|
items/CircuitBoard 458 251 16 16
|
||||||
items/ComponentBus0 356 251 16 16
|
items/ComponentBus0 475 251 16 16
|
||||||
items/ComponentBus1 373 251 16 16
|
items/ComponentBus1 492 251 16 16
|
||||||
items/ComponentBus2 390 251 16 16
|
items/ComponentBus2 305 268 16 16
|
||||||
items/ComponentBus3 407 251 16 16
|
items/ComponentBus3 322 268 16 16
|
||||||
items/DataCard0 305 122 16 128
|
items/DataCard0 305 122 16 128
|
||||||
items/DataCard1 322 122 16 128
|
items/DataCard1 322 122 16 128
|
||||||
items/DataCard2 339 122 16 128
|
items/DataCard2 339 122 16 128
|
||||||
items/DebugCard 424 251 16 16
|
items/DebugCard 339 268 16 16
|
||||||
items/DiskDriveMountable 441 251 16 16
|
items/DiskDriveMountable 356 268 16 16
|
||||||
items/EEPROM 458 251 16 16
|
items/EEPROM 373 268 16 16
|
||||||
items/FloppyDisk_dyeBlack 475 251 16 16
|
items/FloppyDisk_dyeBlack 390 268 16 16
|
||||||
items/FloppyDisk_dyeBlue 492 251 16 16
|
items/FloppyDisk_dyeBlue 407 268 16 16
|
||||||
items/FloppyDisk_dyeBrown 305 268 16 16
|
items/FloppyDisk_dyeBrown 424 268 16 16
|
||||||
items/FloppyDisk_dyeCyan 322 268 16 16
|
items/FloppyDisk_dyeCyan 441 268 16 16
|
||||||
items/FloppyDisk_dyeGray 339 268 16 16
|
items/FloppyDisk_dyeGray 458 268 16 16
|
||||||
items/FloppyDisk_dyeGreen 356 268 16 16
|
items/FloppyDisk_dyeGreen 475 268 16 16
|
||||||
items/FloppyDisk_dyeLightBlue 373 268 16 16
|
items/FloppyDisk_dyeLightBlue 492 268 16 16
|
||||||
items/FloppyDisk_dyeLightGray 390 268 16 16
|
items/FloppyDisk_dyeLightGray 305 285 16 16
|
||||||
items/FloppyDisk_dyeLime 407 268 16 16
|
items/FloppyDisk_dyeLime 322 285 16 16
|
||||||
items/FloppyDisk_dyeMagenta 424 268 16 16
|
items/FloppyDisk_dyeMagenta 339 285 16 16
|
||||||
items/FloppyDisk_dyeOrange 441 268 16 16
|
items/FloppyDisk_dyeOrange 356 285 16 16
|
||||||
items/FloppyDisk_dyePink 458 268 16 16
|
items/FloppyDisk_dyePink 373 285 16 16
|
||||||
items/FloppyDisk_dyePurple 475 268 16 16
|
items/FloppyDisk_dyePurple 390 285 16 16
|
||||||
items/FloppyDisk_dyeRed 492 268 16 16
|
items/FloppyDisk_dyeRed 407 285 16 16
|
||||||
items/FloppyDisk_dyeWhite 305 285 16 16
|
items/FloppyDisk_dyeWhite 424 285 16 16
|
||||||
items/FloppyDisk_dyeYellow 322 285 16 16
|
items/FloppyDisk_dyeYellow 441 285 16 16
|
||||||
items/GraphicsCard0 339 285 16 16
|
items/GraphicsCard0 458 285 16 16
|
||||||
items/GraphicsCard1 356 285 16 16
|
items/GraphicsCard1 475 285 16 16
|
||||||
items/GraphicsCard2 373 285 16 16
|
items/GraphicsCard2 492 285 16 16
|
||||||
items/HardDiskDrive0 390 285 16 16
|
items/HardDiskDrive0 356 71 16 16
|
||||||
items/HardDiskDrive1 407 285 16 16
|
items/HardDiskDrive1 373 71 16 16
|
||||||
items/HardDiskDrive2 424 285 16 16
|
items/HardDiskDrive2 390 71 16 16
|
||||||
items/InternetCard 424 147 16 32
|
items/InternetCard 441 147 16 32
|
||||||
items/LinkedCard 407 122 16 96
|
items/LinkedCard 424 122 16 96
|
||||||
items/Memory0 441 285 16 16
|
items/Memory0 407 71 16 16
|
||||||
items/Memory1 458 285 16 16
|
items/Memory1 424 71 16 16
|
||||||
items/Memory2 475 285 16 16
|
items/Memory2 441 71 16 16
|
||||||
items/Memory3 492 285 16 16
|
items/Memory3 458 71 16 16
|
||||||
items/Memory4 356 71 16 16
|
items/Memory4 475 71 16 16
|
||||||
items/Memory5 373 71 16 16
|
items/Memory5 492 71 16 16
|
||||||
items/NetworkCard 390 71 16 16
|
items/NetworkCard 356 88 16 16
|
||||||
items/RedstoneCard0 407 71 16 16
|
items/RedstoneCard0 373 88 16 16
|
||||||
items/RedstoneCard1 424 71 16 16
|
items/RedstoneCard1 390 88 16 16
|
||||||
items/Server0 441 71 16 16
|
items/Server0 407 88 16 16
|
||||||
items/Server1 458 71 16 16
|
items/Server1 424 88 16 16
|
||||||
items/Server2 475 71 16 16
|
items/Server2 441 88 16 16
|
||||||
items/Server3 492 71 16 16
|
items/Server3 458 88 16 16
|
||||||
items/WirelessNetworkCard0 356 88 16 16
|
items/SoundCard 356 122 16 128
|
||||||
items/WirelessNetworkCard1 373 88 16 16
|
items/WirelessNetworkCard0 475 88 16 16
|
||||||
nodes/Cable 356 236 8 8
|
items/WirelessNetworkCard1 492 88 16 16
|
||||||
nodes/Computer 390 88 16 16
|
nodes/Cable 373 237 8 8
|
||||||
nodes/ComputerActivityOverlay 407 88 16 16
|
nodes/Computer 356 105 16 16
|
||||||
nodes/ComputerErrorOverlay 424 88 16 16
|
nodes/ComputerActivityOverlay 373 105 16 16
|
||||||
nodes/ComputerOnOverlay 441 88 16 16
|
nodes/ComputerErrorOverlay 390 105 16 16
|
||||||
nodes/DiskDrive 458 88 16 16
|
nodes/ComputerOnOverlay 407 105 16 16
|
||||||
nodes/DiskDriveActivity 475 88 16 16
|
nodes/DiskDrive 424 105 16 16
|
||||||
nodes/DiskDriveFloppy 492 88 16 16
|
nodes/DiskDriveActivity 441 105 16 16
|
||||||
nodes/IronNoteBlock 356 105 16 16
|
nodes/DiskDriveFloppy 458 105 16 16
|
||||||
nodes/NewNode 373 105 16 16
|
nodes/IronNoteBlock 475 105 16 16
|
||||||
nodes/NoteBlock 390 105 16 16
|
nodes/NewNode 492 105 16 16
|
||||||
nodes/Relay 407 105 16 16
|
nodes/NoteBlock 487 0 16 16
|
||||||
nodes/Screen 424 105 16 16
|
nodes/Relay 487 17 16 16
|
||||||
nodes/ScreenOnOverlay 441 105 16 16
|
nodes/Screen 487 34 16 16
|
||||||
|
nodes/ScreenOnOverlay 385 51 16 16
|
||||||
panel/BorderB 8 305 4 4
|
panel/BorderB 8 305 4 4
|
||||||
panel/BorderL 63 305 4 2
|
panel/BorderL 63 305 4 2
|
||||||
panel/BorderR 13 305 4 4
|
panel/BorderR 13 305 4 4
|
||||||
@ -117,16 +118,16 @@ panel/CornerBR 28 305 4 4
|
|||||||
panel/CornerTL 33 305 4 4
|
panel/CornerTL 33 305 4 4
|
||||||
panel/CornerTR 38 305 4 4
|
panel/CornerTR 38 305 4 4
|
||||||
panel/Fill 6 316 2 2
|
panel/Fill 6 316 2 2
|
||||||
particles/Note 485 105 7 10
|
particles/Note 480 51 7 10
|
||||||
screen/BorderB 5 305 2 8
|
screen/BorderB 5 305 2 8
|
||||||
screen/BorderT 2 305 2 10
|
screen/BorderT 2 305 2 10
|
||||||
screen/CornerBL 365 236 8 8
|
screen/CornerBL 382 237 8 8
|
||||||
screen/CornerBR 374 236 8 8
|
screen/CornerBR 391 237 8 8
|
||||||
screen/CornerTL 467 105 8 10
|
screen/CornerTL 462 51 8 10
|
||||||
screen/CornerTR 476 105 8 10
|
screen/CornerTR 471 51 8 10
|
||||||
window/BorderDark 2 316 1 4
|
window/BorderDark 2 316 1 4
|
||||||
window/BorderLight 4 316 1 4
|
window/BorderLight 4 316 1 4
|
||||||
window/CloseButton 383 236 7 6
|
window/CloseButton 400 237 7 6
|
||||||
window/CornerBL 43 305 4 4
|
window/CornerBL 43 305 4 4
|
||||||
window/CornerBR 48 305 4 4
|
window/CornerBR 48 305 4 4
|
||||||
window/CornerTL 53 305 4 4
|
window/CornerTL 53 305 4 4
|
||||||
|
|||||||
89
src/main/scala/ocelot/desktop/audio/AL10W.scala
Normal file
89
src/main/scala/ocelot/desktop/audio/AL10W.scala
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package ocelot.desktop.audio
|
||||||
|
|
||||||
|
import ocelot.desktop.util.Logging
|
||||||
|
import org.lwjgl.openal.AL10
|
||||||
|
|
||||||
|
import java.nio.{ByteBuffer, IntBuffer}
|
||||||
|
|
||||||
|
object AL10W extends Logging {
|
||||||
|
private def run[T](func: String)(f: => T): T = {
|
||||||
|
// logger.debug(func)
|
||||||
|
val res = f
|
||||||
|
val err = AL10.alGetError()
|
||||||
|
if (err != AL10.AL_NO_ERROR) {
|
||||||
|
val errName = classOf[AL10].getDeclaredFields.find(field => {
|
||||||
|
try {
|
||||||
|
field.getInt() == err
|
||||||
|
} catch {
|
||||||
|
case _: Exception => false
|
||||||
|
}
|
||||||
|
}).map(_.getName).getOrElse(err.toHexString)
|
||||||
|
logger.error(s"OpenAL error: ${func}: ${errName}")
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
def alIsExtensionPresent(name: String): Boolean = run("alIsExtensionPresent") {
|
||||||
|
AL10.alIsExtensionPresent(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alGenBuffers(): Int = run("alGenBuffers") {
|
||||||
|
AL10.alGenBuffers()
|
||||||
|
}
|
||||||
|
|
||||||
|
def alBufferData(buffer: Int, format: Int, data: ByteBuffer, freq: Int): Unit = run("alBufferData") {
|
||||||
|
AL10.alBufferData(buffer, format, data, freq)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alGetBufferi(buffer: Int, pname: Int): Int = run("alGetBufferi") {
|
||||||
|
AL10.alGetBufferi(buffer, pname)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alDeleteBuffers(buffer: Int): Unit = run("alDeleteBuffers") {
|
||||||
|
AL10.alDeleteBuffers(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alGenSources(): Int = run("alGenSources") {
|
||||||
|
AL10.alGenSources()
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourceQueueBuffers(source: Int, buffer: Int): Unit = run("alSourceQueueBuffers") {
|
||||||
|
AL10.alSourceQueueBuffers(source, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourceUnqueueBuffers(source: Int, buffers: IntBuffer): Unit = run("alSourceUnqueueBuffers") {
|
||||||
|
AL10.alSourceUnqueueBuffers(source, buffers)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alGetSourcei(source: Int, pname: Int): Int = run("alGetSourcei") {
|
||||||
|
AL10.alGetSourcei(source, pname)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourcei(source: Int, pname: Int, value: Int): Unit = run("alSourcei") {
|
||||||
|
AL10.alSourcei(source, pname, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourcef(source: Int, pname: Int, value: Float): Unit = run("alSourcef") {
|
||||||
|
AL10.alSourcef(source, pname, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSource3f(source: Int, pname: Int, v1: Float, v2: Float, v3: Float): Unit = run("alSource3f") {
|
||||||
|
AL10.alSource3f(source, pname, v1, v2, v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourcePlay(source: Int): Unit = run("alSourcePlay") {
|
||||||
|
AL10.alSourcePlay(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourcePause(source: Int): Unit = run("alSourceStop") {
|
||||||
|
AL10.alSourcePause(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alSourceStop(source: Int): Unit = run("alSourceStop") {
|
||||||
|
AL10.alSourceStop(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
def alDeleteSources(source: Int): Unit = run("alDeleteSources") {
|
||||||
|
AL10.alDeleteSources(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,12 +5,13 @@ import ocelot.desktop.util.Logging
|
|||||||
import org.lwjgl.LWJGLException
|
import org.lwjgl.LWJGLException
|
||||||
import org.lwjgl.openal.{AL, AL10, ALC10}
|
import org.lwjgl.openal.{AL, AL10, ALC10}
|
||||||
|
|
||||||
|
import java.nio.{ByteBuffer, ByteOrder}
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
object Audio extends Logging {
|
object Audio extends Logging {
|
||||||
val sampleRate: Int = 44100
|
val sampleRate: Int = 44100
|
||||||
|
|
||||||
private val sources = new mutable.HashMap[SoundSource, SourceState]
|
private val sources = new mutable.HashMap[SoundSource, Int]
|
||||||
private var _disabled = true
|
private var _disabled = true
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +20,6 @@ object Audio extends Logging {
|
|||||||
def init(): Unit = {
|
def init(): Unit = {
|
||||||
try {
|
try {
|
||||||
AL.create()
|
AL.create()
|
||||||
AL10.alGetError()
|
|
||||||
logger.info(s"OpenAL device: ${ALC10.alcGetString(AL.getDevice, ALC10.ALC_DEVICE_SPECIFIER)}")
|
logger.info(s"OpenAL device: ${ALC10.alcGetString(AL.getDevice, ALC10.ALC_DEVICE_SPECIFIER)}")
|
||||||
_disabled = false
|
_disabled = false
|
||||||
} catch {
|
} catch {
|
||||||
@ -31,16 +31,49 @@ object Audio extends Logging {
|
|||||||
|
|
||||||
def isDisabled: Boolean = _disabled
|
def isDisabled: Boolean = _disabled
|
||||||
|
|
||||||
def numSources: Int = synchronized { sources.size }
|
def numSources: Int = synchronized {
|
||||||
|
sources.size
|
||||||
|
}
|
||||||
|
|
||||||
|
def newStream(soundCategory: SoundCategory.Value, pitch: Float = 1f,
|
||||||
|
volume: Float = 1f): (SoundStream, SoundSource) =
|
||||||
|
{
|
||||||
|
var source: SoundSource = null
|
||||||
|
|
||||||
|
val stream = new SoundStream {
|
||||||
|
override def enqueue(samples: SoundSamples): Unit = Audio.synchronized {
|
||||||
|
val sourceId = if (sources.contains(source)) {
|
||||||
|
sources(source)
|
||||||
|
} else {
|
||||||
|
val sourceId = AL10W.alGenSources()
|
||||||
|
|
||||||
|
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
|
||||||
|
AL10W.alSource3f(sourceId, AL10.AL_POSITION, 0f, 0f, 0f)
|
||||||
|
AL10W.alSourcef(sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster)
|
||||||
|
sources.put(source, sourceId)
|
||||||
|
|
||||||
|
sourceId
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupSourceBuffers(sourceId)
|
||||||
|
|
||||||
|
val bufferId = samples.genBuffer()
|
||||||
|
AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
||||||
|
if (AL10W.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING)
|
||||||
|
AL10W.alSourcePlay(sourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume)
|
||||||
|
(stream, source)
|
||||||
|
}
|
||||||
|
|
||||||
def getSourceStatus(source: SoundSource): SoundSource.Status.Value = synchronized {
|
def getSourceStatus(source: SoundSource): SoundSource.Status.Value = synchronized {
|
||||||
if (!sources.contains(source))
|
if (!sources.contains(source))
|
||||||
return SoundSource.Status.Stopped
|
return SoundSource.Status.Stopped
|
||||||
|
|
||||||
sources(source).status
|
val sourceId = sources(source)
|
||||||
|
AL10W.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) match {
|
||||||
val state = sources(source)
|
|
||||||
AL10.alGetSourcei(state.sourceId, AL10.AL_SOURCE_STATE) match {
|
|
||||||
case AL10.AL_PLAYING => SoundSource.Status.Playing
|
case AL10.AL_PLAYING => SoundSource.Status.Playing
|
||||||
case AL10.AL_PAUSED => SoundSource.Status.Paused
|
case AL10.AL_PAUSED => SoundSource.Status.Paused
|
||||||
case _ => SoundSource.Status.Stopped
|
case _ => SoundSource.Status.Stopped
|
||||||
@ -48,75 +81,63 @@ object Audio extends Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def playSource(source: SoundSource): Unit = synchronized {
|
def playSource(source: SoundSource): Unit = synchronized {
|
||||||
|
if (getSourceStatus(source) == SoundSource.Status.Playing)
|
||||||
|
return
|
||||||
|
|
||||||
if (sources.contains(source)) {
|
if (sources.contains(source)) {
|
||||||
AL10.alSourcePlay(sources(source).sourceId)
|
AL10W.alSourcePlay(sources(source))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val sourceId = AL10.alGenSources()
|
val sourceId = AL10W.alGenSources()
|
||||||
val (bufferId, isAssociated) = source.kind match {
|
source.kind match {
|
||||||
case SoundSource.KindSoundBuffer(buffer) => (buffer.bufferId, false)
|
case SoundSource.KindSoundBuffer(buffer) =>
|
||||||
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, channels)) =>
|
AL10W.alSourcei(sourceId, AL10.AL_BUFFER, buffer.bufferId)
|
||||||
val bufferId = AL10.alGenBuffers()
|
case SoundSource.KindSoundSamples(samples) =>
|
||||||
val format = channels match {
|
val bufferId = samples.genBuffer()
|
||||||
case 2 => AL10.AL_FORMAT_STEREO16
|
if (bufferId != -1) AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
||||||
case 1 => AL10.AL_FORMAT_MONO16
|
case SoundSource.KindStream(_) =>
|
||||||
}
|
|
||||||
|
|
||||||
AL10.alBufferData(bufferId, format, buffer, rate)
|
|
||||||
val err = AL10.alGetError
|
|
||||||
if (err != AL10.AL_NO_ERROR) {
|
|
||||||
logger.error(s"Unable to create OpenAL buffer: $err, $format, $rate")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
(bufferId, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AL10.alSourcei(sourceId, AL10.AL_BUFFER, bufferId)
|
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
|
||||||
AL10.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
|
AL10W.alSource3f(sourceId, AL10.AL_POSITION, 0f, 0f, 0f)
|
||||||
AL10.alSource3f(sourceId, AL10.AL_POSITION, 0f, 0f, 0f)
|
AL10W.alSourcei(sourceId, AL10.AL_LOOPING, if (source.looping) AL10.AL_TRUE else AL10.AL_FALSE)
|
||||||
AL10.alSourcei(sourceId, AL10.AL_LOOPING, if (source.looping) AL10.AL_TRUE else AL10.AL_FALSE)
|
AL10W.alSourcef(sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster)
|
||||||
AL10.alSourcef(sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster)
|
AL10W.alSourcePlay(sourceId)
|
||||||
AL10.alSourcePlay(sourceId)
|
|
||||||
|
|
||||||
val state = SourceState(sourceId, associatedBufferId = if (isAssociated) bufferId else -1, SoundSource.Status.Playing)
|
sources.put(source, sourceId)
|
||||||
sources.put(source, state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def pauseSource(source: SoundSource): Unit = synchronized {
|
def pauseSource(source: SoundSource): Unit = synchronized {
|
||||||
|
if (getSourceStatus(source) == SoundSource.Status.Paused)
|
||||||
|
return
|
||||||
|
|
||||||
if (sources.contains(source)) {
|
if (sources.contains(source)) {
|
||||||
AL10.alSourcePause(sources(source).sourceId)
|
AL10W.alSourcePause(sources(source))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def stopSource(source: SoundSource): Unit = synchronized {
|
def stopSource(source: SoundSource): Unit = synchronized {
|
||||||
|
if (getSourceStatus(source) == SoundSource.Status.Stopped)
|
||||||
|
return
|
||||||
|
|
||||||
if (sources.contains(source)) {
|
if (sources.contains(source)) {
|
||||||
val state = sources(source)
|
AL10W.alSourceStop(sources(source))
|
||||||
AL10.alSourceStop(state.sourceId)
|
|
||||||
AL10.alDeleteSources(state.sourceId)
|
|
||||||
if (state.associatedBufferId != -1)
|
|
||||||
AL10.alDeleteBuffers(state.associatedBufferId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def update(): Unit = synchronized {
|
def update(): Unit = synchronized {
|
||||||
if (isDisabled) return
|
if (isDisabled) return
|
||||||
|
|
||||||
sources.filterInPlace { case (source, state) =>
|
sources.filterInPlace { case (source, sourceId) =>
|
||||||
AL10.alSourcef(state.sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster)
|
cleanupSourceBuffers(sourceId)
|
||||||
AL10.alGetSourcei(state.sourceId, AL10.AL_SOURCE_STATE) match {
|
|
||||||
case AL10.AL_PLAYING =>
|
AL10W.alSourcef(sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster)
|
||||||
state.status = SoundSource.Status.Playing
|
AL10W.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) match {
|
||||||
true
|
case AL10.AL_STOPPED =>
|
||||||
case AL10.AL_PAUSED =>
|
deleteSource(sourceId)
|
||||||
state.status = SoundSource.Status.Paused
|
|
||||||
true
|
|
||||||
case _ =>
|
|
||||||
AL10.alDeleteSources(state.sourceId)
|
|
||||||
if (state.associatedBufferId != -1)
|
|
||||||
AL10.alDeleteBuffers(state.associatedBufferId)
|
|
||||||
false
|
false
|
||||||
|
case _ => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,15 +145,32 @@ object Audio extends Logging {
|
|||||||
def destroy(): Unit = synchronized {
|
def destroy(): Unit = synchronized {
|
||||||
if (isDisabled) return
|
if (isDisabled) return
|
||||||
|
|
||||||
for ((_, state) <- sources) {
|
for ((_, sourceId) <- sources) {
|
||||||
AL10.alDeleteSources(state.sourceId)
|
deleteSource(sourceId)
|
||||||
if (state.associatedBufferId != -1)
|
|
||||||
AL10.alDeleteBuffers(state.associatedBufferId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sources.clear()
|
sources.clear()
|
||||||
_disabled = true
|
_disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private case class SourceState(sourceId: Int, associatedBufferId: Int, var status: SoundSource.Status.Value)
|
private def deleteSource(sourceId: Int): Unit = {
|
||||||
|
AL10W.alSourceStop(sourceId)
|
||||||
|
cleanupSourceBuffers(sourceId)
|
||||||
|
AL10W.alDeleteSources(sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def cleanupSourceBuffers(sourceId: Int): Unit = {
|
||||||
|
val count = AL10W.alGetSourcei(sourceId, AL10.AL_BUFFERS_PROCESSED)
|
||||||
|
if (count <= 0) return
|
||||||
|
|
||||||
|
val buff = ByteBuffer.allocateDirect(count * 4)
|
||||||
|
buff.order(ByteOrder.nativeOrder())
|
||||||
|
val buf = buff.asIntBuffer()
|
||||||
|
|
||||||
|
AL10W.alSourceUnqueueBuffers(sourceId, buf)
|
||||||
|
|
||||||
|
for (i <- 0 until count) {
|
||||||
|
AL10W.alDeleteBuffers(buf.get(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,6 @@ object BeepGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data.rewind()
|
data.rewind()
|
||||||
SoundSource.fromSamples(SoundSamples(data, Audio.sampleRate, 1), SoundCategory.Beep)
|
SoundSource.fromSamples(SoundSamples(data, Audio.sampleRate, SoundSamples.Format.Mono16), SoundCategory.Beep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,11 @@ object OggDecoder {
|
|||||||
data.put(dataOut.toByteArray)
|
data.put(dataOut.toByteArray)
|
||||||
data.rewind()
|
data.rewind()
|
||||||
|
|
||||||
SoundSamples(data, rate, channels)
|
val format = channels match {
|
||||||
|
case 1 => SoundSamples.Format.Mono16
|
||||||
|
case 2 => SoundSamples.Format.Stereo16
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundSamples(data, rate, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,9 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(s"Loading sound buffer from '$file'...")
|
logger.debug(s"Loading sound buffer from '$file'...")
|
||||||
_bufferId = AL10.alGenBuffers()
|
_bufferId = AL10W.alGenBuffers()
|
||||||
|
|
||||||
if (AL10.alIsExtensionPresent("AL_EXT_vorbis")) {
|
if (AL10W.alIsExtensionPresent("AL_EXT_vorbis")) {
|
||||||
initWithExt()
|
initWithExt()
|
||||||
} else {
|
} else {
|
||||||
initFallback()
|
initFallback()
|
||||||
@ -29,21 +29,12 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
AL10.alBufferData(bufferId, AL10.AL_FORMAT_VORBIS_EXT, fileBuffer, -1)
|
AL10W.alBufferData(bufferId, AL10.AL_FORMAT_VORBIS_EXT, fileBuffer, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def initFallback(): Unit = {
|
private def initFallback(): Unit = {
|
||||||
val ogg = OggDecoder.decode(getClass.getResourceAsStream(file))
|
val ogg = OggDecoder.decode(getClass.getResourceAsStream(file))
|
||||||
|
_bufferId = ogg.genBuffer()
|
||||||
val format = ogg.channels match {
|
|
||||||
case 2 => AL10.AL_FORMAT_STEREO16
|
|
||||||
case 1 => AL10.AL_FORMAT_MONO16
|
|
||||||
}
|
|
||||||
|
|
||||||
AL10.alBufferData(bufferId, format, ogg.data, ogg.rate)
|
|
||||||
if (AL10.alGetError != AL10.AL_NO_ERROR) {
|
|
||||||
logger.error(s"Unable to create OpenAL buffer from '$file'!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def numSamples: Int = {
|
def numSamples: Int = {
|
||||||
@ -51,9 +42,9 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val sizeBytes = AL10.alGetBufferi(bufferId, AL10.AL_SIZE)
|
val sizeBytes = AL10W.alGetBufferi(bufferId, AL10.AL_SIZE)
|
||||||
val channels = AL10.alGetBufferi(bufferId, AL10.AL_CHANNELS)
|
val channels = AL10W.alGetBufferi(bufferId, AL10.AL_CHANNELS)
|
||||||
val bits = AL10.alGetBufferi(bufferId, AL10.AL_BITS)
|
val bits = AL10W.alGetBufferi(bufferId, AL10.AL_BITS)
|
||||||
|
|
||||||
sizeBytes * 8 / channels / bits
|
sizeBytes * 8 / channels / bits
|
||||||
}
|
}
|
||||||
@ -62,7 +53,7 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
if (bufferId == -1) {
|
if (bufferId == -1) {
|
||||||
44100
|
44100
|
||||||
} else {
|
} else {
|
||||||
AL10.alGetBufferi(bufferId, AL10.AL_FREQUENCY)
|
AL10W.alGetBufferi(bufferId, AL10.AL_FREQUENCY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +61,7 @@ class SoundBuffer(val file: String) extends Resource with Logging {
|
|||||||
|
|
||||||
def freeResource(): Unit = {
|
def freeResource(): Unit = {
|
||||||
if (bufferId != -1) {
|
if (bufferId != -1) {
|
||||||
AL10.alDeleteBuffers(bufferId)
|
AL10W.alDeleteBuffers(bufferId)
|
||||||
logger.debug(s"Destroyed sound buffer (ID: $bufferId) loaded from $file")
|
logger.debug(s"Destroyed sound buffer (ID: $bufferId) loaded from $file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,28 @@
|
|||||||
package ocelot.desktop.audio
|
package ocelot.desktop.audio
|
||||||
|
|
||||||
|
import ocelot.desktop.util.Logging
|
||||||
|
import org.lwjgl.openal.AL10
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
case class SoundSamples(data: ByteBuffer, rate: Int, channels: Int)
|
case class SoundSamples(data: ByteBuffer, rate: Int, format: SoundSamples.Format.Value) extends Logging {
|
||||||
|
def genBuffer(): Int = {
|
||||||
|
val bufferId = AL10W.alGenBuffers()
|
||||||
|
val formatId = format match {
|
||||||
|
case SoundSamples.Format.Stereo16 => AL10.AL_FORMAT_STEREO16
|
||||||
|
case SoundSamples.Format.Mono16 => AL10.AL_FORMAT_MONO16
|
||||||
|
case SoundSamples.Format.Mono8 => AL10.AL_FORMAT_MONO8
|
||||||
|
}
|
||||||
|
|
||||||
|
AL10W.alBufferData(bufferId, formatId, data, rate)
|
||||||
|
bufferId
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object SoundSamples {
|
||||||
|
object Format extends Enumeration {
|
||||||
|
val Stereo16, Mono8, Mono16 = Value
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,8 +12,13 @@ class SoundSource(val kind: SoundSource.Kind,
|
|||||||
val seconds = kind match {
|
val seconds = kind match {
|
||||||
case SoundSource.KindSoundBuffer(buffer) =>
|
case SoundSource.KindSoundBuffer(buffer) =>
|
||||||
buffer.numSamples.toFloat / buffer.sampleRate
|
buffer.numSamples.toFloat / buffer.sampleRate
|
||||||
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, channels)) =>
|
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, format)) =>
|
||||||
buffer.limit().toFloat / (rate * channels)
|
val bps = format match {
|
||||||
|
case SoundSamples.Format.Stereo16 => 2
|
||||||
|
case SoundSamples.Format.Mono8 => 1
|
||||||
|
case SoundSamples.Format.Mono16 => 2
|
||||||
|
}
|
||||||
|
buffer.limit().toFloat / (rate * bps)
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration.ofNanos((seconds * 1e9).toLong)
|
Duration.ofNanos((seconds * 1e9).toLong)
|
||||||
@ -36,15 +41,15 @@ class SoundSource(val kind: SoundSource.Kind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
def play(): Unit = {
|
def play(): Unit = {
|
||||||
if (!isPlaying) Audio.playSource(this)
|
Audio.playSource(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
def pause(): Unit = {
|
def pause(): Unit = {
|
||||||
if (!isPaused) Audio.pauseSource(this)
|
Audio.pauseSource(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
def stop(): Unit = {
|
def stop(): Unit = {
|
||||||
if (!isStopped) Audio.stopSource(this)
|
Audio.stopSource(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +60,8 @@ object SoundSource {
|
|||||||
|
|
||||||
case class KindSoundSamples(samples: SoundSamples) extends Kind
|
case class KindSoundSamples(samples: SoundSamples) extends Kind
|
||||||
|
|
||||||
|
case class KindStream(stream: SoundStream) extends Kind
|
||||||
|
|
||||||
def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value,
|
def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value,
|
||||||
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
|
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
|
||||||
new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume)
|
new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume)
|
||||||
@ -65,6 +72,12 @@ object SoundSource {
|
|||||||
new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
|
new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def fromStream(stream: SoundStream, soundCategory: SoundCategory.Value,
|
||||||
|
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource =
|
||||||
|
{
|
||||||
|
new SoundSource(SoundSource.KindStream(stream), soundCategory, looping, pitch, volume)
|
||||||
|
}
|
||||||
|
|
||||||
object Status extends Enumeration {
|
object Status extends Enumeration {
|
||||||
val Playing, Paused, Stopped = Value
|
val Playing, Paused, Stopped = Value
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/main/scala/ocelot/desktop/audio/SoundStream.scala
Normal file
5
src/main/scala/ocelot/desktop/audio/SoundStream.scala
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package ocelot.desktop.audio
|
||||||
|
|
||||||
|
trait SoundStream {
|
||||||
|
def enqueue(samples: SoundSamples): Unit
|
||||||
|
}
|
||||||
@ -13,10 +13,11 @@ import org.lwjgl.input.Keyboard
|
|||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Floppy, GenericCPU, Inventory}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment, Floppy, GenericCPU, Inventory}
|
||||||
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, HDDUnmanaged, Memory}
|
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, HDDUnmanaged, Memory}
|
||||||
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
|
import totoro.ocelot.brain.event.FileSystemActivityType.Floppy
|
||||||
import totoro.ocelot.brain.event.{BeepEvent, BeepPatternEvent, FileSystemActivityEvent, MachineCrashEvent}
|
import totoro.ocelot.brain.event.{BeepEvent, BeepPatternEvent, FileSystemActivityEvent, MachineCrashEvent, SoundCardAudioEvent}
|
||||||
import totoro.ocelot.brain.loot.Loot
|
import totoro.ocelot.brain.loot.Loot
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ class ComputerNode(val computer: Case) extends Node(computer) with Logging {
|
|||||||
var floppySlot: Option[FloppySlot] = None
|
var floppySlot: Option[FloppySlot] = None
|
||||||
|
|
||||||
private val soundComputerRunning = SoundSource.fromBuffer(SoundBuffers.MachineComputerRunning, SoundCategory.Environment, looping = true)
|
private val soundComputerRunning = SoundSource.fromBuffer(SoundBuffers.MachineComputerRunning, SoundCategory.Environment, looping = true)
|
||||||
|
private var soundCardStream: SoundStream = _
|
||||||
|
|
||||||
setupSlots()
|
setupSlots()
|
||||||
refitSlots()
|
refitSlots()
|
||||||
@ -48,6 +50,17 @@ class ComputerNode(val computer: Case) extends Node(computer) with Logging {
|
|||||||
val soundHDDAccess = SoundBuffers.MachineHDDAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
val soundHDDAccess = SoundBuffers.MachineHDDAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment))
|
||||||
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
|
val sound = if (event.activityType == Floppy) soundFloppyAccess else soundHDDAccess
|
||||||
sound(Random.between(0, sound.length)).play()
|
sound(Random.between(0, sound.length)).play()
|
||||||
|
|
||||||
|
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
||||||
|
val samples = SoundSamples(event.data, 44100, SoundSamples.Format.Mono8)
|
||||||
|
// SoundSource.fromSamples(samples, SoundCategory.Beep).play()
|
||||||
|
if (soundCardStream == null) {
|
||||||
|
val (stream, source) = Audio.newStream(SoundCategory.Beep)
|
||||||
|
soundCardStream = stream
|
||||||
|
soundCardStream.enqueue(samples)
|
||||||
|
} else {
|
||||||
|
soundCardStream.enqueue(samples)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def shouldReceiveEventsFor(address: String): Boolean = super.shouldReceiveEventsFor(address) ||
|
override def shouldReceiveEventsFor(address: String): Boolean = super.shouldReceiveEventsFor(address) ||
|
||||||
|
|||||||
@ -249,11 +249,10 @@ object UiHandler extends Logging {
|
|||||||
if (Display.isCloseRequested)
|
if (Display.isCloseRequested)
|
||||||
OcelotDesktop.exit()
|
OcelotDesktop.exit()
|
||||||
|
|
||||||
Audio.update()
|
|
||||||
|
|
||||||
OcelotDesktop.withTickLockAcquired {
|
OcelotDesktop.withTickLockAcquired {
|
||||||
updateWindowSizeAndPosition()
|
updateWindowSizeAndPosition()
|
||||||
|
|
||||||
|
Audio.update()
|
||||||
KeyEvents.update()
|
KeyEvents.update()
|
||||||
MouseEvents.update()
|
MouseEvents.update()
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package ocelot.desktop.ui.widget.slot
|
package ocelot.desktop.ui.widget.slot
|
||||||
|
|
||||||
import ocelot.desktop.graphics.IconDef
|
import ocelot.desktop.graphics.IconDef
|
||||||
|
import totoro.ocelot.brain.entity.sound_card.SoundCard
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Tiered}
|
import totoro.ocelot.brain.entity.traits.{Entity, Tiered}
|
||||||
import totoro.ocelot.brain.entity.{DataCard, GraphicsCard, InternetCard, LinkedCard, NetworkCard, Redstone, WirelessNetworkCard}
|
import totoro.ocelot.brain.entity.{DataCard, GraphicsCard, InternetCard, LinkedCard, NetworkCard, Redstone, WirelessNetworkCard}
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ object CardRegistry {
|
|||||||
val iconByClassAndTier: mutable.HashMap[(String, Int), IconDef] = mutable.HashMap()
|
val iconByClassAndTier: mutable.HashMap[(String, Int), IconDef] = mutable.HashMap()
|
||||||
val entries: mutable.ArrayBuffer[Entry] = mutable.ArrayBuffer()
|
val entries: mutable.ArrayBuffer[Entry] = mutable.ArrayBuffer()
|
||||||
|
|
||||||
def addEntry[T <: Entity: ClassTag](name: String, tier: Int, icon: IconDef, factory: () => T): Unit = {
|
def addEntry[T <: Entity : ClassTag](name: String, tier: Int, icon: IconDef, factory: () => T): Unit = {
|
||||||
val entry = Entry(name, tier, icon, factory)
|
val entry = Entry(name, tier, icon, factory)
|
||||||
entries += entry
|
entries += entry
|
||||||
val clazz = classTag[T].runtimeClass.getName
|
val clazz = classTag[T].runtimeClass.getName
|
||||||
@ -73,5 +74,8 @@ object CardRegistry {
|
|||||||
() => new DataCard.Tier2)
|
() => new DataCard.Tier2)
|
||||||
addEntry("Data Card", 2, new IconDef("items/DataCard2", animation = DataCardAnimation),
|
addEntry("Data Card", 2, new IconDef("items/DataCard2", animation = DataCardAnimation),
|
||||||
() => new DataCard.Tier3)
|
() => new DataCard.Tier3)
|
||||||
|
|
||||||
|
addEntry("Sound Card", 1, new IconDef("items/SoundCard", animation = DataCardAnimation),
|
||||||
|
() => new SoundCard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user