mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-20 11:09:20 +01:00
Have each sound card create its own sound stream
In addition to that, fixes a long-standing bug concerning brain event dispatch to inventory items: items could receive events coming from a component with a different address. Fixes #175.
This commit is contained in:
parent
d1d2deddff
commit
4bcdebedfa
@ -62,6 +62,8 @@ object Audio extends Logging {
|
|||||||
|
|
||||||
sources.put(source, sourceId)
|
sources.put(source, sourceId)
|
||||||
|
|
||||||
|
logger.debug(s"created new source $sourceId for $source")
|
||||||
|
|
||||||
sourceId
|
sourceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,6 +72,7 @@ object Audio extends Logging {
|
|||||||
|
|
||||||
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
|
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
|
||||||
tx.onFailure { AL10W.alDeleteBuffers(bufferId) }
|
tx.onFailure { AL10W.alDeleteBuffers(bufferId) }
|
||||||
|
logger.debug(s"enqueued buffer $bufferId for source $sourceId ($source)", new Exception())
|
||||||
AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
||||||
|
|
||||||
if (AL10W.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING) {
|
if (AL10W.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING) {
|
||||||
@ -226,6 +229,7 @@ object Audio extends Logging {
|
|||||||
|
|
||||||
@throws[OpenAlException]
|
@throws[OpenAlException]
|
||||||
private def deleteSource(sourceId: Int): Unit = {
|
private def deleteSource(sourceId: Int): Unit = {
|
||||||
|
logger.debug(s"deleting source $sourceId")
|
||||||
AL10W.alSourceStop(sourceId)
|
AL10W.alSourceStop(sourceId)
|
||||||
cleanupSourceBuffers(sourceId)
|
cleanupSourceBuffers(sourceId)
|
||||||
AL10W.alDeleteSources(sourceId)
|
AL10W.alDeleteSources(sourceId)
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
package ocelot.desktop.inventory
|
package ocelot.desktop.inventory
|
||||||
|
|
||||||
import ocelot.desktop.inventory.Inventory.SlotObserver
|
import ocelot.desktop.inventory.Inventory.SlotObserver
|
||||||
import ocelot.desktop.ui.event.{Event, EventAware}
|
import ocelot.desktop.ui.event.{BrainEvent, Event, EventAware}
|
||||||
|
import ocelot.desktop.util.Disposable
|
||||||
import totoro.ocelot.brain.event.NodeEvent
|
import totoro.ocelot.brain.event.NodeEvent
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
/** Provides an inventory — a collection of [[Item]]s indexed by slots. */
|
/** Provides an inventory — a collection of [[Item]]s indexed by slots. */
|
||||||
trait Inventory extends EventAware {
|
trait Inventory extends EventAware with Disposable {
|
||||||
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
// parallels totoro.ocelot.brain.entity.traits.Inventory
|
||||||
// this is intentional
|
// this is intentional
|
||||||
|
|
||||||
@ -20,6 +21,16 @@ trait Inventory extends EventAware {
|
|||||||
private val itemSlots = mutable.HashMap.empty[I, Int]
|
private val itemSlots = mutable.HashMap.empty[I, Int]
|
||||||
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
private val observers = mutable.HashMap.empty[Int, WeakHashSet[SlotObserver]]
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
super.dispose()
|
||||||
|
|
||||||
|
for (slot <- inventoryIterator) {
|
||||||
|
val item = slot.get
|
||||||
|
slot.remove()
|
||||||
|
item.foreach(_.dispose())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Called after a new item is added to the inventory.
|
/** Called after a new item is added to the inventory.
|
||||||
*
|
*
|
||||||
* @param slot the slot the item was added to
|
* @param slot the slot the item was added to
|
||||||
@ -72,7 +83,7 @@ trait Inventory extends EventAware {
|
|||||||
|
|
||||||
for (slot <- inventoryIterator; item <- slot.get) {
|
for (slot <- inventoryIterator; item <- slot.get) {
|
||||||
event match {
|
event match {
|
||||||
case n: NodeEvent if !item.shouldReceiveEventsFor(n.address) => // ignore
|
case BrainEvent(e: NodeEvent) if !item.shouldReceiveEventsFor(e.address) => // ignore
|
||||||
case _ => item.handleEvent(event)
|
case _ => item.handleEvent(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +159,6 @@ trait Inventory extends EventAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final object Slot {
|
final object Slot {
|
||||||
|
|
||||||
/** Creates a proxy to an inventory slot. */
|
/** Creates a proxy to an inventory slot. */
|
||||||
def apply(index: Int) = new Slot(index)
|
def apply(index: Int) = new Slot(index)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,50 @@
|
|||||||
package ocelot.desktop.inventory.item
|
package ocelot.desktop.inventory.item
|
||||||
|
|
||||||
|
import ocelot.desktop.audio.{Audio, SoundCategory, SoundSamples, SoundSource, SoundStream}
|
||||||
import ocelot.desktop.graphics.IconSource
|
import ocelot.desktop.graphics.IconSource
|
||||||
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
import ocelot.desktop.inventory.traits.{CardItem, ComponentItem, PersistableItem}
|
||||||
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
import ocelot.desktop.inventory.{Item, ItemFactory, ItemRecoverer}
|
||||||
|
import ocelot.desktop.ui.event.BrainEvent
|
||||||
import ocelot.desktop.ui.widget.card.SoundCardWindow
|
import ocelot.desktop.ui.widget.card.SoundCardWindow
|
||||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||||
import ocelot.desktop.ui.widget.window.Windowed
|
import ocelot.desktop.ui.widget.window.Windowed
|
||||||
|
import ocelot.desktop.util.Lazy
|
||||||
|
import totoro.ocelot.brain.Settings
|
||||||
import totoro.ocelot.brain.entity.sound_card.SoundCard
|
import totoro.ocelot.brain.entity.sound_card.SoundCard
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment}
|
||||||
|
import totoro.ocelot.brain.event.SoundCardAudioEvent
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
class SoundCardItem(val soundCard: SoundCard)
|
class SoundCardItem(val soundCard: SoundCard)
|
||||||
extends Item with ComponentItem with PersistableItem with CardItem with Windowed[SoundCardWindow] {
|
extends Item
|
||||||
|
with ComponentItem
|
||||||
|
with PersistableItem
|
||||||
|
with CardItem
|
||||||
|
with Windowed[SoundCardWindow] {
|
||||||
|
|
||||||
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
|
override def createWindow(): SoundCardWindow = new SoundCardWindow(soundCard)
|
||||||
|
|
||||||
override def entity: Entity with Environment = soundCard
|
override def entity: SoundCard = soundCard
|
||||||
|
|
||||||
|
private val streamPair = Lazy(Audio.newStream(SoundCategory.Records))
|
||||||
|
private def stream: SoundStream = streamPair.getSync._1
|
||||||
|
private def source: SoundSource = streamPair.getSync._2
|
||||||
|
|
||||||
|
eventHandlers += {
|
||||||
|
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
||||||
|
val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8)
|
||||||
|
stream.enqueue(samples)
|
||||||
|
source.volume = event.volume
|
||||||
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
super.dispose()
|
||||||
|
|
||||||
|
for (stream <- streamPair.getOption) {
|
||||||
|
stream._2.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
override def fillRmbMenu(menu: ContextMenu): Unit = {
|
||||||
menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) {
|
menu.addEntry(ContextMenuEntry("Open card interface", IconSource.Window) {
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import ocelot.desktop.ui.event.handlers.DiskActivityHandler
|
|||||||
import ocelot.desktop.ui.particle.Particle
|
import ocelot.desktop.ui.particle.Particle
|
||||||
import ocelot.desktop.util.Messages
|
import ocelot.desktop.util.Messages
|
||||||
import ocelot.desktop.{ColorScheme, Settings => DesktopSettings}
|
import ocelot.desktop.{ColorScheme, Settings => DesktopSettings}
|
||||||
import totoro.ocelot.brain.Settings
|
|
||||||
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware}
|
||||||
import totoro.ocelot.brain.event._
|
import totoro.ocelot.brain.event._
|
||||||
|
|
||||||
@ -25,10 +24,6 @@ abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceA
|
|||||||
with BoomCardFxHandler
|
with BoomCardFxHandler
|
||||||
with ShiftClickNode {
|
with ShiftClickNode {
|
||||||
|
|
||||||
private lazy val soundCardSounds: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records)
|
|
||||||
private def soundCardStream: SoundStream = soundCardSounds._1
|
|
||||||
private def soundCardSource: SoundSource = soundCardSounds._2
|
|
||||||
|
|
||||||
eventHandlers += {
|
eventHandlers += {
|
||||||
case BrainEvent(event: MachineCrashEvent) =>
|
case BrainEvent(event: MachineCrashEvent) =>
|
||||||
val message = Messages.lift(event.message) match {
|
val message = Messages.lift(event.message) match {
|
||||||
@ -46,11 +41,6 @@ abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceA
|
|||||||
|
|
||||||
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled =>
|
||||||
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
|
BeepGenerator.newBeep(event.pattern, 1000, 200).play()
|
||||||
|
|
||||||
case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled =>
|
|
||||||
val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8)
|
|
||||||
soundCardStream.enqueue(samples)
|
|
||||||
soundCardSource.volume = event.volume
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def drawOverlay(g: Graphics): Unit = HolidayIcon match {
|
protected def drawOverlay(g: Graphics): Unit = HolidayIcon match {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package ocelot.desktop.ui.widget.window
|
package ocelot.desktop.ui.widget.window
|
||||||
|
|
||||||
import ocelot.desktop.util.Persistable
|
import ocelot.desktop.util.{Disposable, Persistable}
|
||||||
import totoro.ocelot.brain.nbt.NBTTagCompound
|
import totoro.ocelot.brain.nbt.NBTTagCompound
|
||||||
|
|
||||||
trait Windowed[T <: Window] extends Persistable {
|
trait Windowed[T <: Window] extends Persistable with Disposable {
|
||||||
protected def createWindow(): T
|
protected def createWindow(): T
|
||||||
|
|
||||||
private var _window: Option[T] = None
|
private var _window: Option[T] = None
|
||||||
@ -33,6 +33,10 @@ trait Windowed[T <: Window] extends Persistable {
|
|||||||
window
|
window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def dispose(): Unit = {
|
||||||
|
super.dispose()
|
||||||
|
closeAndDisposeWindow()
|
||||||
|
}
|
||||||
// ------------------------------- NBT -------------------------------
|
// ------------------------------- NBT -------------------------------
|
||||||
|
|
||||||
protected def windowNBTKey: String = "window"
|
protected def windowNBTKey: String = "window"
|
||||||
|
|||||||
@ -17,7 +17,13 @@ import totoro.ocelot.brain.util.Tier.Tier
|
|||||||
import scala.math.Ordering.Implicits.infixOrderingOps
|
import scala.math.Ordering.Implicits.infixOrderingOps
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
trait ComputerAware extends Logging with SyncedInventory with DefaultSlotItemsFillable with Windowed[ComputerWindow] {
|
trait ComputerAware
|
||||||
|
extends Logging
|
||||||
|
with SyncedInventory
|
||||||
|
with DefaultSlotItemsFillable
|
||||||
|
with Windowed[ComputerWindow]
|
||||||
|
with Disposable {
|
||||||
|
|
||||||
override type I = Item with EntityItem
|
override type I = Item with EntityItem
|
||||||
|
|
||||||
def computer: Computer with TieredPersistable
|
def computer: Computer with TieredPersistable
|
||||||
|
|||||||
23
src/main/scala/ocelot/desktop/util/Lazy.scala
Normal file
23
src/main/scala/ocelot/desktop/util/Lazy.scala
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package ocelot.desktop.util
|
||||||
|
|
||||||
|
class Lazy[A](init: () => A) {
|
||||||
|
private var _value = Option.empty[A]
|
||||||
|
|
||||||
|
def get: A = _value match {
|
||||||
|
case Some(value) => value
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
val v = init()
|
||||||
|
_value = Some(v)
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
def getSync: A = synchronized(get)
|
||||||
|
|
||||||
|
def getOption: Option[A] = _value
|
||||||
|
}
|
||||||
|
|
||||||
|
object Lazy {
|
||||||
|
def apply[A](init: => A): Lazy[A] = new Lazy(() => init)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user