Programación MIDI en C con una API de bajo nivel PortMusic

Anuncio
Programación MIDI en C
con una API de bajo nivel
PortMusic
Taller de Música Electrónica
Sergi Jordà, 2005
E.S.U.P., Universitat Pompeu Fabra
Barcelona
Índice
•
Programa MIDI: introducción
•
•
Polling, interrupciones, sincronía…
Relojes, eventos, callbacks y multithreads
•
•
•
•
•
Introducción: MIDI en Windows
Introducción a PortMusic (instalación, funciones…)
Relojes, callbacks y procesos
Sincronía en multithreading
Eventos MIDI
•
•
Control de tiempo: tempo, ticks…
Standard MIDI Files
•
Artículos y bibliografía
•
•
•
•
Apéndice A: Ejemplos de bucles de control
Apéndice B: Callbacks & punteros a funciones
Apéndice C: Ciclos interactivos – Escalas temporales
Apéndice D: Standard MIDI Files
Un programa MIDI…
•
Es un buen ejemplo de programa que funciona a t.real
§ Recibe inputs
§ Los procesa
§ Ejecuta outputs
•
Además es mucho más estricto que otros tipos de programas en
cuanto al control del tiempo. En qué? & porqué?
§ Frecuencia del ciclo
§ Estabilidad del ciclo
Un programa MIDI…
•
Es un buen ejemplo de programa que funciona a t.real
§ Recibe inputs
§ Los procesa
§ Ejecuta outputs
•
Además es mucho más estricto que otros tipos de programas en
cuanto al control del tiempo. En qué? & porqué?
§ Frecuencia del ciclo
§ Estabilidad del ciclo
Polling, interrupciones, sincronía…
Relojes, eventos, callbacks y multithreads
Síncronos – asíncronos
•
A veces se toma “síncrono” como sinónimo de “en tiempo real”, con
lo que se implica que los sistemas en tiempo real NO pueden ser
asíncronos. Esto no tiene porque ser así, ya que todos ellos
(síncrono, asíncrono, t.real) son conceptos un tanto relativos
•
En un sistema de t.real síncrono, el sistema responde
“instantáneamente” a los eventos externos
§ “Instantaneamente” : en un tiempo notablemente inferior al del
lapso entre eventos
•
En un sistema de t.real asíncrono, se presupone que la densidad de
los eventos puede llegar a ser muy alta (o su granularidad temporal
muy pequeña) por lo que el sistema tan sólo debe dar un
compromiso tipo “tiempo máximo de espera”
•
En la mayoría de APIs MIDI (y de audio), lo que se realiza es una
combinación de ambos mecanismos
Polling vs. Interrupciones
Existen 2 formas de implementar un sistema con entradas en t.real
•
Polling
§ Es el caso más sencillo
§ el sistema mira entradas externas periódicamente
§ Adecuado para un input de tipo continuo (analógico), pero no
para uno con eventos (ya que podrían perderse)
•
Interrupciones
§ Los eventos externos provocan un servicio de interrupción, que
ejecuta un programa breve (que debería terminar antes de que
deban comenzar otras tareas)
§ En el más “breve” de los casos, este programa o proceso
simplemente pone el evento en una cola de entrada
§ Una vez terminado este proceso el programa principal retoma el
punto en el que estaba
Entrada contínua vs. eventos
•
•
•
Si el input fuera mirar la posición del ratón, el valor de un sensor que nos
manda valores continuos, etc…los miramos a cada ciclo y ya está…
Pero si el input genera eventos? (e.g. click ratón, teclado MIDI…) Estos
eventos probablemente puedan producirse en cualquier instante…
En muchos sistemas y APIs (incluidas APIs MIDI) se utiliza una
combinación de ambos (polling & interrupciones)
1. Las entradas MIDI ejecutan una interrupción que se limita a poner los
datos en un buffer de entrada
• Que estructura debe tener este buffer? pila, cola, ….?
• más bien cola…. aunque… algunos tipos de datos (e.g. varios
controles de volumen) se puedan saltar y coger sólo el último (si
nuestro sistema es sólo capaz de procesar un control/ciclo,
manejar todos los volúmenes que se han almacenado podría
suponer un retardo considerable). Otros tipos de datos (e.g. notas)
no deberían saltarse
• De forma periódica (polling) se consulta esta estructura
• En este punto, se procesa la cola hasta que esté vacía
Entrada contínua vs. eventos
•
•
•
Si el input fuera mirar la posición del ratón, el valor de un sensor que nos
manda valores continuos, etc…los miramos a cada ciclo y ya está…
Pero si el input genera eventos? (e.g. click ratón, teclado MIDI…) Estos
eventos probablemente puedan producirse en cualquier instante…
En muchos sistemas y APIs (incluidas APIs MIDI) se utiliza una
combinación de ambos (polling & interrupciones)
1. Las entradas MIDI ejecutan una interrupción que se limita a poner los
datos en un buffer de entrada
• Que estructura debe tener este buffer? pila, cola, ….?
• más bien cola…. aunque… algunos tipos de datos (e.g. varios
controles de volumen) se puedan saltar y coger sólo el último (si
nuestro sistema es sólo capaz de procesar un control/ciclo,
manejar todos los volúmenes que se han almacenado podría
suponer un retardo considerable). Otros tipos de datos (e.g. notas)
no deberían saltarse
• De forma periódica (polling) se consulta esta estructura
• En este punto, se procesa la cola hasta que esté vacía
Relojes (timers) y funciones callback
•
Un sistema de tipo polling puede ejecutarse
§ en un único proceso, con un bucle principal (cfg. Apéndice A)
§ mediante la programación de un reloj, que llamará periódicamente a
otro proceso
•
•
Un sistema basado en interrupciones, suele asociar un proceso a
un evento:
evento: la entrada de un dato è proceso: llamada a función
El caso del reloj es similar ya que el reloj produce periódicamente
un evento de reloj
Sistema con 1 solo ciclo
•
•
En el caso más sencillo, el sistema tiene un único ciclo
En este ciclo se deben
§ Mirar las entradas
§ Mapearlas y procesarlas
§ Generar y mostrar las salidas
•
A nivel de programación esto conlleva un único bucle principal que realiza estas tareas
siempre en el mismo orden
Se debería garantizar que los procesos de un ciclo se pueden completar en menos de lo
que dura un ciclo (antes del inicio del nuevo ciclo) (τ < h)
El programa debe también garantizar que no se “adelantará”. Es decir, una vez terminado
el bucle, no deberá comenzar uno nuevo “hasta que toque” (más adelante veremos varios
algoritmos para controlar estos tiempos) (h = cte)
Muchos sintetizadores de audio por software funcionan así. A continuación se describen
varios aspectos tomando un programa de este tipo como ejemplo
•
•
•
Relojes (timers) y funciones callback
•
Un sistema de tipo polling puede ejecutarse
§ en un único proceso, con un bucle principal (cfg. Apéndice A)
§ mediante la programación de un reloj, que llamará periódicamente a
otro proceso
•
•
•
Un sistema basado en interrupciones, suele asociar un proceso a
un evento:
evento: la entrada de un dato è proceso: llamada a función
El caso del reloj es similar ya que el reloj produce periódicamente
un evento de reloj
La asociación de estos eventos (e.g. entradas, relos, etc.) a una
función se suele realizar mediante funciones callback
Funciones Callback
•
Una función callback es una función que se llama desde un proceso
que nosotros no controlamos (normalmente desde una API)
•
Esta llamada puede ser periódica (eg. llenar un frame de audio) o
motivada por determinados eventos (e.g. recibir mensaje MIDI,
procesar frame video)
En realidad están siempre generadas por eventos (en el primer
caso, un evento de RELOJ)
Se suelen implementar mediante punteros a funciones (cfg.
Apéndice B)
•
•
•
En una función callback, no es conveniente realizar gestión de
memoria, acceso a ficheros, refresco de pantalla… y ningún tipo de
acceso a los recursos del sistema que pueda suponer un retardo
para el resto de los procesos
Relojes
•
Los timers son fundamentales para que las aplicaciones realicen
operaciones periódicas en varios procesos y con máxima precisión
temporal
En windows (por ej.) existe un Timer multimedia con una resolución de
hasta 1 ms, y el standard, que tiene una resolución de ~ 40-50 ms
•
•
Un Timer se suele poner en marcha mediante una llamada a una función a
la que se suele pasar los siguientes parámetros:
§
§
§
§
Periodicidad
Callback function
Tolerancia [opc] (Periodicidad +- Tolerancia)
Puntero [opc] (para intercambio de información entre procesos)
// ejemplo de PortMIDI
Pt_Start(1, &process_midi, (void*)&iData);
// start a timer with millisecond accuracy
Multithreading
•
Si usamos este tipo de arquitecturas (relojes con callbacks o interrupciones en lugar
de polling) tendremos al menos dos “procesos” (el principal y el llamado de vez en
cuando por las interrupciones) è multithreading
§ NOTA: la diferencia entre threads y procesos es que los primeros comparten el
mismo espacio de memoria, variables, etc…
•
Sin embargo, en bastantes casos, el programador no gestiona directamente estos
threads, cuyo uso puede venir facilitado por las librerías que se utilicen
•
Ejemplos:
§ Salida de audio a la tarjeta: una librería (e.g. DirectX) se encarga de esto a partir
de unos frames de tamaño predefinido (y “rellenados” por nuestro programa)
§ Entrada de datos MIDI o serie: las librerías suele invocar un thread que coloca
los datos en una cola
§ Procesado de imagen a t.real: las librerías suele llamar a una función a la que le
pasan el frame (imagen) recién capturado. En estos casos, nuestro programa
NO controla el bucle principal…
§ Si utilizamos alguna librería de audio de más alto nivel que DirectX (e.g. CLAM,
PortAudio…) la librería puede comportarse de forma similar al caso de vídeo. En
lugar de responsabilizarnos del bucle principal, debemos escribir la función que
se ejecuta periódicamente y que se encarga de “rellenar” un frame de audio
(calcular todas sus muestras). La llamada a esta función (callback) queda como
responsabilidad del scheduler de la librería
La librería PortMusic
Cada dispositivo-puerto MIDI (hardware o
virtual) tiene un driver asociado
MIDI en Windows
(drivers virtuales: e.g. MidiYoke)
Entre la aplicación y el driver existe una capa
intermedia (winmm.dll) con centenares de
funciones para
•Audio (Wave), MIDI, Timers, Video,
Joystick
En cualquiera de estos casos, deberemos incluir
mmsystem.h y linkar con winmm.lib
Winmm
•
En el caso de MIDI, las funciones básicas nos permiten
§ Preguntar por puertos disponibles (I & O) y sus propiedades
§ Abrirlos / cerrarlos
§ Recibir mensajes de los puertos de entrada
§ Mandar mensajes a los puertos de salida
§ Además también habrá que utilizar las de relojes…
•
Winmm es suficiente para aplicaciones sencillas (sólo MIDI OUT) (cfg.
Anaya cap. 17), pero demasiado laborioso para aplicaciones más
complejas... Por ello es recomendable utilizar alguna API adicional
Uso de la API PortMusic (en C)
•
•
•
•
•
•
•
MIDI + Audio (con PortAudio)
Multiplataforma (Win MME, Win DX, Win Asio, Linux ALSA, Mac
OS)
Encapsula timers y colas de entrada
Facilita gestión de colas adicionales
Todo el código es abierto
Bajo nivel, no añade latencia
En el caso de Windows, se puede crear aplicaciones MFC, de
consola, o con cualquier otro GUI Toolkit. La API sigue llamando
a winmm.dll por lo que habrá que seguir linkando con winmm.lib
(además de las propias de PortMusic)
Instalación
•
•
•
Download (sólo 85Kb) y la documentación (sólo un .h, que contiene
una descripción de cada una de las funciones)
Hay que compilar los proyectos para obtener las librerías
(portmidi.lib, porttime.lib y pm_dll.dll)
Se incluyen los proyectos que generan estas librerías, junto con
ejemplos sencillos
§ Entrada y salida
§ Envío sistema exclusivo
§ Aplición multithreaded
§ …
Funciones PortMusic
•
•
•
•
Listar y abrir puertos MIDI (IN & OUT)
Recibir mensajes del IN y enviar mensajes al OUT
Crear timers con callbacks para procesar las entradas
Gestionar colas de mensajes para comunicar callbacks y otros
procesos
Estructuras de datos PortMusic
•
•
•
Mensaje MIDI 3 bytes
Win usa un ULONG con el status en el LSB:
MSBà 00 data2 data1 status ß LSB
#define Pm_Message(status, ch, data1, data2)
((((data2) << 16) & 0xFF0000) |
(((data1) << 8) & 0xFF00) |
((status | ch) & 0xFF))
•
•
Evento MIDI: mensaje + timestamp
El timestamp no se mide en ms sino en ticks (ULONG)
typedef long PmMessage;
typedef struct {
PmMessage message;
PmTimestamp timestamp;
} PmEvent;
Entradas y salidas
Entrada y salida (varias opciones)
1.
2.
El puerto de entrada ejecuta un thread independiente que se activa
(otra callback) cuando recibe mensajes
El puerto de entrada deja mensajes en una cola. La callback de
salida mira si hay mensajes en la cola (poll) y los procesa (aún así
tenemos como mínimo 2 threads de que preocuparnos: principal +
timer-callback)
Esta función podría estar mirando en un array (o lista o array, etc.) de
eventos (con timestamp) para ver si debe mandar algo a la salida
(los ticks los debe controlar la función)
•
Esta función no debería ejecutar ninguna llamada tipo leer/escribir
en fichero, consola… se podría retrasar
Comunicación entre threads
Caso A) Imaginemos que al definir la callback indicamos una variable de
intercambio de 32 bits
int iData;
Pt_Start(1,&process_midi,(void*)&iData);
//1 ms, callback, variable de intercambio
Thread principal y callback comparten esta información. OK.
Caso B)
struct Data data[10];
StartTimer(1,&process_midi,(void*)data);
//1 ms, callback, zona memoria de intercambio
Ahora pueden surgir problemas. Porqué? Cual es la diferencia?
Sincronía en Multithreading
•
Por definición, si dos threads son independientes es imposible garantizar la
sincronía entre ellos, con una precisión “máxima”
•
Esto es generalizable al uso de cualquier dato compartido por 2 o más
threads
§ Orden de actualización?
§ Problema más grave: que el thread A que escribe los datos, se
interrumpa durante la escritura para dar paso al thread B que los leerá
(parcialmente actualizados) è 2 procesos leyendo y escribiendo
“simultáneamente” de las mismas posiciones de memoria
•
Soluciones:
§ Semáforos: mecanismos de bloqueo de acceso
§ Más simple: intercambio de datos entre threads mediante colas de
mensajes
• Sólo uno de los threads debe tener acceso de escritura
• Si 2 threads deben intercambiar datos mutuamente (ambos con
posibilidad I/O) è 2 colas
• PortMusic implementa estas colas de comunicación (ej.
midithread.c)
Control de tiempos, tempo, ficheros…
Tempo y ticks
•
•
•
Tempo = negras/minuto
Resolución del secuenciador = ticks/negra
Duración de un tick = ms/tick
•
Cómo calcular la duración de un tick en función del tempo y la
resolución?
60.000 [ms]
1 [min]
----------------- x ---------------------------------1 [min]
Tempo*Resolución [tick]
Problemas?
Tempo y ticks
•
•
•
Tempo = negras/minuto
Resolución del secuenciador = ticks/negra
Duración de un tick = ms/tick
•
Cómo calcular la duración de un tick en función del tempo y la
resolución?
60.000 [ms]
1 [min]
----------------- x ---------------------------------1 [min]
Tempo*Resolución [tick]
Problemas?
En la práctica, no todos los tempos son posibles, ya que la duración
de 1 tick es int
Soluciones:
èCuantizar los tempos (si el usuario introduce un tempo imposible,
se le avisa del redondeo – poco prof!)
èAcumular errores y corregir (e.g. si la duración teórica de 1 tick es
1,5 ms, cada 2 ticks de 1 ms se añade 1 tick fantasma que no se
cuenta - ~años bisiestos)
Además, El timestamp puede ser
absoluto o relativo (en SMF es
relativo, en PortMusic es abierto)
Si es relativo, al insertar un evento
hay que corregir el TimeStamp del
siguiente
Standard MIDI Files
http://www.sfu.ca/sca/Manuals/247/midi/fileformat.html
http://www.sonicspot.com/guide/midifiles.html
Un SMF se almacena como varias pistas, cada una de ellas con TimeStamps relativos (a los
eventos de esta pista)
Para reproducir el fichero
1.
Se convierte cada pista a t absolutos
2.
Se combinan todas las pistas en una sola en memoria
3.
Opcionalmente (si nuestro reloj funciona con relativos), se vuelven a “relativizar”
Si no trabajamos con SMF, y lo hacemos con t absolutos, no es necesario todo esto:
•
•
•
•
Podemos tener un array 2D (un array de secuencias donde cada secuencia es un
array de eventos)
Y un array de índices (cada índice indica el próximo evento de la secuencia)
A cada tick, miramos para cada secuencia si el timestamp del próximo evento coincide
con el tick actual. Si sí, lo mandamos a la salida e incrementamos el índice.
Si además de un secuenciador, quisiéramos un editor, los arrays no son suficientes
(hay que insertar/borrar…). Habría que trabajar con listas enlazadas.
Varios artículos sobre diseño de lenguajes y sistemas
para interactividad en tiempo real
(especialmente en audio)
§ Dannenberg, ``Software Design for Interactive Multimedia
Performance,'' Interface - Journal of New Music Research, 22(3)
(August 1993), pp. 213-228.
§ Brandt and Dannenberg, ``Low-Latency Music Software Using OffThe-Shelf Operating Systems,'' in Proceedings of the International
Computer Music Conference, San Francisco: International Computer
Music Association, (1998), pp.137-141.
§ Brandt and Dannenberg, ``Time in Distributed Real-Time Systems,''
in Proceedings of the 1999 International Computer Music
Conference, San Francisco: International Computer Music
Association, (1999), pp. 523-526.
§ Bencina and Burk, “PortAudio – an Open Source Cross Platform
Audio API”, in Proceedings of the 2001 International Computer
Music Conference, San Francisco: International Computer Music
Association, (2001).
§ è What is latency and how to tune it?
Apéndice A
Ejemplos de bucles de control
•
NB: Se incluyen a “título informativo” ya que la mayoría de APIs
(incl. PortMusic) ya se encargan de la gestión de estos bucles
Suposiciones:
• Queremos hacer algo periódicamente, cada h
• El sistema es multithreated: que un proceso espere un rato
determinado, no significa que el sistema se quede sin hacer nada
Ejemplos de bucles control
•
Cuales son los problemas de estos ejemplos?
§ sleep(h) indica que el proceso espera un tiempo relativo h (podría ser wait,
delay, etc.)
§ sleepUntil(t) indica que el proceso espera hasta el instante t
while (true) do {
executeController();
sleep(h);
}
while (true) {
start = getCurrentTime();
executeController();
end = getCurrentTime();
sleep(h - (end - start));
}
while (true) {
start = getCurrentTime();
executeController();
nexttime = start + h;
sleepUntil(nexttime);
}
No se tiene en cuenta el tiempo de ejecución
del proceso
Podría ser que la tarea se haya interrumpido
entre las líneas 4 y 5
Tampoco se tiene en cuenta el tiempo
transcurrido entre las líneas 5 y 2
Ejemplos de bucles control
•
Cuales son los problemas de estos ejemplos?
§ sleep(h) indica que el proceso espera un tiempo relativo h (podría ser wait,
delay, etc.)
§ sleepUntil(t) indica que el proceso espera hasta el instante t
while (true) do {
executeController();
sleep(h);
}
while (true) {
start = getCurrentTime();
executeController();
end = getCurrentTime();
sleep(h - (end - start));
}
while (true) {
start = getCurrentTime();
executeController();
nexttime = start + h;
sleepUntil(nexttime);
}
No se tiene en cuenta el tiempo de ejecución
del proceso
Podría ser que la tarea se haya interrumpido
entre las líneas 4 y 5
Tampoco se tiene en cuenta el tiempo
transcurrido entre las líneas 5 y 2
Ejemplos de bucles control
•
Cuales son los problemas de estos ejemplos?
§ sleep(h) indica que el proceso espera un tiempo relativo h (podría ser wait,
delay, etc.)
§ sleepUntil(t) indica que el proceso espera hasta el instante t
while (true) do {
executeController();
sleep(h);
}
while (true) {
start = getCurrentTime();
executeController();
end = getCurrentTime();
sleep(h - (end - start));
}
while (true) {
start = getCurrentTime();
executeController();
nexttime = start + h;
sleepUntil(nexttime);
}
No se tiene en cuenta el tiempo de ejecución
del proceso
Pudiera ser que la tarea se hubiera
interrumpido entre las líneas 4 y 5
Tampoco se tiene en cuenta el tiempo
transcurrido entre las líneas 5 y 2
Ejemplos de bucles control
•
Cuales son los problemas de estos ejemplos?
§ sleep(h) indica que el proceso espera un tiempo relativo h (podría ser wait,
delay, etc.)
§ sleepUntil(t) indica que el proceso espera hasta el instante t
while (true) do {
executeController();
sleep(h);
}
while (true) {
start = getCurrentTime();
executeController();
end = getCurrentTime();
sleep(h - (end - start));
}
while (true) {
start = getCurrentTime();
executeController();
nexttime = start + h;
sleepUntil(nexttime);
}
No se tiene en cuenta el tiempo de ejecución
del proceso
Pudiera ser que la tarea se hubiera
interrumpido entre las líneas 4 y 5
Tampoco se tiene en cuenta el tiempo
transcurrido entre las líneas 5 y 2
Ejemplos de bucles control (y 2)
nexttime = getCurrentTime();
while (true) {
executeController();
nexttime = nexttime + h;
sleepUntil(nexttime);
}
• Esto ya es correcto
• Que sucede cuando un ciclo dura más de la cuenta?
è Que el siguiente dura menos
Ejemplos de bucles control (y 2)
nexttime = getCurrentTime();
while (true) {
executeController();
nexttime = nexttime + h;
sleepUntil(nexttime);
}
• Esto ya es correcto
• Que sucede cuando un ciclo dura más de la cuenta?
è Que el siguiente dura menos (se mantiene el promedio)
Esto puede ser lo que busquemos o puede que no… Por ello, se deja al
estudiante, la modificación de este fragmento, para el caso en que se
deseara que el ciclo siguiente a un ciclo demasiado largo, siguiera teniendo
la duración correcta (para ello, tal vez haya que descomponer
executeController() en varias fases [input, output…] )
Apéndice B
Punteros a funciones
•
Las llamadas de tipo callback se implementan mediante punteros a
funciones
Punteros a funciones (1)
•
Los punteros a funciones son punteros al inicio de la función en memoria, a la
dirección inicial dentro del código donde comienza la función.
•
•
•
Se utilizan para ofrecer algoritmos alternativos dentro de un algoritmo mayor…
Para personalizar fragmentos de código dentro de procesos predefinidos…
Permiten crear arrays de funciones de forma que cada elemento del array toma la
dirección de una función …
•
Para pasar una función (f2) como parámetro (de una función f1) se escribe: f1(f2)
Esto es así, porqué al igual que el nombre de un array (sin [ ]) es igual a la
dirección de su primer elemento, el nombre de una función (sin ( ) ) equivale a la
dirección de inicio de esta función en memoria
•
En el prototipo de la función f1 deberemos indicar también el prototipo de la función
f2. Por ejemplo si f2 es: int f2 (char *), y f1 recibe f2 como argumento, entonces la
declaración de f1 será: f1(int(*f2)(char *))
Para llamar a f2, desde el código de f1, escribiremos: (*f2)(…)
f1(int(*f2)(char *)) {
char *p;
int a;
....
a=(*f2)(p);
...
}
En el caso de funciones callback, f1 suele estar escrito. Lo que debemos hacer es
“rellenar” el código de f2
•
•
Punteros a funciones (y 2)
•
•
Los punteros a funciones se utilizan mucho par ordenaciones. En las ordenaciones
se realizan dos acciones básicas: la de comparación y la de cambio de lugar.
La comparación no es algo trivial. No es válido el símbolo < para comparar cadenas,
o muchos otros tipos de datos. A una función de ordenación le podremos determinar
cómo se compara para que sea genérica. El prototipo de una función de este estilo
podría ser:
int ordenar (void *array_a_ordenar, unsigned int n_elem, unsigned int tam_elem, int
(*pfcomparacion)(const void *, const void *))
§ El const se utiliza para evitar que la función de comparación pueda modificar los
elementos a comparar
§ El puntero a elementos a ordenar es de tipo void para poder ordenar cualquier
cosa
§ Por esta misma razón, es necesario indicar el tamaño de cada elemento
•
En ANSI C existe la función qsort, que utiliza el algoritmo quick sort. Lo único que
deberemos escribir para que funcione es la función que compara.
•
Otra aplicación de los punteros a funciones es la de crear menús u opciones
diferentes:
int f1(void);
int f2(void);
int f3(void);
inf (*pf[3])( ); // esto es un array de punteros a cada una de estas tres funciones
Para llamar a una de estas funciones podríamos hacer:
while ((i=opcion( ) ) > 0 && i < 3)
(*pf[i] )( );
Apéndice C
Ciclos interactivos – Escalas temporales
Antes hemos hablado de ciclos que deben ser estables ¿pero …
que frecuencia deben tener? ¿cuanto deben valer este tiempo
justo?
Ciclos Interactivos – Escalas temporales
Imagen
•
•
A partir de ~10 Hz percibimos un continuo de movimiento
§ Cine ?
§ TV ?
§ Generada por ordenador ?
Una cosa es la continuidad de movimiento (evitar saltos) y otra es la
continuidad de brillo (evitar flickering), que mejora conforme
aumenta la frecuencia
§ Por ello, estos sistemas muestran cada imagen 2 veces
(cine=48 Hz, PAL=50 Hz, NTSC=60 Hz)
§ Como se ve entonces una película en TV?
• En Europa (PAL) + rápido (en un factor 24/25 = 0,96). Una
película de 90 minutos dura 86,4 en TV
• En USA (NTSC) cada par de frames se repite x2 y x3 resp
(48+72=60+60)
Ciclos Interactivos – Escalas temporales
Imagen
•
•
A partir de ~10 Hz percibimos un continuo de movimiento
§ Cine : inicios: 12-16 fps – actual: 24 fps – dibuj.anim.: 12-30 fps
§ TV : PAL: 25 fps – NTSC: 30 fps
§ Generada por ordenador : ~12-30 fps (flexible)
Una cosa es la continuidad de movimiento (evitar saltos) y otra es la
continuidad de brillo (evitar flickering), que mejora conforme
aumenta la frecuencia
§ Por ello, estos sistemas muestran cada imagen 2 veces
(cine=48 Hz, PAL=50 Hz, NTSC=60 Hz)
§ Como se ve entonces una película en TV?
• En Europa (PAL) + rápido (en un factor 24/25 = 0,96). Una
película de 90 minutos dura 86,4 en TV
• En USA (NTSC) cada par de frames se repite x2 y x3 resp
(48+72=60+60)
Ciclos Interactivos – Escalas temporales
Sonido
•
Sonido: la freq. de muestreo está relacionada con la
máxima freq. audible (T.Nyquist dice que debe ser como
mínimo el doble)
§ Comprensión “perfecta” voz hablada : a partir de ~3 ó
4.000 Hz
§ Calidad CD : 44.100 Hz
§ Sistemas grabación profesionales : 96.000 Hz
•
Significa esto que un sistema interactivo sonoro debe
funcionar varios miles de veces + rápido que un sistema
interactivo visual?
No se debe confundir el ciclo o la frecuencia de
reproducción de medios, con el ciclo o la frecuencia de
interacción
Ciclos Interactivos – Escalas temporales
Sonido
•
Sonido: la freq. de muestreo está relacionada con la
máxima freq. audible (T.Nyquist dice que debe ser como
mínimo el doble)
§ Comprensión “perfecta” voz hablada : a partir de ~3 ó
4.000 Hz
§ Calidad CD : 44.100 Hz
§ Sistemas grabación profesionales : 96.000 Hz
•
Significa esto que un sistema interactivo sonoro debe
funcionar varios miles de veces + rápido que un sistema
interactivo visual?
No se debe confundir el ciclo o la frecuencia de
reproducción de medios, con el ciclo o la frecuencia de
interacción
Duración de un ciclo?
•
Necesidades de la interacción (depende del tipo de aplicación, de
centésimas a centenares de Hz)
Limitaciones velocidad hard+soft (todos los procesos de un ciclo
deberían completarse antes del inicio del nuevo ciclo)
•
•
Algunos ejemplos
§
§
Windows GUI (timer...)
Termostato aire acondicionado vs. Termostato reactor nuclear
•
•
§
§
§
•
•
Sistemas blandos vs. duros (donde pasar del ciclo puede ser catastrófico)
Para asegurar la estabilidad, de acuerdo con el T.Nyquist, en principio la
duración de un ciclo debería ser como mínimo, igual a la mitad del tiempo de
cambio de cualquier parámetro externo
Aplicación interactiva procesado de vídeo t.real
Reconocimiento de gestos
Teclado + Secuenciador MIDI
La frecuencia del ciclo de entrada no siempre tiene que coincidir con
la del de salida (e.g. reconocimiento de gestos)
Excepto casos como el teclado MIDI, el ciclo de entrada en HCI suele
estar entre 20-100 Hz (10-50 ms)
Duración de un ciclo?
•
Necesidades de la interacción (depende del tipo de aplicación, de
centésimas a centenares de Hz)
Limitaciones velocidad hard+soft (todos los procesos de un ciclo
deberían completarse antes del inicio del nuevo ciclo)
•
•
Algunos ejemplos
§
§
Windows GUI (timer...) ~20 Hz
Termostato aire acondicionado vs. Termostato reactor nuclear
•
•
§
§
§
•
•
Sistemas blandos vs. duros (donde pasar del ciclo puede ser catastrófico)
Para asegurar la estabilidad, de acuerdo con el T.Nyquist, en principio la
duración de un ciclo debería ser como mínimo, igual a la mitad del tiempo de
cambio de cualquier parámetro externo
Aplicación interactiva procesado de vídeo t.real: al frame rate, a no ser que è
Reconocimiento de gestos: frecuencia min. doble de la del gesto
Teclado + Secuenciador MIDI: en algunos programas hasta 1000 Hz!
La frecuencia del ciclo de entrada no siempre tiene que coincidir con
la del de salida (e.g. reconocimiento de gestos)
Excepto casos como el teclado MIDI, el ciclo de entrada en HCI suele
estar entre 20-100 Hz (10-50 ms)
Escalas de tiempos
The Dictionary of Computer Science (Van Nostrand Reinhold, 1993)
defines a simple table of average response times, and processing
methods and models:
Computer Processing Modes and Times
§
§
§
§
§
§
Card Oriented Batch
Keyboard Oriented Batch
Interactive Computing
Online Inquiry and Transactions
Message Switching
Data Acquisition and Control
100-10000s
1-100s
1-10s
1-10s
0.1-10s
0.01-10s
Which of these are real-time?
è (extraído de Real-Time Computing for Human Computer Interfacing,
Perry Cook)
Descargar