use ObjectURI more consistently
[gnash.git] / libcore / asobj / Sound_as.cpp
blob1180e74ac5add195a7d6e641cce933d1b21358c7
1 // Sound_as.cpp: ActionScript "Sound" class, for Gnash.
2 //
3 // Copyright (C) 2009, 2010 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "Sound_as.h"
22 #include <string>
23 #include <boost/scoped_ptr.hpp>
24 #include <boost/scoped_array.hpp>
25 #include <boost/thread/mutex.hpp>
26 #include <boost/cstdint.hpp>
28 #include "RunResources.h"
29 #include "log.h"
30 #include "sound_handler.h"
31 #include "AudioDecoder.h"
32 #include "MediaHandler.h"
33 #include "sound_definition.h"
34 #include "movie_root.h"
35 #include "movie_definition.h"
36 #include "fn_call.h"
37 #include "Global_as.h"
38 #include "GnashException.h" // for ActionException
39 #include "builtin_function.h" // need builtin_function
40 #include "NativeFunction.h" // need builtin_function
41 #include "smart_ptr.h" // for boost intrusive_ptr
42 #include "VM.h"
43 #include "namedStrings.h"
44 #include "StreamProvider.h"
45 #include "ObjectURI.h"
46 #include "Relay.h"
49 namespace gnash {
51 // Forward declarations
52 namespace {
53 as_value sound_new(const fn_call& fn);
54 as_value sound_attachsound(const fn_call& fn);
55 as_value sound_getbytesloaded(const fn_call& fn);
56 as_value sound_setPosition(const fn_call& fn);
57 as_value sound_areSoundsInaccessible(const fn_call& fn);
58 as_value sound_getbytestotal(const fn_call& fn);
59 as_value sound_getpan(const fn_call& fn);
60 as_value sound_setpan(const fn_call& fn);
61 as_value sound_getDuration(const fn_call& fn);
62 as_value sound_setDuration(const fn_call& fn);
63 as_value sound_gettransform(const fn_call& fn);
64 as_value sound_getPosition(const fn_call& fn);
65 as_value sound_getvolume(const fn_call& fn);
66 as_value sound_loadsound(const fn_call& fn);
67 as_value sound_settransform(const fn_call& fn);
68 as_value sound_setvolume(const fn_call& fn);
69 as_value sound_start(const fn_call& fn);
70 as_value sound_stop(const fn_call& fn);
71 as_value checkPolicyFile_getset(const fn_call& fn);
72 void attachSoundInterface(as_object& o);
75 /// A Sound object in ActionScript can control and play sound
77 /// Two types of sound are handled:
78 ///
79 /// 1. external sounds, either loaded or streamed
80 /// 2. embedded sounds, referenced by library (export) symbol.
82 /// Sound objects also control volume, pan, and other properties for a target
83 /// movieclip.
85 /// Sound_as objects
87 /// 1. May be associated with a particular DisplayObject.
88 /// 2. May be associated with one or more playing sounds.
90 /// A Sound_as that is not associated with a particular DisplayObject controls
91 /// the sound properties of the whole Movie.
92 class Sound_as : public ActiveRelay
95 public:
97 Sound_as(as_object* owner);
99 ~Sound_as();
101 /// Make this sound control the given DisplayObject
103 /// NOTE: 0 is accepted, to implement an "invalid"
104 /// controller type.
106 void attachCharacter(DisplayObject* attachedChar);
108 void attachSound(int si, const std::string& name);
110 /// Get number of bytes loaded.
112 /// This only applies to external sounds. If unknown or not external, -1
113 /// is returned.
114 long getBytesLoaded();
116 /// Get total number of bytes in the external sound being loaded
118 /// This only applies to external sounds. If unknown or not external, -1
119 /// is returned.
120 long getBytesTotal();
122 /// Whether the Sound_as has any sound data
123 bool active() const {
124 return soundId >= 0 || isStreaming;
127 /// Get the pan setting of the attached DisplayObject.
129 /// If no object is attached, this retrieves settings for the whole Movie.
130 void getPan();
132 /// Get the sound transform of the attached DisplayObject.
134 /// If no object is attached, this retrieves settings for the whole Movie.
135 void getTransform();
137 /// Get volume from associated resource
139 /// @return true of volume was obtained, false
140 /// otherwise (for example if the associated
141 /// DisplayObject was unloaded).
143 bool getVolume(int& volume);
144 void setVolume(int volume);
146 /// Load an external sound.
148 /// The Sound object is then associated with the external sound.
149 void loadSound(const std::string& file, bool streaming);
151 void setPan();
153 void setTransform();
155 void start(double secsStart, int loops);
157 void stop(int si);
159 /// Get the duration of the sound.
161 /// This is only meaningful when the Sound_as object has an associated
162 /// sound, that is after attachSound or loadSound has been called.
163 size_t getDuration() const;
165 /// Get the position within the sound.
167 /// This is only meaningful when the Sound_as object has an associated
168 /// sound, that is after attachSound or loadSound has been called.
169 size_t getPosition() const;
171 std::string soundName;
173 private:
175 void markReachableResources() const;
177 boost::scoped_ptr<CharacterProxy> _attachedCharacter;
178 int soundId;
179 bool externalSound;
180 bool isStreaming;
182 sound::sound_handler* _soundHandler;
184 media::MediaHandler* _mediaHandler;
186 boost::scoped_ptr<media::MediaParser> _mediaParser;
188 boost::scoped_ptr<media::AudioDecoder> _audioDecoder;
190 /// Number of milliseconds into the sound to start it
192 /// This is set by start()
193 boost::uint64_t _startTime;
195 boost::scoped_array<boost::uint8_t> _leftOverData;
196 boost::uint8_t* _leftOverPtr;
197 boost::uint32_t _leftOverSize;
199 /// This is a sound_handler::aux_streamer_ptr type.
200 static unsigned int getAudioWrapper(void *owner, boost::int16_t* samples,
201 unsigned int nSamples, bool& etEOF);
203 unsigned int getAudio(boost::int16_t* samples, unsigned int nSamples,
204 bool& atEOF);
206 /// The aux streamer for sound handler
207 sound::InputStream* _inputStream;
209 int remainingLoops;
211 /// Query media parser for audio info, create decoder and attach aux streamer
212 /// if found.
214 /// @return an InputStream* if audio found and aux streamer attached,
215 /// 0 if no audio found.
217 /// May throw a MediaException if audio was found but
218 /// audio decoder could not be created
219 ///
220 sound::InputStream* attachAuxStreamerIfNeeded();
222 /// Register a timer for audio info probing
223 void startProbeTimer();
225 /// Unregister the probe timer
226 void stopProbeTimer();
228 virtual void update();
230 /// Probe audio
231 void probeAudio();
233 bool _soundCompleted;
235 boost::mutex _soundCompletedMutex;
237 /// Thread-safe setter for _soundCompleted
238 void markSoundCompleted(bool completed);
240 // Does this sound have a live input stream?
241 bool isAttached() const {
242 return (_inputStream);
247 Sound_as::Sound_as(as_object* owner)
249 ActiveRelay(owner),
250 _attachedCharacter(0),
251 soundId(-1),
252 externalSound(false),
253 isStreaming(false),
254 _soundHandler(getRunResources(*owner).soundHandler()),
255 _mediaHandler(getRunResources(*owner).mediaHandler()),
256 _startTime(0),
257 _leftOverData(),
258 _leftOverPtr(0),
259 _leftOverSize(0),
260 _inputStream(0),
261 remainingLoops(0),
262 _soundCompleted(false)
266 Sound_as::~Sound_as()
268 // Just in case...
269 if (_inputStream && _soundHandler) {
270 _soundHandler->unplugInputStream(_inputStream);
271 _inputStream=0;
276 // extern (used by Global.cpp)
277 void
278 sound_class_init(as_object& where, const ObjectURI& uri)
281 Global_as& gl = getGlobal(where);
282 as_object* proto = createObject(gl);
283 as_object* cl = gl.createClass(&sound_new, proto);
284 attachSoundInterface(*proto);
285 proto->set_member_flags(NSV::PROP_CONSTRUCTOR, PropFlags::readOnly);
286 proto->set_member_flags(NSV::PROP_uuPROTOuu, PropFlags::readOnly, 0);
288 // Register _global.String
289 where.init_member(uri, cl, as_object::DefaultFlags);
293 void
294 registerSoundNative(as_object& global)
296 VM& vm = getVM(global);
297 vm.registerNative(sound_getpan, 500, 0);
298 vm.registerNative(sound_gettransform, 500, 1);
299 vm.registerNative(sound_getvolume, 500, 2);
300 vm.registerNative(sound_setpan, 500, 3);
301 vm.registerNative(sound_settransform, 500, 4);
302 vm.registerNative(sound_setvolume, 500, 5);
303 vm.registerNative(sound_stop, 500, 6);
304 vm.registerNative(sound_attachsound, 500, 7);
305 vm.registerNative(sound_start, 500, 8);
306 vm.registerNative(sound_getDuration, 500, 9);
307 vm.registerNative(sound_setDuration, 500, 10);
308 vm.registerNative(sound_getPosition, 500, 11);
309 vm.registerNative(sound_setPosition, 500, 12);
310 vm.registerNative(sound_loadsound, 500, 13);
311 vm.registerNative(sound_getbytesloaded, 500, 14);
312 vm.registerNative(sound_getbytestotal, 500, 15);
313 vm.registerNative(sound_areSoundsInaccessible, 500, 16);
316 /*private*/
317 void
318 Sound_as::startProbeTimer()
320 getRoot(owner()).addAdvanceCallback(this);
323 /*private*/
324 void
325 Sound_as::stopProbeTimer()
327 #ifdef GNASH_DEBUG_SOUND_AS
328 log_debug("stopProbeTimer called");
329 #endif
330 getRoot(owner()).removeAdvanceCallback(this);
333 void
334 Sound_as::update()
336 probeAudio();
338 VM& vm = getVM(owner());
340 if (active()) {
341 owner().set_member(getURI(vm, "duration"), getDuration());
342 owner().set_member(getURI(vm, "position"), getPosition());
346 void
347 Sound_as::probeAudio()
350 if (isAttached()) {
351 #ifdef GNASH_DEBUG_SOUND_AS
352 log_debug("Probing audio for end");
353 #endif
355 boost::mutex::scoped_lock lock(_soundCompletedMutex);
356 if (_soundCompleted) {
357 // when _soundCompleted is true we're
358 // NOT attached !
359 _mediaParser.reset(); // no use for this anymore...
360 _inputStream = 0;
361 _soundCompleted = false;
362 stopProbeTimer();
364 // dispatch onSoundComplete
365 callMethod(&owner(), NSV::PROP_ON_SOUND_COMPLETE);
368 else if (_mediaParser) {
369 #ifdef GNASH_DEBUG_SOUND_AS
370 log_debug("Probing audio for start");
371 #endif
373 bool parsingCompleted = _mediaParser->parsingCompleted();
374 try {
375 _inputStream = attachAuxStreamerIfNeeded();
377 catch (const MediaException& e) {
378 assert(!_inputStream);
379 assert(!_audioDecoder.get());
380 log_error(_("Could not create audio decoder: %s"), e.what());
381 _mediaParser.reset(); // no use for this anymore...
382 stopProbeTimer();
383 return;
386 if ( ! _inputStream ) {
387 if ( parsingCompleted ) {
388 log_debug("No audio in Sound input.");
389 stopProbeTimer();
390 _mediaParser.reset(); // no use for this anymore...
391 } else {
392 // keep probing
394 } else {
395 // An audio decoder was constructed, good!
396 assert(_audioDecoder.get());
401 #ifdef GNASH_USE_GC
402 void
403 Sound_as::markReachableResources() const
405 if (_attachedCharacter) {
406 _attachedCharacter->setReachable();
409 #endif // GNASH_USE_GC
411 void
412 Sound_as::markSoundCompleted(bool completed)
414 boost::mutex::scoped_lock lock(_soundCompletedMutex);
415 _soundCompleted=completed;
418 void
419 Sound_as::attachCharacter(DisplayObject* attachTo)
421 _attachedCharacter.reset(new CharacterProxy(attachTo, getRoot(owner())));
424 void
425 Sound_as::attachSound(int si, const std::string& name)
427 soundId = si;
428 soundName = name;
430 VM& vm = getVM(owner());
431 owner().set_member(getURI(vm, "duration"), getDuration());
432 owner().set_member(getURI(vm, "position"), getPosition());
436 long
437 Sound_as::getBytesLoaded()
439 if ( _mediaParser ) {
440 return _mediaParser->getBytesLoaded();
443 return -1;
446 long
447 Sound_as::getBytesTotal()
449 if ( _mediaParser ) {
450 return _mediaParser->getBytesTotal();
453 return -1;
456 void
457 Sound_as::getPan()
459 LOG_ONCE(log_unimpl(__FUNCTION__));
462 void
463 Sound_as::getTransform()
465 LOG_ONCE(log_unimpl(__FUNCTION__));
468 bool
469 Sound_as::getVolume(int& volume)
471 // TODO: check what takes precedence in case we
472 // have both an attached DisplayObject *and*
473 // some other sound...
475 if ( _attachedCharacter ) {
476 //log_debug("Sound has an attached DisplayObject");
477 DisplayObject* ch = _attachedCharacter->get();
478 if (! ch) {
479 log_debug("Character attached to Sound was unloaded and "
480 "couldn't rebind");
481 return false;
483 volume = ch->getVolume();
484 return true;
487 // If we're not attached to a DisplayObject we'll need to query
488 // sound_handler for volume. If we have no sound handler, we
489 // can't do much, so we'll return false
490 if (!_soundHandler) {
491 log_debug("We have no sound handler here...");
492 return false;
495 // Now, we may be controlling a specific sound or
496 // the final output as a whole.
497 // If soundId is -1 we're controlling as a whole
499 if (soundId == -1) {
500 volume = _soundHandler->getFinalVolume();
501 } else {
502 volume = _soundHandler->get_volume(soundId);
505 return true;
508 void
509 Sound_as::loadSound(const std::string& file, bool streaming)
511 if ( ! _mediaHandler || ! _soundHandler ) {
512 log_debug("No media or sound handlers, won't load any sound");
513 return;
516 /// If we are already streaming stop doing so as we'll replace
517 /// the media parser
518 if ( _inputStream ) {
519 _soundHandler->unplugInputStream(_inputStream);
520 _inputStream = 0;
523 /// Delete any media parser being used (make sure we have detached!)
524 _mediaParser.reset();
526 /// Start at offset 0, in case a previous ::start() call
527 /// changed that.
528 _startTime=0;
530 const RunResources& rr = getRunResources(owner());
531 URL url(file, rr.streamProvider().baseURL());
533 const RcInitFile& rcfile = RcInitFile::getDefaultInstance();
535 const StreamProvider& streamProvider = rr.streamProvider();
536 std::auto_ptr<IOChannel> inputStream(streamProvider.getStream(url,
537 rcfile.saveStreamingMedia()));
538 if ( ! inputStream.get() ) {
539 log_error( _("Gnash could not open this url: %s"), url );
540 return;
543 externalSound = true;
544 isStreaming = streaming;
546 _mediaParser.reset(_mediaHandler->createMediaParser(inputStream).release());
547 if ( ! _mediaParser ) {
548 log_error(_("Unable to create parser for Sound at %s"), url);
549 // not necessarely correct, the stream might have been found...
550 return;
553 // TODO: use global _soundbuftime
554 _mediaParser->setBufferTime(60000); // one minute buffer... should be fine
556 if (isStreaming) {
557 startProbeTimer();
559 else {
560 LOG_ONCE(log_unimpl("Non-streaming Sound.loadSound: will behave "
561 "as a streaming one"));
562 // if not streaming, we'll probe on .start()
565 VM& vm = getVM(owner());
566 owner().set_member(getURI(vm, "duration"), getDuration());
567 owner().set_member(getURI(vm, "position"), getPosition());
570 sound::InputStream*
571 Sound_as::attachAuxStreamerIfNeeded()
573 media::AudioInfo* audioInfo = _mediaParser->getAudioInfo();
574 if (!audioInfo) return 0;
576 // the following may throw an exception
577 _audioDecoder.reset(_mediaHandler->createAudioDecoder(*audioInfo).release());
579 // start playing ASAP, a call to ::start will just change _startTime
580 #ifdef GNASH_DEBUG_SOUND_AS
581 log_debug("Attaching the aux streamer");
582 #endif
583 return _soundHandler->attach_aux_streamer(getAudioWrapper, (void*) this);
586 void
587 Sound_as::setPan()
589 LOG_ONCE(log_unimpl(__FUNCTION__));
592 void
593 Sound_as::setTransform()
595 LOG_ONCE(log_unimpl(__FUNCTION__));
598 void
599 Sound_as::setVolume(int volume)
601 // TODO: check what takes precedence in case we
602 // have both an attached DisplayObject *and*
603 // some other sound...
605 if ( _attachedCharacter ) {
606 DisplayObject* ch = _attachedCharacter->get();
607 if ( ! ch ) {
608 log_debug("Character attached to Sound was unloaded and "
609 "couldn't rebind");
610 return;
612 ch->setVolume(volume);
613 return;
616 // If we're not attached to a DisplayObject we'll need to use
617 // sound_handler for volume. If we have no sound handler, we
618 // can't do much, so we'll just return
619 if (!_soundHandler) {
620 return;
623 // Now, we may be controlling a specific sound or
624 // the final output as a whole.
625 // If soundId is -1 we're controlling as a whole
627 if ( soundId == -1 ) {
628 _soundHandler->setFinalVolume(volume);
629 } else {
630 _soundHandler->set_volume(soundId, volume);
634 void
635 Sound_as::start(double secOff, int loops)
637 if ( ! _soundHandler ) {
638 log_error("No sound handler, nothing to start...");
639 return;
642 if (externalSound) {
643 if ( ! _mediaParser ) {
644 log_error("No MediaParser initialized, can't start an external sound");
645 return;
648 if (secOff > 0) {
649 _startTime = secOff * 1000;
650 boost::uint32_t seekms = boost::uint32_t(secOff * 1000);
651 // TODO: boost::mutex::scoped_lock parserLock(_parserMutex);
652 _mediaParser->seek(seekms); // well, we try...
655 if (isStreaming) {
656 IF_VERBOSE_ASCODING_ERRORS(
657 log_aserror(_("Sound.start() has no effect on a streaming Sound"));
659 return;
662 // Save how many loops to do (not when streaming)
663 if (loops > 0) {
664 remainingLoops = loops;
667 // TODO: we should really be waiting for the sound to be fully
668 // loaded before starting to play it (!isStreaming case)
669 startProbeTimer();
671 } else {
672 unsigned int inPoint = 0;
674 if ( secOff > 0 ) {
675 inPoint = (secOff*44100);
678 log_debug("Sound.start: secOff:%d", secOff);
680 _soundHandler->startSound(
681 soundId,
682 loops,
683 0, // envelopes
684 true, // allow multiple instances (checked)
685 inPoint
687 startProbeTimer();
691 void
692 Sound_as::stop(int si)
694 if ( ! _soundHandler ) {
695 log_error("No sound handler, nothing to stop...");
696 return;
699 // stop the sound
700 if (si < 0) {
701 if (externalSound) {
702 if ( _inputStream ) {
703 _soundHandler->unplugInputStream(_inputStream);
704 _inputStream=0;
706 } else {
707 _soundHandler->stop_sound(soundId);
709 } else {
710 _soundHandler->stop_sound(si);
714 size_t
715 Sound_as::getDuration() const
717 if ( ! _soundHandler ) {
718 log_error("No sound handler, can't check duration...");
719 return 0;
722 // If this is a event sound get the info from the soundhandler
723 if (!externalSound) {
724 return _soundHandler->get_duration(soundId);
727 // If we have a media parser (we'd do for an externalSound)
728 // try fetching duration from it
729 if ( _mediaParser ) {
730 media::AudioInfo* info = _mediaParser->getAudioInfo();
731 if ( info ) {
732 return info->duration;
736 return 0;
739 size_t
740 Sound_as::getPosition() const
742 if (!_soundHandler) {
743 log_error("No sound handler, can't check position (we're "
744 "likely not playing anyway)...");
745 return 0;
748 // If this is a event sound get the info from the soundhandler
749 if (!externalSound) {
750 return _soundHandler->tell(soundId);
753 if (_mediaParser) {
754 boost::uint64_t ts;
755 if ( _mediaParser->nextAudioFrameTimestamp(ts) ) {
756 return ts;
760 return 0;
765 unsigned int
766 Sound_as::getAudio(boost::int16_t* samples, unsigned int nSamples, bool& atEOF)
768 boost::uint8_t* stream = reinterpret_cast<boost::uint8_t*>(samples);
769 int len = nSamples*2;
771 //GNASH_REPORT_FUNCTION;
773 while (len) {
774 if ( ! _leftOverData ) {
775 bool parsingComplete = _mediaParser->parsingCompleted(); // check *before* calling nextAudioFrame
776 std::auto_ptr<media::EncodedAudioFrame> frame = _mediaParser->nextAudioFrame();
777 if ( ! frame.get() ) {
778 // just wait some more if parsing isn't complete yet
779 if ( ! parsingComplete ) {
780 //log_debug("Parsing not complete and no more audio frames in input, try again later");
781 break;
784 // or detach and stop here...
785 // (should really honour loopings if any,
786 // but that should be only done for non-streaming sound!)
787 //log_debug("Parsing complete and no more audio frames in input, detaching");
789 markSoundCompleted(true);
791 // Setting atEOF to true will detach us.
792 // We should change _inputStream, but need thread safety!
793 // So on probeAudio, if _soundCompleted is set
794 // we'll consider ourselves detached already and set
795 // _inputStream to zero
796 atEOF=true;
797 return nSamples-(len/2);
800 // if we've been asked to start at a specific time, skip
801 // any frame with earlier timestamp
802 if ( frame->timestamp < _startTime ) {
803 //log_debug("This audio frame timestamp (%d) < requested start time (%d)", frame->timestamp, _startTime);
804 continue;
807 _leftOverData.reset( _audioDecoder->decode(*frame, _leftOverSize) );
808 _leftOverPtr = _leftOverData.get();
809 if ( ! _leftOverData ) {
810 log_error("No samples decoded from input of %d bytes", frame->dataSize);
811 continue;
814 //log_debug(" decoded %d bytes of audio", _leftOverSize);
817 assert( !(_leftOverSize%2) );
819 int n = std::min<int>(_leftOverSize, len);
820 //log_debug(" consuming %d bytes of decoded audio", n);
822 std::copy(_leftOverPtr, _leftOverPtr+n, stream);
824 stream += n;
825 _leftOverPtr += n;
826 _leftOverSize -= n;
827 len -= n;
829 if (_leftOverSize == 0) {
830 _leftOverData.reset();
831 _leftOverPtr = 0;
836 // drop any queued video frame
837 while (_mediaParser->nextVideoFrame().get()) {};
839 atEOF=false;
840 return nSamples-(len/2);
843 // audio callback is running in sound handler thread
844 unsigned int
845 Sound_as::getAudioWrapper(void* owner, boost::int16_t* samples,
846 unsigned int nSamples, bool& atEOF)
848 Sound_as* so = static_cast<Sound_as*>(owner);
849 return so->getAudio(samples, nSamples, atEOF);
853 namespace {
855 void
856 attachSoundInterface(as_object& o)
859 int flags = PropFlags::dontEnum |
860 PropFlags::dontDelete |
861 PropFlags::readOnly;
863 VM& vm = getVM(o);
864 o.init_member("getPan", vm.getNative(500, 0), flags);
865 o.init_member("getTransform", vm.getNative(500, 1), flags);
866 o.init_member("getVolume", vm.getNative(500, 2), flags);
867 o.init_member("setPan", vm.getNative(500, 3), flags);
868 o.init_member("setTransform", vm.getNative(500, 4), flags);
869 o.init_member("setVolume", vm.getNative(500, 5), flags);
870 o.init_member("stop", vm.getNative(500, 6), flags);
871 o.init_member("attachSound", vm.getNative(500, 7), flags);
872 o.init_member("start", vm.getNative(500, 8), flags);
874 int flagsn6 = flags | PropFlags::onlySWF6Up;
876 o.init_member("getDuration", vm.getNative(500, 9), flagsn6);
877 o.init_member("setDuration", vm.getNative(500, 10), flagsn6);
878 o.init_member("getPosition", vm.getNative(500, 11), flagsn6);
879 o.init_member("setPosition", vm.getNative(500, 12), flagsn6);
880 o.init_member("loadSound", vm.getNative(500, 13), flagsn6);
881 o.init_member("getBytesLoaded", vm.getNative(500, 14), flagsn6);
882 o.init_member("getBytesTotal", vm.getNative(500, 15), flagsn6);
884 int flagsn9 = PropFlags::dontEnum |
885 PropFlags::dontDelete |
886 PropFlags::readOnly |
887 PropFlags::onlySWF9Up;
889 o.init_member("areSoundsInaccessible", vm.getNative(500, 16), flagsn9);
891 int fl_hp = PropFlags::dontEnum | PropFlags::dontDelete;
893 o.init_property("checkPolicyFile", &checkPolicyFile_getset,
894 &checkPolicyFile_getset, fl_hp);
898 as_value
899 sound_new(const fn_call& fn)
901 as_object* so = fn.this_ptr;
902 Sound_as* s(new Sound_as(so));
903 so->setRelay(s);
905 if (fn.nargs) {
906 IF_VERBOSE_ASCODING_ERRORS(
907 if (fn.nargs > 1) {
908 std::stringstream ss; fn.dump_args(ss);
909 log_aserror("new Sound(%d) : args after first one ignored",
910 ss.str());
915 const as_value& arg0 = fn.arg(0);
917 if (!arg0.is_null() && !arg0.is_undefined()) {
919 as_object* obj = toObject(arg0, getVM(fn));
920 DisplayObject* ch = get<DisplayObject>(obj);
921 IF_VERBOSE_ASCODING_ERRORS(
922 if (!ch) {
923 std::stringstream ss; fn.dump_args(ss);
924 log_aserror("new Sound(%s) : first argument isn't null "
925 "or undefined, and isn't a DisplayObject. "
926 "We'll take as an invalid DisplayObject ref.",
927 ss.str());
931 s->attachCharacter(ch);
935 return as_value();
938 as_value
939 sound_start(const fn_call& fn)
941 IF_VERBOSE_ACTION (
942 log_action(_("-- start sound"));
944 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
945 int loop = 0;
946 double secondOffset = 0;
948 if (fn.nargs > 0) {
949 secondOffset = toNumber(fn.arg(0), getVM(fn));
951 if (fn.nargs > 1) {
952 loop = (int) toNumber(fn.arg(1), getVM(fn)) - 1;
954 // -1 means infinite playing of sound
955 // sanity check
956 loop = loop < 0 ? -1 : loop;
959 so->start(secondOffset, loop);
960 return as_value();
963 as_value
964 sound_stop(const fn_call& fn)
966 IF_VERBOSE_ACTION (
967 log_action(_("-- stop sound "));
969 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
971 int si = -1;
973 if (fn.nargs > 0) {
974 const std::string& name = fn.arg(0).to_string();
976 // check the import.
977 const movie_definition* def = fn.callerDef;
978 assert(def);
980 const boost::uint16_t id = def->exportID(name);
981 if (!id) {
982 IF_VERBOSE_MALFORMED_SWF(
983 log_swferror(_("No such export '%s'"),
984 name);
986 return as_value();
989 sound_sample* ss = def->get_sound_sample(id);
990 if (!ss) {
991 IF_VERBOSE_MALFORMED_SWF(
992 log_swferror(_("Export '%s'is not a sound"), name);
994 return as_value();
997 si = ss->m_sound_handler_id;
1000 so->stop(si);
1001 return as_value();
1004 as_value
1005 sound_attachsound(const fn_call& fn)
1007 IF_VERBOSE_ACTION(
1008 log_action(_("-- attach sound"));
1011 if (fn.nargs < 1) {
1012 IF_VERBOSE_ASCODING_ERRORS(
1013 log_aserror(_("attach sound needs one argument"));
1015 return as_value();
1018 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1020 const std::string& name = fn.arg(0).to_string();
1021 if (name.empty()) {
1022 IF_VERBOSE_ASCODING_ERRORS(
1023 log_aserror(_("attachSound needs a non-empty string"));
1025 return as_value();
1028 // check the import.
1029 // NOTE: we should be checking in the SWF containing the calling code
1030 // (see 'winter bell' from orisinal morning sunshine for a testcase)
1031 const movie_definition* def = fn.callerDef;
1032 assert(def);
1035 const boost::uint16_t id = def->exportID(name);
1036 if (!id) {
1037 IF_VERBOSE_MALFORMED_SWF(
1038 log_swferror(_("No such export '%s'"),
1039 name);
1041 return as_value();
1044 sound_sample* ss = def->get_sound_sample(id);
1045 if (!ss) {
1046 IF_VERBOSE_MALFORMED_SWF(
1047 log_swferror(_("Export '%s'is not a sound"), name);
1049 return as_value();
1052 const int si = ss->m_sound_handler_id;
1054 // sanity check
1055 assert(si >= 0);
1056 so->attachSound(si, name);
1058 return as_value();
1061 as_value
1062 sound_getbytesloaded(const fn_call& fn)
1064 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1065 long loaded = so->getBytesLoaded();
1066 if (loaded < 0) return as_value();
1067 return as_value(loaded);
1070 as_value
1071 sound_getbytestotal(const fn_call& fn)
1073 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1074 long total = so->getBytesTotal();
1075 if (total < 0) return as_value();
1076 return as_value(total);
1079 as_value
1080 sound_getpan(const fn_call& /*fn*/)
1082 LOG_ONCE( log_unimpl ("Sound.getPan()") );
1083 return as_value();
1086 as_value
1087 sound_getDuration(const fn_call& fn)
1089 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1090 if (!so->active()) return as_value();
1091 return as_value(so->getDuration());
1094 as_value
1095 sound_setDuration(const fn_call& /*fn*/)
1097 LOG_ONCE( log_unimpl ("Sound.setDuration()") );
1098 return as_value();
1101 as_value
1102 sound_getPosition(const fn_call& fn)
1104 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1105 if (!so->active()) return as_value();
1106 return as_value(so->getPosition());
1109 as_value
1110 sound_setPosition(const fn_call& /*fn*/)
1112 LOG_ONCE( log_unimpl ("Sound.setPosition()") );
1113 return as_value();
1116 as_value
1117 sound_gettransform(const fn_call& /*fn*/)
1119 LOG_ONCE( log_unimpl ("Sound.getTransform()") );
1120 return as_value();
1123 as_value
1124 sound_getvolume(const fn_call& fn)
1127 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1129 if ( fn.nargs ) {
1130 IF_VERBOSE_ASCODING_ERRORS(
1131 std::stringstream ss; fn.dump_args(ss);
1132 log_aserror("Sound.getVolume(%s) : arguments ignored");
1136 int volume;
1137 if (so->getVolume(volume)) return as_value(volume);
1138 return as_value();
1141 as_value
1142 sound_loadsound(const fn_call& fn)
1144 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1146 if (!fn.nargs) {
1147 IF_VERBOSE_ASCODING_ERRORS(
1148 log_aserror(_("Sound.loadSound() needs at least 1 argument"));
1150 return as_value();
1153 std::string url = fn.arg(0).to_string();
1155 bool streaming = false;
1156 if ( fn.nargs > 1 ) {
1157 streaming = toBool(fn.arg(1), getVM(fn));
1159 IF_VERBOSE_ASCODING_ERRORS(
1160 if ( fn.nargs > 2 )
1162 std::stringstream ss; fn.dump_args(ss);
1163 log_aserror(_("Sound.loadSound(%s): arguments after first 2 "
1164 "discarded"), ss.str());
1169 so->loadSound(url, streaming);
1171 return as_value();
1174 as_value
1175 sound_setpan(const fn_call& /*fn*/)
1177 LOG_ONCE( log_unimpl ("Sound.setPan()") );
1178 return as_value();
1181 as_value
1182 sound_settransform(const fn_call& /*fn*/)
1184 LOG_ONCE( log_unimpl ("Sound.setTransform()") );
1185 return as_value();
1188 as_value
1189 sound_setvolume(const fn_call& fn)
1191 if (fn.nargs < 1) {
1192 IF_VERBOSE_ASCODING_ERRORS(
1193 log_aserror(_("set volume of sound needs one argument"));
1195 return as_value();
1198 Sound_as* so = ensure<ThisIsNative<Sound_as> >(fn);
1199 int volume = (int) toNumber(fn.arg(0), getVM(fn));
1201 so->setVolume(volume);
1202 return as_value();
1205 as_value
1206 checkPolicyFile_getset(const fn_call& /*fn*/)
1208 LOG_ONCE( log_unimpl ("Sound.checkPolicyFile") );
1209 return as_value();
1212 as_value
1213 sound_areSoundsInaccessible(const fn_call& /*fn*/)
1215 // TODO: I guess this would have to do with permissions (crossdomain stuff)
1216 // more then capability.
1217 // See http://www.actionscript.org/forums/showthread.php3?t=160028
1219 // naive test shows this always being undefined..
1221 LOG_ONCE( log_unimpl ("Sound.areSoundsInaccessible()") );
1222 return as_value();
1225 } // anonymous namespace
1226 } // gnash namespace
1228 // local Variables:
1229 // mode: C++
1230 // indent-tabs-mode: nil
1231 // End: