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