Skip to content

Commit 29741a2

Browse files
authored
Fixes to audio streams progress handling. (#307)
* Fixes to audio streams progress handling. * fixup! Fixes to audio streams progress handling. * fixup! Fixes to audio streams progress handling. * Fixed checking total file length. Added unit tests.
1 parent d218b02 commit 29741a2

8 files changed

Lines changed: 246 additions & 85 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
## 5.0.0
1+
## 5.0.1
2+
- fixed **0AB9 ([get_audio_stream_state](https://library.sannybuilder.com/#/sa/audio/0AB9))** not always returning Stopped, despite playback ended
3+
- fixed **2507 ([get_audio_stream_progress](https://library.sannybuilder.com/#/sa/audio/2507))** sometimes returning values smaller than 1.0, despite playback ended
4+
- fixed progress set with **2508 ([set_audio_stream_progress](https://library.sannybuilder.com/#/sa/audio/2508))** being ignored by stopped streams
25

6+
## 5.0.0
37
- support for CLEO modules feature https://github.com/sannybuilder/dev/issues/264
48
- new [Audio](https://github.com/cleolibrary/CLEO5/tree/master/cleo_plugins/Audio) plugin
59
- audio related opcodes moved from CLEO core into separated plugin
@@ -103,8 +107,7 @@
103107
- some errors now cause the script to pause, instead of crashing the game
104108
- updated included Silent's ASI Loader to version 1.3
105109

106-
### Bug Fixes
107-
110+
#### Bug Fixes
108111
- fixed error in **004E (terminate_this_script)** allowing to run multiple missions
109112
- fixed handling of strings longer than 128 characters causing errors in some cases
110113
- fixed error in handling of first string argument in **0AF5 (write_string to_ini_file)**
@@ -114,8 +117,7 @@
114117
- fixed invalid 7 characters length limit of **0AAA (get_script_struct_named)**
115118
- fixed an undefined behavior caused by **0459 (terminate_all_scripts_with_this_name)** when the name matches a custom script
116119

117-
#### SDK AND PLUGINS
118-
120+
#### SDK and plugins
119121
- now all opcodes in range **0-7FFF** can be registered by plugins
120122
- plugins moved to _cleo\cleo_plugins_ directory
121123
- new SDK methods:
@@ -151,19 +153,17 @@
151153
- CLEO_Log
152154

153155
#### CLEO internal
154-
155156
- introduced unit test scripts
156157
- project migrated to VS 2022
157158
- configured game debugging settings
158159
- plugins moved into single solution
159160
- configured automatic releases on GitHub
160161
- added setup_env.bat script
161162

162-
#### Special Thanks
163-
164-
- **123nir** for the alpha-testing, troubleshooting and valuable bug reports
165-
- **Hamal** for the beta-testing, troubleshooting and valuable bug reports
166-
167163
## Older
168-
169164
For previous changes, see [CLEO4 changelog](https://github.com/cleolibrary/CLEO4/blob/master/CHANGELOG.md)
165+
166+
## Special Thanks
167+
- **123nir** for the v5.0.0-alpha testing, troubleshooting and valuable bug reports
168+
- **Hamal** for the v5.0.0-beta testing, troubleshooting and valuable bug reports
169+

cleo_plugins/Audio/CAudioStream.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ CAudioStream::CAudioStream(const char* filepath)
1515
return;
1616
}
1717

18-
unsigned flags = BASS_SAMPLE_SOFTWARE;
18+
unsigned flags = BASS_SAMPLE_SOFTWARE | BASS_STREAM_PRESCAN;
1919
if (CSoundSystem::useFloatAudio) flags |= BASS_SAMPLE_FLOAT;
2020

2121
if (!(streamInternal = BASS_StreamCreateFile(FALSE, filepath, 0, 0, flags)) &&
@@ -71,22 +71,27 @@ float CAudioStream::GetLength() const
7171

7272
void CAudioStream::SetProgress(float value)
7373
{
74+
if (GetState() == Stopped)
75+
{
76+
state = Paused; // resume from set progress
77+
}
78+
7479
value = std::clamp(value, 0.0f, 1.0f);
75-
auto total = BASS_ChannelGetLength(streamInternal, BASS_POS_BYTE);
76-
auto bytePos = QWORD(value * total);
80+
auto bytePos = BASS_ChannelSeconds2Bytes(streamInternal, GetLength() * value);
81+
7782
BASS_ChannelSetPosition(streamInternal, bytePos, BASS_POS_BYTE);
7883
}
7984

8085
float CAudioStream::GetProgress() const
8186
{
82-
auto total = BASS_ChannelGetLength(streamInternal, BASS_POS_BYTE); // returns -1 on error
83-
auto bytePos = BASS_ChannelGetPosition(streamInternal, BASS_POS_BYTE); // returns -1 on error
84-
87+
auto bytePos = BASS_ChannelGetPosition(streamInternal, BASS_POS_BYTE);
8588
if (bytePos == -1) bytePos = 0; // error or not available yet
89+
auto pos = BASS_ChannelBytes2Seconds(streamInternal, bytePos);
8690

87-
float progress = (float)bytePos / total;
88-
progress = std::clamp(progress, 0.0f, 1.0f);
89-
return progress;
91+
auto byteTotal = BASS_ChannelGetLength(streamInternal, BASS_POS_BYTE);
92+
auto total = BASS_ChannelBytes2Seconds(streamInternal, byteTotal);
93+
94+
return (float)(pos / total);
9095
}
9196

9297
CAudioStream::eStreamState CAudioStream::GetState() const
@@ -240,10 +245,12 @@ void CAudioStream::Process()
240245
BASS_ChannelPlay(streamInternal, FALSE);
241246
state = Playing;
242247
}
243-
244-
if (!GetLooping() && GetProgress() >= 1.0f) // end reached
248+
else
245249
{
246-
state = Stopped;
250+
if (state == Playing && BASS_ChannelIsActive(streamInternal) == BASS_ACTIVE_STOPPED) // end reached
251+
{
252+
state = Stopped;
253+
}
247254
}
248255

249256
if (state != Playing) return; // done

cleo_plugins/Audio/CAudioStream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
namespace CLEO
77
{
8+
#pragma pack(push,4)
89
class CAudioStream
910
{
1011
public:
@@ -73,4 +74,5 @@ namespace CLEO
7374
void UpdateVolume();
7475
void UpdateSpeed();
7576
};
77+
#pragma pack(pop)
7678
}

tests/.cleo_tests_runner.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ clear_prints
1313

1414
RUN_TESTS_DIR("cleo:", "cleo_tests")
1515

16+
trace ""
17+
trace "All tests done"
1618
print_big_string {text} "DONE" {time} 5000 {style} TextStyle.MiddleSmaller
1719

1820
terminate_this_custom_script

tests/cleo_tests/Audio/0AB9.txt

Lines changed: 74 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,89 @@
11
{$CLEO .s}
2-
{$USE debug}
3-
{$USE memory}
4-
{$USE audio}
5-
var 0@ : Integer
6-
var 1@ : Integer
7-
var 2@ : Integer
8-
var 3@ : Integer
9-
var 4@ : Integer
10-
var 5@ : Integer
11-
var 6@ : Integer
12-
var 7@ : Integer
13-
var 8@ : Integer
14-
var 9@ : Integer
15-
var 10@ : Integer
2+
{$INCLUDE_ONCE ../cleo_tester.inc}
163

17-
script_name "0AB9" // get_audio_stream_state
18-
19-
debug_on
20-
21-
trace "0AB9 (get_audio_stream_state)"
4+
script_name '0AB9'
5+
test("0AB9 (get_audio_stream_state)", tests)
6+
terminate_this_custom_script
227

8+
const Test_File = ".\Speech.mp3"
239

24-
// load the file
25-
wait 0
26-
if
27-
load_audio_stream ".\Ding.mp3" {store_to} 0@
28-
then
29-
trace "~g~~h~~h~0AB9 (get_audio_stream_state), #0 PASSED"
30-
else
31-
breakpoint "~r~~h~~h~~h~0AB9 (get_audio_stream_state), #0 FAILED!~n~Failed to load file. Handle: %d" 0@
32-
end
10+
function tests
11+
int stream
12+
before_each(@setup)
13+
after_each(@cleanup)
3314

15+
it("should return set state", test1)
16+
it("should return Stopped when finished", test2)
17+
return
18+
3419

35-
// get state
36-
wait 0
37-
0AB9: get_audio_stream_state 0@ {store_to} 1@ // tested opcode
38-
if
39-
1@ == 2 // AudioStreamState.Paused
40-
then
41-
trace "~g~~h~~h~0AB9 (get_audio_stream_state), #1 PASSED"
42-
else
43-
breakpoint "~r~~h~~h~~h~0AB9 (get_audio_stream_state), #1 FAILED!~n~%d Expected~n~%d Occured" 2 1@
44-
end
20+
:setup
21+
stream = load_audio_stream Test_File
22+
assert_result_true()
23+
return
4524

4625

47-
// set new state
48-
wait 0
49-
set_audio_stream_state 0@ {state} AudioStreamAction.Play
50-
trace "~g~~h~~h~0AAD (set_audio_stream_state), #2 PASSED"
26+
:cleanup
27+
remove_audio_stream stream
28+
stream = -1
29+
return
5130

5231

53-
// get updated state
54-
wait 0
55-
0AB9: get_audio_stream_state 0@ {store_to} 1@ // tested opcode
56-
if
57-
1@ == 1 // AudioStreamState.Playing
58-
then
59-
trace "~g~~h~~h~0AB9 (get_audio_stream_state), #3 PASSED"
60-
else
61-
breakpoint "~r~~h~~h~~h~0AB9 (get_audio_stream_state), #3 FAILED!~n~%d Expected~n~%d Occured" 1 1@
62-
end
32+
function test1(stream: int)
33+
int state
34+
35+
// starts as stopped
36+
state = 0x666
37+
state = get_audio_stream_state stream
38+
assert_eq(state, 2) // AudioStreamState.Paused
39+
40+
// still paused
41+
wait {time} 0
42+
state = 0x666
43+
state = get_audio_stream_state stream
44+
assert_eq(state, 2) // AudioStreamState.Paused
45+
46+
// play, effective right away
47+
set_audio_stream_state stream {action} AudioStreamAction.Play
48+
state = 0x666
49+
state = get_audio_stream_state stream
50+
assert_eq(state, 1) // AudioStreamState.Playing
51+
52+
// still playing
53+
wait {time} 0
54+
state = 0x666
55+
state = get_audio_stream_state stream
56+
assert_eq(state, 1) // AudioStreamState.Playing
57+
58+
// stop, effective right away
59+
set_audio_stream_state stream {action} AudioStreamAction.Stop
60+
state = 0x666
61+
state = get_audio_stream_state stream
62+
assert_eq(state, -1) // AudioStreamState.Stopped
63+
64+
// still stopped
65+
wait {time} 0
66+
state = 0x666
67+
state = get_audio_stream_state stream
68+
assert_eq(state, -1) // AudioStreamState.Stopped
69+
70+
// stopped can not turn into paused
71+
set_audio_stream_state stream {action} AudioStreamAction.Pause
72+
state = 0x666
73+
state = get_audio_stream_state stream
74+
assert_eq(state, -1) // AudioStreamState.Stopped
75+
end
6376

6477

65-
// check if state updated after playback end
66-
wait 400 // Ding.mp3 is 0.25s long
67-
0AB9: get_audio_stream_state 0@ {store_to} 1@ // tested opcode
68-
if
69-
1@ == -1 // AudioStreamState.Stopped
70-
then
71-
trace "~g~~h~~h~0AB9 (get_audio_stream_state), #4 PASSED"
72-
else
73-
breakpoint "~r~~h~~h~~h~0AB9 (get_audio_stream_state), #4 FAILED!~n~%d Expected~n~%d Occured" -1 1@
78+
function test2(stream: int)
79+
set_audio_stream_progress stream {progress} 0.99
80+
set_audio_stream_state stream {action} AudioStreamAction.Play
81+
82+
wait {time} 200
83+
int state = 0x666
84+
state = get_audio_stream_state stream
85+
assert_eq(state, -1) // AudioStreamState.Stopped
86+
end
7487
end
7588

7689

tests/cleo_tests/Audio/2507.txt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{$CLEO .s}
2+
{$INCLUDE_ONCE ../cleo_tester.inc}
3+
4+
script_name '2507'
5+
test("2507 (get_audio_stream_progress)", tests)
6+
terminate_this_custom_script
7+
8+
const Test_File = ".\Speech.mp3"
9+
10+
function tests
11+
int stream
12+
before_each(@setup)
13+
after_each(@cleanup)
14+
15+
it("should get progress", test1)
16+
it("should return 1.0 when finished", test2)
17+
return
18+
19+
20+
:setup
21+
stream = load_audio_stream Test_File
22+
assert_result_true()
23+
return
24+
25+
26+
:cleanup
27+
remove_audio_stream stream
28+
stream = -1
29+
return
30+
31+
32+
function test1(stream: int)
33+
float progress
34+
35+
progress = -1.0
36+
progress = get_audio_stream_progress stream
37+
assert_eqf(progress, 0.0)
38+
39+
set_audio_stream_progress stream {progress} 0.5
40+
progress = -1.0
41+
progress = get_audio_stream_progress stream
42+
assert_rangef(progress, 0.499, 0.501)
43+
end
44+
45+
46+
function test2(stream: int)
47+
float progress
48+
49+
set_audio_stream_progress stream {progress} 0.99
50+
set_audio_stream_state stream {action} AudioStreamAction.Play
51+
52+
wait {time} 200
53+
progress = -1.0
54+
progress = get_audio_stream_progress stream
55+
assert_eqf(progress, 1.0)
56+
end
57+
end

0 commit comments

Comments
 (0)