gosu 0.7.33 → 0.7.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. data/Gosu/Async.hpp +10 -8
  2. data/Gosu/Audio.hpp +6 -4
  3. data/Gosu/AutoLink.hpp +0 -0
  4. data/Gosu/Bitmap.hpp +4 -5
  5. data/Gosu/ButtonsX.hpp +1 -1
  6. data/Gosu/Color.hpp +8 -8
  7. data/Gosu/Font.hpp +2 -2
  8. data/Gosu/Graphics.hpp +4 -6
  9. data/Gosu/IO.hpp +11 -4
  10. data/Gosu/Image.hpp +9 -7
  11. data/Gosu/ImageData.hpp +10 -4
  12. data/Gosu/Input.hpp +4 -5
  13. data/Gosu/Sockets.hpp +10 -12
  14. data/Gosu/TR1.hpp +44 -0
  15. data/Gosu/TextInput.hpp +2 -2
  16. data/Gosu/Version.hpp +2 -2
  17. data/Gosu/WinUtility.hpp +5 -6
  18. data/Gosu/Window.hpp +5 -6
  19. data/GosuImpl/Async.cpp +4 -3
  20. data/GosuImpl/Audio/ALChannelManagement.hpp +23 -11
  21. data/GosuImpl/Audio/AudioFile.hpp +11 -3
  22. data/GosuImpl/Audio/AudioOpenAL.cpp +613 -0
  23. data/GosuImpl/Audio/AudioOpenAL.mm +1 -605
  24. data/GosuImpl/Audio/AudioSDL.cpp +12 -14
  25. data/GosuImpl/Audio/AudioToolboxFile.hpp +0 -1
  26. data/GosuImpl/Audio/OggFile.hpp +2 -0
  27. data/GosuImpl/Audio/SndFile.hpp +158 -0
  28. data/GosuImpl/DirectoriesWin.cpp +18 -18
  29. data/GosuImpl/Graphics/BitmapApple.mm +17 -19
  30. data/GosuImpl/Graphics/BitmapBMP.cpp +7 -2
  31. data/GosuImpl/Graphics/BitmapFreeImage.cpp +11 -12
  32. data/GosuImpl/Graphics/BitmapGDIplus.cpp +35 -31
  33. data/GosuImpl/Graphics/BitmapUtils.cpp +1 -1
  34. data/GosuImpl/Graphics/BlockAllocator.cpp +7 -8
  35. data/GosuImpl/Graphics/BlockAllocator.hpp +3 -4
  36. data/GosuImpl/Graphics/Common.hpp +3 -2
  37. data/GosuImpl/Graphics/DrawOp.hpp +2 -3
  38. data/GosuImpl/Graphics/DrawOpQueue.hpp +28 -20
  39. data/GosuImpl/Graphics/Font.cpp +13 -13
  40. data/GosuImpl/Graphics/FormattedString.hpp +93 -86
  41. data/GosuImpl/Graphics/Graphics.cpp +16 -14
  42. data/GosuImpl/Graphics/Image.cpp +7 -3
  43. data/GosuImpl/Graphics/LargeImageData.hpp +4 -5
  44. data/GosuImpl/Graphics/Macro.hpp +11 -11
  45. data/GosuImpl/Graphics/RenderState.hpp +5 -4
  46. data/GosuImpl/Graphics/TexChunk.cpp +3 -3
  47. data/GosuImpl/Graphics/TexChunk.hpp +4 -4
  48. data/GosuImpl/Graphics/Text.cpp +29 -30
  49. data/GosuImpl/Graphics/TextMac.cpp +9 -7
  50. data/GosuImpl/Graphics/TextTTFWin.cpp +3 -1
  51. data/GosuImpl/Graphics/TextTouch.mm +0 -1
  52. data/GosuImpl/Graphics/TextUnix.cpp +12 -4
  53. data/GosuImpl/Graphics/TextWin.cpp +4 -2
  54. data/GosuImpl/Graphics/Texture.cpp +7 -6
  55. data/GosuImpl/Graphics/Texture.hpp +3 -4
  56. data/GosuImpl/InputMac.mm +12 -15
  57. data/GosuImpl/InputTouch.mm +3 -3
  58. data/GosuImpl/InputWin.cpp +149 -159
  59. data/GosuImpl/InputX.cpp +0 -0
  60. data/GosuImpl/MacUtility.hpp +9 -4
  61. data/GosuImpl/RubyGosu.swg +38 -43
  62. data/GosuImpl/RubyGosu_wrap.cxx +89 -96
  63. data/GosuImpl/Sockets/CommSocket.cpp +5 -5
  64. data/GosuImpl/Sockets/Sockets.hpp +4 -4
  65. data/GosuImpl/TimingApple.cpp +2 -3
  66. data/GosuImpl/Utility.cpp +18 -0
  67. data/GosuImpl/WinMain.cpp +0 -1
  68. data/GosuImpl/WinUtility.cpp +2 -2
  69. data/GosuImpl/WindowMac.mm +20 -17
  70. data/GosuImpl/WindowTouch.mm +8 -7
  71. data/GosuImpl/WindowWin.cpp +12 -7
  72. data/GosuImpl/WindowX.cpp +67 -18
  73. data/lib/gosu.rb +14 -12
  74. data/linux/extconf.rb +11 -6
  75. metadata +8 -7
  76. data/GosuImpl/Audio/AudioAudiere.cpp +0 -448
  77. data/GosuImpl/RubyGosu_DllMain.cxx +0 -31
@@ -0,0 +1,44 @@
1
+ //! \file TR1.hpp
2
+ //! Includes all parts of C++03 (TR1) that are relevant for Gosu.
3
+
4
+ #ifndef GOSU_TR1_HPP
5
+ #define GOSU_TR1_HPP
6
+
7
+ #ifdef _MSC_VER
8
+ #include <array>
9
+ #include <memory>
10
+ #include <functional>
11
+ namespace std
12
+ {
13
+ namespace tr1
14
+ {
15
+ typedef unsigned char uint8_t;
16
+ typedef unsigned short uint16_t;
17
+ typedef unsigned int uint32_t;
18
+ typedef unsigned long long uint64_t;
19
+ typedef signed char int8_t;
20
+ typedef signed short int16_t;
21
+ typedef signed int int32_t;
22
+ typedef signed long long int64_t;
23
+ }
24
+ }
25
+ #else
26
+ #include <tr1/array>
27
+ #include <tr1/memory>
28
+ #include <tr1/functional>
29
+ #if defined(__GNUC__) && (__GNUC__ < 4 || __GNUC_MINOR__ < 2)
30
+ #include <stdint.h>
31
+ namespace std
32
+ {
33
+ namespace tr1
34
+ {
35
+ using ::int8_t; using ::int16_t; using ::int32_t; using ::int64_t;
36
+ using ::uint8_t; using ::uint16_t; using ::uint32_t; using ::uint64_t;
37
+ }
38
+ }
39
+ #else
40
+ #include <tr1/cstdint>
41
+ #endif
42
+ #endif
43
+
44
+ #endif
@@ -6,7 +6,7 @@
6
6
 
7
7
  #include <Gosu/Fwd.hpp>
8
8
  #include <Gosu/Platform.hpp>
9
- #include <boost/scoped_ptr.hpp>
9
+ #include <Gosu/TR1.hpp>
10
10
  #include <string>
11
11
 
12
12
  namespace Gosu
@@ -23,7 +23,7 @@ namespace Gosu
23
23
  class TextInput
24
24
  {
25
25
  struct Impl;
26
- boost::scoped_ptr<Impl> pimpl;
26
+ const std::auto_ptr<Impl> pimpl;
27
27
 
28
28
  public:
29
29
  TextInput();
@@ -3,8 +3,8 @@
3
3
 
4
4
  #define GOSU_MAJOR_VERSION 0
5
5
  #define GOSU_MINOR_VERSION 7
6
- #define GOSU_POINT_VERSION 33
7
- #define GOSU_VERSION "0.7.33"
6
+ #define GOSU_POINT_VERSION 35
7
+ #define GOSU_VERSION "0.7.35"
8
8
 
9
9
  #define GOSU_COPYRIGHT_NOTICE \
10
10
  "May contain `ogg', `vorbis' libraries (c) 2002-2008 Xiph.org Foundation" \
@@ -8,8 +8,7 @@
8
8
 
9
9
  #include <windows.h>
10
10
  #include <Gosu/Platform.hpp>
11
- #include <boost/function.hpp>
12
- #include <boost/shared_ptr.hpp>
11
+ #include <Gosu/TR1.hpp>
13
12
  #include <string>
14
13
 
15
14
  namespace Gosu
@@ -31,7 +30,7 @@ namespace Gosu
31
30
  //! Registers a function to be called by handleMessage and
32
31
  //! processMessages. Every message is passed to the hooks and not
33
32
  //! processed further if any hook function returns true.
34
- void registerMessageHook(const boost::function<bool (MSG&)>& hook);
33
+ void registerMessageHook(const std::tr1::function<bool (MSG&)>& hook);
35
34
 
36
35
  //! Throws an exception according to the error which GetLastError()
37
36
  //! returns, optionally prefixed with "While (action), the following
@@ -58,11 +57,11 @@ namespace Gosu
58
57
  }
59
58
 
60
59
  //! Small helper function that transfers ownership of a COM interface
61
- //! to a boost::shared_ptr.
60
+ //! to a std::tr1::shared_ptr.
62
61
  template<typename T>
63
- inline boost::shared_ptr<T> shareComPtr(T* ptr)
62
+ inline std::tr1::shared_ptr<T> shareComPtr(T* ptr)
64
63
  {
65
- return boost::shared_ptr<T>(ptr, releaseComPtr<T>);
64
+ return std::tr1::shared_ptr<T>(ptr, releaseComPtr<T>);
66
65
  }
67
66
 
68
67
  //! Returns the executable's filename.
@@ -7,9 +7,8 @@
7
7
  #include <Gosu/Fwd.hpp>
8
8
  #include <Gosu/Platform.hpp>
9
9
  #include <Gosu/Input.hpp>
10
- #include <boost/scoped_ptr.hpp>
11
- #include <boost/shared_ptr.hpp>
12
- #include <boost/function.hpp>
10
+ #include <Gosu/TR1.hpp>
11
+ #include <memory>
13
12
  #include <string>
14
13
 
15
14
  #ifdef GOSU_IS_WIN
@@ -24,12 +23,12 @@ namespace Gosu
24
23
  //! Convenient all-in-one class that serves as the foundation of a standard
25
24
  //! Gosu application. Manages initialization of all of Gosu's core components
26
25
  //! and provides timing functionality.
27
- //! Note that you should really only use on instance of this class at the same time.
26
+ //! Note that you should really only use one instance of this class at the same time.
28
27
  //! This may or may not change later.
29
28
  class Window
30
29
  {
31
30
  struct Impl;
32
- boost::scoped_ptr<Impl> pimpl;
31
+ const std::auto_ptr<Impl> pimpl;
33
32
 
34
33
  public:
35
34
  //! Constructs a Window.
@@ -100,7 +99,7 @@ namespace Gosu
100
99
  #ifdef GOSU_IS_UNIX
101
100
  // Context for creating shared contexts.
102
101
  // Only on Unices (so far).
103
- typedef boost::shared_ptr<boost::function<void()> > SharedContext;
102
+ typedef std::tr1::shared_ptr<std::tr1::function<void()> > SharedContext;
104
103
  SharedContext createSharedContext();
105
104
  #endif
106
105
 
@@ -1,10 +1,11 @@
1
1
  #include <Gosu/Async.hpp>
2
2
  #include <Gosu/Graphics.hpp>
3
3
  #include <Gosu/Image.hpp>
4
+ #include <Gosu/TR1.hpp>
4
5
  #include <Gosu/Window.hpp>
5
- #include <boost/bind.hpp>
6
6
 
7
- using namespace boost;
7
+ using namespace std;
8
+ using namespace std::tr1;
8
9
 
9
10
  namespace Gosu
10
11
  {
@@ -26,7 +27,7 @@ Gosu::AsyncResult<Gosu::Image>
26
27
  Gosu::asyncNewImage(Window& window, const std::wstring& filename)
27
28
  {
28
29
  shared_ptr<try_mutex> mutex(new try_mutex);
29
- shared_ptr<std::auto_ptr<Image> > image(new std::auto_ptr<Image>);
30
+ shared_ptr<auto_ptr<Image> > image(new std::auto_ptr<Image>);
30
31
  thread thread(bind(asyncNewImage_Impl,
31
32
  ref(window),
32
33
  filename,
@@ -1,20 +1,30 @@
1
- #import <OpenAL/al.h>
2
- #import <OpenAL/alc.h>
1
+ #include <Gosu/Platform.hpp>
2
+ #ifdef GOSU_IS_MAC
3
+ #include <OpenAL/al.h>
4
+ #include <OpenAL/alc.h>
3
5
  #include <GosuImpl/MacUtility.hpp>
4
- #include <boost/scoped_ptr.hpp>
6
+ #else
7
+ #include <AL/al.h>
8
+ #include <AL/alc.h>
9
+ #endif
10
+ #include <cstdlib>
5
11
  #include <algorithm>
12
+ #include <memory>
6
13
 
7
14
  namespace Gosu
8
15
  {
9
- class ALChannelManagement : boost::noncopyable
16
+ class ALChannelManagement
10
17
  {
18
+ ALChannelManagement(const ALChannelManagement&);
19
+ ALChannelManagement& operator=(const ALChannelManagement&);
20
+
11
21
  static ALCdevice* alDevice;
12
22
  static ALCcontext* alContext;
13
23
 
14
24
  enum { NUM_SOURCES = 32 }; // This is what the iPhone supports, I hear.
15
- ALuint alSources[NUM_SOURCES];
16
- ALuint currentToken;
17
- ALuint currentTokens[NUM_SOURCES];
25
+ static ALuint alSources[NUM_SOURCES];
26
+ static ALuint currentToken;
27
+ static ALuint currentTokens[NUM_SOURCES];
18
28
 
19
29
  public:
20
30
  enum { NO_TOKEN = -1, NO_SOURCE = -1, NO_FREE_CHANNEL = -1 };
@@ -31,9 +41,8 @@ namespace Gosu
31
41
  alContext = alcCreateContext(alDevice, 0);
32
42
  alcMakeContextCurrent(alContext);
33
43
  alGenSources(NUM_SOURCES, alSources);
34
- currentToken = 0;
35
44
  std::fill(currentTokens, currentTokens + NUM_SOURCES,
36
- static_cast<NSUInteger>(NO_TOKEN));
45
+ static_cast<ALuint>(NO_TOKEN));
37
46
  }
38
47
 
39
48
  ~ALChannelManagement()
@@ -77,6 +86,9 @@ namespace Gosu
77
86
  };
78
87
  ALCdevice* ALChannelManagement::alDevice = 0;
79
88
  ALCcontext* ALChannelManagement::alContext = 0;
80
-
81
- boost::scoped_ptr<ALChannelManagement> alChannelManagement;
89
+ ALuint ALChannelManagement::alSources[NUM_SOURCES];
90
+ ALuint ALChannelManagement::currentToken = 0;
91
+ ALuint ALChannelManagement::currentTokens[NUM_SOURCES];
92
+
93
+ std::auto_ptr<ALChannelManagement> alChannelManagement;
82
94
  }
@@ -1,17 +1,25 @@
1
1
  #ifndef GOSUIMPL_AUDIO_AUDIOFILE_HPP
2
2
  #define GOSUIMPL_AUDIO_AUDIOFILE_HPP
3
3
 
4
- #include <boost/noncopyable.hpp>
5
4
  #include <vector>
6
- #import <OpenAL/al.h>
5
+ #include <Gosu/Platform.hpp>
6
+ #ifdef GOSU_IS_MAC
7
+ #include <OpenAL/al.h>
8
+ #else
9
+ #include <AL/al.h>
10
+ #endif
7
11
 
8
12
  namespace Gosu
9
13
  {
10
- class AudioFile : boost::noncopyable
14
+ class AudioFile
11
15
  {
16
+ AudioFile(const AudioFile&);
17
+ AudioFile& operator=(const AudioFile&);
18
+
12
19
  std::vector<char> decodedData_;
13
20
 
14
21
  public:
22
+ AudioFile() {}
15
23
  virtual ~AudioFile() {}
16
24
  virtual ALenum format() const = 0;
17
25
  virtual ALuint sampleRate() const = 0;
@@ -0,0 +1,613 @@
1
+ #include <GosuImpl/Audio/ALChannelManagement.hpp>
2
+ #include <GosuImpl/Audio/OggFile.hpp>
3
+
4
+ #include <Gosu/Audio.hpp>
5
+ #include <Gosu/Math.hpp>
6
+ #include <Gosu/IO.hpp>
7
+ #include <Gosu/Utility.hpp>
8
+ #include <Gosu/Platform.hpp>
9
+
10
+ #include <cassert>
11
+ #include <cstdlib>
12
+ #include <algorithm>
13
+ #include <memory>
14
+ #include <stdexcept>
15
+ #include <vector>
16
+
17
+ #ifdef GOSU_IS_MAC
18
+ #include <OpenAL/al.h>
19
+ #include <OpenAL/alc.h>
20
+ #import <Foundation/Foundation.h>
21
+ #include <GosuImpl/Audio/AudioToolboxFile.hpp>
22
+ #define WAVE_FILE AudioToolboxFile
23
+ #else
24
+ #include <AL/al.h>
25
+ #include <AL/alc.h>
26
+ #include <GosuImpl/Audio/SndFile.hpp>
27
+ #define WAVE_FILE SndFile
28
+ #endif
29
+
30
+ #ifdef GOSU_IS_IPHONE
31
+ #import <AVFoundation/AVFoundation.h>
32
+ #endif
33
+
34
+ using namespace std;
35
+
36
+ namespace
37
+ {
38
+ using namespace Gosu;
39
+
40
+ bool isOggFile(Gosu::Reader reader)
41
+ {
42
+ char magicBytes[4];
43
+ reader.read(magicBytes, 4);
44
+ return magicBytes[0] == 'O' && magicBytes[1] == 'g' &&
45
+ magicBytes[2] == 'g' && magicBytes[3] == 'S';
46
+ }
47
+
48
+ bool isOggFile(const wstring& filename)
49
+ {
50
+ Gosu::File file(filename);
51
+ return isOggFile(file.frontReader());
52
+ }
53
+
54
+ Song* curSong = 0;
55
+ bool curSongLooping;
56
+ }
57
+
58
+ // TODO: What is the NSAutoreleasePool good for?
59
+ #ifdef GOSU_IS_MAC
60
+ #include <GosuImpl/MacUtility.hpp>
61
+ #define CONSTRUCTOR_COMMON \
62
+ ObjRef<NSAutoreleasePool> pool([[NSAutoreleasePool alloc] init]); \
63
+ if (!alChannelManagement.get()) \
64
+ alChannelManagement.reset(new ALChannelManagement)
65
+ #else
66
+ #define CONSTRUCTOR_COMMON \
67
+ if (!alChannelManagement.get()) \
68
+ alChannelManagement.reset(new ALChannelManagement)
69
+ #endif
70
+
71
+ Gosu::SampleInstance::SampleInstance(int handle, int extra)
72
+ : handle(handle), extra(extra)
73
+ {
74
+ }
75
+
76
+ bool Gosu::SampleInstance::playing() const
77
+ {
78
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
79
+ if (source == ALChannelManagement::NO_SOURCE)
80
+ return false;
81
+ ALint state;
82
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
83
+ return state == AL_PLAYING;
84
+ }
85
+
86
+ bool Gosu::SampleInstance::paused() const
87
+ {
88
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
89
+ if (source == ALChannelManagement::NO_SOURCE)
90
+ return false;
91
+ ALint state;
92
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
93
+ return state == AL_PAUSED;
94
+ }
95
+
96
+ void Gosu::SampleInstance::pause()
97
+ {
98
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
99
+ if (source == ALChannelManagement::NO_SOURCE)
100
+ return;
101
+ alSourcePause(source);
102
+ }
103
+
104
+ void Gosu::SampleInstance::resume()
105
+ {
106
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
107
+ if (source == ALChannelManagement::NO_SOURCE)
108
+ return;
109
+ ALint state;
110
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
111
+ if (state == AL_PAUSED)
112
+ alSourcePlay(source);
113
+ }
114
+
115
+ void Gosu::SampleInstance::stop()
116
+ {
117
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
118
+ if (source == ALChannelManagement::NO_SOURCE)
119
+ return;
120
+ alSourceStop(source);
121
+ }
122
+
123
+ void Gosu::SampleInstance::changeVolume(double volume)
124
+ {
125
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
126
+ if (source == ALChannelManagement::NO_SOURCE)
127
+ return;
128
+ alSourcef(source, AL_GAIN, volume);
129
+ }
130
+
131
+ void Gosu::SampleInstance::changePan(double pan)
132
+ {
133
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
134
+ if (source == ALChannelManagement::NO_SOURCE)
135
+ return;
136
+ // TODO: This is not the old panning behavior!
137
+ alSource3f(source, AL_POSITION, pan * 10, 0, 0);
138
+ }
139
+
140
+ void Gosu::SampleInstance::changeSpeed(double speed)
141
+ {
142
+ ALuint source = alChannelManagement->sourceIfStillPlaying(handle, extra);
143
+ if (source == ALChannelManagement::NO_SOURCE)
144
+ return;
145
+ alSourcef(source, AL_PITCH, speed);
146
+ }
147
+
148
+ struct Gosu::Sample::SampleData
149
+ {
150
+ ALuint buffer, source;
151
+
152
+ SampleData(AudioFile& audioFile)
153
+ {
154
+ alGenBuffers(1, &buffer);
155
+ alBufferData(buffer,
156
+ audioFile.format(),
157
+ &audioFile.decodedData().front(),
158
+ audioFile.decodedData().size(),
159
+ audioFile.sampleRate());
160
+ }
161
+
162
+ ~SampleData()
163
+ {
164
+ // It's hard to free things in the right order in Ruby/Gosu.
165
+ // Make sure buffer isn't deleted after the context/device are shut down.
166
+ if (!alChannelManagement.get())
167
+ return;
168
+
169
+ alDeleteBuffers(1, &buffer);
170
+ }
171
+
172
+ private:
173
+ SampleData(const SampleData&);
174
+ SampleData& operator=(const SampleData&);
175
+ };
176
+
177
+ Gosu::Sample::Sample(const std::wstring& filename)
178
+ {
179
+ CONSTRUCTOR_COMMON;
180
+
181
+ if (isOggFile(filename))
182
+ {
183
+ Gosu::Buffer buffer;
184
+ Gosu::loadFile(buffer, filename);
185
+ OggFile oggFile(buffer.frontReader());
186
+ data.reset(new SampleData(oggFile));
187
+ }
188
+ else
189
+ {
190
+ WAVE_FILE audioFile(filename);
191
+ data.reset(new SampleData(audioFile));
192
+ }
193
+ }
194
+
195
+ Gosu::Sample::Sample(Reader reader)
196
+ {
197
+ CONSTRUCTOR_COMMON;
198
+
199
+ if (isOggFile(reader))
200
+ {
201
+ OggFile oggFile(reader);
202
+ data.reset(new SampleData(oggFile));
203
+ }
204
+ else
205
+ {
206
+ WAVE_FILE audioFile(reader);
207
+ data.reset(new SampleData(audioFile));
208
+ }
209
+ }
210
+
211
+ Gosu::SampleInstance Gosu::Sample::play(double volume, double speed,
212
+ bool looping) const
213
+ {
214
+ return playPan(0, volume, speed, looping);
215
+ }
216
+
217
+ Gosu::SampleInstance Gosu::Sample::playPan(double pan, double volume,
218
+ double speed, bool looping) const
219
+ {
220
+ std::pair<int, int> channelAndToken = alChannelManagement->reserveChannel();
221
+ if (channelAndToken.first == ALChannelManagement::NO_FREE_CHANNEL)
222
+ return Gosu::SampleInstance(channelAndToken.first, channelAndToken.second);
223
+
224
+ ALuint source = alChannelManagement->sourceIfStillPlaying(channelAndToken.first,
225
+ channelAndToken.second);
226
+ assert(source != ALChannelManagement::NO_SOURCE);
227
+ alSourcei(source, AL_BUFFER, data->buffer);
228
+ // TODO: This is not the old panning behavior!
229
+ alSource3f(source, AL_POSITION, pan * 10, 0, 0);
230
+ alSourcef(source, AL_GAIN, volume);
231
+ alSourcef(source, AL_PITCH, speed);
232
+ alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
233
+ alSourcePlay(source);
234
+
235
+ return Gosu::SampleInstance(channelAndToken.first, channelAndToken.second);
236
+ }
237
+
238
+ class Gosu::Song::BaseData
239
+ {
240
+ BaseData(const BaseData&);
241
+ BaseData& operator=(const BaseData&);
242
+
243
+ double volume_;
244
+
245
+ protected:
246
+ BaseData() : volume_(1) {}
247
+ virtual void applyVolume() = 0;
248
+
249
+ public:
250
+ virtual ~BaseData() {}
251
+
252
+ virtual void play(bool looping) = 0;
253
+ virtual void pause() = 0;
254
+ virtual void resume() = 0;
255
+ virtual bool paused() const = 0;
256
+ virtual void stop() = 0;
257
+
258
+ virtual void update() = 0;
259
+
260
+ double volume() const
261
+ {
262
+ return volume_;
263
+ }
264
+
265
+ void changeVolume(double volume)
266
+ {
267
+ volume_ = clamp(volume, 0.0, 1.0);
268
+ applyVolume();
269
+ }
270
+ };
271
+
272
+ #ifdef GOSU_IS_IPHONE
273
+ // AVAudioPlayer impl
274
+ class Gosu::Song::ModuleData : public BaseData
275
+ {
276
+ ObjRef<AVAudioPlayer> player;
277
+
278
+ void applyVolume()
279
+ {
280
+ player.obj().volume = volume();
281
+ }
282
+
283
+ public:
284
+ ModuleData(const std::wstring& filename)
285
+ {
286
+ std::string utf8Filename = Gosu::wstringToUTF8(filename);
287
+ ObjRef<NSString> nsFilename([[NSString alloc] initWithUTF8String: utf8Filename.c_str()]);
288
+ ObjRef<NSURL> url([[NSURL alloc] initFileURLWithPath: nsFilename.obj()]);
289
+ player.reset([[AVAudioPlayer alloc] initWithContentsOfURL: url.obj() error: NULL]);
290
+ }
291
+
292
+ void play(bool looping)
293
+ {
294
+ if (paused())
295
+ stop();
296
+ player.obj().numberOfLoops = looping ? -1 : 0;
297
+ [player.obj() play];
298
+ }
299
+
300
+ void pause()
301
+ {
302
+ [player.obj() pause];
303
+ }
304
+
305
+ void resume()
306
+ {
307
+ [player.obj() play];
308
+ }
309
+
310
+ bool paused() const
311
+ {
312
+ return !player.obj().playing;
313
+ };
314
+
315
+ void stop()
316
+ {
317
+ [player.obj() stop];
318
+ player.obj().currentTime = 0;
319
+ }
320
+
321
+ void update()
322
+ {
323
+ }
324
+ };
325
+ #endif
326
+
327
+ // AudioFile impl
328
+ class Gosu::Song::StreamData : public BaseData
329
+ {
330
+ std::auto_ptr<AudioFile> file;
331
+ ALuint buffers[2];
332
+
333
+ void applyVolume()
334
+ {
335
+ int source = lookupSource();
336
+ if (source != ALChannelManagement::NO_SOURCE)
337
+ alSourcef(source, AL_GAIN, volume());
338
+ }
339
+
340
+ int lookupSource() const
341
+ {
342
+ return alChannelManagement->sourceForSongs();
343
+ }
344
+
345
+ bool streamToBuffer(ALuint buffer)
346
+ {
347
+ #ifdef GOSU_IS_IPHONE
348
+ static const unsigned BUFFER_SIZE = 4096 * 4;
349
+ #else
350
+ static const unsigned BUFFER_SIZE = 4096 * 8;
351
+ #endif
352
+ char audioData[BUFFER_SIZE];
353
+ std::size_t readBytes = file->readData(audioData, BUFFER_SIZE);
354
+ if (readBytes > 0)
355
+ alBufferData(buffer, file->format(), audioData, readBytes, file->sampleRate());
356
+ return readBytes > 0;
357
+ }
358
+
359
+ public:
360
+ StreamData(const std::wstring& filename)
361
+ {
362
+ if (isOggFile(filename))
363
+ {
364
+ Gosu::File sourceFile(filename);
365
+ file.reset(new OggFile(sourceFile.frontReader()));
366
+ }
367
+ else
368
+ file.reset(new WAVE_FILE(filename));
369
+ alGenBuffers(2, buffers);
370
+ }
371
+
372
+ StreamData(Reader reader)
373
+ {
374
+ if (isOggFile(reader))
375
+ file.reset(new OggFile(reader));
376
+ else
377
+ file.reset(new WAVE_FILE(reader));
378
+ alGenBuffers(2, buffers);
379
+ }
380
+
381
+ ~StreamData()
382
+ {
383
+ if (alChannelManagement.get())
384
+ {
385
+ stop();
386
+ alDeleteBuffers(2, buffers);
387
+ }
388
+ }
389
+
390
+ void play(bool looping)
391
+ {
392
+ int source = lookupSource();
393
+ if (source != ALChannelManagement::NO_SOURCE)
394
+ {
395
+ alSource3f(source, AL_POSITION, 0, 0, 0);
396
+ alSourcef(source, AL_GAIN, volume());
397
+ alSourcef(source, AL_PITCH, 1);
398
+ alSourcei(source, AL_LOOPING, AL_FALSE); // need to implement this manually...
399
+
400
+ streamToBuffer(buffers[0]);
401
+ streamToBuffer(buffers[1]);
402
+
403
+ // TODO: Not good for songs with less than two buffers full of data.
404
+
405
+ alSourceQueueBuffers(source, 2, buffers);
406
+ alSourcePlay(source);
407
+ }
408
+ }
409
+
410
+ void stop()
411
+ {
412
+ int source = lookupSource();
413
+ if (source != ALChannelManagement::NO_SOURCE)
414
+ {
415
+ alSourceStop(source);
416
+
417
+ ALuint buffer;
418
+
419
+ // The number of QUEUED buffers apparently includes the number of
420
+ // PROCESSED ones, so getting rid of the QUEUED ones is enough.
421
+
422
+ int queued;
423
+ alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
424
+ while (queued--)
425
+ alSourceUnqueueBuffers(source, 1, &buffer);
426
+
427
+ //int processed;
428
+ //alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
429
+ //while (processed--)
430
+ // alSourceUnqueueBuffers(source, 1, &buffer);
431
+ }
432
+ file->rewind();
433
+ }
434
+
435
+ void pause()
436
+ {
437
+ int source = lookupSource();
438
+ if (source != ALChannelManagement::NO_SOURCE)
439
+ alSourcePause(source);
440
+ }
441
+
442
+ void resume()
443
+ {
444
+ int source = lookupSource();
445
+ if (source != ALChannelManagement::NO_SOURCE)
446
+ alSourcePlay(source);
447
+ }
448
+
449
+ bool paused() const
450
+ {
451
+ int source = lookupSource();
452
+ if (source == ALChannelManagement::NO_SOURCE)
453
+ return false;
454
+ ALint state;
455
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
456
+ return state == AL_PAUSED;
457
+ }
458
+
459
+ void update()
460
+ {
461
+ int source = lookupSource();
462
+
463
+ ALuint buffer;
464
+ int processed;
465
+ bool active = true;
466
+
467
+ alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
468
+ for (int i = 0; i < processed; ++i)
469
+ {
470
+ alSourceUnqueueBuffers(source, 1, &buffer);
471
+ active = streamToBuffer(buffer);
472
+ if (active)
473
+ alSourceQueueBuffers(source, 1, &buffer);
474
+ }
475
+
476
+ ALint state;
477
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
478
+ if (active && state != AL_PLAYING && state != AL_PAUSED)
479
+ {
480
+ // We seemingly got starved.
481
+ alSourcePlay(source);
482
+ }
483
+ else if (!active)
484
+ {
485
+ // We got starved and there is nothing to play left.
486
+ // In any case, shut down the playback logic for a moment.
487
+ stop();
488
+
489
+ if (curSongLooping)
490
+ // Start anew.
491
+ play(true);
492
+ else
493
+ // Let the world know we're finished.
494
+ curSong = 0;
495
+ }
496
+ }
497
+ };
498
+
499
+ // TODO: Move into proper internal header
500
+ namespace Gosu { bool isExtension(const wchar_t* str, const wchar_t* ext); }
501
+
502
+ Gosu::Song::Song(const std::wstring& filename)
503
+ {
504
+ #ifdef GOSU_IS_IPHONE
505
+ if (isExtension(filename.c_str(), L".mp3") ||
506
+ isExtension(filename.c_str(), L".aac") ||
507
+ isExtension(filename.c_str(), L".m4a"))
508
+ data.reset(new ModuleData(filename));
509
+ else
510
+ #endif
511
+ {
512
+ CONSTRUCTOR_COMMON;
513
+ data.reset(new StreamData(filename));
514
+ }
515
+ }
516
+
517
+ Gosu::Song::Song(Reader reader)
518
+ {
519
+ CONSTRUCTOR_COMMON;
520
+
521
+ data.reset(new StreamData(reader));
522
+ }
523
+
524
+ Gosu::Song::~Song()
525
+ {
526
+ stop();
527
+ }
528
+
529
+ Gosu::Song* Gosu::Song::currentSong()
530
+ {
531
+ return curSong;
532
+ }
533
+
534
+ void Gosu::Song::play(bool looping)
535
+ {
536
+ if (paused())
537
+ data->resume();
538
+
539
+ if (curSong && curSong != this)
540
+ {
541
+ curSong->stop();
542
+ assert(curSong == 0);
543
+ }
544
+
545
+ if (curSong == 0)
546
+ data->play(looping);
547
+
548
+ curSong = this;
549
+ curSongLooping = looping;
550
+ }
551
+
552
+ void Gosu::Song::pause()
553
+ {
554
+ if (curSong == this)
555
+ data->pause(); // may be redundant
556
+ }
557
+
558
+ bool Gosu::Song::paused() const
559
+ {
560
+ return curSong == this && data->paused();
561
+ }
562
+
563
+ void Gosu::Song::stop()
564
+ {
565
+ if (curSong == this)
566
+ {
567
+ data->stop();
568
+ curSong = 0;
569
+ }
570
+ }
571
+
572
+ bool Gosu::Song::playing() const
573
+ {
574
+ return curSong == this && !data->paused();
575
+ }
576
+
577
+ double Gosu::Song::volume() const
578
+ {
579
+ return data->volume();
580
+ }
581
+
582
+ void Gosu::Song::changeVolume(double volume)
583
+ {
584
+ data->changeVolume(volume);
585
+ }
586
+
587
+ void Gosu::Song::update()
588
+ {
589
+ if (currentSong())
590
+ currentSong()->data->update();
591
+ }
592
+
593
+ // Deprecated constructors.
594
+
595
+ Gosu::Sample::Sample(Audio& audio, const std::wstring& filename)
596
+ {
597
+ Sample(filename).data.swap(data);
598
+ }
599
+
600
+ Gosu::Sample::Sample(Audio& audio, Reader reader)
601
+ {
602
+ Sample(reader).data.swap(data);
603
+ }
604
+
605
+ Gosu::Song::Song(Audio& audio, const std::wstring& filename)
606
+ {
607
+ data = Song(filename).data;
608
+ }
609
+
610
+ Gosu::Song::Song(Audio& audio, Type type, Reader reader)
611
+ {
612
+ data = Song(reader).data;
613
+ }