I tidigare inlägg har jag visat hur man automatiskt kan översätta skriven text till morse och därefter generera en ljudfil som man t.ex. kunde köra igenom en radiosändare och om effekten är tillräckligt stark och reflexionerna i atmosfären optimala så kan man i princip höra morsemeddelandet överallt på jorden.
Vad gör jag om jag inte kan morse d.v.s. meddelandet är bara en okänd serie blippar. Kan jag skriva ett program som läser in morsekoden som ljud och översätter ljudmeddelandet till text? Det visar sig att detta, om signalen är optimal, är mycket enkelt att göra. Amplituden på alla ljudsignaler är exakt lika och alla pauser har någon av tre exakt definierade längder.
Signalen har följande utseende om jag tittar på ljudfilen med hjälp av programmet Audacity:
Vi ser att signalen är extremt ren och störningsfri. Pauser har ljudnivån noll helt utan något brus och signalen är en sinusvåg med konstant amplitud.
För att kunna läsa av signalen likriktar vi den först d.v.s. vi tar absolutvärdet av signalen så att alla negativa värden under strecket i figuren blir positiva. Signalen varierar våldsamt mellan 0 och ca. 0.5 och för att vi skall kunna bedöma om vi detekterar ljud eller tystnad filtrerar vi signalen så att vi beräknar medelvärdet av ett antal ljudvärden. Om medelvärdet är klart positivt så hör programmet ljud och då medelvärdet ligger mycket nära noll så är det tyst.
Programmet har en funktion/subrutin. Jag skrev denna gång programmet i språket python. Översättningen går till så att jag samlar ihop fragment av korta ljdsignaler (*) och långa ljudsignaler (-). Ljudsignalerna läggs till en textsträng ända tills vi stöter på en ”teckenpaus” d.v.s. en paus mellan bokstäver. Vi skickar nu textsträngen t.ex. ‘*-‘ till funktionen translate_char(tecken) som jämför morsetecknet med alla morsetecken i morsealfabetet och därefter skriver ut resultatet som bokstaven ‘a’ i detta fall. Vi nollar nu morsesträngen och börjar samla * rep – för följande tecken. För att läsa vad programmet gör så hoppar vi över funktionen translate_char och börjar läsa kommandoraden på vilken vi vill ha endast en parameter d.v.s. ljudfilens namn. Ljudfilen kan vara en mp3- eller en wav-fil. Om vi ger en mp3-fil så konverterar programmet automatiskt mp3 filen till en wav-fil eftersom wav-filen är lättare att hantera rent tekniskt.
#!/home/lasi/miniconda3/bin/python
# Name=morse_receiver.py
# The program reads an audio file and converts the audio back to plain text.
# The analysis works as follows:
# Read the message and record the lengths of sound and silence to a file.
# Determine the length of dots and dashes and the lengths of silence.
# Create a new file vith * - and the character interval + word interval.
# The file can now be analyzed for morse patterns and converted into text.
# This is free code. Use on your own risk.
import wavfile
import sys
import os
def translate_char(m_string):
m_string.strip()
if (m_string=="*-"):
return "A"
if (m_string=="-***"):
return "B"
if (m_string=="-*-*"):
return "C"
if (m_string=="-**"):
return "D"
if (m_string=="*"):
return "E"
if (m_string=="**-*"):
return "F"
if (m_string=="--*"):
return "G"
if (m_string=="****"):
return "H"
if (m_string=="**"):
return "I"
if (m_string=="*---"):
return "J"
if (m_string=="-*-"):
return "K"
if (m_string=="*-**"):
return "L"
if (m_string=="--"):
return "M"
if (m_string=="-*"):
return "N"
if (m_string=="---"):
return "O"
if (m_string=="*--*"):
return "P"
if (m_string=="--*-"):
return "Q"
if (m_string=="*-*"):
return "R"
if (m_string=="***"):
return "S"
if (m_string=="-"):
return "T"
if (m_string=="**-"):
return "U"
if (m_string=="***-"):
return "V"
if (m_string=="*--"):
return "W"
if (m_string=="-**-"):
return "X"
if (m_string=="-*--"):
return "Y"
if (m_string=="--**"):
return "Z"
if (m_string=="*--*-"):
return "Å"
if (m_string=="*-*-"):
return "Ä"
if (m_string=="---*"):
return "Ö"
if (m_string=="*----"):
return "1"
if (m_string=="**---"):
return "2"
if (m_string=="***--"):
return "3"
if (m_string=="****-"):
return "4"
if (m_string=="*****"):
return "5"
if (m_string=="-****"):
return "6"
if (m_string=="--***"):
return "7"
if (m_string=="---**"):
return "8"
if (m_string=="----*"):
return "9"
if (m_string=="-----"):
return "0"
# Primitive error check
print("Error m_string=",m_string)
print("Len=",len(m_string))
return "?"
if (len(sys.argv)<1) or (len(sys.argv)>=3):
print("Usage: morse_receiver.py snd_file")
exit(0)
# We only process wav-files. If I get a mp3 then convert it to wav
# Add further conversions here as nedessary.
# Filtypen bestäms utifrån filnamnet inte från magisk filtyp.
snd_file = sys.argv[1]
print("File to process: ",snd_file)
if snd_file.endswith('.mp3'):
print("MP3 file detected")
# Convert to a wav file
# model ffmpeg -i song.mp3 -ar 44100 song.wav
cmd = "ffmpeg -i "+snd_file+" -loglevel quiet -ar 44100 -y "+snd_file+".wav >/dev/null"
snd_file=snd_file+".wav"
print("Converted file="+snd_file)
# Convert the mp3 file to wav before processing.
os.system(cmd)
f = wavfile.open(snd_file, 'r')
frames=f.num_frames
wav_data=f.read_float(frames)
ampl=0
i=0
snd = False
nosnd = True
pstart=0
sstart=0
my_ch = "";
for d in wav_data:
ampl=(9*ampl+abs(d[0]))/10
i=i+1
if((ampl>0.1) and (nosnd==True)):
sstart=i
l = i-pstart
if(l<5000):
sp=0
elif((l>5000) and (l<20000)):
print(translate_char(my_ch)+" "+my_ch)
my_ch=""
else:
print(translate_char(my_ch)+" "+my_ch+"\n")
my_ch=""
snd = True
nosnd = False
elif((ampl<0.01) and (snd==True)):
pstart = i
if((i-sstart)<5000):
my_ch = my_ch + "*"
else:
my_ch = my_ch + "-"
snd=False
nosnd=True
Vi kontrollerar om vi fick en mp3-fil som parameter. Om detta är fallet så bygger vi upp ett kommando som en textsträng där programmet ffmpeg används för att göra en wav-kopia av mp3-filen. Kommandot utförs av det externa programmet ffmpeg genom att anropa det via system() d.v.s. vi gör inifrån programmet detsamma som vi skulle ha kunnat göra på kommandoraden.
if snd_file.endswith('.mp3'):
print("MP3 file detected")
# Convert to a wav file
# model ffmpeg -i song.mp3 -ar 44100 song.wav
cmd = "ffmpeg -i "+snd_file+" -loglevel quiet -ar 44100 -y "+snd_file+".wav >/dev/null"
snd_file=snd_file+".wav"
print("Converted file="+snd_file)
# Convert the mp3 file to wav before processing.
os.system(cmd)
Vi läser nu in hela ljudfilen i minnet, PDP11 skulle storkna i detta skede eftersom användarminnet skulle ta slut innan ens halva filen är läst … fint att ha lite mera minne i en modern dator! Vi skapar också några hjälpvariabler som vi behöver lite senare. Om jag skulle dekoda en fil på PDP11 så skulle jag läsa in data från skiva i stället för att ha filen i datorns minne. Att använda skiva i stället för minnet fungerar precis lika bra men hastigheten är kanske en tusendel jämfört med att jobba direkt mot minne. PDP11 från 1970-talet skulle tugga länge på en dekodning av en 20 sekunders ljudfil. Gissar någon minut.
Vi börjar nu läsa in värden, ett datavärde i taget från filen som alltså ligger i centralminnet (RAM) och beräknar ett flytande medelvärde över tio ljudvärden. Experiment visade att detta gav en pålitlig detektion. Jag är övertygad om att en annan filtrering skulle fungera lika bra. För en annan ljudfil genererad av en utomstående producemt så skulle vi antagligen behöve experimentera här.
for d in wav_data:
ampl=(9*ampl+abs(d[0]))/10
Vi går nu vidare och ser när vi stöter på ljud och lägger då på minnet vilket ljudvärde 0 … vi hade och kontrollerar samtidigt om vi går från noll (icke ljud) mot ljud (större än ca. 0.1). Vi kan nu skilja på en ljudpuls och en paus. Genom att vi lagrade start och slut på pulsen så kan vi genom subtraktion beräkna längden på en ljudpuls eller en paus. Om vi stötte på en kort ljudpuls så lägger vi till ‘*’ i slutet av variablen my_ch . Om vi stötte på en lång ljudpuls så lägger vi till ‘-‘. Om vi stötte på en mellanlång eller lång paus så vet vi att tecknet är färdigt för översättning. Om vi stöter på en riktigt lång paus så vet vi att ett ord har passerats och då skriver vi ett radbyte för att underlätta läsningen.
Hela detektorn har då följande utseende:
for d in wav_data:
ampl=(9*ampl+abs(d[0]))/10
i=i+1
if((ampl>0.1) and (nosnd==True)):
sstart=i
l = i-pstart
if(l<5000):
sp=0
elif((l>5000) and (l<20000)):
print(translate_char(my_ch)+" "+my_ch)
my_ch=""
else:
print(translate_char(my_ch)+" "+my_ch+"\n")
my_ch=""
snd = True
nosnd = False
elif((ampl<0.01) and (snd==True)):
pstart = i
if((i-sstart)<5000):
my_ch = my_ch + "*"
else:
my_ch = my_ch + "-"
snd=False
nosnd=True
Intresserade läsare kan lyssna på morsekoden nedan. Min reaktion på denna morsemottagare är egentligen att det visade sig vara mycket lättare att skriva mottagaren än jag hade väntat mig.
Om någon läsare vill skriva en mottagare för svårare morsekod t.ex. morsekod där alla tidsvärden varierar då en människa sänder morse så gissar jag att jag skulle spela in alla meddelanden. Därefter skulle jag skriva en dynamisk analysator som gissar längden på kort/långt ljud samt länden av pause. Dv.s. jag skulle mäta längden på alla tidsvärden separat för vatje fil.
En annan komplikation är att signalamplituden sannolikt skulle kunna variera rätt mycket. Även detta skulle kräva separat hantering så att gränsvärdet för ljud/tystnad skulle kunna väljas utgående från signalen i stället för att ges ett fast värde som i detta exemple.
Jag kommer i denna artikel att visa hur jag enkelt kan skapa en svenskspråkig variant av programmeringsspråket C, språket kallar jag sv och kompilatorn blir då csv. Notera att avsikten med denna övning är att illustrera hur enkelt det vore att erbjuda en nationell nybörejarplattform för unga programmerare. För att verifiera programspråket sv skriver jag ett program som läser in en textsträng och sänder resultatet som morsekod. Morsekoden visas också i textform. Följande steg blir att undersöka vilka förändringar jag behöver göra i koden för att programmet skall fungera på min emulerade minidator PDP11/70 som kör en gammal BSD Unix 2.11 från början av 1990-talet. Unix utvecklades i tiden på minidatorn PDP11 som klarade av att hatera flera användare på olika terminaler allt detta på en maskin med 4 MB (4 miljoner byte) minne. En modern hemdator har typiskt 2000 gånger mera minne … eller ännu mer.
Kompilering av ett program skrivet i sv görs på någon sekund. Jag kompilerar morsesändaren smorse.sv dock utan filtyp eftersom mitt kompileringsskript förenklas en aning om jag inte behöver kontrollera filtypen utan kan anta att användaren vet vad hen gör.
./csv smorse Källkod:smorse.sv Översättaren är definierad i swe.lex Kompilerar översätteren swe.lex till c-kod Kompilerar lex analysatorn till körbar maskinkod: sv Översätter sv-programmet smorse.sv till c-kod: smorse.c C-kod finns nu i smorse.c Kompilerar nu till körbar maskinkod! The executable is: smorse.run
Jag kan nu köra programmet med smorse.run
Notera att morsesändningen i ljudfilen är en annan än på skärmen ovan. En utmaning för lyssnare är att bena ut vad som sänts!
Texten nedan visar hur ”kompilatorn” för språket sv är konstruerad. Jag skriver därefter programmet smorse i språket sv för att verifiera att kompileringen fungerar. Notera att programmet på min Linux-dator genererar hörbar morsekod. PDP11 har inte ljudstöd varför jag tänker generera ett simulerat magnetband som huvuddatorn kan sända som ljud.
En elev som lär sig att programmera kommer att stöta på flera samtidiga utmaningar. Vid programmering dyker det upp nya begrepp såsom ”variabel”, ”fil”, ”adress” och många andra begrepp som inte är bekanta från vardagslivet. Eleven lär sig också ett nytt språk, mycket enkelt dock, som beskriver logiken i programmet. Det språk som ligger under de flesta programmeringsspråk idag är engelska. Om vi börjar undervisa tex. en 10-12 årig elev programmering så kan engelskan som sådan vara ett visst problem som kan göra det svårare för eleven att komma över den första tröskeln in i programmeringens värld.
En enkel lösning på problemet programmering på ett främmande språk är naturligtvis att vi i inledningsaskedet skulle låta eleverna programmera på svenska/finska/norska/estniska för att därefter då grunderna finns gå över till engelska som idag är en de facto standard.
Skulle programmering på t.ex. svenska kräva våldsamma ekonomiska resurser? Är inte utveckling av ett nytt programmeringsspråk samt kompilator för det ”nya” språket extremt dyrt? Svaret är att en enkel tvåstegskompilator kan skrivas i ungefär hundra rader kod … inte dyrt alltså!
Om någon läsare är intresserad av att experimentera med språket sv eller modifiera det för något annat språk så kan det löna sig att ta kontakt via en kommentar så skickar jag alla filerna som ett paket med epost. Jag gör inte dessa förfrågningar synliga. Programlistningar på vebben tenderar att vara opålitliga vilket kan leda till onödig felsökning om man kopierar koden direkt från skärmen.
Språket ”sv” samt kompilatorn ”csv”
Min emulerade PDP11/70 minidator kör idag på mitt arbetsbord och jag måste således hitta på något vettigt leksaksprojekt. Jag kör BSD Unix v2.11 på PDP11:an vilket betyder att jag har tillgång till i princip samma programmeringsverktyg som på min huvuddator d.v.s. en kompilator för språket ”c” (kompilatorn heter cc) samt verktyget lex (lex har jag använt mycket sällan).
Ett trevligt miniprojekt kunde då vara att skapa ett svenskt programmeringsspråk som strukturellt är identiskt med programmeringsspråket ”c” under Unix/Linux. Tanken är att det skall vara möjligt att koda helt i ”sv” eller koda i en hybridmiljö där det är möjligt att använda ”c” direkt utan att programmets funktion påverkas. Tanken är att översätta ”sv” till normalt ”c” som därefter kompileras till maskinspråk som vilket normalt c-program som helst.
Fördelen med att använda c som mellanliggande språk är naturligtvis at jag inte behöver skriva den egentliga kompilatorn och maskinkodsgeneratorn (jag har för många år sedan skrivit ett komplett programmeringsspråk ”sil=simple language). Problemet är alltså att skriva en översättare från språket sv till språket c.
Unix ger tillgång till två klassiska verktyg för att skapa kompilatorer lex (lexical analyzer) och yacc (yacc=yet another compiler compiler). Jag har aldrig tidigare aktivt använt någondera. Programmet ”lex” kan enkelt sköta översättningen från sv till c d.v.s. jag behöver inte yacc eftersom min kompilator redan finns under förutsättning att det program lex producerar förmår att skapa kompilerbar c-kod.
Jag definierar ett antal ”REGLER” i lex som beskriver hur t.ex. ett nyckelord, en kommentar, en textsträng o.s.v. definieras. Då en regel passar in på källtexten skriver mitt genererade analysatorprogram ut motsvarande element för språket c.
Då jag skriver min översättare från sv till c i lex blir programmets längd ungefär hundra rader kod d.v.s. programmet är väldigt litet och överskådligt. Mitt sv-språk är i detta skede ett subset av språket c men redan i nedanstående form kan man skriva riktiga program i sv. Språket kan enkelt utvidgas genom att modifiera filen swe.lex. Då jag använder lex för att kompilera min definition av sv blir resultatet ett c-program som heter lex.yy.c . Jag kompilerar därefter lex.yy.c till ett körbart program sv som sköter översättningen av en källkodsfil i sv till motsvarande c-program.
Lex-program för översättning av sv till c
Filen swe.lex kan enkelt modofieras så att en större del av c-språket stöds. Notera att jag har skrivit swe.lex så att resulterande c-filen som ett sv-program översätts till har exakt samma antal rader som källkoden i sv. Detta betyder att då jag kompilerar mitt sv program så stämmer radnumren för felmeddelanden för både sv och c.
Min definition av sv-språket ser ut på följande sätt:
%{
/* A lexical analyzer for the computer language "sv". This is a simple translation */
/* of the "c" language into swedish. The corresponding "compiler" translates a */
/* sv-source into c-language that can be compiled using an ordinary c-compiler */
/* need this for the call to atof() below */
#include <math.h>
/* need this for printf(), fopen() and stdin below */
#include <stdio.h>
%}
WHITESPACE [ \t\n]+
DIGIT [0-9]
ID [a-zA-Z][a-zA-Z0-9]*
CHAR [a-zåäö][A-ZÅÄÖ][0-9][\ ][!?][\n]
COMMENT \/\/.*[\n]
STR1 \".[\\]*\"
APP [\"]
LPAR [(]
RPAR [)]
LWAV [{]
RWAV [}]
TERMINATOR [;]
EXCL [!]
AE [Ä]
ae [ä]
Aring [Å]
aring [å]
COMMA [,]
%%
{DIGIT}+ printf("%d",atoi(yytext));
{DIGIT}+"."{DIGIT}* printf("%s", yytext);
{STR1} printf("%s",yytext);
{LPAR} printf("%s",yytext);
{RPAR} printf("%s",yytext);
{LWAV} printf("%s",yytext);
{RWAV} printf("%s",yytext);
{EXCL} printf("!");
{COMMA} printf(",");
#inkludera printf("#include");
#definiera printf("#define");
program printf("\nmain");
funktion printf(" ");
resultat printf("return ");
alternativ printf("switch");
valt printf("case ");
bryt printf("break");
om printf("if ");
medan printf("while");
upprepa printf("for");
annars printf("else ");
skrivf printf("printf");
skrivr printf("printf");
fskrivf printf("fprintf");
hämtarad printf("getline");
hämta printf("get");
sätt printf("put");
läs printf("read");
läsrd printf("readln");
dröj printf("delay");
heltal printf("int ");
tecken printf("char ");
byte printf("byte ");
sanningsvärde printf("boolean ");
register printf("int");
bitmask printf("int");
likamed printf("==");
mindre_än printf("<");
mindre_än_eller_likamed printf("<=");
större_än printf(">");
större_än_eller_likamed printf(">=");
och printf("&&");
binär_och printf("&");
and printf("&");
or printf("|");
xor printf("^");
binär_exclusiv_och printf("^\n");
vflytta printf("<<");
hflytta printf(">>");
storlek_t printf("size_t");
\<\< printf("<<");
\>\> printf(">>");
= printf("=");
\< printf("<");
\> printf(">");
\. printf(".");
\&\& printf("&&");
\& printf("&");
{ID} printf("%s", yytext);
{TERMINATOR} printf("%s",yytext);
{COMMENT} printf("%s",yytext);
{WHITESPACE} printf("%s",yytext);
"+"|"-"|"*"|"/" printf("%s", yytext);
"{"[^}\n]*"}" /* eat up one-line comments */
%%
int main(int argc, char *argv[])
{
++argv, --argc; /* skip over program name */
if (argc > 0)
yyin = fopen(argv[0], "r");
else
yyin = stdin;
yylex();
return(0);
}
Verifiering av språket ”sv”
Ett enkelt sätt att visa att det nya språket ”fungerar” är naturligtvis att skriva ett riktigt program i programspråket sv. Jag byggde för kanske tjugo år sedan några led-ficklampor med några barnhemsbarn där vi använde en liten mikroprocessor som programmerades i mitt språk ”sil” (simple language) för Microchips processor 16F84. Ficklamporna programmerades så att de olika barnens ficklampor kunde blinka ägarens namn i morsekod.
Nedan visar jag hur man kan skriva ett program i språket sv som läser in en textrad från användaren och ”sänder” texten som morsekod till skärmen men också som morseljud via datorns högtalare. Jag kommer att flytta programmet till PDP11 och något modifiera det så att PDP11 som saknar högtalare i stället skickar ett magnetband till huvuddatorn för sändning, detta beskrivs eventuellt i en senare artikel.
Morsesändare skriven i sv
En morseöversättare gör man enklast så att man tabellerar tecknen ”A-Z”, ”a-z” samt siffrorna 0-9 samt deras motsvarande teckenkoder. För en specifik bokstav vill vi alltså ha en översättarfunktion skrivMorse() av ungefär följande typ (ti=’*’ och taa=’-‘):
heltal skrivMorse(tecken c){ heltal i; tecken morse[16]; alternativ (c){ valt ‘a’: strcpy(morse, ”-”); bryt;
valt ‘b’: strcpy(morse,”-”); bryt; valt ‘c’: strcpy(morse,”–”); bryt; valt ‘d’: strcpy(morse,”-”); bryt; valt ‘e’: strcpy(morse,”*”); bryt; … } skrivf(”Morsekod:%s\n”,morse); …
Som indata till vår funktion ”skrivMorse” ges ett tecken/en bokstav ”c” vid anrop till funktionen. Resultatet av översättningen finns efter exekvering nu i variabeln ”morse”. Jag antar att funktionen är ganska läslig även för personer som inte kan programmera. Nyckelordet ”bryt” betyder att rätt alternativ hittades och exekveringen fortsätter vid ”skrivf(…). Om vi vill översätta bokstaven ”s” till morse så anropar vi vår funktion med:
skrivMorse(‘s’);
Resultatet skulle bli Morsekod:***
Vi börjar vårt egentliga program med att låta programmet be om en text att sända samt läsa in en textrad som innehåller texten. Funktionen skrivf() skriver ut text som kan formatteras för heltal, flyttal etc. Vi kan göra detta med:
skrivf(”Skriv text att sända som morse:”); l=hämtaRad(&line,&len,stdin);
Kommandot ”hämtaRad” använder biblioteksfunktionen ”getline” som finns definierad i biblioteket stdio.h. Vi måste då komma ihåg att deklarera att vi använder biblioteket stdio.h . Vi inkluderar ett standardbibliotek med:
#inkludera <stdio.h>
Vårt program har nu ungefär följande utseende och det utför inte ännu något vettigt :
#inkludera <stdio.h>
#inkludera <stdlib.h>
#inkludera <string.h>
heltal skrivMorse(tecken c){ heltal i; char morse[16]; alternativ (c){ valt ‘a’: strcpy(morse, ”-”); bryt; valt ‘b’: strcpy(morse,”-**”); bryt; … fler definitioner av morsekoder … } }
heltal program(){ heltal l=0; storlek_t len=0; tecken *rad=NULL; skrivf(”Skriv text att sända som morse:”); l=hämtarad(&rad,&len,stdin); // Skriv ut den lästa raden för att verifiera att inläsningen lyckades skrivf(”Inläst rad:%s\n”,rad); }
Variablerna l, len och *rad behövs för anropet till hämtarad().
Då vi skriver in vår text som skall skickas så vill vi gärna hantera texten ordvis d.v.s. vi skickar ett ord i taget och genererar en standardiserad paus mellan orden. För att splittra up vår textsträng i separata ord inkluderar vi biblioteket string (#inkludera <string.h> se ovan). String-biblioteket har en användbar funktion strtok() som splittrar upp den text vi ger som funktionsparameter i en tabell med separata textsträngar (ordsträngar) separerade med mellanslag ‘ ‘. Råtexten bryts alltså vid mellanslag. Jag splittrar upp den ingående råtexten genast då jag deklarerar ordtabellen som jag kallar ett_ord:
tecken *ett_ord = strtok(rad,” ”);
Om jag har matat in texten ”Lasse skickar morse” så kommer ett_ord efter anrop att innehålla textsträngar som jag kommer åt med:
Vi kan nu skriva en ny funktion som vi kallar skicka_text_som_ord(”någon text …”) .
heltal skicka_text_som_ord(tecken rad[]){ heltal i; tecken *ett_ord = strtok(rad,” ”); medan (ett_ord != NULL){ skrivf(”Ord=%s\n”,ett_ord); upprepa(i=0; i<strlen(ett_ord);i++){ // Skriv ett tecken i nuvarande ord skrivMorse(ett_ord[i]); system(teckenpaus); skrivf(”\n”); } skrivf(”\n”); ett_ord = strtok(NULL, ” ”); } }
Efter att vi splittrade upp råtexten i ord så tar vi ett ord i taget och splittrar upp det i bokstäver som skickas för konvertering till morse.
Vi går igenom alla orden i vår råtext med:
medan (ett_ord != NULL){ skrivf(”Ord=%s\n”,ett_ord); … }
Motsvarande konstruktion i språket c är ”while(ett_ord != NULL){ … }” . Vi skickar vidare orden för sändning så länge som ett_ord inte är tomt (NULL).
Vi lägger nu till en slinga för att skicka iväg varje ord bokstav för bokstav för översättning till morse och sändning. Slingan har följande utseende:
upprepa(i=0; i<strlen(ett_ord);i++){
// Skriv ett tecken i nuvarande ord
skrivMorse(ett_ord[i]);
system(teckenpaus);
skrivf("\n");
}
Vi använder konstruktionen ”upprepa” som motsvarar c-språkets ”for” slinga. Slingan går igenom ordsträngen ett_ord bokstav för bokstav tills vi har nått den fulla längden på strängen ett_ord.
Slingan stegar alltså igenom ett_ord på följande sätt:
i=0 ett_ord[i] = ‘L’ som skickas till skrivMorse(‘L’) i=1 ett_ord[i] = ‘a’ som skickas till skrivMorse(‘a’) i=2 ett_ord[i] = ‘s’ som skickas till skrivMorse(‘s’) i=3 ett_ord[i] = ‘s’ som skickas till skrivMorse(‘s’) i=4 ett_ord[i] = ‘e’ som skickas till skrivMorse(‘e’)
Efter att vi har skickat ett helt ord så håller vi paus genom att anropa operativsystemets funktion sleep.
Vi låter systemet sova i 0.7 sekunder mellan ord. Sovtiden mellan bokstäver är 0.1 sekunder.
Vi spelar upp ”tit” och ”taa” under Linux så att jag med hjälp av programmet ”Audacity” genererade en ton med frekvensen 880 Hz. Från denna ton klippte jag två stumpar 0.1 repektive 0.3 sekunder långa som jag sparade som ljudfilerna 0_1.wav samt 0_3.wav . Jag kan spela upp en wavfil under Linux med hjälp av programmet ”aplay”.
Programmet i dess helhet (under linux i detta skede) har då följande utseende:
// Morse
// Programspråket "sv" är språket "c" med svenska kommando-ord.
// Språket översätts till standard "c" som sedan kompileras till maskinspråk
// för att köras.
// Språket "sv" är egentligen ett experiment med Unixverktyget "lex" som
// har konstruerats för att känna igen ord och strukturer i en text.
// Strukturer som hittas skulle normalt skickas vidare till programmet "yacc"
// (yet another compiler compiler = en annan kompilator kompilator).
// Eftersom jag översätter språket "sv" till "c" så behöver jag inte
// någon kompilator eftersom denna redan finns och likaså behöver jag inte
// någon kodgenerator som skulle generera maskinspråk eftersom även den redan
// finns. Notera att jag kan använda c-språk direkt om motsvarande
// sv-konstruktion inte har definierats.
// Notera att endast en delmängd av SV-C har skrivits.
// Vill man ha en mera fullständig
// motsvarighet så måste filen swe.lex utvidgas med nya nyckelord.
// Jag kör för närvarande en emulerad minidator PDP11/70 från 1970-talet.
// Operativsystemet är BSD Unix 2.11.
// Detta är ett experiment i att skriva ett enkelt svenskt
// programmeringsspråk som är körbart på denna urgamla dator.
//
// Lars Silen 2022
// Detta är öppen källkod som fritt får distribueras
// Författaren tar inget ansvar för eventuella fel i genererad kod
#inkludera <stdio.h>
#inkludera <stdlib.h>
#inkludera <string.h>
// Definiera tidslängden på olika element i Morse
tecken teckenpaus[] = "sleep 0.1";
tecken ordpaus[] = "sleep 0.7";
heltal skrivMorse(tecken c){
heltal i;
char morse[16];
alternativ (c){
valt 'a': strcpy(morse, "*-"); bryt;
valt 'b': strcpy(morse,"-***"); bryt;
valt 'c': strcpy(morse,"-*-*"); bryt;
valt 'd': strcpy(morse,"-**"); bryt;
valt 'e': strcpy(morse,"*"); bryt;
valt 'f': strcpy(morse,"**-*"); bryt;
valt 'g': strcpy(morse,"--*"); bryt;
valt 'h': strcpy(morse,"****"); bryt;
valt 'i': strcpy(morse,"**"); bryt;
valt 'j': strcpy(morse,"*---"); bryt;
valt 'k': strcpy(morse,"-*-"); bryt;
valt 'l': strcpy(morse,"*-**"); bryt;
valt 'm': strcpy(morse,"--"); bryt;
valt 'n': strcpy(morse,"-*"); bryt;
valt 'o': strcpy(morse,"---"); bryt;
valt 'p': strcpy(morse,"*--*"); bryt;
valt 'q': strcpy(morse,"--*-"); bryt;
valt 'r': strcpy(morse,"*-*"); bryt;
valt 's': strcpy(morse,"***"); bryt;
valt 't': strcpy(morse,"-"); bryt;
valt 'u': strcpy(morse,"**-"); bryt;
valt 'v': strcpy(morse,"***-"); bryt;
valt 'w': strcpy(morse,"*--"); bryt;
valt 'x': strcpy(morse,"-**-"); bryt;
valt 'y': strcpy(morse,"-*--"); bryt;
valt 'z': strcpy(morse,"--**"); bryt;
// Lägg till ÅÄÖ här om du behöver dem
valt 'A': strcpy(morse, "*-"); bryt;
valt 'B': strcpy(morse,"-***"); bryt;
valt 'C': strcpy(morse,"-*-*"); bryt;
valt 'D': strcpy(morse,"-**"); bryt;
valt 'E': strcpy(morse,"*"); bryt;
valt 'F': strcpy(morse,"**-*"); bryt;
valt 'G': strcpy(morse,"--*"); bryt;
valt 'H': strcpy(morse,"****"); bryt;
valt 'I': strcpy(morse,"**"); bryt;
valt 'J': strcpy(morse,"*---"); bryt;
valt 'K': strcpy(morse,"-*-"); bryt;
valt 'L': strcpy(morse,"*-**"); bryt;
valt 'M': strcpy(morse,"--"); bryt;
valt 'N': strcpy(morse,"-*"); bryt;
valt 'O': strcpy(morse,"---"); bryt;
valt 'P': strcpy(morse,"*--*"); bryt;
valt 'Q': strcpy(morse,"--*-"); bryt;
valt 'R': strcpy(morse,"*-*"); bryt;
valt 'S': strcpy(morse,"***"); bryt;
valt 'T': strcpy(morse,"-"); bryt;
valt 'U': strcpy(morse,"**-"); bryt;
valt 'V': strcpy(morse,"***-"); bryt;
valt 'W': strcpy(morse,"*--"); bryt;
valt 'X': strcpy(morse,"-**-"); bryt;
valt 'Y': strcpy(morse,"-*--"); bryt;
valt 'Z': strcpy(morse,"--**"); bryt;
// Lägg till åäö här om du behöver dem
valt '1': strcpy(morse,"*----"); bryt;
valt '2': strcpy(morse,"**---"); bryt;
valt '3': strcpy(morse,"***--"); bryt;
valt '4': strcpy(morse,"****-"); bryt;
valt '5': strcpy(morse,"*****"); bryt;
valt '6': strcpy(morse,"-****"); bryt;
valt '7': strcpy(morse,"--***"); bryt;
valt '8': strcpy(morse,"---**"); bryt;
valt '9': strcpy(morse,"----*"); bryt;
valt '0': strcpy(morse,"-----"); bryt;
// Lägg till skiljetecken etc här
}
// Skriv bokstaven som sänds (finns som variabelparametern "c" vid anropet)
skrivf("%c ",c);
skrivf("%s ",morse);
// Generera ljud
upprepa(i=0; i<strlen(morse);i++){
om (morse[i] == '*'){
skrivf("ti ");
system("aplay -q 0_1.wav >/dev/null");
} annars {
skrivf("taa ");
system("aplay -q 0_3.wav >/dev/null");
}
// skrivf("\n");
}
}
heltal skicka_text_som_ord(tecken rad[]){
heltal i;
tecken *ett_ord = strtok(rad," ");
medan (ett_ord != NULL){
skrivf("Ord=%s\n",ett_ord);
upprepa(i=0; i<strlen(ett_ord);i++){
// Skriv ett tecken i nuvarande ord
skrivMorse(ett_ord[i]);
system(teckenpaus);
skrivf("\n");
}
skrivf("\n");
ett_ord = strtok(NULL, " ");
}
}
heltal program(){
heltal l=0;
storlek_t len=0;
tecken *rad=NULL;
skrivf("Skriv text att sända som morse:");
l=hämtarad(&rad,&len,stdin);
skicka_text_som_ord(rad);
}
Den genererade c-koden är strukturellt identisk med sv-programmets kod d.v.s. vi gör en ord för ord översättning. Detta betyder att c-kompilatorn ger felmeddelanden som pekar till rätt rad också i sv källkoden. Min editor bör naturligtvis konfigureras så att den visar radnummer för att felsökning skall vara effektiv.
Översättaren bör sannolikt expanderas med ytterligare c-konstruktioner. Det är oklart i hur hög utsträckning det är värt att översätta funktioner i bibliotek men exemplet ”hämtaRad” visar att detta naturligtvis är möjligt. Det är naturligtvis också möjligt att översätta namnen på standardbiblioteken på samma sätt men knappast vettigt eftersom målet är att eleven också bekantar sig med c-språket och dess bibliotek.
Oversättarprogram komplett:
#!/bin/bash
# Name=csv
# Detta är en kompilator för programspråket "sv" som är en svensk översätytning av språket "c".
# Språket "sv" kan enkelt utvidgas genom att modifiera filen "swe.lex".
# Användning: ./csv program
# Notera att källkoden antas vara program.sv .
# Resultatet blir det körbara programmet program.run
#
# En textfil som är en översättning till språket "c" genereras som program.c .
# Lars Silen 2022
# Detta är fri källkod som fritt får användas och modifieras på egen risk.
echo "Källkod:"$1.sv
echo "Översättaren är definierad i swe.lex"
echo "Kompilerar översätteren swe.lex till c-kod"
lex swe.lex
echo "Kompilerar lex analysatorn till körbar maskinkod: sv"
gcc lex.yy.c -ll -o sv
echo "Översätter sv-programmet " $1.sv " till c-kod: " $1.c
./sv $1.sv >$1.c
echo "C-kod finns nu i " $1.c
echo "Kompilerar nu till körbar maskinkod!"
gcc $1.c -o $1.run
echo "The executable is:" $1.run
Notera att skriptet kompilerar om swe.lex varje gång. Användaren kan alltså enkelt lägga till nya definitioner som blir en del av språket. Om användaren uppfattar att språkdefinitionen är stabil så kan man naturligtvis lämna bort raderna lex swe.lex samt gcc lex.yy.c -ll -o sv , tidsvinsten blir dock marginell.
Jag råkade av en slump se att ett elpiano av typen Behringer Eurogrand EG2180 fanns till salu. Annonsen konstaterade att ett par tangenter inte riktigt gick tillbaka efter nedslag. Det begärda priset var 55 Euro d.v.s. ca. 500 SEK. Jag träffade säljaren som visade sig vara en trevlig utländsk studerande som studerar teknik på den avdelning på Aalto universitetet där jag själv i tiderna har undervisat. Säljaren var tydligen rätt förvånad över att jag var ganska ointresserad av att provspela instrumentet efter att jag såg att elektroniken tydligen fungerade. Jag var ju medveten om att jag sannolikt skulle vara tvungen att plocka isär instrumentet helt. Jag visste mycket väl att det fanns många fel.
Det blev affär och jag prutade upp priset till 60 Euro för att en student nog behöver pengarna…
Fig. 1 Utgångmgsläget då instrumentet var inburet men stativet kvar på kärran. De blå tejpbitarna visar tangenter som inte går tillbaka efter nedslag eller tangenter där känslan tydligt var fel men där tangenter trots viss tvekan själv gick tillbaka.
Det första skedet var att kartlägga så många fel som möjligt. Kartläggningen gick ut på att trycka på en knapp i taget och observera hur tangenten uppför sig. Det visade sig naturligtvis att det fanns betydligt fler tvivelaktiga tangenter än vad som angivits i annonsen … inget problem och egentligen helt väntat. Fig. 1 visar den första kartläggningen av fel. Notera att ett par tangenter som inte går tillbaka blev femton i detta skede. I ett senare skede kom det till ett antal fel då inte alla tangentsensorer fungerade korrekt.
Fig. 2 Skyddslocket över tangenterna, samt övre delen av instrumentet är demonterade. Kontrollpanelen som normalt ligger vid bakändan av tangenterna ligger upp och ned över högtalarna (det långa blå kretskortet). Ungefär på mitten, strax nedanför flatkablarna ser man ett litet nästan kvadratiskt elektronikkort som utgör instrumentets hjärna. Jag gissar att kortet i stort sett i fråga om datorkraft motsvarar en Raspeberry Pi dator som kostar 30-40 Euro. Ovanför flatkablarna ser man nätaggregatet.
För att kunna fixa tangentbordet måste det tas loss. Under framkanten av tangentbordet ungefär där limlapparna sitter finns en rad kanske 50 mm långa maskinskruvar skruvade underifrån in i tangentbordet. Genom att ställa instrumentet att stå på bakkanten var det inga problem att lossa skruvarna.
Längs den inre kanten av tangentbordet finns en rad skruvar som håller fast tangenbordet från insidan. En del av dessa skruvar saknades eftersom någon eller flera tidigare ägare har varit inne i instrumentet. Innan man lyfter ut tangentbordet är det skäl att lossa de två smala flatkablarna till processorkortet. Jag märkte kablarna V och H för att veta i vilken ordning kablarna skall moteras. Om man byter plats på kablarna så får man ett intressant instrument där diskanten klingar till vänster för att mitt på instrumentet byta till bas … ingenting går dock sönder om man kopplar fel. Av detta kan man naturligtvis dra slutsatsen att jag åtminstone en gång kopplade fel trots att kablarna var märkta, ”errare humanum est” d.v.s. det är mänskligt att fela. Uttrycket fortsätter ”perseverare autem diabolicum” som man antagligen kan översätta med att om man fortsätter att fela så hör det fan till …
Fig. 3 Det tunga(!) tangentbordet kan då skruvarna har lossats försiktigt lyftas ur och ligger nu på bordet. På undersidan av tangentbordet finns falska klubbor vars uppgift är att ge rätt känsla vid anslag som också slår mot tangentens hastighetssensor. Klubborna är monterade i modulen som i bilden ligger ovanpå tangentbordet. Tangenterna kan enkelt demonteras genom att lossa ett metallstöd och en svart ”kilribba” som fixerar tangenternas bakändor. I bilden har alla tangenter i den losstagna oktavmodulen demonterats. Tangenterna sjuts försiktigt bakåt varefter de kan lyftas bort.Fig. 4 Tangenterna är fixerade på plats med en metallprofil samt en kilribba i plast. Då kilribban tas loss kan en tangent skjutas bakåt och sedan lyftas bort. Det visade sig att det speciellt vid återmonteringen ofta var lättare att montera vissa svarta tangenter före vita tangenter eftersom de vita tangenterna annars låg i vägen vid montering.
För att klubborna skall kunna demonteras måste de två långa gula kretskorten med tangentsensorer tas bort. Totalt blir det då att lossa 66 skruvar …
Fig. 5 Klubbornas moduler (en oktav) är skruvade till de två kretskorten. Det är inte möjligt att lossa en modul utan att lossa kretskorten. I bilden har det ena kretskortet redan demonterats. Längden på ett kretskort är halva längden av tangentbordet, det är alltså skäl att hantera kretskorten med försiktighet så att de inte skadas.
De två kretskorten utgör två enkla scannade brytarmatriser. Antalet kontakter är dubbla antalet mot antalet tangenter eftersom en tangenttryckning alltid aktiverar två kontakter. Kontakterna är monterade på olika avstånd från kretskortet och processorn uppskattar nedslagets hastighet genom att mäta tiden mellan de två kontaktaktiveringarna. I fig. 5 ser man uppstickande gummiben (grå samt genomskinlig) från kontakterna som utgörs av kontaktytor av grafit i gummibubblor som trycks mot spår i kretskortet. Mer om sensorerna senare i texten.
Fig. 6 Klubborna löper mycket dåligt. Notera hur fyra klubbor inte faller tillbaka av egen vikt. Det är också självklart att det inte hjälper att smörja in tangenterna eftersom friktionen finns i klubborna inte i tangenterna. Jag tror inte att det fanns en enda oktavmodul som inte hade kärvande klubbor. Resultatet var att jag servade alla klubbor.
Det är rätt enkelt astt fixa de kärvande klubborna. Man tar en lämplig metalldorn med diametern mindre än 8 mm och slår försiktigt ut axeln som går genom alla hamrarna. Det lönar sig att knacka loss axeln med modulen liggande på ett bord så att man inte blir med en grupp hamrar i famnen. Den urknackade axeln används nu som testverktyg då man med fil, borr eller brotch försiktigt vidgar axelhålet i de olika hamrarna. Jag ökade försiktigt på håldiametern tills klubban löpte lätt utan att kärva men fortfarande utan glapp. Det kan också vara skäl att försiktigt fila klubbans plastdel från sidorna eftersom problemet tydligen är att den gjutna plastdelen sväller med tiden då spänningar från tillverkningen släpper.
Fig. 7 En demonterad klubba. Problemet med kärvande klubba verkar vara att plasten med tiden sväller som en jäsande deg vilket ökar friktionen i hålet i den vita plastdelen överst. Då man jobbar med klubban kan det vara skäl att demontera den lilla triangelformade svarta plastdelen i närheten av axeln. Man kan mycket lätt tappa denna del och jag vet inte var man skulle hitta reservdelar. Lägg den åt sidan då du jobbar med tangenten. Jag tappade en av dessa svarta delar på golvet i den inte välstädade snickarboan men jag hittade den!
Jag behövde ungefär en och en halv timme för att fixa den första oktaven. De följande oktaverna krävde sedan ca. 20 minuter per oktavmodul. Övning ger färdighet.
Efter att de kärvande klubborna var fixade … jag kontrollera alla klubbor inte endast de som ursprungligen märkts med tejp, plockade jag ihop tangentbordet och kopplade det temporärt till instrumentet. Tangenterna fungerade nu perfekt. Det fanns inga kärvande tangenter och touchen var bra. Jag hittade dock ett antal tangenter som uppförde sig konstigt vid en tangenttryckning.Det fanns tre kategorier av fel:
Tangenten gav ett mycket kraftigt ljud helt oberoende av hur kraftigt jag tryckte på tangenten.
Tangenten gav ett svagt ljud då man tryckte på den och ett kraftigt ljud då man släppte upp den.
Tangenten gav ett svagt ljud oberoende av anslagets styrka.
Orsaken till ovanstående fel är följande:
Då tangenten ger ett kraftigt ljud oberoende av hur svagt man trycker på tangenten så betyder detta att den kontakt som ligger längre ifrån kretskortet gör kontakt medan den närliggande kontakten inte gör det. Processorn tolkar då anslagshastigheten som mycket hög med hög ljudvolym som resultat. Lösningen är att kontrollera att det inte finns fett, olja eller damm på kontaktytorna.
Då tangenten endast ger ett svagt ljud oberoende av anslagets styrka så betyder detta att kontakten närmare kretskortet ger kontakt korrekt men den andra kontakten längre ifrån kretskortet sluter inte vilket processorn tolkar som ett extremt långsamt anslag och resultatet blir en mycket svag ton.
Dubbeltonen svag/stark beror sannolikt på kontaminering med t.ex. olja som någon tidigare reparatör hade använt rikligt av. Då tangenten trycks ner så sluter endast den närliggande kontakten till kretskortet med resultatet att vi får en svag första ton. Då jag släpper upp tangenten så har oljan hunnit pressas undan och kontakten längre ifrån kretskortet sluter vilket tolkas som en separat kraftig knapptryckning.
Fig. 8 En tidigare reparatör hade tydligen inte förstått hur avkänningen av knapptryckningar fungerar utan man hade försökt korrigera något fel genom att lägga till ”dämpning?) i form av skumplastbitar. Sensormodulen till vänster är korrekt medan den gula skumplasten till höger inte hör dit.
I fig. 7 har sensorkretskorten monterats loss och vi kan se undersidan och de runda kontakttornen som klubban slår mot. Varje runt torn består av två kontakter. Mitt i ett torn finns en liten rund kontakt kanske en mm från kretskortet. Runt basen på ett torn finns en kontakt som går runt tornet och som ligger mycket nära kretskortet. Då man trycker på sensorn så kommer tydligen ringelektroden först att göra kontakt och sedan centralelektroden. Som jag nämnde ovan så verkar standardkonstruktionen i andra elpianon vara en dubbelrad sensorer med en kontakt per torn.
Man kan utan problem dra loss sensormodulerna för putsning men det blir en del pillrande för att få dem tillbaka på plats. Det är igen skäl att notera att jag inte vill skada någon gummimodul eftersom det kan vara extremt svårt att hitta reservdelar.
Fig. 9 En del av de bortplockade skumplastbitarna som inte skall finnas i sensorerna. Det fanns betydligt mer …
Den jobbigaste delen av reparationen var att hitta problemtangenter. Tangentbordet måste vara hopbyggt för att man skall kunna testa det. För att putsa en kontakt måste jag skruva loss det gula kretskortet i tangentbordet d.v.s. 33 skruvar. Det är skäl att alltid provspela hela tangentbordet och märka konstiga tangenter med tejp och en beskrivning av symptomen. Jag gissar att jag blev tvungen att öppna tangentbordet för justering fyra gånger innan jag fick alla fel fixade.
Fig. 10 Det hade använts rikligt med tunnflytande olja vid tidigare reparationsförsök tydligen för att fixa de kärvande klubborna. Resultatet var att det under en del av sensorerna fanns olja. Notera hur kretskortets svarta kontaktytor är våta av olja och olja finns naturligtvis också på gummidelens grafitytor.
Jag tog loss sensordelen av gummi på två ställen för att fixa kontaktproblem (se beskrivning av symptomen ovan). I fig. 10 ser man hur mittkontakten ser torr ut medan ringkontakten är nerkladdad av olja. Resultatet torde ha varit att ifrågavarande tangenter endast gav ett mycket kraftigt ljud eftersom processorn tror att anslaget sker med nästan oändlig hastighet.
Det visade sig att Biltemas elektronikrengöring på sprayburk fungerade bra. Jag använde vaddstickor fuktade med rengöringsmedel för att putsa kretskortet. Jag tog helt loss sensordelen av gummi och sprutade rikligt av rengöringsämne vid kontaktytorna. I ett skede såg det lite skrämmande ut då rengöringsmedlet tydligen reagerade med gummit vilket fick som resultat att gummidelen rullade ihop sig som en mask. Då jag monterade tillbaka sensordelarna och satte ihop tangentbordet så visade det sig ingen tangent i den genomskinliga sensormodulen i bilden ovan fungerade … uups! Då jag provspelade några timmar senare så hade tvättmedlet avdunstat helt och tangenterna fungerade korrekt.
Fig. 11 Sensormodulen från insidan. Vi ser ringelektroden som i viloläget är väldigt nära kretskortet och tydligen sluter kontakt genast vid en knapptryckning och således sätter igång en hastighetsmätning. I centrum finns en annan elektrod som är moterad kanske en mm högre och som ger inform om när hastighetsmätningen är slut. Notera hur den tvättade sensormodulen försöker rulla ihop sig. Spänningar i gummiblandningen gör ringelektroden elliptisk i stället för cirkelrund. Lyckligtvis så återfick sensorn sin ursprungliga form då allt lösningsmedel hade avdunstat.Fig. 12 En annan sensor som har tagit stryk av olja. Notera hur tre av fyra ringelektroder helt eller delvis är oljiga.Fig. 13 Motsvarande sensormodul innan tvättning. Vi ser att tre av fyra sensorer är nedsölade av olja.
Vad blev resultatet
Efter två dagars arbete är instrumentet hopmonterat och allt fungerar perfekt såvitt jag vet. Resultatet blev ett mycket trevligt elektriskt piano med, som jag uppfattar det, ett härligt ljud och mycket realistisk touch i tangenterna till ett pris på ca. 70 Euro då jag blev tvungen att satsa en tia på elektronikrengöringsmedel.
Projektet var väldigt lärorikt. Jag hade ingen aning om att ett elektriskt piano har hammare i likhet med ett riktigt piano. Felsökningen av starka/svaga och dubbla toner gjorde att jag nu förstår hur instrumentet känner av anslagshastigheten och jag kan genom att lyssna på en spelad ton höra om allt är ok eller om det finns ett sensorfel. Jag uppfattar att användningen av två kontakter för varje tangent är genialisk genom sin enkelhet. Ett sensorfel går att känna igen direkt på ljudet d.v.s. jag vet på förhand vilken typ av fel jag kan förvänta mig d.v.s. jag vet vilkendera sensorn som är orsak till problemet.
Notera att de flesta elektriska pianon har sensorerna placerare som två separata mittelektroder ovanför varandra inte som i Behringerinstrumentet kombinerade. Fördelen med den vanligare konstruktionen är att det är möjligt att hitta reservdelar till andra instrument medan det tydligen inte finns mycket reservdelar tillgängliga för Behringer.
Ett jättetrevligt projekt som inte blev sämre av att jag på slutändan fick ett fullt fungerande instrument som jag annars sannolikt hade varit tvungen att betala 500-700 Euro för. Projektet visar också väldigt konkret att en fullständig genomgång av ett modernt elpiano inte är billigt. Då jag Googlade så hittade jag en prisuppskattning gällande service som gick på $500 vilket nog inte är något rövarpris för ett jobb som jag uppskattar att jag, om jag skulle göra om det, skulle kunna göra på en dag. Det stora jobbet var de kärvande tangenterna/hamrarna. Putsning av sensorerna krävde på slutet ungefär en halv timme mellan varven men innan allt fungerade så blev det några varv.
Jag har inga kontakter/samarbete med Zubersoft (http://www.zubersoft.com) som har skrivit programmet MobileSheetsPro. Artikeln nedan är inte en betald annons för ifrågavarande program.
————————-
Jag spelar i spelmanslaget Altra Volta som är föreningen Arbetets vänners musikklubb. Vi spelar främst finlandssvensk och nordisk folkmusik med inslag av keltisk musik, irländsk etc.
Vi har medlemmar med spelteknik på alla nivåer börjande från absoluta nybörjare till rätt avancerade spelare med lång erfarenhet. Det faktum att vi har också nybörjare betyder att vi har en egen övningsstil. Vi tenderar att spela samma melodi många gånger (5 – 6 ggr) vilket ger personer som spelar på gehör möjlighet att lära sig nya låtar relativt bekvämt. Personer som läser noter använder noter till en början för att senare, får man hoppas, lära sig låten utantill.
Noter utgör ett problem genom att nothäften med någon speciell repertoar blir rätt dyra om en stor bunt skall köpas. Noter tenderar att ”diffundera” d.v.s. noter lånas och kommer aldrig tillbaka. Om man kopierar upp noter och delar ut lösblad så hamnar man rätt snart i ett lösbladshelvete där noter flyter omkring i en salig blandning och det är svårt att hitta en specifik låt då den behövs. Ett sätt att försöka hantera lösbladshelvetet är att samla ihop noter i egna häften … detta fungerar delvis eftersom häften plockas isär, sidor tappas bort och de med möda skapade häftena diffunderar iväg någonstans till nothäftenas okända begravningsplats.
Ett alternativ till att få ordning på noterna hittade vi då vi nyligen blev tvungna att flytta från vår traditionella övningslokal till en ny plats i samma hus. I det nya övningsutrymmet finns en stor TV-skärm i storleken 50-60 tum. Vi märkte att man kan koppla en minnepinne till TV:n och att TV:N klarade av att hantera jpeg-bilder. Vi hade nu ett bekvämt sätt att visa noter i tillräckligt stort format för att hela gruppen skall kunna använda samma noter. Vi upptäckte att noterna på skärmen eventuellt gav bättre focus eftersom allas intresse var fokuserat i samma riktning.
Fig. 1 Ordning på lösbladshelvetet! Författaren sitter längst ut till höger.
Läsplatta
Vi har provat programmet MobileSheetsPro för Androidoperativsystemet och programmet känns logiskt och vettigt att använda. Programmet stöder fotpedal och olika notformat d.v.s. både jpeg-bilder och pdf. Man kan organisera noter i album och skapa spellistor från existerande album. Man kan också söka efter musiktyp, söka på en låts namn etc. MobileSheetsPro kör utan problem i en gammal billig kinesisk surfplatta på tio tum. Plattans pris är ca. 50 E (500 SEK). Plattan har 16 GB internminne vilket räcker för tusentals notblad efter att jag plockade bort alla onödiga applikationer.
Vårt problem blev nu att försöka få MobileSheetsPro kopplat till den stora TV-skärmen. Problemet är att väldigt få läsplattor idag har HDMI-kontakt så att plattans skärm skulle kunna visas på TV-skärmen.
Det slog mig plötsligt att problemet kunde lösas med hjälp av en Raspberry Pi dator. Problemet är dock att programmet vi vill använda inte finns för Raspberry Pi. Vi kunde alltså använda Raspberry Pi som en normal dator och visa noter manuellt på skärmen. Skulle det finnas ett bekvämare sätt?
Android på Raspberry Pi
Fig. 2 En Raspberry Pi är en liten dator som normalt kör Linux på en ARM processor. De flesta mobiltelefoner idag använder närbesläktade ARM processorer varför det borde vara möjligt att köra Android på en Rasbberry Pi.
Första försöket misslyckades och Raspberry Pi startade inte. Man kunde se att processorn via sin LED försökte signalera att någonting hade gått del. Lite Googlande visade att EmteriaOS (Android) kunde vara ett vettigt alternativ.
Det visade sig att det finns en fri testversion av EmteriaOs som utan problem gick att installera på Raspberry Pi. Versionen är tidsbegränsad till åtta timmar varefter systemet stoppar. En licens kostar 19E vilket är överkomligt. Jag har en köpt version av MobileSheetsPro som kostar 15E på Google Play, programmet rekommenderas varmt.
Fig. 3 EmteriaOS Android kör på Raspberry Pi.
Fig. 4 MobileSheetsPro kör på Raspberry Pi.
Det visade sig att programmet fungerar utmärkt på Raspberry Pi. Vi har nu ett lätt hanterbart system för visning av noter på stor skärm som är lätt att administrera, lätt att skriva ut noter om det behövs samt ett system som låter oss skapa program för olika uppträdanden.
Installation av Android på Raspberry Pi
Man hittar information om Android på Raspberry Pi på:
Totalpriset på en notvisningsdator för en stor TV-skärm med HDMI-anslutning blir då följande:
Raspberry Pi model 3 B+ kostar ca. 40 Euro
Minneskort 64 GByte kostar ungefär 10 Euro
Skal/inneslutning till Raspberry Pi kostar ca. 15 Euro
Android operativsystem EmteriaOs utan restriktioner kostar 19 Euro. Det finns en gratis testversion som stänger av sig efter åtta timmar men som kan återstartas för åtta timmar igen … min uppfattning är att gratisversionen är helt adekvat.
MobileSheetsPro kostar ungefär 15 Euro på Google play
Totalt ca. 100 E dock så att nästan alla komponenter fanns i min miljonlåda … jag behövde inte åka iväg kör att köpa någon saknad komponent.
Plats finns för kanske tiotusen sidor noter i extremt kompakt format. Lägger jag till ett SD-minne så finns det plats för hundratusen sidor.
Efter att jag gjorde ovanstående analys av kostnaderna så undersökte jag andra alternativ. Det visar sig att jag kan köpa en motsvarande TV-box med Android operativsystem på Ebay för ca. 19E inklusive transport … så jag beställde ett sådant system men jag får vänta till efter julen på att systemet skall levereras.
Resultatet blir att jag kommer att experimentera med Raspberry Pi alternativet under några veckor medan jag väntar på TV-boxen. Det finns naturligtvis en, mycket liten, risk att programmer MobileSheetsPro inte kan köras på den beställda TV-boxen men jag uppfattar att risken är obetydlig. Jag valde en lite äldre box som kör Android 7.1 eftersom EmteriaOS bygger på samma Androidversion. Det finns TV-boxar som kör t.o.om. Android 9 men jag har lärt mig att det ofta gör ont att vara i absolut första ledet vad gäller dator/program versioner.
Noter för ett spelmanslag
Erfarenheterna med MobileSheetsPro är att noter bör samlas på minnepinne sorterade i kataloger enligt något logiskt system. Från minnepinnen kan man sedan enkelt importera en katalog i taget till olika Album d.v.s. logiska ”nothäften” som får samma namn som katalogerna på pinnen. Samma minnepinne kan dupliceras till alla spelmän (spelmanslag spelhen = hönsflock?) och utskrift till papper får skötas av individen.
Då noterna finns nere på TV-boxen kan jag konstruera spellistor d.v.s. repertoar för olika gigs utgående från material i de olika albumen.
Om en spelman dyker upp med ett intressant stycke som det finns noter till så kan jag enkelt lägga in stycket i samlingen genom att med telefonen ta en bild av notbladet och importera bladet till önskat album.
Jag kompletterar artikeln senare med erfarenheter kring hur TV-boxen fungerar ihop med MobileSheetsPro.
Hur installerar jag MobileSheetsPro
MobileSheetsPro finns på Google Play. Programmet är inte gratis, det kostar ca. 15E d.v.s. ca. 150 SEK. För att kunna ladda ner programmet måste man ha skapat ett betalkonto på Google Play. Jag har aldrig haft några problem med betalning via Google Play.
Välj MobileSheetsPro i Google Play och tryck ”Install”. Installationen går på kanske fem minuter. Det kan vara en god idé att ögna igenom programmets rätt omfattande bruksanvisning så att man får en bild av vilken funktionalitet programmet erbjuder. Bruksanvisningen ger också en förklaring till den terminologi som används i användargränssnittet.