Open Mistahbrock opened 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
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: ================================
/*** *
/***/ // LCD
// Button stuff
// 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..
//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
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.
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
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:
wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008 by David A. Mellis library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net) example added 9 Jul 2009 by Tom Igoe modified 22 Nov 2010 by Tom Igoe modified 7 Nov 2016 by Arturo Guadalupi
This example code is in the public domain.
*/
// include the library code:
// 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;
// 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
}
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
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
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 :)
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.
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.
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
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
process() callback will start running now. */
if (jack_activate (looper.client)) { fprintf (stderr, "cannot activate client"); exit (1); }
/* Connect the ports. You can't do this before the client is
it. */
ports = jack_get_ports (looper.client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); if (ports == NULL) { fprintf(stderr, "no physical capture ports\n"); exit (1); }
// Establish connection between ports if (jack_connect (looper.client, ports[0], jack_port_name (looper.input_portL))) { looper.input_portL = NULL; fprintf (stderr, "cannot connect input ports\n"); } / if (jack_connect (looper.client, ports[1], jack_port_name (looper.input_portR))) { looper.input_portR = NULL; fprintf (stderr, "cannot connect input ports\n"); } / free (ports);
ports = jack_get_ports (looper.client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); if (ports == NULL) { fprintf(stderr, "no physical playback ports\n"); exit (1); }
if (jack_connect (looper.client, jack_port_name (looper.output_portL), ports[0])) { looper.output_portL = NULL; fprintf (stderr, "cannot connect output ports\n"); } / if (jack_connect (looper.client, jack_port_name (looper.output_portR), ports[1])) { looper.output_portR = NULL; fprintf (stderr, "cannot connect output ports\n"); } / free (ports);
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?
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.
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
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