Rewrote wxSound:
* Get rid of #if wxUSE_SOUND from header. wx/sound.h checks this already. * Get rid of pragma interface/implementation. Apple GCC dislikes them anyway. * Allow source file to use precompiled headers (wx/wxprec.h) * Include only needed AppKit/Foundation headers, not AppKit/AppKit.h. * Implement simple constructors inline in header. * Get rid of m_sndname and m_waveLength instance variables. They aren't used. * Add copy constructor (why not). * Move implementation of byte-array constructor into LoadWAV for consistency with UNIX wxSound. * LoadWAV (what was in the constructor) now properly allocs, inits, and releases NSData. The old code for this was not valid. * Rename lastSound to s_currentSound. * Rename isLastSoundLooping to s_loopCurrentSound. * Ignore the sound:didFinishPlaying: delegate message if it is received for an NSSound other than s_currentSound. * Create should not Stop the current sound. * Don't use NSBundle to get a resource sound but use [NSSound soundNamed:] which will include system sounds. * Playing a sound synchronously uses wxEventLoop::Dispatch which will really block (not spin the CPU like Yield). The sound is considered finished playing when s_currentSound is set to something else. In order to make sure we don't get stuck in this event loop the delegate calls wxApp::WakeUpIdle if it releases s_currentSound. * Have IsPlaying() check to make sure s_currentSound is not nil since only messages returning another object or void are allowed to be sent to nil objects. Changes involving retain/release * Get rid of comment about tricky API, it's not. * Get rid of isLastSoundInScope. Cocoa has proper reference counting. * Add SetNSSound which, like the rest of wxCocoa, retains/releases appropriately, sets the delegate, and logs when WXTRACE=COCOA_RetainRelease. * Destructor does SetNSSound(nil) which will always release the NSSound. Create and LoadWAV use SetNSSound method like the rest of wxCocoa. * Make the delegate always release s_currentSound if not (or if done) looping. DoPlay sets s_currentSound to m_cocoaNSSound after retaining it so that the delegate can always safely release it. * Stop, like everything else, does not need check of isLastSoundInScope git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@30043 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -2,32 +2,33 @@
|
||||
// Name: sound.h
|
||||
// Purpose: wxSound class (loads and plays short Windows .wav files).
|
||||
// Optional on non-Windows platforms.
|
||||
// Author: Ryan Norton
|
||||
// Modified by:
|
||||
// Authors: David Elliott, Ryan Norton
|
||||
// Modified by:
|
||||
// Created: 2004-10-02
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) Ryan Norton
|
||||
// Copyright: (c) 2004 David Elliott, Ryan Norton
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_COCOA_SOUND_H_
|
||||
#define _WX_COCOA_SOUND_H_
|
||||
|
||||
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
|
||||
#pragma interface "sound.h"
|
||||
#endif
|
||||
|
||||
#if wxUSE_SOUND
|
||||
|
||||
#include "wx/object.h"
|
||||
#include "wx/cocoa/ObjcRef.h"
|
||||
|
||||
class WXDLLEXPORT wxSound : public wxSoundBase
|
||||
{
|
||||
public:
|
||||
wxSound();
|
||||
wxSound(const wxString& fileName, bool isResource = false);
|
||||
wxSound(int size, const wxByte* data);
|
||||
wxSound()
|
||||
: m_cocoaNSSound(NULL)
|
||||
{}
|
||||
wxSound(const wxString& fileName, bool isResource = false)
|
||||
: m_cocoaNSSound(NULL)
|
||||
{ Create(fileName, isResource); }
|
||||
wxSound(int size, const wxByte* data)
|
||||
: m_cocoaNSSound(NULL)
|
||||
{ LoadWAV(data,size,true); }
|
||||
wxSound(const wxSound& sound); // why not?
|
||||
~wxSound();
|
||||
|
||||
public:
|
||||
@@ -37,18 +38,15 @@ public:
|
||||
static void Stop();
|
||||
static bool IsPlaying();
|
||||
|
||||
void SetNSSound(WX_NSSound cocoaNSSound);
|
||||
inline WX_NSSound GetNSSound()
|
||||
{ return m_cocoaNSSound; }
|
||||
protected:
|
||||
bool DoPlay(unsigned flags) const;
|
||||
|
||||
bool LoadWAV(const wxUint8 *data, size_t length, bool copyData);
|
||||
private:
|
||||
WX_NSSound m_cocoaNSSound; //NSSound handle
|
||||
wxString m_sndname; //file path
|
||||
int m_waveLength; //size of file in memory mode
|
||||
WX_NSSound m_cocoaNSSound;
|
||||
static const wxObjcAutoRefFromAlloc<struct objc_object *> sm_cocoaDelegate;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
// _WX_COCOA_SOUND_H_
|
||||
#endif //ndef _WX_COCOA_SOUND_H_
|
||||
|
@@ -1,39 +1,33 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: sound.cpp
|
||||
// Purpose: wxSound class implementation: optional
|
||||
// Author: Ryan Norton
|
||||
// Authors: David Elliott, Ryan Norton
|
||||
// Modified by:
|
||||
// Created: 2004-10-02
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) Ryan Norton
|
||||
// Copyright: (c) 2004 David Elliott, Ryan Norton
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __GNUG__
|
||||
#pragma implementation "sound.h"
|
||||
#endif
|
||||
|
||||
#include "wx/object.h"
|
||||
#include "wx/string.h"
|
||||
#include "wx/sound.h"
|
||||
|
||||
#include "wx/wxprec.h"
|
||||
#if wxUSE_SOUND
|
||||
|
||||
#include "wx/app.h"
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/app.h"
|
||||
#include "wx/log.h"
|
||||
#endif //ndef WX_PRECOMP
|
||||
#include "wx/sound.h"
|
||||
#include "wx/evtloop.h"
|
||||
|
||||
#include "wx/cocoa/autorelease.h"
|
||||
#include "wx/cocoa/string.h"
|
||||
#include "wx/cocoa/log.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <AppKit/NSSound.h>
|
||||
#import <Foundation/NSData.h>
|
||||
|
||||
//
|
||||
// NB: Vaclav's new wxSound API is really tricky -
|
||||
// Basically, we need to make sure that if the wxSound
|
||||
// object is still in scope we don't release it's NSSound
|
||||
//
|
||||
|
||||
WX_NSSound lastSound=NULL;
|
||||
bool isLastSoundLooping = false;
|
||||
bool isLastSoundInScope = false;
|
||||
static WX_NSSound s_currentSound = nil;
|
||||
static bool s_loopCurrentSound = false;
|
||||
|
||||
// ========================================================================
|
||||
// wxNSSoundDelegate
|
||||
@@ -43,18 +37,31 @@ bool isLastSoundInScope = false;
|
||||
}
|
||||
|
||||
// Delegate methods
|
||||
- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)bOK;
|
||||
- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying;
|
||||
@end // interface wxNSSoundDelegate : NSObject
|
||||
|
||||
@implementation wxNSSoundDelegate : NSObject
|
||||
|
||||
- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)bOK
|
||||
- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying
|
||||
{
|
||||
if (bOK && isLastSoundLooping)
|
||||
[lastSound play];
|
||||
else if (!isLastSoundInScope)
|
||||
// If s_currentSound is not us then some other sound has played.
|
||||
// We can safely ignore this as s_currentSound will have been released
|
||||
// before being set to a different value.
|
||||
if(s_currentSound!=theSound)
|
||||
return;
|
||||
// If playing finished successfully and we are looping, play again.
|
||||
if (finishedPlaying && s_loopCurrentSound)
|
||||
[s_currentSound play];
|
||||
// Otherwise we are done, there is no more current sound playing.
|
||||
else
|
||||
{
|
||||
[lastSound release];
|
||||
if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("[wxNSSoundDelegate -sound:didFinishPlaying:] [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
|
||||
[s_currentSound release];
|
||||
s_currentSound = nil;
|
||||
// Make sure we get out of any modal event loops immediately.
|
||||
// NOTE: When the sound finishes playing Cocoa normally does have
|
||||
// an event so this is probably not necessary.
|
||||
wxTheApp->WakeUpIdle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,109 +73,114 @@ const wxObjcAutoRefFromAlloc<struct objc_object*> wxSound::sm_cocoaDelegate = [[
|
||||
// wxSound
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
wxSound::wxSound()
|
||||
: m_cocoaNSSound(nil)
|
||||
, m_waveLength(0)
|
||||
wxSound::wxSound(const wxSound& sound)
|
||||
: m_cocoaNSSound(sound.m_cocoaNSSound)
|
||||
{
|
||||
}
|
||||
|
||||
wxSound::wxSound(const wxString& sFileName, bool isResource)
|
||||
: m_cocoaNSSound(nil)
|
||||
, m_waveLength(0)
|
||||
{
|
||||
Create(sFileName, isResource);
|
||||
}
|
||||
|
||||
wxSound::wxSound(int size, const wxByte* data)
|
||||
: m_cocoaNSSound(nil)
|
||||
, m_waveLength(size)
|
||||
{
|
||||
NSData* theData = [[NSData alloc] dataWithBytesNoCopy:(void*)data length:size];
|
||||
m_cocoaNSSound = [[NSSound alloc] initWithData:theData];
|
||||
|
||||
[m_cocoaNSSound retain];
|
||||
}
|
||||
|
||||
wxSound::~wxSound()
|
||||
{
|
||||
if (m_cocoaNSSound != lastSound)
|
||||
{
|
||||
[m_cocoaNSSound release];
|
||||
}
|
||||
else
|
||||
isLastSoundInScope = false;
|
||||
SetNSSound(nil);
|
||||
}
|
||||
|
||||
bool wxSound::Create(const wxString& fileName, bool isResource)
|
||||
{
|
||||
wxAutoNSAutoreleasePool thePool;
|
||||
|
||||
Stop();
|
||||
|
||||
if (isResource)
|
||||
{
|
||||
//oftype could be @"snd" @"wav" or @"aiff"; nil or @"" autodetects (?)
|
||||
m_cocoaNSSound = [[NSSound alloc]
|
||||
initWithContentsOfFile:[[NSBundle mainBundle]
|
||||
pathForResource:wxNSStringWithWxString(fileName)
|
||||
ofType:nil]
|
||||
byReference:YES];
|
||||
}
|
||||
SetNSSound([NSSound soundNamed:wxNSStringWithWxString(fileName)]);
|
||||
else
|
||||
m_cocoaNSSound = [[NSSound alloc] initWithContentsOfFile:wxNSStringWithWxString(fileName) byReference:YES];
|
||||
{
|
||||
SetNSSound([[NSSound alloc] initWithContentsOfFile:wxNSStringWithWxString(fileName) byReference:YES]);
|
||||
[m_cocoaNSSound release];
|
||||
}
|
||||
|
||||
m_sndname = fileName;
|
||||
return m_cocoaNSSound;
|
||||
}
|
||||
|
||||
bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData)
|
||||
{
|
||||
NSData* theData;
|
||||
if(copyData)
|
||||
theData = [[NSData alloc] initWithBytes:const_cast<wxUint8*>(data) length:length];
|
||||
else
|
||||
theData = [[NSData alloc] initWithBytesNoCopy:const_cast<wxUint8*>(data) length:length];
|
||||
SetNSSound([[NSSound alloc] initWithData:theData]);
|
||||
[m_cocoaNSSound release];
|
||||
[theData release];
|
||||
return m_cocoaNSSound;
|
||||
}
|
||||
|
||||
void wxSound::SetNSSound(WX_NSSound cocoaNSSound)
|
||||
{
|
||||
bool need_debug = cocoaNSSound || m_cocoaNSSound;
|
||||
if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::SetNSSound [m_cocoaNSSound=%p retainCount]=%d (about to release)"),this,m_cocoaNSSound,[m_cocoaNSSound retainCount]);
|
||||
[cocoaNSSound retain];
|
||||
[m_cocoaNSSound release];
|
||||
m_cocoaNSSound = cocoaNSSound;
|
||||
[m_cocoaNSSound setDelegate:sm_cocoaDelegate];
|
||||
if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::SetNSSound [cocoaNSSound=%p retainCount]=%d (just retained)"),this,cocoaNSSound,[cocoaNSSound retainCount]);
|
||||
}
|
||||
|
||||
bool wxSound::DoPlay(unsigned flags) const
|
||||
{
|
||||
wxASSERT_MSG(!( (flags & wxSOUND_SYNC) && (flags & wxSOUND_LOOP)),
|
||||
wxT("Invalid flag combination passed to wxSound::Play"));
|
||||
Stop(); // this releases and nils s_currentSound
|
||||
|
||||
Stop();
|
||||
// NOTE: We set s_currentSound to the current sound in all cases so that
|
||||
// functions like Stop and IsPlaying can work. It is NOT necessary for
|
||||
// the NSSound to be retained by us for it to continue playing. Cocoa
|
||||
// retains the NSSound when it is played and relases it when finished.
|
||||
|
||||
wxASSERT(!s_currentSound);
|
||||
s_currentSound = [m_cocoaNSSound retain];
|
||||
wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::DoPlay [s_currentSound=%p retainCount]=%d (just retained)"),this,s_currentSound,[s_currentSound retainCount]);
|
||||
s_loopCurrentSound = (flags & wxSOUND_LOOP) == wxSOUND_LOOP;
|
||||
|
||||
if (flags & wxSOUND_ASYNC)
|
||||
{
|
||||
lastSound = m_cocoaNSSound;
|
||||
isLastSoundLooping = (flags & wxSOUND_LOOP) == wxSOUND_LOOP;
|
||||
isLastSoundInScope = true;
|
||||
[m_cocoaNSSound setDelegate:sm_cocoaDelegate];
|
||||
return [m_cocoaNSSound play];
|
||||
}
|
||||
else
|
||||
{
|
||||
[m_cocoaNSSound setDelegate:nil];
|
||||
wxASSERT_MSG(!s_loopCurrentSound,wxT("It is silly to block waiting for a looping sound to finish. Disabling looping"));
|
||||
// actually, it'd probably work although it's kind of stupid to
|
||||
// block here waiting for a sound that's never going to end.
|
||||
// Granted Stop() could be called somehow, but again, silly.
|
||||
s_loopCurrentSound = false;
|
||||
|
||||
//play until done
|
||||
bool bOK = [m_cocoaNSSound play];
|
||||
if(![m_cocoaNSSound play])
|
||||
return false;
|
||||
|
||||
while ([m_cocoaNSSound isPlaying])
|
||||
{
|
||||
wxTheApp->Yield(false);
|
||||
}
|
||||
return bOK;
|
||||
// Process events until the delegate sets s_currentSound to nil
|
||||
// and/or a different sound plays.
|
||||
while (s_currentSound==m_cocoaNSSound)
|
||||
wxEventLoop::GetActive()->Dispatch();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool wxSound::IsPlaying()
|
||||
{
|
||||
return [lastSound isPlaying];
|
||||
// Normally you can send a message to a nil object and it will return
|
||||
// nil. That behavior would probably be okay here but in general it's
|
||||
// not recommended to send a message to a nil object if the return
|
||||
// value is not an object. Better safe than sorry.
|
||||
if(s_currentSound)
|
||||
return [s_currentSound isPlaying];
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void wxSound::Stop()
|
||||
{
|
||||
if (isLastSoundInScope)
|
||||
{
|
||||
isLastSoundInScope = false;
|
||||
|
||||
//remember that even though we're
|
||||
//programatically stopping it, the
|
||||
//delegate will still be called -
|
||||
//so it will free the memory here
|
||||
[((NSSound*&)lastSound) stop];
|
||||
}
|
||||
|
||||
lastSound = nil;
|
||||
// Clear the looping flag so that if the sound finishes playing before
|
||||
// stop is called the sound will already be released and niled.
|
||||
s_loopCurrentSound = false;
|
||||
[s_currentSound stop];
|
||||
/* It's possible that sound:didFinishPlaying: was called and released
|
||||
s_currentSound but it doesn't matter since it will have set it to nil */
|
||||
if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound::Stop [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
|
||||
[s_currentSound release];
|
||||
s_currentSound = nil;
|
||||
}
|
||||
|
||||
#endif //wxUSE_SOUND
|
||||
|
Reference in New Issue
Block a user