gosu 0.7.12 → 0.7.13

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 (156) hide show
  1. data/Gosu/Audio.hpp +1 -0
  2. data/Gosu/Bitmap.hpp +4 -4
  3. data/Gosu/Graphics.hpp +3 -0
  4. data/Gosu/Input.hpp +10 -0
  5. data/Gosu/Text.hpp +1 -0
  6. data/Gosu/Window.hpp +20 -9
  7. data/GosuImpl/Audio/ALChannelManagement.hpp +72 -0
  8. data/GosuImpl/Audio/AudioFileMac.hpp +105 -0
  9. data/GosuImpl/AudioOpenAL.mm +486 -0
  10. data/GosuImpl/DirectoriesTouch.mm +38 -0
  11. data/GosuImpl/Graphics/BitmapBMP.cpp +23 -8
  12. data/GosuImpl/Graphics/BitmapTouch.mm +73 -0
  13. data/GosuImpl/Graphics/BitmapUtils.cpp +0 -2
  14. data/GosuImpl/Graphics/BlockAllocator.cpp +15 -4
  15. data/GosuImpl/Graphics/Common.hpp +21 -0
  16. data/GosuImpl/Graphics/DrawOp.hpp +53 -10
  17. data/GosuImpl/Graphics/GosuView.hpp +28 -0
  18. data/GosuImpl/Graphics/GosuView.mm +159 -0
  19. data/GosuImpl/Graphics/Graphics.cpp +18 -18
  20. data/GosuImpl/Graphics/LargeImageData.cpp +3 -0
  21. data/GosuImpl/Graphics/TexChunk.cpp +3 -2
  22. data/GosuImpl/Graphics/Text.cpp +85 -20
  23. data/GosuImpl/Graphics/TextMac.cpp +14 -27
  24. data/GosuImpl/Graphics/TextTouch.mm +89 -0
  25. data/GosuImpl/Graphics/Texture.cpp +1 -5
  26. data/GosuImpl/Iconv.hpp +5 -3
  27. data/GosuImpl/InputMac.mm +8 -9
  28. data/GosuImpl/InputTouch.mm +47 -0
  29. data/GosuImpl/InputWin.cpp +12 -4
  30. data/GosuImpl/InputX.cpp +3 -1
  31. data/GosuImpl/MacUtility.hpp +41 -0
  32. data/GosuImpl/Math.cpp +1 -1
  33. data/GosuImpl/RubyGosu.swg +17 -6
  34. data/GosuImpl/RubyGosu_wrap.cxx +950 -549
  35. data/GosuImpl/RubyGosu_wrap.h +2 -1
  36. data/GosuImpl/TimingUnix.cpp +15 -17
  37. data/GosuImpl/UtilityTouch.mm +44 -0
  38. data/GosuImpl/WindowMac.mm +39 -19
  39. data/GosuImpl/WindowTouch.mm +154 -0
  40. data/GosuImpl/WindowWin.cpp +27 -10
  41. data/GosuImpl/WindowX.cpp +27 -7
  42. data/linux/extconf.rb +6 -0
  43. data/mac/Gosu.icns +0 -0
  44. data/mac/Gosu.xcodeproj/jlnr.pbxuser +562 -788
  45. data/mac/Gosu.xcodeproj/jlnr.perspectivev3 +113 -103
  46. data/mac/Gosu.xcodeproj/project.pbxproj +258 -173
  47. data/reference/Async_8hpp-source.html +1 -1
  48. data/reference/Audio_8hpp-source.html +91 -90
  49. data/reference/Audio_8hpp.html +1 -1
  50. data/reference/AutoLink_8hpp-source.html +1 -1
  51. data/reference/AutoLink_8hpp.html +1 -1
  52. data/reference/Bitmap_8hpp-source.html +11 -12
  53. data/reference/Bitmap_8hpp.html +1 -1
  54. data/reference/ButtonsMac_8hpp-source.html +1 -1
  55. data/reference/ButtonsWin_8hpp-source.html +1 -1
  56. data/reference/ButtonsX_8hpp-source.html +1 -1
  57. data/reference/Color_8hpp-source.html +1 -1
  58. data/reference/Color_8hpp.html +1 -1
  59. data/reference/Directories_8hpp-source.html +1 -1
  60. data/reference/Directories_8hpp.html +1 -1
  61. data/reference/Font_8hpp-source.html +1 -1
  62. data/reference/Font_8hpp.html +1 -1
  63. data/reference/Fwd_8hpp-source.html +1 -1
  64. data/reference/Fwd_8hpp.html +1 -1
  65. data/reference/Gosu_8hpp-source.html +1 -1
  66. data/reference/Gosu_8hpp.html +1 -1
  67. data/reference/GraphicsBase_8hpp-source.html +1 -1
  68. data/reference/GraphicsBase_8hpp.html +1 -1
  69. data/reference/Graphics_8hpp-source.html +64 -61
  70. data/reference/Graphics_8hpp.html +8 -1
  71. data/reference/IO_8hpp-source.html +1 -1
  72. data/reference/IO_8hpp.html +1 -1
  73. data/reference/ImageData_8hpp-source.html +1 -1
  74. data/reference/ImageData_8hpp.html +1 -1
  75. data/reference/Image_8hpp-source.html +1 -1
  76. data/reference/Image_8hpp.html +1 -1
  77. data/reference/Input_8hpp-source.html +81 -71
  78. data/reference/Input_8hpp.html +7 -1
  79. data/reference/Math_8hpp-source.html +1 -1
  80. data/reference/Math_8hpp.html +1 -1
  81. data/reference/Platform_8hpp-source.html +1 -1
  82. data/reference/Platform_8hpp.html +1 -1
  83. data/reference/RotFlip_8hpp-source.html +1 -1
  84. data/reference/RotFlip_8hpp.html +1 -1
  85. data/reference/Sockets_8hpp-source.html +1 -1
  86. data/reference/Sockets_8hpp.html +1 -1
  87. data/reference/TextInput_8hpp-source.html +1 -1
  88. data/reference/TextInput_8hpp.html +1 -1
  89. data/reference/Text_8hpp-source.html +28 -27
  90. data/reference/Text_8hpp.html +1 -1
  91. data/reference/Timing_8hpp-source.html +1 -1
  92. data/reference/Timing_8hpp.html +1 -1
  93. data/reference/Utility_8hpp-source.html +1 -1
  94. data/reference/Utility_8hpp.html +1 -1
  95. data/reference/WinUtility_8hpp-source.html +1 -1
  96. data/reference/WinUtility_8hpp.html +1 -1
  97. data/reference/Window_8hpp-source.html +76 -68
  98. data/reference/Window_8hpp.html +1 -1
  99. data/reference/annotated.html +1 -1
  100. data/reference/classGosu_1_1Audio-members.html +1 -1
  101. data/reference/classGosu_1_1Audio.html +1 -1
  102. data/reference/classGosu_1_1Bitmap-members.html +3 -2
  103. data/reference/classGosu_1_1Bitmap.html +9 -6
  104. data/reference/classGosu_1_1Buffer-members.html +1 -1
  105. data/reference/classGosu_1_1Buffer.html +1 -1
  106. data/reference/classGosu_1_1Button-members.html +1 -1
  107. data/reference/classGosu_1_1Button.html +2 -2
  108. data/reference/classGosu_1_1Color-members.html +1 -1
  109. data/reference/classGosu_1_1Color.html +1 -1
  110. data/reference/classGosu_1_1File-members.html +1 -1
  111. data/reference/classGosu_1_1File.html +1 -1
  112. data/reference/classGosu_1_1Font-members.html +1 -1
  113. data/reference/classGosu_1_1Font.html +1 -1
  114. data/reference/classGosu_1_1Graphics-members.html +1 -1
  115. data/reference/classGosu_1_1Graphics.html +1 -1
  116. data/reference/classGosu_1_1Image-members.html +1 -1
  117. data/reference/classGosu_1_1Image.html +1 -1
  118. data/reference/classGosu_1_1ImageData-members.html +1 -1
  119. data/reference/classGosu_1_1ImageData.html +1 -1
  120. data/reference/classGosu_1_1Input-members.html +1 -1
  121. data/reference/classGosu_1_1Input.html +1 -1
  122. data/reference/classGosu_1_1MessageSocket-members.html +1 -1
  123. data/reference/classGosu_1_1MessageSocket.html +1 -1
  124. data/reference/classGosu_1_1Resource-members.html +1 -1
  125. data/reference/classGosu_1_1Resource.html +1 -1
  126. data/reference/classGosu_1_1Sample-members.html +1 -1
  127. data/reference/classGosu_1_1Sample.html +1 -1
  128. data/reference/classGosu_1_1SampleInstance-members.html +1 -1
  129. data/reference/classGosu_1_1SampleInstance.html +1 -1
  130. data/reference/classGosu_1_1Song-members.html +1 -1
  131. data/reference/classGosu_1_1Song.html +1 -1
  132. data/reference/classGosu_1_1TextInput-members.html +1 -1
  133. data/reference/classGosu_1_1TextInput.html +1 -1
  134. data/reference/classGosu_1_1Window-members.html +3 -3
  135. data/reference/classGosu_1_1Window.html +27 -9
  136. data/reference/files.html +1 -1
  137. data/reference/functions.html +10 -5
  138. data/reference/functions_enum.html +1 -1
  139. data/reference/functions_func.html +10 -5
  140. data/reference/functions_vars.html +1 -1
  141. data/reference/hierarchy.html +1 -1
  142. data/reference/index.html +1 -1
  143. data/reference/namespaceGosu.html +13 -2
  144. data/reference/namespaceGosu_1_1Colors.html +1 -1
  145. data/reference/namespaceGosu_1_1Win.html +1 -1
  146. data/reference/namespacemembers.html +1 -1
  147. data/reference/namespacemembers_enum.html +1 -1
  148. data/reference/namespacemembers_eval.html +1 -1
  149. data/reference/namespacemembers_func.html +1 -1
  150. data/reference/namespacemembers_type.html +1 -1
  151. data/reference/namespacemembers_vars.html +1 -1
  152. data/reference/namespaces.html +1 -1
  153. data/windows/Gosu.sln +2 -2
  154. data/windows/Gosu.vcproj +7 -8
  155. data/windows/RubyGosu.vcproj +5 -5
  156. metadata +14 -2
@@ -11,6 +11,7 @@
11
11
  #include <windows.h>
12
12
  #endif
13
13
  #include <Gosu/Fwd.hpp>
14
+ #include <Gosu/IO.hpp>
14
15
  #include <Gosu/Platform.hpp>
15
16
  #include <boost/scoped_ptr.hpp>
16
17
  #include <boost/utility.hpp>
@@ -54,10 +54,10 @@ namespace Gosu
54
54
  void insert(const Bitmap& source, int x, int y, unsigned srcX,
55
55
  unsigned srcY, unsigned srcWidth, unsigned srcHeight);
56
56
 
57
- #ifndef __BIG_ENDIAN__
58
- //! Undocumented optimization for Image creation; to be changed.
59
- const unsigned* glCompatibleData() const { return reinterpret_cast<const unsigned*>(&pixels[0]); }
60
- #endif
57
+ //! Direct access to the array of color values. May be useful for optimized
58
+ //! OpenGL operations.
59
+ const unsigned* data() const { return reinterpret_cast<const unsigned*>(&pixels[0]); }
60
+ unsigned* data() { return reinterpret_cast<unsigned*>(&pixels[0]); }
61
61
  };
62
62
 
63
63
  //! Loads a Windows or OS/2 BMP file into the given bitmap.
@@ -12,6 +12,9 @@
12
12
 
13
13
  namespace Gosu
14
14
  {
15
+ unsigned screenWidth();
16
+ unsigned screenHeight();
17
+
15
18
  //! Flags that affect the softness of a border.
16
19
  enum BorderFlags
17
20
  {
@@ -27,6 +27,7 @@
27
27
  #include <Gosu/Fwd.hpp>
28
28
  #include <boost/function.hpp>
29
29
  #include <boost/scoped_ptr.hpp>
30
+ #include <vector>
30
31
 
31
32
  namespace Gosu
32
33
  {
@@ -48,6 +49,11 @@ namespace Gosu
48
49
  Button(ButtonName name) : id(name) {}
49
50
  };
50
51
 
52
+ // Available even on non-iPhone platforms to make it easier to compile the
53
+ // same source for multiple platforms. To be moved to Input.hpp, though.
54
+ struct Touch { void* id; double x, y; };
55
+ typedef std::vector<Touch> Touches;
56
+
51
57
  //! Tests whether two Buttons identify the same physical button.
52
58
  inline bool operator==(Button lhs, Button rhs)
53
59
  {
@@ -71,9 +77,13 @@ namespace Gosu
71
77
  #endif
72
78
 
73
79
  #ifdef GOSU_IS_MAC
80
+ #ifdef GOSU_IS_IPHONE
81
+ Input();
82
+ #else
74
83
  Input(void* nswindow);
75
84
  bool feedNSEvent(void* event);
76
85
  #endif
86
+ #endif
77
87
 
78
88
  #ifdef GOSU_IS_X
79
89
  Input(::Display* display, ::Window window);
@@ -5,6 +5,7 @@
5
5
  #define GOSU_TEXT_HPP
6
6
 
7
7
  #include <Gosu/Fwd.hpp>
8
+ #include <Gosu/Color.hpp>
8
9
  #include <Gosu/GraphicsBase.hpp>
9
10
  #include <string>
10
11
 
@@ -11,7 +11,6 @@
11
11
  #include <boost/shared_ptr.hpp>
12
12
  #include <boost/function.hpp>
13
13
  #include <string>
14
- #include <vector>
15
14
 
16
15
  #ifdef GOSU_IS_WIN
17
16
  #include <windows.h>
@@ -19,11 +18,6 @@
19
18
 
20
19
  namespace Gosu
21
20
  {
22
- #ifdef GOSU_IS_IPHONE
23
- struct Touch { double x, y; };
24
- typedef std::vector<Touch> Touches;
25
- #endif
26
-
27
21
  //! Convenient all-in-one class that serves as the foundation of a standard
28
22
  //! Gosu application. Manages initialization of all of Gosu's core components
29
23
  //! and provides timing functionality.
@@ -42,6 +36,8 @@ namespace Gosu
42
36
 
43
37
  std::wstring caption() const;
44
38
  void setCaption(const std::wstring& caption);
39
+
40
+ double updateInterval() const;
45
41
 
46
42
  //! Starts the main event loop.
47
43
  void show();
@@ -54,7 +50,12 @@ namespace Gosu
54
50
  //! Called after every update and when the OS wants the window to
55
51
  //! repaint itself. Your application's rendering code goes here.
56
52
  virtual void draw() {}
57
-
53
+
54
+ //! Gives the game a chance to say no to being redrawn.
55
+ //! This is not a definitive answer. The operating system can still cause
56
+ //! redraws for one reason or another.
57
+ virtual bool needsRedraw() const { return true; }
58
+
58
59
  //! Called before update when the user pressed a button while the
59
60
  //! window had the focus.
60
61
  virtual void buttonDown(Gosu::Button) {}
@@ -73,19 +74,29 @@ namespace Gosu
73
74
  Input& input();
74
75
 
75
76
  #ifdef GOSU_IS_WIN
76
- // Only on Windows.
77
+ // Only on Windows, used for integrating with GUI toolkits.
77
78
  HWND handle() const;
78
79
  virtual LRESULT handleMessage(UINT message, WPARAM wparam,
79
80
  LPARAM lparam);
80
- #else
81
+ #endif
82
+
83
+ #ifdef GOSU_IS_UNIX
84
+ // Context for creating shared contexts.
81
85
  // Only on Unices (so far).
82
86
  typedef boost::shared_ptr<boost::function<void()> > SharedContext;
83
87
  SharedContext createSharedContext();
84
88
  #endif
89
+
85
90
  #ifdef GOSU_IS_IPHONE
91
+ // iPhone-only callbacks for touch events.
92
+ // Note that it does not hurt to override them even if you compile
93
+ // for another platform; if you don't specify "virtual" the code
94
+ // should even be stripped away cleanly.
86
95
  virtual void touchesBegan(const Touches& touches) {}
87
96
  virtual void touchesMoved(const Touches& touches) {}
88
97
  virtual void touchesEnded(const Touches& touches) {}
98
+ // Currently known touches.
99
+ const Touches& currentTouches() const;
89
100
  #endif
90
101
 
91
102
  #endif
@@ -0,0 +1,72 @@
1
+ #import <OpenAL/al.h>
2
+ #import <OpenAL/alc.h>
3
+ #include <GosuImpl/MacUtility.hpp>
4
+ #include <boost/scoped_ptr.hpp>
5
+ #include <algorithm>
6
+
7
+ namespace Gosu
8
+ {
9
+ class ALChannelManagement : boost::noncopyable
10
+ {
11
+ static ALCdevice* alDevice;
12
+ static ALCcontext* alContext;
13
+
14
+ enum { NUM_SOURCES = 32 }; // This is what the iPhone supports, I hear.
15
+ NSUInteger alSources[NUM_SOURCES];
16
+ NSUInteger currentToken;
17
+ NSUInteger currentTokens[NUM_SOURCES];
18
+
19
+ public:
20
+ enum { NO_TOKEN = -1, NO_SOURCE = -1, NO_FREE_CHANNEL = -1 };
21
+
22
+ ALChannelManagement()
23
+ {
24
+ // Open preferred device
25
+ alDevice = alcOpenDevice(0);
26
+ alContext = alcCreateContext(alDevice, 0);
27
+ alcMakeContextCurrent(alContext);
28
+ alGenSources(NUM_SOURCES, alSources);
29
+ currentToken = 0;
30
+ std::fill(currentTokens, currentTokens + NUM_SOURCES,
31
+ static_cast<NSUInteger>(NO_TOKEN));
32
+ }
33
+
34
+ ~ALChannelManagement()
35
+ {
36
+ alDeleteSources(NUM_SOURCES, alSources);
37
+ alcMakeContextCurrent(0);
38
+ alcDestroyContext(alContext);
39
+ alcCloseDevice(alDevice);
40
+ }
41
+
42
+ std::pair<int, int> reserveChannel()
43
+ {
44
+ int i;
45
+ for (i = 0; i <= NUM_SOURCES; ++i)
46
+ {
47
+ if (i == NUM_SOURCES)
48
+ return std::make_pair<int, int>(NO_FREE_CHANNEL, NO_TOKEN);
49
+ if (currentTokens[i] == NO_TOKEN)
50
+ break;
51
+ ALint state;
52
+ alGetSourcei(alSources[i], AL_SOURCE_STATE, &state);
53
+ if (state != AL_PLAYING && state != AL_PAUSED)
54
+ break;
55
+ }
56
+ ++currentToken;
57
+ currentTokens[i] = currentToken;
58
+ return std::make_pair<int, int>(i, currentToken);
59
+ }
60
+
61
+ int sourceIfStillPlaying(int channel, int token) const
62
+ {
63
+ if (currentTokens[channel] == token)
64
+ return alSources[channel];
65
+ return NO_SOURCE;
66
+ }
67
+ };
68
+ ALCdevice* ALChannelManagement::alDevice = 0;
69
+ ALCcontext* ALChannelManagement::alContext = 0;
70
+
71
+ boost::scoped_ptr<ALChannelManagement> alChannelManagement;
72
+ }
@@ -0,0 +1,105 @@
1
+ #ifndef GOSU_AUDIO_AUDIOFILE_MAC_HPP
2
+ #define GOSU_AUDIO_AUDIOFILE_MAC_HPP
3
+
4
+ #include <AudioToolbox/AudioToolbox.h>
5
+ #include <OpenAL/al.h>
6
+ #include <Gosu/IO.hpp>
7
+ #include <GosuImpl/MacUtility.hpp>
8
+ #include <Gosu/Utility.hpp>
9
+ #include <boost/utility.hpp>
10
+ #include <algorithm>
11
+ #include <vector>
12
+ #import <Foundation/Foundation.h>
13
+
14
+ namespace Gosu
15
+ {
16
+ class AudioFile : boost::noncopyable
17
+ {
18
+ AudioFileID fileID;
19
+ mutable std::vector<char> decodedData;
20
+
21
+ static OSStatus AudioFile_ReadProc(void* inClientData, SInt64 inPosition, UInt32 requestCount,
22
+ void* buffer, UInt32* actualCount)
23
+ {
24
+ const Resource& res = *static_cast<Resource*>(inClientData);
25
+ *actualCount = std::min<UInt32>(requestCount, res.size() - inPosition);
26
+ res.read(inPosition, *actualCount, buffer);
27
+ return noErr;
28
+ }
29
+
30
+ static SInt64 AudioFile_GetSizeProc(void* inClientData)
31
+ {
32
+ const Resource& res = *static_cast<Resource*>(inClientData);
33
+ return res.size();
34
+ }
35
+
36
+ public:
37
+ AudioFile(const std::wstring& filename)
38
+ {
39
+ ObjRef<NSString> utf8Filename([[NSString alloc] initWithUTF8String: wstringToUTF8(filename).c_str()]);
40
+ ObjRef<NSURL> url([[NSURL alloc] initFileURLWithPath: utf8Filename.get()]);
41
+ #ifdef GOSU_IS_IPHONE
42
+ CHECK_OS(AudioFileOpenURL((CFURLRef)url.get(), kAudioFileReadPermission, 0, &fileID));
43
+ #else
44
+ // Use FSRef for compatibility with 10.4 Tiger.
45
+ FSRef fsRef;
46
+ CFURLGetFSRef(reinterpret_cast<CFURLRef>(url.get()), &fsRef);
47
+ CHECK_OS(AudioFileOpen(&fsRef, fsRdPerm, 0, &fileID));
48
+ #endif
49
+ }
50
+
51
+ AudioFile(const Gosu::Resource& resource)
52
+ {
53
+ void* clientData = const_cast<Resource*>(&resource);
54
+ CHECK_OS(AudioFileOpenWithCallbacks(clientData, AudioFile_ReadProc, 0,
55
+ AudioFile_GetSizeProc, 0, 0, &fileID));
56
+ }
57
+
58
+ ~AudioFile()
59
+ {
60
+ AudioFileClose(fileID);
61
+ }
62
+
63
+ UInt32 getSizeOfData() const
64
+ {
65
+ UInt64 sizeOfData = 0;
66
+ UInt32 sizeOfProperty = sizeof sizeOfData;
67
+ CHECK_OS(AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &sizeOfProperty, &sizeOfData));
68
+ return sizeOfData;
69
+ }
70
+
71
+ std::pair<ALuint, ALuint> getFormatAndSampleRate() const
72
+ {
73
+ AudioStreamBasicDescription desc;
74
+ UInt32 sizeOfProperty = sizeof desc;
75
+ CHECK_OS(AudioFileGetProperty(fileID, kAudioFilePropertyDataFormat, &sizeOfProperty, &desc));
76
+ std::pair<ALuint, ALuint> formatAndSampleRate = std::make_pair(0, desc.mSampleRate);
77
+ if (desc.mChannelsPerFrame == 1)
78
+ if (desc.mBitsPerChannel == 8)
79
+ formatAndSampleRate.first = AL_FORMAT_MONO8;
80
+ else if (desc.mBitsPerChannel == 16)
81
+ formatAndSampleRate.first = AL_FORMAT_MONO16;
82
+ else if (desc.mChannelsPerFrame == 2)
83
+ if (desc.mBitsPerChannel == 8)
84
+ formatAndSampleRate.first = AL_FORMAT_STEREO8;
85
+ else if (desc.mBitsPerChannel == 16)
86
+ formatAndSampleRate.first = AL_FORMAT_STEREO16;
87
+ if (formatAndSampleRate.first == 0)
88
+ throw std::runtime_error("Invalid sample format");
89
+ return formatAndSampleRate;
90
+ }
91
+
92
+ const std::vector<char>& getDecodedData() const
93
+ {
94
+ if (decodedData.empty())
95
+ {
96
+ UInt32 sizeOfData = getSizeOfData();
97
+ decodedData.resize(sizeOfData);
98
+ CHECK_OS(AudioFileReadBytes(fileID, false, 0, &sizeOfData, &decodedData[0]));
99
+ }
100
+ return decodedData;
101
+ }
102
+ };
103
+ }
104
+
105
+ #endif
@@ -0,0 +1,486 @@
1
+ #include <GosuImpl/MacUtility.hpp>
2
+ #include <GosuImpl/Audio/AudioFileMac.hpp>
3
+ #include <GosuImpl/Audio/ALChannelManagement.hpp>
4
+
5
+ #include <Gosu/Audio.hpp>
6
+ #include <Gosu/Math.hpp>
7
+ #include <Gosu/IO.hpp>
8
+ #include <Gosu/Utility.hpp>
9
+ #include <Gosu/Platform.hpp>
10
+
11
+ #include <boost/algorithm/string.hpp>
12
+ #include <boost/lexical_cast.hpp>
13
+
14
+ #include <cassert>
15
+ #include <cstdlib>
16
+ #include <algorithm>
17
+ #include <stdexcept>
18
+ #include <vector>
19
+
20
+ #include <OpenAL/al.h>
21
+ #include <OpenAL/alc.h>
22
+
23
+ #if __LP64__ || NS_BUILD_32_LIKE_64
24
+ typedef long NSInteger;
25
+ typedef unsigned long NSUInteger;
26
+ #else
27
+ typedef int NSInteger;
28
+ typedef unsigned int NSUInteger;
29
+ #endif
30
+
31
+ using namespace std;
32
+
33
+ namespace
34
+ {
35
+ using namespace Gosu;
36
+
37
+ /*GOSU_NORETURN void throwLastALError(const char* action)
38
+ {
39
+ string message = "OpenAL error " +
40
+ boost::lexical_cast<string>(alcGetError(alDevice));
41
+ if (action)
42
+ message += " while ", message += action;
43
+ throw runtime_error(message);
44
+ }
45
+
46
+ inline void alCheck(const char* action = 0)
47
+ {
48
+ if (alcGetError(alDevice) != ALC_NO_ERROR)
49
+ throwLastALError(action);
50
+ }*/
51
+
52
+ Song* curSong = 0;
53
+ }
54
+
55
+ Gosu::Audio::Audio()
56
+ {
57
+ if (alChannelManagement)
58
+ throw std::logic_error("Multiple Gosu::Audio instances not supported");
59
+ alChannelManagement.reset(new ALChannelManagement);
60
+ }
61
+
62
+ Gosu::Audio::~Audio()
63
+ {
64
+ alChannelManagement.reset();
65
+ }
66
+
67
+ Gosu::SampleInstance::SampleInstance(int handle, int extra)
68
+ : handle(handle), extra(extra)
69
+ {
70
+ }
71
+
72
+ bool Gosu::SampleInstance::playing() const
73
+ {
74
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
75
+ if (source == ALChannelManagement::NO_SOURCE)
76
+ return false;
77
+ ALint state;
78
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
79
+ return state == AL_PLAYING;
80
+ }
81
+
82
+ bool Gosu::SampleInstance::paused() const
83
+ {
84
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
85
+ if (source == ALChannelManagement::NO_SOURCE)
86
+ return false;
87
+ ALint state;
88
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
89
+ return state == AL_PAUSED;
90
+ }
91
+
92
+ void Gosu::SampleInstance::pause()
93
+ {
94
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
95
+ if (source == ALChannelManagement::NO_SOURCE)
96
+ return;
97
+ alSourcePause(source);
98
+ }
99
+
100
+ void Gosu::SampleInstance::resume()
101
+ {
102
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
103
+ if (source == ALChannelManagement::NO_SOURCE)
104
+ return;
105
+ ALint state;
106
+ alGetSourcei(source, AL_SOURCE_STATE, &state);
107
+ if (state == AL_PAUSED)
108
+ alSourcePlay(source);
109
+ }
110
+
111
+ void Gosu::SampleInstance::stop()
112
+ {
113
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
114
+ if (source == ALChannelManagement::NO_SOURCE)
115
+ return;
116
+ alSourceStop(source);
117
+ }
118
+
119
+ void Gosu::SampleInstance::changeVolume(double volume)
120
+ {
121
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
122
+ if (source == ALChannelManagement::NO_SOURCE)
123
+ return;
124
+ alSourcef(source, AL_GAIN, volume);
125
+ }
126
+
127
+ void Gosu::SampleInstance::changePan(double pan)
128
+ {
129
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
130
+ if (source == ALChannelManagement::NO_SOURCE)
131
+ return;
132
+ // TODO: This is not the old panning behavior!
133
+ alSource3f(source, AL_POSITION, pan * 10, 0, 0);
134
+ }
135
+
136
+ void Gosu::SampleInstance::changeSpeed(double speed)
137
+ {
138
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(handle, extra);
139
+ if (source == ALChannelManagement::NO_SOURCE)
140
+ return;
141
+ alSourcef(source, AL_PITCH, speed);
142
+ }
143
+
144
+ struct Gosu::Sample::SampleData : boost::noncopyable
145
+ {
146
+ NSUInteger buffer, source;
147
+
148
+ SampleData(const AudioFile& audioFile)
149
+ {
150
+ alGenBuffers(1, &buffer);
151
+ alBufferData(buffer,
152
+ audioFile.getFormatAndSampleRate().first,
153
+ &audioFile.getDecodedData().front(),
154
+ audioFile.getDecodedData().size(),
155
+ audioFile.getFormatAndSampleRate().second);
156
+ }
157
+
158
+ ~SampleData()
159
+ {
160
+ // It's hard to free things in the right order in Ruby/Gosu.
161
+ // Make sure buffer isn't deleted after the context/device are shut down.
162
+
163
+ if (!alChannelManagement)
164
+ return;
165
+
166
+ alDeleteBuffers(1, &buffer);
167
+ }
168
+ };
169
+
170
+ Gosu::Sample::Sample(Audio& audio, const std::wstring& filename)
171
+ {
172
+ AudioFile audioFile(filename);
173
+ data.reset(new SampleData(audioFile));
174
+ }
175
+
176
+ Gosu::Sample::Sample(Audio& audio, Reader reader)
177
+ {
178
+ AudioFile audioFile(reader.resource());
179
+ data.reset(new SampleData(audioFile));
180
+ }
181
+
182
+ Gosu::Sample::~Sample()
183
+ {
184
+ }
185
+
186
+ Gosu::SampleInstance Gosu::Sample::play(double volume, double speed,
187
+ bool looping) const
188
+ {
189
+ return playPan(0, volume, speed, looping);
190
+ }
191
+
192
+ Gosu::SampleInstance Gosu::Sample::playPan(double pan, double volume,
193
+ double speed, bool looping) const
194
+ {
195
+ std::pair<int, int> channelAndToken = alChannelManagement->reserveChannel();
196
+ if (channelAndToken.first == ALChannelManagement::NO_FREE_CHANNEL)
197
+ return Gosu::SampleInstance(channelAndToken.first, channelAndToken.second);
198
+
199
+ NSUInteger source = alChannelManagement->sourceIfStillPlaying(channelAndToken.first,
200
+ channelAndToken.second);
201
+ assert(source != ALChannelManagement::NO_SOURCE);
202
+ alSourcei(source, AL_BUFFER, data->buffer);
203
+ // TODO: This is not the old panning behavior!
204
+ alSource3f(source, AL_POSITION, pan * 10, 0, 0);
205
+ alSourcef(source, AL_GAIN, volume);
206
+ alSourcef(source, AL_PITCH, speed);
207
+ alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
208
+ alSourcePlay(source);
209
+
210
+ return Gosu::SampleInstance(channelAndToken.first, channelAndToken.second);
211
+ }
212
+
213
+ class Gosu::Song::BaseData : boost::noncopyable
214
+ {
215
+ // double volume_;
216
+ //
217
+ //protected:
218
+ // BaseData() : volume_(1) {}
219
+ // virtual void applyVolume() = 0;
220
+ //
221
+ //public:
222
+ // virtual ~BaseData() {}
223
+ //
224
+ // virtual void play(bool looping) = 0;
225
+ // virtual void pause() = 0;
226
+ // virtual bool paused() const = 0;
227
+ // virtual void stop() = 0;
228
+ //
229
+ // double volume() const
230
+ // {
231
+ // return volume_;
232
+ // }
233
+ //
234
+ // void changeVolume(double volume)
235
+ // {
236
+ // volume_ = clamp(volume, 0.0, 1.0);
237
+ // applyVolume();
238
+ // }
239
+ };
240
+
241
+ //class Gosu::Song::StreamData : public BaseData
242
+ //{
243
+ // FSOUND_STREAM* stream;
244
+ // int handle;
245
+ // std::vector<char> buffer;
246
+ //
247
+ // static signed char F_CALLBACKAPI endSongCallback(FSOUND_STREAM*, void*,
248
+ // int, void* self)
249
+ // {
250
+ // curSong = 0;
251
+ // static_cast<StreamData*>(self)->handle = -1;
252
+ // return 0;
253
+ // }
254
+ //
255
+ //public:
256
+ // StreamData(Gosu::Reader reader)
257
+ // : stream(0), handle(-1)
258
+ // {
259
+ // buffer.resize(reader.resource().size() - reader.position());
260
+ // reader.read(&buffer[0], buffer.size());
261
+ //
262
+ //// Disabled for licensing reasons.
263
+ //// If you have a license to play MP3 files, compile with GOSU_ALLOW_MP3.
264
+ //#ifndef GOSU_ALLOW_MP3
265
+ // if (buffer.size() > 2 &&
266
+ // ((buffer[0] == '\xff' && (buffer[1] & 0xfe) == '\xfa') ||
267
+ // (buffer[0] == 'I' && buffer[1] == 'D' && buffer[2] == '3')))
268
+ // {
269
+ // throw std::runtime_error("MP3 file playback not allowed");
270
+ // }
271
+ //#endif
272
+ //
273
+ // stream = FSOUND_Stream_Open(&buffer[0], FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL,
274
+ // 0, buffer.size());
275
+ // if (stream == 0)
276
+ // throwLastFMODError();
277
+ //
278
+ // FSOUND_Stream_SetEndCallback(stream, endSongCallback, this);
279
+ // }
280
+ //
281
+ // ~StreamData()
282
+ // {
283
+ // // TODO: Should be checked for earlier, as play would crash too.
284
+ // // This is just because Ruby's GC will free objects in a weird
285
+ // // order.
286
+ // if (!fmodInitialized)
287
+ // return;
288
+ //
289
+ // if (stream != 0)
290
+ // FSOUND_Stream_Close(stream);
291
+ // }
292
+ //
293
+ // void play(bool looping)
294
+ // {
295
+ // if (handle == -1)
296
+ // {
297
+ // handle = FSOUND_Stream_Play(FSOUND_FREE, stream);
298
+ // FSOUND_Stream_SetLoopCount(stream, looping ? -1 : 0);
299
+ // }
300
+ // else if (paused())
301
+ // FSOUND_SetPaused(handle, 0);
302
+ // applyVolume();
303
+ // }
304
+ //
305
+ // void pause()
306
+ // {
307
+ // if (handle != -1)
308
+ // FSOUND_SetPaused(handle, 1);
309
+ // }
310
+ //
311
+ // bool paused() const
312
+ // {
313
+ // return handle != -1 && FSOUND_GetPaused(handle);
314
+ // }
315
+ //
316
+ // void stop()
317
+ // {
318
+ // fmodCheck(FSOUND_Stream_Stop(stream));
319
+ // handle = -1; // The end callback is NOT being called!
320
+ // }
321
+ //
322
+ // void applyVolume()
323
+ // {
324
+ // if (handle != -1)
325
+ // FSOUND_SetVolume(handle, static_cast<int>(volume() * 255));
326
+ // }
327
+ //};
328
+ //
329
+ //class Gosu::Song::ModuleData : public Gosu::Song::BaseData
330
+ //{
331
+ // FMUSIC_MODULE* module_;
332
+ //
333
+ //public:
334
+ // ModuleData(Reader reader)
335
+ // : module_(0)
336
+ // {
337
+ // std::vector<char> buffer(reader.resource().size() - reader.position());
338
+ // reader.read(&buffer[0], buffer.size());
339
+ //
340
+ // module_ = FMUSIC_LoadSongEx(&buffer[0], 0, buffer.size(),
341
+ // FSOUND_LOADMEMORY | FSOUND_LOOP_OFF, 0, 0);
342
+ // if (module_ == 0)
343
+ // throwLastFMODError();
344
+ // }
345
+ //
346
+ // ~ModuleData()
347
+ // {
348
+ // // TODO: Should be checked for earlier, as play would crash too.
349
+ // // This is just because Ruby's GC will free objects in a weird
350
+ // // order.
351
+ // if (!fmodInitialized)
352
+ // return;
353
+ //
354
+ // if (module_ != 0)
355
+ // FMUSIC_FreeSong(module_);
356
+ // }
357
+ //
358
+ // void play(bool looping)
359
+ // {
360
+ // if (paused())
361
+ // FMUSIC_SetPaused(module_, 0);
362
+ // else
363
+ // FMUSIC_PlaySong(module_);
364
+ // FMUSIC_SetLooping(module_, looping);
365
+ // applyVolume();
366
+ // }
367
+ //
368
+ // void pause()
369
+ // {
370
+ // FMUSIC_SetPaused(module_, 1);
371
+ // }
372
+ //
373
+ // bool paused() const
374
+ // {
375
+ // return FMUSIC_GetPaused(module_);
376
+ // }
377
+ //
378
+ // void stop()
379
+ // {
380
+ // fmodCheck(FMUSIC_StopSong(module_));
381
+ // FMUSIC_SetPaused(module_, false);
382
+ // }
383
+ //
384
+ // void applyVolume()
385
+ // {
386
+ // // Weird as it may seem, the FMOD doc really says volume can
387
+ // // be 0 to 256, *inclusive*, for this function.
388
+ // FMUSIC_SetMasterVolume(module_, static_cast<int>(volume() * 256.0));
389
+ // }
390
+ //};
391
+ //
392
+ Gosu::Song::Song(Audio& audio, const std::wstring& filename)
393
+ {
394
+ // Buffer buf;
395
+ // loadFile(buf, filename);
396
+ // Type type = stStream;
397
+ //
398
+ // using boost::iends_with;
399
+ // if (iends_with(filename, ".mod") || iends_with(filename, ".mid") ||
400
+ // iends_with(filename, ".s3m") || iends_with(filename, ".it") ||
401
+ // iends_with(filename, ".xm"))
402
+ // {
403
+ // type = stModule;
404
+ // }
405
+ //
406
+ // // Forward.
407
+ // Song(audio, type, buf.frontReader()).data.swap(data);
408
+ }
409
+
410
+ Gosu::Song::Song(Audio& audio, Type type, Reader reader)
411
+ {
412
+ // switch (type)
413
+ // {
414
+ // case stStream:
415
+ // data.reset(new StreamData(reader));
416
+ // break;
417
+ //
418
+ // case stModule:
419
+ // data.reset(new ModuleData(reader));
420
+ // break;
421
+ //
422
+ // default:
423
+ // throw std::logic_error("Invalid song type");
424
+ // }
425
+ }
426
+
427
+ Gosu::Song::~Song()
428
+ {
429
+ if (alChannelManagement)
430
+ stop();
431
+ }
432
+
433
+ Gosu::Song* Gosu::Song::currentSong()
434
+ {
435
+ return curSong;
436
+ }
437
+
438
+ void Gosu::Song::play(bool looping)
439
+ {
440
+ // if (curSong && curSong != this)
441
+ // {
442
+ // curSong->stop();
443
+ // assert(curSong == 0);
444
+ // }
445
+ //
446
+ // data->play(looping);
447
+ // curSong = this; // may be redundant
448
+ }
449
+
450
+ void Gosu::Song::pause()
451
+ {
452
+ // if (curSong == this)
453
+ // data->pause(); // may be redundant
454
+ }
455
+
456
+ bool Gosu::Song::paused() const
457
+ {
458
+ // return curSong == this && data->paused();
459
+ return false;
460
+ }
461
+
462
+ void Gosu::Song::stop()
463
+ {
464
+ // if (curSong == this)
465
+ // {
466
+ // data->stop();
467
+ // curSong = 0;
468
+ // }
469
+ }
470
+
471
+ bool Gosu::Song::playing() const
472
+ {
473
+ // return curSong == this && !data->paused();
474
+ return false;
475
+ }
476
+
477
+ double Gosu::Song::volume() const
478
+ {
479
+ // return data->volume();
480
+ return 0;
481
+ }
482
+
483
+ void Gosu::Song::changeVolume(double volume)
484
+ {
485
+ // data->changeVolume(volume);
486
+ }