Raspberry Pi 3 SoftSynth Box [Buildlog]

Emulation of old PCs, PC hardware, or PC peripherals.

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-07 @ 16:55

Unfortunately I had no time to do another video today.
And I have to travel until next thursday.
Hope I get the time to comment my scripts and post them here in rhe next few days though.
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-07 @ 22:20

Here are the two python scripts that i use in parallel to drive the logic and the leds.
Unfortunately I don't have access to the bashscripts, that start/restart the synths, but I will post them when I have access to the pi again.
I commented them as good as I can, but if there still are any questions, feel free to ask.

rpimidi.py (main script for handling input, lcd and start/restart actions. I put it into /etc/rc.local to start it automatically when the pi starts):
Code: Select all
#!/usr/bin/env python

# We use the RPLCD Python library (https://github.com/dbrgn/RPLCD)
from RPLCD.i2c import CharLCD
# We need the time library for delay
import time
# Glob makes it easier to parse a folder of files
import glob
# We use the OS library to use system commands
import os
# Alsaaudio is used to control the alsamixer from Python
import alsaaudio
# Importing RPi GPIO to check button input on GPIO pins
import RPi.GPIO as GPIO

# Make a control instance of the PCM device from alsaaudio. "PCM" needs to be replaced with the appropriate string for other USB soundcards as the integrated analog out
m = alsaaudio.Mixer("PCM")

# Set up GPIO pins we use for button inputs

# Set the GPIO numbering scheme to use header pin numbering
GPIO.setmode(GPIO.BOARD)

# Set the four used GPIOs as inputs with internal pullup resistors activated, so we just need to pull them to ground by pressing the button for input
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(13, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(15, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Create instance of the RPLCD. In this case an Hitachi 44780 20x2 LCD with a PCF8574 portextender
lcd = CharLCD(i2c_expander='PCF8574', address=0x27, port=1, cols=21, rows=2, dotsize=8, charmap='A02', auto_linebreaks=False, backlight_enabled=True)

# Make sure the LCD is clear
lcd.clear()

# Declare the global lists to store synths and soundfonts we use in this script
global soundfonts
global synths

# Get synths from file and store them in a list called synths. Probably not needed as there are only 3 available for now: Roland CM32L (Munt), Roland MT32 (Munt), General Midi (FluidSynth)
synths = open("synths.dat").readlines()

# Get all the Sondfont names from all the .sf2 files in a directory and store them in a list named soundfonts
soundfonts = glob.glob('/home/pi/sf2/*.sf2')

# Remove paths from the results
soundfonts = [os.path.basename(x) for x in soundfonts]

# Remove sf2 extension from results
soundfonts = [os.path.splitext(x)[0] for x in soundfonts]

# Sort list alphabetically
soundfonts = sorted(soundfonts)

# declare some more variables for navigating the menus and setting edit mode
sfcounter = 0
syncounter = 0
changed = 0

# Write first Synth to the LCD
lcd.cursor_pos = (0, 0)
lcd.write_string(synths[syncounter])
lcd.cursor_pos = (1, 0)
lcd.write_string("Munt Emulator")

# Clear contents of the lcdmessage.txt file. This file is used to to store the sysex messages from the mt32d application to a file so we can read them to the python script
open('/home/pi/lcdmessage.txt', 'w').close()

# Start the first synth via batch script in the background
os.system('/home/pi/cm32lstart.sh &')

# declare more variables for the volume control and lcdmessage for comparison
volume = 0
new_volume = m.getvolume()[0]
oldmessage = ""

# For debugging only: print the current volume
# print m.getvolume()[0]

# Start main loop
while True:
 
  # open the lcdmessage.txt file for reading
  lcdfile = open("/home/pi/lcdmessage.txt","r")

  # read first line of the file to a variable
  lcdmessage =  lcdfile.readline()

  # if the message is not the same as the last one read, print the new message to the second line of the lcd, then close the file and update the oldmessage variable for comparison with future messages.
  if lcdmessage != oldmessage:
    lcd.cursor_pos = (1, 0)
    lcd.write_string(lcdmessage)
    lcdfile.close()
    oldmessage = lcdmessage

  # Give the main loop a little delay to get cpu usage down to about 17% instead of 100%
  time.sleep(0.005)
 
  # If the button on pin 15 is pressed, check every second for 5 seconds, if the button is still pressed and count down the seconds. If the button is released before the 5 seconds are over, reset the current synth. If th button is still pressed after 5 seconds, display a good bye mesage and shut down the Pi
  if GPIO.input(15) == 0:
   for x in range(0,4):
     time.sleep(1)
     if GPIO.input(15) == 0:
       lcd.cursor_pos = (1, 0)
       lcd.write_string("Shutdown in "+str(5-x)+"    ")
   if GPIO.input(15) == 0:
     lcd.cursor_pos = (1, 0)
     lcd.write_string("Good Bye ...      ")
     os.system("sudo shutdown -h now")

  # If the button on pin 11 is pressed in the main loop, increase the volume variable by 1   
  if GPIO.input(11) == 0:
    new_volume = volume + 1
 
  # If the button  on pin 12 is pressed in the main loop, decrease the volume variable by 1
  if GPIO.input(12) == 0:
    new_volume = volume - 1
 
  # if the new volume is smaller than 0 or higher than 99, set it to 0 or 99
  if int(new_volume) < 0:
    new_volume = 0
  if int(new_volume) > 99:
    new_volume = 99

  # If the new volume is different from the current volume, set the volume to the new volume and use the instance of our alsamixercontrol to set it to the new volume
  if new_volume != volume:
    volume = new_volume
    m.setvolume(volume)
    time.sleep(0.1)
   
  # write the current volume to the lcd. Ad a whitespace before the volume string if the volume is smaller than 10 to prevent a shift in the lcd columns
  lcd.cursor_pos = (0,14)
  if int(volume) > 9:
    lcd.write_string("Vol:" + str(volume))
  else:
    lcd.write_string("Vol: " + str(volume))
   
  # If the button on pin 13 is pressed, enter the Edit function by setting the set variable to 1 (True) and write the String "Edit" to the lcd to indicate that we are in edit mode now
  if GPIO.input(13) == 0:
    set = 1
    lcd.cursor_pos = (0,14)
    lcd.write_string("      ")
    lcd.cursor_pos = (0,15)
    lcd.write_string("Edit")
   # little delay as a software debounce of the button
    time.sleep(0.2)

  # enter Edit loop
  while set == 1:
 
    # if button on pin 12 is pressed during edit loop, check if we are in General Midi mode (syncounter ==2) and make sure that the value of the sfcounter variable is not higher or lower than the number of soundfonts in the soundfonts list
    if GPIO.input(12) == 0:
      if syncounter == 2:
        if sfcounter == len(soundfonts)-1:
          sfcounter = -1

        # increase the soundfontcounter by one, clear the lcdline by filling it with whitespaces, then update the current soundfont from the list and writing it to the lcd after cropping it to the max. characters of the lcd 
        sfcounter = sfcounter + 1
        lcd.cursor_pos = (1, 0)
        lcd.write_string("                    ")
        if syncounter == 2:
          lcd.cursor_pos = (1, 0)
          lcd.write_string(soundfonts[sfcounter][0:19])
       
      # if the current synth is not General Midi, just print Munt Emulator to the second line of the lcd
      else:
          lcd.cursor_pos = (1,0)
          lcd.write_string("Munt Emulator     ")
       
        # Only for debug: Print current sfcounter variable
        # print(sfcounter)
      
      # little delay for software debounce of the button
        time.sleep(0.2)

   # If the button on pin 11 is pressed during Edit loop, do the same as with the soundfonts before and update the lcd with current synth an soundfont, if the syncounter is 2 (General Midi)   
    if GPIO.input(11) == 0:
      if syncounter == len(synths)-1:
       syncounter = -1
      syncounter = syncounter + 1
      lcd.cursor_pos = (0, 0)
      lcd.write_string("                   ")
      lcd.cursor_pos = (0, 0)
      lcd.write_string(synths[syncounter])
      lcd.cursor_pos = (1, 0)
      lcd.write_string("                   ")
      if syncounter == 2:
        lcd.cursor_pos = (1, 0)
        lcd.write_string(soundfonts[sfcounter][0:19])
      else:
        lcd.cursor_pos = (1,0)
        lcd.write_string("Munt Emulator")
      print(syncounter)
    
     # little delay for software debounce of the button
      time.sleep(0.2)
   
   # If the button on pin 13 is pressed again, exit the Edit loop by changing set variable to 0 (False) and set the changed variable to 1 to tell the main loop to restart the synth with the parameters set during edit loop
    if GPIO.input(13) == 0:
      changed = 1
      set = 0

  # If the edit loop signaled, that something has changed (changed == True), print "Please Wait..." to the lcd while starting the newly selected synth with the newly selected soundfont Then update the lcd with the new values and after that, change the changed variable back to 0 (False)
  if changed == 1:
   lcd.cursor_pos = (1,0)
   lcd.write_string("Please Wait...     ")

   print(synths[syncounter])
   synthname = synths[syncounter]
   print(synthname)
   if syncounter == 0:
     os.system('/home/pi/cm32lstart.sh')
     print("cm32l")
   if syncounter == 1:
     os.system('/home/pi/mt32start.sh')
     print("mt32")
   if syncounter == 2:
     os.system('/home/pi/fluidsynthstart.sh \"'+soundfonts[sfcounter]+'.sf2\"')
   lcd.cursor_pos = (1,0)
   lcd.write_string("              ")
   if syncounter == 2:
     lcd.cursor_pos = (1,0)
     lcd.write_string(soundfonts[sfcounter][0:19])
   else:
     lcd.cursor_pos = (1,0)
     lcd.write_string("Munt Emulator")
   changed = 0


midilight.py (script to listen to the midiport and set the leds accordingly to the messages):
Code: Select all
#!/usr/bin/env python

# import sys to exit to shell in case of error
import sys

# import time for delays and stuff
import time

# import the apa102 library to use our APA102 RGB leds (https://github.com/tinue/APA102_Pi)
import apa102

# inport the python rt-midi library (https://github.com/SpotlightKid/python-rtmidi)  to open the midi port and monitor midi messages
from rtmidi.midiutil import open_midiinput

# define an instance of the apa102 library to control our ledstrip with 16 leds (Midid has 16 channels per port), brightness of 20% (those leds are REALLY BRIGHT, so low values suffice here), mosi pin 10 and sclk pin of 11. Also the color order of my ledstrip is rgb. May vary with other models.
strip = apa102.APA102(num_led=16, global_brightness=20, mosi = 10, sclk = 11,
                                  order='rgb')

# initialize a variable that counts how many cycles there was no midi input to reset the leds (some tracks or programs don't send note off for all channels when the song ends)                          
nonecount = 0

# set all pixels to black
for l in range(0, 15):
  strip.set_pixel_rgb(l, 0x000000)

  # update the strip
strip.show()

# define a little helper function to use as a mapfunction analog to arduino's map function (in python, map has different functionality)
def valmap(x, in_min, in_max, out_min, out_max):
    return int((x-in_min) * (out_max-out_min) / (in_max-in_min) + out_min)

# Open a midiport: midiinput(0) refers to first found midiinport, usually 14:0, the systems through port. The USB midiinterface as well as the synths are mapped to this port. This is an easy way to make sure, they always find the right route.
try:
    midiin, aplaymidi = open_midiinput(0)
# if that fails, close the program
except (EOFError, KeyboardInterrupt):
    sys.exit()
   
   # Enter main loop
    while True:
   
       # get a message from the midiport and store it in a list called msg Please refer for the python-rtmidi documentation to see the formatting. It looks like this: [[commandandchannel, note, velocity],sysex and other stuff]
        msg = midiin.get_message()
      
        # declare another variable to only get the first list in the list and if the message read is not "none", store the first list in the msg variabel to the list msg0
        msg0 = [0,0,0]
        if msg != None:
          msg0 = msg[0]
       
          # if there is a message we can read, get the chahnnel and check for note on command (144, 159) from the first variable from the list and update the leds, that correspond to the channels with the info for color hue (strip.wheel(valmap(msg0[1], 21, 108, 0, 220))) and velocity (valmap(msg0[2], 0, 127, 0, 15))
          if msg:
            for x in range(144, 159):
              if x in msg[0]:
                if len(msg0) > 2:
                  strip.set_pixel_rgb(15 - (159 - x), strip.wheel(valmap(msg0[1], 21, 108, 0, 220)), valmap(msg0[2], 0, 127, 0, 15))
             
            # if there is no velocity set (len(msg0) <= 2), just set the brightness to 15%
                else:
                  strip.set_pixel_rgb(15 - (159 - x), strip.wheel(valmap(msg0[1], 21, 108, 0, 220)), 15)
             
          # if a note off command is in the list, set the channel's led to black
            for y in range(128, 143):
              if y in msg[0]:
                strip.set_pixel_rgb(15 - (143 - y), 0x000000)
            
          # as we recieved a message, set the count of cycles with no midiinput to 0 again
            nonecount = 0
         
       # if there is no message to recieve, rtmidi is non-blocking and will set the message to "None" if no message is present at the midiport, increase the nonecount by 1 and go on
        if msg == None:
          nonecount = nonecount + 1
       
        # if the nonecount reaches 5000, reset all leds to black (ca. 10 seconds) and start to count again
          if nonecount > 5000:
            for i in range(0,15):
              strip.set_pixel_rgb(i, 0x000000)
            nonecount = 0
       
        # show our changes to the strip
          strip.show()
       
      #add a delay to tame down cpu usage
        time.sleep(0.001)
# if we stop the scipt by ctrl+c or kill command, end the script and close the midi connection, then remove the instance of midiin
finally:
    print("Exit.")
    midiin.close_port()
del midiin
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-08 @ 18:44

I also want to give the self made midi uart interface a second try.

Here is a picture of etching a prototype board in HCl with some H2O2:
20180127_160932.jpg
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-16 @ 13:12

Here are the scripts to shudown and restart the synths.
I think they are pretty selfexplanatory. TTYmidi is still in there but won't do anything as it is not started.

fluidsynth:
Code: Select all
#/bin/bash

if pgrep ttymidi ; then
  sudo killall ttymidi
  sleep 1
fi

if pgrep -f midilight.py ; then
  sudo kill $(pgrep -f midilight.py)
fi

if pgrep mt32d ; then
  echo "Killing previous instance of mt32d"
  sudo killall mt32d
  while pgrep mt32d ; do
    sleep 0.1
  done
fi

if pgrep fluidsynth ; then
  echo "Killing previous instance of fluidsynth"
  sudo killall fluidsynth
  while pgrep fluidsynth ; do
    sleep 0.1
  done
fi

while aconnect -l |grep 128 ; do
  sleep 0.1
done

sudo LD_PRELOAD=/usr/local/lib/libfluidsynth.so.1.7.0 /usr/local/bin/fluidsynth --server --no-shell --audio-driver=alsa /home/pi/sf2/"$1" &

while ! aconnect -l |grep 128 ; do
  sleep 0.1
done

#sudo ttymidi -s /dev/ttyAMA0 -b 38400 &
sleep 1

aconnect 20:0 14:0
aconnect 129:0 14:0
aconnect 14:0 128:0

sleep 1

python /home/pi/APA102_Pi/midilight.py &


cm32l:
Code: Select all
#/bin/bash

if pgrep ttymidi ; then
  sudo killall ttymidi
  sleep 2
fi

if pgrep -f midilight.py ; then
  sudo kill $(pgrep -f midilight.py)
fi

if pgrep mt32d ; then
  echo "Killing previous instance of mt32d"
  sudo killall mt32d
  while pgrep mt32d ; do
    sleep 1
  done

fi

if pgrep fluidsynth ; then
  echo "Killing previous instance of fluidsynth"
  sudo killall fluidsynth
  while pgrep fluidsynth ; do
    sleep 1
  done
fi

while aconnect -l |grep 128 ; do
  sleep 1
done

LD_PRELOAD=/usr/local/lib/libmt32emu.so.2 /usr/local/bin/mt32d -o 1 -i 24&

while ! aconnect -l |grep 128 ; do
  sleep 1
done

#sudo ttymidi -s /dev/ttyAMA0 -b 38400 &
sleep 2

aconnect 20:0 14:0
aconnect 129:0 14:0
aconnect 14:0 128:0

sleep 1

python /home/pi/APA102_Pi/midilight.py &


mt32:
Code: Select all
#/bin/bash

if pgrep ttymidi ; then
  sudo killall ttymidi
  sleep 2
fi

if pgrep -f midilight.py ; then
  sudo kill $(pgrep -f midilight.py)
fi

if pgrep mt32d ; then
  echo "Killing previous instance of mt32d"
  sudo killall  mt32d
  while pgrep mt32d ; do
    sleep 1
  done
fi

if pgrep fluidsynth ; then
  echo "Killing previous instance of fluidsynth"
  sudo killall fluidsynth
  while pgrep fluidsynth ; do
    sleep 1
  done
fi

while aconnect -l |grep 128 ; do
  sleep 1
done

LD_PRELOAD=/usr/local/lib/libmt32emu.so.2 /usr/local/bin/mt32d -o 2 -i 24&

while ! aconnect -l |grep 128 ; do
  sleep 1
done

#sudo ttymidi -s /dev/ttyAMA0 -b 38400 &
sleep 2

aconnect 20:0 14:0
aconnect 129:0 14:0
aconnect 14:0 128:0

sleep 1

python /home/pi/APA102_Pi/midilight.py &
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-17 @ 16:56

New video demo:
https://youtu.be/_VYdw-AfNGc

I wouldn't say finished but useable.
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-2-17 @ 20:19

mcobit wrote:Right now I am starting/killing processes.
Also helps with hanging notes if a specific synth doesn't get a note off after switching.
Having all synths running seems too resource hungry and more unreliable.


This can be done with a MIDI reset SYSEX. I have the requisite file if you want.
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-17 @ 21:53

As said, having all synths running all the time on different ports and having to find out and make sure the ports are the same every start seems a bit more comlicated to me than starting the synth needed. I don't use any gui and want to have it as automated and idiot proof as possible.
Every synth will consume resources that sometimes are needed e.g. when loading big soundfonts or if you have tracks with higher channel count.

Of course I am interested in the file though.
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-2-17 @ 22:32

The Python ALSA API allows you to select internal MIDI routing points by label, but I suspect you're right in terms of making this whole thing straightforward.

I've experimented with using JACK as a sound server (it's actually pretty much required when I have my MIDI/DAC/ADC module plugged in) and I believe hot switching the outputs while maintaining consistent MIDI state in the synthesizers is viable as long as the CPU and RAM holds out. I suspect this is useful only in corner cases like A/B soundfont comparisons and so forth.
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby mcobit » 2018-2-18 @ 18:35

Maybe I will revise the software again later as I also saw some pythonbindings for libfluidsynth...
I want to reduce overhead where possible though. That's why I decided against pulse, jack, portaudio etc.
mcobit
Member
 
Posts: 123
Joined: 2017-11-15 @ 18:45

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-2-18 @ 23:05

Well, IMO Pulseaudio is awful but Jack is pure sex. I've never had any buffer glitching with it and CPU usage is reasonable. If you have a chance to look at it, let me know what you think.
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby DieKatzchen » 2018-3-08 @ 21:47

A few questions:

How is the midi to uart circuit working? You said you were going to give it another go?

Where did you get your led strips? I'm not seeing any with that tight spacing and that diffused casing.

Would you be willing to share your files for that gorgeous laser cut box?

I'm looking forward to seeing this project unfold.
DieKatzchen
Newbie
 
Posts: 9
Joined: 2017-5-29 @ 19:29

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby DieKatzchen » 2018-3-09 @ 02:41

Another question: Where the hell did you find a 20x2 display? I can't find anything but 16x2 and 20x4.
DieKatzchen
Newbie
 
Posts: 9
Joined: 2017-5-29 @ 19:29

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-3-10 @ 05:47

It's a standard 0220 lcd module. The 2004 and 1602 modules are quite a bit more common, but you can find the 20x2 ones via the usual suspects. Newhaven Display is probably the most prolific manufacturer and they're available in 2x8 and 1x16 interface pinouts (as well as I2C and SPI).
https://www.digikey.ca/products/en/opto ... ageSize=25
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby ghogan42 » 2018-3-18 @ 09:11

First of all, I'd like to thank mcobit and also gdjacobs for posting info and projects here. It makes it a bunch easier when someone else does something first :happy:

I'm just starting a similar project to mcobit's (sound module box with a pi3). I don't even have some of the parts yet. But I've been writing python and bash scripts to control the synths (I'm using a graphic lcd and so I have more stuff to do on the display) and I had a question and maybe a hint or two for other people working on similar projects.

First off, I followed your example and compiled MUNT and FluidSynth with optimized compiler flags. MUNT seems to work just fine so far.

But I did run into a midi file that hit 100% cpu in FluidSynth and made the sound skip. It's the Leisure Suit Larry 6 GM soundtrack (LSL6GM.MID ) from here: http://www.midimusicadventures.com/queststudios/midi-soundtracks/complete-soundtracks/

My pi hits 100% cpu during the opening fanfare of the sierra intro and the sound skips using a couple of common soundfonts (others are fine). Arachno is one that skips (maybe chorium skips too). It's not an issue for a reason I'll explain below, but I'm curious if anyone else with a pi3 can play that MIDI file without issues with the Arachno or other larger soundfonts?

Having said that, the reason it's not an issue is because it led me to the fluidsynth docs where I found out that fluidsynth can be started multithreaded. That takes care of any fluidsynth speed issues. If I set it to use 3 cpus (first thing I tried, I'm sure 2 works fine), then I can throw a forearm on my keyboard while the fanfare plays and fluidsynth doesn't miss a beat while topping out at 170% cpu use. :lol:

Here's how mcobit's script starts fluidsynth:
Code: Select all
/usr/local/bin/fluidsynth --server --no-shell --audio-driver=alsa /home/pi/sf2/"$1" &


Here's how I start fluidsynth:
Code: Select all
/usr/local/bin/fluidsynth --server --no-shell -o synth.cpu-cores=3 -o audio.alsa.device=hw:1 -o audio.period-size=64  --audio-driver=alsa /home/shares/midi_files/soundfonts/"$1" &


You can see the "-o synth.cpu-cores=3" argument to start it up multithreaded.

Another change I made is that I use "-o audio.alsa.device=hw:1" to specify the device (which is my usb sound card) . Without that, fluidsynth tells me that it "requested a period size of 64 but got 940 instead". And latency is terrible that way. According to https://sourceforge.net/p/fluidsynth/wiki/LowLatency/, using the "alsa device layer" is the lowest latency way to use alsa devices. For me it's the difference between good and unusably poor latency. So it's something to try for people that have latency issues with usb sound devices.

I think that's it for now. Once I have something a little closer to a project, I'll make a thread here to post my scripts and whatnot for people. :cool:
ghogan42
Newbie
 
Posts: 8
Joined: 2013-7-23 @ 22:16

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby Solarstorm » 2018-3-18 @ 09:30

Neat!
User avatar
Solarstorm
Member
 
Posts: 203
Joined: 2013-9-14 @ 11:38
Location: Heidelberg, Germany

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-3-21 @ 20:32

ghogan42 wrote:First of all, I'd like to thank mcobit and also gdjacobs for posting info and projects here. It makes it a bunch easier when someone else does something first :happy:

I'm just starting a similar project to mcobit's (sound module box with a pi3). I don't even have some of the parts yet. But I've been writing python and bash scripts to control the synths (I'm using a graphic lcd and so I have more stuff to do on the display) and I had a question and maybe a hint or two for other people working on similar projects.

First off, I followed your example and compiled MUNT and FluidSynth with optimized compiler flags. MUNT seems to work just fine so far.

But I did run into a midi file that hit 100% cpu in FluidSynth and made the sound skip. It's the Leisure Suit Larry 6 GM soundtrack (LSL6GM.MID ) from here: http://www.midimusicadventures.com/queststudios/midi-soundtracks/complete-soundtracks/

My pi hits 100% cpu during the opening fanfare of the sierra intro and the sound skips using a couple of common soundfonts (others are fine). Arachno is one that skips (maybe chorium skips too). It's not an issue for a reason I'll explain below, but I'm curious if anyone else with a pi3 can play that MIDI file without issues with the Arachno or other larger soundfonts?

Having said that, the reason it's not an issue is because it led me to the fluidsynth docs where I found out that fluidsynth can be started multithreaded. That takes care of any fluidsynth speed issues. If I set it to use 3 cpus (first thing I tried, I'm sure 2 works fine), then I can throw a forearm on my keyboard while the fanfare plays and fluidsynth doesn't miss a beat while topping out at 170% cpu use. :lol:

Here's how mcobit's script starts fluidsynth:
Code: Select all
/usr/local/bin/fluidsynth --server --no-shell --audio-driver=alsa /home/pi/sf2/"$1" &


Here's how I start fluidsynth:
Code: Select all
/usr/local/bin/fluidsynth --server --no-shell -o synth.cpu-cores=3 -o audio.alsa.device=hw:1 -o audio.period-size=64  --audio-driver=alsa /home/shares/midi_files/soundfonts/"$1" &


You can see the "-o synth.cpu-cores=3" argument to start it up multithreaded.

Another change I made is that I use "-o audio.alsa.device=hw:1" to specify the device (which is my usb sound card) . Without that, fluidsynth tells me that it "requested a period size of 64 but got 940 instead". And latency is terrible that way. According to https://sourceforge.net/p/fluidsynth/wiki/LowLatency/, using the "alsa device layer" is the lowest latency way to use alsa devices. For me it's the difference between good and unusably poor latency. So it's something to try for people that have latency issues with usb sound devices.

I think that's it for now. Once I have something a little closer to a project, I'll make a thread here to post my scripts and whatnot for people. :cool:


With default clocks, I found it was necessary to limit polyphony in Fluidsynth along with disabling reverb and chorus..
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby ghogan42 » 2018-3-22 @ 04:39

With default clocks, I found it was necessary to limit polyphony in Fluidsynth along with disabling reverb and chorus..


Ah. So I guess it makes sense that I ran into a bit of trouble with the default settings in Fluidsynth. Out of curiosity, did you try fluidsynth's multithreading? Because I'll lower polyphony or effects if I have to, but I haven't had any problems since I upped the number of cpu cores I gave fluidsynth. I'm a midi noob though, so I'm not sure how to test things properly. I don't suppose you can point me towards some demanding midi files I can try to see if I can make fluidsynth choke? I haven't really looked for anything like that yet.
ghogan42
Newbie
 
Posts: 8
Joined: 2013-7-23 @ 22:16

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby gdjacobs » 2018-3-22 @ 15:53

I haven't, actually. That would be worth investigating.

As far as testing, I have a selection of MIDI content that comes from Sierra and LucasArts titles. Usually I use QFG4 and Loom for GM and MT32 computational evaluation punctuated by others to check overall sound..
User avatar
gdjacobs
l33t++
 
Posts: 5493
Joined: 2015-11-03 @ 05:51
Location: The Great White North

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby dreamblaster » 2018-3-24 @ 17:32

nice project ! :happy:
Visit http://www.serdashop.com for retro sound cards, MIDI PCB's, and video converters
OPL2LPT, OPL3LPT, X2, S2, S2P, MCE2VGA, ... many projects ! Have a look, and thanks for your support !
dreamblaster
Oldbie
 
Posts: 595
Joined: 2015-1-18 @ 19:34
Location: Belgium

Re: Raspberry Pi 3 SoftSynth Box [Buildlog]

Postby HunterZ » 2018-3-24 @ 18:45

This is great! Been thinking for a long time that it would be awesome to see Munt's simulated LCD output on a real LCD (which is why I made this: https://youtu.be/ExRFasoLT7o ).

I'm curious why this project only shows SysEx text and not the full MT-32 LCD output though?

I'm also impressed that the rPi CPU is up to the task.
User avatar
HunterZ
l33t++
 
Posts: 6074
Joined: 2003-1-31 @ 19:04
Location: Seattle

PreviousNext

Return to PC Emulation

Who is online

Users browsing this forum: No registered users and 1 guest