avm2: Add `flash.media.SoundChannel` class & associated object storage
This commit is contained in:
parent
3c3228f3c9
commit
8a58956f1f
|
@ -117,6 +117,7 @@ pub struct SystemPrototypes<'gc> {
|
||||||
pub regexp: Object<'gc>,
|
pub regexp: Object<'gc>,
|
||||||
pub vector: Object<'gc>,
|
pub vector: Object<'gc>,
|
||||||
pub soundtransform: Object<'gc>,
|
pub soundtransform: Object<'gc>,
|
||||||
|
pub soundchannel: Object<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> SystemPrototypes<'gc> {
|
impl<'gc> SystemPrototypes<'gc> {
|
||||||
|
@ -168,6 +169,7 @@ impl<'gc> SystemPrototypes<'gc> {
|
||||||
regexp: empty,
|
regexp: empty,
|
||||||
vector: empty,
|
vector: empty,
|
||||||
soundtransform: empty,
|
soundtransform: empty,
|
||||||
|
soundchannel: empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,6 +212,7 @@ pub struct SystemClasses<'gc> {
|
||||||
pub regexp: Object<'gc>,
|
pub regexp: Object<'gc>,
|
||||||
pub vector: Object<'gc>,
|
pub vector: Object<'gc>,
|
||||||
pub soundtransform: Object<'gc>,
|
pub soundtransform: Object<'gc>,
|
||||||
|
pub soundchannel: Object<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> SystemClasses<'gc> {
|
impl<'gc> SystemClasses<'gc> {
|
||||||
|
@ -261,6 +264,7 @@ impl<'gc> SystemClasses<'gc> {
|
||||||
regexp: empty,
|
regexp: empty,
|
||||||
vector: empty,
|
vector: empty,
|
||||||
soundtransform: empty,
|
soundtransform: empty,
|
||||||
|
soundchannel: empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -819,6 +823,13 @@ pub fn load_player_globals<'gc>(
|
||||||
domain,
|
domain,
|
||||||
script,
|
script,
|
||||||
)?;
|
)?;
|
||||||
|
avm2_system_class!(
|
||||||
|
soundchannel,
|
||||||
|
activation,
|
||||||
|
flash::media::soundchannel::create_class(mc),
|
||||||
|
domain,
|
||||||
|
script
|
||||||
|
);
|
||||||
|
|
||||||
// package `flash.text`
|
// package `flash.text`
|
||||||
avm2_system_class!(
|
avm2_system_class!(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! `flash.media` namespace
|
//! `flash.media` namespace
|
||||||
|
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
|
pub mod soundchannel;
|
||||||
pub mod soundmixer;
|
pub mod soundmixer;
|
||||||
pub mod soundtransform;
|
pub mod soundtransform;
|
||||||
pub mod video;
|
pub mod video;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
//! `flash.media.SoundChannel` builtin/prototype
|
||||||
|
|
||||||
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::class::{Class, ClassAttributes};
|
||||||
|
use crate::avm2::method::Method;
|
||||||
|
use crate::avm2::names::{Namespace, QName};
|
||||||
|
use crate::avm2::object::{soundchannel_allocator, Object};
|
||||||
|
use crate::avm2::value::Value;
|
||||||
|
use crate::avm2::Error;
|
||||||
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
|
/// Implements `flash.media.SoundChannel`'s instance constructor.
|
||||||
|
pub fn instance_init<'gc>(
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
this: Option<Object<'gc>>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
if let Some(this) = this {
|
||||||
|
activation.super_init(this, &[])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements `flash.media.SoundChannel`'s class constructor.
|
||||||
|
pub fn class_init<'gc>(
|
||||||
|
_activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
_this: Option<Object<'gc>>,
|
||||||
|
_args: &[Value<'gc>],
|
||||||
|
) -> Result<Value<'gc>, Error> {
|
||||||
|
Ok(Value::Undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct `SoundChannel`'s class.
|
||||||
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
|
let class = Class::new(
|
||||||
|
QName::new(Namespace::package("flash.media"), "SoundChannel"),
|
||||||
|
Some(QName::new(Namespace::package("flash.events"), "EventDispatcher").into()),
|
||||||
|
Method::from_builtin(instance_init, "<SoundChannel instance initializer>", mc),
|
||||||
|
Method::from_builtin(class_init, "<SoundChannel class initializer>", mc),
|
||||||
|
mc,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut write = class.write(mc);
|
||||||
|
|
||||||
|
write.set_attributes(ClassAttributes::SEALED | ClassAttributes::FINAL);
|
||||||
|
write.set_instance_allocator(soundchannel_allocator);
|
||||||
|
|
||||||
|
class
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ use crate::avm2::traits::{Trait, TraitKind};
|
||||||
use crate::avm2::value::{Hint, Value};
|
use crate::avm2::value::{Hint, Value};
|
||||||
use crate::avm2::vector::VectorStorage;
|
use crate::avm2::vector::VectorStorage;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use crate::backend::audio::SoundHandle;
|
use crate::backend::audio::{SoundHandle, SoundInstanceHandle};
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use ruffle_macros::enum_trait_object;
|
use ruffle_macros::enum_trait_object;
|
||||||
|
@ -37,6 +37,7 @@ mod primitive_object;
|
||||||
mod regexp_object;
|
mod regexp_object;
|
||||||
mod script_object;
|
mod script_object;
|
||||||
mod sound_object;
|
mod sound_object;
|
||||||
|
mod soundchannel_object;
|
||||||
mod stage_object;
|
mod stage_object;
|
||||||
mod vector_object;
|
mod vector_object;
|
||||||
mod xml_object;
|
mod xml_object;
|
||||||
|
@ -56,6 +57,7 @@ pub use crate::avm2::object::primitive_object::{primitive_allocator, PrimitiveOb
|
||||||
pub use crate::avm2::object::regexp_object::{regexp_allocator, RegExpObject};
|
pub use crate::avm2::object::regexp_object::{regexp_allocator, RegExpObject};
|
||||||
pub use crate::avm2::object::script_object::ScriptObject;
|
pub use crate::avm2::object::script_object::ScriptObject;
|
||||||
pub use crate::avm2::object::sound_object::{sound_allocator, SoundObject};
|
pub use crate::avm2::object::sound_object::{sound_allocator, SoundObject};
|
||||||
|
pub use crate::avm2::object::soundchannel_object::{soundchannel_allocator, SoundChannelObject};
|
||||||
pub use crate::avm2::object::stage_object::{stage_allocator, StageObject};
|
pub use crate::avm2::object::stage_object::{stage_allocator, StageObject};
|
||||||
pub use crate::avm2::object::vector_object::{vector_allocator, VectorObject};
|
pub use crate::avm2::object::vector_object::{vector_allocator, VectorObject};
|
||||||
pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject};
|
pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject};
|
||||||
|
@ -83,6 +85,7 @@ pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject};
|
||||||
ClassObject(ClassObject<'gc>),
|
ClassObject(ClassObject<'gc>),
|
||||||
VectorObject(VectorObject<'gc>),
|
VectorObject(VectorObject<'gc>),
|
||||||
SoundObject(SoundObject<'gc>),
|
SoundObject(SoundObject<'gc>),
|
||||||
|
SoundChannelObject(SoundChannelObject<'gc>),
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
|
||||||
|
@ -1226,6 +1229,16 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
///
|
///
|
||||||
/// This does nothing if the object is not a sound.
|
/// This does nothing if the object is not a sound.
|
||||||
fn set_sound(self, _mc: MutationContext<'gc, '_>, _sound: SoundHandle) {}
|
fn set_sound(self, _mc: MutationContext<'gc, '_>, _sound: SoundHandle) {}
|
||||||
|
|
||||||
|
/// Unwrap this object's sound instance handle.
|
||||||
|
fn as_sound_instance(self) -> Option<SoundInstanceHandle> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associate the object with a particular sound instance handle.
|
||||||
|
///
|
||||||
|
/// This does nothing if the object is not a sound channel.
|
||||||
|
fn set_sound_instance(self, _mc: MutationContext<'gc, '_>, _sound: SoundInstanceHandle) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ObjectPtr {}
|
pub enum ObjectPtr {}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
//! Object representation for sounds
|
||||||
|
|
||||||
|
use crate::avm2::activation::Activation;
|
||||||
|
use crate::avm2::class::Class;
|
||||||
|
use crate::avm2::names::{Namespace, QName};
|
||||||
|
use crate::avm2::object::script_object::ScriptObjectData;
|
||||||
|
use crate::avm2::object::{Object, ObjectPtr, TObject};
|
||||||
|
use crate::avm2::scope::Scope;
|
||||||
|
use crate::avm2::string::AvmString;
|
||||||
|
use crate::avm2::value::Value;
|
||||||
|
use crate::avm2::Error;
|
||||||
|
use crate::backend::audio::SoundInstanceHandle;
|
||||||
|
use crate::{
|
||||||
|
impl_avm2_custom_object, impl_avm2_custom_object_instance, impl_avm2_custom_object_properties,
|
||||||
|
};
|
||||||
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
|
|
||||||
|
/// A class instance allocator that allocates SoundChannel objects.
|
||||||
|
pub fn soundchannel_allocator<'gc>(
|
||||||
|
class: Object<'gc>,
|
||||||
|
proto: Object<'gc>,
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
) -> Result<Object<'gc>, Error> {
|
||||||
|
let base = ScriptObjectData::base_new(Some(proto), Some(class));
|
||||||
|
|
||||||
|
Ok(SoundChannelObject(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
|
SoundChannelObjectData { base, sound: None },
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Collect, Debug, Copy)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct SoundChannelObject<'gc>(GcCell<'gc, SoundChannelObjectData<'gc>>);
|
||||||
|
|
||||||
|
#[derive(Clone, Collect, Debug)]
|
||||||
|
#[collect(no_drop)]
|
||||||
|
pub struct SoundChannelObjectData<'gc> {
|
||||||
|
/// Base script object
|
||||||
|
base: ScriptObjectData<'gc>,
|
||||||
|
|
||||||
|
/// The sound this object holds.
|
||||||
|
#[collect(require_static)]
|
||||||
|
sound: Option<SoundInstanceHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> SoundChannelObject<'gc> {
|
||||||
|
/// Convert a bare sound instance into it's object representation.
|
||||||
|
///
|
||||||
|
/// In AS3, playing sounds are accessed through subclasses of `Sound`. As a
|
||||||
|
/// result, this needs to take the subclass so that the returned object is
|
||||||
|
/// an instance of the correct class.
|
||||||
|
pub fn from_sound_instance(
|
||||||
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
|
class: Object<'gc>,
|
||||||
|
sound: SoundInstanceHandle,
|
||||||
|
) -> Result<Object<'gc>, Error> {
|
||||||
|
let proto = class
|
||||||
|
.get_property(
|
||||||
|
class,
|
||||||
|
&QName::new(Namespace::public(), "prototype"),
|
||||||
|
activation,
|
||||||
|
)?
|
||||||
|
.coerce_to_object(activation)?;
|
||||||
|
let base = ScriptObjectData::base_new(Some(proto), Some(class));
|
||||||
|
|
||||||
|
let mut sound_object: Object<'gc> = SoundChannelObject(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
|
SoundChannelObjectData {
|
||||||
|
base,
|
||||||
|
sound: Some(sound),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.into();
|
||||||
|
sound_object.install_instance_traits(activation, class)?;
|
||||||
|
|
||||||
|
//TODO: Do we need to call the default initializer of sounds?
|
||||||
|
|
||||||
|
Ok(sound_object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'gc> TObject<'gc> for SoundChannelObject<'gc> {
|
||||||
|
impl_avm2_custom_object!(base);
|
||||||
|
impl_avm2_custom_object_properties!(base);
|
||||||
|
impl_avm2_custom_object_instance!(base);
|
||||||
|
|
||||||
|
fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result<Value<'gc>, Error> {
|
||||||
|
Ok(Object::from(*self).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result<Object<'gc>, Error> {
|
||||||
|
let base = ScriptObjectData::base_new(Some((*self).into()), None);
|
||||||
|
|
||||||
|
Ok(SoundChannelObject(GcCell::allocate(
|
||||||
|
activation.context.gc_context,
|
||||||
|
SoundChannelObjectData { base, sound: None },
|
||||||
|
))
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_sound_instance(self) -> Option<SoundInstanceHandle> {
|
||||||
|
self.0.read().sound
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_sound_instance(self, mc: MutationContext<'gc, '_>, sound: SoundInstanceHandle) {
|
||||||
|
self.0.write(mc).sound = Some(sound);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue