Академический Документы
Профессиональный Документы
Культура Документы
org/
Github - https://github.com/audacious-media-player
Abra o Github e pesquise o que quiser na caixa de busca pois ele pesquisará dentro dos
arquivos de código do Audacious
As imagens que farão parte do trabalho deverão ser printadas e coladas na pasta
compartilhada do Drive - Verifique seu Drive
http://dontpad.com/MedeirosAudacious
http://dontpad.com/PaulosiAudacious
http://dontpad.com/MagalhaesAudacious
DESCREVA AQUI OS TEMAS QUE VOCÊ IRÁ ESCREVER PARA SEREM COPIADAS PARA O SITE
DEPOIS:
<h2>Threads </h2>
<p>Um processo pode ser dividido em diversas partes para se obter um melhor
desempenho de processamento, pois se um processo estiver sendo executado de maneira
individual ele será processado individualmente. Porém um mesmo processo comum pode ser
dividido em diversas processos sendo cada uma das threads um componente do mesmo,
tendo cada uma das partes processadas recebendo atenção do processador de maneira
individual.
Com descreve Tanebaum, "as threads foram inventadas para permitir a combinação de
paralelismo com execução sequencial e chamadas de sistema bloqueantes" [TAN92, p.
509].</p>
<h2>Multithreading</h2>
<p>Execução de várias threads </p>
<h2>Vantagens</h2>
<p>As threads podem resultar diversas vantagens para os programas e para os SO's
[SGG01, p. 83]:
* Melhor capacidade de resposta, por ser mais rápida a criação de uma thread do que
a de um novo processo.
Abaixo está descrita uma das sequências de código que faz parte do pacote de
instalação quando compilado para os sistemas operacionais do Windows que faz uso das
chamadas threads. </p>
-static void
+static gboolean
ensure_gettext_initialized (void)
{
static gsize initialised;
+#ifdef G_OS_WIN32
+ static GThread * init_thread_atomic;
+ GThread * init_thread = g_atomic_pointer_get (& init_thread_atomic);
+
+ /* avoid deadlock if _glib_get_locale_dir() calls back into gettext */
+ if (G_UNLIKELY (init_thread && init_thread == g_thread_self ()))
+ return FALSE;
+#endif
+
if (g_once_init_enter (&initialised))
{
#ifdef G_OS_WIN32
- gchar *tmp = _glib_get_locale_dir ();
+ gchar *tmp;
+
+ g_atomic_pointer_set (&init_thread_atomic, g_thread_self ());
+
+ tmp = _glib_get_locale_dir ();
bindtextdomain (GETTEXT_PACKAGE, tmp);
g_free (tmp);
+
+ g_atomic_pointer_set (&init_thread_atomic, NULL);
#else
bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
#endif
@@ -113,6 +128,8 @@
# endif
g_once_init_leave (&initialised, TRUE);
}
+
+ return TRUE;
}
/**
@@ -128,7 +145,8 @@
const gchar *
glib_gettext (const gchar *str)
{
- ensure_gettext_initialized ();
+ if (G_UNLIKELY (!ensure_gettext_initialized ()))
+ return str;
Em seguida foi definida a variável static gsize initialised. "Uma variável estática tem o
escopo de uma local e a duração de uma global. Isso quer dizer que essas variáveis só são
acessíveis à função que as declara, mas existem durante toda a execução do programa.
Variáveis estáticas têm a vantagem de serem privativas e, ao mesmo tempo, de manterem seu
valor entre uma chamada e outra da função. A palavra static deve prefixar a declaração de
variáveis estáticas" (SLago, pg 40).
Falando sobre o uso de threads na 13º linha o código utiliza as políticas #ifdef
G_OS_WIN32 que possui um código entre esta condicional, que só será compilado se o nome
do símbolo G_OS_WIN32 estiver definido préviamente e neste caso foi definido no header
(cabeçalho) no inicio do código porém está ap.
*Elas são a base para transações atômicas, que por sua vez formam a base para uma
área denominada Processamento de Transações;
<p>Esta condicional faz uso de uma função externa G_UNLIKELY que faz a comparação
de valores lógicos, informando se o ponteiro init_thread é igual a g_thread_self() que também
é uma função e retorna um valor para ser comparado.
Na sequência temos esta parte do código que cuida também do funcionamento das
threads em execução. </p>
<pre><code class="c++">/*
adder.c
Copyright 2011-2016 John Lindgren
*/
#include "playlist-internal.h"
#include "internal.h"
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "audstrings.h"
#include "hook.h"
#include "i18n.h"
#include "list.h"
#include "mainloop.h"
#include "plugins-internal.h"
#include "probe.h"
#include "runtime.h"
#include "tuple.h"
#include "interface.h"
#include "vfs.h"
// regrettably, strcmp_nocase can't be used directly as a
// callback for Index::sort due to taking a third argument;
// strcmp also triggers -Wnoexcept-type with GCC 7
static int filename_compare (const char * a, const char * b)
#ifdef _WIN32
{
return strcmp_nocase (a, b);
}
#else
{
return strcmp (a, b);
}
#endif
struct AddTask : public ListNode
{
Playlist playlist;
int at;
bool play;
Index<PlaylistAddItem> items;
Playlist::FilterFunc filter;
void * user;
};
struct AddResult : public ListNode
{
Playlist playlist;
int at;
bool play;
String title;
Index<PlaylistAddItem> items;
bool saw_folder, filtered;
};
static void * add_worker (void * unused);
static List<AddTask> add_tasks;
static List<AddResult> add_results;
static Playlist current_playlist;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool add_thread_started = false;
static bool add_thread_exited = false;
static pthread_t add_thread;
static QueuedFunc queued_add;
static QueuedFunc status_timer;
static char status_path[512];
static int status_count;
static bool status_shown = false;
static void status_cb (void * unused)
{
pthread_mutex_lock (& mutex);
char scratch[128];
snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found",
"%d files found", status_count), status_count);
if (aud_get_headless_mode ())
{
printf ("Searching, %s ...\r", scratch);
fflush (stdout);
}
else
{
hook_call ("ui show progress", status_path);
hook_call ("ui show progress 2", scratch);
}
status_shown = true;
pthread_mutex_unlock (& mutex);
}
static void status_update (const char * filename, int found)
{
pthread_mutex_lock (& mutex);
snprintf (status_path, sizeof status_path, "%s", filename);
status_count = found;
if (! status_timer.running ())
status_timer.start (250, status_cb, nullptr);
pthread_mutex_unlock (& mutex);
}
static void status_done_locked ()
{
status_timer.stop ();
if (status_shown)
{
if (aud_get_headless_mode ())
printf ("\n");
else
hook_call ("ui hide progress", nullptr);
status_shown = false;
}
}
static void add_file (PlaylistAddItem && item, Playlist::FilterFunc filter,
void * user, AddResult * result, bool skip_invalid)
{
AUDINFO ("Adding file: %s\n", (const char *) item.filename);
status_update (item.filename, result->items.len ());
/*
If possible, we'll wait until the file is added to the playlist to probe
it. There are a couple of reasons why we might need to probe it now:
1. We're adding a folder, and need to skip over non-audio files (the
"skip invalid" flag indicates this case).
2. The file might have subtunes, which we need to expand in order to add
them to the playlist correctly.
<p>É um mecanismo utilizado para a realização de exclusões mútuas as chamadas Mutex, que
é muito utilizada nas programações concorrentes, ou seja, quando mais de um processo
disputa informações ou dados em um sistema operacional. São usadas para que dois processos
ou threads como neste caso, tenham acesso simultâneo a um recurso compartilhado, acesso
esse conhecido como seção crítica. A variável pthread_mutex_t mutex como vemos recebe
como um valor passado por uma variável externa ao código que deverá ser definida durante a
compilação, que se dará de acordo com o sistema operacional onde será executado. </p>
<p>A função apresentada acima status_cb() faz a verificação de status dos processos,
utiliza uma condicional com a função aud_get_headless_mode () externa ao código em análise
mas tem sua importância no funcionamento do aplicativo já que este faz uso da função fflush()
para controle do buffer e a o stdout irá definir qual buffer será limpo, esta função faz parte da
bibilioteca stdio.h, em caso de sucesso a função retorna o valor zero. </p>
<p>Foram definidas também algumas variáveis utilizadas para o controle das threads
em execução e que mostram o status de cada processo. </p>
O leitor pode acompanhar que no código descrito de maneira geral adder.c faz uso de
um procedimento add_finish() que trabalha no controle das playlists do aplicativo, e utiliza a s
mesmas chamadas de funções pthread_mutex_lock(), pthread_mutex_unlock(), porém
também já faz uso das funções para a sincronização das músicas definindo um final de
execução para a playlist definida pelo usuário, para isso é usada a variável booleana
add_thread_exited onde verifica-se o status da thread e define se será bloqueada ou colocada
em execução. No procedimento adder_cleanup() tem como finalidade retirar de execução e
colocar em estado de bloqueio. </p>
<p>Uma análise breve deste código que é parte de uma código externo que será
definido através do arquivo makefile já que recebe como inclusão as definições cue-cache.h,
multihash.h e playlist-internal.h que irão ser unidas via inkeditor na compilação.
#include "cue-cache.h"
#include "multihash.h"
#include "playlist-internal.h"
#include <pthread.h>
struct CueCacheNode {
Index<PlaylistAddItem> items;
NodeState state = NotLoaded;
int refcount = 0;
};
m_node->refcount ++;
CueCacheRef::~CueCacheRef ()
{
pthread_mutex_lock (& mutex);
m_node->refcount --;
if (! m_node->refcount)
cache.remove (m_filename);
switch (m_node->state)
{
case NotLoaded:
// load the cuesheet in this thread
m_node->state = Loading;
pthread_mutex_unlock (& mutex);
playlist_load (m_filename, title, m_node->items);
pthread_mutex_lock (& mutex);
m_node->state = Loaded;
pthread_cond_broadcast (& cond);
break;
case Loading:
// wait for cuesheet to load in another thread
while (m_node->state != Loaded)
pthread_cond_wait (& cond, & mutex);
break;
case Loaded:
// cuesheet already loaded
break;
}
<pre><code class="c++">/*
mainloop.h
Copyright 2014 John Lindgren
*/</pre></code>
<p>Mainloop é uma camada de abstração de loop principal que pode usar GLib ou Qt
como backend. A API é completamente thread-safe e, portanto, pode ser usada como um
meio de chamar de volta para o segmento principal da execução de uma thread. </p>
A Diretiva ifndef A diretiva #ifndef funciona ao contrário da diretiva #ifdef. Sua forma
geral é: </p>
<p>A sequência de declarações será compilada se o nome da macro não tiver sido
definido.
A classe QueuedFunc utiliza o comando typedef void (* Func) (void * data), que faz o
retorno de chamada ocioso de uma só vez e neste caso o typedef define um ponteiro de
funções. Como um simples exemplo o typedef pode ser usado para definir um novo nome para
um determinado tipo. Sua forma geral é: </p>
O comando typedef também pode ser utilizado para dar nome a tipos complexos,
como as estruturas.
É possivel perceber que para qualquer tipo de retorno de chamada a fila queue() e a
start() também param qualquer retorno de chamada anterior. E quando a função stop() é
chamada se o retorno for true um cronômetro periódico estiver em execução, porém, não se
aplica a retornos de chamada único.
Note que este arquivo e o playlist.cc possuem seu próprio mutex. O playlist.cc é
conceitualmente o mutex "externo" e deve ser bloqueado primeiro (em situações que ambos
precisam ser bloqueados) para evitar o impasse. </p>
<pre><code class="c++">/*
playback.cc
Copyright 2009-2014 John Lindgren
*/
#include "drct.h"
#include "internal.h"
#include <assert.h>
#include <pthread.h>
#include "audstrings.h"
#include "hook.h"
#include "i18n.h"
#include "interface.h"
#include "mainloop.h"
#include "output.h"
#include "playlist-internal.h"
#include "plugin.h"
#include "plugins.h"
#include "plugins-internal.h"
#include "runtime.h"
struct PlaybackState {
bool playing = false;
bool thread_running = false;
int control_serial = 0;
int playback_serial = 0;
};
struct PlaybackControl {
bool paused = false;
int seek = -1;
int repeat_a = -1;
int repeat_b = -1;
};
struct PlaybackInfo {
// set by playback_set_info
int entry = -1;
Tuple tuple;
String title;
// set by playback thread
String filename;
int length = -1;
int time_offset = 0;
int stop_time = -1;
ReplayGainInfo gain {};
bool gain_valid = false;
int bitrate = 0;
int samplerate = 0;
int channels = 0;
bool ready = false;
bool ended = false;
bool error = false;
String error_s;
};
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static PlaybackState pb_state;
static PlaybackControl pb_control;
static PlaybackInfo pb_info;
static QueuedFunc end_queue;
static bool song_finished = false;
static int failed_entries = 0;
static void lock ()
{
pthread_mutex_lock (& mutex);
}
static void unlock ()
{
pthread_mutex_unlock (& mutex);
}
static bool lock_if (bool (* test) ())
{
lock ();
if (test ())
return true;
unlock ();
return false;
}
// verifica se o encadeamento da reprodução não está atrasado
static bool in_sync ()
{
return pb_state.playing && pb_state.control_serial == pb_state.playback_serial;
}
// verifica se o encadeamento de reprodução não está atrasado e se a reprodução está
"pronta"
static bool is_ready ()
{
return in_sync () && pb_info.ready;
}
// chamado por playback_entry_set_tuple () para garantir que a tupla ainda se aplique
// para a música atual da perspectiva do segmento principal / playlist; a
// check é necessário porque playback_entry_set_tuple () é ele mesmo chamado de
// o segmento de reprodução
bool playback_check_serial (int serial)
{
lock ();
bool okay = (pb_state.playing && pb_state.control_serial == serial);
unlock ();
return okay;
}
// chamado da lista de reprodução para atualizar a tupla da música atual
void playback_set_info (int entry, Tuple && tuple)
{
// não faz nada se o segmento de reprodução estiver atrasado;
// Nesse caso, playback_set_info () será chamado novamente de qualquer maneira
if (! lock_if (in_sync))
return;
if (tuple.valid () && tuple != pb_info.tuple)
{
pb_info.tuple = std::move (tuple);
// não chame "tuple change" antes de "reprodução pronta"
if (is_ready ())
{
event_queue ("tuple change", nullptr);
output_set_tuple (pb_info.tuple);
}
}
String title = pb_info.tuple.get_str (Tuple::FormattedTitle);
if (entry != pb_info.entry || title != pb_info.title)
{
pb_info.entry = entry;
pb_info.title = title;
// não chame "title change" antes de "playback ready"
if (is_ready ())
event_queue ("title change", nullptr);
}
unlock ();
}
// limpeza comum para playback_play () e playback_stop ()
static void playback_cleanup_locked ()
{
pb_state.playing = false;
pb_control = PlaybackControl ();
// descarte o buffer de áudio se a música não terminar sozinha
if (! song_finished)
output_flush (0);
// limpeza alternada
end_queue.stop ();
song_finished = false;
event_queue_cancel ("playback ready");
event_queue_cancel ("playback pause");
event_queue_cancel ("playback unpause");
event_queue_cancel ("playback seek");
event_queue_cancel ("info change");
event_queue_cancel ("title change");
event_queue_cancel ("tuple change");
aud_set_bool (nullptr, "stop_after_current_song", false);
}
// main thread: interrompe a reprodução quando não é mais necessário tocar músicas void
playback_stop (bool exiting)
{
if (! pb_state.playing && ! exiting)
return;
lock ();
if (pb_state.playing)
playback_cleanup_locked ();
if (pb_state.thread_running)
{
// se sair descarte o áudio do buffer
if (exiting)
output_flush (0, true);
// thread de reprodução de sinal para drenar o buffer de áudio
pb_state.control_serial ++;
pthread_cond_broadcast (& cond);
// aguarde até que o encadeamento de reprodução termine se sair
while (exiting && pb_state.thread_running)
pthread_cond_wait (& cond, & mutex);
}
unlock ();
// limpeza alternada
failed_entries = 0;
}
// chamada do loop de evento de nível superior após a reprodução terminar
static void end_cb (void *)
{
song_finished = true;
hook_call ("playback end", nullptr);
PlaylistEx playlist = Playlist::playing_playlist ();
auto do_stop = [playlist] ()
{
aud_drct_stop ();
playlist.set_position (playlist.get_position ());
};
auto do_next = [playlist] ()
{
if (! playlist.next_song (aud_get_bool (nullptr, "repeat")))
{
playlist.set_position (-1);
hook_call ("playlist end reached", nullptr);
}
};
if (aud_get_bool (nullptr, "no_playlist_advance"))
{
// assumimos aqui que a repetição não está habilitada;
// repetições de uma única música são tratadas em run_playback ()
do_stop ();
}
else if (aud_get_bool (nullptr, "stop_after_current_song"))
{
do_stop ();
do_next ();
}
else
{
// se 10 músicas em sequência falharem ou se falhou a playlist inteira
// (para playlists com menos de 10 músicas) falhou, pare de tentar
if (failed_entries < aud::min (playlist.n_entries (), 10))
do_next ();
else
do_stop ();
}
}
// helper, pode ser chamado a partir do thread principal ou de reprodução
static void request_seek_locked (int time)
{
// configurar o comando "seek", esteja pronto ou não;
//se não estiver pronto, entrará em vigor após open_audio ()
pb_control.seek = aud::max (0, time);
// trigger seek imediatamente se estiver pronto
if (is_ready () && pb_info.length > 0)
{
output_flush (aud::clamp (time, 0, pb_info.length));
event_queue ("playback seek", nullptr);
}
}
// ajudante de segmento de reprodução de thread
static void run_playback ()
{
// devido ao pedido de mutex, não podemos chamar a playlist enquanto estiver bloqueada
DecodeInfo dec = playback_entry_read (pb_state.playback_serial);
if (! lock_if (in_sync))
return;
// para uma entrada de cuesheet, determine o nome do arquivo de origem
pb_info.filename = pb_info.tuple.get_str (Tuple::AudioFile);
if (! pb_info.filename)
pb_info.filename = std::move (dec.filename);
// verifique se possui todos os dados necessários
if (! pb_info.filename || ! pb_info.tuple.valid () || ! dec.ip ||
(! dec.ip->input_info.keys[InputKey::Scheme] && ! dec.file))
{
pb_info.error = true;
pb_info.error_s = std::move (dec.error);
unlock ();
return;
}
// Obter vários outros bits de informação da tupla
pb_info.length = pb_info.tuple.get_int (Tuple::Length);
pb_info.time_offset = aud::max (0, pb_info.tuple.get_int (Tuple::StartTime));
pb_info.stop_time = aud::max (-1, pb_info.tuple.get_int (Tuple::EndTime) -
pb_info.time_offset);
pb_info.gain = pb_info.tuple.get_replay_gain ();
pb_info.gain_valid = pb_info.tuple.has_replay_gain ();
// força inicial busca se estes estão tocando uma faixa segmentada
if (pb_info.time_offset > 0 && pb_control.seek < 0)
pb_control.seek = 0;
unlock ();
while (1)
{
// // forçar a busca inicial se estivermos jogando um controle segmentado fora do controle
para inserir o plugin
if (! dec.ip->play (pb_info.filename, dec.file))
pb_info.error = true;
// close audio (não abra se este estiver fechado)
output_close_audio ();
if (pb_info.error || pb_info.length <= 0)
break;
if (! lock_if (in_sync))
break;
// verifique se o audio precisa ser repetido
pb_info.ended = (pb_control.repeat_a < 0 && ! (aud_get_bool (nullptr,
"repeat") && aud_get_bool (nullptr, "no_playlist_advance")));
if (! pb_info.ended)
request_seek_locked (pb_control.repeat_a);
unlock ();
if (pb_info.ended)
break;
// retorne o ponteiro do arquivo para repetir
if (! open_input_file (pb_info.filename, "r", dec.ip, dec.file, & pb_info.error_s))
{
pb_info.error = true;
break;
}
}
}
// ajudante da thread de reprodução
static void finish_playback_locked ()
{
// registrar qualquer erro de execução ocorrido
if (pb_info.error)
{
failed_entries ++;
if (pb_info.error_s)
aud_ui_show_error (str_printf (_("Error playing %s:\n%s"),
(const char *) pb_info.filename, (const char *) pb_info.error_s));
else
AUDERR ("Playback finished with error.\n");
}
else
failed_entries = 0;
// queue chama função para iniciar a próxima música (ou realizar limpeza)
end_queue.queue (end_cb, nullptr);
}
// thread de reprodução
static void * playback_thread (void *)
{
lock ();
while (1)
{
// aguardando por um comando
while (pb_state.control_serial == pb_state.playback_serial)
pthread_cond_wait (& cond, & mutex);
// busque o comando ("play" ou "drain")
bool play = pb_state.playing;
// atualiza o número série da thread de execução
pb_state.playback_serial = pb_state.control_serial;
unlock ();
if (play)
run_playback ();
else
output_drain ();
lock ();
if (play)
{
// não reportar erros ou enfileirar a próxima música se outro comando estiver pendente
if (in_sync ())
finish_playback_locked ();
pb_info = PlaybackInfo ();
}
else
{
// sair se não tiver recebido um novo comando depois da drenagem
if (pb_state.control_serial == pb_state.playback_serial)
break;
}
}
// sinalize a thread principal que estará saindo
pb_state.thread_running = false;
pthread_cond_broadcast (& cond);
unlock ();
return nullptr;
}
// main thread: iniciando a reprodução de uma nova música
void playback_play (int seek_time, bool pause)
{
lock ();
if (pb_state.playing)
playback_cleanup_locked ();
// ativando o comando "play"
pb_state.playing = true;
pb_state.control_serial ++;
pb_control.paused = pause;
pb_control.seek = (seek_time > 0) ? seek_time : -1;
// inicia thread de execução (ou sinalize se já estiver rodando)
if (pb_state.thread_running)
pthread_cond_broadcast (& cond);
else
{
pthread_t thread;
pthread_create (& thread, nullptr, playback_thread, nullptr);
pthread_detach (thread);
pb_state.thread_running = true;
}
unlock ();
}
// main thread
EXPORT void aud_drct_pause ()
{
if (! pb_state.playing)
return;
lock ();
// ativando o comando "pause" esteja pronto ou não;
// se não estiver pronto, este entrará em vigor após open_audio()
bool pause = ! pb_control.paused;
pb_control.paused = pause;
// aplique pause imediatamente se estiver executando
if (is_ready ())
output_pause (pause);
event_queue (pause ? "playback pause" : "playback unpause", nullptr);
unlock ();
}
// main thread
EXPORT void aud_drct_seek (int time)
{
if (! pb_state.playing)
return;
lock ();
request_seek_locked (time);
unlock ();
}
EXPORT void InputPlugin::open_audio (int format, int rate, int channels)
{
// não abra o áudio se a thread de execução estiver atrasada
if (! lock_if (in_sync))
return;
if (! output_open_audio (pb_info.filename, pb_info.tuple, format, rate,
channels, aud::max (0, pb_control.seek)))
{
pb_info.error = true;
pb_info.error_s = String (_("Invalid audio format"));
unlock ();
return;
}
if (pb_info.gain_valid)
output_set_replay_gain (pb_info.gain);
if (pb_control.paused)
output_pause (true);
pb_info.samplerate = rate;
pb_info.channels = channels;
if (pb_info.ready)
event_queue ("info change", nullptr);
else
event_queue ("playback ready", nullptr);
pb_info.ready = true;
unlock ();
}
EXPORT void InputPlugin::set_replay_gain (const ReplayGainInfo & gain)
{
lock ();
pb_info.gain = gain;
pb_info.gain_valid = true;
if (is_ready ())
output_set_replay_gain (gain);
unlock ();
}
EXPORT void InputPlugin::write_audio (const void * data, int length)
{
if (! lock_if (in_sync))
return;
// buscar configuração A-B
int a = pb_control.repeat_a;
int b = pb_control.repeat_b;
unlock ();
// estes estarão prontos para chamar output_write_audio() mesmo que não estiver mais em
sincronia,
// já que retornará imediatamente se output_flush () tiver sido chamado
int stop_time = (b >= 0) ? b : pb_info.stop_time;
if (output_write_audio (data, length, stop_time))
return;
if (! lock_if (in_sync))
return;
// se estiverem em sincronia, então ocorreu uma das seguintes situações:
// 1. output_flush() foi chamado devido a um pedido de busca
// 2. nós alcançamos o ponto de repetição B
// 3. nós chegamos ao final de um segmento
if (pb_control.seek < 0)
{
if (b >= 0)
request_seek_locked (a);
else
pb_info.ended = true;
}
unlock ();
}
EXPORT Tuple InputPlugin::get_playback_tuple ()
{
lock ();
Tuple tuple = pb_info.tuple.ref ();
unlock ();
// tuplas passadas de plugins de entrada não possuem campos de fallback
// gerado; para consistência, as tuplas devolvidas não devem ser passadas
tuple.delete_fallbacks ();
return tuple;
}
EXPORT void InputPlugin::set_playback_tuple (Tuple && tuple)
{
// devido à ordenação por mutex, não podemos chamar a playlist enquanto estiver
bloqueada;
// em vez disso, playback_entry_set_tuple () chama de volta para o primeiro
// playback_check_serial () e depois playback_set_info ()
playback_entry_set_tuple (pb_state.playback_serial, std::move (tuple));
}
EXPORT void InputPlugin::set_stream_bitrate (int bitrate)
{
lock ();
pb_info.bitrate = bitrate;
if (is_ready ())
event_queue ("info change", nullptr);
unlock ();
}
EXPORT bool InputPlugin::check_stop ()
{
lock ();
bool stop = ! is_ready () || pb_info.ended || pb_info.error;
unlock ();
return stop;
}
EXPORT int InputPlugin::check_seek ()
{
lock ();
int seek = -1;
if (is_ready () && pb_control.seek >= 0 && pb_info.length > 0)
{
seek = pb_info.time_offset + aud::min (pb_control.seek, pb_info.length);
pb_control.seek = -1;
output_resume ();
}
unlock ();
return seek;
}
// thread de proteção
EXPORT bool aud_drct_get_playing ()
{
lock ();
bool playing = pb_state.playing;
unlock ();
return playing;
}
// thread de proteção
EXPORT bool aud_drct_get_ready ()
{
lock ();
bool ready = is_ready ();
unlock ();
return ready;
}
// thread de proteção
EXPORT bool aud_drct_get_paused ()
{
lock ();
bool paused = pb_control.paused;
unlock ();
return paused;
}
// thread de proteção
EXPORT String aud_drct_get_title ()
{
if (! lock_if (is_ready))
return String ();
String title = pb_info.title;
int entry = pb_info.entry;
int length = pb_info.length;
unlock ();
StringBuf prefix = aud_get_bool (nullptr, "show_numbers_in_pl") ?
str_printf ("%d. ", 1 + entry) : StringBuf (0);
StringBuf time = (length > 0) ? str_format_time (length) : StringBuf ();
StringBuf suffix = time ? str_concat ({" (", time, ")"}) : StringBuf (0);
return String (str_concat ({prefix, title, suffix}));
}
// thread de proteção
EXPORT Tuple aud_drct_get_tuple ()
{
lock ();
Tuple tuple = is_ready () ? pb_info.tuple.ref () : Tuple ();
unlock ();
return tuple;
}
// thread de proteção
EXPORT void aud_drct_get_info (int & bitrate, int & samplerate, int & channels)
{
lock ();
bool ready = is_ready ();
bitrate = ready ? pb_info.bitrate : 0;
samplerate = ready ? pb_info.samplerate : 0;
channels = ready ? pb_info.channels : 0;
unlock ();
}
// thread de proteção
EXPORT int aud_drct_get_time ()
{
lock ();
int time = is_ready () ? output_get_time () : 0;
unlock ();
return time;
}
// thread de proteção
EXPORT int aud_drct_get_length ()
{
lock ();
int length = is_ready () ? pb_info.length : -1;
unlock ();
return length;
}
// thread de proteção
EXPORT void aud_drct_set_ab_repeat (int a, int b)
{
if (! pb_state.playing)
return;
lock ();
pb_control.repeat_a = a;
pb_control.repeat_b = b;
if (b >= 0 && is_ready () && output_get_time () >= b)
request_seek_locked (a);
unlock ();
}
// thread de proteção
EXPORT void aud_drct_get_ab_repeat (int & a, int & b)
{
lock ();
a = pb_control.repeat_a;
b = pb_control.repeat_b;
unlock ();
}
</pre></code>
<h1>Referências Bibliográficas: </h1>
<p>Pereira, Silvio do Lago, Linguagem C, 2001, São Paulo.
https://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Atomic_Operations.html
Jandl, Peter, Jr. Notas sobre Sistemas Operacionais/Peter Jandl Jr. Apostila 1. Sistemas
operacionais: Computadores : Processamento de dados : 005.43 2004
https://github.com/audacious-media-player
https://developer.gnome.org/glib/stable/glib-Threads.html
https://pt.wikibooks.org/wiki/Programar_em_C/Pr%C3%A9-processador
https://mail.gnome.org/archives/commits-list/2012-November/msg03969.html
http://linguagemc.com.br/funcao-com-passagem-por-referencia/
www.gnu.org/s/libc/manual/html.../Locating-gettext-catalog.html
http://www.uniriotec.br/~morganna/guia/libc/fc_fflush.html
http://www.br-c.org/doku.php?id=snprintf15:12 22/05/2018</p>