gosu 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Gosu/Audio.hpp +23 -25
  3. data/Gosu/Graphics.hpp +16 -12
  4. data/Gosu/Image.hpp +3 -0
  5. data/Gosu/Version.hpp +2 -2
  6. data/lib/gosu.rb +2 -2
  7. data/lib/gosu/compat.rb +1 -1
  8. data/lib/gosu/patches.rb +5 -0
  9. data/lib/gosu/swig_patches.rb +1 -1
  10. data/rdoc/gosu.rb +10 -10
  11. data/src/Audio.cpp +93 -228
  12. data/src/AudioImpl.cpp +94 -0
  13. data/src/AudioImpl.hpp +33 -0
  14. data/src/AudioToolboxFile.hpp +14 -18
  15. data/src/Bitmap.cpp +36 -30
  16. data/src/BitmapIO.cpp +14 -23
  17. data/src/BlockAllocator.cpp +7 -10
  18. data/src/BlockAllocator.hpp +2 -4
  19. data/src/Channel.cpp +89 -0
  20. data/src/Color.cpp +4 -9
  21. data/src/DirectoriesApple.cpp +13 -13
  22. data/src/DirectoriesUnix.cpp +8 -7
  23. data/src/DirectoriesWin.cpp +12 -11
  24. data/src/EmptyImageData.hpp +54 -0
  25. data/src/FileUnix.cpp +12 -9
  26. data/src/FileWin.cpp +8 -7
  27. data/src/Font.cpp +12 -13
  28. data/src/FormattedString.cpp +237 -0
  29. data/src/FormattedString.hpp +14 -265
  30. data/src/GosuViewController.cpp +2 -5
  31. data/src/Graphics.cpp +38 -39
  32. data/src/IO.cpp +11 -10
  33. data/src/Image.cpp +16 -9
  34. data/src/Input.cpp +16 -15
  35. data/src/InputUIKit.cpp +8 -7
  36. data/src/Macro.cpp +11 -11
  37. data/src/Math.cpp +9 -8
  38. data/src/RubyGosu.cxx +129 -99
  39. data/src/TextApple.cpp +19 -13
  40. data/src/TextInput.cpp +23 -22
  41. data/src/TextWin.cpp +17 -19
  42. data/src/Texture.cpp +15 -10
  43. data/src/Transform.cpp +13 -17
  44. data/src/Utility.cpp +3 -2
  45. data/src/UtilityApple.cpp +10 -11
  46. data/src/UtilityWin.cpp +2 -1
  47. data/src/Version.cpp +5 -4
  48. data/src/WinMain.cpp +3 -3
  49. data/src/WinUtility.cpp +7 -6
  50. data/src/Window.cpp +11 -10
  51. data/src/WindowUIKit.cpp +9 -8
  52. data/src/stb_image.h +782 -480
  53. data/src/stb_image_write.h +425 -15
  54. data/src/stb_vorbis.c +82 -32
  55. metadata +8 -4
  56. data/src/ALChannelManagement.hpp +0 -119
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e780ad64700f539c7956253b0900d69ab828562
4
- data.tar.gz: bf9eea3a88a216860bac22d6cb2b16dddbe363b1
3
+ metadata.gz: 41378387cae1afdcfbfeab14e6adea7ad61aa1e6
4
+ data.tar.gz: b83153c5f9208a2a201154f012c0135f25238b3e
5
5
  SHA512:
6
- metadata.gz: cae8120cd63eac622abf7a59a5c8e2735dc8d35f5c20a91f4189800d577736ff5eca92e22f1b295165f833f1dc546ada16fb3979a5c5624853242841baee4d8b
7
- data.tar.gz: fee3c74049220d40390106f33f444d739b4d614bcdc253225aa6fc2445b936c0de5650c51e9bc34e3ff2c242e0e6d5a5ba782d8558eafbccfcb9f6a7b40e9b35
6
+ metadata.gz: fccec2f1c9755fe8133d02a4b3f2e31789681938a59a41893a6d247ff29a3fdb68d1c5848390c63dfdb490259b552cd05c2d1833cfdd11533d28d1be07817be2
7
+ data.tar.gz: 6542882a5ef52f9360444efe0513b3c31b51912c5c21a18be5cc76e6b1bdf223e5981755afe860cdd45383b0db1fd52cd60028f690450ed33ed25d127dfc9049
@@ -11,41 +11,36 @@
11
11
 
12
12
  namespace Gosu
13
13
  {
14
- //! An instance of a Sample playing. Can be used to stop sounds dynamically,
15
- //! or to check if they are finished.
16
- //! It is recommended that you throw away sample instances if possible,
17
- //! as they could accidentally refer to other sounds being played after
18
- //! a very long time has passed.
19
- class SampleInstance
14
+ //! Sample::play returns a Channel that represents the sound currently being played.
15
+ //! This object can be used to stop sounds dynamically, or to check whether they have finished.
16
+ class Channel
20
17
  {
21
- int handle, extra;
22
- bool alive() const;
18
+ mutable int channel, token;
23
19
 
24
20
  public:
25
- //! Called internally by Sample, do not use.
26
- SampleInstance(int handle, int extra);
27
-
21
+ //! For internal use only.
22
+ Channel(int channel, int token);
23
+
24
+ int current_channel() const;
25
+
28
26
  bool playing() const;
29
27
  bool paused() const;
30
- //! Pauses this instance to be resumed afterwards. It will still keep a channel filled while
31
- //! paused.
28
+ //! Pauses this instance to be resumed afterwards.
29
+ //! It will still occupy an audio channel while paused.
32
30
  void pause();
33
31
  void resume();
34
32
  //! Stops this instance of a sound being played.
35
33
  //! Calling this twice, or too late, does not do any harm.
36
34
  void stop();
37
35
 
38
- //! \param volume Can be anything from 0.0 (silence) to 1.0 (full
39
- //! volume).
40
- void change_volume(double volume);
36
+ //! \param volume Can be anything from 0.0 (silence) to 1.0 (full volume).
37
+ void set_volume(double volume);
41
38
  //! \param pan Can be anything from -1.0 (left) to 1.0 (right).
42
- void change_pan(double pan);
43
- //! \param speed Playback speed is only limited by FMOD's
44
- //! capabilities and can accept very high or low values. Use 1.0 for
45
- //! normal playback speed.
46
- void change_speed(double speed);
39
+ void set_pan(double pan);
40
+ //! \param speed Use 1.0 for normal playback speed.
41
+ void set_speed(double speed);
47
42
  };
48
-
43
+
49
44
  //! A sample is a short sound that is completely loaded in memory, can be
50
45
  //! played multiple times at once and offers very flexible playback
51
46
  //! parameters. Use samples for everything that's not music.
@@ -55,6 +50,9 @@ namespace Gosu
55
50
  std::shared_ptr<SampleData> data;
56
51
 
57
52
  public:
53
+ //! Constructs an empty sample that acts as if the song had a length of 0.
54
+ Sample();
55
+
58
56
  //! Constructs a sample that can be played on the specified audio
59
57
  //! system and loads the sample from a file.
60
58
  explicit Sample(const std::string& filename);
@@ -69,7 +67,7 @@ namespace Gosu
69
67
  //! \param speed Playback speed is only limited by the underlying audio library,
70
68
  //! and can accept very high or low values. Use 1.0 for
71
69
  //! normal playback speed.
72
- SampleInstance play(double volume = 1, double speed = 1, bool looping = false) const;
70
+ Channel play(double volume = 1, double speed = 1, bool looping = false) const;
73
71
 
74
72
  //! Plays the sample with panning. Even if pan is 0.0, the sample will
75
73
  //! not be as loud as if it were played by calling play() due to the
@@ -80,7 +78,7 @@ namespace Gosu
80
78
  //! \param speed Playback speed is only limited by by the underlying audio library,
81
79
  //! and can accept very high
82
80
  //! or low values. Use 1.0 for normal playback speed.
83
- SampleInstance play_pan(double pan, double volume = 1, double speed = 1,
81
+ Channel play_pan(double pan, double volume = 1, double speed = 1,
84
82
  bool looping = false) const;
85
83
  };
86
84
 
@@ -133,7 +131,7 @@ namespace Gosu
133
131
  double volume() const;
134
132
  //! \param volume Can be anything from 0.0 (silence) to 1.0 (full
135
133
  //! volume).
136
- void change_volume(double volume);
134
+ void set_volume(double volume);
137
135
 
138
136
  //! Called every tick by Window for management purposes.
139
137
  static void update();
@@ -32,7 +32,7 @@ namespace Gosu
32
32
  ~Graphics();
33
33
 
34
34
  void set_resolution(unsigned logical_width, unsigned logical_height,
35
- double horizontal_black_bar_width = 0, double vertical_black_bar_height = 0);
35
+ double black_bar_width = 0, double black_bar_height = 0);
36
36
 
37
37
  unsigned width() const;
38
38
  unsigned height() const;
@@ -75,19 +75,22 @@ namespace Gosu
75
75
  //! or end point. Please only use this for debugging purposes. Otherwise, use a quad or
76
76
  //! image to simulate lines, or contribute a better draw_line to Gosu.
77
77
  static void draw_line(double x1, double y1, Color c1,
78
- double x2, double y2, Color c2,
79
- ZPos z, AlphaMode mode = AM_DEFAULT);
78
+ double x2, double y2, Color c2,
79
+ ZPos z, AlphaMode mode = AM_DEFAULT);
80
80
 
81
81
  static void draw_triangle(double x1, double y1, Color c1,
82
- double x2, double y2, Color c2,
83
- double x3, double y3, Color c3,
84
- ZPos z, AlphaMode mode = AM_DEFAULT);
82
+ double x2, double y2, Color c2,
83
+ double x3, double y3, Color c3,
84
+ ZPos z, AlphaMode mode = AM_DEFAULT);
85
85
 
86
86
  static void draw_quad(double x1, double y1, Color c1,
87
- double x2, double y2, Color c2,
88
- double x3, double y3, Color c3,
89
- double x4, double y4, Color c4,
90
- ZPos z, AlphaMode mode = AM_DEFAULT);
87
+ double x2, double y2, Color c2,
88
+ double x3, double y3, Color c3,
89
+ double x4, double y4, Color c4,
90
+ ZPos z, AlphaMode mode = AM_DEFAULT);
91
+
92
+ static void draw_rect(double x, double y, double width, double height,
93
+ Color c, ZPos z, AlphaMode mode = AM_DEFAULT);
91
94
 
92
95
  //! For internal use only.
93
96
  void set_physical_resolution(unsigned physical_width, unsigned physical_height);
@@ -97,7 +100,8 @@ namespace Gosu
97
100
 
98
101
  //! Turns a portion of a bitmap into something that can be drawn on a Graphics object.
99
102
  static std::unique_ptr<ImageData> create_image(const Bitmap& src,
100
- unsigned src_x, unsigned src_y, unsigned src_width, unsigned src_height,
101
- unsigned image_flags);
103
+ unsigned src_x, unsigned src_y,
104
+ unsigned src_width, unsigned src_height,
105
+ unsigned image_flags);
102
106
  };
103
107
  }
@@ -17,6 +17,9 @@ namespace Gosu
17
17
  std::shared_ptr<ImageData> data_;
18
18
 
19
19
  public:
20
+ //! Creates an empty image. It will have a width and height of 0, and not contain anything.
21
+ Image();
22
+
20
23
  //! Loads an image from a given filename.
21
24
  //!
22
25
  //! A color key of #ff00ff is automatically applied to BMP image files.
@@ -3,8 +3,8 @@
3
3
  #include <string>
4
4
 
5
5
  #define GOSU_MAJOR_VERSION 0
6
- #define GOSU_MINOR_VERSION 12
7
- #define GOSU_POINT_VERSION 1
6
+ #define GOSU_MINOR_VERSION 13
7
+ #define GOSU_POINT_VERSION 0
8
8
 
9
9
  namespace Gosu
10
10
  {
@@ -7,14 +7,14 @@ if RUBY_PLATFORM =~ /mswin$|mingw32|mingw64|win32\-|\-win32/
7
7
 
8
8
  begin
9
9
  # Make DLLs available as shown here:
10
- # https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers
10
+ # https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers
11
11
  require 'ruby_installer'
12
12
  RubyInstaller::Runtime.add_dll_directory(binary_path)
13
13
  rescue LoadError
14
14
  # Add this gem to the PATH on Windows so that bundled DLLs can be found.
15
15
  # When running through Ocra on Windows, we need to be careful to preserve the ENV["PATH"]
16
16
  # encoding (see #385).
17
- path_encoding = ENV["PATH"].encoding
17
+ path_encoding = ENV["PATH"].encoding
18
18
  ENV["PATH"] = "#{binary_path.encode(path_encoding)};#{ENV["PATH"]}"
19
19
  end
20
20
 
@@ -143,7 +143,7 @@ class Gosu::Window
143
143
  end
144
144
 
145
145
  # Instance methods that have been turned into module methods.
146
- %w(draw_line draw_triangle draw_quad
146
+ %w(draw_line draw_triangle draw_quad draw_rect
147
147
  flush gl clip_to record
148
148
  transform translate rotate scale
149
149
  button_id_to_char char_to_button_id button_down?).each do |method|
@@ -45,6 +45,11 @@ module Gosu
45
45
  end
46
46
  end
47
47
 
48
+ module Gosu
49
+ # Backwards compatibility: Channel was called SampleInstance before Gosu 0.13.0.
50
+ SampleInstance = Channel
51
+ end
52
+
48
53
  class Gosu::Window
49
54
  # Call Thread.pass every tick, which may or may not be necessary for friendly co-existence with
50
55
  # Ruby's Thread class.
@@ -30,7 +30,7 @@ class Gosu::Window
30
30
  rescue Exception => e
31
31
  # Exit the message loop naturally, then re-throw during the next tick.
32
32
  @_exception = e
33
- close
33
+ close!
34
34
  false
35
35
  end
36
36
  end
@@ -506,9 +506,9 @@ module Gosu
506
506
  ##
507
507
  # Plays the sample without panning.
508
508
  #
509
- # @return [SampleInstance]
510
- # @param volume [Float] see {SampleInstance#volume=}
511
- # @param speed [Float] see {SampleInstance#speed=}
509
+ # @return [Channel]
510
+ # @param volume [Float] see {Channel#volume=}
511
+ # @param speed [Float] see {Channel#speed=}
512
512
  # @param looping [true, false] whether the sample should play in a loop. If you pass true, be sure to store the return value of this method so that you can later stop the looping sound.
513
513
  #
514
514
  # @see #play_pan
@@ -517,10 +517,10 @@ module Gosu
517
517
  ##
518
518
  # Plays the sample with panning.
519
519
  #
520
- # @return [SampleInstance]
521
- # @param pan [Float] see {SampleInstance#pan=}
522
- # @param volume [Float] see {SampleInstance#volume=}
523
- # @param speed [Float] see {SampleInstance#speed=}
520
+ # @return [Channel]
521
+ # @param pan [Float] see {Channel#pan=}
522
+ # @param volume [Float] see {Channel#volume=}
523
+ # @param speed [Float] see {Channel#speed=}
524
524
  # @param looping [true, false] whether the sample should play in a loop. If you pass true, be sure to store the return value of this method so that you can later stop the looping sound.
525
525
  #
526
526
  # @see #play
@@ -528,10 +528,10 @@ module Gosu
528
528
  end
529
529
 
530
530
  ##
531
- # An instance of a {Gosu::Sample} playing. Can be used to stop sounds dynamically, or to check if they are finished.
531
+ # {Sample#play} returns a Channel that represents the sound currently being played.
532
532
  #
533
- # It is recommended to throw away sample instances as soon as possible, as holding onto them for a long time can prevent unneeded samples being properly disposed.
534
- class SampleInstance
533
+ # This object can be used to stop sounds dynamically, or to check whether they have finished.
534
+ class Channel
535
535
  ##
536
536
  # Sets the playback volume, in the range [0.0; 1.0], where 0 is completely silent and 1 is full volume. Values outside of this range will be clamped to [0.0; 1.0].
537
537
  # @param [Float]
@@ -1,4 +1,4 @@
1
- #include "ALChannelManagement.hpp"
1
+ #include "AudioImpl.hpp"
2
2
  #include "OggFile.hpp"
3
3
  #include <Gosu/Audio.hpp>
4
4
  #include <Gosu/Math.hpp>
@@ -8,19 +8,12 @@
8
8
  #include <cassert>
9
9
  #include <cstdlib>
10
10
  #include <algorithm>
11
- #include <memory>
12
- #include <stdexcept>
13
- #include <vector>
14
11
 
15
12
  #ifdef GOSU_IS_MAC
16
- #include <OpenAL/al.h>
17
- #include <OpenAL/alc.h>
18
13
  #import <Foundation/Foundation.h>
19
14
  #include "AudioToolboxFile.hpp"
20
15
  #define WAVE_FILE AudioToolboxFile
21
16
  #else
22
- #include <AL/al.h>
23
- #include <AL/alc.h>
24
17
  #include "MPEGFile.hpp"
25
18
  #include "SndFile.hpp"
26
19
  #define WAVE_FILE SndFile
@@ -32,116 +25,23 @@
32
25
 
33
26
  using namespace std;
34
27
 
35
- namespace
28
+ static bool is_ogg_file(Gosu::Reader reader)
36
29
  {
37
- bool is_ogg_file(Gosu::Reader reader)
38
- {
39
- char magic_bytes[4];
40
- reader.read(magic_bytes, 4);
41
- return magic_bytes[0] == 'O' && magic_bytes[1] == 'g' &&
42
- magic_bytes[2] == 'g' && magic_bytes[3] == 'S';
43
- }
44
-
45
- bool is_ogg_file(const string& filename)
46
- {
47
- Gosu::File file(filename);
48
- return is_ogg_file(file.front_reader());
49
- }
50
-
51
- Gosu::Song* cur_song = nullptr;
52
- bool cur_song_looping;
53
- }
54
-
55
- #ifdef GOSU_IS_MAC
56
- #define CONSTRUCTOR_BEGIN \
57
- @autoreleasepool { \
58
- if (!al_channel_management.get()) \
59
- al_channel_management.reset(new ALChannelManagement)
60
- #define CONSTRUCTOR_END \
61
- }
62
- #else
63
- #define CONSTRUCTOR_BEGIN \
64
- if (!al_channel_management.get()) \
65
- al_channel_management.reset(new ALChannelManagement)
66
- #define CONSTRUCTOR_END
67
- #endif
68
-
69
- Gosu::SampleInstance::SampleInstance(int handle, int extra)
70
- : handle(handle), extra(extra)
71
- {
72
- }
73
-
74
- bool Gosu::SampleInstance::playing() const
75
- {
76
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
77
- if (source == ALChannelManagement::NO_SOURCE) return false;
30
+ if (reader.resource().size() < 4) return false;
78
31
 
79
- ALint state;
80
- alGetSourcei(source, AL_SOURCE_STATE, &state);
81
- return state == AL_PLAYING;
32
+ char bytes[4];
33
+ reader.read(bytes, 4);
34
+ return bytes[0] == 'O' && bytes[1] == 'g' && bytes[2] == 'g' && bytes[3] == 'S';
82
35
  }
83
36
 
84
- bool Gosu::SampleInstance::paused() const
37
+ static bool is_ogg_file(const string& filename)
85
38
  {
86
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
87
- if (source == ALChannelManagement::NO_SOURCE) return false;
88
-
89
- ALint state;
90
- alGetSourcei(source, AL_SOURCE_STATE, &state);
91
- return state == AL_PAUSED;
39
+ Gosu::File file(filename);
40
+ return is_ogg_file(file.front_reader());
92
41
  }
93
42
 
94
- void Gosu::SampleInstance::pause()
95
- {
96
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
97
- if (source == ALChannelManagement::NO_SOURCE) return;
98
-
99
- alSourcePause(source);
100
- }
101
-
102
- void Gosu::SampleInstance::resume()
103
- {
104
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
105
- if (source == ALChannelManagement::NO_SOURCE) return;
106
-
107
- ALint state;
108
- alGetSourcei(source, AL_SOURCE_STATE, &state);
109
- if (state == AL_PAUSED) {
110
- alSourcePlay(source);
111
- }
112
- }
113
-
114
- void Gosu::SampleInstance::stop()
115
- {
116
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
117
- if (source == ALChannelManagement::NO_SOURCE) return;
118
-
119
- alSourceStop(source);
120
- }
121
-
122
- void Gosu::SampleInstance::change_volume(double volume)
123
- {
124
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
125
- if (source == ALChannelManagement::NO_SOURCE) return;
126
-
127
- alSourcef(source, AL_GAIN, max(volume, 0.0));
128
- }
129
-
130
- void Gosu::SampleInstance::change_pan(double pan)
131
- {
132
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
133
- if (source == ALChannelManagement::NO_SOURCE) return;
134
-
135
- alSource3f(source, AL_POSITION, pan * 10, 0, 0);
136
- }
137
-
138
- void Gosu::SampleInstance::change_speed(double speed)
139
- {
140
- ALuint source = al_channel_management->source_if_still_playing(handle, extra);
141
- if (source == ALChannelManagement::NO_SOURCE) return;
142
-
143
- alSourcef(source, AL_PITCH, speed);
144
- }
43
+ static Gosu::Song* cur_song = nullptr;
44
+ static bool cur_song_looping;
145
45
 
146
46
  struct Gosu::Sample::SampleData
147
47
  {
@@ -149,6 +49,7 @@ struct Gosu::Sample::SampleData
149
49
 
150
50
  SampleData(AudioFile& audio_file)
151
51
  {
52
+ al_initialize();
152
53
  alGenBuffers(1, &buffer);
153
54
  alBufferData(buffer, audio_file.format(), &audio_file.decoded_data().front(),
154
55
  static_cast<ALsizei>(audio_file.decoded_data().size()),
@@ -159,24 +60,20 @@ struct Gosu::Sample::SampleData
159
60
  {
160
61
  // It's hard to free things in the right order in Ruby/Gosu.
161
62
  // Make sure buffer isn't deleted after the context/device are shut down.
162
- if (!al_channel_management.get()) {
163
- return;
164
- }
63
+ if (!al_initialized()) return;
165
64
 
166
65
  alDeleteBuffers(1, &buffer);
167
66
  }
168
-
169
- private:
170
- SampleData(const SampleData&);
171
- SampleData& operator=(const SampleData&);
172
67
  };
173
68
 
174
- Gosu::Sample::Sample(const string& filename)
69
+ Gosu::Sample::Sample()
175
70
  {
176
- CONSTRUCTOR_BEGIN;
71
+ }
177
72
 
73
+ Gosu::Sample::Sample(const string& filename)
74
+ {
178
75
  if (is_ogg_file(filename)) {
179
- Gosu::File file(filename);
76
+ File file(filename);
180
77
  OggFile ogg_file(file.front_reader());
181
78
  data.reset(new SampleData(ogg_file));
182
79
  }
@@ -184,62 +81,54 @@ Gosu::Sample::Sample(const string& filename)
184
81
  WAVE_FILE audio_file(filename);
185
82
  data.reset(new SampleData(audio_file));
186
83
  }
187
-
188
- CONSTRUCTOR_END;
189
84
  }
190
85
 
191
- Gosu::Sample::Sample(Reader reader)
86
+ Gosu::Sample::Sample(Gosu::Reader reader)
192
87
  {
193
- CONSTRUCTOR_BEGIN;
194
-
195
88
  if (is_ogg_file(reader)) {
196
89
  OggFile ogg_file(reader);
197
90
  data.reset(new SampleData(ogg_file));
91
+ return;
198
92
  }
199
- else {
200
- try {
201
- WAVE_FILE audio_file(reader);
202
- data.reset(new SampleData(audio_file));
203
- }
204
- catch (const runtime_error& ex) {
205
- #ifndef GOSU_IS_MAC
206
- if (string(ex.what()).find("unknown format") != string::npos) {
207
- MPEGFile mpeg_file(reader);
208
- data.reset(new SampleData(mpeg_file));
209
- }
210
- else
211
- #endif
212
- throw ex;
93
+
94
+ try {
95
+ WAVE_FILE audio_file(reader);
96
+ data.reset(new SampleData(audio_file));
97
+ }
98
+ catch (const runtime_error& ex) {
99
+ #ifndef GOSU_IS_MAC
100
+ if (string(ex.what()).find("unknown format") != string::npos) {
101
+ MPEGFile mpeg_file(reader);
102
+ data.reset(new SampleData(mpeg_file));
103
+ return;
213
104
  }
105
+ #endif
106
+ throw ex;
214
107
  }
215
-
216
- CONSTRUCTOR_END;
217
108
  }
218
109
 
219
- Gosu::SampleInstance Gosu::Sample::play(double volume, double speed, bool looping) const
110
+ Gosu::Channel Gosu::Sample::play(double volume, double speed, bool looping) const
220
111
  {
221
112
  return play_pan(0, volume, speed, looping);
222
113
  }
223
114
 
224
- Gosu::SampleInstance Gosu::Sample::play_pan(double pan, double volume, double speed,
225
- bool looping) const
115
+ Gosu::Channel Gosu::Sample::play_pan(double pan, double volume, double speed, bool looping) const
226
116
  {
227
- pair<int, int> channel_and_token = al_channel_management->reserve_channel();
228
- if (channel_and_token.first == ALChannelManagement::NO_FREE_CHANNEL) {
229
- return Gosu::SampleInstance(channel_and_token.first, channel_and_token.second);
230
- }
117
+ if (!data) return Channel(NO_CHANNEL, 0);
118
+
119
+ Channel channel = allocate_channel();
120
+
121
+ // Couldn't allocate a free channel.
122
+ if (channel.current_channel() == NO_CHANNEL) return channel;
231
123
 
232
- ALuint source = al_channel_management->source_if_still_playing(channel_and_token.first,
233
- channel_and_token.second);
234
- assert (source != ALChannelManagement::NO_SOURCE);
124
+ ALuint source = al_source_for_channel(channel.current_channel());
235
125
  alSourcei(source, AL_BUFFER, data->buffer);
236
126
  alSource3f(source, AL_POSITION, pan * 10, 0, 0);
237
127
  alSourcef(source, AL_GAIN, max(volume, 0.0));
238
128
  alSourcef(source, AL_PITCH, speed);
239
129
  alSourcei(source, AL_LOOPING, looping ? AL_TRUE : AL_FALSE);
240
130
  alSourcePlay(source);
241
-
242
- return Gosu::SampleInstance(channel_and_token.first, channel_and_token.second);
131
+ return channel;
243
132
  }
244
133
 
245
134
  class Gosu::Song::BaseData
@@ -267,7 +156,7 @@ public:
267
156
  return volume_;
268
157
  }
269
158
 
270
- void change_volume(double volume)
159
+ void set_volume(double volume)
271
160
  {
272
161
  volume_ = clamp(volume, 0.0, 1.0);
273
162
  apply_volume();
@@ -336,15 +225,7 @@ class Gosu::Song::StreamData : public BaseData
336
225
 
337
226
  void apply_volume() override
338
227
  {
339
- int source = lookup_source();
340
- if (source != ALChannelManagement::NO_SOURCE) {
341
- alSourcef(source, AL_GAIN, max(volume(), 0.0));
342
- }
343
- }
344
-
345
- int lookup_source() const
346
- {
347
- return al_channel_management->source_for_songs();
228
+ alSourcef(al_source_for_songs(), AL_GAIN, max(volume(), 0.0));
348
229
  }
349
230
 
350
231
  bool stream_to_buffer(ALuint buffer)
@@ -367,7 +248,7 @@ public:
367
248
  StreamData(const string& filename)
368
249
  {
369
250
  if (is_ogg_file(filename)) {
370
- Gosu::File source_file(filename);
251
+ File source_file(filename);
371
252
  file.reset(new OggFile(source_file.front_reader()));
372
253
  }
373
254
  else {
@@ -377,14 +258,16 @@ public:
377
258
  catch (const runtime_error& ex) {
378
259
  #ifndef GOSU_IS_MAC
379
260
  if (string(ex.what()).find("unknown format") != string::npos) {
380
- Gosu::File source_file(filename);
261
+ File source_file(filename);
381
262
  file.reset(new MPEGFile(source_file.front_reader()));
382
263
  }
383
264
  else
384
265
  #endif
385
- throw ex;
266
+ throw ex;
386
267
  }
387
268
  }
269
+
270
+ al_initialize();
388
271
  alGenBuffers(2, buffers);
389
272
  }
390
273
 
@@ -404,88 +287,79 @@ public:
404
287
  }
405
288
  else
406
289
  #endif
407
- throw ex;
290
+ throw ex;
408
291
  }
409
292
  }
293
+
294
+ al_initialize();
410
295
  alGenBuffers(2, buffers);
411
296
  }
412
297
 
413
298
  ~StreamData()
414
299
  {
415
- if (al_channel_management.get()) {
416
- alDeleteBuffers(2, buffers);
417
- }
300
+ // It's hard to free things in the right order in Ruby/Gosu.
301
+ // Make sure buffers aren't deleted after the context/device are shut down.
302
+ if (!al_initialized()) return;
303
+
304
+ alDeleteBuffers(2, buffers);
418
305
  }
419
306
 
420
307
  void play(bool looping) override
421
308
  {
422
- int source = lookup_source();
423
- if (source != ALChannelManagement::NO_SOURCE) {
424
- alSource3f(source, AL_POSITION, 0, 0, 0);
425
- alSourcef(source, AL_GAIN, max(volume(), 0.0));
426
- alSourcef(source, AL_PITCH, 1);
427
- alSourcei(source, AL_LOOPING, AL_FALSE); // need to implement this manually...
428
-
429
- stream_to_buffer(buffers[0]);
430
- stream_to_buffer(buffers[1]);
431
-
432
- // TODO: Not good for songs with less than two buffers full of data.
433
-
434
- alSourceQueueBuffers(source, 2, buffers);
435
- alSourcePlay(source);
436
- }
309
+ ALuint source = al_source_for_songs();
310
+
311
+ alSource3f(source, AL_POSITION, 0, 0, 0);
312
+ alSourcef(source, AL_GAIN, max(volume(), 0.0));
313
+ alSourcef(source, AL_PITCH, 1);
314
+ alSourcei(source, AL_LOOPING, AL_FALSE); // need to implement this manually...
315
+
316
+ stream_to_buffer(buffers[0]);
317
+ stream_to_buffer(buffers[1]);
318
+
319
+ // TODO: Not good for songs with less than two buffers full of data.
320
+ alSourceQueueBuffers(source, 2, buffers);
321
+ alSourcePlay(source);
437
322
  }
438
323
 
439
324
  void stop() override
440
325
  {
441
- int source = lookup_source();
442
- if (source != ALChannelManagement::NO_SOURCE) {
443
- alSourceStop(source);
444
-
445
- ALuint buffer;
446
-
447
- // The number of QUEUED buffers apparently includes the number of
448
- // PROCESSED ones, so getting rid of the QUEUED ones is enough.
449
-
450
- int queued;
451
- alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
452
- while (queued--) {
453
- alSourceUnqueueBuffers(source, 1, &buffer);
454
- }
326
+ ALuint source = al_source_for_songs();
327
+
328
+ alSourceStop(source);
329
+
330
+ // Unqueue all buffers for this source.
331
+ // The number of QUEUED buffers apparently includes the number of PROCESSED ones,
332
+ // so getting rid of the QUEUED ones is enough.
333
+ ALuint buffer;
334
+ int queued;
335
+ alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
336
+ while (queued--) {
337
+ alSourceUnqueueBuffers(source, 1, &buffer);
455
338
  }
339
+
456
340
  file->rewind();
457
341
  }
458
342
 
459
343
  void pause() override
460
344
  {
461
- int source = lookup_source();
462
- if (source != ALChannelManagement::NO_SOURCE) {
463
- alSourcePause(source);
464
- }
345
+ alSourcePause(al_source_for_songs());
465
346
  }
466
347
 
467
348
  void resume() override
468
349
  {
469
- int source = lookup_source();
470
- if (source != ALChannelManagement::NO_SOURCE) {
471
- alSourcePlay(source);
472
- }
350
+ alSourcePlay(al_source_for_songs());
473
351
  }
474
352
 
475
353
  bool paused() const override
476
354
  {
477
- int source = lookup_source();
478
- if (source == ALChannelManagement::NO_SOURCE) {
479
- return false;
480
- }
481
355
  ALint state;
482
- alGetSourcei(source, AL_SOURCE_STATE, &state);
356
+ alGetSourcei(al_source_for_songs(), AL_SOURCE_STATE, &state);
483
357
  return state == AL_PAUSED;
484
358
  }
485
359
 
486
360
  void update() override
487
361
  {
488
- int source = lookup_source();
362
+ ALuint source = al_source_for_songs();
489
363
 
490
364
  ALuint buffer;
491
365
  int processed;
@@ -495,9 +369,7 @@ public:
495
369
  for (int i = 0; i < processed; ++i) {
496
370
  alSourceUnqueueBuffers(source, 1, &buffer);
497
371
  active = stream_to_buffer(buffer);
498
- if (active) {
499
- alSourceQueueBuffers(source, 1, &buffer);
500
- }
372
+ if (active) alSourceQueueBuffers(source, 1, &buffer);
501
373
  }
502
374
 
503
375
  ALint state;
@@ -528,23 +400,16 @@ Gosu::Song::Song(const string& filename)
528
400
  if (has_extension(filename, ".mp3") ||
529
401
  has_extension(filename, ".aac") ||
530
402
  has_extension(filename, ".m4a")) {
531
- CONSTRUCTOR_BEGIN;
532
403
  data.reset(new ModuleData(filename));
533
- CONSTRUCTOR_END;
534
- return;
535
404
  }
405
+ else
536
406
  #endif
537
-
538
- CONSTRUCTOR_BEGIN;
539
407
  data.reset(new StreamData(filename));
540
- CONSTRUCTOR_END;
541
408
  }
542
409
 
543
410
  Gosu::Song::Song(Reader reader)
544
411
  {
545
- CONSTRUCTOR_BEGIN;
546
412
  data.reset(new StreamData(reader));
547
- CONSTRUCTOR_END;
548
413
  }
549
414
 
550
415
  Gosu::Song::~Song()
@@ -579,7 +444,7 @@ void Gosu::Song::play(bool looping)
579
444
  void Gosu::Song::pause()
580
445
  {
581
446
  if (cur_song == this) {
582
- data->pause(); // may be redundant
447
+ data->pause();
583
448
  }
584
449
  }
585
450
 
@@ -606,9 +471,9 @@ double Gosu::Song::volume() const
606
471
  return data->volume();
607
472
  }
608
473
 
609
- void Gosu::Song::change_volume(double volume)
474
+ void Gosu::Song::set_volume(double volume)
610
475
  {
611
- data->change_volume(volume);
476
+ data->set_volume(volume);
612
477
  }
613
478
 
614
479
  void Gosu::Song::update()