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.
- data/Gosu/Async.hpp +10 -8
- data/Gosu/Audio.hpp +6 -4
- data/Gosu/AutoLink.hpp +0 -0
- data/Gosu/Bitmap.hpp +4 -5
- data/Gosu/ButtonsX.hpp +1 -1
- data/Gosu/Color.hpp +8 -8
- data/Gosu/Font.hpp +2 -2
- data/Gosu/Graphics.hpp +4 -6
- data/Gosu/IO.hpp +11 -4
- data/Gosu/Image.hpp +9 -7
- data/Gosu/ImageData.hpp +10 -4
- data/Gosu/Input.hpp +4 -5
- data/Gosu/Sockets.hpp +10 -12
- data/Gosu/TR1.hpp +44 -0
- data/Gosu/TextInput.hpp +2 -2
- data/Gosu/Version.hpp +2 -2
- data/Gosu/WinUtility.hpp +5 -6
- data/Gosu/Window.hpp +5 -6
- data/GosuImpl/Async.cpp +4 -3
- data/GosuImpl/Audio/ALChannelManagement.hpp +23 -11
- data/GosuImpl/Audio/AudioFile.hpp +11 -3
- data/GosuImpl/Audio/AudioOpenAL.cpp +613 -0
- data/GosuImpl/Audio/AudioOpenAL.mm +1 -605
- data/GosuImpl/Audio/AudioSDL.cpp +12 -14
- data/GosuImpl/Audio/AudioToolboxFile.hpp +0 -1
- data/GosuImpl/Audio/OggFile.hpp +2 -0
- data/GosuImpl/Audio/SndFile.hpp +158 -0
- data/GosuImpl/DirectoriesWin.cpp +18 -18
- data/GosuImpl/Graphics/BitmapApple.mm +17 -19
- data/GosuImpl/Graphics/BitmapBMP.cpp +7 -2
- data/GosuImpl/Graphics/BitmapFreeImage.cpp +11 -12
- data/GosuImpl/Graphics/BitmapGDIplus.cpp +35 -31
- data/GosuImpl/Graphics/BitmapUtils.cpp +1 -1
- data/GosuImpl/Graphics/BlockAllocator.cpp +7 -8
- data/GosuImpl/Graphics/BlockAllocator.hpp +3 -4
- data/GosuImpl/Graphics/Common.hpp +3 -2
- data/GosuImpl/Graphics/DrawOp.hpp +2 -3
- data/GosuImpl/Graphics/DrawOpQueue.hpp +28 -20
- data/GosuImpl/Graphics/Font.cpp +13 -13
- data/GosuImpl/Graphics/FormattedString.hpp +93 -86
- data/GosuImpl/Graphics/Graphics.cpp +16 -14
- data/GosuImpl/Graphics/Image.cpp +7 -3
- data/GosuImpl/Graphics/LargeImageData.hpp +4 -5
- data/GosuImpl/Graphics/Macro.hpp +11 -11
- data/GosuImpl/Graphics/RenderState.hpp +5 -4
- data/GosuImpl/Graphics/TexChunk.cpp +3 -3
- data/GosuImpl/Graphics/TexChunk.hpp +4 -4
- data/GosuImpl/Graphics/Text.cpp +29 -30
- data/GosuImpl/Graphics/TextMac.cpp +9 -7
- data/GosuImpl/Graphics/TextTTFWin.cpp +3 -1
- data/GosuImpl/Graphics/TextTouch.mm +0 -1
- data/GosuImpl/Graphics/TextUnix.cpp +12 -4
- data/GosuImpl/Graphics/TextWin.cpp +4 -2
- data/GosuImpl/Graphics/Texture.cpp +7 -6
- data/GosuImpl/Graphics/Texture.hpp +3 -4
- data/GosuImpl/InputMac.mm +12 -15
- data/GosuImpl/InputTouch.mm +3 -3
- data/GosuImpl/InputWin.cpp +149 -159
- data/GosuImpl/InputX.cpp +0 -0
- data/GosuImpl/MacUtility.hpp +9 -4
- data/GosuImpl/RubyGosu.swg +38 -43
- data/GosuImpl/RubyGosu_wrap.cxx +89 -96
- data/GosuImpl/Sockets/CommSocket.cpp +5 -5
- data/GosuImpl/Sockets/Sockets.hpp +4 -4
- data/GosuImpl/TimingApple.cpp +2 -3
- data/GosuImpl/Utility.cpp +18 -0
- data/GosuImpl/WinMain.cpp +0 -1
- data/GosuImpl/WinUtility.cpp +2 -2
- data/GosuImpl/WindowMac.mm +20 -17
- data/GosuImpl/WindowTouch.mm +8 -7
- data/GosuImpl/WindowWin.cpp +12 -7
- data/GosuImpl/WindowX.cpp +67 -18
- data/lib/gosu.rb +14 -12
- data/linux/extconf.rb +11 -6
- metadata +8 -7
- data/GosuImpl/Audio/AudioAudiere.cpp +0 -448
- data/GosuImpl/RubyGosu_DllMain.cxx +0 -31
data/Gosu/TR1.hpp
ADDED
@@ -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
|
data/Gosu/TextInput.hpp
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
#include <Gosu/Fwd.hpp>
|
8
8
|
#include <Gosu/Platform.hpp>
|
9
|
-
#include <
|
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
|
-
|
26
|
+
const std::auto_ptr<Impl> pimpl;
|
27
27
|
|
28
28
|
public:
|
29
29
|
TextInput();
|
data/Gosu/Version.hpp
CHANGED
@@ -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
|
7
|
-
#define GOSU_VERSION "0.7.
|
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" \
|
data/Gosu/WinUtility.hpp
CHANGED
@@ -8,8 +8,7 @@
|
|
8
8
|
|
9
9
|
#include <windows.h>
|
10
10
|
#include <Gosu/Platform.hpp>
|
11
|
-
#include <
|
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
|
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
|
60
|
+
//! to a std::tr1::shared_ptr.
|
62
61
|
template<typename T>
|
63
|
-
inline
|
62
|
+
inline std::tr1::shared_ptr<T> shareComPtr(T* ptr)
|
64
63
|
{
|
65
|
-
return
|
64
|
+
return std::tr1::shared_ptr<T>(ptr, releaseComPtr<T>);
|
66
65
|
}
|
67
66
|
|
68
67
|
//! Returns the executable's filename.
|
data/Gosu/Window.hpp
CHANGED
@@ -7,9 +7,8 @@
|
|
7
7
|
#include <Gosu/Fwd.hpp>
|
8
8
|
#include <Gosu/Platform.hpp>
|
9
9
|
#include <Gosu/Input.hpp>
|
10
|
-
#include <
|
11
|
-
#include <
|
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
|
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
|
-
|
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
|
102
|
+
typedef std::tr1::shared_ptr<std::tr1::function<void()> > SharedContext;
|
104
103
|
SharedContext createSharedContext();
|
105
104
|
#endif
|
106
105
|
|
data/GosuImpl/Async.cpp
CHANGED
@@ -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
|
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<
|
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
|
-
#
|
2
|
-
#
|
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
|
-
#
|
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
|
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<
|
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
|
-
|
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
|
-
#
|
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
|
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
|
+
}
|