gosu 0.7.12 → 0.7.13

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