class _sound_channel
var song: song
var track_index: int
var next_pattern_index: int
var sync_data: int
var events: track_event[]
var next_event_index: int
var instrument: io_instrument
var write_index: byte
var samples_per_tick: pair
var is_playing: bool
var looping: bool
end class
module sound
var _sound_channels: _sound_channel[]
var _note_to_pitch: pair[]
func init()
new _sound_channel[](6) -> sound::_sound_channels
sound::_sound_channels.resize(6)
0 -> io::jamdac_enable
var queue: ^io_audio_queue
var i: int
0 -> i
loop
new _sound_channel() -> sound::_sound_channels[i]
io::audio_queues[i] -> queue
to_address(queue.events) -> queue.write_address
queue.write_address -> queue.read_address
0 -> queue.purge_read_address
i + 1 -> i
if i >= 6
do drop
end loop
io::audio_queues[0] -> queue
var new_audio_event: ^io_audio_event
queue.events[0] -> new_audio_event
7 -> new_audio_event.kind
to_address(art::reverb) -> new_audio_event.operand
queue.events[1] -> new_audio_event
3 -> new_audio_event.kind
to_address(art::envelope_3) -> new_audio_event.operand
queue.events[2] -> new_audio_event
5 -> new_audio_event.kind
3 -> new_audio_event.operand
to_address(queue.events[3]) -> queue.write_address
3 -> sound::_sound_channels[0].write_index
1 -> io::jamdac_enable
kernel::trace_num(0)
loop
if queue.read_address = queue.write_address
do drop
end loop
end func
func play_track(track: track, channel_index: int)
sound::_play_track(track, channel_index, false)
end func
func loop_track(track: track, channel_index: int)
sound::_play_track(track, channel_index, true)
end func
func _play_track(track: track, channel_index: int, looping: bool)
if sound::_sound_channels = null
do return
sound::stop_channel(channel_index)
var channel: _sound_channel
sound::_sound_channels[channel_index] -> channel
null -> channel.song
looping -> channel.looping
459 -> channel.samples_per_tick
true -> channel.is_playing
0 -> channel.track_index
0 -> channel.next_pattern_index
0 -> channel.sync_data
track.events -> channel.events
0 -> channel.next_event_index
end func
func stop_channel(channel_index: int)
if sound::_sound_channels = null
do return
var channel: _sound_channel
sound::_sound_channels[channel_index] -> channel
null -> channel.song
false -> channel.is_playing
null -> channel.events
null -> channel.instrument
var queue: ^io_audio_queue
io::audio_queues[channel_index] -> queue
var write_index: byte, write_address: int, read_address: int
queue.read_address -> read_address
queue.write_address -> write_address
if read_address <> write_address then
queue.write_address -> queue.purge_read_address
end if
channel.write_index -> write_index
var new_audio_event: ^io_audio_event
queue.events[write_index] -> new_audio_event
0 -> new_audio_event.kind
0 -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
to_address(queue.events[write_index]) -> write_address
write_index -> channel.write_index
write_address -> queue.write_address
end func
func play_song(song: song)
sound::_play_song(song, false)
end func
func loop_song(song: song)
sound::_play_song(song, true)
end func
func _play_song(song: song, looping: bool)
if sound::_sound_channels = null
do return
var i: int, track_count: int
var pattern: song_pattern
var sync_data: int
song.patterns[0] -> pattern
pattern.tracks.size -> track_count
if track_count > 1 then
track_count -> sync_data
else
0 -> sync_data
end if
0 -> i
loop
if i >= track_count
do drop
sound::stop_channel(i)
var channel: _sound_channel
sound::_sound_channels[i] -> channel
song -> channel.song
looping -> channel.looping
true -> channel.is_playing
i -> channel.track_index
1 -> channel.next_pattern_index
sync_data -> channel.sync_data
pattern.tracks[i].events -> channel.events
pattern.samples_per_tick -> channel.samples_per_tick
0 -> channel.next_event_index
null -> channel.instrument
i + 1 -> i
end loop
end func
func _process_channel(
| channel: _sound_channel,
| queue: ^io_audio_queue)
var write_index: byte, write_address: int, read_address: int
var temp: int
var new_audio_event: ^io_audio_event
if channel.events = null and not channel.is_playing
do return
channel.write_index -> write_index
to_address(queue.events[write_index]) -> write_address
if write_address <> queue.write_address
do kernel::fail("_process_channel() write_address out of sync")
if queue.purge_read_address <> 0
do return
queue.read_address -> read_address
if channel.events <> null then
loop
if read_address <= write_address then
read_address + 500 -> temp
else
read_address -> temp
end if
if temp - write_address < 30
do drop
if channel.next_event_index >= channel.events.size then
if channel.song <> null then
var patterns: song_pattern[]
channel.song.patterns -> patterns
if channel.next_pattern_index >= patterns.size then
if channel.looping then
0 -> channel.next_pattern_index
else
null -> channel.events
drop
end if
end if
var pattern: song_pattern
patterns[channel.next_pattern_index] -> pattern
pattern.samples_per_tick -> channel.samples_per_tick
pattern.tracks[channel.track_index].events -> channel.events
channel.next_pattern_index + 1 -> channel.next_pattern_index
0 -> channel.next_event_index
else
if channel.looping then
0 -> channel.next_event_index
else
null -> channel.events
drop
end if
end if
if channel.sync_data <> 0 then
queue.events[write_index] -> new_audio_event
9 -> new_audio_event.kind
channel.sync_data -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
end if
end if
var track_event: track_event
channel.events[channel.next_event_index] -> track_event
channel.next_event_index + 1 -> channel.next_event_index
var wait_time_samples: int
channel.samples_per_tick * track_event.duration_ticks -> wait_time_samples
var track_event_note: byte
track_event.note -> track_event_note
if track_event_note < 254 then
wait_time_samples - 30 -> wait_time_samples
queue.events[write_index] -> new_audio_event
10 -> new_audio_event.kind
0 -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
var instrument: io_instrument
art::instruments[track_event.instrument] -> instrument
if channel.instrument <> instrument then
instrument -> channel.instrument
wait_time_samples - 30 -> wait_time_samples
queue.events[write_index] -> new_audio_event
0 -> new_audio_event.kind
to_address(instrument) -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
end if
wait_time_samples - 30 -> wait_time_samples
queue.events[write_index] -> new_audio_event
1 -> new_audio_event.kind
sound::_note_to_pitch[track_event_note] -> temp
math::bit_or(temp, math::shift_left(track_event.mod_byte, 20)) -> temp
temp -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
end if
wait_time_samples - 30 -> wait_time_samples
queue.events[write_index] -> new_audio_event
8 -> new_audio_event.kind
wait_time_samples -> new_audio_event.operand
to_byte(write_index + 1) -> write_index
if write_index >= 100
do 0 -> write_index
to_address(queue.events[write_index]) -> write_address
end loop
write_index -> channel.write_index
write_address -> queue.write_address
end if
if write_address = read_address then
false -> channel.is_playing
else
true -> channel.is_playing
end if
end func
func is_playing(channel_index: int): bool
var channel: _sound_channel, result: bool
if sound::_sound_channels = null then
false -> result
else
sound::_sound_channels[channel_index] -> channel
channel.is_playing -> result
end if
return result
end func
func think()
if sound::_sound_channels = null
do return
var i: int
0 -> i
loop
sound::_process_channel(sound::_sound_channels[i],
| io::audio_queues[i])
i + 1 -> i
if i >= 6
do drop
end loop
end func
end module
data sound::_note_to_pitch
[
19, 20, 21, 23, 24, 25, 27, 29, 30, 32,
34, 36, 38, 40, 43, 45, 48, 51, 54, 57,
60, 64, 68, 72, 76, 81, 85, 91, 96, 102,
108, 114, 121, 128, 136, 144, 152, 161, 171, 181,
192, 203, 215, 228, 242, 256, 271, 287, 304, 323,
342, 362, 384, 406, 431, 456, 483, 512, 542, 575,
609, 645, 683, 724, 767, 813, 861, 912, 967, 1024,
1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825,
1933, 2048, 2170, 2299, 2435, 2580, 2734, 2896, 3069, 3251,
3444, 3649, 3866, 4096, 4340, 4598, 4871, 5161, 5468, 5793,
6137, 6502, 6889, 7298, 7732, 8192, 8679, 9195, 9742, 10321,
10935, 11585, 12274, 13004, 13777, 14596, 15464, 16384, 17358, 18390,
19484, 20643, 21870, 23170, 24548, 26008, 27554, 29193
]
end data