Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / cocoa / sound.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/sound.mm
3 // Purpose:     wxSound class implementation: optional
4 // Authors:     David Elliott, Ryan Norton
5 // Modified by: 
6 // Created:     2004-10-02
7 // Copyright:   (c) 2004 David Elliott, Ryan Norton
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12 #if wxUSE_SOUND
13
14 #ifndef WX_PRECOMP
15     #include "wx/app.h"
16     #include "wx/log.h"
17 #endif //ndef WX_PRECOMP
18 #include "wx/sound.h"
19 #include "wx/evtloop.h"
20
21 #include "wx/cocoa/autorelease.h"
22 #include "wx/cocoa/string.h"
23 #include "wx/cocoa/log.h"
24
25 #include "wx/cocoa/objc/objc_uniquifying.h"
26
27 #import <AppKit/NSSound.h>
28 #import <Foundation/NSData.h>
29
30 static WX_NSSound s_currentSound = nil;
31 static bool s_loopCurrentSound = false;
32
33 // ========================================================================
34 // wxNSSoundDelegate
35 // ========================================================================
36 @interface wxNSSoundDelegate : NSObject
37 {
38 }
39
40 // Delegate methods
41 - (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying;
42 @end // interface wxNSSoundDelegate : NSObject
43 WX_DECLARE_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
44
45 @implementation wxNSSoundDelegate : NSObject
46
47 - (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying
48 {
49     // If s_currentSound is not us then some other sound has played.
50     // We can safely ignore this as s_currentSound will have been released
51     // before being set to a different value.
52     if(s_currentSound!=theSound)
53         return;
54     // If playing finished successfully and we are looping, play again.
55     if (finishedPlaying && s_loopCurrentSound)
56         [s_currentSound play];
57     // Otherwise we are done, there is no more current sound playing.
58     else
59     {
60         if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("[wxNSSoundDelegate -sound:didFinishPlaying:] [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
61         [s_currentSound release];
62         s_currentSound = nil;
63         // Make sure we get out of any modal event loops immediately.
64         // NOTE: When the sound finishes playing Cocoa normally does have
65         // an event so this is probably not necessary.
66         wxTheApp->WakeUpIdle();
67     }
68 }
69
70 @end // wxNSSoundDelegate
71 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
72
73 const wxObjcAutoRefFromAlloc<struct objc_object*> wxSound::sm_cocoaDelegate = [[WX_GET_OBJC_CLASS(wxNSSoundDelegate) alloc] init];
74
75 // ------------------------------------------------------------------
76 //          wxSound
77 // ------------------------------------------------------------------
78
79 wxSound::wxSound(const wxSound& sound)
80 :   m_cocoaNSSound(sound.m_cocoaNSSound)
81 {
82     [m_cocoaNSSound retain];
83 }
84
85 wxSound::~wxSound()
86 {
87     SetNSSound(nil);
88 }
89
90 bool wxSound::Create(const wxString& fileName, bool isResource)
91 {
92     wxAutoNSAutoreleasePool thePool;
93
94     if (isResource)
95         SetNSSound([NSSound soundNamed:wxNSStringWithWxString(fileName)]);
96     else
97     {
98         SetNSSound([[NSSound alloc] initWithContentsOfFile:wxNSStringWithWxString(fileName) byReference:YES]);
99         [m_cocoaNSSound release];
100     }
101
102     return m_cocoaNSSound;
103 }
104
105 bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData)
106 {
107     NSData* theData;
108     if(copyData)
109         theData = [[NSData alloc] initWithBytes:const_cast<wxUint8*>(data) length:length];
110     else
111         theData = [[NSData alloc] initWithBytesNoCopy:const_cast<wxUint8*>(data) length:length];
112     SetNSSound([[NSSound alloc] initWithData:theData]);
113     [m_cocoaNSSound release];
114     [theData release];
115     return m_cocoaNSSound;
116 }
117
118 void wxSound::SetNSSound(WX_NSSound cocoaNSSound)
119 {
120     bool need_debug = cocoaNSSound || m_cocoaNSSound;
121     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]);
122     [cocoaNSSound retain];
123     [m_cocoaNSSound release];
124     m_cocoaNSSound = cocoaNSSound;
125     [m_cocoaNSSound setDelegate:sm_cocoaDelegate];
126     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::SetNSSound [cocoaNSSound=%p retainCount]=%d (just retained)"),this,cocoaNSSound,[cocoaNSSound retainCount]);
127 }
128
129 bool wxSound::DoPlay(unsigned flags) const
130 {
131     Stop(); // this releases and nils s_currentSound
132
133     // NOTE: We set s_currentSound to the current sound in all cases so that
134     // functions like Stop and IsPlaying can work.  It is NOT necessary for
135     // the NSSound to be retained by us for it to continue playing.  Cocoa
136     // retains the NSSound when it is played and relases it when finished.
137
138     wxASSERT(!s_currentSound);
139     s_currentSound = [m_cocoaNSSound retain];
140     wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::DoPlay [s_currentSound=%p retainCount]=%d (just retained)"),this,s_currentSound,[s_currentSound retainCount]);
141     s_loopCurrentSound = (flags & wxSOUND_LOOP) == wxSOUND_LOOP;
142
143     if (flags & wxSOUND_ASYNC)
144         return [m_cocoaNSSound play];
145     else
146     {
147         wxASSERT_MSG(!s_loopCurrentSound,wxT("It is silly to block waiting for a looping sound to finish.  Disabling looping"));
148         // actually, it'd probably work although it's kind of stupid to
149         // block here waiting for a sound that's never going to end.
150         // Granted Stop() could be called somehow, but again, silly.
151         s_loopCurrentSound = false;
152
153         if(![m_cocoaNSSound play])
154             return false;
155
156         // Process events until the delegate sets s_currentSound to nil
157         // and/or a different sound plays.
158         while (s_currentSound==m_cocoaNSSound)
159             wxEventLoop::GetActive()->Dispatch();
160         return true;
161     }
162 }
163
164 bool wxSound::IsPlaying()
165 {
166     // Normally you can send a message to a nil object and it will return
167     // nil.  That behaviour would probably be okay here but in general it's
168     // not recommended to send a message to a nil object if the return
169     // value is not an object.  Better safe than sorry.
170     if(s_currentSound)
171         return [s_currentSound isPlaying];
172     else
173         return false;
174 }
175
176 void wxSound::Stop()
177 {
178     // Clear the looping flag so that if the sound finishes playing before
179     // stop is called the sound will already be released and niled.
180     s_loopCurrentSound = false;
181     [s_currentSound stop];
182     /* It's possible that sound:didFinishPlaying: was called and released
183        s_currentSound but it doesn't matter since it will have set it to nil */
184     if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound::Stop [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
185     [s_currentSound release];
186     s_currentSound = nil;
187 }
188
189 #endif //wxUSE_SOUND