rchanrussell / rpi_audio_looper_c

An audio looper for the Raspberry Pi, in C, UART interface
https://rschanrussell.wordpress.com/projects/raspberry-pi-audio-looper/
12 stars 1 forks source link

In the lack of knowledge... :) #3

Open Mistahbrock opened 4 years ago

Mistahbrock commented 4 years ago

Hello Robert

by changing the line 801 "looper->sfd = serialOpen("/dev/ttyAMA0", 115200);" to "looper->sfd = serialOpen("/dev/ttyUSB0", 115200);" I use a usb cable form the nano thereby eliminating the need for a TTL cable and converter.

I have occasional "Invalid last char" but the nano controller works for now

I am a total noob, so any hints is welcomed, thanks for a nice idea, best regards Ole from Denmark

rchanrussell commented 4 years ago

Hello Ole, I am sorry I didn't see this notification sooner. So you're using a USB to serial adapter with the USB connected to the RPi and the wires connected to the nano controller?

Could you ensure you flush the serial port on the nano controller? Have you tried printing the invalid char?

There's some debug and discussion in these two if you haven't read them already: https://www.raspberrypi.org/forums/viewtopic.php?t=177076 https://www.raspberrypi.org/forums/viewtopic.php?t=254096

I am just guessing what's going on, but I would try to debug with printf and see what's the last char, just in case it's the first char of the next command or something like that.

I am in the process of redesigning the software. I hope to make it much more configurable and maybe even support GPIO or UART depending on the user's choice or which sends a signal. I have not figured it all out yet as I am starting from scratch, instead of just an idea in the head and prototyping (which is what "master" currently is).

Thanks for using this! I do have some bugs as pointed out: https://rschanrussell.wordpress.com/projects/raspberry-pi-audio-looper/the-audio-looper-design/comment-page-1/#comment-73 so bear with me.

Regards, ~Robert

Mistahbrock commented 4 years ago

Hello Robert

Well, I'm using a ordinary USB host to client cable for a nano,, so the setup and drivers are already taken care for in the OS. super easy :)

And perhaps this hardware would be the perfect match for your code : http://www.audioinjector.net

For now, my Arduino Nano code looks like this, sorry for the formatting, I can't figure out the insert code thing: ============================= INO CODE BEGIN: ================================

/*** *

// Button stuff

include "avdweb_Switch.h"

// initialize the library with the numbers of the interface pins //LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address

// Set the pins on the I2C chip used for LCD connections: // addr, en,rw,rs,d4,d5,d6,d7,bl,blpol LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address 0x20

// constants won't change. They're used here to set pin numbers: // foot switch const byte fcButtonPin1 = 5; // button1 using digital pin 5 on Arduino Nano Switch fcButton = Switch(fcButtonPin1);

// Define fixed numbers for timing : // live and LED live..

define Blink_1 12

define Blink_2 24

define wait 100

//LEDs const int RED_LED = 8; // LED using digital pin 8 on Arduino Nano for pin 1 on bicolor red LED const int GREEN_LED = 9; // LED using digital pin 9 on Arduino Nano for pin 1 on bicolor green LED

// Mode 0 1 2 3 4
enum loopMode { STOP, REC, PLAY, OVERDUB, DELETE }; // Modes

// Variables will change: // setup a stop mode boolean operator bool stopMode = true; // begin in false mode, looper is not running yet...

// setup a couple of recording mode boolean operator bool recMode = false; // true equals REC mode, this will be set on first click bool odubMode = false; // true equals ODUB mode, this will be set on first run

int loopState = 0; // looper functions, 0 is stopped

//==================================================================================// // Initialize //==================================================================================// void setup() { // Open serial communications and wait for port to open, I scaled down the speed from 115200 to 9600 to make the connection more reliable and stabe // after all what I need is only commands from the buttons and to the LED's on the Arduino Serial.begin(9600); // set group 1 active Serial.print("g1000\r\n");

while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only }

pinMode(RED_LED,OUTPUT); pinMode(GREEN_LED,OUTPUT);

// set up the LCD's number of columns and rows: lcd.begin(20, 4); lcd.clear();

//Intro Screen message lcd.setCursor(0,0); lcd.print("Rhyno Audio"); lcd.setCursor(0,1); lcd.print("RPI-Looper RC-1.0A"); delay(800);

// Init LCD lcd.clear(); lcd.setCursor(0,0); // Init loopstates and LCD status setMode(); LcdStatus(); }

//==================================================================================// // run main loop //==================================================================================// void loop() { // multiresponseButton complex events fcButton.poll();

// Long press in rec/ play acivates stop if(fcButton.longPress()) { // send pi-looper stop cmd over USB serial Serial.print("s0000\r\n"); delay(wait); // Stop loopState = 0; // set loopState to STOP // Update loopstate and LCD status setMode(); LcdStatus(); }

// rec / play function if(fcButton.singleClick()) { // toggle between rec / play function if (recMode == false) { // send command to pi-looper : record track 1 - group 1 over USB serial Serial.print("r00g1\r\n"); delay(wait); // Record loopState = 1; // set loopState to REC // Update loopstate and LCD status setMode(); LcdStatus(); }

else if (recMode == true)
  {
    // send command to pi-looper : play track 1 - group 1 over USB serial r = repeat enabled
    Serial.print("p00g1r\r\n");
    // Play
    loopState = 2;                             // set loopState to PLAY
    // Update loopstate and LCD status
    setMode();
    LcdStatus();
  }

else if (recMode == true && odubMode == true)
  {
    // send command to pi-looper : play track 1 - group 1 over USB serial r = repeat enabled
    Serial.print("o01g1\r\n");
    delay(wait);
    // Overdub
    loopState = 3;                             // set loopState to OVERDUB
    // Update loopstate and LCD status
    setMode();
    LcdStatus();
  }  
}

// Double click in stopmode activates delete if(fcButton.doubleClick()) { // Delete only in stop mode if (stopMode == true) { // - Delete: remove a track from a group // dXXgY: command - d, track XX, group Y // send command to pi-looper : delete track 1 - group 1 over USB serial Serial.print("d01g1\r\n"); delay(wait); // Delete loopState = 4; // set loopState to DELETE // Update loopstate and LCD status setMode(); LcdStatus(); } } }

//==================================================================================// // Functions and sub functions : //==================================================================================// void setMode() { // All LED's Off digitalWrite(RED_LED, LOW); digitalWrite(GREEN_LED, LOW);

switch (loopState) { case STOP: // loopState 0 LStop(); break;

case REC: // loopState 1 LRec();
break;

case PLAY: // loopState 2 LPlay(); break;

case OVERDUB: // loopState 3 LOdub(); break;

case DELETE: // loopState 4 LDelete(); break; } }

// set looper modes void LStop() { // set LED's state HIGH or LOW digitalWrite(RED_LED, LOW); digitalWrite(GREEN_LED, LOW);

// turn on stopMode, here I want to enable delete if (stopMode == false) { stopMode = !stopMode; }

// Turn off recMode if (recMode == true) { recMode = !recMode; } }

void LRec() { // set LED's state HIGH or LOW digitalWrite(RED_LED, HIGH); digitalWrite(GREEN_LED, LOW);

// Turn on recMode if it was off
if (recMode == false) { recMode = !recMode; }

// Turn on overdub Mode if (odubMode == true) { loopState = 3; } }

void LPlay() { // set LED's state HIGH or LOW digitalWrite(RED_LED, LOW); digitalWrite(GREEN_LED, HIGH);

// Turn off recMode if it was on and set overdub mode ready if (recMode == true) { recMode = !recMode; }

// Turn on overdub Mode if (odubMode == false) { odubMode = !odubMode; } }

void LOdub() { // set LED's state HIGH or LOW digitalWrite(RED_LED, HIGH); digitalWrite(GREEN_LED, LOW);

}

void LcdStatus() { // Top messages line 0 lcd.clear(); lcd.setCursor(0,0); lcd.print("loopState : "); lcd.setCursor(14,0); lcd.print(loopState);

// Upper messages line 1 lcd.setCursor(0,1); lcd.print("recMode is :"); lcd.setCursor(14,1); lcd.print(recMode); // get respones from pi-looper via rpi's USB0 Serial port // Lower messages line 2 lcd.setCursor(0,2); lcd.print("odubMode is :"); lcd.setCursor(14,2); lcd.print(odubMode); // Bottom messages line 3 lcd.setCursor(0,3); if (loopState == 0) { lcd.print("Stopped..."); } else if (loopState == 1) { lcd.print("Recording..."); } else if (loopState == 2) { lcd.print("Playing..."); } else if (loopState == 3) { lcd.print("Overdubbing..."); } else if (loopState == 4) { lcd.setCursor(0,2); lcd.print("Deleting..."); delay(450); lcd.print("Ready...");
} }

void LDelete() { if (stopMode == true) { // Turn off odubMode, only in stopmode if (odubMode == true) { odubMode = !odubMode; } // Blink LED's and show fancy bar... FLashit(); } }

void FLashit() { // make the LED blink digitalWrite(RED_LED,HIGH); // light up redLED delay(Blink_1); // long pause

digitalWrite(GREEN_LED,HIGH); // light up greenLED delay(Blink_2); // short pause

digitalWrite(RED_LED,LOW); // switch off redLED delay(Blink_1); // long pause

digitalWrite(GREEN_LED,LOW); // switch off greenLED delay(Blink_2);

// Show fancy bar in bottom part of LCD line 3 lcd.setCursor(0,3); lcd.print("<==================>"); delay (wait * 5);

}

// --------------------------------- Rhyno Audio - 2020 ---------------------------------- //

============================= INO CODE END ======================================

Thank for the suggestions, all the best, Brock

rchanrussell commented 4 years ago

Hmm just skimming for your commands I see you're using Serial.print. I had better experience with Serial.write(buffer, sizeToWrite);

Try that and see if it removes the issue you're having with invalid chars. Also my code is only using \r, not \r \n. Try that too.

I haven't found my Particle.io Photon code yet (well I found test code but I'm not sure where the actual code is) but I do have my arduino samples and I had two methods, one i listed above, the other was just printing single chars at a time, but the last was always \r, never \r\n.

Mistahbrock commented 4 years ago

Thanks for the great tips

I was mimicking the python syntax from your script, being a total noob

I'll give the write a try and follow your advices

All the best, Brock

rchanrussell commented 4 years ago

Hi Brock,

I ams sorry, I didn't realize it. Those Python scripts were for testing. It sounds like it could be a buffering issue. I have arduino examples I can share (though I haven't had time to search yet for the file I was using before... I might not have even updated it because I was testing with this one below with a level controller in between):

/* LiquidCrystal Library - Hello World

Demonstrates the use a 16x2 LCD display. The LiquidCrystal library works with all LCD displays that are compatible with the Hitachi HD44780 driver. There are many of them out there, and you can usually tell them by the 16-pin interface.

This sketch prints "Hello World!" to the LCD and shows the time.

The circuit:

*/

// include the library code:

include

// initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); int adc_key_in = 0; int lcd_key = 0;

define btnRIGHT 0

define btnUP 1

define btnDOWN 2

define btnLEFT 3

define btnSELECT 4

define btnNONE 5

// global variables unsigned long debounceIncrTrack; unsigned long debouncePlayRec; unsigned long debounceReset; unsigned long timeDelayPlayRec; byte activeTrack; byte activeGroup = 1; int maxTrack = 10; int maxGroup = 4; bool recPlay; bool prevRecPlay; bool incrTrack;

// Functions void incrTrackHandler() { if ((millis() - debounceIncrTrack) > 50) { activeTrack++; if (activeTrack == maxTrack) { activeTrack = 0; } } debounceIncrTrack = millis(); }

void decrTrackHandler() { if ((millis() - debounceIncrTrack) > 50) { activeTrack--; if (activeTrack < 0) { activeTrack = 0; } } debounceIncrTrack = millis(); }

void recPlayHandler() { if ((millis() - debouncePlayRec) > 50) { // rec/play if (recPlay) { lcd.setCursor(0, 1); lcd.print("P"); Serial.print('p'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('\r'); recPlay = false; } else { lcd.setCursor(0, 1); lcd.print("R"); Serial.print('r'); Serial.print('0'); Serial.print(activeTrack, DEC); Serial.print('g'); Serial.print(activeGroup, DEC); Serial.print('\r'); recPlay = true; } } debouncePlayRec = millis(); }

void resetHandler() { if ((millis() - debounceReset) > 50) { lcd.setCursor(0, 1); lcd.print("S"); Serial.print('s'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('\r'); } debounceReset = millis(); }

void quitHandler() { if ((millis() - debounceReset) > 50) { lcd.setCursor(0, 1); lcd.print("Q"); Serial.print('q'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('0'); Serial.print('\r'); } debounceReset = millis(); }

void updateUserInfo() { lcd.setCursor(0, 0); lcd.print("Grp: 1,"); lcd.print("Trk: "); lcd.print(activeTrack); }

// read the buttons void read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return; // We make this the 1st option for speed reasons since it will be the most likely result // For V1.1 us this threshold / if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 250) return btnUP; if (adc_key_in < 450) return btnDOWN; if (adc_key_in < 650) return btnLEFT; if (adc_key_in < 850) return btnSELECT;
/

// For V1.0 comment the other threshold and use the one below:

if (adc_key_in < 50) // right { recPlayHandler(); } else if (adc_key_in < 195) // up { incrTrackHandler(); } else if (adc_key_in < 380) // down { decrTrackHandler(); } else if (adc_key_in < 555) // left { resetHandler(); } else if (adc_key_in < 790) // select { quitHandler(); }

updateUserInfo(); }

void setup() { // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("Ready");

timeDelayPlayRec = 0; activeTrack = 0; recPlay = false; debounceIncrTrack = millis(); debouncePlayRec = millis();

Serial.begin(115200); }

void loop() { // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): read_LCD_buttons(); // read the buttons

}

Mistahbrock commented 4 years ago

Hello Russel

Thanks a million, I was going in circles, I'll check your codes and see where that gets me :)

Have a nice day :)

Hmm, using both your code from above and my mix I get only to this point on th pi machine: ----------- begin -------- engine sample rate: 48000 Recording CC 140 Recording track 0 on group 1, frame delay 809 RecDataCopy masterIDX 0, callCounter 140, offset 3236, bytes 860 ----------- end --------

After this there is no response from the looper .pgm and I havn't succeed in actually recording anything yet...

Update: I suddenly realised that your code is based on Jackd1 and I was using jackd2,

Now I finally recorded a very crude loop, as there is some complaints "unknown source port in attempted connection [(null)] cannot connect input ports"

I looks promising :)

Best regards

rchanrussell commented 4 years ago

Hi Brock,

Are you sure about data flow? Do you have your startup script parameters setup correctly? Did you follow my setup and ensure it works for you? Did you rebuild/compile JackAudio and install it? I went through the setup a few times to try to get the steps correct, but there's a lot that needs to happen.

It's all required to get the data to flow. The looper should be printing stuff, JackAudio as well. Ensure the SerialPi is setup too.

I am not annoyed, just trying to help.

~Robert

Mistahbrock commented 4 years ago

Hello Robert

Good to know, I tend to get carried away sometimes.

Now I'm able to record, playback and overdub and delete, all commands are working fine, but there is still issues : Jack have trouble finding ports, I guess it's my .jackdrc or .asoundrc

And when I record there is a ringmodulation like effect on the signal and when I overdub there is occasionaly a sinelike tone before the a dropout.

the screen looks like this : --------------------------------- ssh on IP PI4 --------------------- : root@raspberrypi:~/Developer/rpi_audio_looper_c/src# ./pgm connect(2) call to /dev/shm/jack-0/default/jack_0 failed (err=No such file or directory) jackd 0.125.0rc1 Copyright 2001-2009 Paul Davis, Stephane Letz, Jack O'Quinn, Torben Hohn and others. jackd comes with ABSOLUTELY NO WARRANTY This is free software, and you are welcome to redistribute it under certain conditions; see the file COPYING for details

JACK compiled with System V SHM support. loading driver .. creating alsa driver ... hw:0,0|hw:0,0|128|3|48000|0|0|nomon|swmeter|soft-mode|32bit configuring for 48000Hz, period = 128 frames (2.7 ms), buffer = 3 periods ALSA: final selected sample format for capture: 16bit little-endian ALSA: use 3 periods for capture ALSA: final selected sample format for playback: 16bit little-endian ALSA: use 3 periods for playback JACK server started engine sample rate: 48000 unknown source port in attempted connection [(null)] cannot connect input ports ------------------------------- ssh on PI4 ------------------------ :

Still the progression is going in the right direction.

Have a nice weekend :)

rchanrussell commented 4 years ago

Just keep checking the configuration, try the links I reference in the setup and check things over. Make sure you're configured for your audio interface. The frequency of 48000? Not 44100? What are you using to interface the audio to the RPi?

Very glad you've got the controls working, that's a great start!

I managed to get some good ideas written down in my design doc. Naturally my mind jumps to implementation LOL but it's progressing and I still feel motivated.

Mistahbrock commented 4 years ago

Im using a dirt-cheap c-media usb plug which I uncased and shielded to a close to dead silent state.

And i read somewhere that usb works better at 4800 than in 44100 rates.

I have the exact same issues running at 44100 sr.

If I get the code up and running I will make a fork on my git.

Mistahbrock commented 4 years ago

Well I seem to be stuck

Whenever I record I´the sound crackles and distorts, I can playback but when I overdub it gets even worse and a terrible squeaky sound occurs.

If I had a bigger brain and more coding knowledge I might solve it, but for now I need a break

All the best, Brock

rchanrussell commented 4 years ago

I remember having some issues with a cheap USB recording but it was due to it being a mono input and I was trying to record stereo. That's the other issue, by default it tries to record in stereo. Is this the issue? Mono vs stereo? It's a bit of a pain to remove the stereo :( bad SW design.

The new version I am very slowly working on will support mono/stereo as part of the configuration input command.

The work around would be to go through main() and comment out the inputR related items...

/* create four ports -- left in and out, right in and out */

looper.input_portL = jack_port_register (looper.client, "inputL",
                 JACK_DEFAULT_AUDIO_TYPE,
                 JackPortIsInput, 0);

/ looper.input_portR = jack_port_register (looper.client, "inputR", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); / looper.output_portL = jack_port_register (looper.client, "outputL", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); / looper.output_portR = jack_port_register (looper.client, "outputR", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); / if ((looper.input_portL == NULL) || (looper.output_portL == NULL)) { fprintf(stderr, "no more JACK ports available\n"); exit (1); } / if ((looper.input_portR == NULL) || (looper.output_portR == NULL)) { fprintf(stderr, "no more JACK ports available\n"); exit (1); } / /* Tell the JACK server that we are ready to roll. Our

Try that just in case your input was like the cheapo mine was that didn't support stereo.

Other than that, did you try the setup stuff I recommended, turning off certain things and ensuring running headless and all that?

rchanrussell commented 4 years ago

I can't wait to eventually get the code redesigned. I'm only working on this once a week. I'm redesigning first and documenting and figuring out what should own what and how to break up the code. Right now "master" is a mess that's difficult to debug. I'm going to completely re-write it starting from the base example and reading what each api is intended to do. I didn't do that before; I just tried to get a prototype done.

Best of luck.

Mistahbrock commented 4 years ago

Thanks for a quick response, Maybe I'll dive in to it but my head needs a rest, but I'll be looking forward for more news on this splendid idea.

All the best, Brock