gosu 0.7.33 → 0.7.35

Sign up to get free protection for your applications and to get access to all the features.
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
+ }