mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 02:59:19 +01:00
Moved to ocelot sound system
This commit is contained in:
parent
3f07bae62c
commit
6b6c70ee73
@ -1 +1 @@
|
|||||||
Subproject commit f40c686e2a012807f23662f987b90f3f73df21a0
|
Subproject commit 40ec8c7014a1d4607ee0c31488783ae82c8827a5
|
||||||
BIN
sprites/window/OpenFMRadio.png
Normal file
BIN
sprites/window/OpenFMRadio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 72 KiB |
@ -1,156 +1,169 @@
|
|||||||
BackgroundPattern 0 0 304 304
|
BackgroundPattern 0 0 304 304
|
||||||
BarSegment 29 355 16 4
|
BarSegment 495 305 16 4
|
||||||
ComputerMotherboard 305 0 79 70
|
ComputerMotherboard 233 305 79 70
|
||||||
Empty 12 414 1 1
|
Empty 495 339 1 1
|
||||||
EmptySlot 458 147 18 18
|
EmptySlot 322 129 18 18
|
||||||
Knob 385 0 50 50
|
Knob 313 305 50 50
|
||||||
KnobCenter 436 0 50 50
|
KnobCenter 364 305 50 50
|
||||||
KnobLimits 305 71 50 50
|
KnobLimits 415 305 50 50
|
||||||
ShadowBorder 0 394 1 24
|
ShadowBorder 491 25 1 24
|
||||||
ShadowCorner 441 122 24 24
|
ShadowCorner 441 0 24 24
|
||||||
TabArrow 0 340 8 14
|
TabArrow 441 75 8 14
|
||||||
buttons/BottomDrawerClose 477 147 18 18
|
buttons/BottomDrawerClose 341 129 18 18
|
||||||
buttons/BottomDrawerOpen 441 180 18 18
|
buttons/BottomDrawerOpen 360 129 18 18
|
||||||
buttons/PowerOff 460 180 18 18
|
buttons/OpenFMRadioCloseOff 478 325 7 8
|
||||||
buttons/PowerOn 479 180 18 18
|
buttons/OpenFMRadioCloseOn 486 325 7 8
|
||||||
icons/ButtonCheck 466 122 17 17
|
buttons/OpenFMRadioRedstoneOff 469 316 8 8
|
||||||
icons/ButtonClipboard 484 122 17 17
|
buttons/OpenFMRadioRedstoneOn 478 316 8 8
|
||||||
icons/ButtonRandomize 373 219 17 17
|
buttons/OpenFMRadioStartOff 466 0 24 24
|
||||||
icons/CPU 305 251 16 16
|
buttons/OpenFMRadioStartOn 441 25 24 24
|
||||||
icons/Card 322 251 16 16
|
buttons/OpenFMRadioStopOff 466 25 24 24
|
||||||
icons/ComponentBus 339 251 16 16
|
buttons/OpenFMRadioStopOn 441 50 24 24
|
||||||
icons/DragLMB 441 199 21 14
|
buttons/OpenFMRadioVolumeDownOff 450 75 10 10
|
||||||
icons/DragRMB 463 199 21 14
|
buttons/OpenFMRadioVolumeDownOn 461 75 10 10
|
||||||
icons/EEPROM 356 251 16 16
|
buttons/OpenFMRadioVolumeUpOff 472 75 10 10
|
||||||
icons/Floppy 373 251 16 16
|
buttons/OpenFMRadioVolumeUpOn 483 75 10 10
|
||||||
icons/HDD 390 251 16 16
|
buttons/PowerOff 379 129 18 18
|
||||||
icons/LMB 26 322 11 14
|
buttons/PowerOn 398 129 18 18
|
||||||
icons/Memory 407 251 16 16
|
icons/ButtonCheck 305 162 17 17
|
||||||
icons/NA 424 251 16 16
|
icons/ButtonClipboard 323 162 17 17
|
||||||
icons/NotificationError 50 322 11 11
|
icons/ButtonRandomize 341 162 17 17
|
||||||
icons/NotificationInfo 62 322 11 11
|
icons/CPU 305 180 16 16
|
||||||
icons/NotificationWarning 74 322 11 11
|
icons/Card 322 180 16 16
|
||||||
icons/RMB 38 322 11 14
|
icons/ComponentBus 339 180 16 16
|
||||||
icons/RackMountable 441 251 16 16
|
icons/DragLMB 417 129 21 14
|
||||||
icons/SettingsSound 0 322 12 17
|
icons/DragRMB 439 129 21 14
|
||||||
icons/SettingsUI 13 322 12 17
|
icons/EEPROM 356 180 16 16
|
||||||
icons/Tier0 458 251 16 16
|
icons/Floppy 373 180 16 16
|
||||||
icons/Tier1 475 251 16 16
|
icons/HDD 390 180 16 16
|
||||||
icons/Tier2 492 251 16 16
|
icons/LMB 492 50 11 14
|
||||||
icons/WaveLFSR 70 305 24 10
|
icons/Memory 407 180 16 16
|
||||||
icons/WaveNoise 95 305 24 10
|
icons/NA 424 180 16 16
|
||||||
icons/WaveSawtooth 120 305 24 10
|
icons/NotificationError 473 129 11 11
|
||||||
icons/WaveSine 145 305 24 10
|
icons/NotificationInfo 485 129 11 11
|
||||||
icons/WaveSquare 170 305 24 10
|
icons/NotificationWarning 497 129 11 11
|
||||||
icons/WaveTriangle 195 305 24 10
|
icons/RMB 461 129 11 14
|
||||||
icons/WireArrowLeft 2 394 4 8
|
icons/RackMountable 441 180 16 16
|
||||||
icons/WireArrowRight 7 394 4 8
|
icons/SettingsSound 466 50 12 17
|
||||||
items/APU0 373 122 16 96
|
icons/SettingsUI 479 50 12 17
|
||||||
items/APU1 390 122 16 96
|
icons/Tier0 458 180 16 16
|
||||||
items/APU2 407 122 16 96
|
icons/Tier1 475 180 16 16
|
||||||
items/CPU0 305 268 16 16
|
icons/Tier2 492 180 16 16
|
||||||
items/CPU1 322 268 16 16
|
icons/WaveLFSR 392 282 24 10
|
||||||
items/CPU2 339 268 16 16
|
icons/WaveNoise 417 282 24 10
|
||||||
items/CardBase 356 268 16 16
|
icons/WaveSawtooth 442 282 24 10
|
||||||
items/CircuitBoard 373 268 16 16
|
icons/WaveSine 467 282 24 10
|
||||||
items/ComponentBus0 390 268 16 16
|
icons/WaveSquare 380 162 24 10
|
||||||
items/ComponentBus1 407 268 16 16
|
icons/WaveTriangle 405 162 24 10
|
||||||
items/ComponentBus2 424 268 16 16
|
icons/WireArrowLeft 493 25 4 8
|
||||||
items/ComponentBus3 441 268 16 16
|
icons/WireArrowRight 498 25 4 8
|
||||||
items/DataCard0 305 122 16 128
|
items/APU0 373 0 16 96
|
||||||
items/DataCard1 322 122 16 128
|
items/APU1 390 0 16 96
|
||||||
items/DataCard2 339 122 16 128
|
items/APU2 407 0 16 96
|
||||||
items/DebugCard 458 268 16 16
|
items/CPU0 305 197 16 16
|
||||||
items/DiskDriveMountable 475 268 16 16
|
items/CPU1 322 197 16 16
|
||||||
items/EEPROM 492 268 16 16
|
items/CPU2 339 197 16 16
|
||||||
items/FloppyDisk_dyeBlack 305 285 16 16
|
items/CardBase 356 197 16 16
|
||||||
items/FloppyDisk_dyeBlue 322 285 16 16
|
items/CircuitBoard 373 197 16 16
|
||||||
items/FloppyDisk_dyeBrown 339 285 16 16
|
items/ComponentBus0 390 197 16 16
|
||||||
items/FloppyDisk_dyeCyan 356 285 16 16
|
items/ComponentBus1 407 197 16 16
|
||||||
items/FloppyDisk_dyeGray 373 285 16 16
|
items/ComponentBus2 424 197 16 16
|
||||||
items/FloppyDisk_dyeGreen 390 285 16 16
|
items/ComponentBus3 441 197 16 16
|
||||||
items/FloppyDisk_dyeLightBlue 407 285 16 16
|
items/DataCard0 305 0 16 128
|
||||||
items/FloppyDisk_dyeLightGray 424 285 16 16
|
items/DataCard1 322 0 16 128
|
||||||
items/FloppyDisk_dyeLime 441 285 16 16
|
items/DataCard2 339 0 16 128
|
||||||
items/FloppyDisk_dyeMagenta 458 285 16 16
|
items/DebugCard 458 197 16 16
|
||||||
items/FloppyDisk_dyeOrange 475 285 16 16
|
items/DiskDriveMountable 475 197 16 16
|
||||||
items/FloppyDisk_dyePink 492 285 16 16
|
items/EEPROM 492 197 16 16
|
||||||
items/FloppyDisk_dyePurple 356 71 16 16
|
items/FloppyDisk_dyeBlack 305 214 16 16
|
||||||
items/FloppyDisk_dyeRed 373 71 16 16
|
items/FloppyDisk_dyeBlue 322 214 16 16
|
||||||
items/FloppyDisk_dyeWhite 390 71 16 16
|
items/FloppyDisk_dyeBrown 339 214 16 16
|
||||||
items/FloppyDisk_dyeYellow 407 71 16 16
|
items/FloppyDisk_dyeCyan 356 214 16 16
|
||||||
items/GraphicsCard0 424 71 16 16
|
items/FloppyDisk_dyeGray 373 214 16 16
|
||||||
items/GraphicsCard1 441 71 16 16
|
items/FloppyDisk_dyeGreen 390 214 16 16
|
||||||
items/GraphicsCard2 458 71 16 16
|
items/FloppyDisk_dyeLightBlue 407 214 16 16
|
||||||
items/HardDiskDrive0 475 71 16 16
|
items/FloppyDisk_dyeLightGray 424 214 16 16
|
||||||
items/HardDiskDrive1 492 71 16 16
|
items/FloppyDisk_dyeLime 441 214 16 16
|
||||||
items/HardDiskDrive2 356 88 16 16
|
items/FloppyDisk_dyeMagenta 458 214 16 16
|
||||||
items/InternetCard 441 147 16 32
|
items/FloppyDisk_dyeOrange 475 214 16 16
|
||||||
items/LinkedCard 424 122 16 96
|
items/FloppyDisk_dyePink 492 214 16 16
|
||||||
items/Memory0 373 88 16 16
|
items/FloppyDisk_dyePurple 305 231 16 16
|
||||||
items/Memory1 390 88 16 16
|
items/FloppyDisk_dyeRed 322 231 16 16
|
||||||
items/Memory2 407 88 16 16
|
items/FloppyDisk_dyeWhite 339 231 16 16
|
||||||
items/Memory3 424 88 16 16
|
items/FloppyDisk_dyeYellow 356 231 16 16
|
||||||
items/Memory4 441 88 16 16
|
items/GraphicsCard0 373 231 16 16
|
||||||
items/Memory5 458 88 16 16
|
items/GraphicsCard1 390 231 16 16
|
||||||
items/NetworkCard 475 88 16 16
|
items/GraphicsCard2 407 231 16 16
|
||||||
items/RedstoneCard0 492 88 16 16
|
items/HardDiskDrive0 424 231 16 16
|
||||||
items/RedstoneCard1 356 105 16 16
|
items/HardDiskDrive1 441 231 16 16
|
||||||
items/Server0 373 105 16 16
|
items/HardDiskDrive2 458 231 16 16
|
||||||
items/Server1 390 105 16 16
|
items/InternetCard 305 129 16 32
|
||||||
items/Server2 407 105 16 16
|
items/LinkedCard 424 0 16 96
|
||||||
items/Server3 424 105 16 16
|
items/Memory0 475 231 16 16
|
||||||
items/SoundCard 356 122 16 128
|
items/Memory1 492 231 16 16
|
||||||
items/WirelessNetworkCard0 441 105 16 16
|
items/Memory2 305 248 16 16
|
||||||
items/WirelessNetworkCard1 458 105 16 16
|
items/Memory3 322 248 16 16
|
||||||
light-panel/BookmarkLeft 51 305 18 14
|
items/Memory4 339 248 16 16
|
||||||
light-panel/BookmarkRight 391 219 20 14
|
items/Memory5 356 248 16 16
|
||||||
light-panel/BorderB 8 403 4 4
|
items/NetworkCard 373 248 16 16
|
||||||
light-panel/BorderL 98 403 4 2
|
items/RedstoneCard0 390 248 16 16
|
||||||
light-panel/BorderR 13 403 4 4
|
items/RedstoneCard1 407 248 16 16
|
||||||
light-panel/BorderT 18 403 4 4
|
items/Server0 424 248 16 16
|
||||||
light-panel/CornerBL 23 403 4 4
|
items/Server1 441 248 16 16
|
||||||
light-panel/CornerBR 28 403 4 4
|
items/Server2 458 248 16 16
|
||||||
light-panel/CornerTL 33 403 4 4
|
items/Server3 475 248 16 16
|
||||||
light-panel/CornerTR 38 403 4 4
|
items/SoundCard 356 0 16 128
|
||||||
light-panel/Fill 6 414 2 2
|
items/WirelessNetworkCard0 492 248 16 16
|
||||||
light-panel/Vent 0 355 2 38
|
items/WirelessNetworkCard1 305 265 16 16
|
||||||
nodes/Cable 3 366 8 8
|
light-panel/BookmarkLeft 373 282 18 14
|
||||||
nodes/Camera 475 105 16 16
|
light-panel/BookmarkRight 359 162 20 14
|
||||||
nodes/Computer 492 105 16 16
|
light-panel/BorderB 499 34 4 4
|
||||||
nodes/ComputerActivityOverlay 487 0 16 16
|
light-panel/BorderL 479 339 4 2
|
||||||
nodes/ComputerErrorOverlay 487 17 16 16
|
light-panel/BorderR 504 34 4 4
|
||||||
nodes/ComputerOnOverlay 487 34 16 16
|
light-panel/BorderT 493 45 4 4
|
||||||
nodes/DiskDrive 385 51 16 16
|
light-panel/CornerBL 498 45 4 4
|
||||||
nodes/DiskDriveActivity 402 51 16 16
|
light-panel/CornerBR 503 45 4 4
|
||||||
nodes/DiskDriveFloppy 419 51 16 16
|
light-panel/CornerTL 503 25 4 4
|
||||||
nodes/IronNoteBlock 436 51 16 16
|
light-panel/CornerTR 502 325 4 4
|
||||||
nodes/NewNode 453 51 16 16
|
light-panel/Fill 489 339 2 2
|
||||||
nodes/NoteBlock 470 51 16 16
|
light-panel/Vent 466 305 2 38
|
||||||
nodes/OpenFMRadio 487 51 16 16
|
nodes/Cable 487 316 8 8
|
||||||
nodes/Relay 0 305 16 16
|
nodes/Camera 322 265 16 16
|
||||||
nodes/Screen 17 305 16 16
|
nodes/Computer 339 265 16 16
|
||||||
nodes/ScreenOnOverlay 34 305 16 16
|
nodes/ComputerActivityOverlay 356 265 16 16
|
||||||
panel/BorderB 43 403 4 4
|
nodes/ComputerErrorOverlay 373 265 16 16
|
||||||
panel/BorderL 103 403 4 2
|
nodes/ComputerOnOverlay 390 265 16 16
|
||||||
panel/BorderR 48 403 4 4
|
nodes/DiskDrive 407 265 16 16
|
||||||
panel/BorderT 53 403 4 4
|
nodes/DiskDriveActivity 424 265 16 16
|
||||||
panel/CornerBL 58 403 4 4
|
nodes/DiskDriveFloppy 441 265 16 16
|
||||||
panel/CornerBR 63 403 4 4
|
nodes/IronNoteBlock 458 265 16 16
|
||||||
panel/CornerTL 68 403 4 4
|
nodes/NewNode 475 265 16 16
|
||||||
panel/CornerTR 73 403 4 4
|
nodes/NoteBlock 492 265 16 16
|
||||||
panel/Fill 9 414 2 2
|
nodes/OpenFMRadio 305 282 16 16
|
||||||
particles/Note 21 355 7 10
|
nodes/Relay 322 282 16 16
|
||||||
screen/BorderB 5 403 2 8
|
nodes/Screen 339 282 16 16
|
||||||
screen/BorderT 2 403 2 10
|
nodes/ScreenOnOverlay 356 282 16 16
|
||||||
screen/CornerBL 12 366 8 8
|
panel/BorderB 507 325 4 4
|
||||||
screen/CornerBR 21 366 8 8
|
panel/BorderL 484 339 4 2
|
||||||
screen/CornerTL 3 355 8 10
|
panel/BorderR 469 334 4 4
|
||||||
screen/CornerTR 12 355 8 10
|
panel/BorderT 474 334 4 4
|
||||||
window/BorderDark 2 414 1 4
|
panel/CornerBL 479 334 4 4
|
||||||
window/BorderLight 4 414 1 4
|
panel/CornerBR 484 334 4 4
|
||||||
window/CloseButton 30 366 7 6
|
panel/CornerTL 489 334 4 4
|
||||||
window/CornerBL 78 403 4 4
|
panel/CornerTR 494 334 4 4
|
||||||
window/CornerBR 83 403 4 4
|
panel/Fill 492 339 2 2
|
||||||
window/CornerTL 88 403 4 4
|
particles/Note 487 305 7 10
|
||||||
window/CornerTR 93 403 4 4
|
screen/BorderB 496 34 2 8
|
||||||
|
screen/BorderT 493 34 2 10
|
||||||
|
screen/CornerBL 496 316 8 8
|
||||||
|
screen/CornerBR 469 325 8 8
|
||||||
|
screen/CornerTL 469 305 8 10
|
||||||
|
screen/CornerTR 478 305 8 10
|
||||||
|
window/BorderDark 509 334 1 4
|
||||||
|
window/BorderLight 508 25 1 4
|
||||||
|
window/CloseButton 494 325 7 6
|
||||||
|
window/CornerBL 499 334 4 4
|
||||||
|
window/CornerBR 504 334 4 4
|
||||||
|
window/CornerTL 469 339 4 4
|
||||||
|
window/CornerTR 474 339 4 4
|
||||||
|
window/OpenFMRadio 0 305 232 105
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package ocelot.desktop.entity.openfmradio
|
package ocelot.desktop.entity
|
||||||
|
|
||||||
import ocelot.desktop.audio.{Audio, SoundCategory}
|
import ocelot.desktop.audio.SoundSamples.Format
|
||||||
|
import ocelot.desktop.audio.{Audio, SoundCategory, SoundSamples, SoundSource}
|
||||||
import ocelot.desktop.color.IntColor
|
import ocelot.desktop.color.IntColor
|
||||||
|
import org.lwjgl.BufferUtils
|
||||||
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
|
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
|
||||||
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment}
|
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment}
|
||||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||||
@ -9,7 +11,11 @@ import totoro.ocelot.brain.network.{Component, Network, Visibility}
|
|||||||
import totoro.ocelot.brain.util.ResultWrapper.result
|
import totoro.ocelot.brain.util.ResultWrapper.result
|
||||||
import totoro.ocelot.brain.workspace.Workspace
|
import totoro.ocelot.brain.workspace.Workspace
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream
|
||||||
import java.net.{HttpURLConnection, URI}
|
import java.net.{HttpURLConnection, URI}
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
import javax.sound.sampled.AudioFormat.Encoding
|
||||||
|
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
||||||
|
|
||||||
class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
||||||
override val node: Component =
|
override val node: Component =
|
||||||
@ -20,52 +26,118 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
|||||||
private val defaultScreenText = "OpenFM"
|
private val defaultScreenText = "OpenFM"
|
||||||
private val defaultScreenColor = IntColor(0x0AFF0A)
|
private val defaultScreenColor = IntColor(0x0AFF0A)
|
||||||
|
|
||||||
var url: Option[String] = None
|
|
||||||
var screenColor: IntColor = defaultScreenColor
|
var screenColor: IntColor = defaultScreenColor
|
||||||
var screenText: String = defaultScreenText
|
var screenText: String = defaultScreenText
|
||||||
|
|
||||||
private var player: Option[StreamAudioPlayer] = None
|
private var thread: Option[Thread] = None
|
||||||
private var playerThread: Option[Thread] = None
|
|
||||||
private var _volume: Float = 1
|
private var _volume: Float = 1
|
||||||
|
private var _url: Option[String] = None
|
||||||
|
|
||||||
def start(): Boolean = {
|
private var soundSource: Option[SoundSource] = None
|
||||||
|
|
||||||
|
def url: Option[String] = _url
|
||||||
|
|
||||||
|
def url_=(value: Option[String]): Unit = {
|
||||||
|
_url =
|
||||||
|
if (value.isDefined)
|
||||||
|
Option(value.get.take(32))
|
||||||
|
else
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
private def playSynchronously(): Unit = {
|
||||||
|
try {
|
||||||
|
// Trying to parse URL and sending request to host
|
||||||
|
val connection = new URI(url.get).toURL.openConnection.asInstanceOf[HttpURLConnection]
|
||||||
|
connection.setDoInput(true)
|
||||||
|
connection.setDoOutput(true)
|
||||||
|
connection.setRequestMethod("GET")
|
||||||
|
|
||||||
|
// Wrapping stream to another one with hardcoded format
|
||||||
|
val inputStream = AudioSystem.getAudioInputStream(
|
||||||
|
new AudioFormat(
|
||||||
|
Encoding.PCM_SIGNED,
|
||||||
|
44100,
|
||||||
|
16,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
44100,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
// Obtaining audio input stream from HTTP connection
|
||||||
|
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keeping input stream format parameters here to offload the reading loop
|
||||||
|
val inputStreamFormat = inputStream.getFormat
|
||||||
|
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
||||||
|
|
||||||
|
val inputStreamSoundSampleFormat =
|
||||||
|
if (inputStreamFormat.getChannels > 1)
|
||||||
|
Format.Stereo16
|
||||||
|
else
|
||||||
|
Format.Mono16
|
||||||
|
|
||||||
|
// Creating Ocelot audio output stream
|
||||||
|
val (outputStream, source) = Audio.newStream(SoundCategory.Environment, volume = volume)
|
||||||
|
soundSource = Option(source)
|
||||||
|
|
||||||
|
// Reading chunks from input stream and passing them to output
|
||||||
|
val buffer = new Array[Byte](65536)
|
||||||
|
var bytesRead = 0
|
||||||
|
|
||||||
|
while (!Thread.currentThread().isInterrupted && bytesRead != -1) {
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
outputStream.enqueue(new SoundSamples(
|
||||||
|
BufferUtils
|
||||||
|
.createByteBuffer(bytesRead)
|
||||||
|
.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
|
.put(buffer, 0, bytesRead)
|
||||||
|
.flip,
|
||||||
|
inputStreamSampleRate,
|
||||||
|
inputStreamSoundSampleFormat
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRead = inputStream.read(buffer, 0, buffer.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
case _: InterruptedException =>
|
||||||
|
case e: Exception => e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.synchronized {
|
||||||
|
if (soundSource.isDefined) {
|
||||||
|
soundSource.get.stop()
|
||||||
|
soundSource = None
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread.isDefined)
|
||||||
|
thread = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def play(): Boolean = {
|
||||||
if (url.isEmpty)
|
if (url.isEmpty)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (isPlaying)
|
if (isPlaying)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
this.playerThread = Option(new Thread(() => {
|
this.synchronized {
|
||||||
try {
|
thread = Option(new Thread(() => playSynchronously()))
|
||||||
val connection = new URI(url.get).toURL.openConnection.asInstanceOf[HttpURLConnection]
|
thread.get.setPriority(Thread.MIN_PRIORITY)
|
||||||
connection.setDoInput(true)
|
thread.get.start()
|
||||||
connection.setDoOutput(true)
|
}
|
||||||
connection.setRequestMethod("GET")
|
|
||||||
|
|
||||||
player = Option(new StreamAudioPlayer(connection.getInputStream))
|
|
||||||
player.get.volume = volume
|
|
||||||
player.get.start()
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
case e: Exception =>
|
|
||||||
stop()
|
|
||||||
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
playerThread.get.setPriority(Thread.MIN_PRIORITY)
|
|
||||||
playerThread.get.start()
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
def stop(): Boolean = {
|
def stop(): Boolean = {
|
||||||
if (player.isDefined) {
|
this.synchronized {
|
||||||
player.get.stop()
|
if (thread.isDefined)
|
||||||
|
thread.get.interrupt()
|
||||||
player = None
|
|
||||||
playerThread = None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
@ -76,17 +148,12 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
|||||||
def volume_=(value: Float): Unit = {
|
def volume_=(value: Float): Unit = {
|
||||||
_volume = value
|
_volume = value
|
||||||
|
|
||||||
if (player.isDefined)
|
if (soundSource.isDefined)
|
||||||
player.get.volume = value
|
soundSource.get.volume = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private def isPlaying: Boolean = {
|
def isPlaying: Boolean = {
|
||||||
if (player.isDefined)
|
thread.isDefined
|
||||||
player.get.isPlaying
|
|
||||||
else if (playerThread.isDefined)
|
|
||||||
playerThread.get.isAlive
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def dispose(): Unit = {
|
override def dispose(): Unit = {
|
||||||
@ -153,7 +220,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
|||||||
|
|
||||||
@Callback()
|
@Callback()
|
||||||
def start(context: Context, args: Arguments): Array[AnyRef] = {
|
def start(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
result(start())
|
result(play())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Callback()
|
@Callback()
|
||||||
@ -221,7 +288,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo {
|
|||||||
|
|
||||||
// Playing again if previously saved value was true
|
// Playing again if previously saved value was true
|
||||||
if (nbt.hasKey("isPlaying") && nbt.getBoolean("isPlaying"))
|
if (nbt.hasKey("isPlaying") && nbt.getBoolean("isPlaying"))
|
||||||
start()
|
play()
|
||||||
}
|
}
|
||||||
|
|
||||||
override def save(nbt: NBTTagCompound): Unit = {
|
override def save(nbt: NBTTagCompound): Unit = {
|
||||||
@ -1,104 +0,0 @@
|
|||||||
package ocelot.desktop.entity.openfmradio
|
|
||||||
|
|
||||||
import java.io.FilterInputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An Erroring InputStream when mark limits are exceeded
|
|
||||||
* Fixes MP3SPI infinitely reading until it finds a false positive
|
|
||||||
*/
|
|
||||||
class MarkErrorInputStream(proxy: InputStream) extends FilterInputStream(proxy) {
|
|
||||||
if (!proxy.markSupported)
|
|
||||||
throw new IllegalArgumentException("input stream does not support mark")
|
|
||||||
|
|
||||||
private var position = 0L
|
|
||||||
private var markLimit = 0L
|
|
||||||
private var markActive = false
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
private def limit(value: Long): Long = {
|
|
||||||
if (markActive) {
|
|
||||||
val avail = markLimit - position
|
|
||||||
|
|
||||||
if (avail <= 0) {
|
|
||||||
in.reset()
|
|
||||||
throw new IOException("mark limit exceeded")
|
|
||||||
}
|
|
||||||
else if (avail < value) {
|
|
||||||
return avail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
private def addPos(n: Long): Unit = {
|
|
||||||
if (n >= 0 && markActive)
|
|
||||||
position += n
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def read: Int = {
|
|
||||||
limit(1)
|
|
||||||
val result = super.read
|
|
||||||
addPos(1)
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def read(bts: Array[Byte]): Int = {
|
|
||||||
val result = super.read(bts, 0, limit(bts.length).toInt)
|
|
||||||
addPos(result)
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def read(bts: Array[Byte], off: Int, len: Int): Int = {
|
|
||||||
val result = super.read(bts, off, limit(len).toInt)
|
|
||||||
addPos(result)
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def skip(ln: Long): Long = {
|
|
||||||
val result = super.skip(limit(ln))
|
|
||||||
addPos(result)
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mark(readLimit: Int): Unit = {
|
|
||||||
var newLimit = readLimit
|
|
||||||
|
|
||||||
// prevent mp3spi's insane limit of 4096001.
|
|
||||||
if (newLimit == 4096001)
|
|
||||||
newLimit = 4096
|
|
||||||
|
|
||||||
super.mark(newLimit)
|
|
||||||
|
|
||||||
markActive = true
|
|
||||||
markLimit = newLimit
|
|
||||||
position = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def reset(): Unit = {
|
|
||||||
super.reset()
|
|
||||||
|
|
||||||
markActive = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
override def available: Int = {
|
|
||||||
// Allows BufferedInputStream to stop reading.
|
|
||||||
if (markActive && position >= markLimit)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
limit(super.available).toInt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
package ocelot.desktop.entity.openfmradio
|
|
||||||
|
|
||||||
import ocelot.desktop.audio.SoundCategory
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
import java.nio.IntBuffer
|
|
||||||
import javax.sound.sampled.AudioFormat
|
|
||||||
import javax.sound.sampled.AudioFormat.Encoding
|
|
||||||
import javax.sound.sampled.AudioInputStream
|
|
||||||
import javax.sound.sampled.AudioSystem
|
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException
|
|
||||||
import org.lwjgl.BufferUtils
|
|
||||||
import org.lwjgl.openal.AL10
|
|
||||||
|
|
||||||
class StreamAudioPlayer(var stream: AudioInputStream) {
|
|
||||||
def this(inputStream: InputStream) {
|
|
||||||
this(AudioSystem.getAudioInputStream(new MarkErrorInputStream(new BufferedInputStream(inputStream))))
|
|
||||||
}
|
|
||||||
|
|
||||||
private var buffer: IntBuffer = _
|
|
||||||
private var source: IntBuffer = _
|
|
||||||
private var _volume = 1f
|
|
||||||
private var _isPlaying = false
|
|
||||||
|
|
||||||
def isPlaying: Boolean = _isPlaying
|
|
||||||
|
|
||||||
def isPlaying_=(value: Boolean): Unit = _isPlaying = value
|
|
||||||
|
|
||||||
def volume: Float = _volume
|
|
||||||
|
|
||||||
def volume_=(value: Float): Unit = {
|
|
||||||
_volume = value
|
|
||||||
|
|
||||||
if (isPlaying && source != null)
|
|
||||||
UpdateALGain()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def hasALError: Boolean = {
|
|
||||||
AL10.alGetError != AL10.AL_NO_ERROR
|
|
||||||
}
|
|
||||||
|
|
||||||
private def getOutFormat = new AudioFormat(
|
|
||||||
Encoding.PCM_SIGNED,
|
|
||||||
44100,
|
|
||||||
16,
|
|
||||||
2,
|
|
||||||
4,
|
|
||||||
44100,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
@throws[UnsupportedAudioFileException]
|
|
||||||
@throws[IOException]
|
|
||||||
def start(): Unit = {
|
|
||||||
if (isPlaying)
|
|
||||||
return
|
|
||||||
|
|
||||||
val outFormat = getOutFormat
|
|
||||||
source = BufferUtils.createIntBuffer(1)
|
|
||||||
|
|
||||||
AL10.alGenSources(source)
|
|
||||||
|
|
||||||
if (hasALError) {
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
AL10.alSourcei(source.get(0), AL10.AL_LOOPING, AL10.AL_FALSE)
|
|
||||||
AL10.alSourcef(source.get(0), AL10.AL_PITCH, 1)
|
|
||||||
UpdateALGain()
|
|
||||||
|
|
||||||
if (hasALError) {
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
isPlaying = true
|
|
||||||
|
|
||||||
stream(AudioSystem.getAudioInputStream(outFormat, stream))
|
|
||||||
|
|
||||||
if (isPlaying) {
|
|
||||||
while (AL10.alGetSourcei(source.get(0), AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
case _: InterruptedException =>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
@throws[IOException]
|
|
||||||
private def stream(in: AudioInputStream): Unit = {
|
|
||||||
val format = in.getFormat
|
|
||||||
val dataBuffer = new Array[Byte](65536)
|
|
||||||
var n = 0
|
|
||||||
|
|
||||||
while (isPlaying && n != -1) {
|
|
||||||
if (n > 0) {
|
|
||||||
if (buffer == null) {
|
|
||||||
buffer = BufferUtils.createIntBuffer(1)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
val processed = AL10.alGetSourcei(source.get(0), AL10.AL_BUFFERS_PROCESSED)
|
|
||||||
|
|
||||||
if (processed > 0) {
|
|
||||||
AL10.alSourceUnqueueBuffers(source.get(0), buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AL10.alGenBuffers(buffer)
|
|
||||||
|
|
||||||
val data = BufferUtils.createByteBuffer(n).order(ByteOrder.LITTLE_ENDIAN).put(dataBuffer, 0, n).flip
|
|
||||||
|
|
||||||
AL10.alBufferData(
|
|
||||||
buffer.get(0),
|
|
||||||
if (format.getChannels > 1)
|
|
||||||
AL10.AL_FORMAT_STEREO16
|
|
||||||
else
|
|
||||||
AL10.AL_FORMAT_MONO16,
|
|
||||||
data,
|
|
||||||
format.getSampleRate.toInt
|
|
||||||
)
|
|
||||||
|
|
||||||
AL10.alSourceQueueBuffers(source.get(0), buffer)
|
|
||||||
|
|
||||||
val state = AL10.alGetSourcei(source.get(0), AL10.AL_SOURCE_STATE)
|
|
||||||
|
|
||||||
if (isPlaying && state != AL10.AL_PLAYING)
|
|
||||||
AL10.alSourcePlay(source.get(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
n = in.read(dataBuffer, 0, dataBuffer.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def stop(): Unit = {
|
|
||||||
if (!isPlaying)
|
|
||||||
return
|
|
||||||
|
|
||||||
isPlaying = false
|
|
||||||
|
|
||||||
if (source != null) {
|
|
||||||
AL10.alSourceStop(source)
|
|
||||||
AL10.alDeleteSources(source)
|
|
||||||
source = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer != null) {
|
|
||||||
AL10.alDeleteBuffers(buffer)
|
|
||||||
buffer = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def UpdateALGain(): Unit = {
|
|
||||||
AL10.alSourcef(
|
|
||||||
source.get(0),
|
|
||||||
AL10.AL_GAIN,
|
|
||||||
volume * SoundCategory.getSettingsValue(SoundCategory.Environment)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,8 +3,7 @@ package ocelot.desktop.node
|
|||||||
import ocelot.desktop.node.nodes._
|
import ocelot.desktop.node.nodes._
|
||||||
import totoro.ocelot.brain.entity.traits.GenericCamera
|
import totoro.ocelot.brain.entity.traits.GenericCamera
|
||||||
import totoro.ocelot.brain.entity.{Cable, Case, FloppyDiskDrive, IronNoteBlock, NoteBlock, Relay, Screen}
|
import totoro.ocelot.brain.entity.{Cable, Case, FloppyDiskDrive, IronNoteBlock, NoteBlock, Relay, Screen}
|
||||||
import ocelot.desktop.entity.Camera
|
import ocelot.desktop.entity.{Camera, OpenFMRadio}
|
||||||
import ocelot.desktop.entity.openfmradio.OpenFMRadio
|
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package ocelot.desktop.node.nodes
|
|||||||
|
|
||||||
import ocelot.desktop.OcelotDesktop
|
import ocelot.desktop.OcelotDesktop
|
||||||
import ocelot.desktop.color.{Color, RGBAColorNorm}
|
import ocelot.desktop.color.{Color, RGBAColorNorm}
|
||||||
import ocelot.desktop.entity.openfmradio.OpenFMRadio
|
import ocelot.desktop.entity.OpenFMRadio
|
||||||
import ocelot.desktop.graphics.Graphics
|
import ocelot.desktop.graphics.Graphics
|
||||||
import ocelot.desktop.node.Node
|
import ocelot.desktop.node.Node
|
||||||
|
import ocelot.desktop.windows.{CameraWindow, OpenFMRadioWindow}
|
||||||
|
|
||||||
class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends Node(openFMRadio) {
|
class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends Node(openFMRadio) {
|
||||||
override def icon: String = "nodes/OpenFMRadio"
|
override def icon: String = "nodes/OpenFMRadio"
|
||||||
@ -73,4 +74,11 @@ class OpenFMRadioNode(val openFMRadio: OpenFMRadio) extends Node(openFMRadio) {
|
|||||||
|
|
||||||
openFMRadio.stop()
|
openFMRadio.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var currentWindow: Option[OpenFMRadioWindow] = None
|
||||||
|
|
||||||
|
override def window: Option[OpenFMRadioWindow] = {
|
||||||
|
currentWindow = Option(currentWindow.getOrElse(new OpenFMRadioWindow(this)))
|
||||||
|
currentWindow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -34,12 +34,26 @@ class IconButton(
|
|||||||
if (state == HoverEvent.State.Enter) onHoverEnter()
|
if (state == HoverEvent.State.Enter) onHoverEnter()
|
||||||
else onHoverLeave()
|
else onHoverLeave()
|
||||||
|
|
||||||
case MouseEvent(MouseEvent.State.Press, MouseEvent.Button.Left) =>
|
case MouseEvent(MouseEvent.State.Press, MouseEvent.Button.Left) => {
|
||||||
if (!isSwitch || !isOn) press() else release()
|
if (isSwitch) {
|
||||||
clickSoundSource.play()
|
if (isOn) {
|
||||||
|
release()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
press()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
press()
|
||||||
|
}
|
||||||
|
|
||||||
case ClickEvent(MouseEvent.Button.Left, _) =>
|
clickSoundSource.play()
|
||||||
if (!isSwitch || !isOn) release() else press()
|
}
|
||||||
|
|
||||||
|
case ClickEvent(MouseEvent.Button.Left, _) => {
|
||||||
|
if (!isSwitch)
|
||||||
|
release()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def isOn: Boolean = _isOn
|
def isOn: Boolean = _isOn
|
||||||
@ -56,20 +70,29 @@ class IconButton(
|
|||||||
if (labelTooltip.isDefined)
|
if (labelTooltip.isDefined)
|
||||||
root.get.tooltipPool.closeTooltip(labelTooltip.get)
|
root.get.tooltipPool.closeTooltip(labelTooltip.get)
|
||||||
|
|
||||||
def press(): Unit = {
|
def playPressAnimation(): Unit = {
|
||||||
colorAnimation.goto(pressedColor)
|
colorAnimation.goto(pressedColor)
|
||||||
alphaAnimation.goto(1f)
|
alphaAnimation.goto(1f)
|
||||||
_isOn = true
|
_isOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
def playReleaseAnimation(): Unit = {
|
||||||
|
colorAnimation.goto(releasedColor)
|
||||||
|
alphaAnimation.goto(0f)
|
||||||
|
_isOn = false
|
||||||
|
}
|
||||||
|
|
||||||
|
def press(): Unit = {
|
||||||
|
playPressAnimation()
|
||||||
onPressed()
|
onPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
def release(): Unit = {
|
def release(): Unit = {
|
||||||
colorAnimation.goto(releasedColor)
|
playReleaseAnimation()
|
||||||
alphaAnimation.goto(0f)
|
|
||||||
_isOn = false
|
|
||||||
onReleased()
|
onReleased()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size = minimumSize
|
size = minimumSize
|
||||||
|
|
||||||
private def releasedIconSize: Size2D = Spritesheet.spriteSize(releasedIcon) * sizeMultiplier
|
private def releasedIconSize: Size2D = Spritesheet.spriteSize(releasedIcon) * sizeMultiplier
|
||||||
@ -100,8 +123,8 @@ class IconButton(
|
|||||||
super.update()
|
super.update()
|
||||||
colorAnimation.update()
|
colorAnimation.update()
|
||||||
alphaAnimation.update()
|
alphaAnimation.update()
|
||||||
if (isOn && !_isOn) press()
|
if (isOn && !_isOn) playPressAnimation()
|
||||||
if (!isOn && _isOn) release()
|
if (!isOn && _isOn) playReleaseAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClick
|
override protected def clickSoundSource: SoundSource = SoundSources.InterfaceClick
|
||||||
|
|||||||
@ -201,7 +201,7 @@ class ComputerWindow(computerNode: ComputerNode) extends BasicWindow {
|
|||||||
|
|
||||||
override def load(nbt: NBTTagCompound): Unit = {
|
override def load(nbt: NBTTagCompound): Unit = {
|
||||||
bottomDrawerAnimation.load(nbt, "drawerAnimation")
|
bottomDrawerAnimation.load(nbt, "drawerAnimation")
|
||||||
if (bottomDrawerAnimation.isGoingUp) drawerButton.press()
|
if (bottomDrawerAnimation.isGoingUp) drawerButton.playPressAnimation()
|
||||||
super.load(nbt)
|
super.load(nbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,62 @@
|
|||||||
|
package ocelot.desktop.windows
|
||||||
|
|
||||||
|
import ocelot.desktop.audio.{SoundSource, SoundSources}
|
||||||
|
import ocelot.desktop.color.Color
|
||||||
|
import ocelot.desktop.entity.OpenFMRadio
|
||||||
|
import ocelot.desktop.geometry.{Padding2D, Size2D}
|
||||||
|
import ocelot.desktop.graphics.Graphics
|
||||||
|
import ocelot.desktop.node.nodes.{ComputerNode, OpenFMRadioNode}
|
||||||
|
import ocelot.desktop.ui.event.MouseEvent
|
||||||
|
import ocelot.desktop.ui.layout.{Layout, LinearLayout}
|
||||||
|
import ocelot.desktop.ui.widget._
|
||||||
|
import ocelot.desktop.ui.widget.tooltip.Tooltip
|
||||||
|
import ocelot.desktop.ui.widget.window.BasicWindow
|
||||||
|
import ocelot.desktop.util.animation.UnitAnimation
|
||||||
|
import ocelot.desktop.util.{DrawUtils, Orientation}
|
||||||
|
import ocelot.desktop.{ColorScheme, OcelotDesktop}
|
||||||
|
import totoro.ocelot.brain.entity.Case
|
||||||
|
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||||
|
|
||||||
|
class OpenFMRadioWindow(radioNode: OpenFMRadioNode) extends BasicWindow {
|
||||||
|
private def radio: OpenFMRadio = radioNode.openFMRadio
|
||||||
|
|
||||||
|
children :+= new Widget {
|
||||||
|
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
|
||||||
|
|
||||||
|
children :+= new PaddingBox(
|
||||||
|
new Widget {
|
||||||
|
children :+= new IconButton(
|
||||||
|
"buttons/PowerOff",
|
||||||
|
"buttons/PowerOn",
|
||||||
|
isSwitch = true
|
||||||
|
) {
|
||||||
|
override def isOn: Boolean = radio.isPlaying
|
||||||
|
|
||||||
|
override def onPressed(): Unit = {
|
||||||
|
radio.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onReleased(): Unit = {
|
||||||
|
radio.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override def clickSoundSource: SoundSource = SoundSources.MinecraftClick
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Padding2D(top = 44, left = 22)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def update(): Unit = {
|
||||||
|
super.update()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override def draw(g: Graphics): Unit = {
|
||||||
|
beginDraw(g)
|
||||||
|
DrawUtils.windowWithShadow(g, position.x, position.y, size.width, size.height, 1f, 0.5f)
|
||||||
|
drawChildren(g)
|
||||||
|
endDraw(g)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user