1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 //
26 // A decoder for AdLib music.
27 //
28 
29 
30 #include "bstone_adlib_music_decoder.h"
31 #include <cstdint>
32 #include "bstone_endian.h"
33 
34 
35 namespace bstone {
36 
37 
AdlibMusicDecoder()38 AdlibMusicDecoder::AdlibMusicDecoder() :
39         commands_count_(),
40         command_index_(),
41         samples_per_tick_(),
42         remains_count_()
43 {
44 }
45 
46 // (virtual)
~AdlibMusicDecoder()47 AdlibMusicDecoder::~AdlibMusicDecoder()
48 {
49     uninitialize();
50 }
51 
52 // (virtual)
initialize(const void * raw_data,int raw_size,int dst_rate)53 bool AdlibMusicDecoder::initialize(
54     const void* raw_data,
55     int raw_size,
56     int dst_rate)
57 {
58     if (!AdlibDecoder::initialize(
59         raw_data,
60         raw_size,
61         dst_rate))
62     {
63         return false;
64     }
65 
66     reader_.open(raw_data, raw_size);
67 
68     int commands_size = bstone::Endian::le(reader_.read_u16());
69 
70     if ((commands_size % 4) != 0) {
71         return false;
72     }
73 
74     if ((commands_size + 2) > raw_size) {
75         return false;
76     }
77 
78     command_index_ = 0;
79     commands_count_ = commands_size / 4;
80 
81     samples_per_tick_ =  emulator_.get_sample_rate() / get_tick_rate();
82     remains_count_ = 0;
83 
84     int ticks_count = 0;
85 
86     for (int i = 0; i < commands_count_; ++i) {
87         reader_.skip(2);
88         ticks_count += bstone::Endian::le(reader_.read_u16());
89     }
90 
91     set_dst_length_in_samples(ticks_count * samples_per_tick_);
92 
93     reader_.set_position(2);
94 
95     set_is_initialized(true);
96 
97     return true;
98 }
99 
100 // (virtual)
uninitialize()101 void AdlibMusicDecoder::uninitialize()
102 {
103     reader_.close();
104     commands_count_ = 0;
105     command_index_ = 0;
106     samples_per_tick_ = 0;
107     remains_count_ = 0;
108 
109     AdlibDecoder::uninitialize();
110 }
111 
112 // (virtual)
reset()113 bool AdlibMusicDecoder::reset()
114 {
115     if (!AdlibDecoder::reset()) {
116         return false;
117     }
118 
119     reader_.set_position(2);
120 
121     command_index_ = 0;
122     remains_count_ = 0;
123 
124     return true;
125 }
126 
127 // (virtual)
clone()128 AudioDecoder* AdlibMusicDecoder::clone()
129 {
130     return new AdlibMusicDecoder(*this);
131 }
132 
133 // (virtual)
decode(int dst_count,int16_t * dst_data)134 int AdlibMusicDecoder::decode(
135     int dst_count,
136     int16_t* dst_data)
137 {
138     if (!is_initialized()) {
139         return 0;
140     }
141 
142     if (dst_count < 1) {
143         return 0;
144     }
145 
146     if (!dst_data) {
147         return 0;
148     }
149 
150     if (command_index_ == commands_count_ && remains_count_ == 0) {
151         return 0;
152     }
153 
154     int decoded_samples_count = 0;
155 
156     for (bool quit = false; !quit; ) {
157         if (remains_count_ > 0) {
158             int count = std::min(dst_count, remains_count_);
159 
160             emulator_.generate(count, dst_data);
161 
162             dst_data += count;
163             dst_count -= count;
164             remains_count_ -= count;
165             decoded_samples_count += count;
166         } else {
167             int delay = 0;
168 
169             while (command_index_ < commands_count_ && delay == 0) {
170                 int command_port = reader_.read_u8();
171                 int command_value = reader_.read_u8();
172                 delay = bstone::Endian::le(reader_.read_u16());
173 
174                 emulator_.write(command_port, command_value);
175                 ++command_index_;
176             }
177 
178             if (delay > 0) {
179                 remains_count_ = delay * samples_per_tick_;
180             }
181         }
182 
183         quit =
184             (command_index_ == commands_count_ && remains_count_ == 0) ||
185             dst_count == 0;
186     }
187 
188     return decoded_samples_count;
189 }
190 
191 // (static)
get_tick_rate()192 int AdlibMusicDecoder::get_tick_rate()
193 {
194     return 700;
195 }
196 
197 
198 } // bstone
199