]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/sound.mm
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / cocoa / sound.mm
CommitLineData
0c0be4a3 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/cocoa/sound.mm
0c0be4a3 3// Purpose: wxSound class implementation: optional
85c9f98b 4// Authors: David Elliott, Ryan Norton
0c0be4a3
RN
5// Modified by:
6// Created: 2004-10-02
85c9f98b 7// Copyright: (c) 2004 David Elliott, Ryan Norton
4fca6ee1 8// Licence: wxWindows licence
0c0be4a3 9/////////////////////////////////////////////////////////////////////////////
dcb68102 10
85c9f98b
DE
11#include "wx/wxprec.h"
12#if wxUSE_SOUND
0c0be4a3 13
85c9f98b
DE
14#ifndef WX_PRECOMP
15 #include "wx/app.h"
16 #include "wx/log.h"
17#endif //ndef WX_PRECOMP
0c0be4a3 18#include "wx/sound.h"
85c9f98b 19#include "wx/evtloop.h"
0c0be4a3 20
0c0be4a3
RN
21#include "wx/cocoa/autorelease.h"
22#include "wx/cocoa/string.h"
85c9f98b 23#include "wx/cocoa/log.h"
0c0be4a3 24
e7e1ad7d
DE
25#include "wx/cocoa/objc/objc_uniquifying.h"
26
85c9f98b
DE
27#import <AppKit/NSSound.h>
28#import <Foundation/NSData.h>
0c0be4a3 29
85c9f98b
DE
30static WX_NSSound s_currentSound = nil;
31static bool s_loopCurrentSound = false;
0c0be4a3
RN
32
33// ========================================================================
34// wxNSSoundDelegate
35// ========================================================================
36@interface wxNSSoundDelegate : NSObject
37{
38}
39
40// Delegate methods
85c9f98b 41- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying;
0c0be4a3 42@end // interface wxNSSoundDelegate : NSObject
e7e1ad7d 43WX_DECLARE_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
0c0be4a3
RN
44
45@implementation wxNSSoundDelegate : NSObject
46
85c9f98b 47- (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying
0c0be4a3 48{
85c9f98b
DE
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
0c0be4a3 59 {
85c9f98b
DE
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();
0c0be4a3
RN
67 }
68}
69
70@end // wxNSSoundDelegate
e7e1ad7d 71WX_IMPLEMENT_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
0c0be4a3 72
e7e1ad7d 73const wxObjcAutoRefFromAlloc<struct objc_object*> wxSound::sm_cocoaDelegate = [[WX_GET_OBJC_CLASS(wxNSSoundDelegate) alloc] init];
a2c1097b 74
0c0be4a3
RN
75// ------------------------------------------------------------------
76// wxSound
77// ------------------------------------------------------------------
78
85c9f98b
DE
79wxSound::wxSound(const wxSound& sound)
80: m_cocoaNSSound(sound.m_cocoaNSSound)
0c0be4a3 81{
85c9f98b 82 [m_cocoaNSSound retain];
0c0be4a3
RN
83}
84
85wxSound::~wxSound()
86{
85c9f98b 87 SetNSSound(nil);
0c0be4a3
RN
88}
89
90bool wxSound::Create(const wxString& fileName, bool isResource)
91{
92 wxAutoNSAutoreleasePool thePool;
93
0c0be4a3 94 if (isResource)
85c9f98b
DE
95 SetNSSound([NSSound soundNamed:wxNSStringWithWxString(fileName)]);
96 else
0c0be4a3 97 {
85c9f98b
DE
98 SetNSSound([[NSSound alloc] initWithContentsOfFile:wxNSStringWithWxString(fileName) byReference:YES]);
99 [m_cocoaNSSound release];
0c0be4a3 100 }
0c0be4a3 101
a2c1097b 102 return m_cocoaNSSound;
0c0be4a3
RN
103}
104
85c9f98b
DE
105bool 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
118void 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
0c0be4a3
RN
129bool wxSound::DoPlay(unsigned flags) const
130{
85c9f98b
DE
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.
0c0be4a3 137
85c9f98b
DE
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;
0c0be4a3
RN
142
143 if (flags & wxSOUND_ASYNC)
a2c1097b 144 return [m_cocoaNSSound play];
0c0be4a3
RN
145 else
146 {
85c9f98b
DE
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;
0c0be4a3
RN
161 }
162}
163
164bool wxSound::IsPlaying()
165{
85c9f98b 166 // Normally you can send a message to a nil object and it will return
4c51a665 167 // nil. That behaviour would probably be okay here but in general it's
85c9f98b
DE
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;
0c0be4a3
RN
174}
175
176void wxSound::Stop()
177{
85c9f98b
DE
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;
0c0be4a3
RN
187}
188
189#endif //wxUSE_SOUND