QB64 Phoenix Edition v3.8.0 Released!
#54
Music 
https://github.com/zserge/1bitr

Had a lot of fun with this for at least a week with a Lua script I wrote doing 20-minute muzak LOL. Even recorded it to wave and imported it into Audacity creating a stereo pair and applied reverb. Was very bored that day.

Code: (Select All)
-- by mnrvovrfc, verified 17-Jun-2023

function table_create(atable)
  function local_table_copy(t)
    local tret

    function local_copy(t)
      local tret = { }
      for i = 1, #t do
        tret[i] = t[i]
      end
      for k, v in pairs(t) do
        if type(v) == "table" then
          tret[k] = local_copy(v)
        elseif type(k) ~= "number" then
          tret[k] = v
        end
      end
      return tret
    end

    tret = local_copy(t)
    return tret
  end

  local tret = {
    insert = function (s, ...)
               local rest = { ... }
               if rest == nil or #rest == 0 then return end
               local lrest = #rest
               for i = 1, lrest do
                 table.insert(s, rest[i])
               end
             end,
    first =  function (s, t)
               if t == nil then return end
               table.insert(s, 1, t)
             end,
    enter =  function (s, t)
               if type(t) ~= "table" or #t == 0 then return end
               local ts = local_table_copy(t)
               table.insert(s, ts)
             end,
    concat = function (s, delim, pfrom, pto)
               return table.concat(s, delim, pfrom, pto)
             end,
    copy =   function (s, ts, append)
               if type(ts) ~= "table" then return end
               if next(ts) == nil then return end
               if append == nil then s:delete() end
               local n = #ts
               for i = 1, n do
                 table.insert(s, ts[i])
               end
             end,
    remove = function (s, spos)
               table.remove(s, spos)
             end,
    delete = function (s)
               local n = #s
               while n > 0 do
                 table.remove(s)
                 n = n - 1
               end
             end,
    clear  = function (s)
               s:delete()
             end,
    is_empty = function (s)
                -- not a good way, but the alternative is to search all elements for one with numeric index
                -- the assumption is that if table_create() is used to create a table then
                --   "insert" method would be used to add elements, like an array in BASIC
                return (#s == 0)
             end,
    find =   function (s, aval)
               if aval == nil then return nil end
               if type(aval) ~= "number" and type(aval) ~= "string" then return nil end
               if type(aval) == "number" then aval = tostring(aval) end
               local laval = string.lower(aval)
               local akey, ve
               for kk, vv in pairs(s) do
                 if type(vv) == "number" then ve = tostring(vv) end
                 if type(vv) == "string" then ve = string.lower(vv) end
                 if ve == laval then
                   akey = kk
                   break
                 end
               end
               return akey
             end
  }
  if type(atable) == "table" and next(atable) ~= nil then
    local t = tret
    tret = local_table_copy(atable)
    if not tret.insert then
      tret.insert = t.insert
      tret.clear = t.clear
      tret.concat = t.concat
      tret.copy = t.copy
      tret.delete = t.delete
      tret.enter = t.enter
      tret.find = t.find
      tret.first = t.first
      tret.is_empty = t.is_empty
      tret.remove = t.remove
    end
  end
  return tret
end

function sformat(aformat, arg1, ...)
  if arg1 == nil then
    return aformat
  end
  return string.format(aformat, arg1, ...)
end

function ran(a, b)
  if type(a) ~= "number" then return nil end
  if math.floor(a) == math.floor(b) then return a end
  if a < 0 and b < 0 then
    a = a * -1
    b = b * -1
    if a > b then a, b = b, a end
    return math.random(a, b) * -1
  end
  if a > b then a, b = b, a end
  if a < 0 or b < 0 then
    local x = a * -1
    if b < 0 then x = b * -1 end
    return math.random(a + x, b + x) - x
  end
  return math.random(a, b)
end

function choicet(tbl)
  if type(tbl) ~= "table" then
    return ""
  end
  local x
  local numele = #tbl
  if numele == 0 then return "" end
  if numele == 1 then return tbl[1] end
  repeat
    x = math.random(1, numele)
  until x ~= choicetprev
  choicetprev = x
  return tbl[x]
end

function findstr(txt, lookfor)
  return string.find(txt, lookfor, 1, true)
end


-- main program

tn = table_create()
tc = { "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-" }

for j = 3, 7 do
    for i = 1, 12 do
        tn:insert(sformat("%s%d", tc[i], j))
    end
end
tc = nil

nprog = {
    { 3, 4, 3, 4 },
    { 3, 4, 3, 5 },
    { 3, 4, 5, 6 },
    { 3, 5, 5, 6 },
    { 5, 6, 5, 7 },
    { 5, 5, 5, 7 },
    { 4, 5, 5, 5 },
    { 4, 5, 5, 7 },
    { 4, 7, 5, 5 },
    { 4, 7, 5, 7 },
    { 3, 7, 5, 5 },
    { 3, 7, 5, 7 },
    { 3, 7, 7, 6 },
    { 3, 7, 7, 7 },
    { 4, 7, 7, 6 },
    { 4, 7, 5, 5 },
    { 4, 5, 7, 7 },
    { 5, 5, 7, 7 },
    { 6, 7, 7, 7 },
    { 6, 5, 5, 5 },
    { 6, 3, 7, 5 },
    { 4, 7, 3, 5 },
    { 3, 7, 4, 5 },
    { 7, 7, 7, 7 }
}
lnprog = #nprog

popsong = {
    { 4, 4, 2, 4, 4, 2, 2, 2, 4, 4, 2, 2, 2, 4, 4, 2, 4, 4, 2, 2, 2, 1 },
    { 4, 4, 2, 4, 4, 2, 2, 2, 8, 8, 4, 2, 2, 2, 4, 4, 2, 4, 8, 8, 2, 2, 2, 1 },
    { 4, 4, 8, 8, 8, 8, 4, 4, 2, 8, 8, 8, 8, 2, 8, 8, 4, 2, 8, 8, 8, 8, 2, 4, 4, 2, 8, 8, 4, 8, 8, 8, 8, 2, 2, 1 },
    { 2, 8, 8, 2, 4, 8, 8, 8, 8, 2, 4, 2, 4, 8, 8, 2, 8, 8, 8, 8, 2, 4, 4, 4, 8, 8, 8, 8, 4, 2, 4, 8, 8, 8, 8, 1 },
    { 4, 4, 2, 8, 8, 8, 8, 4, 2, 2, 8, 8, 8, 1 },
    { 4, 2, 4, 2, 2, 8, 8, 8, 8, 2, 2 },
    { 2, 8, 8, 2, 4, 2, 2, 2, 8, 8, 2, 4, 2, 4, 4 },
    { 4, 2, 4, 8, 8, 8, 8, 4, 2, 2, 2 },
    { 8, 8, 8, 8, 2, 8, 8, 8, 8, 4, 4, 4, 4, 4 },
    { 8, 8, 2, 8, 8, 8, 8, 2, 8, 8, 4, 4, 4, 4 },
    { 2, 8, 8, 2, 4, 2, 2, 2, 8, 8, 2, 4, 2, 8, 3 },
    { 8, 8, 2, 8, 8, 4, 2, 8, 8, 8, 8, 4, 8, 3 },
    { 0 }
}
lpopsong = #popsong

popline = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 6 }
raise = { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2 }

for o = 10, 59 do
repeat
    goahead = true
    u = ran(64, 125) * 2
    t = table_create{ u }
    for i = 1, 3 do
        t:insert(t[i] / 2)
    end
    for i = 1, #t do
        sg = tostring(t[i])
        if findstr(sg, '.') then
            if not findstr(sg, ".0") then
                -- print("Warning: dividing the note length doesn't produce a whole value.")
                goahead = false
            end
        end
    end
until goahead

notelen = { t[1], t[2], 0, t[3], 0, 0, 0, t[4] }
t = nil
notelen[3] = notelen[4] + notelen[8]
-- notelen[6] = notelen[8] + math.floor(notelen[8] / 2)

noat, nute = table_create(), table_create()
u = ran(1, 18)
uu = u
m = ran(3, 7)
t = choicet(nprog)
for i = 1, #t do
    noat:insert(u)
    if i == m then noat:insert(0) end
    u = u + t[i]
end
noat:insert(u)
u = uu
m = ran(3, 7)
t = choicet(nprog)
for i = 1, #t do
    nute:insert(u)
    if i == m then nute:insert(0) end
    u = u + t[i]
end
nute:insert(u)

fo = io.open(sformat(os.getenv("HOME") .. "/Documents/z1bitr/musak%02d", o - 9), "w")
if fo then fo:write("; 0\n") end
song = choicet(popsong)
if song[1] == 0 then
    local t = { 4, 4, 8, 8, 2, 8, 8, 4, 4, 2, 8, 8, 8, 8, 2, 8, 8, 4, 2, 8, 8, 8, 8, 2, 4, 4, 2, 8, 8, 4, 8, 8, 8, 8, 2, 1 }
    local l = #t
    local w = ran(1, l)
    local x = w - 1
    local r = 0
    local g = ran(2, 4)
    local c = 1
    local d
    song = table_create()
    repeat
        if t[w] == 2 then
            r = r + 1
            if r == g then
                song:insert(3, 8)
            else
                song:insert(t[w])
            end
        else
            song:insert(t[w])
        end
        w = w + 1
        if w > l then break end
        c = c + 1
    until c > l
    if c <= l then
        if x == 0 then
            print("SOMETHING IS WRONG!")
            os.exit()
        end
        if ran(1, 2) == 1 then
            w = x
            d = -1
        else
            w = 1
            d = 1
        end
        repeat
            if t[w] == 2 then
                r = r + 1
                if r == g then
                    song:insert(3, 8)
                else
                    song:insert(t[w])
                end
            else
                song:insert(t[w])
            end
            w = w + d
            c = c + 1
        until c > l
    end
end
lsong = #song
pass =  math.ceil(math.sqrt(o))
y = ran(1, pass)
for j = 1, pass do
    kount = 1
    newpopline = table_create()
    for i = 1, lsong do
        newpopline:insert(choicet(popline))
    end
    repeat
        u = song[kount]
        -- restrict the pause to the two shortest note lengths
        trig = newpopline[kount]
        if j == y then
            if nute[trig] then
                if u < 4 and nute[trig] == 0 then
                    trig = 1
                end
            else
                trig = 1
            end
        else
            if noat[trig] then
                if u < 4 and noat[trig] == 0 then
                    trig = 1
                end
            else
                trig = 1
            end
        end
        -- the lowest two notes in the sequence never vary
        if (j == y and trig > 2) then
            w = choicet(raise)
            if w == -1 then w = 3 end
        elseif (trig > 3) or (trig == 3 and ran(1, 3) == 1) then
            w = choicet(raise)
        else
            w = 0
        end
        if j == y then
            if nute[trig] == 0 then
                -- out-of-range on purpose so it's later considered a pause
                v = 1e+6
            else
                v = nute[trig] + w
            end
        else
            if noat[trig] == 0 then
                v = 1e+6
            else
                v = noat[trig] + w
            end
        end
        if v > #tn then
            st = "---"
        else
            st = tn[v]
        end
        print(sformat("%s%c%02x", st, 32, notelen[u]))
        if fo then
            fo:write(sformat("%s%c%02x\n", st, 32, notelen[u]))
            fo:write(sformat("%s%c%02x\n", st, 32, notelen[u]))
        end
        kount = kount + 1
    until kount > lsong
end
if fo then fo:close() end
print("===")
end  -- for o

ROFL doesn't sound enough like Color Computer playing through a budget television. It had to be the Color Computer 2 because the COCO3 did have "PLAY" which supported volume but could play one voice at a time with square wave.

This is an early version of the script I made, ended up making another one which created sets of three for one song, for left, center and right channels for stereo music. This script presented here requires Lua v5.4 or later. The line with "io.open()" statement could be changed easily to something that makes sense on Windows ("USERPROFILE" environment variable instead of "HOME"). Make sure "1bitr" is installed, and "z1bitr" directory (which is what I called the installation directory) exists inside "Documents" of your regular user's account before running this script.

Then feed one of the text files to the program like this:

Code: (Select All)
$ ./1bitr < musak01

EDIT: Sorry for the lack of indentation, it seems this forum really wants CHR$(9) characters for it, it sucks.
Reply


Messages In This Thread
RE: QB64 Phoenix Edition v3.8.0 Released! - by mnrvovrfc - 06-18-2023, 12:00 AM



Users browsing this thread: 25 Guest(s)