@zep I was recently trying to make a dynamic soundtrack with Picotron but found it to be likely impossible. Ideally I'd like to switch between two tracks and have them sync up (if you're on beat 3 of measure 2 of track A, it should switch to beat 3 of measure 2 of track B). However, music() has no parameter to specify where to start a track. Since sfx() has an optional offset parameter, I can't imagine it would be hard to add, and it would help a lot with creating dynamic soundtracks or other things like pausing music and resuming at a specific point.
Edit: Alternatively, a good approach for creating dynamic soundtracks would be temporarily muting channels. Track A could be made on the first four channels, and Track B on the last four. Then, by changing which channels are muted, you could effectively switch between tracks. I don't know how this could be implemented, though.
Hi, +1 from me, because today I failed to port my dynamic music from PICO-8 to Picotron :)
In PICO-8 I was doing some "hacking" by covering the "off" music channel with a short looped empty SFX, then turning it "on" with stopping that SFX. In Picotron stopping the SFX with sfx(-1, …) means stopping the SFX of the music pattern as well, therefore I can no longer achieve that.
Ideally, I would have a way (at least some memory address to poke) to turn specific channels on and off.
And yes, while looking for a solution, I also realized the offset would be a good workaround in my case (and maybe a desired solution in some other case) :)
@beetrootpaul haha, I tried the exact same thing in picotron by muting a channel of music at a time. I'd done the same thing in Pico-8 and was annoyed to find it didn't work on Picotron too.
Some other approach I tried today, without success:
pokeing the music data:
enable_music_layers = function (layers)
local channel_mask = 1
if layers[1] then
channel_mask = channel_mask + 2
end
if layers[2] then
channel_mask = channel_mask + 4
end
if layers[3] then
channel_mask = channel_mask + 8
end
-- Toggle channels of all 4 patterns of the music.
poke(0x030100 + 9, channel_mask)
poke(0x030100 + 29, channel_mask)
poke(0x030100 + 49, channel_mask)
poke(0x030100 + 69, channel_mask)
end |
The adjusted channels apply only from the next started pattern, sadly. I assume Picotron reads an entire pattern from there, so any changes during its playback are not caught.
Then, I tried with note API, as it's said to be a low level control and it accepts 0xff as "do not change this param of a current note", so I can change just volume to 0. Attached to game's update loop, with mute_channel_<n> set in proper gameplay triggers separately:
update = function ()
if mute_channel_1 then
note(0xff, 0xff, 0, 0xff, 0xff, 1)
end
if mute_channel_2 then
note(0xff, 0xff, 0, 0xff, 0xff, 2)
end
if mute_channel_3 then
note(0xff, 0xff, 0, 0xff, 0xff, 3)
end
end, |
With this case the notes were still playing their tiny start portions. So, I guess, it ended up as a race between notes being played and update spamming them with "set the volume to 0 for the current note, plase"
This isn't tick-precise, but it can start from a specific row.
local floor = math.floor
local round = function(x) return floor(x+0.5) end
--- Starts playing music from a specific row of a pattern.
--- @param pattern integer The pattern to start playing from.
--- @param row integer? The row to start playing from. Defaults to 0.
--- @param fade_length integer? How many milliseconds the fading effect lasts. Defaults to 0.
--- @param channel_mask integer? An 8-bit mask of which channels will be enabled during playback. Defaults to 0xFF.
--- @param row_channel integer? Which channel the row refers to. Defaults to the lowest enabled.
local function play_from(pattern, row, fade_length, channel_mask, row_channel)
row = row or 0
channel_mask = channel_mask or 255
if channel_mask & 255 == 0 or row == 0 then
music(pattern, fade_length, channel_mask)
return
end
local pattern_addr = 0x30100 + pattern * 20
local tracks = {}
local speeds = {}
local row_offsets = {}
local enabled = peek(0x30109 + pattern * 20) & channel_mask
local lowest_enabled
local tick_offset
for i = 0, 7 do
tracks[i] = peek(pattern_addr + i)
speeds[i] = peek(0x050002 + 328 * tracks[i])
if not lowest_enabled and enabled & 1 << i != 0 then
lowest_enabled = i
end
end
row_channel = row_channel or lowest_enabled
tick_offset = row*speeds[row_channel]
for i = 0, 7 do
row_offsets[i] = round(tick_offset / speeds[i])
end
local track_length_addr = 0x050003 + 328 * tracks[lowest_enabled]
local pattern_length = peek2(track_length_addr)
if pattern_length <= 0 then pattern_length = peek2(0x30022) end
if pattern_length >= 0 then
poke2(track_length_addr, max(pattern_length - row_offsets[lowest_enabled], 0))
music(pattern, fade_length, channel_mask)
poke2(track_length_addr, pattern_length)
end
for i = 0, 7 do
if enabled & 1 << i != 0 then
sfx(tracks[i], i, row_offsets[i])
end
end
end
|
[Please log in to post a comment]




