Skip to content

Instantly share code, notes, and snippets.

@reo6
Last active January 9, 2023 10:47
Show Gist options
  • Save reo6/f14e8bef2df1098f8ab4ba7b9f8ff067 to your computer and use it in GitHub Desktop.
Save reo6/f14e8bef2df1098f8ab4ba7b9f8ff067 to your computer and use it in GitHub Desktop.

I'm working on a Looper project using python and jackclient-python library. Keep a tab open for the code.

Expected Behaviour

If you know what is a loop pedal and how it functions, you'll probably get it. A simple looper, without overdubbing. So there's a button for toggling recorder. User makes connections with a patchbay, hits record, starts playing something, hits it again and the recording is done. So the audio is recorded between two hits. That audio should be looped now.

What I did

As you would know, jack applications work with a process function. So here's mine:

def process(self, frames):
        current_blocks = [port.get_array() for port in self.input_ports]

        if self.looper.mode == LoopMode.PLAY:
            for outport, take, block in zip(
                    self.output_ports,
                    self.looper.get_next_takes(),
                    current_blocks):

                outport.get_buffer()[:] = take + block

        elif self.looper.mode == LoopMode.RECORD:
            self.temp_take.append(current_blocks)

            for outport, block in zip(self.output_ports, current_blocks):
                outport.get_buffer()[:] = block

Each audio block is a numpy array here. There's a temporary list to save every audio block until recording is done. Here's what happens while toggling:

    def switch_modes(self):
        if self.looper.mode == LoopMode.PLAY:
            # Record mode is on.
            self.temp_take = []
            self.looper.mode = LoopMode.RECORD

        elif self.looper.mode == LoopMode.RECORD:
            # This means the record is over,
            # and we are ready to put it to our take.
            blocks1 = list(map(lambda b: b[0], self.temp_take))
            blocks2 = list(map(lambda b: b[1], self.temp_take))
            self.looper.record_takes(blocks1, blocks2)

            self.looper.mode = LoopMode.PLAY

Simple, right? So here's a summary: After recording begins, incoming audio blocks are played and saved to the temporary list. When recording is done, toggler function writes the temporary list to the looper take. process plays the every recorded audio block in an order, rolls back when recorded blocks are over. Which is supposed to result in a simple looper that functions correctly... Right...

Current Behaviour

I've created a youtube video and edited it to explain what happens when I run the code above. Nothing plays when I finish recording, and when I play hi-hat sound I hear it like twice.


ANY feedback that might help is appreciated. Thanks!

@reo6
Copy link
Author

reo6 commented Jan 9, 2023

...

It's solved. It turned out that get_array was not copying the values of the memory area that contains the audio block. So, before appending current_blocks to the temp list, I copied it using copy.deepcopy and it just worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment