Ophavsret © 1999-2001 Stefan Westerfeld & Jeff Tranter
Tilladelse er hermed givet til at kopiere, videregive og/eller ændre dette dokument under betingelserne i GNU's Fri Dokumentation License, Version 1.1 eller en vilkårlig senere version publiceret af Free Software Foundation; uden invariante afsnit, uden tekster før omslaget, og uden tekster efter omslaget. En kopi af licensen er inkluderet i afsnittet ved navn " Medvirkende og licens".
Denne håndbog beskriver aRts, den analoge realtidssynthesizer.
Indholdsfortegnelse
Den analoge realtidssynthesizer, eller aRts, er et modulært system til at syntetisere lyd og musik på en digital computer. Med små byggeblokke, som kaldes moduler, kan brugeren let opbygge komplekse værktøjer til at behandle lyd. Moduler sørger for typiske funktioner såsom bølgeformsgeneratorer, filtre, lydeffekter, samt miksning og afspilning af digital lyd i forskellige filformater.
Lydserveren artsd blander lyd fra forskellige kilder i realtid, hvilket lader flere lydprogrammer få transparent delt adgang til lydkortet.
Ved at bruge MCOP, multimediakommunikationsprotokollen, kan multimedieprogrammer blive netværksgennemsigtige, sikkerhedsidentificerede, og virke på flere platforme med grænseflade som defineres på en sproguafhængig måde med IDL. Ældre programmer som ikke understøtter aRts håndteres også. Som en central komponent i desktopmiljøet KDE 2, sørger aRts for grundlaget for KDE's arkitektur for multimedie, og vil støtte flere medietyper inklusive video i fremtiden. Ligesom KDE, så kører aRts på flere operativsystemer, inklusive Linux® og BSD-varianter. Den kan også bruges uafhængig af KDE.
Denne håndbog er beregnet til at give fuldstændig dokumentation for aRts for brugere med forskellige kundskabsniveauer. Afhængig af om du er en hverdagsbruger af multimedieprogrammer som kører ved hjælp af aRts eller en udvikler af multimedieprogrammer, kan du vælge at læse håndbogens afsnit i forskellig rækkefølge.
Vi foreslår at du først læser kapitlet Hent og byg aRts hvis du har brug for først at få aRts installeret og kørbart. Hvis du allerede har et fungerende system, formodentlig installeret sammen med din operativsystemdistribution, kan du vælge at springe over dette afsnit.
Derefter bør du læse afsnittene i kapitlet aRts værktøjer, især artsd, artscontrol;, artsshell, og artsdsp. Dette hjælper dig til at bruge aRts på den mest effektive måde.
Hvis du er interesseret i at trænge yderligere ind i aRts' funktion, så læs kapitlet om aRts-builder og gennemgå øvelsen. Dette bør give dig en forståelse for aRts' kraftfulde muligheder og de moduler som er beregnet til at bruge uden at behøve at kunne programmere.
Hvis du vil lære dig mere om hvordan aRts virker internt, enten for at udvikle multimedieprogrammer eller for at udvide selve aRts, så læs nogle eller alle kapitlerne om aRts i detalje. Dette bør give dig en forståelse for alle begreber som kræves for aRts-programmeludvikling.
Hvis du er særligt interesseret i MIDI-funktionerne i aRts, bør du læse kapitlet om MIDI.
Hvis du vil udvikle multimedieprogrammer som bruger aRts, beskrives de forskellige programmelgrænseflader (API) detaljeret i kapitlet aRts-API.
Hvis du vil udvide aRts ved at oprette nye moduler, læs kapitlet aRts-moduler.
Hvis du ændrer et eksisterende program til at køre sammen med aRts, så læs kapitlet Overførsel af programmer til at passe sammen med aRts.
Du kan også finde ud af hvordan du kan hjælpe med og bidrage til aRts-projektet i kapitlet Bidrag til aRts, læs om kommende udvikling af aRts i kapitlet Fremtidigt arbejde, og finde link til mere information i afsnittet Referencer.
Vi har også afrundet håndbogen med en del yderligere materiale, inklusive svar på almindelige spørgsmål, en liste over bidragydere, detaljer om aRts ophavsret og licenser, og en del baggrundsmateriale om digitallyd og MIDI. En ordforklaring indgår også.
Denne håndbog er stadigvæk i stor udstrækning ufærdig. Du må gerne bidrage med at skrive dele af den, men hvis du vil gøre dette, så kontakt først Jeff Tranter (tranter AT kde.org)
eller Stefan Westerfeld (stefan AT space.twc.de)
for at undgå dobbeltarbejde.
Mod slutningen af 1997 begyndte Stefan Westerfeld at arbejde med et modulært realtidssystem for lydsyntese. Koden kørtes oprindeligt på et PowerPC-system med AIX®. Den første implementering var meget enkel, men understøttede et flow-system med fuldstændig funktion som kunne gøre sådanne ting som at afspille MP3-filer og sende data gennem lydeffektmoduler.
Næste skridt var at implementere en grafisk grænseflade, så moduler kunne manipuleres grafisk. Stefan havde en del god erfaring med at bruge KDE, så derfor valgtes KDE som den grafiske værktøjskasse, (med viden om at det kunne blive nødvendigt at lave en version med GNOME/Gtk+ også) og dette gjorde at Linux® senere valgtes som den hovedsagelige udviklingsplatform. Projektet, som oprindeligt hed ksynth, omdøbtes til aRts og udviklingstakten øgedes. På dette tidspunkt var projektet næsten komplet, med en CORBA-baseret protokol, dusinvis af moduler, et grafisk modulredigeringsværktøj, C og C++ grænseflade, dokumentation, værktøj og en e-mail-liste og netside med en lille gruppe udviklere. Projektet var kommet et godt stykke af vejen efter kun noget mere end et års udvikling.
Da KDE-gruppen begyndte at planlægge for KDE 2.0, blev det klart at KDE behøvede en kraftfuldere infrastruktur for lyd og andre medietyper. Beslutningen blev taget at tilrette aRts, eftersom det var et skridt i den rigtige retning med en gennemprøvet arkitektur. En ny stor udviklingsanstrengelse gjordes for denne nye version af aRts, frem for alt udskiftningen af CORBA-koden med et helt nyt undersystem, MCOP, optimeret for multimedie. Version 0.4 af aRts indgik i udgave 2.0 af KDE.
Arbejdet med aRts fortsætter, med at forbedre ydelsen og tilføje nye funktioner. Bemærk selvom aRts nu er en central komponent i KDE, kan den bruges uden KDE, og bruges også af programmer som går udenfor traditionel multimedie. Projektet har modtaget en vis interesse fra GNOME-gruppen, som åbner en mulighed for at det i fremtiden kan blive standardarkitekturen for multimedie på UNIX® desktopsystemer.
Et antal værktøjer levereres sammen med aRts til at kontrollere og indrette dens opførsel. Du skal kende en smule til de fleste af disse værktøjer for at kunne bruge aRts på en effektiv måde. Dette afsnit beskriver hvert af værktøjerne og deres kommandoflag.
Når man kør aRts under KDE, sørger KDE's kontrolcenter for en gruppe af indstillinger i kategorien Lyd. Visse af disse bruges af aRts. Man kan også tilknytte lyd til forskellige vinduehåndterings- og KDE-begivenheder med dialogen Lyd+Systembekendtgørelser. Se håndbogen for KControl for mere information om hvordan dialogens indstillinger bruges.
Adgangen til lydkort kontrolleres af artsd, lydserveren for aRts-dæmonen. Dette tillader at flere programmer samtidigt sender forespørgsler til serveren, hvor de kan blandes og afspilles. Uden en central lydserver ville et enkelt program som bruger en lydenhed forhindre andre programmer i at bruge det.
For at bruge aRts skal der være en og kun en kopi af artsd som kører. Den startes typisk når KDE startes hvis dette er aktiveret i KControl's Lydserver-modul.
Programmet accepterer følgende flag:
artsd [-n
-p
-N
-W
] [n
-a
lydmetode
-r
samplingrate
-b
bit
-d
-D
enhedsnavn
-F
fragmenter
-S
størrelse
-s
sekunder
-m
] [programnavn
-h
-A
-v
-l
]niveau
-r samplingrate
Indstil samplingrate som skal bruges.
-h
Vis kommandobrug.
-n
Aktivér netværkstransparens.
-p port
Sæt TCP-port som skal bruges (forudsætter -n
).
-u
Offentlig, ingen godkendelse (farligt).
-d
Aktivér fuld duplex.
-D enhedsnavn
Angiv lydenhed (almindeligvis /dev/dsp
).
-F fragmenter
Sæt antal fragmenter.
-S størrelse
Sæt fragmentstørrelse i byte.
-s sekunder
Indstil forsinkelsen før autosuspendér aktiveres i sekunder. Værdien nul lukker af for autosuspendér.
-m programnavn
Angiv navnet på et program som bruges til at vise fejl, advarsler og informationsmeddelelser. Hvis du kører KDE kan du bruge værktøjet artsmessage til dette.
-N
Forøg størrelsen af netværksbufferne til en værdi som er passende til at køre med et 10 Mbps lokalt netværk. Dette er det samme som at bruge flaget -w 5 (se nedenfor).
-w n
Når artsd køres via en netværkstilslutning til en anden værtsmaskine vil man ofte bruge en stor buffer for at undgå pauser. aRts foreslår en minimal bufferstørrelse for programmer. Uden denne valgmulighed baserer den standardstørrelsen på fragmentstørrelsen * antal fragmenter. Med dette tilvalg kan man øge størrelsen fra standardstørrelsen med en faktor n
.
-l niveau
Indstil informationsniveau: 3 (tavs), 2 (advarsler), 1 (info), 0 (fejlsøgning).
-v
Vis versionsniveau.
I de fleste tilfælde er det nok kun at køre artsd.
For at give gode realtidsegenskaber køres artsd oftest som en realtidsproces (på platforme hvor realtidsprioriteter understøttes). Dette kræver root
-rettigheder, så for at minimere sikkerhedsproblemerne, kan artsd startes med det lille omsluttende program artswrapper som helt enkelt skifter til realtidsprioritet (mens det kører som root
) og derefter starter artsd som en anden bruger end root
.
Hvis du gør artswrapper SUID root
, kommer det formodentlig til at forbedre kvaliteten af lydgengivningen ved at reducere huller i musikken. Dog øges også risikoen for at en fejl i koden, eller en ondsindet bruger kan få maskinen til at bryde sammen eller skade den på anden måde. Desuden kan det at prioritere høj lydkvalitet på flerbrugermaskiner forårsage forringet ydelse for brugere som forsøger at bruge maskinen på en “produktiv” måde.
Kommandoen artsshell er beregnet som et værktøj til at udføre diverse funktioner som hører sammen med lydserveren. Formodentlig vil værktøjet blive udvidet med nye kommandoer i fremtiden (se kommentarerne i kildekoden for nogle idéer).
Kommandoen accepterer følgende format:
artsshell [[suspend] | [status] | [terminate] | [autosuspend secs
] | [networkbuffers n
] | [volume [volume
]] | [stereoeffect options
]] [-h
-q
]
artsshell [flag] kommando
[kommandotilvalg
]
Følgende flag understøttes:
-q
Undertryk al udskrift.
-h
Vis kommandobrug.
Følgende kommandoer understøttes:
suspend
Suspendér lydserveren.
status
Vis statusinformation for lydserveren.
terminate
Afslut lydserveren. Dette kan forvirre og/eller forårsage sammenbrud for programmer som for øjeblikket bruger den.
autosuspend
sekunder
Indstil forsinkelsen for aktivering af autosuspension til det angivne antal sekunder. Lydserveren vil blive suspenderet hvis den er inaktiv så længe. Værdien nul deaktiverer autosuspension.
networkbuffers
n
Indstiller størrelsen af netværksbufferne til en faktor n
gange standardstørrelsen.
volume
[volume
]Indstiller lydstyrkeskalering for lydserverens lyduddata. Parameteren volume
er et decimaltal. Uden parameter vises den nuværende lydstyrke.
stereoeffect liste
Vis alle tilgængelige stereoeffektmoduler.
stereoeffect insert [top|bottom]
navn
Indsæt en stereoeffekt i stereoeffekt-stakken. Returnerer en identifikator som senere kan bruges til at fjerne den. Effekten kan installeres længst oppe eller længst nede (normalværdien).
stereoeffect remove
id
Fjerner stereoeffekten med identifikatoren id
fra effektstakken.
Programmet artsplay er et enkelt værktøj til at afspille en lydfil. Det accepterer en enkelt parameter som svarer til navnet på lydfilen som sendes til lydserveren for at spilles. Lydfilen kan være en hvilken som helst almindelig lydfiltype, såsom wav
eller au
. Dette værktøj er godt til at prøve om lydserveren virker. Ved at køre to kommandoer parallelt eller i hurtig rækkefølge kan man demonstrere hvordan lydserveren blander mere end en lydkilde.
Lydserveren håndterer kun programmer som understøtter aRts. Mange gamle programmer vil ønske at komme direkte til lydenheden. Kommandoen artsdsp sørger for en interimløsning som lader de fleste af disse programmer køre uforandrede.
Når et program køres med artsdsp fanges alle forsøg på at komme til lydenheden /dev/dsp
og forvandles til kald til aRts programmeringsgrænseflade. Selv om enhedsemuleringen ikke er perfekt, så virker de fleste programmer på denne måde, omend med en vis forværring i ydelse og latenstid.
Kommandoen artsdsp følger formatet:
artsdsp [flag
] programargumenter
Følgende flag genkendes:
-h
, --help
Vis kortfattet hjælp.
-n
--name
= navn
Brug navn
til at identificere en afspiller for artsd.
-m
--mmap
Emulér hukommelsesafbildning (f.eks for Quake).
-v
--verbose
Vis parametre.
Et typisk kald er:
artsdsp
-v
-m
realplay
song.mp3
Visse programmer virker bedre med flaget --mmap
. Lydenhedens funktioner er ikke alle fuldstændigt emulerede, men de fleste programmer skulle virke. Hvis du finder et som ikke gør det, så indsend en detaljeret fejlrapport så udviklerne kan ordne det. Igen, husk at dette er en midlertidig løsning og noget af en grim hurtigløsning. Den bedste løsning er at tilføje indbygget støtte for aRts i programmerne. Hvis dit favoritlydprogram ikke har støtte for aRts, så bed udviklerne om at sørge for det.
Dette er et enkelt værktøj til at sende rå lyddata til lydserveren. Man skal angive dataformatet (samplingsfrekvens, samplingsstørrelse og antal kanaler). Dette er formodentlig ikke et værktøj som man behøver bruge ofte, men det kan være bekvemt for testformål. Kommandosyntaksen er:
artscat [ flag
] [ filnavn
]
Hvis intet filnavn angives læser programmet fra standardinddata. Følgende flag understøttes:
-r
samplingsfrekvens
Sæt den samplingsfrekvens som bruges.
-b
bit
Sæt samplingsstørrelse som bruges (8 eller 16).
-c
kanaler
Sæt antal kanaler (1 eller 2).
-h
Vis kommandobrug og afslut.
Dette er et grafisk værktøj til at udføre et antal opgaver som hører sammen med lydserveren. Standardvinduet viser to lydstyrkeindikatorer og en skyder til at indstille den generelle afspilningslydstyrke. Fra menuen kan du vælge andre funktioner:
Åbner et vindue som viser en skærm med en realtidsspektrumanalysator.
Viser aktive lydkilder og lader dig forbinde dem til alle tilgængelige busser.
Viser om lydserveren kører og om realtidsskemalægning bruges. Angiver når serveren vil autosuspendere og tillader dig at suspendere med det samme.
Viser aktive MIDI ind- og udgange og tillader dig at lave forbindelser [HUSK: virker dette endnu? Behøver flere detaljer].
Tilslutter en FreeVerb efterklangseffekt til stakken af aRts udskriftseffekter og lader dig kontrollere lydeffektindstillingerne grafisk.
Ændrer lydstyrkeindikatorerne i hovedvinduet til at bruge et visningsformat med farvede lysdioder i stedet for bjælker.
Dette værktøj er der for at hjælpe udviklere som bruger C-API i aRts. Det angiver de rigtige oversætter- og linkflag som behøves når man kompilerer og linker med aRts. Det er beregnet til at bruges i make-filer for at hjælpe til med flytbarhed. Kommandoen accepterer tre flag:
--cflags
Viser oversætterflag som behøves når man kompilerer med aRts C-API.
--libs
Viser linkerflag som behøves når man linker med aRts C-API.
Viser versionen af kommandoen artsc-config.
Typisk udskrift fra kommandoen vises nedenfor:
%
artsc-config
--cflags
-I/usr/local/kde2/include/artsc
%
artsc-config
--libs
-L/usr/local/kde2/lib -ldl -lartsc -DPIC -fPIC -lpthread
%
artsc-config
--version
0.9.5
Man skal kunne bruge dette værktøj i en make-fil med en regel såsom:
artsc: artsc.c gcc `artsc-config --cflags` -o artsc artsc.c `artsc-config --libs`
Kommandoen mcopidl er IDL-fil oversætteren for MCOP, multimedia-kommunikationsprotokollen som bruges af aRts. Grænseflader i aRts defineres med IDL, et sproguafhængig grænsefladesdefinitionssprog. Værktøjet mcopidl accepterer en IDL-fil som inddata og laver C++ deklarations- og kildekodefiler for en klasse som implementerer grænsefladen. Kommandoen accepterer følgende syntaks:
mcopidl [ flag
] filnavn
Gyldige flag er:
-I
mappe
Søg i mappe
efter deklarationsfiler.
-e
navn
Spring over en struct, grænseflade eller nummereringstypen navn
ved kodegenerering.
-t
Opret også .mcoptype
/.mcopclass
filer som indeholder typeinformation for IDL-filen.
Mere information om MCOP og IDL findes i afsnittet Grænseflader og IDL.
Først af alt, når du forsøger at køre aRts-builder, skal du også køre lydserveren (artsd). Sædvanligvis, hvis du bruger KDE 2.1, vil dette allerede være tilfældet. Hvis ikke, så kan du indstille automatisk start af lydserveren i KControl, med Lyd+Lydserver.
Når du kører aRts, kører den altid små moduler. aRts-builder er et værktøj til at oprette nye strukturer af små sammenkoblede moduler. Du klikker helt enkelt indeni gitteret for at tilføje et modul. For at gøre dette, vælges de fra menuen , og der klikkes et sted i den grøngrå plan.
Moduler har ofte porte (hvor lydsignaler almindeligtvis ledes ind eller ud). For at koble to porte sammen, klikkes på den første, som gør at den bliver orange, og derefter klikkes på den anden. Du kan kun forbinde en ind-port (på oversiden af et modul) med en ud-port (på undersiden af et modul). Hvis du vil angive en konstant værdi for en port (eller afbryde forbindelsen til den), gøres det ved at dobbeltklikke på porten.
Start aRts-builder.
Du behøver et Synth_AMAN_PLAY-modul for at høre uddata som du laver. Så opret et Synth_AMAN_PLAY-modul ved at vælge ->->-> og klikke på den tomme modulplads. Placér den cirka nedenfor den femte linje, eftersom vi vil tilføje en del ting ovenfor.
Modulet vil have en parameter title
(porten længst til venstre), og autoRestoreID
(ved siden af porten længst til venstre) for at den skal kunne findes. For at udfylde disse her, dobbeltklikkes på portene, der vælges en konstant værdi og skrives gennemgang
i redigeringsfeltet. Klik på for at anvende.
Vælg ->. Du hører absolut ingenting. Afspilningsmodulet har også brug for noget inddata... ;) Hvis du har lyttet til tavsheden et stykke tyd, klik så på og gå til trin 2.
Opret en Synth_WAVE_SIN-modul (fra ->->) og placér den over Synth_AMAN_PLAY-modulet (lad en linjes mellemrum være mellem dem).
Som du ser, så producerer den noget uddata, men kræver pos som inddata. Lad os først sende uddata til højtalerne. Klik på porten out på Synth_WAVE_SIN og derefter på porten left på Synth_AMAN_PLAY. Voila, du har sammenkoblet to moduler.
Oscillatorer i aRts kræver ikke en frekvens som inddata, men i stedet en position på bølgeform. Positionen skal være mellem 0 og 1, som i et standardobjekt Synth_WAVE_SIN oversættes til området 0 .. 2*Pi. For at oprette oscillerende værdier fra en frekvens, bruges et Synth_FREQUENCY modul.
Opret en Synth_FREQUENCY-modul (fra ->->) og forbind dets “pos” udgang til “pos” indgangen på Synth_WAVE_SIN. Sæt frekvensporten for frekvensgeneratoren til den konstante værdi 440.
Vælg ->. Du vil høre en sinusbølge på 440 Hz i en af dine højtalere. Hvis du har lyttet til den et stykke tid, så klik på og gå til trin 3.
O.k., det ville være bedre hvis du kunne høre sinusbølgen i begge højtalerne. Forbind også den højre port på Synth_PLAY til udgangen fra Synth_WAVE_SIN.
Opret et Synth_SEQUENCE-objekt (fra ->->). Det skal placeres øverst på skærmen. Hvis du behøver mere plads kan du flytte de andre moduler ved at markere dem (for at markere flere moduler holdes shift-tasten nede), og trække dem rundt.
Forbind nu frekvensudgangen på Synth_SEQUENCE til frekvensindgangen på Synth_FREQUENCY-modulet. Angiv derefter sekvensens hastighed som konstant værdi 0.13 (hastigheden er porten længst til venstre).
Gå nu til porten længst til højre (seq) på Synth_SEQUENCE og skriv A-3;C-4;E-4;C-4;
som konstant værdi. Dette angiver en sekvens. Mere om dette i modulreferencen.
Synth_SEQUENCE behøver virkelig en sekvens og hastigheden. Uden disse får du måske hukommelsesdump.
Vælg ->. Du vil høre en behagelig sekvens spille. Når du har nydt følelsen, så klik på og gå til trin 4.
Opret et Synth_PSCALE-modul (fra ->->). Afbryd forbindelsen fra udgangen for sinusbølgen ved at dobbeltklikke på den og vælg ikke forbundet. Forbind
Udgangen SIN til indgangen på PSCALE
PSCALE udgangen til AMAN_PLAY i venstre side
PSCALE udgangen til AMAN_PLAY i højre side
SEQUENCE pos til PSCALE pos
Til slut, sættes PSCALE top til en værdi, for eksempel 0.1.
Hvordan virker dette nu: Synth_SEQUENCE giver yderligere information om positionen for tonen den spiller lige nu, hvor 0 betyder netop påbegyndt og 1 betyder afsluttet. Modulet Synth_PSCALE skalerer lydstrømmen som sendes gennem den fra lydstyrke 0 (tavs) til 1 (oprindelig lydstyrke) tilbage til 0 (tavs) ifølge positionen. Positionen hvor toppen skal ske kan angives som top. 0.1 betyder at efter 10 % af tonen er spillet har lydstyrken nået sin maksimale værdi, og begynder derefter at aftage.
Vælg ->. Du vil høre en behagelig sekvens spille. Når du har nydt følelsen, klik på og gå til trin 5.
Start endnu en aRts-builder.
Placér en Synth_AMAN_PLAY i den, og indstil den til et rimeligt navn. Placér en Synth_BUS_DOWNLINK i den og:
Sæt Synth_BUS_DOWNLINK bus til lyd (det er kun et navn, kald den Peter hvis du har lyst)
Forbind Synth_BUS_DOWNLINK left (venstre) til Synth_AMAN_PLAY left.
Forbind Synth_BUS_DOWNLINK right (højre) til Synth_AMAN_PLAY right.
Begynd at køre strukturen. Som ventet hører du ingenting, ... ikke endnu.
Gå tilbage til strukturen med Synth_WAVE_SIN tingene og udbyt Synth_AMAN_PLAY-modulet mod en Synth_BUS_UPLINK, og indstil navnet til lyd (eller Peter hvis du vil). At fjerne moduler gøres ved at markere dem og vælge -> fra menuen (eller trykke på tasten Del).
Vælg ->. Du vil høre sekvensen med skalerede toner, transporteret over bussen.
Hvis du vil finde ud af hvorfor noget sådant her kan være nyttigt, så klik på (i aRts-builder som kører Synth_SEQUENCE tingene, du kan lade den anden fortsætte med at køre) og gå til trin 6.
Vælg -> fra menuen i den aRts-bygger som indeholder Synth_SEQUENCE tingene, og kald den gennemgang. Tryk på .
Vælg ->.
Start yderligere en aRts-builder og vælg ->, og indlæs gennemgangen igen.
Nu kan du vælge -> i begge aRts-builder'er som har strukturen. Nu hører du det samme to gange. Afhængig af hvornår du starter dem kommer det til at lyde mere eller mindre godt.
En anden ting som det kan være godt at gøre på dette tidspunkt er at starte Noatun, og spille en mp3
-fil. Start derefter artscontrol. Gå til ->. Det du ser er Noatun og din struktur “gennemgang” afspille noget. Det gode som du kan gøre er: dobbeltklik på Noatun. Du får nu en liste over tilgængelige busser. Og se? Du kan lade Noatun sende sin udskrift via lydbussen til afspilningsstrukturen som din gennemgang sørger for.
Endelig, nu skal du kunne lave din sinusbølge om til et rigtigt instrument. Dette er kun meningsfuldt hvis du har noget passende som kan sende MIDI-begivenheder til aRts. Her beskriver jeg hvordan du kan bruge et eksternt keyboard, men en sequencer, såsom Brahms, som kender til midibussen virker også.
Først af alt, så ryd op på din desktop indtil du kun har én aRts-builder med sinusbølgestrukturen i gang (men lad den ikke køre). Gå derefter tre gange til ->, og tre gange til ->. Placér portene et sted.
Gå til +Ændr positioner og navne og døb portene frequency, velocity, pressed, left, right, done.
Endelig kan du slette modulet Synth_SEQUENCE, og i stedet forbinde inddataporten frequency for strukturen til frekvensporten på Synth_FREQUENCY. Hmm. Men hvad skal man gøre med pos?
Denne er ikke tilgængelig, eftersom der ikke er nogen algoritme i værdier som kan forudse hvornår brugeren vil slippe tasten han netop trykkede ned på sit midi-keyboard. Vi har i stedet parameteren pressed, som kun angiver om brugeren stadigvæk holder tasten nede. (pressed = 1: tasten stadigvæk nedtrykt, pressed = 0: tasten sluppet)
Dette betyder også at Synth_PSCALE-objektet skal udskiftes nu. Kobl en Synth_ENVELOPE_ADSR ind i stedet (fra ->->). Forbind:
Strukturen pressed indgang til ADSR active
SIN udgangen til ADSR indgangen
ADSR udgangen til den venstre strukturudgang
ADSR udgangen til den højre strukturudgang
Sæt parametrene attack til 0.1, decay til 0.2, sustain til 0.7, release til 0.1.
En ting til som vi skal tænke på er at instrumentstrukturen skal på en eller anden måde vide når den har spillet færdig og så kan ryddes væk, eftersom den ellers aldrig ville stoppe også selvom tasten er sluppet. Heldigvis ved ADSR enveloppen når der ikke er mere at høre, eftersom den alligevel skalerer signalet ned til nul på et tidspunkt efter tasten er sluppet.
Dette angives ved at sætte udgangen done til 1. Så forbind denne til strukturens udgang. Strukturen fjernes så snart done bliver 1.
Omdøb din struktur til instrument_gennemgang (fra ->. Gem den derefter med Gem som (det standardnavn som nu angives skal være instrument_gennemgang).
Start artscontrol, og gå til ->, og vælg ->. Tilsidst skal du kunne vælge dit instrument (gennemgang) her.
Åbn en terminal og skriv midisend
. Du vil se at midisend og instrumentet nu er på listen i aRts MIDI-håndtering. Efter at have valgt begge to og trykket på , er vi til sidst klare. Tag dit keyboard og begynd at spille (det skal naturligvis være forbundet til din maskine).
Du bør nu kunne arbejde med aRts. Her er nogle vink om hvad du nu vil kunne gøre for at forbedre dine strukturer:
Forsøg at bruge noget andet end en sinusbølge. Hvis du kobler en trekantbølge ind, TRI, synes du formodentlig ikke at lyden er særligt behagelig, Men forsøg at tilføje et SHELVE_CUTOFF-filter direkte efter trekantbølgen for at klippe frekvenserne ovenfor ved en vis frekvens (forsøg med noget i retning af 1000 Hz, eller endnu bedre to gange inddatafrekvensen eller inddatafrekvensen + 2000 Hz eller noget sådant).
Forsøg at bruge mere end en oscillator. Synth_XFADE kan bruges til at overtone (blande) to signaler, Synth_ADD til at addere dem.
Forsøg at sætte frekvenserne for oscillatorerne til nogle forskellige værdier, det giver behagelige oscillationer.
Eksperimentér med mere end en envelope.
Forsøg at syntetisere instrumenter med forskellig lyd til venstre og højre.
Forsøg at efterbehandle signalet når det kommer ud fra bussens nedlink. Du kunne for eksempel blande en forsinket version af signalet med originalen for at få en ekkoeffekt.
Forsøg at bruge anslagsstyrken, velocity (dette er styrken som tasten er trykket ned med, man kan også kalde det lydstyrken). Specialeffekter opstår altid hvis dette ikke kun ændrer lydstyrken på signalerne som laves, men også instrumentets lyd (for eksempel klipningsfrekvensen).
...
Hvis du har lavet noget storartet, så overvej gerne at give det til aRts' netside, eller til at indgå i næste udgave.
aRts-builder levereres med flere eksempler, som kan åbnes med ->. Nogle af dem er i mappen, mens nogen (som af en eller anden grund ikke virker med den nuværende udgave) er efterladt i todo-mappen.
Eksemplerne kan inddeles i flere kategorier:
Selvstændige eksempler som illustrerer hvordan hvert af de indbyggede aRts-moduler bruges (som hedder example_*.arts
). Disse sender typisk noget uddata til et lydkort.
Instrumenter som bygges fra aRts-moduler på lavere niveau (som hedder instrument_*.arts
). Disse følger en standardkonvention for ind- og udporte så de kan bruges af MIDI-håndteringen i artscontrol.
Skabeloner til at oprette nye moduler (som hedder template_*.arts
).
Effekter som kan bruges som genbrugelige byggeblokke (som hedder effect_*.arts
) [ alle i todo ]
Mikserelementer som bruges til at oprette miksere, inklusive grafiske styremoduler (som hedder mixer_element_*.arts
). [ alle i todo ]
Diverse moduler som ikke passer ind i nogen af de ovenstående kategorier.
Detaljeret beskrivelse af hvert modul:
example_stereo_beep.arts
Laver en 440 Hz sinusbølge i venstre kanal og en 880 Hz sinusbølge i højre kanal, og sender dem til lydkortet. Denne henvises til i aRts dokumentation.
example_sine.arts
Laver en 440 Hz sinusbølge.
example_pulse.arts
Laver en 440 Hz pulsbølge med 20% aktivitet.
example_softsaw.arts
Laver en 440 Hz savtandsbølge.
example_square.arts
Laver en 440 Hz firkantbølge.
example_tri.arts
Laver en 440 Hz trekantbølge.
example_noise.arts
Laver hvid støj.
example_dtmf1.arts
Laver en dualtone ved at producere sinusbølger med 697 og 1209 Hz, skalere dem til 0,5 og addere dem. Dette er DTMF-tonen for cifferet "1" på en trykknaptelefon.
example_atan_saturate.arts
Kører en trekantbølge gennem et atan-mætningsfilter.
example_autopanner.arts
Bruger autopanorering til at panorere en 400 Hz sinusbølge mellem venstre og højre højtaler med en hastighed på 2 Hz.
example_brickwall.arts
Skalerer en sinusbølge med en faktor 5 og kører den derefter gennem ved en murstensvægsbegrænser.
example_bus.arts
Linker ned fra en bus som hedder “Bus” og linker op til bussen “out_soundcard” med venstre og højre kanal byttet om.
example_cdelay.arts
Linker ned fra en bus som hedder “Delay” og linker højre kanal op gennem en 0,5 sekunders forsinkelse, og venstre kanal uforandret. Du kan bruge artscontrol til at forbinde effekten til lydafspilning og observere resultatet.
example_delay.arts
Dette er det samme som example_cdelay.arts
men bruger forsinkelseseffekten.
example_capture_wav.arts
Dette bruger Synth_CAPTURE_WAV til at gemme en 400 Hz sinusbølge som en wav-fil. Kør modulet i nogle sekunder, og kig derefter på filen som blev lavet i /tmp
. Du kan afspille filen med en mediaspiller såsom kaiman.
example_data.arts
Dette bruger Data-modulet til at oprette en konstant strøm af værdien “3” og sender den til et Debug-modul for at vise det periodisk. Det indeholder også et Nil-modul, og viser hvordan den kan bruges til ikke at gøre noget overhovedet.
example_adsr.arts
Viser hvordan en enkelt instrumentlyd kan laves med envelope ADSR-modulet, som udløses repetitivt af en firkantbølge.
example_fm.arts
Dette bruger FM Source-modulet til at lave en 440 Hz sinusbølge som frekvensmoduleres med en 5 Hz hastighed.
example_freeverb.arts
Dette forbinder Freeverb-effekten fra et busnedlink til et busoplink. Du kan bruge artscontrol til at forbinde effekten til lydafspilning og observere resultatet.
example_flanger.arts
Dette implementerer en enkel flangereffekt (det synes dog ikke at virke endnu).
example_moog.arts
Denne struktur kombinerer de to kanaler fra en bus ind i en, sender den gennem et Moog VCF filter, og sender den derefter til bussen out_soundcard.
example_pitch_shift.arts
Denne struktur sender venstrekanalens lyddata gennem en tonehøjdeændringseffekt. Justér hastighedsparameteren for at variere effekten.
example_rc.arts
Denne struktur sender hvid støj gennem et RC-filer og ud til lydkortet. Ved at kigge på FFT måleren i artscontrol kan du se hvordan dette adskiller sig fra en ufiltreret støjbølgeform.
example_sequence.arts
Dette demonstrerer Sequence-modulet ved at spille en sekvens af toner.
example_shelve_cutoff.arts
Denne struktur sender hvid støj gennem et klippefilter og ud til lydkortet. Ved at kigge på FFT måleren i artscontrol kan du se hvordan dette adskiller sig fra en ufiltreret støjbølgeform.
example_equalizer.arts
Dette demonstrerer modulet Std_Equalizer. Det forstærker de lave og høje frekvenser med 6 dB.
example_tremolo.arts
Dette demonstrerer tremoloeffekten. Det modulerer venstre og højre kanal med en 10 Hz tremolo.
example_xfade.arts
Dette eksempel blander 440 og 880 Hz sinusbølger med en overtoning. Justér værdien på overtoningens procentindgang fra -1 til 1 for at kontrollere blandningen af de to signaler.
example_pscale.arts
Dette illustrerer Pscale-modulet (jeg er ikke sikker på at det er et meningsfuldt eksempel).
example_play_wav.arts
Dette illustrerer modulet Play Wave. Du skal angive hele søgestien til en .wav
-fil som filnavneparameter.
Dette viser modulet Multi Add, som accepterer et hvilket som helst antal indgange. Det opsummerer tre Data-moduler som producerer inddata 1, 2 og 3, og viser resultatet 6.
Idéen med aRts er at syntese kan gøres med små moduler, som kun gør en eneste ting, og derefter kombinere dem i komplekse strukturer. De små moduler har normalt indgange, hvor de kan modtage nogle signaler eller parametre, og udgange hvor de producerer nogle signaler.
Et modul (Synth_ADD) tager for eksempel kun de to signaler på sine indgange og lægger dem sammen. Resultatet er tilgængeligt som et uddatasignal. De steder hvor moduler sørger for deres ind- eller udsignaler kaldes porte.
En struktur er en kombination af sammenkoblede moduler, hvor nogen kan have parametre som er kodet direkte på deres inddataporte, andre kan være sammenkoblede, og en del kan være slet ikke forbundet.
Det du kan gøre med aRts-builder er at beskrive strukturer. Du beskriver hvilke moduler du vil skal kobles sammen med hvilke andre moduler. Når du er klar, kan du gemme strukturbeskrivelsen i en fil, eller bede aRts om at oprette (køre) den struktur som du har beskrevet.
Derefter hører du formodlig noget lyd, hvis du har gjort alt på den rigtige måde.
Antag at du har et program som hedder “musepling” (som skal afgive en “pling”-lyd hvis du klikker på en museknap). Latensen er tiden mellem din finger trykker på museknappen til du hører plinget. Latensen for dette scenario består af flere forskellige latenstider, som har forskellige årsager.
I dette enkle program opstår latensen på følgende steder:
Tiden til kernen har meddelt X11-serveren at museknappen er trykket ned.
Tiden til X11-serveren har meddelt dit program at museknappen er trykket ned.
Tiden til musepling-programmet har bestemt at denne knap var værd at få et pling afspillet.
Tiden det tager for musepling-programmet at fortælle lydserveren at den skal afspille et pling.
Tiden det tager for plinget (som lydserveren begynder at mikse med øvrig uddata med det samme) at gå gennem bufferet data, til det virkelig når stedet hvor lydkortet spiller.
Tiden det tager for pling-lyden at gå fra højtalerne til dine ører.
De første tre punkter er latensen udenfor aRts. De er interessante, men udenfor dette dokuments rækkevidde. Ikke desto mindre skal du vide at de findes, så selvom du har optimeret alt andet til virkelig små værdier, så får du måske ikke nødvendigvis nøjagtigt det resultat du forventer dig.
At bede serveren om at spille noget indebærer oftest kun et enkelt MCOP-kald. Der er målinger som bekræfter at det kan lade sig gøre at bede serveren at spille noget 9000 gange pr sekund med den nuværende implementering, på samme værtsmaskine med Unix domæne-sokler. Jeg antager at det meste af dette er kernens omkostning, for at skifte fra en proces til en anden. Naturligvis ændres denne værdi med den nøjagtige type af parametrene. Hvis man overfører et helt billede med et kald, bliver det langsommere end hvis man kun overfører en "long" værdi. Det samme er sandt for returværdien. For almindelige strenge (som filnavnet på wav
-filen som skal afspilles) skulle dette ikke være et problem.
Dette betyder at vi kan tilnærme denne tid med 1/9000 sekund, det vil sige under 0,15 ms. Vi vil se at dette ikke er relevant.
Derefter kommer tiden efter serveren begynder at spille og lydkortet tager imod noget. Serveren skal buffre data, så ingen pauser høres når andre programmer, såsom din X11-server eller “musepling”-programmet, kører. Den måde dette håndteres på Linux® er at der er et antal fragmenter af en vis størrelse. Serveren genopfylder fragmenter, og lydkortet afspiller fragmenter.
Så antag at der er tre fragmenter. Serveren genopfylder det første og lydkortet begynder at afspille det. Serveren genopfylder det andet. Serveren genopfylder det tredje. Serveren er klar og andre programmer kan nu gøre noget.
Når lydkortet har afspillet det første fragment, begynder det at afspille det andet og serveren begynder at genopfylde det første, og så videre.
Den maksimale latenstiden du får med alt dette er (antal fragmenter) * (størrelse på hvert fragment) / (samplingsfrekvens * (størrelse på hver sampling)). Hvis vi antager 44 kHz stereo, og syv fragmenter på 1024 byte (de nuværende standardindstillinger i aRts), så får vi 40 ms.
Disse værdier kan indstilles efter dine behov. CPU-brugen øges dog med mindre latenstider, eftersom lydserveren skal genopfylde bufferne oftere, og med mindre dele. Det er også oftest umuligt at nå bedre værdier uden at give lydserveren realtidsprioritet, eftersom man ellers ofte får pauser.
Det er imidlertid realistisk at lave noget i stil med 3 fragmenter med 256 byte hver, som ville ændre denne værdi til 4,4 ms. Med 4,4 ms forsinkelse ville aRts CPU-forbrug være cirka 7,5 %. Med en 40 ms forsinkelse, ville den være cirka 3 % (for en PII-350, og værdien kan afhænge af dit lydkort, version af kernen og andet).
Så er der tiden som det tager for pling-lyden at gå fra højtalerne til dine ører. Antag at din afstand fra højtalerne er 2 meter. Lyden bevæger sig med hastigheden 330 meter pr sekund. Så vi kan ansætte denne tid til 6 ms.
Programmer med lydstrømme er dem som laver deres lyd selv. Tænk dig til et spil som sender en konstant strøm med samplinger, og nu skal tilpasses til at afspille lyd via aRts. Som et eksempel: når jeg trykker på en tast så hopper figuren som jeg bruger, og en bang-lyd afspilles.
Først så skal du vide hvordan aRts håndterer strømme. Det er meget lignende I/O med lydkortet. Spillet sender nogle pakker med samplinger til lydserveren, lad os antage tre styk. Så snart lydserveren er klar med den første pakke, sender den en bekræftelse tilbage til spillet om at denne pakke er færdig.
Spillet laver yderligere en lydpakke og sender den til serveren. I mellemtiden begynder serveren at konsumere den anden lydpakke, og så videre. Latenstiderne ligner dem i det enklere tilfælde:
Tiden til kernen har meddelt X11-serveren at en knap er trykket ned.
Tiden til X11-serveren har meddelt spillet at en knap er trykket ned.
Tiden til spillet har bestemt at denne knap var værd at få et bang afspillet.
Tiden til lydpakken som afspilles er begyndt at putte bang-lyden ind når lydserveren.
Tiden det tager for banget (som lydserveren begynder at mikse med øvrig uddata med det samme) at gå gennem bufferdata, til det virkelig når stedet hvor lydkortet spiller.
Tiden det tager for bang-lyden fra højtalerne at nå dine ører.
De eksterne latenstider er, som ovenfor, udenfor dette dokuments rækkevidde.
Det er åbenbart at latenstiden for strømme afhænger af tiden det tager for alle pakker som bruges at afspilles en gang. Så den er (antal pakker) * (størrelse på hver pakke) / (samplingsfrekvensen * (størrelse på hver sampling)).
Som du ser er dette samme formel som gælder for fragmenterne. For spil er der dog ingen grund til at have så korte forsinkelser som ovenfor. Jeg vil sige at et realistisk eksempel for et spil kunne være 2048 byte pr pakke, når der bruges 3 pakker. Latenstidsresultatet ville så være 35 ms.
Dette er baseret på følgende: antag at et spil viser 25 billeder pr sekund (for skærmen). Det er antageligt helt sikkert at antage at en forskel på et billede for lydudskriften ikke vil kunne mærkes. Derfor er 1/25 sekunds forsinkelse for lydstrømmen acceptabelt, hvilket på sin side betyder at 40 ms skulle være o.k.
De fleste personer kører heller ikke deres spil med realtidsprioritet, og faren for pauser i lyden kan ikke negligeres. Strømme med 3 pakker på 256 byte er mulige (jeg prøvede det) - men forårsager meget CPU-forbrug til strømning.
Latenstider på serversiden kan du beregne præcis som ovenfor.
Der er mange faktorer som påvirker CPU-forbrug i et komplekst scenario, med nogle programmer med lydstrømme og nogle andre programmer, nogle plugin i serveren, osv. For at angive nogle få:
Rå CPU-forbrug for de nødvendige beregninger.
aRts interne skemalægningsomkostning - hvordan aRts bestemmer hvornår hvilket modul skal beregne hvad.
Omkostning til konvertering af heltal til decimaltal.
MCOP protokolomkostning.
Kernens proces/sammenhængsskift.
Kernens kommunikationsomkostning.
For beregning af rå CPU-forbrug, hvis du afspiller to strømme samtidigt skal du gøre additioner. Hvis du anvender et filter, er visse beregninger indblandede. For at tage et forenklet eksempel, at addere to strømme kræver måske fire CPU-cykler pr addition, på en 350 MHz processor er dette 44100 * 2 * 4 / 350000000 = 0,1 % CPU-forbrug.
aRts interne skemalægning: aRts skal bestemme hvilket plugin som skal beregne hvad hvornår. Dette tager tid. Brug et profileringsværktøj hvis du er interesseret i det. Hvad som kan siges i almindelighed er at jo mindre realtid som bruges (dvs. jo større blokke som kan beregnes af gangen) desto mindre skemalægningsomkostning fås. Udover beregning af blokke med 128 samplinger af gangen (altså med brug af fragmentstørrelser på 512 byte) er skemalægningsomkostningen formodentlig ikke værd at bryde sig om.
Konvertering fra heltal til decimaltal: aRts bruger decimaltal som internt dataformat. De er enkle at håndtere, og på moderne processorer er de ikke meget langsommere end heltalsoperationer. Hvis der er klienter som afspiller data som ikke er decimaltal (såsom et spil som skal lave sin lydudskrift via aRts), behøves konvertering. Det samme gælder hvis du vil afspille lyd på dit lydkort. Lydkortet behøver heltal, så du skal konvertere.
Her er værdier for en Celeron, cirka klokcykler pr sampling, med -O2 og egcs 2.91.66 (målt af Eugene Smith (hamster AT null.ru)
). Dette er naturligvis yderst processorafhængigt:
convert_mono_8_float: 14 convert_stereo_i8_2float: 28 convert_mono_16le_float: 40 interpolate_mono_16le_float: 200 convert_stereo_i16le_2float: 80 convert_mono_float_16le: 80
Så dette betyder 1 % CPU-forbrug for konvertering og 5 % for interpolation på denne 350 MHz processor.
MCOP protokollomkostning: MCOP klarer, som en tommelfingerregel, 9000 kald pr sekund. En stor del af dette er ikke MCOP's fejl, men hører sammen med de to grunde for kernen som nævnes nedenfor. Dette giver i alle tilfælde en basis for at udføre beregninger af hvad omkostningen er for strømning.
Hver datapakke som sendes med en strøm kan anses for at være et MCOP-kald. Store pakker er naturligvis langsommere end 9000 pakker/s, men det giver en god idé.
Antag at du bruger pakkestørrelser på 1024 byte. På denne måde, for at overføre en strøm med 44 kHz stereo, behøver du at overføre 44100 * 4 / 1024 = 172 pakker pr sekund. Antag at du kunne overføre 9000 pakker med 100 % CPU-forbrug, så får du (172 *100) / 9000 = 2 % CPU-forbrug på grund af strømningen med 1024 byte pakker.
Dette er en approksimation. Det viser i alle tilfælde at du ville klare dig meget bedre (hvis du har råd for latenstiden), med for eksempel at bruge pakker på 4096 byte. Her kan vi oprette en kompakt formel, ved at beregne pakkestørrelsen som forårsager 100 % CPU-forbrug som 44100 * 4 / 9000 = 19,6 samplinger, og på den måde få hurtigformlen:
CPU-forbrug for en strøm i procent = 1960 / (din pakkestørrelse)
som giver os 0,5 % CPU-forbrug med en strøm af 4096 byte pakker.
Kernens proces/sammenhængsskift: Dette er en del af MCOP-protokollens omkostning. At skifte mellem to processer tager tid. Der er en ny hukommelsesafbildning, cacher er ugyldige, og en del andet (hvis en ekspert på kernen læser dette - fortæl mig om de nøjagtige grunde). Dette betyder: det tager tid.
Jeg er ikke sikker på hvor mange procesbyte Linux® kan lave pr sekund, men værdien er ikke uendelig. Så af MCOP-protokollens omkostning, antager jeg at en hel del afhænger af processkift. MCOP først påbegyndtes prøvede jeg samme kommunikation inde i en proces, og det var meget hurtigere (cirka fire gange hurtigere).
Kernens kommunikationsomkostning: Dette er en del af MCOP-protokollens omkostning. At overføre data mellem processer gøres for øjeblikket via et udtag (sokkel). Dette er bekvemt, eftersom den almindelige select() metode kan bruges til at afgøre hvornår en meddelelse er ankommet. Det kan også kombineres med andre I/O-kilder såsom lyd-I/O, X11-server eller hvad som helst andet.
Disse læse- og skrivekald koster definitivt processorcykler. For små kald (som at overføre en midi-begivenhed) er det formodentlig ikke så farligt, men for store kald (som at overføre et videobillede på flere Mbyte) er det helt klart et problem.
At tilføje brug af delt hukommelse til MCOP hvor det er passende er formodentlig den bedste løsning. Det skal dog gøres transparent for anvendelsesprogrammerne.
Tag et profileringsværktøj og udfør andre test for at finde ud af nøjagtigt hvordan nuværende lydstrømme påvirkes af ikke at bruge delt hukommelse. Det er dog ikke så dårligt, eftersom lydstrømme (afspilning af mp3) kan gøres med totalt 6 % CPU-forbrug for artsd og artscat (og 5 % for mp3-afkoderen). Dette omfatter alting fra nødvendige beregninger til omkostning for udtaget, så jeg vil bedømme at man måske ville vinde cirka 1 % på at bruge delt hukommelse.
Disse er lavet med den nuværende udviklingsversion. Jeg ville også forsøge med rigtigt svære tilfælde, så dette er ikke hvad programmer til daglig brug ville gøre.
Jeg skrev et program som hedder streamsound som sender datastrømmen til aRts. Her køres det med realtidsprioritet (uden problemer), og et lille plugin på serversiden (lydstyrkeskalning og klipning):
4974 stefan 20 0 2360 2360 1784 S 0 17.7 1.8 0:21 artsd 5016 stefan 20 0 2208 2208 1684 S 0 7.2 1.7 0:02 streamsound 5002 stefan 20 0 2208 2208 1684 S 0 6.8 1.7 0:07 streamsound 4997 stefan 20 0 2208 2208 1684 S 0 6.6 1.7 0:07 streamsound
Hvert af programmerne sender en strøm med 3 fragmenter på 1024 byte (18 ms). Der er tre sådanne klienter som kører samtidigt. Jeg ved at det synes at være lidt vel meget, men som jeg sagde: tag et profileringsværktøj og find ud af hvad som koster tid, og hvis du vil, forbedr det.
Jeg tror i alt fald ikke at bruge strømning sådan her er realistisk eller giver mening. For at gøre det hele endnu mere ekstremt, forsøgte jeg med den mindst mulige latenstid. Resultat: man kan bruge strømme uden afbrud med et klientprogram, hvis man tager 2 fragmenter med 128 byte mellem aRts og lydkortet, og mellem klientprogrammet og aRts. Dette betyder at man har en total maksimal latenstid på 128 * 4 / 44100 * 4 = 3 ms, hvor 1,5 ms genereres på grund af I/O til lydkortet og 1,5 ms genereres af kommunikation med aRts. Begge programmer skal køre med realtidsprioritet.
Men dette koster en enorm mængde CPU. Dette eksempel koster cirka 45 % på min P-II/350. Det begynder også at klikke hvis man starter top, flytter vinduer på X11-skærmen eller laver disk-I/O. Alt dette har med kernen at gøre. Problemet er at skemalægge to eller flere processer med realtidsprioritet også koster en enorm anstrengelse, endnu mere end kommunikation og meddelelse til hinanden, osv..
Tilsidst, et mere hverdagsagtigt eksempel: Dette er aRts med artsd og en artscat (en klient med datastrøm) som kører 16 fragmenter på 4096 byte:
5548 stefan 12 0 2364 2364 1752 R 0 4.9 1.8 0:03 artsd 5554 stefan 3 0 752 752 572 R 0 0.7 0.5 0:00 top 5550 stefan 2 0 2280 2280 1696 S 0 0.5 1.7 0:00 artscat
Busser er forbindelser som bygges dynamisk for at overføre lyd. Der er et antal oplink og nedlink. Alle signaler fra oplinkene adderes og sendes til nedlinkene.
Busser er for øjeblikket implementerede til at virke med stereo, så du kan kun overføre stereodata via busser. Hvis du vil have monodata, ja, overfør det kun på en kanal og sæt den anden til nul eller noget vilkårligt. Hvad du skal gøre er at oprette en eller flere Synth_BUS_UPLINK-objekter og give dem et busnavn, som de skal tale med (f.eks. “lyd” eller “trommer”). Send derefter helt enkelt data derind.
Derefter skal du oprette et eller flere Synth_BUS_DOWNLINK-objekter, og fortælle dem om busnavnet (“lyd” eller “trommer”... hvis det passer sammen, kommer data igennem), og den blandede lyd kommer ud igen.
Oplinkene og nedlinkene kan være i forskellige strukturer, du kan til og med have forskellige aRts-byggere som kører og starte et oplink i en og tage imod data i den anden med et nedlink.
Det der er rart ved busser er at de er fuldstændigt dynamiske. Klienter kan kobles ind eller ud i farten. Det skal ikke kunne høres noget klik eller støj når dette sker.
Du skal naturligvis ikke tilkoble eller frakoble en klient mens den spiller et signal, eftersom den formodentlig ikke er nul når den kobles ud, og der så opstår et klik.
aRts/MCOP afhænger helt af at opdele objekter i små komponenter. Dette gør alt meget fleksibelt, eftersom man let kan udvide systemet ved at tilføje nye komponenter, som implementerer nye effekter, filformater, oscillatorer, grafiske elementer, ... Eftersom næsten alt er komponenter, kan næsten alt let udvides uden at ændre eksisterende kildekode. Nye komponenter kan enkelt indlæses dynamisk for at forbedre programmer som allerede eksisterer.
For at dette skal virke, kræves der dog to ting:
Komponenter skal fortælle at de findes - de skal beskrive hvilke storartede ting de tilbyder, så programmerne kan bruge dem.
Programmer skal aktivt lede efter komponenter som de ville kunne bruge, i stedet for altid at bruge samme komponenter til en vis opgave.
Kombinationen af dette: komponenter som siger “her er jeg, jeg er smart, brug mig”, og programmer (eller om man vil, andre komponenter) som går ud og leder efter hvilken komponenter de kan bruge for at få noget gjort, kaldes at handle.
I aRts beskriver komponenter sig selv ved at angive værdier som de “understøtter” som egenskaber. En typisk egenskab for en filindlæsningkomponent kan være filendelsen for filerne som den kan behandle. Typiske værdier kan være wav
, aiff
eller mp3
.
I virkeligheden kan hver komponent vælge at tilbyde mange forskellige værdier for en egenskab. Så en enkelt komponent ville kunne tilbyde at læse både wav
og aiff
-filer, ved at angive at den understøtter disse værdier for egenskaben “Endelse”.
For at gøre dette, skal en komponent placere en .mcopclass
-fil som indeholder egenskaberne den understøtter på et passende sted. For vort eksempel, kan den se sådan her ud (og ville være installeret i
): komponentmappen
/Arts/WavPlayObject.mcopclass
Interface=Arts::WavPlayObject,Arts::PlayObject,Arts::SynthModule,Arts::Object Author="Stefan Westerfeld <stefan@space.twc.de>" URL="http://www.arts-project.org" Extension=wav,aiff MimeType=audio/x-wav,audio/x-aiff
Det er vigtigt at filnavnet på .mcopclass
-filen også angiver hvad komponentens grænseflade hedder. Handleren kigger ikke på indholdet overhovedet, hvis filen (som her) hedder Arts/WavPlayObject.mcopclass
, og komponentgrænsefladen hedder Arts::WavPlayObject
(moduler hører sammen med mapper).
For at lede efter komponenter er der to grænseflader (som er definerede i core.idl
, så de er tilgængelige i hvert program), som hedder Arts::TraderQuery
og Arts::TraderOffer
. Du går på en “indkøbsrunde” efter komponenter sådan her:
Opret et forespørgselsobjekt:
Arts::TraderQuery query;
Angiv hvad du vil have. Som du så ovenfor, beskriver komponenter sig selv med egenskaber, som de sætter til visse værdier. Så at specificere hvad du vil have gøres ved at vælge komponenter som understøtter en vis værdi for en egenskab. Dette sker med metoden supports i TraderQuery:
query.supports("Interface","Arts::PlayObject"); query.supports("Extension","wav");
Tilsidst udføres forespørgslen med metoden query. Derefter får du (forhåbentlig) nogle tilbud:
vector<Arts::TraderOffer> *offers = query.query();
Nu kan du undersøge hvad du fandt. Det vigtige er metoden interfaceName i TraderOffer, som giver dig navnene på komponenterne som svarede på spørgsmålet. Du kan også finde ud af yderligere egenskaber med getProperty. Følgende kode løber helt enkelt gennem alle komponenterne, udskriver deres grænsefladenavn (som ville kunne bruges til at oprette dem), og fjerner resultaterne af forespørgslen igen:
vector<Arts::TraderOffer>::iterator i; for(i = offers->begin(); i != offers->end(); i++) cout << i->interfaceName() << endl; delete offers;
For at denne slags handelsservice skal være nyttig, er det vigtigt på en eller anden måde at blive enig om hvilke egenskaber som komponenter normalt skal definere. Det er væsentligt at mere eller mindre alle komponenter indenfor et vist område bruger samme sæt egenskaber til at beskrive sig selv (og samme sæt værdier når det behøves), så programmer (eller andre komponenter) kan finde dem.
Author (type streng, valgfri): Forfatter. Dette kan bruges til endelig at lade verden finde ud af at du har skrevet noget. Du kan skrive hvad du vil her, en e-mail-adresse er naturligvis en god hjælp.
Buildable (type boolean, anbefales): Bygbar. Dette angiver om komponenten er brugbar med RAD-værktøj (såsom aRts-builder) som bruger komponenter ved at tildele egenskaber og forbinde porte. Det anbefales at denne værdi sættes til true for næsten alle signalbehandlingskomponenter (såsom filer, lydeffekter, oscillatorer, ...), og for alle andre objekter som kan bruges på en RAD-lignende måde, men ikke for interne objekter som for eksempel Arts::InterfaceRepo
.
Extension (type streng, brugt hvor det passer): Filendelse. Alle moduler som håndterer filer bør overveje at bruge dette. Du angiver filendelsen med små bogstaver uden “.” her, så noget som wav
skulle virke udmærket.
Interface (type streng, kræves): Grænseflade. Dette skal omfatte hele listen af (nyttige) grænseflader som din komponent understøtter, formodentlig inklusive Arts::Object
og hvis anvendeligt Arts::SynthModule
.
Language (type streng, anbefales): Sprog. Hvis du ønsker at din komponent skal indlæses dynamisk, skal du angive sproget her. For øjeblikket er den eneste tilladte værdi C++
, som betyder at komponenten er skrevet med den normale C++ programmeringsgrænseflade. Hvis du angiver dette, skal du også angive egenskaben “Library” nedenfor.
Library (type streng, brugt hvor det passer): Bibliotek. Komponenter som er skrevet i C++ kan indlæses dynamisk. For at gøre dette skal du kompilere dem i et dynamisk indlæseligt libtool (.la
) modul. Her kan du angive navnet på .la
-filen som indeholder din komponent. Husk at bruge REGISTER_IMPLEMENTATION (som altid).
MimeType (type streng, brug hvor det passer): Mimetype. Alle som håndterer filer bør overveje at bruge dette. Du skal angive standard-mimetypen med små bogstaver her, for eksempel audio/x-wav
.
URL (type streng, valgfri): Hvis du vil fortælle hvor man kan finde en ny version af komponenten (eller en netside eller noget andet), kan du gøre dette. Dette skal være en standard HTTP- eller FTP-netadresse.
Hver navnerumsdeklaration hører sammen med en deklaration af en “modul” i MCOP IDL.
// mcop idl module M { interface A { } }; interface B;
I dette tilfælde ville den genererede C++ kode for IDL-fragmentet se sådan her ud:
// C++ deklaration namespace M { /* deklaration af A_base/A_skel/A_stub og lignende */ class A { // Smartwrap referenceklasse /* [...] */ }; } /* deklaration af B_base/B_skel/B_stub og lignende */ class B { /* [...] */ };
Så når du henviser til klasserne fra eksemplet ovenfor i din C++ kode, skal du skrive M::A
, men kun B. Du kan imidlertid bruge “using M” et sted, som med alle navnerum i C++.
Der er et globalt navnerum som kaldes “Arts”, som alle programmer og biblioteker som hører til aRts selv bruger til at lægge deres deklarationer i. Dette betyder at når du skriver C++ kode som afhænger af aRts, skal du normalt bruge præfikset Arts::
for hver klasse du bruger, sådan her:
int main(int argc, char **argv) { Arts::Dispatcher dispatcher; Arts::SimpleSoundServer server(Arts::Reference("global:Arts_SimpleSoundServer")); server.play("/var/foo/nogen_fil.wav");
Det andet alternativ er at skrive "using" en gang, sådan her:
using namespace Arts; int main(int argc, char **argv) { Dispatcher dispatcher; SimpleSoundServer server(Reference("global:Arts_SimpleSoundServer")); server.play("/var/foo/nogen_fil.wav"); [...]
I IDL-filer, har du egentlig ikke noget valg. Hvis du skriver kode som tilhører aRts selv, skal du putte den i modulet aRts.
// IDL-fil for aRts-kode: #include <artsflow.idl> module Arts { // put den i Arts-navnerum interface Synth_TWEAK : SynthModule { in audio stream invalue; out audio stream outvalue; attribute float tweakFactor; }; };
Hvis du skriver kode som ikke hører til selve aRts, skal du ikke putte den i navnerummet “Arts”. Du kan dog oprette et eget navnerum hvis du vil. Under alle omstændigheder, skal du bruge præfiks for klasser fra aRts som du bruger.
// IDL-fil for kode som ikke hører til aRts: #include <artsflow.idl> // skriv enten med eller uden moduldeklaration, og de genererede klasser // kommer ikke til at bruge et navnerum: interface Synth_TWEAK2 : Arts::SynthModule { in audio stream invalue; out audio stream outvalue; attribute float tweakFactor; }; // du kan dog vælge et eget navnerum hvis du vil, så hvis du // skriver programmet "PowerRadio", ville du kunne gøre sådan her: module PowerRadio { struct Station { string name; float frequency; }; interface Tuner : Arts::SynthModule { attribute Station station; // intet præfiks for Station, samme modul out audio stream left, right; }; };
MCOP har ofte brug for at henvise til navne på typer og grænseflader for typekonverteringer, grænseflader og metodesignaturer. Disse repræsenteres af strenge i de almindelige MCOP-datastrukturer, mens navnerummet altid er fuldstændigt repræsenteret i C++ stilen. Dette betyder at strengene skal indeholde “M::A” og “B”, ifølge eksemplerne ovenfor.
Bemærk at dette til og med gælder hvis navnerumskvalifikatorerne ikke blev givet inde i IDL-teksten, eftersom sammenhængen klargør hvilket navnerum grænsefladen A
var beregnet til at bruge.
At bruge tråde er ikke muligt på alle platforme. Dette er grunden til at aRts oprindeligt blev skrevet uden at bruge tråde overhovedet. For næsten alle problemer, findes der en løsning uden tråde som gør det samme som hvert løsning med tråde.
For eksempel, i stedet for at placere lyduddata i en separat tråd, og gøre den blokerende, bruger aRts lyduddata som ikke blokerer, og regner ud hvornår næste del af uddata skal skrives med select()
.
aRts understøtter i det mindste (i de nyeste versioner) støtte for dem som vil implementere deres objekter med tråde. Hvis du for eksempel allerede har kode for en mp3
-afspiller, og koden forventer at mp3
-afkoderen skal køres i en separat tråd, er det oftest lettest at beholde denne konstruktion.
Implementeringen af aRts/MCOP er opbygget ved at dele tilstanden mellem forskellige objekter på tydelige og mindre tydlige måder. En kort liste af delte tilstande omfatter:
Afsenderobjektet som laver MCOP-kommunikation
Referenceregningen (Smartwrappers).
I/O-håndteringen som håndterer tidsgrænser og fd-tidsmåling.
Objekthåndteringen som laver objekter og indlæser plugin dynamisk.
Flydesystemet som kalder calculateBlock i passende tilfælde.
Ingen af de ovenstående objekter forventer at blive brugt samtidigt (dvs. at blive kaldt fra forskellige tråde samtidigt). I almindelighed er der to måder at løse dette:
Kræv at den som kalder hvilken som helst funktion i objektet skaffer sig en lås inden den bruges.
Gør objekterne virkeligt trådsikre og/eller opret instanser af dem for hver tråd.
aRts bruger den første måde. Du behøver en lås hver gang du skal have adgang til et af disse objekter. Den anden måde er sværere at gøre. Et hurtigt fiks som forsøger at opnå dette findes på http://space.twc.de/~stefan/kde/download/arts-mt.tar.gz, men for øjeblikket virker en minimal måde formodentlig bedre, og forårsager mindre problemer med eksisterende programmer.
Du kan få/slippe låsen med de to funktioner:
Generelt behøver du ikke at skaffe en lås (og du skal ikke forsøge at gøre det), hvis den allerede holdes. En liste over betingelser når dette er tilfældet er:
Du tager imod et tilbagekald fra I/O-håndteringen (tidsgrænse eller fd).
Du kaldes på grund af nogle MCOP-forespørgsler.
Du kaldes fra NotificationManager.
Du kaldes fra flydesystemet (calculateBlock)
Der er også nogle undtagelse for funktioner som du kun kan kalde i hovedtråden, og af den grund aldrig behøver et låse for at kalde dem:
Opret og fjern afsenderen eller I/O-håndteringen.
Dispatcher::run()
/ IOManager::run()
IOManager::processOneEvent()
Men det er alt. For alt andet som på nogen måde hører sammen med aRts, skal du skaffe låsen, og slippe den igen når du er klar. Her er et enkelt eksempel:
class SuspendTimeThread : Arts::Thread { public: void run() { /* * du behøver denne lås fordi: * - oprettelse af en reference behøver en lås (eftersom global: går til * objekthåndteringen, som derefter kunne have brug for GlobalComm * objektet for at slå hver forbindelse der skal gøres op) * - tildeling af en smartwrapper behøver en lås * - oprettelse af et objekt fra en reference behøver en lås (eftersom * den kan få brug for at forbinde til en server) */ Arts::Dispatcher::lock(); Arts::SoundServer server = Arts::Reference("global:Arts_SoundServer"); Arts::Dispatcher::unlock(); for(;;) { /* * du behøver en lås her, eftersom * - følge en reference for en smartwrapper behøver en lås * (eftersom den kan oprette objektet når den bruges) * - at gøre et MCOP-kald behøver en lås */ Arts::Dispatcher::lock(); long seconds = server.secondsUntilSuspend(); Arts::Dispatcher::unlock(); printf("sekunder til ventetilstand = %d",seconds); sleep(1); } } }
Følgende trådrelaterede klasser er tilgængelige for øjeblikket:
Arts::Thread
- som indkapsler en tråd.
Arts::Mutex
- som indkapsler en mutex.
Arts::ThreadCondition
- som giver støtte for at vække tråde som venter på at en vis betingelse skal blive sand.
Arts::SystemThreads
- som indkapsler operativsystemets trådningslager (og giver nogle hjælpefunktioner for anvendelsesprogrammører).
Se linkene for dokumentationen.
MCOP-referencer er et af de mest centrale begreber i MCOP programmering. Dette afsnit forsøger at beskrive nøjagtigt hvordan referencer bruges, og behandler især fejltilfælde (server bryder sammen).
En MCOP reference er ikke et objekt, men en reference til et objekt: Selv om følgende deklaration
Arts::Synth_PLAY p;ser ud som en definition af et objekt, så deklarerer den kun en reference til et objekt. Som C++ programmør, kan du også se den som Synth_PLAY *, en slags peger til et Synth_PLAY-objekt. Det betyder specielt at p kan være det samme som en NULL-peger.
Du kan oprette en NULL-reference ved eksplicit at tildele den.
Arts::Synth_PLAY p = Arts::Synth_PLAY::null();
At kalde objekter med en NULL-reference forårsager et hukommelsesdump
Arts::Synth_PLAY p = Arts::Synth_PLAY::null(); string s = p.toString();
forårsager et hukommelsesdump. Hvis man sammenligner dette med en peger, er det stort set det samme som
QWindow* w = 0; w->show();hvilket enhver C++ programmør ved at man skal undgå.
Uinitierede objekter forsøger at oprette sig selv når de først bruges
Arts::Synth_PLAY p; string s = p.toString();
er noget anderledes end at følge en NULL-peger. Du fortalte slet ikke objektet hvad det er, og nu forsøger du at bruge det. Gætværket her er at du vil have en ny lokal instans af et Arts::Synth_PLAY-objekt. Du kan naturligvis have villet gøre noget andet (såsom at oprette objektet et andet sted, eller bruge et eksisterende fjernobjekt). Det er i alle tilfælde en bekvem genvej til at oprette objekter. At oprette et objekt når det først bruges virker ikke når du allerede har tildelt det til noget andet (som en null-reference).
Den tilsvarende C++ terminologi ville være
QWidget* w; w->show();som naturligvis helt enkelt giver en segmenteringsfejl i C++. Så dette er anderledes her. Denne måde at oprette objekt er tricket, eftersom det ikke er nødvendigt at der findes en implementering for din grænseflade.
Betragt for eksempel et abstrakt objekt såsom et Arts::PlayObject. Der er naturligvis konkrete PlayObjects, såsom de til for at afspille mp3-filer eller wav-filer, men
Arts::PlayObject po; po.play();mislykkes helt sikkert. Problemet er at selvom et PlayObject forsøges at blive lavet, så mislykkes det eftersom der kun er objekter såsom Arts::WavPlayObject og lignende. Brug derfor kun denne måde at oprette objekter hvis du er sikker på at der er en implementering.
Referencer kan pege på samme objekt
Arts::SimpleSoundServer s = Arts::Reference("global:Arts_SimpleSoundServer"); Arts::SimpleSoundServer s2 = s;
laver to referencer som angiver samme objekt. Det kopierer ikke nogen værdi, og laver ikke to objekter.
Alle objekter referenceregnes. Så snart et objekt ikke har nogen referencer længere, slettes det. Der er ingen måde udtrykkeligt at fjerne et objekt, men du kan dog bruge noget sådant her
Arts::Synth_PLAY p; p.start(); [...] p = Arts::Synth_PLAY::null();for at få Synth_PLAY-objektet til at forsvinde til slut. Specielt er det aldrig nødvendigt at bruge new og delete i sammenhæng med referencer.
Eftersom referencer kan pege på fjernobjekter, kan serverne som indeholder disse objekter bryde sammen. Hvad sker så?
Et sammenbrud ændrer ikke om en reference er en null-reference. Dette betyder at hvis foo.isNull()
var true inden et serversammenbrud er den også true efter et serversammenbrud (hvilket er indlysende). Det betyder også at hvis foo.isNull()
var false inden et serversammenbrud (foo angav et objekt) er den også false efter serversammenbruddet.
At kalde metoder med en gyldig reference forbliver sikkert. Antag at serveren som indeholder objektet calc brød sammen. Kald til objekter såsom
int k = calc.subtract(i,j)er stadigvæk sikre. Det er åbenbart at subtract skal returnere noget, hvilket den ikke kan eftersom fjernobjektet ikke længere findes. I dette tilfælde ville (k == 0) være sand. I almindelighed forsøger operationer at returnere noget “neutralt” som resultat, såsom 0.0, en null-reference for objekter eller tomme strenge, når objektet ikke længere findes.
Kontrol med error()
afslører om noget virkede.
I ovenstående tilfælde, ville
int k = calc.subtract(i,j) if(k.error()) { printf("k er ikke i-j!\n"); }udskrive
k er ikke i-j
når fjernkaldet ikke virkede. Ellers er k
virkelig resultatet af subtraktionsoperationen som udføres af fjernobjektet (intet serversammenbrud). For metoder som gør ting såsom at fjerne en fil, kan du ikke vide med sikkerhed om det virkelig er sket. Naturligvis skete det hvis .error()
er false. Men hvis .error()
er true, er der to muligheder: Filen blev slettet, og serveren brød sammen præcis efter den blev slette, men inden resultatet overførtes.
Serveren brød sammen inden den kunne fjerne filen.
Brug af indlejrede kald er farligt i et program som skal være sikkert mod sammenbrud.
Brug af noget i retning af
window.titlebar().setTitle("foo");er ikke en god idé. Antag at du ved at vinduet indeholder en gyldig vinduesreference. Antag at du ved at
window.titlebar()
returnerer en reference til navnelisten eftersom vinduesobjektet er rigtigt implementeret. Sætningen ovenfor er imidlertid alligevel ikke sikker. Hvad kan ske hvis serveren som indeholder vinduesobjektet er brudt sammen. Så vil du, uafhængig af hvor god implementeringen af Window er, få en null-reference som resultat af operationen window.titlebar(). Og derefter vil kaldet til setTitle med denne null-reference naturligvis også føre til et sammenbrud.
Så en sikker variant af dette ville være
Titlebar titlebar = window.titlebar(); if(!window.error()) titlebar.setTitle("foo");og tilføj den rigtige fejlhåndtering hvis du vil. Hvis du ikke stoler på implementeringen af Window, kan du lige så godt bruge
Titlebar titlebar = window.titlebar(); if(!titlebar.isNull()) titlebar.setTitle("foo");som begge er sikre.
Der er andre fejlbetingelser, såsom nedkobling af netværket (antag at du tager kablet mellem din server og klient væk mens dit program kører). Deres effekt er imidlertid den samme som et serversammenbrud.
Totalt set er det naturligvis et policy-spørgsmål hvor strengt du forsøger at håndtere kommunikationsfejl i hele dit program. Du kan følge metoden “hvis serveren bryder sammen, skal vi fejlsøge den til den aldrig bryder sammen igen”, som ville betyde at du ikke behøver bryde dig om alle disse problemer.
Et objekt skal ejes af nogen for at eksistere. Hvis det ikke gør det, vil det ophøre med at eksistere (mere eller mindre) med det samme. Internt angives en ejer ved at kalde _copy()
, som forøger en reference tæller, og en ejer fjernes ved at kalde _release()
. Så snart referencetælleren når nul, så slettes objektet.
Som en variation på temaet, angives fjernbrug med _useRemote()
, og opløses med _releaseRemote()
. Disse funktioner har en liste over hvilken server som har kaldt dem (og derfor ejer objektet). Dette bruges hvis serveren kobler ned (dvs. sammenbrud, netværksfejl), for at fjerne referencer som stadigvæk findes til objektet. Dette gøres i _disconnectRemote()
.
Nu er der et problem. Betragt en returværdi. I almindelige tilfælde ejes returværdiobjektet ikke af funktionen som kaldes længere. Det ejes heller ikke af den som kalder, førend meddelelsen som indeholder objektet er modtaget. Så der er en tid med objekter som “mangler ejere”.
Når vi nu sender et objekt kan man være rimeligt sikker på at så snart det modtages, ejes det af nogen igen, med mindre, igen, modtageren bryder sammen. Dette betyder i alle tilfælde at specielle hensyn skal tages for objekter i det mindste mens der sendes, og formodentlig også mens der modtages, så de ikke fjernes med det samme.
Måden som MCOP gør dette er ved at “mærke” objekter som er ved at blive kopieret over netværket. Inden en sådan kopiering begynder, kaldes _copyRemote
. Dette forhindrer at objektet fjernes et stykke tid (5 sekunder). Så snart modtageren kalder _useRemote()
, fjernes mærket igen. Så alle objekter som sendes over netværket, mærkes inden overførslen.
Hvis modtageren modtager et objekt som findes på samme server, så bruges _useRemote()
naturligvis ikke. I dette specialtilfælde, findes funktionen _cancelCopyRemote()
til at fjerne mærket manuelt. Foruden dette, er der også en tidsbaseret fjernelse af mærker, hvis mærkning udførtes, men modtageren ikke virkelig fik objektet (på grund af sammenbrud, netværksfejl). Dette gøres med klassen ReferenceClean
.
GUI-elementer er for øjeblikket på det eksperimentelle stadium. Dette afsnit beskriver hvad det er meningen der skal ske med dem, så hvis du er udvikler, kan du forstå hvordan aRts vil håndtere grafiske grænseflader i fremtiden. Der er også allerede en del kode på plads.
GUI-elementer skal bruges til at lade syntesestrukturer vekselvirke med brugeren. I det enkleste tilfælde skal brugeren kunne ændre nogle parametre for en struktur direkte (som en forstærkningsfaktor som bruges inden det endelige afspilningsmodul).
I mere komplekse tilfælde, kan man tænke sig at brugeren ændrer parametre for grupper af strukturer og/eller strukturer som ikke kører endnu, såsom at ændre ADSR enveloppen for det aktive MIDI-instrument. Noget andet ville kunne være at angive filnavnet for et samplingsbaseret instrument.
På den anden side, kunne brugeren ville overvåge hvad syntheziseren gør. Der kunne være oscilloskoper, spektrumanalysatorer, lydstyrkemålere og “eksperimenter” som for eksempel regner frekvensoverførselskurven ud for et given filtermodul.
Til sidst, skal GUI-elementer kunne kontrollere hele strukturen af alt som kører inden i aRts, og på hvilken måde. Brugeren skal kunne tildele instrumenter til midi-kanaler, starte nye effektbehandlingsskridt, indstille sit hovedmikserpanel (som selv er opbygget af aRts-strukturer) for at få yderligere en kanal eller bruge en anden strategi for tonekontrol.
Som du ser, skal GUI-elementer give alle mulighederne i et virtuelt studie som aRts simulerer for brugeren. Naturligvis skal de også vekselvirke med midi-indgange (såsom skydere som også flyttes hvis de får MIDI-inddata som ændrer tilsvarende parametre), og formodentlig til og med oprette begivenheder selv, for at vekselvirkning med brugeren skal kunne indspilles via en sequencer.
Teknisk set er idéen at have en IDL-basisklasse for alle grafiske komponenter (Arts::Widget
), og aflede et antal almindelige komponenter fra den (såsom Arts::Poti
, Arts::Panel
, Arts::Window
, ...).
Derefter kan man implementere disse grafiske komponenter med en værktøjskasse, for eksempel Qt™ eller Gtk. Til slut, bør effekter bygge deres grafiske grænseflader fra eksisterende komponenter. En efterklangseffekt ville for eksempel kunne bygge sin grafiske grænseflade fra fem Arts::Poti
-tingester og et Arts::Window
. Så HVIS der findes en Qt™ implementering for disse grundkomponenter, kan effekten vises med Qt™. Hvis der findes en Gtk implementering, virker det også med Gtk (og ser mere eller mindre ud på samme måde).
Til sidst, eftersom vi bruger IDL her, kan aRts-builder (eller andre værktøjer) sætte grafiske grænseflader sammen visuelt, eller generere grafiske grænseflader automatisk, med tips for parameterværdier, kun baseret på grænsefladen. Det burde være ganske ligetil at skrive en klasse til at “oprette grafisk grænseflader fra en beskrivelse”, som tager en beskrivelse af en grafisk grænseflade (som indeholder de forskellige parametre og grafiske komponenter), og laver et levende objekt for en grafisk grænseflade ud fra den.
Baseret på IDL og aRts/MCOP-komponentmodellen, bør det være let at udvide de mulige objekter som kan bruges til den grafiske grænseflade præcis lige så let som det er at tilføje et plugin til aRts som for eksempel implementerer et nyt filter.
MIDI-understøttelsen i aRts kan gøre et antal forskellige ting. For det første, tillader den kommunikation mellem forskellige dele af programmellet som producerer eller konsumerer MIDI-begivenheder. Hvis du for eksempel har en sequencer og en samplingsenhed som begge bruger aRts, kan aRts sende MIDI-begivenheder fra sequenceren til samplingsenheden.
På den anden side kan aRts også hjælpe programmer til at vekselvirke med maskinellet. Hvis en programmeldel (for eksempel samplingen) virker sammen med aRts, kan den desuden tage imod MIDI-begivenheder fra et eksternt MIDI-keyboard.
Til slut, så udgør aRts en udmærket modulær synthesizer. Den er konstrueret til at gøre netop dette. Man kan altså bygge instrumenter af små moduler med aRts-byggeren, og derefter bruge instrumenterne til at komponere eller spille musik. Syntese betyder ikke nødvendigtvis kun ren syntese, men der er moduler som kan bruges til at afspille samplinger. På denne måde kan aRts være både en samplingsenhed eller synthesizer, og eftersom den er fuldstændig modulær, er den meget let at udvide, meget let at eksperimentere med, kraftfuld og fleksibel.
Den centrale komponent i aRts som holder rede på hvilke programmer der er tilsluttet og hvordan midi-begivenheder skal sendes mellem dem er midi-håndteringen. For at se eller påvirke hvad den gør, startes artscontrol. Derefter vælges Vis+Vis midihåndtering i menuen.
Til venstre ser man Midi-inddata. Der er alle objekter som producerer MIDI-begivenheder på en liste, såsom en ekstern MIDI-port som sender data fra et tilsluttet MIDI-keyboard, en sequencer som spiller en sang og så videre. Til højre ser man Midi-udskrift. Der er alting som konsumerer MIDI-begivenheder på en liste, såsom en simuleret samplingsenhed (programmel), eller den eksterne MIDI-port som en hardware-samplingsenhed der er tilsluttet udenfor din maskine. Nye programmer, såsom en sequencer eller lignende, registrerer sig selv, så at listen ændres med tiden.
Du kan tilslutte indgange og udgange hvis du markerer en indgang i listen til venstre og en udgang til højre, og vælger Forbind med knappen nedenfor. Afbryd forbindelse virker på samme måde. Du ser hvad der er tilsluttet som små linjer mellem indgange og udgange, midt i vinduet. Bemærk at du kan tilslutte en indgang til mere end en udgang (og omvendt).
Programmer (såsom sequenceren Brahms) tilføjes af sig selv når de startes og fjernes fra listen når de afsluttes. Men du kan også tilføje nye enheder med menuen Tilføj.
Dette laver et nyt aRts-objekt som kommunikerer med en ekstern midi-port.
Eftersom eksterne midi-porte både kan sende og modtage data, så tilføjes både en indgang og udgang med dette valg. Med Linux®, skal du enten have en OSS-driver (eller OSS/Free, som følger med Linux®-kernen) eller en ALSA-driver installeret for dit lydkort, for at dette skal virke. Du bliver spurgt om enhedens navn. Dette er oftest /dev/midi
eller /dev/midi00
.
Hvis du har mere end en MIDI-enhed eller en MIDI-tilbagekoblingsenhed installeret, kan der dog være flere valgmuligheder. For at vise information om midi-porte, startes KDE's kontrolcenter, og Information+Lyd vælges.
Dette tilføjer en ny MIDI-udgang med et aRts synteseinstrument. Hvis du vælger menupunktet, kommer en dialog frem, som lader dig angive et instrument. Du kan oprette nye instrumenter med artsbuilder. Alle .arts
-filer med et navn som begynder med instrument_
vises her.
I virkeligheden er det rigtig let at komme i gang. Du behøver en version af Brahms som virker med KDE 2.1, som findes i kmusic
CVS-modulet. Der er også information om hvordan man får fat på Brahms på aRts' hjemmeside i afsnittet om download.
Når du har startet programmet, ses det i MIDI-håndteringen. Hvis du vil bruge syntese, så tilføj helt enkelt et MIDI-instrument med Tilføj+aRts syntese midi uddata.
Vælg et instrument (for eksempel organ2). Forbind dem ved brug af knappen Forbind. Derefter kan du begynde at komponere i Brahms, og resultatet syntetiseres med aRts.
Det er oftest en god ide at have artscontrol-vinduet åbent, og se at lydstyrken ikke er for høj (lydkvaliteten bliver dårligere når bjælkerne når den øvre grænse). Nu kan du begynde at skrive en ny aRts demo, og når du er klar kan du få den publiceret på aRts-project.org ;-).
midisend er et lille program som lader dig sende MIDI-begivenheder fra skallen. Det registreres som en klient ligesom alle andre programmer. Den enkleste måde at bruge det er at skrive
hvilket opnår cirka det samme som at tilføje en MIDI-port til artscontrol. (Ikke helt, fordi midisend kun sender begivenheder). Forskellen er at det er for eksempel er let at starte midisend på forskellige computere (og på den måde bruge netværkstransparens).%
midisend
-f
/dev/midi00
Det er også muligt at lade midisend sende data fra stdin
, hvilket du kan bruge til at sende data fra programmer som ikke understøtter aRts, sådan her:
%
program_som_sender_MIDI_begivenheder_til_standarduddata
| midisend-f
-
Den måde aRts udfører midi-syntese er følgende: Du har strukturer som har et antal indgange, hvor de henter frekvensen, hastigheden (lydstyrke) og en parameter som angiver om tasten stadigvæk er trykket ned. Strukturen skal nu syntetisere nøjagtigt den tone med den lydstyrke, og reagere på parameteren pressed (hvor pressed = 1 betyder at brugeren holder tasten nede og pressed = 0 betyder at brugeren har sluppet tasten).
Når MIDI-begivenheder ankommer, laver aRts nye strukturer for tonerne efter behov, angiver parametrene, og rydder dem væk når de er færdige.
For at oprette og bruge en sådan struktur, skal du gøre følgende:
For at komme i gang er den behageligste måde at åbne template_Instrument.arts
i aRts-builder.
Dette kan opnås ved at bruge -> og vælge i fildialogen. Dette laver en tom struktur med de nødvendige parametre, som du kun behøver “udfylde”.
For at håndtere parameteren "pressed", er det praktisk at bruge Synth_ENVELOPE_ADSR, eller hvis en wav-fil med tromme spilles, så spil den blot alligevel og ignorér parameteren.
Strukturen skal indikere hvornår den ikke behøves længere med uddata “done”. Hvis done er 1, antager aRts at strukturen kan slettes. Praktisk nok så sørger ADSR-envelopen for en parameter som angiver når den er færdig, så du behøver kun forbinde den til done-udgangen for strukturen.
Du bør omdøbe din struktur til et navn som begynder med instrument_
, såsom instrument_piano.arts
- du bør gemme filen under samme navn i mappen $
(hvor artsbuilder normalt gemmer filer). HOME
/arts/structures
Til sidst, når du har gemt den, kan du bruge den med artscontrol i MIDI-håndteringen.
Åh, og din struktur skal naturligvis afspille lyddata som den laver via venstre og højre udgang for strukturen, som siden afspilles med lydhåndteringen (du kan se dette i artscontrol), så du til slut kan høre lyden (eller efterbehandle den med lydeffekter).
En god måde at lære sig hvordan man laver instrumenter er at åbne et eksisterende instrument med Fil+Åbn eksempel og se hvordan det virker ;)
Instrumentkort er instrumenter som opfører sig forskelligt afhængig af tonehøjden, programmet, kanalen eller hastigheden. Man kan for eksempel oprette et piano med fem oktaver, og bruge en sampling for hver oktav (med tonehøjden forskudt tilsvarende). Dette lyder meget bedre end kun at bruge en sampling.
Man kan også bygge et trommekort, som spiller en særlig trommesampling for hver tast.
Til sidst er det meget nyttigt hvis man kombinerer helt forskellige lyde i et instrumentkort for forskellige programmer. På denne måde kan man bruge sin sequencer, eksterne keyboard eller en anden MIDI-kilde til at skifte mellem lyde uden at behøve at manipulere aRts under arbejdets gang.
Et godt eksempel på dette er arts_all
, som helt enkelt kombinerer alle instrumenter som levereres med aRts i et kort. På denne måde behøver man kun angive at dette “instrument” skal bruges en gang i artscontrol, og derefter kan man komponere en hel sang i en sequencer uden overhovedet at bryde sig om aRts. Brug for en anden lyd? Ændr helt enkelt programmet i sequenceren, og aRts sørger for en ny lyd.
Oprettelse af sådanne kort er ganske ligetil. Man behøver kun at oprette en tekstfil, og skrive regler som ser sådan her ud:
ON[ betingelser ...]
DO structure=en_struktur
.arts
Betingelserne kan være en eller flere af følgende:
pitch
Tonehøjden som spilles. Man bruger dette hvis man vil opdele instrumentet afhængig af tonhøjde. I vort oprindelige eksempel, skulle et piano som bruger forskellige samplinger for forskellige oktaver bruge dette som betingelser. Man kan angive en enkel tonehøjde, såsom
eller et interval af tonehøjder, såsom pitch
=62
. De mulige tonehøjder går mellem pitch
=60
-72
0
og 127
.
program
Programmet som er aktivt på kanalen som tonen afspilles i. Oftest lader sequenceren dig vælge “instrument” via programindstillingen. Enkle programmer eller intervaller er tilladte, dvs.
eller program
=3
. Mulige programmer går mellem program
=3
-6
0
og 127
.
channel
Kanalen som tonen sendes på. Enkle kanaler eller intervaller er tilladte, dvs.
eller channel
=0
. Mulige kanaler går mellem channel
=0
-8
0
og 15
.
velocity
Hastigheden (lydstyrken) som tonen har. Enkle hastigheder (hvem skulle ville bruge det?) eller intervaller er tilladte, dvs.
eller velocity
=127
. Mulige hastigheder går mellem velocity
=64
-127
0
og 127
.
Et fuldstændigt eksempel for et kort kan være (dette kommer fra den nuværende instrument_arts_all.arts-map
):
ON program=0 DO structure=instrument_tri.arts ON program=1 DO structure=instrument_organ2.arts ON program=2 DO structure=instrument_slide1.arts ON program=3 DO structure=instrument_square.arts ON program=4 DO structure=instrument_neworgan.arts ON program=5 DO structure=instrument_nokind.arts ON program=6 DO structure=instrument_full_square.arts ON program=7 DO structure=instrument_simple_sin.arts ON program=8 DO structure=instrument_simple_square.arts ON program=9 DO structure=instrument_simple_tri.arts ON program=10 DO structure=instrument_slide.arts ON program=11 pitch=60 DO structure=instrument_deepdrum.arts ON program=11 pitch=61 DO structure=instrument_chirpdrum.arts
Som du ser, vælges strukturen afhængig af programmet. For program 11, ses et “trommekort” (med to indgange), som vil spille en “deepdrum” med C-5 (tonehøjde = 60), og en “chirpdrum” med C#5 (tonehøjde = 61).
For at kortfiler automatisk skal ses i artscontrol som et valg for instrumenterne, skal de hedde instrument_
og enten være i din hjemmemappe i noget
.arts-map$
, eller i KDE-mappen i HOME
/arts/structures$
. Strukturer som bruges af kortene kan enten angives med en absolut søgesti, eller relativt til mappen som kortfilen findes i. KDEDIR
/share/apps/artsbuilder/examples
Udvidelse af arts_all map eller til og med at lave et fuldstændigt generelt MIDI-kort for aRts er en god idé for at gøre aRts lettere at bruge direkte som det er. Overvej gerne at bidrage med interessante instrumenter som du laver, så de kan inkluderes i fremtidige udgaver af aRts.
MCOP er standarden som aRts anvender til:
Kommunikation mellem objekter.
Netværkstransparens.
Beskrivelse af objektgrænseflader.
Sproguafhængighed.
En vigtig del af MCOP er grænsefladesbeskrivelsessproget, IDL, som bruges til at definere mange af aRts programmeringsgrænseflader og andre grænseflader på en sproguafhængig måde.
For at bruge en IDL-grænseflade fra C++, kompileres den med IDL oversætteren til C++ kode. Når du implementerer en grænseflade, afleder du fra skabelonklassen som IDL oversætteren har oprettet. Når man bruger en grænseflade gør man det med en omliggende skal. På denne måde kan MCOP bruge en protokol hvis objektet du taler med ikke er lokalt - man får netværkstransparens.
I dette kapitel er emnet at beskrive de grundlæggende funktioner i objektmodellen som er resultatet af at bruge MCOP, protokollen, hvordan MCOP bruges med C++ (sprogbindning), og så videre.
Meget af den service som varetages af aRts, såsom modulerne og lydserveren defineres i form af grænseflader. Grænseflader specificeres i et programsprogsuafhængig format: IDL.
Dette tillader at mange af implementeringsdetaljerne såsom formatet på multimediadatastrømmene, netværkstransparens og programsprogsuafhængig skjules for specifikationen af grænsefladen. Et værktøj, mcopidl, oversætter grænsefladedefinitionen til et specifikt programmeringssprog (for øjeblikket understøttes kun C++).
Værktøjet laver en skeletklasse med al standardkode og grundlæggende funktionalitet. Man afleder fra denne klassen for at implementere de funktioner man vil have.
Det IDL som bruges af aRts ligner det som bruges af CORBA og DCOM.
IDL-filer kan indholde:
C-stil #include-direktiv for andre IDL-filer.
Definitioner af nummereringstyper og struct-typer, som i C/C++.
Definitioner af grænseflade.
I IDL defineres grænseflade på en måde som minder meget om en C++ klasse eller C-struct, dog med visse begrænsninger. Som i C++, så kan grænseflader være underklasser til andre grænseflader med arv. Grænsefladedefinitioner kan indeholde tre ting: Strømme, egenskaber og metoder.
Strømme definerer multimediadata, en af de vigtigste komponenter i et modul. Strømme defineres med følgende format:
[ async ] in|out [ multi ] typ
stream navn
[ , navn
] ;
Strømme har en defineret retning i forhold til modulet, som angives af de nødvendige parametre in eller out. Typeargumentet definerer datatypen, som kan være en hvilken som helst af typerne for egenskaber som beskrives senere (ikke alle understøttes endnu). Mange moduler bruger strømtypen audio, som er et alias for float eftersom det er det interne dataformatet for lydstrømme. Flere strømme af samme type kan indgå i samme definition med navne adskilt med kommategn.
Strømme er normalt synkrone, hvilket betyder at de er kontinuerlige dataflows med en konstant hastighed, såsom PCM-lyd. Parameteren async angiver en asynkron strøm, som bruges til ikke-kontinuerlige datastrømme. Det almindeligste eksempel på en asynkron strøm er MIDI-meddelelser.
Nøgleordet multi, som kun er gyldigt for inddatastrømme, angiver at grænsefladen understøtter et variabelt antal indgange. Dette er nyttigt til at implementere enheder såsom en mixer som kan tage imod et hvilket som helst antal inddatastrømme.
Attributter er data som hører sammen med en udgave af en grænseflade. De deklareres som medlemsvariabler i C++, og kan bruges af en hvilken som helst af de primitive typer boolean, byte, long, string, eller float. Du kan også bruge selvdefinerede struct- eller nummereringstyper samt sekvenser af variabel størrelse med syntaksen sekvens<type>. Attributter kan valgfrit markeres som skrivebeskyttede.
Som i C++, kan metoder defineres i grænseflader. Metodeparametrene er begrænsede til samme typer som attributterne. Nøgleordet oneway angiver en metode som returnerer umiddelbart og køres asynkront.
Flere grænseflader for standardmoduler er allerede definerede for dig i aRts, såsom StereoEffect
, og SimpleSoundServer
.
Et enkelt eksempel på et modul taget fra aRts er modulet med konstant forsinkelse, som findes i filen kdemultimedia/arts/modules/artsmodules.idl
. Grænsefladedefinitionen angives nedenfor:
interface Synth_CDELAY : SynthModule { attribute float time; in audio stream invalue; out audio stream outvalue; };
Modulet arver SynthModule
. Den grænseflade, som findes i artsflow.idl
, definerer alle standardmetoder som implementeres i alle syntesemoduler.
CDELAY-effekten forsinker en stereolydstrøm med tidsværdien som angives som en decimaltal parameter. Grænsefladedefinitionen har en attribut af typen float til at opbevare forsinkelsesværdien. Den definerer to inddata- og to uddatastrømme (typisk for stereoeffekter). Ingen metoder kræves ud over de arvede.
Dette afsnit dækker en del yderligere emneområder som hører sammen med strømme.
Der er forskellige krav for hvordan et modul kan håndtere strømningen. For at illustrere dette, betragt følgende eksempel:
Skalering af et signal med en faktor to.
Udfører frekvenskonvertering af samplinger.
Dekomprimering af et runlength-kodet signal.
Læs MIDI-begivenheder fra /dev/midi00
og indsæt dem i en strøm.
Det første tilfælde er det enkleste: når modulet modtager 200 inddatasamplinger producerer det 200 uddatasamplinger. Det producerer kun uddata når det får inddata.
Det andet tilfælde producerer forskellige antal uddatasamplinger når det får 200 inddatasamplinger. Det afhænger af hvilken konvertering som udføres, men antallet er kendt i forvejen.
Det tredje tilfælde er endnu værre. Fra begyndelsen kan man ikke engang gætte hvor meget data som laves af 200 inddata byte (formodentlig meget mere end 200 byte, men...).
Det sidste tilfælde er et modul som aktiveres af sig selv, og sommetider laver data.
I aRts-0.3.4, håndteredes kun strømme af den første type, og de fleste ting virkede godt. Dette er formodentlig hvad du mest behøver når du skriver moduler som behandler lyd. Problemerne med de andre, mere komplekse slags strømme er, at de er svære at programmere, og at man for det meste ikke behøver funktionerne. Dette er grunden til at vi gør dette med to forskellige slags strømtyper: synkrone og asynkrone.
Synkrone strømme har følgende egenskaber:
Moduler skal kunne beregne data af en hvilken som helst længde, givet tilstrækkelig meget inddata.
Alle strømme har samme samplingsrate.
Funktionen calculateBlock()
kaldes når tilstrækkeligt med data er tilgængelig, og modulet kan stole på at pegerne angiver data.
Der er ingen allokering eller afallokering der skal gøres.
Asynkrone strømme, på den anden side, opfører sig sådan her:
Moduler kan producere data ind imellem, eller med varierende samplingsfrekvens, eller kun hvis de får inddata fra en fil. De skal ikke følge reglen “skal kunne håndtere forespørgsler af en hvilken som helst størrelse”.
Asynkrone strømme for et modul kan have helt forskellige samplingsrater.
Udgående strømme: der er særlige funktioner til at allokere pakker, til at sende pakker - og en valgfri mekanisme til at spørge efter data som fortæller når mere data skal laves.
Indkommende strømme: et kald sendes når en ny pakke modtages. Man skal fortælle når man er færdig med at behandle al data i den pakke, og dette må ikke ske med det samme (man kan fortælle om det når som helst senere, og hvis alle har behandlet en pakke, bliver den frigjort/genbrugt).
Når strømme deklareres, bruges nøgleordet “async” til at angive at strømmen skal være asynkron. Så antag for eksempel at du vil konvertere en asynkron strøm af byte til en synkron strøm af samplinger. Grænsefladen ville så kunne se sådan her ud:
interface ByteStreamToAudio : SynthModule { async in byte stream inddata; // den asynkrone inddatasampling out audio stream left,right; // de synkrone uddatasamplinger };
Antag at du har bestemt dig for at skrive et modul som laver asynkron lyd. Dens grænseflade kunne se sådan her ud:
interface SomeModule : SynthModule { async out byte stream outdata; };
Hvordan sender man data? Den første metode kaldes “trykleverance”. Med asynkrone strømme sender man data som pakker. Det betyder at individuelle pakker sendes som i eksemplet ovenfor. Den virkelige proces er: allokér en pakke, fyld den, send den.
Her følger det i form af kode. Først allokerer vi en pakke:
DataPacket<mcopbyte> *packet = outdata.allocPacket(100);
Så fylder vi den:
// typekonvertering så fgets får en (char *) peger char *data = (char *)packet->contents; // som du ser, kan du krympe pakkestørrelsen efter allokeringen // hvis du vil if(fgets(data,100,stdin)) packet->size = strlen(data); else packet->size = 0;
Nu sender vi den:
packet->send();
Dette er meget enkelt, men hvis vi vil sende pakker nøjagtigt så hurtigt som modtageren kan håndtere dem, behøves en anden måde, metoden med “trækleverance”. Man beder om at sende pakker så hurtigt som modtageren er klar til at behandle dem. Man begynder med en vis mængde pakker som sendes. Mens modtageren behandler pakke efter pakke, begynder man at fylde dem igen med friske data, og sende dem igen.
Du starter det ved at kalde setPull. For eksempel:
outdata.setPull(8, 1024);
Dette betyder at du vil sende pakke via uddata. Du vil begynde med at sende 8 pakker på én gang, og når modtageren behandler nogle af dem, vil du fylde dem op igen.
Derefter behøver du at implementere en metode som fylder pakken, som kunne se sådan her ud:
void request_outdata(DataPacket<mcopbyte> *packet) { packet->size = 1024; // skal ikke være mere end 1024 for(int i = 0;i < 1024; i++) packet->contents[i] = (mcopbyte)'A'; packet->send(); }
Det er alt. Når du ikke har flere data, kan du begynde at sende pakker med størrelsen nul, som stopper trækleverancerne.
Bemærk at det er væsentligt at give metoden nøjagtigt navnet request_
. strømnavn
Vi beskrev netop at sende data. At modtage data er meget enklere. Antag at du har et enkelt filter, ToLower, som helt enkelt konverterer alle bogstaver til små:
interface ToLower { async in byte stream inddata; async out byte stream uddata; };
Dette er virkeligt enkelt at implementere. Her er hele implementationen:
class ToLower_impl : public ToLower_skel { public: void process_inddata(DataPacket<mcopbyte> *inpacket) { DataPacket<mcopbyte> *outpacket = ouddata.allocPacket(inpacket->size); // lav om til små bogstaver char *instring = (char *)inpacket->contents; char *outstring = (char *)outpacket->contents; for(int i=0;i<inpacket->size;i++) outstring[i] = tolower(instring[i]); inpacket->processed(); outpacket->send(); } }; REGISTER_IMPLEMENTATION(ToLower_impl);
Igen er det væsentligt at give metoden navnet process_
. strømnavn
Som du kan se, så får du et kald til en funktion for hver pakke som ankommer (process_indata
i vort tilfælde). Du skal kalde metoden processed()
for en pakke for at angive at du har behandlet den.
Her er et implementeringstip: Hvis det tager lang tid at behandle data (dvs. hvis du skal vente på udskrift til lydkortet eller noget sådant), så kald ikke processed med det samme, men opbevar hele datapakken og kald kun processed når du virkelig har behandlet pakken. På denne måde, har afsenderne en chance for at vide hvor lang tid det virkelig tager at udføre arbejdet.
Eftersom synkronisering ikke er så behagelig med asynkrone strømme, skal man bruge synkrone strømme så ofte som muligt, og kun asynkrone hvis det er nødvendigt.
Antag at du har to objekter, for eksempel en AudioProducer og en AudioConsumer. AudioProducer har en uddatastrøm og AudioConsumer har en inddatastrøm. Hver gang du vil forbinde dem, bruger du disse to strømme. Den første brug af defaulting er at lade dig oprette forbindelsen uden at angive portene i dette tilfælde.
Antag nu at de to objekter ovenfor kan håndtere stereo, og begge har en “venstre” og “højre” port. Du vil stadigvæk skulle kunne koble dem sammen lige så let som tidligere. Men hvordan kan forbindelsesystemet vide hvilken udgang som skal kobles til hvilken indgang? Det har ingen måde at koble strømmene rigtigt sammen. Defaulting bruges så til at angive flere strømme med en vis rækkefølge. På den måde, hvis du forbinder et objekt med to standard uddatastrømme til et andet med to standard inddatastrømme, behøver du ikke angive portene, og forbindelserne gøres rigtigt.
Dette er naturligvis ikke begrænset til stereo. Hvilket som helst antal strømme kan gøres standard hvis det behøves, og forbindelsesfunktionen kontrollerer at antallet af standarder for to objekter passer sammen (med de angivne retninger) hvis du ikke angiver portene som skal bruges.
Syntaksen er den følgende: I IDL kan du bruge nøgleordet default i strømdeklarationen, eller på en enkelt linje. For eksempel:
interface TwoToOneMixer { default in audio stream input1, input2; out audio stream output; };
I dette eksempel kommer objektet til at forvente at dets to inddataporte skal forbindes som standard. Rækkefølgen er den som angives på linjen, så et objekt som dette:
interface DualNoiseGenerator { out audio stream bzzt, couic; default couic, bzzt; };
laver automatisk en forbindelse fra “couic” til “input1”, og “bzzt” til “input2” Bemærk at eftersom der kun er én udgang for mikseren, kommer den til at være standard i dette tilfælde (se nedenfor). Syntaksen som bruges i støjgeneratoren er nyttig til for at angive en anden rækkefølge end i deklarationen, eller til kun at vælge nogle få porte som standard. Retningen på portene på denne linje slås op af mcopidl, så angiv dem ikke. Du kan til og med blande ind- og udporte på en sådan linje, kun rækkefølgen spiller en rolle.
Der er nogle regler som følges når arv bruges:
Hvis en standardliste angives i IDL så skal den bruges. En forælders port kan også indgå i listen, hvad enten de var standard forælderen eller ej.
Ellers arves forældrenes standarder. Rækkefølgen er forælder1 forvalg1, forælder1 forvalg2..., forælder2 forvalg1... Hvis der er en fælles forfader som bruger to forældregrene, laves en sammenfletning som ligner “virtual public” ved standardens første plads i listen.
Hvis der stadigvæk ikke er nogen standard og en eneste strøm i en vis retning, så bruges den som standard for den retning.
Ændringsmeddelelser for attributter er en måde at vide når en attribut ændres. De kan i en vis udstrækning sammenlignes med et Qt™- eller Gtk-signal eller slot. Hvis du for eksempel har et grafisk element, en skyder, som indstiller et tal mellem 0 og 100, har du oftest et objekt som gør noget med tallet (det kan for eksempel kontrollere lydstyrken på et lydsignal). Så vil du gerne at så snart skyderen flyttes skal objektet som indsteller lydstyrken underrettes. En forbindelse mellem en sender og modtagar.
MCOP håndterer dette ved at kunne give meddelelser når attributter ændres. Alt som deklareres som “attribute” i IDL kan afgive sådanne ændringsmeddelelser, og skal gøre det, nårsomhelst de ændres. Alt som deklareres som “attribute” kan også modtage sådanne ændringsmeddelelser. Så hvis du for eksempel har to IDL-grænseflader såsom disse her:
interface Slider { attribute long min,max; attribute long position; }; interface VolumeControl : Arts::StereoEffect { attribute long volume; // 0..100 };
kan du forbinde dem med ændringsmeddelelser. Det virker med det normale flydesystems forbindelsesoperation. I dette tilfælde, ser C++ koden som forbinder de to objekter sådan her ud
#include <connect.h> using namespace Arts; [...] connect(slider,"position_changed",volumeControl,"volume");
Som du kan se, tilbyder hver egenskab to forskellige strømme, en for at sende ændringsmeddelelser, som hedder
, og en til at modtage ændringsmeddelelser, som hedder attributnavn
_changedattributnavn
.
Det er vigtigt at vide at ændringsmeddelelser og asynkrone strømme passer sammen. De er også netværkstransparente. Så du kan forbinde en flyttelsesegenskabs ændringsmeddelelse for en grafisk komponent til en asynkron strøm for en syntesemodul som kører på en anden maskine. Dette betyder naturligvis også at ændringsmeddelelser ikke er synkrone, hvilket betyder at det kan tage tid inden de virkelig modtages efter at du har sendt dem.
Når du implementerer objekter som har egenskaber, skal du sende ændringsmeddelelser så snart en egenskab ændres. Koden til at gøre dette ser sådan her ud:
void KPoti_impl::value(float newValue) { if(newValue != _value) { _value = newValue; value_changed(newValue); // <- send ændringsmeddelelse } }
Det anbefales varmt at bruge kode som denne for alle objekter som du implementerer, så ændringsmeddelelser kan bruges af andre. Du bør dog undgå at sende meddelelser for ofte, så hvis du laver signalbehandling er det formodentlig bedst at holde styr på hvornår du sendte din seneste meddelelse, så du ikke sender en for hver sampling.
Det vil være særligt nyttigt at bruge ændringsmeddelelser sammen med målere (objekter som for eksempel visualiserer lyddata), grafiske grænseflader, kontrolelementer og monitorer. Kode som bruger dette findes i kdelibs/arts/tests
, og i den eksperimentelle artsgui-implementation, som du finder i kdemultimedia/arts/gui
.
.mcoprc
Filen .mcoprc
(i hver brugers hjemmemappe) kan bruges til at indstille MCOP på nogle måder. For øjeblikket er følgende muligt:
Navnet på en grænseflade som skal bruges til global kommunikation. Global kommunikation bruges til at finde andre objekter og hente den hemmelige cookie. Alle MCOP klienter/servere som skal kunne kommunikere med hinanden skal have et GlobalComm-objekt for at kunne dele information. For øjeblikket er de mulige værdier “Arts::TmpGlobalComm” for at kommunikere via mappen /tmp/mcop-
(som kun virker på den lokale maskine) og “Arts::X11GlobalComm” for at kommunikere via rootvinduesegenskaberne på X11-serveren. brugernavn
Angiver hvor handlerinformation skal findes. Du kan angive mere end en mappe her, og adskille dem med kommategn.
Angiver fra hvilke mapper udvidelser (i form af delte biblioteker) indlæses. Flere værdier kan adskilles med kommategn.
Et eksempel som bruger alt det ovenstående er:
# $HOME/.mcoprc file GlobalComm=Arts::X11GlobalComm # hvis du er udvikler kan det være bekvemt at tilføje et mappe i din # hjemmemappe til TraderPath/ExtensionPath søgestierne for at kunne tilføje # til komponenter uden at installere dem TraderPath="/opt/kde2/lib/mcop","/home/joe/mcopdevel/mcop" ExtensionPath="/opt/kde2/lib","/home/joe/mcopdevel/lib"
Hvis du har brugt CORBA tidligere, vil du kunne mærke at MCOP er meget lig. I virkeligheden så brugte aRts CORBA inden version 0.4.
Den grundlæggende idé med CORBA er den samme: man implementerer objekter (komponenter). Ved at bruge funktionerne i MCOP er objekter ikke kun tilgængelige som normale klasser fra samme proces (via standard C++ teknikker), men de er også transparent tilgængelige for en fjernserver. For at dette skal virke, er det første du skal gøre at specificere grænsefladen for dine objekter i en IDL-fil, præcis som for CORBA-IDL. Der er kun et fåtal af forskelle.
I MCOP er der ingen “in” og “out” parametre for metodekald. Parametre er altid indgående, og returværdien er altid udgående, hvilket betyder at grænsefladen:
// CORBA idl interface Account { void deposit( in long amount ); void withdraw( in long amount ); long balance(); };
skrives som
// MCOP idl interface Account { void deposit( long amount ); void withdraw( long amount ); long balance(); };
i MCOP.
Der er ingen støtte for undtagelser. MCOP har ikke undtagelser, den bruger noget andet til fejlhåndtering.
Der er ingen union-typer og ingen typedefinitioner. Jeg ved ikke om det er en virkelig svaghed, noget man desperat behøver for at overleve.
Der er ingen understøttelse for at sende grænseflader eller objektreferencer
Du deklarerer sekvenser som “sequencetyp
” i MCOP. Der er intet behov for en typedefinition. For eksempel, i stedet for:
// CORBA idl struct Line { long x1,y1,x2,y2; }; typedef sequence<Line> LineSeq; interface Plotter { void draw(in LineSeq lines); };
ville du skrive
// MCOP idl struct Line { long x1,y1,x2,y2; }; interface Plotter { void draw(sequence<Line> lines); };
Du kan deklarere strømme, som senere behandles af aRts' skelet. Strømme deklareres på en måde som ligner egenskaber. For eksempel:
// MCOP idl interface Synth_ADD : SynthModule { in audio stream signal1,signal2; out audio stream outvalue; };
Dette betyder at dit objekt vil acceptere to indkommende synkrone lydstrømme som kaldes signal1 og signal2. Synkron betyder at de er strømme som leverer x samplinger pr sekund (eller anden tid), så skemalæggeren altid garanterer at du får en balanceret mængde inddata (f.eks. 200 samplinger af signal1 er der og 200 samplinger af signal2 er der). Du garanterer at hvis dit objekt kaldes med disse 200 samplinger af signal1 + signal2, så kan det producere præcis 200 samplinger uddata.
Dette adskiller sig fra CORBA i hovedsagen hvad angår:
Strenge bruger C++ STL-klassen string
. Når de opbevares i sekvenser, opbevares de “enkelt”, hvilket betyder at de anses for at være en primitiv type. Derfor behøver de at blive kopieret.
long er enkle long (forventes at være 32 bit).
Sekvenser bruger C++ STL-klassen vector
.
Strukturer afledes alle fra MCOP klassen Type
, og genereres af MCOP IDL-oversætteren. Når de opbevares i sekvenser, opbevares de ikke “enkelt”, men som pegere, eftersom der ellers ville opstå for meget kopiering.
Efter at have sendt dem gennem IDL-oversætteren, skal du aflede fra klassen _skel
. Antag for eksempel at du har defineret grænsefladen sådan her:
// MCOP idl: hello.idl interface Hello { void hello(string s); string concat(string s1, string s2); long sum2(long a, long b); };
Du sender det gennem IDL-oversætteren ved at kalde mcopidl
, som derefter laver hello.idl
hello.cc
og hello.h
. For at implementere dette, behøver du en C++ klasse som arver skelettet:
// C++ deklarationsfil - indsæt hello.h et sted class Hello_impl : virtual public Hello_skel { public: void hello(const string& s); string concat(const string& s1, const string& s2); long sum2(long a, long b); };
Til sidst skal du implementere metoderne som almindelig C++.
// C++ implementeringsfil // som du ser sendes strenge som konstante strengreferencer void Hello_impl::hello(const string& s) { printf("Hello '%s'!\n",s.c_str()); } // når de er en returværdi sendes de som "normale" strenge string Hello_impl::concat(const string& s1, const string& s2) { return s1+s2; } long Hello_impl::sum2(long a, long b) { return a+b; }
Når du har gjort dette, har du et objekt som kan kommunikere med MCOP. Opret kun et (med de normale C++ faciliteter til at oprette et objekt):
Hello_impl server;
Og så snart du giver nogen referencen
string reference = server._toString(); printf("%s\n",reference.c_str());
og gå til MCOP:s venteløkke
Dispatcher::the()->run();
har alle adgang til objektet med
// denne kode kan køre hvorsomhelst - ikke nødvendigtvis i samme proces // (den kan også køre på en anden maskine/arkitektur) Hello *h = Hello::_fromString([objektreferencen som blev udskrevet ovenifor]);
og kalde-metoder:
if(h) h->hello("test"); else printf("Adgang mislykkedes?\n");
Eftersom en MCOP-server lytter på en TCP-port, kan potentielt set alle (hvis du er forbundet til internettet) forsøge at forbinde til en MCOP-tjeneste. Derfor er det vigtigt at identificere klienter. MCOP bruger md5-auth protokollen.
Protokollen md5-auth gør følgende for at sikre at kun udvalgte (pålidelige) klienter kan forbinde til en server:
Den antager at du kan give hver klient en hemmelig cookie.
Hver gang en klient forbindes, verificerer den at denne klient kender til den hemmelige cookie, uden virkelig at sende den (ikke engang på en form som nogen som lytter på netværkstrafik kunne finde ud af).
For at give hver klient den hemmelige cookie, putter MCOP den (normalt) i mcop
mappen (i /tmp/mcop-
). Du kan naturligvis kopiere den til andre maskiner. Men hvis du gør det, så brug en sikker overførselsmekanisme, såsom scp (fra ssh). USER
/secret-cookie
Godkendelsen af klienter anvender følgende skridt:
[SERVER] opret en ny (tilfældig) cookie R
[SERVER] send den til klienten
[KLIENT] læs den "hemmelige cookie" S fra en fil
[KLIENT] bland cookierne R og S for at oprette en blandet cookie M med MD5-algoritmen
[KLIENT] send M til serveren
[SERVER] verificerer at blanding af R og S giver præcis samme cookie som cookien M som blev modtaget fra klienten. Hvis dette stemmer er godkendelsen lykkedes.
Denne algoritme skulle være sikker, under forudsætning af at
De hemmelige og tilfældige cookier er “tilfældige nok” og
MD5-algoritmen ikke tillader at “originalteksten” kan afgøres, det vil sige den hemmelige cookie S og den tilfældige cookie R (som er kendt alligevel), fra den blandede cookie M.
MCOP-protokollen starter hver ny forbindelse med en godkendelsesprocess. Basalt ser den sådan her ud:
Serveren sender meddelelsen ServerHello, som beskriver de kendte godkendelsesprotokoller.
Klienten sender meddelelsen ClientHello, som indeholder godkendelsesinformation.
Serveren sender meddelelsen AuthAccept.
For at se at sikkerheden virkelig virker, bør vi kigge på hvordan meddelelser behandles for ikke godkendte forbindelser:
Inden godkendelsen er lykkedes, tager serveren ikke imod andre meddelelser fra forbindelsen. I stedet, hvis serveren for eksempel forventer sig meddelelsen “ClientHello”, og får meddelelsen mcopInvocation, så lukker den ned for forbindelsen.
Hvis klienten ikke sender en gyldig MCOP-meddelelse overhovedet (ingen magisk værdi MCOP i meddelelseshovedet) i godkendelsesfasen, uden noget andet, så lukkes der for forbindelsen.
Hvis klienten forsøger at sende en meget stor meddelelse (> 4096 byte) under godkendelsesfasen, kappes meddelelsesstørrelsen ned til 0 byte, hvilket gør at den ikke accepteres til godkendelse. Dette er der for at forhindre at ikke godkendte klienter sender f.eks. en 100 Mbyte meddelelse, som ville blive modtaget og kunne forårsage at serveren løber tør for hukommelse.
Hvis klienten sender en fejlagtig ClientHello meddelelse (en hvor afkodningen mislykkes), så lukkes der for forbindelsen.
Hvis klienten ikke sender noget i det hele taget, skal en tidsgrænse overskrides (endnu ikke implementeret).
Den har begrebsmæssige ligheder med CORBA, men er beregnet til at kunne udvides på alle måder som kræves for multimediaoperationer i realtid.
Den sørger for en multimediaobjektmodel, som både kan bruges til kommunikation mellem komponenter i et adresserum (en proces), og mellem komponenter som findes i forskellige tråde, processer eller på forskellige værtsmaskiner.
Totalt set, vil den blive konstrueret til ekstremt høj ydelse (så alt skal optimeres for at være voldsomt hurtigt), passende for meget kommunikationsintensive multimedieprogrammer. At for eksempel sende video rundt er en af tilpasningerne for MCOP, hvor de fleste CORBA-implementationer ville gå i knæ.
Grænsefladedefinitionerne kan håndtere følgende selv:
Kontinuerlige datastrømme (såsom lyddata).
Begivenhedsstrømme af data (såsom MIDI-begivenheder).
Rigtig referenceregning.
og de vigtigste CORBA-kneb, såsom
Synkrone metodekald.
Asynkrone metodekald.
Konstruere brugerdefinerede datatyper.
Multipel arv.
Sende objektreferencer.
Konstruktionsmål/idéer:
Kodning skal være enkelt at implementere.
Afkodning kræver at modtageren véd hvilken type som skal afkodes.
Modtageren forventes at bruge al information, så mulighed for at hoppe over data findes kun i protokollen i det omfang at:
Hvis man ved at en blok af data bliver modtaget, behøver man ikke kigge på hver del efter en slutmarkør.
Hvis man ved at en streng vil blive modtaget, behøver man ikke læse til en null-byte for at finde ud af dens længde ved afkodning, men,
Hvis man véd at en sekvens af strenge bliver modtaget skal man kigge på længden for hver af dem for at finde ud af slutningen af sekvensen, eftersom strenge har variabel længde. Men hvis strengene bruges til noget nyttigt, skal det gøres i alle tilfælde, så dette er ikke noget tab.
Så lidt ekstra omkostning som muligt.
Kodningen af de forskellige typer vises i tabellen nedenfor:
Type | Kodningsproces | Resultat |
---|---|---|
void | typen void kodes ved at udelade den, så ingenting skrives til strømmen for den. | |
long | kodes som fire byte, med den mest signifikante først, så tallet 10001025 (som er 0x989a81) vil blive kodet som: | 0x00 0x98 0x9a 0x81 |
nummereringstyper | kodes som long | |
byte | kodes som en enkelt byte, så 0x42 vil blive kodet som: | 0x42 |
streng | kodes som en long, som indeholder længden på den efterfølgende streng, og derefter sekvensen af tegn. Strenge skal slutte med en null-byte (som indgår i længdeberegningen). Vigtigtinklusive den sidste 0 byte i længderegningen! “hello” ville blive kodet som: | 0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00 |
boolean | kodes som en byte, som indeholder 0 hvis false eller 1 hvis true, så at den booleske værdi true kodes som: | 0x01 |
float | kodes med fire-byte IEEE754 repræsentationen, detaljeret dokumentation om hvordan IEEE fungerer findes her: http://twister.ou.edu/workshop.docs/ common-tools/ numerical_comp_guide/ ncg_math.doc.html og her: http://java.sun.com/docs/books/ vmspec/ 2nd-edition/ html/ Overview.doc.html. Så værdien 2,15 ville blive kodet som: | 0x9a 0x99 0x09 0x40 |
struct | En struktur kodes ved at kode dens indhold. Der kræves ingen yderligere præfiks eller suffiks, så strukturen struct test { string name; // som er "hello" long value; // som er 10001025 (0x989a81) }; ville blive kodet som: |
0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x98 0x9a 0x81 |
sekvens | En sekvens kodes ved at lave en liste over antallet af elementer som følger, og derefter kode elementerne en efter en. Så en sekvens med tre "long a", med a[0] = 0x12345678, a[1] = 0x01 og a[2] = 0x42 ville blive kodet som |
0x00 0x00 0x00 0x03 0x12 0x34 0x56 0x78 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x42 |
Hvis du har brug for at henvise til en type, angives alle primitive typer med navnene som findes ovenfor. Strukturer og nummereringstyper får deres egne navne (som Header). Sekvenser benævnes som *normal type
, så en sekvens af long er “*long” og en sekvens af Header struct'er er “*Header”.
Formatet på MCOP's meddelelseshovede defineres af denne struktur:
struct Header { long magic; // værdien 0x4d434f50, som kodes som MCOP long messageLength; long messageType; };
De mulige meddelelsetyper er for øjeblikket
mcopServerHello = 1 mcopClientHello = 2 mcopAuthAccept = 3 mcopInvocation = 4 mcopReturn = 5 mcopOnewayInvocation = 6
Lidt information om MCOP's meddelelseshåndtering:
Hver meddelelse begynder med et hoved.
Visse meddelelsestyper skal ignoreres af serveren, indtil godkendelsen er færdig.
Efter at hovedet er modtaget, kan protokolhåndteringen (forbindelsen) tage imod meddelelsen i sin helhed, uden at kigge på indholdet.
Meddelelseslængden i hovedet er naturligvis sommetider redundant, hvilket gør at metoden ikke altid er minimal med hensyn til antal byte.
Dette giver dog en enkel (og hurtig) implementation for behandling af meddelelser som ikke blokerer. Ved hjælp af hovedet kan meddelelser modtages af protokolhåndteringsklasser i baggrunden (uden blokering), hvis der er mange forbindelser til serveren kan alle behandles parallelt. Man behøver ikke kigge på meddelelsens indhold for at tage imod en meddelelse (og afgøre når man er klar), kun på hovedet, så koden for dette er ganske enkel.
Når en meddelelse er på plads, kan den afkodes og behandles i et enkelt skridt, uden at bryde sig om tilfælde hvor al data måske ikke er modtaget (eftersom meddelelseslængden garanterer at alt er på plads).
For at kalde en fjernmetode, skal man sende følgende struktur i en MCOP-meddelelse med messageType = 1 (mcopInvocation):
struct Invocation { long objectID; long methodID; long requestID; };
derefter sendes parametrene som en struktur, f.eks. hvis man kalder metoden "string concat(string s1, string s2)", sendes strukturen
struct InvocationBody { string s1; string s2; };
hvis metoden deklareredes som envejs, hvilket betyder asynkront uden returværdi, er det alt. Ellers får man en meddelelse som svar med messageType = 2 (mcopReturn).
struct ReturnCode { long requestID; <resulttype> result; };
hvor <resulttype> er typen for resultatet. Eftersom void-typer udelades ved kodning, kan man også kun angive requestID hvis man returnerer fra en void-metode.
Så vor "string concat(string s1, string s2)" ville give en returkode som
struct ReturnCode { long requestID; string result; };
For at lave kald, skal man kende til metoderne som et objekt understøtter. For at gøre dette, er methodID 0, 1, 2 og 3 hårdkodede for visse funktioner. Det vil sige
long _lookupMethod(MethodDef methodDef); // metode-id altid 0 string _interfaceName(); // metod-id altid 1 InterfaceDef _queryInterface(string name); // metodie-id altid 2 TypeDef _queryType(string name); // metodie-id altid 3
for at læse dette, behøver du naturligvis også
struct MethodDef { string methodName; string type; long flags; // nu sat til 0 (kræves for strømme) sequence<ParamDef> signature; }; struct ParamDef { string name; long typeCode; };
parameterfeltet indeholder typekomponenter som angiver typerne for parametrene. Typen for returkoden angives i typefeltet for MethodDef.
Hvis man skal være streng, er det kun metoderne _lookupMethod()
og _interfaceName()
som varierer fra objekt til objekt, mens _queryInterface()
og _queryType()
altid er ens.
Hvad er en sådan her methodID? Hvis man laver et MCOP-kald, forventes man at sende nummeret for metoden som kaldes. Årsagen til dette er at numre kan behandles meget hurtigere end strenge når en MCOP-forespørgsel køres.
Så hvordan skaffer man sig et sådan nummer? Hvis man kender til signaturen for metoden, dvs. en MethodDef som beskriver den (som indeholder navn, type, parameternavn, parametertyper og lignende), så kan man sende den til _lookupMethod for objektet hvor man vil kalde en metode. Eftersom _lookupMethod er hårdkodet til methodID 0, skulle det ikke være nogen problem at gøre dette.
Hvis man på den anden side ikke kender til metodens signatur, kan man finde hvilke metoder der understøttes ved at bruge _interfaceName, _queryInterface og _queryType.
Eftersom KDE droppede CORBA fuldstændigt, og i stedet bruger DCOP overalt, opstår naturligvis spørgsmålet hvorfor ikke aRts også gør det. Trods alt findes DCOP-understøttelse i KApplication
, er godt vedligeholdt, forventes at integreres udmærket med libICE, og alt muligt andet.
Eftersom der (muligvis) vil være mange som spørger om det virkelig er nødvendigt at have MCOP foruden DCOP, så er svaret her. Misforstå mig ikke, jeg forsøger ikke at sige at “DCOP er dårlig”. Jeg forsøger kun at sige at “DCOP ikke er den rette løsning for aRts” (mens det er en god løsning for andre ting).
Først skal man forstå nøjagtigt hvad DCOP blev skrevet for. Oprettet på to dage under mødet KDE-TWO, var det hensigten at være så enkel som muligt, en virkelig “letvægts” kommunikationsprotokol. I særdeleshed udelod implementationen alt som kunne indebære kompleksitet, for eksempel et fuldstændigt begreb om hvordan datatyper skal kodes.
Selvom DCOP ikke bryder sig om visse ting (som hvordan man sender en streng på en netværkstransparent måde), skal dette gøres. Så alt som DCOP ikke gør overlades til Qt™ i KDE-programmerne som bruger DCOP i dag. Dette er i hovedsagen typehåndtering (som bruger Qt™'s serialiseringsoperator).
Så DCOP er en minimal protokol som gør det helt muligt for KDE-programmer at sende enkle meddelelser såsom “åbn et vindue som peger på http://www.kde.org” eller “dine indstillinger er ændrede”. Inde i aRts fokuseres imidlertid på andre ting.
Idéen er at små plugin i aRts skal kommunikere med sådanne datastrukturer som “midi-begivenheder” og “sangpositionspegere” og “flydegrafer”.
Dette er komplekse datatyper, som skal sendes mellem forskellige objekter, og håndteres som strømme, eller parametre. MCOP sørger for et typebegreb til at definere komplekse datatyper ud fra enklere (ligesom struct'er og felter i C++). DCOP bryder sig ikke om typer i det hele taget, så dette problem skulle overlades til programmøren, såsom at skrive C++ klasser for typerne, og sikre sig at de kan serialisere på en rigtig måde (for eksempel understøttelse for Qt™'s serialiseringsoperator).
Men på den måde ville de ikke være tilgængelige for noget andet end direkte kodning i C++. I særdeleshed ville man ikke kunne oprette et scriptsprog som ville kunne kende til alle typer som et plugin kan gøre tilgængelige, eftersom de ikke ville være selvbeskrivende.
Samme argument gælder også grænseflader i stor udstrækning. DCOP-objekter gør ikke deres forhold, arvhierarkier, osv. tilgængelige. Hvis man ville skrive en objektbladrer som ville vise “hvilke egenskaber har dette objekt”, ville det mislykkes.
Selvom Matthias fortalte mig at man har en speciel funktion “functions” for hvert objekt som informerer om hvilke metoder som objektet understøtter, udelader dette ting såsom egenskaber, strømme og arvrelationer.
Dette gør at programmer som aRts-builder ikke ville fungere overhovedet. Men husk, det var ikke meningen at DCOP skulle være en objektmodel (eftersom Qt™ allerede har en med moc og lignende), heller ikke være noget som CORBA, men sørge for kommunikation mellem programmer.
Årsagen til at MCOP overhovedet findes er at den skal virke godt med strømme mellem objekter. aRts bruger mange små plugin, som forbindes med strømme. CORBA-versionen af aRts var tvunget til at introducere en besværlig opdeling mellem “SynthModule-objekt”, som var de interne arbejdsmoduler som oprettede strømme, og “CORBA-grænseflade”, som var noget eksternt.
Meget kode bekymrede sig om at opnå at vekselvirkningen mellem “SynthModule-objekt” og “CORBA-grænsefladen” så naturlig ud, men den gjorde det aldrig, eftersom CORBA ikke kendte til noget om strømme overhovedet. MCOP gør det. Kig på koden (noget i stil med simplesoundserver_impl.cc
). Adskilligt bedre! Strømme kan deklareres i modulernes grænseflade, og implementeres på en måde som ser naturlig ud.
Man kan ikke benægte det. En af grunderne til at jeg skrev MCOP var hurtighed. Her er et argument for at MCOP definitivt vil være hurtigere end DCOP (selv uden at angive tal).
Et kald i MCOP vil have et hoved med seks “long”. Det vil sige:
magisk værdi “MCOP”
meddelelsetype (kald)
størrelse på forespørgsler i byte
ID for forespørgsler
ID for målobjektet
ID for målmetoden
Efter dette følger parametrene. Bemærk at afkode dette går ekstremt hurtigt. Man kan bruge tabelopslag for at finde afkodningsfunktionen for objektet og metoden, hvilket betyder at kompleksiteten er O(1) [det tager lige lang tid uafhængig af hvor mange objekter som findes, og hvor mange funktioner som findes].
Hvis dette sammenlignes med DCOP, ser du at der er i det mindste
en streng for målobjektet - noget i stil med “myCalculator”
en streng som ligner “addNumber(int,int)” for at angive metoden
yderligere protokolinformation tilføjet af libICE, og andre DCOP-specifikke ting som jeg ikke kender til
Disse er meget sværere at afkode, eftersom man skal tolke strengen, lede efter funktionen, osv.
Med DCOP sendes alle kald gennem en server (DCOPServer). Det betyder at håndteringen af et synkront kald ser sådan her ud:
Klientprocessen sender kald.
DCOPserver (manden i midten) tager imod kaldet og ser efter hvor det skal hen, og sender det til den “rigtige” server.
Serverprocessen tager imod kaldet, udfører forespørgsler og sender resultatet.
DCOPserver (manden i midten) tager imod resultatet og ... sender det til klienten.
Klienten afkoder svaret.
I MCOP ser samme kald ud sådan her ud:
Klientprocessen sender kald.
Serverprocessen tager imod kaldet, udfører forespørgsler og sender resultatet.
Klienten afkoder svaret.
Antag at begge er rigtigt implementerede. MCOP's ikke-hierarkiske strategi bør være hurtigere med en faktor to, end DCOP's mand i midten-strategi. Bemærk dog at der naturligvis var grunde til at vælge DCOP-strategien, som er at hvis du har 20 programmer som kører, og hvert program taler med hvert andet program, så behøver man 20 forbindelser med DCOP, og 200 med MCOP. I tilfældet med multimedie er det dog ikke meningen at dette skal være et almindeligt scenario.
Jeg forsøgte at sammenligne MCOP og DCOP, med at gøre et kald som lægger to tal sammen. Jeg ændrede testdcop for at opnå dette. Testen var dog måske ikke præcis på DCOP-siden. Jeg kaldte metoden i samme proces som gjorde kaldet til DCOP, og jeg vidste ikke hvordan man blev af med en fejlsøgningsmeddelelse, så jeg brugte omdirigering af udskriften.
Testen brugte kun et objekt og en funktion, så resultaterne for DCOP forventes at blive mindre med flere objekt og funktioner, mens resultaterne for MCOP bør forblive de samme. Desuden var dcopserver-processen ikke forbundet til andre programmer, og det er måske sådan at hvis mange programmer er forbundne så mindskes overføringsydelsen.
Resultatet jeg fik var at mens DCOP fik lidt mere end 2000 kald pr sekund, fik MCOP noget mere end 8000 kald pr sekund. Det betyder en faktor 4. Jeg véd at MCOP ikke er afstemt for den maksimalt mulige hastighed endnu. (Som sammenligning: CORBA, med mico-implementationen, klarer noget mellem 1000 og 1500 kald pr sekund).
Hvis du vil have “rigtigere” data, så overvej at skrive et lille måleprogram for DCOP og send det til mig.
CORBA havde den behagelige funktion at man kunne bruge objekter som man havde implementeret, som “separat serverproces”, eller som “bibliotek”. Man kunne bruge samme kode for at gøre det, og CORBA bestemte transparent hvad der skulle gøres. Med DCOP, er det ikke rigtigt meningen, og så vidt jeg ved ikke egentlig muligt.
MCOP på den anden siden skulle understøtte dette fra begyndelsen. Så man kan køre en effekt inde i artsd. Men for en bølgeformseditor kan man også vælge at køre samme effekt inde i processen.
Mens DCOP i hovedsagen er en måde at kommunikere mellem programmer, er MCOP også en måde at kommunikere inde i programmerne. Især for multimediestrømme er dette vigtigt (eftersom man kan køre flere MCOP-objekter parallelt, for at løse en multimediaopgave i et program).
Selvom MCOP ikke gør det for øjeblikket, er mulighederne åbne for at implementere servicekvalitetsfunktioner. Noget i stil med at “den MIDI-begivenhed er virkelig rigtigt vigtig, sammenlignet med dette kald”. Eller noget som “skal være der til tiden”.
På den anden siden kan strømoverførsler integreres i MCOP-protokollen på en behagelig måde, og kombineres med QoS-ting. Under forudsætning af at protokollen kan ændres, bør MCOP strømoverførsler ikke blive virkelig langsommere end en konventionel TCP-strøm, men de vil være enklere og mere konsekvente at bruge.
Der er ingen grund til at basere mellemprogrammer for multimedie på Qt™. Ved at bestemme sig for det, og bruge alle de behagelige Qt™-strømme og andre ting, kan det let føre til at mellemprogrammer kun bliver en sag for Qt™-(eller i virkeligheden kun KDE). Jeg mener at hvis jeg nogensinde ser at GNOME også bruger DCOP, eller noget lignende, er det naturligvis beviset for at jeg har taget fejl.
Selvom jeg ved at DCOP i grunden ikke kender til de datatyper som den sender, så man ville kunne bruge DCOP uden Qt™, se hvordan den bruges i daglig KDE-brug: man sender typer rundt såsom QString
, QRect
, QPixmap
, QCString
, .... Disse bruger Qt™'s-serialisering. Så hvis nogen vælger at understøtte DCOP i et GNOME-program, skal han enten angive at han bruger QString
,... typer (selvom han ikke gør det), og emulere måden som Qt™ bruger til strømme, eller også skulle han sende andre streng-, pixmap- og rect-typer rundt, og på den måde alligevel ikke kunne virke sammen med KDE-programmer.
Nå, under alle omstændigheder var det altid meningen at aRts var beregnet til at virke med eller uden KDE, med eller uden Qt™, med eller uden X11, og måske til og med med eller uden Linux® (og jeg har ikke engang indvendinger mod personer som tilretter den til operativsystemer som ikke er frie).
Min indstilling er at komponenter som ikke indgår i en grafisk grænseflade skal skrives uafhængig af denne, for at muliggøre at de deles mellem et større antal udviklere (og brugere).
Jeg indser at brug af to IPC-protokoller kan være ubekvemt. Desuden er begge to ikke standardiserede. Af de grunde som blev angivet ovenfor er det ikke muligt at skifte til DCOP. Hvis der er betydeligt interesse i at finde en måde at forene de to, så kan vi forsøge. Vi ville til og med kunne forsøge at få MCOP til at tale IIOP, når skulle vi have en CORBA ORB ;).
Jeg talte en del med Matthias Ettrich om fremtiden med de to protokoller, og vi fandt mange måde som tingene kunne gå videre. MCOP ville for eksempel kunne håndtere meddelelsekommunikationen i DCOP, og på denne måde få protokollerne nærmere til hinanden
Nogle mulige løsninger ville være:
Skriv en portal mellem MCOP og DCOP (hvilket bør være muligt, og ville muliggøre samkørsel). Bemærk at der findes en eksperimentel prototype, hvis du skulle have lyst til at arbejde med det.
Integrér alt det DCOP-brugere forventer sig i MCOP, og forsøg kun at bruge MCOP - man skulle også kunne tilføje en “mand i midten” mulighed i MCOP)
Basér DCOP på MCOP i stedet for libICE, og begynd langsomt at integrere tingene tættere sammen.
Den værste mulighed er måske ikke at bruge hver protokol til alt det som den var beregnet til (der er nogle store forskelle i målene med deres konstruktion), og ikke forsøge slå dem sammen til et.
aRts er ikke kun et program, den sørger for også et antal forskellige programmeringsgrænseflader (API) til forskellige formål. I dette afsnit, forsøger jeg at beskrive "helhedsbilledet", et hurtigt overblik over hvad disse grænseflader er tænkte at bruges til, og hvordan de hænger sammen.
Der er en vigtig distinktion som skal gøres: De fleste grænseflader er sprog- og pladsuafhængige eftersom de er specificerede som mcopidl. Dette betyder at man egentlig kan bruge den tjeneste de sørger for fra et hvilket som helst sprog, implementere dem i et hvilket som helst sprog, og man behøver ikke tage sig af om man taler med lokal- eller fjernobjekter. Her er først en liste af dem:
Grundlæggende definitioner som danner kernen i MCOP-funktionen, såsom selve protokollen, definitioner af objekter, handleren, flowsystemet og så videre.
Disse indeholder flowsystemet som man bruger til at forbinde lydstrømmene, definitionen af Arts::SynthModule som er grundlaget for alle grænseflade som har strømme, og til slut nogle nyttige lydobjekter.
Her defineres Arts::PlayObject, et objekt som kan spille en mediatype. Mediaspillere såsom KDE's mediespiller noatun vil kunne afspille hvilket som helst medie som har et PlayObject. Derfor giver det mening at implementere PlayObject for forskellige formater (såsom mp3, mpg video, midi, wav, ...) med dette som grundlag, og der findes allerede mange.
Her defineres en grænseflade for systemets lydserver artsd. Grænsefladen benævnes Arts::SoundServer, og implementerer funktioner såsom at tage imod strømme fra netværket, spille samplinger, oprette andre egne aRts-objekter og så videre. Netværkstransparens er underforstået eftersom MCOP bruges (som for alt øvrigt her).
Dette modul definerer grundlæggende flowfunktioner, dvs. kombinerer enklere objekter til mere komplekse ved at definere en graf som binder dem sammen. Den definerer den grundlæggende grænseflade Arts::StructureDesc, Arts::ModuleDesc og Arts::PortDesc som indeholder en beskrivelse af en struktur, modul og port. Der er også en måde at oprette et "levende netværk af objekter" fra disse forbindelser og værdibeskrivelserne ved brug af en fabrik.
Dette modulet definerer grundlæggende midi-funktioner, såsom objekter som laver midi-begivenheder, hvad en midi-begivenhed er, og Arts::MidiManager til at forbinde producenter og konsumenter af midi-begivenheder, og så videre. Som altid er netværkstransparens underforstået.
Her er der diverse yderligere filtre, oscillatorer, lydeffekter, forsinkelser og så videre, alt som behøves for rigtig nyttig signalbehandling, og for at opbygge komplekse instrumenter og effekter fra disse grundlæggende byggeblokke.
Denne tager sig af synlige objekter. Den definerer den grundlæggende type Arts::Widget som alle moduler med en grafisk grænseflade udgår fra. Dette giver uafhængighed af værktøjskasse, og ... visuel redigering af den grafiske grænseflade, og mulighed for at serialisere den grafiske grænseflade. Desuden, eftersom de grafiske komponenter har normale egenskaber, kan deres værdier på en ligetil måde forbindes til visse signalbehandlingsmoduler. (dvs. værdien af en skyder til klipning for et filter). Som altid, netværkstransparent.
Hvor det er muligt implementeres aRts selv med IDL. På den anden side er der nogle sprogspecifikke programmeringsgrænseflader, som enten bruger enkel C++ eller C. Det er ofte fornuftigt at bruge IDL-grænseflade hvis muligt, og de øvrige grænseflader når det er nødvendigt. Her er en liste over sprogspecifikke programmeringsgrænseflade:
Disse er KDE's programmeringsgrænseflader for bekvemmelighed med de enkle og vældigt almindelige tilfælde, hvor man kun vil afspille en sampling. Grænsefladen er enkel C++, Qt/KDE-optimerede, og så enkle som de kan være.
Enkel C-grænseflade for lydserveren. Meget nyttig for at tilrette gamle programmer.
Her sker al magi som har med MCOP at gøre. Biblioteket indeholder de grundlæggende ting som behøves for at skrive et enkelt MCOP-program, afsenderen, tidtagning, I/O-håndtering, men også de interne funktioner som behøves for at selve MCOP-protokollen skal fungere.
Foruden implementeringen af artsflow.idl, nogle nyttige værktøjer for konvertering af samplingsrater.
Integration af MCOP i Qt's begivenhedsløkke, når man skriver Qt-programmer som bruger MCOP.
C-grænsefladen for aRts oprettedes for at gøre det let at skrive og overføre enkle C-programmer til aRts lydserver. Den sørger for strømningsfunktioner (at sende samplingsstrømme til artsd), enten med eller uden blokering. For de fleste programmer tager man helt enkelt de få systemkald væk som håndterer lydenheden og skifter dem ud mod passende kald til aRts.
Jeg lavede to overførsler for at verificere idéen: mpg123 og quake. Du kan skaffe programrettelserne herfra. Bidrag gerne med dine egne programrettelser til vedligeholderen af aRts eller til programmelpakken for multimedia så de kan integrere understøttelsen for aRts i deres kode.
At sende lyd til lydserveren med grænsefladen er meget enkelt:
indsæt deklarationsfilen med #include <artsc.h>
initiér grænsefladen med arts_init()
opret en strøm med arts_play_stream()
indstil særlige parametre med arts_stream_set()
skriv samplingsdata til strømmen med arts_write()
luk strømmen med arts_close_stream()
frigør grænsefladen med arts_free()
Her er et eksempel på et lille program som viser dette:
#include <stdio.h> #include <artsc.h> int main() { arts_stream_t stream; char buffer[8192]; int bytes; int errorcode; errorcode = arts_init(); if (errorcode < 0) { fprintf(stderr, "arts_init fejl: %s\n", arts_error_text(errorcode)); return 1; } stream = arts_play_stream(44100, 16, 2, "artsctest"); while((bytes = fread(buffer, 1, 8192, stdin)) > 0) { errorcode = arts_write(stream, buffer, bytes); if(errorcode < 0) { fprintf(stderr, "arts_write fejl: %s\n", arts_error_text(errorcode)); return 1; } } arts_close_stream(stream); arts_free(); return 0; }
For let at kunne kompilere og linke programmer med aRts C-grænseflade, findes værktøjet artsc-config som kender til hvilke biblioteker som man skal linke med og hvor deklarationsfilerne findes. Det kaldes med
artsc-config --libs
for at finde ud af bibliotekerne og
artsc-config --cflags
for at finde ud af yderligere C-oversætterflag. Eksemplet ovenfor ville være kunnet kompileres med kommandolinjen:
cc
-o artsctest artsctest.c `artsc-config --cflags` `artsc-config --libs`
cc
-o artsctest
artsctest.c
`artsc-config --cflags`
`artsc-config --libs`
Dette kapitel beskriver alle standardmodulerne i aRts. En af de mest kraftfulde funktioner i aRts er at moduler kan sammenkobles til strukturer som implementerer nye funktioner såsom lydeffekter og instrumenter.
Moduler opdeles i to kategorier. Syntesemoduler bruges til at implementere “rørlægningen” som manipulerer multimedie-datastrømme for at lave nye effekter, instrumenter, miksere og programmer. Visuelle moduler tillader dig at oprette en grafisk grænseflade til at styre lydstrukturerne som opbygges med syntesemodulerne.
Denne multiplicerer signalet med en faktor. Du kan bruge dette til at skalere signaler ned (0 < faktor < 1) eller op (faktor > 1) eller invertere signaler (faktor < 0). Bemærk at faktoren kan være et signal og ikke behøver at være konstant (f.eks. envelopper eller rigtige signaler).
Dette dividerer et signal med en faktor. Du kan bruge dette til at dividere et signal med et andet. Eller sætte indværdi11 til 1 så du derved får den reciprokke værdi af indværdi2 som udværdi. Pas på med at indværdi2 aldrig nærmer sig 0 ellers vil du få problemer med divisioner med nul.
Denne adderer et vilkårligt antal signaler. Hvis du har brug for at opsummere bølgeformer som slaves af fire forskellige oscillatorer, kan du for eksempel tilslutte alle deres udgange til et Synth_MULTI_ADD-modul. Dette er effektivere end at bruge tre Synth_ADD-moduler.
Denne overtoner to signaler. Hvis procentindgangen er -1, høres kun signalet til venstre, hvis den er 1, høres kun det til højre. Hvis den er 0, høres begge signalerne med samme lydstyrke.
Denne gør at du kan sørge for at signalerne bliver indenfor et veldefineret område. Hvis du har to signaler som er mellem -1 og 1 inden overtoningen, forbliver de i samme område bagefter.
Det modsatte af overtoning. Denne tager et monosignal og deler det op til et stereosignal. Den bruges til automatisk at panorere inddata-signalet mellem venstre og højre udgang. Dette gør miksningen mere levende. En standardanvendelse kunne være en guitar- eller sololyd.
Forbind en LFO, for eksempel en sinus eller savtakbølge til inlfo, og vælg en frekvens mellem 0,1 og 5 Hz for at få en traditionel effekt eller endnu mere for specialeffekter (FX).
Denne forsinker inddata-signalet en vis tid. Tidsangivelsen skal være mellem 0 og maxdelay for en forsinkelse mellem 0 og maxdelay sekunder.
Denne slags forsinkelse kan ikke bruges i tilbagemeldingsstrukturer. Dette skyldes at det er en varierbar forsinkelse. Det går an at ændre længden mens den kører, og endog sætte den til nul. Men eftersom de egne uddata behøves for at beregne næste sampling i en tilbagemeldingsstruktur, kan en forsinkelse med en værdi som kan gå ned til nul under syntese føre til en låsningssituation.
Brug CDELAY'er i sådanne situationer, eller kombinér en kort konstant forsinkelse (på 0,001 sekunder) med en variabel forsinkelse.
Man kan også kombinere en CDELAY og en DELAY for at opnå en forsinkelse med variabel længde i en tilbagekobling. Sørg blot for at du har med en CDELAY at gøre.
Denne forlænger inddatasignalet en vis tid. Tidsangivelsen skal være større end 0 for en forsinkelse af på sekunder eller mere. Forsinkelsen er konstant under beregningen, hvilket betyder at den ikke kan ændres.
Dette sparer beregningstid eftersom ingen interpolation gøres, og er nyttigt til rekursive strukturer. Se beskrivelsen ovenfor (Synth_DELAY).
Dette er en klassisk ADSR-envelop, som betyder at du angiver:
Hvis brugeren holder tasten nede lige nu.
Inddatasignalet.
Tiden som skal gå fra brugerens tryk på tasten og til signalet når sin maksimale amplitude (i sekunder).
Tiden som skal gå fra signalet har nået sin maksimale amplitude til signalet går tilbage til et konstant niveau (i sekunder).
Det konstante niveau som holdes for signalet bagefter, til brugeren slipper tasten.
Tiden som skal gå fra brugeren har sluppet tasten til signalet går ned til nul (i sekunder).
Du får det behandlede signal som uddata-værdi. Når ASDR-envelopen er klar, sætter den done til 1. Du kan bruge dette til at sørge for uddata “done” for et instrument (som gør at instrumentstrukturen fjernes fra MIDI-relæobjektet når lyden er hørt op).
Modulet Synth_PSCALE vil skalere lydstrømmen som sendes gennem den fra lydstyrke 0 (tavs) til 1 (oprindelig lydstyrke) tilbage til 0 (tavs), ifølge positionen (hent positionen fra en Synth_SEQUENCE). Positionen hvor toppen skal være kan angives som pos.
Eksempel: sæt top til 0,1 betyder at efter at 10 % af tonen er afspillet, har lydstyrken nået sin maksimale værdi, og begynder derefter at aftage.
Dette er en efterklangseffekt. I den nuværende implementering er det tænkt at sende en stereosignal gennem modulet, og det vil så -tilføje- efterklangseffekten til signalet.
Dette betyder at den også kan bruges inde i en StereoEffectStack.
Inddata-signalet skal forbindes til (inleft, inright), og uddata-signalet bliver (outleft, outright).
Parametrene som du kan indstille er:
Størrelsen af rummet som efterklangseffekten simulerer (område: 0 .. 1, hvor 1 er det største mulige rum).
Dette angiver et filter som gør at det simulerede rum absorberer høje frekvenser (område 0 .. 1, hvor 1 betyder absorbere høje frekvenser i stor udstrækning).
Størrelsen af efterklangsignalet (dvs. den del af signalet som skal ændres af filtret, og forårsage en “vådt” lyd, dvs. en “efterklangslyd”).
Størrelsen af rent signal som sendes gennem, som forårsager et ekko (eller kombineret forsinkelse) i stedet for en efterklangseffekt (område: 0 .. 1).
Størrelsen af udvidet stereoeffekt som efterklangsalgoritmen tilføjer til efterklangseffekten, for at gøre efterklangslyden bredere i stereolydbilledet (område 0 .. 1).
[HUSK: Jeg tror at hvis mode er 1, beholder efterklangen det nuværende lydbillede for lyden, mens 0 er normal behandling]
Tremolomodulet modulerer amplituden ifølge en LFO-bølgeform. Traditionelt bruger man en sinusbølge, men hvorfor begrænse sig? Hvad man får er en meget intensiv effekt som skærer gennem de fleste arrangementer på grund af sit store dynamiske omfang. Tremoloeffekten er stadigvæk en favoriteffekt bland guitarister, selvom den ikke er så populær som i tresserne.
[HUSK: for øjeblikket implementeres dette som invalue + abs(inlfo) - måske ville det være bedre at implementere det som invalue * (1+inlfo*depth), hvor depth ville være en parameter mellem 0 .. 1 - dette afgøres efter KDE2.1. Hvis du har en kommentar, så skriv til aRts' e-mail-liste ;). ]
En flanger er en tidsvariabel forsinkelseseffekt. For at gøre udvikling af komplekse flangereffekter enklere, leveres dette modul som indeholder kernen af en enkanals flangerenhed.
Den har følgende porte:
Signalet som du vil behandle.
Helst en sinusbølge som modulerer forsinkelsestiden inde i flangeren (-1 .. 1).
Den minimale værdi for tiden inde i flangeren i millisekunder. Foreslåede værdier: forsøg med noget i retning af 1 ms. Brug helst værdier < 1000 ms.
Den maksimale værdi for tiden inde i flangeren i millisekunder. Foreslåede værdier: forsøg med noget i retning af 5 ms. Brug helst værdier < 1000 ms.
Uddatasignalet. Det er vigtigt at du blander dette med originalsignalet (uden flanger) for at få den ønskede effekt.
Du kan bruge dette som en basis for koreffekter.
Denne tonehøjdeforandrende effekt ændrer frekvensen på inddatasignalet uden at påvirke hastigheden. En anvendelse af dette er for eksempel at ændre tonehøjden på din stemme mens du indspiller (og afspiller) den i realtid.
Parameteren speed er den relative hastighed som signalet afspilles med. Så med en hastighed på to bliver lyden dobbelt så høj (dvs. en inddatafrekvens på 440 Hz giver en uddatafrekvens på 880 Hz).
Parameteren frequency bruges internt til at ændre mellem forskellige korn i signalet. Den er indstillelig, og afhængig af valget kommer tonehøjdens ændring til at lyde mere eller mindre realistisk i dit brugstilfælde. En god værdi at begynde med er noget i stil med 5 eller 10.
Dette modul klipper et signal så det passer i området [-1;1]. Det gør ingenting for at begrænse den forvridning som opstår når et stærk signal klippes af. Man kan bruge dette som en lydeffekt (for eksempel til at oprette en noget afklippet sinusbølge). Det er dog oftest en god idé at lade signalet gå gennem et lowpass-filter bagefter hvis du gør dette, for at gøre lyden mindre skærende.
Dette er en god byggeblok for en parametriserbar tonekontrol. Dens parametre er:
Signalet som filtreres af tonekontrollen.
Hvordan lave frekvenser skal ændres. Værdien angives i dB, mens 0 betyder at lave frekvenser ikke skal ændres, ville -6 betyde at de dæmpes med 6 dB, og +6 betyder at de øges med 6 dB.
Hvordan middelfrekvenser skal ændres af tonekontrollen i dB (se low).
Hvordan høje frekvenser skal ændres af tonekontrollen i dB (se low).
Dette er midtfrekvensen for tonekontrollen i Hz, midtfrekvensen befinder sig omkring dette spektrum, med de lave og høje frekvenser nedenfor og ovenfor. Bemærk at frekvensen ikke kan være højere end en halvdel af samplingsfrekvensen, oftest er den 22050 Hz, og ikke lavere end 1 Hz.
Dette påvirker hvor bredt midterspektret er. Det skal være et positivt tal > 0. En værdi på et er rimelig, højere værdier af q betyder et smallere spektrum af midterfrekvenser. Lavere værdier end et betyder et bredere spektrum.
Et dæmpet resonansfilter som filtrerer alle frekvenser omkring en højeste værdi. Der er ingen nyttig måde at angive en midterfrekvens (som ikke skal klippes ), eftersom inddata er to underlige konstanter f og b. Koden er meget gammel, fra synthesizerns tidligste dage, og vil formodentlig blive skiftet ud mod et nyt filter som har en frekvens og en resonansværdi som parametre.
Forsøg med noget som ligner b=5, f=5 or b=10, f=10 or b=15, f=15 i alle tilfælde.
Dette modul indlæser en instrumentstruktur fra en fil, og registrerer sig som en midi-udgang med aRts MIDI-håndtering. Toner som sendes til denne udgang gør at instrumentstemmer laves.
Du kan indstille noget sådant her på en behageligere måde i artscontrol i stedet for manuelt i aRts-builder.
Afspiller en sekvens af toner om og om igen. Tonerne angives i spornotation, og adskilles af semikolon. Et eksempel er A-3;C-4;E-4;C-4;
. Hastigheden angives som sekunder pr tone, så hvis du vil angive 120 slag pr minut, angiver du formodentlig 0,5 sekunder/tone, idet 60 sekunder/0,5 sekunder pr tone = 120 slag pr minut.
Du kan give hver tone en længde i forhold til hastigheden ved at bruge et kolon efter tonen fulgt af længden. A-3:2;C-4:0.5;D-4:0.5;E-4;
viser dette. Som du kan se, tenderer midi-tonesætningsprogrammer til at tilbyde større bekvemmelighed ;)
Synth_SEQUENCE giver yderligere information om positionen for tonen som afspilles lige nu, hvor 0 betyder lige påbegyndt og 1 betyder afsluttet. Denne information kan man bruge med Synth_PSCALE (se nedenfor).
Dette afspiller en wav
-fil. Den er kun tilstede hvis du har libaudiofile på computeren. Bølgeformsfilen afspilles så snart modulet laves.
Den stopper så snart den er klar, og done sættes til 1. Hastighedsparameteren kan bruges til at afspille filen hurtigere eller langsommere, hvor 1.0 er den normale (indspillede) hastighed.
Normalt behøver man ikke dette modul, hvis man ikke skriver selvstændige programmer. Inde i artsd, er der allerede et Synth_PLAY modul, og at oprette et til virker ikke.
Modulet Synth_PLAY giver lydsignalet som uddata til lydkortet. Venstre og højre kanal skal indeholde normaliseret inddata for kanalerne. Hvis inddata ikke er mellem -1 og 1, får man klipning.
Som allerede nævnt, må der kun være et Synth_PLAY modul, eftersom det har direkte adgang til lydkortet. Brug busser til at blande mere end en lydstrøm sammen inden afspilning. Brug modulet Synth_AMAN_PLAY for at få noget som ligner uddata inde i artsd.
Bemærk at Synth_PLAY også kontrollerer tiden for hele strukturen. Dette betyder: ingen Synth_PLAY = ingen kilde for tid = ingen lyd. Så der behøves (nøjagtigt) et Synth_PLAY objekt.
Normalt behøver man ikke dette modul, hvis man ikke skriver selvstændige programmer. Inde i artsd, er der normalt allerede et Synth_RECORD modul, og at oprette et til virker ikke.
Modulet Synth_RECORD indspiller et signal fra lydkortet. Venstre og højre kanal indeholder inddata for kanalerne (mellem -1 og 1).
Som allerede nævnt, må der kun findes et Synth_RECORD modul, eftersom det har direkte adgang til lydkortet. Brug busser for at bruge den indspillede lydstrøm på mere end et sted. Brug modulet Synth_AMAN_RECORD for at få noget som ligner en indgang inde i artsd. For at dette skal virke skal artsd køre med full dupleks aktiveret.
Modulet Synth_AMAN_PLAY giver lydsignalet som uddata. Det er godt (men ikke nødvendigt) at uddata er et normaliserer signal (mellem -1 og 1).
Dette modul bruger lydhåndteringen til at angive hvor signalet bliver spillet. Lydhåndteringen kan styres gennem artscontrol. For at gøre det mere intuitivt at bruge, er det godt at navngive signalet som afspilles. Det kan opnås ved at angive title. En anden funktion i lydhåndteringen er at kunne huske hvor et signal sidst blev afspillet. For at kunne gøre dette skal den kunne skelne mellem signaler. Derfor skal man også angive en entydig værdi for autoRestoreID.
Modulet Synth_AMAN_RECORD optager et lydsignal fra en ekstern kilde (dvs. linje ind/mikrofon) inde i artsd. Uddata vil være et normaliseret signal (mellem -1 og 1).
Dette modul bruger lydhåndteringen til at angive hvor signalet afspilles. Lydhåndteringen kan styres med artscontrol. For at gøre det mere intuitivt at bruge, er det godt at navngive signalet som afspilles. Det kan opnås ved at angive title. En anden funktion i lydhåndtereringen er at kunne huske hvor et signal afspilledes sidst. For at kunne gøre dette skal den kunne skelne mellem signaler. Derfor skal man også angive en entydig værdi for autoRestoreID.
Man kan bruge dette til fejlsøgning. Det udskriver værdien for signalet ved invalue i regelmæssige intervaller (cirka 1 sekund), sammen med kommentaren som du har angivet. På denne måde kan man finde ud af om visse signaler forbliver indenfor visse områder, eller om de findes i det hele taget.
Man kan bruge dette til at fejlsøge hvordan MIDI-begivenheder virkelig kommer ind i aRts.
Når en MIDI_DEBUG kører, udskriver artsserver linjer såsom:
201 100753.837585 on 0 42 127
202 101323.128355 off 0 42
Den første linje fortæller at 100753 ms (dvs. 100 sekunder) efter MIDI_DEBUG startede, kom en MIDI-begivenhed "on" på kanal 0. Denne midi-begivenhed havde hastighed (lydstyrke) 127, den højest mulige. Næste linje viser midi-begivenhed da tasten blev sluppet. [HUSK: dette virker ikke for øjeblikket, få det til at virke, og gør det via MIDI-håndteringen].
Oscillatorer i aRts kræver ikke en frekvens som inddata, men i stedet en position i bølgen. Positionen skal være mellem 0 og 1, som i et standardobjekt Synth_WAVE_SIN oversættes til området 0 .. 2*Pi. For at oprette oscillerende værdier fra en frekvens, bruges et Synth_FREQUENCY modul.
Dette bruges til frekvensmodulering. Angiv frekvensen med indgangen frequency og put et andet signal til moduleringsindgangen. Indstil derefter modlevel til et eller andet, f.eks. 0,3. Frekvensen moduleres derefter med moduleringen. Prøv det. Virker godt hvis man tilføjer en tilbagekobling, hvilket betyder at man tager en kombination af det forsinkede uddatasignal fra Synth_FM_SOURCE (man skal sende det til en oscillator eftersom det kun virker som Synth_FREQUENCY) og et andet signal for at få et godt resultat.
Virker godt sammen med Synth_WAVE_SIN oscillatorer.
Sinusoscillator. Angiv et pos signal fra Synth_FREQUENCY eller Synth_FM_SOURCE som indsignal, og få en sinusbølge som udsignal. Signalet pos angiver positionen i bølgeformen, området 0 .. 1 afbildes til 0 .. 2*Pi internt.
Triangelbølgeoscillator. Angiv et pos signal fra Synth_FREQUENCY eller Synth_FM_SOURCE som indsignal, og få en triangelbølge som udsignal. Signalet pos angiver positionen i bølgeformen, området 0 .. 1 afbildes til 0 .. 2*Pi internt. Vær forsigtig. Indsignalet skal være i området 0 .. 1 for at udsignalet kan give et godt resultat.
Firkantbølgeoscillator. Angiv et pos signal fra Synth_FREQUENCY eller Synth_FM_SOURCE som indsignal, og få en firkantbølge som udsignal. Signalet pos angiver positionen i bølgeformen, området 0 .. 1 afbildes til 0 .. 2*Pi internt. Vær forsigtig. Indsignalet skal være i området 0 .. 1 for at udsignalet kan give et godt resultat.
Blød savtandsbølge, som ligner Synth_WAVE_TRI oscillatoren i udseende. Angiv et pos signal fra Synth_FREQUENCY eller Synth_FM_SOURCE som indsignal, og få en blød savtandsbølge som udsignal. Signalet pos angiver positionen i bølgeformen, området 0 .. 1 afbildes til 0 .. 2*Pi internt. Vær forsigtig. Indsignalen skal være i området 0 .. 1 for at udsignalet kan give et godt resultat.
Pulsoscillator. Dette modul ligner den rektangulære oscillator (Synth_WAVE_RECT), men det sørger for et indstilleligt op/ned-forhold, ved parameteren dutycycle. Angiv et pos signal fra Synth_FREQUENCY eller Synth_FM_SOURCE som indsignal, og få en pulsbølge som udsignal. Signalet pos angiver positionen i bølgeformen, området 0 .. 1 afbildes til 0 .. 2*Pi internt. Vær forsigtig. Indsignalet skal være i området 0 .. 1 for at udsignalet kan give et godt resultat.
Dette modul reducerer det dynamiske omfang af et signal. Reduktion er for eksempel nyttigt for at kompensere for de store variationer i lydstyrke når nogen taler i en mikrofon.
Så snart inddataniveauet overstiger et vist niveau (tærskelen) så reduceres signalet. Den multiplicerer helt enkelt alt over tærskelen med en ratio, som skal være en værdi mellem 0 og 1. Tilsidst multipliceres hele signalet med uddatafaktoren.
Argumenterne attack og release forsinker starten og slutningen af reduktionen. Brug dette hvis du for eksempel stadigvæk vil høre den stærke begyndelse af en bastromme. Argumenterne angives i millisekunder, og en attack eller release på 0 ms er mulig, men kan resultere i en let støj.
Værktøjet artsdsp, tidligere beskrevet, tillader de fleste ældre lydprogrammer som taler direkte med lydenheder at virke rigtigt med aRts. Programmer som er skrevet til at bruge Enlightenment Sound Daemon (esd) virker også i de fleste tilfælde ved at køre esd med artsdsp.
Dette giver en god løsning på kort sigt for at ændre eksisterende programmer til KDE. Det tillader dog ikke programmet direkte at drage fordel af hele aRts' kraftfulde funktioner, såsom at bruge moduler og andre multimedietyper end digitallyd. Hvis programmet gør mere end kun helt enkelt at afspille lydfiler, er det oftest bedre at tilføje indbygget støtte for aRts i programmet.
At bruge aRts betyder også at programmet ikke behøver gøre så meget arbejde - det kan drage fordel af funktioner i aRts til at håndtere ting såsom kodning for forskellige mediaformater og kontrol af lydkort.
Når du bruger aRts, er der et antal forskellige programmeringsgrænseflader (API) at vælge blandt. Beslutningen om hvilket som skal bruges afhænger af et antal forskellige faktorer, blandt andet hvilken slags medietype der bruges (lyd, MIDI, lyd-CD, etc.), de funktioner der kræves af grænsefladen, og om programmet er skrevet i C++. I de fleste tilfælde bør valget være ganske klart baseret på de nødvendige funktioner.
For flytbarhed mellem platforme kan programmer som skal kunne køre i andre miljøer end KDE ikke stole på at aRts er tilgængeligt. At bruge en plugin-paradigme er en god måde at støtte forskellige multimediemiljøer. At lave et plugin-API åbent og dokumenteret (især for programmer uden adgang til kildekode) har også den fordel at en anden end programudvikleren kan implementere plugin til aRts.
aRts-projektet behøver hjælp fra udviklere for at at tilføje understøttelse for aRts i eksisterende multimedieprogrammer, skrive nye multimedieprogrammer og forbedre aRts' muligheder. Du behøver dog ikke være en udvikler for at bidrage. Vi behøver også hjælp fra testere til at indsende fejlrapporter, oversættere til at oversætte programteksten og dokumentationen til andre sprog, grafikere til at oprette ikoner (især for artsbuilder moduler), musikere til at lave aRts-moduleksempler, og forfattere til at skrive eller gennemse dokumentation.
De fleste udviklingsdiskussioner om aRts finder sted via to e-mail-lister. Dette er stedet at diskutere nye funktioner og implementeringsidéer og at spørge efter hjælp med problemer.
KDE's multimedie e-mail-liste er til for generelle KDE multimediespørgsmål inklusive aRts samt multimedieprogrammer såsom Noatun og aKtion. Du kan abonnere fra netsiden på http://www.kde.org/mailinglists.html eller sende e-mail med emnet subscribe
til din-e-mail-adresse
(kde-multimedia-request AT kde.org)
. Listen findes også arkiveret på http://lists.kde.org.
E-mail-listen for aRts er til for spørgsmål som kun berører aRts, inklusive brug af aRts udenfor KDE. For at abonnere, send e-mail til (arts-request AT space.twc.de)
med meddelelsesteksten subscribe
. Listen arkiveres på http://space.twc.de/~stefan/arts-archive. din-epostadresse
For at opnå en konsekvent læsning af al kildekode, er det vigtigt at holde kodningsstilen ens i hele aRts' kildekode. Vær derfor rar og forsøg at skrive/formatere din kildekode i overensstemmelse med dette, også selvom du kun skriver et modul, eftersom det gør det enklere for forskellige personer at vedligeholde kildekodetræet, og lettere at kopiere dele af kildekoden fra en fil til en anden.
Qt™/Java™-stil. Dette betyder at store bogstaver bruges til at markere nye ord, og at det første bogstav altid er lille. Ingen understregninger.
Dette betyder for eksempel:
createStructureDesc() updateWidget(); start();
Klassemedlemmer har ikke store bogstaver, som for eksempel menubar eller button.
Når der er funktioner som bruges til adgang, skal standarden som bruges være ifølge MCOP-måden, dvs. hvis der er et "long" medlem foo
, som ikke skal være synlig, så laves:
foo(long new_value); long foo();
funktioner for at hente eller sætte en værdi. I dette tilfælde, skal den rigtige værdi for foo
opbevares i _foo.
Alle klasser skal have store bogstaver for hvert ord, hvilket betyder ModuleView
, SynthModule
. Alle klasser som hører til biblioteker skal bruge aRts-navnerummet, såsom Arts::Soundserver
.
Implementeringer af MCOP-klasser skal døbes Class_impl
, som for eksempel SoundServer_impl
.
Parametre har altid små bogstaver.
Lokale variabler har altid små bogstaver, og kan have navne såsom i
, p
, x
osv. hvis det passer.
Et tabulatortegn er lige så meget som fire blanke tegn.
Normalt behøver du ikke bruge mellemrum i udtryk. Du kan i alle tilfælde bruge dem mellem operatorer og deres operander. Hvis du skriver et mellemrum før en operator (f.eks. +), skal du også skrive et mellemrum efter operatoren. Den eneste undtagelse fra dette er udtryk som ligner lister (med ,), hvor du kun skal bruge et mellemrum efter ",", men ikke før. Det er også i orden at udelade mellemrum her.
Følgende eksempel demonstrerer god brug af mellemrum:
{ int a,b; int c, d, e; int f = 4; a=b=c=d+e+f; a = b = c = d + e + f; if(a == 4) { a = b = c = (d+e)/2; } while(b<3) c--; arts_debug("%d\n", c); }
Følgende eksempel demonstrerer hvordan man ikke skal bruge mellemrum. For funktionskald, efter if, while, for, switch og så videre, skrives intet mellemrum.
{ // DÅRLIGT: Hvis du skriver en liste, skriv kun mellemrum efter "," int a , b , c , d , e , f; // DÅRLIGT: ikke symmetrisk brug af mellemrum for = operatoren a= 5; // DÅRLIGT: Hvis det anses at være en funktion, og ikke følges af et mellemrum if (a == 5) { } // DÅRLIGT: skriv ikke et mellemrum efter while while (a--) b++; // DÅRLIGT: Funktionsnavne følges ikke af et mellemrum arts_debug ("%d\n", c); // DÅRLIGT: heller ikke medlemsnavne Arts::Object o = Arts::Object::null (); }
Kildekodefiler skal ikke have store bogstaver i navnet. De skal have samme navne som klassen hvis de implementerer en enkelt klasse. Deres filendelse skal være .cc
hvis de indeholder Qt™- og grafikuafhængig kode, og .cpp
hvis de indeholder Qt™- og grafikafhængig kode. Implementeringsfiler for grænseflader skal benævnes
, hvis Foo er grænsefladens navn. foo
_impl
IDL-filer skal benævnes på en beskrivende måde med tanke på den samling grænseflader de indeholder, også helt med små bogstaver. I særdeleshed er det ikke godt at benævne en IDL-fil som klassen selv, eftersom .mcopclass-handleren og typeinfoposterne så vil kollidere.
Dette afsnit beskriver en del af det pågående arbejde med aRts. Udviklingen går hurtigt fremad, så denne information kan være forældet. Du bør kontrollere listefilen TODO og arkiverne for e-mail-listerne for at holde styr på hvilke nye funktioner der er planlagt. Deltag gerne i ny konstruktion og implementation.
Dette er et udkast som forsøger at give dig en oversigt over hvordan nye teknologier vil integreres i aRts. Det dækker følgende:
Hvordan grænseflader virker.
Afkodere - afkodning af mp3 eller wav-strømme til en form som gør at de kan bruges som data.
Video.
Trådning.
Synkronisering.
Dynamisk udvidelse/maskering.
Dynamisk sammensætning.
GUI
MIDI
Dette er pågående arbejde. Det bør dog kunne give grundlaget hvis du vil kigge på ny teknologi i aRts. Det bør give dig en almen idé om hvordan disse problemer vil blive angrebet. Korrigér gerne alt du ser her.
Ting som vil bruge aRts-teknologi (så vær venlig og koordinér anstrengelserne):
K-telefon (tale via IP)
Noatun (videoafspiller / lydafspiller)
artscontrol (styreprogram for lydserveren, og for målere)
Brahms (musiksequencer)
Kaiman (KDE2 medieafspiller - kmedia2-kompliant)
mpglib/kmpg (mpg lyd- og videoafspilningsteknologi)
SDL (direkte medialag for spil, som endnu ikke er påbegyndt men som kan være behageligt)
Elektriske ører (ophavsmanden kontaktede mig - status ukendt)
MCOP-grænsefladen er grundlaget for aRts-begrebet. De er det netværkstransparente ækvivalente til C++ klasser. Så snart det er muligt bør du indrette din konstruktion mod grænseflader. En grænseflade består af fire dele:
Synkrone strømme
Asynkrone strømme
Metoder
Egenskaber
Disse kan blandes på en hvilken som helst måde du vil. Nye teknologier bør defineres ved hjælp af grænseflader. Læs afsnittene om asynkrone strømme og synkrone strømme, samt KMedia2-grænsefladen, som er gode eksempler på hvordan sådanne ting virker.
Grænseflader specificeres i .idl
-kode og køres gennem mcopidl-oversætteren. Man afleder
klassen for at implementere dem, og bruger Grænsefladensnavn
_implREGISTER_IMPLEMENTATION (Grænsefladensnavn_impl)
til at indsætte en objektimplementering i MCOP's objektsystem.
Kmedia2-grænsefladen lader dig se bort fra at wav-filer, mp3-filer eller hvad som helst består af datastrømme. I stedet implementerer du kun metoder for at spille dem.
På den måde kan du skrive en bølgeformsladningsrutine på en måde så du kan spille bølgeformsfiler (som PlayObject), men ingen anden kan bruge din kode.
Asynkrone strømme ville være alternativet. Man definerer en grænseflade som tillader at datablokke sendes ind, og hentes ud. Dette ser sådan her ud i MCOP:
interface Codec { in async byte stream indata; out async byte stream outdata; };
Afkodere kan naturligvis også angive egenskaber for at udsende yderligere data, som formatinformation.
interface ByteAudioCodec { in async byte stream indata; out async byte stream outdata; readonly attribute samplingRate, bits, channels; };
Denne ByteAudioCodec
kan for eksempel forbindes til et ByteStreamToAudio
-objekt, for at oprette rigtigt flydende lyd.
Andre typer af afkodere kan naturligvis involvere at sende videodata direkte ud, som
interface VideoCodec { in async byte stream indata; out video stream outdata; /* bemærk: videostrømme findes ikke endnu */ };
Sandsynligvis bør et afkodningsbegreb bruges i stedet for måden “du ved hvordan det spilles med det gør jeg ikke” som for eksempel WavPlayObject
bruger for øjeblikket. Nogen skal dog sætte sig ned og eksperimentere lidt inden en programmeringsgrænseflade kan defineres.
Min idé er at sørge for video som asynkrone strømme for en indbygget MCOP-datatype som indeholder billeder. Denne datatype er ikke lavet endnu. Ved at gøre dette, kan plugin som håndterer video kobles sammen på samme måde som lydi-plugin.
Der er nogle ting som det er vigtigt ikke at udelade:
Der er RGB og YUV farverum.
Formatet bør markeres i strømmen på en måde.
Synkronisering er vigtig.
Min idé er at lade muligheden for at ændre implementeringen af VideoFrame
-klassen være åben, så den kan opbevare ting i et delt hukommelsessegment. Ved at gøre dette kan til og med videostrømme mellem forskellige processer blive mulige uden alt for store problemer.
For video er den almindelige situation dog at alle ting er i samme proces, fra afkodningen til visningen.
Jeg har lavet en prototypeimplementering af videostrømme, som kan hentes herfra. Dette skal integreres med MCOP efter nogle eksperimenter.
En visningskomponent som understøtter XMITSHM (med RGB og YUV) bør der sørges for. Martin Vogt fortalte mig at han arbejder på en sådan.
For øjeblikket er MCOP helt og holdent en eneste tråd. For video kan vi måske ikke længere komme udenom tråde. O.k. Der er nogle ting som skal håndteres med forsigtighed:
SmartWrappers - de er ikke trådsikre på grund af usikker referenceregning og lignende.
Afsenderen / I/O - heller ikke trådsikre.
Hvad jeg i alle tilfælde kan tænke mig er at gøre udvalgte moduler trådsikre, både for synkrone og asynkrone strømme. På denne måde kan man skemalægge signalstrømmen på to eller flere processorer, med et flowsystem som kender til tråde. Dette burde også hjælpe en hel del med lyd med multiprocessorer.
Hvordan det ville virke:
Flowsystemet bestemmer hvilke moduler der skal beregne hvad, dvs:
videobilleder (med metoden process_indata)
synkrone lydstrømme (calculateBlock)
andre asynkrone strømme, i hovedsagen datastrømme
Modulerne kan beregne disse ting i egne tråde. For lyd giver det mening at genbruge tråde (f.eks. håndtere den med fire tråde hvis der er fire processorer, også selvom 100 moduler kører). For video- og datakompression, kan det være bekvemmere at have en blokerende implementering i en egen tråd, som synkroniseres med resten af MCOP med flowsystemet.
Moduler må ikke bruge MCOP-funktioner (som fjernkaldes) mens trådede operationer er i gang.
Video og MIDI (og lyd) kan kræve synkronisering. I grunden er dette tidsstempler. Idéen jeg har er at tilknytte tidsstempler til de asynkrone strømme, ved at tilføje et tidsstempel til hver pakke. Hvis man sender to videobilleder, gøres det helt enkelt som to pakker (de er store alligevel), så man kan have to forskellige tidsstempler.
Lyd skal have underforståede tidsstempler, eftersom det er synkront.
Det bør være muligt at sige: En effekt FX består af disse enkle moduler. FX bør se ud som en normal MCOP-modul (se maskering), men i virkeligheden bestå af andre moduler.
Dette kræves for aRts-builder.
Alle komponenter i den grafiske grænseflade vil være MCOP-moduler. De bør have egenskaber som størrelse, etiket, farve, ... En RAD-bygger (aRts-builder) bør kunne sammensætte dem visuelt.
Den grafiske grænseflade bør det være muligt at gemme ved at gemme egenskaberne.
MIDI-tingene vil blive implementeret som asynkrone strømme. Der er to alternativer, en er at bruge normale MCOP-strukturer til at definere typerne og den anden er at introducere yderligere egne typer.
Jeg tror at normale strukturer vil slå til, dvs noget som:
struct MidiEvent { byte b1,b2,b3; sequence<byte> sysex; }
Asynkrone strømme bør understøtte egne strømningstyper.
Dette er den hoved-hjemmesiden for KDE-relateret multimedieinformation.
Dette er aRts-projektets hjemmeside.
Kapitel 14 i denne offentliggjorte bog dækker multimedie, inklusive aRts. Den er tilgængelig på tryk eller på nettet med kommentarer på http://www.andamooka.org.
Dette sted har en udtømmende liste over lyd- og MIDI-programmer for Linux®.
Dette afsnit besvarer nogle spørgsmål som ofte stilles om aRts.
13.1. | Støtter KDE mit lydkort for lydafspilning? | ||||||||||||||||||||
KDE bruger aRts til at afspille lyd, og aRts bruger driverne kernen for Linux®, enten OSS eller ALSA (med emulering af OSS). Hvis dit lydkort enten understøttes af ALSA eller OSS og er rigtigt indstillet (dvs. alle andre Linux®-programmer kan afspille lyd), kommer det til at virke. Der er dog nogle problemer med specifik hardware, læs afsnittet om hardwarespecifikke problemer hvis du har problemer med artsd på din maskine. I mellemtiderne er støtte for diverse andre platforme også tilføjet. Her er en fuldstændig liste over hvordan den seneste version af aRts kan afspille lyd. Hvis du har en platform som ikke understøttes, så overvej gerne at overføre aRts til din platform.
| |||||||||||||||||||||
13.2. | Jeg kan ikke spille | ||||||||||||||||||||
Tjek at artsd er linket til | |||||||||||||||||||||
13.3. | Jeg hører lyd når jeg er logget på som | ||||||||||||||||||||
Tilladelserne for filen
Du kan opnå samme virkning i et terminalvindue med kommandoen For at begrænse adgangen af lyd til særlige brugere kan du bruge gruppetilladelser. For visse Linux®-distributioner, for eksempel Debian/Potato, ejes | |||||||||||||||||||||
13.4. | Dette hjælper for artsd, men hvad med for KMix, KMid, KsCD? | ||||||||||||||||||||
Der er forskellige andre enheder som sørger for funktioner som der skal være adgang til for multimedieprogrammer. Du kan behandle dem på samme måde, enten ved at gøre dem tilgængelige for alle, eller bruge grupper for at kontrollere adgangen. Her er en liste, som måske stadigvæk er ufuldstændig (og hvis der er flere enheder på formen
| |||||||||||||||||||||
13.5. | Hvad kan jeg gøre hvis artsd ikke starter eller bryder sammen når den kører? | ||||||||||||||||||||
Forsøg først at bruge standardindstillingerne i KControl (eller hvis du starter manuelt, angive ingen ekstra flag bortset fra eventuelt En god måde at regne ud hvorfor artsd ikke starter (eller bryder sammen når den kørt) er at starte den manuelt. Åbn et Konsole-vindue og skriv:
Du kan også tilføje flaget
Ved at gøre dette får du formodentlig nogen nyttig information om hvorfor den ikke startede. Eller hvis den bryder sammen mens noget særligt foregår, kan du gøre det og se “hvordan” den bryder sammen. Hvis du vil rapportere en fejl, kan et backtrace oprettet med gdb og/eller en strace hjælpe med til at finde problemet. | |||||||||||||||||||||
13.6. | Kan jeg flytte artsd (flytte kompilerede filer til en anden mappe)? | ||||||||||||||||||||
Du kan ikke flytte aRts helt perfekt. Problemet er at artswrapper har stedet for artsd indkompileret af sikkerhedsgrunde. Du kan imidlertid bruge | |||||||||||||||||||||
13.7. | Kan jeg kompilere aRts med gcc-3.0? | ||||||||||||||||||||
Et kort svar: nej, aRts virker ikke hvis du kompilerer den med gcc-3.0. Langt svar: i den officielle udgave af gcc-3.0, er der to fejl som påvirker aRts. Det første problem med gcc-3.0, c++/2733, er ganske ufarligt (og har at gøre med problemer med asm-sætningen). Det gør at convert.cc ikke kan kompileres. Dette er rettet i gcc-3.0 CVS, og vil ikke være et problem med gcc-3.0.1 og senere. En måde at gå udenom problemet er også tilføjet i CVS-versionen af KDE/aRts. Det andet problem med gcc-3.0, c++/3145 (som forårsager fejlagtig kodegenerering i visse tilfælde af multipel virtuel arv) er kritisk. Programmer som artsd bryder helt enkelt sammen når de startes hvis de er kompileret med gcc-3.0. Selvom visse fremskridt er gjort i gcc-3.0 grenen når dette skrives, bryder artsd stadigvæk vældigt ofte sammen, uforudsigeligt. | |||||||||||||||||||||
13.8. | Hvilke programmer kører med aRts? | ||||||||||||||||||||
Selvfølgelig så virker alle programmer som indgår i KDE med aRts. Dette omfatter:
Visse KDE-programmer som endnu ikke indgår i KDE-udgaver (f.eks. i kdenonbeta) understøtter også aRts, inklusive:
Følgende programmer som ikke hører til -KDE er kendte for at virke med aRts:
Følgende programmer er kendte for ikke at virke med aRts:
Se også svarene på spørgsmålene i afsnittet om programmer som ikke understøtter aRts. Dette afsnit er ufuldstændigt. Hvis du har mere information om programmer som understøttes eller ej, så vær venlig at sende dem til forfatteren så at de kan tilføjes her. |
13.1. | Jeg kan ikke bruge aRts-builder. Den bryder sammen når jeg kører et modul! |
Den mest sandsynlige grund er at du bruger gamle strukturer eller moduler som ikke understøttes i KDE 2 versionen. Desværre er dokumentationen på nettet for aRts-0.3.4.1 som er helt forældet. Det oftest rapporterede sammenbrud er at hvis en struktur køres i aRts-builder så fås fejlmeddelelsen [artsd] Synth_PLAY: lydsystemet bruges allerede. Du skal bruge et Synth_AMAN_PLAY modul i stedet for Synth_PLAY så forsvinder problemet. Se også aRts-builder's hjælpefil (tryk på F1 i aRts-builder). Nyere udgaver af aRts-builder (KDE 2.1 beta 1 og senere) levereres med et antal eksempler som du kan bruge. |
aRts programmel ophavsret 1998-2001 Stefan Westerfeld (stefan AT space.twc.de)
Dokumentation ophavsret 1999-2001 Stefan Westerfeld (stefan AT space.twc.de)
og Jeff Tranter (tranter AT kde.org)
.
Dansk oversættelse Erik Kjær Pedersen (erik AT binghamton.edu)
Dokumentation er udgivet under betingelserne i GNU Free Documentation License.
Alle biblioteker som indgår i aRts udgives under betingelseren i GNU Lesser General Public licensen. En overvældende majoritet af koden i aRts findes i biblioteker, inklusive hele MCOP og ArtsFlow. Dette tillader at bibliotekerne bruges af programmer som ikke er fri eller åbne hvis det ønskes sådan.
Det er et fåtal af programmer (såsom artsd), som udgives under betingelseren i GNU General Public License. Eftersom der har været forskellige meninger om det er tilladt eller ej at linke GPL-programmer med Qt™, har jeg også tilføjet en udtrykkelig anmærkning som tillader dette, som en tilføjelse til GPL: tilladelse gives også at til at linke dette program med Qt™-biblioteket, hvor Qt™ behandles som et bibliotek som normalt følger med operativsystemets kerne, hvad enten dette er tilfældet eller ej.
Indholdsfortegnelse
For at kunne bruge aRts skal du naturligtvis have det installeret og køre det på systemet. Der er to metoder at opnå dette, som beskrives i de følgende afsnit.
Den hurtigste og enkleste måde at få aRts køreklar er at installere færdigkompilerede binære pakker for systemet. De fleste nyere Linux®-distributioner indeholder KDE, og hvis det er KDE 2.0 eller senere er aRts med. Hvis KDE ikke følger med installationen, kan det findes tilgængeligt til download fra din operativsystemleverandør. Som et alternativ kan det findes tilgængeligt fra tredjepart. Sørg for at du bruger pakker som passer sammen med dit operativsystem.
En grundlæggende installation af KDE indeholder lydserveren, som tillader de fleste programmer at afspille lyd. Hvis du vil have hele opsætningen med multimedieværktøj og programmer, vil du formodentlig skulle installere yderligere valgfrie pakke.
Bagdelen ved at bruge færdigkompilerede binære filer er at de måske ikke indeholder den nyeste version af aRts. Dette er særlig sandsynligt hvis de er kommet på cd-rom, eftersom udviklingstakten for aRts og KDE er sådan at cd-rom udgaver oftest ikke kan følge med. Du kan også komme ud for at færdigkompilerede binære pakker ikke er tilgængelige, hvis du har en mindre almindelig arkitektur eller operativsystemdistribution, og du må derfor bruge den anden metoden.
Selv om det tager tid, er den mest fleksible måde at bygge aRts selv at kompilere den fra kildekode. Dette sikrer dig at du har en version som er optimalt kompileret for din systemopsætning og lader dig bygge den nyeste version.
Her har du to valg, du kan enten installere den nyeste stabile version som indgår i KDE eller du kan skaffe den nyeste (men måske ustabile) udgave direkte fra KDE-projektets CVS-arkiv. De fleste brugere som ikke udvikler med aRts bør bruge den stabile version. Du kan hente den fra ftp://ftp.kde.org eller et af de mange spejle. Hvis du allerede udvikler aktivt med aRts vil du formodentlig bruge CVS-versionen. Hvis du vil bruge aRts uden KDE, kan du hente en selvstændig udviklingsversion fra http://space.twc.de/~stefan/kde/arts-snapshot-doc.html.
Bemærk at hvis du bygger fra CVS, vil visse komponenter i aRts (dvs. de grundlæggende centrale komponenter inklusive lydserveren) være i CVS-modulet kdelibs, mens yderligere komponenter (f.eks. artsbuilder) indgår i kdemultimedia. Dette vil muligvis blive ændret i fremtiden. Du kan også finde en version i modulet kmusic, det er den gamle version (før KDE 2.0) som nu er forældet.
Kravene til at bygge aRts er i hovedsagen de samme som for KDE. Konfigurationsscripterne bør opdage systemindstillingerne og angive om nogle nødvendige komponenter mangler. Sørg for at du har en fungerende lyddriver-rutine på systemet (enten OSS/Free driver i kernen, OSS-driveren fra 4Front Technologies, eller en ALSA-driver med OSS-støtte).
Mere information om at hente og installere KDE (inklusive aRts) findes i KDE's OSS.
Advanced Linux® Sound Architecture. En lydkortsdriver for Linux® som for øjeblikket ikke indgår som standard i kildekoden for kernen.
Analog realtidssynthesizer. Navnet på den multimediaarkitektur/bibliotek/værktøjskasse som bruges af KDE-projektet (bemærk de store bogstaver)
Berkeley Software Distribution. Refererer her til et af flere forskellige UNIX®-kompatible operativsystemer som har deres oprindelse i BSD UNIX®.
Common Object Request Broker Architecture. En standard for at implementere objektorienteret fjernkørsel.
Concurrent Versions System. Et konfigurationshåndteringssystem for programmel som bruges af mange programmelprojekter inklusive KDE og aRts.
Fast Fourier Transform. En algoritme til at konvertere data fra tids- til frekvensdomæne, som ofte bruges i signalbehandling.
Muligheden for et lydkort til samtidigt at indspille og afspille lyd.
GNU General Public License. En programmellicens som blev lavet af Free Software Foundation, som definerer betingelserne for at udgive frit programmel.
Grafisk brugergrænseflade
Interface Definition Language. Et format uafhængigt af programmeringssprog til at definere grænseflader (metoder og data).
K-desktopmiljø. Et projekt for at udvikle et frit grafisk desktopmiljø for UNIX®-kompatible systemer.
GNU Lesser General Public License. En programmellicens som blev lavet af Free Software Foundation, som definerer betingelserne for at udgive frit programmel, med endnu færre begrænsninger end GPL som ofte bruges til programmelbiblioteker.
Multimedia COmmunication Protocol. Protokollen som bruges til kommunikation mellem aRts programmoduler, som har ligheder med CORBA men er enklere og optimeret for multimedia.
Musical Instrument Digital Interface. En standardprotokol for kommunikation mellem elektroniske musikinstrumenter, angiver også ofte filformatet som bruges til at opbevare MIDI-kommandoer.
Open Sound System. Lyddriverne som indgår i Linux®' kerne (sommetider kaldet OSS/Free) eller en kommerciel version som sælges af 4Front Technologies.
Would you like to make a comment or contribute an update to this page?
Send feedback to the KDE Docs Team