This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
bloxz64's profile picture

Coroutine not using included while loop

Started by bloxz64, 30 September 2014 - 01:19 AM
bloxz64 #1
Posted 30 September 2014 - 03:19 AM
I've been trying to make a music playing program that can play multiple notes at the same time. But the coroutine I've set up for it doesn't seem to be using the while true loop in the notes function, and is becoming dead after the triggerNote() runs.

Setup is as seen here:
www.imgur.com/Eza7x1a
Pastebin: dXNVvYF7


n = {}
n[1] = peripheral.wrap("left")
n[2] = peripheral.wrap("right")
n[3] = peripheral.wrap("back")
c = coroutine
il = {}

function notes()
  print("a")
  while true do
    local i = c.yield() -- yeild up for info
    table.insert(il,i[1])
    local r = i
    table.remove(r,1)
    --print(textutils.serialize(r))
    c.resume(l[r[1]],r) -- pass down refined info
    --print(textutils.serialize(il))
    local r = table.remove(il)
    --print(r)
    n[r].triggerNote()  -- yeild up playing note
    print("b")
    c.resume(l[r[1]])   -- pass down continue
    print("c")
  end
  print("d")
end

function res()
  while true do
    c.yeild()
  end
end


l = {}
for itr=1,3 do
  table.insert(l,c.create(notes))
  c.resume(l[itr])
  end
table.insert(l,c.create(res))

c.resume(l[1],{1,2,3,4})
print(c.status(l[1]))
c.resume(l[1])
print(c.status(l[1]))
sleep(.3)

c.resume(l[1],{1,2,3,4})
c.resume(l[1])
Any help would be greatly appreciated.
EDIT: spelling
Edited on 30 September 2014 - 01:44 AM
Bomb Bloke #2
Posted 30 September 2014 - 05:58 AM
If your co-routines yield when you ask them to play a note, they'll expect you to resume them with the event data signifying that the operation was completed. Resume 'em with anything else (eg nothing) and they'll likely fall over.

I think there's some screwiness in the order in which you're resuming, but I don't see much benefit in untangling things. I suggest using the parallel API instead, as it's specifically built for this kind of thing.

It may be the case that there's no accurate way to determine which event corresponds to which Note Block, in which case controlling multiple such peripherals in separate co-routines simply won't work. It depends on what the events look like. Dunno, try pulling one and printing out its content.

I've managed to handle something like three notes per twentieth of a second through MoarPeripherals Iron Note Blocks (times however many such blocks I had attached - the actual ceiling should be pretty high, maybe a dozen notes, I dunno, maybe more). But they don't yield when called, so I'd imagine they're significantly faster than regular Note Blocks (which I haven't much messed with).
bloxz64 #3
Posted 30 September 2014 - 10:39 PM
Sadly I dont have the iron noteblock or i would have used it. Also the parallel.waitForAny and waitForAll are playing the notes with the slightest delay, which, in most cases would be fine but I plan on expanding it to 10 noteblocks as this is just a proof of concept. Instead I used

while true do
  e,n = os.pullEvent()
  print(e..": "..n
end
to pull and print events in a separate tab. Came up with this:
Pastebin 3QSHVENr

n = {}
n[1] = peripheral.wrap("left")
n[2] = peripheral.wrap("right")
n[3] = peripheral.wrap("back")
c = coroutine
il = {}

function notes() -- main coroutine
  while true do
    print("start loop")
    local i = c.yield() -- yeild up for info
    table.insert(il,i[1])
    local r = i
    table.remove(r,1)
    c.resume(l[r[1]],r) -- pass down refined info
    local r = table.remove(il)
    n[r].triggerNote()  -- yeild up playing note
    c.resume(l[r[1]],"op_tick_sync",1) -- pass down continue
    print("reached end")
  end
  print("exited loop")
end

function res()  -- for final yeild
  while true do
    c.yeild()
  end
end

function stat() -- display statuses
  for itr=1,3 do
    print(itr.."-"..c.status(l[itr]))
  end
end

l = {}
for itr=1,3 do  -- creat list of coroutines
  table.insert(l,c.create(notes))
  c.resume(l[itr])
  end
table.insert(l,c.create(res))

 -- Main program --
c.resume(l[1],{1,2,3,4})
stat()
c.resume(l[1],"op_tick_sync",1)
stat()
print("done 1") -- done part 1

sleep(.3)
c.resume(l[1],{1,2,3,4}) -- whatever index is passed in dies
stat()
c.resume(l[1],"op_tick_sync",1)
stat()

--parallel.waitForAny(n[1].triggerNote(),n[2].triggerNote(),n[3].triggerNote())
Now whatever I cast into the 2nd iteration of playing the notes, causes that coroutine to become dead.
Bomb Bloke #4
Posted 01 October 2014 - 04:28 AM
Your use of parallel.waitForAny() is incorrect - you need to "pass the functions" to it, not "call the functions and pass the result". That, plus use of "any" may have undesired results.

That line you've got commented out down the bottom would be better written as:

parallel.waitForAll(n[1].triggerNote, n[2].triggerNote, n[3].triggerNote)

… assuming you chop out all the co-routine stuff from the rest of the script, that should work well.

I fired up the game and had a bit of a play around - here's what I came up with. Give it a try if you're still stuck:

local noteBlock, triggerNoteFunc = {}, {}

do
	-- Get all attached peripherals.
	local periphs = peripheral.getNames()
	
	-- For each peripheral, if it's a note block,
	for i=1,#periphs do if peripheral.getType(periphs[i]) == "music" then
		
		-- Stick the wrapped block in our noteBlock table,
		noteBlock[#noteBlock+1] = peripheral.wrap(periphs[i])
		
		-- and copy the pointer for the triggerNote function to the triggerNoteFunc table.
		triggerNoteFunc[#triggerNoteFunc+1] = noteBlock[#noteBlock].triggerNote
	end end
end

for i=0,25 do
	-- Change pitch:
	for j=1,#noteBlock do noteBlock[j].setPitch(i) end

	-- Take all the function pointers in triggerNoteFunc and pass them to parallel.waitForAny:
	parallel.waitForAll(unpack(triggerNoteFunc))
	
	sleep(1)
end
bloxz64 #5
Posted 01 October 2014 - 10:36 PM
I feel stupid for not thinking that using brackets would run the function and pass in the results instead of the function. Anyway dicided to rework the whole program, but now I'm getting this error and it's completely baffling me. http://i.imgur.com/3jJx3y3h.jpg code:

n = {}
table.insert(n,peripheral.wrap("music_19"))--1
table.insert(n,peripheral.wrap("music_20"))--2
table.insert(n,peripheral.wrap("music_21"))--3
table.insert(n,peripheral.wrap("music_9")) --4
table.insert(n,peripheral.wrap("music_10"))--5
table.insert(n,peripheral.wrap("front"))   --6
table.insert(n,peripheral.wrap("music_11"))--7
table.insert(n,peripheral.wrap("music_12"))--8
table.insert(n,peripheral.wrap("music_22"))--9
table.insert(n,peripheral.wrap("music_23"))--10

s = setPitch
t = triggerNote
p = parallel.waitForAny

function sp(notes)
  for itr=1,#notes do
    if itr > 0 then
      n[itr].setPitch(notes[itr])
    end
  end
end

function tp(notes)
  toPlay = {}
  for itr=1,#notes do
    print(itr)
    if notes[itr] > 0 then
      toPlay[itr] = n[itr].triggerNote
    end
  end
  --print(textutils.serialize(toPlay))
  return toPlay
end

function spn(noteList)
  sp(noteList)
  p(unpack(tp(noteList)))
end

sp({6,0,10,13})
x = tp({6,0,10.13})
print(x)
print(textutils.serialize(x))
--spn({6,0,10,13})
Soooo yeah. I don't get it at all. Why is it returning a table, but when I try to serialize it is trying to call a function?
Bomb Bloke #6
Posted 02 October 2014 - 03:29 AM
The function you call when you attempt to serialise is textutils.serialize().

The error you get when you attempt to serialise is caused by the function pointers stored in the table you're attempting to serialise (for the peripheral's "triggerNote" functions). It's not trying to "run" these, it's trying to turn them into a string.
bloxz64 #7
Posted 02 October 2014 - 04:43 AM
I'm not sure I get what you mean. Is it not actually calling the function or what? Why wouldn't it call the function? If not how do I make it call the function?
Edited on 02 October 2014 - 02:48 AM
Bomb Bloke #8
Posted 02 October 2014 - 04:54 AM
What function are you talking about, and why do you want to call it?

If you're talking about textutils.serialize():

You are running that function.

That function is erroring because you are asking it to serialise a table which contains function pointers.
bloxz64 #9
Posted 02 October 2014 - 05:51 AM
I was talking about triggerNote, but for x, how do I make it so that I can call it? Is there a way to "compile" it so as to make it not contain those function pointers? Do I even need to? Anyway you can figure it out I just can't figure out why spn(unpack(toPlay)) isn't playing the notes. I was trying to print the stuff parallel is using, but that doesn't seem to work.
Edited on 02 October 2014 - 03:56 AM
Bomb Bloke #10
Posted 02 October 2014 - 08:03 AM
1) Ditch the textutils.serialize() call.

2) Re-write spn like this:

function spn(noteList)
  p(unpack(noteList))
end

3) Call spn like this:

spn(x)

If you're still stuck, try this re-write:

noteBlock = {"music_19","music_20","music_21","music_9","music_10","front","music_11","music_12","music_22","music_23"}
for i=1,#noteBlock do noteBlock[i] = peripheral.wrap(noteBlock[i]) end

function playNotes(noteList)
	local playList = {}
	
	for i=1,#noteList do
		noteBlock[i].setPitch(noteList[i])
		playList[i] = noteBlock[i].triggerNote
	end
	
	parallel.waitForAll(unpack(playList))
end

playNotes({6,0,10,13})
bloxz64 #11
Posted 03 October 2014 - 01:19 PM
Thanks.for the help! Now to learn the file system…